Commit 1acdbc65 authored by Victor Costan's avatar Victor Costan Committed by Commit Bot

IndexedDB: Refactor value wrapping code.

This is extracted from the WASM + IndexedDB code into a separate CL, for
better reviewability. Serializing WASM modules will add a format to the
value-wrapping logic, pushing the total complexity to the point where
the benefits of having clearly separated lifecycle steps for
IDBValueWrapper exceed the benefits of having a terse API.

Therefore, this CL introduces a clear separation between the stages of
an IDBValueWrapper's lifecycle, which are:
1) Cloning (a structured clone may be needed to compute index values)
2) Wrapping
3) Extracting information for the backend -- the data bytes and the Blob list

The CL also introduces logic for including byte vectors in wrappers,
which will be needed for the V8 snapshot version -- details in
https://docs.google.com/document/d/1IgUeRLza0WC3uAzXxla5rH1bs5jUHKbLjsfU--zdYag

Last, this CL makes it possible to call
IDBValueWrapper::WrapIfBiggerThan() multiple times, which will come in
handy for the new wrapping format -- details in
https://crrev.com/c/709596/17/third_party/WebKit/Source/modules/indexeddb/IDBValueWrapping.cpp


Bug: 719007
Change-Id: Ie74ddfd4ec849d4777ab9a3af5dc254fc5b14837
Reviewed-on: https://chromium-review.googlesource.com/804969
Commit-Queue: Victor Costan <pwnall@chromium.org>
Reviewed-by: default avatarJoshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#522369}
parent 7ebea99b
......@@ -529,13 +529,13 @@ IDBRequest* IDBObjectStore::put(ScriptState* script_state,
IDBRequest* request = IDBRequest::Create(
script_state, source, transaction_.Get(), std::move(metrics));
value_wrapper.DoneCloning();
value_wrapper.WrapIfBiggerThan(IDBValueWrapper::kWrapThreshold);
value_wrapper.ExtractBlobDataHandles(request->transit_blob_handles());
request->transit_blob_handles() = value_wrapper.TakeBlobDataHandles();
BackendDB()->Put(
transaction_->Id(), Id(), WebData(value_wrapper.ExtractWireBytes()),
value_wrapper.WrappedBlobInfo(), key,
static_cast<WebIDBPutMode>(put_mode),
transaction_->Id(), Id(), WebData(value_wrapper.TakeWireBytes()),
value_wrapper.TakeBlobInfo(), key, static_cast<WebIDBPutMode>(put_mode),
request->CreateWebCallbacks().release(), index_ids, index_keys);
return request;
......
......@@ -296,8 +296,8 @@ class MODULES_EXPORT IDBRequest : public EventTargetWithInlineData,
// (which hangs onto the BlobDataHandle) may get garbage-collected. IDBRequest
// needs to hang onto the BlobDataHandle as well, to avoid having the
// browser-side Blob get destroyed before the IndexedDB request is processed.
inline Vector<scoped_refptr<BlobDataHandle>>* transit_blob_handles() {
return &transit_blob_handles_;
inline Vector<scoped_refptr<BlobDataHandle>>& transit_blob_handles() {
return transit_blob_handles_;
}
#if DCHECK_IS_ON()
......
......@@ -28,19 +28,20 @@ scoped_refptr<IDBValue> CreateIDBValueForTesting(v8::Isolate* isolate,
IDBValueWrapper wrapper(isolate, v8_array,
SerializedScriptValue::SerializeOptions::kSerialize,
non_throwable_exception_state);
wrapper.DoneCloning();
wrapper.WrapIfBiggerThan(create_wrapped_value ? 0 : 1024 * element_count);
std::unique_ptr<Vector<scoped_refptr<BlobDataHandle>>> blob_data_handles =
std::make_unique<Vector<scoped_refptr<BlobDataHandle>>>();
wrapper.ExtractBlobDataHandles(blob_data_handles.get());
Vector<WebBlobInfo>& blob_infos = wrapper.WrappedBlobInfo();
scoped_refptr<SharedBuffer> wrapped_marker_buffer =
wrapper.ExtractWireBytes();
Vector<scoped_refptr<BlobDataHandle>> blob_data_handles =
wrapper.TakeBlobDataHandles();
Vector<WebBlobInfo> blob_infos = wrapper.TakeBlobInfo();
scoped_refptr<SharedBuffer> wrapped_marker_buffer = wrapper.TakeWireBytes();
IDBKey* key = IDBKey::CreateNumber(42.0);
IDBKeyPath key_path(String("primaryKey"));
scoped_refptr<IDBValue> idb_value = IDBValue::Create(
std::move(wrapped_marker_buffer), std::move(blob_data_handles),
std::move(wrapped_marker_buffer),
std::make_unique<Vector<scoped_refptr<BlobDataHandle>>>(
blob_data_handles),
std::make_unique<Vector<WebBlobInfo>>(blob_infos), key, key_path);
DCHECK_EQ(create_wrapped_value,
......
......@@ -13,7 +13,6 @@
#include "modules/indexeddb/IDBRequest.h"
#include "modules/indexeddb/IDBValue.h"
#include "platform/blob/BlobData.h"
#include "platform/wtf/text/StringView.h"
#include "platform/wtf/text/WTFString.h"
namespace blink {
......@@ -42,16 +41,15 @@ namespace {
// The SSV format version whose encoding hole is (ab)used for wrapping.
const static uint8_t kRequiresProcessingSSVPseudoVersion = 17;
// Identifies IndexedDB values that were wrapped in Blobs. The wrapper has the
// following format:
// SSV processing command replacing the SSV data bytes with a Blob's contents.
//
// 1) 0xFF - kVersionTag
// 2) 0x11 - kRequiresProcessingSSVPseudoVersion
// 3) 0x01 - kBlobWrappedValue
// 3) 0x01 - kReplaceWithBlob
// 4) varint - Blob size
// 5) varint - the offset of the SSV-wrapping Blob in the IDBValue list of Blobs
// (should always be the last Blob)
const static uint8_t kBlobWrappedValue = 1;
const static uint8_t kReplaceWithBlob = 1;
} // namespace
......@@ -68,8 +66,9 @@ IDBValueWrapper::IDBValueWrapper(
serialized_value_ = SerializedScriptValue::Serialize(isolate, value, options,
exception_state);
if (serialized_value_)
if (serialized_value_) {
original_data_length_ = serialized_value_->DataLengthInBytes();
}
#if DCHECK_IS_ON()
if (exception_state.HadException())
had_exception_ = true;
......@@ -82,9 +81,9 @@ IDBValueWrapper::~IDBValueWrapper() {}
void IDBValueWrapper::Clone(ScriptState* script_state, ScriptValue* clone) {
#if DCHECK_IS_ON()
DCHECK(!had_exception_) << __FUNCTION__
DCHECK(!had_exception_) << __func__
<< " called on wrapper with serialization exception";
DCHECK(!wrap_called_) << "Clone() called after WrapIfBiggerThan()";
DCHECK(!done_cloning_) << __func__ << " called after DoneCloning()";
#endif // DCHECK_IS_ON()
bool read_wasm_from_stream = true;
......@@ -95,7 +94,8 @@ void IDBValueWrapper::Clone(ScriptState* script_state, ScriptValue* clone) {
&blob_info_, read_wasm_from_stream);
}
void IDBValueWrapper::WriteVarint(unsigned value, Vector<char>& output) {
// static
void IDBValueWrapper::WriteVarInt(unsigned value, Vector<char>& output) {
// Writes an unsigned integer as a base-128 varint.
// The number is written, 7 bits at a time, from the least significant to
// the most significant 7 bits. Each byte, except the last, has the MSB set.
......@@ -107,22 +107,39 @@ void IDBValueWrapper::WriteVarint(unsigned value, Vector<char>& output) {
output.back() &= 0x7F;
}
bool IDBValueWrapper::WrapIfBiggerThan(unsigned max_bytes) {
// static
void IDBValueWrapper::WriteBytes(const Vector<uint8_t>& bytes,
Vector<char>& output) {
IDBValueWrapper::WriteVarInt(bytes.size(), output);
output.Append(bytes.data(), bytes.size());
}
void IDBValueWrapper::DoneCloning() {
#if DCHECK_IS_ON()
DCHECK(!had_exception_) << __FUNCTION__
DCHECK(!had_exception_) << __func__
<< " called on wrapper with serialization exception";
DCHECK(!wrap_called_) << __FUNCTION__ << " called twice on the same wrapper";
wrap_called_ = true;
DCHECK(!done_cloning_) << __func__ << " called twice";
done_cloning_ = true;
#endif // DCHECK_IS_ON()
StringView wire_data = serialized_value_->GetWireData();
DCHECK(wire_data.Is8Bit());
unsigned wire_data_size = wire_data.length();
if (wire_data_size <= max_bytes) {
wire_bytes_.ReserveInitialCapacity(wire_data_size);
wire_bytes_.Append(wire_data.Characters8(), wire_data_size);
wire_data_ = serialized_value_->GetWireData();
DCHECK(wire_data_.Is8Bit());
for (const auto& kvp : serialized_value_->BlobDataHandles())
blob_handles_.push_back(std::move(kvp.value));
}
bool IDBValueWrapper::WrapIfBiggerThan(unsigned max_bytes) {
#if DCHECK_IS_ON()
DCHECK(done_cloning_) << __func__ << " called before DoneCloning()";
DCHECK(owns_blob_handles_)
<< __func__ << " called after TakeBlobDataHandles()";
DCHECK(owns_blob_info_) << __func__ << " called after TakeBlobInfo()";
DCHECK(owns_wire_bytes_) << __func__ << " called after TakeWireBytes()";
#endif // DCHECK_IS_ON()
unsigned wire_data_size = wire_data_.length();
if (wire_data_size <= max_bytes)
return false;
}
// TODO(pwnall): The MIME type should probably be an atomic string.
String mime_type(kWrapMimeType);
......@@ -130,37 +147,48 @@ bool IDBValueWrapper::WrapIfBiggerThan(unsigned max_bytes) {
// Blob::Create to avoid a buffer copy.
std::unique_ptr<BlobData> wrapper_blob_data = BlobData::Create();
wrapper_blob_data->SetContentType(String(kWrapMimeType));
wrapper_blob_data->AppendBytes(wire_data.Characters8(), wire_data_size);
wrapper_handle_ =
wrapper_blob_data->AppendBytes(wire_data_.Characters8(), wire_data_size);
scoped_refptr<BlobDataHandle> wrapper_handle =
BlobDataHandle::Create(std::move(wrapper_blob_data), wire_data_size);
blob_info_.emplace_back(wrapper_handle_->Uuid(), wrapper_handle_->GetType(),
blob_info_.emplace_back(wrapper_handle->Uuid(), wrapper_handle->GetType(),
wire_data_size);
wire_bytes_.clear();
wire_bytes_.push_back(kVersionTag);
wire_bytes_.push_back(kRequiresProcessingSSVPseudoVersion);
wire_bytes_.push_back(kBlobWrappedValue);
IDBValueWrapper::WriteVarint(wrapper_handle_->size(), wire_bytes_);
IDBValueWrapper::WriteVarint(serialized_value_->BlobDataHandles().size(),
wire_bytes_);
blob_handles_.push_back(std::move(wrapper_handle));
wire_data_buffer_.clear();
wire_data_buffer_.push_back(kVersionTag);
wire_data_buffer_.push_back(kRequiresProcessingSSVPseudoVersion);
wire_data_buffer_.push_back(kReplaceWithBlob);
IDBValueWrapper::WriteVarInt(wire_data_size, wire_data_buffer_);
IDBValueWrapper::WriteVarInt(serialized_value_->BlobDataHandles().size(),
wire_data_buffer_);
wire_data_ = StringView(wire_data_buffer_.data(), wire_data_buffer_.size());
DCHECK(!wire_data_buffer_.IsEmpty());
DCHECK(wire_data_.Is8Bit());
return true;
}
void IDBValueWrapper::ExtractBlobDataHandles(
Vector<scoped_refptr<BlobDataHandle>>* blob_data_handles) {
for (const auto& kvp : serialized_value_->BlobDataHandles())
blob_data_handles->push_back(kvp.value);
if (wrapper_handle_)
blob_data_handles->push_back(std::move(wrapper_handle_));
}
scoped_refptr<SharedBuffer> IDBValueWrapper::ExtractWireBytes() {
scoped_refptr<SharedBuffer> IDBValueWrapper::TakeWireBytes() {
#if DCHECK_IS_ON()
DCHECK(!had_exception_) << __FUNCTION__
<< " called on wrapper with serialization exception";
DCHECK(done_cloning_) << __func__ << " called before DoneCloning()";
DCHECK(owns_wire_bytes_) << __func__ << " called twice";
owns_wire_bytes_ = false;
#endif // DCHECK_IS_ON()
return SharedBuffer::AdoptVector(wire_bytes_);
if (wire_data_buffer_.IsEmpty()) {
// The wire bytes are coming directly from the SSV's GetWireData() call.
DCHECK_EQ(wire_data_.Characters8(),
serialized_value_->GetWireData().Characters8());
DCHECK_EQ(wire_data_.length(), serialized_value_->GetWireData().length());
return SharedBuffer::Create(wire_data_.Characters8(),
static_cast<size_t>(wire_data_.length()));
}
// The wire bytes are coming from wire_data_buffer_, so we can avoid a copy.
DCHECK_EQ(wire_data_buffer_.data(),
reinterpret_cast<const char*>(wire_data_.Characters8()));
DCHECK_EQ(wire_data_buffer_.size(), wire_data_.length());
return SharedBuffer::AdoptVector(wire_data_buffer_);
}
IDBValueUnwrapper::IDBValueUnwrapper() {
......@@ -176,7 +204,7 @@ bool IDBValueUnwrapper::IsWrapped(IDBValue* value) {
return header[0] == kVersionTag &&
header[1] == kRequiresProcessingSSVPseudoVersion &&
header[2] == kBlobWrappedValue;
header[2] == kReplaceWithBlob;
}
bool IDBValueUnwrapper::IsWrapped(
......@@ -225,11 +253,11 @@ bool IDBValueUnwrapper::Parse(IDBValue* value) {
end_ = data + value->data_->size();
current_ = data + 3;
if (!ReadVarint(blob_size_))
if (!ReadVarInt(blob_size_))
return Reset();
unsigned blob_offset;
if (!ReadVarint(blob_offset))
if (!ReadVarInt(blob_offset))
return Reset();
size_t value_blob_count = value->blob_data_->size();
......@@ -249,7 +277,7 @@ scoped_refptr<BlobDataHandle> IDBValueUnwrapper::WrapperBlobHandle() {
return std::move(blob_handle_);
}
bool IDBValueUnwrapper::ReadVarint(unsigned& value) {
bool IDBValueUnwrapper::ReadVarInt(unsigned& value) {
value = 0;
unsigned shift = 0;
bool has_another_byte;
......@@ -269,6 +297,22 @@ bool IDBValueUnwrapper::ReadVarint(unsigned& value) {
return true;
}
bool IDBValueUnwrapper::ReadBytes(Vector<uint8_t>& value) {
unsigned length;
if (!ReadVarInt(length))
return false;
DCHECK_LE(current_, end_);
if (end_ - current_ < static_cast<ptrdiff_t>(length))
return false;
Vector<uint8_t> result;
result.ReserveInitialCapacity(length);
result.Append(current_, length);
value = std::move(result);
current_ += length;
return true;
}
bool IDBValueUnwrapper::Reset() {
#if DCHECK_IS_ON()
blob_handle_ = nullptr;
......
......@@ -12,6 +12,7 @@
#include "platform/SharedBuffer.h"
#include "platform/wtf/Allocator.h"
#include "platform/wtf/Vector.h"
#include "platform/wtf/text/StringView.h"
#include "public/platform/WebBlobInfo.h"
#include "v8/include/v8.h"
......@@ -28,6 +29,29 @@ class SharedBuffer;
// Logic for serializing V8 values for storage in IndexedDB.
//
// An IDBValueWrapper instance drives the serialization of a single V8 value to
// IndexedDB. An instance's lifecycle goes through the following stages:
// 1) Cloning - Right after an instance is constructed, its internal
// representation is optimized for structured cloning via the Clone() method.
// This may be necessary when extracting the primary key and/or index keys
// for the serialized value.
// 2) Wrapping - DoneCloning() transitions the instance to an internal
// reprensetation optimized for wrapping via WrapIfBiggerThan().
// 3) Reading results - After any desired wrapping is performed, the Take*()
// methods yield the serialized value components passed to the backing store.
// To avoid unnecessary copies, the Take*() methods move out parts of the
// internal representation, so each Take*() method can be called at most
// once.
//
// Example usage:
// auto wrapper = new IDBValueWrapper();
// wrapper.Clone(...); // Structured clone used to extract keys.
// wrapper.DoneCloning();
// wrapper.WrapIfBiggerThan(kWrapThreshold);
// wrapper.TakeWireBytes();
// wrapper.TakeBlobDataHandles();
// wrapper.TakeBlobInfo();
//
// V8 values are stored on disk using the format implemented in
// SerializedScriptValue (SSV), which is essentialy a byte array plus an array
// of attached Blobs. For "normal" (not too large) V8 values, the SSV output's
......@@ -62,6 +86,10 @@ class MODULES_EXPORT IDBValueWrapper {
//
// The serialization process can throw an exception. The caller is responsible
// for checking exception_state.
//
// The wrapper's internal representation is optimized for cloning the
// serialized value. DoneCloning() must be called to transition to an internal
// representation optimized for writing.
IDBValueWrapper(
v8::Isolate*,
v8::Local<v8::Value>,
......@@ -75,41 +103,55 @@ class MODULES_EXPORT IDBValueWrapper {
// a value's key and index keys are extracted from a structured clone of the
// value, which avoids the issue of side-effects in custom getters.
//
// This method cannot be called after WrapIfBiggerThan().
// This method cannot be called after DoneCloning().
void Clone(ScriptState*, ScriptValue* clone);
// Optimizes the serialized value's internal representation for writing to
// disk.
//
// This must be called before Take*() methods can be called. After this method
// is called, Clone() cannot be called anymore.
void DoneCloning();
// Conditionally wraps the serialized value's byte array into a Blob.
//
// The byte array is wrapped if its size exceeds max_bytes. In production, the
// max_bytes threshold is currently always kWrapThreshold.
//
// This method must be called before ExtractWireBytes() and cannot be called
// after ExtractWireBytes().
// This method must be called before the Take*() methods are called.
bool WrapIfBiggerThan(unsigned max_bytes);
// Obtains the BlobDataHandles from the serialized value's Blob array.
// Obtains the byte array for the serialized value.
//
// This method must be called at most once, and must be called after
// WrapIfBiggerThan().
void ExtractBlobDataHandles(
Vector<scoped_refptr<BlobDataHandle>>* blob_data_handles);
scoped_refptr<SharedBuffer> TakeWireBytes();
// Obtains the byte array for the serialized value.
// Obtains the BlobDataHandles from the serialized value's Blob array.
//
// This method must be called at most once, and must be called after
// WrapIfBiggerThan().
scoped_refptr<SharedBuffer> ExtractWireBytes();
// DoneCloning().
Vector<scoped_refptr<BlobDataHandle>> TakeBlobDataHandles() {
#if DCHECK_IS_ON()
DCHECK(done_cloning_) << __func__ << " called before DoneCloning()";
DCHECK(owns_blob_handles_) << __func__ << " called twice";
owns_blob_handles_ = false;
#endif // DCHECK_IS_ON()
return std::move(blob_handles_);
}
// Obtains WebBlobInfos for the serialized value's Blob array.
//
// This method must be called at most once, and must be called after
// WrapIfBiggerThan().
inline Vector<WebBlobInfo>& WrappedBlobInfo() {
// DoneCloning().
inline Vector<WebBlobInfo> TakeBlobInfo() {
#if DCHECK_IS_ON()
DCHECK(!had_exception_)
<< "WrapBlobInfo() called on wrapper with serialization exception";
DCHECK(done_cloning_) << __func__ << " called before DoneCloning()";
DCHECK(owns_blob_info_) << __func__ << " called twice";
owns_blob_info_ = false;
#endif // DCHECK_IS_ON()
return blob_info_;
return std::move(blob_info_);
}
size_t DataLengthBeforeWrapInBytes() { return original_data_length_; }
......@@ -128,17 +170,32 @@ class MODULES_EXPORT IDBValueWrapper {
"application/vnd.blink-idb-value-wrapper";
// Used to serialize the wrapped value. Exposed for testing.
static void WriteVarint(unsigned value, Vector<char>& output);
static void WriteVarInt(unsigned value, Vector<char>& output);
static void WriteBytes(const Vector<uint8_t>& bytes, Vector<char>& output);
private:
// V8 value serialization state.
scoped_refptr<SerializedScriptValue> serialized_value_;
scoped_refptr<BlobDataHandle> wrapper_handle_;
Vector<scoped_refptr<BlobDataHandle>> blob_handles_;
Vector<WebBlobInfo> blob_info_;
Vector<char> wire_bytes_;
// Buffer for wire data that is not stored in SerializedScriptValue.
//
// This buffer ends up storing metadata generated by wrapping operations.
Vector<char> wire_data_buffer_;
// Points into SerializedScriptValue's data buffer, or into wire_data_buffer_.
StringView wire_data_;
size_t original_data_length_ = 0;
#if DCHECK_IS_ON()
// Accounting for lifecycle stages.
bool had_exception_ = false;
bool wrap_called_ = false;
bool done_cloning_ = false;
bool owns_blob_handles_ = true;
bool owns_blob_info_ = true;
bool owns_wire_bytes_ = true;
#endif // DCHECK_IS_ON()
};
......@@ -189,10 +246,11 @@ class MODULES_EXPORT IDBValueUnwrapper {
private:
// Only present in tests.
friend class IDBValueUnwrapperReadVarintTestHelper;
friend class IDBValueUnwrapperReadTestHelper;
// Used to deserialize the wrapped value.
bool ReadVarint(unsigned& value);
bool ReadVarInt(unsigned&);
bool ReadBytes(Vector<uint8_t>&);
// Resets the parsing state.
bool Reset();
......
......@@ -4,6 +4,7 @@
#include "modules/indexeddb/IDBValueWrapping.h"
#include <algorithm>
#include <limits>
#include <memory>
......@@ -20,65 +21,65 @@
namespace blink {
TEST(IDBValueWrapperTest, WriteVarintOneByte) {
TEST(IDBValueWrapperTest, WriteVarIntOneByte) {
Vector<char> output;
IDBValueWrapper::WriteVarint(0, output);
IDBValueWrapper::WriteVarInt(0, output);
ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x00', output.data()[0]);
output.clear();
IDBValueWrapper::WriteVarint(1, output);
IDBValueWrapper::WriteVarInt(1, output);
ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x01', output.data()[0]);
output.clear();
IDBValueWrapper::WriteVarint(0x34, output);
IDBValueWrapper::WriteVarInt(0x34, output);
ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x34', output.data()[0]);
output.clear();
IDBValueWrapper::WriteVarint(0x7f, output);
IDBValueWrapper::WriteVarInt(0x7f, output);
ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x7f', output.data()[0]);
}
TEST(IDBValueWrapperTest, WriteVarintMultiByte) {
TEST(IDBValueWrapperTest, WriteVarIntMultiByte) {
Vector<char> output;
IDBValueWrapper::WriteVarint(0xff, output);
IDBValueWrapper::WriteVarInt(0xff, output);
ASSERT_EQ(2U, output.size());
EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\x01', output.data()[1]);
output.clear();
IDBValueWrapper::WriteVarint(0x100, output);
IDBValueWrapper::WriteVarInt(0x100, output);
ASSERT_EQ(2U, output.size());
EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x02', output.data()[1]);
output.clear();
IDBValueWrapper::WriteVarint(0x1234, output);
IDBValueWrapper::WriteVarInt(0x1234, output);
ASSERT_EQ(2U, output.size());
EXPECT_EQ('\xb4', output.data()[0]);
EXPECT_EQ('\x24', output.data()[1]);
output.clear();
IDBValueWrapper::WriteVarint(0xabcd, output);
IDBValueWrapper::WriteVarInt(0xabcd, output);
ASSERT_EQ(3U, output.size());
EXPECT_EQ('\xcd', output.data()[0]);
EXPECT_EQ('\xd7', output.data()[1]);
EXPECT_EQ('\x2', output.data()[2]);
output.clear();
IDBValueWrapper::WriteVarint(0x123456, output);
IDBValueWrapper::WriteVarInt(0x123456, output);
ASSERT_EQ(3U, output.size());
EXPECT_EQ('\xd6', output.data()[0]);
EXPECT_EQ('\xe8', output.data()[1]);
EXPECT_EQ('\x48', output.data()[2]);
output.clear();
IDBValueWrapper::WriteVarint(0xabcdef, output);
IDBValueWrapper::WriteVarInt(0xabcdef, output);
ASSERT_EQ(4U, output.size());
EXPECT_EQ('\xef', output.data()[0]);
EXPECT_EQ('\x9b', output.data()[1]);
......@@ -87,36 +88,36 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByte) {
output.clear();
}
TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
TEST(IDBValueWrapperTest, WriteVarIntMultiByteEdgeCases) {
Vector<char> output;
IDBValueWrapper::WriteVarint(0x80, output);
IDBValueWrapper::WriteVarInt(0x80, output);
ASSERT_EQ(2U, output.size());
EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x01', output.data()[1]);
output.clear();
IDBValueWrapper::WriteVarint(0x3fff, output);
IDBValueWrapper::WriteVarInt(0x3fff, output);
ASSERT_EQ(2U, output.size());
EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\x7f', output.data()[1]);
output.clear();
IDBValueWrapper::WriteVarint(0x4000, output);
IDBValueWrapper::WriteVarInt(0x4000, output);
ASSERT_EQ(3U, output.size());
EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x80', output.data()[1]);
EXPECT_EQ('\x01', output.data()[2]);
output.clear();
IDBValueWrapper::WriteVarint(0x1fffff, output);
IDBValueWrapper::WriteVarInt(0x1fffff, output);
ASSERT_EQ(3U, output.size());
EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\xff', output.data()[1]);
EXPECT_EQ('\x7f', output.data()[2]);
output.clear();
IDBValueWrapper::WriteVarint(0x200000, output);
IDBValueWrapper::WriteVarInt(0x200000, output);
ASSERT_EQ(4U, output.size());
EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x80', output.data()[1]);
......@@ -124,7 +125,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
EXPECT_EQ('\x01', output.data()[3]);
output.clear();
IDBValueWrapper::WriteVarint(0xfffffff, output);
IDBValueWrapper::WriteVarInt(0xfffffff, output);
ASSERT_EQ(4U, output.size());
EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\xff', output.data()[1]);
......@@ -132,7 +133,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
EXPECT_EQ('\x7f', output.data()[3]);
output.clear();
IDBValueWrapper::WriteVarint(0x10000000, output);
IDBValueWrapper::WriteVarInt(0x10000000, output);
ASSERT_EQ(5U, output.size());
EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x80', output.data()[1]);
......@@ -142,7 +143,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
output.clear();
// Maximum value of unsigned on 32-bit platforms.
IDBValueWrapper::WriteVarint(0xffffffff, output);
IDBValueWrapper::WriteVarInt(0xffffffff, output);
ASSERT_EQ(5U, output.size());
EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\xff', output.data()[1]);
......@@ -152,216 +153,322 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
output.clear();
}
TEST(IDBValueWrapperTest, WriteBytes) {
Vector<char> output;
Vector<uint8_t> empty;
IDBValueWrapper::WriteBytes(empty, output);
ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x00', output.data()[0]);
output.clear();
Vector<uint8_t> one_char;
one_char.Append("\x42", 1);
IDBValueWrapper::WriteBytes(one_char, output);
ASSERT_EQ(2U, output.size());
EXPECT_EQ('\x01', output.data()[0]);
EXPECT_EQ('\x42', output.data()[1]);
output.clear();
Vector<uint8_t> long_vector;
for (int i = 0; i < 256; ++i)
long_vector.push_back(static_cast<uint8_t>(i));
IDBValueWrapper::WriteBytes(long_vector, output);
ASSERT_EQ(258U, output.size());
EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x02', output.data()[1]);
EXPECT_TRUE(std::equal(long_vector.begin(), long_vector.end(),
reinterpret_cast<const uint8_t*>(output.data() + 2)));
output.clear();
}
// Friend class of IDBValueUnwrapper with access to its internals.
class IDBValueUnwrapperReadVarintTestHelper {
class IDBValueUnwrapperReadTestHelper {
public:
void ReadVarint(const char* start, size_t buffer_size) {
void ReadVarInt(const char* start, size_t buffer_size) {
IDBValueUnwrapper unwrapper;
const uint8_t* buffer_start = reinterpret_cast<const uint8_t*>(start);
const uint8_t* buffer_end = buffer_start + buffer_size;
unwrapper.current_ = buffer_start;
unwrapper.end_ = buffer_end;
success_ = unwrapper.ReadVarint(read_value_);
success_ = unwrapper.ReadVarInt(read_varint_);
ASSERT_EQ(unwrapper.end_, buffer_end)
<< "ReadVarint should not change end_";
<< "ReadVarInt should not change end_";
ASSERT_LE(unwrapper.current_, unwrapper.end_)
<< "ReadVarInt should not move current_ past end_";
consumed_bytes_ = unwrapper.current_ - buffer_start;
}
void ReadBytes(const char* start, size_t buffer_size) {
IDBValueUnwrapper unwrapper;
const uint8_t* buffer_start = reinterpret_cast<const uint8_t*>(start);
const uint8_t* buffer_end = buffer_start + buffer_size;
unwrapper.current_ = buffer_start;
unwrapper.end_ = buffer_end;
success_ = unwrapper.ReadBytes(read_bytes_);
ASSERT_EQ(unwrapper.end_, buffer_end) << "ReadBytes should not change end_";
ASSERT_LE(unwrapper.current_, unwrapper.end_)
<< "ReadVarint should not move current_ past end_";
<< "ReadBytes should not move current_ past end_";
consumed_bytes_ = unwrapper.current_ - buffer_start;
}
bool success() { return success_; }
unsigned consumed_bytes() { return consumed_bytes_; }
unsigned read_value() { return read_value_; }
unsigned read_varint() { return read_varint_; }
const Vector<uint8_t>& read_bytes() { return read_bytes_; }
private:
bool success_;
unsigned consumed_bytes_;
unsigned read_value_;
unsigned read_varint_;
Vector<uint8_t> read_bytes_;
};
TEST(IDBValueUnwrapperTest, ReadVarintOneByte) {
IDBValueUnwrapperReadVarintTestHelper helper;
TEST(IDBValueUnwrapperTest, ReadVarIntOneByte) {
IDBValueUnwrapperReadTestHelper helper;
// Most test cases have an extra byte at the end of the input to verify that
// the parser doesn't consume too much data.
helper.ReadVarint("\x00\x01", 2);
helper.ReadVarInt("\x00\x01", 2);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_value());
EXPECT_EQ(0U, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadVarint("\x01\x01", 2);
helper.ReadVarInt("\x01\x01", 2);
EXPECT_TRUE(helper.success());
EXPECT_EQ(1U, helper.read_value());
EXPECT_EQ(1U, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadVarint("\x7f\x01", 2);
helper.ReadVarInt("\x7f\x01", 2);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x7fU, helper.read_value());
EXPECT_EQ(0x7fU, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadVarint("\x7f\x01", 1);
helper.ReadVarInt("\x7f\x01", 1);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x7fU, helper.read_value());
EXPECT_EQ(0x7fU, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, ReadVarintMultiBytes) {
IDBValueUnwrapperReadVarintTestHelper helper;
TEST(IDBValueUnwrapperTest, ReadVarIntMultiBytes) {
IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\xff\x01\x01", 3);
helper.ReadVarInt("\xff\x01\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0xffU, helper.read_value());
EXPECT_EQ(0xffU, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\x80\x02\x01", 3);
helper.ReadVarInt("\x80\x02\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x100U, helper.read_value());
EXPECT_EQ(0x100U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xb4\x24\x01", 3);
helper.ReadVarInt("\xb4\x24\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x1234U, helper.read_value());
EXPECT_EQ(0x1234U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xcd\xd7\x02\x01", 4);
helper.ReadVarInt("\xcd\xd7\x02\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0xabcdU, helper.read_value());
EXPECT_EQ(0xabcdU, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\xd6\xe8\x48\x01", 4);
helper.ReadVarInt("\xd6\xe8\x48\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x123456U, helper.read_value());
EXPECT_EQ(0x123456U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\xd6\xe8\x48\x01", 3);
helper.ReadVarInt("\xd6\xe8\x48\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x123456U, helper.read_value());
EXPECT_EQ(0x123456U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\xef\x9b\xaf\x05\x01", 5);
helper.ReadVarInt("\xef\x9b\xaf\x05\x01", 5);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0xabcdefU, helper.read_value());
EXPECT_EQ(0xabcdefU, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes());
helper.ReadVarint("\xef\x9b\xaf\x05\x01", 4);
helper.ReadVarInt("\xef\x9b\xaf\x05\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0xabcdefU, helper.read_value());
EXPECT_EQ(0xabcdefU, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, ReadVarintMultiByteEdgeCases) {
IDBValueUnwrapperReadVarintTestHelper helper;
TEST(IDBValueUnwrapperTest, ReadVarIntMultiByteEdgeCases) {
IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\x80\x01\x01", 3);
helper.ReadVarInt("\x80\x01\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x80U, helper.read_value());
EXPECT_EQ(0x80U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xff\x7f\x01", 3);
helper.ReadVarInt("\xff\x7f\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x3fffU, helper.read_value());
EXPECT_EQ(0x3fffU, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\x80\x80\x01\x01", 4);
helper.ReadVarInt("\x80\x80\x01\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x4000U, helper.read_value());
EXPECT_EQ(0x4000U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\xff\xff\x7f\x01", 4);
helper.ReadVarInt("\xff\xff\x7f\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x1fffffU, helper.read_value());
EXPECT_EQ(0x1fffffU, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\x80\x80\x80\x01\x01", 5);
helper.ReadVarInt("\x80\x80\x80\x01\x01", 5);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x200000U, helper.read_value());
EXPECT_EQ(0x200000U, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes());
helper.ReadVarint("\xff\xff\xff\x7f\x01", 5);
helper.ReadVarInt("\xff\xff\xff\x7f\x01", 5);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0xfffffffU, helper.read_value());
EXPECT_EQ(0xfffffffU, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes());
helper.ReadVarint("\x80\x80\x80\x80\x01\x01", 6);
helper.ReadVarInt("\x80\x80\x80\x80\x01\x01", 6);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x10000000U, helper.read_value());
EXPECT_EQ(0x10000000U, helper.read_varint());
EXPECT_EQ(5U, helper.consumed_bytes());
helper.ReadVarint("\xff\xff\xff\xff\x0f\x01", 6);
helper.ReadVarInt("\xff\xff\xff\xff\x0f\x01", 6);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0xffffffffU, helper.read_value());
EXPECT_EQ(0xffffffffU, helper.read_varint());
EXPECT_EQ(5U, helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, ReadVarintTruncatedInput) {
IDBValueUnwrapperReadVarintTestHelper helper;
TEST(IDBValueUnwrapperTest, ReadVarIntTruncatedInput) {
IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\x01", 0);
helper.ReadVarInt("\x01", 0);
EXPECT_FALSE(helper.success());
helper.ReadVarint("\x80\x01", 1);
helper.ReadVarInt("\x80\x01", 1);
EXPECT_FALSE(helper.success());
helper.ReadVarint("\xff\x01", 1);
helper.ReadVarInt("\xff\x01", 1);
EXPECT_FALSE(helper.success());
helper.ReadVarint("\x80\x80\x01", 2);
helper.ReadVarInt("\x80\x80\x01", 2);
EXPECT_FALSE(helper.success());
helper.ReadVarint("\xff\xff\x01", 2);
helper.ReadVarInt("\xff\xff\x01", 2);
EXPECT_FALSE(helper.success());
helper.ReadVarint("\x80\x80\x80\x80\x01", 4);
helper.ReadVarInt("\x80\x80\x80\x80\x01", 4);
EXPECT_FALSE(helper.success());
helper.ReadVarint("\xff\xff\xff\xff\x01", 4);
helper.ReadVarInt("\xff\xff\xff\xff\x01", 4);
EXPECT_FALSE(helper.success());
}
TEST(IDBValueUnwrapperTest, ReadVarintDenormalizedInput) {
IDBValueUnwrapperReadVarintTestHelper helper;
TEST(IDBValueUnwrapperTest, ReadVarIntDenormalizedInput) {
IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\x80\x00\x01", 3);
helper.ReadVarInt("\x80\x00\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_value());
EXPECT_EQ(0U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xff\x00\x01", 3);
helper.ReadVarInt("\xff\x00\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x7fU, helper.read_value());
EXPECT_EQ(0x7fU, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\x80\x80\x00\x01", 4);
helper.ReadVarInt("\x80\x80\x00\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_value());
EXPECT_EQ(0U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\x80\xff\x00\x01", 4);
helper.ReadVarInt("\x80\xff\x00\x01", 4);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x3f80U, helper.read_value());
EXPECT_EQ(0x3f80U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes());
helper.ReadVarint("\x80\xff\x80\xff\x00\x01", 6);
helper.ReadVarInt("\x80\xff\x80\xff\x00\x01", 6);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0x0fe03f80U, helper.read_value());
EXPECT_EQ(0x0fe03f80U, helper.read_varint());
EXPECT_EQ(5U, helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, WriteVarintMaxUnsignedRoundtrip) {
TEST(IDBValueUnwrapperTest, WriteVarIntMaxUnsignedRoundtrip) {
unsigned max_value = std::numeric_limits<unsigned>::max();
Vector<char> output;
IDBValueWrapper::WriteVarint(max_value, output);
IDBValueWrapper::WriteVarInt(max_value, output);
IDBValueUnwrapperReadVarintTestHelper helper;
helper.ReadVarint(output.data(), output.size());
IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarInt(output.data(), output.size());
EXPECT_TRUE(helper.success());
EXPECT_EQ(max_value, helper.read_value());
EXPECT_EQ(max_value, helper.read_varint());
EXPECT_EQ(output.size(), helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, ReadBytes) {
IDBValueUnwrapperReadTestHelper helper;
// Most test cases have an extra byte at the end of the input to verify that
// the parser doesn't consume too much data.
helper.ReadBytes("\x00\x01", 2);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_bytes().size());
EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadBytes("\x01\x42\x01", 3);
EXPECT_TRUE(helper.success());
ASSERT_EQ(1U, helper.read_bytes().size());
EXPECT_EQ('\x42', helper.read_bytes().data()[0]);
EXPECT_EQ(2U, helper.consumed_bytes());
Vector<uint8_t> long_output;
long_output.push_back(0x80);
long_output.push_back(0x02);
for (int i = 0; i < 256; ++i)
long_output.push_back(static_cast<unsigned char>(i));
long_output.push_back(0x01);
helper.ReadBytes(reinterpret_cast<char*>(long_output.data()),
long_output.size());
EXPECT_TRUE(helper.success());
ASSERT_EQ(256U, helper.read_bytes().size());
ASSERT_EQ(long_output.size() - 1, helper.consumed_bytes());
EXPECT_TRUE(std::equal(helper.read_bytes().begin(), helper.read_bytes().end(),
long_output.data() + 2));
helper.ReadBytes("\x01\x42\x01", 2);
EXPECT_TRUE(helper.success());
ASSERT_EQ(1U, helper.read_bytes().size());
EXPECT_EQ('\x42', helper.read_bytes().data()[0]);
EXPECT_EQ(2U, helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, ReadBytesTruncatedInput) {
IDBValueUnwrapperReadTestHelper helper;
helper.ReadBytes("\x01\x42", 0);
EXPECT_FALSE(helper.success());
helper.ReadBytes("\x01\x42", 1);
EXPECT_FALSE(helper.success());
helper.ReadBytes("\x03\x42\x42\x42", 3);
EXPECT_FALSE(helper.success());
}
TEST(IDBValueUnwrapperTest, ReadBytesDenormalizedInput) {
IDBValueUnwrapperReadTestHelper helper;
helper.ReadBytes("\x80\x00\x01", 3);
EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_bytes().size());
EXPECT_EQ(2U, helper.consumed_bytes());
}
TEST(IDBValueUnwrapperTest, IsWrapped) {
V8TestingScope scope;
NonThrowableExceptionState non_throwable_exception_state;
......@@ -369,12 +476,12 @@ TEST(IDBValueUnwrapperTest, IsWrapped) {
IDBValueWrapper wrapper(scope.GetIsolate(), v8_true,
SerializedScriptValue::SerializeOptions::kSerialize,
non_throwable_exception_state);
wrapper.DoneCloning();
wrapper.WrapIfBiggerThan(0);
Vector<scoped_refptr<BlobDataHandle>> blob_data_handles;
wrapper.ExtractBlobDataHandles(&blob_data_handles);
Vector<WebBlobInfo>& blob_infos = wrapper.WrappedBlobInfo();
scoped_refptr<SharedBuffer> wrapped_marker_buffer =
wrapper.ExtractWireBytes();
Vector<scoped_refptr<BlobDataHandle>> blob_data_handles =
wrapper.TakeBlobDataHandles();
Vector<WebBlobInfo> blob_infos = wrapper.TakeBlobInfo();
scoped_refptr<SharedBuffer> wrapped_marker_buffer = wrapper.TakeWireBytes();
IDBKey* key = IDBKey::CreateNumber(42.0);
IDBKeyPath key_path(String("primaryKey"));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment