// // Copyright (c) 2016-2019 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) // // Official repository: https://github.com/boostorg/beast // #ifndef BOOST_BEAST_TEST_BUFFER_HPP #define BOOST_BEAST_TEST_BUFFER_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace beast { /** A MutableBufferSequence for tests, where length is always 3. */ class buffers_triple { net::mutable_buffer b_[3]; public: using value_type = net::mutable_buffer; using const_iterator = net::mutable_buffer const*; buffers_triple( buffers_triple const&) = default; buffers_triple& operator=( buffers_triple const&) = default; buffers_triple(char* data, std::size_t size) { b_[0] = {data, size/6}; data += b_[0].size(); size -= b_[0].size(); b_[1] = {data, 2*size/5}; data += b_[1].size(); size -= b_[1].size(); b_[2] = {data, size}; BOOST_ASSERT(b_[0].size() > 0); BOOST_ASSERT(b_[1].size() > 0); BOOST_ASSERT(b_[2].size() > 0); } bool operator==(buffers_triple const& rhs) const noexcept { return b_[0].data() == rhs.b_[0].data() && b_[0].size() == rhs.b_[0].size() && b_[1].data() == rhs.b_[1].data() && b_[1].size() == rhs.b_[1].size() && b_[2].data() == rhs.b_[2].data() && b_[2].size() == rhs.b_[2].size(); } bool operator!=(buffers_triple const& rhs) const noexcept { return !(*this == rhs); } const_iterator begin() const noexcept { return &b_[0]; } const_iterator end() const noexcept { return &b_[3]; } }; template std::size_t buffers_length( ConstBufferSequence const& buffers) { return std::distance( net::buffer_sequence_begin(buffers), net::buffer_sequence_end(buffers)); } //------------------------------------------------------------------------------ namespace detail { template void test_mutable_buffers( MutableBufferSequence const&, net::const_buffer) { } template void test_mutable_buffers( MutableBufferSequence const& b, net::mutable_buffer) { string_view src = "Hello, world!"; BOOST_ASSERT(buffer_bytes(b) <= src.size()); if(src.size() > buffer_bytes(b)) src = {src.data(), buffer_bytes(b)}; net::buffer_copy(b, net::const_buffer( src.data(), src.size())); BEAST_EXPECT(beast::buffers_to_string(b) == src); } } // detail /** Test an instance of a constant or mutable buffer sequence. */ template void test_buffer_sequence( ConstBufferSequence const& buffers) { BOOST_STATIC_ASSERT( net::is_const_buffer_sequence< ConstBufferSequence>::value); using iterator = decltype( net::buffer_sequence_begin(buffers)); BEAST_EXPECT(sizeof(iterator) > 0); auto const size = buffer_bytes(buffers); BEAST_EXPECT(size > 0 ); // begin, end auto const length = std::distance( net::buffer_sequence_begin(buffers), net::buffer_sequence_end(buffers)); BEAST_EXPECT(length > 0); BEAST_EXPECT( net::buffer_sequence_begin(buffers) != net::buffer_sequence_end(buffers)); // copy construction ConstBufferSequence b1(buffers); BEAST_EXPECT(buffer_bytes(b1) == size); // copy assignment ConstBufferSequence b2(buffers); b2 = b1; BEAST_EXPECT(buffer_bytes(b2) == size); // iterators { iterator it1{}; iterator it2{}; iterator it3 = net::buffer_sequence_begin(buffers); iterator it4 = net::buffer_sequence_end(buffers); BEAST_EXPECT(it1 == it2); BEAST_EXPECT(it1 != it3); BEAST_EXPECT(it3 != it1); BEAST_EXPECT(it1 != it4); BEAST_EXPECT(it4 != it1); } // bidirectional { auto const first = net::buffer_sequence_begin(buffers); auto const last = net::buffer_sequence_end(buffers); std::size_t n, m; iterator it; // pre-increment m = 0; n = length; for(it = first; n--; ++it) m += buffer_bytes(*it); BEAST_EXPECT(it == last); BEAST_EXPECT(m == size); // post-increment m = 0; n = length; for(it = first; n--;) m += buffer_bytes(*it++); BEAST_EXPECT(it == last); BEAST_EXPECT(m == size); // pre-decrement m = 0; n = length; for(it = last; n--;) m += buffer_bytes(*--it); BEAST_EXPECT(it == first); BEAST_EXPECT(m == size); // post-decrement m = 0; n = length; for(it = last; n--;) { it--; m += buffer_bytes(*it); } BEAST_EXPECT(it == first); BEAST_EXPECT(m == size); } detail::test_mutable_buffers(buffers, buffers_type{}); } //------------------------------------------------------------------------------ /** Metafunction to determine if a type meets the requirements of MutableDynamicBuffer_v0 */ /* @{ */ // VFALCO This trait needs tests template struct is_mutable_dynamic_buffer : std::false_type { }; template struct is_mutable_dynamic_buffer() = std::declval().data(), std::declval() = std::declval().cdata(), std::declval() = std::declval().data() ) > > : net::is_dynamic_buffer_v1 { }; /** @} */ namespace detail { template void buffers_fill( MutableBufferSequence const& buffers, char c) { auto const end = net::buffer_sequence_end(buffers); for(auto it = net::buffer_sequence_begin(buffers); it != end; ++it) { net::mutable_buffer b(*it); std::fill( static_cast(b.data()), static_cast(b.data()) + b.size(), c); } } template void test_mutable_dynamic_buffer( MutableDynamicBuffer_v0 const&, std::false_type) { } template void test_mutable_dynamic_buffer( MutableDynamicBuffer_v0 const& b0, std::true_type) { BOOST_STATIC_ASSERT( net::is_mutable_buffer_sequence::value); BOOST_STATIC_ASSERT( std::is_convertible< typename MutableDynamicBuffer_v0::mutable_buffers_type, typename MutableDynamicBuffer_v0::const_buffers_type>::value); string_view src = "Hello, world!"; if(src.size() > b0.max_size()) src = {src.data(), b0.max_size()}; // modify readable bytes { MutableDynamicBuffer_v0 b(b0); auto const mb = b.prepare(src.size()); BEAST_EXPECT(buffer_bytes(mb) == src.size()); buffers_fill(mb, '*'); b.commit(src.size()); BEAST_EXPECT(b.size() == src.size()); BEAST_EXPECT( beast::buffers_to_string(b.data()) == std::string(src.size(), '*')); BEAST_EXPECT( beast::buffers_to_string(b.cdata()) == std::string(src.size(), '*')); auto const n = net::buffer_copy( b.data(), net::const_buffer( src.data(), src.size())); BEAST_EXPECT(n == src.size()); BEAST_EXPECT( beast::buffers_to_string(b.data()) == src); BEAST_EXPECT( beast::buffers_to_string(b.cdata()) == src); } // mutable to const sequence conversion { MutableDynamicBuffer_v0 b(b0); b.commit(net::buffer_copy( b.prepare(src.size()), net::const_buffer(src.data(), src.size()))); auto mb = b.data(); auto cb = static_cast< MutableDynamicBuffer_v0 const&>(b).data(); auto cbc = b.cdata(); BEAST_EXPECT( beast::buffers_to_string(b.data()) == src); BEAST_EXPECT( beast::buffers_to_string(b.cdata()) == src); beast::test_buffer_sequence(cb); beast::test_buffer_sequence(cbc); beast::test_buffer_sequence(mb); { decltype(mb) mb2(mb); mb = mb2; decltype(cb) cb2(cb); cb = cb2; decltype(cbc) cbc2(cbc); cbc = cbc2; } { decltype(cb) cb2(mb); decltype(cbc) cbc2(mb); cb2 = mb; cbc2 = mb; } } } } // detail /** Test an instance of a dynamic buffer or mutable dynamic buffer. */ template void test_dynamic_buffer( DynamicBuffer_v0 const& b0) { BOOST_STATIC_ASSERT( net::is_dynamic_buffer_v1::value); BOOST_STATIC_ASSERT( net::is_const_buffer_sequence::value); BOOST_STATIC_ASSERT( net::is_mutable_buffer_sequence::value); BEAST_EXPECT(b0.size() == 0); BEAST_EXPECT(buffer_bytes(b0.data()) == 0); // members { string_view src = "Hello, world!"; DynamicBuffer_v0 b1(b0); auto const mb = b1.prepare(src.size()); b1.commit(net::buffer_copy(mb, net::const_buffer(src.data(), src.size()))); // copy constructor { DynamicBuffer_v0 b2(b1); BEAST_EXPECT(b2.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b1.data()) == buffers_to_string(b2.data())); // https://github.com/boostorg/beast/issues/1621 b2.consume(1); DynamicBuffer_v0 b3(b2); BEAST_EXPECT(b3.size() == b2.size()); BEAST_EXPECT( buffers_to_string(b2.data()) == buffers_to_string(b3.data())); } // move constructor { DynamicBuffer_v0 b2(b1); DynamicBuffer_v0 b3(std::move(b2)); BEAST_EXPECT(b3.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b3.data()) == buffers_to_string(b1.data())); } // copy assignment { DynamicBuffer_v0 b2(b0); b2 = b1; BEAST_EXPECT(b2.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b1.data()) == buffers_to_string(b2.data())); // self assignment b2 = *&b2; BEAST_EXPECT(b2.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b2.data()) == buffers_to_string(b1.data())); // https://github.com/boostorg/beast/issues/1621 b2.consume(1); DynamicBuffer_v0 b3(b2); BEAST_EXPECT(b3.size() == b2.size()); BEAST_EXPECT( buffers_to_string(b2.data()) == buffers_to_string(b3.data())); } // move assignment { DynamicBuffer_v0 b2(b1); DynamicBuffer_v0 b3(b0); b3 = std::move(b2); BEAST_EXPECT(b3.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b3.data()) == buffers_to_string(b1.data())); // self move b3 = std::move(b3); BEAST_EXPECT(b3.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b3.data()) == buffers_to_string(b1.data())); } // swap { DynamicBuffer_v0 b2(b1); DynamicBuffer_v0 b3(b0); BEAST_EXPECT(b2.size() == b1.size()); BEAST_EXPECT(b3.size() == b0.size()); using std::swap; swap(b2, b3); BEAST_EXPECT(b2.size() == b0.size()); BEAST_EXPECT(b3.size() == b1.size()); BEAST_EXPECT( buffers_to_string(b3.data()) == buffers_to_string(b1.data())); } } // n == 0 { DynamicBuffer_v0 b(b0); b.commit(1); BEAST_EXPECT(b.size() == 0); BEAST_EXPECT(buffer_bytes(b.prepare(0)) == 0); b.commit(0); BEAST_EXPECT(b.size() == 0); b.commit(1); BEAST_EXPECT(b.size() == 0); b.commit(b.max_size() + 1); BEAST_EXPECT(b.size() == 0); b.consume(0); BEAST_EXPECT(b.size() == 0); b.consume(1); BEAST_EXPECT(b.size() == 0); b.consume(b.max_size() + 1); BEAST_EXPECT(b.size() == 0); } // max_size { DynamicBuffer_v0 b(b0); if(BEAST_EXPECT( b.max_size() + 1 > b.max_size())) { try { b.prepare(b.max_size() + 1); BEAST_FAIL(); } catch(std::length_error const&) { BEAST_PASS(); } catch(...) { BEAST_FAIL(); } } } // setup source buffer char buf[13]; unsigned char k0 = 0; string_view src(buf, sizeof(buf)); if(src.size() > b0.max_size()) src = {src.data(), b0.max_size()}; BEAST_EXPECT(b0.max_size() >= src.size()); BEAST_EXPECT(b0.size() == 0); BEAST_EXPECT(buffer_bytes(b0.data()) == 0); auto const make_new_src = [&buf, &k0, &src] { auto k = k0++; for(std::size_t i = 0; i < src.size(); ++i) buf[i] = k++; }; // readable / writable buffer sequence tests { make_new_src(); DynamicBuffer_v0 b(b0); auto const& bc(b); auto const mb = b.prepare(src.size()); BEAST_EXPECT(buffer_bytes(mb) == src.size()); beast::test_buffer_sequence(mb); b.commit(net::buffer_copy(mb, net::const_buffer(src.data(), src.size()))); BEAST_EXPECT( buffer_bytes(bc.data()) == src.size()); beast::test_buffer_sequence(bc.data()); } // h = in size // i = prepare size // j = commit size // k = consume size for(std::size_t h = 1; h <= src.size(); ++h) { string_view in(src.data(), h); for(std::size_t i = 1; i <= in.size(); ++i) { for(std::size_t j = 1; j <= i + 1; ++j) { for(std::size_t k = 1; k <= in.size(); ++k) { { make_new_src(); DynamicBuffer_v0 b(b0); auto const& bc(b); net::const_buffer cb(in.data(), in.size()); while(cb.size() > 0) { auto const mb = b.prepare( std::min(i, b.max_size() - b.size())); auto const n = net::buffer_copy(mb, net::const_buffer(cb.data(), std::min(j, cb.size()))); b.commit(n); cb += n; } BEAST_EXPECT(b.size() == in.size()); BEAST_EXPECT( buffer_bytes(bc.data()) == in.size()); BEAST_EXPECT(beast::buffers_to_string( bc.data()) == in); while(b.size() > 0) b.consume(k); BEAST_EXPECT(buffer_bytes(bc.data()) == 0); } } } } } // MutableDynamicBuffer_v0 refinement detail::test_mutable_dynamic_buffer(b0, is_mutable_dynamic_buffer{}); } } // beast } // boost #endif