//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: msgpack // // "Token" writing implementation, which supports both tokens and deduction for // common types. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef msgpack_core_writer_ce48a51aa6ed0858 #define msgpack_core_writer_ce48a51aa6ed0858 #include "parselink/msgpack/core/error.h" #include "parselink/msgpack/core/format.h" #include "parselink/msgpack/util/endianness.h" #include #include #include namespace msgpack { enum class writer_error { none, unsupported, not_implemented, bad_value, out_of_space }; // Helper template for writing a non-integral datatype to an output. // template // struct write_adapter { // template // static constexpr tl::expected write(T const& t); //}; template constexpr inline decltype(auto) write_bytes( std::array&& data, Itr out) noexcept { for (auto b : data) { *out++ = b; } return out; } #if 0 // Figure out the smallest namespace detail { constexpr auto const& deduce_format(std::uint64_t value) { if (value <= format::positive_fixint::mask) return format:: } template struct write_adapter { static constexpr auto size(T t) noexcept { } } } // namespace detail #endif template struct write_adapter {}; template struct write_adapter { static constexpr auto size(T) noexcept { return sizeof(T); } template static constexpr auto write(T t, Itr out) noexcept { return write_bytes(detail::raw_cast(host_to_be(t)), out); } }; template <> struct write_adapter { static constexpr auto size(std::string_view str) noexcept { return str.size(); } template static constexpr auto write(std::string_view str, Itr out) noexcept { std::byte const* beg = reinterpret_cast(&*str.begin()); std::copy(beg, beg + str.size(), out); return out += str.size(); } }; template <> struct write_adapter> { static constexpr auto size(std::span bytes) noexcept { return bytes.size(); } template static constexpr auto write( std::span bytes, Itr out) noexcept { std::copy(bytes.begin(), bytes.end(), out); return out += bytes.size(); } }; template <> struct write_adapter { static constexpr auto value(map_desc desc) noexcept { return desc.count; } }; template <> struct write_adapter { static constexpr auto value(array_desc desc) noexcept { return desc.count; } }; // TODO: These could be optimized away because invalid/nil never contain real // data. template <> struct write_adapter { static constexpr auto value(invalid) noexcept { return 0; } }; template <> struct write_adapter { static constexpr auto value(nil) noexcept { return 0; } }; namespace detail { template using expected = tl::expected; template constexpr inline std::size_t calculate_space( typename F::value_type val) noexcept { // At a minimum, one byte is needed to store the format. std::size_t size = sizeof(typename F::first_type); if (!is_fixtype) { ++size; // For format } if constexpr (F::payload_type == format::payload::variable) { size += write_adapter::size(val); } return size; } // The "first type" is either the size of the variable length payload or // a fixed-length value. Additionally, this "first type" may be inlined // into the marker byte if it's a fix type. template constexpr inline expected pack_first( typename F::value_type value) noexcept { using value_type = typename F::value_type; if constexpr (F::payload_type == format::payload::variable) { return typename F::first_type(write_adapter::size(value)); } else { if constexpr (requires { write_adapter::value; }) { return typename F::first_type( write_adapter::value(value)); } else { return typename F::first_type(value); } } } template constexpr inline expected write( typename F::value_type&& value, Itr out, Itr const end) { using diff_type = typename std::iterator_traits::difference_type; if (diff_type(calculate_space(value)) > std::distance(out, end)) { return tl::make_unexpected(error::out_of_space); } auto marker = F::marker; auto result = pack_first(value); if (!result) { return tl::make_unexpected(result.error()); } if constexpr (is_fixtype) { if (*result > 0xff) { return tl::make_unexpected(error::bad_value); } if constexpr (F::flags & format::flag::apply_mask) { marker |= std::byte(*result); } else { marker = std::byte(*result); } if ((marker & ~F::mask) != F::marker) { return tl::make_unexpected(error::bad_value); } } *out++ = marker; if constexpr (!is_fixtype) { out = write_adapter::write( *result, out); } if constexpr (F::payload_type == format::payload::variable) { out = write_adapter::write(value, out); } return out; } template struct format_hint; template <> struct format_hint { using type = format::positive_fixint; }; template <> struct format_hint { using type = format::uint16; }; } // namespace detail class writer { public: template using expected = detail::expected; constexpr writer(std::span dest) : data(dest) , curr(std::begin(data)) , end(std::end(data)) {} template constexpr expected write( typename F::value_type&& v) noexcept { using value_type = typename F::value_type; if (curr == end) return tl::make_unexpected(error::out_of_space); auto result = detail::write(std::forward(v), curr, end); if (!result) { return tl::make_unexpected(result.error()); } curr = *result; return tl::monostate{}; } template requires requires { typename detail::format_hint::type; } constexpr expected write(T&& v) { return write::type>(std::forward(v)); } constexpr auto pos() const noexcept { return curr; } constexpr auto tell() const noexcept { return std::distance(std::begin(data), curr); } constexpr auto subspan() const noexcept { return std::span(std::begin(data), tell()); } private: std::span data; decltype(data)::iterator curr; decltype(data)::iterator end; }; } // namespace msgpack #endif // msgpack_core_writer_ce48a51aa6ed0858