From 9da5dc49f74047f19dc8083824f28b8a06e8796f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sun, 2 Jun 2024 13:08:18 +0200 Subject: [PATCH] Refactor move constructor and swap implementations for small_vector as in some cass unnecesary heap allocation was performed. Add tests for this. --- include/boost/container/small_vector.hpp | 66 +++---- include/boost/container/vector.hpp | 218 +++++++++++++++++----- test/small_vector_test.cpp | 220 ++++++++++++++++++++++- 3 files changed, 425 insertions(+), 79 deletions(-) diff --git a/include/boost/container/small_vector.hpp b/include/boost/container/small_vector.hpp index 860840ec..e20a8fed 100644 --- a/include/boost/container/small_vector.hpp +++ b/include/boost/container/small_vector.hpp @@ -283,7 +283,7 @@ class small_vector_allocator { return !(l == r); } #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED - private: + public: typedef small_vector_base derived_type; typedef typename dtl::vector_for_small_vector @@ -357,6 +357,7 @@ class small_vector_base typedef typename allocator_traits::const_pointer const_pointer; typedef typename allocator_traits::void_pointer void_pointer; typedef typename allocator_traits::const_void_pointer const_void_pointer; + typedef typename base_type::size_type size_type; private: @@ -378,18 +379,26 @@ class small_vector_base public: + BOOST_CONTAINER_ATTRIBUTE_NODISCARD bool is_small() const + { return this->internal_storage() == this->data(); } + protected: - inline explicit small_vector_base(initial_capacity_t, std::size_t initial_capacity) + inline explicit small_vector_base(initial_capacity_t, size_type initial_capacity) : base_type(initial_capacity_t(), this->internal_storage(), initial_capacity) {} template - inline explicit small_vector_base(initial_capacity_t, std::size_t capacity, BOOST_FWD_REF(AllocFwd) a) + inline explicit small_vector_base(initial_capacity_t, size_type capacity, BOOST_FWD_REF(AllocFwd) a) : base_type(initial_capacity_t(), this->internal_storage(), capacity, ::boost::forward(a)) {} - inline explicit small_vector_base(maybe_initial_capacity_t, std::size_t initial_capacity, std::size_t initial_size) + template + inline explicit small_vector_base(initial_capacity_t, size_type capacity, BOOST_FWD_REF(AllocFwd) a, small_vector_base &x) + : base_type(initial_capacity_t(), this->internal_storage(), capacity, ::boost::forward(a), x) + {} + + inline explicit small_vector_base(maybe_initial_capacity_t, size_type initial_capacity, size_type initial_size) : base_type( maybe_initial_capacity_t() , (initial_capacity >= initial_size) ? this->internal_storage() : pointer() , (initial_capacity >= initial_size) ? initial_capacity : initial_size @@ -397,7 +406,7 @@ class small_vector_base {} template - inline explicit small_vector_base(maybe_initial_capacity_t, std::size_t initial_capacity, std::size_t initial_size, BOOST_FWD_REF(AllocFwd) a) + inline explicit small_vector_base(maybe_initial_capacity_t, size_type initial_capacity, size_type initial_size, BOOST_FWD_REF(AllocFwd) a) : base_type(maybe_initial_capacity_t() , (initial_capacity >= initial_size) ? this->internal_storage() : pointer() , (initial_capacity >= initial_size) ? initial_capacity : initial_size @@ -405,11 +414,19 @@ class small_vector_base ) {} + void prot_shrink_to_fit_small(const size_type small_capacity) + { + this->base_type::prot_shrink_to_fit_small(this->internal_storage(), small_capacity); + } + using base_type::protected_set_size; //~small_vector_base(){} #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + inline void prot_swap(small_vector_base& other, size_type internal_capacity_value) + { this->base_type::prot_swap_small(other, internal_capacity_value); } + public: inline small_vector_base& operator=(BOOST_COPY_ASSIGN_REF(small_vector_base) other) { return static_cast(this->base_type::operator=(static_cast(other))); } @@ -418,24 +435,8 @@ class small_vector_base { return static_cast(this->base_type::operator=(BOOST_MOVE_BASE(base_type, other))); } inline void swap(small_vector_base &other) - { return this->base_type::swap(other); } + { return this->base_type::prot_swap_small(other, 0u); } - #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED - protected: - void move_construct_impl(base_type &x) - { - if(base_type::is_propagable_from(x.get_stored_allocator(), x.data(), this->base_type::get_stored_allocator(), true)){ - this->steal_resources(x); - } - else{ - const typename base_type::size_type sz = x.size(); - ::boost::container::uninitialized_move_alloc_n_source - (this->base_type::get_stored_allocator(), x.begin(), sz, this->begin()); - this->protected_set_size(sz); - x.clear(); - } - } - #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED }; #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED @@ -550,7 +551,7 @@ class small_vector typedef typename base_type::size_type size_type; typedef typename base_type::value_type value_type; - inline static std::size_t internal_capacity() + inline static size_type internal_capacity() { return static_capacity; } typedef allocator_traits allocator_traits_type; @@ -627,17 +628,17 @@ class small_vector { this->assign(other.cbegin(), other.cend()); } inline explicit small_vector(BOOST_RV_REF(base_type) other) - : base_type(initial_capacity_t(), internal_capacity(), ::boost::move(other.get_stored_allocator())) - { this->base_type::move_construct_impl(other); } + : base_type(initial_capacity_t(), internal_capacity(), ::boost::move(other.get_stored_allocator()), other) + {} inline small_vector(BOOST_RV_REF(small_vector) other) BOOST_NOEXCEPT_IF(boost::container::dtl::is_nothrow_move_constructible::value) - : base_type(initial_capacity_t(), internal_capacity(), ::boost::move(other.get_stored_allocator())) - { this->base_type::move_construct_impl(other); } + : base_type(initial_capacity_t(), internal_capacity(), ::boost::move(other.get_stored_allocator()), other) + {} inline small_vector(BOOST_RV_REF(small_vector) other, const allocator_type &a) - : base_type(initial_capacity_t(), internal_capacity(), a) - { this->base_type::move_construct_impl(other); } + : base_type(initial_capacity_t(), internal_capacity(), a, other) + {} #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) inline small_vector(std::initializer_list il, const allocator_type& a = allocator_type()) @@ -663,7 +664,12 @@ class small_vector { return static_cast(this->base_type::operator=(boost::move(other))); } inline void swap(small_vector &other) - { return this->base_type::swap(other); } + { return this->base_type::prot_swap(other, static_capacity); } + + inline void shrink_to_fit() + { + this->base_type::prot_shrink_to_fit_small(this->internal_capacity()); + } }; }} diff --git a/include/boost/container/vector.hpp b/include/boost/container/vector.hpp index 5046be94..fa2b4441 100644 --- a/include/boost/container/vector.hpp +++ b/include/boost/container/vector.hpp @@ -51,6 +51,7 @@ #include #include #include +#include // move/detail #if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) #include @@ -351,23 +352,23 @@ struct vector_alloc_holder public: - inline - static bool is_propagable_from(const allocator_type &from_alloc, pointer p, const allocator_type &to_alloc, bool const propagate_allocator) + template + inline static bool is_propagable_from(const allocator_type &from_alloc, pointer p, const allocator_type &to_alloc) { - (void)propagate_allocator; (void)p; (void)to_alloc; (void)from_alloc; + (void)p; (void)to_alloc; (void)from_alloc; const bool all_storage_propagable = !allocator_traits_type::is_partially_propagable::value || !allocator_traits_type::storage_is_unpropagable(from_alloc, p); return all_storage_propagable && - (propagate_allocator || allocator_traits_type::is_always_equal::value || allocator_traits_type::equal(from_alloc, to_alloc)); + (PropagateAllocator || allocator_traits_type::is_always_equal::value || allocator_traits_type::equal(from_alloc, to_alloc)); } - inline - static bool are_swap_propagable(const allocator_type &l_a, pointer l_p, const allocator_type &r_a, pointer r_p, bool const propagate_allocator) + template + inline static bool are_swap_propagable(const allocator_type &l_a, pointer l_p, const allocator_type &r_a, pointer r_p) { - (void)propagate_allocator; (void)l_p; (void)r_p; (void)l_a; (void)r_a; + (void)l_p; (void)r_p; (void)l_a; (void)r_a; const bool all_storage_propagable = !allocator_traits_type::is_partially_propagable::value || !(allocator_traits_type::storage_is_unpropagable(l_a, l_p) || allocator_traits_type::storage_is_unpropagable(r_a, r_p)); - return all_storage_propagable && (propagate_allocator || allocator_traits_type::equal(l_a, r_a)); + return all_storage_propagable && (PropagateAllocator || allocator_traits_type::is_always_equal::value || allocator_traits_type::equal(l_a, r_a)); } //Constructor, does not throw @@ -837,14 +838,14 @@ class vector protected: - inline - static bool is_propagable_from(const allocator_type &from_alloc, pointer p, const allocator_type &to_alloc, bool const propagate_allocator) - { return alloc_holder_t::is_propagable_from(from_alloc, p, to_alloc, propagate_allocator); } + template + inline static bool is_propagable_from(const allocator_type &from_alloc, pointer p, const allocator_type &to_alloc) + { return alloc_holder_t::template is_propagable_from(from_alloc, p, to_alloc); } - inline - static bool are_swap_propagable( const allocator_type &l_a, pointer l_p - , const allocator_type &r_a, pointer r_p, bool const propagate_allocator) - { return alloc_holder_t::are_swap_propagable(l_a, l_p, r_a, r_p, propagate_allocator); } + template + inline static bool are_swap_propagable( const allocator_type &l_a, pointer l_p + , const allocator_type &r_a, pointer r_p) + { return alloc_holder_t::template are_swap_propagable(l_a, l_p, r_a, r_p); } #endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED @@ -866,6 +867,25 @@ class vector : m_holder(initial_capacity_t(), initial_memory, cap, ::boost::forward(a)) {} + template + inline vector(initial_capacity_t, pointer initial_memory, size_type cap, BOOST_FWD_REF(AllocFwd) a, vector &x) + : m_holder(initial_capacity_t(), initial_memory, cap, ::boost::forward(a)) + { + allocator_type &this_al = this->get_stored_allocator(); + if (this->template is_propagable_from(x.get_stored_allocator(), x.data(), this_al)) { + this->steal_resources(x); + } + else { + const size_type sz = x.size(); + ::boost::container::uninitialized_move_alloc_n_source + ( this_al, x.priv_raw_begin(), sz + //Use launder to stop false positives from -Warray-bounds + , boost::move_detail::launder(this->priv_raw_begin())); + this->protected_set_size(sz); + x.clear(); + } + } + inline vector(initial_capacity_t, pointer initial_memory, size_type cap) : m_holder(initial_capacity_t(), initial_memory, cap) {} @@ -1163,11 +1183,11 @@ class vector vector(BOOST_RV_REF(vector) x, const allocator_type &a) : m_holder( vector_uninitialized_size, a //In this allocator move constructor the allocator won't be propagated --v - , is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a, false) ? 0 : x.size() + , is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a) ? 0 : x.size() ) { //In this allocator move constructor the allocator won't be propagated ---v - if(is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a, false)){ + if(is_propagable_from(x.get_stored_allocator(), x.m_holder.start(), a)){ this->m_holder.steal_resources(x.m_holder); } else{ @@ -2549,7 +2569,7 @@ class vector //In this allocator move constructor the allocator might will be propagated, but to support small_vector-like //types, we need to check the currently owned buffers to know if they are propagable. - const bool is_buffer_propagable_from_x = is_propagable_from(x_alloc, x.m_holder.start(), this_alloc, propagate_alloc); + const bool is_buffer_propagable_from_x = is_propagable_from(x_alloc, x.m_holder.start(), this_alloc); if (is_buffer_propagable_from_x) { this->priv_move_assign_steal_or_assign(boost::move(x), dtl::true_type()); @@ -2631,46 +2651,115 @@ class vector template //Template it to avoid it in explicit instantiations void priv_swap(Vector &x, dtl::false_type) //version_N { - const bool propagate_alloc = allocator_traits_type::propagate_on_container_swap::value; + BOOST_ASSERT(allocator_traits_type::propagate_on_container_swap::value || + allocator_traits_type::is_always_equal::value || + this->get_stored_allocator() == x.get_stored_allocator()); + + if (BOOST_UNLIKELY(&x == this)) { + return; + } + + //Just swap internals + this->m_holder.swap_resources(x.m_holder); + //And now swap the allocator + dtl::bool_ flag; + dtl::swap_alloc(this->m_holder.alloc(), x.m_holder.alloc(), flag); + } + + protected: + template //Template it to avoid it in explicit instantiations + void prot_swap_small(Vector &x, std::size_t internal_capacity) //version_N + { if (BOOST_UNLIKELY(&x == this)){ return; } - else if(are_swap_propagable( this->get_stored_allocator(), this->m_holder.start() - , x.get_stored_allocator(), x.m_holder.start(), propagate_alloc)){ - //Just swap internals - this->m_holder.swap_resources(x.m_holder); + + const bool propagate_alloc = allocator_traits_type::propagate_on_container_swap::value; + if(are_swap_propagable + ( this->get_stored_allocator(), this->m_holder.start(), x.get_stored_allocator(), x.m_holder.start())){ + this->priv_swap(x, dtl::false_()); + return; } - else{ - //Else swap element by element... + + allocator_type &th_al = this->get_stored_allocator(); + allocator_type &ot_al = x.get_stored_allocator(); + + const bool is_this_data_propagable = is_propagable_from(th_al, this->data(), ot_al); + const bool is_that_data_propagable = is_propagable_from(ot_al, x.data(), th_al); + + if(internal_capacity && (is_this_data_propagable || is_that_data_propagable)) { + //steal memory from src to dst, but move elements from dst to src + vector& extmem = is_this_data_propagable ? *this : x; + vector& intmem = is_this_data_propagable ? x : *this; + + //Reset extmem to the internal storage and backup data + pointer const orig_extdata = extmem.data(); + const size_type orig_extmem_size = extmem.size(); + const size_type orig_extmem_cap = extmem.capacity(); + + //New safe state for extmem -> empty, internal storage + extmem.m_holder.m_start = extmem.get_stored_allocator().internal_storage(); + extmem.m_holder.set_stored_size(0u); + extmem.m_holder.set_stored_capacity(internal_capacity); + + { + //Deallocate on exception + typename value_traits::ArrayDeallocator new_buffer_deallocator(orig_extdata, extmem.get_stored_allocator(), orig_extmem_cap); + typename value_traits::ArrayDestructor new_values_destroyer(orig_extdata, extmem.get_stored_allocator(), orig_extmem_size); + + //Move internal memory data to the internal memory data of the target, this can throw + BOOST_ASSERT(extmem.capacity() >= intmem.size()); + ::boost::container::uninitialized_move_alloc_n + (intmem.get_stored_allocator(), this->priv_raw_begin(), intmem.size(), extmem.priv_raw_begin()); + + //Exception not thrown, commit new state + extmem.m_holder.set_stored_size(intmem.size()); + //Throwing part passed, disable rollback + new_buffer_deallocator.release(); + new_values_destroyer.release(); + } + + //Destroy moved elements from intmem + boost::container::destroy_alloc_n + ( intmem.get_stored_allocator(), this->priv_raw_begin() + , intmem.size()); + + //Adopt dynamic buffer + intmem.m_holder.m_start = orig_extdata; + intmem.m_holder.set_stored_size(orig_extmem_size); + intmem.m_holder.set_stored_capacity(orig_extmem_cap); + + //And now swap the allocator + dtl::swap_alloc(this->m_holder.alloc(), x.m_holder.alloc(), dtl::bool_()); + } + else { //swap element by element and insert rest bool const t_smaller = this->size() < x.size(); vector &sml = t_smaller ? *this : x; vector &big = t_smaller ? x : *this; - //For empty containers, maybe storage can be moved from the other (just like in the move constructor) - if(sml.empty() && is_propagable_from(big.get_stored_allocator(), big.data(), sml.get_allocator(), propagate_alloc)){ - if(BOOST_LIKELY(0u != sml.capacity())) - sml.m_holder.deallocate(sml.m_holder.m_start, sml.m_holder.m_capacity); - sml.steal_resources(big); - } - else { - //Else swap element by element... - size_type const common_elements = sml.size(); - for(size_type i = 0; i != common_elements; ++i){ - boost::adl_move_swap(sml[i], big[i]); - } - //... and move-insert the remaining range - sml.insert( sml.cend() - , boost::make_move_iterator(boost::movelib::iterator_to_raw_pointer(big.nth(common_elements))) - , boost::make_move_iterator(boost::movelib::iterator_to_raw_pointer(big.end())) - ); - //Destroy remaining elements - big.erase(big.nth(common_elements), big.cend()); + //swap element by element until common size + size_type const common_elements = sml.size(); + for(size_type i = 0; i != common_elements; ++i){ + boost::adl_move_swap(sml[i], big[i]); } + + //And now swap the allocator to be able to construct new elements in sml with the proper allocator + dtl::swap_alloc(this->m_holder.alloc(), x.m_holder.alloc(), dtl::bool_()); + + //move-insert the remaining range + T *const raw_big_nth = boost::movelib::iterator_to_raw_pointer(big.nth(common_elements)); + sml.insert(sml.cend() + , boost::make_move_iterator(raw_big_nth) + , boost::make_move_iterator(boost::movelib::iterator_to_raw_pointer(big.end()))); + + //Destroy remaining, moved, elements with their original allocator + boost::container::destroy_alloc_n + ( sml.get_stored_allocator(), raw_big_nth + , std::size_t(big.m_holder.m_size - common_elements)); + big.m_holder.set_stored_size(common_elements); } - //And now swap the allocator - dtl::swap_alloc(this->m_holder.alloc(), x.m_holder.alloc(), dtl::bool_()); } - + private: inline void priv_move_to_new_buffer(size_type, version_0) { alloc_holder_t::on_capacity_overflow(); } @@ -2780,6 +2869,41 @@ class vector inline dtl::insert_value_initialized_n_proxy priv_resize_proxy(value_init_t) { return dtl::insert_value_initialized_n_proxy(); } + protected: + void prot_shrink_to_fit_small(pointer const small_buffer, const size_type small_capacity) + { + const size_type cp = this->m_holder.capacity(); + if (cp && this->m_holder.m_start != small_buffer) { //Do something only if a dynamic buffer is used + const size_type sz = this->size(); + if (!sz) { + if (BOOST_LIKELY(!!this->m_holder.m_start)) + this->m_holder.deallocate(this->m_holder.m_start, cp); + this->m_holder.m_start = small_buffer; + this->m_holder.set_stored_capacity(small_capacity); + } + else if(sz <= small_capacity) { + T *const oldbuf = boost::movelib::to_raw_pointer(this->m_holder.m_start); + ::boost::container::uninitialized_move_alloc_n + ( this->get_stored_allocator() + , oldbuf + , sz + , boost::movelib::to_raw_pointer(small_buffer) + ); + boost::container::destroy_alloc_n(this->get_stored_allocator(), oldbuf, sz); + + this->m_holder.m_start = small_buffer; + this->m_holder.set_stored_capacity(small_capacity); + + if (BOOST_LIKELY(!!this->m_holder.m_start)) + this->m_holder.deallocate(this->m_holder.m_start, cp); + } + else if (sz < cp) { + this->priv_move_to_new_buffer(sz, alloc_version()); + } + } + } + + private: inline void priv_shrink_to_fit(version_0) BOOST_NOEXCEPT_OR_NOTHROW {} diff --git a/test/small_vector_test.cpp b/test/small_vector_test.cpp index 2b32e5a3..403319aa 100644 --- a/test/small_vector_test.cpp +++ b/test/small_vector_test.cpp @@ -84,6 +84,140 @@ bool test_small_vector_base_test() if (!boost::container::test::CheckEqualContainers(sm5_move, sm5_copy)) return false; } + { + typedef boost::container::small_vector sm5_t; + typedef boost::container::small_vector sm7_t; + + { + //Both in static memory + sm5_t sm5; + sm5.push_back(1); + + sm7_t sm7; + sm7.push_back(2); + + sm5_t sm5_copy(sm5); + sm7_t sm7_copy(sm7); + smb_t& smb5 = sm5_copy; + smb_t& smb7 = sm7_copy; + if (!sm5.is_small() || !sm7.is_small()) + return false; + + smb5.swap(smb7); + if (!boost::container::test::CheckEqualContainers(sm5_copy, sm7)) + return false; + if (!boost::container::test::CheckEqualContainers(sm7_copy, sm5)) + return false; + + if (!sm5.is_small() || !sm7.is_small()) + return false; + + smb5.swap(smb7); + if (!boost::container::test::CheckEqualContainers(sm7_copy, sm7)) + return false; + if (!boost::container::test::CheckEqualContainers(sm5_copy, sm5)) + return false; + + if (!sm5.is_small() || !sm7.is_small()) + return false; + } + { + //Both in dynamic memory + sm5_t sm5; + for(std::size_t i = 0, max = sm5.capacity()+1; i != max; ++i){ + sm5.push_back(int(i)); + } + + sm7_t sm7; + for(std::size_t i = 0, max = sm7.capacity()+1; i != max; ++i){ + sm7.push_back(int(i)); + } + + sm5_t sm5_copy(sm5); + sm7_t sm7_copy(sm7); + smb_t& smb5 = sm5_copy; + smb_t& smb7 = sm7_copy; + + if (smb5.is_small() || smb7.is_small()) + return false; + + smb5.swap(smb7); + if (!boost::container::test::CheckEqualContainers(sm5_copy, sm7)) + return false; + if (!boost::container::test::CheckEqualContainers(sm7_copy, sm5)) + return false; + + if (smb5.is_small() || smb7.is_small()) + return false; + + smb5.swap(smb7); + if (!boost::container::test::CheckEqualContainers(sm7_copy, sm7)) + return false; + if (!boost::container::test::CheckEqualContainers(sm5_copy, sm5)) + return false; + + if (smb5.is_small() || smb7.is_small()) + return false; + } + { + //sm7 in dynamic memory + sm5_t sm5; + for(std::size_t i = 0, max = sm5.capacity()-1; i != max; ++i){ + sm5.push_back(int(i)); + } + + sm7_t sm7; + for(std::size_t i = 0, max = sm7.capacity()+1; i != max; ++i){ + sm7.push_back(int(i)); + } + + sm5_t sm5_copy(sm5); + sm7_t sm7_copy(sm7); + smb_t& smb5 = sm5_copy; + smb_t& smb7 = sm7_copy; + + if (!smb5.is_small() || smb7.is_small()) + return false; + + //As small_vector_base is capacity-erased, will make an element-wise swap, + //remaining smb7 elements won't fit in smb5's internal buffer so both will be dynamic + smb5.swap(smb7); + if (!boost::container::test::CheckEqualContainers(sm5_copy, sm7)) + return false; + if (!boost::container::test::CheckEqualContainers(sm7_copy, sm5)) + return false; + + if (smb5.is_small() || smb7.is_small()) + return false; + + //Swap them again (both dynamic) + smb5.swap(smb7); + if (!boost::container::test::CheckEqualContainers(sm7_copy, sm7)) + return false; + if (!boost::container::test::CheckEqualContainers(sm5_copy, sm5)) + return false; + + if (smb5.is_small() || smb7.is_small()) + return false; + + //Try again with one dynamic, but the reverse option + //shrink to fit should free the dynamic buffer after clear + //and both should be small again using the internal buffer + sm5_copy.clear(); + sm5_copy.shrink_to_fit(); + sm7_copy.clear(); + sm7_copy.shrink_to_fit(); + + if (!smb5.is_small() || !smb7.is_small()) + return false; + + sm5_copy = sm5; + sm7_copy = sm7; + + if (!smb5.is_small() || smb7.is_small()) + return false; + } + } return true; } @@ -116,13 +250,28 @@ bool test_swap() if(v.size() != w_size || w.size() != v_size) return false; } + { //v bigger than static capacity, w enough capacity for static + vec v; + for (std::size_t i = 0, max = v.capacity() + 1; i != max; ++i) { + v.push_back(int(i)); + } + vec w; + for (std::size_t i = 0, max = w.capacity() / 2; i != max; ++i) { + w.push_back(int(i)); + } + const std::size_t v_size = v.size(); + const std::size_t w_size = w.size(); + v.swap(w); + if (v.size() != w_size || w.size() != v_size) + return false; + } { //v & w smaller than static capacity vec v; for(std::size_t i = 0, max = v.capacity()-1; i != max; ++i){ v.push_back(int(i)); } vec w; - for(std::size_t i = 0, max = v.capacity()/2; i != max; ++i){ + for(std::size_t i = 0, max = w.capacity()/2; i != max; ++i){ w.push_back(int(i)); } const std::size_t v_size = v.size(); @@ -137,7 +286,7 @@ bool test_swap() v.push_back(int(i)); } vec w; - for(std::size_t i = 0, max = v.capacity()*2; i != max; ++i){ + for(std::size_t i = 0, max = w.capacity()*2; i != max; ++i){ w.push_back(int(i)); } const std::size_t v_size = v.size(); @@ -146,6 +295,73 @@ bool test_swap() if(v.size() != w_size || w.size() != v_size) return false; } + + //Now test internal buffer/dynamic buffer swapping + { + typedef boost::container::small_vector sm5_t; + + { + sm5_t sm5; + for (std::size_t i = 0, max = sm5.capacity() - 1; i != max; ++i) { + sm5.push_back(int(i)); + } + + sm5_t sm5_copy(sm5); + { + sm5_t sm5_dyn(sm5); + sm5_dyn.resize(sm5_dyn.capacity() + 1u); + sm5_dyn.resize(sm5.size()); + + if (sm5_dyn != sm5 || sm5_dyn.is_small()) + return false; + + //Swap derived small vector one static one dynamic + sm5_copy.swap(sm5_dyn); + + if (sm5_dyn != sm5) + return false; + + //Dynamic buffer should be transferred, the old dynamic should be small now + if (sm5_copy.is_small() || !sm5_dyn.is_small()) + return false; + + //Swap derived small vector one static one dynamic + sm5_copy.swap(sm5_dyn); + + if (sm5_dyn != sm5) + return false; + + //Dynamic buffer should be transferred, the old dynamic should be small now + if (!sm5_copy.is_small() || sm5_dyn.is_small()) + return false; + } + { + sm5_t sm5_int(sm5); + if (sm5_int != sm5 || !sm5_int.is_small()) + return false; + + //Swap derived small vector one static one dynamic + sm5_copy.swap(sm5_int); + + if (sm5_int != sm5) + return false; + + //No dynamic memory should be present as small capacity is enough + if (!sm5_copy.is_small() || !sm5_int.is_small()) + return false; + + //Swap derived small vector one static one dynamic + sm5_copy.swap(sm5_int); + + if (sm5_int != sm5) + return false; + + //No dynamic memory should be present as small capacity is enough + if (!sm5_copy.is_small() || !sm5_int.is_small()) + return false; + } + } + } return true; }