parselink-old/source/include/parselink/msgpack/token/reader.h

123 lines
4.1 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() noexcept {
using dist_type = decltype(std::distance(curr_, end_));
return std::max(dist_type(0), std::distance(curr_, end_));
}
// 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 {
if (curr_ >= end_) {
return tl::make_unexpected(error::end_of_message);
}
// Enumerate the current byte first by switch statement, then by
// fix types.
long int size = 0;
auto id = *curr_;
++curr_;
switch (id) {
case format::uint8::marker:
size = 1;
case format::uint16::marker:
size = 2;
case format::uint32::marker:
size = 3;
case format::uint64::marker:
size = 4;
if (remaining() < size) {
return tl::make_unexpected(error::incomplete_message);
}
return detail::read_value<format::type::unsigned_int>(size, curr_);
case format::int8::marker:
size = 1;
case format::int16::marker:
size = 2;
case format::int32::marker:
size = 3;
case format::int64::marker:
size = 4;
if (remaining() < size) {
return tl::make_unexpected(error::incomplete_message);
}
return detail::read_value<format::type::signed_int>(size, curr_);
}
return tl::make_unexpected(error::not_implemented);
}
// 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