diff --git a/CHANGELOG.md b/CHANGELOG.md index 78b989fd..807d7ed1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ Version 286: * Refactor multi_buffer +* Refactor buffers_adapter API Changes: * multi_buffer::mutable_data_type is deprecated. Use multi_buffer::mutable_buffers_type instead +* buffers_adaptor::mutable_data_type is deprecated. Use buffers_adaptor::mutable_buffers_type instead -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/buffers_adaptor.hpp b/include/boost/beast/core/buffers_adaptor.hpp index d6400481..0485d379 100644 --- a/include/boost/beast/core/buffers_adaptor.hpp +++ b/include/boost/beast/core/buffers_adaptor.hpp @@ -43,7 +43,7 @@ class buffers_adaptor buffers_iterator_type; template - class readable_bytes; + class subrange; MutableBufferSequence bs_; iter_type begin_; @@ -106,16 +106,17 @@ public: /// The ConstBufferSequence used to represent the readable bytes. using const_buffers_type = __implementation_defined__; - /// The MutableBufferSequence used to represent the readable bytes. - using mutable_data_type = __implementation_defined__; - /// The MutableBufferSequence used to represent the writable bytes. using mutable_buffers_type = __implementation_defined__; #else - using const_buffers_type = readable_bytes; - using mutable_data_type = readable_bytes; - class mutable_buffers_type; + using const_buffers_type = subrange; + +#ifdef BOOST_BEAST_ALLOW_DEPRECATED + using mutable_data_type = subrange; +#endif + + using mutable_buffers_type = subrange; #endif /// Returns the number of readable bytes. @@ -151,7 +152,7 @@ public: } /// Returns a mutable buffer sequence representing the readable bytes. - mutable_data_type + mutable_buffers_type data() noexcept; /** Returns a mutable buffer sequence representing writable bytes. @@ -216,6 +217,17 @@ public: */ void consume(std::size_t n) noexcept; + +private: + + subrange + make_subrange(std::size_t pos, std::size_t n); + + subrange + make_subrange(std::size_t pos, std::size_t n) const; + + friend struct buffers_adaptor_test_hook; + }; } // beast diff --git a/include/boost/beast/core/detail/buffer.hpp b/include/boost/beast/core/detail/buffer.hpp index c898d95c..b78bdbe1 100644 --- a/include/boost/beast/core/detail/buffer.hpp +++ b/include/boost/beast/core/detail/buffer.hpp @@ -11,6 +11,7 @@ #define BOOST_BEAST_CORE_DETAIL_BUFFER_HPP #include +#include #include #include diff --git a/include/boost/beast/core/impl/buffers_adaptor.hpp b/include/boost/beast/core/impl/buffers_adaptor.hpp index 3eac32f6..07c78de8 100644 --- a/include/boost/beast/core/impl/buffers_adaptor.hpp +++ b/include/boost/beast/core/impl/buffers_adaptor.hpp @@ -11,6 +11,7 @@ #define BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP #include +#include #include #include #include @@ -34,71 +35,88 @@ namespace beast { template template -class buffers_adaptor:: - readable_bytes +class buffers_adaptor::subrange { - buffers_adaptor const* b_; - public: - using value_type = typename - std::conditional::type; + using value_type = typename std::conditional< + isMutable, + net::mutable_buffer, + net::const_buffer>::type; - class const_iterator; + struct iterator; - readable_bytes() = delete; + // construct from two iterators plus optionally subrange definition + subrange( + iter_type first, // iterator to first buffer in storage + iter_type last, // iterator to last buffer in storage + std::size_t pos = 0, // the offset in bytes from the beginning of the storage + std::size_t n = // the total length of the subrange + (std::numeric_limits::max)()); #if BOOST_WORKAROUND(BOOST_MSVC, < 1910) - readable_bytes( - readable_bytes const& other) - : b_(other.b_) + subrange( + subrange const& other) + : first_(other.first_) + , last_(other.last_) + , first_offset_(other.first_offset_) + , last_size_(other.last_size_) { } - readable_bytes& operator=( - readable_bytes const& other) + subrange& operator=( + subrange const& other) { - b_ = other.b_; + first_ = other.first_; + last_ = other.last_; + first_offset_ = other.first_offset_; + last_size_ = other.last_size_; return *this; } #else - readable_bytes( - readable_bytes const&) = default; - readable_bytes& operator=( - readable_bytes const&) = default; + subrange( + subrange const&) = default; + subrange& operator=( + subrange const&) = default; #endif - template::type> - readable_bytes( - readable_bytes const& other) noexcept - : b_(other.b_) + // allow conversion from mutable to const + template::type * = nullptr> + subrange(subrange const &other) + : first_(other.first_) + , last_(other.last_) + , first_offset_(other.first_offset_) + , last_size_(other.last_size_) { } - template::type> - readable_bytes& operator=( - readable_bytes const& other) noexcept - { - b_ = other.b_; - return *this; - } - - const_iterator + iterator begin() const; - const_iterator + iterator end() const; private: - friend class buffers_adaptor; - readable_bytes(buffers_adaptor const& b) - : b_(&b) - { - } + friend subrange; + + void + adjust( + std::size_t pos, + std::size_t n); + +private: + // points to the first buffer in the sequence + iter_type first_; + + // Points to one past the end of the underlying buffer sequence + iter_type last_; + + // The initial offset into the first buffer + std::size_t first_offset_; + + // how many bytes in the penultimate buffer are used (if any) + std::size_t last_size_; }; #if BOOST_WORKAROUND(BOOST_MSVC, < 1910) @@ -109,257 +127,55 @@ private: template template -class buffers_adaptor:: - readable_bytes:: - const_iterator +struct buffers_adaptor:: + subrange:: + iterator { - iter_type it_{}; - buffers_adaptor const* b_ = nullptr; - -public: + using iterator_category = std::bidirectional_iterator_tag; using value_type = typename - std::conditional::type; - using pointer = value_type const*; - using reference = value_type; + buffers_adaptor:: + template subrange:: + value_type; + using reference = value_type&; + using pointer = value_type*; using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - const_iterator() = default; - const_iterator(const_iterator const& other) = default; - const_iterator& operator=(const_iterator const& other) = default; + iterator( + subrange const *parent, + iter_type it); - bool - operator==(const_iterator const& other) const - { - return b_ == other.b_ && it_ == other.it_; - } + iterator(); - bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - value_type const b = *it_; - return value_type{b.data(), - (b_->out_ == net::buffer_sequence_end(b_->bs_) || - it_ != b_->out_) ? b.size() : b_->out_pos_} + - (it_ == b_->begin_ ? b_->in_pos_ : 0); - } + value_type + operator*() const; pointer operator->() const = delete; - const_iterator& - operator++() - { - ++it_; - return *this; - } + iterator & + operator++(); - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } + iterator + operator++(int); - const_iterator& - operator--() - { - --it_; - return *this; - } + iterator & + operator--(); - const_iterator - operator--(int) - { - auto temp = *this; - --(*this); - return temp; - } - -private: - friend class readable_bytes; - - const_iterator( - buffers_adaptor const& b, - iter_type iter) - : it_(iter) - , b_(&b) - { - } -}; - -template -template -auto -buffers_adaptor:: -readable_bytes:: -begin() const -> - const_iterator -{ - return const_iterator{*b_, b_->begin_}; -} - -template -template -auto -buffers_adaptor:: -readable_bytes:: -readable_bytes::end() const -> - const_iterator -{ - return const_iterator{*b_, b_->end_impl()}; -} - -//------------------------------------------------------------------------------ - -template -class buffers_adaptor:: -mutable_buffers_type -{ - buffers_adaptor const* b_; - -public: - using value_type = net::mutable_buffer; - - class const_iterator; - - mutable_buffers_type() = delete; - mutable_buffers_type( - mutable_buffers_type const&) = default; - mutable_buffers_type& operator=( - mutable_buffers_type const&) = default; - - const_iterator - begin() const; - - const_iterator - end() const; - -private: - friend class buffers_adaptor; - - mutable_buffers_type( - buffers_adaptor const& b) - : b_(&b) - { - } -}; - -template -class buffers_adaptor:: -mutable_buffers_type::const_iterator -{ - iter_type it_{}; - buffers_adaptor const* b_ = nullptr; - -public: - using value_type = net::mutable_buffer; - using pointer = value_type const*; - using reference = value_type; - using difference_type = std::ptrdiff_t; - using iterator_category = - std::bidirectional_iterator_tag; - - const_iterator() = default; - const_iterator(const_iterator const& other) = default; - const_iterator& operator=(const_iterator const& other) = default; + iterator + operator--(int); bool - operator==(const_iterator const& other) const - { - return b_ == other.b_ && it_ == other.it_; - } + operator==(iterator const &b) const; bool - operator!=(const_iterator const& other) const - { - return !(*this == other); - } - - reference - operator*() const - { - value_type const b = *it_; - return value_type{b.data(), - it_ == std::prev(b_->end_) ? - b_->out_end_ : b.size()} + - (it_ == b_->out_ ? b_->out_pos_ : 0); - } - - pointer - operator->() const = delete; - - const_iterator& - operator++() - { - ++it_; - return *this; - } - - const_iterator - operator++(int) - { - auto temp = *this; - ++(*this); - return temp; - } - - const_iterator& - operator--() - { - --it_; - return *this; - } - - const_iterator - operator--(int) - { - auto temp = *this; - --(*this); - return temp; - } + operator!=(iterator const &b) const; private: - friend class mutable_buffers_type; - const_iterator(buffers_adaptor const& b, - iter_type iter) - : it_(iter) - , b_(&b) - { - } + subrange const *parent_; + iter_type it_; }; -template -auto -buffers_adaptor:: -mutable_buffers_type:: -begin() const -> - const_iterator -{ - return const_iterator{*b_, b_->out_}; -} - -template -auto -buffers_adaptor:: -mutable_buffers_type:: -end() const -> - const_iterator -{ - return const_iterator{*b_, b_->end_}; -} - //------------------------------------------------------------------------------ template @@ -479,16 +295,20 @@ buffers_adaptor:: data() const noexcept -> const_buffers_type { - return const_buffers_type{*this}; + return const_buffers_type( + begin_, end_, + in_pos_, in_size_); } template auto buffers_adaptor:: data() noexcept -> - mutable_data_type + mutable_buffers_type { - return mutable_data_type{*this}; + return mutable_buffers_type( + begin_, end_, + in_pos_, in_size_); } template @@ -497,6 +317,7 @@ buffers_adaptor:: prepare(std::size_t n) -> mutable_buffers_type { + auto prepared = n; end_ = out_; if(end_ != net::buffer_sequence_end(bs_)) { @@ -529,7 +350,7 @@ prepare(std::size_t n) -> if(n > 0) BOOST_THROW_EXCEPTION(std::length_error{ "buffers_adaptor too long"}); - return mutable_buffers_type{*this}; + return mutable_buffers_type(out_, end_, out_pos_, prepared); } template @@ -601,6 +422,285 @@ consume(std::size_t n) noexcept } } +template +auto +buffers_adaptor:: + make_subrange(std::size_t pos, std::size_t n) -> + subrange +{ + return subrange( + begin_, net::buffer_sequence_end(bs_), + in_pos_ + pos, n); +} + +template +auto +buffers_adaptor:: + make_subrange(std::size_t pos, std::size_t n) const -> + subrange +{ + return subrange( + begin_, net::buffer_sequence_end(bs_), + in_pos_ + pos, n); +} + +// ------------------------------------------------------------------------- +// subrange + +template +template +buffers_adaptor:: +subrange:: +subrange( + iter_type first, // iterator to first buffer in storage + iter_type last, // iterator to last buffer in storage + std::size_t pos, // the offset in bytes from the beginning of the storage + std::size_t n) // the total length of the subrange + : first_(first) + , last_(last) + , first_offset_(0) + , last_size_((std::numeric_limits::max)()) +{ + adjust(pos, n); +} + +template +template +void +buffers_adaptor:: + subrange:: +adjust( + std::size_t pos, + std::size_t n) +{ + if (n == 0) + last_ = first_; + + if (first_ == last_) + { + first_offset_ = 0; + last_size_ = 0; + return; + } + + auto is_last = [this](iter_type iter) { + return std::next(iter) == last_; + }; + + + pos += first_offset_; + while (pos) + { + auto adjust = (std::min)(pos, first_->size()); + if (adjust >= first_->size()) + { + ++first_; + first_offset_ = 0; + pos -= adjust; + } + else + { + first_offset_ = adjust; + pos = 0; + break; + } + } + + auto current = first_; + auto max_elem = current->size() - first_offset_; + if (is_last(current)) + { + // both first and last element + last_size_ = (std::min)(max_elem, n); + last_ = std::next(current); + return; + } + else if (max_elem >= n) + { + last_ = std::next(current); + last_size_ = n; + } + else + { + n -= max_elem; + ++current; + } + + for (;;) + { + max_elem = current->size(); + if (is_last(current)) + { + last_size_ = (std::min)(n, last_size_); + return; + } + else if (max_elem < n) + { + n -= max_elem; + ++current; + } + else + { + last_size_ = n; + last_ = std::next(current); + return; + } + } +} + + +template +template +auto +buffers_adaptor:: + subrange:: +begin() const -> +iterator +{ + return iterator(this, first_); +} + +template +template +auto +buffers_adaptor:: + subrange:: +end() const -> +iterator +{ + return iterator(this, last_); +} + +// ------------------------------------------------------------------------- +// buffers_adaptor::subrange::iterator + +template +template +buffers_adaptor:: +subrange:: +iterator:: +iterator() + : parent_(nullptr) + , it_() +{ +} + +template +template +buffers_adaptor:: +subrange:: +iterator:: +iterator(subrange const *parent, + iter_type it) + : parent_(parent) + , it_(it) +{ +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator*() const -> + value_type +{ + value_type result = *it_; + + if (it_ == parent_->first_) + result += parent_->first_offset_; + + if (std::next(it_) == parent_->last_) + { + result = value_type( + result.data(), + (std::min)( + parent_->last_size_, + result.size())); + } + + return result; +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator++() -> + iterator & +{ + ++it_; + return *this; +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator++(int) -> + iterator +{ + auto result = *this; + ++it_; + return result; +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator--() -> + iterator & +{ + --it_; + return *this; +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator--(int) -> + iterator +{ + auto result = *this; + --it_; + return result; +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator==(iterator const &b) const -> + bool +{ + return it_ == b.it_; +} + +template +template +auto +buffers_adaptor:: + subrange:: + iterator:: +operator!=(iterator const &b) const -> + bool +{ + return !(*this == b); +} + } // beast } // boost diff --git a/test/beast/core/buffers_adaptor.cpp b/test/beast/core/buffers_adaptor.cpp index 498871e4..29301023 100644 --- a/test/beast/core/buffers_adaptor.cpp +++ b/test/beast/core/buffers_adaptor.cpp @@ -24,6 +24,33 @@ namespace boost { namespace beast { +struct buffers_adaptor_test_hook +{ + template + static + auto + make_subrange( + buffers_adaptor &adaptor, + std::size_t pos = 0, + std::size_t n = (std::numeric_limits::max)()) + -> typename buffers_adaptor::mutable_buffers_type + { + return adaptor.make_subrange(pos, n); + } + + template + static + auto + make_subrange( + buffers_adaptor const& adaptor, + std::size_t pos = 0, + std::size_t n = (std::numeric_limits::max)()) + -> typename buffers_adaptor::const_buffers_type + { + return adaptor.make_subrange(pos, n); + } +}; + class buffers_adaptor_test : public unit_test::suite { public: @@ -85,6 +112,54 @@ public: read_size(ba, 1024); } + template + void + testSubrange() + { + auto exemplar = std::string("the quick brown fox jumps over the lazy dog"); + + auto iterate_test = [&]( + std::size_t a, + std::size_t b, + std::size_t c) + { + static const auto func = "iterate_test"; + auto buffers = std::vector(); + if (a) + buffers.push_back(net::buffer(&exemplar[0], a)); + if (b - a) + buffers.push_back(net::buffer(&exemplar[a], (b - a))); + if (c - b) + buffers.push_back(net::buffer(&exemplar[b], (c - b))); + auto adapter = buffers_adaptor>(buffers); + + + using value_type = + typename std::conditional< + isMutable, + net::mutable_buffer, + net::const_buffer>::type; + + using maybe_mutable = + typename std::conditional< + isMutable, + buffers_adaptor>&, + buffers_adaptor> const&>::type; + + auto sub = buffers_adaptor_test_hook::make_subrange(static_cast(adapter)); + BEAST_EXPECTS(typeid(typename decltype(sub)::value_type) == typeid(value_type), func); + BEAST_EXPECT(buffers_to_string(sub) == exemplar.substr(0, c)); + }; + + iterate_test(0, 0, 1); + + for (std::size_t a = 0; a <= exemplar.size(); ++a) + for (std::size_t b = a; b <= exemplar.size(); ++b) + for (std::size_t c = b; c <= exemplar.size(); ++c) + iterate_test(a, b, c); + } + + void run() override { @@ -95,6 +170,8 @@ public: testBuffersAdapter(); testCommit(); #endif + testSubrange(); + testSubrange(); } };