Chromium Code Reviews| Index: content/browser/indexed_db/indexed_db_leveldb_coding.cc |
| diff --git a/content/browser/indexed_db/indexed_db_leveldb_coding.cc b/content/browser/indexed_db/indexed_db_leveldb_coding.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..10e5fc3a43cf54e5c55047d03cbd58b149922bf2 |
| --- /dev/null |
| +++ b/content/browser/indexed_db/indexed_db_leveldb_coding.cc |
| @@ -0,0 +1,1898 @@ |
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "content/browser/indexed_db/indexed_db_leveldb_coding.h" |
| + |
| +#include <string> |
| +#include "base/logging.h" |
| +#include "base/string16.h" |
| +#include "base/sys_byteorder.h" |
| +#include "content/browser/indexed_db/leveldb/leveldb_slice.h" |
| +#include "content/common/indexed_db/indexed_db_key.h" |
| +#include "content/common/indexed_db/indexed_db_key_path.h" |
| +#include "third_party/WebKit/Source/Platform/chromium/public/WebIDBKeyPath.h" |
| + |
| +// LevelDB stores key/value pairs. Keys and values are strings of bytes, |
| +// normally of type std::vector<char>. |
| +// |
| +// The keys in the backing store are variable-length tuples with different types |
| +// of fields. Each key in the backing store starts with a ternary prefix: |
| +// (database id, object store id, index id). For each, 0 is reserved for |
| +// meta-data. |
| +// The prefix makes sure that data for a specific database, object store, and |
| +// index are grouped together. The locality is important for performance: common |
| +// operations should only need a minimal number of seek operations. For example, |
| +// all the meta-data for a database is grouped together so that reading that |
| +// meta-data only requires one seek. |
| +// |
| +// Each key type has a class (in square brackets below) which knows how to |
| +// encode, decode, and compare that key type. |
| +// |
| +// Global meta-data have keys with prefix (0,0,0), followed by a type byte: |
| +// |
| +// <0, 0, 0, 0> => |
| +// IndexedDB/LevelDB schema version [SchemaVersionKey] |
| +// <0, 0, 0, 1> => The maximum |
| +// database id ever allocated [MaxDatabaseIdKey] |
| +// <0, 0, 0, 2> => |
| +// SerializedScriptValue version [DataVersionKey] |
| +// <0, 0, 0, 100, database id> => Existence |
| +// implies the database id is in the free list [DatabaseFreeListKey] |
| +// <0, 0, 0, 201, utf16 origin name, utf16 database name> => Database id |
| +// [DatabaseNameKey] |
| +// |
| +// |
| +// Database meta-data: |
| +// |
| +// Again, the prefix is followed by a type byte. |
| +// |
| +// <database id, 0, 0, 0> => utf16 origin name [DatabaseMetaDataKey] |
| +// <database id, 0, 0, 1> => utf16 database name [DatabaseMetaDataKey] |
| +// <database id, 0, 0, 2> => utf16 user version data [DatabaseMetaDataKey] |
| +// <database id, 0, 0, 3> => maximum object store id ever allocated |
| +// [DatabaseMetaDataKey] |
| +// <database id, 0, 0, 4> => user integer version (var int) |
| +// [DatabaseMetaDataKey] |
| +// |
| +// |
| +// Object store meta-data: |
| +// |
| +// The prefix is followed by a type byte, then a variable-length integer, |
| +// and then another type byte. |
| +// |
| +// <database id, 0, 0, 50, object store id, 0> => utf16 object store name |
| +// [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 1> => utf16 key path |
| +// [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 2> => has auto increment |
| +// [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 3> => is evictable |
| +// [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 4> => last "version" number |
| +// [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 5> => maximum index id ever |
| +// allocated [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 6> => has key path (vs. null) |
| +// [ObjectStoreMetaDataKey] |
| +// <database id, 0, 0, 50, object store id, 7> => key generator current |
| +// number [ObjectStoreMetaDataKey] |
| +// |
| +// |
| +// Index meta-data: |
| +// |
| +// The prefix is followed by a type byte, then two variable-length integers, |
| +// and then another type byte. |
| +// |
| +// <database id, 0, 0, 100, object store id, index id, 0> => utf16 index |
| +// name [IndexMetaDataKey] |
| +// <database id, 0, 0, 100, object store id, index id, 1> => are index keys |
| +// unique [IndexMetaDataKey] |
| +// <database id, 0, 0, 100, object store id, index id, 2> => utf16 key path |
| +// [IndexMetaDataKey] |
| +// <database id, 0, 0, 100, object store id, index id, 3> => is index |
| +// multi-entry [IndexMetaDataKey] |
| +// |
| +// |
| +// Other object store and index meta-data: |
| +// |
| +// The prefix is followed by a type byte. The object store and index id are |
| +// variable length integers, the utf16 strings are variable length strings. |
| +// |
| +// <database id, 0, 0, 150, object store id> => existence |
| +// implies the object store id is in the free list [ObjectStoreFreeListKey] |
| +// <database id, 0, 0, 151, object store id, index id> => existence |
| +// implies the index id is in the free list [IndexFreeListKey] |
| +// <database id, 0, 0, 200, utf16 object store name> => object |
| +// store id [ObjectStoreNamesKey] |
| +// <database id, 0, 0, 201, object store id, utf16 index name> => index id |
| +// [IndexNamesKey] |
| +// |
| +// |
| +// Object store data: |
| +// |
| +// The prefix is followed by a type byte. The user key is an encoded |
| +// IndexedDBKey. |
| +// |
| +// <database id, object store id, 1, user key> => "version", serialized |
| +// script value [ObjectStoreDataKey] |
| +// |
| +// |
| +// "Exists" entry: |
| +// |
| +// The prefix is followed by a type byte. The user key is an encoded |
| +// IndexedDBKey. |
| +// |
| +// <database id, object store id, 2, user key> => "version" [ExistsEntryKey] |
| +// |
| +// |
| +// Index data: |
| +// |
| +// The prefix is followed by a type byte. The index key is an encoded |
| +// IndexedDBKey. The sequence number is a variable length integer. |
| +// The primary key is an encoded IndexedDBKey. |
| +// |
| +// <database id, object store id, index id, index key, sequence number, |
| +// primary key> => "version", primary key [IndexDataKey] |
| +// |
| +// (The sequence number is obsolete; it was used to allow two entries with |
| +// the same user (index) key in non-unique indexes prior to the inclusion of |
| +// the primary key in the data. The "version" field is used to weed out |
| +// stale |
| +// index data. Whenever new object store data is inserted, it gets a new |
| +// "version" number, and new index data is written with this number. When |
| +// the index is used for look-ups, entries are validated against the |
| +// "exists" entries, and records with old "version" numbers are deleted |
| +// when they are encountered in get_primary_key_via_index, |
| +// IndexCursorImpl::load_current_row, and |
| +// IndexKeyCursorImpl::load_current_row). |
| + |
| +using WebKit::WebIDBKey; |
| +using WebKit::WebIDBKeyPath; |
| + |
| +namespace content { |
| + |
| +#ifndef INT64_MAX |
| +#define INT64_MAX 0x7fffffffffffffffLL |
|
jamesr
2013/05/22 18:59:44
any particular reason against using std::numeric_l
jsbell
2013/05/22 22:21:14
None at all, in Chromium code. Done!
|
| +#endif |
| +#ifndef INT32_MAX |
| +#define INT32_MAX 0x7fffffffL |
| +#endif |
| + |
| +// As most of the IndexedDBKeys and encoded values are short, we |
| +// initialize some Vectors with a default inline buffer size to reduce |
| +// the memory re-allocations when the Vectors are appended. |
| +static const size_t kDefaultInlineBufferSize = 32; |
| + |
| +static const unsigned char kIndexedDBKeyNullTypeByte = 0; |
| +static const unsigned char kIndexedDBKeyStringTypeByte = 1; |
| +static const unsigned char kIndexedDBKeyDateTypeByte = 2; |
| +static const unsigned char kIndexedDBKeyNumberTypeByte = 3; |
| +static const unsigned char kIndexedDBKeyArrayTypeByte = 4; |
| +static const unsigned char kIndexedDBKeyMinKeyTypeByte = 5; |
| + |
| +static const unsigned char kIndexedDBKeyPathTypeCodedByte1 = 0; |
| +static const unsigned char kIndexedDBKeyPathTypeCodedByte2 = 0; |
| + |
| +static const unsigned char kObjectStoreDataIndexId = 1; |
| +static const unsigned char kExistsEntryIndexId = 2; |
| + |
| +static const unsigned char kSchemaVersionTypeByte = 0; |
| +static const unsigned char kMaxDatabaseIdTypeByte = 1; |
| +static const unsigned char kDataVersionTypeByte = 2; |
| +static const unsigned char kMaxSimpleGlobalMetaDataTypeByte = |
| + 3; // Insert before this and increment. |
| +static const unsigned char kDatabaseFreeListTypeByte = 100; |
| +static const unsigned char kDatabaseNameTypeByte = 201; |
| + |
| +static const unsigned char kObjectStoreMetaDataTypeByte = 50; |
| +static const unsigned char kIndexMetaDataTypeByte = 100; |
| +static const unsigned char kObjectStoreFreeListTypeByte = 150; |
| +static const unsigned char kIndexFreeListTypeByte = 151; |
| +static const unsigned char kObjectStoreNamesTypeByte = 200; |
| +static const unsigned char kIndexNamesKeyTypeByte = 201; |
| + |
| +static const unsigned char kObjectMetaDataTypeMaximum = 255; |
| +static const unsigned char kIndexMetaDataTypeMaximum = 255; |
| + |
| +const unsigned char kMinimumIndexId = 30; |
| + |
| +std::vector<char> EncodeByte(unsigned char c) { |
| + std::vector<char> v; |
| + v.reserve(kDefaultInlineBufferSize); |
| + v.push_back(c); |
| + |
| + DCHECK(v.size() <= kDefaultInlineBufferSize); |
| + return v; |
| +} |
| + |
| +const char* DecodeByte(const char* p, |
| + const char* limit, |
| + unsigned char& found_char) { |
| + if (p >= limit) |
| + return 0; |
| + |
| + found_char = *p++; |
| + return p; |
| +} |
| + |
| +std::vector<char> MaxIDBKey() { return EncodeByte(kIndexedDBKeyNullTypeByte); } |
| + |
| +std::vector<char> MinIDBKey() { |
| + return EncodeByte(kIndexedDBKeyMinKeyTypeByte); |
| +} |
| + |
| +std::vector<char> EncodeBool(bool b) { |
| + std::vector<char> ret; |
| + ret.reserve(kDefaultInlineBufferSize); |
| + ret.push_back(b ? 1 : 0); |
| + |
| + DCHECK(ret.size() <= kDefaultInlineBufferSize); |
| + return ret; |
| +} |
| + |
| +bool DecodeBool(const char* begin, const char* end) { |
| + DCHECK(begin < end); |
| + return *begin; |
| +} |
| + |
| +std::vector<char> EncodeInt(int64_t nParam) { |
| + DCHECK_GE(nParam, 0); |
| + uint64_t n = static_cast<uint64_t>(nParam); |
| + std::vector<char> ret; |
| + ret.reserve(kDefaultInlineBufferSize); |
| + |
| + do { |
| + unsigned char c = n; |
| + ret.push_back(c); |
| + n >>= 8; |
| + } while (n); |
| + |
| + DCHECK(ret.size() <= kDefaultInlineBufferSize); |
| + return ret; |
| +} |
| + |
| +static int CompareInts(int64_t a, int64_t b) { |
| + DCHECK_GE(a, 0); |
| + DCHECK_GE(b, 0); |
| + |
| + int64_t diff = a - b; |
| + if (diff < 0) |
| + return -1; |
| + if (diff > 0) |
| + return 1; |
| + return 0; |
| +} |
| + |
| +std::vector<char> EncodeVarInt(int64_t nParam) { |
| + DCHECK_GE(nParam, 0); |
| + uint64_t n = static_cast<uint64_t>(nParam); |
| + std::vector<char> ret; |
| + ret.reserve(kDefaultInlineBufferSize); |
| + |
| + do { |
| + unsigned char c = n & 0x7f; |
| + n >>= 7; |
| + if (n) |
| + c |= 0x80; |
| + ret.push_back(c); |
| + } while (n); |
| + |
| + DCHECK(ret.size() <= kDefaultInlineBufferSize); |
| + return ret; |
| +} |
| + |
| +const char* DecodeVarInt(const char* p, const char* limit, int64_t& found_int) { |
| + DCHECK_GE(limit, p); |
| + found_int = 0; |
| + int shift = 0; |
| + |
| + do { |
| + if (p >= limit) |
| + return 0; |
| + |
| + unsigned char c = *p; |
| + found_int |= static_cast<int64_t>(c & 0x7f) << shift; |
| + shift += 7; |
| + } while (*p++ & 0x80); |
| + return p; |
| +} |
| + |
| +std::vector<char>::iterator DecodeVarInt(std::vector<char>::iterator start, |
| + std::vector<char>::iterator limit, |
| + int64_t& found_int) { |
| + DCHECK(limit >= start); |
|
jamesr
2013/05/22 18:59:44
DCHECK_GE
|
| + found_int = 0; |
| + int shift = 0; |
| + std::vector<char>::iterator p = start; |
| + do { |
| + if (p >= limit) |
| + return start; |
| + |
| + unsigned char c = *p; |
| + found_int |= static_cast<int64_t>(c & 0x7f) << shift; |
| + shift += 7; |
| + } while (*p++ & 0x80); |
| + return p; |
| +} |
| + |
| +std::vector<char> EncodeString(const string16& s) { |
| + // Backing store is UTF-16BE, convert from host endianness. |
| + size_t length = s.length(); |
| + std::vector<char> ret(length * sizeof(char16)); |
| + |
| + const char16* src = s.c_str(); |
| + char16* dst = reinterpret_cast<char16*>(ret.data()); |
| + for (unsigned i = 0; i < length; ++i) |
| + *dst++ = htons(*src++); |
| + |
| + return ret; |
| +} |
| + |
| +string16 DecodeString(const char* start, const char* end) { |
| + // Backing store is UTF-16BE, convert to host endianness. |
| + DCHECK_GE(end, start); |
| + DCHECK(!((end - start) % sizeof(char16))); |
| + |
| + size_t length = (end - start) / sizeof(char16); |
| + string16 decoded; |
| + decoded.reserve(length); |
| + const char16* encoded = reinterpret_cast<const char16*>(start); |
| + for (unsigned i = 0; i < length; ++i) |
| + decoded.push_back(ntohs(*encoded++)); |
| + return decoded; |
| +} |
| + |
| +std::vector<char> EncodeStringWithLength(const string16& s) { |
| + std::vector<char> result = EncodeVarInt(s.length()); |
| + std::vector<char> encoded_value = EncodeString(s); |
| + result.insert(result.end(), encoded_value.begin(), encoded_value.end()); |
| + return result; |
| +} |
| + |
| +const char* DecodeStringWithLength(const char* p, |
| + const char* limit, |
| + string16& found_string) { |
| + DCHECK_GE(limit, p); |
| + int64_t len; |
| + p = DecodeVarInt(p, limit, len); |
| + if (!p || len < 0 || p + len * 2 > limit) |
| + return 0; |
| + |
| + found_string = DecodeString(p, p + len * 2); |
| + p += len * 2; |
| + return p; |
| +} |
| + |
| +int CompareEncodedStringsWithLength(const char*& p, |
| + const char* limit_p, |
| + const char*& q, |
| + const char* limit_q, |
| + bool& ok) { |
| + DCHECK(&p != &q); |
| + DCHECK_GE(limit_p, p); |
| + DCHECK_GE(limit_q, q); |
| + int64_t len_p, len_q; |
| + p = DecodeVarInt(p, limit_p, len_p); |
| + q = DecodeVarInt(q, limit_q, len_q); |
| + if (!p || !q || len_p < 0 || len_q < 0) { |
| + ok = false; |
| + return 0; |
| + } |
| + DCHECK(p && q); |
| + DCHECK_GE(len_p, 0); |
| + DCHECK_GE(len_q, 0); |
| + DCHECK(p + len_p * 2 <= limit_p); |
| + DCHECK(q + len_q * 2 <= limit_q); |
| + |
| + const char* start_p = p; |
| + const char* start_q = q; |
| + p += len_p * 2; |
| + q += len_q * 2; |
| + |
| + if (p > limit_p || q > limit_q) { |
| + ok = false; |
| + return 0; |
| + } |
| + |
| + ok = true; |
| + const size_t lmin = static_cast<size_t>(len_p < len_q ? len_p : len_q); |
| + if (int x = memcmp(start_p, start_q, lmin * 2)) |
| + return x; |
| + |
| + if (len_p == len_q) |
| + return 0; |
| + |
| + return (len_p > len_q) ? 1 : -1; |
| +} |
| + |
| +std::vector<char> EncodeDouble(double x) { |
| + // TODO(jsbell): It would be nice if we could be byte order independent. |
| + const char* p = reinterpret_cast<char*>(&x); |
| + std::vector<char> v; |
| + v.reserve(kDefaultInlineBufferSize); |
| + v.insert(v.end(), p, p + sizeof(x)); |
| + |
| + DCHECK(v.size() <= kDefaultInlineBufferSize); |
| + return v; |
| +} |
| + |
| +const char* DecodeDouble(const char* p, const char* limit, double* d) { |
| + if (p + sizeof(*d) > limit) |
| + return 0; |
| + |
| + char* x = reinterpret_cast<char*>(d); |
| + for (size_t i = 0; i < sizeof(*d); ++i) |
| + *x++ = *p++; |
| + return p; |
| +} |
| + |
| +std::vector<char> EncodeIDBKey(const IndexedDBKey& key) { |
| + std::vector<char> ret; |
| + ret.reserve(kDefaultInlineBufferSize); |
| + EncodeIDBKey(key, ret); |
| + return ret; |
| +} |
| + |
| +void EncodeIDBKey(const IndexedDBKey& key, std::vector<char>& into) { |
| + size_t previous_size = into.size(); |
| + DCHECK(key.IsValid()); |
| + switch (key.type()) { |
| + case WebIDBKey::NullType: |
| + case WebIDBKey::InvalidType: |
| + case WebIDBKey::MinType: { |
| + NOTREACHED(); |
| + into.push_back(kIndexedDBKeyNullTypeByte); |
| + return; |
| + } |
| + case WebIDBKey::ArrayType: { |
| + into.push_back(kIndexedDBKeyArrayTypeByte); |
| + size_t length = key.array().size(); |
| + std::vector<char> encoded_length = EncodeVarInt(length); |
| + into.insert(into.end(), encoded_length.begin(), encoded_length.end()); |
| + for (size_t i = 0; i < length; ++i) |
| + EncodeIDBKey(key.array()[i], into); |
| + DCHECK(into.size() > previous_size); |
| + return; |
| + } |
| + case WebIDBKey::StringType: { |
| + into.push_back(kIndexedDBKeyStringTypeByte); |
| + std::vector<char> tmp = EncodeStringWithLength(key.string()); |
| + into.insert(into.end(), tmp.begin(), tmp.end()); |
| + DCHECK(into.size() > previous_size); |
| + return; |
| + } |
| + case WebIDBKey::DateType: { |
| + into.push_back(kIndexedDBKeyDateTypeByte); |
| + std::vector<char> tmp = EncodeDouble(key.date()); |
| + into.insert(into.end(), tmp.begin(), tmp.end()); |
| + DCHECK(into.size() - previous_size == 9); |
| + return; |
| + } |
| + case WebIDBKey::NumberType: { |
| + into.push_back(kIndexedDBKeyNumberTypeByte); |
| + std::vector<char> tmp = EncodeDouble(key.number()); |
| + into.insert(into.end(), tmp.begin(), tmp.end()); |
| + DCHECK(into.size() - previous_size == 9); |
| + return; |
| + } |
| + } |
| + |
| + NOTREACHED(); |
| +} |
| + |
| +const char* DecodeIDBKey(const char* p, |
| + const char* limit, |
| + scoped_ptr<IndexedDBKey>* found_key) { |
| + DCHECK_GE(limit, p); |
| + if (p >= limit) |
| + return 0; |
| + |
| + unsigned char type = *p++; |
| + |
| + switch (type) { |
| + case kIndexedDBKeyNullTypeByte: |
| + *found_key = make_scoped_ptr(new IndexedDBKey()); |
| + return p; |
| + |
| + case kIndexedDBKeyArrayTypeByte: { |
| + int64_t length; |
| + p = DecodeVarInt(p, limit, length); |
| + if (!p || length < 0) |
| + return 0; |
| + IndexedDBKey::KeyArray array; |
| + while (length--) { |
| + scoped_ptr<IndexedDBKey> key; |
| + p = DecodeIDBKey(p, limit, &key); |
| + if (!p) |
| + return 0; |
| + array.push_back(*key); |
| + } |
| + *found_key = make_scoped_ptr(new IndexedDBKey(array)); |
| + return p; |
| + } |
| + case kIndexedDBKeyStringTypeByte: { |
| + string16 s; |
| + p = DecodeStringWithLength(p, limit, s); |
| + if (!p) |
| + return 0; |
| + *found_key = make_scoped_ptr(new IndexedDBKey(s)); |
| + return p; |
| + } |
| + case kIndexedDBKeyDateTypeByte: { |
| + double d; |
| + p = DecodeDouble(p, limit, &d); |
| + if (!p) |
| + return 0; |
| + *found_key = make_scoped_ptr(new IndexedDBKey(d, WebIDBKey::DateType)); |
| + return p; |
| + } |
| + case kIndexedDBKeyNumberTypeByte: { |
| + double d; |
| + p = DecodeDouble(p, limit, &d); |
| + if (!p) |
| + return 0; |
| + *found_key = make_scoped_ptr(new IndexedDBKey(d, WebIDBKey::NumberType)); |
| + return p; |
| + } |
| + } |
| + |
| + NOTREACHED(); |
| + return 0; |
| +} |
| + |
| +const char* ExtractEncodedIDBKey(const char* start, |
| + const char* limit, |
| + std::vector<char>* result = 0) { |
| + const char* p = start; |
| + if (p >= limit) |
| + return 0; |
| + |
| + unsigned char type = *p++; |
| + |
| + switch (type) { |
| + case kIndexedDBKeyNullTypeByte: |
| + case kIndexedDBKeyMinKeyTypeByte: |
| + break; |
| + case kIndexedDBKeyArrayTypeByte: { |
| + int64_t length; |
| + p = DecodeVarInt(p, limit, length); |
| + if (!p || length < 0) |
| + return 0; |
| + while (length--) { |
| + p = ExtractEncodedIDBKey(p, limit); |
| + if (!p) |
| + return 0; |
| + } |
| + break; |
| + } |
| + case kIndexedDBKeyStringTypeByte: { |
| + int64_t length; |
| + p = DecodeVarInt(p, limit, length); |
| + if (!p || length < 0 || p + length * 2 > limit) |
| + return 0; |
| + p += length * 2; |
| + break; |
| + } |
| + case kIndexedDBKeyDateTypeByte: |
| + case kIndexedDBKeyNumberTypeByte: |
| + if (p + sizeof(double) > limit) |
| + return 0; |
| + p += sizeof(double); |
| + break; |
| + } |
| + |
| + if (result) { |
| + DCHECK(p); |
| + DCHECK(p <= limit); |
| + result->assign(start, p); |
| + } |
| + |
| + return p; |
| +} |
| + |
| +static WebIDBKey::Type KeyTypeByteToKeyType(unsigned char type) { |
| + switch (type) { |
| + case kIndexedDBKeyNullTypeByte: |
| + return WebIDBKey::InvalidType; |
| + case kIndexedDBKeyArrayTypeByte: |
| + return WebIDBKey::ArrayType; |
| + case kIndexedDBKeyStringTypeByte: |
| + return WebIDBKey::StringType; |
| + case kIndexedDBKeyDateTypeByte: |
| + return WebIDBKey::DateType; |
| + case kIndexedDBKeyNumberTypeByte: |
| + return WebIDBKey::NumberType; |
| + case kIndexedDBKeyMinKeyTypeByte: |
| + return WebIDBKey::MinType; |
| + } |
| + |
| + NOTREACHED(); |
| + return WebIDBKey::InvalidType; |
| +} |
| + |
| +static int CompareTypes(WebIDBKey::Type a, WebIDBKey::Type b) { return b - a; } |
| + |
| +int CompareEncodedIDBKeys(const char*& ptr_a, |
| + const char* limit_a, |
| + const char*& ptr_b, |
| + const char* limit_b, |
| + bool& ok) { |
| + ok = true; |
| + DCHECK(&ptr_a != &ptr_b); |
|
jamesr
2013/05/22 18:59:44
DCHECK_NE
|
| + DCHECK(ptr_a < limit_a); |
|
jamesr
2013/05/22 18:59:44
DCHECK_LT
|
| + DCHECK(ptr_b < limit_b); |
|
jamesr
2013/05/22 18:59:44
DCHECK_LT
|
| + unsigned char type_a = *ptr_a++; |
| + unsigned char type_b = *ptr_b++; |
| + |
| + if (int x = CompareTypes(KeyTypeByteToKeyType(type_a), |
| + KeyTypeByteToKeyType(type_b))) |
| + return x; |
| + |
| + switch (type_a) { |
| + case kIndexedDBKeyNullTypeByte: |
| + case kIndexedDBKeyMinKeyTypeByte: |
| + // Null type or max type; no payload to compare. |
| + return 0; |
| + case kIndexedDBKeyArrayTypeByte: { |
| + int64_t length_a, length_b; |
| + ptr_a = DecodeVarInt(ptr_a, limit_a, length_a); |
| + ptr_b = DecodeVarInt(ptr_b, limit_b, length_b); |
| + if (!ptr_a || !ptr_b || length_a < 0 || length_b < 0) { |
| + ok = false; |
| + return 0; |
| + } |
| + for (int64_t i = 0; i < length_a && i < length_b; ++i) { |
| + int result = CompareEncodedIDBKeys(ptr_a, limit_a, ptr_b, limit_b, ok); |
| + if (!ok || result) |
| + return result; |
| + } |
| + if (length_a < length_b) |
| + return -1; |
| + if (length_a > length_b) |
| + return 1; |
| + return 0; |
| + } |
| + case kIndexedDBKeyStringTypeByte: |
| + return CompareEncodedStringsWithLength( |
| + ptr_a, limit_a, ptr_b, limit_b, ok); |
| + case kIndexedDBKeyDateTypeByte: |
| + case kIndexedDBKeyNumberTypeByte: { |
| + double d, e; |
| + ptr_a = DecodeDouble(ptr_a, limit_a, &d); |
| + ptr_b = DecodeDouble(ptr_b, limit_b, &e); |
| + DCHECK(ptr_a); |
| + DCHECK(ptr_b); |
| + if (!ptr_a || !ptr_b) { |
| + ok = false; |
| + return 0; |
| + } |
| + if (d < e) |
| + return -1; |
| + if (d > e) |
| + return 1; |
| + return 0; |
| + } |
| + } |
| + |
| + NOTREACHED(); |
| + return 0; |
| +} |
| + |
| +int CompareEncodedIDBKeys(const std::vector<char>& key_a, |
| + const std::vector<char>& key_b, |
| + bool& ok) { |
| + DCHECK(key_a.size() >= 1); |
| + DCHECK(key_b.size() >= 1); |
| + |
| + const char* ptr_a = key_a.data(); |
| + const char* limit_a = ptr_a + key_a.size(); |
| + const char* ptr_b = key_b.data(); |
| + const char* limit_b = ptr_b + key_b.size(); |
| + |
| + return CompareEncodedIDBKeys(ptr_a, limit_a, ptr_b, limit_b, ok); |
| +} |
| + |
| +std::vector<char> EncodeIDBKeyPath(const IndexedDBKeyPath& key_path) { |
| + // May be typed, or may be a raw string. An invalid leading |
| + // byte is used to identify typed coding. New records are |
| + // always written as typed. |
| + std::vector<char> ret; |
| + ret.reserve(kDefaultInlineBufferSize); |
| + ret.push_back(kIndexedDBKeyPathTypeCodedByte1); |
| + ret.push_back(kIndexedDBKeyPathTypeCodedByte2); |
| + ret.push_back(static_cast<char>(key_path.type())); |
| + switch (key_path.type()) { |
| + case WebIDBKeyPath::NullType: |
| + break; |
| + case WebIDBKeyPath::StringType: { |
| + std::vector<char> encoded_string = |
| + EncodeStringWithLength(key_path.string()); |
| + ret.insert(ret.end(), encoded_string.begin(), encoded_string.end()); |
| + break; |
| + } |
| + case WebIDBKeyPath::ArrayType: { |
| + const std::vector<string16>& array = key_path.array(); |
| + size_t count = array.size(); |
| + std::vector<char> encoded_count = EncodeVarInt(count); |
| + ret.insert(ret.end(), encoded_count.begin(), encoded_count.end()); |
| + for (size_t i = 0; i < count; ++i) { |
| + std::vector<char> encoded_string = EncodeStringWithLength(array[i]); |
| + ret.insert(ret.end(), encoded_string.begin(), encoded_string.end()); |
| + } |
| + break; |
| + } |
| + } |
| + return ret; |
| +} |
| + |
| +IndexedDBKeyPath DecodeIDBKeyPath(const char* p, const char* limit) { |
| + // May be typed, or may be a raw string. An invalid leading |
| + // byte sequence is used to identify typed coding. New records are |
| + // always written as typed. |
| + if (p == limit || |
| + (limit - p >= 2 && (*p != kIndexedDBKeyPathTypeCodedByte1 || |
| + *(p + 1) != kIndexedDBKeyPathTypeCodedByte2))) |
| + return IndexedDBKeyPath(DecodeString(p, limit)); |
| + p += 2; |
| + |
| + DCHECK(p != limit); |
|
jamesr
2013/05/22 18:59:44
DCHECK_NE
|
| + WebIDBKeyPath::Type type = static_cast<WebIDBKeyPath::Type>(*p++); |
| + switch (type) { |
| + case WebIDBKeyPath::NullType: |
| + DCHECK_EQ(p, limit); |
| + return IndexedDBKeyPath(); |
| + case WebIDBKeyPath::StringType: { |
| + string16 string; |
| + p = DecodeStringWithLength(p, limit, string); |
| + DCHECK_EQ(p, limit); |
| + return IndexedDBKeyPath(string); |
| + } |
| + case WebIDBKeyPath::ArrayType: { |
| + std::vector<string16> array; |
| + int64_t count; |
| + p = DecodeVarInt(p, limit, count); |
| + DCHECK(p); |
| + DCHECK_GE(count, 0); |
| + while (count--) { |
| + string16 string; |
| + p = DecodeStringWithLength(p, limit, string); |
| + DCHECK(p); |
| + array.push_back(string); |
| + } |
| + DCHECK_EQ(p, limit); |
| + return IndexedDBKeyPath(array); |
| + } |
| + } |
| + NOTREACHED(); |
| + return IndexedDBKeyPath(); |
| +} |
| + |
| +namespace { |
| + |
| +template <typename KeyType> |
| +int |
| +Compare(const LevelDBSlice& a, const LevelDBSlice& b, bool, bool& ok) { |
| + KeyType key_a; |
| + KeyType key_b; |
| + |
| + const char* ptr_a = KeyType::Decode(a.begin(), a.end(), &key_a); |
| + DCHECK(ptr_a); |
| + if (!ptr_a) { |
| + ok = false; |
| + return 0; |
| + } |
| + const char* ptr_b = KeyType::Decode(b.begin(), b.end(), &key_b); |
| + DCHECK(ptr_b); |
| + if (!ptr_b) { |
| + ok = false; |
| + return 0; |
| + } |
| + |
| + ok = true; |
| + return key_a.Compare(key_b); |
| +} |
| + |
| +template <> |
| +int |
| +Compare<ExistsEntryKey>(const LevelDBSlice& a, |
| + const LevelDBSlice& b, |
| + bool, |
| + bool& ok) { |
| + KeyPrefix prefix_a; |
| + KeyPrefix prefix_b; |
| + const char* ptr_a = KeyPrefix::Decode(a.begin(), a.end(), &prefix_a); |
| + const char* ptr_b = KeyPrefix::Decode(b.begin(), b.end(), &prefix_b); |
| + DCHECK(ptr_a); |
| + DCHECK(ptr_b); |
| + DCHECK(prefix_a.database_id_); |
| + DCHECK(prefix_a.object_store_id_); |
| + DCHECK_EQ(prefix_a.index_id_, ExistsEntryKey::kSpecialIndexNumber); |
| + DCHECK(prefix_b.database_id_); |
| + DCHECK(prefix_b.object_store_id_); |
| + DCHECK_EQ(prefix_b.index_id_, ExistsEntryKey::kSpecialIndexNumber); |
| + DCHECK(ptr_a != a.end()); |
| + DCHECK(ptr_b != b.end()); |
| + // Prefixes are not compared - it is assumed this was already done. |
| + DCHECK(!prefix_a.Compare(prefix_b)); |
| + |
| + return CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok); |
| +} |
| + |
| +template <> |
| +int |
| +Compare<ObjectStoreDataKey>(const LevelDBSlice& a, |
| + const LevelDBSlice& b, |
| + bool, |
| + bool& ok) { |
| + KeyPrefix prefix_a; |
| + KeyPrefix prefix_b; |
| + const char* ptr_a = KeyPrefix::Decode(a.begin(), a.end(), &prefix_a); |
| + const char* ptr_b = KeyPrefix::Decode(b.begin(), b.end(), &prefix_b); |
| + DCHECK(ptr_a); |
| + DCHECK(ptr_b); |
| + DCHECK(prefix_a.database_id_); |
| + DCHECK(prefix_a.object_store_id_); |
| + DCHECK_EQ(prefix_a.index_id_, ObjectStoreDataKey::kSpecialIndexNumber); |
| + DCHECK(prefix_b.database_id_); |
| + DCHECK(prefix_b.object_store_id_); |
| + DCHECK_EQ(prefix_b.index_id_, ObjectStoreDataKey::kSpecialIndexNumber); |
| + DCHECK(ptr_a != a.end()); |
| + DCHECK(ptr_b != b.end()); |
| + // Prefixes are not compared - it is assumed this was already done. |
| + DCHECK(!prefix_a.Compare(prefix_b)); |
| + |
| + return CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok); |
| +} |
| + |
| +template <> |
| +int |
| +Compare<IndexDataKey>(const LevelDBSlice& a, |
| + const LevelDBSlice& b, |
| + bool ignore_duplicates, |
| + bool& ok) { |
| + KeyPrefix prefix_a; |
| + KeyPrefix prefix_b; |
| + const char* ptr_a = KeyPrefix::Decode(a.begin(), a.end(), &prefix_a); |
| + const char* ptr_b = KeyPrefix::Decode(b.begin(), b.end(), &prefix_b); |
| + DCHECK(ptr_a); |
| + DCHECK(ptr_b); |
| + DCHECK(prefix_a.database_id_); |
| + DCHECK(prefix_a.object_store_id_); |
| + DCHECK_GE(prefix_a.index_id_, kMinimumIndexId); |
| + DCHECK(prefix_b.database_id_); |
| + DCHECK(prefix_b.object_store_id_); |
| + DCHECK_GE(prefix_b.index_id_, kMinimumIndexId); |
| + DCHECK(ptr_a != a.end()); |
| + DCHECK(ptr_b != b.end()); |
| + // Prefixes are not compared - it is assumed this was already done. |
| + DCHECK(!prefix_a.Compare(prefix_b)); |
| + |
| + // index key |
| + int result = CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok); |
| + if (!ok || result) |
| + return result; |
| + if (ignore_duplicates) |
| + return 0; |
| + |
| + // sequence number [optional] |
| + int64_t sequence_number_a = -1; |
| + int64_t sequence_number_b = -1; |
| + if (ptr_a != a.end()) |
| + ptr_a = DecodeVarInt(ptr_a, a.end(), sequence_number_a); |
| + if (ptr_b != b.end()) |
| + ptr_b = DecodeVarInt(ptr_b, b.end(), sequence_number_b); |
| + |
| + // primary key [optional] |
| + if (!ptr_a || !ptr_b) |
| + return 0; |
| + if (ptr_a == a.end() && ptr_b == b.end()) |
| + return 0; |
| + if (ptr_a == a.end()) |
| + return -1; |
| + if (ptr_b == b.end()) |
| + return 1; |
| + |
| + result = CompareEncodedIDBKeys(ptr_a, a.end(), ptr_b, b.end(), ok); |
| + if (!ok || result) |
| + return result; |
| + |
| + return CompareInts(sequence_number_a, sequence_number_b); |
| +} |
| + |
| +int Compare(const LevelDBSlice& a, |
| + const LevelDBSlice& b, |
| + bool index_keys, |
| + bool& ok) { |
| + const char* ptr_a = a.begin(); |
| + const char* ptr_b = b.begin(); |
| + const char* end_a = a.end(); |
| + const char* end_b = b.end(); |
| + |
| + KeyPrefix prefix_a; |
| + KeyPrefix prefix_b; |
| + |
| + ptr_a = KeyPrefix::Decode(ptr_a, end_a, &prefix_a); |
| + ptr_b = KeyPrefix::Decode(ptr_b, end_b, &prefix_b); |
| + DCHECK(ptr_a); |
| + DCHECK(ptr_b); |
| + if (!ptr_a || !ptr_b) { |
| + ok = false; |
| + return 0; |
| + } |
| + |
| + ok = true; |
| + if (int x = prefix_a.Compare(prefix_b)) |
| + return x; |
| + |
| + if (prefix_a.type() == KeyPrefix::GLOBAL_METADATA) { |
| + DCHECK(ptr_a != end_a); |
| + DCHECK(ptr_b != end_b); |
| + |
| + unsigned char type_byte_a = *ptr_a++; |
| + unsigned char type_byte_b = *ptr_b++; |
| + |
| + if (int x = type_byte_a - type_byte_b) |
| + return x; |
| + if (type_byte_a < kMaxSimpleGlobalMetaDataTypeByte) |
| + return 0; |
| + |
| + const bool ignore_duplicates = false; |
| + if (type_byte_a == kDatabaseFreeListTypeByte) |
| + return Compare<DatabaseFreeListKey>(a, b, ignore_duplicates, ok); |
| + if (type_byte_a == kDatabaseNameTypeByte) |
| + return Compare<DatabaseNameKey>(a, b, ignore_duplicates, ok); |
| + } |
| + |
| + if (prefix_a.type() == KeyPrefix::DATABASE_METADATA) { |
| + DCHECK(ptr_a != end_a); |
| + DCHECK(ptr_b != end_b); |
|
jamesr
2013/05/22 18:59:44
_NE
|
| + |
| + unsigned char type_byte_a = *ptr_a++; |
| + unsigned char type_byte_b = *ptr_b++; |
| + |
| + if (int x = type_byte_a - type_byte_b) |
| + return x; |
| + if (type_byte_a < DatabaseMetaDataKey::MAX_SIMPLE_METADATA_TYPE) |
| + return 0; |
| + |
| + const bool ignore_duplicates = false; |
| + if (type_byte_a == kObjectStoreMetaDataTypeByte) |
| + return Compare<ObjectStoreMetaDataKey>(a, b, ignore_duplicates, ok); |
| + if (type_byte_a == kIndexMetaDataTypeByte) |
| + return Compare<IndexMetaDataKey>(a, b, ignore_duplicates, ok); |
| + if (type_byte_a == kObjectStoreFreeListTypeByte) |
| + return Compare<ObjectStoreFreeListKey>(a, b, ignore_duplicates, ok); |
| + if (type_byte_a == kIndexFreeListTypeByte) |
| + return Compare<IndexFreeListKey>(a, b, ignore_duplicates, ok); |
| + if (type_byte_a == kObjectStoreNamesTypeByte) |
| + return Compare<ObjectStoreNamesKey>(a, b, ignore_duplicates, ok); |
| + if (type_byte_a == kIndexNamesKeyTypeByte) |
| + return Compare<IndexNamesKey>(a, b, ignore_duplicates, ok); |
| + } |
| + |
| + if (prefix_a.type() == KeyPrefix::OBJECT_STORE_DATA) { |
| + if (ptr_a == end_a && ptr_b == end_b) |
| + return 0; |
| + if (ptr_a == end_a) |
| + return -1; |
| + if (ptr_b == end_b) |
| + return 1; // TODO(jsbell): This case of non-existing user keys should not |
| + // have |
| + // to be handled this way. |
| + |
| + const bool ignore_duplicates = false; |
| + return Compare<ObjectStoreDataKey>(a, b, ignore_duplicates, ok); |
| + } |
| + if (prefix_a.type() == KeyPrefix::EXISTS_ENTRY) { |
| + if (ptr_a == end_a && ptr_b == end_b) |
| + return 0; |
| + if (ptr_a == end_a) |
| + return -1; |
| + if (ptr_b == end_b) |
| + return 1; // TODO(jsbell): This case of non-existing user keys should not |
| + // have |
| + // to be handled this way. |
| + |
| + const bool ignore_duplicates = false; |
| + return Compare<ExistsEntryKey>(a, b, ignore_duplicates, ok); |
| + } |
| + if (prefix_a.type() == KeyPrefix::INDEX_DATA) { |
| + if (ptr_a == end_a && ptr_b == end_b) |
| + return 0; |
| + if (ptr_a == end_a) |
| + return -1; |
| + if (ptr_b == end_b) |
| + return 1; // TODO(jsbell): This case of non-existing user keys should not |
| + // have |
| + // to be handled this way. |
| + |
| + bool ignore_duplicates = index_keys; |
| + return Compare<IndexDataKey>(a, b, ignore_duplicates, ok); |
| + } |
| + |
| + NOTREACHED(); |
| + ok = false; |
| + return 0; |
| +} |
| + |
| +} // namespace |
| + |
| +int Compare(const LevelDBSlice& a, const LevelDBSlice& b, bool index_keys) { |
| + bool ok; |
| + int result = Compare(a, b, index_keys, ok); |
| + DCHECK(ok); |
| + if (!ok) |
| + return 0; |
| + return result; |
| +} |
| + |
| +KeyPrefix::KeyPrefix() |
| + : database_id_(INVALID_TYPE), |
| + object_store_id_(INVALID_TYPE), |
| + index_id_(INVALID_TYPE) {} |
| + |
| +KeyPrefix::KeyPrefix(int64_t database_id) |
| + : database_id_(database_id), object_store_id_(0), index_id_(0) { |
| + DCHECK(KeyPrefix::IsValidDatabaseId(database_id)); |
| +} |
| + |
| +KeyPrefix::KeyPrefix(int64_t database_id, int64_t object_store_id) |
| + : database_id_(database_id), |
| + object_store_id_(object_store_id), |
| + index_id_(0) { |
| + DCHECK(KeyPrefix::IsValidDatabaseId(database_id)); |
| + DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id)); |
| +} |
| + |
| +KeyPrefix::KeyPrefix(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) |
| + : database_id_(database_id), |
| + object_store_id_(object_store_id), |
| + index_id_(index_id) { |
| + DCHECK(KeyPrefix::IsValidDatabaseId(database_id)); |
| + DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id)); |
| + DCHECK(KeyPrefix::IsValidIndexId(index_id)); |
| +} |
| + |
| +KeyPrefix::KeyPrefix(enum Type type, |
| + int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) |
| + : database_id_(database_id), |
| + object_store_id_(object_store_id), |
| + index_id_(index_id) { |
| + DCHECK_EQ(type, INVALID_TYPE); |
| + DCHECK(KeyPrefix::IsValidDatabaseId(database_id)); |
| + DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id)); |
| +} |
| + |
| +KeyPrefix KeyPrefix::CreateWithSpecialIndex(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) { |
| + DCHECK(KeyPrefix::IsValidDatabaseId(database_id)); |
| + DCHECK(KeyPrefix::IsValidObjectStoreId(object_store_id)); |
| + DCHECK(index_id); |
| + return KeyPrefix(INVALID_TYPE, database_id, object_store_id, index_id); |
| +} |
| + |
| +bool KeyPrefix::IsValidDatabaseId(int64_t database_id) { |
| + return (database_id > 0) && (database_id < KeyPrefix::kMaxDatabaseId); |
| +} |
| + |
| +bool KeyPrefix::IsValidObjectStoreId(int64_t object_store_id) { |
| + return (object_store_id > 0) && |
| + (object_store_id < KeyPrefix::kMaxObjectStoreId); |
| +} |
| + |
| +bool KeyPrefix::IsValidIndexId(int64_t index_id) { |
| + return (index_id >= kMinimumIndexId) && (index_id < KeyPrefix::kMaxIndexId); |
| +} |
| + |
| +const char* KeyPrefix::Decode(const char* start, |
| + const char* limit, |
| + KeyPrefix* result) { |
| + if (start == limit) |
| + return 0; |
| + |
| + unsigned char first_byte = *start++; |
| + |
| + int database_id_bytes = ((first_byte >> 5) & 0x7) + 1; |
| + int object_store_id_bytes = ((first_byte >> 2) & 0x7) + 1; |
| + int index_id_bytes = (first_byte & 0x3) + 1; |
| + |
| + if (start + database_id_bytes + object_store_id_bytes + index_id_bytes > |
| + limit) |
| + return 0; |
| + |
| + result->database_id_ = DecodeInt(start, start + database_id_bytes); |
| + start += database_id_bytes; |
| + result->object_store_id_ = DecodeInt(start, start + object_store_id_bytes); |
| + start += object_store_id_bytes; |
| + result->index_id_ = DecodeInt(start, start + index_id_bytes); |
| + start += index_id_bytes; |
| + |
| + return start; |
| +} |
| + |
| +std::vector<char> KeyPrefix::EncodeEmpty() { |
| + const std::vector<char> result(4, 0); |
| + DCHECK(EncodeInternal(0, 0, 0) == std::vector<char>(4, 0)); |
| + return result; |
| +} |
| + |
| +std::vector<char> KeyPrefix::Encode() const { |
| + DCHECK(database_id_ != kInvalidId); |
| + DCHECK(object_store_id_ != kInvalidId); |
| + DCHECK(index_id_ != kInvalidId); |
|
jamesr
2013/05/22 18:59:44
_NE
|
| + return EncodeInternal(database_id_, object_store_id_, index_id_); |
| +} |
| + |
| +std::vector<char> KeyPrefix::EncodeInternal(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) { |
| + std::vector<char> database_id_string = |
| + EncodeIntSafely(database_id, kMaxDatabaseId); |
| + std::vector<char> object_store_id_string = |
| + EncodeIntSafely(object_store_id, kMaxObjectStoreId); |
| + std::vector<char> index_id_string = EncodeIntSafely(index_id, kMaxIndexId); |
| + |
| + DCHECK(database_id_string.size() <= kMaxDatabaseIdSizeBytes); |
| + DCHECK(object_store_id_string.size() <= kMaxObjectStoreIdSizeBytes); |
| + DCHECK(index_id_string.size() <= kMaxIndexIdSizeBytes); |
|
jamesr
2013/05/22 18:59:44
_LE
|
| + |
| + unsigned char first_byte = |
| + (database_id_string.size() - 1) |
| + << (kMaxObjectStoreIdSizeBits + kMaxIndexIdSizeBits) | |
| + (object_store_id_string.size() - 1) << kMaxIndexIdSizeBits | |
| + (index_id_string.size() - 1); |
| + COMPILE_ASSERT(kMaxDatabaseIdSizeBits + kMaxObjectStoreIdSizeBits + |
| + kMaxIndexIdSizeBits == |
| + sizeof(first_byte) * 8, |
| + CANT_ENCODE_IDS); |
| + std::vector<char> ret; |
| + ret.reserve(kDefaultInlineBufferSize); |
| + ret.push_back(first_byte); |
| + ret.insert(ret.end(), database_id_string.begin(), database_id_string.end()); |
| + ret.insert( |
| + ret.end(), object_store_id_string.begin(), object_store_id_string.end()); |
| + ret.insert(ret.end(), index_id_string.begin(), index_id_string.end()); |
| + |
| + DCHECK(ret.size() <= kDefaultInlineBufferSize); |
|
jamesr
2013/05/22 18:59:44
_LE
|
| + return ret; |
| +} |
| + |
| +int KeyPrefix::Compare(const KeyPrefix& other) const { |
| + DCHECK(database_id_ != kInvalidId); |
| + DCHECK(object_store_id_ != kInvalidId); |
| + DCHECK(index_id_ != kInvalidId); |
|
jamesr
2013/05/22 18:59:44
_NE
|
| + |
| + if (database_id_ != other.database_id_) |
| + return CompareInts(database_id_, other.database_id_); |
| + if (object_store_id_ != other.object_store_id_) |
| + return CompareInts(object_store_id_, other.object_store_id_); |
| + if (index_id_ != other.index_id_) |
| + return CompareInts(index_id_, other.index_id_); |
| + return 0; |
| +} |
| + |
| +KeyPrefix::Type KeyPrefix::type() const { |
| + DCHECK(database_id_ != kInvalidId); |
| + DCHECK(object_store_id_ != kInvalidId); |
| + DCHECK(index_id_ != kInvalidId); |
|
jamesr
2013/05/22 18:59:44
_NE
|
| + |
| + if (!database_id_) |
| + return GLOBAL_METADATA; |
| + if (!object_store_id_) |
| + return DATABASE_METADATA; |
| + if (index_id_ == kObjectStoreDataIndexId) |
| + return OBJECT_STORE_DATA; |
| + if (index_id_ == kExistsEntryIndexId) |
| + return EXISTS_ENTRY; |
| + if (index_id_ >= kMinimumIndexId) |
| + return INDEX_DATA; |
| + |
| + NOTREACHED(); |
| + return INVALID_TYPE; |
| +} |
| + |
| +std::vector<char> SchemaVersionKey::Encode() { |
| + std::vector<char> ret = KeyPrefix::EncodeEmpty(); |
| + ret.push_back(kSchemaVersionTypeByte); |
| + return ret; |
| +} |
| + |
| +std::vector<char> MaxDatabaseIdKey::Encode() { |
| + std::vector<char> ret = KeyPrefix::EncodeEmpty(); |
| + ret.push_back(kMaxDatabaseIdTypeByte); |
| + return ret; |
| +} |
| + |
| +std::vector<char> DataVersionKey::Encode() { |
| + std::vector<char> ret = KeyPrefix::EncodeEmpty(); |
| + ret.push_back(kDataVersionTypeByte); |
| + return ret; |
| +} |
| + |
| +DatabaseFreeListKey::DatabaseFreeListKey() : database_id_(-1) {} |
| + |
| +const char* DatabaseFreeListKey::Decode(const char* start, |
| + const char* limit, |
| + DatabaseFreeListKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(!prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kDatabaseFreeListTypeByte); |
| + if (p == limit) |
| + return 0; |
| + return DecodeVarInt(p, limit, result->database_id_); |
| +} |
| + |
| +std::vector<char> DatabaseFreeListKey::Encode(int64_t database_id) { |
| + std::vector<char> ret = KeyPrefix::EncodeEmpty(); |
| + ret.push_back(kDatabaseFreeListTypeByte); |
| + std::vector<char> tmp = EncodeVarInt(database_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> DatabaseFreeListKey::EncodeMaxKey() { |
| + return Encode(INT64_MAX); |
| +} |
| + |
| +int64_t DatabaseFreeListKey::DatabaseId() const { |
| + DCHECK_GE(database_id_, 0); |
| + return database_id_; |
| +} |
| + |
| +int DatabaseFreeListKey::Compare(const DatabaseFreeListKey& other) const { |
| + DCHECK_GE(database_id_, 0); |
| + return CompareInts(database_id_, other.database_id_); |
| +} |
| + |
| +const char* DatabaseNameKey::Decode(const char* start, |
| + const char* limit, |
| + DatabaseNameKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return p; |
| + DCHECK(!prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kDatabaseNameTypeByte); |
| + if (p == limit) |
| + return 0; |
| + p = DecodeStringWithLength(p, limit, result->origin_); |
| + if (!p) |
| + return 0; |
| + return DecodeStringWithLength(p, limit, result->database_name_); |
| +} |
| + |
| +std::vector<char> DatabaseNameKey::Encode(const string16& origin, |
| + const string16& database_name) { |
| + std::vector<char> ret = KeyPrefix::EncodeEmpty(); |
| + ret.push_back(kDatabaseNameTypeByte); |
| + std::vector<char> tmp = EncodeStringWithLength(origin); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + tmp = EncodeStringWithLength(database_name); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> DatabaseNameKey::EncodeMinKeyForOrigin( |
| + const string16& origin) { |
| + return Encode(origin, string16()); |
| +} |
| + |
| +std::vector<char> DatabaseNameKey::EncodeStopKeyForOrigin( |
| + const string16& origin) { |
| + // just after origin in collation order |
| + return EncodeMinKeyForOrigin(origin + base::char16('\x01')); |
| +} |
| + |
| +int DatabaseNameKey::Compare(const DatabaseNameKey& other) { |
| + if (int x = origin_.compare(other.origin_)) |
| + return x; |
| + return database_name_.compare(other.database_name_); |
| +} |
| + |
| +std::vector<char> DatabaseMetaDataKey::Encode(int64_t database_id, |
| + MetaDataType meta_data_type) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(meta_data_type); |
| + return ret; |
| +} |
| + |
| +ObjectStoreMetaDataKey::ObjectStoreMetaDataKey() |
| + : object_store_id_(-1), meta_data_type_(-1) {} |
| + |
| +const char* ObjectStoreMetaDataKey::Decode(const char* start, |
| + const char* limit, |
| + ObjectStoreMetaDataKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kObjectStoreMetaDataTypeByte); |
| + if (p == limit) |
| + return 0; |
| + p = DecodeVarInt(p, limit, result->object_store_id_); |
| + if (!p) |
| + return 0; |
| + DCHECK(result->object_store_id_); |
| + if (p == limit) |
| + return 0; |
| + return DecodeByte(p, limit, result->meta_data_type_); |
| +} |
| + |
| +std::vector<char> ObjectStoreMetaDataKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + unsigned char meta_data_type) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(kObjectStoreMetaDataTypeByte); |
| + std::vector<char> tmp = EncodeVarInt(object_store_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + ret.push_back(meta_data_type); |
| + return ret; |
| +} |
| + |
| +std::vector<char> ObjectStoreMetaDataKey::EncodeMaxKey(int64_t database_id) { |
| + return Encode(database_id, INT64_MAX, kObjectMetaDataTypeMaximum); |
| +} |
| + |
| +std::vector<char> ObjectStoreMetaDataKey::EncodeMaxKey( |
| + int64_t database_id, |
| + int64_t object_store_id) { |
| + return Encode(database_id, object_store_id, kObjectMetaDataTypeMaximum); |
| +} |
| + |
| +int64_t ObjectStoreMetaDataKey::ObjectStoreId() const { |
| + DCHECK_GE(object_store_id_, 0); |
| + return object_store_id_; |
| +} |
| +unsigned char ObjectStoreMetaDataKey::MetaDataType() const { |
| + return meta_data_type_; |
| +} |
| + |
| +int ObjectStoreMetaDataKey::Compare(const ObjectStoreMetaDataKey& other) { |
| + DCHECK_GE(object_store_id_, 0); |
| + if (int x = CompareInts(object_store_id_, other.object_store_id_)) |
| + return x; |
| + int64_t result = meta_data_type_ - other.meta_data_type_; |
| + if (result < 0) |
| + return -1; |
| + return (result > 0) ? 1 : result; |
| +} |
| + |
| +IndexMetaDataKey::IndexMetaDataKey() |
| + : object_store_id_(-1), index_id_(-1), meta_data_type_(0) {} |
| + |
| +const char* IndexMetaDataKey::Decode(const char* start, |
| + const char* limit, |
| + IndexMetaDataKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kIndexMetaDataTypeByte); |
| + if (p == limit) |
| + return 0; |
| + p = DecodeVarInt(p, limit, result->object_store_id_); |
| + if (!p) |
| + return 0; |
| + p = DecodeVarInt(p, limit, result->index_id_); |
| + if (!p) |
| + return 0; |
| + if (p == limit) |
| + return 0; |
| + return DecodeByte(p, limit, result->meta_data_type_); |
| +} |
| + |
| +std::vector<char> IndexMetaDataKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id, |
| + unsigned char meta_data_type) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(kIndexMetaDataTypeByte); |
| + std::vector<char> tmp = EncodeVarInt(object_store_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + tmp = EncodeVarInt(index_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + tmp = EncodeByte(meta_data_type); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> IndexMetaDataKey::EncodeMaxKey(int64_t database_id, |
| + int64_t object_store_id) { |
| + return Encode( |
| + database_id, object_store_id, INT64_MAX, kIndexMetaDataTypeMaximum); |
| +} |
| + |
| +std::vector<char> IndexMetaDataKey::EncodeMaxKey(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) { |
| + return Encode( |
| + database_id, object_store_id, index_id, kIndexMetaDataTypeMaximum); |
| +} |
| + |
| +int IndexMetaDataKey::Compare(const IndexMetaDataKey& other) { |
| + DCHECK_GE(object_store_id_, 0); |
| + DCHECK_GE(index_id_, 0); |
| + |
| + if (int x = CompareInts(object_store_id_, other.object_store_id_)) |
| + return x; |
| + if (int x = CompareInts(index_id_, other.index_id_)) |
| + return x; |
| + return meta_data_type_ - other.meta_data_type_; |
| +} |
| + |
| +int64_t IndexMetaDataKey::IndexId() const { |
| + DCHECK_GE(index_id_, 0); |
| + return index_id_; |
| +} |
| + |
| +ObjectStoreFreeListKey::ObjectStoreFreeListKey() : object_store_id_(-1) {} |
| + |
| +const char* ObjectStoreFreeListKey::Decode(const char* start, |
| + const char* limit, |
| + ObjectStoreFreeListKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kObjectStoreFreeListTypeByte); |
| + if (p == limit) |
| + return 0; |
| + return DecodeVarInt(p, limit, result->object_store_id_); |
| +} |
| + |
| +std::vector<char> ObjectStoreFreeListKey::Encode(int64_t database_id, |
| + int64_t object_store_id) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(kObjectStoreFreeListTypeByte); |
| + std::vector<char> tmp = EncodeVarInt(object_store_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> ObjectStoreFreeListKey::EncodeMaxKey(int64_t database_id) { |
| + return Encode(database_id, INT64_MAX); |
| +} |
| + |
| +int64_t ObjectStoreFreeListKey::ObjectStoreId() const { |
| + DCHECK_GE(object_store_id_, 0); |
| + return object_store_id_; |
| +} |
| + |
| +int ObjectStoreFreeListKey::Compare(const ObjectStoreFreeListKey& other) { |
| + // TODO(jsbell): It may seem strange that we're not comparing database id's, |
| + // but that comparison will have been made earlier. |
| + // We should probably make this more clear, though... |
| + DCHECK_GE(object_store_id_, 0); |
| + return CompareInts(object_store_id_, other.object_store_id_); |
| +} |
| + |
| +IndexFreeListKey::IndexFreeListKey() : object_store_id_(-1), index_id_(-1) {} |
| + |
| +const char* IndexFreeListKey::Decode(const char* start, |
| + const char* limit, |
| + IndexFreeListKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kIndexFreeListTypeByte); |
| + if (p == limit) |
| + return 0; |
| + p = DecodeVarInt(p, limit, result->object_store_id_); |
| + if (!p) |
| + return 0; |
| + return DecodeVarInt(p, limit, result->index_id_); |
| +} |
| + |
| +std::vector<char> IndexFreeListKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(kIndexFreeListTypeByte); |
| + std::vector<char> tmp = EncodeVarInt(object_store_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + tmp = EncodeVarInt(index_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> IndexFreeListKey::EncodeMaxKey(int64_t database_id, |
| + int64_t object_store_id) { |
| + return Encode(database_id, object_store_id, INT64_MAX); |
| +} |
| + |
| +int IndexFreeListKey::Compare(const IndexFreeListKey& other) { |
| + DCHECK_GE(object_store_id_, 0); |
| + DCHECK_GE(index_id_, 0); |
| + if (int x = CompareInts(object_store_id_, other.object_store_id_)) |
| + return x; |
| + return CompareInts(index_id_, other.index_id_); |
| +} |
| + |
| +int64_t IndexFreeListKey::ObjectStoreId() const { |
| + DCHECK_GE(object_store_id_, 0); |
| + return object_store_id_; |
| +} |
| + |
| +int64_t IndexFreeListKey::IndexId() const { |
| + DCHECK_GE(index_id_, 0); |
| + return index_id_; |
| +} |
| + |
| +// TODO(jsbell): We never use this to look up object store ids, because a |
| +// mapping |
| +// is kept in the IndexedDBDatabaseImpl. Can the mapping become unreliable? |
| +// Can we remove this? |
|
jamesr
2013/05/22 18:59:44
comments wrapped oddly
jsbell
2013/05/22 22:21:14
Done.
|
| +const char* ObjectStoreNamesKey::Decode(const char* start, |
| + const char* limit, |
| + ObjectStoreNamesKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kObjectStoreNamesTypeByte); |
| + return DecodeStringWithLength(p, limit, result->object_store_name_); |
| +} |
| + |
| +std::vector<char> ObjectStoreNamesKey::Encode( |
| + int64_t database_id, |
| + const string16& object_store_name) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(kObjectStoreNamesTypeByte); |
| + std::vector<char> tmp = EncodeStringWithLength(object_store_name); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +int ObjectStoreNamesKey::Compare(const ObjectStoreNamesKey& other) { |
| + return object_store_name_.compare(other.object_store_name_); |
| +} |
| + |
| +IndexNamesKey::IndexNamesKey() : object_store_id_(-1) {} |
| + |
| +// TODO(jsbell): We never use this to look up index ids, because a mapping |
| +// is kept at a higher level. |
| +const char* IndexNamesKey::Decode(const char* start, |
| + const char* limit, |
| + IndexNamesKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(!prefix.object_store_id_); |
| + DCHECK(!prefix.index_id_); |
| + if (p == limit) |
| + return 0; |
| + unsigned char type_byte = 0; |
| + p = DecodeByte(p, limit, type_byte); |
| + DCHECK_EQ(type_byte, kIndexNamesKeyTypeByte); |
| + if (p == limit) |
| + return 0; |
| + p = DecodeVarInt(p, limit, result->object_store_id_); |
| + if (!p) |
| + return 0; |
| + return DecodeStringWithLength(p, limit, result->index_name_); |
| +} |
| + |
| +std::vector<char> IndexNamesKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + const string16& index_name) { |
| + KeyPrefix prefix(database_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.push_back(kIndexNamesKeyTypeByte); |
| + std::vector<char> tmp = EncodeVarInt(object_store_id); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + tmp = EncodeStringWithLength(index_name); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + return ret; |
| +} |
| + |
| +int IndexNamesKey::Compare(const IndexNamesKey& other) { |
| + DCHECK_GE(object_store_id_, 0); |
| + if (int x = CompareInts(object_store_id_, other.object_store_id_)) |
| + return x; |
| + return index_name_.compare(other.index_name_); |
| +} |
| + |
| +ObjectStoreDataKey::ObjectStoreDataKey() {} |
| +ObjectStoreDataKey::~ObjectStoreDataKey() {} |
| + |
| +const char* ObjectStoreDataKey::Decode(const char* start, |
| + const char* end, |
| + ObjectStoreDataKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, end, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(prefix.object_store_id_); |
| + DCHECK_EQ(prefix.index_id_, kSpecialIndexNumber); |
| + if (p == end) |
| + return 0; |
| + return ExtractEncodedIDBKey(p, end, &result->encoded_user_key_); |
| +} |
| + |
| +std::vector<char> ObjectStoreDataKey::Encode( |
| + int64_t database_id, |
| + int64_t object_store_id, |
| + const std::vector<char> encoded_user_key) { |
| + KeyPrefix prefix(KeyPrefix::CreateWithSpecialIndex( |
| + database_id, object_store_id, kSpecialIndexNumber)); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.insert(ret.end(), encoded_user_key.begin(), encoded_user_key.end()); |
| + |
| + return ret; |
| +} |
| + |
| +std::vector<char> ObjectStoreDataKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + const IndexedDBKey& user_key) { |
| + return Encode(database_id, object_store_id, EncodeIDBKey(user_key)); |
| +} |
| + |
| +int ObjectStoreDataKey::Compare(const ObjectStoreDataKey& other, bool& ok) { |
| + return CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok); |
| +} |
| + |
| +scoped_ptr<IndexedDBKey> ObjectStoreDataKey::user_key() const { |
| + scoped_ptr<IndexedDBKey> key; |
| + DecodeIDBKey(&encoded_user_key_[0], |
| + &encoded_user_key_[0] + encoded_user_key_.size(), |
| + &key); |
| + return key.Pass(); |
| +} |
| + |
| +const int64_t ObjectStoreDataKey::kSpecialIndexNumber = kObjectStoreDataIndexId; |
| + |
| +ExistsEntryKey::ExistsEntryKey() {} |
| +ExistsEntryKey::~ExistsEntryKey() {} |
| + |
| +const char* ExistsEntryKey::Decode(const char* start, |
| + const char* end, |
| + ExistsEntryKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, end, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(prefix.object_store_id_); |
| + DCHECK_EQ(prefix.index_id_, kSpecialIndexNumber); |
| + if (p == end) |
| + return 0; |
| + return ExtractEncodedIDBKey(p, end, &result->encoded_user_key_); |
| +} |
| + |
| +std::vector<char> ExistsEntryKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + const std::vector<char>& encoded_key) { |
| + KeyPrefix prefix(KeyPrefix::CreateWithSpecialIndex( |
| + database_id, object_store_id, kSpecialIndexNumber)); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.insert(ret.end(), encoded_key.begin(), encoded_key.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> ExistsEntryKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + const IndexedDBKey& user_key) { |
| + return Encode(database_id, object_store_id, EncodeIDBKey(user_key)); |
| +} |
| + |
| +int ExistsEntryKey::Compare(const ExistsEntryKey& other, bool& ok) { |
| + return CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok); |
| +} |
| + |
| +scoped_ptr<IndexedDBKey> ExistsEntryKey::user_key() const { |
| + scoped_ptr<IndexedDBKey> key; |
| + DecodeIDBKey(&encoded_user_key_[0], |
| + &encoded_user_key_[0] + encoded_user_key_.size(), |
| + &key); |
| + return key.Pass(); |
| +} |
| + |
| +const int64_t ExistsEntryKey::kSpecialIndexNumber = kExistsEntryIndexId; |
| + |
| +IndexDataKey::IndexDataKey() |
| + : database_id_(-1), |
| + object_store_id_(-1), |
| + index_id_(-1), |
| + sequence_number_(-1) {} |
| + |
| +IndexDataKey::~IndexDataKey() {} |
| + |
| +const char* IndexDataKey::Decode(const char* start, |
| + const char* limit, |
| + IndexDataKey* result) { |
| + KeyPrefix prefix; |
| + const char* p = KeyPrefix::Decode(start, limit, &prefix); |
| + if (!p) |
| + return 0; |
| + DCHECK(prefix.database_id_); |
| + DCHECK(prefix.object_store_id_); |
| + DCHECK_GE(prefix.index_id_, kMinimumIndexId); |
| + result->database_id_ = prefix.database_id_; |
| + result->object_store_id_ = prefix.object_store_id_; |
| + result->index_id_ = prefix.index_id_; |
| + result->sequence_number_ = -1; |
| + result->encoded_primary_key_ = MinIDBKey(); |
| + |
| + p = ExtractEncodedIDBKey(p, limit, &result->encoded_user_key_); |
| + if (!p) |
| + return 0; |
| + |
| + // [optional] sequence number |
| + if (p == limit) |
| + return p; |
| + p = DecodeVarInt(p, limit, result->sequence_number_); |
| + if (!p) |
| + return 0; |
| + |
| + // [optional] primary key |
| + if (p == limit) |
| + return p; |
| + p = ExtractEncodedIDBKey(p, limit, &result->encoded_primary_key_); |
| + if (!p) |
| + return 0; |
| + |
| + return p; |
| +} |
| + |
| +std::vector<char> IndexDataKey::Encode( |
| + int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id, |
| + const std::vector<char>& encoded_user_key, |
| + const std::vector<char>& encoded_primary_key, |
| + int64_t sequence_number) { |
| + KeyPrefix prefix(database_id, object_store_id, index_id); |
| + std::vector<char> ret = prefix.Encode(); |
| + ret.insert(ret.end(), encoded_user_key.begin(), encoded_user_key.end()); |
| + std::vector<char> tmp = EncodeVarInt(sequence_number); |
| + ret.insert(ret.end(), tmp.begin(), tmp.end()); |
| + ret.insert(ret.end(), encoded_primary_key.begin(), encoded_primary_key.end()); |
| + return ret; |
| +} |
| + |
| +std::vector<char> IndexDataKey::Encode(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id, |
| + const IndexedDBKey& user_key) { |
| + return Encode(database_id, |
| + object_store_id, |
| + index_id, |
| + EncodeIDBKey(user_key), |
| + MinIDBKey()); |
| +} |
| + |
| +std::vector<char> IndexDataKey::EncodeMinKey(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) { |
| + return Encode( |
| + database_id, object_store_id, index_id, MinIDBKey(), MinIDBKey()); |
| +} |
| + |
| +std::vector<char> IndexDataKey::EncodeMaxKey(int64_t database_id, |
| + int64_t object_store_id, |
| + int64_t index_id) { |
| + return Encode(database_id, |
| + object_store_id, |
| + index_id, |
| + MaxIDBKey(), |
| + MaxIDBKey(), |
| + INT64_MAX); |
| +} |
| + |
| +int IndexDataKey::Compare(const IndexDataKey& other, |
| + bool ignore_duplicates, |
| + bool& ok) { |
| + DCHECK_GE(database_id_, 0); |
| + DCHECK_GE(object_store_id_, 0); |
| + DCHECK_GE(index_id_, 0); |
| + int result = |
| + CompareEncodedIDBKeys(encoded_user_key_, other.encoded_user_key_, ok); |
| + if (!ok || result) |
| + return result; |
| + if (ignore_duplicates) |
| + return 0; |
| + result = CompareEncodedIDBKeys( |
| + encoded_primary_key_, other.encoded_primary_key_, ok); |
| + if (!ok || result) |
| + return result; |
| + return CompareInts(sequence_number_, other.sequence_number_); |
| +} |
| + |
| +int64_t IndexDataKey::DatabaseId() const { |
| + DCHECK_GE(database_id_, 0); |
| + return database_id_; |
| +} |
| + |
| +int64_t IndexDataKey::ObjectStoreId() const { |
| + DCHECK_GE(object_store_id_, 0); |
| + return object_store_id_; |
| +} |
| + |
| +int64_t IndexDataKey::IndexId() const { |
| + DCHECK_GE(index_id_, 0); |
| + return index_id_; |
| +} |
| + |
| +scoped_ptr<IndexedDBKey> IndexDataKey::user_key() const { |
| + scoped_ptr<IndexedDBKey> key; |
| + DecodeIDBKey(&encoded_user_key_[0], |
| + &encoded_user_key_[0] + encoded_user_key_.size(), |
| + &key); |
| + return key.Pass(); |
| +} |
| + |
| +scoped_ptr<IndexedDBKey> IndexDataKey::primary_key() const { |
| + scoped_ptr<IndexedDBKey> key; |
| + DecodeIDBKey(&encoded_primary_key_[0], |
| + &encoded_primary_key_[0] + encoded_primary_key_.size(), |
| + &key); |
| + return key.Pass(); |
| +} |
| + |
| +} // namespace content |