stack_vec.h 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. #pragma once
  2. #include <util/generic/vector.h>
  3. #include <util/ysaveload.h>
  4. #include <type_traits>
  5. // A vector preallocated on the stack.
  6. // After exceeding the preconfigured stack space falls back to the heap.
  7. // Publicly inherits TVector, but disallows swap (and hence shrink_to_fit, also operator= is reimplemented via copying).
  8. //
  9. // Inspired by: http://qt-project.org/doc/qt-4.8/qvarlengtharray.html#details
  10. template <typename T, size_t CountOnStack = 256, bool UseFallbackAlloc = true, class Alloc = std::allocator<T>>
  11. class TStackVec;
  12. template <typename T, class Alloc = std::allocator<T>>
  13. using TSmallVec = TStackVec<T, 16, true, Alloc>;
  14. template <typename T, size_t CountOnStack = 256>
  15. using TStackOnlyVec = TStackVec<T, CountOnStack, false>;
  16. namespace NPrivate {
  17. template <class Alloc, class StackAlloc, typename T, typename U>
  18. struct TRebind {
  19. typedef TReboundAllocator<Alloc, U> other;
  20. };
  21. template <class Alloc, class StackAlloc, typename T>
  22. struct TRebind<Alloc, StackAlloc, T, T> {
  23. typedef StackAlloc other;
  24. };
  25. template <typename T, size_t CountOnStack, bool UseFallbackAlloc, class Alloc = std::allocator<T>>
  26. class TStackBasedAllocator: public Alloc {
  27. public:
  28. typedef TStackBasedAllocator<T, CountOnStack, UseFallbackAlloc, Alloc> TSelf;
  29. using typename Alloc::difference_type;
  30. using typename Alloc::size_type;
  31. using typename Alloc::value_type;
  32. template <class U>
  33. struct rebind: public ::NPrivate::TRebind<Alloc, TSelf, T, U> {
  34. };
  35. public:
  36. //NOTE: it is important to make this syntax; using =default will lead to memset https://godbolt.org/z/vTqzK9aWr
  37. TStackBasedAllocator() noexcept {}
  38. template <
  39. typename... TArgs,
  40. typename = std::enable_if_t<
  41. std::is_constructible_v<Alloc, TArgs...>
  42. >
  43. >
  44. TStackBasedAllocator(TArgs&&... args)
  45. : Alloc(std::forward<TArgs>(args)...)
  46. {}
  47. T* allocate(size_type n) {
  48. if (!IsStorageUsed && CountOnStack >= n) {
  49. IsStorageUsed = true;
  50. return reinterpret_cast<T*>(&StackBasedStorage[0]);
  51. } else {
  52. if constexpr (!UseFallbackAlloc) {
  53. Y_ABORT(
  54. "Stack storage overflow. Capacity: %d, requested: %d", (int)CountOnStack, int(n));
  55. }
  56. return FallbackAllocator().allocate(n);
  57. }
  58. }
  59. void deallocate(T* p, size_type n) {
  60. if (p >= reinterpret_cast<T*>(&StackBasedStorage[0]) &&
  61. p < reinterpret_cast<T*>(&StackBasedStorage[CountOnStack])) {
  62. Y_ABORT_UNLESS(IsStorageUsed);
  63. IsStorageUsed = false;
  64. } else {
  65. FallbackAllocator().deallocate(p, n);
  66. }
  67. }
  68. private:
  69. std::aligned_storage_t<sizeof(T), alignof(T)> StackBasedStorage[CountOnStack];
  70. bool IsStorageUsed = false;
  71. private:
  72. Alloc& FallbackAllocator() noexcept {
  73. return static_cast<Alloc&>(*this);
  74. }
  75. };
  76. }
  77. template <typename T, size_t CountOnStack, bool UseFallbackAlloc, class Alloc>
  78. class TStackVec: public TVector<T, ::NPrivate::TStackBasedAllocator<T, CountOnStack, UseFallbackAlloc, TReboundAllocator<Alloc, T>>> {
  79. private:
  80. using TBase = TVector<T, ::NPrivate::TStackBasedAllocator<T, CountOnStack, UseFallbackAlloc, TReboundAllocator<Alloc, T>>>;
  81. using TAllocator = typename TBase::allocator_type;
  82. public:
  83. using typename TBase::const_iterator;
  84. using typename TBase::const_reverse_iterator;
  85. using typename TBase::iterator;
  86. using typename TBase::reverse_iterator;
  87. using typename TBase::size_type;
  88. using typename TBase::value_type;
  89. public:
  90. TStackVec(const TAllocator& alloc = TAllocator())
  91. : TBase(alloc)
  92. {
  93. TBase::reserve(CountOnStack);
  94. }
  95. explicit TStackVec(size_type count, const TAllocator& alloc = TAllocator())
  96. : TBase(alloc)
  97. {
  98. if (count <= CountOnStack) {
  99. TBase::reserve(CountOnStack);
  100. }
  101. TBase::resize(count);
  102. }
  103. TStackVec(size_type count, const T& val, const TAllocator& alloc = TAllocator())
  104. : TBase(alloc)
  105. {
  106. if (count <= CountOnStack) {
  107. TBase::reserve(CountOnStack);
  108. }
  109. TBase::assign(count, val);
  110. }
  111. TStackVec(const TStackVec& src)
  112. : TStackVec(src.begin(), src.end())
  113. {
  114. }
  115. template <class A>
  116. TStackVec(const TVector<T, A>& src)
  117. : TStackVec(src.begin(), src.end())
  118. {
  119. }
  120. TStackVec(std::initializer_list<T> il, const TAllocator& alloc = TAllocator())
  121. : TStackVec(il.begin(), il.end(), alloc)
  122. {
  123. }
  124. template <class TIter>
  125. TStackVec(TIter first, TIter last, const TAllocator& alloc = TAllocator())
  126. : TBase(alloc)
  127. {
  128. // NB(eeight) Since we want to call 'reserve' here, we cannot just delegate to TVector ctor.
  129. // The best way to insert values afterwards is to call TVector::insert. However there is a caveat.
  130. // In order to call this ctor of TVector, T needs to be just move-constructible. Insert however
  131. // requires T to be move-assignable.
  132. TBase::reserve(CountOnStack);
  133. if constexpr (std::is_move_assignable_v<T>) {
  134. // Fast path
  135. TBase::insert(TBase::end(), first, last);
  136. } else {
  137. // Slow path.
  138. for (; first != last; ++first) {
  139. TBase::push_back(*first);
  140. }
  141. }
  142. }
  143. public:
  144. void swap(TStackVec&) = delete;
  145. void shrink_to_fit() = delete;
  146. TStackVec& operator=(const TStackVec& src) {
  147. TBase::assign(src.begin(), src.end());
  148. return *this;
  149. }
  150. template <class A>
  151. TStackVec& operator=(const TVector<T, A>& src) {
  152. TBase::assign(src.begin(), src.end());
  153. return *this;
  154. }
  155. TStackVec& operator=(std::initializer_list<T> il) {
  156. TBase::assign(il.begin(), il.end());
  157. return *this;
  158. }
  159. };
  160. template <typename T, size_t CountOnStack, class Alloc>
  161. class TSerializer<TStackVec<T, CountOnStack, true, Alloc>>: public TVectorSerializer<TStackVec<T, CountOnStack, true, Alloc>> {
  162. };
  163. template <typename T, size_t CountOnStack, class Alloc>
  164. class TSerializer<TStackVec<T, CountOnStack, false, Alloc>> {
  165. public:
  166. static void Save(IOutputStream* rh, const TStackVec<T, CountOnStack, false, Alloc>& v) {
  167. if constexpr (CountOnStack < 256) {
  168. ::Save(rh, (ui8)v.size());
  169. } else {
  170. ::Save(rh, v.size());
  171. }
  172. ::SaveArray(rh, v.data(), v.size());
  173. }
  174. static void Load(IInputStream* rh, TStackVec<T, CountOnStack, false, Alloc>& v) {
  175. std::conditional_t<CountOnStack < 256, ui8, size_t> size;
  176. ::Load(rh, size);
  177. v.resize(size);
  178. ::LoadPodArray(rh, v.data(), v.size());
  179. }
  180. };