//----------------------------------------------------------------------------- // ___ __ _ _ // / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __ // / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ / // / ___/ (_| | | \__ \ __/ /__| | | | | < // \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ . // //----------------------------------------------------------------------------- // Author: Kurt Sassenrath // Module: msgpack // // Default packer tests. // // Copyright (c) 2023 Kurt Sassenrath. // // License TBD. //----------------------------------------------------------------------------- #include "parselink/msgpack/core/packer.h" #include #include #include "rng.h" #include #include using namespace boost::ut; namespace { template constexpr std::array make_bytes(Bytes&&... bytes) { return {static_cast(std::forward(bytes))...}; } constexpr auto equal(auto a, auto b) { return std::equal(std::begin(a), std::end(a), std::begin(b), std::end(b)); } template struct test_data {}; 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 : test_data { static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max(); } }; 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 T> 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(T value) noexcept { return std::string_view(value).size() <= std::numeric_limits::max(); } }; template <> struct test_data> { static constexpr auto test1 = make_bytes(0x02, 0x03); static constexpr auto values = std::to_array>({test1}); static constexpr auto payload = make_bytes(0xc4, 0x02, 0x02, 0x03); static constexpr auto valid(auto value) noexcept { return value.size() <= std::numeric_limits::max(); } }; template struct test_data : test_data { static constexpr auto valid(auto value) noexcept { return value <= std::numeric_limits::max() && value >= std::numeric_limits::lowest(); } }; template bool test_deduced() noexcept { constexpr auto const& expected_payload = test_data::payload; std::array payload; msgpack::packer packer(payload); for (auto const& value : test_data::values) { if (!test_data::valid(value)) break; expect(!!packer.pack(T(value))); auto expect = std::span(expected_payload.begin(), packer.tell()); auto correct = equal(packer.subspan(), expect); if (!correct) { fmt::print("Deduction failed for '{}'\n", T(value)); fmt::print("\tActual: {::#04x}\n", packer.subspan()); fmt::print("\tExpect: {::#04x}\n", expect); return false; } } return true; } } // anonymous namespace suite packer = [] { "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack"_test = [] { expect(test_deduced()); }; "packer::pack>"_test = [] { expect(test_deduced>()); }; }; static constexpr auto num_bytes(std::uint64_t count) { if (count <= 0x7f) return 0; if (count < 0x100) return 1; if (count < 0x10000) return 2; if (count < 0x100000000) return 4; return 8; } static constexpr std::size_t bit_count_bytes(std::uint64_t count) { if (count <= 0x7f) return 0; return std::bit_ceil(std::uint64_t((std::bit_width(count) + 7) >> 3)); } suite perf = [] { "test"_test = [] { for (auto j = 0; j < 10; ++j) { using namespace std::chrono_literals; std::vector a; std::vector r1; std::vector r2; a.reserve(10000000); r1.reserve(10000000); r2.reserve(10000000); rng x; for (auto i = 0; i < 10000000; ++i) { a.push_back(x.get()); } auto start2 = std::chrono::steady_clock::now(); for (auto v : a) { r2.push_back(num_bytes(v)); } auto end2 = std::chrono::steady_clock::now(); auto start = std::chrono::steady_clock::now(); for (auto v : a) { r1.push_back(bit_count_bytes(v)); } auto end = std::chrono::steady_clock::now(); fmt::print("Time taken: {}us vs {}us\n", (end - start) / 1us, (end2 - start2) / 1us); } }; };