• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2016-2020 Joaquin M Lopez Munoz.
2  * Distributed under the Boost Software License, Version 1.0.
3  * (See accompanying file LICENSE_1_0.txt or copy at
4  * http://www.boost.org/LICENSE_1_0.txt)
5  *
6  * See http://www.boost.org/libs/poly_collection for library home page.
7  */
8 
9 #ifndef BOOST_POLY_COLLECTION_DETAIL_SPLIT_SEGMENT_HPP
10 #define BOOST_POLY_COLLECTION_DETAIL_SPLIT_SEGMENT_HPP
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <boost/poly_collection/detail/segment_backend.hpp>
17 #include <boost/poly_collection/detail/value_holder.hpp>
18 #include <iterator>
19 #include <memory>
20 #include <new>
21 #include <utility>
22 #include <vector>
23 
24 namespace boost{
25 
26 namespace poly_collection{
27 
28 namespace detail{
29 
30 /* segment_backend implementation that maintains two internal vectors, one for
31  * value_type's (the index) and another for the concrete elements those refer
32  * to (the store).
33  *
34  * Requires:
35  *   - [const_]base_iterator is constructible from value_type*.
36  *   - value_type is copy constructible.
37  *   - Model::make_value_type(x) returns a value_type created from a reference
38  *     to the concrete type.
39  *
40  * Conversion from base_iterator to local_iterator<Concrete> requires accesing
41  * value_type internal info, so the end() base_iterator has to be made to point
42  * to a valid element of index, which implies size(index)=size(store)+1. This
43  * slightly complicates the memory management.
44  */
45 
46 template<typename Model,typename Concrete,typename Allocator>
47 class split_segment:public segment_backend<Model,Allocator>
48 {
49   using value_type=typename Model::value_type;
50   using store_value_type=value_holder<Concrete>;
51   using store=std::vector<
52     store_value_type,
53     typename std::allocator_traits<Allocator>::
54       template rebind_alloc<store_value_type>
55   >;
56   using store_iterator=typename store::iterator;
57   using const_store_iterator=typename store::const_iterator;
58   using index=std::vector<
59     value_type,
60     typename std::allocator_traits<Allocator>::
61       template rebind_alloc<value_type>
62   >;
63   using const_index_iterator=typename index::const_iterator;
64   using segment_backend=detail::segment_backend<Model,Allocator>;
65   using typename segment_backend::segment_backend_unique_ptr;
66   using typename segment_backend::value_pointer;
67   using typename segment_backend::const_value_pointer;
68   using typename segment_backend::base_iterator;
69   using typename segment_backend::const_base_iterator;
70   using const_iterator=
71     typename segment_backend::template const_iterator<Concrete>;
72   using typename segment_backend::base_sentinel;
73   using typename segment_backend::range;
74   using segment_allocator_type=typename std::allocator_traits<Allocator>::
75     template rebind_alloc<split_segment>;
76 
77 public:
78   virtual ~split_segment()=default;
79 
make(const segment_allocator_type & al)80   static segment_backend_unique_ptr make(const segment_allocator_type& al)
81   {
82     return new_(al,al);
83   }
84 
copy() const85   virtual segment_backend_unique_ptr copy()const
86   {
87     return new_(s.get_allocator(),store{s});
88   }
89 
copy(const Allocator & al) const90   virtual segment_backend_unique_ptr copy(const Allocator& al)const
91   {
92     return new_(al,store{s,al});
93   }
94 
empty_copy(const Allocator & al) const95   virtual segment_backend_unique_ptr empty_copy(const Allocator& al)const
96   {
97     return new_(al,al);
98   }
99 
move(const Allocator & al)100   virtual segment_backend_unique_ptr move(const Allocator& al)
101   {
102     return new_(al,store{std::move(s),al});
103   }
104 
equal(const segment_backend & x) const105   virtual bool equal(const segment_backend& x)const
106   {
107     return s==static_cast<const split_segment&>(x).s;
108   }
109 
get_allocator() const110   virtual Allocator     get_allocator()const noexcept
111                          {return s.get_allocator();}
begin() const112   virtual base_iterator begin()const noexcept{return nv_begin();}
nv_begin() const113   base_iterator         nv_begin()const noexcept
114                          {return base_iterator{value_ptr(i.data())};}
end() const115   virtual base_iterator end()const noexcept{return nv_end();}
nv_end() const116   base_iterator         nv_end()const noexcept
117                          {return base_iterator{value_ptr(i.data()+s.size())};}
empty() const118   virtual bool          empty()const noexcept{return nv_empty();}
nv_empty() const119   bool                  nv_empty()const noexcept{return s.empty();}
size() const120   virtual std::size_t   size()const noexcept{return nv_size();}
nv_size() const121   std::size_t           nv_size()const noexcept{return s.size();}
max_size() const122   virtual std::size_t   max_size()const noexcept{return nv_max_size();}
nv_max_size() const123   std::size_t           nv_max_size()const noexcept{return s.max_size()-1;}
capacity() const124   virtual std::size_t   capacity()const noexcept{return nv_capacity();}
nv_capacity() const125   std::size_t           nv_capacity()const noexcept{return s.capacity();}
126 
reserve(std::size_t n)127   virtual base_sentinel reserve(std::size_t n){return nv_reserve(n);}
128 
nv_reserve(std::size_t n)129   base_sentinel nv_reserve(std::size_t n)
130   {
131     bool rebuild=n>s.capacity();
132     i.reserve(n+1);
133     s.reserve(n);
134     if(rebuild)rebuild_index();
135     return sentinel();
136   };
137 
shrink_to_fit()138   virtual base_sentinel shrink_to_fit(){return nv_shrink_to_fit();}
139 
nv_shrink_to_fit()140   base_sentinel nv_shrink_to_fit()
141   {
142     try{
143       auto p=s.data();
144       if(!s.empty())s.shrink_to_fit();
145       else{
146         store ss{s.get_allocator()};
147         ss.reserve(1); /* --> s.data()!=nullptr */
148         s.swap(ss);
149       }
150       if(p!=s.data()){
151         index ii{{},i.get_allocator()};
152         ii.reserve(s.capacity()+1);
153         i.swap(ii);
154         build_index();
155       }
156     }
157     catch(...){
158       rebuild_index();
159       throw;
160     }
161     return sentinel();
162   }
163 
164   template<typename Iterator,typename... Args>
nv_emplace(Iterator p,Args &&...args)165   range nv_emplace(Iterator p,Args&&... args)
166   {
167     auto q=prereserve(p);
168     auto it=s.emplace(
169       iterator_from(q),
170       value_holder_emplacing_ctor,std::forward<Args>(args)...);
171     push_index_entry();
172     return range_from(it);
173   }
174 
175   template<typename... Args>
nv_emplace_back(Args &&...args)176   range nv_emplace_back(Args&&... args)
177   {
178     prereserve();
179     s.emplace_back(value_holder_emplacing_ctor,std::forward<Args>(args)...);
180     push_index_entry();
181     return range_from(s.size()-1);
182   }
183 
push_back(const_value_pointer x)184   virtual range push_back(const_value_pointer x)
185   {return nv_push_back(const_concrete_ref(x));}
186 
nv_push_back(const Concrete & x)187   range nv_push_back(const Concrete& x)
188   {
189     prereserve();
190     s.emplace_back(x);
191     push_index_entry();
192     return range_from(s.size()-1);
193   }
194 
push_back_move(value_pointer x)195   virtual range push_back_move(value_pointer x)
196   {return nv_push_back(std::move(concrete_ref(x)));}
197 
nv_push_back(Concrete && x)198   range nv_push_back(Concrete&& x)
199   {
200     prereserve();
201     s.emplace_back(std::move(x));
202     push_index_entry();
203     return range_from(s.size()-1);
204   }
205 
insert(const_base_iterator p,const_value_pointer x)206   virtual range insert(const_base_iterator p,const_value_pointer x)
207   {return nv_insert(const_iterator(p),const_concrete_ref(x));}
208 
nv_insert(const_iterator p,const Concrete & x)209   range nv_insert(const_iterator p,const Concrete& x)
210   {
211     p=prereserve(p);
212     auto it=s.emplace(iterator_from(p),x);
213     push_index_entry();
214     return range_from(it);
215   }
216 
insert_move(const_base_iterator p,value_pointer x)217   virtual range insert_move(const_base_iterator p,value_pointer x)
218   {return nv_insert(const_iterator(p),std::move(concrete_ref(x)));}
219 
nv_insert(const_iterator p,Concrete && x)220   range nv_insert(const_iterator p,Concrete&& x)
221   {
222     p=prereserve(p);
223     auto it=s.emplace(iterator_from(p),std::move(x));
224     push_index_entry();
225     return range_from(it);
226   }
227 
228   template<typename InputIterator>
nv_insert(InputIterator first,InputIterator last)229   range nv_insert(InputIterator first,InputIterator last)
230   {
231     return nv_insert(
232       const_iterator(concrete_ptr(s.data()+s.size())),first,last);
233   }
234 
235   template<typename InputIterator>
nv_insert(const_iterator p,InputIterator first,InputIterator last)236   range nv_insert(const_iterator p,InputIterator first,InputIterator last)
237   {
238     return insert(
239       p,first,last,
240       typename std::iterator_traits<InputIterator>::iterator_category{});
241   }
242 
erase(const_base_iterator p)243   virtual range erase(const_base_iterator p)
244   {return nv_erase(const_iterator(p));}
245 
nv_erase(const_iterator p)246   range nv_erase(const_iterator p)
247   {
248     pop_index_entry();
249     return range_from(s.erase(iterator_from(p)));
250   }
251 
erase(const_base_iterator first,const_base_iterator last)252   virtual range erase(const_base_iterator first,const_base_iterator last)
253   {return nv_erase(const_iterator(first),const_iterator(last));}
254 
nv_erase(const_iterator first,const_iterator last)255   range nv_erase(const_iterator first,const_iterator last)
256   {
257     std::size_t n=s.size();
258     auto it=s.erase(iterator_from(first),iterator_from(last));
259     pop_index_entry(n-s.size());
260     return range_from(it);
261   }
262 
erase_till_end(const_base_iterator first)263   virtual range erase_till_end(const_base_iterator first)
264   {
265     std::size_t n=s.size();
266     auto it=s.erase(iterator_from(first),s.end());
267     pop_index_entry(n-s.size());
268     return range_from(it);
269   }
270 
erase_from_begin(const_base_iterator last)271   virtual range erase_from_begin(const_base_iterator last)
272   {
273     std::size_t n=s.size();
274     auto it=s.erase(s.begin(),iterator_from(last));
275     pop_index_entry(n-s.size());
276     return range_from(it);
277   }
278 
clear()279   base_sentinel clear()noexcept{return nv_clear();}
280 
nv_clear()281   base_sentinel nv_clear()noexcept
282   {
283     s.clear();
284     for(std::size_t n=i.size()-1;n--;)i.pop_back();
285     return sentinel();
286   }
287 
288 private:
289   template<typename... Args>
new_(segment_allocator_type al,Args &&...args)290   static segment_backend_unique_ptr new_(
291     segment_allocator_type al,Args&&... args)
292   {
293     auto p=std::allocator_traits<segment_allocator_type>::allocate(al,1);
294     try{
295       ::new ((void*)p) split_segment{std::forward<Args>(args)...};
296     }
297     catch(...){
298       std::allocator_traits<segment_allocator_type>::deallocate(al,p,1);
299       throw;
300     }
301     return {p,&delete_};
302   }
303 
delete_(segment_backend * p)304   static void delete_(segment_backend* p)
305   {
306     auto q=static_cast<split_segment*>(p);
307     auto al=segment_allocator_type{q->s.get_allocator()};
308     q->~split_segment();
309     std::allocator_traits<segment_allocator_type>::deallocate(al,q,1);
310   }
311 
split_segment(const Allocator & al)312   split_segment(const Allocator& al):
313     s{typename store::allocator_type{al}},
314     i{{},typename index::allocator_type{al}}
315   {
316     s.reserve(1); /* --> s.data()!=nullptr */
317     build_index();
318   }
319 
split_segment(store && s_)320   split_segment(store&& s_):
321     s{std::move(s_)},i{{},typename index::allocator_type{s.get_allocator()}}
322   {
323     s.reserve(1); /* --> s.data()!=nullptr */
324     build_index();
325   }
326 
prereserve()327   void prereserve()
328   {
329     if(s.size()==s.capacity())expand();
330   }
331 
prereserve(const_base_iterator p)332   const_base_iterator prereserve(const_base_iterator p)
333   {
334     if(s.size()==s.capacity()){
335       auto n=p-i.data();
336       expand();
337       return const_base_iterator{i.data()+n};
338     }
339     else return p;
340   }
341 
prereserve(const_iterator p)342   const_iterator prereserve(const_iterator p)
343   {
344     if(s.size()==s.capacity()){
345       auto n=p-const_concrete_ptr(s.data());
346       expand();
347       return const_concrete_ptr(s.data())+n;
348     }
349     else return p;
350   }
351 
prereserve(const_iterator p,std::size_t m)352   const_iterator prereserve(const_iterator p,std::size_t m)
353   {
354     if(s.size()+m>s.capacity()){
355       auto n=p-const_concrete_ptr(s.data());
356       expand(m);
357       return const_concrete_ptr(s.data())+n;
358     }
359     else return p;
360   }
361 
expand()362   void expand()
363   {
364     std::size_t c=
365       s.size()<=1||(s.max_size()-1-s.size())/2<s.size()?
366         s.size()+1:
367         s.size()+s.size()/2;
368     i.reserve(c+1);
369     s.reserve(c);
370     rebuild_index();
371   }
372 
expand(std::size_t m)373   void expand(std::size_t m)
374   {
375     i.reserve(s.size()+m+1);
376     s.reserve(s.size()+m);
377     rebuild_index();
378   }
379 
build_index(std::size_t start=0)380   void build_index(std::size_t start=0)
381   {
382     for(std::size_t n=start,m=s.size();n<=m;++n){
383       i.push_back(Model::make_value_type(concrete_ref(s.data()[n])));
384     };
385   }
386 
rebuild_index()387   void rebuild_index()
388   {
389     i.clear();
390     build_index();
391   }
392 
push_index_entry()393   void push_index_entry()
394   {
395     build_index(s.size());
396   }
397 
pop_index_entry(std::size_t n=1)398   void pop_index_entry(std::size_t n=1)
399   {
400     while(n--)i.pop_back();
401   }
402 
concrete_ref(value_pointer p)403   static Concrete& concrete_ref(value_pointer p)noexcept
404   {
405     return *static_cast<Concrete*>(p);
406   }
407 
concrete_ref(store_value_type & r)408   static Concrete& concrete_ref(store_value_type& r)noexcept
409   {
410     return *concrete_ptr(&r);
411   }
412 
const_concrete_ref(const_value_pointer p)413   static const Concrete& const_concrete_ref(const_value_pointer p)noexcept
414   {
415     return *static_cast<const Concrete*>(p);
416   }
417 
concrete_ptr(store_value_type * p)418   static Concrete* concrete_ptr(store_value_type* p)noexcept
419   {
420     return reinterpret_cast<Concrete*>(
421       static_cast<value_holder_base<Concrete>*>(p));
422   }
423 
const_concrete_ptr(const store_value_type * p)424   static const Concrete* const_concrete_ptr(const store_value_type* p)noexcept
425   {
426     return concrete_ptr(const_cast<store_value_type*>(p));
427   }
428 
value_ptr(const value_type * p)429   static value_type* value_ptr(const value_type* p)noexcept
430   {
431     return const_cast<value_type*>(p);
432   }
433 
434   /* It would have sufficed if iterator_from returned const_store_iterator
435    * except for the fact that some old versions of libstdc++ claiming to be
436    * C++11 compliant do not however provide std::vector modifier ops taking
437    * const_iterator's.
438    */
439 
iterator_from(const_base_iterator p)440   store_iterator iterator_from(const_base_iterator p)
441   {
442     return s.begin()+(p-i.data());
443   }
444 
iterator_from(const_iterator p)445   store_iterator iterator_from(const_iterator p)
446   {
447     return s.begin()+(p-const_concrete_ptr(s.data()));
448   }
449 
sentinel() const450   base_sentinel sentinel()const noexcept
451   {
452     return base_iterator{value_ptr(i.data()+s.size())};
453   }
454 
range_from(const_store_iterator it) const455   range range_from(const_store_iterator it)const
456   {
457     return {base_iterator{value_ptr(i.data()+(it-s.begin()))},sentinel()};
458   }
459 
range_from(std::size_t n) const460   range range_from(std::size_t n)const
461   {
462     return {base_iterator{value_ptr(i.data()+n)},sentinel()};
463   }
464 
465   template<typename InputIterator>
insert(const_iterator p,InputIterator first,InputIterator last,std::input_iterator_tag)466   range insert(
467     const_iterator p,InputIterator first,InputIterator last,
468     std::input_iterator_tag)
469   {
470     std::size_t n=0;
471     for(;first!=last;++first,++n,++p){
472       p=prereserve(p);
473       s.emplace(iterator_from(p),*first);
474       push_index_entry();
475     }
476     return range_from(iterator_from(p-n));
477   }
478 
479   template<typename InputIterator>
insert(const_iterator p,InputIterator first,InputIterator last,std::forward_iterator_tag)480   range insert(
481     const_iterator p,InputIterator first,InputIterator last,
482     std::forward_iterator_tag)
483   {
484     auto n=s.size();
485     auto m=static_cast<std::size_t>(std::distance(first,last));
486     if(m){
487       p=prereserve(p,m);
488       try{
489         s.insert(iterator_from(p),first,last);
490       }
491       catch(...){
492         build_index(n+1);
493         throw;
494       }
495       build_index(n+1);
496     }
497     return range_from(iterator_from(p));
498   }
499 
500   store s;
501   index i;
502 };
503 
504 } /* namespace poly_collection::detail */
505 
506 } /* namespace poly_collection */
507 
508 } /* namespace boost */
509 
510 #endif
511