OLD | NEW |
---|---|
(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 | |
OLD | NEW |