serde-generate/runtime/cpp/serde.hpp (498 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates
// SPDX-License-Identifier: MIT OR Apache-2.0
#pragma once
#include <array>
#include <cstdint>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <tuple>
#include <type_traits>
#include <variant>
#include <vector>
namespace serde {
class serialization_error : public std::invalid_argument {
public:
explicit serialization_error(const std::string &what_arg)
: std::invalid_argument(what_arg) {}
explicit serialization_error(const char *what_arg)
: std::invalid_argument(what_arg) {}
};
class deserialization_error : public std::invalid_argument {
public:
explicit deserialization_error(const std::string &what_arg)
: std::invalid_argument(what_arg) {}
explicit deserialization_error(const char *what_arg)
: std::invalid_argument(what_arg) {}
};
// Basic implementation for 128-bit unsigned integers.
struct uint128_t {
uint64_t high;
uint64_t low;
friend bool operator==(const uint128_t &, const uint128_t &);
};
inline bool operator==(const uint128_t &lhs, const uint128_t &rhs) {
return lhs.high == rhs.high && lhs.low == rhs.low;
}
// 128-bit signed integers.
struct int128_t {
int64_t high;
uint64_t low;
friend bool operator==(const int128_t &, const int128_t &);
};
inline bool operator==(const int128_t &lhs, const int128_t &rhs) {
return lhs.high == rhs.high && lhs.low == rhs.low;
}
// A copyable unique_ptr with value semantics.
// Freely inspired by the following discussion:
// https://codereview.stackexchange.com/questions/103744/deepptr-a-deep-copying-unique-ptr-wrapper-in-c
template <typename T>
class value_ptr {
public:
value_ptr() : ptr_(nullptr) {}
value_ptr(const T &value) : ptr_(new T{value}) {}
value_ptr(const value_ptr &other) : ptr_(nullptr) {
if (other) {
ptr_ = std::unique_ptr<T>{new T{*other}};
}
}
value_ptr &operator=(const value_ptr &other) {
value_ptr temp{other};
std::swap(ptr_, temp.ptr_);
return *this;
}
value_ptr(value_ptr &&other) = default;
value_ptr &operator=(value_ptr &&other) = default;
T &operator*() { return *ptr_; }
const T &operator*() const { return *ptr_; }
T *const operator->() { return ptr_.operator->(); }
const T *const operator->() const { return ptr_.operator->(); }
const T *const get() const { return ptr_.get(); }
operator bool() const { return (bool)ptr_; }
template <typename U>
friend bool operator==(const value_ptr<U> &, const value_ptr<U> &);
private:
std::unique_ptr<T> ptr_;
};
template <typename T>
bool operator==(const value_ptr<T> &lhs, const value_ptr<T> &rhs) {
return *lhs == *rhs;
}
// Trait to enable serialization of values of type T.
// This is similar to the `serde::Serialize` trait in Rust.
template <typename T>
struct Serializable {
template <typename Serializer>
static void serialize(const T &value, Serializer &serializer);
};
// Trait to enable deserialization of values of type T.
// This is similar to the `serde::Deserialize` trait in Rust.
template <typename T>
struct Deserializable {
template <typename Deserializer>
static T deserialize(Deserializer &deserializer);
};
// --- Implementation of Serializable for base types ---
// string
template <>
struct Serializable<std::string> {
template <typename Serializer>
static void serialize(const std::string &value, Serializer &serializer) {
serializer.serialize_str(value);
}
};
// unit
template <>
struct Serializable<std::monostate> {
template <typename Serializer>
static void serialize(const std::monostate &_value,
Serializer &serializer) {
serializer.serialize_unit();
}
};
// bool
template <>
struct Serializable<bool> {
template <typename Serializer>
static void serialize(const bool &value, Serializer &serializer) {
serializer.serialize_bool(value);
}
};
// UTF-8 char
template <>
struct Serializable<char32_t> {
template <typename Serializer>
static void serialize(const char32_t &value, Serializer &serializer) {
serializer.serialize_char(value);
}
};
// f32
template <>
struct Serializable<float> {
template <typename Serializer>
static void serialize(const float &value, Serializer &serializer) {
serializer.serialize_f32(value);
}
};
// f64
template <>
struct Serializable<double> {
template <typename Serializer>
static void serialize(const double &value, Serializer &serializer) {
serializer.serialize_f64(value);
}
};
// u8
template <>
struct Serializable<uint8_t> {
template <typename Serializer>
static void serialize(const uint8_t &value, Serializer &serializer) {
serializer.serialize_u8(value);
}
};
// u16
template <>
struct Serializable<uint16_t> {
template <typename Serializer>
static void serialize(const uint16_t &value, Serializer &serializer) {
serializer.serialize_u16(value);
}
};
// u32
template <>
struct Serializable<uint32_t> {
template <typename Serializer>
static void serialize(const uint32_t &value, Serializer &serializer) {
serializer.serialize_u32(value);
}
};
// u64
template <>
struct Serializable<uint64_t> {
template <typename Serializer>
static void serialize(const uint64_t &value, Serializer &serializer) {
serializer.serialize_u64(value);
}
};
// u128
template <>
struct Serializable<uint128_t> {
template <typename Serializer>
static void serialize(const uint128_t &value, Serializer &serializer) {
serializer.serialize_u128(value);
}
};
// i8
template <>
struct Serializable<int8_t> {
template <typename Serializer>
static void serialize(const int8_t &value, Serializer &serializer) {
serializer.serialize_i8(value);
}
};
// i16
template <>
struct Serializable<int16_t> {
template <typename Serializer>
static void serialize(const int16_t &value, Serializer &serializer) {
serializer.serialize_i16(value);
}
};
// i32
template <>
struct Serializable<int32_t> {
template <typename Serializer>
static void serialize(const int32_t &value, Serializer &serializer) {
serializer.serialize_i32(value);
}
};
// i64
template <>
struct Serializable<int64_t> {
template <typename Serializer>
static void serialize(const int64_t &value, Serializer &serializer) {
serializer.serialize_i64(value);
}
};
// i128
template <>
struct Serializable<int128_t> {
template <typename Serializer>
static void serialize(const int128_t &value, Serializer &serializer) {
serializer.serialize_i128(value);
}
};
// --- Derivation of Serializable for composite types ---
// Value pointers (non-nullable)
template <typename T>
struct Serializable<value_ptr<T>> {
template <typename Serializer>
static void serialize(const value_ptr<T> &value, Serializer &serializer) {
Serializable<T>::serialize(*value, serializer);
}
};
// Options
template <typename T>
struct Serializable<std::optional<T>> {
template <typename Serializer>
static void serialize(const std::optional<T> &option,
Serializer &serializer) {
if (option.has_value()) {
serializer.serialize_option_tag(true);
Serializable<T>::serialize(option.value(), serializer);
} else {
serializer.serialize_option_tag(false);
}
}
};
// Vectors (sequences)
template <typename T, typename Allocator>
struct Serializable<std::vector<T, Allocator>> {
template <typename Serializer>
static void serialize(const std::vector<T, Allocator> &value,
Serializer &serializer) {
serializer.serialize_len(value.size());
for (const T &item : value) {
Serializable<T>::serialize(item, serializer);
}
}
};
// Fixed-size arrays
template <typename T, std::size_t N>
struct Serializable<std::array<T, N>> {
template <typename Serializer>
static void serialize(const std::array<T, N> &value,
Serializer &serializer) {
for (const T &item : value) {
Serializable<T>::serialize(item, serializer);
}
}
};
// Maps
template <typename K, typename V, typename Allocator>
struct Serializable<std::map<K, V, Allocator>> {
template <typename Serializer>
static void serialize(const std::map<K, V, Allocator> &value,
Serializer &serializer) {
serializer.serialize_len(value.size());
std::vector<size_t> offsets;
for (const auto &item : value) {
if constexpr (Serializer::enforce_strict_map_ordering) {
offsets.push_back(serializer.get_buffer_offset());
}
Serializable<K>::serialize(item.first, serializer);
Serializable<V>::serialize(item.second, serializer);
}
if constexpr (Serializer::enforce_strict_map_ordering) {
serializer.sort_last_entries(std::move(offsets));
}
}
};
// Tuples
template <class... Types>
struct Serializable<std::tuple<Types...>> {
template <typename Serializer>
static void serialize(const std::tuple<Types...> &value,
Serializer &serializer) {
// Visit each of the type components.
std::apply(
[&serializer](Types const &... args) {
(Serializable<Types>::serialize(args, serializer), ...);
},
value);
}
};
// Enums
template <class... Types>
struct Serializable<std::variant<Types...>> {
template <typename Serializer>
static void serialize(const std::variant<Types...> &value,
Serializer &serializer) {
// Write the variant index.
serializer.serialize_variant_index(value.index());
// Visit the inner type.
std::visit(
[&serializer](const auto &arg) {
using T = typename std::decay<decltype(arg)>::type;
Serializable<T>::serialize(arg, serializer);
},
value);
}
};
// --- Implementation of Deserializable for base types ---
// string
template <>
struct Deserializable<std::string> {
template <typename Deserializer>
static std::string deserialize(Deserializer &deserializer) {
return deserializer.deserialize_str();
}
};
// unit
template <>
struct Deserializable<std::monostate> {
template <typename Deserializer>
static std::monostate deserialize(Deserializer &deserializer) {
return deserializer.deserialize_unit();
}
};
// bool
template <>
struct Deserializable<bool> {
template <typename Deserializer>
static bool deserialize(Deserializer &deserializer) {
return deserializer.deserialize_bool();
}
};
// f32
template <>
struct Deserializable<float> {
template <typename Deserializer>
static float deserialize(Deserializer &deserializer) {
return deserializer.deserialize_f32();
}
};
// f64
template <>
struct Deserializable<double> {
template <typename Deserializer>
static double deserialize(Deserializer &deserializer) {
return deserializer.deserialize_f64();
}
};
// UTF-8 char
template <>
struct Deserializable<char32_t> {
template <typename Deserializer>
static char32_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_char();
}
};
// u8
template <>
struct Deserializable<uint8_t> {
template <typename Deserializer>
static uint8_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_u8();
}
};
// u16
template <>
struct Deserializable<uint16_t> {
template <typename Deserializer>
static uint16_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_u16();
}
};
// u32
template <>
struct Deserializable<uint32_t> {
template <typename Deserializer>
static uint32_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_u32();
}
};
// u64
template <>
struct Deserializable<uint64_t> {
template <typename Deserializer>
static uint64_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_u64();
}
};
// u128
template <>
struct Deserializable<uint128_t> {
template <typename Deserializer>
static uint128_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_u128();
}
};
// i8
template <>
struct Deserializable<int8_t> {
template <typename Deserializer>
static int8_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_i8();
}
};
// i16
template <>
struct Deserializable<int16_t> {
template <typename Deserializer>
static int16_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_i16();
}
};
// i32
template <>
struct Deserializable<int32_t> {
template <typename Deserializer>
static int32_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_i32();
}
};
// i64
template <>
struct Deserializable<int64_t> {
template <typename Deserializer>
static int64_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_i64();
}
};
// i128
template <>
struct Deserializable<int128_t> {
template <typename Deserializer>
static int128_t deserialize(Deserializer &deserializer) {
return deserializer.deserialize_i128();
}
};
// --- Derivation of Deserializable for composite types ---
// Value pointers
template <typename T>
struct Deserializable<value_ptr<T>> {
template <typename Deserializer>
static value_ptr<T> deserialize(Deserializer &deserializer) {
return value_ptr<T>(Deserializable<T>::deserialize(deserializer));
}
};
// Options
template <typename T>
struct Deserializable<std::optional<T>> {
template <typename Deserializer>
static std::optional<T> deserialize(Deserializer &deserializer) {
auto tag = deserializer.deserialize_option_tag();
if (!tag) {
return {};
} else {
return {Deserializable<T>::deserialize(deserializer)};
}
}
};
// Vectors
template <typename T, typename Allocator>
struct Deserializable<std::vector<T, Allocator>> {
template <typename Deserializer>
static std::vector<T> deserialize(Deserializer &deserializer) {
std::vector<T> result;
size_t len = deserializer.deserialize_len();
for (size_t i = 0; i < len; i++) {
result.push_back(Deserializable<T>::deserialize(deserializer));
}
return result;
}
};
// Maps
template <typename K, typename V>
struct Deserializable<std::map<K, V>> {
template <typename Deserializer>
static std::map<K, V> deserialize(Deserializer &deserializer) {
std::map<K, V> result;
size_t len = deserializer.deserialize_len();
std::optional<std::tuple<size_t, size_t>> previous_key_slice;
for (size_t i = 0; i < len; i++) {
if constexpr (Deserializer::enforce_strict_map_ordering) {
auto start = deserializer.get_buffer_offset();
auto key = Deserializable<K>::deserialize(deserializer);
auto end = deserializer.get_buffer_offset();
if (previous_key_slice.has_value()) {
deserializer.check_that_key_slices_are_increasing(
previous_key_slice.value(), {start, end});
}
previous_key_slice = {start, end};
auto value = Deserializable<V>::deserialize(deserializer);
result.insert({key, value});
} else {
auto key = Deserializable<K>::deserialize(deserializer);
auto value = Deserializable<V>::deserialize(deserializer);
result.insert({key, value});
}
}
return result;
}
};
// Fixed-size arrays
template <typename T, std::size_t N>
struct Deserializable<std::array<T, N>> {
template <typename Deserializer>
static std::array<T, N> deserialize(Deserializer &deserializer) {
std::array<T, N> result;
for (T &item : result) {
item = Deserializable<T>::deserialize(deserializer);
}
return result;
}
};
// Tuples
template <class... Types>
struct Deserializable<std::tuple<Types...>> {
template <typename Deserializer>
static std::tuple<Types...> deserialize(Deserializer &deserializer) {
// Visit each of the type components.
return std::make_tuple(
Deserializable<Types>::deserialize(deserializer)...);
}
};
// Enums
template <class... Types>
struct Deserializable<std::variant<Types...>> {
template <typename Deserializer>
static std::variant<Types...> deserialize(Deserializer &deserializer) {
// A "case" is analog to a particular branch in switch-case over the
// index. Given the variant type `T` known statically, we create a
// closure that will deserialize a value `T` and return it as a variant.
using Case = std::function<std::variant<Types...>(Deserializer &)>;
auto make_case = [](auto tag) -> Case {
// Obtain the type `T` encoded in the type of `tag ==
// std::common_type<T>{}`.
using T = typename decltype(tag)::type;
auto f = [](Deserializer &de) {
return std::variant<Types...>(
Deserializable<T>::deserialize(de));
};
return f;
};
// The static array of all the cases for this variant.
static const std::array<Case, sizeof...(Types)> cases = {
make_case(std::common_type<Types>{})...};
// Read the variant index and execute the corresponding case.
auto index = deserializer.deserialize_variant_index();
if (index > cases.size()) {
throw deserialization_error("Unknown variant index for enum");
}
return cases.at(index)(deserializer);
}
};
} // end of namespace serde