#include "parselink/msgpack/core/reader.h" #include "rng.h" #include #include namespace { constexpr static std::size_t rng_samples_count = 10; template constexpr auto enum_name() noexcept { return __PRETTY_FUNCTION__; } using namespace boost::ut; namespace format = msgpack::format; 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; } } // namespace suite reader = [] { "empty span"_test = [] { std::span bytes; msgpack::reader reader(bytes); auto v = reader.read(); expect(v.error() == msgpack::error::end_of_message); }; //////////////////////////////////////////////////////////////////////////////// // Unsigned integers //////////////////////////////////////////////////////////////////////////////// "reader::read"_test = [] { using fmt = format::positive_fixint; using error = msgpack::error; /** * All bytes 0x00->0x79 are effectively literal uint8s. */ { constexpr auto payload = generate_bytes( [] { return make_contiguous_range(0, 0x79); }); msgpack::reader reader(payload); static_assert(std::is_same_v< typename decltype(reader.read())::value_type, std::uint8_t>); for (auto byte : payload) { auto result = reader.read(); expect(result == std::uint8_t(byte)); } auto result = reader.read(); expect(result == tl::make_unexpected(error::end_of_message)); } { constexpr auto payload = generate_bytes( [] { return make_contiguous_range(0x80, 0xff); }); // Continually narrow the span over the range, as read will not // advance the internal span on failure. for (auto itr = payload.begin(); itr != payload.end(); ++itr) { msgpack::reader reader(std::span(itr, std::end(payload))); auto result = reader.read(); expect(result == tl::make_unexpected(error::wrong_type)); } } }; "reader::read"_test = [] { using fmt = format::uint8; using error = msgpack::error; /** * The uint8 format is 0xcc followed by one byte of data. */ { constexpr auto payload = generate_bytes([] { return zip(std::byte{0xcc}, make_contiguous_range(0x00, 0xff)); }); msgpack::reader reader(payload); static_assert(std::is_same_v< typename decltype(reader.read())::value_type, std::uint8_t>); for (auto i = 0; i < 0x100; ++i) { auto result = reader.read(); expect(result == std::uint8_t(i)); } auto result = reader.read(); expect(result == tl::make_unexpected(error::end_of_message)); } { using error = msgpack::error; // Test that partial read fails. constexpr auto payload = make_bytes(0xcc); msgpack::reader reader(payload); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); // Retry, ensure the error remains the same. result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } }; "reader::read"_test = [] { using fmt = format::uint16; using error = msgpack::error; rng rng; { auto samples = rng.get(); std::vector payload; for (auto sample : samples) { auto bytes = ::detail::raw_cast(host_to_be(sample)); payload.push_back(std::byte{0xcd}); for (auto byte : bytes) { payload.push_back(byte); } } msgpack::reader reader(payload); for (auto sample : samples) { auto result = reader.read(); expect(result == sample); } auto result = reader.read(); expect(result == tl::make_unexpected(error::end_of_message)); } { constexpr auto payload = make_bytes(0xee); msgpack::reader reader(payload); auto result = reader.read(); expect(result == tl::make_unexpected(error::wrong_type)); } // Test that partial read fails. { constexpr auto payload = make_bytes(0xcd); msgpack::reader reader(payload); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); constexpr auto payload2 = make_bytes(0xcd, 0x01); reader = msgpack::reader(payload2); expect(result == tl::make_unexpected(error::incomplete_message)); } }; "reader::read"_test = [] { using fmt = format::uint32; using error = msgpack::error; rng rng; auto samples = rng.get(); std::vector payload; for (auto sample : samples) { auto bytes = ::detail::raw_cast(host_to_be(sample)); payload.push_back(std::byte{0xce}); for (auto byte : bytes) { payload.push_back(byte); } } msgpack::reader reader(payload); for (auto sample : samples) { auto result = reader.read(); expect(bool(result)); expect(result == sample); } // Test that partial read fails. { constexpr auto payload = make_bytes(0xce, 0x01, 0x02, 0x03, 0x09); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == 0x01020309); } }; "reader::read"_test = [] { using fmt = format::uint64; using error = msgpack::error; rng rng; auto samples = rng.get(); std::vector payload; for (auto sample : samples) { auto bytes = ::detail::raw_cast(host_to_be(sample)); payload.push_back(std::byte{0xcf}); for (auto byte : bytes) { payload.push_back(byte); } } msgpack::reader reader(payload); for (auto sample : samples) { auto result = reader.read(); expect(bool(result)); expect(result == sample); } { constexpr auto payload = make_bytes( 0xcf, 0x01, 0x02, 0x03, 0x09, 0x10, 0x20, 0x30, 0x90); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == 0x0102030910203090); } }; //////////////////////////////////////////////////////////////////////////////// // Signed integers //////////////////////////////////////////////////////////////////////////////// "reader::read"_test = [] { using fmt = format::negative_fixint; using error = msgpack::error; /** * All bytes 0x00->0x79 are effectively literal uint8s. */ { constexpr auto payload = generate_bytes( [] { return make_contiguous_range(0xe0, 0xff); }); msgpack::reader reader(payload); static_assert(std::is_same_v< typename decltype(reader.read())::value_type, std::int8_t>); for (auto byte : payload) { auto result = reader.read(); expect(result == std::int8_t(byte)); } auto result = reader.read(); expect(result == tl::make_unexpected(error::end_of_message)); } { constexpr auto payload = generate_bytes( [] { return make_contiguous_range(0x00, 0xdf); }); // Continually narrow the span over the range, as read will not // advance the internal span on failure. for (auto itr = payload.begin(); itr != payload.end(); ++itr) { msgpack::reader reader(std::span(itr, std::end(payload))); auto result = reader.read(); expect(result == tl::make_unexpected(error::wrong_type)); } } }; "reader::read"_test = [] { using fmt = format::int8; using error = msgpack::error; /** * The uint8 format is 0xcc followed by one byte of data. */ { constexpr auto payload = generate_bytes([] { return zip(std::byte{0xd0}, make_contiguous_range(0x00, 0xff)); }); msgpack::reader reader(payload); static_assert(std::is_same_v< typename decltype(reader.read())::value_type, std::int8_t>); for (auto i = 0; i < 0x100; ++i) { auto result = reader.read(); expect(*result == std::int8_t(i)); } auto result = reader.read(); expect(result == tl::make_unexpected(error::end_of_message)); } { using error = msgpack::error; // Test that partial read fails. constexpr auto payload = make_bytes(0xd0); msgpack::reader reader(payload); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); // Retry, ensure the error remains the same. result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } }; "reader::read"_test = [] { using fmt = format::int16; using error = msgpack::error; rng rng; { auto samples = rng.get(); std::vector payload; for (auto sample : samples) { auto bytes = ::detail::raw_cast(host_to_be(sample)); payload.push_back(std::byte{0xd1}); for (auto byte : bytes) { payload.push_back(byte); } } msgpack::reader reader(payload); for (auto sample : samples) { auto result = reader.read(); expect(result == sample); } auto result = reader.read(); expect(result == tl::make_unexpected(error::end_of_message)); } { constexpr auto payload = make_bytes(0xd2); msgpack::reader reader(payload); auto result = reader.read(); expect(result == tl::make_unexpected(error::wrong_type)); } // Test that partial read fails. { constexpr auto payload = make_bytes(0xd1); msgpack::reader reader(payload); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); constexpr auto payload2 = make_bytes(0xd1, 0x01); reader = msgpack::reader(payload2); expect(result == tl::make_unexpected(error::incomplete_message)); } }; "reader::read"_test = [] { using fmt = format::int32; using error = msgpack::error; rng rng; auto samples = rng.get(); std::vector payload; for (auto sample : samples) { auto bytes = ::detail::raw_cast(host_to_be(sample)); payload.push_back(std::byte{0xd2}); for (auto byte : bytes) { payload.push_back(byte); } } msgpack::reader reader(payload); for (auto sample : samples) { auto result = reader.read(); expect(bool(result)); expect(result == sample); } // Test that partial read fails. { constexpr auto payload = make_bytes(0xd2, 0xff, 0xff, 0xff, 0xfe); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == -2); } }; "reader::read"_test = [] { using fmt = format::int64; using error = msgpack::error; rng rng; auto samples = rng.get(); std::vector payload; for (auto sample : samples) { auto bytes = ::detail::raw_cast(host_to_be(sample)); payload.push_back(std::byte{0xd3}); for (auto byte : bytes) { payload.push_back(byte); } } msgpack::reader reader(payload); for (auto sample : samples) { auto result = reader.read(); expect(bool(result)); expect(result == sample); } { constexpr auto payload = make_bytes( 0xd3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == -16); } }; //////////////////////////////////////////////////////////////////////////////// // Strings //////////////////////////////////////////////////////////////////////////////// "reader::read"_test = [] { using fmt = msgpack::format::fixstr; using error = msgpack::error; constexpr std::string_view sv = "hello"; constexpr auto payload = cat(make_bytes(0xa0 + sv.size()), generate_bytes([sv] { return from_string_view(sv); })); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == sv); }; "reader::read"_test = [] { using fmt = msgpack::format::str8; using error = msgpack::error; constexpr std::string_view sv = "hello d"; constexpr auto payload = cat(make_bytes(0xd9, sv.size()), generate_bytes([sv] { return from_string_view(sv); })); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == sv); }; "reader::read"_test = [] { using fmt = msgpack::format::str16; using error = msgpack::error; constexpr std::string_view sv = "hello world"; constexpr auto payload = cat(make_bytes(0xda, 0x00, sv.size()), generate_bytes([sv] { return from_string_view(sv); })); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == sv); }; "reader::read"_test = [] { using fmt = msgpack::format::str32; using error = msgpack::error; constexpr std::string_view sv = "hello world"; constexpr auto payload = cat(make_bytes(0xdb, 0x00, 0x00, 0x00, sv.size()), generate_bytes([sv] { return from_string_view(sv); })); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result == sv); }; //////////////////////////////////////////////////////////////////////////////// // Binary payloads //////////////////////////////////////////////////////////////////////////////// "reader::read"_test = [] { using fmt = msgpack::format::bin8; using error = msgpack::error; constexpr auto bv = make_bytes(0x0, 0x01, 0x02, 0x04); constexpr auto payload = cat(make_bytes(0xc4, bv.size()), bv); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(result.has_value()); expect(std::equal( (*result).begin(), (*result).end(), bv.begin(), bv.end())); }; "reader::read"_test = [] { using fmt = msgpack::format::bin16; using error = msgpack::error; constexpr auto bv = generate_bytes([] { auto rg = make_contiguous_range(0, 0xff); std::vector rg2(rg.size() * 2); auto itr = std::copy(rg.begin(), rg.end(), rg2.begin()); std::copy(rg.begin(), rg.end(), itr); return rg2; }); constexpr auto payload = cat(make_bytes(0xc5, bv.size() >> 8, bv.size() & 0xff), bv); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(std::equal( (*result).begin(), (*result).end(), bv.begin(), bv.end())); }; // TODO: Support this with a proper test payload "reader::read"_test = [] { using fmt = msgpack::format::bin32; using error = msgpack::error; constexpr auto bv = generate_bytes([] { auto rg = make_contiguous_range(0, 0xff); std::vector rg2(rg.size() * 2); auto itr = std::copy(rg.begin(), rg.end(), rg2.begin()); std::copy(rg.begin(), rg.end(), itr); return rg2; }); constexpr auto payload = cat(make_bytes(0xc6, bv.size() >> 24, bv.size() >> 16, bv.size() >> 8, bv.size() & 0xff), bv); for (auto itr = payload.begin() + 1; itr != payload.end(); ++itr) { msgpack::reader reader({payload.begin(), itr}); auto result = reader.read(); expect(result == tl::make_unexpected(error::incomplete_message)); } msgpack::reader reader(payload); auto result = reader.read(); expect(std::equal( (*result).begin(), (*result).end(), bv.begin(), bv.end())); }; //////////////////////////////////////////////////////////////////////////////// // Misc formats //////////////////////////////////////////////////////////////////////////////// "reader::read"_test = [] { using fmt = msgpack::format::nil; constexpr auto payload = make_bytes(0xc0); msgpack::reader reader(payload); auto result = reader.read(); expect(result.has_value()); }; "reader::read"_test = [] { using fmt = msgpack::format::boolean; constexpr auto payload = make_bytes(0xc2, 0xc3); msgpack::reader reader(payload); auto result = reader.read(); expect(result == false); result = reader.read(); expect(result == true); }; //////////////////////////////////////////////////////////////////////////////// // Structural formats //////////////////////////////////////////////////////////////////////////////// "reader::read"_test = [] { using fmt = msgpack::format::fixarray; using error = msgpack::error; // 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::reader reader(payload); auto result = reader.read(); expect(result == msgpack::array_desc{5}); for (std::size_t i = 0; i < (*result).count; ++i) { auto v = reader.read(); expect(v == 0x85 - i); } auto end = reader.read(); expect(end == tl::make_unexpected(error::end_of_message)); }; "reader::read"_test = [] { using fmt = msgpack::format::array16; using error = msgpack::error; // 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::reader reader(payload); auto result = reader.read(); expect(result == msgpack::array_desc{5}); for (std::size_t i = 0; i < (*result).count; ++i) { auto v = reader.read(); expect(v == 0x85 - i); } auto end = reader.read(); expect(end == tl::make_unexpected(error::end_of_message)); }; "reader::read"_test = [] { using fmt = msgpack::format::array32; using error = msgpack::error; // 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::reader reader(payload); auto result = reader.read(); expect(result == msgpack::array_desc{5}); for (std::size_t i = 0; i < (*result).count; ++i) { auto v = reader.read(); expect(v == 0x85 - i); } auto end = reader.read(); expect(end == tl::make_unexpected(error::end_of_message)); }; };