Updated position insertion code.

Fixed backwards expansion calling too many destructors nd added more tests to detect similar errors.
This commit is contained in:
Ion Gaztañaga
2014-09-17 21:52:34 +02:00
parent 0ccf576872
commit 5313086d54
2 changed files with 231 additions and 54 deletions

View File

@@ -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,22 +1724,142 @@ 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));
}
//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)
{
this->priv_insert_ordered_at(element_count, last_position_it, true, last_skip_it, last_value_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;
}
}
#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)
{
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:
@@ -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);

View File

@@ -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,20 +187,26 @@ bool test_insert_with_expand_bwd()
for(int iteration = 0; iteration < Iterations; ++iteration)
{
value_type *memory = new value_type[MemorySize];
BOOST_TRY {
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;
}
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);
@@ -155,6 +214,10 @@ bool test_insert_with_expand_bwd()
, 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
@@ -165,13 +228,6 @@ bool test_insert_with_expand_bwd()
return false;
}
}
BOOST_CATCH(...){
delete [](const_cast<non_volatile_value_type*>(memory));
BOOST_RETHROW;
}
BOOST_CATCH_END
delete [](const_cast<non_volatile_value_type*>(memory));
}
return true;
}