//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: msgpack // // Token-based reader for unpacking MessagePack data. Ensure lifetime of the // MessagePack buffer exceeds the lifetime of parsed tokens. // // TBD: How best to handle arrays and maps. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #ifndef msgpack_token_reader_8daff350a0b1a519 #define msgpack_token_reader_8daff350a0b1a519 #include "type.h" #include "../core/error.h" #include "../util/endianness.h" #include "../core/format.h" #include namespace msgpack { namespace detail { constexpr inline decltype(auto) read(std::size_t size, auto& inp) noexcept { std::array value; be_to_host(inp, inp + size, std::begin(value)); inp += size; return value; } template constexpr inline decltype(auto) read_value(std::size_t size, auto& inp) { using value_type = typename token_traits::storage_type; return token{std::bit_cast(read(size, inp))}; } } // namespace detail class token_reader { public: constexpr token_reader(std::span src) noexcept : data_(src), curr_{src.begin()}, end_(src.end()) {} constexpr auto remaining(auto itr) noexcept { using dist_type = decltype(std::distance(itr, end_)); return std::max(dist_type(0), std::distance(itr, end_)); } constexpr auto remaining() noexcept { return remaining(curr_); } // Read the next token. If the reader currently points to the end of the // byte buffer, then end_of_message is returned, and if there is still // some data present in the buffer, then incomplete_message is returned, // potentially hinting that further buffering is required. constexpr tl::expected read_one() noexcept { token tok; if (curr_ >= end_) { return tl::make_unexpected(error::end_of_message); } auto curr = curr_; // Enumerate the current byte first by switch statement, then by // fix types. long int size = 0; std::size_t var_size = 0; auto id = *curr; format::type token_type; ++curr; switch (id) { case format::uint8::marker: size = sizeof(format::uint8::value_type); token_type = format::type::unsigned_int; break; case format::uint16::marker: size = sizeof(format::uint16::value_type); token_type = format::type::unsigned_int; break; case format::uint32::marker: size = sizeof(format::uint32::value_type); token_type = format::type::unsigned_int; break; case format::uint64::marker: size = sizeof(format::uint64::value_type); token_type = format::type::unsigned_int; break; case format::int8::marker: size = sizeof(format::int8::value_type); token_type = format::type::signed_int; break; case format::int16::marker: size = sizeof(format::int16::value_type); token_type = format::type::signed_int; break; case format::int32::marker: size = sizeof(format::int32::value_type); token_type = format::type::signed_int; break; case format::int64::marker: size = sizeof(format::int64::value_type); token_type = format::type::signed_int; break; case format::str8::marker: size = sizeof(format::str8::first_type); token_type = format::type::string; break; case format::str16::marker: size = sizeof(format::str16::first_type); token_type = format::type::string; break; case format::str32::marker: size = sizeof(format::str32::first_type); token_type = format::type::string; break; default: return tl::make_unexpected(error::not_implemented); } switch (token_type) { case format::type::unsigned_int: if (remaining(curr) < size) { return tl::make_unexpected(error::out_of_space); } tok = detail::read_value(size, curr); break; case format::type::signed_int: if (remaining(curr) < size) { return tl::make_unexpected(error::out_of_space); } tok = detail::read_value(size, curr); break; case format::type::string: if (remaining(curr) < size) { return tl::make_unexpected(error::incomplete_message); } var_size = std::bit_cast(detail::read(size, curr)); if (std::size_t(remaining(curr)) < var_size) { return tl::make_unexpected(error::incomplete_message); } using type = token_traits::storage_type; { auto ptr = reinterpret_cast(&*curr); tok = token{std::string_view{ptr, var_size}}; curr += var_size; } default: break; } curr_ = curr; return {tok}; } // Read multiple tokens from the byte buffer. The number of tokens parsed // can be surmised from the returned span of tokens. If the reader // currently points to the end of the byte buffer, then // error::end_of_message is returned, and if there is not enough data to // fully parse a token, then incomplete_message is returned. constexpr tl::expected, error> read_some( std::span token_buffer) noexcept; private: std::span data_; decltype(data_)::iterator curr_; decltype(data_)::iterator end_; }; } // namespace msgpack #endif // msgpack_token_reader_8daff350a0b1a519