#include "parselink/msgpack/core/writer.h" #include #include #include #include using namespace boost::ut; namespace format = msgpack::format; namespace { template constexpr std::array make_bytes(Bytes&&... bytes) { return {std::byte(std::forward(bytes))...}; } template struct oversized_array { std::array data; std::size_t size; }; constexpr auto to_bytes_array_oversized(auto const& container) { oversized_array 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()); std::array out; std::copy(std::begin(oversized.data), std::next(std::begin(oversized.data), oversized.size), std::begin(out)); return out; } template consteval auto cat( std::array const& a, std::array const& b) { std::array out; std::copy(std::begin(a), std::next(std::begin(a), std::size(a)), std::begin(out)); std::copy(std::begin(b), std::next(std::begin(b), std::size(b)), std::next(std::begin(out), std::size(a))); return out; } template constexpr auto zip(T val, Itr begin, Itr end) { std::vector output; for (auto itr = begin; itr != end; ++itr) { output.emplace_back(val); output.emplace_back(*itr); } return output; } template constexpr auto zip(T val, Iterable const& itr) { return zip(val, std::begin(itr), std::end(itr)); } constexpr auto from_string_view(std::string_view sv) { std::vector range; range.resize(sv.size()); auto itr = range.begin(); for (auto c : sv) { *itr = std::byte(c); ++itr; } return range; } constexpr auto make_contiguous_range(std::uint8_t start, std::uint8_t end) { auto count = std::size_t(end) - std::size_t(start) + 1; std::vector range; range.resize(count); for (auto i = std::size_t(start); i <= std::size_t(end); ++i) { range[i - std::size_t(start)] = std::byte(i); } return range; } constexpr auto equal(auto a, auto b) { return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b)); } } // namespace suite writer = [] { "writer empty span"_test = [] { std::array payload; msgpack::writer writer(payload); expect(writer.tell() == 0); }; "writer::write"_test = [] { using fmt = format::positive_fixint; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0x32, 0x55); msgpack::writer writer(payload); expect(!!writer.write(std::uint8_t{0x32})); expect(writer.tell() == 1); expect(*writer.subspan().begin() == *expected.begin()); expect(writer.write(std::uint8_t{0x82}) == tl::make_unexpected(error::bad_value)); expect(!!writer.write(std::uint8_t{0x55})); expect(writer.tell() == 2); expect(equal(writer.subspan(), expected)); expect(writer.write(std::uint8_t{0x82}) == tl::make_unexpected(error::out_of_space)); expect(writer.write(std::uint8_t{0x32}) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::uint8; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xcc, 0x32, 0xcc, 0x82); msgpack::writer writer(payload); auto result = writer.write(std::uint8_t{0x32}); expect(!!result); expect(writer.tell() == 2); expect(equal(writer.subspan(), std::span{expected.begin(), 2})); expect(!!writer.write(std::uint8_t{0x82})); expect(equal(writer.subspan(), expected)); expect(writer.write(std::uint8_t{0x01}) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::uint16; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xcd, 0x32, 0xcc, 0xcd, 0xaa, 0xff); msgpack::writer writer(payload); expect(!!writer.write(std::uint16_t{0x32cc})); expect(writer.tell() == 3); expect(equal(writer.subspan(), std::span{expected.begin(), 3})); expect(!!writer.write(0xaaff)); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::uint32; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xce, 0x01, 0x02, 0x03, 0x04); msgpack::writer writer(payload); expect(!!writer.write(0x01020304)); expect(writer.tell() == 5); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::uint64; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes( 0xcf, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); msgpack::writer writer(payload); expect(!!writer.write(0x0102030405060708)); expect(writer.tell() == 9); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::negative_fixint; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xff, 0xe0); msgpack::writer writer(payload); expect(!!writer.write(-1)); expect(writer.tell() == 1); expect(*writer.subspan().begin() == std::byte{0xff}); expect(writer.write(-33) == tl::make_unexpected(error::bad_value)); expect(!!writer.write(-32)); expect(writer.tell() == 2); expect(equal(writer.subspan(), expected)); expect(writer.write(-5) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::int8; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xd0, 0x32, 0xd0, 0xfb); msgpack::writer writer(payload); expect(!!writer.write(std::int8_t{0x32})); expect(writer.tell() == 2); expect(equal(writer.subspan(), std::span{expected.begin(), 2})); expect(!!writer.write(std::int8_t{-5})); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::int16; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xd1, 0x32, 0xcc, 0xd1, 0xff, 0xfa); msgpack::writer writer(payload); expect(!!writer.write(std::int16_t{0x32cc})); expect(writer.tell() == 3); expect(equal(writer.subspan(), std::span{expected.begin(), 3})); expect(!!writer.write(-6)); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::int32; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes(0xd2, 0x01, 0x02, 0x03, 0x04); msgpack::writer writer(payload); expect(!!writer.write(0x01020304)); expect(writer.tell() == 5); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::int64; using error = msgpack::error; std::array payload; auto constexpr expected = make_bytes( 0xd3, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08); msgpack::writer writer(payload); expect(!!writer.write(std::int64_t{0x0102030405060708})); expect(writer.tell() == 9); expect(equal(writer.subspan(), expected)); expect(writer.write(0x01) == tl::make_unexpected(error::out_of_space)); }; "writer::write"_test = [] { using fmt = format::fixstr; std::array payload; auto constexpr expected = make_bytes(0xa2, 'o', 'h'); msgpack::writer writer(payload); expect(!!writer.write("oh")); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::str8; std::array payload; auto constexpr expected = make_bytes(0xd9, 44, 't', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g'); msgpack::writer writer(payload); std::string_view txt = "the quick brown fox jumped over the lazy dog"; expect(!!writer.write(std::move(txt))); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::str16; std::array payload; auto constexpr expected = make_bytes(0xda, 0, 44, 't', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g'); msgpack::writer writer(payload); std::string_view txt = "the quick brown fox jumped over the lazy dog"; expect(!!writer.write(std::move(txt))); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::str32; std::array payload; auto constexpr expected = make_bytes(0xdb, 0, 0, 0, 44, 't', 'h', 'e', ' ', 'q', 'u', 'i', 'c', 'k', ' ', 'b', 'r', 'o', 'w', 'n', ' ', 'f', 'o', 'x', ' ', 'j', 'u', 'm', 'p', 'e', 'd', ' ', 'o', 'v', 'e', 'r', ' ', 't', 'h', 'e', ' ', 'l', 'a', 'z', 'y', ' ', 'd', 'o', 'g'); msgpack::writer writer(payload); std::string_view txt = "the quick brown fox jumped over the lazy dog"; expect(!!writer.write(std::move(txt))); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::bin8; std::array payload; auto constexpr expected = make_bytes( 0xc4, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa); msgpack::writer writer(payload); std::span bv(expected.begin() + 2, expected.end()); expect(!!writer.write(std::move(bv))); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::bin16; std::array payload; auto constexpr expected = make_bytes( 0xc5, 0x0, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa); msgpack::writer writer(payload); std::span bv(expected.begin() + 3, expected.end()); expect(!!writer.write(std::move(bv))); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::bin32; std::array payload; auto constexpr expected = make_bytes(0xc6, 0x0, 0x0, 0x0, 0x07, 0x01, 0x02, 0x03, 0x04, 0xf8, 0xf9, 0xfa); msgpack::writer writer(payload); std::span bv(expected.begin() + 5, expected.end()); expect(!!writer.write(std::move(bv))); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::fixmap; std::array payload; auto constexpr expected = make_bytes(0x83); msgpack::writer writer(payload); expect(!!writer.write(msgpack::map_desc{3})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::map16; std::array payload; auto constexpr expected = make_bytes(0xde, 0x01, 0x00); msgpack::writer writer(payload); expect(!!writer.write(msgpack::map_desc{256})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::map32; std::array payload; auto constexpr expected = make_bytes(0xdf, 0x00, 0x01, 0x00, 0x00); msgpack::writer writer(payload); expect(!!writer.write(msgpack::map_desc{0x10000})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::fixarray; std::array payload; auto constexpr expected = make_bytes(0x93); msgpack::writer writer(payload); expect(!!writer.write(msgpack::array_desc{3})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::array16; std::array payload; auto constexpr expected = make_bytes(0xdc, 0x01, 0x00); msgpack::writer writer(payload); expect(!!writer.write(msgpack::array_desc{256})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::array32; std::array payload; auto constexpr expected = make_bytes(0xdd, 0x00, 0x01, 0x00, 0x00); msgpack::writer writer(payload); expect(!!writer.write(msgpack::array_desc{0x10000})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::nil; std::array payload; auto constexpr expected = make_bytes(0xc0); msgpack::writer writer(payload); expect(!!writer.write({})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::invalid; std::array payload; auto constexpr expected = make_bytes(0xc1); msgpack::writer writer(payload); expect(!!writer.write({})); expect(equal(writer.subspan(), expected)); }; "writer::write"_test = [] { using fmt = format::boolean; std::array payload; auto constexpr expected = make_bytes(0xc2, 0xc3); msgpack::writer writer(payload); expect(!!writer.write(false)); expect(!!writer.write(true)); expect(equal(writer.subspan(), expected)); }; }; // Deduced writer tests namespace { template struct test_data {}; template struct test_data { static constexpr auto values = std::to_array({ -1, // negative fixint -32, // negative fixint -33, // int8 -128, // int8 0, // int8 127, // int8 128, // int16 -129, // int16 -32768, // int16 32767, // int16 -32769, // int32 32768, // int32 -2147483648, // int32 2147483647, // int32 -2147483649, // int64 2147483648, // int64 std::numeric_limits::lowest(), // int64 std::numeric_limits::max(), // int64 }); static constexpr auto payload = make_bytes(0xff, // negative fixint 0xe0, // negative fixint 0xd0, 0xdf, // int8 0xd0, 0x80, // int8 0xd0, 0x0, // int8 0xd0, 0x7f, // int8 0xd1, 0x00, 0x80, // int16 0xd1, 0xff, 0x7f, // int16 0xd1, 0x80, 0x00, // int16 0xd1, 0x7f, 0xff, // int16 0xd2, 0xff, 0xff, 0x7f, 0xff, // int32 0xd2, 0x00, 0x00, 0x80, 0x00, // int32 0xd2, 0x80, 0x00, 0x00, 0x00, // int32 0xd2, 0x7f, 0xff, 0xff, 0xff, // int32 0xd3, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, // int64 0xd3, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, // int64 0xd3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // int64 0xd3, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // int64 ); static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max() && value >= std::numeric_limits::lowest(); } }; template struct test_data { static constexpr auto values = std::to_array({ 0x00, // positive fixint 0x79, // positive fixint 0x80, // uint8 0xff, // uint8 0x100, // uint16 0xffff, // uint16 0x10000, // uint32 0xffffffff, // uint32 0x100000000, // uint64 0xffffffffffffffff // uint64 }); static constexpr auto payload = make_bytes(0x00, // positive fixint 0x79, // positive fixint 0xcc, 0x80, // uint8 0xcc, 0xff, // uint8 0xcd, 0x01, 0x00, // uint16 0xcd, 0xff, 0xff, // uint16 0xce, 0x00, 0x01, 0x00, 0x00, // uint32 0xce, 0xff, 0xff, 0xff, 0xff, // uint32 0xcf, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, // uint64 0xcf, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // uint64 ); static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max(); } }; template <> struct test_data { static constexpr auto values = std::to_array({ "", // fixstr "0", // fixstr "0123456789abcdef0123456789abcde", // fixstr "0123456789abcdef0123456789abcdef", // str8 }); static constexpr auto payload = make_bytes(0xa0, // fixstr 0xa1, 0x30, // fixstr // fixstr 0xbf, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, // str8 0xd9, 0x20, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66); static constexpr auto valid(auto value) noexcept { return value.size() <= std::numeric_limits::max(); } }; template bool test_deduced() noexcept { constexpr auto const& expected_payload = test_data::payload; std::array payload; msgpack::writer writer(payload); for (auto const& value : test_data::values) { if (!test_data::valid(value)) break; expect(!!writer.write2(T(value))); auto expect = std::span(expected_payload.begin(), writer.tell()); auto correct = equal(writer.subspan(), expect); if (!correct) { fmt::print("Deduction failed for '{}'\n", T(value)); fmt::print("\tActual: {::#04x}\n", writer.subspan()); fmt::print("\tExpect: {::#04x}\n", expect); return false; } } return true; } } // anonymous namespace suite deduced_writer = [] { "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; "writer::write (deduced + compressed)"_test = [] { expect(test_deduced()); }; };