density
C++11 library for paged memory management, function queues, heterogeneous queues and lifo memory management
lifo.h
1 
2 // Copyright Giuseppe Campana (giu.campana@gmail.com) 2016-2018.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 #pragma once
8 #include <cstring> // for memcpy
9 #include <density/default_allocator.h>
10 #include <density/density_common.h>
11 #include <density/runtime_type.h>
12 #include <iterator>
13 #include <type_traits>
14 
15 namespace density
16 {
45  template <typename UNDERLYING_ALLOCATOR = default_allocator, size_t ALIGNMENT = alignof(void *)>
46  class lifo_allocator : private UNDERLYING_ALLOCATOR
47  {
48  public:
49  static_assert(ALIGNMENT > 0 && is_power_of_2(ALIGNMENT), "ALIGNMENT must be a power of 2");
50 
52  using underlying_allocator = UNDERLYING_ALLOCATOR;
53 
55  static constexpr size_t alignment = ALIGNMENT;
56 
57  // compile time checks
58  static_assert(
59  alignment <= UNDERLYING_ALLOCATOR::page_alignment,
60  "The alignment of the pages is too small");
61 
63  constexpr lifo_allocator() noexcept {}
64 
66  lifo_allocator(const UNDERLYING_ALLOCATOR & i_underlying_allocator)
67  : UNDERLYING_ALLOCATOR(i_underlying_allocator)
68  {
69  }
70 
72  lifo_allocator(UNDERLYING_ALLOCATOR && i_underlying_allocator)
73  : UNDERLYING_ALLOCATOR(std::move(i_underlying_allocator))
74  {
75  }
76 
78  lifo_allocator(const lifo_allocator &) = delete;
79 
81  lifo_allocator & operator=(const lifo_allocator &) = delete;
82 
93  void * allocate(size_t i_size)
94  {
95  DENSITY_ASSUME_UINT_ALIGNED(i_size, alignment);
96 
97  auto const new_top = m_top + i_size;
98  auto const new_offset =
99  new_top - uint_lower_align(m_top, UNDERLYING_ALLOCATOR::page_alignment);
100  if (!DENSITY_LIKELY(new_offset < UNDERLYING_ALLOCATOR::page_size))
101  {
102  // page overflow
103  return allocate_slow_path(i_size);
104  }
105  else
106  {
107  // advance m_top
108  DENSITY_ASSUME(i_size <= UNDERLYING_ALLOCATOR::page_size);
109  auto const new_block = reinterpret_cast<void *>(m_top);
110  m_top = new_top;
111  return new_block;
112  }
113  }
114 
129  void * allocate_empty() noexcept { return reinterpret_cast<void *>(m_top); }
130 
140  void deallocate(void * i_block, size_t i_size) noexcept
141  {
142  DENSITY_ASSUME(i_block != nullptr);
143  DENSITY_ASSUME_UINT_ALIGNED(i_size, alignment);
144 
145  // this check detects page switches and external blocks
146  if (!DENSITY_LIKELY(same_page(i_block, reinterpret_cast<void *>(m_top))))
147  {
148  deallocate_slow_path(i_block, i_size);
149  }
150  else
151  {
152  m_top = reinterpret_cast<uintptr_t>(i_block);
153  }
154  }
155 
172  void * reallocate(void * i_block, size_t i_old_size, size_t i_new_size)
173  {
174  // branch depending on the old block: this check detects page switches and external blocks
175  if (same_page(i_block, reinterpret_cast<void *>(m_top)))
176  {
177  // the following call sets the top only when it is sure to not throw
178  auto const new_block = set_top_and_allocate(i_block, i_new_size);
179 
180  copy(i_block, i_old_size, new_block, i_new_size);
181  return new_block;
182  }
183  else
184  {
185  if (
186  (m_top & (UNDERLYING_ALLOCATOR::page_alignment - 1)) == sizeof(PageHeader) &&
187  same_page(reinterpret_cast<PageHeader *>(m_top)[-1].m_prev_page, i_block))
188  {
189  auto const page_to_deallocate = reinterpret_cast<void *>(m_top);
190  DENSITY_ASSERT_INTERNAL(!same_page(page_to_deallocate, i_block));
191 
192  // the following call sets the top only when it is sure to not throw
193  auto const new_block = set_top_and_allocate(i_block, i_new_size);
194 
195  copy(i_block, i_old_size, new_block, i_new_size);
196 
197  UNDERLYING_ALLOCATOR::deallocate_page(page_to_deallocate);
198  return new_block;
199  }
200  else
201  {
202  // the old block is external
203  auto const new_block = allocate(i_new_size);
204  copy(i_block, i_old_size, new_block, i_new_size);
205  UNDERLYING_ALLOCATOR::deallocate(i_block, i_old_size, alignment);
206  return new_block;
207  }
208  }
209  }
210 
213  {
214  if (m_top != s_virgin_top)
215  {
216  UNDERLYING_ALLOCATOR::deallocate_page(reinterpret_cast<void *>(m_top));
217  }
218  }
219 
221  UNDERLYING_ALLOCATOR & underlying_allocator_ref() noexcept { return *this; }
222 
224  const UNDERLYING_ALLOCATOR & underlying_allocator_ref() const noexcept { return *this; }
225 
226  private:
228  struct alignas(alignment) alignas(void *) PageHeader
229  {
230  void * m_prev_page;
231  };
232 
233  static_assert(
234  UNDERLYING_ALLOCATOR::page_size / 2 > sizeof(PageHeader), "page_size is too small");
235 
237  static bool same_page(const void * i_first, const void * i_second) noexcept
238  {
239  auto const page_mask = UNDERLYING_ALLOCATOR::page_alignment - 1;
240  return ((reinterpret_cast<uintptr_t>(i_first) ^ reinterpret_cast<uintptr_t>(i_second)) &
241  ~page_mask) == 0;
242  }
243 
244  DENSITY_NO_INLINE void * allocate_slow_path(size_t i_size)
245  {
246  DENSITY_ASSUME_UINT_ALIGNED(i_size, alignment);
247  if (i_size < UNDERLYING_ALLOCATOR::page_size / 2)
248  {
249  // allocate a new page
250  auto const new_page = UNDERLYING_ALLOCATOR::allocate_page();
251  DENSITY_ASSUME(new_page != nullptr);
252  auto const new_header = new (new_page) PageHeader;
253  new_header->m_prev_page = reinterpret_cast<void *>(m_top);
254  m_top = reinterpret_cast<uintptr_t>(new_header + 1) + i_size;
255  return new_header + 1;
256  }
257  else
258  {
259  // external block
260  return UNDERLYING_ALLOCATOR::allocate(i_size, alignment);
261  }
262  }
263 
264  DENSITY_NO_INLINE void deallocate_slow_path(void * i_block, size_t i_size) noexcept
265  {
266  DENSITY_ASSUME_UINT_ALIGNED(i_size, alignment);
267 
268  if (
269  (m_top & (UNDERLYING_ALLOCATOR::page_alignment - 1)) == sizeof(PageHeader) &&
270  same_page(reinterpret_cast<PageHeader *>(m_top)[-1].m_prev_page, i_block))
271  {
272  // deallocate the top page
273  auto const page_to_deallocate = reinterpret_cast<void *>(m_top);
274  DENSITY_ASSERT_INTERNAL(!same_page(page_to_deallocate, i_block));
275  UNDERLYING_ALLOCATOR::deallocate_page(page_to_deallocate);
276  m_top = reinterpret_cast<uintptr_t>(i_block);
277  }
278  else
279  {
280  // external block
281  UNDERLYING_ALLOCATOR::deallocate(i_block, i_size, alignment);
282  }
283  }
284 
287  void * set_top_and_allocate(void * const i_current_top, size_t const i_size)
288  {
289  DENSITY_ASSUME_UINT_ALIGNED(i_size, alignment);
290 
291  auto const current_top = reinterpret_cast<uintptr_t>(i_current_top);
292 
293  // we have to procrastinate the assignment of m_top until we have allocated the page or the external block
294  auto const new_top = current_top + i_size;
295  auto const new_offset =
296  new_top - uint_lower_align(current_top, UNDERLYING_ALLOCATOR::page_alignment);
297  if (new_offset < UNDERLYING_ALLOCATOR::page_size)
298  {
299  DENSITY_ASSUME(i_size <= UNDERLYING_ALLOCATOR::page_size);
300  auto const new_block = i_current_top;
301  m_top = new_top;
302  return new_block;
303  }
304  else if (i_size < UNDERLYING_ALLOCATOR::page_size / 2)
305  {
306  // allocate a new page
307  auto const new_page =
308  static_cast<PageHeader *>(UNDERLYING_ALLOCATOR::allocate_page());
309  new_page->m_prev_page = reinterpret_cast<void *>(current_top);
310  m_top = reinterpret_cast<uintptr_t>(new_page + 1) + i_size;
311  return new_page + 1;
312  }
313  else
314  {
315  // external block
316  auto const new_external_block = UNDERLYING_ALLOCATOR::allocate(i_size, alignment);
317  m_top = reinterpret_cast<uintptr_t>(i_current_top);
318  return new_external_block;
319  }
320  }
321 
322  void copy(
323  void * i_old_block, size_t i_old_size, void * i_new_block, size_t i_new_size) noexcept
324  {
325  if (i_old_block != i_new_block)
326  {
327  auto const size_to_copy = detail::size_min(i_new_size, i_old_size);
328  DENSITY_ASSUME_UINT_ALIGNED(size_to_copy, alignment);
329  DENSITY_ASSUME_ALIGNED(i_old_block, alignment);
330  DENSITY_ASSUME_ALIGNED(i_new_block, alignment);
331  memcpy(i_new_block, i_old_block, size_to_copy);
332  }
333  }
334 
335  private:
336  static constexpr uintptr_t s_virgin_top =
337  uint_lower_align(UNDERLYING_ALLOCATOR::page_alignment - 1, alignment);
338  uintptr_t m_top = s_virgin_top;
341  };
342 
343  namespace detail
344  {
346  template <int = 0> class ThreadLifoAllocator
347  {
348 #ifndef DENSITY_USER_DATA_STACK
349 
350  // default data stack
351 
352  public:
353  static constexpr size_t alignment =
355 
356  static void * allocate(size_t i_size) { return s_allocator.allocate(i_size); }
357 
358  static void * allocate_empty() noexcept { return s_allocator.allocate_empty(); }
359 
360  static void * reallocate(void * i_block, size_t i_old_size, size_t i_new_size)
361  {
362  return s_allocator.reallocate(i_block, i_old_size, i_new_size);
363  }
364 
365  static void deallocate(void * i_block, size_t i_size) noexcept
366  {
367  s_allocator.deallocate(i_block, i_size);
368  }
369 
370  private:
371  static thread_local lifo_allocator<data_stack_underlying_allocator> s_allocator;
372 #else
373 
374  // DENSITY_USER_DATA_STACK is defined: forward to the user defined data stack
375 
376  public:
377  static constexpr size_t alignment = user_data_stack::alignment;
378 
379  static void * allocate(size_t i_size) { return user_data_stack::allocate(i_size); }
380 
381  static void * allocate_empty() noexcept { return user_data_stack::allocate_empty(); }
382 
383  static void * reallocate(void * i_block, size_t i_old_size, size_t i_new_size)
384  {
385  return user_data_stack::reallocate(i_block, i_old_size, i_new_size);
386  }
387 
388  static void deallocate(void * i_block, size_t i_size) noexcept
389  {
390  user_data_stack::deallocate(i_block, i_size);
391  }
392 
393 #endif
394  };
395 
396 #ifndef DENSITY_USER_DATA_STACK
397  template <int DUMMY>
399  ThreadLifoAllocator<DUMMY>::s_allocator;
400 #endif
401 
402  } // namespace detail
403 
422  {
423  public:
425  static constexpr size_t alignment = detail::ThreadLifoAllocator<>::alignment;
426 
428  lifo_buffer() noexcept : m_data(detail::ThreadLifoAllocator<>::allocate_empty()), m_size(0)
429  {
430  }
431 
434  lifo_buffer(size_t i_size)
435  : m_data(detail::ThreadLifoAllocator<>::allocate(uint_upper_align(i_size, alignment))),
436  m_size(i_size)
437  {
438  }
439 
441  lifo_buffer(const lifo_buffer &) = delete;
442 
444  lifo_buffer & operator=(const lifo_buffer &) = delete;
445 
448  {
449  detail::ThreadLifoAllocator<>::deallocate(m_data, uint_upper_align(m_size, alignment));
450  }
451 
464  void * resize(size_t i_new_size)
465  {
466  /* we must allocate before updating any data member, so that in case of exception no change remains */
467  auto const block = m_data = detail::ThreadLifoAllocator<>::reallocate(
468  m_data, uint_upper_align(m_size, alignment), uint_upper_align(i_new_size, alignment));
469  m_size = i_new_size;
470  return block;
471  }
472 
475  void * data() const noexcept { return m_data; }
476 
478  size_t size() const noexcept { return m_size; }
479 
480  private:
481  void * m_data;
482  size_t m_size;
483  };
484 
485  namespace detail
486  {
487  template <
488  typename TYPE,
489  bool OVER_ALIGNED = (alignof(TYPE) > detail::ThreadLifoAllocator<>::alignment)>
490  class LifoArrayImpl;
491 
492  template <typename TYPE> class LifoArrayImpl<TYPE, false>
493  {
494  public:
495  static size_t compute_mem_size(size_t i_element_count) noexcept
496  {
497  auto const mem_size = i_element_count * sizeof(TYPE);
498  bool already_aligned = sizeof(TYPE) % detail::ThreadLifoAllocator<>::alignment == 0;
499  if (already_aligned)
500  return mem_size;
501  else
502  return uint_upper_align(mem_size, detail::ThreadLifoAllocator<>::alignment);
503  }
504 
505  LifoArrayImpl(size_t i_element_count)
506  : m_elements(static_cast<TYPE *>(
507  detail::ThreadLifoAllocator<>::allocate(compute_mem_size(i_element_count)))),
508  m_size(i_element_count)
509  {
510  }
511 
512  ~LifoArrayImpl()
513  {
514  detail::ThreadLifoAllocator<>::deallocate(m_elements, compute_mem_size(m_size));
515  }
516 
517  protected:
518  TYPE * const m_elements;
519  size_t const m_size;
520  };
521 
522  template <typename TYPE> class LifoArrayImpl<TYPE, true>
523  {
524  public:
525  static constexpr size_t size_overhead =
526  alignof(TYPE) - detail::ThreadLifoAllocator<>::alignment;
527 
528  static size_t compute_mem_size(size_t i_element_count) noexcept
529  {
530  auto const mem_size = i_element_count * sizeof(TYPE) + size_overhead;
531  bool already_aligned = sizeof(TYPE) % detail::ThreadLifoAllocator<>::alignment == 0;
532  if (already_aligned)
533  return mem_size;
534  else
535  return uint_upper_align(mem_size, detail::ThreadLifoAllocator<>::alignment);
536  }
537 
538  LifoArrayImpl(size_t i_size) : m_size(i_size)
539  {
540  auto const mem_size = compute_mem_size(m_size);
541  m_block = detail::ThreadLifoAllocator<>::allocate(mem_size);
542  m_elements = static_cast<TYPE *>(address_upper_align(m_block, alignof(TYPE)));
544  address_diff(m_elements, m_block) <= size_overhead &&
545  m_elements + m_size <= address_add(m_block, mem_size));
546  }
547 
548  ~LifoArrayImpl()
549  {
550  auto const mem_size = compute_mem_size(m_size);
551  detail::ThreadLifoAllocator<>::deallocate(m_block, mem_size);
552  }
553 
554  protected:
555  void * m_block;
556  TYPE * m_elements;
557  size_t const m_size;
558  };
559  } // namespace detail
560 
583  template <typename TYPE> class lifo_array final : detail::LifoArrayImpl<TYPE>
584  {
585  struct PrivateTag
586  {
587  };
588 
589  public:
590  using value_type = TYPE;
591  using reference = TYPE &;
592  using const_reference = TYPE &;
593  using pointer = TYPE *;
594  using const_pointer = const TYPE *;
595  using size_type = size_t;
596  using difference_type = ptrdiff_t;
597 
598  class iterator
599  {
600  public:
601  using difference_type = ptrdiff_t;
602  using value_type = TYPE;
603  using pointer = TYPE *;
604  using reference = TYPE &;
605  using iterator_category = std::random_access_iterator_tag;
606 
607  iterator(PrivateTag, TYPE * i_ptr) noexcept : m_curr(i_ptr) {}
608 
609  iterator() noexcept = default;
610  iterator(const iterator &) noexcept = default;
611  iterator & operator=(const iterator &) noexcept = default;
612 
613  TYPE * operator->() const noexcept { return m_curr; }
614 
615  TYPE & operator*() const & noexcept { return *m_curr; }
616 
617  TYPE && operator*() const && noexcept { return *m_curr; }
618 
619  iterator & operator++() noexcept
620  {
621  m_curr++;
622  return *this;
623  }
624 
625  iterator operator++(int) noexcept
626  {
627  iterator copy(*this);
628  operator++();
629  return copy;
630  }
631 
632  iterator & operator+=(difference_type i_diff) noexcept
633  {
634  m_curr += i_diff;
635  return *this;
636  }
637 
638  iterator & operator--() noexcept
639  {
640  m_curr--;
641  return *this;
642  }
643 
644  iterator operator--(int) noexcept
645  {
646  iterator copy(*this);
647  operator++();
648  return copy;
649  }
650 
651  iterator & operator-=(difference_type i_diff) noexcept
652  {
653  m_curr -= i_diff;
654  return *this;
655  }
656 
657  friend difference_type
658  operator-(const iterator & i_first, const iterator & i_second) noexcept
659  {
660  return i_first.m_curr - i_second.m_curr;
661  }
662 
663  // relational operators
664 
665  friend bool operator<(const iterator & i_first, const iterator & i_second) noexcept
666  {
667  return i_first.m_curr < i_second.m_curr;
668  }
669 
670  friend bool operator<=(const iterator & i_first, const iterator & i_second) noexcept
671  {
672  return i_first.m_curr <= i_second.m_curr;
673  }
674 
675  friend bool operator==(const iterator & i_first, const iterator & i_second) noexcept
676  {
677  return i_first.m_curr == i_second.m_curr;
678  }
679 
680  friend bool operator!=(const iterator & i_first, const iterator & i_second) noexcept
681  {
682  return i_first.m_curr != i_second.m_curr;
683  }
684 
685  friend bool operator>(const iterator & i_first, const iterator & i_second) noexcept
686  {
687  return i_first.m_curr > i_second.m_curr;
688  }
689 
690  friend bool operator>=(const iterator & i_first, const iterator & i_second) noexcept
691  {
692  return i_first.m_curr >= i_second.m_curr;
693  }
694 
695  private:
696  TYPE * m_curr{};
697  };
698 
699  class const_iterator
700  {
701  public:
702  using difference_type = ptrdiff_t;
703  using value_type = const TYPE;
704  using pointer = const TYPE *;
705  using reference = const TYPE &;
706  using iterator_category = std::random_access_iterator_tag;
707 
708  const_iterator(PrivateTag, const TYPE * i_ptr) noexcept : m_curr(i_ptr) {}
709 
710  const_iterator() noexcept = default;
711  const_iterator(const const_iterator &) noexcept = default;
712  const_iterator & operator=(const const_iterator &) noexcept = default;
713 
714  const TYPE * operator->() const noexcept { return m_curr; }
715 
716  const TYPE & operator*() const & noexcept { return *m_curr; }
717 
718  const TYPE && operator*() const && noexcept { return *m_curr; }
719 
720  const_iterator & operator++() noexcept
721  {
722  m_curr++;
723  return *this;
724  }
725 
726  const_iterator operator++(int) noexcept
727  {
728  const_iterator copy(*this);
729  operator++();
730  return copy;
731  }
732 
733  const_iterator & operator+=(difference_type i_diff) noexcept
734  {
735  m_curr += i_diff;
736  return *this;
737  }
738 
739  const_iterator & operator--() noexcept
740  {
741  m_curr--;
742  return *this;
743  }
744 
745  const_iterator operator--(int) noexcept
746  {
747  const_iterator copy(*this);
748  operator++();
749  return copy;
750  }
751 
752  const_iterator & operator-=(difference_type i_diff) noexcept
753  {
754  m_curr -= i_diff;
755  return *this;
756  }
757 
758  friend difference_type
759  operator-(const const_iterator & i_first, const const_iterator & i_second) noexcept
760  {
761  return i_first.m_curr - i_second.m_curr;
762  }
763 
764  // relational operators
765 
766  friend bool
767  operator<(const const_iterator & i_first, const const_iterator & i_second) noexcept
768  {
769  return i_first.m_curr < i_second.m_curr;
770  }
771 
772  friend bool
773  operator<=(const const_iterator & i_first, const const_iterator & i_second) noexcept
774  {
775  return i_first.m_curr <= i_second.m_curr;
776  }
777 
778  friend bool
779  operator==(const const_iterator & i_first, const const_iterator & i_second) noexcept
780  {
781  return i_first.m_curr == i_second.m_curr;
782  }
783 
784  friend bool
785  operator!=(const const_iterator & i_first, const const_iterator & i_second) noexcept
786  {
787  return i_first.m_curr != i_second.m_curr;
788  }
789 
790  friend bool
791  operator>(const const_iterator & i_first, const const_iterator & i_second) noexcept
792  {
793  return i_first.m_curr > i_second.m_curr;
794  }
795 
796  friend bool
797  operator>=(const const_iterator & i_first, const const_iterator & i_second) noexcept
798  {
799  return i_first.m_curr >= i_second.m_curr;
800  }
801 
802  private:
803  const TYPE * m_curr{};
804  };
805 
808  lifo_array(size_t i_size) : detail::LifoArrayImpl<TYPE>(i_size)
809  {
810  default_construct(std::is_pod<TYPE>());
811  }
812 
818  template <typename... CONSTRUCTION_PARAMS>
819  lifo_array(size_t i_size, const CONSTRUCTION_PARAMS &... i_construction_params)
820  : detail::LifoArrayImpl<TYPE>(i_size)
821  {
822  size_t element_index = 0;
823  try
824  {
825  for (; element_index < i_size; element_index++)
826  {
827  new (m_elements + element_index) TYPE(i_construction_params...);
828  }
829  }
830  catch (...)
831  {
832  // destroy all the elements constructed till now, and then deallocate
833  auto it = m_elements + element_index;
834  it--;
835  for (; it >= m_elements; it--)
836  {
837  it->TYPE::~TYPE();
838  }
839  throw;
840  }
841  }
842 
848  template <typename INPUT_ITERATOR>
850  INPUT_ITERATOR i_begin,
851  INPUT_ITERATOR i_end,
852  typename std::iterator_traits<INPUT_ITERATOR>::iterator_category =
853  typename std::iterator_traits<INPUT_ITERATOR>::iterator_category())
854  : detail::LifoArrayImpl<TYPE>(std::distance(i_begin, i_end))
855  {
856  auto dest_element = m_elements;
857  try
858  {
859  for (; i_begin != i_end; ++i_begin)
860  {
861  DENSITY_ASSUME(dest_element != nullptr);
862  new (dest_element) TYPE(*i_begin);
863  ++dest_element;
864  }
865  }
866  catch (...)
867  {
868  // destroy all the elements constructed until now. The destructor of the base class will deallocate the block
869  auto count_to_destroy = dest_element - m_elements;
870  while (count_to_destroy > 0)
871  {
872  --dest_element;
873  --count_to_destroy;
874  dest_element->TYPE::~TYPE();
875  }
876  throw;
877  }
878  }
879 
881  lifo_array(const lifo_array &) = delete;
882 
884  lifo_array & operator=(const lifo_array &) = delete;
885 
887  ~lifo_array() { destroy_elements(std::is_trivially_destructible<TYPE>()); }
888 
890  size_t size() const noexcept { return m_size; }
891 
897  TYPE & operator[](size_t i_index) noexcept
898  {
899  DENSITY_ASSUME(i_index < m_size);
900  return m_elements[i_index];
901  }
902 
908  const TYPE & operator[](size_t i_index) const noexcept
909  {
910  DENSITY_ASSUME(i_index < m_size);
911  return m_elements[i_index];
912  }
913 
915  pointer data() noexcept { return m_elements; }
916 
918  const_pointer data() const noexcept { return m_elements; }
919 
920  iterator begin() noexcept { return iterator(PrivateTag{}, m_elements); }
921 
922  iterator end() noexcept { return iterator(PrivateTag{}, m_elements + m_size); }
923 
924  const_iterator cbegin() const noexcept { return const_iterator(PrivateTag{}, m_elements); }
925 
926  const_iterator cend() const noexcept
927  {
928  return const_iterator(PrivateTag{}, m_elements + m_size);
929  }
930 
931  const_iterator begin() const noexcept { return const_iterator(PrivateTag{}, m_elements); }
932 
933  const_iterator end() const noexcept
934  {
935  return const_iterator(PrivateTag{}, m_elements + m_size);
936  }
937 
938  private:
939  // trivially default constructible types
940  void default_construct(std::true_type) {}
941 
942  // non-trivially default constructible types
943  void default_construct(std::false_type)
944  {
945  size_t element_index = 0;
946  try
947  {
948  for (; element_index < m_size; element_index++)
949  {
950  auto const dest = m_elements + element_index;
951  DENSITY_ASSUME(dest != nullptr);
952  new (dest) TYPE();
953  }
954  }
955  catch (...)
956  {
957  // destroy all the elements constructed until now. The destructor of the base class will deallocate the block
958  auto count_to_destroy = element_index;
959  while (count_to_destroy > 0)
960  {
961  --count_to_destroy;
962  m_elements[count_to_destroy].TYPE::~TYPE();
963  }
964  throw;
965  }
966  }
967 
968  void destroy_elements(std::true_type) noexcept {}
969 
970  void destroy_elements(std::false_type) noexcept
971  {
972  auto it = m_elements + m_size;
973  it--;
974  for (; it >= m_elements; it--)
975  {
976  it->TYPE::~TYPE();
977  }
978  }
979 
980  private:
981  using detail::LifoArrayImpl<TYPE>::m_elements;
982  using detail::LifoArrayImpl<TYPE>::m_size;
983  };
984 
1153 } // namespace density
void * address_add(void *i_address, size_t i_offset) noexcept
Definition: density_common.h:142
lifo_allocator & operator=(const lifo_allocator &)=delete
Definition: conc_function_queue.h:11
void * reallocate(void *i_block, size_t i_old_size, size_t i_new_size)
Definition: lifo.h:172
~lifo_allocator()
Definition: lifo.h:212
void * allocate(size_t i_size)
Definition: lifo.h:93
Definition: runtime_type.h:1061
lifo_allocator(UNDERLYING_ALLOCATOR &&i_underlying_allocator)
Definition: lifo.h:72
lifo_array(INPUT_ITERATOR i_begin, INPUT_ITERATOR i_end, typename std::iterator_traits< INPUT_ITERATOR >::iterator_category=typename std::iterator_traits< INPUT_ITERATOR >::iterator_category())
Definition: lifo.h:849
Definition: lifo.h:421
lifo_array(size_t i_size, const CONSTRUCTION_PARAMS &...i_construction_params)
Definition: lifo.h:819
uintptr_t address_diff(const void *i_end_address, const void *i_start_address) noexcept
Definition: density_common.h:184
const UNDERLYING_ALLOCATOR & underlying_allocator_ref() const noexcept
Definition: lifo.h:224
lifo_buffer(size_t i_size)
Definition: lifo.h:434
#define DENSITY_NO_INLINE
Definition: density_config.h:86
void * data() const noexcept
Definition: lifo.h:475
#define DENSITY_ASSUME_ALIGNED(address, constexpr_alignment)
Definition: density_config.h:59
lifo_allocator(const UNDERLYING_ALLOCATOR &i_underlying_allocator)
Definition: lifo.h:66
UNDERLYING_ALLOCATOR & underlying_allocator_ref() noexcept
Definition: lifo.h:221
constexpr UINT uint_lower_align(UINT i_uint, size_t i_alignment) noexcept
Definition: density_common.h:311
Definition: default_allocator.h:149
const_pointer data() const noexcept
Definition: lifo.h:918
~lifo_array()
Definition: lifo.h:887
void * resize(size_t i_new_size)
Definition: lifo.h:464
lifo_array(size_t i_size)
Definition: lifo.h:808
lifo_buffer() noexcept
Definition: lifo.h:428
size_t size() const noexcept
Definition: lifo.h:890
constexpr bool is_power_of_2(size_t i_number) noexcept
Definition: density_common.h:109
Definition: lifo.h:583
void * address_upper_align(void *i_address, size_t i_alignment) noexcept
Definition: density_common.h:264
void * allocate_empty() noexcept
Definition: lifo.h:129
~lifo_buffer()
Definition: lifo.h:447
#define DENSITY_ASSERT_INTERNAL(...)
Definition: density_config.h:28
size_t size() const noexcept
Definition: lifo.h:478
constexpr lifo_allocator() noexcept
Definition: lifo.h:63
const TYPE & operator[](size_t i_index) const noexcept
Definition: lifo.h:908
constexpr UINT uint_upper_align(UINT i_uint, size_t i_alignment) noexcept
Definition: density_common.h:297
pointer data() noexcept
Definition: lifo.h:915
void deallocate(void *i_block, size_t i_size) noexcept
Definition: lifo.h:140
#define DENSITY_LIKELY(bool_expr)
Definition: density_config.h:76
Definition: lifo.h:46
static constexpr size_t alignment
Definition: lifo.h:55
#define DENSITY_ASSUME(bool_expr,...)
Definition: density_config.h:46
TYPE & operator[](size_t i_index) noexcept
Definition: lifo.h:897