stack_vec.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. TStackBasedAllocator() = default;
  37. template <
  38. typename... TArgs,
  39. typename = std::enable_if_t<
  40. std::is_constructible_v<Alloc, TArgs...>
  41. >
  42. >
  43. TStackBasedAllocator(TArgs&&... args)
  44. : Alloc(std::forward<TArgs>(args)...)
  45. {}
  46. T* allocate(size_type n) {
  47. if (!IsStorageUsed && CountOnStack >= n) {
  48. IsStorageUsed = true;
  49. return reinterpret_cast<T*>(&StackBasedStorage[0]);
  50. } else {
  51. if constexpr (!UseFallbackAlloc) {
  52. Y_FAIL(
  53. "Stack storage overflow. Capacity: %d, requested: %d", (int)CountOnStack, int(n));
  54. }
  55. return FallbackAllocator().allocate(n);
  56. }
  57. }
  58. void deallocate(T* p, size_type n) {
  59. if (p >= reinterpret_cast<T*>(&StackBasedStorage[0]) &&
  60. p < reinterpret_cast<T*>(&StackBasedStorage[CountOnStack])) {
  61. Y_VERIFY(IsStorageUsed);
  62. IsStorageUsed = false;
  63. } else {
  64. FallbackAllocator().deallocate(p, n);
  65. }
  66. }
  67. private:
  68. std::aligned_storage_t<sizeof(T), alignof(T)> StackBasedStorage[CountOnStack];
  69. bool IsStorageUsed = false;
  70. private:
  71. Alloc& FallbackAllocator() noexcept {
  72. return static_cast<Alloc&>(*this);
  73. }
  74. };
  75. }
  76. template <typename T, size_t CountOnStack, bool UseFallbackAlloc, class Alloc>
  77. class TStackVec: public TVector<T, ::NPrivate::TStackBasedAllocator<T, CountOnStack, UseFallbackAlloc, TReboundAllocator<Alloc, T>>> {
  78. private:
  79. using TBase = TVector<T, ::NPrivate::TStackBasedAllocator<T, CountOnStack, UseFallbackAlloc, TReboundAllocator<Alloc, T>>>;
  80. using TAllocator = typename TBase::allocator_type;
  81. public:
  82. using typename TBase::const_iterator;
  83. using typename TBase::const_reverse_iterator;
  84. using typename TBase::iterator;
  85. using typename TBase::reverse_iterator;
  86. using typename TBase::size_type;
  87. using typename TBase::value_type;
  88. public:
  89. TStackVec(const TAllocator& alloc = TAllocator())
  90. : TBase(alloc)
  91. {
  92. TBase::reserve(CountOnStack);
  93. }
  94. explicit TStackVec(size_type count, const TAllocator& alloc = TAllocator())
  95. : TBase(alloc)
  96. {
  97. if (count <= CountOnStack) {
  98. TBase::reserve(CountOnStack);
  99. }
  100. TBase::resize(count);
  101. }
  102. TStackVec(size_type count, const T& val, const TAllocator& alloc = TAllocator())
  103. : TBase(alloc)
  104. {
  105. if (count <= CountOnStack) {
  106. TBase::reserve(CountOnStack);
  107. }
  108. TBase::assign(count, val);
  109. }
  110. TStackVec(const TStackVec& src)
  111. : TStackVec(src.begin(), src.end())
  112. {
  113. }
  114. template <class A>
  115. TStackVec(const TVector<T, A>& src)
  116. : TStackVec(src.begin(), src.end())
  117. {
  118. }
  119. TStackVec(std::initializer_list<T> il, const TAllocator& alloc = TAllocator())
  120. : TStackVec(il.begin(), il.end(), alloc)
  121. {
  122. }
  123. template <class TIter>
  124. TStackVec(TIter first, TIter last, const TAllocator& alloc = TAllocator())
  125. : TBase(alloc)
  126. {
  127. // NB(eeight) Since we want to call 'reserve' here, we cannot just delegate to TVector ctor.
  128. // The best way to insert values afterwards is to call TVector::insert. However there is a caveat.
  129. // In order to call this ctor of TVector, T needs to be just move-constructible. Insert however
  130. // requires T to be move-assignable.
  131. TBase::reserve(CountOnStack);
  132. if constexpr (std::is_move_assignable_v<T>) {
  133. // Fast path
  134. TBase::insert(TBase::end(), first, last);
  135. } else {
  136. // Slow path.
  137. for (; first != last; ++first) {
  138. TBase::push_back(*first);
  139. }
  140. }
  141. }
  142. public:
  143. void swap(TStackVec&) = delete;
  144. void shrink_to_fit() = delete;
  145. TStackVec& operator=(const TStackVec& src) {
  146. TBase::assign(src.begin(), src.end());
  147. return *this;
  148. }
  149. template <class A>
  150. TStackVec& operator=(const TVector<T, A>& src) {
  151. TBase::assign(src.begin(), src.end());
  152. return *this;
  153. }
  154. TStackVec& operator=(std::initializer_list<T> il) {
  155. TBase::assign(il.begin(), il.end());
  156. return *this;
  157. }
  158. };
  159. template <typename T, size_t CountOnStack, class Alloc>
  160. class TSerializer<TStackVec<T, CountOnStack, true, Alloc>>: public TVectorSerializer<TStackVec<T, CountOnStack, true, Alloc>> {
  161. };
  162. template <typename T, size_t CountOnStack, class Alloc>
  163. class TSerializer<TStackVec<T, CountOnStack, false, Alloc>> {
  164. public:
  165. static void Save(IOutputStream* rh, const TStackVec<T, CountOnStack, false, Alloc>& v) {
  166. if constexpr (CountOnStack < 256) {
  167. ::Save(rh, (ui8)v.size());
  168. } else {
  169. ::Save(rh, v.size());
  170. }
  171. ::SaveArray(rh, v.data(), v.size());
  172. }
  173. static void Load(IInputStream* rh, TStackVec<T, CountOnStack, false, Alloc>& v) {
  174. std::conditional_t<CountOnStack < 256, ui8, size_t> size;
  175. ::Load(rh, size);
  176. v.resize(size);
  177. ::LoadPodArray(rh, v.data(), v.size());
  178. }
  179. };