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