/* Copyright 2016-2020 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) * * See http://www.boost.org/libs/poly_collection for library home page. */ #ifndef BOOST_POLY_COLLECTION_DETAIL_SPLIT_SEGMENT_HPP #define BOOST_POLY_COLLECTION_DETAIL_SPLIT_SEGMENT_HPP #if defined(_MSC_VER) #pragma once #endif #include #include #include #include #include #include #include namespace boost{ namespace poly_collection{ namespace detail{ /* segment_backend implementation that maintains two internal vectors, one for * value_type's (the index) and another for the concrete elements those refer * to (the store). * * Requires: * - [const_]base_iterator is constructible from value_type*. * - value_type is copy constructible. * - Model::make_value_type(x) returns a value_type created from a reference * to the concrete type. * * Conversion from base_iterator to local_iterator requires accesing * value_type internal info, so the end() base_iterator has to be made to point * to a valid element of index, which implies size(index)=size(store)+1. This * slightly complicates the memory management. */ template class split_segment:public segment_backend { using value_type=typename Model::value_type; using store_value_type=value_holder; using store=std::vector< store_value_type, typename std::allocator_traits:: template rebind_alloc >; using store_iterator=typename store::iterator; using const_store_iterator=typename store::const_iterator; using index=std::vector< value_type, typename std::allocator_traits:: template rebind_alloc >; using const_index_iterator=typename index::const_iterator; using segment_backend=detail::segment_backend; using typename segment_backend::segment_backend_unique_ptr; using typename segment_backend::value_pointer; using typename segment_backend::const_value_pointer; using typename segment_backend::base_iterator; using typename segment_backend::const_base_iterator; using const_iterator= typename segment_backend::template const_iterator; using typename segment_backend::base_sentinel; using typename segment_backend::range; using segment_allocator_type=typename std::allocator_traits:: template rebind_alloc; public: virtual ~split_segment()=default; static segment_backend_unique_ptr make(const segment_allocator_type& al) { return new_(al,al); } virtual segment_backend_unique_ptr copy()const { return new_(s.get_allocator(),store{s}); } virtual segment_backend_unique_ptr copy(const Allocator& al)const { return new_(al,store{s,al}); } virtual segment_backend_unique_ptr empty_copy(const Allocator& al)const { return new_(al,al); } virtual segment_backend_unique_ptr move(const Allocator& al) { return new_(al,store{std::move(s),al}); } virtual bool equal(const segment_backend& x)const { return s==static_cast(x).s; } virtual Allocator get_allocator()const noexcept {return s.get_allocator();} virtual base_iterator begin()const noexcept{return nv_begin();} base_iterator nv_begin()const noexcept {return base_iterator{value_ptr(i.data())};} virtual base_iterator end()const noexcept{return nv_end();} base_iterator nv_end()const noexcept {return base_iterator{value_ptr(i.data()+s.size())};} virtual bool empty()const noexcept{return nv_empty();} bool nv_empty()const noexcept{return s.empty();} virtual std::size_t size()const noexcept{return nv_size();} std::size_t nv_size()const noexcept{return s.size();} virtual std::size_t max_size()const noexcept{return nv_max_size();} std::size_t nv_max_size()const noexcept{return s.max_size()-1;} virtual std::size_t capacity()const noexcept{return nv_capacity();} std::size_t nv_capacity()const noexcept{return s.capacity();} virtual base_sentinel reserve(std::size_t n){return nv_reserve(n);} base_sentinel nv_reserve(std::size_t n) { bool rebuild=n>s.capacity(); i.reserve(n+1); s.reserve(n); if(rebuild)rebuild_index(); return sentinel(); }; virtual base_sentinel shrink_to_fit(){return nv_shrink_to_fit();} base_sentinel nv_shrink_to_fit() { try{ auto p=s.data(); if(!s.empty())s.shrink_to_fit(); else{ store ss{s.get_allocator()}; ss.reserve(1); /* --> s.data()!=nullptr */ s.swap(ss); } if(p!=s.data()){ index ii{{},i.get_allocator()}; ii.reserve(s.capacity()+1); i.swap(ii); build_index(); } } catch(...){ rebuild_index(); throw; } return sentinel(); } template range nv_emplace(Iterator p,Args&&... args) { auto q=prereserve(p); auto it=s.emplace( iterator_from(q), value_holder_emplacing_ctor,std::forward(args)...); push_index_entry(); return range_from(it); } template range nv_emplace_back(Args&&... args) { prereserve(); s.emplace_back(value_holder_emplacing_ctor,std::forward(args)...); push_index_entry(); return range_from(s.size()-1); } virtual range push_back(const_value_pointer x) {return nv_push_back(const_concrete_ref(x));} range nv_push_back(const Concrete& x) { prereserve(); s.emplace_back(x); push_index_entry(); return range_from(s.size()-1); } virtual range push_back_move(value_pointer x) {return nv_push_back(std::move(concrete_ref(x)));} range nv_push_back(Concrete&& x) { prereserve(); s.emplace_back(std::move(x)); push_index_entry(); return range_from(s.size()-1); } virtual range insert(const_base_iterator p,const_value_pointer x) {return nv_insert(const_iterator(p),const_concrete_ref(x));} range nv_insert(const_iterator p,const Concrete& x) { p=prereserve(p); auto it=s.emplace(iterator_from(p),x); push_index_entry(); return range_from(it); } virtual range insert_move(const_base_iterator p,value_pointer x) {return nv_insert(const_iterator(p),std::move(concrete_ref(x)));} range nv_insert(const_iterator p,Concrete&& x) { p=prereserve(p); auto it=s.emplace(iterator_from(p),std::move(x)); push_index_entry(); return range_from(it); } template range nv_insert(InputIterator first,InputIterator last) { return nv_insert( const_iterator(concrete_ptr(s.data()+s.size())),first,last); } template range nv_insert(const_iterator p,InputIterator first,InputIterator last) { return insert( p,first,last, typename std::iterator_traits::iterator_category{}); } virtual range erase(const_base_iterator p) {return nv_erase(const_iterator(p));} range nv_erase(const_iterator p) { pop_index_entry(); return range_from(s.erase(iterator_from(p))); } virtual range erase(const_base_iterator first,const_base_iterator last) {return nv_erase(const_iterator(first),const_iterator(last));} range nv_erase(const_iterator first,const_iterator last) { std::size_t n=s.size(); auto it=s.erase(iterator_from(first),iterator_from(last)); pop_index_entry(n-s.size()); return range_from(it); } virtual range erase_till_end(const_base_iterator first) { std::size_t n=s.size(); auto it=s.erase(iterator_from(first),s.end()); pop_index_entry(n-s.size()); return range_from(it); } virtual range erase_from_begin(const_base_iterator last) { std::size_t n=s.size(); auto it=s.erase(s.begin(),iterator_from(last)); pop_index_entry(n-s.size()); return range_from(it); } base_sentinel clear()noexcept{return nv_clear();} base_sentinel nv_clear()noexcept { s.clear(); for(std::size_t n=i.size()-1;n--;)i.pop_back(); return sentinel(); } private: template static segment_backend_unique_ptr new_( segment_allocator_type al,Args&&... args) { auto p=std::allocator_traits::allocate(al,1); try{ ::new ((void*)p) split_segment{std::forward(args)...}; } catch(...){ std::allocator_traits::deallocate(al,p,1); throw; } return {p,&delete_}; } static void delete_(segment_backend* p) { auto q=static_cast(p); auto al=segment_allocator_type{q->s.get_allocator()}; q->~split_segment(); std::allocator_traits::deallocate(al,q,1); } split_segment(const Allocator& al): s{typename store::allocator_type{al}}, i{{},typename index::allocator_type{al}} { s.reserve(1); /* --> s.data()!=nullptr */ build_index(); } split_segment(store&& s_): s{std::move(s_)},i{{},typename index::allocator_type{s.get_allocator()}} { s.reserve(1); /* --> s.data()!=nullptr */ build_index(); } void prereserve() { if(s.size()==s.capacity())expand(); } const_base_iterator prereserve(const_base_iterator p) { if(s.size()==s.capacity()){ auto n=p-i.data(); expand(); return const_base_iterator{i.data()+n}; } else return p; } const_iterator prereserve(const_iterator p) { if(s.size()==s.capacity()){ auto n=p-const_concrete_ptr(s.data()); expand(); return const_concrete_ptr(s.data())+n; } else return p; } const_iterator prereserve(const_iterator p,std::size_t m) { if(s.size()+m>s.capacity()){ auto n=p-const_concrete_ptr(s.data()); expand(m); return const_concrete_ptr(s.data())+n; } else return p; } void expand() { std::size_t c= s.size()<=1||(s.max_size()-1-s.size())/2(p); } static Concrete& concrete_ref(store_value_type& r)noexcept { return *concrete_ptr(&r); } static const Concrete& const_concrete_ref(const_value_pointer p)noexcept { return *static_cast(p); } static Concrete* concrete_ptr(store_value_type* p)noexcept { return reinterpret_cast( static_cast*>(p)); } static const Concrete* const_concrete_ptr(const store_value_type* p)noexcept { return concrete_ptr(const_cast(p)); } static value_type* value_ptr(const value_type* p)noexcept { return const_cast(p); } /* It would have sufficed if iterator_from returned const_store_iterator * except for the fact that some old versions of libstdc++ claiming to be * C++11 compliant do not however provide std::vector modifier ops taking * const_iterator's. */ store_iterator iterator_from(const_base_iterator p) { return s.begin()+(p-i.data()); } store_iterator iterator_from(const_iterator p) { return s.begin()+(p-const_concrete_ptr(s.data())); } base_sentinel sentinel()const noexcept { return base_iterator{value_ptr(i.data()+s.size())}; } range range_from(const_store_iterator it)const { return {base_iterator{value_ptr(i.data()+(it-s.begin()))},sentinel()}; } range range_from(std::size_t n)const { return {base_iterator{value_ptr(i.data()+n)},sentinel()}; } template range insert( const_iterator p,InputIterator first,InputIterator last, std::input_iterator_tag) { std::size_t n=0; for(;first!=last;++first,++n,++p){ p=prereserve(p); s.emplace(iterator_from(p),*first); push_index_entry(); } return range_from(iterator_from(p-n)); } template range insert( const_iterator p,InputIterator first,InputIterator last, std::forward_iterator_tag) { auto n=s.size(); auto m=static_cast(std::distance(first,last)); if(m){ p=prereserve(p,m); try{ s.insert(iterator_from(p),first,last); } catch(...){ build_index(n+1); throw; } build_index(n+1); } return range_from(iterator_from(p)); } store s; index i; }; } /* namespace poly_collection::detail */ } /* namespace poly_collection */ } /* namespace boost */ #endif