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/rand_util.h" | |
9 #include "base/string_number_conversions.h" | |
10 | |
11 namespace syncer { | |
12 | |
13 // static. | |
14 bool UniquePosition::IsValidSuffix(const std::string& suffix) { | |
15 // The suffix must be exactly the specified length, otherwise unique suffixes | |
16 // are not sufficient to guarantee unique positions (because prefix + suffix | |
17 // == p + refixsuffix). | |
18 return suffix.length() == kSuffixLength; | |
19 } | |
20 | |
21 // static. | |
22 bool UniquePosition::IsValidBytes(const std::string& bytes) { | |
23 return bytes.length() > kSuffixLength | |
24 && bytes[bytes.length()-1] == kTerminatorByte; | |
25 } | |
26 | |
27 // static. | |
28 UniquePosition UniquePosition::CreateInvalid() { | |
29 UniquePosition pos; | |
30 DCHECK(!pos.IsValid()); | |
31 return pos; | |
32 } | |
33 | |
34 // static. | |
35 UniquePosition UniquePosition::FromBytes( | |
36 const std::string& bytes) { | |
37 UniquePosition result(bytes); | |
38 DCHECK(result.IsValid()); | |
39 return result; | |
40 } | |
41 | |
42 // static. | |
43 UniquePosition UniquePosition::FromInt64( | |
44 int64 x, const std::string& suffix) { | |
45 uint64 y = static_cast<uint64>(x); | |
46 y ^= 0x8000000000000000ULL; // Make it non-negative. | |
47 std::string bytes(8, 0); | |
48 for (int i = 7; i >= 0; --i) { | |
49 bytes[i] = static_cast<uint8>(y); | |
50 y >>= 8; | |
51 } | |
52 return UniquePosition(bytes, suffix); | |
53 } | |
54 | |
55 // static. | |
56 UniquePosition UniquePosition::InitialPosition( | |
57 const std::string& suffix) { | |
58 DCHECK(IsValidSuffix(suffix)); | |
59 return UniquePosition("", suffix); | |
60 } | |
61 | |
62 // static. | |
63 UniquePosition UniquePosition::Before( | |
64 const UniquePosition& x, | |
65 const std::string& suffix) { | |
66 DCHECK(IsValidSuffix(suffix)); | |
67 DCHECK(x.IsValid()); | |
68 const std::string& before = FindSmallerWithSuffix(x.bytes_, suffix); | |
69 return UniquePosition(before, suffix); | |
70 } | |
71 | |
72 // static. | |
73 UniquePosition UniquePosition::After( | |
74 const UniquePosition& x, | |
75 const std::string& suffix) { | |
76 DCHECK(IsValidSuffix(suffix)); | |
77 DCHECK(x.IsValid()); | |
78 const std::string& after = FindGreaterWithSuffix(x.bytes_, suffix); | |
79 return UniquePosition(after, suffix); | |
80 } | |
81 | |
82 // static. | |
83 UniquePosition UniquePosition::Between( | |
84 const UniquePosition& before, | |
85 const UniquePosition& after, | |
86 const std::string& suffix) { | |
87 DCHECK(before.IsValid()); | |
88 DCHECK(after.IsValid()); | |
89 DCHECK(before.LessThan(after)); | |
90 DCHECK(IsValidSuffix(suffix)); | |
91 const std::string& mid = | |
92 FindBetweenWithSuffix(before.bytes_, after.bytes_, suffix); | |
93 return UniquePosition(mid, suffix); | |
94 } | |
95 | |
96 // static. | |
97 const std::string UniquePosition::GenerateUniqueSuffix() { | |
98 return base::RandBytesAsString(kSuffixLength); | |
99 } | |
100 | |
101 // static. | |
102 const std::string UniquePosition::GenerateBookmarkSuffix( | |
103 const std::string& decoded_originator_cache_guid, | |
104 int64 numeric_originator_item_id) { | |
105 std::string result(8, 0); | |
106 | |
107 // The first 8 bytes come from the originator id. | |
108 int64 byte_iterator = numeric_originator_item_id; | |
109 for (int i = 7; i >= 0; --i) { | |
110 result[i] = static_cast<uint8>(byte_iterator); | |
111 byte_iterator >>= 8; | |
112 } | |
113 | |
114 // The next 16 bytes come from the decoded cache guid. | |
115 DCHECK_EQ(16u, decoded_originator_cache_guid.length()); | |
116 result.append(decoded_originator_cache_guid); | |
117 | |
118 return result; | |
119 } | |
120 | |
121 UniquePosition::UniquePosition() : is_valid_(false) { }; | |
122 | |
123 bool UniquePosition::LessThan(const UniquePosition& other) const { | |
124 DCHECK(this->IsValid()); | |
125 DCHECK(other.IsValid()); | |
126 return bytes_ < other.bytes_; | |
127 } | |
128 | |
129 bool UniquePosition::Equals(const UniquePosition& other) const { | |
130 if (!this->IsValid() && !other.IsValid()) | |
131 return true; | |
132 | |
133 return bytes_ == other.bytes_; | |
134 } | |
135 | |
136 const std::string& UniquePosition::ToInternalValue() const { | |
137 return bytes_; | |
138 } | |
139 | |
140 int64 UniquePosition::ToInt64() const { | |
141 uint64 y = 0; | |
142 const std::string& s = ToInternalValue(); | |
143 size_t l = sizeof(int64); | |
144 if (s.length() < l) { | |
145 NOTREACHED(); | |
146 l = s.length(); | |
147 } | |
148 for (size_t i = 0; i < l; ++i) { | |
149 const uint8 byte = s[l - i - 1]; | |
150 y |= static_cast<uint64>(byte) << (i * 8); | |
151 } | |
152 y ^= 0x8000000000000000ULL; | |
153 // This is technically implementation-defined if y > INT64_MAX, so | |
154 // we're assuming that we're on a twos-complement machine. | |
155 return static_cast<int64>(y); | |
156 } | |
157 | |
158 bool UniquePosition::IsValid() const { | |
159 return is_valid_; | |
160 } | |
161 | |
162 std::string UniquePosition::ToDebugString() const { | |
163 std::string debug_string = base::HexEncode(bytes_.data(), bytes_.length()); | |
164 if (!IsValid()) { | |
165 debug_string = "INVALID[" + debug_string + "]"; | |
166 } | |
167 return debug_string;; | |
168 } | |
169 | |
170 std::string UniquePosition::FindSmallerWithSuffix( | |
171 const std::string& reference, | |
172 const std::string& suffix) { | |
173 // When comparing, we need to take into account the terminator that will be | |
174 // appended to the suffix when this is made into a UniquePosition. | |
175 const std::string suffixFF = suffix + kTerminatorByte; | |
176 std::string ret; | |
177 | |
178 // Does our suffix alone place us where we want to be? | |
179 if (suffixFF < reference) { | |
180 return ""; | |
181 } | |
182 | |
183 for (size_t i = 0; i < reference.length(); ++i) { | |
184 const uint8 digit = reference[i]; | |
185 if (digit != 0) { | |
186 ret.push_back(digit/2); | |
187 return ret; | |
188 } else { | |
189 ret.push_back(0); | |
190 // We failed to differentiate ourselves with this digit. But maybe the we | |
191 // can append the suffix at this point and get the position we're looking | |
192 // for that way. | |
193 if (suffixFF < reference.substr(i+1)) { | |
194 return ret; | |
195 } | |
196 } | |
197 } | |
198 | |
199 // Was it 0x00 all the way to the end? Then we can't insert before it. | |
200 // That's why we enforce that all positions should end with a max digit, | |
201 // which should ensure we never end up here. | |
202 NOTREACHED(); | |
203 return ""; | |
204 } | |
205 | |
206 // static | |
207 std::string UniquePosition::FindGreaterWithSuffix( | |
208 const std::string& reference, | |
209 const std::string& suffix) { | |
210 // When comparing, we need to take into account the terminator that will be | |
211 // appended to the suffix when this is made into a UniquePosition. | |
212 const std::string suffixFF = suffix + kTerminatorByte; | |
213 | |
214 std::string ret; | |
215 | |
216 // Does our suffix alone place us where we want to be? | |
217 if (reference < suffixFF) { | |
218 return ""; | |
219 } | |
220 | |
221 for (size_t i = 0; i < reference.length(); ++i) { | |
222 const uint8 digit = reference[i]; | |
223 if (digit != 255) { | |
akalin
2012/12/18 22:38:20
use kuint8max
rlarocque
2012/12/18 23:51:09
Done.
| |
224 ret.push_back((digit+256U)/2U); | |
akalin
2012/12/18 22:38:20
prefer "digit + (kuint8max - digit + 1) / 2" as it
rlarocque
2012/12/18 23:51:09
Done.
| |
225 return ret; | |
226 } else { | |
227 ret.push_back(255); | |
akalin
2012/12/18 22:38:20
kuint8max
rlarocque
2012/12/18 23:51:09
Done.
| |
228 // We failed to differentiate ourselves with this digit. But maybe the we | |
229 // can append the suffix at this point and get the position we're looking | |
230 // for that way. | |
231 if (reference.substr(i+1) < suffixFF) { | |
232 return ret; | |
233 } | |
234 } | |
235 } | |
236 | |
237 // Still here? Then we must increase our length in order to exceed before. | |
238 ret.push_back(255); | |
akalin
2012/12/18 22:38:20
here too
also before -> reference
rlarocque
2012/12/18 23:51:09
Done.
| |
239 | |
240 DCHECK_LT(reference, ret); | |
241 return ret; | |
242 } | |
243 | |
244 // static | |
245 std::string UniquePosition::FindBetweenWithSuffix( | |
246 const std::string& before, | |
247 const std::string& after, | |
248 const std::string& suffix) { | |
249 DCHECK(IsValidSuffix(suffix)); | |
250 DCHECK_NE(before, after); | |
251 DCHECK_LT(before, after); | |
252 | |
253 // When comparing, we need to take into account the terminator that will be | |
254 // appended when this is made into a UniquePosition. | |
255 const std::string suffixFF = suffix + kTerminatorByte; | |
256 | |
257 std::string mid; | |
258 | |
259 // Sometimes our suffix puts us where we want to be. | |
260 if (before < suffixFF && suffixFF < after) { | |
261 return ""; | |
262 } | |
263 | |
264 size_t i = 0; | |
265 for ( ; i < std::min(before.length(), after.length()); ++i) { | |
266 uint8 a_digit = before[i]; | |
267 uint8 b_digit = after[i]; | |
268 | |
269 if (b_digit - a_digit >= 2) { | |
270 mid.push_back((b_digit + a_digit) / 2U); | |
akalin
2012/12/17 18:47:45
can overflow? perhaps a_digit + (b_digit - a_digi
rlarocque
2012/12/18 23:51:09
Done.
| |
271 return mid; | |
272 } else if (a_digit == b_digit) { | |
273 mid.push_back(a_digit); | |
274 | |
275 // Both strings are equal so far. Will appending the suffix at this point | |
276 // give us the comparison we're looking for? | |
277 if (suffixFF < before.substr(i, 0) && after.substr(i, 0) < suffixFF) { | |
akalin
2012/12/18 22:38:20
hey how does this work? you want the substring of
rlarocque
2012/12/18 23:51:09
Yep, that's very wrong.
We can get away with it b
| |
278 return mid; | |
279 } | |
280 } else { | |
281 DCHECK_EQ(b_digit - a_digit, 1); // Implied by above if branches. | |
282 | |
283 // The two options are off by one digit. The choice of whether to round | |
284 // up or down here will have consequences on what we do with the remaining | |
285 // digits. Exploring both options is an optimization and is not required | |
286 // for the correctness of this algorithm. | |
287 | |
288 // Option A: Round down the current digit. This makes our |mid| < | |
289 // |after|, no matter what we append afterwards. We then focus on | |
290 // appending digits until |mid| > |before|. | |
291 std::string mid_a = mid; | |
292 mid_a.push_back(a_digit); | |
293 mid_a.append(FindGreaterWithSuffix(before.substr(i+1), suffix)); | |
294 | |
295 // Option B: Round up the current digit. This makes our |mid| > |before|, | |
296 // no matter what we append afterwards. We then focus on appending digits | |
297 // until |mid| < |after|. Note that this option may not be viable if the | |
298 // current digit is the last one in |after|, so we skip the option in that | |
299 // case. | |
300 if (after.length() > i+1) { | |
301 std::string mid_b = mid; | |
302 mid_b.push_back(b_digit); | |
303 mid_b.append(FindSmallerWithSuffix(after.substr(i+1), suffix)); | |
304 | |
305 // Does this give us a shorter position value? If so, use it. | |
306 if (mid_b.length() < mid_a.length()) { | |
307 return mid_b; | |
308 } | |
309 } | |
310 return mid_a; | |
311 } | |
312 } | |
313 | |
314 // If we haven't found a midpoint yet, the following must be true: | |
315 DCHECK_EQ(before.substr(0, i), after.substr(0, i)); | |
316 DCHECK_EQ(before, mid); | |
317 DCHECK_LT(before.length(), after.length()); | |
318 | |
319 // We know that we'll need to append at least one more byte to |mid| in the | |
320 // process of making it < |after|. Appending any digit, regardless of the | |
321 // value, will make |before| < |mid|. Therefore, the following will get us a | |
322 // valid position. | |
323 | |
324 mid.append(FindSmallerWithSuffix(after.substr(i), suffix)); | |
325 return mid; | |
326 } | |
327 | |
328 UniquePosition::UniquePosition(const std::string& internal_rep) | |
329 : bytes_(internal_rep), | |
330 is_valid_(IsValidBytes(bytes_)) { | |
331 DCHECK(IsValid()); | |
332 } | |
333 | |
334 UniquePosition::UniquePosition( | |
335 const std::string& prefix, | |
336 const std::string& suffix) | |
337 : bytes_(prefix + suffix + kTerminatorByte), | |
338 is_valid_(IsValidBytes(bytes_)) { | |
339 DCHECK(IsValidSuffix(suffix)); | |
340 DCHECK(IsValid()); | |
341 } | |
342 | |
343 } // namespace syncer | |
OLD | NEW |