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(const std::string& bytes) { |
| 42 UniquePosition result(bytes); |
| 43 return result; |
| 44 } |
| 45 |
| 46 // static. |
| 47 UniquePosition UniquePosition::FromInt64( |
| 48 int64 x, const std::string& suffix) { |
| 49 uint64 y = static_cast<uint64>(x); |
| 50 y ^= 0x8000000000000000ULL; // Make it non-negative. |
| 51 std::string bytes(8, 0); |
| 52 for (int i = 7; i >= 0; --i) { |
| 53 bytes[i] = static_cast<uint8>(y); |
| 54 y >>= 8; |
| 55 } |
| 56 return UniquePosition(bytes, suffix); |
| 57 } |
| 58 |
| 59 // static. |
| 60 UniquePosition UniquePosition::InitialPosition( |
| 61 const std::string& suffix) { |
| 62 DCHECK(IsValidSuffix(suffix)); |
| 63 return UniquePosition("", suffix); |
| 64 } |
| 65 |
| 66 // static. |
| 67 UniquePosition UniquePosition::Before( |
| 68 const UniquePosition& x, |
| 69 const std::string& suffix) { |
| 70 DCHECK(IsValidSuffix(suffix)); |
| 71 DCHECK(x.IsValid()); |
| 72 const std::string& before = FindSmallerWithSuffix(x.bytes_, suffix); |
| 73 return UniquePosition(before, suffix); |
| 74 } |
| 75 |
| 76 // static. |
| 77 UniquePosition UniquePosition::After( |
| 78 const UniquePosition& x, |
| 79 const std::string& suffix) { |
| 80 DCHECK(IsValidSuffix(suffix)); |
| 81 DCHECK(x.IsValid()); |
| 82 const std::string& after = FindGreaterWithSuffix(x.bytes_, suffix); |
| 83 return UniquePosition(after, suffix); |
| 84 } |
| 85 |
| 86 // static. |
| 87 UniquePosition UniquePosition::Between( |
| 88 const UniquePosition& before, |
| 89 const UniquePosition& after, |
| 90 const std::string& suffix) { |
| 91 DCHECK(before.IsValid()); |
| 92 DCHECK(after.IsValid()); |
| 93 DCHECK(before.LessThan(after)); |
| 94 DCHECK(IsValidSuffix(suffix)); |
| 95 const std::string& mid = |
| 96 FindBetweenWithSuffix(before.bytes_, after.bytes_, suffix); |
| 97 return UniquePosition(mid, suffix); |
| 98 } |
| 99 |
| 100 UniquePosition::UniquePosition() : is_valid_(false) {} |
| 101 |
| 102 bool UniquePosition::LessThan(const UniquePosition& other) const { |
| 103 DCHECK(this->IsValid()); |
| 104 DCHECK(other.IsValid()); |
| 105 return bytes_ < other.bytes_; |
| 106 } |
| 107 |
| 108 bool UniquePosition::Equals(const UniquePosition& other) const { |
| 109 if (!this->IsValid() && !other.IsValid()) |
| 110 return true; |
| 111 |
| 112 return bytes_ == other.bytes_; |
| 113 } |
| 114 |
| 115 const std::string& UniquePosition::ToInternalValue() const { |
| 116 return bytes_; |
| 117 } |
| 118 |
| 119 int64 UniquePosition::ToInt64() const { |
| 120 uint64 y = 0; |
| 121 const std::string& s = ToInternalValue(); |
| 122 size_t l = sizeof(int64); |
| 123 if (s.length() < l) { |
| 124 NOTREACHED(); |
| 125 l = s.length(); |
| 126 } |
| 127 for (size_t i = 0; i < l; ++i) { |
| 128 const uint8 byte = s[l - i - 1]; |
| 129 y |= static_cast<uint64>(byte) << (i * 8); |
| 130 } |
| 131 y ^= 0x8000000000000000ULL; |
| 132 // This is technically implementation-defined if y > INT64_MAX, so |
| 133 // we're assuming that we're on a twos-complement machine. |
| 134 return static_cast<int64>(y); |
| 135 } |
| 136 |
| 137 bool UniquePosition::IsValid() const { |
| 138 return is_valid_; |
| 139 } |
| 140 |
| 141 std::string UniquePosition::ToDebugString() const { |
| 142 std::string debug_string = base::HexEncode(bytes_.data(), bytes_.length()); |
| 143 if (!IsValid()) { |
| 144 debug_string = "INVALID[" + debug_string + "]"; |
| 145 } |
| 146 return debug_string;; |
| 147 } |
| 148 |
| 149 std::string UniquePosition::FindSmallerWithSuffix( |
| 150 const std::string& reference, |
| 151 const std::string& suffix) { |
| 152 size_t ref_zeroes = reference.find_first_not_of('\0'); |
| 153 size_t suffix_zeroes = suffix.find_first_not_of('\0'); |
| 154 |
| 155 // Neither of our inputs are allowed to have trailing zeroes, so the following |
| 156 // must be true. |
| 157 DCHECK_NE(ref_zeroes, std::string::npos); |
| 158 DCHECK_NE(suffix_zeroes, std::string::npos); |
| 159 |
| 160 if (suffix_zeroes > ref_zeroes) { |
| 161 // Implies suffix < ref. |
| 162 return ""; |
| 163 } |
| 164 |
| 165 if (suffix.substr(suffix_zeroes) < reference.substr(ref_zeroes)) { |
| 166 // Prepend zeroes so the result has as many zero digits as |reference|. |
| 167 return std::string(ref_zeroes - suffix_zeroes, '\0'); |
| 168 } else if (suffix_zeroes > 1) { |
| 169 // Prepend zeroes so the result has one more zero digit than |reference|. |
| 170 // We could also take the "else" branch below, but taking this branch will |
| 171 // give us a smaller result. |
| 172 return std::string(ref_zeroes - suffix_zeroes + 1, '\0'); |
| 173 } else { |
| 174 // Prepend zeroes to match those in the |reference|, then something smaller |
| 175 // than the first non-zero digit in |reference|. |
| 176 char lt_digit = static_cast<uint8>(reference[ref_zeroes])/2; |
| 177 return std::string(ref_zeroes, '\0') + lt_digit; |
| 178 } |
| 179 } |
| 180 |
| 181 // static |
| 182 std::string UniquePosition::FindGreaterWithSuffix( |
| 183 const std::string& reference, |
| 184 const std::string& suffix) { |
| 185 size_t ref_FFs = reference.find_first_not_of(kuint8max); |
| 186 size_t suffix_FFs = suffix.find_first_not_of(kuint8max); |
| 187 |
| 188 if (ref_FFs == std::string::npos) { |
| 189 ref_FFs = reference.length(); |
| 190 } |
| 191 if (suffix_FFs == std::string::npos) { |
| 192 suffix_FFs = suffix.length(); |
| 193 } |
| 194 |
| 195 if (suffix_FFs > ref_FFs) { |
| 196 // Implies suffix > reference. |
| 197 return ""; |
| 198 } |
| 199 |
| 200 if (suffix.substr(suffix_FFs) > reference.substr(ref_FFs)) { |
| 201 // Prepend FF digits to match those in |reference|. |
| 202 return std::string(ref_FFs - suffix_FFs, kuint8max); |
| 203 } else if (suffix_FFs > 1) { |
| 204 // Prepend enough leading FF digits so result has one more of them than |
| 205 // |reference| does. We could also take the "else" branch below, but this |
| 206 // gives us a smaller result. |
| 207 return std::string(ref_FFs - suffix_FFs + 1, kuint8max); |
| 208 } else { |
| 209 // Prepend FF digits to match those in |reference|, then something larger |
| 210 // than the first non-FF digit in |reference|. |
| 211 char gt_digit = static_cast<uint8>(reference[ref_FFs]) + |
| 212 (kuint8max - static_cast<uint8>(reference[ref_FFs]) + 1) / 2; |
| 213 return std::string(ref_FFs, kuint8max) + gt_digit; |
| 214 } |
| 215 } |
| 216 |
| 217 // TODO(rlarocque): Is there a better algorithm that we could use here? |
| 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)) { |
| 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 |