Refactor buffers_adaptor

This commit is contained in:
Richard Hodges
2020-02-25 16:02:37 +01:00
committed by Vinnie Falco
parent eebf5773ed
commit 69ef525d5c
5 changed files with 474 additions and 282 deletions

View File

@@ -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
--------------------------------------------------------------------------------

View File

@@ -43,7 +43,7 @@ class buffers_adaptor
buffers_iterator_type<MutableBufferSequence>;
template<bool>
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<false>;
using mutable_data_type = readable_bytes<true>;
class mutable_buffers_type;
using const_buffers_type = subrange<false>;
#ifdef BOOST_BEAST_ALLOW_DEPRECATED
using mutable_data_type = subrange<true>;
#endif
using mutable_buffers_type = subrange<true>;
#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<true>
make_subrange(std::size_t pos, std::size_t n);
subrange<false>
make_subrange(std::size_t pos, std::size_t n) const;
friend struct buffers_adaptor_test_hook;
};
} // beast

View File

@@ -11,6 +11,7 @@
#define BOOST_BEAST_CORE_DETAIL_BUFFER_HPP
#include <boost/beast/core/error.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/optional.hpp>
#include <stdexcept>

View File

@@ -11,6 +11,7 @@
#define BOOST_BEAST_IMPL_BUFFERS_ADAPTOR_HPP
#include <boost/beast/core/buffer_traits.hpp>
#include <boost/beast/core/buffers_adaptor.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/config/workaround.hpp>
#include <boost/throw_exception.hpp>
@@ -34,71 +35,88 @@ namespace beast {
template<class MutableBufferSequence>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::
readable_bytes
class buffers_adaptor<MutableBufferSequence>::subrange
{
buffers_adaptor const* b_;
public:
using value_type = typename
std::conditional<isMutable,
net::mutable_buffer,
net::const_buffer>::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<std::size_t>::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<bool isMutable_ = isMutable, class =
typename std::enable_if<! isMutable_>::type>
readable_bytes(
readable_bytes<true> const& other) noexcept
: b_(other.b_)
// allow conversion from mutable to const
template<bool isMutable_ = isMutable, typename
std::enable_if<!isMutable_>::type * = nullptr>
subrange(subrange<true> const &other)
: first_(other.first_)
, last_(other.last_)
, first_offset_(other.first_offset_)
, last_size_(other.last_size_)
{
}
template<bool isMutable_ = isMutable, class =
typename std::enable_if<! isMutable_>::type>
readable_bytes& operator=(
readable_bytes<true> 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<!isMutable>;
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<class MutableBufferSequence>
template<bool isMutable>
class buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
const_iterator
struct buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator
{
iter_type it_{};
buffers_adaptor const* b_ = nullptr;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = typename
std::conditional<isMutable,
net::mutable_buffer,
net::const_buffer>::type;
using pointer = value_type const*;
using reference = value_type;
buffers_adaptor<MutableBufferSequence>::
template subrange<isMutable>::
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<isMutable> 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<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
begin() const ->
const_iterator
{
return const_iterator{*b_, b_->begin_};
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
readable_bytes<isMutable>::
readable_bytes::end() const ->
const_iterator
{
return const_iterator{*b_, b_->end_impl()};
}
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
class buffers_adaptor<MutableBufferSequence>::
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 MutableBufferSequence>
class buffers_adaptor<MutableBufferSequence>::
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<isMutable> const *parent_;
iter_type it_;
};
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
mutable_buffers_type::
begin() const ->
const_iterator
{
return const_iterator{*b_, b_->out_};
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
mutable_buffers_type::
end() const ->
const_iterator
{
return const_iterator{*b_, b_->end_};
}
//------------------------------------------------------------------------------
template<class MutableBufferSequence>
@@ -479,16 +295,20 @@ buffers_adaptor<MutableBufferSequence>::
data() const noexcept ->
const_buffers_type
{
return const_buffers_type{*this};
return const_buffers_type(
begin_, end_,
in_pos_, in_size_);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
data() noexcept ->
mutable_data_type
mutable_buffers_type
{
return mutable_data_type{*this};
return mutable_buffers_type(
begin_, end_,
in_pos_, in_size_);
}
template<class MutableBufferSequence>
@@ -497,6 +317,7 @@ buffers_adaptor<MutableBufferSequence>::
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<class MutableBufferSequence>
@@ -601,6 +422,285 @@ consume(std::size_t n) noexcept
}
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
make_subrange(std::size_t pos, std::size_t n) ->
subrange<true>
{
return subrange<true>(
begin_, net::buffer_sequence_end(bs_),
in_pos_ + pos, n);
}
template<class MutableBufferSequence>
auto
buffers_adaptor<MutableBufferSequence>::
make_subrange(std::size_t pos, std::size_t n) const ->
subrange<false>
{
return subrange<false>(
begin_, net::buffer_sequence_end(bs_),
in_pos_ + pos, n);
}
// -------------------------------------------------------------------------
// subrange
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
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<std::size_t>::max)())
{
adjust(pos, n);
}
template<class MutableBufferSequence>
template<bool isMutable>
void
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
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<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
begin() const ->
iterator
{
return iterator(this, first_);
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
end() const ->
iterator
{
return iterator(this, last_);
}
// -------------------------------------------------------------------------
// buffers_adaptor::subrange::iterator
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
iterator()
: parent_(nullptr)
, it_()
{
}
template<class MutableBufferSequence>
template<bool isMutable>
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
iterator(subrange<isMutable> const *parent,
iter_type it)
: parent_(parent)
, it_(it)
{
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
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<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator++() ->
iterator &
{
++it_;
return *this;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator++(int) ->
iterator
{
auto result = *this;
++it_;
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator--() ->
iterator &
{
--it_;
return *this;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator--(int) ->
iterator
{
auto result = *this;
--it_;
return result;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator==(iterator const &b) const ->
bool
{
return it_ == b.it_;
}
template<class MutableBufferSequence>
template<bool isMutable>
auto
buffers_adaptor<MutableBufferSequence>::
subrange<isMutable>::
iterator::
operator!=(iterator const &b) const ->
bool
{
return !(*this == b);
}
} // beast
} // boost

View File

@@ -24,6 +24,33 @@
namespace boost {
namespace beast {
struct buffers_adaptor_test_hook
{
template<class MutableBufferSequence>
static
auto
make_subrange(
buffers_adaptor <MutableBufferSequence> &adaptor,
std::size_t pos = 0,
std::size_t n = (std::numeric_limits<std::size_t>::max)())
-> typename buffers_adaptor<MutableBufferSequence>::mutable_buffers_type
{
return adaptor.make_subrange(pos, n);
}
template<class MutableBufferSequence>
static
auto
make_subrange(
buffers_adaptor<MutableBufferSequence> const& adaptor,
std::size_t pos = 0,
std::size_t n = (std::numeric_limits<std::size_t>::max)())
-> typename buffers_adaptor<MutableBufferSequence>::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<bool isMutable>
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<net::mutable_buffer>();
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<std::vector<net::mutable_buffer>>(buffers);
using value_type =
typename std::conditional<
isMutable,
net::mutable_buffer,
net::const_buffer>::type;
using maybe_mutable =
typename std::conditional<
isMutable,
buffers_adaptor<std::vector<net::mutable_buffer>>&,
buffers_adaptor<std::vector<net::mutable_buffer>> const&>::type;
auto sub = buffers_adaptor_test_hook::make_subrange(static_cast<maybe_mutable>(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<true>();
testSubrange<false>();
}
};