orbtree
vector_stacked.h
1 /* -*- C++ -*-
2  * vector_stacked.h
3  *
4  * Copyright 2018,2020 Daniel Kondor <kondor.dani@gmail.com>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of the nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  *
33  */
34 
35 
82 #ifndef VECTOR_STACKED_H
83 #define VECTOR_STACKED_H
84 
85 #include <limits>
86 #include <new>
87 #include <stdexcept>
88 #include <vector>
89 #include <type_traits>
90 #include <iterator>
91 #include <stdlib.h>
92 #include <string.h>
93 
94 
95 /* constexpr if support only for c++17 or newer */
96 #if __cplusplus >= 201703L
97 #define CONSTEXPR constexpr
98 #else
99 #define CONSTEXPR
100 #endif
101 
102 #ifdef USE_LIBDIVIDE
103 #include "libdivide.h"
104 namespace stacked_vector {
105 
110  struct divider {
111  size_t n;
112  libdivide::divider<size_t> div;
113 
114  explicit divider(size_t x):n(x),div(x) { }
115 
116  bool operator == (size_t x) const { return n == x; }
117  bool operator != (size_t x) const { return n != x; }
118  bool operator < (size_t x) const { return n < x; }
119  bool operator <= (size_t x) const { return n <= x; }
120  bool operator > (size_t x) const { return n > x; }
121  bool operator >= (size_t x) const { return n >= x; }
122  };
123 
124  size_t operator / (size_t x, const divider& d) { return x / d.div; }
125  size_t& operator /= (size_t& x, const divider& d) { x /= d.div; return x; }
126  size_t operator * (size_t x, const divider& d) { return x * d.n; }
127  size_t& operator *= (size_t& x, const divider& d) { x *= d.n; return x; }
128 
129  size_t operator + (size_t x, const divider& d) { return x + d.n; }
130  size_t& operator += (size_t& x, const divider& d) { x += d.n; return x; }
131  size_t operator - (size_t x, const divider& d) { return x - d.n; }
132  size_t& operator -= (size_t& x, const divider& d) { x -= d.n; return x; }
133 
134  bool operator == (size_t x, const divider& d) { return d == x; }
135  bool operator != (size_t x, const divider& d) { return d != x; }
136  bool operator > (size_t x, const divider& d) { return d < x; }
137  bool operator >= (size_t x, const divider& d) { return d <= x; }
138  bool operator < (size_t x, const divider& d) { return d > x; }
139  bool operator <= (size_t x, const divider& d) { return d >= x; }
140 
141 }
142 #endif
143 
144 
145 namespace stacked_vector {
146 
148  template<class It>
150  constexpr static bool value =
151  std::is_same< typename std::iterator_traits<It>::iterator_category, typename std::forward_iterator_tag >::value ||
152  std::is_same< typename std::iterator_traits<It>::iterator_category, typename std::bidirectional_iterator_tag >::value ||
153  std::is_same< typename std::iterator_traits<It>::iterator_category, typename std::random_access_iterator_tag >::value
154 #if __cplusplus >= 202000L
155  || std::is_same< typename std::iterator_traits<It>::iterator_category, typename std::contiguous_iterator_tag >::value
156 #endif
157  ;
158  };
160  template<class It>
162  constexpr static bool value = at_least_forward_iterator<It>::value ||
163  std::is_same< typename std::iterator_traits<It>::iterator_category, typename std::input_iterator_tag >::value;
164  };
165 
166 
167 template <class T>
168 using std_vector_wrapper = std::vector<T>;
169 
170 template <class T, template<class> class vector_type = std_vector_wrapper>
171 class vector {
172  protected:
173  vector_type<T*> stack;
174  size_t p_size;
175  size_t p_capacity;
176  size_t p_stack_size;
178 #ifdef USE_LIBDIVIDE
179  divider max_grow;
180 #else
181  size_t max_grow;
182 #endif
183  static constexpr size_t p_max_capacity = std::numeric_limits<size_t>::max() / sizeof(T);
185 
188  template<class U = T>
189  bool copy_or_move(U* target, size_t new_size,
190  typename std::enable_if< std::is_nothrow_move_constructible<U>::value >::type* = 0) {
191  size_t i;
192  for(i=0;i<p_size;i++) {
193  if(i<new_size) new(target + i) U(std::move(stack[0][i]));
194  stack[0][i].~U();
195  }
196  return true;
197  }
198  template<class U = T>
199  bool copy_or_move(U* target, size_t new_size,
200  typename std::enable_if< ! std::is_nothrow_move_constructible<U>::value >::type* = 0) {
201  size_t copy_size = p_size < new_size ? p_size : new_size;
202  U* tmp = std::uninitialized_copy(stack[0], stack[0] + copy_size, target);
203  if(tmp != target + copy_size) return false;
204  for(size_t i=0;i<p_size;i++) stack[0][i].~U();
205  }
206 
210  bool change_size(size_t new_size) {
211  if(new_size > max_grow) return false;
212  if(new_size == p_stack_size) return true;
213  T* new_array = (T*)malloc(sizeof(T)*new_size);
214  if(!new_array) return false;
215  if(stack.size() == 0) {
216  try {
217  stack.push_back(new_array);
218  }
219  catch(std::bad_alloc& x) {
220  free(new_array);
221  return false;
222  }
223  }
224  else {
225  if(!copy_or_move<T>(new_array,new_size)) {
226  free(new_array);
227  return false;
228  }
229  free(stack[0]);
230  stack[0] = new_array;
231  }
232  p_capacity = new_size;
233  p_stack_size = new_size;
234  if(new_size < p_size) p_size = new_size;
235  return true;
236  }
241  bool grow_vector(size_t minimum_size = 0) {
242  size_t new_size = minimum_size;
243  if(!new_size) {
244  if(p_stack_size == max_grow) return grow_one();
245  new_size = p_stack_size*2UL;
246  if(!new_size) new_size = 1UL;
247  }
248  if(new_size > max_grow)
249 #ifdef USE_LIBDIVIDE
250  new_size = max_grow.n;
251 #else
252  new_size = max_grow;
253 #endif
254  if(!change_size(new_size)) return false;
255  while(minimum_size > p_capacity) if(!grow_one()) return false;
256  return true;
257  }
258 
260  bool grow_one() {
261  // maximum number of elements in stack such that p_capacity will not overflow
262  size_t max_stack_size = std::numeric_limits<size_t>::max() / p_stack_size;
263  if(stack.size() >= max_stack_size) return false;
264  T* new_array = (T*)malloc(sizeof(T)*p_stack_size);
265  if(!new_array) return false;
266  try {
267  stack.push_back(new_array);
268  }
269  catch(std::bad_alloc& x) {
270  free(new_array);
271  return false;
272  }
273  p_capacity += p_stack_size;
274  return true;
275  }
276 
278  std::pair<size_t,size_t> get_indices(size_t i) const {
279 #ifdef USE_LIBDIVIDE
280  size_t i1 = i / max_grow;
281  size_t i2 = (i - i1*max_grow);
282 #else
283  size_t i1 = i / max_grow;
284  size_t i2 = i % max_grow;
285 #endif
286  return std::pair<size_t,size_t>(i1,i2);
287  }
288 
291  T* get_addr(size_t i) { auto x = get_indices(i); return stack[x.first] + x.second; }
294  const T* get_addr(size_t i) const { auto x = get_indices(i); return stack[x.first] + x.second; }
297  T& get_ref(size_t i) { auto x = get_indices(i); return stack[x.first][x.second]; }
300  const T& get_ref(size_t i) const { auto x = get_indices(i); return stack[x.first][x.second]; }
301 
302  public:
303  /* types */
304  typedef T value_type;
305  typedef size_t size_type;
306  typedef ssize_t difference_type;
307  typedef T& reference;
308  typedef const T& const_reference;
309  typedef T* pointer;
310  typedef const T* const_pointer;
311 
312  /* Constructors */
313 
315  vector() noexcept : p_size(0),p_capacity(0),p_stack_size(0),max_grow(131072) { }
317  explicit vector(size_t count, const T& value = T(), size_t max_grow_ = 131072);
319  template<class It, typename std::enable_if<at_least_input_iterator<It>::value, int>::type = 0>
320  vector(It first, It last, size_t max_grow_ = 131072);
322  vector(const vector& v);
323  vector(vector&& v);
324  /* 5. assignment */
325  vector& operator = (const vector& v);
326  vector& operator = (vector&& v);
327  /* 6. swap */
328  void swap(vector& v);
329 
330  /* Destructor */
331  ~vector() {
332  /* call destructors of existing elements -- these are not allowed to throw an exception! */
333  if(p_size) resize(0);
334  for(auto x : stack) free(x);
335  }
336 
337 
338  /* Basic access to members */
339 
341  size_t size() const { return p_size; }
343  size_t capacity() const { return p_capacity; }
345  size_t max_size() const { return p_max_capacity; }
347  size_t max_capacity() const { return p_max_capacity; }
349  bool empty() const { return p_size == 0; }
351  size_t get_stack_array_size() const {
352 #ifdef USE_LIBDIVIDE
353  return max_grow.n;
354 #else
355  return max_grow;
356 #endif
357  }
358 
359 
360 
361  /* data access functions
362  * similarly to std::vector, only at() checks bounds, all other versions
363  * result in undefined behavior if an out of bounds is attempted */
364 
366  T& operator[](size_t i) { return get_ref(i); }
368  const T& operator[](size_t i) const { return get_ref(i); }
370  T& at(size_t i) { if(i < p_size) return get_ref(i); throw std::out_of_range("vector::at(): index out of range!\n"); }
372  const T& at(size_t i) const { if(i < p_size) return get_ref(i); throw std::out_of_range("vector::at(): index out of range!\n"); }
374  T& front() { return stack[0][0]; }
376  const T& front() const { return stack[0][0]; }
378  T& back() { return get_ref(p_size-1); }
380  const T& back() const { return get_ref(p_size-1); }
381 
382  /* reserve memory */
384  bool reserve_nothrow(size_t n);
386  void reserve(size_t n) { if(!reserve_nothrow(n)) throw std::bad_alloc(); }
388  void shrink_to_fit(size_t new_capacity = 0);
389 
390  /* insert /create elements at the end of the vector
391  * versions that throw an exception if out of memory */
393  void push_back(const T& x);
395  void push_back(T&& x);
397  template<class... Args> T& emplace_back(Args&&... args);
398  /* versions that return false if out of memory -- note: any exceptions
399  * from T's (copy / move) constructor are propagated, i.e. still can
400  * throw an exception if those throw */
402  bool push_back_nothrow(const T& x);
404  bool push_back_nothrow(T&& x);
406  template<class... Args> bool emplace_back_nothrow(Args&&... args);
407 
408  /* remove elements -- T's destructor should not throw exception! */
410  void clear() { while(p_size) pop_back(); }
412  void pop_back() {
413  if(p_size) {
414  --p_size;
415  get_ref(p_size).~T();
416  }
417  }
418 
421  bool resize_nothrow(size_t count);
424  bool resize_nothrow(size_t count, const T& x);
427  void resize(size_t count) { if(!resize_nothrow(count)) throw std::bad_alloc(); }
430  void resize(size_t count, const T& x) { if(!resize_nothrow(count,x)) throw std::bad_alloc(); }
431 
432  protected:
434  template<bool is_const>
436  public:
437  typedef std::random_access_iterator_tag iterator_category;
438  typedef T value_type;
439  typedef T* pointer;
440  typedef T& reference;
441  typedef ssize_t difference_type;
442  /*
443  typedef typename _Iterator::iterator_category iterator_category;
444  typedef typename _Iterator::value_type value_type;
445  typedef typename _Iterator::difference_type difference_type;
446  typedef typename _Iterator::pointer pointer;
447  typedef typename _Iterator::reference reference;
448  */
449 
450  friend class iterator_base<!is_const>;
451  friend class vector<T,vector_type>;
452 
453  protected:
454  typedef typename std::conditional<is_const, const vector, vector>::type vector_type1;
455  vector_type1* v;
456  size_t pos;
457 
458  /* make non-const iterator from const iterator -- only possible from within the vector
459  template<bool is_const_ = is_const>
460  iterator_base(const iterator_base<true>& it, typename std::enable_if<is_const_>::type* = 0 ):v(it.v),pos(it.pos) { } */
461 
462  public:
463  iterator_base():v(0),pos(0) { }
464  explicit iterator_base(vector_type1& v_, size_t pos_ = 0):v(&v_),pos(pos_) { }
465  explicit iterator_base(vector_type1* v_, size_t pos_ = 0):v(v_),pos(pos_) { }
466 
467  /* any iterator can be copied from non-const iterator;
468  * this is not explicit so comparison operators can auto-convert */
469  iterator_base(const iterator_base<false>& it):v(it.v),pos(it.pos) { }
470  /* only const iterator can be copied from const iterator */
471  template<bool is_const_ = is_const>
472  iterator_base(const iterator_base<true>& it, typename std::enable_if<is_const_>::type* = 0 ):v(it.v),pos(it.pos) { }
473 
474 
475 
477  reference operator * () {
478  return (*v)[pos];
479  }
481  pointer operator -> () {
482  return &((*v)[pos]);
483  }
485 
487  template<bool is_const2> bool operator == (const iterator_base<is_const2>& i) const { return pos == i.pos; }
489 
491  template<bool is_const2> bool operator != (const iterator_base<is_const2>& i) const { return pos != i.pos; }
492 
494  template<bool is_const2> bool operator < (const iterator_base<is_const2>& i) const { return pos < i.pos; }
496  template<bool is_const2> bool operator > (const iterator_base<is_const2>& i) const { return pos > i.pos; }
498  template<bool is_const2> bool operator <= (const iterator_base<is_const2>& i) const { return pos <= i.pos; }
500  template<bool is_const2> bool operator >= (const iterator_base<is_const2>& i) const { return pos >= i.pos; }
501 
503  iterator_base& operator ++() { ++pos; return *this; }
505  iterator_base operator ++(int) { iterator_base<is_const> i(*this); ++pos; return i; }
507  iterator_base& operator --() { --pos; return *this; }
509  iterator_base operator --(int) { iterator_base<is_const> i(*this); --pos; return i; }
510 
512  iterator_base& operator +=(ssize_t i) { pos += i; return *this; }
514  iterator_base& operator -=(ssize_t i) { pos -= i; return *this; }
515  iterator_base operator +(ssize_t i) const { iterator_base<is_const> it(*this); it += i; return it; }
516  iterator_base operator -(ssize_t i) const { iterator_base<is_const> it(*this); it -= i; return it; }
517 
519  template<bool is_const2> ssize_t operator - (const iterator_base<is_const2>& i) const {
520  return ((ssize_t)pos) - ((ssize_t)(i.pos));
521  }
522 
523  typename std::conditional<is_const, const reference, reference>::type operator [] (ssize_t i) { return (*v)[pos+i]; }
524  };
525 
528  bool insert_helper(size_t pos, size_t new_pos);
529 
531  iterator_base<false> make_iterator(size_t pos) { return iterator_base<false>(*this,pos); }
535  iterator_base<true> make_iterator(size_t pos) const { return iterator_base<true>(*this,pos); }
536 
537  public:
540 
541  iterator begin() { return iterator(*this,0); }
542  const_iterator begin() const { return const_iterator(*this,0); }
543  const_iterator cbegin() const { return const_iterator(*this,0); }
544  iterator end() { return iterator(*this,p_size); }
545  const_iterator end() const { return const_iterator(*this,p_size); }
546  const_iterator cend() const { return const_iterator(*this,p_size); }
547 
548  iterator erase(const_iterator pos);
549  iterator erase(const_iterator first, const_iterator last);
550 
551  /* inserts that do not throw exception when out of memory,
552  * instead they return whether the insert was successful
553  * the res iterator is updated to point to the inserted element
554  * if the insert is successful, otherwise it is not changed */
555 
560  bool insert_nothrow(const_iterator pos, iterator& res, const T& x);
565  bool insert_nothrow(const_iterator pos, iterator& res, T&& x);
570  bool insert_nothrow(const_iterator pos, iterator& res, size_t count, const T& x);
575  template<class InputIt, typename std::enable_if<at_least_input_iterator<InputIt>::value, int>::type = 0>
576  bool insert_nothrow(const_iterator pos, iterator& res, InputIt first, InputIt last);
577 
579  iterator insert(const_iterator pos, const T& x) {
580  iterator res;
581  if(insert_nothrow(pos,res,x)) return res;
582  else throw std::bad_alloc();
583  }
585  iterator insert(const_iterator pos, T&& x) {
586  iterator res;
587  if(insert_nothrow(pos,res,std::forward<T>(x))) return res;
588  else throw std::bad_alloc();
589  }
591  iterator insert(const_iterator pos, size_t count, const T& x) {
592  iterator res;
593  if(insert_nothrow(pos,res,count,x)) return res;
594  else throw std::bad_alloc();
595  }
597  template<class InputIt, typename std::enable_if<at_least_input_iterator<InputIt>::value, int>::type = 0>
598  iterator insert(const_iterator pos, InputIt first, InputIt last) {
599  iterator res;
600  if(insert_nothrow(pos,res,first,last)) return res;
601  else throw std::bad_alloc();
602  }
603 };
604 
605 
606 template <class T, template<class> class vector_type>
607 auto operator + (ssize_t i, const typename vector<T,vector_type>::iterator& it) -> typename vector<T,vector_type>::iterator {
608  typename vector<T,vector_type>::iterator it2(it);
609  it2 += i;
610  return it2;
611 }
612 
613 template <class T, template<class> class vector_type>
614 auto operator + (ssize_t i, const typename vector<T,vector_type>::const_iterator& it) -> typename vector<T,vector_type>::const_iterator {
615  typename vector<T,vector_type>::const_iterator it2(it);
616  it2 += i;
617  return it2;
618 }
619 
620 
621 
622 /* Constructors */
623 template <class T, template<class> class vt>
624 vector<T,vt>::vector(size_t count, const T& value, size_t max_grow_) :
625  p_size(0),p_capacity(0),p_stack_size(0),max_grow(max_grow_) {
626  reserve(count);
627  for(;p_size < count; ++p_size) new(get_addr(p_size)) T(value);
628 }
629 
630 template <class T, template<class> class vt> template<class It,
631  typename std::enable_if< at_least_input_iterator<It>::value, int>::type >
632 vector<T,vt>::vector(It first, It last, size_t max_grow_) :
633  p_size(0),p_capacity(0),p_stack_size(0),max_grow(max_grow_) {
634  for(; first != last; ++first) push_back(*first);
635 }
636 
637 template <class T, template<class> class vt>
639  reserve(v.size());
640  for(;p_size < v.size(); ++p_size) new(get_addr(p_size)) T(v[p_size]);
641 }
642 
643 template <class T, template<class> class vt>
645  using std::swap;
646  swap(stack, v.stack);
647  swap(p_size, v.p_size);
648  swap(p_capacity, v.p_capacity);
649  swap(p_stack_size, v.p_stack_size);
650  swap(max_grow, v.max_grow);
651 }
652 
653 template <class T, template<class> class vt>
655  swap(v);
656 }
657 
658 template <class T, template<class> class vt>
660  resize(0);
661  shrink_to_fit(v.size());
662  reserve(v.size());
663  for(;p_size < v.size(); ++p_size) new(get_addr(p_size)) T(v[p_size]);
664  return *this;
665 }
666 
667 template <class T, template<class> class vt>
669  resize(0);
670  for(auto x : stack) free(x);
671  stack.resize(0);
672  swap(v);
673 }
674 
675 template <class T, template<class> class vt>
677  if(n > p_max_capacity) return false;
678  if(n <= p_capacity) return true;
679  return grow_vector(n);
680 }
681 
682 template <class T, template<class> class vt>
683 void vector<T,vt>::shrink_to_fit(size_t new_capacity) {
684  if(new_capacity < p_size) new_capacity = p_size;
685  if(new_capacity > p_capacity) return;
686  if(new_capacity > max_grow) {
687 #ifdef USE_LIBDIVIDE
688  size_t tmp = new_capacity / max_grow;
689  if(new_capacity - tmp*max_grow) new_capacity += max_grow;
690 #else
691  if(new_capacity % max_grow) new_capacity += max_grow;
692 #endif
693  while(p_capacity > new_capacity) {
694  free(stack.back());
695  stack.pop_back();
696  p_capacity -= max_grow;
697  }
698  }
699  else {
700  size_t new_stack_size = (new_capacity > 0)?1:0;
701  for(size_t i = new_stack_size; i < stack.size(); i++) free(stack[i]);
702  stack.resize(new_stack_size);
703  if(new_capacity) change_size(new_capacity);
704  }
705  stack.shrink_to_fit();
706 }
707 
708 
709 /* insert /create elements at the end of the vector */
710 template <class T, template<class> class vt>
711 void vector<T,vt>::push_back(const T& x) {
712  if(!push_back_nothrow(x)) throw std::bad_alloc();
713 }
714 template <class T, template<class> class vt>
716  if(!push_back_nothrow(std::forward<T>(x))) throw std::bad_alloc();
717 }
718 template<class T, template<class> class vt> template<class... Args>
719 T& vector<T,vt>::emplace_back(Args&&... args) {
720  if(!emplace_back_nothrow(std::forward<Args>(args)...)) throw std::bad_alloc();
721  return back();
722 }
723 template <class T, template<class> class vt>
725  if(p_size == p_capacity) if(!grow_vector()) return false;
726  new(get_addr(p_size)) T(x); /* copy constructor -- might still throw an exception */
727  p_size++;
728  return true;
729 }
730 template <class T, template<class> class vt>
732  if(p_size == p_capacity) if(!grow_vector()) return false;
733  new(get_addr(p_size)) T(std::forward<T>(x)); /* move constructor -- might throw an exception */
734  p_size++;
735  return true;
736 }
737 template<class T, template<class> class vt> template<class... Args>
739  if(p_size == p_capacity) if(!grow_vector()) return false;
740  new(get_addr(p_size)) T(std::forward<Args>(args)...); /* constructor -- might throw an exception */
741  p_size++;
742  return true;
743 }
744 
745 
746 
747 template <class T, template<class> class vt>
748 bool vector<T,vt>::resize_nothrow(size_t count) {
749  if(count == p_size) return true;
750  if(!count) { clear(); return true; }
751  if(count < p_size) {
752  do { pop_back(); } while(p_size > count);
753  return true;
754  }
755  /* here count > p_size */
756  if(count > p_capacity) if(!grow_vector(count)) return false;
757  for(; p_size < count; p_size++) new(get_addr(p_size)) T();
758  return true;
759 }
760 template <class T, template<class> class vt>
761 bool vector<T,vt>::resize_nothrow(size_t count, const T& x) {
762  if(count == p_size) return true;
763  if(!count) { clear(); return true; }
764  if(count < p_size) {
765  do { pop_back(); } while(p_size > count);
766  return true;
767  }
768  /* here count > p_size */
769  if(count > p_capacity) if(!grow_vector(count)) return false;
770  for(; p_size < count; p_size++) new(get_addr(p_size)) T(x);
771  return true;
772 }
773 
774 
775 template <class T, template<class> class vt>
777  if(pos.pos >= p_size) throw std::out_of_range("vector::erase(): iterator out of bounds!\n");
778  for(size_t p2 = pos.pos; p2 + 1 < p_size; p2++) get_ref(p2) = std::move(get_ref(p2+1));
779  p_size--;
780  get_ref(p_size).~T();
781  return make_iterator(pos.pos);
782 }
783 template <class T, template<class> class vt>
785  if(first.pos >= p_size) throw std::out_of_range("vector::erase(): iterator out of bounds!\n");
786  ssize_t dist = last - first;
787  if(!dist) return make_iterator(first);
788  if(dist < 0) throw std::out_of_range("vector::erase(): invalid iterators!\n");
789  for(size_t p2 = first.pos; p2 + dist < p_size; p2++) get_ref(p2) = std::move(get_ref(p2+dist));
790 
791  size_t new_size = p_size - dist;
792  for(;p_size > new_size; --p_size) get_ref(p_size-1).~T();
793  return make_iterator(first);
794 }
795 
796 
797 
798 /* move elements to new position from given position
799  * requires that new_pos >= pos and p_size > pos */
800 template <class T, template<class> class vt>
801 bool vector<T,vt>::insert_helper(size_t pos, size_t new_pos) {
802  size_t diff = new_pos - pos;
803  if(p_size > p_max_capacity - diff || !reserve_nothrow(p_size + diff)) return false;
804  size_t remaining = p_size - pos;
805 
806  for(size_t i = 0; i < diff; i++) {
807  size_t j = p_size + i;
808  new(get_addr(j)) T(std::move(get_ref( p_size - diff + i )));
809  }
810  for(size_t i = p_size - diff; i > pos; i--) get_ref(i - 1UL + diff) = std::move(get_ref(i - 1UL));
811  p_size += diff;
812  return true;
813 }
814 
815 
816 
817 template <class T, template<class> class vt>
819  res = make_iterator(pos);
820  if(pos == cend()) return push_back_nothrow(x);
821  if(!insert_helper(pos.pos,pos.pos+1)) return false;
822  get_ref(pos.pos) = x;
823  return true;
824 }
825 
826 template <class T, template<class> class vt>
828  res = make_iterator(pos);
829  if(pos == cend()) return push_back_nothrow(std::forward<T>(x));
830  if(!insert_helper(pos.pos,pos.pos+1)) return false;
831  get_ref(pos.pos) = std::move(x);
832  return true;
833 }
834 
835 
836 template <class T, template<class> class vt>
837 bool vector<T,vt>::insert_nothrow(vector<T,vt>::const_iterator pos, vector<T,vt>::iterator& res, size_t count, const T& x) {
838  res = make_iterator(pos);
839  if(pos == cend()) return resize_nothrow(p_size + count, x);
840  if(!insert_helper(pos.pos,pos.pos+count)) return false;
841  for(size_t i = 0; i < count; i++) get_ref(pos.pos + i) = x;
842  return true;
843 }
844 
845 
846 template <class T, template<class> class vt> template<class InputIt,
847  typename std::enable_if<at_least_input_iterator<InputIt>::value, int>::type >
848 bool vector<T,vt>::insert_nothrow(vector<T,vt>::const_iterator pos, vector<T,vt>::iterator& res, InputIt first, InputIt last) {
849  res = make_iterator(pos);
850  if(first != last) {
852  ssize_t dist = std::distance(first,last);
853  if(dist < 0) return false;
854 
855  if(pos == cend()) {
856  if(!reserve_nothrow(p_size + dist)) return false;
857  for(; first != last; ++first) push_back(*first);
858  return true;
859  }
860 
861  if(!insert_helper(pos.pos,pos.pos+dist)) return false;
862  for(size_t i = pos.pos; first != last; ++first, i++) get_ref(i) = *first;
863  }
864  else {
866  for(size_t i = pos.pos; first != last; ++first, i++)
867  if(!insert_nothrow(vector<T,vt>::iterator(*this,i),tmp,*first)) return false;
868  }
869  }
870  return true;
871 }
872 
873 
874 
875 } // namespace stacked_vector
876 
877 #undef CONSTEXPR
878 
879 #endif
880 
881 
bool emplace_back_nothrow(Args &&... args)
Construct an element at the end of the vector. Does not throw exception, return value indicates if in...
Definition: vector_stacked.h:738
vector() noexcept
default constructor, creates empty vector, maximum growth is 128k elements
Definition: vector_stacked.h:315
size_t p_size
number of elements in vector
Definition: vector_stacked.h:174
const_iterator end() const
Iterator to the end.
Definition: vector_stacked.h:545
iterator_base< false > make_iterator(iterator_base< true > pos)
Helper to make a non-const copy of a const iterator – only possible if this class is not const...
Definition: vector_stacked.h:533
void pop_back()
Removes the last element; does not free up memory, use shrink_to_fit() for that.
Definition: vector_stacked.h:412
const_iterator cbegin() const
Iterator to the beginning.
Definition: vector_stacked.h:543
size_t p_capacity
current capacity of vector
Definition: vector_stacked.h:175
const_iterator begin() const
Iterator to the beginning.
Definition: vector_stacked.h:542
iterator_base< true > make_iterator(size_t pos) const
Helper to create an iterator based on a position.
Definition: vector_stacked.h:535
iterator_base< false > make_iterator(size_t pos)
Helper to create an iterator based on a position.
Definition: vector_stacked.h:531
void push_back(const T &x)
Insert element at the end of the vector. Can throw an exception if memory allocation fails...
Definition: vector_stacked.h:711
bool push_back_nothrow(const T &x)
Insert element at the end of the vector. Does not throw exception, return value indicates if insert w...
Definition: vector_stacked.h:724
bool grow_one()
Add one element to the stack.
Definition: vector_stacked.h:260
void reserve(size_t n)
Reserve memory for at least n elements. Throws an exception if memory allocation is not successful...
Definition: vector_stacked.h:386
bool insert_nothrow(const_iterator pos, iterator &res, const T &x)
Inserts a copy of x at the given position. Does not throw an exception on memory allocation failure...
bool insert_helper(size_t pos, size_t new_pos)
Helper for the insert functions – moves elements starting from pos to new_pos, allocating memory if ...
Definition: vector_stacked.h:801
Iterators store a reference to this class and a position.
Definition: vector_stacked.h:435
void resize(size_t count)
Resize vector. If new size is larger than current size, new elements are default constructed. Can throw an exception on memory allocation error.
Definition: vector_stacked.h:427
T & emplace_back(Args &&... args)
Construct an element at the end of the vector. Can throw an exception if memory allocation fails...
Definition: vector_stacked.h:719
bool reserve_nothrow(size_t n)
Reserve memory for at least n elements. Returns true if allocation was successfull, false otherwise.
Definition: vector_stacked.h:676
bool empty() const
Returns true if the vector is empty.
Definition: vector_stacked.h:349
T & operator[](size_t i)
Access the ith element. It is undefined behavior to if i >= size()
Definition: vector_stacked.h:366
size_t max_size() const
Maximum size to avoid overflow when calculating memory size.
Definition: vector_stacked.h:345
bool change_size(size_t new_size)
Reallocate memory in the first stack element to the given new size, moving / copying elements to the ...
Definition: vector_stacked.h:210
iterator begin()
Iterator to the beginning.
Definition: vector_stacked.h:541
bool copy_or_move(U *target, size_t new_size, typename std::enable_if< std::is_nothrow_move_constructible< U >::value >::type *=0)
Helper function to copy or move values to new array, selecting copy or move based on SFINAE...
Definition: vector_stacked.h:189
iterator end()
Iterator to the end.
Definition: vector_stacked.h:544
const_iterator cend() const
Iterator to the end.
Definition: vector_stacked.h:546
std::pair< size_t, size_t > get_indices(size_t i) const
Helper to get internal indices (which stack + position in stack) based on item index.
Definition: vector_stacked.h:278
size_t max_capacity() const
Maximum capacity to avoid overflow when calculating memory size.
Definition: vector_stacked.h:347
void resize(size_t count, const T &x)
Resize vector. If new size is larger than current size, new elements are inserted as copies of x...
Definition: vector_stacked.h:430
bool resize_nothrow(size_t count)
Resize vector. If new size is larger than current size, new elements are default constructed. Does not throw exception on memory allocation error, return value indicates if resize was successful.
Definition: vector_stacked.h:748
void shrink_to_fit(size_t new_capacity=0)
Free up unused memory, keeping at least the given new_capacity (if new_capacity > size())...
Definition: vector_stacked.h:683
size_t get_stack_array_size() const
Get the maximum growth size. Separate arrays are allocated by this amount.
Definition: vector_stacked.h:351
const T & operator[](size_t i) const
Access the ith element. It is undefined behavior to if i >= size()
Definition: vector_stacked.h:368
iterator insert(const_iterator pos, size_t count, const T &x)
Inserts count copies of x at pos. Can throw an exception if out of memory.
Definition: vector_stacked.h:591
const T & front() const
Access the first element. It is undefined behavior if this function is called on an empty vector...
Definition: vector_stacked.h:376
T & front()
Access the first element. It is undefined behavior if this function is called on an empty vector...
Definition: vector_stacked.h:374
size_t max_grow
grow memory by maximum this many elements at a time, i.e. maximum value for p_stack_size. Does not change, but not const to allow for swapping / copying / moving vectors with different value.
Definition: vector_stacked.h:181
iterator erase(const_iterator pos)
Erase element at the given position.
T & at(size_t i)
Access the ith element with bounds checking, throws an exception if i >= size()
Definition: vector_stacked.h:370
const T & get_ref(size_t i) const
Helper to get reference of element at index. Does not check if index is in range, it is undefined beh...
Definition: vector_stacked.h:300
iterator insert(const_iterator pos, const T &x)
Inserts a copy of x at pos. Can throw an exception if out of memory.
Definition: vector_stacked.h:579
T & back()
Access the last element. It is undefined behavior if this function is called on an empty vector...
Definition: vector_stacked.h:378
size_t capacity() const
Current capacity, i.e. the number of elements that can be stored without allocating more memory...
Definition: vector_stacked.h:343
const T & at(size_t i) const
Access the ith element with bounds checking, throws an exception if i >= size()
Definition: vector_stacked.h:372
helper to distinguish iterators
Definition: vector_stacked.h:161
T * get_addr(size_t i)
Helper to get memory address of element at index. Does not check if index is in range, it is undefined behavior to call this function with i >= size().
Definition: vector_stacked.h:291
static constexpr size_t p_max_capacity
maximum safe capacity to avoid overflow
Definition: vector_stacked.h:184
size_t size() const
Current size, i.e. the number of elements stored in this vector.
Definition: vector_stacked.h:341
Definition: vector_stacked.h:145
C++ vector-like container that internally maintains a "stack" of vectors instead of having one large ...
Definition: vector_stacked.h:171
T & get_ref(size_t i)
Helper to get reference of element at index. Does not check if index is in range, it is undefined beh...
Definition: vector_stacked.h:297
helper to distinguish iterators
Definition: vector_stacked.h:149
iterator insert(const_iterator pos, InputIt first, InputIt last)
Inserts the elements in the range [first,last) at pos. Can throw an exception if out of memory...
Definition: vector_stacked.h:598
iterator insert(const_iterator pos, T &&x)
Inserts x at pos. Can throw an exception if out of memory.
Definition: vector_stacked.h:585
const T & back() const
Access the last element. It is undefined behavior if this function is called on an empty vector...
Definition: vector_stacked.h:380
size_t p_stack_size
size of one array in the stack
Definition: vector_stacked.h:176
bool grow_vector(size_t minimum_size=0)
Attempt to grow vector either to the given minimum size or using the default strategy, i.e. doubling size until the first element in the stack is full and after allocating max_grow elements at a time.
Definition: vector_stacked.h:241
const T * get_addr(size_t i) const
Helper to get memory address of element at index. Does not check if index is in range, it is undefined behavior to call this function with i >= size().
Definition: vector_stacked.h:294
void clear()
Removes all elements; does not free up memory, use shrink_to_fit() for that.
Definition: vector_stacked.h:410