blob: a8f7927b4974796c7e883bc7fdd58492eeb8fd93 [file] [edit]
/*
* Copyright (C) 2010-2020 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.
*/
#pragma once
#include <utility>
#include <wtf/CheckedArithmetic.h>
#include <wtf/Forward.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Vector.h>
#include <wtf/persistence/PersistentDecoder.h>
#include <wtf/persistence/PersistentEncoder.h>
namespace WTF::Persistence {
template<typename> struct Coder;
class Decoder;
class Encoder;
template<typename T, typename U> struct Coder<std::pair<T, U>> {
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const std::pair<T, U>& pair)
{
encoder << pair.first << pair.second;
}
template<typename Decoder>
static std::optional<std::pair<T, U>> decodeForPersistence(Decoder& decoder)
{
std::optional<T> first;
decoder >> first;
if (!first)
return std::nullopt;
std::optional<U> second;
decoder >> second;
if (!second)
return std::nullopt;
return {{ WTF::move(*first), WTF::move(*second) }};
}
};
template<typename T> struct Coder<std::optional<T>> {
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const std::optional<T>& optional)
{
if (!optional) {
encoder << false;
return;
}
encoder << true;
encoder << optional.value();
}
template<typename Decoder>
static std::optional<std::optional<T>> decodeForPersistence(Decoder& decoder)
{
std::optional<bool> isEngaged;
decoder >> isEngaged;
if (!isEngaged)
return std::nullopt;
if (!*isEngaged)
return std::optional<std::optional<T>> { std::optional<T> { std::nullopt } };
std::optional<T> value;
decoder >> value;
if (!value)
return std::nullopt;
return std::optional<std::optional<T>> { std::optional<T> { WTF::move(*value) } };
}
};
template<typename KeyType, typename ValueType> struct Coder<WTF::KeyValuePair<KeyType, ValueType>> {
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const WTF::KeyValuePair<KeyType, ValueType>& pair)
{
encoder << pair.key << pair.value;
}
template<typename Decoder>
static std::optional<WTF::KeyValuePair<KeyType, ValueType>> decodeForPersistence(Decoder& decoder)
{
std::optional<KeyType> key;
decoder >> key;
if (!key)
return std::nullopt;
std::optional<ValueType> value;
decoder >>value;
if (!value)
return std::nullopt;
return {{ WTF::move(*key), WTF::move(*value) }};
}
};
template<bool fixedSizeElements, typename T, size_t inlineCapacity> struct VectorCoder;
template<typename T, size_t inlineCapacity> struct VectorCoder<false, T, inlineCapacity> {
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const Vector<T, inlineCapacity>& vector)
{
encoder << static_cast<uint64_t>(vector.size());
for (size_t i = 0; i < vector.size(); ++i)
encoder << vector[i];
}
template<typename Decoder>
static std::optional<Vector<T, inlineCapacity>> decodeForPersistence(Decoder& decoder)
{
std::optional<uint64_t> size;
decoder >> size;
if (!size)
return std::nullopt;
Vector<T, inlineCapacity> tmp;
for (size_t i = 0; i < *size; ++i) {
std::optional<T> element;
decoder >> element;
if (!element)
return std::nullopt;
tmp.append(WTF::move(*element));
}
tmp.shrinkToFit();
return tmp;
}
};
template<typename T, size_t inlineCapacity> struct VectorCoder<true, T, inlineCapacity> {
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const Vector<T, inlineCapacity>& vector)
{
encoder << static_cast<uint64_t>(vector.size());
encoder.encodeFixedLengthData(asBytes(vector.span()));
}
template<typename Decoder>
static std::optional<Vector<T, inlineCapacity>> decodeForPersistence(Decoder& decoder)
{
std::optional<uint64_t> decodedSize;
decoder >> decodedSize;
if (!decodedSize)
return std::nullopt;
if (!isInBounds<size_t>(*decodedSize))
return std::nullopt;
auto size = static_cast<size_t>(*decodedSize);
// Since we know the total size of the elements, we can allocate the vector in
// one fell swoop. Before allocating we must however make sure that the decoder buffer
// is big enough.
if (!decoder.template bufferIsLargeEnoughToContain<T>(size))
return std::nullopt;
Vector<T, inlineCapacity> temp;
temp.grow(size);
if (!decoder.decodeFixedLengthData(temp.mutableSpan()))
return std::nullopt;
return temp;
}
};
template<typename T, size_t inlineCapacity> struct Coder<Vector<T, inlineCapacity>> : VectorCoder<std::is_arithmetic<T>::value, T, inlineCapacity> { };
template<typename KeyArg, typename MappedArg, typename HashArg, typename KeyTraitsArg, typename MappedTraitsArg> struct Coder<HashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg>> {
typedef HashMap<KeyArg, MappedArg, HashArg, KeyTraitsArg, MappedTraitsArg> HashMapType;
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const HashMapType& hashMap)
{
encoder << static_cast<uint64_t>(hashMap.size());
for (typename HashMapType::const_iterator it = hashMap.begin(), end = hashMap.end(); it != end; ++it)
encoder << *it;
}
template<typename Decoder>
static std::optional<HashMapType> decodeForPersistence(Decoder& decoder)
{
std::optional<uint64_t> hashMapSize;
decoder >> hashMapSize;
if (!hashMapSize)
return std::nullopt;
HashMapType tempHashMap;
tempHashMap.reserveInitialCapacity(static_cast<unsigned>(*hashMapSize));
for (uint64_t i = 0; i < *hashMapSize; ++i) {
std::optional<KeyArg> key;
decoder >> key;
if (!key)
return std::nullopt;
std::optional<MappedArg> value;
decoder >> value;
if (!value)
return std::nullopt;
if (!tempHashMap.add(WTF::move(*key), WTF::move(*value)).isNewEntry) {
// The hash map already has the specified key, bail.
return std::nullopt;
}
}
return tempHashMap;
}
};
template<typename KeyArg, typename HashArg, typename KeyTraitsArg> struct Coder<HashSet<KeyArg, HashArg, KeyTraitsArg>> {
typedef HashSet<KeyArg, HashArg, KeyTraitsArg> HashSetType;
template<typename Encoder>
static void encodeForPersistence(Encoder& encoder, const HashSetType& hashSet)
{
encoder << static_cast<uint64_t>(hashSet.size());
for (typename HashSetType::const_iterator it = hashSet.begin(), end = hashSet.end(); it != end; ++it)
encoder << *it;
}
template<typename Decoder>
static std::optional<HashSetType> decodeForPersistence(Decoder& decoder)
{
std::optional<uint64_t> hashSetSize;
decoder >> hashSetSize;
if (!hashSetSize)
return std::nullopt;
HashSetType tempHashSet;
for (uint64_t i = 0; i < *hashSetSize; ++i) {
std::optional<KeyArg> key;
decoder >> key;
if (!key)
return std::nullopt;
if (!tempHashSet.add(WTF::move(*key)).isNewEntry) {
// The hash map already has the specified key, bail.
return std::nullopt;
}
}
return tempHashSet;
}
};
template<typename... Types> struct Coder<Variant<Types...>> {
using PersistentEncodedVariantIndex = uint8_t;
template<typename Encoder, typename T>
static void encodeForPersistence(Encoder& encoder, T&& variant)
{
static_assert(std::is_same_v<std::remove_cvref_t<T>, Variant<Types...>>);
static_assert(sizeof...(Types) <= static_cast<size_t>(std::numeric_limits<PersistentEncodedVariantIndex>::max()));
PersistentEncodedVariantIndex i = variant.index();
encoder << i;
encodeForPersistence(encoder, std::forward<T>(variant), std::index_sequence<> { }, i);
}
template<typename Encoder, typename T, size_t... Indices>
static void encodeForPersistence(Encoder& encoder, T&& variant, std::index_sequence<Indices...>, size_t i)
{
constexpr size_t index = sizeof...(Indices);
if constexpr (index < sizeof...(Types)) {
if (index == i) {
encoder << std::get<index>(std::forward<T>(variant));
return;
}
encodeForPersistence(encoder, std::forward<T>(variant), std::make_index_sequence<index + 1> { }, i);
}
}
template<typename Decoder>
static std::optional<Variant<Types...>> decodeForPersistence(Decoder& decoder)
{
std::optional<PersistentEncodedVariantIndex> i;
decoder >> i;
if (!i || *i >= sizeof...(Types))
return std::nullopt;
return decodeForPersistence(decoder, std::index_sequence<> { }, *i);
}
template<typename Decoder, size_t... Indices>
static std::optional<Variant<Types...>> decodeForPersistence(Decoder& decoder, std::index_sequence<Indices...>, size_t i)
{
constexpr size_t index = sizeof...(Indices);
if constexpr (index < sizeof...(Types)) {
if (index == i) {
std::optional<typename WTF::VariantAlternativeT<index, Variant<Types...>>> optional;
decoder >> optional;
if (!optional)
return std::nullopt;
return std::make_optional<Variant<Types...>>(WTF::InPlaceIndex<index>, WTF::move(*optional));
}
return decodeForPersistence(decoder, std::make_index_sequence<index + 1> { }, i);
} else
return std::nullopt;
}
};
#define DECLARE_CODER(class) \
template<> struct Coder<class> { \
WTF_EXPORT_PRIVATE static void encodeForPersistence(Encoder&, const class&); \
WTF_EXPORT_PRIVATE static std::optional<class> decodeForPersistence(Decoder&); \
}
DECLARE_CODER(AtomString);
DECLARE_CODER(CString);
DECLARE_CODER(Seconds);
DECLARE_CODER(String);
DECLARE_CODER(SHA1::Digest);
DECLARE_CODER(URL);
DECLARE_CODER(WallTime);
#undef DECLARE_CODER
}