Chromium Code Reviews| 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 |