mirror of
https://github.com/boostorg/container.git
synced 2025-08-02 14:04:26 +02:00
Added new experimental merge functions to speed up flat_xxx range insertion
This commit is contained in:
@@ -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 {
|
||||||
|
@@ -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;
|
||||||
|
Reference in New Issue
Block a user