//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: msgpack // // Core reading implementation, upon which readers are built. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef msgpack_core_reader_6c2f66f02585565 #define msgpack_core_reader_6c2f66f02585565 #include "../util/endianness.h" #include "error.h" #include "format.h" #include namespace msgpack { /** * reader_policy dictates how flexible the reader can be when reading data * from a binary blob containing MessagePack formats. * * More flexible policies will potentially include more code as a consequence. * If structural integrity or code size is important, use the strict policy. */ namespace reader_policy { struct relaxed {}; // Similar formats are acceptable. struct strict {}; // Formats must exactly match the caller's request. } // namespace reader_policy namespace detail { // Generic mechanism for reading a number of bytes out of an interator. template constexpr inline decltype(auto) read_bytes(Itr& inp) noexcept { std::array out; auto const end = inp + N; auto dst = out.begin(); while (inp != end) { *dst = std::byte{*inp}; ++dst; ++inp; } return out; } // Read an integral type out of the iterator. template constexpr inline decltype(auto) read_integral(Itr& inp) noexcept { constexpr auto N = sizeof(T); if constexpr (sizeof(T) == 1) { return T(*inp++); } else { return be_to_host(::detail::value_cast(read_bytes(inp))); } } // This helper structure provides a customization point for converting an // iterator to some other type (e.g. a pointer) in the event that the // final format does not have a suitable constructor. It's only currently // used for string_view, but may be overridden on platforms that provide // their own types. template struct iterator_adapter { static constexpr auto convert(Iter itr) { return itr; } }; template struct iterator_adapter { static constexpr auto convert(Iter itr) { return ::detail::value_cast(&*itr); } }; template using expected = tl::expected; template constexpr inline bool accept(Iter& itr) noexcept { if constexpr (is_fixtype) { // Don't advance the iterator -- part of the format is locked away // within the format byte. return (std::byte{*itr} & ~F::mask) == F::marker; } else { // Advance the iterator past the format byte. return std::byte{*itr++} == F::marker; } } // Read a value from a MessagePack message. Assumes that itr != end to // start with. template requires(!format::unimplemented::value) constexpr inline auto read(Iter& itr, Iter const end) noexcept -> expected { // Copy off the iterator. Update it at the end _if_ everything goes // smoothly. auto cur = itr; if (!accept(cur)) return tl::make_unexpected(error::wrong_type); // First thing's first. Read the format's "first_type", which is either // the payload or the length of the payload. Ensure we have enough data // from the span before we do so, though. using first_type = typename F::first_type; using value_type = typename F::value_type; using diff_type = typename std::iterator_traits::difference_type; if (std::distance(cur, end) < diff_type{sizeof(first_type)}) { return tl::make_unexpected(error::incomplete_message); } auto f = read_integral(cur); if constexpr (is_fixtype && (F::flags & format::flag::apply_mask)) { f &= decltype(f)(F::mask); } if constexpr (F::payload_type == format::payload::fixed) { // We've read all we need to read. Update itr and return the value. itr = cur; return value_type(f); } else { // We're reading a variable length payload. `f` is the length of the // payload. Ensure that the span has enough data and construct the // value_type accordingly. if (std::distance(cur, end) < diff_type{f}) { return tl::make_unexpected(error::incomplete_message); } itr = cur + f; using adapt = iterator_adapter; return value_type(adapt::convert(cur), f); } } // "Relaxed" reader policies allow for certain conversions to take place // at runtime. For instance, any smaller type of the same category is // allowed. // // TODO: Possible improvement to readability; specify a single format list // containing all formats, and filter them down for each instantiated // format. Note that this would probably have compile-time performance // implications. template struct relaxed_conversions {}; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template <> struct relaxed_conversions { using type_list = format_list; }; template concept has_conversions = format_type && requires { typename relaxed_conversions::type_list; }; template struct reader_policy_traits {}; template <> struct reader_policy_traits { template constexpr static bool accept(Iter itr) noexcept { return detail::accept(itr); } template constexpr static decltype(auto) read(Iter& itr, Iter const end) noexcept { return detail::read(itr, end); }; }; template constexpr static auto relaxed_read_impl(Iter& itr, Iter const end) noexcept -> expected { if constexpr (I < fmtlist::size) { using this_format = format_list_at; auto v = detail::read(itr, end); if (v.has_value()) { return v.value(); } else if (v.error() == error::wrong_type) { return relaxed_read_impl(itr, end); } else { return v; } } else { return tl::make_unexpected(error::wrong_type); } } template <> struct reader_policy_traits { template constexpr static decltype(auto) read(Iter& itr, Iter const& end) noexcept { if constexpr (has_conversions) { using format_list = typename relaxed_conversions::type_list; return relaxed_read_impl(itr, end); } else { // Fall back on strict policy return detail::read(itr, end); } } }; } // namespace detail template class reader { public: template using expected = detail::expected; constexpr reader(std::span src) : data(src) , curr(std::begin(data)) , end(std::end(data)) {} template constexpr expected read() noexcept { if (curr == end) return tl::make_unexpected(error::end_of_message); return detail::reader_policy_traits::template read( curr, end); } constexpr auto pos() const noexcept { return curr; } constexpr auto subview() const noexcept { return std::span(curr, end); } constexpr auto tell() const noexcept { return std::distance(data.begin(), curr); } constexpr void skip(std::size_t count) noexcept { if (std::distance(curr, end) < count) { curr = end; } else { curr += count; } } private: std::span data; decltype(data)::iterator curr; decltype(data)::iterator end; }; } // namespace msgpack #endif // msgpack_core_reader_6c2f66f02585565