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

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: Modifying DropUnneededDigits logic and 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 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
tfarina 2011/10/20 14:54:35 I think you can remove this comment. if not, pleas
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 zero digits from a value as they provide no value.
29 void RemoveTrailingZeros(std::string* value) {
30 DCHECK(!value->empty());
31
32 int end_position = value->length() - 1;
33 while ((*value)[end_position] == kZeroDigit) {
34 --end_position;
35 }
36
37 value->resize(end_position + 1);
38 }
39
tfarina 2011/10/20 14:54:35 remove extra line.
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));
tfarina 2011/10/20 14:54:35 instead of static_cast<size_t>, 0U?
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 void DropUnneededDigits(const std::string& start,
csharp 2011/10/20 14:35:32 I ended up taking a different approach to how we d
73 std::string* value) {
74 CHECK_GT(*value, start);
75
76 RemoveTrailingZeros(value);
77 // See if the value can have its last digit removed without affecting
78 // the ordering.
79 if (value->length() > 1) {
80 std::string shorter_value = value->substr(0, value->length() - 1);
81 // We must drop the trailing zeros before comparing |shorter_value| to
82 // |start| because if we don't we may have |shorter_value|=|start|+|a|*
83 // where |shorter_value| > |start| but not when it drops the trailing |a|s
84 // to become a valid StringOrdinal value.
85 RemoveTrailingZeros(&shorter_value);
86
87 if (shorter_value > start) {
88 value->resize(shorter_value.length());
89 }
90 }
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 }
108 add_half = (char_value % 2 == 1);
109 }
110 DCHECK(!add_half);
111
112 return midpoint;
113 }
114
115 // Create a StringOrdinal that is lexigraphically greater than |start| and
116 // lexigraphically less than |end|. The returned StringOrdinal will be roughly
117 // between |start| and |end|.
118 StringOrdinal CreateStringOrdinalBetween(const StringOrdinal& start,
119 const StringOrdinal& end) {
120 CHECK(start.IsValid());
121 CHECK(end.IsValid());
122 CHECK(start.LessThan(end));
123 const std::string& start_string = start.ToString();
124 const std::string& end_string = end.ToString();
125 DCHECK_LT(start_string, end_string);
126
127 std::string midpoint = ComputeMidpoint(start_string, end_string);
128
129 DropUnneededDigits(start_string, &midpoint);
130
131 DCHECK_GT(midpoint, start_string);
132 DCHECK_LT(midpoint, end_string);
133
134 StringOrdinal midpoint_ordinal(midpoint);
135 DCHECK(midpoint_ordinal.IsValid());
136 return midpoint_ordinal;
137 }
138
139 // Returns true iff the input string matches the format [a-z]*[b-z].
140 bool IsValidStringOrdinal(const std::string& value) {
141 if (value.empty()) {
tfarina 2011/10/20 14:54:35 no need of {} in single-line statements, here and
142 return false;
143 }
144
145 for (size_t i = 0; i < value.length(); ++i) {
146 if (value[i] < kZeroDigit || value[i] > kMaxDigit) {
147 return false;
148 }
149 }
150
151 return (value[value.length() - 1] != kZeroDigit);
tfarina 2011/10/20 14:54:35 no need of parentheses here.
152 }
153 } // namespace
tfarina 2011/10/20 14:54:35 add a blank line above.
154
155 StringOrdinal::StringOrdinal(const std::string& string_ordinal)
156 : string_ordinal_(string_ordinal),
tfarina 2011/10/20 14:54:35 indent this 4 spaces.
157 is_valid_(IsValidStringOrdinal(string_ordinal_)) {
158
159 }
160
161 StringOrdinal StringOrdinal::Invalid() {
162 return StringOrdinal("");
163 }
164
165 bool StringOrdinal::IsValid() const {
166 return is_valid_;
167 }
168
169 bool StringOrdinal::LessThan(const StringOrdinal& other) const {
170 CHECK(IsValid());
171 CHECK(other.IsValid());
172 return string_ordinal_ < other.string_ordinal_;
173 }
174
175 bool StringOrdinal::Equal(const StringOrdinal& other) const {
176 CHECK(IsValid());
177 CHECK(other.IsValid());
178 return string_ordinal_ == other.string_ordinal_;
179 }
180
181 StringOrdinal StringOrdinal::CreateBetween(const StringOrdinal& other) const {
182 CHECK(IsValid());
183 CHECK(other.IsValid());
184 CHECK(!Equal(other));
185
186 if (LessThan(other)) {
187 return CreateStringOrdinalBetween(*this, other);
188 } else {
189 return CreateStringOrdinalBetween(other, *this);
190 }
191 }
192
193 StringOrdinal StringOrdinal::CreateBefore() const {
194 CHECK(IsValid());
195 // Create the smallest valid StringOrdinal of the appropriate length
196 // to be the minimum boundary.
197 const size_t length = string_ordinal_.length();
198 std::string start(length, kZeroDigit);
199 start[length - 1] = kMinDigit;
200 if (start == string_ordinal_) {
201 start[length - 1] = kZeroDigit;
202 start += kMinDigit;
203 }
204
205 // Even though |start| is already a valid StringOrdinal that is less
206 // than |*this|, we don't return it because we wouldn't have much space in
207 // front of it to insert potential future values.
208 return CreateBetween(StringOrdinal(start));
209 }
210
211 StringOrdinal StringOrdinal::CreateAfter() const {
212 CHECK(IsValid());
213 // Create the largest valid StringOrdinal of the appropriate length to be
214 // the maximum boundary.
215 std::string end(string_ordinal_.length(), kMaxDigit);
216 if (end == string_ordinal_) {
217 end += kMaxDigit;
218 }
219
220 // Even though |end| is already a valid StringOrdinal that is greater than
221 // |*this|, we don't return it because we wouldn't have much space after
222 // it to insert potential future values.
223 return CreateBetween(StringOrdinal(end));
224 }
225
226 std::string StringOrdinal::ToString() const {
227 CHECK(IsValid());
228 return string_ordinal_;
229 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698