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

Side by Side Diff: sync/internal_api/public/base/unique_position.cc

Issue 11569045: Initial UniquePositions implementation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update algorithm Created 8 years 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) 2012 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 "sync/internal_api/public/base/unique_position.h"
6
7 #include "base/logging.h"
8 #include "base/string_number_conversions.h"
9
10 namespace syncer {
11
12 const size_t UniquePosition::kSuffixLength = 28;
13
14 // static.
15 bool UniquePosition::IsValidSuffix(const std::string& suffix) {
16 // The suffix must be exactly the specified length, otherwise unique suffixes
17 // are not sufficient to guarantee unique positions (because prefix + suffix
18 // == p + refixsuffix).
19 return suffix.length() == kSuffixLength;
20 }
21
22 // static.
23 bool UniquePosition::IsValidBytes(const std::string& bytes) {
24 // The first condition ensures that our suffix uniqueness is sufficient to
25 // guarantee position uniqueness. Otherwise, it's possible the end of some
26 // prefix + some short suffix == some long suffix.
27 // The second condition ensures that FindSmallerWithSuffix can always return a
28 // result.
29 return bytes.length() >= kSuffixLength
30 && bytes[bytes.length()-1] != 0;
31 }
32
33 // static.
34 UniquePosition UniquePosition::CreateInvalid() {
35 UniquePosition pos;
36 DCHECK(!pos.IsValid());
37 return pos;
38 }
39
40 // static.
41 UniquePosition UniquePosition::FromBytes(
42 const std::string& bytes) {
akalin 2012/12/29 10:17:13 fits on previous line?
rlarocque 2013/01/07 23:22:12 Done.
43 UniquePosition result(bytes);
44 return result;
45 }
46
47 // static.
48 UniquePosition UniquePosition::FromInt64(
49 int64 x, const std::string& suffix) {
50 uint64 y = static_cast<uint64>(x);
51 y ^= 0x8000000000000000ULL; // Make it non-negative.
52 std::string bytes(8, 0);
53 for (int i = 7; i >= 0; --i) {
54 bytes[i] = static_cast<uint8>(y);
55 y >>= 8;
56 }
57 return UniquePosition(bytes, suffix);
58 }
59
60 // static.
61 UniquePosition UniquePosition::InitialPosition(
62 const std::string& suffix) {
63 DCHECK(IsValidSuffix(suffix));
64 return UniquePosition("", suffix);
65 }
66
67 // static.
68 UniquePosition UniquePosition::Before(
69 const UniquePosition& x,
70 const std::string& suffix) {
71 DCHECK(IsValidSuffix(suffix));
72 DCHECK(x.IsValid());
73 const std::string& before = FindSmallerWithSuffix(x.bytes_, suffix);
74 return UniquePosition(before, suffix);
75 }
76
77 // static.
78 UniquePosition UniquePosition::After(
79 const UniquePosition& x,
80 const std::string& suffix) {
81 DCHECK(IsValidSuffix(suffix));
82 DCHECK(x.IsValid());
83 const std::string& after = FindGreaterWithSuffix(x.bytes_, suffix);
84 return UniquePosition(after, suffix);
85 }
86
87 // static.
88 UniquePosition UniquePosition::Between(
89 const UniquePosition& before,
90 const UniquePosition& after,
91 const std::string& suffix) {
92 DCHECK(before.IsValid());
93 DCHECK(after.IsValid());
94 DCHECK(before.LessThan(after));
95 DCHECK(IsValidSuffix(suffix));
96 const std::string& mid =
97 FindBetweenWithSuffix(before.bytes_, after.bytes_, suffix);
98 return UniquePosition(mid, suffix);
99 }
100
101 UniquePosition::UniquePosition() : is_valid_(false) { };
akalin 2012/12/29 10:17:13 "{ };" -> "{}"
rlarocque 2013/01/07 23:22:12 Done.
102
103 bool UniquePosition::LessThan(const UniquePosition& other) const {
104 DCHECK(this->IsValid());
105 DCHECK(other.IsValid());
106 return bytes_ < other.bytes_;
107 }
108
109 bool UniquePosition::Equals(const UniquePosition& other) const {
110 if (!this->IsValid() && !other.IsValid())
111 return true;
112
113 return bytes_ == other.bytes_;
114 }
115
116 const std::string& UniquePosition::ToInternalValue() const {
117 return bytes_;
118 }
119
120 int64 UniquePosition::ToInt64() const {
121 uint64 y = 0;
122 const std::string& s = ToInternalValue();
123 size_t l = sizeof(int64);
124 if (s.length() < l) {
125 NOTREACHED();
126 l = s.length();
127 }
128 for (size_t i = 0; i < l; ++i) {
129 const uint8 byte = s[l - i - 1];
130 y |= static_cast<uint64>(byte) << (i * 8);
131 }
132 y ^= 0x8000000000000000ULL;
133 // This is technically implementation-defined if y > INT64_MAX, so
134 // we're assuming that we're on a twos-complement machine.
135 return static_cast<int64>(y);
136 }
137
138 bool UniquePosition::IsValid() const {
139 return is_valid_;
140 }
141
142 std::string UniquePosition::ToDebugString() const {
143 std::string debug_string = base::HexEncode(bytes_.data(), bytes_.length());
144 if (!IsValid()) {
145 debug_string = "INVALID[" + debug_string + "]";
146 }
147 return debug_string;;
148 }
149
150 std::string UniquePosition::FindSmallerWithSuffix(
151 const std::string& reference,
152 const std::string& suffix) {
153 size_t ref_zeroes = reference.find_first_not_of('\0');
154 size_t suffix_zeroes = suffix.find_first_not_of('\0');
155
156 // Neither of our inputs are allowed to have trailing zeroes, so the following
157 // must be true.
158 DCHECK(ref_zeroes != std::string::npos);
akalin 2012/12/29 10:17:13 DCHECK_NE?
rlarocque 2013/01/07 23:22:12 Done.
159 DCHECK(suffix_zeroes != std::string::npos);
160
161 if (suffix_zeroes > ref_zeroes) {
162 // Implies ref < suffix.
akalin 2012/12/29 10:17:13 backwards -- suffix < ref
rlarocque 2013/01/07 23:22:12 Done.
163 return "";
164 }
165
166 if (suffix.substr(suffix_zeroes) < reference.substr(ref_zeroes)) {
167 // Prepend zeroes so the result has as many zero digits as |reference|.
168 return std::string(ref_zeroes - suffix_zeroes, '\0');
169 } else if (suffix_zeroes > 1) {
170 // Prepend zeroes so the result has one more zero digit than |reference|.
171 // We could also take the "else" branch below, but taking this branch will
172 // give us a smaller result.
173 return std::string(ref_zeroes - suffix_zeroes + 1, '\0');
174 } else {
175 // Prepend zeroes to match those in the |reference|, then something smaller
176 // than the first non-zero digit in |reference|.
177 char lt_digit = (uint8)reference[ref_zeroes]/2;
akalin 2012/12/29 10:17:13 static_cast<uint8>?
rlarocque 2013/01/07 23:22:12 Done.
178 return std::string(ref_zeroes, '\0') + lt_digit;
179 }
180 }
181
182 // static
183 std::string UniquePosition::FindGreaterWithSuffix(
184 const std::string& reference,
185 const std::string& suffix) {
186 size_t ref_FFs = reference.find_first_not_of(kuint8max);
187 size_t suffix_FFs = suffix.find_first_not_of(kuint8max);
188
189 if (ref_FFs == std::string::npos) {
190 ref_FFs = reference.length();
191 }
192 if (suffix_FFs == std::string::npos) {
193 suffix_FFs = suffix.length();
194 }
195
196 if (suffix_FFs > ref_FFs) {
197 // Implies suffix > reference
akalin 2012/12/29 10:17:13 append .
rlarocque 2013/01/07 23:22:12 Done.
198 return "";
199 }
200
201 if (suffix.substr(suffix_FFs) > reference.substr(ref_FFs)) {
202 // Prepend FF digits to match those in |reference|.
203 return std::string(ref_FFs - suffix_FFs, kuint8max);
204 } else if (suffix_FFs > 1) {
205 // Prepend enough leading FF digits so result has one more of them than
206 // |reference| does. We could also take the "else" branch below, but this
207 // gives us a smaller result.
208 return std::string(ref_FFs - suffix_FFs + 1, kuint8max);
209 } else {
210 // Prepend FF digits to match those in |reference|, then something larger
211 // than the first non-FF digit in |reference|.
212 char gt_digit = (uint8)reference[ref_FFs] +
akalin 2012/12/29 10:17:13 static_cast<uint8>
rlarocque 2013/01/07 23:22:12 Done.
213 (kuint8max - (uint8)reference[ref_FFs] + 1) / 2;
214 return std::string(ref_FFs, kuint8max) + gt_digit;
215 }
216 }
217
218 // static
219 std::string UniquePosition::FindBetweenWithSuffix(
220 const std::string& before,
221 const std::string& after,
222 const std::string& suffix) {
223 DCHECK(IsValidSuffix(suffix));
224 DCHECK_NE(before, after);
225 DCHECK_LT(before, after);
226
227 std::string mid;
228
229 // Sometimes our suffix puts us where we want to be.
230 if (before < suffix && suffix < after) {
231 return "";
232 }
233
234 size_t i = 0;
235 for ( ; i < std::min(before.length(), after.length()); ++i) {
236 uint8 a_digit = before[i];
237 uint8 b_digit = after[i];
238
239 if (b_digit - a_digit >= 2) {
240 mid.push_back(a_digit + (b_digit - a_digit)/2);
241 return mid;
242 } else if (a_digit == b_digit) {
243 mid.push_back(a_digit);
244
245 // Both strings are equal so far. Will appending the suffix at this point
246 // give us the comparison we're looking for?
247 if (before.substr(i+1) < suffix && suffix < after.substr(i+1)) {
akalin 2012/12/29 10:17:13 pretty sure you can rewrite this one to something
rlarocque 2013/01/07 23:22:12 I'm not sure I follow you. How would the binary s
248 return mid;
249 }
250 } else {
251 DCHECK_EQ(b_digit - a_digit, 1); // Implied by above if branches.
252
253 // The two options are off by one digit. The choice of whether to round
254 // up or down here will have consequences on what we do with the remaining
255 // digits. Exploring both options is an optimization and is not required
256 // for the correctness of this algorithm.
257
258 // Option A: Round down the current digit. This makes our |mid| <
259 // |after|, no matter what we append afterwards. We then focus on
260 // appending digits until |mid| > |before|.
261 std::string mid_a = mid;
262 mid_a.push_back(a_digit);
263 mid_a.append(FindGreaterWithSuffix(before.substr(i+1), suffix));
264
265 // Option B: Round up the current digit. This makes our |mid| > |before|,
266 // no matter what we append afterwards. We then focus on appending digits
267 // until |mid| < |after|. Note that this option may not be viable if the
268 // current digit is the last one in |after|, so we skip the option in that
269 // case.
270 if (after.length() > i+1) {
271 std::string mid_b = mid;
272 mid_b.push_back(b_digit);
273 mid_b.append(FindSmallerWithSuffix(after.substr(i+1), suffix));
274
275 // Does this give us a shorter position value? If so, use it.
276 if (mid_b.length() < mid_a.length()) {
277 return mid_b;
278 }
279 }
280 return mid_a;
281 }
282 }
283
284 // If we haven't found a midpoint yet, the following must be true:
285 DCHECK_EQ(before.substr(0, i), after.substr(0, i));
286 DCHECK_EQ(before, mid);
287 DCHECK_LT(before.length(), after.length());
288
289 // We know that we'll need to append at least one more byte to |mid| in the
290 // process of making it < |after|. Appending any digit, regardless of the
291 // value, will make |before| < |mid|. Therefore, the following will get us a
292 // valid position.
293
294 mid.append(FindSmallerWithSuffix(after.substr(i), suffix));
295 return mid;
296 }
297
298 UniquePosition::UniquePosition(const std::string& internal_rep)
299 : bytes_(internal_rep),
300 is_valid_(IsValidBytes(bytes_)) {
301 }
302
303 UniquePosition::UniquePosition(
304 const std::string& prefix,
305 const std::string& suffix)
306 : bytes_(prefix + suffix),
307 is_valid_(IsValidBytes(bytes_)) {
308 DCHECK(IsValidSuffix(suffix));
309 DCHECK(IsValid());
310 }
311
312 } // namespace syncer
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698