forked from boostorg/container
Updated position insertion code.
Fixed backwards expansion calling too many destructors nd added more tests to detect similar errors.
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <boost/detail/no_exceptions_support.hpp>
|
||||
#include <boost/core/no_exceptions_support.hpp>
|
||||
#include <boost/type_traits/has_trivial_destructor.hpp>
|
||||
#include <boost/type_traits/has_trivial_copy.hpp>
|
||||
#include <boost/type_traits/has_trivial_assign.hpp>
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <boost/move/utility.hpp>
|
||||
#include <boost/move/iterator.hpp>
|
||||
#include <boost/move/detail/move_helpers.hpp>
|
||||
#include <boost/move/traits.hpp>
|
||||
#include <boost/intrusive/pointer_traits.hpp>
|
||||
#include <boost/container/detail/mpl.hpp>
|
||||
#include <boost/container/detail/type_traits.hpp>
|
||||
@@ -1376,8 +1377,8 @@ class vector
|
||||
template<class ...Args>
|
||||
void emplace_back(Args &&...args)
|
||||
{
|
||||
T* back_pos = container_detail::to_raw_pointer(this->m_holder.start()) + this->m_holder.m_size;
|
||||
if (this->m_holder.m_size < this->m_holder.capacity()){
|
||||
T* const back_pos = container_detail::to_raw_pointer(this->m_holder.start()) + this->m_holder.m_size;
|
||||
//There is more memory, just construct a new object at the end
|
||||
allocator_traits_type::construct(this->m_holder.alloc(), back_pos, ::boost::forward<Args>(args)...);
|
||||
++this->m_holder.m_size;
|
||||
@@ -1414,7 +1415,7 @@ class vector
|
||||
BOOST_PP_EXPR_IF(n, template<) BOOST_PP_ENUM_PARAMS(n, class P) BOOST_PP_EXPR_IF(n, >) \
|
||||
void emplace_back(BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_LIST, _)) \
|
||||
{ \
|
||||
T* back_pos = container_detail::to_raw_pointer \
|
||||
T* const back_pos = container_detail::to_raw_pointer \
|
||||
(this->m_holder.start()) + this->m_holder.m_size; \
|
||||
if (this->m_holder.m_size < this->m_holder.capacity()){ \
|
||||
allocator_traits_type::construct (this->m_holder.alloc() \
|
||||
@@ -1723,23 +1724,143 @@ class vector
|
||||
{ x.swap(y); }
|
||||
|
||||
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
|
||||
//! <b>Effects</b>: If n is less than or equal to capacity(), this call has no
|
||||
//! effect. Otherwise, it is a request for allocation of additional memory
|
||||
//! (memory expansion) that will not invalidate iterators.
|
||||
//! If the request is successful, then capacity() is greater than or equal to
|
||||
//! n; otherwise, capacity() is unchanged. In either case, size() is unchanged.
|
||||
//!
|
||||
//! <b>Throws</b>: If memory allocation allocation throws or T's copy/move constructor throws.
|
||||
//!
|
||||
//! <b>Note</b>: Non-standard extension.
|
||||
bool stable_reserve(size_type new_cap)
|
||||
{
|
||||
const bool room_enough = this->capacity() < new_cap;
|
||||
if(!room_enough && alloc_version::value < 2){
|
||||
return false;
|
||||
}
|
||||
else{
|
||||
//There is not enough memory, try to expand the old one
|
||||
size_type real_cap = 0;
|
||||
std::pair<pointer, bool> ret = this->m_holder.allocation_command
|
||||
(expand_fwd, new_cap, new_cap, real_cap, this->m_holder.start());
|
||||
//Check for forward expansion
|
||||
if(ret.second){
|
||||
#ifdef BOOST_CONTAINER_VECTOR_ALLOC_STATS
|
||||
++this->num_expand_fwd;
|
||||
#endif
|
||||
this->m_holder.capacity(real_cap);
|
||||
}
|
||||
return ret.second;
|
||||
}
|
||||
}
|
||||
|
||||
//Absolutely experimental. This function might change, disappear or simply crash!
|
||||
template<class BiDirPosConstIt, class BiDirValueIt>
|
||||
void insert_ordered_at(size_type element_count, BiDirPosConstIt last_position_it, BiDirValueIt last_value_it)
|
||||
void insert_ordered_at(const size_type element_count, BiDirPosConstIt last_position_it, BiDirValueIt last_value_it)
|
||||
{
|
||||
const size_type *dummy = 0;
|
||||
this->priv_insert_ordered_at(element_count, last_position_it, false, dummy, last_value_it);
|
||||
const size_type old_size_pos = this->size();
|
||||
this->reserve(old_size_pos + element_count);
|
||||
T* const begin_ptr = container_detail::to_raw_pointer(this->m_holder.start());
|
||||
size_type insertions_left = element_count;
|
||||
size_type next_pos = old_size_pos;
|
||||
size_type hole_size = element_count;
|
||||
|
||||
//Exception rollback. If any copy throws before the hole is filled, values
|
||||
//already inserted/copied at the end of the buffer will be destroyed.
|
||||
typename value_traits::ArrayDestructor past_hole_values_destroyer
|
||||
(begin_ptr + old_size_pos + element_count, this->m_holder.alloc(), size_type(0u));
|
||||
//Loop for each insertion backwards, first moving the elements after the insertion point,
|
||||
//then inserting the element.
|
||||
while(insertions_left){
|
||||
size_type pos = static_cast<size_type>(*(--last_position_it));
|
||||
while(pos == size_type(-1)){
|
||||
--last_value_it;
|
||||
pos = static_cast<size_type>(*(--last_position_it));
|
||||
}
|
||||
|
||||
BOOST_ASSERT(pos != size_type(-1) && pos <= old_size_pos);
|
||||
//If needed shift the range after the insertion point and the previous insertion point.
|
||||
//Function will take care if the shift crosses the size() boundary, using copy/move
|
||||
//or uninitialized copy/move if necessary.
|
||||
size_type new_hole_size = (pos != next_pos)
|
||||
? priv_insert_ordered_at_shift_range(pos, next_pos, this->size(), insertions_left)
|
||||
: hole_size
|
||||
;
|
||||
if(new_hole_size > 0){
|
||||
//The hole was reduced by priv_insert_ordered_at_shift_range so expand exception rollback range backwards
|
||||
past_hole_values_destroyer.increment_size_backwards(next_pos - pos);
|
||||
//Insert the new value in the hole
|
||||
allocator_traits_type::construct(this->m_holder.alloc(), begin_ptr + pos + insertions_left - 1, *(--last_value_it));
|
||||
--new_hole_size;
|
||||
if(new_hole_size == 0){
|
||||
//Hole was just filled, disable exception rollback and change vector size
|
||||
past_hole_values_destroyer.release();
|
||||
this->m_holder.m_size += element_count;
|
||||
}
|
||||
else{
|
||||
//The hole was reduced by the new insertion by one
|
||||
past_hole_values_destroyer.increment_size_backwards(size_type(1u));
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(hole_size){
|
||||
//Hole was just filled by priv_insert_ordered_at_shift_range, disable exception rollback and change vector size
|
||||
past_hole_values_destroyer.release();
|
||||
this->m_holder.m_size += element_count;
|
||||
}
|
||||
//Insert the new value in the already constructed range
|
||||
begin_ptr[pos + insertions_left - 1] = *(--last_value_it);
|
||||
}
|
||||
--insertions_left;
|
||||
hole_size = new_hole_size;
|
||||
next_pos = pos;
|
||||
}
|
||||
}
|
||||
|
||||
//Absolutely experimental. This function might change, disappear or simply crash!
|
||||
template<class BiDirPosConstIt, class BiDirSkipConstIt, class BiDirValueIt>
|
||||
void insert_ordered_at( size_type element_count, BiDirPosConstIt last_position_it
|
||||
, BiDirSkipConstIt last_skip_it, BiDirValueIt last_value_it)
|
||||
#if defined(BOOST_CONTAINER_PERFECT_FORWARDING) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
|
||||
//! <b>Effects</b>: Inserts an object of type T constructed with
|
||||
//! std::forward<Args>(args)... in the end of the vector.
|
||||
//!
|
||||
//! <b>Throws</b>: If memory allocation throws or the in-place constructor throws or
|
||||
//! T's copy/move constructor throws.
|
||||
//!
|
||||
//! <b>Complexity</b>: Amortized constant time.
|
||||
template<class ...Args>
|
||||
bool stable_emplace_back(Args &&...args)
|
||||
{
|
||||
this->priv_insert_ordered_at(element_count, last_position_it, true, last_skip_it, last_value_it);
|
||||
const bool room_enough = this->m_holder.m_size < this->m_holder.capacity();
|
||||
if (room_enough){
|
||||
T* const back_pos = container_detail::to_raw_pointer(this->m_holder.start()) + this->m_holder.m_size;
|
||||
//There is more memory, just construct a new object at the end
|
||||
allocator_traits_type::construct(this->m_holder.alloc(), back_pos, ::boost::forward<Args>(args)...);
|
||||
++this->m_holder.m_size;
|
||||
}
|
||||
return room_enough;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define BOOST_PP_LOCAL_MACRO(n) \
|
||||
BOOST_PP_EXPR_IF(n, template<) BOOST_PP_ENUM_PARAMS(n, class P) BOOST_PP_EXPR_IF(n, >) \
|
||||
bool stable_emplace_back(BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_LIST, _)) \
|
||||
{ \
|
||||
const bool room_enough = this->m_holder.m_size < this->m_holder.capacity(); \
|
||||
if (room_enough){ \
|
||||
T* const back_pos = container_detail::to_raw_pointer \
|
||||
(this->m_holder.start()) + this->m_holder.m_size; \
|
||||
allocator_traits_type::construct (this->m_holder.alloc() \
|
||||
, back_pos BOOST_PP_ENUM_TRAILING(n, BOOST_CONTAINER_PP_PARAM_FORWARD, _) ); \
|
||||
++this->m_holder.m_size; \
|
||||
} \
|
||||
return room_enough; \
|
||||
} \
|
||||
//!
|
||||
#define BOOST_PP_LOCAL_LIMITS (0, BOOST_CONTAINER_MAX_CONSTRUCTOR_PARAMETERS)
|
||||
#include BOOST_PP_LOCAL_ITERATE()
|
||||
|
||||
#endif //#ifdef BOOST_CONTAINER_PERFECT_FORWARDING
|
||||
|
||||
private:
|
||||
|
||||
template<class OtherAllocator>
|
||||
@@ -2506,7 +2627,7 @@ class vector
|
||||
(this->m_holder.alloc(), pos, raw_gap, new_start + before_plus_new);
|
||||
//Now we have a contiguous buffer so program trailing element destruction
|
||||
//and update size to the final size.
|
||||
old_values_destroyer.shrink_forward(elemsbefore + raw_gap);
|
||||
old_values_destroyer.shrink_forward(new_size-s_before);
|
||||
this->m_holder.m_size = new_size;
|
||||
//Now move remaining last objects in the old buffer begin
|
||||
::boost::move(pos + raw_gap, old_finish, old_start);
|
||||
|
@@ -17,40 +17,71 @@
|
||||
#include "expand_bwd_test_allocator.hpp"
|
||||
#include <algorithm>
|
||||
#include <boost/type_traits/remove_volatile.hpp>
|
||||
#include <boost/move/make_unique.hpp>
|
||||
|
||||
namespace boost { namespace container { namespace test {
|
||||
|
||||
template<class T>
|
||||
struct value_holder
|
||||
{
|
||||
value_holder(T val) : m_value(val){}
|
||||
value_holder(): m_value(0){}
|
||||
~value_holder(){ m_value = 0; }
|
||||
explicit value_holder(T val) : m_value(val)
|
||||
{ ++count; }
|
||||
|
||||
value_holder(): m_value(0)
|
||||
{ ++count; }
|
||||
|
||||
value_holder(const value_holder &other)
|
||||
: m_value(other.m_value)
|
||||
{ ++count; }
|
||||
|
||||
value_holder &operator=(const value_holder&other)
|
||||
{ m_value = other.m_value; return *this; }
|
||||
|
||||
value_holder &operator=(T val)
|
||||
{ m_value = val; return *this; }
|
||||
|
||||
~value_holder()
|
||||
{ m_value = 0; --count; }
|
||||
|
||||
bool operator == (const value_holder &other) const
|
||||
{ return m_value == other.m_value; }
|
||||
bool operator != (const value_holder &other) const
|
||||
{ return m_value != other.m_value; }
|
||||
|
||||
T m_value;
|
||||
static unsigned count;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
unsigned value_holder<T>::count = 0;
|
||||
|
||||
template<class T>
|
||||
struct triple_value_holder
|
||||
{
|
||||
triple_value_holder(T val)
|
||||
explicit triple_value_holder(T val)
|
||||
: m_value1(val)
|
||||
, m_value2(val)
|
||||
, m_value3(val)
|
||||
{}
|
||||
{ ++count; }
|
||||
|
||||
triple_value_holder()
|
||||
: m_value1(0)
|
||||
, m_value2(0)
|
||||
, m_value3(0)
|
||||
{}
|
||||
{ ++count; }
|
||||
|
||||
triple_value_holder(const triple_value_holder &other)
|
||||
: m_value1(other.m_value1), m_value2(other.m_value2), m_value3(other.m_value3)
|
||||
{ ++count; }
|
||||
|
||||
triple_value_holder &operator=(const triple_value_holder &other)
|
||||
{ m_value1 = other.m_value1; m_value2 = other.m_value2; m_value3 = other.m_value3; ; return *this; }
|
||||
|
||||
triple_value_holder &operator=(T val)
|
||||
{ m_value1 = val; m_value2 = val; m_value3 = val; ; return *this; }
|
||||
|
||||
~triple_value_holder()
|
||||
{ m_value1 = m_value2 = m_value3 = 0; }
|
||||
{ m_value1 = m_value2 = m_value3 = 0; --count; }
|
||||
|
||||
bool operator == (const triple_value_holder &other) const
|
||||
{
|
||||
@@ -69,12 +100,34 @@ struct triple_value_holder
|
||||
T m_value1;
|
||||
T m_value2;
|
||||
T m_value3;
|
||||
static unsigned count;
|
||||
};
|
||||
|
||||
template<class T>
|
||||
unsigned triple_value_holder<T>::count = 0;
|
||||
|
||||
typedef value_holder<int> int_holder;
|
||||
typedef triple_value_holder<int> triple_int_holder;
|
||||
|
||||
template<class T>
|
||||
struct life_count
|
||||
{
|
||||
static unsigned check(unsigned) { return true; }
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct life_count< triple_value_holder<T> >
|
||||
{
|
||||
static unsigned check(unsigned c)
|
||||
{ return c == triple_value_holder<T>::count; }
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct life_count< value_holder<T> >
|
||||
{
|
||||
static unsigned check(unsigned c)
|
||||
{ return c == value_holder<T>::count; }
|
||||
};
|
||||
|
||||
//Function to check if both sets are equal
|
||||
template <class Vector1, class Vector2>
|
||||
@@ -134,43 +187,46 @@ bool test_insert_with_expand_bwd()
|
||||
|
||||
for(int iteration = 0; iteration < Iterations; ++iteration)
|
||||
{
|
||||
value_type *memory = new value_type[MemorySize];
|
||||
BOOST_TRY {
|
||||
std::vector<non_volatile_value_type> initial_data;
|
||||
initial_data.resize(InitialSize[iteration]);
|
||||
for(int i = 0; i < InitialSize[iteration]; ++i){
|
||||
initial_data[i] = i;
|
||||
}
|
||||
|
||||
Vect data_to_insert;
|
||||
data_to_insert.resize(InsertSize[iteration]);
|
||||
for(int i = 0; i < InsertSize[iteration]; ++i){
|
||||
data_to_insert[i] = -i;
|
||||
}
|
||||
|
||||
expand_bwd_test_allocator<value_type> alloc
|
||||
(&memory[0], MemorySize, Offset[iteration]);
|
||||
VectorWithExpandBwdAllocator vector(alloc);
|
||||
vector.insert( vector.begin()
|
||||
, initial_data.begin(), initial_data.end());
|
||||
vector.insert( vector.begin() + Position[iteration]
|
||||
, data_to_insert.begin(), data_to_insert.end());
|
||||
initial_data.insert(initial_data.begin() + Position[iteration]
|
||||
, data_to_insert.begin(), data_to_insert.end());
|
||||
//Now check that values are equal
|
||||
if(!CheckEqualVector(vector, initial_data)){
|
||||
std::cout << "test_assign_with_expand_bwd::CheckEqualVector failed." << std::endl
|
||||
<< " Class: " << typeid(VectorWithExpandBwdAllocator).name() << std::endl
|
||||
<< " Iteration: " << iteration << std::endl;
|
||||
return false;
|
||||
}
|
||||
boost::movelib::unique_ptr<char[]> memptr =
|
||||
boost::movelib::make_unique_definit<char[]>(MemorySize*sizeof(value_type));
|
||||
value_type *memory = (value_type*)memptr.get();
|
||||
std::vector<non_volatile_value_type> initial_data;
|
||||
initial_data.resize(InitialSize[iteration]);
|
||||
for(int i = 0; i < InitialSize[iteration]; ++i){
|
||||
initial_data[i] = i;
|
||||
}
|
||||
BOOST_CATCH(...){
|
||||
delete [](const_cast<non_volatile_value_type*>(memory));
|
||||
BOOST_RETHROW;
|
||||
|
||||
if(!life_count<value_type>::check(InitialSize[iteration]))
|
||||
return false;
|
||||
Vect data_to_insert;
|
||||
data_to_insert.resize(InsertSize[iteration]);
|
||||
for(int i = 0; i < InsertSize[iteration]; ++i){
|
||||
data_to_insert[i] = -i;
|
||||
}
|
||||
|
||||
if(!life_count<value_type>::check(InitialSize[iteration]+InsertSize[iteration]))
|
||||
return false;
|
||||
|
||||
expand_bwd_test_allocator<value_type> alloc
|
||||
(&memory[0], MemorySize, Offset[iteration]);
|
||||
VectorWithExpandBwdAllocator vector(alloc);
|
||||
vector.insert( vector.begin()
|
||||
, initial_data.begin(), initial_data.end());
|
||||
vector.insert( vector.begin() + Position[iteration]
|
||||
, data_to_insert.begin(), data_to_insert.end());
|
||||
|
||||
if(!life_count<value_type>::check(InitialSize[iteration]*2+InsertSize[iteration]*2))
|
||||
return false;
|
||||
|
||||
initial_data.insert(initial_data.begin() + Position[iteration]
|
||||
, data_to_insert.begin(), data_to_insert.end());
|
||||
//Now check that values are equal
|
||||
if(!CheckEqualVector(vector, initial_data)){
|
||||
std::cout << "test_assign_with_expand_bwd::CheckEqualVector failed." << std::endl
|
||||
<< " Class: " << typeid(VectorWithExpandBwdAllocator).name() << std::endl
|
||||
<< " Iteration: " << iteration << std::endl;
|
||||
return false;
|
||||
}
|
||||
BOOST_CATCH_END
|
||||
delete [](const_cast<non_volatile_value_type*>(memory));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
Reference in New Issue
Block a user