IDBSerialization.cpp [plain text]
#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);
static const uint8_t legacySerializedKeyVersion = 'b';
if (data[0] == legacySerializedKeyVersion)
return true;
#elif USE(GLIB)
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;
}
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::create(WTFMove(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;
StringVector<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::create(WTFMove(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);
}
const uint8_t* current = data;
const uint8_t* end = data + size;
if (current++[0] != SIDBKeyVersion)
return false;
if (decodeKey(current, end, result)) {
return current == end;
}
return false;
}
}
#endif // ENABLE(INDEXED_DATABASE)