Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1187)

Unified Diff: chrome/common/string_ordinal.cc

Issue 8236002: Create StringOrdinal to allow placement of strings in sorted lists (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Clearer comment for AddHalf failing and extra test for its loop Created 9 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « chrome/common/string_ordinal.h ('k') | chrome/common/string_ordinal_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/common/string_ordinal.cc
diff --git a/chrome/common/string_ordinal.cc b/chrome/common/string_ordinal.cc
new file mode 100644
index 0000000000000000000000000000000000000000..1f6b91a43c059e134e1775dc2cb0f8ea33eff00f
--- /dev/null
+++ b/chrome/common/string_ordinal.cc
@@ -0,0 +1,239 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/string_ordinal.h"
+
+#include <algorithm>
+#include <cstddef>
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+
+namespace {
+
+// Constants for StringOrdinal digits.
+const char kZeroDigit = 'a';
+const char kMinDigit = 'b';
+const char kMidDigit = 'n';
+const char kMaxDigit = 'z';
+const int kMidDigitValue = kMidDigit - kZeroDigit;
+const int kMaxDigitValue = kMaxDigit - kZeroDigit;
+const int kRadix = kMaxDigitValue + 1;
+COMPILE_ASSERT(kMidDigitValue == 13, kMidDigitValue_incorrect);
+COMPILE_ASSERT(kMaxDigitValue == 25, kMaxDigitValue_incorrect);
+COMPILE_ASSERT(kRadix == 26, kRadix_incorrect);
+
+// Helper Functions
+
+// Returns the length that string value.substr(0, length) would be with
+// trailing zeros removed.
+size_t GetLengthWithoutTrailingZeros(const std::string& value, size_t length) {
+ DCHECK(!value.empty());
+
+ size_t end_position = value.find_last_not_of(kZeroDigit, length - 1);
akalin 2011/10/26 18:36:42 if you want to consider the entire string, just om
csharp 2011/10/26 19:59:18 We don't want to always consider the entire string
+
+ // If no non kZeroDigit is found then the string is a string of all zeros
+ // digits so we return 0 as the correct length.
+ if (end_position == std::string::npos)
+ return 0;
+
+ return end_position + 1;
+}
+
+// Return the digit value at position i, padding with kZeroDigit if required.
+int GetPositionValue(const std::string& str, size_t i) {
+ return (i < str.length()) ? (str[i] - kZeroDigit) : 0;
+}
+
+// Add kMidDigitValue to the value at position index. This returns false if
+// adding the half results in an overflow past the first digit, otherwise it
+// returns true. This is used by ComputeMidpoint.
+bool AddHalf(size_t position, std::string& value) {
+ DCHECK_GT(position, 0U);
+ DCHECK_LT(position, value.length());
+
+ // We can't perform this operation directly on the string because
+ // overflow can occur and mess up the values.
+ int new_position_value = value[position] + kMidDigitValue;
+
+ if (new_position_value <= kMaxDigit) {
+ value[position] = new_position_value;
+ } else {
+ value[position] = new_position_value - kRadix;
+ ++value[position - 1];
+
+ for (size_t i = position - 1; value[i] > kMaxDigit; --i) {
+ if (i == 0U)
+ return false;
+ value[i] -= kRadix;
+ ++value[i - 1];
+ }
+ }
+
+ return true;
+}
+
+// Drops off the last digit of value and then all trailing zeros iff that
+// doesn't change its ordering as greater than |start|.
+void DropUnneededDigits(const std::string& start, std::string* value) {
+ CHECK_GT(*value, start);
+
+ size_t drop_length = GetLengthWithoutTrailingZeros(*value, value->length());
+ // See if the value can have its last digit removed without affecting
+ // the ordering.
+ if (drop_length > 1) {
+ // We must drop the trailing zeros before comparing |shorter_value| to
+ // |start| because if we don't we may have |shorter_value|=|start|+|a|*
+ // where |shorter_value| > |start| but not when it drops the trailing |a|s
+ // to become a valid StringOrdinal value.
+ int truncated_length = GetLengthWithoutTrailingZeros(*value,
+ drop_length - 1);
+
+ if (truncated_length != 0 && value->compare(0, truncated_length, start) > 0)
+ drop_length = truncated_length;
+ }
+
+ value->resize(drop_length);
+}
+
+// Compute the midpoint string that is between |start| and |end|.
+std::string ComputeMidpoint(const std::string& start,
+ const std::string& end) {
+ size_t max_size = std::max(start.length(), end.length()) + 1;
+ std::string midpoint(max_size, kZeroDigit);
+
+ bool add_half = false;
+ for (size_t i = 0; i < max_size; ++i) {
+ int char_value = GetPositionValue(start, i);
+ char_value += GetPositionValue(end, i);
+
+ midpoint[i] += (char_value / 2);
+ if (add_half) {
+ // AddHalf only returns false if (midpoint[0] > kMaxDigit) and it
akalin 2011/10/26 18:36:42 Just say that AddHalf() returning false implies th
csharp 2011/10/26 19:59:18 Done.
+ // needs to overflow to the digit before it, which doesn't exist. This
+ // would mean that the midpoint string would have have a different
+ // pre-first digit than both |start| and |end|, which means it isn't
+ // between them anymore.
+ CHECK(AddHalf(i, midpoint));
+ }
+
+ add_half = (char_value % 2 == 1);
+ }
+ DCHECK(!add_half);
+
+ return midpoint;
+}
+
+// Create a StringOrdinal that is lexigraphically greater than |start| and
+// lexigraphically less than |end|. The returned StringOrdinal will be roughly
+// between |start| and |end|.
+StringOrdinal CreateStringOrdinalBetween(const StringOrdinal& start,
+ const StringOrdinal& end) {
+ CHECK(start.IsValid());
+ CHECK(end.IsValid());
+ CHECK(start.LessThan(end));
+ const std::string& start_string = start.ToString();
+ const std::string& end_string = end.ToString();
+ DCHECK_LT(start_string, end_string);
+
+ std::string midpoint = ComputeMidpoint(start_string, end_string);
+
+ DropUnneededDigits(start_string, &midpoint);
+
+ DCHECK_GT(midpoint, start_string);
+ DCHECK_LT(midpoint, end_string);
+
+ StringOrdinal midpoint_ordinal(midpoint);
+ DCHECK(midpoint_ordinal.IsValid());
+ return midpoint_ordinal;
+}
+
+// Returns true iff the input string matches the format [a-z]*[b-z].
+bool IsValidStringOrdinal(const std::string& value) {
+ if (value.empty())
+ return false;
+
+ for (size_t i = 0; i < value.length(); ++i) {
+ if (value[i] < kZeroDigit || value[i] > kMaxDigit)
+ return false;
+ }
+
+ return value[value.length() - 1] != kZeroDigit;
+}
+
+} // namespace
+
+StringOrdinal::StringOrdinal(const std::string& string_ordinal)
+ : string_ordinal_(string_ordinal),
+ is_valid_(IsValidStringOrdinal(string_ordinal_)) {
+}
+
+StringOrdinal::StringOrdinal() : string_ordinal_(""),
+ is_valid_(false) {
+}
+
+bool StringOrdinal::IsValid() const {
+ return is_valid_;
+}
+
+bool StringOrdinal::LessThan(const StringOrdinal& other) const {
+ CHECK(IsValid());
+ CHECK(other.IsValid());
+ return string_ordinal_ < other.string_ordinal_;
+}
+
+bool StringOrdinal::Equal(const StringOrdinal& other) const {
+ CHECK(IsValid());
+ CHECK(other.IsValid());
+ return string_ordinal_ == other.string_ordinal_;
+}
+
+StringOrdinal StringOrdinal::CreateBetween(const StringOrdinal& other) const {
+ CHECK(IsValid());
+ CHECK(other.IsValid());
+ CHECK(!Equal(other));
+
+ if (LessThan(other)) {
+ return CreateStringOrdinalBetween(*this, other);
+ } else {
+ return CreateStringOrdinalBetween(other, *this);
+ }
+}
+
+StringOrdinal StringOrdinal::CreateBefore() const {
+ CHECK(IsValid());
+ // Create the smallest valid StringOrdinal of the appropriate length
+ // to be the minimum boundary.
+ const size_t length = string_ordinal_.length();
+ std::string start(length, kZeroDigit);
+ start[length - 1] = kMinDigit;
+ if (start == string_ordinal_) {
+ start[length - 1] = kZeroDigit;
+ start += kMinDigit;
+ }
+
+ // Even though |start| is already a valid StringOrdinal that is less
+ // than |*this|, we don't return it because we wouldn't have much space in
+ // front of it to insert potential future values.
+ return CreateBetween(StringOrdinal(start));
+}
+
+StringOrdinal StringOrdinal::CreateAfter() const {
+ CHECK(IsValid());
+ // Create the largest valid StringOrdinal of the appropriate length to be
+ // the maximum boundary.
+ std::string end(string_ordinal_.length(), kMaxDigit);
+ if (end == string_ordinal_)
+ end += kMaxDigit;
+
+ // Even though |end| is already a valid StringOrdinal that is greater than
+ // |*this|, we don't return it because we wouldn't have much space after
+ // it to insert potential future values.
+ return CreateBetween(StringOrdinal(end));
+}
+
+std::string StringOrdinal::ToString() const {
+ CHECK(IsValid());
+ return string_ordinal_;
+}
« no previous file with comments | « chrome/common/string_ordinal.h ('k') | chrome/common/string_ordinal_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698