diff --git a/include/msgpack23/msgpack23.h b/include/msgpack23/msgpack23.h index 2adfe36..f507ac1 100644 --- a/include/msgpack23/msgpack23.h +++ b/include/msgpack23/msgpack23.h @@ -1006,6 +1006,24 @@ namespace msgpack23 { value.assign(src, src + bin_size); increment(bin_size); } + + // Zero-copy alternative to the vector overload: returns a view into the + // internal buffer instead of copying. Valid only for the Unpacker's lifetime. + template + void unpack_type(std::span &value) { + std::size_t bin_size = 0; + if (read_conditional(bin_size) + or read_conditional(bin_size) + or read_conditional(bin_size)) { + } else { + throw std::logic_error("Unexpected value"); + } + if (position_ + bin_size > data_.size()) { + throw std::out_of_range("Span position is out of range"); + } + value = data_.subspan(position_, bin_size); + increment(bin_size); + } }; template diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4a5fe04..d4513f9 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -17,6 +17,7 @@ add_executable( main.cpp map_tests.cpp object_packing_tests.cpp + span_tests.cpp string_tests.cpp type_packing_tests.cpp uint8_tests.cpp diff --git a/tests/span_tests.cpp b/tests/span_tests.cpp new file mode 100644 index 0000000..5449d19 --- /dev/null +++ b/tests/span_tests.cpp @@ -0,0 +1,52 @@ +// +// Created by Neara Software on 28/02/2026. +// + +#include +#include + +namespace { + class msgpack23_span : public testing::TestWithParam { + }; + + TEST_P(msgpack23_span, binarySpanRoundTrip) { + std::vector expected{}; + for (std::size_t i = 0; i < GetParam(); ++i) { + expected.emplace_back(static_cast(i)); + } + std::vector data{}; + msgpack23::Packer packer{std::back_insert_iterator(data)}; + packer(expected); + + msgpack23::Unpacker unpacker{data}; + std::span actual{}; + unpacker(actual); + + ASSERT_EQ(actual.size(), expected.size()); + EXPECT_TRUE(std::equal(actual.begin(), actual.end(), expected.begin())); + } + + TEST_P(msgpack23_span, binarySpanIsZeroCopy) { + std::vector expected(GetParam(), 0x42); + std::vector data{}; + msgpack23::Packer packer{std::back_insert_iterator(data)}; + packer(expected); + + msgpack23::Unpacker unpacker{data}; + std::span actual{}; + unpacker(actual); + + // Span must point into the packed buffer, not a separate allocation + EXPECT_GE(actual.data(), data.data()); + EXPECT_LE(actual.data() + actual.size(), data.data() + data.size()); + } + + constexpr std::size_t span_sizes[] = { + 1, + std::numeric_limits::max() - 1, // bin8 near-max + std::numeric_limits::max() + 1, // bin16 boundary + std::numeric_limits::max() - 1, // bin16 near-max + std::numeric_limits::max() + 1, // bin32 boundary + }; + INSTANTIATE_TEST_SUITE_P(SomeValuesTest, msgpack23_span, testing::ValuesIn(span_sizes)); +}