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

Side by Side 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: Adjusting code to comply with code review comments 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/common/string_ordinal.h"
6
7 #include <algorithm>
8 #include <cstddef>
9
10 #include "base/basictypes.h"
11 #include "base/logging.h"
12
13 namespace {
14 // Constants
15 const char kZeroDigit = 'a';
16 const char kMinDigit = 'b';
17 const char kMidDigit = 'n';
18 const char kMaxDigit = 'z';
19 const int kMidDigitValue = kMidDigit - kZeroDigit;
20 const int kMaxDigitValue = kMaxDigit - kZeroDigit;
21 const int kRadix = kMaxDigitValue + 1;
22 COMPILE_ASSERT(kMidDigitValue == 13, kMidDigitValue_incorrect);
23 COMPILE_ASSERT(kMaxDigitValue == 25, kMaxDigitValue_incorrect);
24 COMPILE_ASSERT(kRadix == 26, kRadix_incorrect);
25
26 // Helper Functions
27
28 // Remove all trailing zeros from a value as they provide no value.
akalin 2011/10/20 05:25:49 zeros -> zero digits
29 std::string RemoveTrailingZeros(const std::string& value) {
akalin 2011/10/20 05:25:49 Make this a destructive operation, like: void Rem
30 DCHECK(!value.empty());
31
32 int end_position = value.length() - 1;
33 while (value[end_position] == kZeroDigit) {
34 --end_position;
35 }
36
37 return value.substr(0, end_position + 1);
38 }
39
40
41 // Return the digit value at position i, padding with kZeroDigit if required.
42 int GetPositionValue(const std::string& str, size_t i) {
43 return (i < str.length()) ? (str[i] - kZeroDigit) : 0;
44 }
45
46 // Add kMidDigitValue to the value at position index because
47 // the previous index values had an odd difference, so their correct
48 // middle value is x and a half, so the half is now inserted.
49 void AddHalf(size_t position, std::string& value) {
50 DCHECK_GT(position, static_cast<size_t>(0));
51
52 // We can't perform this operation directly on the string because
53 // overflow can occur and mess up the values.
54 int new_position_value = value[position] + kMidDigitValue;
55
56 if (new_position_value <= kMaxDigit) {
57 value[position] = new_position_value;
58 } else {
59 value[position] = new_position_value - kRadix;
60 ++value[position - 1];
61
62 for (size_t i = position - 1; value[i] > kMaxDigit; --i) {
63 CHECK_GT(i, static_cast<size_t>(0));
64 value[i] -= kRadix;
65 ++value[i - 1];
66 }
67 }
68 }
69
70 // Drops off the last digit of value and then all trailing zeros iff that
71 // doesn't change its ordering as greater than |start|.
72 std::string DropUnneededDigits(const std::string& value,
akalin 2011/10/20 05:25:49 make this a destructive operation, also, so: void
tfarina 2011/10/20 12:58:40 Output parameters should be at the end of paramete
73 const std::string& start) {
74 CHECK_GT(value, start);
75
76 std::string shorter_value = value.substr(0, value.length() - 1);
akalin 2011/10/20 05:25:49 it's possible that it may be enough to remove the
77 shorter_value = RemoveTrailingZeros(shorter_value);
78
79 if (shorter_value != start) {
80 return shorter_value;
81 }
82 return value;
83 }
84
85 // Compute the midpoint string that is between |start| and |end|.
86 std::string ComputeMidpoint(const std::string& start,
87 const std::string& end) {
88 size_t max_size = std::max(start.length(), end.length()) + 1;
89 std::string midpoint(max_size, kZeroDigit);
90
91 bool add_half = false;
92 for (size_t i = 0; i < max_size; ++i) {
93 int char_value = GetPositionValue(start, i);
94 char_value += GetPositionValue(end, i);
95
96 midpoint[i] += (char_value / 2);
97 if (add_half) {
98 AddHalf(i, midpoint);
99 }
100 add_half = (char_value % 2 == 1);
101 }
102
akalin 2011/10/20 05:25:49 DCHECK(!add_half); here
103 return midpoint;
104 }
105
106 // Create a StringOrdinal that is lexigraphically greater than |start| and
107 // lexigraphically less than |end|. The returned StringOrdinal will be roughly
108 // between |start| and |end|.
109 StringOrdinal CreateStringOrdinalBetween(const StringOrdinal& start,
110 const StringOrdinal& end) {
111 CHECK(start.IsValid());
112 CHECK(end.IsValid());
113 CHECK(start.LessThan(end));
114 const std::string& start_string = start.ToString();
115 const std::string& end_string = end.ToString();
116 DCHECK_LT(start_string, end_string);
117
118 std::string midpoint = ComputeMidpoint(start_string, end_string);
119
120 midpoint = DropUnneededDigits(midpoint, start_string);
121
122 DCHECK_GT(midpoint, start_string);
123 DCHECK_LT(midpoint, end_string);
124
125 return StringOrdinal(midpoint);
akalin 2011/10/20 05:25:49 Do; StringOrdinal midpoint_ordinal(midpoint); DCH
126 }
127
128 // Returns true iff the input string matches the format [a-z]*[b-z].
129 bool IsValidStringOrdinal(const std::string& value) {
130 if (value.empty()) {
131 return false;
132 }
133
134 for (size_t i = 0; i < value.length(); ++i) {
135 if (value[i] < kZeroDigit || value[i] > kMaxDigit) {
136 return false;
137 }
138 }
139
140 return (value[value.length() - 1] != kZeroDigit);
141 }
142 } // namespace
143
144 StringOrdinal::StringOrdinal(const std::string& string_ordinal)
145 : string_ordinal_(string_ordinal),
146 is_valid_(IsValidStringOrdinal(string_ordinal_)) {
147
148 }
149
150 StringOrdinal::StringOrdinal() : is_valid_(false) {
151 }
152
153 bool StringOrdinal::IsValid() const {
154 return is_valid_;
155 }
156
157 bool StringOrdinal::LessThan(const StringOrdinal& other) const {
158 CHECK(IsValid());
159 CHECK(other.IsValid());
160 return string_ordinal_ < other.string_ordinal_;
161 }
162
163 bool StringOrdinal::Equal(const StringOrdinal& other) const {
164 CHECK(IsValid());
165 CHECK(other.IsValid());
166 return string_ordinal_ == other.string_ordinal_;
167 }
168
169 StringOrdinal StringOrdinal::CreateBetween(const StringOrdinal& other) const {
170 CHECK(IsValid());
171 CHECK(other.IsValid());
172 CHECK(!Equal(other));
173
174 if (LessThan(other)) {
175 return CreateStringOrdinalBetween(*this, other);
176 } else {
177 return CreateStringOrdinalBetween(other, *this);
178 }
179 }
180
181 StringOrdinal StringOrdinal::CreateBefore() const {
182 CHECK(IsValid());
183 // Create the smallest, valid StringOrdinal to be the minimum boundary.
akalin 2011/10/20 05:25:49 no comma between 'smallest' and 'valid' StringOrd
184 const size_t length = string_ordinal_.length();
185 std::string start(length, kZeroDigit);
186 start[length - 1] = kMinDigit;
187 if (start == string_ordinal_) {
188 start[length - 1] = kZeroDigit;
189 start += kMinDigit;
190 }
191
192 // Even though |start| is already a valid StringOrdinal that is less
193 // than |*this|, we don't return it because we wouldn't have much space in
194 // front of it to insert potential future values.
195 return CreateBetween(StringOrdinal(start));
196 }
197
198 StringOrdinal StringOrdinal::CreateAfter() const {
199 CHECK(IsValid());
200 std::string end(string_ordinal_.length(), kMaxDigit);
akalin 2011/10/20 05:25:49 Add a comment similar to CreateBefore(); // Creat
201 if (end == string_ordinal_) {
202 end += kMaxDigit;
203 }
204
205 // Even though |end| is already a valid StringOrdinal that is greater than
206 // |*this|, we don't return it because we wouldn't have much space after
207 // it to insert potential future values.
208 return CreateBetween(StringOrdinal(end));
209 }
210
211 std::string StringOrdinal::ToString() const {
212 CHECK(IsValid());
213 return string_ordinal_;
214 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698