diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp')
-rw-r--r-- | Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp new file mode 100644 index 000000000..4806d5cc5 --- /dev/null +++ b/Source/WebCore/Modules/indexeddb/server/IDBSerialization.cpp @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2014, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "config.h" +#include "IDBSerialization.h" + +#if ENABLE(INDEXED_DATABASE) + +#include "IDBKeyData.h" +#include "IDBKeyPath.h" +#include "KeyedCoding.h" + +#if USE(GLIB) +#include <glib.h> +#include <wtf/glib/GRefPtr.h> +#endif + +namespace WebCore { + +enum class KeyPathType { Null, String, Array }; + +RefPtr<SharedBuffer> serializeIDBKeyPath(const std::optional<IDBKeyPath>& keyPath) +{ + auto encoder = KeyedEncoder::encoder(); + + if (keyPath) { + auto visitor = WTF::makeVisitor([&](const String& string) { + encoder->encodeEnum("type", KeyPathType::String); + encoder->encodeString("string", string); + }, [&](const Vector<String>& vector) { + encoder->encodeEnum("type", KeyPathType::Array); + encoder->encodeObjects("array", vector.begin(), vector.end(), [](WebCore::KeyedEncoder& encoder, const String& string) { + encoder.encodeString("string", string); + }); + }); + WTF::visit(visitor, keyPath.value()); + } else + encoder->encodeEnum("type", KeyPathType::Null); + + return encoder->finishEncoding(); +} + +bool deserializeIDBKeyPath(const uint8_t* data, size_t size, std::optional<IDBKeyPath>& result) +{ + if (!data || !size) + return false; + + auto decoder = KeyedDecoder::decoder(data, size); + + KeyPathType type; + bool succeeded = decoder->decodeEnum("type", type, [](KeyPathType value) { + return value == KeyPathType::Null || value == KeyPathType::String || value == KeyPathType::Array; + }); + if (!succeeded) + return false; + + switch (type) { + case KeyPathType::Null: + break; + case KeyPathType::String: { + String string; + if (!decoder->decodeString("string", string)) + return false; + result = IDBKeyPath(WTFMove(string)); + break; + } + case KeyPathType::Array: { + Vector<String> vector; + succeeded = decoder->decodeObjects("array", vector, [](KeyedDecoder& decoder, String& result) { + return decoder.decodeString("string", result); + }); + if (!succeeded) + return false; + result = IDBKeyPath(WTFMove(vector)); + break; + } + } + return true; +} + +static bool isLegacySerializedIDBKeyData(const uint8_t* data, size_t size) +{ +#if USE(CF) + UNUSED_PARAM(size); + + // This is the magic character that begins serialized PropertyLists, and tells us whether + // the key we're looking at is an old-style key. + static const uint8_t legacySerializedKeyVersion = 'b'; + if (data[0] == legacySerializedKeyVersion) + return true; +#elif USE(GLIB) + // KeyedEncoderGLib uses a GVariant dictionary, so check if the given data is a valid GVariant dictionary. + GRefPtr<GBytes> bytes = adoptGRef(g_bytes_new(data, size)); + GRefPtr<GVariant> variant = g_variant_new_from_bytes(G_VARIANT_TYPE("a{sv}"), bytes.get(), FALSE); + return g_variant_is_normal_form(variant.get()); +#endif + return false; +} + + +/* +The IDBKeyData serialization format is as follows: +[1 byte version header][Key Buffer] + +The Key Buffer serialization format is as follows: +[1 byte key type][Type specific data] + +Type specific serialization formats are as follows for each of the types: +Min: +[0 bytes] + +Number: +[8 bytes representing a double encoded in little endian] + +Date: +[8 bytes representing a double encoded in little endian] + +String: +[4 bytes representing string "length" in little endian]["length" number of 2-byte pairs representing ECMAScript 16-bit code units] + +Binary: +[8 bytes representing the "size" of the binary blob]["size" bytes] + +Array: +[8 bytes representing the "length" of the key array]["length" individual Key Buffer entries] + +Max: +[0 bytes] +*/ + +static const uint8_t SIDBKeyVersion = 0x00; +enum class SIDBKeyType : uint8_t { + Min = 0x00, + Number = 0x20, + Date = 0x40, + String = 0x60, + Binary = 0x80, + Array = 0xA0, + Max = 0xFF, +}; + +static SIDBKeyType serializedTypeForKeyType(IndexedDB::KeyType type) +{ + switch (type) { + case IndexedDB::KeyType::Min: + return SIDBKeyType::Min; + case IndexedDB::KeyType::Number: + return SIDBKeyType::Number; + case IndexedDB::KeyType::Date: + return SIDBKeyType::Date; + case IndexedDB::KeyType::String: + return SIDBKeyType::String; + case IndexedDB::KeyType::Binary: + return SIDBKeyType::Binary; + case IndexedDB::KeyType::Array: + return SIDBKeyType::Array; + case IndexedDB::KeyType::Max: + return SIDBKeyType::Max; + case IndexedDB::KeyType::Invalid: + RELEASE_ASSERT_NOT_REACHED(); + }; + + RELEASE_ASSERT_NOT_REACHED(); +} + +#if CPU(BIG_ENDIAN) || CPU(MIDDLE_ENDIAN) || CPU(NEEDS_ALIGNED_ACCESS) +template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value) +{ + for (unsigned i = 0; i < sizeof(T); i++) { + buffer.append(value & 0xFF); + value >>= 8; + } +} + +template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value) +{ + if (ptr > end - sizeof(value)) + return false; + + value = 0; + for (size_t i = 0; i < sizeof(T); i++) + value += ((T)*ptr++) << (i * 8); + return true; +} +#else +template <typename T> static void writeLittleEndian(Vector<char>& buffer, T value) +{ + buffer.append(reinterpret_cast<uint8_t*>(&value), sizeof(value)); +} + +template <typename T> static bool readLittleEndian(const uint8_t*& ptr, const uint8_t* end, T& value) +{ + if (ptr > end - sizeof(value)) + return false; + + value = *reinterpret_cast<const T*>(ptr); + ptr += sizeof(T); + + return true; +} +#endif + +static void writeDouble(Vector<char>& data, double d) +{ + writeLittleEndian(data, *reinterpret_cast<uint64_t*>(&d)); +} + +static bool readDouble(const uint8_t*& data, const uint8_t* end, double& d) +{ + return readLittleEndian(data, end, *reinterpret_cast<uint64_t*>(&d)); +} + +static void encodeKey(Vector<char>& data, const IDBKeyData& key) +{ + SIDBKeyType type = serializedTypeForKeyType(key.type()); + data.append(static_cast<char>(type)); + + switch (type) { + case SIDBKeyType::Number: + writeDouble(data, key.number()); + break; + case SIDBKeyType::Date: + writeDouble(data, key.date()); + break; + case SIDBKeyType::String: { + auto string = key.string(); + uint32_t length = string.length(); + writeLittleEndian(data, length); + + for (size_t i = 0; i < length; ++i) + writeLittleEndian(data, string[i]); + + break; + } + case SIDBKeyType::Binary: { + auto& buffer = key.binary(); + uint64_t size = buffer.size(); + writeLittleEndian(data, size); + + auto* bufferData = buffer.data(); + ASSERT(bufferData || !size); + if (bufferData) + data.append(bufferData->data(), bufferData->size()); + + break; + } + case SIDBKeyType::Array: { + auto& array = key.array(); + uint64_t size = array.size(); + writeLittleEndian(data, size); + for (auto& key : array) + encodeKey(data, key); + + break; + } + case SIDBKeyType::Min: + case SIDBKeyType::Max: + break; + } +} + +RefPtr<SharedBuffer> serializeIDBKeyData(const IDBKeyData& key) +{ + Vector<char> data; + data.append(SIDBKeyVersion); + + encodeKey(data, key); + return SharedBuffer::adoptVector(data); +} + +static bool decodeKey(const uint8_t*& data, const uint8_t* end, IDBKeyData& result) +{ + if (!data || data >= end) + return false; + + SIDBKeyType type = static_cast<SIDBKeyType>(data++[0]); + switch (type) { + case SIDBKeyType::Min: + result = IDBKeyData::minimum(); + return true; + case SIDBKeyType::Max: + result = IDBKeyData::maximum(); + return true; + case SIDBKeyType::Number: { + double d; + if (!readDouble(data, end, d)) + return false; + + result.setNumberValue(d); + return true; + } + case SIDBKeyType::Date: { + double d; + if (!readDouble(data, end, d)) + return false; + + result.setDateValue(d); + return true; + } + case SIDBKeyType::String: { + uint32_t length; + if (!readLittleEndian(data, end, length)) + return false; + + if (static_cast<uint64_t>(end - data) < length * 2) + return false; + + Vector<UChar> buffer; + buffer.reserveInitialCapacity(length); + for (size_t i = 0; i < length; i++) { + uint16_t ch; + if (!readLittleEndian(data, end, ch)) + return false; + buffer.uncheckedAppend(ch); + } + + result.setStringValue(String::adopt(WTFMove(buffer))); + + return true; + } + case SIDBKeyType::Binary: { + uint64_t size64; + if (!readLittleEndian(data, end, size64)) + return false; + + if (static_cast<uint64_t>(end - data) < size64) + return false; + + if (size64 > std::numeric_limits<size_t>::max()) + return false; + + size_t size = static_cast<size_t>(size64); + Vector<uint8_t> dataVector; + + dataVector.append(data, size); + data += size; + + result.setBinaryValue(ThreadSafeDataBuffer::adoptVector(dataVector)); + return true; + } + case SIDBKeyType::Array: { + uint64_t size64; + if (!readLittleEndian(data, end, size64)) + return false; + + if (size64 > std::numeric_limits<size_t>::max()) + return false; + + size_t size = static_cast<size_t>(size64); + Vector<IDBKeyData> array; + array.reserveInitialCapacity(size); + + for (size_t i = 0; i < size; ++i) { + IDBKeyData keyData; + if (!decodeKey(data, end, keyData)) + return false; + + ASSERT(keyData.isValid()); + array.uncheckedAppend(WTFMove(keyData)); + } + + result.setArrayValue(array); + + return true; + } + default: + LOG_ERROR("decodeKey encountered unexpected type: %i", (int)type); + return false; + } +} + +bool deserializeIDBKeyData(const uint8_t* data, size_t size, IDBKeyData& result) +{ + if (!data || !size) + return false; + + if (isLegacySerializedIDBKeyData(data, size)) { + auto decoder = KeyedDecoder::decoder(data, size); + return IDBKeyData::decode(*decoder, result); + } + + // Verify this is a SerializedIDBKey version we understand. + const uint8_t* current = data; + const uint8_t* end = data + size; + if (current++[0] != SIDBKeyVersion) + return false; + + if (decodeKey(current, end, result)) { + // Even if we successfully decoded a key, the deserialize is only successful + // if we actually consumed all input data. + return current == end; + } + + return false; +} + +} // namespace WebCore + +#endif // ENABLE(INDEXED_DATABASE) |