diff --git a/CHANGELOG.md b/CHANGELOG.md index f1f6179c..95ec785e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ API Changes: * Invoke callback on pings and pongs +* Move basic_streambuf to streambuf.hpp -------------------------------------------------------------------------------- diff --git a/include/beast/core.hpp b/include/beast/core.hpp index a46ad003..74525890 100644 --- a/include/beast/core.hpp +++ b/include/beast/core.hpp @@ -9,7 +9,6 @@ #define BEAST_CORE_HPP #include -#include #include #include #include diff --git a/include/beast/core/basic_streambuf.hpp b/include/beast/core/basic_streambuf.hpp deleted file mode 100644 index 9dac4c06..00000000 --- a/include/beast/core/basic_streambuf.hpp +++ /dev/null @@ -1,333 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot 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) -// - -#ifndef BEAST_BASIC_STREAMBUF_HPP -#define BEAST_BASIC_STREAMBUF_HPP - -#include -#include -#include -#include -#include -#include -#include - -namespace beast { - -/** A @b `DynamicBuffer` that uses multiple buffers internally. - - The implementation uses a sequence of one or more character arrays - of varying sizes. Additional character array objects are appended to - the sequence to accommodate changes in the size of the character - sequence. - - @note Meets the requirements of @b DynamicBuffer. - - @tparam Allocator The allocator to use for managing memory. -*/ -template -class basic_streambuf -#if ! GENERATING_DOCS - : private detail::empty_base_optimization< - typename std::allocator_traits:: - template rebind_alloc> -#endif -{ -public: -#if GENERATING_DOCS - /// The type of allocator used. - using allocator_type = Allocator; -#else - using allocator_type = typename - std::allocator_traits:: - template rebind_alloc; -#endif - -private: - // Storage for the list of buffers representing the input - // and output sequences. The allocation for each element - // contains `element` followed by raw storage bytes. - class element; - - using alloc_traits = std::allocator_traits; - using list_type = typename boost::intrusive::make_list>::type; - using iterator = typename list_type::iterator; - using const_iterator = typename list_type::const_iterator; - - using size_type = typename std::allocator_traits::size_type; - using const_buffer = boost::asio::const_buffer; - using mutable_buffer = boost::asio::mutable_buffer; - - static_assert(std::is_base_of::iterator_category>::value, - "BidirectionalIterator requirements not met"); - - static_assert(std::is_base_of::iterator_category>::value, - "BidirectionalIterator requirements not met"); - - list_type list_; // list of allocated buffers - iterator out_; // element that contains out_pos_ - size_type alloc_size_; // min amount to allocate - size_type in_size_ = 0; // size of the input sequence - size_type in_pos_ = 0; // input offset in list_.front() - size_type out_pos_ = 0; // output offset in *out_ - size_type out_end_ = 0; // output end offset in list_.back() - -public: -#if GENERATING_DOCS - /// The type used to represent the input sequence as a list of buffers. - using const_buffers_type = implementation_defined; - - /// The type used to represent the output sequence as a list of buffers. - using mutable_buffers_type = implementation_defined; - -#else - class const_buffers_type; - - class mutable_buffers_type; - -#endif - - /// Destructor. - ~basic_streambuf(); - - /** Move constructor. - - The new object will have the input sequence of - the other stream buffer, and an empty output sequence. - - @note After the move, the moved-from object will have - an empty input and output sequence, with no internal - buffers allocated. - */ - basic_streambuf(basic_streambuf&&); - - /** Move constructor. - - The new object will have the input sequence of - the other stream buffer, and an empty output sequence. - - @note After the move, the moved-from object will have - an empty input and output sequence, with no internal - buffers allocated. - - @param alloc The allocator to associate with the - stream buffer. - */ - basic_streambuf(basic_streambuf&&, - allocator_type const& alloc); - - /** Move assignment. - - This object will have the input sequence of - the other stream buffer, and an empty output sequence. - - @note After the move, the moved-from object will have - an empty input and output sequence, with no internal - buffers allocated. - */ - basic_streambuf& - operator=(basic_streambuf&&); - - /** Copy constructor. - - This object will have a copy of the other stream - buffer's input sequence, and an empty output sequence. - */ - basic_streambuf(basic_streambuf const&); - - /** Copy constructor. - - This object will have a copy of the other stream - buffer's input sequence, and an empty output sequence. - - @param alloc The allocator to associate with the - stream buffer. - */ - basic_streambuf(basic_streambuf const&, - allocator_type const& alloc); - - /** Copy assignment. - - This object will have a copy of the other stream - buffer's input sequence, and an empty output sequence. - */ - basic_streambuf& operator=(basic_streambuf const&); - - /** Copy constructor. - - This object will have a copy of the other stream - buffer's input sequence, and an empty output sequence. - */ - template - basic_streambuf(basic_streambuf const&); - - /** Copy constructor. - - This object will have a copy of the other stream - buffer's input sequence, and an empty output sequence. - - @param alloc The allocator to associate with the - stream buffer. - */ - template - basic_streambuf(basic_streambuf const&, - allocator_type const& alloc); - - /** Copy assignment. - - This object will have a copy of the other stream - buffer's input sequence, and an empty output sequence. - */ - template - basic_streambuf& operator=(basic_streambuf const&); - - /** Construct a stream buffer. - - @param alloc_size The size of buffer to allocate. This is a - soft limit, calls to prepare for buffers exceeding this size - will allocate the larger size. The default allocation size - is 1KB (1024 bytes). - - @param alloc The allocator to use. If this parameter is - unspecified, a default constructed allocator will be used. - */ - explicit - basic_streambuf(std::size_t alloc_size = 1024, - Allocator const& alloc = allocator_type{}); - - /// Returns a copy of the associated allocator. - allocator_type - get_allocator() const - { - return this->member(); - } - - /** Returns the default allocation size. - - This is the smallest size that the stream buffer will allocate. - The size of the allocation can influence capacity, which will - affect algorithms that use capacity to efficiently read from - streams. - */ - std::size_t - alloc_size() const - { - return alloc_size_; - } - - /** Set the default allocation size. - - This is the smallest size that the stream buffer will allocate. - The size of the allocation can influence capacity, which will - affect algorithms that use capacity to efficiently read from - streams. - - @note This will not affect any already-existing allocations. - - @param n The number of bytes. - */ - void - alloc_size(std::size_t n) - { - alloc_size_ = n; - } - - /// Returns the size of the input sequence. - size_type - size() const - { - return in_size_; - } - - /// Returns the permitted maximum sum of the sizes of the input and output sequence. - size_type - max_size() const - { - return (std::numeric_limits::max)(); - } - - /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation. - std::size_t - capacity() const; - - /** Get a list of buffers that represents the input sequence. - - @note These buffers remain valid across subsequent calls to `prepare`. - */ - const_buffers_type - data() const; - - /** Get a list of buffers that represents the output sequence, with the given size. - - @note Buffers representing the input sequence acquired prior to - this call remain valid. - */ - mutable_buffers_type - prepare(size_type n); - - /** Move bytes from the output sequence to the input sequence. - - @note Buffers representing the input sequence acquired prior to - this call remain valid. - */ - void - commit(size_type n); - - /// Remove bytes from the input sequence. - void - consume(size_type n); - - // Helper for boost::asio::read_until - template - friend - std::size_t - read_size_helper(basic_streambuf< - OtherAllocator> const& streambuf, std::size_t max_size); - -private: - void - clear(); - - void - move_assign(basic_streambuf& other, std::false_type); - - void - move_assign(basic_streambuf& other, std::true_type); - - void - copy_assign(basic_streambuf const& other, std::false_type); - - void - copy_assign(basic_streambuf const& other, std::true_type); - - void - delete_list(); - - void - debug_check() const; -}; - -/** Format output to a @ref basic_streambuf. - - @param streambuf The @ref basic_streambuf to write to. - - @param t The object to write. - - @return A reference to the @ref basic_streambuf. -*/ -template -basic_streambuf& -operator<<(basic_streambuf& streambuf, T const& t); - -} // beast - -#include - -#endif diff --git a/include/beast/core/impl/basic_streambuf.ipp b/include/beast/core/impl/streambuf.ipp similarity index 98% rename from include/beast/core/impl/basic_streambuf.ipp rename to include/beast/core/impl/streambuf.ipp index 710a5a1a..5844c8fb 100644 --- a/include/beast/core/impl/basic_streambuf.ipp +++ b/include/beast/core/impl/streambuf.ipp @@ -5,8 +5,8 @@ // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // -#ifndef BEAST_IMPL_BASIC_STREAMBUF_IPP -#define BEAST_IMPL_BASIC_STREAMBUF_IPP +#ifndef BEAST_IMPL_STREAMBUF_IPP +#define BEAST_IMPL_STREAMBUF_IPP #include #include @@ -624,7 +624,7 @@ basic_streambuf::prepare(size_type n) -> auto const len = e.size() + sizeof(e); alloc_traits::destroy(this->member(), &e); alloc_traits::deallocate(this->member(), - reinterpret_cast(&e), len); + reinterpret_cast(&e), len); } return mutable_buffers_type(*this); } @@ -696,7 +696,7 @@ basic_streambuf::consume(size_type n) auto const len = e.size() + sizeof(e); alloc_traits::destroy(this->member(), &e); alloc_traits::deallocate(this->member(), - reinterpret_cast(&e), len); + reinterpret_cast(&e), len); debug_check(); } else @@ -808,7 +808,7 @@ basic_streambuf::delete_list() auto const n = e.size() + sizeof(e); alloc_traits::destroy(this->member(), &e); alloc_traits::deallocate(this->member(), - reinterpret_cast(&e), n); + reinterpret_cast(&e), n); } } diff --git a/include/beast/core/streambuf.hpp b/include/beast/core/streambuf.hpp index 1726543f..401de0b1 100644 --- a/include/beast/core/streambuf.hpp +++ b/include/beast/core/streambuf.hpp @@ -8,10 +8,312 @@ #ifndef BEAST_STREAMBUF_HPP #define BEAST_STREAMBUF_HPP -#include +#include +#include +#include +#include +#include +#include +#include namespace beast { +/** A @b `DynamicBuffer` that uses multiple buffers internally. + + The implementation uses a sequence of one or more character arrays + of varying sizes. Additional character array objects are appended to + the sequence to accommodate changes in the size of the character + sequence. + + @note Meets the requirements of @b DynamicBuffer. + + @tparam Allocator The allocator to use for managing memory. +*/ +template +class basic_streambuf +#if ! GENERATING_DOCS + : private detail::empty_base_optimization< + typename std::allocator_traits:: + template rebind_alloc> +#endif +{ +public: +#if GENERATING_DOCS + /// The type of allocator used. + using allocator_type = Allocator; +#else + using allocator_type = typename + std::allocator_traits:: + template rebind_alloc; +#endif + +private: + // Storage for the list of buffers representing the input + // and output sequences. The allocation for each element + // contains `element` followed by raw storage bytes. + class element; + + using alloc_traits = std::allocator_traits; + using list_type = typename boost::intrusive::make_list>::type; + using iterator = typename list_type::iterator; + using const_iterator = typename list_type::const_iterator; + + using size_type = typename std::allocator_traits::size_type; + using const_buffer = boost::asio::const_buffer; + using mutable_buffer = boost::asio::mutable_buffer; + + static_assert(std::is_base_of::iterator_category>::value, + "BidirectionalIterator requirements not met"); + + static_assert(std::is_base_of::iterator_category>::value, + "BidirectionalIterator requirements not met"); + + list_type list_; // list of allocated buffers + iterator out_; // element that contains out_pos_ + size_type alloc_size_; // min amount to allocate + size_type in_size_ = 0; // size of the input sequence + size_type in_pos_ = 0; // input offset in list_.front() + size_type out_pos_ = 0; // output offset in *out_ + size_type out_end_ = 0; // output end offset in list_.back() + +public: +#if GENERATING_DOCS + /// The type used to represent the input sequence as a list of buffers. + using const_buffers_type = implementation_defined; + + /// The type used to represent the output sequence as a list of buffers. + using mutable_buffers_type = implementation_defined; + +#else + class const_buffers_type; + + class mutable_buffers_type; + +#endif + + /// Destructor. + ~basic_streambuf(); + + /** Move constructor. + + The new object will have the input sequence of + the other stream buffer, and an empty output sequence. + + @note After the move, the moved-from object will have + an empty input and output sequence, with no internal + buffers allocated. + */ + basic_streambuf(basic_streambuf&&); + + /** Move constructor. + + The new object will have the input sequence of + the other stream buffer, and an empty output sequence. + + @note After the move, the moved-from object will have + an empty input and output sequence, with no internal + buffers allocated. + + @param alloc The allocator to associate with the + stream buffer. + */ + basic_streambuf(basic_streambuf&&, + allocator_type const& alloc); + + /** Move assignment. + + This object will have the input sequence of + the other stream buffer, and an empty output sequence. + + @note After the move, the moved-from object will have + an empty input and output sequence, with no internal + buffers allocated. + */ + basic_streambuf& + operator=(basic_streambuf&&); + + /** Copy constructor. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + */ + basic_streambuf(basic_streambuf const&); + + /** Copy constructor. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + + @param alloc The allocator to associate with the + stream buffer. + */ + basic_streambuf(basic_streambuf const&, + allocator_type const& alloc); + + /** Copy assignment. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + */ + basic_streambuf& operator=(basic_streambuf const&); + + /** Copy constructor. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + */ + template + basic_streambuf(basic_streambuf const&); + + /** Copy constructor. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + + @param alloc The allocator to associate with the + stream buffer. + */ + template + basic_streambuf(basic_streambuf const&, + allocator_type const& alloc); + + /** Copy assignment. + + This object will have a copy of the other stream + buffer's input sequence, and an empty output sequence. + */ + template + basic_streambuf& operator=(basic_streambuf const&); + + /** Construct a stream buffer. + + @param alloc_size The size of buffer to allocate. This is a + soft limit, calls to prepare for buffers exceeding this size + will allocate the larger size. The default allocation size + is 1KB (1024 bytes). + + @param alloc The allocator to use. If this parameter is + unspecified, a default constructed allocator will be used. + */ + explicit + basic_streambuf(std::size_t alloc_size = 1024, + Allocator const& alloc = allocator_type{}); + + /// Returns a copy of the associated allocator. + allocator_type + get_allocator() const + { + return this->member(); + } + + /** Returns the default allocation size. + + This is the smallest size that the stream buffer will allocate. + The size of the allocation can influence capacity, which will + affect algorithms that use capacity to efficiently read from + streams. + */ + std::size_t + alloc_size() const + { + return alloc_size_; + } + + /** Set the default allocation size. + + This is the smallest size that the stream buffer will allocate. + The size of the allocation can influence capacity, which will + affect algorithms that use capacity to efficiently read from + streams. + + @note This will not affect any already-existing allocations. + + @param n The number of bytes. + */ + void + alloc_size(std::size_t n) + { + alloc_size_ = n; + } + + /// Returns the size of the input sequence. + size_type + size() const + { + return in_size_; + } + + /// Returns the permitted maximum sum of the sizes of the input and output sequence. + size_type + max_size() const + { + return (std::numeric_limits::max)(); + } + + /// Returns the maximum sum of the sizes of the input sequence and output sequence the buffer can hold without requiring reallocation. + std::size_t + capacity() const; + + /** Get a list of buffers that represents the input sequence. + + @note These buffers remain valid across subsequent calls to `prepare`. + */ + const_buffers_type + data() const; + + /** Get a list of buffers that represents the output sequence, with the given size. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. + */ + mutable_buffers_type + prepare(size_type n); + + /** Move bytes from the output sequence to the input sequence. + + @note Buffers representing the input sequence acquired prior to + this call remain valid. + */ + void + commit(size_type n); + + /// Remove bytes from the input sequence. + void + consume(size_type n); + + // Helper for boost::asio::read_until + template + friend + std::size_t + read_size_helper(basic_streambuf< + OtherAllocator> const& streambuf, std::size_t max_size); + +private: + void + clear(); + + void + move_assign(basic_streambuf& other, std::false_type); + + void + move_assign(basic_streambuf& other, std::true_type); + + void + copy_assign(basic_streambuf const& other, std::false_type); + + void + copy_assign(basic_streambuf const& other, std::true_type); + + void + delete_list(); + + void + debug_check() const; +}; + /** A @b `DynamicBuffer` that uses multiple buffers internally. The implementation uses a sequence of one or more character arrays @@ -23,6 +325,20 @@ namespace beast { */ using streambuf = basic_streambuf>; +/** Format output to a @ref basic_streambuf. + + @param streambuf The @ref basic_streambuf to write to. + + @param t The object to write. + + @return A reference to the @ref basic_streambuf. +*/ +template +basic_streambuf& +operator<<(basic_streambuf& streambuf, T const& t); + } // beast +#include + #endif diff --git a/test/Jamfile b/test/Jamfile index 5ffb5d59..6b8c52c1 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -16,7 +16,6 @@ compile zlib.cpp : : ; unit-test core-tests : ../extras/beast/unit_test/main.cpp core/async_completion.cpp - core/basic_streambuf.cpp core/bind_handler.cpp core/buffer_cat.cpp core/buffer_concepts.cpp diff --git a/test/core/CMakeLists.txt b/test/core/CMakeLists.txt index 1ef6f5a7..ad5aa722 100644 --- a/test/core/CMakeLists.txt +++ b/test/core/CMakeLists.txt @@ -10,7 +10,6 @@ add_executable (core-tests ../../extras/beast/unit_test/main.cpp buffer_test.hpp async_completion.cpp - basic_streambuf.cpp bind_handler.cpp buffer_cat.cpp buffer_concepts.cpp diff --git a/test/core/basic_streambuf.cpp b/test/core/basic_streambuf.cpp deleted file mode 100644 index 00b468dd..00000000 --- a/test/core/basic_streambuf.cpp +++ /dev/null @@ -1,480 +0,0 @@ -// -// Copyright (c) 2013-2016 Vinnie Falco (vinnie dot falco at gmail dot 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) -// - -// Test that header file is self-contained. -#include - -#include "buffer_test.hpp" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace beast { - -struct test_allocator_info -{ - std::size_t ncopy = 0; - std::size_t nmove = 0; - std::size_t nselect = 0; -}; - -template -class test_allocator; - -template -struct test_allocator_base -{ -}; - -template -struct test_allocator_base -{ - static - test_allocator - select_on_container_copy_construction( - test_allocator const& a) - { - return test_allocator{}; - } -}; - -template -class test_allocator : public test_allocator_base< - T, Assign, Move, Swap, Select> -{ - std::size_t id_; - std::shared_ptr info_; - - template - friend class test_allocator; - -public: - using value_type = T; - using propagate_on_container_copy_assignment = - std::integral_constant; - using propagate_on_container_move_assignment = - std::integral_constant; - using propagate_on_container_swap = - std::integral_constant; - - template - struct rebind - { - using other = test_allocator< - U, Assign, Move, Swap, Select>; - }; - - test_allocator() - : id_([] - { - static std::atomic< - std::size_t> sid(0); - return ++sid; - }()) - , info_(std::make_shared()) - { - } - - test_allocator(test_allocator const& u) noexcept - : id_(u.id_) - , info_(u.info_) - { - ++info_->ncopy; - } - - template - test_allocator(test_allocator< - U, Assign, Move, Swap, Select> const& u) noexcept - : id_(u.id_) - , info_(u.info_) - { - ++info_->ncopy; - } - - test_allocator(test_allocator&& t) - : id_(t.id_) - , info_(t.info_) - { - ++info_->nmove; - } - - value_type* - allocate(std::size_t n) - { - return static_cast( - ::operator new (n*sizeof(value_type))); - } - - void - deallocate(value_type* p, std::size_t) noexcept - { - ::operator delete(p); - } - - std::size_t - id() const - { - return id_; - } - - test_allocator_info const* - operator->() const - { - return info_.get(); - } -}; - -class basic_streambuf_test : public beast::unit_test::suite -{ -public: - template - static - bool - eq(basic_streambuf const& sb1, - basic_streambuf const& sb2) - { - return to_string(sb1.data()) == to_string(sb2.data()); - } - - template - void - expect_size(std::size_t n, ConstBufferSequence const& buffers) - { - BEAST_EXPECT(test::size_pre(buffers) == n); - BEAST_EXPECT(test::size_post(buffers) == n); - BEAST_EXPECT(test::size_rev_pre(buffers) == n); - BEAST_EXPECT(test::size_rev_post(buffers) == n); - } - - template - static - void - self_assign(U& u, V&& v) - { - u = std::forward(v); - } - - void testSpecialMembers() - { - using boost::asio::buffer; - using boost::asio::buffer_cast; - using boost::asio::buffer_size; - std::string const s = "Hello, world"; - BEAST_EXPECT(s.size() == 12); - for(std::size_t i = 1; i < 12; ++i) { - for(std::size_t x = 1; x < 4; ++x) { - for(std::size_t y = 1; y < 4; ++y) { - std::size_t z = s.size() - (x + y); - { - streambuf sb(i); - sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x))); - sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y))); - sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z))); - BEAST_EXPECT(to_string(sb.data()) == s); - { - streambuf sb2(sb); - BEAST_EXPECT(eq(sb, sb2)); - } - { - streambuf sb2; - sb2 = sb; - BEAST_EXPECT(eq(sb, sb2)); - } - { - streambuf sb2(std::move(sb)); - BEAST_EXPECT(to_string(sb2.data()) == s); - expect_size(0, sb.data()); - sb = std::move(sb2); - BEAST_EXPECT(to_string(sb.data()) == s); - expect_size(0, sb2.data()); - } - self_assign(sb, sb); - BEAST_EXPECT(to_string(sb.data()) == s); - self_assign(sb, std::move(sb)); - BEAST_EXPECT(to_string(sb.data()) == s); - } - }}} - try - { - streambuf sb0(0); - fail(); - } - catch(std::exception const&) - { - pass(); - } - } - - void testAllocator() - { - // VFALCO This needs work - { - using alloc_type = - test_allocator; - using sb_type = basic_streambuf; - sb_type sb; - BEAST_EXPECT(sb.get_allocator().id() == 1); - } - { - using alloc_type = - test_allocator; - using sb_type = basic_streambuf; - sb_type sb; - BEAST_EXPECT(sb.get_allocator().id() == 2); - sb_type sb2(sb); - BEAST_EXPECT(sb2.get_allocator().id() == 2); - sb_type sb3(sb, alloc_type{}); - } - } - - void - testPrepare() - { - using boost::asio::buffer_size; - { - streambuf sb(2); - BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5); - BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8); - BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7); - } - { - streambuf sb(2); - sb.prepare(2); - BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2); - BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3); - BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2); - } - } - - void testCommit() - { - using boost::asio::buffer_size; - streambuf sb(2); - sb.prepare(2); - sb.prepare(5); - sb.commit(1); - expect_size(1, sb.data()); - } - - void testConsume() - { - using boost::asio::buffer_size; - streambuf sb(1); - expect_size(5, sb.prepare(5)); - sb.commit(3); - expect_size(3, sb.data()); - sb.consume(1); - expect_size(2, sb.data()); - } - - void testMatrix() - { - using boost::asio::buffer; - using boost::asio::buffer_cast; - using boost::asio::buffer_size; - std::string const s = "Hello, world"; - BEAST_EXPECT(s.size() == 12); - for(std::size_t i = 1; i < 12; ++i) { - for(std::size_t x = 1; x < 4; ++x) { - for(std::size_t y = 1; y < 4; ++y) { - for(std::size_t t = 1; t < 4; ++ t) { - for(std::size_t u = 1; u < 4; ++ u) { - std::size_t z = s.size() - (x + y); - std::size_t v = s.size() - (t + u); - { - streambuf sb(i); - { - auto d = sb.prepare(z); - BEAST_EXPECT(buffer_size(d) == z); - } - { - auto d = sb.prepare(0); - BEAST_EXPECT(buffer_size(d) == 0); - } - { - auto d = sb.prepare(y); - BEAST_EXPECT(buffer_size(d) == y); - } - { - auto d = sb.prepare(x); - BEAST_EXPECT(buffer_size(d) == x); - sb.commit(buffer_copy(d, buffer(s.data(), x))); - } - BEAST_EXPECT(sb.size() == x); - BEAST_EXPECT(buffer_size(sb.data()) == sb.size()); - { - auto d = sb.prepare(x); - BEAST_EXPECT(buffer_size(d) == x); - } - { - auto d = sb.prepare(0); - BEAST_EXPECT(buffer_size(d) == 0); - } - { - auto d = sb.prepare(z); - BEAST_EXPECT(buffer_size(d) == z); - } - { - auto d = sb.prepare(y); - BEAST_EXPECT(buffer_size(d) == y); - sb.commit(buffer_copy(d, buffer(s.data()+x, y))); - } - sb.commit(1); - BEAST_EXPECT(sb.size() == x + y); - BEAST_EXPECT(buffer_size(sb.data()) == sb.size()); - { - auto d = sb.prepare(x); - BEAST_EXPECT(buffer_size(d) == x); - } - { - auto d = sb.prepare(y); - BEAST_EXPECT(buffer_size(d) == y); - } - { - auto d = sb.prepare(0); - BEAST_EXPECT(buffer_size(d) == 0); - } - { - auto d = sb.prepare(z); - BEAST_EXPECT(buffer_size(d) == z); - sb.commit(buffer_copy(d, buffer(s.data()+x+y, z))); - } - sb.commit(2); - BEAST_EXPECT(sb.size() == x + y + z); - BEAST_EXPECT(buffer_size(sb.data()) == sb.size()); - BEAST_EXPECT(to_string(sb.data()) == s); - sb.consume(t); - { - auto d = sb.prepare(0); - BEAST_EXPECT(buffer_size(d) == 0); - } - BEAST_EXPECT(to_string(sb.data()) == s.substr(t, std::string::npos)); - sb.consume(u); - BEAST_EXPECT(to_string(sb.data()) == s.substr(t + u, std::string::npos)); - sb.consume(v); - BEAST_EXPECT(to_string(sb.data()) == ""); - sb.consume(1); - { - auto d = sb.prepare(0); - BEAST_EXPECT(buffer_size(d) == 0); - } - } - }}}}} - } - - void testIterators() - { - using boost::asio::buffer_size; - streambuf sb(1); - sb.prepare(1); - sb.commit(1); - sb.prepare(2); - sb.commit(2); - expect_size(3, sb.data()); - sb.prepare(1); - expect_size(3, sb.prepare(3)); - sb.commit(2); - BEAST_EXPECT(test::buffer_count(sb.data()) == 4); - } - - void testOutputStream() - { - streambuf sb; - sb << "x"; - BEAST_EXPECT(to_string(sb.data()) == "x"); - } - - void testCapacity() - { - using boost::asio::buffer_size; - { - streambuf sb{10}; - BEAST_EXPECT(sb.alloc_size() == 10); - BEAST_EXPECT(read_size_helper(sb, 1) == 1); - BEAST_EXPECT(read_size_helper(sb, 10) == 10); - BEAST_EXPECT(read_size_helper(sb, 20) == 20); - BEAST_EXPECT(read_size_helper(sb, 1000) == 512); - sb.prepare(3); - sb.commit(3); - BEAST_EXPECT(read_size_helper(sb, 10) == 7); - BEAST_EXPECT(read_size_helper(sb, 1000) == 7); - } - { - streambuf sb(1000); - BEAST_EXPECT(sb.alloc_size() == 1000); - BEAST_EXPECT(read_size_helper(sb, 1) == 1); - BEAST_EXPECT(read_size_helper(sb, 1000) == 1000); - BEAST_EXPECT(read_size_helper(sb, 2000) == 1000); - sb.prepare(3); - BEAST_EXPECT(read_size_helper(sb, 1) == 1); - BEAST_EXPECT(read_size_helper(sb, 1000) == 1000); - BEAST_EXPECT(read_size_helper(sb, 2000) == 1000); - sb.commit(3); - BEAST_EXPECT(read_size_helper(sb, 1) == 1); - BEAST_EXPECT(read_size_helper(sb, 1000) == 997); - BEAST_EXPECT(read_size_helper(sb, 2000) == 997); - sb.consume(2); - BEAST_EXPECT(read_size_helper(sb, 1) == 1); - BEAST_EXPECT(read_size_helper(sb, 1000) == 997); - BEAST_EXPECT(read_size_helper(sb, 2000) == 997); - } - { - streambuf sb{2}; - BEAST_EXPECT(sb.alloc_size() == 2); - BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1); - BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2); - BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5); - BEAST_EXPECT(read_size_helper(sb, 10) == 6); - } - { - auto avail = - [](streambuf const& sb) - { - return sb.capacity() - sb.size(); - }; - streambuf sb{100}; - BEAST_EXPECT(sb.alloc_size() == 100); - BEAST_EXPECT(avail(sb) == 0); - sb.prepare(100); - BEAST_EXPECT(avail(sb) == 100); - sb.commit(100); - BEAST_EXPECT(avail(sb) == 0); - sb.consume(100); - BEAST_EXPECT(avail(sb) == 0); - sb.alloc_size(200); - BEAST_EXPECT(sb.alloc_size() == 200); - sb.prepare(1); - BEAST_EXPECT(avail(sb) == 200); - } - } - - void run() override - { - testSpecialMembers(); - testAllocator(); - testPrepare(); - testCommit(); - testConsume(); - testMatrix(); - testIterators(); - testOutputStream(); - testCapacity(); - } -}; - -BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast); - -} // beast diff --git a/test/core/streambuf.cpp b/test/core/streambuf.cpp index 3f46658d..815f49c7 100644 --- a/test/core/streambuf.cpp +++ b/test/core/streambuf.cpp @@ -8,9 +8,475 @@ // Test that header file is self-contained. #include +#include "buffer_test.hpp" #include +#include +#include +#include +#include +#include +#include +#include + namespace beast { static_assert(is_DynamicBuffer::value, ""); +struct test_allocator_info +{ + std::size_t ncopy = 0; + std::size_t nmove = 0; + std::size_t nselect = 0; +}; + +template +class test_allocator; + +template +struct test_allocator_base +{ +}; + +template +struct test_allocator_base +{ + static + test_allocator + select_on_container_copy_construction( + test_allocator const& a) + { + return test_allocator{}; + } +}; + +template +class test_allocator : public test_allocator_base< + T, Assign, Move, Swap, Select> +{ + std::size_t id_; + std::shared_ptr info_; + + template + friend class test_allocator; + +public: + using value_type = T; + using propagate_on_container_copy_assignment = + std::integral_constant; + using propagate_on_container_move_assignment = + std::integral_constant; + using propagate_on_container_swap = + std::integral_constant; + + template + struct rebind + { + using other = test_allocator< + U, Assign, Move, Swap, Select>; + }; + + test_allocator() + : id_([] + { + static std::atomic< + std::size_t> sid(0); + return ++sid; + }()) + , info_(std::make_shared()) + { + } + + test_allocator(test_allocator const& u) noexcept + : id_(u.id_) + , info_(u.info_) + { + ++info_->ncopy; + } + + template + test_allocator(test_allocator< + U, Assign, Move, Swap, Select> const& u) noexcept + : id_(u.id_) + , info_(u.info_) + { + ++info_->ncopy; + } + + test_allocator(test_allocator&& t) + : id_(t.id_) + , info_(t.info_) + { + ++info_->nmove; + } + + value_type* + allocate(std::size_t n) + { + return static_cast( + ::operator new (n*sizeof(value_type))); + } + + void + deallocate(value_type* p, std::size_t) noexcept + { + ::operator delete(p); + } + + std::size_t + id() const + { + return id_; + } + + test_allocator_info const* + operator->() const + { + return info_.get(); + } +}; + +class basic_streambuf_test : public beast::unit_test::suite +{ +public: + template + static + bool + eq(basic_streambuf const& sb1, + basic_streambuf const& sb2) + { + return to_string(sb1.data()) == to_string(sb2.data()); + } + + template + void + expect_size(std::size_t n, ConstBufferSequence const& buffers) + { + BEAST_EXPECT(test::size_pre(buffers) == n); + BEAST_EXPECT(test::size_post(buffers) == n); + BEAST_EXPECT(test::size_rev_pre(buffers) == n); + BEAST_EXPECT(test::size_rev_post(buffers) == n); + } + + template + static + void + self_assign(U& u, V&& v) + { + u = std::forward(v); + } + + void testSpecialMembers() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string const s = "Hello, world"; + BEAST_EXPECT(s.size() == 12); + for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + std::size_t z = s.size() - (x + y); + { + streambuf sb(i); + sb.commit(buffer_copy(sb.prepare(x), buffer(s.data(), x))); + sb.commit(buffer_copy(sb.prepare(y), buffer(s.data()+x, y))); + sb.commit(buffer_copy(sb.prepare(z), buffer(s.data()+x+y, z))); + BEAST_EXPECT(to_string(sb.data()) == s); + { + streambuf sb2(sb); + BEAST_EXPECT(eq(sb, sb2)); + } + { + streambuf sb2; + sb2 = sb; + BEAST_EXPECT(eq(sb, sb2)); + } + { + streambuf sb2(std::move(sb)); + BEAST_EXPECT(to_string(sb2.data()) == s); + expect_size(0, sb.data()); + sb = std::move(sb2); + BEAST_EXPECT(to_string(sb.data()) == s); + expect_size(0, sb2.data()); + } + self_assign(sb, sb); + BEAST_EXPECT(to_string(sb.data()) == s); + self_assign(sb, std::move(sb)); + BEAST_EXPECT(to_string(sb.data()) == s); + } + }}} + try + { + streambuf sb0(0); + fail(); + } + catch(std::exception const&) + { + pass(); + } + } + + void testAllocator() + { + // VFALCO This needs work + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + sb_type sb; + BEAST_EXPECT(sb.get_allocator().id() == 1); + } + { + using alloc_type = + test_allocator; + using sb_type = basic_streambuf; + sb_type sb; + BEAST_EXPECT(sb.get_allocator().id() == 2); + sb_type sb2(sb); + BEAST_EXPECT(sb2.get_allocator().id() == 2); + sb_type sb3(sb, alloc_type{}); + } + } + + void + testPrepare() + { + using boost::asio::buffer_size; + { + streambuf sb(2); + BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5); + BEAST_EXPECT(buffer_size(sb.prepare(8)) == 8); + BEAST_EXPECT(buffer_size(sb.prepare(7)) == 7); + } + { + streambuf sb(2); + sb.prepare(2); + BEAST_EXPECT(test::buffer_count(sb.prepare(5)) == 2); + BEAST_EXPECT(test::buffer_count(sb.prepare(8)) == 3); + BEAST_EXPECT(test::buffer_count(sb.prepare(4)) == 2); + } + } + + void testCommit() + { + using boost::asio::buffer_size; + streambuf sb(2); + sb.prepare(2); + sb.prepare(5); + sb.commit(1); + expect_size(1, sb.data()); + } + + void testConsume() + { + using boost::asio::buffer_size; + streambuf sb(1); + expect_size(5, sb.prepare(5)); + sb.commit(3); + expect_size(3, sb.data()); + sb.consume(1); + expect_size(2, sb.data()); + } + + void testMatrix() + { + using boost::asio::buffer; + using boost::asio::buffer_cast; + using boost::asio::buffer_size; + std::string const s = "Hello, world"; + BEAST_EXPECT(s.size() == 12); + for(std::size_t i = 1; i < 12; ++i) { + for(std::size_t x = 1; x < 4; ++x) { + for(std::size_t y = 1; y < 4; ++y) { + for(std::size_t t = 1; t < 4; ++ t) { + for(std::size_t u = 1; u < 4; ++ u) { + std::size_t z = s.size() - (x + y); + std::size_t v = s.size() - (t + u); + { + streambuf sb(i); + { + auto d = sb.prepare(z); + BEAST_EXPECT(buffer_size(d) == z); + } + { + auto d = sb.prepare(0); + BEAST_EXPECT(buffer_size(d) == 0); + } + { + auto d = sb.prepare(y); + BEAST_EXPECT(buffer_size(d) == y); + } + { + auto d = sb.prepare(x); + BEAST_EXPECT(buffer_size(d) == x); + sb.commit(buffer_copy(d, buffer(s.data(), x))); + } + BEAST_EXPECT(sb.size() == x); + BEAST_EXPECT(buffer_size(sb.data()) == sb.size()); + { + auto d = sb.prepare(x); + BEAST_EXPECT(buffer_size(d) == x); + } + { + auto d = sb.prepare(0); + BEAST_EXPECT(buffer_size(d) == 0); + } + { + auto d = sb.prepare(z); + BEAST_EXPECT(buffer_size(d) == z); + } + { + auto d = sb.prepare(y); + BEAST_EXPECT(buffer_size(d) == y); + sb.commit(buffer_copy(d, buffer(s.data()+x, y))); + } + sb.commit(1); + BEAST_EXPECT(sb.size() == x + y); + BEAST_EXPECT(buffer_size(sb.data()) == sb.size()); + { + auto d = sb.prepare(x); + BEAST_EXPECT(buffer_size(d) == x); + } + { + auto d = sb.prepare(y); + BEAST_EXPECT(buffer_size(d) == y); + } + { + auto d = sb.prepare(0); + BEAST_EXPECT(buffer_size(d) == 0); + } + { + auto d = sb.prepare(z); + BEAST_EXPECT(buffer_size(d) == z); + sb.commit(buffer_copy(d, buffer(s.data()+x+y, z))); + } + sb.commit(2); + BEAST_EXPECT(sb.size() == x + y + z); + BEAST_EXPECT(buffer_size(sb.data()) == sb.size()); + BEAST_EXPECT(to_string(sb.data()) == s); + sb.consume(t); + { + auto d = sb.prepare(0); + BEAST_EXPECT(buffer_size(d) == 0); + } + BEAST_EXPECT(to_string(sb.data()) == s.substr(t, std::string::npos)); + sb.consume(u); + BEAST_EXPECT(to_string(sb.data()) == s.substr(t + u, std::string::npos)); + sb.consume(v); + BEAST_EXPECT(to_string(sb.data()) == ""); + sb.consume(1); + { + auto d = sb.prepare(0); + BEAST_EXPECT(buffer_size(d) == 0); + } + } + }}}}} + } + + void testIterators() + { + using boost::asio::buffer_size; + streambuf sb(1); + sb.prepare(1); + sb.commit(1); + sb.prepare(2); + sb.commit(2); + expect_size(3, sb.data()); + sb.prepare(1); + expect_size(3, sb.prepare(3)); + sb.commit(2); + BEAST_EXPECT(test::buffer_count(sb.data()) == 4); + } + + void testOutputStream() + { + streambuf sb; + sb << "x"; + BEAST_EXPECT(to_string(sb.data()) == "x"); + } + + void testCapacity() + { + using boost::asio::buffer_size; + { + streambuf sb{10}; + BEAST_EXPECT(sb.alloc_size() == 10); + BEAST_EXPECT(read_size_helper(sb, 1) == 1); + BEAST_EXPECT(read_size_helper(sb, 10) == 10); + BEAST_EXPECT(read_size_helper(sb, 20) == 20); + BEAST_EXPECT(read_size_helper(sb, 1000) == 512); + sb.prepare(3); + sb.commit(3); + BEAST_EXPECT(read_size_helper(sb, 10) == 7); + BEAST_EXPECT(read_size_helper(sb, 1000) == 7); + } + { + streambuf sb(1000); + BEAST_EXPECT(sb.alloc_size() == 1000); + BEAST_EXPECT(read_size_helper(sb, 1) == 1); + BEAST_EXPECT(read_size_helper(sb, 1000) == 1000); + BEAST_EXPECT(read_size_helper(sb, 2000) == 1000); + sb.prepare(3); + BEAST_EXPECT(read_size_helper(sb, 1) == 1); + BEAST_EXPECT(read_size_helper(sb, 1000) == 1000); + BEAST_EXPECT(read_size_helper(sb, 2000) == 1000); + sb.commit(3); + BEAST_EXPECT(read_size_helper(sb, 1) == 1); + BEAST_EXPECT(read_size_helper(sb, 1000) == 997); + BEAST_EXPECT(read_size_helper(sb, 2000) == 997); + sb.consume(2); + BEAST_EXPECT(read_size_helper(sb, 1) == 1); + BEAST_EXPECT(read_size_helper(sb, 1000) == 997); + BEAST_EXPECT(read_size_helper(sb, 2000) == 997); + } + { + streambuf sb{2}; + BEAST_EXPECT(sb.alloc_size() == 2); + BEAST_EXPECT(test::buffer_count(sb.prepare(2)) == 1); + BEAST_EXPECT(test::buffer_count(sb.prepare(3)) == 2); + BEAST_EXPECT(buffer_size(sb.prepare(5)) == 5); + BEAST_EXPECT(read_size_helper(sb, 10) == 6); + } + { + auto avail = + [](streambuf const& sb) + { + return sb.capacity() - sb.size(); + }; + streambuf sb{100}; + BEAST_EXPECT(sb.alloc_size() == 100); + BEAST_EXPECT(avail(sb) == 0); + sb.prepare(100); + BEAST_EXPECT(avail(sb) == 100); + sb.commit(100); + BEAST_EXPECT(avail(sb) == 0); + sb.consume(100); + BEAST_EXPECT(avail(sb) == 0); + sb.alloc_size(200); + BEAST_EXPECT(sb.alloc_size() == 200); + sb.prepare(1); + BEAST_EXPECT(avail(sb) == 200); + } + } + + void run() override + { + testSpecialMembers(); + testAllocator(); + testPrepare(); + testCommit(); + testConsume(); + testMatrix(); + testIterators(); + testOutputStream(); + testCapacity(); + } +}; + +BEAST_DEFINE_TESTSUITE(basic_streambuf,core,beast); + } // beast