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, ...@@ -529,13 +529,13 @@ IDBRequest* IDBObjectStore::put(ScriptState* script_state,
IDBRequest* request = IDBRequest::Create( IDBRequest* request = IDBRequest::Create(
script_state, source, transaction_.Get(), std::move(metrics)); script_state, source, transaction_.Get(), std::move(metrics));
value_wrapper.DoneCloning();
value_wrapper.WrapIfBiggerThan(IDBValueWrapper::kWrapThreshold); value_wrapper.WrapIfBiggerThan(IDBValueWrapper::kWrapThreshold);
value_wrapper.ExtractBlobDataHandles(request->transit_blob_handles());
request->transit_blob_handles() = value_wrapper.TakeBlobDataHandles();
BackendDB()->Put( BackendDB()->Put(
transaction_->Id(), Id(), WebData(value_wrapper.ExtractWireBytes()), transaction_->Id(), Id(), WebData(value_wrapper.TakeWireBytes()),
value_wrapper.WrappedBlobInfo(), key, value_wrapper.TakeBlobInfo(), key, static_cast<WebIDBPutMode>(put_mode),
static_cast<WebIDBPutMode>(put_mode),
request->CreateWebCallbacks().release(), index_ids, index_keys); request->CreateWebCallbacks().release(), index_ids, index_keys);
return request; return request;
......
...@@ -296,8 +296,8 @@ class MODULES_EXPORT IDBRequest : public EventTargetWithInlineData, ...@@ -296,8 +296,8 @@ class MODULES_EXPORT IDBRequest : public EventTargetWithInlineData,
// (which hangs onto the BlobDataHandle) may get garbage-collected. IDBRequest // (which hangs onto the BlobDataHandle) may get garbage-collected. IDBRequest
// needs to hang onto the BlobDataHandle as well, to avoid having the // needs to hang onto the BlobDataHandle as well, to avoid having the
// browser-side Blob get destroyed before the IndexedDB request is processed. // browser-side Blob get destroyed before the IndexedDB request is processed.
inline Vector<scoped_refptr<BlobDataHandle>>* transit_blob_handles() { inline Vector<scoped_refptr<BlobDataHandle>>& transit_blob_handles() {
return &transit_blob_handles_; return transit_blob_handles_;
} }
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
......
...@@ -28,19 +28,20 @@ scoped_refptr<IDBValue> CreateIDBValueForTesting(v8::Isolate* isolate, ...@@ -28,19 +28,20 @@ scoped_refptr<IDBValue> CreateIDBValueForTesting(v8::Isolate* isolate,
IDBValueWrapper wrapper(isolate, v8_array, IDBValueWrapper wrapper(isolate, v8_array,
SerializedScriptValue::SerializeOptions::kSerialize, SerializedScriptValue::SerializeOptions::kSerialize,
non_throwable_exception_state); non_throwable_exception_state);
wrapper.DoneCloning();
wrapper.WrapIfBiggerThan(create_wrapped_value ? 0 : 1024 * element_count); wrapper.WrapIfBiggerThan(create_wrapped_value ? 0 : 1024 * element_count);
std::unique_ptr<Vector<scoped_refptr<BlobDataHandle>>> blob_data_handles = Vector<scoped_refptr<BlobDataHandle>> blob_data_handles =
std::make_unique<Vector<scoped_refptr<BlobDataHandle>>>(); wrapper.TakeBlobDataHandles();
wrapper.ExtractBlobDataHandles(blob_data_handles.get()); Vector<WebBlobInfo> blob_infos = wrapper.TakeBlobInfo();
Vector<WebBlobInfo>& blob_infos = wrapper.WrappedBlobInfo(); scoped_refptr<SharedBuffer> wrapped_marker_buffer = wrapper.TakeWireBytes();
scoped_refptr<SharedBuffer> wrapped_marker_buffer =
wrapper.ExtractWireBytes();
IDBKey* key = IDBKey::CreateNumber(42.0); IDBKey* key = IDBKey::CreateNumber(42.0);
IDBKeyPath key_path(String("primaryKey")); IDBKeyPath key_path(String("primaryKey"));
scoped_refptr<IDBValue> idb_value = IDBValue::Create( 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); std::make_unique<Vector<WebBlobInfo>>(blob_infos), key, key_path);
DCHECK_EQ(create_wrapped_value, DCHECK_EQ(create_wrapped_value,
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include "modules/indexeddb/IDBRequest.h" #include "modules/indexeddb/IDBRequest.h"
#include "modules/indexeddb/IDBValue.h" #include "modules/indexeddb/IDBValue.h"
#include "platform/blob/BlobData.h" #include "platform/blob/BlobData.h"
#include "platform/wtf/text/StringView.h"
#include "platform/wtf/text/WTFString.h" #include "platform/wtf/text/WTFString.h"
namespace blink { namespace blink {
...@@ -42,16 +41,15 @@ namespace { ...@@ -42,16 +41,15 @@ namespace {
// The SSV format version whose encoding hole is (ab)used for wrapping. // The SSV format version whose encoding hole is (ab)used for wrapping.
const static uint8_t kRequiresProcessingSSVPseudoVersion = 17; const static uint8_t kRequiresProcessingSSVPseudoVersion = 17;
// Identifies IndexedDB values that were wrapped in Blobs. The wrapper has the // SSV processing command replacing the SSV data bytes with a Blob's contents.
// following format:
// //
// 1) 0xFF - kVersionTag // 1) 0xFF - kVersionTag
// 2) 0x11 - kRequiresProcessingSSVPseudoVersion // 2) 0x11 - kRequiresProcessingSSVPseudoVersion
// 3) 0x01 - kBlobWrappedValue // 3) 0x01 - kReplaceWithBlob
// 4) varint - Blob size // 4) varint - Blob size
// 5) varint - the offset of the SSV-wrapping Blob in the IDBValue list of Blobs // 5) varint - the offset of the SSV-wrapping Blob in the IDBValue list of Blobs
// (should always be the last Blob) // (should always be the last Blob)
const static uint8_t kBlobWrappedValue = 1; const static uint8_t kReplaceWithBlob = 1;
} // namespace } // namespace
...@@ -68,8 +66,9 @@ IDBValueWrapper::IDBValueWrapper( ...@@ -68,8 +66,9 @@ IDBValueWrapper::IDBValueWrapper(
serialized_value_ = SerializedScriptValue::Serialize(isolate, value, options, serialized_value_ = SerializedScriptValue::Serialize(isolate, value, options,
exception_state); exception_state);
if (serialized_value_) if (serialized_value_) {
original_data_length_ = serialized_value_->DataLengthInBytes(); original_data_length_ = serialized_value_->DataLengthInBytes();
}
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
if (exception_state.HadException()) if (exception_state.HadException())
had_exception_ = true; had_exception_ = true;
...@@ -82,9 +81,9 @@ IDBValueWrapper::~IDBValueWrapper() {} ...@@ -82,9 +81,9 @@ IDBValueWrapper::~IDBValueWrapper() {}
void IDBValueWrapper::Clone(ScriptState* script_state, ScriptValue* clone) { void IDBValueWrapper::Clone(ScriptState* script_state, ScriptValue* clone) {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK(!had_exception_) << __FUNCTION__ DCHECK(!had_exception_) << __func__
<< " called on wrapper with serialization exception"; << " called on wrapper with serialization exception";
DCHECK(!wrap_called_) << "Clone() called after WrapIfBiggerThan()"; DCHECK(!done_cloning_) << __func__ << " called after DoneCloning()";
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
bool read_wasm_from_stream = true; bool read_wasm_from_stream = true;
...@@ -95,7 +94,8 @@ void IDBValueWrapper::Clone(ScriptState* script_state, ScriptValue* clone) { ...@@ -95,7 +94,8 @@ void IDBValueWrapper::Clone(ScriptState* script_state, ScriptValue* clone) {
&blob_info_, read_wasm_from_stream); &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. // Writes an unsigned integer as a base-128 varint.
// The number is written, 7 bits at a time, from the least significant to // 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. // 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) { ...@@ -107,22 +107,39 @@ void IDBValueWrapper::WriteVarint(unsigned value, Vector<char>& output) {
output.back() &= 0x7F; 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() #if DCHECK_IS_ON()
DCHECK(!had_exception_) << __FUNCTION__ DCHECK(!had_exception_) << __func__
<< " called on wrapper with serialization exception"; << " called on wrapper with serialization exception";
DCHECK(!wrap_called_) << __FUNCTION__ << " called twice on the same wrapper"; DCHECK(!done_cloning_) << __func__ << " called twice";
wrap_called_ = true; done_cloning_ = true;
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
StringView wire_data = serialized_value_->GetWireData(); wire_data_ = serialized_value_->GetWireData();
DCHECK(wire_data.Is8Bit()); DCHECK(wire_data_.Is8Bit());
unsigned wire_data_size = wire_data.length(); for (const auto& kvp : serialized_value_->BlobDataHandles())
if (wire_data_size <= max_bytes) { blob_handles_.push_back(std::move(kvp.value));
wire_bytes_.ReserveInitialCapacity(wire_data_size); }
wire_bytes_.Append(wire_data.Characters8(), wire_data_size);
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; return false;
}
// TODO(pwnall): The MIME type should probably be an atomic string. // TODO(pwnall): The MIME type should probably be an atomic string.
String mime_type(kWrapMimeType); String mime_type(kWrapMimeType);
...@@ -130,37 +147,48 @@ bool IDBValueWrapper::WrapIfBiggerThan(unsigned max_bytes) { ...@@ -130,37 +147,48 @@ bool IDBValueWrapper::WrapIfBiggerThan(unsigned max_bytes) {
// Blob::Create to avoid a buffer copy. // Blob::Create to avoid a buffer copy.
std::unique_ptr<BlobData> wrapper_blob_data = BlobData::Create(); std::unique_ptr<BlobData> wrapper_blob_data = BlobData::Create();
wrapper_blob_data->SetContentType(String(kWrapMimeType)); wrapper_blob_data->SetContentType(String(kWrapMimeType));
wrapper_blob_data->AppendBytes(wire_data.Characters8(), wire_data_size); wrapper_blob_data->AppendBytes(wire_data_.Characters8(), wire_data_size);
wrapper_handle_ = scoped_refptr<BlobDataHandle> wrapper_handle =
BlobDataHandle::Create(std::move(wrapper_blob_data), wire_data_size); 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_data_size);
blob_handles_.push_back(std::move(wrapper_handle));
wire_bytes_.clear();
wire_bytes_.push_back(kVersionTag); wire_data_buffer_.clear();
wire_bytes_.push_back(kRequiresProcessingSSVPseudoVersion); wire_data_buffer_.push_back(kVersionTag);
wire_bytes_.push_back(kBlobWrappedValue); wire_data_buffer_.push_back(kRequiresProcessingSSVPseudoVersion);
IDBValueWrapper::WriteVarint(wrapper_handle_->size(), wire_bytes_); wire_data_buffer_.push_back(kReplaceWithBlob);
IDBValueWrapper::WriteVarint(serialized_value_->BlobDataHandles().size(), IDBValueWrapper::WriteVarInt(wire_data_size, wire_data_buffer_);
wire_bytes_); 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; return true;
} }
void IDBValueWrapper::ExtractBlobDataHandles( scoped_refptr<SharedBuffer> IDBValueWrapper::TakeWireBytes() {
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() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK(!had_exception_) << __FUNCTION__ DCHECK(done_cloning_) << __func__ << " called before DoneCloning()";
<< " called on wrapper with serialization exception"; DCHECK(owns_wire_bytes_) << __func__ << " called twice";
owns_wire_bytes_ = false;
#endif // DCHECK_IS_ON() #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() { IDBValueUnwrapper::IDBValueUnwrapper() {
...@@ -176,7 +204,7 @@ bool IDBValueUnwrapper::IsWrapped(IDBValue* value) { ...@@ -176,7 +204,7 @@ bool IDBValueUnwrapper::IsWrapped(IDBValue* value) {
return header[0] == kVersionTag && return header[0] == kVersionTag &&
header[1] == kRequiresProcessingSSVPseudoVersion && header[1] == kRequiresProcessingSSVPseudoVersion &&
header[2] == kBlobWrappedValue; header[2] == kReplaceWithBlob;
} }
bool IDBValueUnwrapper::IsWrapped( bool IDBValueUnwrapper::IsWrapped(
...@@ -225,11 +253,11 @@ bool IDBValueUnwrapper::Parse(IDBValue* value) { ...@@ -225,11 +253,11 @@ bool IDBValueUnwrapper::Parse(IDBValue* value) {
end_ = data + value->data_->size(); end_ = data + value->data_->size();
current_ = data + 3; current_ = data + 3;
if (!ReadVarint(blob_size_)) if (!ReadVarInt(blob_size_))
return Reset(); return Reset();
unsigned blob_offset; unsigned blob_offset;
if (!ReadVarint(blob_offset)) if (!ReadVarInt(blob_offset))
return Reset(); return Reset();
size_t value_blob_count = value->blob_data_->size(); size_t value_blob_count = value->blob_data_->size();
...@@ -249,7 +277,7 @@ scoped_refptr<BlobDataHandle> IDBValueUnwrapper::WrapperBlobHandle() { ...@@ -249,7 +277,7 @@ scoped_refptr<BlobDataHandle> IDBValueUnwrapper::WrapperBlobHandle() {
return std::move(blob_handle_); return std::move(blob_handle_);
} }
bool IDBValueUnwrapper::ReadVarint(unsigned& value) { bool IDBValueUnwrapper::ReadVarInt(unsigned& value) {
value = 0; value = 0;
unsigned shift = 0; unsigned shift = 0;
bool has_another_byte; bool has_another_byte;
...@@ -269,6 +297,22 @@ bool IDBValueUnwrapper::ReadVarint(unsigned& value) { ...@@ -269,6 +297,22 @@ bool IDBValueUnwrapper::ReadVarint(unsigned& value) {
return true; 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() { bool IDBValueUnwrapper::Reset() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
blob_handle_ = nullptr; blob_handle_ = nullptr;
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include "platform/SharedBuffer.h" #include "platform/SharedBuffer.h"
#include "platform/wtf/Allocator.h" #include "platform/wtf/Allocator.h"
#include "platform/wtf/Vector.h" #include "platform/wtf/Vector.h"
#include "platform/wtf/text/StringView.h"
#include "public/platform/WebBlobInfo.h" #include "public/platform/WebBlobInfo.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
...@@ -28,6 +29,29 @@ class SharedBuffer; ...@@ -28,6 +29,29 @@ class SharedBuffer;
// Logic for serializing V8 values for storage in IndexedDB. // 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 // V8 values are stored on disk using the format implemented in
// SerializedScriptValue (SSV), which is essentialy a byte array plus an array // 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 // of attached Blobs. For "normal" (not too large) V8 values, the SSV output's
...@@ -62,6 +86,10 @@ class MODULES_EXPORT IDBValueWrapper { ...@@ -62,6 +86,10 @@ class MODULES_EXPORT IDBValueWrapper {
// //
// The serialization process can throw an exception. The caller is responsible // The serialization process can throw an exception. The caller is responsible
// for checking exception_state. // 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( IDBValueWrapper(
v8::Isolate*, v8::Isolate*,
v8::Local<v8::Value>, v8::Local<v8::Value>,
...@@ -75,41 +103,55 @@ class MODULES_EXPORT IDBValueWrapper { ...@@ -75,41 +103,55 @@ class MODULES_EXPORT IDBValueWrapper {
// a value's key and index keys are extracted from a structured clone of the // 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. // 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); 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. // 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 // The byte array is wrapped if its size exceeds max_bytes. In production, the
// max_bytes threshold is currently always kWrapThreshold. // max_bytes threshold is currently always kWrapThreshold.
// //
// This method must be called before ExtractWireBytes() and cannot be called // This method must be called before the Take*() methods are called.
// after ExtractWireBytes().
bool WrapIfBiggerThan(unsigned max_bytes); 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 // This method must be called at most once, and must be called after
// WrapIfBiggerThan(). // WrapIfBiggerThan().
void ExtractBlobDataHandles( scoped_refptr<SharedBuffer> TakeWireBytes();
Vector<scoped_refptr<BlobDataHandle>>* blob_data_handles);
// 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 // This method must be called at most once, and must be called after
// WrapIfBiggerThan(). // DoneCloning().
scoped_refptr<SharedBuffer> ExtractWireBytes(); 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. // Obtains WebBlobInfos for the serialized value's Blob array.
// //
// This method must be called at most once, and must be called after // This method must be called at most once, and must be called after
// WrapIfBiggerThan(). // DoneCloning().
inline Vector<WebBlobInfo>& WrappedBlobInfo() { inline Vector<WebBlobInfo> TakeBlobInfo() {
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
DCHECK(!had_exception_) DCHECK(done_cloning_) << __func__ << " called before DoneCloning()";
<< "WrapBlobInfo() called on wrapper with serialization exception"; DCHECK(owns_blob_info_) << __func__ << " called twice";
owns_blob_info_ = false;
#endif // DCHECK_IS_ON() #endif // DCHECK_IS_ON()
return blob_info_; return std::move(blob_info_);
} }
size_t DataLengthBeforeWrapInBytes() { return original_data_length_; } size_t DataLengthBeforeWrapInBytes() { return original_data_length_; }
...@@ -128,17 +170,32 @@ class MODULES_EXPORT IDBValueWrapper { ...@@ -128,17 +170,32 @@ class MODULES_EXPORT IDBValueWrapper {
"application/vnd.blink-idb-value-wrapper"; "application/vnd.blink-idb-value-wrapper";
// Used to serialize the wrapped value. Exposed for testing. // 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: private:
// V8 value serialization state.
scoped_refptr<SerializedScriptValue> serialized_value_; scoped_refptr<SerializedScriptValue> serialized_value_;
scoped_refptr<BlobDataHandle> wrapper_handle_; Vector<scoped_refptr<BlobDataHandle>> blob_handles_;
Vector<WebBlobInfo> blob_info_; 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; size_t original_data_length_ = 0;
#if DCHECK_IS_ON() #if DCHECK_IS_ON()
// Accounting for lifecycle stages.
bool had_exception_ = false; 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() #endif // DCHECK_IS_ON()
}; };
...@@ -189,10 +246,11 @@ class MODULES_EXPORT IDBValueUnwrapper { ...@@ -189,10 +246,11 @@ class MODULES_EXPORT IDBValueUnwrapper {
private: private:
// Only present in tests. // Only present in tests.
friend class IDBValueUnwrapperReadVarintTestHelper; friend class IDBValueUnwrapperReadTestHelper;
// Used to deserialize the wrapped value. // Used to deserialize the wrapped value.
bool ReadVarint(unsigned& value); bool ReadVarInt(unsigned&);
bool ReadBytes(Vector<uint8_t>&);
// Resets the parsing state. // Resets the parsing state.
bool Reset(); bool Reset();
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "modules/indexeddb/IDBValueWrapping.h" #include "modules/indexeddb/IDBValueWrapping.h"
#include <algorithm>
#include <limits> #include <limits>
#include <memory> #include <memory>
...@@ -20,65 +21,65 @@ ...@@ -20,65 +21,65 @@
namespace blink { namespace blink {
TEST(IDBValueWrapperTest, WriteVarintOneByte) { TEST(IDBValueWrapperTest, WriteVarIntOneByte) {
Vector<char> output; Vector<char> output;
IDBValueWrapper::WriteVarint(0, output); IDBValueWrapper::WriteVarInt(0, output);
ASSERT_EQ(1U, output.size()); ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x00', output.data()[0]); EXPECT_EQ('\x00', output.data()[0]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(1, output); IDBValueWrapper::WriteVarInt(1, output);
ASSERT_EQ(1U, output.size()); ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x01', output.data()[0]); EXPECT_EQ('\x01', output.data()[0]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x34, output); IDBValueWrapper::WriteVarInt(0x34, output);
ASSERT_EQ(1U, output.size()); ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x34', output.data()[0]); EXPECT_EQ('\x34', output.data()[0]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x7f, output); IDBValueWrapper::WriteVarInt(0x7f, output);
ASSERT_EQ(1U, output.size()); ASSERT_EQ(1U, output.size());
EXPECT_EQ('\x7f', output.data()[0]); EXPECT_EQ('\x7f', output.data()[0]);
} }
TEST(IDBValueWrapperTest, WriteVarintMultiByte) { TEST(IDBValueWrapperTest, WriteVarIntMultiByte) {
Vector<char> output; Vector<char> output;
IDBValueWrapper::WriteVarint(0xff, output); IDBValueWrapper::WriteVarInt(0xff, output);
ASSERT_EQ(2U, output.size()); ASSERT_EQ(2U, output.size());
EXPECT_EQ('\xff', output.data()[0]); EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\x01', output.data()[1]); EXPECT_EQ('\x01', output.data()[1]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x100, output); IDBValueWrapper::WriteVarInt(0x100, output);
ASSERT_EQ(2U, output.size()); ASSERT_EQ(2U, output.size());
EXPECT_EQ('\x80', output.data()[0]); EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x02', output.data()[1]); EXPECT_EQ('\x02', output.data()[1]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x1234, output); IDBValueWrapper::WriteVarInt(0x1234, output);
ASSERT_EQ(2U, output.size()); ASSERT_EQ(2U, output.size());
EXPECT_EQ('\xb4', output.data()[0]); EXPECT_EQ('\xb4', output.data()[0]);
EXPECT_EQ('\x24', output.data()[1]); EXPECT_EQ('\x24', output.data()[1]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0xabcd, output); IDBValueWrapper::WriteVarInt(0xabcd, output);
ASSERT_EQ(3U, output.size()); ASSERT_EQ(3U, output.size());
EXPECT_EQ('\xcd', output.data()[0]); EXPECT_EQ('\xcd', output.data()[0]);
EXPECT_EQ('\xd7', output.data()[1]); EXPECT_EQ('\xd7', output.data()[1]);
EXPECT_EQ('\x2', output.data()[2]); EXPECT_EQ('\x2', output.data()[2]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x123456, output); IDBValueWrapper::WriteVarInt(0x123456, output);
ASSERT_EQ(3U, output.size()); ASSERT_EQ(3U, output.size());
EXPECT_EQ('\xd6', output.data()[0]); EXPECT_EQ('\xd6', output.data()[0]);
EXPECT_EQ('\xe8', output.data()[1]); EXPECT_EQ('\xe8', output.data()[1]);
EXPECT_EQ('\x48', output.data()[2]); EXPECT_EQ('\x48', output.data()[2]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0xabcdef, output); IDBValueWrapper::WriteVarInt(0xabcdef, output);
ASSERT_EQ(4U, output.size()); ASSERT_EQ(4U, output.size());
EXPECT_EQ('\xef', output.data()[0]); EXPECT_EQ('\xef', output.data()[0]);
EXPECT_EQ('\x9b', output.data()[1]); EXPECT_EQ('\x9b', output.data()[1]);
...@@ -87,36 +88,36 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByte) { ...@@ -87,36 +88,36 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByte) {
output.clear(); output.clear();
} }
TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) { TEST(IDBValueWrapperTest, WriteVarIntMultiByteEdgeCases) {
Vector<char> output; Vector<char> output;
IDBValueWrapper::WriteVarint(0x80, output); IDBValueWrapper::WriteVarInt(0x80, output);
ASSERT_EQ(2U, output.size()); ASSERT_EQ(2U, output.size());
EXPECT_EQ('\x80', output.data()[0]); EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x01', output.data()[1]); EXPECT_EQ('\x01', output.data()[1]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x3fff, output); IDBValueWrapper::WriteVarInt(0x3fff, output);
ASSERT_EQ(2U, output.size()); ASSERT_EQ(2U, output.size());
EXPECT_EQ('\xff', output.data()[0]); EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\x7f', output.data()[1]); EXPECT_EQ('\x7f', output.data()[1]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x4000, output); IDBValueWrapper::WriteVarInt(0x4000, output);
ASSERT_EQ(3U, output.size()); ASSERT_EQ(3U, output.size());
EXPECT_EQ('\x80', output.data()[0]); EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x80', output.data()[1]); EXPECT_EQ('\x80', output.data()[1]);
EXPECT_EQ('\x01', output.data()[2]); EXPECT_EQ('\x01', output.data()[2]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x1fffff, output); IDBValueWrapper::WriteVarInt(0x1fffff, output);
ASSERT_EQ(3U, output.size()); ASSERT_EQ(3U, output.size());
EXPECT_EQ('\xff', output.data()[0]); EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\xff', output.data()[1]); EXPECT_EQ('\xff', output.data()[1]);
EXPECT_EQ('\x7f', output.data()[2]); EXPECT_EQ('\x7f', output.data()[2]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x200000, output); IDBValueWrapper::WriteVarInt(0x200000, output);
ASSERT_EQ(4U, output.size()); ASSERT_EQ(4U, output.size());
EXPECT_EQ('\x80', output.data()[0]); EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x80', output.data()[1]); EXPECT_EQ('\x80', output.data()[1]);
...@@ -124,7 +125,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) { ...@@ -124,7 +125,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
EXPECT_EQ('\x01', output.data()[3]); EXPECT_EQ('\x01', output.data()[3]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0xfffffff, output); IDBValueWrapper::WriteVarInt(0xfffffff, output);
ASSERT_EQ(4U, output.size()); ASSERT_EQ(4U, output.size());
EXPECT_EQ('\xff', output.data()[0]); EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\xff', output.data()[1]); EXPECT_EQ('\xff', output.data()[1]);
...@@ -132,7 +133,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) { ...@@ -132,7 +133,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
EXPECT_EQ('\x7f', output.data()[3]); EXPECT_EQ('\x7f', output.data()[3]);
output.clear(); output.clear();
IDBValueWrapper::WriteVarint(0x10000000, output); IDBValueWrapper::WriteVarInt(0x10000000, output);
ASSERT_EQ(5U, output.size()); ASSERT_EQ(5U, output.size());
EXPECT_EQ('\x80', output.data()[0]); EXPECT_EQ('\x80', output.data()[0]);
EXPECT_EQ('\x80', output.data()[1]); EXPECT_EQ('\x80', output.data()[1]);
...@@ -142,7 +143,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) { ...@@ -142,7 +143,7 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
output.clear(); output.clear();
// Maximum value of unsigned on 32-bit platforms. // Maximum value of unsigned on 32-bit platforms.
IDBValueWrapper::WriteVarint(0xffffffff, output); IDBValueWrapper::WriteVarInt(0xffffffff, output);
ASSERT_EQ(5U, output.size()); ASSERT_EQ(5U, output.size());
EXPECT_EQ('\xff', output.data()[0]); EXPECT_EQ('\xff', output.data()[0]);
EXPECT_EQ('\xff', output.data()[1]); EXPECT_EQ('\xff', output.data()[1]);
...@@ -152,216 +153,322 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) { ...@@ -152,216 +153,322 @@ TEST(IDBValueWrapperTest, WriteVarintMultiByteEdgeCases) {
output.clear(); 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. // Friend class of IDBValueUnwrapper with access to its internals.
class IDBValueUnwrapperReadVarintTestHelper { class IDBValueUnwrapperReadTestHelper {
public: public:
void ReadVarint(const char* start, size_t buffer_size) { void ReadVarInt(const char* start, size_t buffer_size) {
IDBValueUnwrapper unwrapper; IDBValueUnwrapper unwrapper;
const uint8_t* buffer_start = reinterpret_cast<const uint8_t*>(start); const uint8_t* buffer_start = reinterpret_cast<const uint8_t*>(start);
const uint8_t* buffer_end = buffer_start + buffer_size; const uint8_t* buffer_end = buffer_start + buffer_size;
unwrapper.current_ = buffer_start; unwrapper.current_ = buffer_start;
unwrapper.end_ = buffer_end; unwrapper.end_ = buffer_end;
success_ = unwrapper.ReadVarint(read_value_); success_ = unwrapper.ReadVarInt(read_varint_);
ASSERT_EQ(unwrapper.end_, buffer_end) 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_) 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; consumed_bytes_ = unwrapper.current_ - buffer_start;
} }
bool success() { return success_; } bool success() { return success_; }
unsigned consumed_bytes() { return consumed_bytes_; } 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: private:
bool success_; bool success_;
unsigned consumed_bytes_; unsigned consumed_bytes_;
unsigned read_value_; unsigned read_varint_;
Vector<uint8_t> read_bytes_;
}; };
TEST(IDBValueUnwrapperTest, ReadVarintOneByte) { TEST(IDBValueUnwrapperTest, ReadVarIntOneByte) {
IDBValueUnwrapperReadVarintTestHelper helper; IDBValueUnwrapperReadTestHelper helper;
// Most test cases have an extra byte at the end of the input to verify that // Most test cases have an extra byte at the end of the input to verify that
// the parser doesn't consume too much data. // the parser doesn't consume too much data.
helper.ReadVarint("\x00\x01", 2); helper.ReadVarInt("\x00\x01", 2);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_value()); EXPECT_EQ(0U, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes()); EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadVarint("\x01\x01", 2); helper.ReadVarInt("\x01\x01", 2);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(1U, helper.read_value()); EXPECT_EQ(1U, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes()); EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadVarint("\x7f\x01", 2); helper.ReadVarInt("\x7f\x01", 2);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x7fU, helper.read_value()); EXPECT_EQ(0x7fU, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes()); EXPECT_EQ(1U, helper.consumed_bytes());
helper.ReadVarint("\x7f\x01", 1); helper.ReadVarInt("\x7f\x01", 1);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x7fU, helper.read_value()); EXPECT_EQ(0x7fU, helper.read_varint());
EXPECT_EQ(1U, helper.consumed_bytes()); EXPECT_EQ(1U, helper.consumed_bytes());
} }
TEST(IDBValueUnwrapperTest, ReadVarintMultiBytes) { TEST(IDBValueUnwrapperTest, ReadVarIntMultiBytes) {
IDBValueUnwrapperReadVarintTestHelper helper; IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\xff\x01\x01", 3); helper.ReadVarInt("\xff\x01\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0xffU, helper.read_value()); EXPECT_EQ(0xffU, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\x80\x02\x01", 3); helper.ReadVarInt("\x80\x02\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x100U, helper.read_value()); EXPECT_EQ(0x100U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xb4\x24\x01", 3); helper.ReadVarInt("\xb4\x24\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x1234U, helper.read_value()); EXPECT_EQ(0x1234U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0xabcdU, helper.read_value()); EXPECT_EQ(0xabcdU, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x123456U, helper.read_value()); EXPECT_EQ(0x123456U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x123456U, helper.read_value()); EXPECT_EQ(0x123456U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0xabcdefU, helper.read_value()); EXPECT_EQ(0xabcdefU, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0xabcdefU, helper.read_value()); EXPECT_EQ(0xabcdefU, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes()); EXPECT_EQ(4U, helper.consumed_bytes());
} }
TEST(IDBValueUnwrapperTest, ReadVarintMultiByteEdgeCases) { TEST(IDBValueUnwrapperTest, ReadVarIntMultiByteEdgeCases) {
IDBValueUnwrapperReadVarintTestHelper helper; IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\x80\x01\x01", 3); helper.ReadVarInt("\x80\x01\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x80U, helper.read_value()); EXPECT_EQ(0x80U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xff\x7f\x01", 3); helper.ReadVarInt("\xff\x7f\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x3fffU, helper.read_value()); EXPECT_EQ(0x3fffU, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x4000U, helper.read_value()); EXPECT_EQ(0x4000U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x1fffffU, helper.read_value()); EXPECT_EQ(0x1fffffU, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x200000U, helper.read_value()); EXPECT_EQ(0x200000U, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0xfffffffU, helper.read_value()); EXPECT_EQ(0xfffffffU, helper.read_varint());
EXPECT_EQ(4U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x10000000U, helper.read_value()); EXPECT_EQ(0x10000000U, helper.read_varint());
EXPECT_EQ(5U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0xffffffffU, helper.read_value()); EXPECT_EQ(0xffffffffU, helper.read_varint());
EXPECT_EQ(5U, helper.consumed_bytes()); EXPECT_EQ(5U, helper.consumed_bytes());
} }
TEST(IDBValueUnwrapperTest, ReadVarintTruncatedInput) { TEST(IDBValueUnwrapperTest, ReadVarIntTruncatedInput) {
IDBValueUnwrapperReadVarintTestHelper helper; IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\x01", 0); helper.ReadVarInt("\x01", 0);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
helper.ReadVarint("\x80\x01", 1); helper.ReadVarInt("\x80\x01", 1);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
helper.ReadVarint("\xff\x01", 1); helper.ReadVarInt("\xff\x01", 1);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
helper.ReadVarint("\x80\x80\x01", 2); helper.ReadVarInt("\x80\x80\x01", 2);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
helper.ReadVarint("\xff\xff\x01", 2); helper.ReadVarInt("\xff\xff\x01", 2);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
helper.ReadVarint("\x80\x80\x80\x80\x01", 4); helper.ReadVarInt("\x80\x80\x80\x80\x01", 4);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
helper.ReadVarint("\xff\xff\xff\xff\x01", 4); helper.ReadVarInt("\xff\xff\xff\xff\x01", 4);
EXPECT_FALSE(helper.success()); EXPECT_FALSE(helper.success());
} }
TEST(IDBValueUnwrapperTest, ReadVarintDenormalizedInput) { TEST(IDBValueUnwrapperTest, ReadVarIntDenormalizedInput) {
IDBValueUnwrapperReadVarintTestHelper helper; IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint("\x80\x00\x01", 3); helper.ReadVarInt("\x80\x00\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_value()); EXPECT_EQ(0U, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); EXPECT_EQ(2U, helper.consumed_bytes());
helper.ReadVarint("\xff\x00\x01", 3); helper.ReadVarInt("\xff\x00\x01", 3);
EXPECT_TRUE(helper.success()); EXPECT_TRUE(helper.success());
EXPECT_EQ(0x7fU, helper.read_value()); EXPECT_EQ(0x7fU, helper.read_varint());
EXPECT_EQ(2U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0U, helper.read_value()); EXPECT_EQ(0U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x3f80U, helper.read_value()); EXPECT_EQ(0x3f80U, helper.read_varint());
EXPECT_EQ(3U, helper.consumed_bytes()); 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_TRUE(helper.success());
EXPECT_EQ(0x0fe03f80U, helper.read_value()); EXPECT_EQ(0x0fe03f80U, helper.read_varint());
EXPECT_EQ(5U, helper.consumed_bytes()); EXPECT_EQ(5U, helper.consumed_bytes());
} }
TEST(IDBValueUnwrapperTest, WriteVarintMaxUnsignedRoundtrip) { TEST(IDBValueUnwrapperTest, WriteVarIntMaxUnsignedRoundtrip) {
unsigned max_value = std::numeric_limits<unsigned>::max(); unsigned max_value = std::numeric_limits<unsigned>::max();
Vector<char> output; Vector<char> output;
IDBValueWrapper::WriteVarint(max_value, output); IDBValueWrapper::WriteVarInt(max_value, output);
IDBValueUnwrapperReadVarintTestHelper helper; IDBValueUnwrapperReadTestHelper helper;
helper.ReadVarint(output.data(), output.size()); helper.ReadVarInt(output.data(), output.size());
EXPECT_TRUE(helper.success()); 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()); 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) { TEST(IDBValueUnwrapperTest, IsWrapped) {
V8TestingScope scope; V8TestingScope scope;
NonThrowableExceptionState non_throwable_exception_state; NonThrowableExceptionState non_throwable_exception_state;
...@@ -369,12 +476,12 @@ TEST(IDBValueUnwrapperTest, IsWrapped) { ...@@ -369,12 +476,12 @@ TEST(IDBValueUnwrapperTest, IsWrapped) {
IDBValueWrapper wrapper(scope.GetIsolate(), v8_true, IDBValueWrapper wrapper(scope.GetIsolate(), v8_true,
SerializedScriptValue::SerializeOptions::kSerialize, SerializedScriptValue::SerializeOptions::kSerialize,
non_throwable_exception_state); non_throwable_exception_state);
wrapper.DoneCloning();
wrapper.WrapIfBiggerThan(0); wrapper.WrapIfBiggerThan(0);
Vector<scoped_refptr<BlobDataHandle>> blob_data_handles; Vector<scoped_refptr<BlobDataHandle>> blob_data_handles =
wrapper.ExtractBlobDataHandles(&blob_data_handles); wrapper.TakeBlobDataHandles();
Vector<WebBlobInfo>& blob_infos = wrapper.WrappedBlobInfo(); Vector<WebBlobInfo> blob_infos = wrapper.TakeBlobInfo();
scoped_refptr<SharedBuffer> wrapped_marker_buffer = scoped_refptr<SharedBuffer> wrapped_marker_buffer = wrapper.TakeWireBytes();
wrapper.ExtractWireBytes();
IDBKey* key = IDBKey::CreateNumber(42.0); IDBKey* key = IDBKey::CreateNumber(42.0);
IDBKeyPath key_path(String("primaryKey")); 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