Map/Array support as "tokens"

This essentially completes the token_reader. In the future, we will
probably want something that lets us allocate maps/arrays more
seamlessly, but we can start working on client-server architecture with
this reader.
This commit is contained in:
Kurt Sassenrath 2023-09-18 07:46:23 -07:00
parent c5c03a2a40
commit f894b8eaf6
4 changed files with 236 additions and 15 deletions

View File

@ -145,6 +145,16 @@ namespace format {
constexpr static type value = type::invalid; constexpr static type value = type::invalid;
}; };
template <>
struct resolve_token_type<array_desc> {
constexpr static type value = type::array;
};
template <>
struct resolve_token_type<map_desc> {
constexpr static type value = type::map;
};
template <typename T> template <typename T>
constexpr static auto resolve_type_v = resolve_token_type<T>::value; constexpr static auto resolve_type_v = resolve_token_type<T>::value;

View File

@ -92,6 +92,16 @@ constexpr inline format::traits const& traits_lookup(std::byte id) {
case format::bin32::marker: case format::bin32::marker:
return format::get_traits<format::bin32>(); return format::get_traits<format::bin32>();
case format::array16::marker:
return format::get_traits<format::array16>();
case format::array32::marker:
return format::get_traits<format::array32>();
case format::map16::marker:
return format::get_traits<format::map16>();
case format::map32::marker:
return format::get_traits<format::map32>();
case format::nil::marker: case format::nil::marker:
return format::get_traits<format::nil>(); return format::get_traits<format::nil>();
case format::invalid::marker: case format::invalid::marker:
@ -104,15 +114,21 @@ constexpr inline format::traits const& traits_lookup(std::byte id) {
} }
// TODO(ksassenrath): Handle fixtype formats. // TODO(ksassenrath): Handle fixtype formats.
if ((id & ~format::fixstr::mask) == format::fixstr::marker) {
return format::get_traits<format::fixstr>();
}
if ((id & ~format::negative_fixint::mask) == format::negative_fixint::marker) { if ((id & ~format::negative_fixint::mask) == format::negative_fixint::marker) {
return format::get_traits<format::negative_fixint>(); return format::get_traits<format::negative_fixint>();
} }
if ((id & ~format::positive_fixint::mask) == format::positive_fixint::marker) { if ((id & ~format::positive_fixint::mask) == format::positive_fixint::marker) {
return format::get_traits<format::positive_fixint>(); return format::get_traits<format::positive_fixint>();
} }
if ((id & ~format::fixstr::mask) == format::fixstr::marker) {
return format::get_traits<format::fixstr>();
}
if ((id & ~format::fixmap::mask) == format::fixmap::marker) {
return format::get_traits<format::fixmap>();
}
if ((id & ~format::fixarray::mask) == format::fixarray::marker) {
return format::get_traits<format::fixarray>();
}
return format::no_traits; return format::no_traits;
} }
@ -200,20 +216,24 @@ public:
} }
case format::type::string: { case format::type::string: {
using type = token_traits<format::type::string>::storage_type; using type = token_traits<format::type::string>::storage_type;
{ auto ptr = reinterpret_cast<type>(&*curr);
auto ptr = reinterpret_cast<type>(&*curr); tok = token{std::string_view{ptr, var_size}};
tok = token{std::string_view{ptr, var_size}}; curr += var_size;
curr += var_size;
}
break; break;
} }
case format::type::binary: { case format::type::binary: {
using type = token_traits<format::type::binary>::storage_type; using type = token_traits<format::type::binary>::storage_type;
{ auto ptr = reinterpret_cast<type>(&*curr);
auto ptr = reinterpret_cast<type>(&*curr); tok = token{std::span<std::byte const>{ptr, var_size}};
tok = token{std::span<std::byte const>{ptr, var_size}}; curr += var_size;
curr += var_size; break;
} }
case format::type::array: {
tok = token{array_desc(var_size)};
break;
}
case format::type::map: {
tok = token{map_desc(var_size)};
break; break;
} }
default: default:

