diff --git a/include/boost/buffers/any_buffers.hpp b/include/boost/buffers/any_buffers.hpp index b45334e..f1b50a4 100644 --- a/include/boost/buffers/any_buffers.hpp +++ b/include/boost/buffers/any_buffers.hpp @@ -14,7 +14,7 @@ #include #include #include - +#include #include #include #include @@ -34,14 +34,11 @@ namespace buffers { nothrow copy constructible. Larger iterators fall back to an index-based traversal strategy. - @note The wrapped buffer sequence must remain valid - for the lifetime of this object. - @tparam IsConst If `true`, the sequence yields @ref const_buffer elements. If `false`, it yields @ref mutable_buffer elements. - @see any_const_buffers, any_mutable_buffers, make_any_buffers + @see any_const_buffers, any_mutable_buffers */ template class any_buffers @@ -61,6 +58,61 @@ class any_buffers */ class const_iterator; + /** Destructor. + */ + ~any_buffers() + { + p_->destroy(); + } + + /** Constructor. + Default-constructed objects are empty with zero length. + */ + any_buffers() noexcept; + + /** Constructor. + */ + any_buffers( + any_buffers const& other) noexcept + { + other.p_->copy(*this); + } + + /** Assignment. + */ + any_buffers& + operator=( + any_buffers const& other) noexcept + { + if(this == &other) + return *this; + p_->destroy(); + other.p_->copy(*this); + return *this; + } + + /** Constructor. + + The type-erased buffer sequence is constructed + from the specified buffer sequence, which must satisfy + `ConstBufferSequence`. If `IsConst` is `false`, must + also satisfy `MutableBufferSequence`. + + @param buffers The buffer sequence to type-erase. + */ + template::type>::value>::type> + any_buffers( + BufferSequence&& buffers) + { + using T = typename std::decay::type; + construct(std::forward(buffers), + std::integral_constant) <= sbo_size)>{}); + } + /** Return an iterator to the beginning. @return An iterator pointing to the first buffer, @@ -74,75 +126,68 @@ class any_buffers */ const_iterator end() const noexcept; - /** Construct from a buffer sequence. +private: + friend struct any_buffers_test; - Wraps the given buffer sequence for type-erased - access. The caller must ensure `bs` remains valid - for the lifetime of this object. + static constexpr std::size_t sbo_size = 6 * sizeof(void*); - @param bs The buffer sequence to wrap. Must satisfy - `ConstBufferSequence`. If `IsConst` is `false`, must - also satisfy `MutableBufferSequence`. + static constexpr std::size_t iter_sbo_size = 4 * sizeof(void*); - @tparam BufferSequence The concrete buffer sequence type. - */ - template - any_buffers(BufferSequence const& bs); - -private: - static constexpr std::size_t sbo_size = 4 * sizeof(void*); + struct BOOST_SYMBOL_VISIBLE + any_impl + { + virtual ~any_impl() = default; + virtual bool is_small_buffers() const noexcept = 0; + virtual bool is_small_iter() const noexcept = 0; + virtual void destroy() const = 0; + virtual void copy(any_buffers& dest) const = 0; + virtual void it_copy(void*, void const*) const = 0; + virtual void it_destroy(void*) const = 0; + virtual void inc(void*) const = 0; + virtual void dec(void*) const = 0; + virtual auto deref(void const*) const -> value_type = 0; + virtual bool equal(void const*, void const*) const = 0; + virtual void begin(void*) const = 0; + virtual void end(void*) const = 0; + }; - struct iter_ops; + template()))) <= iter_sbo_size)> + struct impl; - template - static iter_ops const* make_ops() + // small buffer sequence + template + void construct(T&& t, std::true_type) { - using iter_t = decltype(buffers::begin( - std::declval())); - static constexpr bool is_small = - sizeof(iter_t) <= sbo_size && - alignof(iter_t) <= alignof(std::max_align_t) && - std::is_nothrow_copy_constructible::value; - return make_ops( - std::integral_constant{}); + using U = typename std::decay::type; + p_ = ::new(&storage_) impl( + std::forward(t)); } - template - static iter_ops const* make_ops(std::true_type); - - template - static iter_ops const* make_ops(std::false_type); - - template - std::size_t get_length(Buffers const& bs) + template + void construct(T&& t, std::false_type) { - using iter_t = decltype(buffers::begin( - std::declval())); - static constexpr bool is_small = - sizeof(iter_t) <= sbo_size && - alignof(iter_t) <= alignof(std::max_align_t) && - std::is_nothrow_copy_constructible::value; - return get_length(bs, std::integral_constant{}); + using U = typename std::decay::type; + p_ = new impl(std::forward(t)); } - template - std::size_t get_length(Buffers const&, std::true_type) + bool is_small_buffers() const noexcept { - // no-op since length is not needed - return 0; + return p_->is_small_buffers(); } - template - std::size_t get_length(Buffers const& bs, std::false_type) + bool is_small_iter() const noexcept { - return buffers::length(bs); + return p_->is_small_iter(); } - void const* bs_ = nullptr; - std::size_t len_ = 0; - iter_ops const* ops_ = nullptr; + alignas(std::max_align_t) + unsigned char mutable storage_[sbo_size] = {}; + any_impl const* p_ = nullptr; }; +//----------------------------------------------- + /** Alias for a type-erased const buffer sequence. Equivalent to `any_buffers`. @@ -161,18 +206,225 @@ using any_mutable_buffers = any_buffers; //----------------------------------------------- +// small iterator +template +template +struct any_buffers:: + impl : any_impl +{ + using iter_t = decltype(buffers::begin( + std::declval())); + + template + explicit impl(T_&& t) noexcept + : t_(std::forward(t)) + { + } + + bool is_small_buffers() const noexcept override + { + return sizeof(*this) <= sbo_size; + } + + bool is_small_iter() const noexcept override + { + return true; + } + + void destroy() const override + { + destroy(std::integral_constant{}); + } + + void destroy(std::true_type) const // small buffers + { + this->~impl(); + } + + void destroy(std::false_type) const + { + if(--refs_ == 0) + delete this; + } + + void copy(any_buffers& dest) const override + { + copy(dest, std::integral_constant{}); + } + + void copy(any_buffers& dest, std::true_type) const // small buffers + { + dest.p_ = ::new(&dest.storage_) impl(t_); + } + + void copy(any_buffers& dest, std::false_type) const + { + ++refs_; + dest.p_ = this; + } + + void it_copy(void* dest, void const* src) const override + { + ::new(dest) iter_t(*static_cast(src)); + } + + void it_destroy(void* p) const override + { + static_cast(p)->~iter_t(); + } + + void inc(void* p) const override + { + ++(*static_cast(p)); + } + + void dec(void* p) const override + { + --(*static_cast(p)); + } + + value_type deref(void const* p) const override + { + return *(*static_cast(p)); + } + + bool equal(void const* it0, void const* it1) const override + { + return *static_cast(it0) == + *static_cast(it1); + } + + void begin(void* p) const override + { + ::new(p) iter_t(buffers::begin(t_)); + } + + void end(void* p) const override + { + ::new(p) iter_t(buffers::end(t_)); + } + +private: + T t_; + std::atomic mutable refs_{1}; +}; + template +template struct any_buffers:: - iter_ops + impl : any_impl { - void (*copy)(void*, void const*); - void (*destroy)(void*); - void (*increment)(void*); - void (*decrement)(void*); - value_type (*deref)(void const*); - bool (*equal)(void const*, void const*); - void (*construct_begin)(void*, void const*); - void (*construct_end)(void*, void const*, std::size_t); + struct iter_t + { + std::size_t i; + }; + + template + explicit impl(T_&& t) noexcept + : t_(std::forward(t)) + , len_(length(t_)) + { + } + + bool is_small_buffers() const noexcept override + { + return sizeof(*this) <= + any_buffers::sbo_size; + } + + bool is_small_iter() const noexcept override + { + return false; + } + + void destroy() const override + { + destroy(std::integral_constant::sbo_size>{}); + } + + void destroy(std::true_type) const // small buffers + { + this->~impl(); + } + + void destroy(std::false_type) const + { + if(--refs_ == 0) + delete this; + } + + void copy(any_buffers& dest) const override + { + copy(dest, std::integral_constant::sbo_size>{}); + } + + void copy(any_buffers& dest, + std::true_type) const // small buffers + { + dest.p_ = ::new(&dest.storage_) impl(t_); + } + + void copy(any_buffers& dest, + std::false_type) const + { + ++refs_; + dest.p_ = this; + } + + void it_copy(void* dest, void const* src) const override + { + ::new(dest) iter_t(*static_cast(src)); + } + + void it_destroy(void* p) const override + { + static_cast(p)->~iter_t(); + } + + void inc(void* p) const override + { + ++static_cast(p)->i; + } + + void dec(void* p) const override + { + --static_cast(p)->i; + } + + typename any_buffers::value_type + deref(void const* p) const override + { + auto const& it_ = *static_cast(p); + auto it = buffers::begin(t_); + for(auto i = it_.i; i; --i) + ++it; + return *it; + } + + bool equal(void const* it0, void const* it1) const override + { + return static_cast(it0)->i == + static_cast(it1)->i; + } + + void begin(void* p) const override + { + ::new(p) iter_t{ 0 }; + } + + void end(void* p) const override + { + ::new(p) iter_t{ len_ }; + } + +private: + T t_; + std::atomic mutable refs_{1}; + std::size_t len_; }; //----------------------------------------------- @@ -224,7 +476,7 @@ class any_buffers:: */ ~const_iterator() { - ops_->destroy(&storage_); + p_->it_destroy(&storage_); } /** Default constructor. @@ -232,7 +484,7 @@ class any_buffers:: Constructs a singular iterator. A default-constructed iterator may only be assigned to or destroyed. */ - const_iterator() = default; + const_iterator() noexcept; /** Copy constructor. @@ -240,9 +492,9 @@ class any_buffers:: */ const_iterator( const_iterator const& other) noexcept - : ops_(other.ops_) + : p_(other.p_) { - ops_->copy(&storage_, &other.storage_); + p_->it_copy(&storage_, &other.storage_); } /** Copy assignment. @@ -253,13 +505,11 @@ class any_buffers:: const_iterator& operator=( const_iterator const& other) noexcept { - if(this != &other) - { - ops_->destroy(&storage_); - - ops_ = other.ops_; - ops_->copy(&storage_, &other.storage_); - } + if(this == &other) + return *this; + p_->it_destroy(&storage_); + p_ = other.p_; + p_->it_copy(&storage_, &other.storage_); return *this; } @@ -273,11 +523,9 @@ class any_buffers:: operator==( const_iterator const& other) const noexcept { - if(ops_ != other.ops_) - return false; - if(ops_ == nullptr) + if(p_ != other.p_) return false; - return ops_->equal(&storage_, &other.storage_); + return p_->equal(&storage_, &other.storage_); } /** Test for inequality. @@ -303,8 +551,7 @@ class any_buffers:: reference operator*() const noexcept { - BOOST_ASSERT(ops_ != nullptr); - return ops_->deref(&storage_); + return p_->deref(&storage_); } /** Pre-increment. @@ -318,8 +565,7 @@ class any_buffers:: const_iterator& operator++() noexcept { - BOOST_ASSERT(ops_ != nullptr); - ops_->increment(&storage_); + p_->inc(&storage_); return *this; } @@ -350,8 +596,8 @@ class any_buffers:: const_iterator& operator--() noexcept { - BOOST_ASSERT(ops_ != nullptr); - ops_->decrement(&storage_); + BOOST_ASSERT(p_ != nullptr); + p_->dec(&storage_); return *this; } @@ -378,262 +624,49 @@ class any_buffers:: struct end_tag {}; const_iterator(begin_tag, - iter_ops const* ops, - void const* bs) noexcept - : ops_(ops) + any_impl const* p) noexcept + : p_(p) { - ops_->construct_begin(&storage_, bs); + p_->begin(&storage_); + BOOST_ASSERT(p_ != nullptr); } const_iterator(end_tag, - iter_ops const* ops, - void const* bs, - std::size_t len) noexcept - : ops_(ops) + any_impl const* p) noexcept + : p_(p) { - ops_->construct_end(&storage_, bs, len); + p_->end(&storage_); + BOOST_ASSERT(p_ != nullptr); } alignas(std::max_align_t) - unsigned char mutable storage_[sbo_size] = {}; - any_buffers::iter_ops const* ops_ = nullptr; -}; - -//----------------------------------------------- - -namespace detail { - -template -class holder -{ -protected: - template - explicit holder(T_&& t) noexcept - : t_(std::forward(t)) - { - } - - T t_; + unsigned char mutable storage_[iter_sbo_size] = {}; + any_buffers::any_impl const* p_ = nullptr; }; -} // detail - -/** An owning type-erased buffer sequence. - - This class stores a copy of a buffer sequence and - provides type-erased access to it. Unlike @ref any_buffers, - this class owns the underlying buffer sequence. - - @tparam BufferSequence The concrete buffer sequence type. - - @tparam IsConst If `true`, exposes @ref const_buffer - elements. Defaults to `true` if `BufferSequence` is - not a mutable buffer sequence. - - @see any_buffers, make_any_buffers -*/ -template::value> -class any_buffers_impl - : private detail::holder - , public any_buffers -{ -public: - /** Move constructor. - */ - any_buffers_impl(any_buffers_impl&&) = default; - - /** Construct from a buffer sequence. - - Stores a copy of `bs` and initializes the - type-erased view. - - @param bs The buffer sequence to store. - - @tparam BufferSequence_ The source type, - which must be convertible to `BufferSequence`. - */ - template - any_buffers_impl( - BufferSequence_&& bs) noexcept - : detail::holder( - std::forward(bs)) - , any_buffers(this->t_) - { - } -}; - -/** Create an owning type-erased buffer sequence. - - Returns an @ref any_buffers_impl that stores a - decayed copy of `bs` and provides type-erased - access to it. - - @param bs The buffer sequence to wrap. - - @return An @ref any_buffers_impl owning a copy of `bs`. - - @tparam BufferSequence The buffer sequence type. - - @see any_buffers, any_buffers_impl -*/ -template -auto -make_any_buffers( - BufferSequence&& bs) -> - any_buffers_impl::type> -{ - return any_buffers_impl::type>( - std::forward(bs)); -} - //----------------------------------------------- -template -template -any_buffers:: -any_buffers( - BufferSequence const& bs) - : bs_(&bs) - , len_(get_length(bs)) - , ops_(make_ops()) -{ - BOOST_CORE_STATIC_ASSERT( - is_const_buffer_sequence::value); - BOOST_CORE_STATIC_ASSERT(IsConst || - is_mutable_buffer_sequence::value); -} +template<> +BOOST_BUFFERS_DECL +any_buffers:: +any_buffers() noexcept; -// small iterators -template -template -auto -any_buffers:: -make_ops(std::true_type) -> - iter_ops const* -{ - using iter_t = decltype(buffers::begin( - std::declval())); - static const iter_ops ops = { - // copy - [](void* dest, void const* src) - { - ::new(dest) iter_t(*static_cast(src)); - }, - // destroy - [](void* p) - { - static_cast(p)->~iter_t(); - }, - // increment - [](void* p) - { - ++(*static_cast(p)); - }, - // decrement - [](void* p) - { - --(*static_cast(p)); - }, - // deref - [](void const* p) -> value_type - { - return *(*static_cast(p)); - }, - // equal - [](void const* a, void const* b) -> bool - { - return *static_cast(a) == - *static_cast(b); - }, - // construct_begin - [](void* storage, void const* bs) - { - ::new(storage) iter_t(buffers::begin( - *static_cast(bs))); - }, - // construct_end - [](void* storage, void const* bs, std::size_t) - { - ::new(storage) iter_t(buffers::end( - *static_cast(bs))); - } - }; - return &ops; -} +template<> +BOOST_BUFFERS_DECL +any_buffers:: +any_buffers() noexcept; -// large iterators -template -template -auto -any_buffers:: -make_ops(std::false_type) -> - iter_ops const* -{ - struct iter_t - { - std::size_t i_; - Buffers const* bs_; - }; +template<> +BOOST_BUFFERS_DECL +any_buffers:: +const_iterator:: +const_iterator() noexcept; - BOOST_CORE_STATIC_ASSERT(sizeof(iter_t) <= sbo_size); - - static const iter_ops ops = { - // copy - [](void* dest, void const* src) - { - ::new(dest) iter_t(*static_cast(src)); - }, - // destroy - [](void* p) - { - static_cast(p)->~iter_t(); - }, - // increment - [](void* p) - { - ++static_cast(p)->i_; - }, - // decrement - [](void* p) - { - --static_cast(p)->i_; - }, - // deref - [](void const* p) -> value_type - { - auto const& it_ = *static_cast(p); - auto it = buffers::begin(*it_.bs_); - for(auto i = it_.i_; i; --i) - ++it; - return *it; - }, - // equal - [](void const* a, void const* b) -> bool - { - auto const& it0 = *static_cast(a); - auto const& it1 = *static_cast(b); - return - it0.i_ == it1.i_ && - it0.bs_ == it1.bs_; - }, - // construct_begin - [](void* storage, void const* bs) - { - ::new(storage) iter_t{ 0, - static_cast(bs) }; - }, - // construct_end - [](void* storage, void const* bs, std::size_t len) - { - ::new(storage) iter_t{ len, - static_cast(bs) }; - } - }; - return &ops; -} +template<> +BOOST_BUFFERS_DECL +any_buffers:: +const_iterator:: +const_iterator() noexcept; //----------------------------------------------- @@ -643,9 +676,10 @@ any_buffers:: begin() const noexcept -> const_iterator { - return const_iterator( - typename const_iterator::begin_tag{}, - ops_, bs_); + if(! p_) + return const_iterator(); + return const_iterator(typename + const_iterator::begin_tag{}, p_); } template @@ -654,13 +688,12 @@ any_buffers:: end() const noexcept -> const_iterator { - return const_iterator( - typename const_iterator::end_tag{}, - ops_, bs_, len_); + if(! p_) + return const_iterator(); + return const_iterator(typename + const_iterator::end_tag{}, p_); } -//----------------------------------------------- - } // buffers } // boost diff --git a/src/any_buffers.cpp b/src/any_buffers.cpp new file mode 100644 index 0000000..5b6601f --- /dev/null +++ b/src/any_buffers.cpp @@ -0,0 +1,163 @@ +// +// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/buffers +// + +#include +#include + +namespace boost { +namespace buffers { + +template<> +any_buffers:: +any_buffers() noexcept +{ + struct impl : any_impl + { + bool is_small_buffers() const noexcept override + { + return true; + } + + bool is_small_iter() const noexcept override + { + return true; + } + + void destroy() const override + { + } + + void copy(any_buffers& dest) const override + { + dest.p_ = this; + } + + void it_copy(void*, void const*) const override + { + } + + void it_destroy(void*) const override + { + } + + void inc(void*) const override + { + } + + void dec(void*) const override + { + } + + value_type deref(void const*) const override + { + return {}; + } + + bool equal(void const*, void const*) const override + { + return true; + } + + void begin(void*) const override + { + } + + void end(void*) const override + { + } + }; + + static impl const instance; + p_ = &instance; +} + +template<> +any_buffers:: +any_buffers() noexcept +{ + struct impl : any_impl + { + bool is_small_buffers() const noexcept override + { + return true; + } + + bool is_small_iter() const noexcept override + { + return true; + } + + void destroy() const override + { + } + + void copy(any_buffers& dest) const override + { + dest.p_ = this; + } + + void it_copy(void*, void const*) const override + { + } + + void it_destroy(void*) const override + { + } + + void inc(void*) const override + { + } + + void dec(void*) const override + { + } + + value_type deref(void const*) const override + { + return {}; + } + + bool equal(void const*, void const*) const override + { + return true; + } + + void begin(void*) const override + { + } + + void end(void*) const override + { + } + }; + + static impl const instance; + p_ = &instance; +} + +template<> +any_buffers:: +any_buffers:: +const_iterator:: +const_iterator() noexcept + : p_(any_buffers().begin().p_) +{ +} + +template<> +any_buffers:: +any_buffers:: +const_iterator:: +const_iterator() noexcept + : p_(any_buffers().begin().p_) +{ +} + +} // buffers +} // boost diff --git a/test/unit/any_buffers.cpp b/test/unit/any_buffers.cpp index f0d3b30..e6afeeb 100644 --- a/test/unit/any_buffers.cpp +++ b/test/unit/any_buffers.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -33,25 +34,92 @@ namespace { struct any_buffers_test { + template + void checkConst(T const& t, core::string_view s0) + { + BOOST_TEST_EQ(to_string(t), s0); + any_const_buffers ab(t); + BOOST_TEST_EQ(length(ab), length(t)); + BOOST_TEST_EQ(to_string(ab), s0); + any_const_buffers ab2(ab); + BOOST_TEST_EQ(to_string(ab2), to_string(ab)); + BOOST_TEST_EQ(length(ab2), length(ab)); + } + + template + void checkMutable(T const& t, core::string_view s0) + { + BOOST_TEST_EQ(size(t), s0.size()); + any_mutable_buffers ab(t); + BOOST_TEST_EQ(length(ab), length(t)); + auto n = copy(ab, const_buffer(s0.data(), s0.size())); + BOOST_TEST_EQ(n, s0.size()); + BOOST_TEST_EQ(to_string(ab), s0); + BOOST_TEST_EQ(to_string(ab), to_string(t)); + } + + void testEmpty() + { + { + any_const_buffers ab; + BOOST_TEST(ab.is_small_buffers()); + BOOST_TEST(ab.is_small_iter()); + BOOST_TEST_EQ(size(ab), 0); + BOOST_TEST_EQ(ab.begin(), ab.end()); + BOOST_TEST_EQ(length(ab), 0); + auto it0 = ab.begin(); + auto it1 = it0; + BOOST_TEST_EQ(it0, it1); + BOOST_TEST_EQ(size(*it0), 0); + ++it0; + BOOST_TEST_EQ(size(*it0), 0); + --it0; + BOOST_TEST_EQ(size(*it0), 0); + checkConst(ab, ""); + } + { + any_mutable_buffers ab; + BOOST_TEST(ab.is_small_buffers()); + BOOST_TEST(ab.is_small_iter()); + BOOST_TEST_EQ(size(ab), 0); + BOOST_TEST_EQ(ab.begin(), ab.end()); + BOOST_TEST_EQ(length(ab), 0); + auto it0 = ab.begin(); + auto it1 = it0; + BOOST_TEST_EQ(it0, it1); + BOOST_TEST_EQ(size(*it0), 0); + ++it0; + BOOST_TEST_EQ(size(*it0), 0); + --it0; + BOOST_TEST_EQ(size(*it0), 0); + checkMutable(ab, ""); + checkConst(ab, ""); + } + } + void run() { + testEmpty(); + core::string_view s0 = "Hello, world!"; core::string_view s1 = "Goodbye, wg21!"; // const_buffer { const_buffer bs(s0.data(), s0.size()); - any_buffers ab(bs); - BOOST_TEST_EQ(to_string(ab), to_string(bs)); - BOOST_TEST_EQ(to_string(make_any_buffers(bs)), to_string(bs)); + BOOST_TEST(any_const_buffers(bs).is_small_buffers()); + BOOST_TEST(any_const_buffers(bs).is_small_iter()); + checkConst(bs, s0); } // mutable_buffer { - const_buffer bs(s0.data(), s0.size()); - any_buffers ab(bs); - BOOST_TEST_EQ(to_string(ab), to_string(bs)); - BOOST_TEST_EQ(to_string(make_any_buffers(bs)), to_string(bs)); + char buf[13]; + mutable_buffer bs(buf, sizeof(buf)); + BOOST_TEST(any_mutable_buffers().is_small_buffers()); + BOOST_TEST(any_mutable_buffers().is_small_iter()); + checkMutable(bs, s0); + checkConst(bs, s0); } // const_buffer_pair @@ -59,9 +127,25 @@ struct any_buffers_test const_buffer_pair bs({{ { s0.data(), s0.size() }, { s1.data(), s1.size() } }}); - any_buffers ab(bs); + any_const_buffers ab(bs); + BOOST_TEST(ab.is_small_buffers()); + BOOST_TEST(ab.is_small_iter()); BOOST_TEST_EQ(to_string(ab), to_string(bs)); - BOOST_TEST_EQ(to_string(make_any_buffers(bs)), to_string(bs)); + BOOST_TEST_EQ(to_string(any_const_buffers(bs)), to_string(bs)); + } + + // mutable_buffer_pair + { + char b0[16]; + char b1[16]; + mutable_buffer_pair bs({{ + { b0, sizeof(b0) }, + { b1, sizeof(b1) } }}); + any_mutable_buffers ab(bs); + BOOST_TEST(ab.is_small_buffers()); + BOOST_TEST(ab.is_small_iter()); + BOOST_TEST_EQ(size(ab), sizeof(b0) + sizeof(b1)); + BOOST_TEST_EQ(size(ab), size(bs)); } // slice (big iterators) @@ -70,9 +154,11 @@ struct any_buffers_test { s0.data(), s0.size() }, { s1.data(), s1.size() } }}); slice_of cb(bs); - any_buffers ab(cb); + any_const_buffers ab(cb); + BOOST_TEST(! ab.is_small_buffers()); + BOOST_TEST(! ab.is_small_iter()); BOOST_TEST_EQ(to_string(ab), to_string(bs)); - BOOST_TEST_EQ(to_string(make_any_buffers(bs)), to_string(bs)); + BOOST_TEST_EQ(to_string(any_const_buffers(bs)), to_string(bs)); } } };