Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1834)

Unified Diff: third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp

Issue 2803593005: Avoid unnecessary byte-swapping in blink's SerializedScriptValue. (Closed)
Patch Set: Correct handling for SSV version 0. Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
diff --git a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
index fade5395cbe6467da81cfce877e42040d235579f..e52039d12801369b8f98071192802c2ca3fdfb84 100644
--- a/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
+++ b/third_party/WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp
@@ -35,6 +35,7 @@
#include "bindings/core/v8/DOMWrapperWorld.h"
#include "bindings/core/v8/ExceptionState.h"
#include "bindings/core/v8/ScriptState.h"
+#include "bindings/core/v8/SerializationTag.h"
#include "bindings/core/v8/SerializedScriptValueFactory.h"
#include "bindings/core/v8/Transferables.h"
#include "bindings/core/v8/V8ArrayBuffer.h"
@@ -89,22 +90,108 @@ PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(
return adoptRef(new SerializedScriptValue(data));
}
+// Versions 16 and below (prior to April 2017) used ntohs() to byte-flip SSV
jsbell 2017/04/07 23:40:34 nit: "byte flip" or "byte swap"? "swap" seems more
pwnall 2017/04/08 01:09:08 Done. After reading "byte-swap", my brain instantl
+// data when converting it to the wire format. This was a historical accient.
+//
+// As IndexedDB stores SSVs to disk indefinitely, we still need to keep around
+// the code needed to deserialize the old format.
+inline static bool isByteSwappedWiredData(const char* data, size_t length) {
+ // TODO(pwnall): Bail early if we're on big-endian hardware. Chromium doesn't
+ // currently support big-endian hardware, and there's no header exposing
+ // endianness yet.
jsbell 2017/04/07 23:40:34 ARCH_CPU_LITTLE_ENDIAN seems used in several place
pwnall 2017/04/08 01:09:07 Sadly, it seems like we're not allowed to use it i
+
+ // The first SSV version without byte-flipping has two envelopes (Blink, V8),
+ // each of which is at least 2 bytes long.
+ if (length < 4)
+ return true;
+
+ if (static_cast<uint8_t>(data[0]) == VersionTag) {
jsbell 2017/04/07 23:40:34 Maybe precede this with a list of the different ca
pwnall 2017/04/08 01:09:07 Done.
+ // The only case where byte-flipped data can have 0xFF in byte zero is
+ // serialization version 0. This can only happen if byte one is a tag
+ // (supported in version 0) that takes in extra data, and the first byte of
+ // extra data is 0xFF. There are 13 such tags, listed below. These tags
+ // cannot be used as version numbers in the Blink-side SSV envelope.
jsbell 2017/04/07 23:40:34 nit: remove extra leading space
pwnall 2017/04/08 01:09:07 Done.
+ //
+ // 35 - 0x23 - # - ImageDataTag
+ // 64 - 0x40 - @ - SparseArrayTag
+ // 68 - 0x44 - D - DateTag
+ // 73 - 0x49 - I - Int32Tag
+ // 78 - 0x4E - N - NumberTag
+ // 82 - 0x52 - R - RegExpTag
+ // 83 - 0x53 - S - StringTag
+ // 85 - 0x55 - U - Uint32Tag
+ // 91 - 0x5B - [ - ArrayTag
+ // 98 - 0x62 - b - BlobTag
+ // 102 - 0x66 - f - FileTag
+ // 108 - 0x6C - l - FileListTag
+ // 123 - 0x7B - { - ObjectTag
+ //
+ // Why we care about version 0:
+ //
+ // IndexedDB stores values using the SSV format. Currently, IndexedDB does
+ // not do any sort of migration, so a value written with a SSV version will
+ // be stored with that version until it is removed via an update or delete.
+ //
+ // IndexedDB was shipped in Chrome 11, which was released on April 27, 2011.
+ // SSV version 1 was added in WebKit r91698, which was shipped in Chrome 14,
+ // which was released on September 16, 2011.
+
+ // Fast path until the Blink-side SSV envelope reaches version 35.
+ if (SerializedScriptValue::wireFormatVersion < 35) {
+ if (static_cast<uint8_t>(data[1]) < 35)
+ return false;
+
+ // TODO(pwnall): Add UMA metric here.
+ return true;
+ }
+
+ // Slower path that would kick in after version 35, assuming we don't remove
jbroman 2017/04/11 15:01:47 This is dead code. Is it actually useful to have i
pwnall 2017/04/11 19:10:55 I felt bad leaving a static_assert that'd force a
+ // support for SSV version 0 by then.
+
+ static uint8_t version0Tags[] = {35, 64, 68, 73, 78, 82, 83,
jbroman 2017/04/11 15:01:47 const
pwnall 2017/04/11 19:10:55 Done.
+ 85, 91, 98, 102, 108, 123};
+
+ uint8_t maybeVersion0Tag = static_cast<uint8_t>(data[1]);
+ for (size_t i = 0; i < 13; ++i) {
jsbell 2017/04/07 23:40:34 ARRAY_SIZE(version0Tags) (Also, could binary sear
pwnall 2017/04/08 01:09:08 Done.
jbroman 2017/04/11 15:01:47 This loop is equivalent to: return std::count(std
pwnall 2017/04/11 19:10:55 Done.
+ if (maybeVersion0Tag == version0Tags[i])
+ return true;
+ }
+ return false;
+ }
+
+ if (static_cast<uint8_t>(data[1]) == VersionTag) {
+ // The last SSV format that used byte-flipping was version 16. The version
+ // number is stored (before byte-flipping) after a serialization tag, which
+ // is 0xFF.
+ return static_cast<uint8_t>(data[0]) != VersionTag;
+ }
+
+ // If VersionTag isn't in any of the first two bytes, this is SSV version 0,
+ // which was byte-flipped.
+ return true;
+}
+
PassRefPtr<SerializedScriptValue> SerializedScriptValue::create(
const char* data,
size_t length) {
if (!data)
return create();
- // Decode wire data from big endian to host byte order.
DCHECK(!(length % sizeof(UChar)));
- size_t stringLength = length / sizeof(UChar);
- StringBuffer<UChar> buffer(stringLength);
const UChar* src = reinterpret_cast<const UChar*>(data);
- UChar* dst = buffer.characters();
- for (size_t i = 0; i < stringLength; i++)
- dst[i] = ntohs(src[i]);
+ size_t stringLength = length / sizeof(UChar);
+
+ if (isByteSwappedWiredData(data, length)) {
+ // Decode wire data from big endian to host byte order.
+ StringBuffer<UChar> buffer(stringLength);
+ UChar* dst = buffer.characters();
+ for (size_t i = 0; i < stringLength; ++i)
+ dst[i] = ntohs(src[i]);
+
+ return adoptRef(new SerializedScriptValue(String::adopt(buffer)));
+ }
- return adoptRef(new SerializedScriptValue(String::adopt(buffer)));
+ return adoptRef(new SerializedScriptValue(String(src, stringLength)));
}
SerializedScriptValue::SerializedScriptValue()
@@ -134,8 +221,7 @@ SerializedScriptValue::~SerializedScriptValue() {
}
PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue() {
- // UChar rather than uint8_t here to get host endian behavior.
- static const UChar kNullData[] = {0xff09, 0x3000};
+ static const uint8_t kNullData[] = {0xff, 17, 0xff, 13, '0', 0x00};
jsbell 2017/04/07 23:40:34 Maybe document what this is, while we're here? Is
pwnall 2017/04/08 01:09:07 Done. kNull is not exported from V8, so I couldn'
return create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData));
}
@@ -152,22 +238,17 @@ String SerializedScriptValue::toWireString() const {
return wireString;
}
-// Convert serialized string to big endian wire data.
void SerializedScriptValue::toWireBytes(Vector<char>& result) const {
DCHECK(result.isEmpty());
- size_t wireSizeBytes = (m_dataBufferSize + 1) & ~1;
- result.resize(wireSizeBytes);
-
- const UChar* src = reinterpret_cast<UChar*>(m_dataBuffer.get());
- UChar* dst = reinterpret_cast<UChar*>(result.data());
- for (size_t i = 0; i < m_dataBufferSize / 2; i++)
- dst[i] = htons(src[i]);
+ size_t resultSize = (m_dataBufferSize + 1) & ~1;
jsbell 2017/04/07 23:40:34 How feasible is follow-on work to not expose the r
pwnall 2017/04/08 01:09:08 Seems fairly feasible to me. There aren't a lot of
+ result.resize(resultSize);
+ memcpy(result.data(), m_dataBuffer.get(), m_dataBufferSize);
- // This is equivalent to swapping the byte order of the two bytes (x, 0),
- // depending on endianness.
- if (m_dataBufferSize % 2)
- dst[wireSizeBytes / 2 - 1] = m_dataBuffer[m_dataBufferSize - 1] << 8;
+ if (resultSize > m_dataBufferSize) {
+ DCHECK_EQ(resultSize, m_dataBufferSize + 1);
+ result[m_dataBufferSize] = 0;
+ }
}
static void accumulateArrayBuffersForAllWorlds(

Powered by Google App Engine
This is Rietveld 408576698