View File

@ -132,6 +132,16 @@ struct token_traits<format::type::binary> {
using storage_type = std::byte const*; using storage_type = std::byte const*;
}; };
template <>
struct token_traits<format::type::array> {
using storage_type = std::uint64_t;
};
template <>
struct token_traits<format::type::map> {
using storage_type = std::uint64_t;
};
template <> template <>
struct token_traits<format::type::boolean> { struct token_traits<format::type::boolean> {
using storage_type = bool; using storage_type = bool;
@ -180,6 +190,16 @@ public:
value_.bp = bv.data(); value_.bp = bv.data();
} }
explicit token_base(array_desc const& value) noexcept {
size_and_type_.set_enum(format::type::array);
size_and_type_.set_size(value.count);
}
explicit token_base(map_desc const& value) noexcept {
size_and_type_.set_enum(format::type::map);
size_and_type_.set_size(value.count);
}
explicit token_base(nil) noexcept { explicit token_base(nil) noexcept {
size_and_type_.set_enum(format::type::nil); size_and_type_.set_enum(format::type::nil);
} }
@ -227,7 +247,8 @@ private:
typename token_traits<format::type::string>::storage_type str; typename token_traits<format::type::string>::storage_type str;
typename token_traits<format::type::binary>::storage_type bp; typename token_traits<format::type::binary>::storage_type bp;
typename token_traits<format::type::boolean>::storage_type b; typename token_traits<format::type::boolean>::storage_type b;
token_base* obj; // TODO: What to store here for array/map types? Possibly a pointer
// to the end of the array/map, if/when known?
} value_; } value_;
size_and_enum<std::uint32_t, format::type> size_and_type_{}; size_and_enum<std::uint32_t, format::type> size_and_type_{};
}; };
@ -296,6 +317,26 @@ token_base<8>::get() const noexcept
return invalid{}; return invalid{};
} }
template<>
constexpr tl::expected<array_desc, error>
token_base<8>::get() const noexcept
{
if (type() != format::type::array) {
return tl::make_unexpected(error::wrong_type);
}
return array_desc{size_and_type_.get_size()};
}
template<>
constexpr tl::expected<map_desc, error>
token_base<8>::get() const noexcept
{
if (type() != format::type::map) {
return tl::make_unexpected(error::wrong_type);
}
return map_desc{size_and_type_.get_size()};
}
using token = token_base<sizeof(void*)>; using token = token_base<sizeof(void*)>;
} // namespace msgpack } // namespace msgpack

View File

