Better buffer_cat:

buffer_cat now determines if all of the buffer sequences in
the list of passed buffer sequences each have value types which
are convertible to mutable_buffer. The returned concatenated
sequence will be a MutableBufferSequence if all the passed
buffer sequences meet the requirements of MutableBufferSequence,
else the returned concatenated sequence will be a ConstBufferSequence.
This commit is contained in:
Vinnie Falco
2016-11-08 19:00:40 -05:00
parent 6afa6afc98
commit be67224390
7 changed files with 305 additions and 127 deletions

View File

@@ -5,6 +5,7 @@
* Use boost::lexical_cast instead of std::to_string
* Fix prepare_buffers value_type
* Fix consuming_buffers value_type
* Better buffer_cat
HTTP

View File

@@ -19,18 +19,21 @@
namespace beast {
/** Concatenate 2 or more buffer sequences to form a `ConstBufferSequence`.
/** Concatenate 2 or more buffer sequences.
This function returns a @b `ConstBufferSequence` that when iterated,
efficiently concatenates the input buffer sequences. Copies of the
arguments passed will be made; however, the returned object does
not take ownership of the underlying memory. The application is still
responsible for managing the lifetime of the referenced memory.
This function returns a constant or mutable buffer sequence which,
when iterated, efficiently concatenates the input buffer sequences.
Copies of the arguments passed will be made; however, the returned
object does not take ownership of the underlying memory. The application
is still responsible for managing the lifetime of the referenced memory.
@param buffers The list of buffer sequences to concatenate.
@return A new @b `ConstBufferSequence` that represents the
concatenation of the input buffer sequences.
@return A new buffer sequence that represents the concatenation of
the input buffer sequences. This buffer sequence will be a
@b MutableBufferSequence if each of the passed buffer sequences is
also a @b MutableBufferSequence, else the returned buffer sequence
will be a @b ConstBufferSequence.
*/
#if GENERATING_DOCS
template<class... BufferSequence>
@@ -38,13 +41,14 @@ implementation_defined
buffer_cat(BufferSequence const&... buffers)
#else
template<class B1, class B2, class... Bn>
detail::buffer_cat_helper<
boost::asio::const_buffer, B1, B2, Bn...>
detail::buffer_cat_helper<B1, B2, Bn...>
buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn)
#endif
{
static_assert(
detail::is_all_ConstBufferSequence<B1, B2, Bn...>::value,
"BufferSequence requirements not met");
return detail::buffer_cat_helper<
boost::asio::const_buffer,
B1, B2, Bn...>{b1, b2, bn...};
}

View File

