From 3246e11ca29e14f65c19b2bb1be7e2f9ddcbc165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Tue, 14 Apr 2015 15:22:06 +0200 Subject: [PATCH] Added new experimental merge functions to speed up flat_xxx range insertion --- include/boost/container/detail/flat_tree.hpp | 98 +--- include/boost/container/vector.hpp | 445 +++++++++++++------ 2 files changed, 334 insertions(+), 209 deletions(-) diff --git a/include/boost/container/detail/flat_tree.hpp b/include/boost/container/detail/flat_tree.hpp index a94043c..a68e7b2 100644 --- a/include/boost/container/detail/flat_tree.hpp +++ b/include/boost/container/detail/flat_tree.hpp @@ -470,20 +470,22 @@ class flat_tree template void insert_equal(ordered_range_t, BidirIt first, BidirIt last #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) - , typename container_detail::enable_if_c - < !container_detail::is_input_iterator::value && - !container_detail::is_forward_iterator::value + , typename container_detail::disable_if_or + < void + , container_detail::is_input_iterator + , container_detail::is_forward_iterator >::type * = 0 #endif ) - { this->priv_insert_ordered_range(false, first, last); } + { this->m_data.m_vect.merge(first, last); } template void insert_unique(ordered_unique_range_t, InIt first, InIt last #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) - , typename container_detail::enable_if_c - < container_detail::is_input_iterator::value || - container_detail::is_forward_iterator::value + , typename container_detail::enable_if_or + < void + , container_detail::is_input_iterator + , container_detail::is_forward_iterator >::type * = 0 #endif ) @@ -504,7 +506,7 @@ class flat_tree >::type * = 0 #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) @@ -855,8 +857,8 @@ class flat_tree } template - RanIt priv_upper_bound(RanIt first, const RanIt last, - const key_type & key) const + RanIt priv_upper_bound + (RanIt first, const RanIt last,const key_type & key) const { const Compare &key_cmp = this->m_data.get_comp(); KeyOfValue key_extract; @@ -904,9 +906,9 @@ class flat_tree //Middle is equal to key last = first; last += len; + RanIt const first_ret = this->priv_lower_bound(first, middle, key); return std::pair - ( this->priv_lower_bound(first, middle, key) - , this->priv_upper_bound(++middle, last, key)); + ( first_ret, this->priv_upper_bound(++middle, last, key)); } } return std::pair(first, first); @@ -939,81 +941,11 @@ class flat_tree for ( ; first != last; ++first){ //If ordered, then try hint version //to achieve constant-time complexity per insertion + //in some cases pos = this->insert_equal(pos, *first); ++pos; } } - - template - void priv_insert_ordered_range(const bool unique_values, BidirIt first, BidirIt last) - { - size_type len = static_cast(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 positions = - ::boost::movelib::make_unique_definit(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::value_type - //because it can be different from container::value_type - //(e.g. conversion between std::pair -> boost::container::pair - const typename boost::container::iterator_traits::value_type & val = *first; - pos = const_cast(*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(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::value_type - //because it can be different from container::value_type - //(e.g. conversion between std::pair -> boost::container::pair - const typename boost::container::iterator_traits::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 { diff --git a/include/boost/container/vector.hpp b/include/boost/container/vector.hpp index 1b6d963..b65a62c 100644 --- a/include/boost/container/vector.hpp +++ b/include/boost/container/vector.hpp @@ -57,6 +57,7 @@ // other #include #include +#include //std #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) @@ -179,6 +180,70 @@ class vec_iterator { return l.m_ptr >= r.m_ptr; } }; +template +struct vector_insert_ordered_cursor +{ + typedef typename iterator_traits::value_type size_type; + typedef typename iterator_traits::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 +struct vector_merge_cursor +{ + typedef SizeType size_type; + typedef typename iterator_traits::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((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 { template @@ -640,6 +705,13 @@ class vector { #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + struct value_less + { + typedef typename boost::container::allocator_traits::value_type value_type; + bool operator()(const value_type &a, const value_type &b) const + { return a < b; } + }; + typedef typename container_detail::version::type alloc_version; typedef boost::container::container_detail::vector_alloc_holder alloc_holder_t; alloc_holder_t m_holder; @@ -1043,10 +1115,11 @@ class vector //! //! Note: Non-standard extension to support static_vector template - typename container_detail::enable_if_c - < container_detail::is_version::value && - !container_detail::is_same::value - , vector& >::type + typename container_detail::enable_if_and + < vector& + , container_detail::is_version + , container_detail::is_different + >::type operator=(BOOST_RV_REF_BEG vector BOOST_RV_REF_END x) { this->priv_move_assign(boost::move(x)); @@ -1064,10 +1137,11 @@ class vector //! //! Note: Non-standard extension to support static_vector template - typename container_detail::enable_if_c - < container_detail::is_version::value && - !container_detail::is_same::value - , vector& >::type + typename container_detail::enable_if_and + < vector& + , container_detail::is_version + , container_detail::is_different + >::type operator=(const vector &x) { this->priv_copy_assign(x); @@ -1084,11 +1158,15 @@ class vector //! Complexity: Linear to n. template void assign(InIt first, InIt last - BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::enable_if_c - < !container_detail::is_convertible::value && - ( container_detail::is_input_iterator::value || - container_detail::is_same::value ) - >::type * = 0) ) + BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::disable_if_or + < void + BOOST_MOVE_I container_detail::is_convertible + BOOST_MOVE_I container_detail::and_ + < container_detail::is_different + BOOST_MOVE_I container_detail::is_not_input_iterator + > + >::type * = 0) + ) { //Overwrite all elements we can from [first, last) iterator cur = this->begin(); @@ -1129,10 +1207,11 @@ class vector //! Complexity: Linear to n. template void assign(FwdIt first, FwdIt last - BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::enable_if_c - < !container_detail::is_convertible::value && - ( !container_detail::is_input_iterator::value && - !container_detail::is_same::value ) + BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::disable_if_or + < void + BOOST_MOVE_I container_detail::is_same + BOOST_MOVE_I container_detail::is_convertible + BOOST_MOVE_I container_detail::is_input_iterator >::type * = 0) ) { @@ -1791,10 +1870,13 @@ class vector //! Complexity: Linear to boost::container::iterator_distance [first, last). template iterator insert(const_iterator pos, InIt first, InIt last - BOOST_CONTAINER_DOCIGN(BOOST_MOVE_I typename container_detail::enable_if_c - < !container_detail::is_convertible::value - && container_detail::is_input_iterator::value - >::type * = 0) + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename container_detail::disable_if_or + < void + , container_detail::is_convertible + , container_detail::is_not_input_iterator + >::type * = 0 + #endif ) { const size_type n_pos = pos - this->cbegin(); @@ -1809,9 +1891,10 @@ class vector #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) template iterator insert(const_iterator pos, FwdIt first, FwdIt last - , typename container_detail::enable_if_c - < !container_detail::is_convertible::value - && !container_detail::is_input_iterator::value + , typename container_detail::disable_if_or + < void + , container_detail::is_convertible + , container_detail::is_input_iterator >::type * = 0 ) { @@ -1931,9 +2014,11 @@ class vector //! Note: Non-standard extension to support static_vector template void swap(vector & x - , typename container_detail::enable_if_c - < container_detail::is_version::value && - !container_detail::is_same::value >::type * = 0 + , typename container_detail::enable_if_and + < void + , container_detail::is_version + , container_detail::is_different + >::type * = 0 ) { this->m_holder.deep_swap(x.m_holder); } @@ -2016,13 +2101,37 @@ class vector //Absolutely experimental. This function might change, disappear or simply crash! template void insert_ordered_at(const size_type element_count, BiDirPosConstIt last_position_it, BiDirValueIt last_value_it) + { + typedef container_detail::vector_insert_ordered_cursor inserter_t; + return this->priv_insert_ordered_at(element_count, inserter_t(last_position_it, last_value_it)); + } + + template + void merge(BidirIt first, BidirIt last) + { this->merge(first, last, value_less()); } + + template + void merge(BidirIt first, BidirIt last, Compare comp) + { this->priv_merge(container_detail::false_type(), first, last, comp); } + + template + void merge_unique(BidirIt first, BidirIt last) + { this->priv_merge(container_detail::true_type(), first, last, value_less()); } + + template + void merge_unique(BidirIt first, BidirIt last, Compare comp) + { this->priv_merge(container_detail::true_type(), first, last, comp); } + + private: + template + void priv_insert_ordered_at(const size_type element_count, PositionValue position_value) { 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; + size_type prev_pos = old_size_pos; + size_type old_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. @@ -2031,52 +2140,195 @@ class vector //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(*(--last_position_it)); - while(pos == size_type(-1)){ - --last_value_it; - pos = static_cast(*(--last_position_it)); - } - - BOOST_ASSERT(pos != size_type(-1) && pos <= old_size_pos); + --position_value; + size_type const pos = position_value.get_pos(); + BOOST_ASSERT(pos != size_type(-1) && pos <= old_size_pos && pos <= prev_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 + size_type new_hole_size = (pos != prev_pos) + ? priv_insert_ordered_at_shift_range(pos, prev_pos, this->size(), insertions_left) + : 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 - 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 - 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){ + allocator_traits_type::construct(this->m_holder.alloc(), begin_ptr + pos + insertions_left - 1, position_value.get_val()); + if(--new_hole_size){ + //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 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){ + if(old_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); + begin_ptr[pos + insertions_left - 1] = position_value.get_val(); } --insertions_left; - hole_size = new_hole_size; - next_pos = pos; + old_hole_size = new_hole_size; + prev_pos = pos; } } - private: + template + void priv_merge(UniqueBool, BidirIt first, BidirIt last, Compare comp) + { + size_type const n = static_cast(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 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::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((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 + 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(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 + 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 { return this->m_holder.m_size < this->m_holder.capacity(); } @@ -2113,9 +2365,11 @@ class vector template void priv_move_assign(BOOST_RV_REF_BEG vector BOOST_RV_REF_END x - , typename container_detail::enable_if_c - < !container_detail::is_version::value && - container_detail::is_same::value>::type * = 0) + , typename container_detail::disable_if_or + < void + , container_detail::is_version + , container_detail::is_different + >::type * = 0) { //for move constructor, no aliasing (&x != this) is assummed. BOOST_ASSERT(this != &x); @@ -2167,10 +2421,12 @@ class vector } template - void priv_copy_assign(const vector &x - , typename container_detail::enable_if_c - < !container_detail::is_version::value && - container_detail::is_same::value >::type * = 0) + typename container_detail::disable_if_or + < void + , container_detail::is_version + , container_detail::is_different + >::type + priv_copy_assign(const vector &x) { allocator_type &this_alloc = this->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); } - //Absolutely experimental. This function might change, disappear or simply crash! - template - 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(*(--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 //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 // // first_pos last_pos limit_pos @@ -2689,7 +2882,7 @@ class vector //All uninitialized_moved ::boost::container::uninitialized_move_alloc (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: 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) { //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 T* const old_finish = this->back_raw(); const size_type elems_after = old_finish - pos;