parselink-old/include/parselink/msgpack/core/format.h

438 lines
12 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: msgpack
//
// An implementation of the MessagePack binary format specification.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef msgpack_format_849b5c5238d8212
#define msgpack_format_849b5c5238d8212
#include <bit>
#include <cstdint>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
namespace msgpack {
//-----------------------------------------------------------------------------
// Supporting types/tags for formats
//-----------------------------------------------------------------------------
struct nil {
nil() = default;
// This constructor is used by the reader implementation.
nil(auto){};
};
struct invalid {};
using boolean = bool;
struct array_desc {
constexpr array_desc() noexcept = default;
constexpr array_desc(auto sz) noexcept
: count(std::size_t(sz)){};
// This constructor is implemented for use by reader
constexpr array_desc(auto, auto sz) noexcept
: count(std::size_t(sz)){};
constexpr bool operator==(array_desc const& other) const noexcept {
return count == other.count;
}
std::size_t count = 0;
};
struct map_desc {
constexpr map_desc() noexcept = default;
constexpr map_desc(auto sz) noexcept
: count(std::size_t(sz)){};
// This constructor is implemented for use by reader
constexpr map_desc(auto, auto sz) noexcept
: count(std::size_t(sz)){};
std::size_t count = 0;
};
namespace format {
// Classification of the format type. Used by the token API to distinguish
// different formats.
enum class type : std::uint8_t {
invalid,
unsigned_int,
signed_int,
string,
binary,
nil,
boolean,
array,
map,
array_view,
map_view,
};
// Flags that may control the behavior of readers/writers.
enum flag : std::uint8_t {
apply_mask = 1,
fixed_size = 2,
};
// MessagePack formats break down into one of the following schemes:
// Marker byte + Fixed-length value
// Marker byte + Fixed-length payload length + payload
//
// In addition, there are "fix" types that embed the literal value or the
// payload length within the marker byte.
enum payload : std::uint8_t { fixed, variable };
// Base structure for all MessagePack formats to inherit from.
struct base_format {
constexpr static flag flags = {};
};
// Traits describing a particular format.
struct traits {
std::uint8_t flags{};
std::uint8_t size{};
std::byte mask{};
type token_type{};
constexpr auto operator<=>(traits const&) const noexcept = default;
};
// "Sentinel" traits instance indicating no trait found
constexpr static traits no_traits{};
struct fixtype_format : base_format {};
template <class>
struct resolve_token_type {
constexpr static type value = type::invalid;
};
template <std::integral T>
struct resolve_token_type<T> {
constexpr static type value =
std::is_signed_v<T> ? type::signed_int : type::unsigned_int;
};
template <>
struct resolve_token_type<std::string_view> {
constexpr static type value = type::string;
};
template <>
struct resolve_token_type<std::span<std::byte const>> {
constexpr static type value = type::binary;
};
template <>
struct resolve_token_type<bool> {
constexpr static type value = type::boolean;
};
template <>
struct resolve_token_type<nil> {
constexpr static type value = type::nil;
};
template <>
struct resolve_token_type<invalid> {
constexpr static type value = type::invalid;
};
template <>
struct resolve_token_type<array_desc> {
constexpr static type value = type::array;
};
template <>
struct resolve_token_type<map_desc> {
constexpr static type value = type::map;
};
template <typename T>
constexpr static auto resolve_type_v = resolve_token_type<T>::value;
// The library's representation of certain data types does not always
// match the underlying data serialized in the message. For example,
// arrays and maps are encoded as a marker byte + fixed length value, but
// msgpack will present them wrapped in a structure for stronger type
// semantics.
template <std::uint8_t Marker, typename First, typename Value = First>
struct format : base_format {
using first_type = First;
using value_type = Value; // Can be overridden
constexpr static std::byte marker{Marker};
constexpr static payload payload_type{payload::fixed};
constexpr static std::uint8_t flags{};
};
template <std::uint8_t Marker, std::uint8_t Mask, typename First = std::uint8_t>
struct fixtype : fixtype_format {
using first_type = First;
using value_type = first_type; // Can be overridden
constexpr static std::byte marker{Marker};
constexpr static std::byte mask{Mask};
constexpr static payload payload_type{payload::fixed};
constexpr static auto flags{flag::apply_mask};
};
/*
* Integral types
*/
// Positive/negative fixint represent the literal value specified, do not
// need to mask it off.
struct positive_fixint : fixtype<0x00, 0x7f> {
constexpr static flag flags{};
};
struct negative_fixint : fixtype<0xe0, 0x1f, std::int8_t> {
constexpr static flag flags{};
};
struct uint8 : format<0xcc, std::uint8_t> {};
struct uint16 : format<0xcd, std::uint16_t> {};
struct uint32 : format<0xce, std::uint32_t> {};
struct uint64 : format<0xcf, std::uint64_t> {};
struct int8 : format<0xd0, std::int8_t> {};
struct int16 : format<0xd1, std::int16_t> {};
struct int32 : format<0xd2, std::int32_t> {};
struct int64 : format<0xd3, std::int64_t> {};
/*
* Other primitive types
*/
struct nil : fixtype<0xc0, 0x00> {
using value_type = msgpack::nil;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
struct invalid : fixtype<0xc1, 0x00> {
using value_type = msgpack::invalid;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
struct boolean : fixtype<0xc2, 0x01> {
using value_type = bool;
constexpr static flag flags{flag::fixed_size | flag::apply_mask};
};
/*
* Maps
*/
template <typename Fmt>
struct map_format : Fmt {
using value_type = map_desc;
};
struct fixmap : map_format<fixtype<0x80, 0x0f>> {};
struct map16 : map_format<format<0xde, std::uint16_t>> {};
struct map32 : map_format<format<0xdf, std::uint32_t>> {};
/*
* Arrays
*/
template <typename Fmt>
struct array_format : Fmt {
using value_type = array_desc;
};
struct fixarray : array_format<fixtype<0x90, 0x0f>> {};
struct array16 : array_format<format<0xdc, std::uint16_t>> {};
struct array32 : array_format<format<0xdd, std::uint32_t>> {};
/*
* Strings
*/
template <typename Fmt>
struct string_format : Fmt {
using value_type = std::string_view;
constexpr static auto payload_type = payload::variable;
};
struct fixstr : string_format<fixtype<0xa0, 0x1f>> {};
struct str8 : string_format<format<0xd9, std::uint8_t>> {};
struct str16 : string_format<format<0xda, std::uint16_t>> {};
struct str32 : string_format<format<0xdb, std::uint32_t>> {};
/*
* Binary arrays
*/
template <typename Fmt>
struct bin_format : Fmt {
using value_type = std::span<std::byte const>;
constexpr static auto payload_type = payload::variable;
};
struct bin8 : bin_format<format<0xc4, std::uint8_t>> {};
struct bin16 : bin_format<format<0xc5, std::uint16_t>> {};
struct bin32 : bin_format<format<0xc6, std::uint32_t>> {};
/*
* Extension types, not yet supported.
*/
template <typename Fmt>
struct unimplemented_format : Fmt {};
struct fixext1 : unimplemented_format<fixtype<0xd4, 0x00>> {};
struct fixext2 : unimplemented_format<fixtype<0xd5, 0x00>> {};
struct fixext4 : unimplemented_format<fixtype<0xd6, 0x00>> {};
struct fixext8 : unimplemented_format<fixtype<0xd7, 0x00>> {};
struct fixext16 : unimplemented_format<fixtype<0xd8, 0x00>> {};
struct ext8 : unimplemented_format<format<0xc7, std::uint8_t>> {};
struct ext16 : unimplemented_format<format<0xc8, std::uint16_t>> {};
struct ext32 : unimplemented_format<format<0xc9, std::uint32_t>> {};
template <typename T>
struct unimplemented : std::false_type {};
template <typename Fmt>
struct unimplemented<unimplemented_format<Fmt>> : std::true_type {};
namespace detail {
// Simple typelist for looking up compatible types.
// TODO: Use traits to generate the compatibility list automatically?
template <std::size_t I, typename T>
struct type_list_entry {
using type = T;
};
template <typename...>
struct type_list_impl;
template <std::size_t... Is, typename... Ts>
struct type_list_impl<std::index_sequence<Is...>, Ts...>
: type_list_entry<Is, Ts>... {
static constexpr auto size = sizeof...(Ts);
};
template <typename T>
struct typelist_lookup_tag {
using type = T;
};
template <std::size_t I, typename T>
typelist_lookup_tag<T> typelist_lookup(type_list_entry<I, T> const&);
template <std::size_t I, typename List>
using type_list_at =
typename decltype(typelist_lookup<I>(std::declval<List>()))::type;
template <typename... Ts>
using type_list =
detail::type_list_impl<std::make_index_sequence<sizeof...(Ts)>, Ts...>;
template <typename, typename, template <typename> typename>
struct filter_type_list;
template <typename... Ts, template <typename> typename Predicate>
struct filter_type_list<type_list<>, type_list<Ts...>, Predicate> {
using type = type_list<Ts...>;
};
template <typename Head, typename... Tail, typename... Ts,
template <typename> typename Predicate>
struct filter_type_list<type_list<Head, Tail...>, type_list<Ts...>, Predicate> {
using type = typename std::conditional_t<Predicate<Head>::value,
filter_type_list<type_list<Tail...>, type_list<Ts..., Head>,
Predicate>,
filter_type_list<type_list<Tail...>, type_list<Ts...>,
Predicate>>::type;
};
template <typename List, template <typename> typename Predicate>
using filter = filter_type_list<List, type_list<>, Predicate>;
template <typename List, template <typename> typename Predicate>
using filter_t = typename filter<List, Predicate>::type;
} // namespace detail
} // namespace format
template <typename T>
concept format_type = std::is_base_of_v<format::base_format, T>;
template <typename T>
concept is_fixtype = std::is_base_of_v<format::fixtype_format, T>;
namespace format {
// This template instantiates a format::traits object for a given format.
// This should only occur once per format type, and should happen at
// compile time.
//
// This isn't the prettiest way to go about it.
template <format_type Format>
inline traits const& get_traits() noexcept {
constexpr static auto inst = [] {
// Fixtypes define the size within the identifier byte, so the
// trait size must be zero.
auto size =
is_fixtype<Format> ? 0 : sizeof(typename Format::first_type);
traits inst{.flags = Format::flags,
.size = std::uint8_t(size),
.token_type = resolve_type_v<typename Format::value_type>};
// Add in the fixed_size flag for integral types.
if constexpr (std::is_integral_v<typename Format::value_type>) {
inst.flags |= flag::fixed_size;
}
if constexpr (is_fixtype<Format>) {
inst.mask = Format::mask;
}
return inst;
}();
return inst;
};
} // namespace format
template <format_type... Formats>
using format_list = format::detail::type_list<Formats...>;
template <std::size_t I, typename List>
using format_list_at = format::detail::type_list_at<I, List>;
} // namespace msgpack
#endif // oh_msgpack_format_849b5c5238d8212