parselink-old/tests/msgpack/test_token_views.cpp
Kurt Sassenrath 1c7047e314 Implement token map_view, additional formatters.
msgpack::map_view can be used to iterate, pair-wise, over a range of
msgpack::token. It will immediately return if the first token is not a
map, and will skip over nested map/arrays.

Note for the future: It will be handy to be able to get the subspan
corresponding to the nested map/array. Will think about how to solve
that later.

Begin incorporating map_view into the server.

Add formatters for std::byte, dynamic theme for bool, and spans thereof.
Maybe switch to range?
2023-10-12 14:52:06 -07:00

220 lines
7.3 KiB
C++

#include <msgpack/token.h>
#include <boost/ut.hpp>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include <magic_enum.hpp>
using namespace boost::ut;
namespace {
template <typename T>
constexpr bool operator==(std::span<T const> a, std::span<T const> b) noexcept {
return std::equal(a.begin(), a.end(), b.begin(), b.end());
}
template <typename T>
constexpr std::array<std::byte, sizeof(T)> as_bytes(T&& t) {
return std::bit_cast<std::array<std::byte, sizeof(T)>>(std::forward<T>(t));
}
template <typename... Bytes>
constexpr std::array<std::byte, sizeof...(Bytes)> make_bytes(Bytes &&...bytes) {
return {std::byte(std::forward<Bytes>(bytes))...};
}
template <typename T, std::size_t C = 1024 * 1024> struct oversized_array {
std::array<T, C> data;
std::size_t size;
};
constexpr auto to_bytes_array_oversized(auto const &container) {
using value_type = std::decay_t<decltype(container[0])>;
oversized_array<value_type> arr;
std::copy(std::begin(container), std::end(container), std::begin(arr.data));
arr.size = std::distance(std::begin(container), std::end(container));
return arr;
}
consteval auto generate_bytes(auto callable) {
constexpr auto oversized = to_bytes_array_oversized(callable());
using value_type = std::decay_t<decltype(oversized.data[0])>;
std::array<value_type, oversized.size> out;
std::copy(std::begin(oversized.data),
std::next(std::begin(oversized.data), oversized.size),
std::begin(out));
return out;
}
consteval auto build_string(auto callable) {
constexpr auto string_array = generate_bytes(callable);
return string_array;
}
template <std::size_t ... Sizes>
constexpr auto cat(std::array<std::byte, Sizes>const&... a) noexcept {
std::array<std::byte, (Sizes + ...)> out;
std::size_t index{};
((std::copy_n(a.begin(), Sizes, out.begin() + index), index += Sizes), ...);
return out;
}
constexpr auto repeat(std::span<std::byte const> sv, std::size_t count) {
std::vector<std::byte> range;
range.reserve(sv.size() * count);
for (decltype(count) i = 0; i < count; ++i) {
std::copy_n(sv.begin(), sv.size(), std::back_inserter(range));
}
return range;
}
constexpr auto repeat(std::string_view sv, std::size_t count) {
std::vector<char> range;
range.reserve(sv.size() * count);
for (decltype(count) i = 0; i < count; ++i) {
std::copy_n(sv.begin(), sv.size(), std::back_inserter(range));
}
return range;
}
constexpr auto from_string_view(std::string_view sv) {
std::vector<std::byte> range;
range.resize(sv.size());
auto itr = range.begin();
for (auto c : sv) {
*itr = std::byte(c);
++itr;
}
return range;
}
template <typename T, typename E>
std::ostream &operator<<(std::ostream &os, tl::expected<T, E> const &exp) {
if (exp.has_value()) {
os << "Value: '" << *exp << "'";
} else {
os << "Error";
}
return os;
}
bool test_incomplete_message(auto const& payload) {
// Test incomplete message.
for (decltype(payload.size()) i = 1; i < payload.size() - 1; ++i) {
// Test incomplete message.
msgpack::token_reader reader(std::span(payload.data(), i));
auto token = reader.read_one();
if (token != tl::make_unexpected(msgpack::error::incomplete_message)) {
fmt::print("Got the wrong response reading subview[0,{}] of payload: {}\n",
i,
token->get<std::string_view>().value());
return false;
}
}
return true;
}
bool test_end_of_message(auto& reader) {
return reader.read_one() ==
tl::make_unexpected(msgpack::error::end_of_message);
}
}
template <>
struct fmt::formatter<msgpack::token> {
template <typename ParseContext>
constexpr auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template<typename FormatContext>
auto format(msgpack::token const& v, FormatContext& ctx) const {
auto out = fmt::format_to(ctx.out(), "<msgpack {} = ", magic_enum::enum_name(v.type()));
switch (v.type()) {
case msgpack::format::type::unsigned_int:
fmt::format_to(out, "{}", (*(v.get<std::uint64_t>())));
break;
case msgpack::format::type::signed_int:
out = fmt::format_to(out, "{}", (*(v.get<std::uint64_t>())));
break;
case msgpack::format::type::boolean:
out = fmt::format_to(out, "{}", (*(v.get<bool>())));
break;
case msgpack::format::type::string:
out = fmt::format_to(out, "{}", (*(v.get<std::string_view>())));
break;
case msgpack::format::type::binary:
out = fmt::format_to(out, "{}",
(*(v.get<std::span<std::byte const>>())));
break;
case msgpack::format::type::map:
out = fmt::format_to(out, "(arity: {})",
(v.get<msgpack::map_desc>()->count));
break;
case msgpack::format::type::array:
out = fmt::format_to(out, "(arity: {})",
(v.get<msgpack::array_desc>()->count));
break;
case msgpack::format::type::nil:
out = fmt::format_to(out, "(nil)");
break;
case msgpack::format::type::invalid:
out = fmt::format_to(out, "(invalid)");
break;
default:
break;
}
return fmt::format_to(out, ">");
}
};
suite views_tests = [] {
"read format::fixmap"_test = [] {
// A MessagePack map of 3 strings to 8-bit unsigned integers.
static constexpr auto strings = std::to_array<std::string_view>({
"one", "two", "three", "array", "four", "map", "five",
});
constexpr auto payload = cat(
make_bytes(0x87, 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, 0xa5),
generate_bytes([] { return from_string_view(strings[3]); }),
// Array of size 2, two fixints:
make_bytes(0x92, 0x32, 0x33),
make_bytes(0xa4),
generate_bytes([] { return from_string_view(strings[4]); }),
make_bytes(0x04, 0xa3),
generate_bytes([] { return from_string_view(strings[5]); }),
// Map of size 3, 6 total fixints.
make_bytes(0x83, 0x01, 0x02, 0x03, 0x04, 0x33, 0x11),
make_bytes(0xa4),
generate_bytes([] { return from_string_view(strings[6]); })
//make_bytes(0x05)
);
std::array<msgpack::token, 32> tokens;
msgpack::token_reader reader(payload);
auto result = reader.read_some(tokens).map([](auto read_tokens) {
for (auto const& [k, v] : msgpack::map_view(read_tokens)) {
fmt::print("map[{}] = {}\n", k, v);
}
});
expect(result.has_value());
};
};