This source file includes following definitions.
- IsValidSuffix
- IsValidBytes
- CreateInvalid
- FromProto
- FromInt64
- InitialPosition
- Before
- After
- Between
- LessThan
- Equals
- ToProto
- SerializeToString
- ToInt64
- IsValid
- ToDebugString
- GetSuffixForTest
- FindSmallerWithSuffix
- FindGreaterWithSuffix
- FindBetweenWithSuffix
- is_valid_
- is_valid_
- WriteEncodedRunLength
- ReadEncodedRunLength
- IsRepeatedCharPrefix
- Compress
- CompressImpl
- Uncompress
- IsValidCompressed
#include "sync/internal_api/public/base/unique_position.h"
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "sync/protocol/unique_position.pb.h"
#include "third_party/zlib/zlib.h"
namespace syncer {
const size_t UniquePosition::kSuffixLength = 28;
const size_t UniquePosition::kCompressBytesThreshold = 128;
bool UniquePosition::IsValidSuffix(const std::string& suffix) {
return suffix.length() == kSuffixLength;
}
bool UniquePosition::IsValidBytes(const std::string& bytes) {
return bytes.length() >= kSuffixLength
&& bytes[bytes.length()-1] != 0;
}
UniquePosition UniquePosition::CreateInvalid() {
UniquePosition pos;
DCHECK(!pos.IsValid());
return pos;
}
UniquePosition UniquePosition::FromProto(const sync_pb::UniquePosition& proto) {
if (proto.has_custom_compressed_v1()) {
return UniquePosition(proto.custom_compressed_v1());
} else if (proto.has_value() && !proto.value().empty()) {
return UniquePosition(Compress(proto.value()));
} else if (proto.has_compressed_value() && proto.has_uncompressed_length()) {
uLongf uncompressed_len = proto.uncompressed_length();
std::string un_gzipped;
un_gzipped.resize(uncompressed_len);
int result = uncompress(
reinterpret_cast<Bytef*>(string_as_array(&un_gzipped)),
&uncompressed_len,
reinterpret_cast<const Bytef*>(proto.compressed_value().data()),
proto.compressed_value().size());
if (result != Z_OK) {
DLOG(ERROR) << "Unzip failed " << result;
return UniquePosition::CreateInvalid();
}
if (uncompressed_len != proto.uncompressed_length()) {
DLOG(ERROR)
<< "Uncompressed length " << uncompressed_len
<< " did not match specified length " << proto.uncompressed_length();
return UniquePosition::CreateInvalid();
}
return UniquePosition(Compress(un_gzipped));
} else {
return UniquePosition::CreateInvalid();
}
}
UniquePosition UniquePosition::FromInt64(
int64 x, const std::string& suffix) {
uint64 y = static_cast<uint64>(x);
y ^= 0x8000000000000000ULL;
std::string bytes(8, 0);
for (int i = 7; i >= 0; --i) {
bytes[i] = static_cast<uint8>(y);
y >>= 8;
}
return UniquePosition(bytes + suffix, suffix);
}
UniquePosition UniquePosition::InitialPosition(
const std::string& suffix) {
DCHECK(IsValidSuffix(suffix));
return UniquePosition(suffix, suffix);
}
UniquePosition UniquePosition::Before(
const UniquePosition& x,
const std::string& suffix) {
DCHECK(IsValidSuffix(suffix));
DCHECK(x.IsValid());
const std::string& before = FindSmallerWithSuffix(
Uncompress(x.compressed_), suffix);
return UniquePosition(before + suffix, suffix);
}
UniquePosition UniquePosition::After(
const UniquePosition& x,
const std::string& suffix) {
DCHECK(IsValidSuffix(suffix));
DCHECK(x.IsValid());
const std::string& after = FindGreaterWithSuffix(
Uncompress(x.compressed_), suffix);
return UniquePosition(after + suffix, suffix);
}
UniquePosition UniquePosition::Between(
const UniquePosition& before,
const UniquePosition& after,
const std::string& suffix) {
DCHECK(before.IsValid());
DCHECK(after.IsValid());
DCHECK(before.LessThan(after));
DCHECK(IsValidSuffix(suffix));
const std::string& mid = FindBetweenWithSuffix(
Uncompress(before.compressed_),
Uncompress(after.compressed_),
suffix);
return UniquePosition(mid + suffix, suffix);
}
UniquePosition::UniquePosition() : is_valid_(false) {}
bool UniquePosition::LessThan(const UniquePosition& other) const {
DCHECK(this->IsValid());
DCHECK(other.IsValid());
return compressed_ < other.compressed_;
}
bool UniquePosition::Equals(const UniquePosition& other) const {
if (!this->IsValid() && !other.IsValid())
return true;
return compressed_ == other.compressed_;
}
void UniquePosition::ToProto(sync_pb::UniquePosition* proto) const {
proto->Clear();
proto->set_custom_compressed_v1(compressed_);
}
void UniquePosition::SerializeToString(std::string* blob) const {
DCHECK(blob);
sync_pb::UniquePosition proto;
ToProto(&proto);
proto.SerializeToString(blob);
}
int64 UniquePosition::ToInt64() const {
uint64 y = 0;
const std::string& s = Uncompress(compressed_);
size_t l = sizeof(int64);
if (s.length() < l) {
NOTREACHED();
l = s.length();
}
for (size_t i = 0; i < l; ++i) {
const uint8 byte = s[l - i - 1];
y |= static_cast<uint64>(byte) << (i * 8);
}
y ^= 0x8000000000000000ULL;
return static_cast<int64>(y);
}
bool UniquePosition::IsValid() const {
return is_valid_;
}
std::string UniquePosition::ToDebugString() const {
const std::string bytes = Uncompress(compressed_);
if (bytes.empty())
return std::string("INVALID[]");
std::string debug_string = base::HexEncode(bytes.data(), bytes.length());
if (!IsValid()) {
debug_string = "INVALID[" + debug_string + "]";
}
std::string compressed_string =
base::HexEncode(compressed_.data(), compressed_.length());
debug_string.append(", compressed: " + compressed_string);
return debug_string;
}
std::string UniquePosition::GetSuffixForTest() const {
const std::string bytes = Uncompress(compressed_);
const size_t prefix_len = bytes.length() - kSuffixLength;
return bytes.substr(prefix_len, std::string::npos);
}
std::string UniquePosition::FindSmallerWithSuffix(
const std::string& reference,
const std::string& suffix) {
size_t ref_zeroes = reference.find_first_not_of('\0');
size_t suffix_zeroes = suffix.find_first_not_of('\0');
DCHECK_NE(ref_zeroes, std::string::npos);
DCHECK_NE(suffix_zeroes, std::string::npos);
if (suffix_zeroes > ref_zeroes) {
return std::string();
}
if (suffix.substr(suffix_zeroes) < reference.substr(ref_zeroes)) {
return std::string(ref_zeroes - suffix_zeroes, '\0');
} else if (suffix_zeroes > 1) {
return std::string(ref_zeroes - suffix_zeroes + 1, '\0');
} else {
char lt_digit = static_cast<uint8>(reference[ref_zeroes])/2;
return std::string(ref_zeroes, '\0') + lt_digit;
}
}
std::string UniquePosition::FindGreaterWithSuffix(
const std::string& reference,
const std::string& suffix) {
size_t ref_FFs = reference.find_first_not_of(kuint8max);
size_t suffix_FFs = suffix.find_first_not_of(kuint8max);
if (ref_FFs == std::string::npos) {
ref_FFs = reference.length();
}
if (suffix_FFs == std::string::npos) {
suffix_FFs = suffix.length();
}
if (suffix_FFs > ref_FFs) {
return std::string();
}
if (suffix.substr(suffix_FFs) > reference.substr(ref_FFs)) {
return std::string(ref_FFs - suffix_FFs, kuint8max);
} else if (suffix_FFs > 1) {
return std::string(ref_FFs - suffix_FFs + 1, kuint8max);
} else {
char gt_digit = static_cast<uint8>(reference[ref_FFs]) +
(kuint8max - static_cast<uint8>(reference[ref_FFs]) + 1) / 2;
return std::string(ref_FFs, kuint8max) + gt_digit;
}
}
std::string UniquePosition::FindBetweenWithSuffix(
const std::string& before,
const std::string& after,
const std::string& suffix) {
DCHECK(IsValidSuffix(suffix));
DCHECK_NE(before, after);
DCHECK_LT(before, after);
std::string mid;
if (before < suffix && suffix < after) {
return std::string();
}
size_t i = 0;
for ( ; i < std::min(before.length(), after.length()); ++i) {
uint8 a_digit = before[i];
uint8 b_digit = after[i];
if (b_digit - a_digit >= 2) {
mid.push_back(a_digit + (b_digit - a_digit)/2);
return mid;
} else if (a_digit == b_digit) {
mid.push_back(a_digit);
if (before.substr(i+1) < suffix && suffix < after.substr(i+1)) {
return mid;
}
} else {
DCHECK_EQ(b_digit - a_digit, 1);
std::string mid_a = mid;
mid_a.push_back(a_digit);
mid_a.append(FindGreaterWithSuffix(before.substr(i+1), suffix));
if (after.length() > i+1) {
std::string mid_b = mid;
mid_b.push_back(b_digit);
mid_b.append(FindSmallerWithSuffix(after.substr(i+1), suffix));
if (mid_b.length() < mid_a.length()) {
return mid_b;
}
}
return mid_a;
}
}
DCHECK_EQ(before.substr(0, i), after.substr(0, i));
DCHECK_EQ(before, mid);
DCHECK_LT(before.length(), after.length());
mid.append(FindSmallerWithSuffix(after.substr(i), suffix));
return mid;
}
UniquePosition::UniquePosition(const std::string& internal_rep)
: compressed_(internal_rep),
is_valid_(IsValidBytes(Uncompress(internal_rep))) {
}
UniquePosition::UniquePosition(
const std::string& uncompressed,
const std::string& suffix)
: compressed_(Compress(uncompressed)),
is_valid_(IsValidBytes(uncompressed)) {
DCHECK(uncompressed.rfind(suffix) + kSuffixLength == uncompressed.length());
DCHECK(IsValidSuffix(suffix));
DCHECK(IsValid());
}
namespace {
static void WriteEncodedRunLength(uint32 length,
bool high_encoding,
std::string* output_str) {
CHECK_GE(length, 4U);
CHECK_LT(length, 0x80000000);
uint32 encoded_length;
if (high_encoding) {
encoded_length = 0xffffffff - length;
} else {
encoded_length = length;
}
output_str->append(1, 0xff & (encoded_length >> 24U));
output_str->append(1, 0xff & (encoded_length >> 16U));
output_str->append(1, 0xff & (encoded_length >> 8U));
output_str->append(1, 0xff & (encoded_length >> 0U));
}
static uint32 ReadEncodedRunLength(const std::string& str, size_t i) {
DCHECK_LE(i + 4, str.length());
uint32 encoded_length =
((uint8)(str[i+3]) << 0) |
((uint8)(str[i+2]) << 8) |
((uint8)(str[i+1]) << 16) |
((uint8)(str[i+0]) << 24);
uint32 length;
if (encoded_length & 0x80000000) {
length = 0xffffffff - encoded_length;
} else {
length = encoded_length;
}
return length;
}
static bool IsRepeatedCharPrefix(const std::string& chars, size_t start_index) {
return chars[start_index] == chars[start_index+1]
&& chars[start_index] == chars[start_index+2]
&& chars[start_index] == chars[start_index+3];
}
}
std::string UniquePosition::Compress(const std::string& str) {
DCHECK(IsValidBytes(str));
std::string compressed = CompressImpl(str);
DCHECK(IsValidCompressed(compressed));
DCHECK_EQ(str, Uncompress(compressed));
return compressed;
}
std::string UniquePosition::CompressImpl(const std::string& str) {
std::string output;
output.reserve(48);
for (size_t i = 0; i < str.length(); ) {
if (i + 4 <= str.length() && IsRepeatedCharPrefix(str, i)) {
output.append(str, i, 4);
const char rep_digit = str[i];
const size_t runs_until = str.find_first_not_of(rep_digit, i+4);
size_t run_length;
bool encode_high;
if (runs_until == std::string::npos) {
run_length = str.length() - i;
encode_high = false;
} else {
run_length = runs_until - i;
encode_high = static_cast<uint8>(str[runs_until]) >
static_cast<uint8>(rep_digit);
}
DCHECK_LT(run_length, static_cast<size_t>(kint32max))
<< "This implementation can't encode run-lengths greater than 2^31.";
WriteEncodedRunLength(run_length, encode_high, &output);
i += run_length;
} else {
const size_t len = std::min(static_cast<size_t>(8), str.length() - i);
output.append(str, i, len);
i += len;
}
}
return output;
}
std::string UniquePosition::Uncompress(const std::string& str) {
std::string output;
size_t i = 0;
for (i = 0; i + 8 <= str.length(); i += 8) {
if (IsRepeatedCharPrefix(str, i)) {
const char rep_digit = str[i];
uint32 length = ReadEncodedRunLength(str, i+4);
output.append(length, rep_digit);
} else {
output.append(str, i, 8);
}
}
output.append(str, i, std::string::npos);
return output;
}
bool UniquePosition::IsValidCompressed(const std::string& str) {
for (size_t i = 0; i + 8 <= str.length(); i += 8) {
if (IsRepeatedCharPrefix(str, i)) {
uint32 count = ReadEncodedRunLength(str, i+4);
if (count < 4) {
return false;
}
if (str[i] == str[i+4]) {
return false;
}
}
}
return true;
}
}