//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // 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 #include #include #include #include #include namespace msgpack { //----------------------------------------------------------------------------- // Supporting types/tags for formats //----------------------------------------------------------------------------- struct nil { constexpr nil() noexcept = default; constexpr bool operator==(nil const& other) const noexcept = default; }; struct invalid { constexpr invalid() noexcept = default; constexpr bool operator==(invalid const& other) const noexcept = default; }; 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 struct resolve_token_type { constexpr static type value = type::invalid; }; template struct resolve_token_type { constexpr static type value = std::is_signed_v ? type::signed_int : type::unsigned_int; }; template <> struct resolve_token_type { constexpr static type value = type::string; }; template <> struct resolve_token_type> { constexpr static type value = type::binary; }; template <> struct resolve_token_type { constexpr static type value = type::boolean; }; template <> struct resolve_token_type { constexpr static type value = type::nil; }; template <> struct resolve_token_type { constexpr static type value = type::invalid; }; template <> struct resolve_token_type { constexpr static type value = type::array; }; template <> struct resolve_token_type { constexpr static type value = type::map; }; template constexpr static auto resolve_type_v = resolve_token_type::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 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 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}; }; struct invalid : fixtype<0xc1, 0x00> { using value_type = msgpack::invalid; constexpr static flag flags{flag::fixed_size}; }; struct boolean : fixtype<0xc2, 0x01> { using value_type = bool; constexpr static flag flags{flag::fixed_size | flag::apply_mask}; }; /* * Maps */ template struct map_format : Fmt { using value_type = map_desc; }; struct fixmap : map_format> {}; struct map16 : map_format> {}; struct map32 : map_format> {}; /* * Arrays */ template struct array_format : Fmt { using value_type = array_desc; }; struct fixarray : array_format> {}; struct array16 : array_format> {}; struct array32 : array_format> {}; /* * Strings */ template struct string_format : Fmt { using value_type = std::string_view; constexpr static auto payload_type = payload::variable; }; struct fixstr : string_format> {}; struct str8 : string_format> {}; struct str16 : string_format> {}; struct str32 : string_format> {}; /* * Binary arrays */ template struct bin_format : Fmt { using value_type = std::span; constexpr static auto payload_type = payload::variable; }; struct bin8 : bin_format> {}; struct bin16 : bin_format> {}; struct bin32 : bin_format> {}; /* * Extension types, not yet supported. */ template struct unimplemented_format : Fmt {}; struct fixext1 : unimplemented_format> {}; struct fixext2 : unimplemented_format> {}; struct fixext4 : unimplemented_format> {}; struct fixext8 : unimplemented_format> {}; struct fixext16 : unimplemented_format> {}; struct ext8 : unimplemented_format> {}; struct ext16 : unimplemented_format> {}; struct ext32 : unimplemented_format> {}; template struct unimplemented : std::false_type {}; template struct unimplemented> : std::true_type {}; namespace detail { // Simple typelist for looking up compatible types. // TODO: Use traits to generate the compatibility list automatically? template struct type_list_entry { using type = T; }; template struct type_list_impl; template struct type_list_impl, Ts...> : type_list_entry... { static constexpr auto size = sizeof...(Ts); }; template struct typelist_lookup_tag { using type = T; }; template typelist_lookup_tag typelist_lookup(type_list_entry const&); template using type_list_at = typename decltype(typelist_lookup(std::declval()))::type; template using type_list = detail::type_list_impl, Ts...>; template typename> struct filter_type_list; template typename Predicate> struct filter_type_list, type_list, Predicate> { using type = type_list; }; template typename Predicate> struct filter_type_list, type_list, Predicate> { using type = typename std::conditional_t::value, filter_type_list, type_list, Predicate>, filter_type_list, type_list, Predicate>>::type; }; template typename Predicate> using filter = filter_type_list, Predicate>; template typename Predicate> using filter_t = typename filter::type; } // namespace detail } // namespace format template concept format_type = std::is_base_of_v; template concept is_fixtype = std::is_base_of_v; 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 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 ? 0 : sizeof(typename Format::first_type); traits inst{.flags = Format::flags, .size = std::uint8_t(size), .token_type = resolve_type_v}; // Add in the fixed_size flag for integral types. if constexpr (std::is_integral_v) { inst.flags |= flag::fixed_size; } if constexpr (is_fixtype) { inst.mask = Format::mask; } return inst; }(); return inst; }; } // namespace format template using format_list = format::detail::type_list; template using format_list_at = format::detail::type_list_at; } // namespace msgpack #endif // oh_msgpack_format_849b5c5238d8212