@@ -8,6 +8,8 @@
#ifndef BEAST_DETAIL_BUFFER_CAT_HPP
#define BEAST_DETAIL_BUFFER_CAT_HPP
#include <beast/core/buffer_concepts.hpp>
#include <beast/core/detail/type_traits.hpp>
#include <boost/asio/buffer.hpp>
#include <cstdint>
#include <iterator>
@@ -19,24 +21,36 @@
namespace beast {
namespace detail {
template<class ValueType, class... Bs>
template<class... Bn>
struct common_buffers_type
{
using type = typename std::conditional<
std::is_convertible<std::tuple<Bn...>,
typename repeat_tuple<sizeof...(Bn),
boost::asio::mutable_buffer>::type>::value,
boost::asio::mutable_buffer,
boost::asio::const_buffer>::type;
};
template<class... Bn>
class buffer_cat_helper
{
std::tuple<Bs...> bs_;
std::tuple<Bn...> bn_;
public:
using value_type = ValueType;
using value_type = typename
common_buffers_type<Bn...>::type;
class const_iterator;
buffer_cat_helper(buffer_cat_helper&&) = default;
buffer_cat_helper(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = default;
buffer_cat_helper& operator=(buffer_cat_helper const&) = default;
buffer_cat_helper& operator=(buffer_cat_helper&&) = delete;
buffer_cat_helper& operator=(buffer_cat_helper const&) = delete;
explicit
buffer_cat_helper(Bs const&... bs)
: bs_(bs...)
buffer_cat_helper(Bn const&... bn)
: bn_(bn...)
{
}
@@ -47,39 +61,22 @@ public:
end() const;
};
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<class ValueType, class... Bs>
class buffer_cat_helper<
ValueType, Bs...>::const_iterator
template<class... Bn>
class buffer_cat_helper<Bn...>::const_iterator
{
std::size_t n_;
std::tuple<Bs...> const* bs_;
std::tuple<Bn...> const* bn_;
std::array<std::uint8_t,
max_sizeof<typename Bs::const_iterator...>()> buf_;
max_sizeof<typename Bn::const_iterator...>()> buf_;
friend class buffer_cat_helper<ValueType, Bs...>;
friend class buffer_cat_helper<Bn...>;
template<std::size_t I>
using C = std::integral_constant<std::size_t, I>;
template<std::size_t I>
using iter_t = typename std::tuple_element<
I, std::tuple<Bs...>>::type::const_iterator;
I, std::tuple<Bn...>>::type::const_iterator;
template<std::size_t I>
iter_t<I>&
@@ -98,7 +95,8 @@ class buffer_cat_helper<
}
public:
using value_type = ValueType;
using value_type = typename
common_buffers_type<Bn...>::type;
using pointer = value_type const*;
using reference = value_type;
using difference_type = std::ptrdiff_t;
@@ -151,39 +149,39 @@ public:
private:
const_iterator(
std::tuple<Bs...> const& bs, bool at_end);
std::tuple<Bn...> const& bn, bool at_end);
void
construct(C<sizeof...(Bs)>)
construct(C<sizeof...(Bn)> const&)
{
auto constexpr I = sizeof...(Bs);
auto constexpr I = sizeof...(Bn);
n_ = I;
}
template<std::size_t I>
void
construct(C<I>)
construct(C<I> const&)
{
if(std::get<I>(*bs_).begin() !=
std::get<I>(*bs_).end())
if(std::get<I>(*bn_).begin() !=
std::get<I>(*bn_).end())
{
n_ = I;
new(buf_.data()) iter_t<I>{
std::get<I>(*bs_).begin()};
std::get<I>(*bn_).begin()};
return;
}
construct(C<I+1>{});
}
void
destroy(C<sizeof...(Bs)>)
destroy(C<sizeof...(Bn)> const&)
{
return;
}
template<std::size_t I>
void
destroy(C<I>)
destroy(C<I> const&)
{
if(n_ == I)
{
@@ -195,13 +193,15 @@ private:
}
void
move(C<sizeof...(Bs)>, const_iterator&&)
move(const_iterator&&,
C<sizeof...(Bn)> const&)
{
}
template<std::size_t I>
void
move(C<I>, const_iterator&& other)
move(const_iterator&& other,
C<I> const&)
{
if(n_ == I)
{
@@ -209,17 +209,19 @@ private:
std::move(other.iter<I>())};
return;
}
move(C<I+1>{}, std::move(other));
move(std::move(other), C<I+1>{});
}
void
copy(C<sizeof...(Bs)>, const_iterator const&)
copy(const_iterator const&,
C<sizeof...(Bn)> const&)
{
}
template<std::size_t I>
void
copy(C<I>, const_iterator const& other)
copy(const_iterator const& other,
C<I> const&)
{
if(n_ == I)
{
@@ -227,35 +229,36 @@ private:
other.iter<I>()};
return;
}
copy(C<I+1>{}, other);
copy(other, C<I+1>{});
}
bool
equal(C<sizeof...(Bs)>,
const_iterator const&) const
equal(const_iterator const&,
C<sizeof...(Bn)> const&) const
{
return true;
}
template<std::size_t I>
bool
equal(C<I>, const_iterator const& other) const
equal(const_iterator const& other,
C<I> const&) const
{
if(n_ == I)
return iter<I>() == other.iter<I>();
return equal(C<I+1>{}, other);
return equal(other, C<I+1>{});
}
[[noreturn]]
reference
dereference(C<sizeof...(Bs)>) const
dereference(C<sizeof...(Bn)> const&) const
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
reference
dereference(C<I>) const
dereference(C<I> const&) const
{
if(n_ == I)
return *iter<I>();
@@ -264,19 +267,19 @@ private:
[[noreturn]]
void
increment(C<sizeof...(Bs)>)
increment(C<sizeof...(Bn)> const&)
{
throw std::logic_error("invalid iterator");
}
template<std::size_t I>
void
increment(C<I>)
increment(C<I> const&)
{
if(n_ == I)
{
if(++iter<I>() !=
std::get<I>(*bs_).end())
std::get<I>(*bn_).end())
return;
using Iter = iter_t<I>;
iter<I>().~Iter();
@@ -286,23 +289,23 @@ private:
}
void
decrement(C<sizeof...(Bs)>)
decrement(C<sizeof...(Bn)> const&)
{
auto constexpr I = sizeof...(Bs);
auto constexpr I = sizeof...(Bn);
if(n_ == I)
{
--n_;
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
std::get<I-1>(*bn_).end()};
}
decrement(C<I-1>{});
}
void
decrement(C<0>)
decrement(C<0> const&)
{
auto constexpr I = 0;
if(iter<I>() != std::get<I>(*bs_).begin())
if(iter<I>() != std::get<I>(*bn_).begin())
{
--iter<I>();
return;
@@ -312,11 +315,11 @@ private:
template<std::size_t I>
void
decrement(C<I>)
decrement(C<I> const&)
{
if(n_ == I)
{
if(iter<I>() != std::get<I>(*bs_).begin())
if(iter<I>() != std::get<I>(*bn_).begin())
{
--iter<I>();
return;
@@ -325,7 +328,7 @@ private:
using Iter = iter_t<I>;
iter<I>().~Iter();
new(buf_.data()) iter_t<I-1>{
std::get<I-1>(*bs_).end()};
std::get<I-1>(*bn_).end()};
}
decrement(C<I-1>{});
}
@@ -333,54 +336,54 @@ private:
//------------------------------------------------------------------------------
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::~const_iterator()
{
destroy(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator()
: n_(sizeof...(Bs))
, bs_(nullptr)
: n_(sizeof...(Bn))
, bn_(nullptr)
{
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator(
std::tuple<Bs...> const& bs, bool at_end)
: bs_(&bs)
std::tuple<Bn...> const& bn, bool at_end)
: bn_(&bn)
{
if(at_end)
n_ = sizeof...(Bs);
n_ = sizeof...(Bn);
else
construct(C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator(const_iterator&& other)
: n_(other.n_)
, bs_(other.bs_)
, bn_(other.bn_)
{
move(C<0>{}, std::move(other));
move(std::move(other), C<0>{});
}
template<class ValueType, class... Bs>
buffer_cat_helper<ValueType, Bs...>::
template<class... Bn>
buffer_cat_helper<Bn...>::
const_iterator::const_iterator(const_iterator const& other)
: n_(other.n_)
, bs_(other.bs_)
, bn_(other.bn_)
{
copy(C<0>{}, other);
copy(other, C<0>{});
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator=(const_iterator&& other) ->
const_iterator&
{
@@ -388,14 +391,14 @@ const_iterator::operator=(const_iterator&& other) ->
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
move(C<0>{}, std::move(other));
bn_ = other.bn_;
move(std::move(other), C<0>{});
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator=(const_iterator const& other) ->
const_iterator&
{
@@ -403,35 +406,35 @@ const_iterator&
return *this;
destroy(C<0>{});
n_ = other.n_;
bs_ = other.bs_;
copy(C<0>{}, other);
bn_ = other.bn_;
copy(other, C<0>{});
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
bool
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator==(const_iterator const& other) const
{
if(bs_ != other.bs_)
if(bn_ != other.bn_)
return false;
if(n_ != other.n_)
return false;
return equal(C<0>{}, other);
return equal(other, C<0>{});
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator*() const ->
reference
{
return dereference(C<0>{});
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator++() ->
const_iterator&
{
@@ -439,30 +442,32 @@ const_iterator::operator++() ->
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
auto
buffer_cat_helper<ValueType, Bs...>::
buffer_cat_helper<Bn...>::
const_iterator::operator--() ->
const_iterator&
{
decrement(C<sizeof...(Bs)>{});
decrement(C<sizeof...(Bn)>{});
return *this;
}
template<class ValueType, class... Bs>
template<class... Bn>
inline
auto
buffer_cat_helper<ValueType, Bs...>::begin() const ->
buffer_cat_helper<Bn...>::begin() const ->
const_iterator
{
return const_iterator(bs_, false);
return const_iterator{bn_, false};
}
template<class ValueType, class... Bs>
template<class... Bn>
inline
auto
buffer_cat_helper<ValueType, Bs...>::end() const ->
buffer_cat_helper<Bn...>::end() const ->
const_iterator
{
return const_iterator(bs_, true);
return const_iterator{bn_, true};
}
} // detail

View File

@@ -85,6 +85,20 @@ public:
type3::value && type4::value>;
};
template<class B1, class... Bn>
struct is_all_ConstBufferSequence
: std::integral_constant<bool,
is_BufferSequence<B1, boost::asio::const_buffer>::type::value &&
is_all_ConstBufferSequence<Bn...>::value>
{
};
template<class B1>
struct is_all_ConstBufferSequence<B1>
: is_BufferSequence<B1, boost::asio::const_buffer>::type
{
};
template<class T>
class is_DynamicBuffer
{

View File

@@ -5,8 +5,10 @@
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BEAST_DETAIL_VOID_T_HPP
#define BEAST_DETAIL_VOID_T_HPP
#ifndef BEAST_DETAIL_TYPE_TRAITS_HPP
#define BEAST_DETAIL_TYPE_TRAITS_HPP
#include <type_traits>
namespace beast {
namespace detail {
@@ -33,6 +35,48 @@ void
ignore_unused()
{}
template<class U>
std::size_t constexpr
max_sizeof()
{
return sizeof(U);
}
template<class U0, class U1, class... Us>
std::size_t constexpr
max_sizeof()
{
return
max_sizeof<U0>() > max_sizeof<U1, Us...>() ?
max_sizeof<U0>() : max_sizeof<U1, Us...>();
}
template<unsigned N, class T, class... Tn>
struct repeat_tuple_impl
{
using type = typename repeat_tuple_impl<
N - 1, T, T, Tn...>::type;
};
template<class T, class... Tn>
struct repeat_tuple_impl<0, T, Tn...>
{
using type = std::tuple<T, Tn...>;
};
template<unsigned N, class T>
struct repeat_tuple
{
using type =
typename repeat_tuple_impl<N-1, T>::type;
};
template<class T>
struct repeat_tuple<0, T>
{
using type = std::tuple<>;
};
} // detail
} // beast

View File

@@ -123,8 +123,10 @@ template<class ConstBufferSequence>
#if GENERATING_DOCS
implementation_defined
#else
beast::detail::buffer_cat_helper<boost::asio::const_buffer,
chunk_encode_text, ConstBufferSequence, boost::asio::const_buffers_1>
beast::detail::buffer_cat_helper<
chunk_encode_text,
ConstBufferSequence,
boost::asio::const_buffers_1>
#endif
chunk_encode(bool fin, ConstBufferSequence const& buffers)
{

View File

@@ -13,6 +13,7 @@
#include <boost/asio/streambuf.hpp>
#include <iterator>
#include <list>
#include <type_traits>
#include <vector>
namespace beast {
@@ -20,14 +21,65 @@ namespace beast {
class buffer_cat_test : public unit_test::suite
{
public:
template< class Iterator >
template<class Iterator>
static
std::reverse_iterator<Iterator>
make_reverse_iterator( Iterator i )
make_reverse_iterator(Iterator i)
{
return std::reverse_iterator<Iterator>(i);
}
template<class ConstBufferSequence>
static
std::size_t
bsize1(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.begin(); it != bs.end(); ++it)
n += buffer_size(*it);
return n;
}
template<class ConstBufferSequence>
static
std::size_t
bsize2(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.begin(); it != bs.end(); it++)
n += buffer_size(*it);
return n;
}
template<class ConstBufferSequence>
static
std::size_t
bsize3(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.end(); it != bs.begin();)
n += buffer_size(*--it);
return n;
}
template<class ConstBufferSequence>
static
std::size_t
bsize4(ConstBufferSequence const& bs)
{
using boost::asio::buffer_size;
std::size_t n = 0;
for(auto it = bs.end(); it != bs.begin();)
{
it--;
n += buffer_size(*it);
}
return n;
}
void testBufferCat()
{
using boost::asio::buffer_size;
@@ -48,6 +100,10 @@ public:
auto bs = buffer_cat(
b1, b2, b3, b4, b5, b6);
BEAST_EXPECT(buffer_size(bs) == 10);
BEAST_EXPECT(bsize1(bs) == 10);
BEAST_EXPECT(bsize2(bs) == 10);
BEAST_EXPECT(bsize3(bs) == 10);
BEAST_EXPECT(bsize4(bs) == 10);
std::vector<const_buffer> v;
for(auto iter = make_reverse_iterator(bs.end());
iter != make_reverse_iterator(bs.begin()); ++iter)
@@ -55,8 +111,6 @@ public:
BEAST_EXPECT(buffer_size(bs) == 10);
decltype(bs) bs2(bs);
auto bs3(std::move(bs));
bs = bs2;
bs3 = std::move(bs2);
{
boost::asio::streambuf sb1, sb2;
BEAST_EXPECT(buffer_size(buffer_cat(
@@ -153,6 +207,60 @@ public:
void run() override
{
using boost::asio::const_buffer;
using boost::asio::const_buffers_1;
using boost::asio::mutable_buffer;
using boost::asio::mutable_buffers_1;
struct user_defined : mutable_buffer
{
};
// Check is_all_ConstBufferSequence
static_assert(
detail::is_all_ConstBufferSequence<
const_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
const_buffers_1, const_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
mutable_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
mutable_buffers_1, mutable_buffers_1
>::value, "");
static_assert(
detail::is_all_ConstBufferSequence<
const_buffers_1, mutable_buffers_1
>::value, "");
static_assert(
! detail::is_all_ConstBufferSequence<
const_buffers_1, mutable_buffers_1, int
>::value, "");
// Ensure that concatenating mutable buffer
// sequences results in a mutable buffer sequence
static_assert(std::is_same<
mutable_buffer,
decltype(buffer_cat(
std::declval<mutable_buffer>(),
std::declval<user_defined>(),
std::declval<mutable_buffer>()
))::value_type>::value, "");
// Ensure that concatenating mixed buffer
// sequences results in a const buffer sequence.
static_assert(std::is_same<
const_buffer,
decltype(buffer_cat(
std::declval<mutable_buffer>(),
std::declval<user_defined>(),
std::declval<const_buffer>()
))::value_type>::value, "");
testBufferCat();
testIterators();
}