////////////////////////////////////////////////////////////////////////////// // // (C) Copyright Ion Gaztanaga 2008-2012. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/container for documentation. // ////////////////////////////////////////////////////////////////////////////// // Stable vector. // // Copyright 2008 Joaquin M Lopez Munoz. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) // ////////////////////////////////////////////////////////////////////////////// #ifndef BOOST_CONTAINER_STABLE_VECTOR_HPP #define BOOST_CONTAINER_STABLE_VECTOR_HPP #if (defined _MSC_VER) && (_MSC_VER >= 1200) # pragma once #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include ///@cond #include //#define STABLE_VECTOR_ENABLE_INVARIANT_CHECKING #if defined(STABLE_VECTOR_ENABLE_INVARIANT_CHECKING) #include #endif ///@endcond namespace boost { namespace container { ///@cond namespace stable_vector_detail{ template struct smart_ptr_type { typedef typename SmartPtr::value_type value_type; typedef value_type *pointer; static pointer get (const SmartPtr &smartptr) { return smartptr.get();} }; template struct smart_ptr_type { typedef T value_type; typedef value_type *pointer; static pointer get (pointer ptr) { return ptr;} }; template class clear_on_destroy { public: clear_on_destroy(C &c) : c_(c), do_clear_(true) {} void release() { do_clear_ = false; } ~clear_on_destroy() { if(do_clear_){ c_.clear(); c_.clear_pool(); } } private: clear_on_destroy(const clear_on_destroy &); clear_on_destroy &operator=(const clear_on_destroy &); C &c_; bool do_clear_; }; template struct node_type_base { node_type_base() {} void set_pointer(const VoidPtr &p) { up = p; } VoidPtr up; }; template struct node_type : public node_type_base { private: node_type(); public: T value; }; template class iterator : public std::iterator< std::random_access_iterator_tag , T , typename boost::intrusive:: pointer_traits::difference_type , Pointer , Reference> { typedef typename boost::intrusive:: pointer_traits::template rebind_pointer::type void_ptr; typedef typename boost::intrusive:: pointer_traits::template rebind_pointer::type const_void_ptr; typedef node_type node_type_t; typedef typename boost::intrusive:: pointer_traits::template rebind_pointer::type node_type_ptr_t; typedef typename boost::intrusive:: pointer_traits::template rebind_pointer::type const_node_type_ptr_t; typedef typename boost::intrusive:: pointer_traits::template rebind_pointer::type void_ptr_ptr; friend class iterator::template rebind_pointer::type>; public: typedef std::random_access_iterator_tag iterator_category; typedef T value_type; typedef typename boost::intrusive:: pointer_traits::difference_type difference_type; typedef Pointer pointer; typedef Reference reference; iterator() {} explicit iterator(node_type_ptr_t pn) : pn(pn) {} iterator(const iterator::template rebind_pointer::type>& x) : pn(x.pn) {} private: static node_type_ptr_t node_ptr_cast(const void_ptr &p) { return node_type_ptr_t(static_cast(container_detail::to_raw_pointer(p))); } static const_node_type_ptr_t node_ptr_cast(const const_void_ptr &p) { return const_node_type_ptr_t(static_cast(container_detail::to_raw_pointer(p))); } static void_ptr_ptr void_ptr_ptr_cast(const void_ptr &p) { return void_ptr_ptr(static_cast(container_detail::to_raw_pointer(p))); } reference dereference() const { return pn->value; } bool equal(const iterator& x) const { return pn==x.pn; } void increment() { pn = node_ptr_cast(*(void_ptr_ptr_cast(pn->up)+1)); } void decrement() { pn = node_ptr_cast(*(void_ptr_ptr_cast(pn->up)-1)); } void advance(difference_type n) { pn = node_ptr_cast(*(void_ptr_ptr_cast(pn->up)+n)); } difference_type distance_to(const iterator& x)const { return void_ptr_ptr_cast(x.pn->up) - void_ptr_ptr_cast(pn->up); } public: //Pointer like operators reference operator*() const { return this->dereference(); } pointer operator->() const { return pointer(&this->dereference()); } //Increment / Decrement iterator& operator++() { this->increment(); return *this; } iterator operator++(int) { iterator tmp(*this); ++*this; return iterator(tmp); } iterator& operator--() { this->decrement(); return *this; } iterator operator--(int) { iterator tmp(*this); --*this; return iterator(tmp); } reference operator[](difference_type off) const { iterator tmp(*this); tmp += off; return *tmp; } iterator& operator+=(difference_type off) { pn = node_ptr_cast(*(void_ptr_ptr_cast(pn->up)+off)); return *this; } friend iterator operator+(const iterator &left, difference_type off) { iterator tmp(left); tmp += off; return tmp; } friend iterator operator+(difference_type off, const iterator& right) { iterator tmp(right); tmp += off; return tmp; } iterator& operator-=(difference_type off) { *this += -off; return *this; } friend iterator operator-(const iterator &left, difference_type off) { iterator tmp(left); tmp -= off; return tmp; } friend difference_type operator-(const iterator& left, const iterator& right) { return void_ptr_ptr_cast(left.pn->up) - void_ptr_ptr_cast(right.pn->up); } //Comparison operators friend bool operator== (const iterator& l, const iterator& r) { return l.pn == r.pn; } friend bool operator!= (const iterator& l, const iterator& r) { return l.pn != r.pn; } friend bool operator< (const iterator& l, const iterator& r) { return void_ptr_ptr_cast(l.pn->up) < void_ptr_ptr_cast(r.pn->up); } friend bool operator<= (const iterator& l, const iterator& r) { return void_ptr_ptr_cast(l.pn->up) <= void_ptr_ptr_cast(r.pn->up); } friend bool operator> (const iterator& l, const iterator& r) { return void_ptr_ptr_cast(l.pn->up) > void_ptr_ptr_cast(r.pn->up); } friend bool operator>= (const iterator& l, const iterator& r) { return void_ptr_ptr_cast(l.pn->up) >= void_ptr_ptr_cast(r.pn->up); } node_type_ptr_t pn; }; template struct select_multiallocation_chain { typedef typename A::multiallocation_chain type; }; template struct select_multiallocation_chain { typedef typename boost::intrusive::pointer_traits ::pointer>:: template rebind_pointer::type void_ptr; typedef container_detail::basic_multiallocation_chain multialloc_cached_counted; typedef boost::container::container_detail:: transform_multiallocation_chain < multialloc_cached_counted , typename allocator_traits::value_type> type; }; } //namespace stable_vector_detail #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) #if defined(STABLE_VECTOR_ENABLE_INVARIANT_CHECKING) #define STABLE_VECTOR_CHECK_INVARIANT \ invariant_checker BOOST_JOIN(check_invariant_,__LINE__)(*this); \ BOOST_JOIN(check_invariant_,__LINE__).touch(); #else #define STABLE_VECTOR_CHECK_INVARIANT #endif //#if defined(STABLE_VECTOR_ENABLE_INVARIANT_CHECKING) #endif //#if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) /// @endcond //! Originally developed by Joaquin M. Lopez Munoz, stable_vector is std::vector //! drop-in replacement implemented as a node container, offering iterator and reference //! stability. //! //! More details taken the author's blog: //! ( //! Introducing stable_vector) //! //! We present stable_vector, a fully STL-compliant stable container that provides //! most of the features of std::vector except element contiguity. //! //! General properties: stable_vector satisfies all the requirements of a container, //! a reversible container and a sequence and provides all the optional operations //! present in std::vector. Like std::vector, iterators are random access. //! stable_vector does not provide element contiguity; in exchange for this absence, //! the container is stable, i.e. references and iterators to an element of a stable_vector //! remain valid as long as the element is not erased, and an iterator that has been //! assigned the return value of end() always remain valid until the destruction of //! the associated stable_vector. //! //! Operation complexity: The big-O complexities of stable_vector operations match //! exactly those of std::vector. In general, insertion/deletion is constant time at //! the end of the sequence and linear elsewhere. Unlike std::vector, stable_vector //! does not internally perform any value_type destruction, copy or assignment //! operations other than those exactly corresponding to the insertion of new //! elements or deletion of stored elements, which can sometimes compensate in terms //! of performance for the extra burden of doing more pointer manipulation and an //! additional allocation per element. //! //! Exception safety: As stable_vector does not internally copy elements around, some //! operations provide stronger exception safety guarantees than in std::vector: #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template > #else template #endif class stable_vector { ///@cond typedef allocator_traits allocator_traits_type; typedef typename container_detail:: move_const_ref_type::type insert_const_ref_type; typedef typename boost::intrusive::pointer_traits :: template rebind_pointer::type void_ptr; typedef typename boost::intrusive::pointer_traits ::template rebind_pointer::type const_void_ptr; typedef typename boost::intrusive::pointer_traits ::template rebind_pointer::type void_ptr_ptr; typedef typename boost::intrusive::pointer_traits ::template rebind_pointer::type const_void_ptr_ptr; typedef stable_vector_detail::node_type node_type_t; typedef typename boost::intrusive::pointer_traits ::template rebind_pointer::type node_type_ptr_t; typedef stable_vector_detail::node_type_base node_type_base_t; typedef typename boost::intrusive::pointer_traits ::template rebind_pointer::type node_type_base_ptr_t; typedef ::boost::container::vector::type> impl_type; typedef typename impl_type::iterator impl_iterator; typedef typename impl_type::const_iterator const_impl_iterator; typedef ::boost::container::container_detail:: integral_constant allocator_v1; typedef ::boost::container::container_detail:: integral_constant allocator_v2; typedef ::boost::container::container_detail::integral_constant ::value> alloc_version; typedef typename allocator_traits_type:: template portable_rebind_alloc ::type node_allocator_type; node_type_ptr_t allocate_one() { return this->allocate_one(alloc_version()); } template node_type_ptr_t allocate_one(AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { return node_alloc().allocate(1); } template node_type_ptr_t allocate_one(AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { return node_alloc().allocate_one(); } void deallocate_one(node_type_ptr_t p) { return this->deallocate_one(p, alloc_version()); } template void deallocate_one(node_type_ptr_t p, AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { node_alloc().deallocate(p, 1); } template void deallocate_one(node_type_ptr_t p, AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { node_alloc().deallocate_one(p); } friend class stable_vector_detail::clear_on_destroy; ///@endcond public: // types: typedef typename allocator_traits_type::reference reference; typedef typename allocator_traits_type::const_reference const_reference; typedef typename allocator_traits_type::pointer pointer; typedef typename allocator_traits_type::const_pointer const_pointer; typedef stable_vector_detail::iterator iterator; typedef stable_vector_detail::iterator const_iterator; typedef typename impl_type::size_type size_type; typedef typename iterator::difference_type difference_type; typedef T value_type; typedef A allocator_type; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_iterator const_reverse_iterator; typedef node_allocator_type stored_allocator_type; ///@cond private: BOOST_COPYABLE_AND_MOVABLE(stable_vector) static const size_type ExtraPointers = 3; //This container stores metadata at the end of the void_ptr vector with additional 3 pointers: // back() is impl.back() - ExtraPointers; // end node index is impl.end()[-3] // Node cache first is impl.end()[-2]; // Node cache last is *impl.back(); typedef typename stable_vector_detail:: select_multiallocation_chain < node_allocator_type , alloc_version::value >::type multiallocation_chain; ///@endcond public: //! Effects: Default constructs a stable_vector. //! //! Throws: If allocator_type's default constructor throws. //! //! Complexity: Constant. stable_vector() : internal_data(), impl() { STABLE_VECTOR_CHECK_INVARIANT; } //! Effects: Constructs a stable_vector taking the allocator as parameter. //! //! Throws: If allocator_type's copy constructor throws. //! //! Complexity: Constant. explicit stable_vector(const allocator_type& al) : internal_data(al),impl(al) { STABLE_VECTOR_CHECK_INVARIANT; } //! Effects: Constructs a stable_vector that will use a copy of allocator a //! and inserts n default contructed values. //! //! Throws: If allocator_type's default constructor or copy constructor //! throws or T's default or copy constructor throws. //! //! Complexity: Linear to n. explicit stable_vector(size_type n) : internal_data(),impl() { stable_vector_detail::clear_on_destroy cod(*this); this->resize(n); STABLE_VECTOR_CHECK_INVARIANT; cod.release(); } //! Effects: Constructs a stable_vector that will use a copy of allocator a //! and inserts n copies of value. //! //! Throws: If allocator_type's default constructor or copy constructor //! throws or T's default or copy constructor throws. //! //! Complexity: Linear to n. stable_vector(size_type n, const T& t, const allocator_type& al = allocator_type()) : internal_data(al),impl(al) { stable_vector_detail::clear_on_destroy cod(*this); this->insert(this->cbegin(), n, t); STABLE_VECTOR_CHECK_INVARIANT; cod.release(); } //! Effects: Constructs a stable_vector that will use a copy of allocator a //! and inserts a copy of the range [first, last) in the stable_vector. //! //! Throws: If allocator_type's default constructor or copy constructor //! throws or T's constructor taking an dereferenced InIt throws. //! //! Complexity: Linear to the range [first, last). template stable_vector(InputIterator first,InputIterator last, const allocator_type& al = allocator_type()) : internal_data(al),impl(al) { stable_vector_detail::clear_on_destroy cod(*this); this->insert(this->cbegin(), first, last); STABLE_VECTOR_CHECK_INVARIANT; cod.release(); } //! Effects: Copy constructs a stable_vector. //! //! Postcondition: x == *this. //! //! Complexity: Linear to the elements x contains. stable_vector(const stable_vector& x) : internal_data(allocator_traits:: select_on_container_copy_construction(x.node_alloc())) , impl(allocator_traits:: select_on_container_copy_construction(x.impl.get_stored_allocator())) { stable_vector_detail::clear_on_destroy cod(*this); this->insert(this->cbegin(), x.begin(), x.end()); STABLE_VECTOR_CHECK_INVARIANT; cod.release(); } //! Effects: Move constructor. Moves mx's resources to *this. //! //! Throws: If allocator_type's copy constructor throws. //! //! Complexity: Constant. stable_vector(BOOST_RV_REF(stable_vector) x) : internal_data(boost::move(x.node_alloc())), impl(boost::move(x.impl)) { this->priv_swap_members(x); } //! Effects: Copy constructs a stable_vector using the specified allocator. //! //! Postcondition: x == *this. //! //! Complexity: Linear to the elements x contains. stable_vector(const stable_vector& x, const allocator_type &a) : internal_data(a), impl(a) { stable_vector_detail::clear_on_destroy cod(*this); this->insert(this->cbegin(), x.begin(), x.end()); STABLE_VECTOR_CHECK_INVARIANT; cod.release(); } //! Effects: Move constructor using the specified allocator. //! Moves mx's resources to *this. //! //! Throws: If allocator_type's copy constructor throws. //! //! Complexity: Constant if a == x.get_allocator(), linear otherwise stable_vector(BOOST_RV_REF(stable_vector) x, const allocator_type &a) : internal_data(a), impl(a) { if(this->node_alloc() == x.node_alloc()){ this->priv_swap_members(x); } else{ stable_vector_detail::clear_on_destroy cod(*this); this->insert(this->cbegin(), x.begin(), x.end()); STABLE_VECTOR_CHECK_INVARIANT; cod.release(); } } //! Effects: Destroys the stable_vector. All stored values are destroyed //! and used memory is deallocated. //! //! Throws: Nothing. //! //! Complexity: Linear to the number of elements. ~stable_vector() { this->clear(); clear_pool(); } //! Effects: Makes *this contain the same elements as x. //! //! Postcondition: this->size() == x.size(). *this contains a copy //! of each of x's elements. //! //! Throws: If memory allocation throws or T's copy constructor throws. //! //! Complexity: Linear to the number of elements in x. stable_vector& operator=(BOOST_COPY_ASSIGN_REF(stable_vector) x) { STABLE_VECTOR_CHECK_INVARIANT; if (&x != this){ node_allocator_type &this_alloc = this->node_alloc(); const node_allocator_type &x_alloc = x.node_alloc(); container_detail::bool_ flag; if(flag && this_alloc != x_alloc){ this->clear(); this->shrink_to_fit(); } container_detail::assign_alloc(this->node_alloc(), x.node_alloc(), flag); container_detail::assign_alloc(this->impl.get_stored_allocator(), x.impl.get_stored_allocator(), flag); this->assign(x.begin(), x.end()); } return *this; } //! Effects: Move assignment. All mx's values are transferred to *this. //! //! Postcondition: x.empty(). *this contains a the elements x had //! before the function. //! //! Throws: If allocator_type's copy constructor throws. //! //! Complexity: Linear. stable_vector& operator=(BOOST_RV_REF(stable_vector) x) { if (&x != this){ node_allocator_type &this_alloc = this->node_alloc(); node_allocator_type &x_alloc = x.node_alloc(); //If allocators are equal we can just swap pointers if(this_alloc == x_alloc){ //Destroy objects but retain memory this->clear(); this->impl = boost::move(x.impl); this->priv_swap_members(x); //Move allocator if needed container_detail::bool_ flag; container_detail::move_alloc(this->node_alloc(), x.node_alloc(), flag); } //If unequal allocators, then do a one by one move else{ typedef typename std::iterator_traits::iterator_category ItCat; this->assign( boost::make_move_iterator(x.begin()) , boost::make_move_iterator(x.end())); } } return *this; } //! Effects: Assigns the the range [first, last) to *this. //! //! Throws: If memory allocation throws or //! T's constructor from dereferencing InpIt throws. //! //! Complexity: Linear to n. template void assign(InputIterator first,InputIterator last) { assign_dispatch(first, last, boost::is_integral()); } //! Effects: Assigns the n copies of val to *this. //! //! Throws: If memory allocation throws or T's copy constructor throws. //! //! Complexity: Linear to n. void assign(size_type n,const T& t) { typedef constant_iterator cvalue_iterator; return assign_dispatch(cvalue_iterator(t, n), cvalue_iterator(), boost::mpl::false_()); } //! Effects: Returns a copy of the internal allocator. //! //! Throws: If allocator's copy constructor throws. //! //! Complexity: Constant. allocator_type get_allocator()const {return this->node_alloc();} //! Effects: Returns a reference to the internal allocator. //! //! Throws: Nothing //! //! Complexity: Constant. //! //! Note: Non-standard extension. const stored_allocator_type &get_stored_allocator() const BOOST_CONTAINER_NOEXCEPT { return node_alloc(); } //! Effects: Returns a reference to the internal allocator. //! //! Throws: Nothing //! //! Complexity: Constant. //! //! Note: Non-standard extension. stored_allocator_type &get_stored_allocator() BOOST_CONTAINER_NOEXCEPT { return node_alloc(); } //! Effects: Returns an iterator to the first element contained in the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. iterator begin() { return (impl.empty()) ? end(): iterator(node_ptr_cast(impl.front())) ; } //! Effects: Returns a const_iterator to the first element contained in the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_iterator begin()const { return (impl.empty()) ? cend() : const_iterator(node_ptr_cast(impl.front())) ; } //! Effects: Returns an iterator to the end of the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. iterator end() {return iterator(get_end_node());} //! Effects: Returns a const_iterator to the end of the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_iterator end()const {return const_iterator(get_end_node());} //! Effects: Returns a reverse_iterator pointing to the beginning //! of the reversed stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. reverse_iterator rbegin() {return reverse_iterator(this->end());} //! Effects: Returns a const_reverse_iterator pointing to the beginning //! of the reversed stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reverse_iterator rbegin()const {return const_reverse_iterator(this->end());} //! Effects: Returns a reverse_iterator pointing to the end //! of the reversed stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. reverse_iterator rend() {return reverse_iterator(this->begin());} //! Effects: Returns a const_reverse_iterator pointing to the end //! of the reversed stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reverse_iterator rend()const {return const_reverse_iterator(this->begin());} //! Effects: Returns a const_iterator to the first element contained in the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_iterator cbegin()const {return this->begin();} //! Effects: Returns a const_iterator to the end of the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_iterator cend()const {return this->end();} //! Effects: Returns a const_reverse_iterator pointing to the beginning //! of the reversed stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reverse_iterator crbegin()const{return this->rbegin();} //! Effects: Returns a const_reverse_iterator pointing to the end //! of the reversed stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reverse_iterator crend()const {return this->rend();} //! Effects: Returns the number of the elements contained in the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. size_type size() const { return impl.empty() ? 0 : (impl.size() - ExtraPointers); } //! Effects: Returns the largest possible size of the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant. size_type max_size() const { return impl.max_size() - ExtraPointers; } //! Effects: Number of elements for which memory has been allocated. //! capacity() is always greater than or equal to size(). //! //! Throws: Nothing. //! //! Complexity: Constant. size_type capacity() const { if(!impl.capacity()){ return 0; } else{ const size_type num_nodes = this->impl.size() + this->internal_data.pool_size; const size_type num_buck = this->impl.capacity(); return (num_nodes < num_buck) ? num_nodes : num_buck; } } //! Effects: Returns true if the stable_vector contains no elements. //! //! Throws: Nothing. //! //! Complexity: Constant. bool empty() const { return impl.empty() || impl.size() == ExtraPointers; } //! Effects: Inserts or erases elements at the end such that //! the size becomes n. New elements are copy constructed from x. //! //! Throws: If memory allocation throws, or T's copy constructor throws. //! //! Complexity: Linear to the difference between size() and new_size. void resize(size_type n, const T& t) { STABLE_VECTOR_CHECK_INVARIANT; if(n > size()) this->insert(this->cend(), n - this->size(), t); else if(n < this->size()) this->erase(this->cbegin() + n, this->cend()); } //! Effects: Inserts or erases elements at the end such that //! the size becomes n. New elements are default constructed. //! //! Throws: If memory allocation throws, or T's copy constructor throws. //! //! Complexity: Linear to the difference between size() and new_size. void resize(size_type n) { typedef default_construct_iterator default_iterator; STABLE_VECTOR_CHECK_INVARIANT; if(n > size()) this->insert(this->cend(), default_iterator(n - this->size()), default_iterator()); else if(n < this->size()) this->erase(this->cbegin() + n, this->cend()); } //! Effects: If n is less than or equal to capacity(), this call has no //! effect. Otherwise, it is a request for allocation of additional memory. //! If the request is successful, then capacity() is greater than or equal to //! n; otherwise, capacity() is unchanged. In either case, size() is unchanged. //! //! Throws: If memory allocation allocation throws. void reserve(size_type n) { STABLE_VECTOR_CHECK_INVARIANT; if(n > this->max_size()) throw std::bad_alloc(); size_type size = this->size(); size_type old_capacity = this->capacity(); if(n > old_capacity){ this->initialize_end_node(n); const void * old_ptr = &impl[0]; impl.reserve(n + ExtraPointers); bool realloced = &impl[0] != old_ptr; //Fix the pointers for the newly allocated buffer if(realloced){ this->align_nodes(impl.begin(), impl.begin()+size+1); } //Now fill pool if data is not enough if((n - size) > this->internal_data.pool_size){ this->add_to_pool((n - size) - this->internal_data.pool_size); } } } //! Requires: size() > n. //! //! Effects: Returns a reference to the nth element //! from the beginning of the container. //! //! Throws: Nothing. //! //! Complexity: Constant. reference operator[](size_type n){return value(impl[n]);} //! Requires: size() > n. //! //! Effects: Returns a const reference to the nth element //! from the beginning of the container. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reference operator[](size_type n)const{return value(impl[n]);} //! Requires: size() > n. //! //! Effects: Returns a reference to the nth element //! from the beginning of the container. //! //! Throws: std::range_error if n >= size() //! //! Complexity: Constant. reference at(size_type n) { if(n>=size()) throw std::out_of_range("invalid subscript at stable_vector::at"); return operator[](n); } //! Requires: size() > n. //! //! Effects: Returns a const reference to the nth element //! from the beginning of the container. //! //! Throws: std::range_error if n >= size() //! //! Complexity: Constant. const_reference at(size_type n)const { if(n>=size()) throw std::out_of_range("invalid subscript at stable_vector::at"); return operator[](n); } //! Requires: !empty() //! //! Effects: Returns a reference to the first //! element of the container. //! //! Throws: Nothing. //! //! Complexity: Constant. reference front() { return value(impl.front()); } //! Requires: !empty() //! //! Effects: Returns a const reference to the first //! element of the container. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reference front()const { return value(impl.front()); } //! Requires: !empty() //! //! Effects: Returns a reference to the last //! element of the container. //! //! Throws: Nothing. //! //! Complexity: Constant. reference back() { return value(*(&impl.back() - ExtraPointers)); } //! Requires: !empty() //! //! Effects: Returns a const reference to the last //! element of the container. //! //! Throws: Nothing. //! //! Complexity: Constant. const_reference back()const { return value(*(&impl.back() - ExtraPointers)); } //! Effects: Inserts a copy of x at the end of the stable_vector. //! //! Throws: If memory allocation throws or //! T's copy constructor throws. //! //! Complexity: Amortized constant time. void push_back(insert_const_ref_type x) { return priv_push_back(x); } #if defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) void push_back(T &x) { push_back(const_cast(x)); } template void push_back(const U &u, typename container_detail::enable_if_c ::value && !::boost::has_move_emulation_enabled::value >::type* =0) { return priv_push_back(u); } #endif //! Effects: Constructs a new element in the end of the stable_vector //! and moves the resources of mx to this new element. //! //! Throws: If memory allocation throws. //! //! Complexity: Amortized constant time. void push_back(BOOST_RV_REF(T) t) { this->insert(end(), boost::move(t)); } //! Effects: Removes the last element from the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Constant time. void pop_back() { this->erase(this->end()-1); } //! Requires: position must be a valid iterator of *this. //! //! Effects: Insert a copy of x before position. //! //! Throws: If memory allocation throws or x's copy constructor throws. //! //! Complexity: If position is end(), amortized constant time //! Linear time otherwise. iterator insert(const_iterator position, insert_const_ref_type x) { return this->priv_insert(position, x); } #if defined(BOOST_NO_RVALUE_REFERENCES) && !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) iterator insert(const_iterator position, T &x) { return this->insert(position, const_cast(x)); } template iterator insert(const_iterator position, const U &u, typename container_detail::enable_if_c ::value && !::boost::has_move_emulation_enabled::value >::type* =0) { return this->priv_insert(position, u); } #endif //! Requires: position must be a valid iterator of *this. //! //! Effects: Insert a new element before position with mx's resources. //! //! Throws: If memory allocation throws. //! //! Complexity: If position is end(), amortized constant time //! Linear time otherwise. iterator insert(const_iterator position, BOOST_RV_REF(T) x) { typedef repeat_iterator repeat_it; typedef boost::move_iterator repeat_move_it; //Just call more general insert(pos, size, value) and return iterator size_type pos_n = position - cbegin(); this->insert(position ,repeat_move_it(repeat_it(x, 1)) ,repeat_move_it(repeat_it())); return iterator(this->begin() + pos_n); } //! Requires: pos must be a valid iterator of *this. //! //! Effects: Insert n copies of x before pos. //! //! Throws: If memory allocation throws or T's copy constructor throws. //! //! Complexity: Linear to n. void insert(const_iterator position, size_type n, const T& t) { STABLE_VECTOR_CHECK_INVARIANT; this->insert_not_iter(position, n, t); } //! Requires: pos must be a valid iterator of *this. //! //! Effects: Insert a copy of the [first, last) range before pos. //! //! Throws: If memory allocation throws, T's constructor from a //! dereferenced InpIt throws or T's copy constructor throws. //! //! Complexity: Linear to std::distance [first, last). template void insert(const_iterator position,InputIterator first, InputIterator last) { STABLE_VECTOR_CHECK_INVARIANT; this->insert_iter(position,first,last, boost::mpl::not_ >()); } #if defined(BOOST_CONTAINER_PERFECT_FORWARDING) || defined(BOOST_CONTAINER_DOXYGEN_INVOKED) //! Effects: Inserts an object of type T constructed with //! std::forward(args)... in the end of the stable_vector. //! //! Throws: If memory allocation throws or the in-place constructor throws. //! //! Complexity: Amortized constant time. template void emplace_back(Args &&...args) { typedef emplace_functor EmplaceFunctor; typedef emplace_iterator EmplaceIterator; EmplaceFunctor &&ef = EmplaceFunctor(boost::forward(args)...); this->insert(this->cend(), EmplaceIterator(ef), EmplaceIterator()); } //! Requires: position must be a valid iterator of *this. //! //! Effects: Inserts an object of type T constructed with //! std::forward(args)... before position //! //! Throws: If memory allocation throws or the in-place constructor throws. //! //! Complexity: If position is end(), amortized constant time //! Linear time otherwise. template iterator emplace(const_iterator position, Args && ...args) { //Just call more general insert(pos, size, value) and return iterator size_type pos_n = position - cbegin(); typedef emplace_functor EmplaceFunctor; typedef emplace_iterator EmplaceIterator; EmplaceFunctor &&ef = EmplaceFunctor(boost::forward(args)...); this->insert(position, EmplaceIterator(ef), EmplaceIterator()); return iterator(this->begin() + pos_n); } #else #define BOOST_PP_LOCAL_MACRO(n) \ BOOST_PP_EXPR_IF(n, template<) BOOST_PP_ENUM_PARAMS(n, class P) BOOST_PP_EXPR_IF(n, >) \ void emplace_back(BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_LIST, _)) \ { \ typedef BOOST_PP_CAT(BOOST_PP_CAT(emplace_functor, n), arg) \ BOOST_PP_EXPR_IF(n, <) BOOST_PP_ENUM_PARAMS(n, P) BOOST_PP_EXPR_IF(n, >) \ EmplaceFunctor; \ typedef emplace_iterator EmplaceIterator; \ EmplaceFunctor ef BOOST_PP_LPAREN_IF(n) \ BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_FORWARD, _) \ BOOST_PP_RPAREN_IF(n); \ this->insert(this->cend() , EmplaceIterator(ef), EmplaceIterator()); \ } \ \ BOOST_PP_EXPR_IF(n, template<) BOOST_PP_ENUM_PARAMS(n, class P) BOOST_PP_EXPR_IF(n, >) \ iterator emplace(const_iterator pos \ BOOST_PP_ENUM_TRAILING(n, BOOST_CONTAINER_PP_PARAM_LIST, _)) \ { \ typedef BOOST_PP_CAT(BOOST_PP_CAT(emplace_functor, n), arg) \ BOOST_PP_EXPR_IF(n, <) BOOST_PP_ENUM_PARAMS(n, P) BOOST_PP_EXPR_IF(n, >) \ EmplaceFunctor; \ typedef emplace_iterator EmplaceIterator; \ EmplaceFunctor ef BOOST_PP_LPAREN_IF(n) \ BOOST_PP_ENUM(n, BOOST_CONTAINER_PP_PARAM_FORWARD, _) \ BOOST_PP_RPAREN_IF(n); \ size_type pos_n = pos - this->cbegin(); \ this->insert(pos, EmplaceIterator(ef), EmplaceIterator()); \ return iterator(this->begin() + pos_n); \ } \ //! #define BOOST_PP_LOCAL_LIMITS (0, BOOST_CONTAINER_MAX_CONSTRUCTOR_PARAMETERS) #include BOOST_PP_LOCAL_ITERATE() #endif //#ifdef BOOST_CONTAINER_PERFECT_FORWARDING //! Effects: Erases the element at position pos. //! //! Throws: Nothing. //! //! Complexity: Linear to the elements between pos and the //! last element. Constant if pos is the last element. iterator erase(const_iterator position) { STABLE_VECTOR_CHECK_INVARIANT; difference_type d = position - this->cbegin(); impl_iterator it = impl.begin() + d; this->delete_node(*it); it = impl.erase(it); this->align_nodes(it, get_last_align()); return this->begin()+d; } //! Effects: Erases the elements pointed by [first, last). //! //! Throws: Nothing. //! //! Complexity: Linear to the distance between first and last //! plus linear to the elements between pos and the last element. iterator erase(const_iterator first, const_iterator last) { return priv_erase(first, last, alloc_version()); } //! Effects: Swaps the contents of *this and x. //! //! Throws: Nothing. //! //! Complexity: Constant. void swap(stable_vector & x) { STABLE_VECTOR_CHECK_INVARIANT; container_detail::bool_ flag; container_detail::swap_alloc(this->node_alloc(), x.node_alloc(), flag); //vector's allocator is swapped here this->impl.swap(x.impl); this->priv_swap_members(x); } //! Effects: Erases all the elements of the stable_vector. //! //! Throws: Nothing. //! //! Complexity: Linear to the number of elements in the stable_vector. void clear() { this->erase(this->cbegin(),this->cend()); } //! Effects: Tries to deallocate the excess of memory created //! with previous allocations. The size of the stable_vector is unchanged //! //! Throws: If memory allocation throws. //! //! Complexity: Linear to size(). void shrink_to_fit() { if(this->capacity()){ //First empty allocated node pool this->clear_pool(); //If empty completely destroy the index, let's recover default-constructed state if(this->empty()){ this->impl.clear(); this->impl.shrink_to_fit(); this->internal_data.set_end_pointer_to_default_constructed(); } //Otherwise, try to shrink-to-fit the index and readjust pointers if necessary else{ const size_type size = this->size(); const void* old_ptr = &impl[0]; this->impl.shrink_to_fit(); bool realloced = &impl[0] != old_ptr; //Fix the pointers for the newly allocated buffer if(realloced){ this->align_nodes(impl.begin(), impl.begin()+size+1); } } } } /// @cond iterator priv_insert(const_iterator position, const value_type &t) { typedef constant_iterator cvalue_iterator; return this->insert_iter(position, cvalue_iterator(t, 1), cvalue_iterator(), std::forward_iterator_tag()); } void priv_push_back(const value_type &t) { this->insert(end(), t); } template void clear_pool(AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { if(!impl.empty() && impl.back()){ void_ptr &pool_first_ref = impl.end()[-2]; void_ptr &pool_last_ref = impl.back(); multiallocation_chain holder; holder.incorporate_after(holder.before_begin(), pool_first_ref, pool_last_ref, this->internal_data.pool_size); while(!holder.empty()){ node_type_ptr_t n = holder.front(); holder.pop_front(); this->deallocate_one(n); } pool_first_ref = pool_last_ref = 0; this->internal_data.pool_size = 0; } } template void clear_pool(AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { if(!impl.empty() && impl.back()){ void_ptr &pool_first_ref = impl.end()[-2]; void_ptr &pool_last_ref = impl.back(); multiallocation_chain holder; holder.incorporate_after(holder.before_begin(), pool_first_ref, pool_last_ref, internal_data.pool_size); node_alloc().deallocate_individual(boost::move(holder)); pool_first_ref = pool_last_ref = 0; this->internal_data.pool_size = 0; } } void clear_pool() { this->clear_pool(alloc_version()); } void add_to_pool(size_type n) { this->add_to_pool(n, alloc_version()); } template void add_to_pool(size_type n, AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { size_type remaining = n; while(remaining--){ this->put_in_pool(this->allocate_one()); } } template void add_to_pool(size_type n, AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { void_ptr &pool_first_ref = impl.end()[-2]; void_ptr &pool_last_ref = impl.back(); multiallocation_chain holder; holder.incorporate_after(holder.before_begin(), pool_first_ref, pool_last_ref, internal_data.pool_size); //BOOST_STATIC_ASSERT((::boost::has_move_emulation_enabled::value == true)); multiallocation_chain m (node_alloc().allocate_individual(n)); holder.splice_after(holder.before_begin(), m, m.before_begin(), m.last(), n); this->internal_data.pool_size += n; std::pair data(holder.extract_data()); pool_first_ref = data.first; pool_last_ref = data.second; } void put_in_pool(node_type_ptr_t p) { void_ptr &pool_first_ref = impl.end()[-2]; void_ptr &pool_last_ref = impl.back(); multiallocation_chain holder; holder.incorporate_after(holder.before_begin(), pool_first_ref, pool_last_ref, internal_data.pool_size); holder.push_front(p); ++this->internal_data.pool_size; std::pair ret(holder.extract_data()); pool_first_ref = ret.first; pool_last_ref = ret.second; } node_type_ptr_t get_from_pool() { if(!impl.back()){ return node_type_ptr_t(0); } else{ void_ptr &pool_first_ref = impl.end()[-2]; void_ptr &pool_last_ref = impl.back(); multiallocation_chain holder; holder.incorporate_after(holder.before_begin(), pool_first_ref, pool_last_ref, internal_data.pool_size); node_type_ptr_t ret = holder.front(); holder.pop_front(); --this->internal_data.pool_size; if(!internal_data.pool_size){ pool_first_ref = pool_last_ref = void_ptr(0); } else{ std::pair data(holder.extract_data()); pool_first_ref = data.first; pool_last_ref = data.second; } return ret; } } void insert_iter_prolog(size_type n, difference_type d) { initialize_end_node(n); const void* old_ptr = &impl[0]; //size_type old_capacity = capacity(); //size_type old_size = size(); impl.insert(impl.begin()+d, n, 0); bool realloced = &impl[0] != old_ptr; //Fix the pointers for the newly allocated buffer if(realloced){ align_nodes(impl.begin(), impl.begin()+d); } } template void assign_dispatch(InputIterator first, InputIterator last, boost::mpl::false_) { STABLE_VECTOR_CHECK_INVARIANT; iterator first1 = this->begin(); iterator last1 = this->end(); for ( ; first1 != last1 && first != last; ++first1, ++first) *first1 = *first; if (first == last){ this->erase(first1, last1); } else{ this->insert(last1, first, last); } } template void assign_dispatch(Integer n, Integer t, boost::mpl::true_) { typedef constant_iterator cvalue_iterator; this->assign_dispatch(cvalue_iterator(t, n), cvalue_iterator(), boost::mpl::false_()); } iterator priv_erase(const_iterator first, const_iterator last, allocator_v1) { STABLE_VECTOR_CHECK_INVARIANT; difference_type d1 = first - this->cbegin(), d2 = last - this->cbegin(); if(d1 != d2){ impl_iterator it1(impl.begin() + d1), it2(impl.begin() + d2); for(impl_iterator it = it1; it != it2; ++it) this->delete_node(*it); impl_iterator e = impl.erase(it1, it2); this->align_nodes(e, get_last_align()); } return iterator(this->begin() + d1); } impl_iterator get_last_align() { return impl.end() - (ExtraPointers - 1); } const_impl_iterator get_last_align() const { return impl.cend() - (ExtraPointers - 1); } template iterator priv_erase(const_iterator first, const_iterator last, AllocatorVersion, typename boost::container::container_detail::enable_if_c ::value>::type * = 0) { STABLE_VECTOR_CHECK_INVARIANT; return priv_erase(first, last, allocator_v1()); } static node_type_ptr_t node_ptr_cast(const void_ptr &p) { return node_type_ptr_t(static_cast(container_detail::to_raw_pointer(p))); } static node_type_base_ptr_t node_base_ptr_cast(const void_ptr &p) { return node_type_base_ptr_t(static_cast(container_detail::to_raw_pointer(p))); } static value_type& value(const void_ptr &p) { return node_ptr_cast(p)->value; } void initialize_end_node(size_type impl_capacity = 0) { if(impl.empty()){ impl.reserve(impl_capacity + ExtraPointers); impl.resize (ExtraPointers, void_ptr(0)); impl[0] = &this->internal_data.end_node; this->internal_data.end_node.up = &impl[0]; } } void readjust_end_node() { if(!this->impl.empty()){ void_ptr &end_node_ref = *(this->get_last_align()-1); end_node_ref = this->get_end_node(); this->internal_data.end_node.up = &end_node_ref; } else{ this->internal_data.end_node.up = void_ptr(&this->internal_data.end_node.up); } } node_type_ptr_t get_end_node() const { const node_type_base_t* cp = &this->internal_data.end_node; node_type_base_t* p = const_cast(cp); return node_ptr_cast(p); } template void_ptr new_node(const void_ptr &up, Iter it) { node_type_ptr_t p = this->allocate_one(); try{ boost::container::construct_in_place(this->node_alloc(), container_detail::addressof(p->value), it); //This does not throw ::new(static_cast(container_detail::to_raw_pointer(p))) node_type_base_t; p->set_pointer(up); } catch(...){ this->deallocate_one(p); throw; } return p; } void delete_node(const void_ptr &p) { node_type_ptr_t n(node_ptr_cast(p)); allocator_traits:: destroy(this->node_alloc(), container_detail::to_raw_pointer(n)); this->put_in_pool(n); } static void align_nodes(impl_iterator first, impl_iterator last) { while(first!=last){ node_ptr_cast(*first)->up = void_ptr(&*first); ++first; } } void insert_not_iter(const_iterator position, size_type n, const T& t) { typedef constant_iterator cvalue_iterator; this->insert_iter(position, cvalue_iterator(t, n), cvalue_iterator(), std::forward_iterator_tag()); } template void insert_iter(const_iterator position,InputIterator first,InputIterator last, boost::mpl::true_) { typedef typename std::iterator_traits::iterator_category category; this->insert_iter(position, first, last, category()); } template void insert_iter(const_iterator position,InputIterator first,InputIterator last,std::input_iterator_tag) { for(; first!=last; ++first){ this->insert(position, *first); } } template iterator insert_iter(const_iterator position, InputIterator first, InputIterator last, std::forward_iterator_tag) { size_type n = (size_type)std::distance(first,last); difference_type d = position-this->cbegin(); if(n){ this->insert_iter_prolog(n, d); const impl_iterator it(impl.begin() + d); this->insert_iter_fwd(it, first, last, n); //Fix the pointers for the newly allocated buffer this->align_nodes(it + n, get_last_align()); } return this->begin() + d; } template void insert_iter_fwd_alloc(const impl_iterator it, FwdIterator first, FwdIterator last, difference_type n, allocator_v1) { size_type i=0; try{ while(first!=last){ it[i] = this->new_node(void_ptr_ptr(&it[i]), first); ++first; ++i; } } catch(...){ impl_iterator e = impl.erase(it + i, it + n); this->align_nodes(e, get_last_align()); throw; } } template void insert_iter_fwd_alloc(const impl_iterator it, FwdIterator first, FwdIterator last, difference_type n, allocator_v2) { multiallocation_chain mem(node_alloc().allocate_individual(n)); size_type i = 0; node_type_ptr_t p = 0; try{ while(first != last){ p = mem.front(); mem.pop_front(); //This can throw boost::container::construct_in_place(this->node_alloc(), container_detail::addressof(p->value), first); //This does not throw ::new(static_cast(container_detail::to_raw_pointer(p))) node_type_base_t; p->set_pointer(void_ptr_ptr(&it[i])); ++first; it[i] = p; ++i; } } catch(...){ node_alloc().deallocate_one(p); node_alloc().deallocate_many(boost::move(mem)); impl_iterator e = impl.erase(it+i, it+n); this->align_nodes(e, get_last_align()); throw; } } template void insert_iter_fwd(const impl_iterator it, FwdIterator first, FwdIterator last, difference_type n) { size_type i = 0; node_type_ptr_t p = 0; try{ while(first != last){ p = this->get_from_pool(); if(!p){ insert_iter_fwd_alloc(it+i, first, last, n-i, alloc_version()); break; } //This can throw boost::container::construct_in_place(this->node_alloc(), container_detail::addressof(p->value), first); //This does not throw ::new(static_cast(container_detail::to_raw_pointer(p))) node_type_base_t; p->set_pointer(void_ptr_ptr(&it[i])); ++first; it[i]=p; ++i; } } catch(...){ put_in_pool(p); impl_iterator e = impl.erase(it+i, it+n); this->align_nodes(e, get_last_align()); throw; } } template void insert_iter(const_iterator position, InputIterator first, InputIterator last, boost::mpl::false_) { this->insert_not_iter(position, first, last); } #if defined(STABLE_VECTOR_ENABLE_INVARIANT_CHECKING) bool invariant()const { if(impl.empty()) return !capacity() && !size(); if(get_end_node() != *(impl.end() - ExtraPointers)){ return false; } for(const_impl_iterator it = impl.begin(), it_end = get_last_align(); it != it_end; ++it){ if(const_void_ptr(node_ptr_cast(*it)->up) != const_void_ptr(const_void_ptr_ptr(&*it))) return false; } size_type n = capacity()-size(); const void_ptr &pool_head = impl.back(); size_type num_pool = 0; node_type_ptr_t p = node_ptr_cast(pool_head); while(p){ ++num_pool; p = node_ptr_cast(p->up); } return n >= num_pool; } class invariant_checker { invariant_checker(const invariant_checker &); invariant_checker & operator=(const invariant_checker &); const stable_vector* p; public: invariant_checker(const stable_vector& v):p(&v){} ~invariant_checker(){BOOST_ASSERT(p->invariant());} void touch(){} }; #endif class ebo_holder : public node_allocator_type { private: BOOST_MOVABLE_BUT_NOT_COPYABLE(ebo_holder) public: /* explicit ebo_holder(BOOST_RV_REF(ebo_holder) x) : node_allocator_type(boost::move(static_cast(x))) , pool_size(0) , end_node() {} */ template explicit ebo_holder(BOOST_FWD_REF(AllocatorRLValue) a) : node_allocator_type(boost::forward(a)) , pool_size(0) , end_node() { this->set_end_pointer_to_default_constructed(); } ebo_holder() : node_allocator_type() , pool_size(0) , end_node() { this->set_end_pointer_to_default_constructed(); } void set_end_pointer_to_default_constructed() { end_node.set_pointer(void_ptr(&end_node.up)); } size_type pool_size; node_type_base_t end_node; } internal_data; void priv_swap_members(stable_vector &x) { container_detail::do_swap(this->internal_data.pool_size, x.internal_data.pool_size); this->readjust_end_node(); x.readjust_end_node(); } node_allocator_type &node_alloc() { return internal_data; } const node_allocator_type &node_alloc() const { return internal_data; } impl_type impl; /// @endcond }; template bool operator==(const stable_vector& x,const stable_vector& y) { return x.size()==y.size()&&std::equal(x.begin(),x.end(),y.begin()); } template bool operator< (const stable_vector& x,const stable_vector& y) { return std::lexicographical_compare(x.begin(),x.end(),y.begin(),y.end()); } template bool operator!=(const stable_vector& x,const stable_vector& y) { return !(x==y); } template bool operator> (const stable_vector& x,const stable_vector& y) { return y bool operator>=(const stable_vector& x,const stable_vector& y) { return !(x bool operator<=(const stable_vector& x,const stable_vector& y) { return !(x>y); } // specialized algorithms: template void swap(stable_vector& x,stable_vector& y) { x.swap(y); } /// @cond #undef STABLE_VECTOR_CHECK_INVARIANT /// @endcond }} #include #endif //BOOST_CONTAINER_STABLE_VECTOR_HPP