Added new experimental merge functions to speed up flat_xxx range insertion

This commit is contained in:
Ion Gaztañaga
2015-04-14 15:22:06 +02:00
parent 6477543f3b
commit 3246e11ca2
2 changed files with 334 additions and 209 deletions

View File

@@ -470,20 +470,22 @@ class flat_tree
template <class BidirIt> template <class BidirIt>
void insert_equal(ordered_range_t, BidirIt first, BidirIt last void insert_equal(ordered_range_t, BidirIt first, BidirIt last
#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
, typename container_detail::enable_if_c , typename container_detail::disable_if_or
< !container_detail::is_input_iterator<BidirIt>::value && < void
!container_detail::is_forward_iterator<BidirIt>::value , container_detail::is_input_iterator<BidirIt>
, container_detail::is_forward_iterator<BidirIt>
>::type * = 0 >::type * = 0
#endif #endif
) )
{ this->priv_insert_ordered_range(false, first, last); } { this->m_data.m_vect.merge(first, last); }
template <class InIt> template <class InIt>
void insert_unique(ordered_unique_range_t, InIt first, InIt last void insert_unique(ordered_unique_range_t, InIt first, InIt last
#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
, typename container_detail::enable_if_c , typename container_detail::enable_if_or
< container_detail::is_input_iterator<InIt>::value || < void
container_detail::is_forward_iterator<InIt>::value , container_detail::is_input_iterator<InIt>
, container_detail::is_forward_iterator<InIt>
>::type * = 0 >::type * = 0
#endif #endif
) )
@@ -504,7 +506,7 @@ class flat_tree
>::type * = 0 >::type * = 0
#endif #endif
) )
{ this->priv_insert_ordered_range(true, first, last); } { this->m_data.m_vect.merge_unique(first, last); }
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
@@ -855,8 +857,8 @@ class flat_tree
} }
template <class RanIt> template <class RanIt>
RanIt priv_upper_bound(RanIt first, const RanIt last, RanIt priv_upper_bound
const key_type & key) const (RanIt first, const RanIt last,const key_type & key) const
{ {
const Compare &key_cmp = this->m_data.get_comp(); const Compare &key_cmp = this->m_data.get_comp();
KeyOfValue key_extract; KeyOfValue key_extract;
@@ -904,9 +906,9 @@ class flat_tree
//Middle is equal to key //Middle is equal to key
last = first; last = first;
last += len; last += len;
RanIt const first_ret = this->priv_lower_bound(first, middle, key);
return std::pair<RanIt, RanIt> return std::pair<RanIt, RanIt>
( this->priv_lower_bound(first, middle, key) ( first_ret, this->priv_upper_bound(++middle, last, key));
, this->priv_upper_bound(++middle, last, key));
} }
} }
return std::pair<RanIt, RanIt>(first, first); return std::pair<RanIt, RanIt>(first, first);
@@ -939,81 +941,11 @@ class flat_tree
for ( ; first != last; ++first){ for ( ; first != last; ++first){
//If ordered, then try hint version //If ordered, then try hint version
//to achieve constant-time complexity per insertion //to achieve constant-time complexity per insertion
//in some cases
pos = this->insert_equal(pos, *first); pos = this->insert_equal(pos, *first);
++pos; ++pos;
} }
} }
template <class BidirIt>
void priv_insert_ordered_range(const bool unique_values, BidirIt first, BidirIt last)
{
size_type len = static_cast<size_type>(boost::container::iterator_distance(first, last));
//Prereserve all memory so that iterators are not invalidated
this->reserve(this->size()+len);
//Auxiliary data for insertion positions.
const size_type BurstSize = len;
const ::boost::movelib::unique_ptr<size_type[]> positions =
::boost::movelib::make_unique_definit<size_type[]>(BurstSize);
const const_iterator b(this->cbegin());
const const_iterator ce(this->cend());
const_iterator pos(b);
const value_compare &val_cmp = this->m_data;
//Loop in burst sizes
bool back_insert = false;
while(len && !back_insert){
const size_type burst = len < BurstSize ? len : BurstSize;
size_type unique_burst = 0u;
size_type checked = 0;
for(; checked != burst; ++checked){
//Get the insertion position for each key, use iterator_traits<BidirIt>::value_type
//because it can be different from container::value_type
//(e.g. conversion between std::pair<T1, T2> -> boost::container::pair<T1, T2>
const typename boost::container::iterator_traits<BidirIt>::value_type & val = *first;
pos = const_cast<const flat_tree&>(*this).priv_lower_bound(pos, ce, KeyOfValue()(val));
//Check if already present
if (pos != ce){
++first;
--len;
positions[checked] = (unique_values && !val_cmp(val, *pos)) ?
size_type(-1) : (++unique_burst, static_cast<size_type>(pos - b));
}
else{ //this element and the remaining should be back inserted
back_insert = true;
break;
}
}
if(unique_burst){
//Insert all in a single step in the precalculated positions
this->m_data.m_vect.insert_ordered_at(unique_burst, positions.get() + checked, first);
//Next search position updated, iterator still valid because we've preserved the vector
pos += unique_burst;
}
}
//The remaining range should be back inserted
if(unique_values){
while(len--){
BidirIt next(first);
++next;
//Use iterator_traits<BidirIt>::value_type
//because it can be different from container::value_type
//(e.g. conversion between std::pair<T1, T2> -> boost::container::pair<T1, T2>
const typename boost::container::iterator_traits<BidirIt>::value_type & val = *first;
if (next == last || val_cmp(val, *next)){
const bool room = this->m_data.m_vect.stable_emplace_back(*first);
(void)room;
BOOST_ASSERT(room);
}
first = next;
}
BOOST_ASSERT(first == last);
}
else{
BOOST_ASSERT(size_type(boost::container::iterator_distance(first, last)) == len);
if(len)
this->m_data.m_vect.insert(this->m_data.m_vect.cend(), len, first, last);
}
}
}; };
} //namespace container_detail { } //namespace container_detail {

