192 lines
6.9 KiB
C++
192 lines
6.9 KiB
C++
//-----------------------------------------------------------------------------
|
|
// ___ __ _ _
|
|
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
|
|
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
|
|
// / ___/ (_| | | \__ \ __/ /__| | | | | <
|
|
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
// 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 <tl/expected.hpp>
|
|
|
|
namespace msgpack {
|
|
|
|
namespace detail {
|
|
|
|
constexpr inline decltype(auto) read(std::size_t size, auto& inp)
|
|
noexcept {
|
|
std::array<std::byte, token::word_size> value;
|
|
be_to_host(inp, inp + size, std::begin(value));
|
|
inp += size;
|
|
return value;
|
|
}
|
|
|
|
template <format::type FormatType>
|
|
constexpr inline decltype(auto) read_value(std::size_t size, auto& inp) {
|
|
using value_type = typename token_traits<FormatType>::storage_type;
|
|
return token{std::bit_cast<value_type>(read(size, inp))};
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
class token_reader {
|
|
public:
|
|
|
|
constexpr token_reader(std::span<std::byte const> 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<token, error> 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;
|
|
traits&
|
|
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;
|
|
case format::bin8::marker:
|
|
size = sizeof(format::bin8::first_type);
|
|
token_type = format::type::bytes;
|
|
break;
|
|
case format::bin16::marker:
|
|
size = sizeof(format::bin16::first_type);
|
|
token_type = format::type::bytes;
|
|
break;
|
|
case format::bin32::marker:
|
|
size = sizeof(format::bin32::first_type);
|
|
token_type = format::type::bytes;
|
|
break;
|
|
default:
|
|
return tl::make_unexpected(error::not_implemented);
|
|
}
|
|
|
|
if (remaining(curr) < size) {
|
|
return tl::make_unexpected(error::incomplete_message);
|
|
}
|
|
|
|
switch (token_type) {
|
|
case format::type::unsigned_int:
|
|
tok = detail::read_value<format::type::unsigned_int>(size, curr);
|
|
break;
|
|
case format::type::signed_int:
|
|
tok = detail::read_value<format::type::signed_int>(size, curr);
|
|
break;
|
|
case format::type::string:
|
|
var_size = std::bit_cast<std::size_t>(detail::read(size, curr));
|
|
if (std::size_t(remaining(curr)) < var_size) {
|
|
return tl::make_unexpected(error::incomplete_message);
|
|
}
|
|
using type = token_traits<format::type::string>::storage_type;
|
|
{
|
|
auto ptr = reinterpret_cast<type>(&*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<std::span<token>, error> read_some(
|
|
std::span<token> token_buffer) noexcept;
|
|
private:
|
|
std::span<std::byte const> data_;
|
|
decltype(data_)::iterator curr_;
|
|
decltype(data_)::iterator end_;
|
|
};
|
|
|
|
} // namespace msgpack
|
|
|
|
#endif // msgpack_token_reader_8daff350a0b1a519
|