Refactor move constructor and swap implementations for small_vector as in some cass unnecesary heap allocation was performed. Add tests for this.

This commit is contained in:
Ion Gaztañaga
2024-06-02 13:08:18 +02:00
parent 4142050048
commit 9da5dc49f7
3 changed files with 426 additions and 80 deletions

View File

@ -283,7 +283,7 @@ class small_vector_allocator
{ return !(l == r); }
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
private:
public:
typedef small_vector_base<value_type, allocator_type, Options> derived_type;
typedef typename dtl::vector_for_small_vector
@ -357,6 +357,7 @@ class small_vector_base
typedef typename allocator_traits<allocator_type>::const_pointer const_pointer;
typedef typename allocator_traits<allocator_type>::void_pointer void_pointer;
typedef typename allocator_traits<allocator_type>::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<class AllocFwd>
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<AllocFwd>(a))
{}
inline explicit small_vector_base(maybe_initial_capacity_t, std::size_t initial_capacity, std::size_t initial_size)
template<class AllocFwd>
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<AllocFwd>(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<class AllocFwd>
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<small_vector_base&>(this->base_type::operator=(static_cast<base_type const&>(other))); }
@ -418,24 +435,8 @@ class small_vector_base
{ return static_cast<small_vector_base&>(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<typename base_type::allocator_type> 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_type>::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<value_type> il, const allocator_type& a = allocator_type())
@ -663,7 +664,12 @@ class small_vector
{ return static_cast<small_vector&>(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());
}
};
}}

View File

@ -51,6 +51,7 @@
#include <boost/move/iterator.hpp>
#include <boost/move/traits.hpp>
#include <boost/move/utility_core.hpp>
#include <boost/move/detail/launder.hpp>
// move/detail
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
#include <boost/move/detail/fwd_macros.hpp>
@ -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 <bool PropagateAllocator>
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 <bool PropagateAllocator>
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 @@ private:
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 <bool PropagateAllocator>
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<PropagateAllocator>(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 <bool PropagateAllocator>
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<PropagateAllocator>(l_a, l_p, r_a, r_p); }
#endif //#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
@ -866,6 +867,25 @@ private:
: m_holder(initial_capacity_t(), initial_memory, cap, ::boost::forward<AllocFwd>(a))
{}
template<class AllocFwd>
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<AllocFwd>(a))
{
allocator_type &this_al = this->get_stored_allocator();
if (this->template is_propagable_from<true>(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 @@ private:
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<false>(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<false>(x.get_stored_allocator(), x.m_holder.start(), a)){
this->m_holder.steal_resources(x.m_holder);
}
else{
@ -2549,7 +2569,7 @@ private:
//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<propagate_alloc>(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 @@ private:
template<class Vector> //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_<allocator_traits_type::propagate_on_container_swap::value> flag;
dtl::swap_alloc(this->m_holder.alloc(), x.m_holder.alloc(), flag);
}
protected:
template<class Vector> //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<propagate_alloc>
( 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<propagate_alloc>(th_al, this->data(), ot_al);
const bool is_that_data_propagable = is_propagable_from<propagate_alloc>(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_<propagate_alloc>());
}
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);
//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]);
}
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());
}
}
//And now swap the allocator
dtl::swap_alloc(this->m_holder.alloc(), x.m_holder.alloc(), dtl::bool_<propagate_alloc>());
}
//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_<propagate_alloc>());
//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);
}
}
private:
inline void priv_move_to_new_buffer(size_type, version_0)
{ alloc_holder_t::on_capacity_overflow(); }
@ -2780,6 +2869,41 @@ private:
inline dtl::insert_value_initialized_n_proxy<allocator_type> priv_resize_proxy(value_init_t)
{ return dtl::insert_value_initialized_n_proxy<allocator_type>(); }
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
{}

View File

@ -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<int, 5> sm5_t;
typedef boost::container::small_vector<int, 7> 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<int, 5> 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;
}