View File

@@ -57,6 +57,7 @@
// other // other
#include <boost/core/no_exceptions_support.hpp> #include <boost/core/no_exceptions_support.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/cstdint.hpp>
//std //std
#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST)
@@ -179,6 +180,70 @@ class vec_iterator
{ return l.m_ptr >= r.m_ptr; } { return l.m_ptr >= r.m_ptr; }
}; };
template<class BiDirPosConstIt, class BiDirValueIt>
struct vector_insert_ordered_cursor
{
typedef typename iterator_traits<BiDirPosConstIt>::value_type size_type;
typedef typename iterator_traits<BiDirValueIt>::reference reference;
vector_insert_ordered_cursor(BiDirPosConstIt posit, BiDirValueIt valueit)
: last_position_it(posit), last_value_it(valueit)
{}
void operator --()
{
--last_value_it;
--last_position_it;
while(this->get_pos() == size_type(-1)){
--last_value_it;
--last_position_it;
}
}
size_type get_pos() const
{ return *last_position_it; }
reference get_val()
{ return *last_value_it; }
BiDirPosConstIt last_position_it;
BiDirValueIt last_value_it;
};
template<class T, class SizeType, class BiDirValueIt, class Comp>
struct vector_merge_cursor
{
typedef SizeType size_type;
typedef typename iterator_traits<BiDirValueIt>::reference reference;
vector_merge_cursor(T *pbeg, T *plast, BiDirValueIt valueit, Comp cmp)
: m_pbeg(pbeg), m_pcur(--plast), m_valueit(valueit), m_cmp(cmp)
{}
void operator --()
{
--m_valueit;
const T &t = *m_valueit;
while((m_pcur + 1) != m_pbeg){
if(!m_cmp(t, *m_pcur)){
break;
}
--m_pcur;
}
}
size_type get_pos() const
{ return static_cast<size_type>((m_pcur + 1) - m_pbeg); }
reference get_val()
{ return *m_valueit; }
T *const m_pbeg;
T *m_pcur;
BiDirValueIt m_valueit;
Comp m_cmp;
};
} //namespace container_detail { } //namespace container_detail {
template<class Pointer, bool IsConst> template<class Pointer, bool IsConst>
@@ -640,6 +705,13 @@ class vector
{ {
#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED
struct value_less
{
typedef typename boost::container::allocator_traits<Allocator>::value_type value_type;
bool operator()(const value_type &a, const value_type &b) const
{ return a < b; }
};
typedef typename container_detail::version<Allocator>::type alloc_version; typedef typename container_detail::version<Allocator>::type alloc_version;
typedef boost::container::container_detail::vector_alloc_holder<Allocator> alloc_holder_t; typedef boost::container::container_detail::vector_alloc_holder<Allocator> alloc_holder_t;
alloc_holder_t m_holder; alloc_holder_t m_holder;
@@ -1043,10 +1115,11 @@ class vector
//! //!
//! <b>Note</b>: Non-standard extension to support static_vector //! <b>Note</b>: Non-standard extension to support static_vector
template<class OtherAllocator> template<class OtherAllocator>
typename container_detail::enable_if_c typename container_detail::enable_if_and
< container_detail::is_version<OtherAllocator, 0>::value && < vector&
!container_detail::is_same<OtherAllocator, allocator_type>::value , container_detail::is_version<OtherAllocator, 0>
, vector& >::type , container_detail::is_different<OtherAllocator, allocator_type>
>::type
operator=(BOOST_RV_REF_BEG vector<value_type, OtherAllocator> BOOST_RV_REF_END x) operator=(BOOST_RV_REF_BEG vector<value_type, OtherAllocator> BOOST_RV_REF_END x)
{ {
this->priv_move_assign(boost::move(x)); this->priv_move_assign(boost::move(x));
@@ -1064,10 +1137,11 @@ class vector
//! //!
//! <b>Note</b>: Non-standard extension to support static_vector //! <b>Note</b>: Non-standard extension to support static_vector
template<class OtherAllocator> template<class OtherAllocator>
typename container_detail::enable_if_c typename container_detail::enable_if_and
< container_detail::is_version<OtherAllocator, 0>::value && < vector&
!container_detail::is_same<OtherAllocator, allocator_type>::value , container_detail::is_version<OtherAllocator, 0>
, vector& >::type , container_detail::is_different<OtherAllocator, allocator_type>
>::type
operator=(const vector<value_type, OtherAllocator> &x) operator=(const vector<value_type, OtherAllocator> &x)
{ {
this->priv_copy_assign(x); this->priv_copy_assign(x);
@@ -1084,11 +1158,15 @@ class vector
//! <b>Complexity</b>: Linear to n. //! <b>Complexity</b>: Linear to n.
template <class InIt> template <class InIt>
void assign(InIt first, InIt last void assign(InIt first, InIt last
BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::enable_if_c BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::disable_if_or
< !container_detail::is_convertible<InIt BOOST_MOVE_I size_type>::value && < void
( container_detail::is_input_iterator<InIt>::value || BOOST_MOVE_I container_detail::is_convertible<InIt BOOST_MOVE_I size_type>
container_detail::is_same<alloc_version BOOST_MOVE_I version_0>::value ) BOOST_MOVE_I container_detail::and_
>::type * = 0) ) < container_detail::is_different<alloc_version BOOST_MOVE_I version_0>
BOOST_MOVE_I container_detail::is_not_input_iterator<InIt>
>
>::type * = 0)
)
{ {
//Overwrite all elements we can from [first, last) //Overwrite all elements we can from [first, last)
iterator cur = this->begin(); iterator cur = this->begin();
@@ -1129,10 +1207,11 @@ class vector
//! <b>Complexity</b>: Linear to n. //! <b>Complexity</b>: Linear to n.
template <class FwdIt> template <class FwdIt>
void assign(FwdIt first, FwdIt last void assign(FwdIt first, FwdIt last
BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::enable_if_c BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::disable_if_or
< !container_detail::is_convertible<FwdIt BOOST_MOVE_I size_type>::value && < void
( !container_detail::is_input_iterator<FwdIt>::value && BOOST_MOVE_I container_detail::is_same<alloc_version BOOST_MOVE_I version_0>
!container_detail::is_same<alloc_version BOOST_MOVE_I version_0>::value ) BOOST_MOVE_I container_detail::is_convertible<FwdIt BOOST_MOVE_I size_type>
BOOST_MOVE_I container_detail::is_input_iterator<FwdIt>
>::type * = 0) >::type * = 0)
) )
{ {
@@ -1791,10 +1870,13 @@ class vector
//! <b>Complexity</b>: Linear to boost::container::iterator_distance [first, last). //! <b>Complexity</b>: Linear to boost::container::iterator_distance [first, last).
template <class InIt> template <class InIt>
iterator insert(const_iterator pos, InIt first, InIt last iterator insert(const_iterator pos, InIt first, InIt last
BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::enable_if_c #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
< !container_detail::is_convertible<InIt BOOST_MOVE_I size_type>::value , typename container_detail::disable_if_or
&& container_detail::is_input_iterator<InIt>::value < void
>::type * = 0) , container_detail::is_convertible<InIt, size_type>
, container_detail::is_not_input_iterator<InIt>
>::type * = 0
#endif
) )
{ {
const size_type n_pos = pos - this->cbegin(); const size_type n_pos = pos - this->cbegin();
@@ -1809,9 +1891,10 @@ class vector
#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED)
template <class FwdIt> template <class FwdIt>
iterator insert(const_iterator pos, FwdIt first, FwdIt last iterator insert(const_iterator pos, FwdIt first, FwdIt last
, typename container_detail::enable_if_c , typename container_detail::disable_if_or
< !container_detail::is_convertible<FwdIt, size_type>::value < void
&& !container_detail::is_input_iterator<FwdIt>::value , container_detail::is_convertible<FwdIt, size_type>
, container_detail::is_input_iterator<FwdIt>
>::type * = 0 >::type * = 0
) )
{ {
@@ -1931,9 +2014,11 @@ class vector
//! <b>Note</b>: Non-standard extension to support static_vector //! <b>Note</b>: Non-standard extension to support static_vector
template<class OtherAllocator> template<class OtherAllocator>
void swap(vector<T, OtherAllocator> & x void swap(vector<T, OtherAllocator> & x
, typename container_detail::enable_if_c , typename container_detail::enable_if_and
< container_detail::is_version<OtherAllocator, 0>::value && < void
!container_detail::is_same<OtherAllocator, allocator_type>::value >::type * = 0 , container_detail::is_version<OtherAllocator, 0>
, container_detail::is_different<OtherAllocator, allocator_type>
>::type * = 0
) )
{ this->m_holder.deep_swap(x.m_holder); } { this->m_holder.deep_swap(x.m_holder); }
@@ -2016,13 +2101,37 @@ class vector
//Absolutely experimental. This function might change, disappear or simply crash! //Absolutely experimental. This function might change, disappear or simply crash!
template<class BiDirPosConstIt, class BiDirValueIt> template<class BiDirPosConstIt, class BiDirValueIt>
void insert_ordered_at(const 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)
{
typedef container_detail::vector_insert_ordered_cursor<BiDirPosConstIt, BiDirValueIt> inserter_t;
return this->priv_insert_ordered_at(element_count, inserter_t(last_position_it, last_value_it));
}
template<class BidirIt>
void merge(BidirIt first, BidirIt last)
{ this->merge(first, last, value_less()); }
template<class BidirIt, class Compare>
void merge(BidirIt first, BidirIt last, Compare comp)
{ this->priv_merge(container_detail::false_type(), first, last, comp); }
template<class BidirIt>
void merge_unique(BidirIt first, BidirIt last)
{ this->priv_merge(container_detail::true_type(), first, last, value_less()); }
template<class BidirIt, class Compare>
void merge_unique(BidirIt first, BidirIt last, Compare comp)
{ this->priv_merge(container_detail::true_type(), first, last, comp); }
private:
template<class PositionValue>
void priv_insert_ordered_at(const size_type element_count, PositionValue position_value)
{ {
const size_type old_size_pos = this->size(); const size_type old_size_pos = this->size();
this->reserve(old_size_pos + element_count); this->reserve(old_size_pos + element_count);
T* const begin_ptr = container_detail::to_raw_pointer(this->m_holder.start()); T* const begin_ptr = container_detail::to_raw_pointer(this->m_holder.start());
size_type insertions_left = element_count; size_type insertions_left = element_count;
size_type next_pos = old_size_pos; size_type prev_pos = old_size_pos;
size_type hole_size = element_count; size_type old_hole_size = element_count;
//Exception rollback. If any copy throws before the hole is filled, values //Exception rollback. If any copy throws before the hole is filled, values
//already inserted/copied at the end of the buffer will be destroyed. //already inserted/copied at the end of the buffer will be destroyed.
@@ -2031,52 +2140,195 @@ class vector
//Loop for each insertion backwards, first moving the elements after the insertion point, //Loop for each insertion backwards, first moving the elements after the insertion point,
//then inserting the element. //then inserting the element.
while(insertions_left){ while(insertions_left){
size_type pos = static_cast<size_type>(*(--last_position_it)); --position_value;
while(pos == size_type(-1)){ size_type const pos = position_value.get_pos();
--last_value_it; BOOST_ASSERT(pos != size_type(-1) && pos <= old_size_pos && pos <= prev_pos);
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. //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 //Function will take care if the shift crosses the size() boundary, using copy/move
//or uninitialized copy/move if necessary. //or uninitialized copy/move if necessary.
size_type new_hole_size = (pos != next_pos) size_type new_hole_size = (pos != prev_pos)
? priv_insert_ordered_at_shift_range(pos, next_pos, this->size(), insertions_left) ? priv_insert_ordered_at_shift_range(pos, prev_pos, this->size(), insertions_left)
: hole_size : old_hole_size
; ;
if(new_hole_size > 0){ if(new_hole_size){
//The hole was reduced by priv_insert_ordered_at_shift_range so expand exception rollback range backwards //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); past_hole_values_destroyer.increment_size_backwards(prev_pos - pos);
//Insert the new value in the hole //Insert the new value in the hole
allocator_traits_type::construct(this->m_holder.alloc(), begin_ptr + pos + insertions_left - 1, *(--last_value_it)); allocator_traits_type::construct(this->m_holder.alloc(), begin_ptr + pos + insertions_left - 1, position_value.get_val());
--new_hole_size; if(--new_hole_size){
if(new_hole_size == 0){ //The hole was reduced by the new insertion by one
past_hole_values_destroyer.increment_size_backwards(size_type(1u));
}
else{
//Hole was just filled, disable exception rollback and change vector size //Hole was just filled, disable exception rollback and change vector size
past_hole_values_destroyer.release(); past_hole_values_destroyer.release();
this->m_holder.m_size += element_count; 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{ else{
if(hole_size){ if(old_hole_size){
//Hole was just filled by priv_insert_ordered_at_shift_range, disable exception rollback and change vector size //Hole was just filled by priv_insert_ordered_at_shift_range, disable exception rollback and change vector size
past_hole_values_destroyer.release(); past_hole_values_destroyer.release();
this->m_holder.m_size += element_count; this->m_holder.m_size += element_count;
} }
//Insert the new value in the already constructed range //Insert the new value in the already constructed range
begin_ptr[pos + insertions_left - 1] = *(--last_value_it); begin_ptr[pos + insertions_left - 1] = position_value.get_val();
} }
--insertions_left; --insertions_left;
hole_size = new_hole_size; old_hole_size = new_hole_size;
next_pos = pos; prev_pos = pos;
} }
} }
private: template<class UniqueBool, class BidirIt, class Compare>
void priv_merge(UniqueBool, BidirIt first, BidirIt last, Compare comp)
{
size_type const n = static_cast<size_type>(boost::container::iterator_distance(first, last));
if(BOOST_LIKELY(n)){
size_type const s = this->size();
if(BOOST_LIKELY(s)){
size_type const c = this->capacity();
size_type const free_c = (c - s);
//Use a new buffer if current one is too small for new elements,
//or there is no room for position indexes
bool new_buffer = false;
if(free_c < n){
new_buffer = true;
}
else if(!UniqueBool::value && free_c >= n){
typedef container_detail::vector_merge_cursor<T, size_type, BidirIt, Compare> inserter_t;
T* const pbeg = container_detail::to_raw_pointer(m_holder.start());
return this->priv_insert_ordered_at(n, inserter_t(pbeg, pbeg + s, last, comp));
}
else if(UniqueBool::value){ //Query for room to store n + 1 indexes (+1 to guarantee potential alignment overhead).
//No need to destroy them as they are integral types, which simplifies things a lot.
std::size_t const sz_vlt = sizeof(value_type);
std::size_t const sz_szt = sizeof(size_type);
new_buffer = (c-s-n)*sz_vlt/sz_szt < (n+1);
}
if(new_buffer){
size_type const new_size = s + n;
size_type new_cap = new_size;
pointer p = pointer();
p = this->m_holder.allocation_command(allocate_new, new_size, new_cap, p);
this->priv_merge_in_new_buffer(UniqueBool(), first, n, comp, p, new_cap);
}
else{
//Use trailing memory to store position offsets
uintptr_t const szt_align_mask = container_detail::alignment_of<size_type>::value - 1;
boost::uintptr_t const addr = boost::uintptr_t(container_detail::to_raw_pointer(m_holder.start()) + s + n);
//Align memory before casting to address
size_type *const paddr = reinterpret_cast<size_type *>((addr + szt_align_mask) & ~szt_align_mask);
this->priv_insert_ordered_range(UniqueBool(), n, first, last, paddr, comp);
}
}
else{
this->insert(this->cend(), n, first, last);
}
}
}
template <class UniqueBool, class BidirIt, class Compare>
void priv_insert_ordered_range
(UniqueBool, size_type const n, BidirIt first, BidirIt const last, size_type positions[], Compare comp)
{
//Linear: at most N + M -1 comparisons
//Log: MlogN
//Average
//Linear: N + M - 2
//Log: MlogN
//N+M - 2
//N
//(N+M)/2 < MlogN
//(N/M+1)/2 <= logN
//bool const linear = !s || !n || (s <= n) || ((s+n)/n/2 < logN);
size_type const s = this->size();
size_type remaining = n;
T* const pbeg = container_detail::to_raw_pointer(m_holder.start());
T* const pend = pbeg + s;
T* pcur = pbeg;
size_type *position = positions;
size_type added_in_middle = 0;
if(first != last && pcur != pend){
while(1){
//maintain stability moving external values only if they are strictly less
if(comp(*first, *pcur)) {
*position = static_cast<size_type>(pcur - pbeg);
BOOST_ASSERT((position == positions) || (*(position-1) == size_type(-1)) || (*(position-1) <= *position));
++position;
++added_in_middle;
--remaining;
if(++first == last) break;
}
else if(UniqueBool::value && !comp(*pcur, *first)){
*position = size_type(-1);
++position;
--remaining;
if(++first == last) break;
}
else{
if(++pcur == pend) break;
}
}
}
this->insert_ordered_at(added_in_middle, position, first);
this->insert(this->cend(), remaining, first, last);
}
template<class UniqueBool, class FwdIt, class Compare>
void priv_merge_in_new_buffer
(UniqueBool, FwdIt first, size_type n, Compare comp, pointer new_storage, size_type const new_cap)
{
BOOST_ASSERT((new_cap >= this->size() ) && (new_cap - this->size()) >= n);
allocator_type &a = this->m_holder.alloc();
typename value_traits::ArrayDeallocator new_buffer_deallocator(new_storage, a, new_cap);
typename value_traits::ArrayDestructor new_values_destroyer(new_storage, a, 0u);
T* pbeg = container_detail::to_raw_pointer(m_holder.start());
size_type const old_size = this->size();
T* const pend = pbeg + old_size;
T* d_first = container_detail::to_raw_pointer(new_storage);
size_type added = n;
//Merge in new buffer loop
while(1){
if(!n) {
::boost::container::uninitialized_move_alloc(this->m_holder.alloc(), pbeg, pend, d_first);
break;
}
else if(pbeg == pend) {
::boost::container::uninitialized_move_alloc_n(this->m_holder.alloc(), first, n, d_first);
break;
}
//maintain stability moving external values only if they are strictly less
else if(comp(*first, *pbeg)) {
*d_first = ::boost::move(*first);
++first;
--n;
++d_first;
}
else if(UniqueBool::value && !comp(*pbeg, *first)){
++first;
--n;
--added;
}
else{
*d_first = ::boost::move(*pbeg);
++pbeg;
++d_first;
}
}
//Nothrow operations
pointer const old_p = this->m_holder.start();
size_type const old_cap = this->m_holder.capacity();
boost::container::destroy_alloc_n(a, container_detail::to_raw_pointer(old_p), old_size);
a.deallocate(old_p, old_cap);
this->m_holder.m_size = old_size + added;
this->m_holder.start(new_storage);
this->m_holder.capacity(new_cap);
new_buffer_deallocator.release();
new_values_destroyer.release();
}
bool room_enough() const bool room_enough() const
{ return this->m_holder.m_size < this->m_holder.capacity(); } { return this->m_holder.m_size < this->m_holder.capacity(); }
@@ -2113,9 +2365,11 @@ class vector
template<class OtherAllocator> template<class OtherAllocator>
void priv_move_assign(BOOST_RV_REF_BEG vector<T, OtherAllocator> BOOST_RV_REF_END x void priv_move_assign(BOOST_RV_REF_BEG vector<T, OtherAllocator> BOOST_RV_REF_END x
, typename container_detail::enable_if_c , typename container_detail::disable_if_or
< !container_detail::is_version<OtherAllocator, 0>::value && < void
container_detail::is_same<OtherAllocator, allocator_type>::value>::type * = 0) , container_detail::is_version<OtherAllocator, 0>
, container_detail::is_different<OtherAllocator, allocator_type>
>::type * = 0)
{ {
//for move constructor, no aliasing (&x != this) is assummed. //for move constructor, no aliasing (&x != this) is assummed.
BOOST_ASSERT(this != &x); BOOST_ASSERT(this != &x);
@@ -2167,10 +2421,12 @@ class vector
} }
template<class OtherAllocator> template<class OtherAllocator>
void priv_copy_assign(const vector<T, OtherAllocator> &x typename container_detail::disable_if_or
, typename container_detail::enable_if_c < void
< !container_detail::is_version<OtherAllocator, 0>::value && , container_detail::is_version<OtherAllocator, 0>
container_detail::is_same<OtherAllocator, allocator_type>::value >::type * = 0) , container_detail::is_different<OtherAllocator, allocator_type>
>::type
priv_copy_assign(const vector<T, OtherAllocator> &x)
{ {
allocator_type &this_alloc = this->m_holder.alloc(); allocator_type &this_alloc = this->m_holder.alloc();
const allocator_type &x_alloc = x.m_holder.alloc(); const allocator_type &x_alloc = x.m_holder.alloc();
@@ -2554,69 +2810,6 @@ class vector
return this->priv_forward_range_insert(this->back_ptr(), n, insert_range_proxy); return this->priv_forward_range_insert(this->back_ptr(), n, insert_range_proxy);
} }
//Absolutely experimental. This function might change, disappear or simply crash!
template<class BiDirPosConstIt, class BiDirSkipConstIt, class BiDirValueIt>
void priv_insert_ordered_at( size_type element_count, BiDirPosConstIt last_position_it
, bool do_skip, BiDirSkipConstIt last_skip_it, BiDirValueIt 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){
if(do_skip){
size_type n = *(--last_skip_it);
boost::container::iterator_advance(last_value_it, -difference_type(n));
}
const size_type pos = static_cast<size_type>(*(--last_position_it));
BOOST_ASSERT(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;
}
}
//Takes the range pointed by [first_pos, last_pos) and shifts it to the right //Takes the range pointed by [first_pos, last_pos) and shifts it to the right
//by 'shift_count'. 'limit_pos' marks the end of constructed elements. //by 'shift_count'. 'limit_pos' marks the end of constructed elements.
// //
@@ -2647,7 +2840,7 @@ class vector
// |_>_>_>_>_>^ // |_>_>_>_>_>^
// //
// //
//New situation in Case B (hole_size > 0): //New situation in Case B (hole_size >= 0):
// range is moved through uninitialized moves // range is moved through uninitialized moves
// //
// first_pos last_pos limit_pos // first_pos last_pos limit_pos
@@ -2689,7 +2882,7 @@ class vector
//All uninitialized_moved //All uninitialized_moved
::boost::container::uninitialized_move_alloc ::boost::container::uninitialized_move_alloc
(this->m_holder.alloc(), first_ptr, last_ptr, first_ptr + shift_count); (this->m_holder.alloc(), first_ptr, last_ptr, first_ptr + shift_count);
hole_size = last_pos + shift_count - limit_pos; hole_size = first_pos + shift_count - limit_pos;
} }
//Case C: //Case C:
else{ else{
@@ -2716,7 +2909,7 @@ class vector
void priv_forward_range_insert_expand_forward(T* const pos, const size_type n, InsertionProxy insert_range_proxy) void priv_forward_range_insert_expand_forward(T* const pos, const size_type n, InsertionProxy insert_range_proxy)
{ {
//n can't be 0, because there is nothing to do in that case //n can't be 0, because there is nothing to do in that case
if(!n) return; if(BOOST_UNLIKELY(!n)) return;
//There is enough memory //There is enough memory
T* const old_finish = this->back_raw(); T* const old_finish = this->back_raw();
const size_type elems_after = old_finish - pos; const size_type elems_after = old_finish - pos;