#pragma once #include #include #include // A vector preallocated on the stack. // After exceeding the preconfigured stack space falls back to the heap. // Publicly inherits TVector, but disallows swap (and hence shrink_to_fit, also operator= is reimplemented via copying). // // Inspired by: http://qt-project.org/doc/qt-4.8/qvarlengtharray.html#details template > class TStackVec; template > using TSmallVec = TStackVec; template using TStackOnlyVec = TStackVec; namespace NPrivate { template struct TRebind { typedef TReboundAllocator other; }; template struct TRebind { typedef StackAlloc other; }; template > class TStackBasedAllocator: public Alloc { public: typedef TStackBasedAllocator TSelf; using typename Alloc::difference_type; using typename Alloc::size_type; using typename Alloc::value_type; template struct rebind: public ::NPrivate::TRebind { }; public: //NOTE: it is important to make this syntax; using =default will lead to memset https://godbolt.org/z/vTqzK9aWr TStackBasedAllocator() noexcept {} template < typename... TArgs, typename = std::enable_if_t< std::is_constructible_v > > TStackBasedAllocator(TArgs&&... args) : Alloc(std::forward(args)...) {} T* allocate(size_type n) { if (!IsStorageUsed && CountOnStack >= n) { IsStorageUsed = true; return reinterpret_cast(&StackBasedStorage[0]); } else { if constexpr (!UseFallbackAlloc) { Y_ABORT( "Stack storage overflow. Capacity: %d, requested: %d", (int)CountOnStack, int(n)); } return FallbackAllocator().allocate(n); } } void deallocate(T* p, size_type n) { if (p >= reinterpret_cast(&StackBasedStorage[0]) && p < reinterpret_cast(&StackBasedStorage[CountOnStack])) { Y_ABORT_UNLESS(IsStorageUsed); IsStorageUsed = false; } else { FallbackAllocator().deallocate(p, n); } } private: std::aligned_storage_t StackBasedStorage[CountOnStack]; bool IsStorageUsed = false; private: Alloc& FallbackAllocator() noexcept { return static_cast(*this); } }; } template class TStackVec: public TVector>> { private: using TBase = TVector>>; using TAllocator = typename TBase::allocator_type; public: using typename TBase::const_iterator; using typename TBase::const_reverse_iterator; using typename TBase::iterator; using typename TBase::reverse_iterator; using typename TBase::size_type; using typename TBase::value_type; public: TStackVec(const TAllocator& alloc = TAllocator()) : TBase(alloc) { TBase::reserve(CountOnStack); } explicit TStackVec(size_type count, const TAllocator& alloc = TAllocator()) : TBase(alloc) { if (count <= CountOnStack) { TBase::reserve(CountOnStack); } TBase::resize(count); } TStackVec(size_type count, const T& val, const TAllocator& alloc = TAllocator()) : TBase(alloc) { if (count <= CountOnStack) { TBase::reserve(CountOnStack); } TBase::assign(count, val); } TStackVec(const TStackVec& src) : TStackVec(src.begin(), src.end()) { } template TStackVec(const TVector& src) : TStackVec(src.begin(), src.end()) { } TStackVec(std::initializer_list il, const TAllocator& alloc = TAllocator()) : TStackVec(il.begin(), il.end(), alloc) { } template TStackVec(TIter first, TIter last, const TAllocator& alloc = TAllocator()) : TBase(alloc) { // NB(eeight) Since we want to call 'reserve' here, we cannot just delegate to TVector ctor. // The best way to insert values afterwards is to call TVector::insert. However there is a caveat. // In order to call this ctor of TVector, T needs to be just move-constructible. Insert however // requires T to be move-assignable. TBase::reserve(CountOnStack); if constexpr (std::is_move_assignable_v) { // Fast path TBase::insert(TBase::end(), first, last); } else { // Slow path. for (; first != last; ++first) { TBase::push_back(*first); } } } public: void swap(TStackVec&) = delete; void shrink_to_fit() = delete; TStackVec& operator=(const TStackVec& src) { TBase::assign(src.begin(), src.end()); return *this; } template TStackVec& operator=(const TVector& src) { TBase::assign(src.begin(), src.end()); return *this; } TStackVec& operator=(std::initializer_list il) { TBase::assign(il.begin(), il.end()); return *this; } }; template class TSerializer>: public TVectorSerializer> { }; template class TSerializer> { public: static void Save(IOutputStream* rh, const TStackVec& v) { if constexpr (CountOnStack < 256) { ::Save(rh, (ui8)v.size()); } else { ::Save(rh, v.size()); } ::SaveArray(rh, v.data(), v.size()); } static void Load(IInputStream* rh, TStackVec& v) { std::conditional_t size; ::Load(rh, size); v.resize(size); ::LoadPodArray(rh, v.data(), v.size()); } };