@ -1,7 +1,7 @@
#include <msgpack/token.h> #include <msgpack/token.h>
#include <fmt/format.h>
#include <boost/ut.hpp> #include <boost/ut.hpp>
#include <fmt/format.h>
#include <magic_enum.hpp> #include <magic_enum.hpp>
using namespace boost::ut; using namespace boost::ut;
@ -486,6 +486,156 @@ suite reader_tests = [] {
expect(test_end_of_message(reader)); expect(test_end_of_message(reader));
}; };
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
// Structural format tests (map/array)
//--------------------------------------------------------------------------
"read format::fixarray"_test = [] {
// A MessagePack array of 5 8-bit unsigned integers.
constexpr auto payload = make_bytes(0x95, 0xcc, 0x85, 0xcc, 0x84, 0xcc,
0x83, 0xcc, 0x82, 0xcc, 0x81);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::array);
auto array = token->get<msgpack::array_desc>();
expect(array && array->count == 5);
for (std::size_t i = 0; i < array->count; ++i) {
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
}
expect(test_end_of_message(reader));
};
"read format::array16"_test = [] {
// A MessagePack array of 5 8-bit unsigned integers.
constexpr auto payload = make_bytes(0xdc, 0x00, 0x05,
0xcc, 0x85, 0xcc, 0x84, 0xcc, 0x83, 0xcc, 0x82, 0xcc, 0x81);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::array);
auto array = token->get<msgpack::array_desc>();
expect(array && array->count == 5);
for (std::size_t i = 0; i < array->count; ++i) {
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
}
expect(test_end_of_message(reader));
};
"read format::array32"_test = [] {
// A MessagePack array of 5 8-bit unsigned integers.
constexpr auto payload = make_bytes(0xdd, 0x00, 0x00, 0x00, 0x05,
0xcc, 0x85, 0xcc, 0x84, 0xcc, 0x83, 0xcc, 0x82, 0xcc, 0x81);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::array);
auto array = token->get<msgpack::array_desc>();
expect(array && array->count == 5);
for (std::size_t i = 0; i < array->count; ++i) {
auto v = reader.read_one();
expect(v && v->get<std::uint8_t>() == 0x85 - i);
}
expect(test_end_of_message(reader));
};
"read format::fixmap"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr std::array<std::string_view, 3> strings = {
"one", "two", "three"
};
constexpr auto payload = cat(
make_bytes(0x83, 0xa3),
generate_bytes([] { return from_string_view(strings[0]); }),
make_bytes(0x01, 0xa3),
generate_bytes([] { return from_string_view(strings[1]); }),
make_bytes(0x02, 0xa5),
generate_bytes([] { return from_string_view(strings[2]); }),
make_bytes(0x03)
);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::map);
auto map = token->get<msgpack::map_desc>();
expect(map && map->count == 3);
for (std::size_t i = 0; i < map->count; ++i) {
auto str = reader.read_one();
expect(str && str->get<std::string_view>() == strings[i]);
auto value = reader.read_one();
expect(value && value->get<std::uint8_t>() == i + 1);
}
expect(test_end_of_message(reader));
};
"read format::map16"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr std::array<std::string_view, 3> strings = {
"one", "two", "three"
};
constexpr auto payload = cat(
make_bytes(0xde, 0x00, 0x03, 0xa3),
generate_bytes([] { return from_string_view(strings[0]); }),
make_bytes(0x01, 0xa3),
generate_bytes([] { return from_string_view(strings[1]); }),
make_bytes(0x02, 0xa5),
generate_bytes([] { return from_string_view(strings[2]); }),
make_bytes(0x03)
);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::map);
auto map = token->get<msgpack::map_desc>();
expect(map && map->count == 3);
for (std::size_t i = 0; i < map->count; ++i) {
auto str = reader.read_one();
expect(str && str->get<std::string_view>() == strings[i]);
auto value = reader.read_one();
expect(value && value->get<std::uint8_t>() == i + 1);
}
expect(test_end_of_message(reader));
};
"read format::map32"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr std::array<std::string_view, 3> strings = {
"one", "two", "three"
};
constexpr auto payload = cat(
make_bytes(0xdf, 0x00, 0x00, 0x00, 0x03, 0xa3),
generate_bytes([] { return from_string_view(strings[0]); }),
make_bytes(0x01, 0xa3),
generate_bytes([] { return from_string_view(strings[1]); }),
make_bytes(0x02, 0xa5),
generate_bytes([] { return from_string_view(strings[2]); }),
make_bytes(0x03)
);
msgpack::token_reader reader(payload);
auto token = reader.read_one();
expect(token && token->type() == msgpack::format::type::map);
auto map = token->get<msgpack::map_desc>();
expect(map && map->count == 3);
for (std::size_t i = 0; i < map->count; ++i) {
auto str = reader.read_one();
expect(str && str->get<std::string_view>() == strings[i]);
auto value = reader.read_one();
expect(value && value->get<std::uint8_t>() == i + 1);
}
expect(test_end_of_message(reader));
};
//--------------------------------------------------------------------------
// Assorted format tests // Assorted format tests
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
"read format::boolean"_test = [] { "read format::boolean"_test = [] {