From be6722439003b5d7744e0437961ade92531f9390 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Tue, 8 Nov 2016 19:00:40 -0500 Subject: [PATCH] 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. --- CHANGELOG.md | 1 + include/beast/core/buffer_cat.hpp | 28 ++- include/beast/core/detail/buffer_cat.hpp | 219 +++++++++--------- include/beast/core/detail/buffer_concepts.hpp | 14 ++ include/beast/core/detail/type_traits.hpp | 48 +++- include/beast/http/chunk_encode.hpp | 6 +- test/core/buffer_cat.cpp | 116 +++++++++- 7 files changed, 305 insertions(+), 127 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 300bcee6..ecde73a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/include/beast/core/buffer_cat.hpp b/include/beast/core/buffer_cat.hpp index 36f7e37f..d8c24b44 100644 --- a/include/beast/core/buffer_cat.hpp +++ b/include/beast/core/buffer_cat.hpp @@ -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 @@ -38,14 +41,15 @@ implementation_defined buffer_cat(BufferSequence const&... buffers) #else template -detail::buffer_cat_helper< - boost::asio::const_buffer, B1, B2, Bn...> +detail::buffer_cat_helper buffer_cat(B1 const& b1, B2 const& b2, Bn const&... bn) #endif { + static_assert( + detail::is_all_ConstBufferSequence::value, + "BufferSequence requirements not met"); return detail::buffer_cat_helper< - boost::asio::const_buffer, - B1, B2, Bn...>{b1, b2, bn...}; + B1, B2, Bn...>{b1, b2, bn...}; } } // beast diff --git a/include/beast/core/detail/buffer_cat.hpp b/include/beast/core/detail/buffer_cat.hpp index 65ca2214..fdf2b610 100644 --- a/include/beast/core/detail/buffer_cat.hpp +++ b/include/beast/core/detail/buffer_cat.hpp @@ -8,6 +8,8 @@ #ifndef BEAST_DETAIL_BUFFER_CAT_HPP #define BEAST_DETAIL_BUFFER_CAT_HPP +#include +#include #include #include #include @@ -19,24 +21,36 @@ namespace beast { namespace detail { -template +template +struct common_buffers_type +{ + using type = typename std::conditional< + std::is_convertible, + typename repeat_tuple::type>::value, + boost::asio::mutable_buffer, + boost::asio::const_buffer>::type; +}; + +template class buffer_cat_helper { - std::tuple bs_; + std::tuple bn_; public: - using value_type = ValueType; + using value_type = typename + common_buffers_type::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 -std::size_t constexpr -max_sizeof() -{ - return sizeof(U); -} - -template -std::size_t constexpr -max_sizeof() -{ - return - max_sizeof() > max_sizeof() ? - max_sizeof() : max_sizeof(); -} - -template -class buffer_cat_helper< - ValueType, Bs...>::const_iterator +template +class buffer_cat_helper::const_iterator { std::size_t n_; - std::tuple const* bs_; + std::tuple const* bn_; std::array()> buf_; + max_sizeof()> buf_; - friend class buffer_cat_helper; + friend class buffer_cat_helper; template using C = std::integral_constant; template using iter_t = typename std::tuple_element< - I, std::tuple>::type::const_iterator; + I, std::tuple>::type::const_iterator; template iter_t& @@ -98,7 +95,8 @@ class buffer_cat_helper< } public: - using value_type = ValueType; + using value_type = typename + common_buffers_type::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 const& bs, bool at_end); + std::tuple const& bn, bool at_end); void - construct(C) + construct(C const&) { - auto constexpr I = sizeof...(Bs); + auto constexpr I = sizeof...(Bn); n_ = I; } template void - construct(C) + construct(C const&) { - if(std::get(*bs_).begin() != - std::get(*bs_).end()) + if(std::get(*bn_).begin() != + std::get(*bn_).end()) { n_ = I; new(buf_.data()) iter_t{ - std::get(*bs_).begin()}; + std::get(*bn_).begin()}; return; } construct(C{}); } void - destroy(C) + destroy(C const&) { return; } template void - destroy(C) + destroy(C const&) { if(n_ == I) { @@ -195,13 +193,15 @@ private: } void - move(C, const_iterator&&) + move(const_iterator&&, + C const&) { } template void - move(C, const_iterator&& other) + move(const_iterator&& other, + C const&) { if(n_ == I) { @@ -209,17 +209,19 @@ private: std::move(other.iter())}; return; } - move(C{}, std::move(other)); + move(std::move(other), C{}); } void - copy(C, const_iterator const&) + copy(const_iterator const&, + C const&) { } template void - copy(C, const_iterator const& other) + copy(const_iterator const& other, + C const&) { if(n_ == I) { @@ -227,35 +229,36 @@ private: other.iter()}; return; } - copy(C{}, other); + copy(other, C{}); } bool - equal(C, - const_iterator const&) const + equal(const_iterator const&, + C const&) const { return true; } template bool - equal(C, const_iterator const& other) const + equal(const_iterator const& other, + C const&) const { if(n_ == I) return iter() == other.iter(); - return equal(C{}, other); + return equal(other, C{}); } [[noreturn]] reference - dereference(C) const + dereference(C const&) const { throw std::logic_error("invalid iterator"); } template reference - dereference(C) const + dereference(C const&) const { if(n_ == I) return *iter(); @@ -264,19 +267,19 @@ private: [[noreturn]] void - increment(C) + increment(C const&) { throw std::logic_error("invalid iterator"); } template void - increment(C) + increment(C const&) { if(n_ == I) { if(++iter() != - std::get(*bs_).end()) + std::get(*bn_).end()) return; using Iter = iter_t; iter().~Iter(); @@ -286,23 +289,23 @@ private: } void - decrement(C) + decrement(C const&) { - auto constexpr I = sizeof...(Bs); + auto constexpr I = sizeof...(Bn); if(n_ == I) { --n_; new(buf_.data()) iter_t{ - std::get(*bs_).end()}; + std::get(*bn_).end()}; } decrement(C{}); } void - decrement(C<0>) + decrement(C<0> const&) { auto constexpr I = 0; - if(iter() != std::get(*bs_).begin()) + if(iter() != std::get(*bn_).begin()) { --iter(); return; @@ -312,11 +315,11 @@ private: template void - decrement(C) + decrement(C const&) { if(n_ == I) { - if(iter() != std::get(*bs_).begin()) + if(iter() != std::get(*bn_).begin()) { --iter(); return; @@ -325,7 +328,7 @@ private: using Iter = iter_t; iter().~Iter(); new(buf_.data()) iter_t{ - std::get(*bs_).end()}; + std::get(*bn_).end()}; } decrement(C{}); } @@ -333,54 +336,54 @@ private: //------------------------------------------------------------------------------ -template -buffer_cat_helper:: +template +buffer_cat_helper:: const_iterator::~const_iterator() { destroy(C<0>{}); } -template -buffer_cat_helper:: +template +buffer_cat_helper:: const_iterator::const_iterator() - : n_(sizeof...(Bs)) - , bs_(nullptr) + : n_(sizeof...(Bn)) + , bn_(nullptr) { } -template -buffer_cat_helper:: +template +buffer_cat_helper:: const_iterator::const_iterator( - std::tuple const& bs, bool at_end) - : bs_(&bs) + std::tuple const& bn, bool at_end) + : bn_(&bn) { if(at_end) - n_ = sizeof...(Bs); + n_ = sizeof...(Bn); else construct(C<0>{}); } -template -buffer_cat_helper:: +template +buffer_cat_helper:: 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 -buffer_cat_helper:: +template +buffer_cat_helper:: 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 +template auto -buffer_cat_helper:: +buffer_cat_helper:: 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 +template auto -buffer_cat_helper:: +buffer_cat_helper:: 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 +template bool -buffer_cat_helper:: +buffer_cat_helper:: 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 +template auto -buffer_cat_helper:: +buffer_cat_helper:: const_iterator::operator*() const -> reference { return dereference(C<0>{}); } -template +template auto -buffer_cat_helper:: +buffer_cat_helper:: const_iterator::operator++() -> const_iterator& { @@ -439,30 +442,32 @@ const_iterator::operator++() -> return *this; } -template +template auto -buffer_cat_helper:: +buffer_cat_helper:: const_iterator::operator--() -> const_iterator& { - decrement(C{}); + decrement(C{}); return *this; } -template +template +inline auto -buffer_cat_helper::begin() const -> +buffer_cat_helper::begin() const -> const_iterator { - return const_iterator(bs_, false); + return const_iterator{bn_, false}; } -template +template +inline auto -buffer_cat_helper::end() const -> +buffer_cat_helper::end() const -> const_iterator { - return const_iterator(bs_, true); + return const_iterator{bn_, true}; } } // detail diff --git a/include/beast/core/detail/buffer_concepts.hpp b/include/beast/core/detail/buffer_concepts.hpp index 22d82918..6a4ef13d 100644 --- a/include/beast/core/detail/buffer_concepts.hpp +++ b/include/beast/core/detail/buffer_concepts.hpp @@ -85,6 +85,20 @@ public: type3::value && type4::value>; }; +template +struct is_all_ConstBufferSequence + : std::integral_constant::type::value && + is_all_ConstBufferSequence::value> +{ +}; + +template +struct is_all_ConstBufferSequence + : is_BufferSequence::type +{ +}; + template class is_DynamicBuffer { diff --git a/include/beast/core/detail/type_traits.hpp b/include/beast/core/detail/type_traits.hpp index 93b1ad05..3f6108cc 100644 --- a/include/beast/core/detail/type_traits.hpp +++ b/include/beast/core/detail/type_traits.hpp @@ -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 namespace beast { namespace detail { @@ -33,6 +35,48 @@ void ignore_unused() {} +template +std::size_t constexpr +max_sizeof() +{ + return sizeof(U); +} + +template +std::size_t constexpr +max_sizeof() +{ + return + max_sizeof() > max_sizeof() ? + max_sizeof() : max_sizeof(); +} + +template +struct repeat_tuple_impl +{ + using type = typename repeat_tuple_impl< + N - 1, T, T, Tn...>::type; +}; + +template +struct repeat_tuple_impl<0, T, Tn...> +{ + using type = std::tuple; +}; + +template +struct repeat_tuple +{ + using type = + typename repeat_tuple_impl::type; +}; + +template +struct repeat_tuple<0, T> +{ + using type = std::tuple<>; +}; + } // detail } // beast diff --git a/include/beast/http/chunk_encode.hpp b/include/beast/http/chunk_encode.hpp index 51749316..6d9836f2 100644 --- a/include/beast/http/chunk_encode.hpp +++ b/include/beast/http/chunk_encode.hpp @@ -123,8 +123,10 @@ template #if GENERATING_DOCS implementation_defined #else -beast::detail::buffer_cat_helper +beast::detail::buffer_cat_helper< + chunk_encode_text, + ConstBufferSequence, + boost::asio::const_buffers_1> #endif chunk_encode(bool fin, ConstBufferSequence const& buffers) { diff --git a/test/core/buffer_cat.cpp b/test/core/buffer_cat.cpp index 7833f2b9..41338622 100644 --- a/test/core/buffer_cat.cpp +++ b/test/core/buffer_cat.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include namespace beast { @@ -20,14 +21,65 @@ namespace beast { class buffer_cat_test : public unit_test::suite { public: - template< class Iterator > + template static std::reverse_iterator - make_reverse_iterator( Iterator i ) + make_reverse_iterator(Iterator i) { return std::reverse_iterator(i); } + template + 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 + 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 + 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 + 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 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(), + std::declval(), + std::declval() + ))::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(), + std::declval(), + std::declval() + ))::value_type>::value, ""); + testBufferCat(); testIterators(); }