| Index: src/value-serializer.cc
|
| diff --git a/src/value-serializer.cc b/src/value-serializer.cc
|
| index cf76ad25ed9d7e1887f52644a12f2bd3df524ff9..8358b66b66d9f79dfc9e885e05865b14e341bc44 100644
|
| --- a/src/value-serializer.cc
|
| +++ b/src/value-serializer.cc
|
| @@ -18,6 +18,18 @@ namespace internal {
|
|
|
| static const uint32_t kLatestVersion = 9;
|
|
|
| +template <typename T>
|
| +static size_t BytesNeededForVarint(T value) {
|
| + static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
|
| + "Only unsigned integer types can be written as varints.");
|
| + size_t result = 0;
|
| + do {
|
| + result++;
|
| + value >>= 7;
|
| + } while (value);
|
| + return result;
|
| +}
|
| +
|
| enum class SerializationTag : uint8_t {
|
| // version:uint32_t (if at beginning of data, sets version > 0)
|
| kVersion = 0xFF,
|
| @@ -39,6 +51,9 @@ enum class SerializationTag : uint8_t {
|
| // Number represented as a 64-bit double.
|
| // Host byte order is used (N.B. this makes the format non-portable).
|
| kDouble = 'N',
|
| + // byteLength:uint32_t, then raw data
|
| + kUtf8String = 'S',
|
| + kTwoByteString = 'c',
|
| };
|
|
|
| ValueSerializer::ValueSerializer() {}
|
| @@ -92,6 +107,24 @@ void ValueSerializer::WriteDouble(double value) {
|
| reinterpret_cast<const uint8_t*>(&value + 1));
|
| }
|
|
|
| +void ValueSerializer::WriteOneByteString(Vector<const uint8_t> chars) {
|
| + WriteVarint<uint32_t>(chars.length());
|
| + buffer_.insert(buffer_.end(), chars.begin(), chars.end());
|
| +}
|
| +
|
| +void ValueSerializer::WriteTwoByteString(Vector<const uc16> chars) {
|
| + // Warning: this uses host endianness.
|
| + WriteVarint<uint32_t>(chars.length() * sizeof(uc16));
|
| + buffer_.insert(buffer_.end(), reinterpret_cast<const uint8_t*>(chars.begin()),
|
| + reinterpret_cast<const uint8_t*>(chars.end()));
|
| +}
|
| +
|
| +uint8_t* ValueSerializer::ReserveRawBytes(size_t bytes) {
|
| + auto old_size = buffer_.size();
|
| + buffer_.resize(buffer_.size() + bytes);
|
| + return &buffer_[old_size];
|
| +}
|
| +
|
| Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
|
| if (object->IsSmi()) {
|
| WriteSmi(Smi::cast(*object));
|
| @@ -108,6 +141,10 @@ Maybe<bool> ValueSerializer::WriteObject(Handle<Object> object) {
|
| WriteHeapNumber(HeapNumber::cast(*object));
|
| return Just(true);
|
| default:
|
| + if (object->IsString()) {
|
| + WriteString(Handle<String>::cast(object));
|
| + return Just(true);
|
| + }
|
| UNIMPLEMENTED();
|
| return Nothing<bool>();
|
| }
|
| @@ -146,6 +183,41 @@ void ValueSerializer::WriteHeapNumber(HeapNumber* number) {
|
| WriteDouble(number->value());
|
| }
|
|
|
| +void ValueSerializer::WriteString(Handle<String> string) {
|
| + string = String::Flatten(string);
|
| + DisallowHeapAllocation no_gc;
|
| + String::FlatContent flat = string->GetFlatContent();
|
| + DCHECK(flat.IsFlat());
|
| + if (flat.IsOneByte()) {
|
| + // The existing format uses UTF-8, rather than Latin-1. As a result we must
|
| + // to do work to encode strings that have characters outside ASCII.
|
| + // TODO(jbroman): In a future format version, consider adding a tag for
|
| + // Latin-1 strings, so that this can be skipped.
|
| + WriteTag(SerializationTag::kUtf8String);
|
| + Vector<const uint8_t> chars = flat.ToOneByteVector();
|
| + if (String::IsAscii(chars.begin(), chars.length())) {
|
| + WriteOneByteString(chars);
|
| + } else {
|
| + v8::Local<v8::String> api_string = Utils::ToLocal(string);
|
| + uint32_t utf8_length = api_string->Utf8Length();
|
| + WriteVarint(utf8_length);
|
| + api_string->WriteUtf8(
|
| + reinterpret_cast<char*>(ReserveRawBytes(utf8_length)), utf8_length,
|
| + nullptr, v8::String::NO_NULL_TERMINATION);
|
| + }
|
| + } else if (flat.IsTwoByte()) {
|
| + Vector<const uc16> chars = flat.ToUC16Vector();
|
| + uint32_t byte_length = chars.length() * sizeof(uc16);
|
| + // The existing reading code expects 16-byte strings to be aligned.
|
| + if ((buffer_.size() + 1 + BytesNeededForVarint(byte_length)) & 1)
|
| + WriteTag(SerializationTag::kPadding);
|
| + WriteTag(SerializationTag::kTwoByteString);
|
| + WriteTwoByteString(chars);
|
| + } else {
|
| + UNREACHABLE();
|
| + }
|
| +}
|
| +
|
| ValueDeserializer::ValueDeserializer(Isolate* isolate,
|
| Vector<const uint8_t> data)
|
| : isolate_(isolate),
|
| @@ -223,6 +295,13 @@ Maybe<double> ValueDeserializer::ReadDouble() {
|
| return Just(value);
|
| }
|
|
|
| +Maybe<Vector<const uint8_t>> ValueDeserializer::ReadRawBytes(int size) {
|
| + if (size > end_ - position_) return Nothing<Vector<const uint8_t>>();
|
| + const uint8_t* start = position_;
|
| + position_ += size;
|
| + return Just(Vector<const uint8_t>(start, size));
|
| +}
|
| +
|
| MaybeHandle<Object> ValueDeserializer::ReadObject() {
|
| SerializationTag tag;
|
| if (!ReadTag().To(&tag)) return MaybeHandle<Object>();
|
| @@ -254,10 +333,49 @@ MaybeHandle<Object> ValueDeserializer::ReadObject() {
|
| if (number.IsNothing()) return MaybeHandle<Object>();
|
| return isolate_->factory()->NewNumber(number.FromJust());
|
| }
|
| + case SerializationTag::kUtf8String:
|
| + return ReadUtf8String();
|
| + case SerializationTag::kTwoByteString:
|
| + return ReadTwoByteString();
|
| default:
|
| return MaybeHandle<Object>();
|
| }
|
| }
|
|
|
| +MaybeHandle<String> ValueDeserializer::ReadUtf8String() {
|
| + uint32_t utf8_length;
|
| + Vector<const uint8_t> utf8_bytes;
|
| + if (!ReadVarint<uint32_t>().To(&utf8_length) ||
|
| + utf8_length >
|
| + static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
|
| + !ReadRawBytes(utf8_length).To(&utf8_bytes))
|
| + return MaybeHandle<String>();
|
| + return isolate_->factory()->NewStringFromUtf8(
|
| + Vector<const char>::cast(utf8_bytes));
|
| +}
|
| +
|
| +MaybeHandle<String> ValueDeserializer::ReadTwoByteString() {
|
| + uint32_t byte_length;
|
| + Vector<const uint8_t> bytes;
|
| + if (!ReadVarint<uint32_t>().To(&byte_length) ||
|
| + byte_length >
|
| + static_cast<uint32_t>(std::numeric_limits<int32_t>::max()) ||
|
| + byte_length % sizeof(uc16) != 0 || !ReadRawBytes(byte_length).To(&bytes))
|
| + return MaybeHandle<String>();
|
| +
|
| + // Allocate an uninitialized string so that we can do a raw memcpy into the
|
| + // string on the heap (regardless of alignment).
|
| + Handle<SeqTwoByteString> string;
|
| + if (!isolate_->factory()
|
| + ->NewRawTwoByteString(byte_length / sizeof(uc16))
|
| + .ToHandle(&string))
|
| + return MaybeHandle<String>();
|
| +
|
| + // Copy the bytes directly into the new string.
|
| + // Warning: this uses host endianness.
|
| + memcpy(string->GetChars(), bytes.begin(), bytes.length());
|
| + return string;
|
| +}
|
| +
|
| } // namespace internal
|
| } // namespace v8
|
|
|