diff --git a/source/include/parselink/msgpack/core/format.h b/source/include/parselink/msgpack/core/format.h index 95c767b..fbf0421 100644 --- a/source/include/parselink/msgpack/core/format.h +++ b/source/include/parselink/msgpack/core/format.h @@ -145,6 +145,16 @@ namespace format { constexpr static type value = type::invalid; }; + template <> + struct resolve_token_type { + constexpr static type value = type::array; + }; + + template <> + struct resolve_token_type { + constexpr static type value = type::map; + }; + template constexpr static auto resolve_type_v = resolve_token_type::value; diff --git a/source/include/parselink/msgpack/token/reader.h b/source/include/parselink/msgpack/token/reader.h index d1d33fa..f244ad4 100644 --- a/source/include/parselink/msgpack/token/reader.h +++ b/source/include/parselink/msgpack/token/reader.h @@ -92,6 +92,16 @@ constexpr inline format::traits const& traits_lookup(std::byte id) { case format::bin32::marker: return format::get_traits(); + case format::array16::marker: + return format::get_traits(); + case format::array32::marker: + return format::get_traits(); + + case format::map16::marker: + return format::get_traits(); + case format::map32::marker: + return format::get_traits(); + case format::nil::marker: return format::get_traits(); case format::invalid::marker: @@ -104,15 +114,21 @@ constexpr inline format::traits const& traits_lookup(std::byte id) { } // TODO(ksassenrath): Handle fixtype formats. - if ((id & ~format::fixstr::mask) == format::fixstr::marker) { - return format::get_traits(); - } if ((id & ~format::negative_fixint::mask) == format::negative_fixint::marker) { return format::get_traits(); } if ((id & ~format::positive_fixint::mask) == format::positive_fixint::marker) { return format::get_traits(); } + if ((id & ~format::fixstr::mask) == format::fixstr::marker) { + return format::get_traits(); + } + if ((id & ~format::fixmap::mask) == format::fixmap::marker) { + return format::get_traits(); + } + if ((id & ~format::fixarray::mask) == format::fixarray::marker) { + return format::get_traits(); + } return format::no_traits; } @@ -200,20 +216,24 @@ public: } case format::type::string: { using type = token_traits::storage_type; - { - auto ptr = reinterpret_cast(&*curr); - tok = token{std::string_view{ptr, var_size}}; - curr += var_size; - } + auto ptr = reinterpret_cast(&*curr); + tok = token{std::string_view{ptr, var_size}}; + curr += var_size; break; } case format::type::binary: { using type = token_traits::storage_type; - { - auto ptr = reinterpret_cast(&*curr); - tok = token{std::span{ptr, var_size}}; - curr += var_size; - } + auto ptr = reinterpret_cast(&*curr); + tok = token{std::span{ptr, 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; } default: diff --git a/source/include/parselink/msgpack/token/type.h b/source/include/parselink/msgpack/token/type.h index c26e960..c289fc4 100644 --- a/source/include/parselink/msgpack/token/type.h +++ b/source/include/parselink/msgpack/token/type.h @@ -132,6 +132,16 @@ struct token_traits { using storage_type = std::byte const*; }; +template <> +struct token_traits { + using storage_type = std::uint64_t; +}; + +template <> +struct token_traits { + using storage_type = std::uint64_t; +}; + template <> struct token_traits { using storage_type = bool; @@ -180,6 +190,16 @@ public: 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 { size_and_type_.set_enum(format::type::nil); } @@ -227,7 +247,8 @@ private: typename token_traits::storage_type str; typename token_traits::storage_type bp; typename token_traits::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_; size_and_enum size_and_type_{}; }; @@ -296,6 +317,26 @@ token_base<8>::get() const noexcept return invalid{}; } +template<> +constexpr tl::expected +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 +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; } // namespace msgpack diff --git a/tests/msgpack/test_token_reader.cpp b/tests/msgpack/test_token_reader.cpp index 1c20014..2f25c02 100644 --- a/tests/msgpack/test_token_reader.cpp +++ b/tests/msgpack/test_token_reader.cpp @@ -1,7 +1,7 @@ #include -#include #include +#include #include using namespace boost::ut; @@ -486,6 +486,156 @@ suite reader_tests = [] { 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(); + expect(array && array->count == 5); + + for (std::size_t i = 0; i < array->count; ++i) { + auto v = reader.read_one(); + expect(v && v->get() == 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(); + expect(array && array->count == 5); + + for (std::size_t i = 0; i < array->count; ++i) { + auto v = reader.read_one(); + expect(v && v->get() == 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(); + expect(array && array->count == 5); + + for (std::size_t i = 0; i < array->count; ++i) { + auto v = reader.read_one(); + expect(v && v->get() == 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 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(); + expect(map && map->count == 3); + + for (std::size_t i = 0; i < map->count; ++i) { + auto str = reader.read_one(); + expect(str && str->get() == strings[i]); + auto value = reader.read_one(); + expect(value && value->get() == 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 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(); + expect(map && map->count == 3); + + for (std::size_t i = 0; i < map->count; ++i) { + auto str = reader.read_one(); + expect(str && str->get() == strings[i]); + auto value = reader.read_one(); + expect(value && value->get() == 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 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(); + expect(map && map->count == 3); + + for (std::size_t i = 0; i < map->count; ++i) { + auto str = reader.read_one(); + expect(str && str->get() == strings[i]); + auto value = reader.read_one(); + expect(value && value->get() == i + 1); + } + + expect(test_end_of_message(reader)); + }; + //-------------------------------------------------------------------------- // Assorted format tests //-------------------------------------------------------------------------- "read format::boolean"_test = [] {