• 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_PACKED_SEGMENT_HPP
10 #define BOOST_POLY_COLLECTION_DETAIL_PACKED_SEGMENT_HPP
11 
12 #if defined(_MSC_VER)
13 #pragma once
14 #endif
15 
16 #include <boost/detail/workaround.hpp>
17 #include <boost/poly_collection/detail/segment_backend.hpp>
18 #include <boost/poly_collection/detail/value_holder.hpp>
19 #include <memory>
20 #include <new>
21 #include <vector>
22 #include <utility>
23 
24 namespace boost{
25 
26 namespace poly_collection{
27 
28 namespace detail{
29 
30 /* segment_backend implementation where value_type& and Concrete& actually refer
31  * to the same stored entity.
32  *
33  * Requires:
34  *  - [const_]base_iterator is a stride iterator constructible from
35  *    {value_type*,sizeof(store_value_type)}.
36  *  - Model provides a function value_ptr for
37  *    const Concrete* -> const value_type* conversion.
38  */
39 
40 template<typename Model,typename Concrete,typename Allocator>
41 class packed_segment:public segment_backend<Model,Allocator>
42 {
43   using value_type=typename Model::value_type;
44   using store_value_type=value_holder<Concrete>;
45   using store=std::vector<
46     store_value_type,
47     typename std::allocator_traits<Allocator>::
48       template rebind_alloc<store_value_type>
49   >;
50   using store_iterator=typename store::iterator;
51   using const_store_iterator=typename store::const_iterator;
52   using segment_backend=detail::segment_backend<Model,Allocator>;
53   using typename segment_backend::segment_backend_unique_ptr;
54   using typename segment_backend::value_pointer;
55   using typename segment_backend::const_value_pointer;
56   using typename segment_backend::base_iterator;
57   using typename segment_backend::const_base_iterator;
58   using const_iterator=
59     typename segment_backend::template const_iterator<Concrete>;
60   using typename segment_backend::base_sentinel;
61   using typename segment_backend::range;
62   using segment_allocator_type=typename std::allocator_traits<Allocator>::
63     template rebind_alloc<packed_segment>;
64 
65 public:
66   virtual ~packed_segment()=default;
67 
make(const segment_allocator_type & al)68   static segment_backend_unique_ptr make(const segment_allocator_type& al)
69   {
70     return new_(al,al);
71   }
72 
copy() const73   virtual segment_backend_unique_ptr copy()const
74   {
75     return new_(s.get_allocator(),store{s});
76   }
77 
copy(const Allocator & al) const78   virtual segment_backend_unique_ptr copy(const Allocator& al)const
79   {
80     return new_(al,store{s,al});
81   }
82 
empty_copy(const Allocator & al) const83   virtual segment_backend_unique_ptr empty_copy(const Allocator& al)const
84   {
85     return new_(al,al);
86   }
87 
move(const Allocator & al)88   virtual segment_backend_unique_ptr move(const Allocator& al)
89   {
90     return new_(al,store{std::move(s),al});
91   }
92 
equal(const segment_backend & x) const93   virtual bool equal(const segment_backend& x)const
94   {
95     return s==static_cast<const packed_segment&>(x).s;
96   }
97 
get_allocator() const98   virtual Allocator get_allocator()const noexcept{return s.get_allocator();}
99 
begin() const100   virtual base_iterator begin()const noexcept{return nv_begin();}
101 
nv_begin() const102   base_iterator nv_begin()const noexcept
103   {
104     return {value_ptr(s.data()),sizeof(store_value_type)};
105   }
106 
end() const107   virtual base_iterator end()const noexcept{return nv_end();}
108 
nv_end() const109   base_iterator nv_end()const noexcept
110   {
111     return {value_ptr(s.data()+s.size()),sizeof(store_value_type)};
112   }
113 
empty() const114   virtual bool          empty()const noexcept{return nv_empty();}
nv_empty() const115   bool                  nv_empty()const noexcept{return s.empty();}
size() const116   virtual std::size_t   size()const noexcept{return nv_size();}
nv_size() const117   std::size_t           nv_size()const noexcept{return s.size();}
max_size() const118   virtual std::size_t   max_size()const noexcept{return nv_max_size();}
nv_max_size() const119   std::size_t           nv_max_size()const noexcept{return s.max_size();}
capacity() const120   virtual std::size_t   capacity()const noexcept{return nv_capacity();}
nv_capacity() const121   std::size_t           nv_capacity()const noexcept{return s.capacity();}
reserve(std::size_t n)122   virtual base_sentinel reserve(std::size_t n){return nv_reserve(n);}
nv_reserve(std::size_t n)123   base_sentinel         nv_reserve(std::size_t n)
124                           {s.reserve(n);return sentinel();}
shrink_to_fit()125   virtual base_sentinel shrink_to_fit(){return nv_shrink_to_fit();}
nv_shrink_to_fit()126   base_sentinel         nv_shrink_to_fit()
127                           {s.shrink_to_fit();return sentinel();}
128 
129   template<typename Iterator,typename... Args>
nv_emplace(Iterator p,Args &&...args)130   range nv_emplace(Iterator p,Args&&... args)
131   {
132     return range_from(
133       s.emplace(
134         iterator_from(p),
135         value_holder_emplacing_ctor,std::forward<Args>(args)...));
136   }
137 
138   template<typename... Args>
nv_emplace_back(Args &&...args)139   range nv_emplace_back(Args&&... args)
140   {
141     s.emplace_back(value_holder_emplacing_ctor,std::forward<Args>(args)...);
142     return range_from(s.size()-1);
143   }
144 
push_back(const_value_pointer x)145   virtual range push_back(const_value_pointer x)
146   {return nv_push_back(const_concrete_ref(x));}
147 
nv_push_back(const Concrete & x)148   range nv_push_back(const Concrete& x)
149   {
150     s.emplace_back(x);
151     return range_from(s.size()-1);
152   }
153 
push_back_move(value_pointer x)154   virtual range push_back_move(value_pointer x)
155   {return nv_push_back(std::move(concrete_ref(x)));}
156 
nv_push_back(Concrete && x)157   range nv_push_back(Concrete&& x)
158   {
159     s.emplace_back(std::move(x));
160     return range_from(s.size()-1);
161   }
162 
insert(const_base_iterator p,const_value_pointer x)163   virtual range insert(const_base_iterator p,const_value_pointer x)
164   {return nv_insert(const_iterator(p),const_concrete_ref(x));}
165 
nv_insert(const_iterator p,const Concrete & x)166   range nv_insert(const_iterator p,const Concrete& x)
167   {
168     return range_from(s.emplace(iterator_from(p),x));
169   }
170 
insert_move(const_base_iterator p,value_pointer x)171   virtual range insert_move(const_base_iterator p,value_pointer x)
172   {return nv_insert(const_iterator(p),std::move(concrete_ref(x)));}
173 
nv_insert(const_iterator p,Concrete && x)174   range nv_insert(const_iterator p,Concrete&& x)
175   {
176     return range_from(s.emplace(iterator_from(p),std::move(x)));
177   }
178 
179   template<typename InputIterator>
nv_insert(InputIterator first,InputIterator last)180   range nv_insert(InputIterator first,InputIterator last)
181   {
182     return nv_insert(
183      const_iterator(concrete_ptr(s.data()+s.size())),first,last);
184   }
185 
186   template<typename InputIterator>
nv_insert(const_iterator p,InputIterator first,InputIterator last)187   range nv_insert(const_iterator p,InputIterator first,InputIterator last)
188   {
189 #if BOOST_WORKAROUND(BOOST_LIBSTDCXX_VERSION,<40900)
190     /* std::vector::insert(pos,first,last) returns void rather than iterator */
191 
192     auto n=const_store_value_type_ptr(p)-s.data();
193     s.insert(s.begin()+n,first,last);
194     return range_from(static_cast<std::size_t>(n));
195 #else
196     return range_from(s.insert(iterator_from(p),first,last));
197 #endif
198   }
199 
erase(const_base_iterator p)200   virtual range erase(const_base_iterator p)
201   {return nv_erase(const_iterator(p));}
202 
nv_erase(const_iterator p)203   range nv_erase(const_iterator p)
204   {
205     return range_from(s.erase(iterator_from(p)));
206   }
207 
erase(const_base_iterator first,const_base_iterator last)208   virtual range erase(const_base_iterator first,const_base_iterator last)
209   {return nv_erase(const_iterator(first),const_iterator(last));}
210 
nv_erase(const_iterator first,const_iterator last)211   range nv_erase(const_iterator first,const_iterator last)
212   {
213     return range_from(s.erase(iterator_from(first),iterator_from(last)));
214   }
215 
erase_till_end(const_base_iterator first)216   virtual range erase_till_end(const_base_iterator first)
217   {
218     return range_from(s.erase(iterator_from(first),s.end()));
219   }
220 
erase_from_begin(const_base_iterator last)221   virtual range erase_from_begin(const_base_iterator last)
222   {
223     return range_from(s.erase(s.begin(),iterator_from(last)));
224   }
225 
clear()226   virtual base_sentinel clear()noexcept{return nv_clear();}
nv_clear()227   base_sentinel         nv_clear()noexcept{s.clear();return sentinel();}
228 
229 private:
230   template<typename... Args>
new_(segment_allocator_type al,Args &&...args)231   static segment_backend_unique_ptr new_(
232     segment_allocator_type al,Args&&... args)
233   {
234     auto p=std::allocator_traits<segment_allocator_type>::allocate(al,1);
235     try{
236       ::new ((void*)p) packed_segment{std::forward<Args>(args)...};
237     }
238     catch(...){
239       std::allocator_traits<segment_allocator_type>::deallocate(al,p,1);
240       throw;
241     }
242     return {p,&delete_};
243   }
244 
delete_(segment_backend * p)245   static void delete_(segment_backend* p)
246   {
247     auto q=static_cast<packed_segment*>(p);
248     auto al=segment_allocator_type{q->s.get_allocator()};
249     q->~packed_segment();
250     std::allocator_traits<segment_allocator_type>::deallocate(al,q,1);
251   }
252 
packed_segment(const Allocator & al)253   packed_segment(const Allocator& al):s{typename store::allocator_type{al}}{}
packed_segment(store && s)254   packed_segment(store&& s):s{std::move(s)}{}
255 
concrete_ref(value_pointer p)256   static Concrete& concrete_ref(value_pointer p)noexcept
257   {
258     return *static_cast<Concrete*>(p);
259   }
260 
const_concrete_ref(const_value_pointer p)261   static const Concrete& const_concrete_ref(const_value_pointer p)noexcept
262   {
263     return *static_cast<const Concrete*>(p);
264   }
265 
concrete_ptr(store_value_type * p)266   static Concrete* concrete_ptr(store_value_type* p)noexcept
267   {
268     return reinterpret_cast<Concrete*>(
269       static_cast<value_holder_base<Concrete>*>(p));
270   }
271 
const_concrete_ptr(const store_value_type * p)272   static const Concrete* const_concrete_ptr(const store_value_type* p)noexcept
273   {
274     return concrete_ptr(const_cast<store_value_type*>(p));
275   }
276 
value_ptr(const store_value_type * p)277   static value_type* value_ptr(const store_value_type* p)noexcept
278   {
279     return const_cast<value_type*>(Model::value_ptr(const_concrete_ptr(p)));
280   }
281 
const_store_value_type_ptr(const Concrete * p)282   static const store_value_type* const_store_value_type_ptr(
283     const Concrete* p)noexcept
284   {
285     return static_cast<const value_holder<Concrete>*>(
286       reinterpret_cast<const value_holder_base<Concrete>*>(p));
287   }
288 
289   /* It would have sufficed if iterator_from returned const_store_iterator
290    * except for the fact that some old versions of libstdc++ claiming to be
291    * C++11 compliant do not however provide std::vector modifier ops taking
292    * const_iterator's.
293    */
294 
iterator_from(const_base_iterator p)295   store_iterator iterator_from(const_base_iterator p)
296   {
297     return iterator_from(static_cast<const_iterator>(p));
298   }
299 
iterator_from(const_iterator p)300   store_iterator iterator_from(const_iterator p)
301   {
302     return s.begin()+(const_store_value_type_ptr(p)-s.data());
303   }
304 
sentinel() const305   base_sentinel sentinel()const noexcept
306   {
307     return base_iterator{
308       value_ptr(s.data()+s.size()),
309       sizeof(store_value_type)
310     };
311   }
312 
range_from(const_store_iterator it) const313   range range_from(const_store_iterator it)const
314   {
315     return {
316       {value_ptr(s.data()+(it-s.begin())),sizeof(store_value_type)},
317       sentinel()
318     };
319   }
320 
range_from(std::size_t n) const321   range range_from(std::size_t n)const
322   {
323     return {
324       {value_ptr(s.data()+n),sizeof(store_value_type)},
325       sentinel()
326     };
327   }
328 
329   store s;
330 };
331 
332 } /* namespace poly_collection::detail */
333 
334 } /* namespace poly_collection */
335 
336 } /* namespace boost */
337 
338 #endif
339