From b367776c37ae8eee1ba69b29bb98bd6ef51b0ac4 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Sat, 15 Dec 2018 15:49:39 -0800 Subject: [PATCH] Fixes and tidying for flat_buffer: * Fix reserve() size calculation * Improved test coverage --- CHANGELOG.md | 1 + include/boost/beast/core/impl/flat_buffer.hpp | 11 +- test/beast/core/buffer_test.hpp | 29 +++- test/beast/core/flat_buffer.cpp | 93 +++++------ .../boost/beast/test/test_allocator.hpp | 158 +++++++++++++++++- 5 files changed, 225 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 376c84d3..ef4f9263 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Version 200 * Use new buffer traits, remove old unused traits * Optimize for size on buffers_cat preconditions * Refactor buffers_suffix +* Tidy up flat_buffer tests API Changes: diff --git a/include/boost/beast/core/impl/flat_buffer.hpp b/include/boost/beast/core/impl/flat_buffer.hpp index 49c37fe4..15e7246f 100644 --- a/include/boost/beast/core/impl/flat_buffer.hpp +++ b/include/boost/beast/core/impl/flat_buffer.hpp @@ -253,7 +253,7 @@ reserve(std::size_t n) if(max_ < n) max_ = n; if(capacity() < n) - prepare(n - capacity()); + prepare(n - size()); } template @@ -293,13 +293,16 @@ basic_flat_buffer:: prepare(std::size_t n) -> mutable_buffers_type { + auto const len = size(); + if(len > max_ || n > (max_ - len)) + BOOST_THROW_EXCEPTION(std::length_error{ + "basic_flat_buffer too long"}); if(n <= dist(out_, end_)) { // existing capacity is sufficient last_ = out_ + n; return{out_, n}; } - auto const len = size(); if(n <= capacity() - len) { // after a memmove, @@ -311,10 +314,6 @@ prepare(std::size_t n) -> last_ = out_ + n; return {out_, n}; } - // enforce maximum capacity - if(n > max_ - len) - BOOST_THROW_EXCEPTION(std::length_error{ - "basic_flat_buffer overflow"}); // allocate a new buffer auto const new_size = (std::min)( max_, diff --git a/test/beast/core/buffer_test.hpp b/test/beast/core/buffer_test.hpp index ba07569e..f9f7607f 100644 --- a/test/beast/core/buffer_test.hpp +++ b/test/beast/core/buffer_test.hpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -347,6 +346,24 @@ struct is_mutable_dynamic_buffer +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( @@ -369,6 +386,11 @@ test_mutable_dynamic_buffer( net::is_mutable_buffer_sequence::value); + BOOST_STATIC_ASSERT( + std::is_convertible< + typename MutableDynamicBuffer::mutable_data_type, + typename MutableDynamicBuffer::const_buffers_type>::value); + string_view src = "Hello, world!"; if(src.size() > b0.max_size()) src = {src.data(), b0.max_size()}; @@ -377,11 +399,8 @@ test_mutable_dynamic_buffer( { MutableDynamicBuffer b(b0); auto const mb = b.prepare(src.size()); - using iter_type = net::buffers_iterator; SUITE_EXPECT(test, buffer_size(mb) == src.size()); - std::fill( - iter_type::begin(mb), - iter_type::end(mb), '*'); + buffers_fill(mb, '*'); b.commit(src.size()); SUITE_EXPECT(test, b.size() == src.size()); SUITE_EXPECT(test, diff --git a/test/beast/core/flat_buffer.cpp b/test/beast/core/flat_buffer.cpp index f4c58d18..27fe821f 100644 --- a/test/beast/core/flat_buffer.cpp +++ b/test/beast/core/flat_buffer.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -27,55 +26,16 @@ namespace beast { class flat_buffer_test : public beast::unit_test::suite { public: - BOOST_STATIC_ASSERT( - net::is_dynamic_buffer< - flat_buffer>::value); - BOOST_STATIC_ASSERT( - net::is_const_buffer_sequence< - flat_buffer::const_buffers_type>::value); - BOOST_STATIC_ASSERT( - net::is_mutable_buffer_sequence< - flat_buffer::mutable_data_type>::value); - BOOST_STATIC_ASSERT( - net::is_mutable_buffer_sequence< - flat_buffer::mutable_buffers_type>::value); - BOOST_STATIC_ASSERT(std::is_convertible< - flat_buffer::mutable_data_type, - flat_buffer::const_buffers_type>::value); - - template void - testMutableData() + testDynamicBuffer() { - DynamicBuffer b; - DynamicBuffer const& cb = b; - ostream(b) << "Hello"; - BOOST_STATIC_ASSERT( - net::is_const_buffer_sequence< - decltype(cb.data())>::value && - ! net::is_mutable_buffer_sequence< - decltype(cb.data())>::value); - BOOST_STATIC_ASSERT( - net::is_const_buffer_sequence< - decltype(cb.cdata())>::value && - ! net::is_mutable_buffer_sequence< - decltype(cb.cdata())>::value); - BOOST_STATIC_ASSERT( - net::is_mutable_buffer_sequence< - decltype(b.data())>::value); - std::for_each( - net::buffers_iterator::begin(b.data()), - net::buffers_iterator::end(b.data()), - [](char& c) - { - c = static_cast(std::toupper(c)); - }); - BEAST_EXPECT(buffers_to_string(b.data()) == "HELLO"); - BEAST_EXPECT(buffers_to_string(b.cdata()) == "HELLO"); + flat_buffer b(30); + BEAST_EXPECT(b.max_size() == 30); + test_dynamic_buffer(*this, b); } void - testBuffer() + testSpecialMembers() { using namespace test; @@ -181,6 +141,16 @@ public: BEAST_EXPECT(buffers_to_string(b1.data()) == "Hello"); BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello"); } + { + flat_buffer b1; + ostream(b1) << "Hello"; + basic_flat_buffer b2; + b2.reserve(1); + BEAST_EXPECT(b2.capacity() == 1); + b2 = b1; + BEAST_EXPECT(buffers_to_string(b2.data()) == "Hello"); + BEAST_EXPECT(b2.capacity() == b2.size()); + } // move assignment { @@ -319,6 +289,24 @@ public: BEAST_EXPECT(b.max_size() == 32); } + // allocator max_size + { + basic_flat_buffer b; + auto a = b.get_allocator(); + BOOST_STATIC_ASSERT( + ! std::is_const::value); + a->max_size = 30; + try + { + b.prepare(1000); + fail("", __FILE__, __LINE__); + } + catch(std::length_error const&) + { + pass(); + } + } + // read_size { flat_buffer b{10}; @@ -392,6 +380,11 @@ public: b.commit(20); b.reserve(50); BEAST_EXPECT(b.capacity() == 50); + + b.max_size(b.capacity()); + b.reserve(b.max_size() + 20); + BEAST_EXPECT(b.capacity() == 70); + BEAST_EXPECT(b.max_size() == 70); } // shrink to fit @@ -406,14 +399,20 @@ public: BEAST_EXPECT(b.capacity() >= 125); b.shrink_to_fit(); BEAST_EXPECT(b.capacity() == b.size()); + b.shrink_to_fit(); + BEAST_EXPECT(b.capacity() == b.size()); + b.consume(b.size()); + BEAST_EXPECT(b.size() == 0); + b.shrink_to_fit(); + BEAST_EXPECT(b.capacity() == 0); } } void run() override { - testBuffer(); - testMutableData(); + testDynamicBuffer(); + testSpecialMembers(); } }; diff --git a/test/extras/include/boost/beast/test/test_allocator.hpp b/test/extras/include/boost/beast/test/test_allocator.hpp index 6dfabb0a..c3c3ee6e 100644 --- a/test/extras/include/boost/beast/test/test_allocator.hpp +++ b/test/extras/include/boost/beast/test/test_allocator.hpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace boost { @@ -26,6 +27,8 @@ struct test_allocator_info std::size_t nmassign = 0; std::size_t ncpassign = 0; std::size_t nselect = 0; + std::size_t max_size = ( + std::numeric_limits::max)(); test_allocator_info() : id([] @@ -37,28 +40,55 @@ struct test_allocator_info } }; -template +template< + class T, + bool Equal, + bool Assign, + bool Move, + bool Swap, + bool Select> class test_allocator; -template +template< + class T, + bool Equal, + bool Assign, + bool Move, + bool Swap, + bool Select> struct test_allocator_base { }; -template -struct test_allocator_base +// Select == true +template< + class T, + bool Equal, + bool Assign, + bool Move, + bool Swap> +struct test_allocator_base< + T, Equal, Assign, Move, Swap, true> { static test_allocator select_on_container_copy_construction(test_allocator< T, Equal, Assign, Move, Swap, true> const&) { - return test_allocator{}; + return test_allocator{}; } }; -template -class test_allocator : public test_allocator_base< +template< + class T, + bool Equal, + bool Assign, + bool Move, + bool Swap, + bool Select> +class test_allocator + : public test_allocator_base< T, Equal, Assign, Move, Swap, Select> { std::shared_ptr info_; @@ -85,7 +115,8 @@ public: }; test_allocator() - : info_(std::make_shared()) + : info_(std::make_shared< + test_allocator_info>()) { } @@ -138,6 +169,18 @@ public: ::operator delete(p); } + std::size_t + max_size() const + { + return info_->max_size; + } + + void + max_size(std::size_t n) + { + info_->max_size = n; + } + bool operator==(test_allocator const& other) const { @@ -156,13 +199,110 @@ public: return info_->id; } - test_allocator_info const* + test_allocator_info* operator->() const { return info_.get(); } }; +//------------------------------------------------------------------------------ + +#if 0 +struct allocator_info +{ + std::size_t const id; + + allocator_info() + : id([] + { + static std::atomic sid(0); + return ++sid; + }()) + { + } +}; + +struct allocator_defaults +{ + static std::size_t constexpr max_size = + (std::numeric_limits::max)(); +}; + +template< + class T, + class Traits = allocator_defaults> +struct allocator +{ +public: + using value_type = T; + +#if 0 + template + struct rebind + { + using other = + test_allocator; + }; +#endif + + allocator() = default; + allocator(allocator&& t) = default; + allocator(allocator const& u) = default; + + template< + class U, + class = typename std::enable_if< + ! std::is_same::value>::type> + allocator( + allocator const& u) noexcept + { + } + + allocator& + operator=(allocator&& u) noexcept + { + return *this; + } + + allocator& + operator=(allocator const& u) noexcept + { + return *this; + } + + 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 + max_size() const + { + } + + bool + operator==(test_allocator const& other) const + { + return id() == other.id() || Equal; + } + + bool + operator!=(test_allocator const& other) const + { + return ! this->operator==(other); + } +}; +#endif + } // test } // beast } // boost