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

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: Clarifying name of GetLengthWithTrailingZeros 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
15 // Constants for StringOrdinal digits.
16 const char kZeroDigit = 'a';
17 const char kMinDigit = 'b';
18 const char kMidDigit = 'n';
19 const char kMaxDigit = 'z';
20 const int kMidDigitValue = kMidDigit - kZeroDigit;
21 const int kMaxDigitValue = kMaxDigit - kZeroDigit;
22 const int kRadix = kMaxDigitValue + 1;
23 COMPILE_ASSERT(kMidDigitValue == 13, kMidDigitValue_incorrect);
24 COMPILE_ASSERT(kMaxDigitValue == 25, kMaxDigitValue_incorrect);
25 COMPILE_ASSERT(kRadix == 26, kRadix_incorrect);
26
27 // Helper Functions
28
29 // Returns the length that string value.substr(0, length) would be with
30 // trailing zeros removed.
31 size_t GetLengthWithoutTrailingZeros(const std::string& value, size_t length) {
32 DCHECK(!value.empty());
33
34 int end_position = length - 1;
35 while (value[end_position] == kZeroDigit) {
MAD 2011/10/25 15:13:29 Check for end_position > 0
akalin 2011/10/25 17:22:19 Ah, right. The implicit cast to int is gross, too
36 --end_position;
37 }
38
39 return end_position + 1;
40 }
41
42 // Return the digit value at position i, padding with kZeroDigit if required.
43 int GetPositionValue(const std::string& str, size_t i) {
44 return (i < str.length()) ? (str[i] - kZeroDigit) : 0;
45 }
46
47 // Add kMidDigitValue to the value at position index. This is used by
48 // ComputeMidpoint.
49 void AddHalf(size_t position, std::string& value) {
50 DCHECK_GT(position, 0U);
MAD 2011/10/25 15:13:29 Also check that position is LT size...
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, 0U);
MAD 2011/10/25 15:13:29 So we crash when all previous values have reached
akalin 2011/10/25 17:22:19 Yeah, you're right. This should probably return a
csharp1 2011/10/25 18:50:48 We would, but this case can't occur. For this to
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 void DropUnneededDigits(const std::string& start, std::string* value) {
73 CHECK_GT(*value, start);
74
75 size_t drop_length = GetLengthWithoutTrailingZeros(*value, value->length());
76 // See if the value can have its last digit removed without affecting
77 // the ordering.
78 if (drop_length > 1) {
79 // We must drop the trailing zeros before comparing |shorter_value| to
80 // |start| because if we don't we may have |shorter_value|=|start|+|a|*
81 // where |shorter_value| > |start| but not when it drops the trailing |a|s
82 // to become a valid StringOrdinal value.
83 int truncated_length = GetLengthWithoutTrailingZeros(*value,
84 drop_length - 1);
85
86 if (value->compare(0, truncated_length, start) > 0)
87 drop_length = truncated_length;
88 }
89
90 value->resize(drop_length);
91 }
92
93 // Compute the midpoint string that is between |start| and |end|.
94 std::string ComputeMidpoint(const std::string& start,
95 const std::string& end) {
96 size_t max_size = std::max(start.length(), end.length()) + 1;
97 std::string midpoint(max_size, kZeroDigit);
98
99 bool add_half = false;
100 for (size_t i = 0; i < max_size; ++i) {
101 int char_value = GetPositionValue(start, i);
102 char_value += GetPositionValue(end, i);
103
104 midpoint[i] += (char_value / 2);
105 if (add_half)
106 AddHalf(i, midpoint);
107 add_half = (char_value % 2 == 1);
108 }
109 DCHECK(!add_half);
110
111 return midpoint;
112 }
113
114 // Create a StringOrdinal that is lexigraphically greater than |start| and
115 // lexigraphically less than |end|. The returned StringOrdinal will be roughly
116 // between |start| and |end|.
117 StringOrdinal CreateStringOrdinalBetween(const StringOrdinal& start,
118 const StringOrdinal& end) {
119 CHECK(start.IsValid());
120 CHECK(end.IsValid());
121 CHECK(start.LessThan(end));
122 const std::string& start_string = start.ToString();
123 const std::string& end_string = end.ToString();
124 DCHECK_LT(start_string, end_string);
125
126 std::string midpoint = ComputeMidpoint(start_string, end_string);
127
128 DropUnneededDigits(start_string, &midpoint);
129
130 DCHECK_GT(midpoint, start_string);
131 DCHECK_LT(midpoint, end_string);
132
133 StringOrdinal midpoint_ordinal(midpoint);
134 DCHECK(midpoint_ordinal.IsValid());
135 return midpoint_ordinal;
136 }
137
138 // Returns true iff the input string matches the format [a-z]*[b-z].
139 bool IsValidStringOrdinal(const std::string& value) {
140 if (value.empty())
141 return false;
142
143 for (size_t i = 0; i < value.length(); ++i) {
144 if (value[i] < kZeroDigit || value[i] > kMaxDigit)
145 return false;
146 }
147
148 return value[value.length() - 1] != kZeroDigit;
149 }
150
151 } // namespace
152
153 StringOrdinal::StringOrdinal(const std::string& string_ordinal)
154 : string_ordinal_(string_ordinal),
155 is_valid_(IsValidStringOrdinal(string_ordinal_)) {
156 }
157
158 StringOrdinal::StringOrdinal() : string_ordinal_(""),
159 is_valid_(false) {
160 }
161
162 bool StringOrdinal::IsValid() const {
163 return is_valid_;
164 }
165
166 bool StringOrdinal::LessThan(const StringOrdinal& other) const {
167 CHECK(IsValid());
168 CHECK(other.IsValid());
169 return string_ordinal_ < other.string_ordinal_;
170 }
171
172 bool StringOrdinal::Equal(const StringOrdinal& other) const {
173 CHECK(IsValid());
174 CHECK(other.IsValid());
175 return string_ordinal_ == other.string_ordinal_;
176 }
177
178 StringOrdinal StringOrdinal::CreateBetween(const StringOrdinal& other) const {
179 CHECK(IsValid());
180 CHECK(other.IsValid());
181 CHECK(!Equal(other));
182
183 if (LessThan(other)) {
184 return CreateStringOrdinalBetween(*this, other);
185 } else {
186 return CreateStringOrdinalBetween(other, *this);
187 }
188 }
189
190 StringOrdinal StringOrdinal::CreateBefore() const {
191 CHECK(IsValid());
192 // Create the smallest valid StringOrdinal of the appropriate length
193 // to be the minimum boundary.
194 const size_t length = string_ordinal_.length();
195 std::string start(length, kZeroDigit);
196 start[length - 1] = kMinDigit;
197 if (start == string_ordinal_) {
198 start[length - 1] = kZeroDigit;
199 start += kMinDigit;
200 }
201
202 // Even though |start| is already a valid StringOrdinal that is less
203 // than |*this|, we don't return it because we wouldn't have much space in
204 // front of it to insert potential future values.
205 return CreateBetween(StringOrdinal(start));
206 }
207
208 StringOrdinal StringOrdinal::CreateAfter() const {
209 CHECK(IsValid());
210 // Create the largest valid StringOrdinal of the appropriate length to be
211 // the maximum boundary.
212 std::string end(string_ordinal_.length(), kMaxDigit);
213 if (end == string_ordinal_)
214 end += kMaxDigit;
215
216 // Even though |end| is already a valid StringOrdinal that is greater than
217 // |*this|, we don't return it because we wouldn't have much space after
218 // it to insert potential future values.
219 return CreateBetween(StringOrdinal(end));
220 }
221
222 std::string StringOrdinal::ToString() const {
223 CHECK(IsValid());
224 return string_ordinal_;
225 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698