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

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: Changing StringIndex to StringOrdinal and making code review changes. 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
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..de87cf81adfb92d0d2939aae6cdb97aa7ed81075
--- /dev/null
+++ b/chrome/common/string_ordinal.cc
@@ -0,0 +1,207 @@
+// 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/logging.h"
+
+namespace {
+// Constants
+const char kZeroDigit = 'a';
+const char kMinDigit = 'b';
+const char kMidDigit = 'n';
+const char kMaxDigit = 'z';
+const int kMaxDigitValue = kMaxDigit - kZeroDigit;
+const int kMidDigitValue = kMidDigit - kZeroDigit;
akalin 2011/10/18 17:59:27 put kMidDigitValue before kMaxDigitValue
+const int kFullDigitValue = (kMaxDigit + 1) - kZeroDigit;
akalin 2011/10/18 17:59:27 #include base/basictypes.h and use COMPILE_ASSERT
akalin 2011/10/18 17:59:27 a better name would be kRadix, and you can set it
+
+// Helper Functions
+
+// Remove all trailing zeros from a value as they provide no value.
+std::string RemoveTrailingZeros(const std::string& value) {
+ DCHECK(!value.empty());
+
+ int end_position = value.length() - 1;
+ while (value[end_position] == kZeroDigit) {
+ --end_position;
+ }
+
+ return value.substr(0, 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 because
+// the previous index values had an odd difference, so their correct
+// middle value is x and a half, so the half is now inserted.
+void AddHalf(size_t position, std::string& value) {
+ DCHECK_GT(position, static_cast<size_t>(0));
+
+ // We can't preform this operation directly on the string because
akalin 2011/10/18 17:59:27 preform -> perform
+ // overflow can occur and mess up the values.
+ int new_position_value = value[position] + kMidDigitValue;
akalin 2011/10/18 17:59:27 do static_cast<int>(value[position]) just to be cl
+
+ if (new_position_value <= kMaxDigit) {
+ value[position] = new_position_value;
+ } else {
+ value[position] = new_position_value - kFullDigitValue;
+ ++value[position - 1];
+
+ for (size_t i = position - 1; value[i] > kMaxDigit; --i) {
+ CHECK_GT(i, static_cast<size_t>(0));
+ value[i] = value[i] - kFullDigitValue;
akalin 2011/10/18 17:59:27 -=
+ ++value[i - 1];
+ }
+ }
+}
+
+// Drops off the last digit of value and then all trailing zeros iff that
+// doesn't change its ordering as greater than start.
+std::string DropUnneededDigits(std::string value,
akalin 2011/10/18 17:59:27 use const refs to strings instead of passing by va
+ std::string start) {
+ CHECK(value > start);
akalin 2011/10/18 17:59:27 CHECK_GT
+
+ std::string shorter_value = value.substr(0, value.length() - 1);
+ shorter_value = RemoveTrailingZeros(shorter_value);
+
+ if (shorter_value != start) {
+ return shorter_value;
+ }
+ return value;
+}
+
+// Compute the midpoint string that is between start and end.
akalin 2011/10/18 17:59:27 start -> |start| end -> |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 middle_string(max_size, kZeroDigit);
akalin 2011/10/18 17:59:27 middle_string -> midpoint
+
+ bool add_half = false;
+ for (size_t i = 0; i < max_size; ++i) {
+ int char_value = GetPositionValue(start, i);
+ char_value += GetPositionValue(end, i);
+
+ middle_string[i] += (char_value / 2);
+ if (add_half) {
+ AddHalf(i, middle_string);
+ }
+ add_half = (char_value % 2 == 1);
+ }
+
+ return middle_string;
+}
+
+// Create a StringOrdinal that is lexigraphically greater than start and
akalin 2011/10/18 17:59:27 start -> |start| end -> |end|
+// lexigraphically less than end, ideally the StringOrdinal will be a
akalin 2011/10/18 17:59:27 "end, ideally" -> "end. The returned StringOrdina
+// middle value between the two StringIndexs.
akalin 2011/10/18 17:59:27 StringOrdinals
+StringOrdinal CreateStringOrdinalBetween(const StringOrdinal& start,
+ const StringOrdinal& end) {
+ CHECK(start.IsValid());
+ CHECK(end.IsValid());
akalin 2011/10/18 17:59:27 CHECK(start.LessThan(end))
+ std::string start_string = start.ToString();
akalin 2011/10/18 17:59:27 use const refs
+ std::string end_string = end.ToString();
akalin 2011/10/18 17:59:27 here, too
+ DCHECK_LT(start_string, end_string);
+
+ std::string middle_string = ComputeMidpoint(start_string, end_string);
akalin 2011/10/18 17:59:27 middle_string -> midpoint
+
+ middle_string = DropUnneededDigits(middle_string, start_string);
+
+ DCHECK_GT(middle_string, start_string);
+ DCHECK_LT(middle_string, end_string);
+
+ return StringOrdinal(middle_string);
+}
+
+// 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& str)
+ : string_index_(str),
+ is_valid_(IsValidStringOrdinal(string_index_)) {
+
+}
+
+StringOrdinal::StringOrdinal() : is_valid_(false) {
+}
+
+bool StringOrdinal::IsValid() const {
+ return is_valid_;
+}
+
+bool StringOrdinal::LessThan(const StringOrdinal& other) const {
+ CHECK(IsValid());
+ CHECK(other.IsValid());
+ return string_index_ < other.string_index_;
+}
+
+bool StringOrdinal::Equal(const StringOrdinal& other) const {
+ CHECK(IsValid());
+ CHECK(other.IsValid());
+ return string_index_ == other.string_index_;
+}
+
+StringOrdinal StringOrdinal::CreateBetween(const StringOrdinal& other) const {
+ CHECK(IsValid());
+ CHECK(other.IsValid());
akalin 2011/10/18 17:59:27 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 to be the minimum boundary.
+ const size_t length = string_index_.length();
+ std::string start_string = std::string(length, kZeroDigit);
akalin 2011/10/18 17:59:27 start_string -> start (to be consistent with Creat
akalin 2011/10/18 17:59:27 don't do std::string foo = std::string(...), just
+ start_string[length - 1] = kMinDigit;
+ if (start_string == string_index_) {
+ start_string[length - 1] = kZeroDigit;
+ start_string += kMinDigit;
+ }
+
+ // Even though start_string is already a valid StringOrdinal that is less
akalin 2011/10/18 17:59:27 |start_string|
+ // than *this, we don't return it because we wouldn't have much space in
akalin 2011/10/18 17:59:27 |*this|
+ // front of it to insert potential future values.
+ return StringOrdinal(start_string).CreateBetween(*this);
akalin 2011/10/18 17:59:27 can actually just do: return CreateBetween(String
+}
+
+StringOrdinal StringOrdinal::CreateAfter() const {
+ CHECK(IsValid());
+ std::string end = std::string(string_index_.length(), kMaxDigit);
akalin 2011/10/18 17:59:27 std::string end(...)
+ if (end == string_index_) {
+ end += kMaxDigit;
+ }
+
+ // Even though end is already a valid StringOrdinal that is greater than
akalin 2011/10/18 17:59:27 |end|
+ // *this, we don't return it because we wouldn't have much space after
akalin 2011/10/18 17:59:27 |*this|
+ // it to insert potential future values.
+ return CreateBetween(StringOrdinal(end));
+}
+
+std::string StringOrdinal::ToString() const {
akalin 2011/10/18 17:59:27 CHECK(is_valid_)
+ return string_index_;
+}

Powered by Google App Engine
This is Rietveld 408576698