diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b5a9506..c4cb0d29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Version 261: * Remove redundant includes in websocket * Simplify websocket::detail::prng * Don't over-allocate in http::basic_fields +* Fix multi_buffer allocation alignment -------------------------------------------------------------------------------- diff --git a/include/boost/beast/core/impl/multi_buffer.hpp b/include/boost/beast/core/impl/multi_buffer.hpp index fde5ed68..f4dfd1c7 100644 --- a/include/boost/beast/core/impl/multi_buffer.hpp +++ b/include/boost/beast/core/impl/multi_buffer.hpp @@ -87,41 +87,6 @@ namespace beast { in_pos_ out_pos_ == 0 out_end_ == 0 */ -//------------------------------------------------------------------------------ - -template -class basic_multi_buffer::element - : public boost::intrusive::list_base_hook< - boost::intrusive::link_mode< - boost::intrusive::normal_link>> -{ - using size_type = typename - detail::allocator_traits::size_type; - - size_type const size_; - -public: - element(element const&) = delete; - - explicit - element(size_type n) noexcept - : size_(n) - { - } - - size_type - size() const noexcept - { - return size_; - } - - char* - data() const noexcept - { - return const_cast( - reinterpret_cast(this + 1)); - } -}; //------------------------------------------------------------------------------ @@ -486,7 +451,7 @@ template basic_multi_buffer:: basic_multi_buffer( Allocator const& alloc) noexcept - : boost::empty_value( + : boost::empty_value( boost::empty_init_t(), alloc) , max_(alloc_traits::max_size(this->get())) , out_(list_.end()) @@ -498,8 +463,8 @@ basic_multi_buffer:: basic_multi_buffer( std::size_t limit, Allocator const& alloc) noexcept - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), alloc) + : boost::empty_value( + boost::empty_init_t(), alloc) , max_(limit) , out_(list_.end()) { @@ -509,7 +474,7 @@ template basic_multi_buffer:: basic_multi_buffer( basic_multi_buffer&& other) noexcept - : boost::empty_value( + : boost::empty_value( boost::empty_init_t(), std::move(other.get())) , max_(other.max_) , in_size_(boost::exchange(other.in_size_, 0)) @@ -529,8 +494,8 @@ basic_multi_buffer:: basic_multi_buffer( basic_multi_buffer&& other, Allocator const& alloc) - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), alloc) + : boost::empty_value( + boost::empty_init_t(), alloc) , max_(other.max_) { if(this->get() != other.get()) @@ -561,7 +526,7 @@ template basic_multi_buffer:: basic_multi_buffer( basic_multi_buffer const& other) - : boost::empty_value( + : boost::empty_value( boost::empty_init_t(), alloc_traits:: select_on_container_copy_construction( other.get())) @@ -576,7 +541,7 @@ basic_multi_buffer:: basic_multi_buffer( basic_multi_buffer const& other, Allocator const& alloc) - : boost::empty_value( + : boost::empty_value( boost::empty_init_t(), alloc) , max_(other.max_) , out_(list_.end()) @@ -600,8 +565,8 @@ basic_multi_buffer:: basic_multi_buffer( basic_multi_buffer const& other, allocator_type const& alloc) - : boost::empty_value< - base_alloc_type>(boost::empty_init_t(), alloc) + : boost::empty_value( + boost::empty_init_t(), alloc) , max_(other.max_) , out_(list_.end()) { @@ -991,6 +956,7 @@ consume(size_type n) noexcept { if(list_.empty()) return; + auto a = rebind_type{this->get()}; for(;;) { if(list_.begin() != out_) @@ -1013,8 +979,8 @@ consume(size_type n) noexcept list_.erase(list_.iterator_to(e)); auto const len = sizeof(e) + e.size(); e.~element(); - alloc_traits::deallocate(this->get(), - reinterpret_cast(&e), len); + alloc_traits::deallocate(a, + reinterpret_cast(&e), len); #if BOOST_BEAST_MULTI_BUFFER_DEBUG_CHECK debug_check(); #endif @@ -1215,10 +1181,13 @@ void basic_multi_buffer:: destroy(element& e) { - auto const len = sizeof(e) + e.size(); + auto a = rebind_type{this->get()}; + auto const n = + (sizeof(element) + e.size() + sizeof(align_type) - 1) / + sizeof(align_type); e.~element(); - alloc_traits::deallocate(this->get(), - reinterpret_cast(&e), len); + alloc_traits::deallocate(a, + reinterpret_cast(&e), n); } template @@ -1230,9 +1199,11 @@ alloc(std::size_t size) -> if(size > alloc_traits::max_size(this->get())) BOOST_THROW_EXCEPTION(std::length_error( "A basic_multi_buffer exceeded the allocator's maximum size")); - return *::new(alloc_traits::allocate( - this->get(), - sizeof(element) + size)) element(size); + auto a = rebind_type{this->get()}; + auto const p = alloc_traits::allocate(a, + (sizeof(element) + size + sizeof(align_type) - 1) / + sizeof(align_type)); + return *(::new(p) element(size)); } template diff --git a/include/boost/beast/core/multi_buffer.hpp b/include/boost/beast/core/multi_buffer.hpp index 2cf477a8..837310c0 100644 --- a/include/boost/beast/core/multi_buffer.hpp +++ b/include/boost/beast/core/multi_buffer.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -62,14 +63,13 @@ namespace beast { template class basic_multi_buffer #if ! BOOST_BEAST_DOXYGEN - : private boost::empty_value< - typename detail::allocator_traits:: - template rebind_alloc> + : private boost::empty_value #endif { - using base_alloc_type = typename - detail::allocator_traits:: - template rebind_alloc; + // Fancy pointers are not supported + static_assert(std::is_pointer::pointer>::value, + "Allocator must use regular pointers"); static bool constexpr default_nothrow = std::is_nothrow_default_constructible::value; @@ -77,19 +77,61 @@ class basic_multi_buffer // 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; + class element + : public boost::intrusive::list_base_hook< + boost::intrusive::link_mode< + boost::intrusive::normal_link>> + { + using size_type = typename + detail::allocator_traits::size_type; + + size_type const size_; + + public: + element(element const&) = delete; + + explicit + element(size_type n) noexcept + : size_(n) + { + } + + size_type + size() const noexcept + { + return size_; + } + + char* + data() const noexcept + { + return const_cast( + reinterpret_cast(this + 1)); + } + }; template class readable_bytes; - using alloc_traits = - beast::detail::allocator_traits; - using list_type = typename boost::intrusive::make_list>::type; - using iter = typename list_type::iterator; - using const_iter = typename list_type::const_iterator; + using size_type = typename + detail::allocator_traits::size_type; - using size_type = typename alloc_traits::size_type; + using align_type = typename + boost::type_with_alignment::type; + + using rebind_type = typename + beast::detail::allocator_traits:: + template rebind_alloc; + + using alloc_traits = + beast::detail::allocator_traits; + + using list_type = typename boost::intrusive::make_list< + element, boost::intrusive::constant_time_size>::type; + + using iter = typename list_type::iterator; + + using const_iter = typename list_type::const_iterator; using pocma = typename alloc_traits::propagate_on_container_move_assignment;