From 23863230de1c57ef251becb399937f7a77ea83a9 Mon Sep 17 00:00:00 2001 From: Vinnie Falco Date: Fri, 9 Jun 2017 17:36:45 -0700 Subject: [PATCH] flat_buffer is an AllocatorAwareContainer fix #453 --- CHANGELOG.md | 2 + include/beast/core/flat_buffer.hpp | 226 +++++++------ include/beast/core/impl/flat_buffer.ipp | 412 +++++++++++++++++------- test/core/flat_buffer.cpp | 6 +- 4 files changed, 437 insertions(+), 209 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59bbef07..2b297279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ Version 52: +* flat_buffer is an AllocatorAwareContainer + API Changes: * `auto_fragment` is a member of `stream` diff --git a/include/beast/core/flat_buffer.hpp b/include/beast/core/flat_buffer.hpp index f67d0b51..9878a811 100644 --- a/include/beast/core/flat_buffer.hpp +++ b/include/beast/core/flat_buffer.hpp @@ -89,109 +89,120 @@ public: /// The type used to represent the output sequence as a list of buffers. using mutable_buffers_type = boost::asio::mutable_buffers_1; - /// Copy assignment (disallowed). - basic_flat_buffer& - operator=(basic_flat_buffer const&) = delete; - - /// Destructor. + /// Destructor ~basic_flat_buffer(); - /** Move constructor. + /** Constructor - The new object will have the same input sequence - and an empty output sequence. - - @note After the move, the moved-from object will - have a capacity of zero, an empty input sequence, - and an empty output sequence. + Upon construction, capacity will be zero. */ - basic_flat_buffer(basic_flat_buffer&&); + basic_flat_buffer(); - /** Move constructor. + /** Constructor - The new object will have the same input sequence - and an empty output sequence. + Upon construction, capacity will be zero. - @note After the move, the moved-from object will - have a capacity of zero, an empty input sequence, - and an empty output sequence. - - @param alloc The allocator to associate with the - stream buffer. - */ - basic_flat_buffer(basic_flat_buffer&&, - Allocator const& alloc); - - /** Copy constructor. - - The new object will have a copy of the input sequence - and an empty output sequence. - */ - basic_flat_buffer(basic_flat_buffer const&); - - /** Copy constructor. - - The new object will have a copy of the input sequence - and an empty output sequence. - - @param alloc The allocator to associate with the - stream buffer. - */ - basic_flat_buffer(basic_flat_buffer const&, - Allocator const& alloc); - - /** Copy constructor. - - The new object will have a copy of the input sequence - and an empty output sequence. - */ - template - basic_flat_buffer( - basic_flat_buffer const&); - - /** Copy constructor. - - The new object will have a copy of the input sequence - and an empty output sequence. - - @param alloc The allocator to associate with the - stream buffer. - */ - template - basic_flat_buffer( - basic_flat_buffer const&, - Allocator const& alloc); - - /** Construct a flat stream buffer. - - No allocation is performed; the buffer will have - empty input and output sequences. - - @param limit An optional non-zero value specifying the - maximum of the sum of the input and output sequence sizes - that can be allocated. If unspecified, the largest - possible value of `std::size_t` is used. + @param limit The setting for @ref max_size. */ explicit - basic_flat_buffer(std::size_t limit = ( - std::numeric_limits::max)()); + basic_flat_buffer(std::size_t limit); - /** Construct a flat stream buffer. + /** Constructor - No allocation is performed; the buffer will have - empty input and output sequences. + Upon construction, capacity will be zero. - @param alloc The allocator to associate with the - stream buffer. - - @param limit An optional non-zero value specifying the - maximum of the sum of the input and output sequence sizes - that can be allocated. If unspecified, the largest - possible value of `std::size_t` is used. + @param alloc The allocator to construct with. */ - basic_flat_buffer(Allocator const& alloc, - std::size_t limit = ( - std::numeric_limits::max)()); + explicit + basic_flat_buffer(Allocator const& alloc); + + /** Constructor + + Upon construction, capacity will be zero. + + @param limit The setting for @ref max_size. + + @param alloc The allocator to use. + */ + basic_flat_buffer( + std::size_t limit, Allocator const& alloc); + + /** Move constructor + + After the move, `*this` will have an empty output sequence. + + @param other The object to move from. After the move, + The object's state will be as if constructed using + its current allocator and limit. + */ + basic_flat_buffer(basic_flat_buffer&& other); + + /** Move constructor + + After the move, `*this` will have an empty output sequence. + + @param other The object to move from. After the move, + The object's state will be as if constructed using + its current allocator and limit. + + @param alloc The allocator to use. + */ + basic_flat_buffer( + basic_flat_buffer&& other, Allocator const& alloc); + + /** Copy constructor + + @param other The object to copy from. + */ + basic_flat_buffer(basic_flat_buffer const& other); + + /** Copy constructor + + @param other The object to copy from. + + @param alloc The allocator to use. + */ + basic_flat_buffer(basic_flat_buffer const& other, + Allocator const& alloc); + + /** Copy constructor + + @param other The object to copy from. + */ + template + basic_flat_buffer( + basic_flat_buffer const& other); + + /** Copy constructor + + @param other The object to copy from. + + @param alloc The allocator to use. + */ + template + basic_flat_buffer( + basic_flat_buffer const& other, + Allocator const& alloc); + + /** Move assignment + + After the move, `*this` will have an empty output sequence. + + @param other The object to move from. After the move, + The object's state will be as if constructed using + its current allocator and limit. + */ + basic_flat_buffer& + operator=(basic_flat_buffer&& other); + + /** Copy assignment + + After the copy, `*this` will have an empty output sequence. + + @param other The object to copy from. + */ + basic_flat_buffer& + operator=(basic_flat_buffer const& other); /// Returns a copy of the associated allocator. allocator_type @@ -287,14 +298,41 @@ public: void shrink_to_fit(); + template + friend + void + swap( + basic_flat_buffer& lhs, + basic_flat_buffer& rhs); + private: void - move_from(basic_flat_buffer& other); + reset(); - template + template void - copy_from(basic_flat_buffer< - OtherAlloc> const& other); + copy_from(DynamicBuffer const& other); + + void + move_assign(basic_flat_buffer&, std::true_type); + + void + move_assign(basic_flat_buffer&, std::false_type); + + void + copy_assign(basic_flat_buffer const&, std::true_type); + + void + copy_assign(basic_flat_buffer const&, std::false_type); + + void + swap(basic_flat_buffer&); + + void + swap(basic_flat_buffer&, std::true_type); + + void + swap(basic_flat_buffer&, std::false_type); }; using flat_buffer = diff --git a/include/beast/core/impl/flat_buffer.ipp b/include/beast/core/impl/flat_buffer.ipp index 99668754..66a01099 100644 --- a/include/beast/core/impl/flat_buffer.ipp +++ b/include/beast/core/impl/flat_buffer.ipp @@ -36,50 +36,7 @@ next_pow2(std::size_t x) } // detail -template -void -basic_flat_buffer:: -move_from(basic_flat_buffer& other) -{ - begin_ = other.begin_; - in_ = other.in_; - out_ = other.out_; - last_ = out_; - end_ = other.end_; - max_ = other.max_; - other.begin_ = nullptr; - other.in_ = nullptr; - other.out_ = nullptr; - other.last_ = nullptr; - other.end_ = nullptr; -} - -template -template -void -basic_flat_buffer:: -copy_from(basic_flat_buffer< - OtherAlloc> const& other) -{ - max_ = other.max_; - auto const n = other.size(); - if(n > 0) - { - begin_ = alloc_traits::allocate( - this->member(), n); - in_ = begin_; - out_ = begin_ + n; - last_ = out_; - end_ = out_; - std::memcpy(in_, other.in_, n); - return; - } - begin_ = nullptr; - in_ = nullptr; - out_ = nullptr; - last_ = nullptr; - end_ = nullptr; -} +//------------------------------------------------------------------------------ template basic_flat_buffer:: @@ -92,69 +49,14 @@ basic_flat_buffer:: template basic_flat_buffer:: -basic_flat_buffer(basic_flat_buffer&& other) - : detail::empty_base_optimization< - allocator_type>(std::move(other.member())) +basic_flat_buffer() + : begin_(nullptr) + , in_(nullptr) + , out_(nullptr) + , last_(nullptr) + , end_(nullptr) + , max_((std::numeric_limits::max)()) { - move_from(other); -} - -template -basic_flat_buffer:: -basic_flat_buffer(basic_flat_buffer&& other, - Allocator const& alloc) - : detail::empty_base_optimization< - allocator_type>(alloc) -{ - if(this->member() != other.member()) - { - copy_from(other); - return; - } - move_from(other); -} - -template -basic_flat_buffer:: -basic_flat_buffer( - basic_flat_buffer const& other) - : detail::empty_base_optimization( - alloc_traits::select_on_container_copy_construction( - other.member())) -{ - copy_from(other); -} - -template -basic_flat_buffer:: -basic_flat_buffer( - basic_flat_buffer const& other, - Allocator const& alloc) - : detail::empty_base_optimization< - allocator_type>(alloc) -{ - copy_from(other); -} - -template -template -basic_flat_buffer:: -basic_flat_buffer( - basic_flat_buffer const& other) -{ - copy_from(other); -} - -template -template -basic_flat_buffer:: -basic_flat_buffer( - basic_flat_buffer const& other, - Allocator const& alloc) - : detail::empty_base_optimization< - allocator_type>(alloc) -{ - copy_from(other); } template @@ -167,15 +69,25 @@ basic_flat_buffer(std::size_t limit) , end_(nullptr) , max_(limit) { - BOOST_ASSERT(limit >= 1); } template basic_flat_buffer:: -basic_flat_buffer(Allocator const& alloc, - std::size_t limit) - : detail::empty_base_optimization< - allocator_type>(alloc) +basic_flat_buffer(Allocator const& alloc) + : detail::empty_base_optimization(alloc) + , begin_(nullptr) + , in_(nullptr) + , out_(nullptr) + , last_(nullptr) + , end_(nullptr) + , max_((std::numeric_limits::max)()) +{ +} + +template +basic_flat_buffer:: +basic_flat_buffer(std::size_t limit, Allocator const& alloc) + : detail::empty_base_optimization(alloc) , begin_(nullptr) , in_(nullptr) , out_(nullptr) @@ -183,9 +95,148 @@ basic_flat_buffer(Allocator const& alloc, , end_(nullptr) , max_(limit) { - BOOST_ASSERT(limit >= 1); } +template +basic_flat_buffer:: +basic_flat_buffer(basic_flat_buffer&& other) + : detail::empty_base_optimization( + std::move(other.member())) + , begin_(other.begin_) + , in_(other.in_) + , out_(other.out_) + , last_(out_) + , end_(other.end_) + , max_(other.max_) +{ + other.begin_ = nullptr; + other.in_ = nullptr; + other.out_ = nullptr; + other.last_ = nullptr; + other.end_ = nullptr; +} + +template +basic_flat_buffer:: +basic_flat_buffer(basic_flat_buffer&& other, + Allocator const& alloc) + : detail::empty_base_optimization(alloc) +{ + if(this->member() != other.member()) + { + begin_ = nullptr; + in_ = nullptr; + out_ = nullptr; + last_ = nullptr; + end_ = nullptr; + max_ = other.max_; + copy_from(other); + other.reset(); + } + else + { + begin_ = other.begin_; + in_ = other.in_; + out_ = other.out_; + last_ = out_; + end_ = other.end_; + max_ = other.max_; + other.begin_ = nullptr; + other.in_ = nullptr; + other.out_ = nullptr; + other.last_ = nullptr; + other.end_ = nullptr; + } +} + +template +basic_flat_buffer:: +basic_flat_buffer(basic_flat_buffer const& other) + : detail::empty_base_optimization( + alloc_traits::select_on_container_copy_construction( + other.member())) + , begin_(nullptr) + , in_(nullptr) + , out_(nullptr) + , last_(nullptr) + , end_(nullptr) + , max_(other.max_) +{ + copy_from(other); +} + +template +basic_flat_buffer:: +basic_flat_buffer(basic_flat_buffer const& other, + Allocator const& alloc) + : detail::empty_base_optimization(alloc) + , begin_(nullptr) + , in_(nullptr) + , out_(nullptr) + , last_(nullptr) + , end_(nullptr) + , max_(other.max_) +{ + copy_from(other); +} + +template +template +basic_flat_buffer:: +basic_flat_buffer( + basic_flat_buffer const& other) + : begin_(nullptr) + , in_(nullptr) + , out_(nullptr) + , last_(nullptr) + , end_(nullptr) + , max_(other.max_) +{ + copy_from(other); +} + +template +template +basic_flat_buffer:: +basic_flat_buffer(basic_flat_buffer const& other, + Allocator const& alloc) + : detail::empty_base_optimization(alloc) + , begin_(nullptr) + , in_(nullptr) + , out_(nullptr) + , last_(nullptr) + , end_(nullptr) + , max_(other.max_) +{ + copy_from(other); +} + +template +auto +basic_flat_buffer:: +operator=(basic_flat_buffer&& other) -> + basic_flat_buffer& +{ + if(this != &other) + move_assign(other, + typename alloc_traits::propagate_on_container_move_assignment{}); + return *this; +} + +template +auto +basic_flat_buffer:: +operator=(basic_flat_buffer const& other) -> + basic_flat_buffer& +{ + if(this != &other) + copy_assign(other, + typename alloc_traits::propagate_on_container_copy_assignment{}); + return *this; +} + +//------------------------------------------------------------------------------ + template auto basic_flat_buffer:: @@ -311,6 +362,143 @@ shrink_to_fit() end_ = out_; } +//------------------------------------------------------------------------------ + +template +inline +void +basic_flat_buffer:: +reset() +{ + consume(size()); + shrink_to_fit(); +} + +template +template +inline +void +basic_flat_buffer:: +copy_from(DynamicBuffer const& buffer) +{ + if(buffer.size() == 0) + return; + using boost::asio::buffer_copy; + commit(buffer_copy( + prepare(buffer.size()), buffer.data())); +} + +template +inline +void +basic_flat_buffer:: +move_assign(basic_flat_buffer& other, std::true_type) +{ + reset(); + this->member() = std::move(other.member()); + begin_ = other.begin_; + in_ = other.in_; + out_ = other.out_; + last_ = out_; + end_ = other.end_; + max_ = other.max_; + other.begin_ = nullptr; + other.in_ = nullptr; + other.out_ = nullptr; + other.last_ = nullptr; + other.end_ = nullptr; +} + +template +inline +void +basic_flat_buffer:: +move_assign(basic_flat_buffer& other, std::false_type) +{ + reset(); + if(this->member() != other.member()) + { + copy_from(other); + other.reset(); + } + else + { + move_assign(other, std::true_type{}); + } +} + +template +inline +void +basic_flat_buffer:: +copy_assign(basic_flat_buffer const& other, std::true_type) +{ + reset(); + this->member() = other.member(); + copy_from(other); +} + +template +inline +void +basic_flat_buffer:: +copy_assign(basic_flat_buffer const& other, std::false_type) +{ + reset(); + copy_from(other); +} + +template +inline +void +basic_flat_buffer:: +swap(basic_flat_buffer& other) +{ + swap(other, + typename alloc_traits::propagate_on_container_swap{}); +} + +template +inline +void +basic_flat_buffer:: +swap(basic_flat_buffer& other, std::true_type) +{ + using std::swap; + swap(this->member(), other.member()); + swap(this->begin_, other.begin_); + swap(this->in_, other.in_); + swap(this->out_, other.out_); + this->last_ = this->out_; + other->last_ = other->out_; + swap(this->end_, other.end_); +} + +template +inline +void +basic_flat_buffer:: +swap(basic_flat_buffer& other, std::false_type) +{ + BOOST_ASSERT(this->member() == other.member()); + using std::swap; + swap(this->begin_, other.begin_); + swap(this->in_, other.in_); + swap(this->out_, other.out_); + this->last_ = this->out_; + other->last_ = other->out_; + swap(this->end_, other.end_); +} + +template +void +swap( + basic_flat_buffer& lhs, + basic_flat_buffer& rhs) +{ + lhs.swap(rhs); +} + } // beast #endif diff --git a/test/core/flat_buffer.cpp b/test/core/flat_buffer.cpp index 45da3b3c..b336033a 100644 --- a/test/core/flat_buffer.cpp +++ b/test/core/flat_buffer.cpp @@ -87,7 +87,7 @@ public: basic_flat_buffer b1{10}; } { - basic_flat_buffer b1{allocator{}, 10}; + basic_flat_buffer b1{10, allocator{}}; } } @@ -193,11 +193,11 @@ public: basic_flat_buffer; alloc_type alloc; { - type fba{alloc, 1}; + type fba{1, alloc}; BEAST_EXPECT(fba.max_size() == 1); } { - type fba{alloc, 1024}; + type fba{1024, alloc}; BEAST_EXPECT(fba.max_size() == 1024); } {