#pragma once #include "alloc.h" #include #include #include #include #include #include #include #include #include #include /** * Memory pool implements a memory allocation scheme that is very fast, but * limited in its usage. * * A common use case is when you want to allocate a bunch of small objects, and * then release them all at some point of your program. Using memory pool, you * can just drop them off into oblivion without calling any destructors, * provided that all associated memory was allocated on the pool. */ class TMemoryPool { private: using TBlock = IAllocator::TBlock; class TChunk: public TIntrusiveListItem { public: inline TChunk(size_t len = 0) noexcept : Cur_((char*)(this + 1)) , Left_(len) { Y_ASSERT((((size_t)Cur_) % PLATFORM_DATA_ALIGN) == 0); } inline void* Allocate(size_t len) noexcept { if (Left_ >= len) { char* ret = Cur_; Cur_ += len; Left_ -= len; return ret; } return nullptr; } inline void* Allocate(size_t len, size_t align) noexcept { size_t pad = AlignUp(Cur_, align) - Cur_; void* ret = Allocate(pad + len); if (ret) { return static_cast(ret) + pad; } return nullptr; } inline size_t BlockLength() const noexcept { return (Cur_ + Left_) - (char*)this; } inline size_t Used() const noexcept { return Cur_ - (const char*)this; } inline size_t Left() const noexcept { return Left_; } inline const char* Data() const noexcept { return (const char*)(this + 1); } inline char* Data() noexcept { return (char*)(this + 1); } inline size_t DataSize() const noexcept { return Cur_ - Data(); } inline void ResetChunk() noexcept { size_t total = DataSize() + Left(); Cur_ = Data(); Left_ = total; } private: char* Cur_; size_t Left_; }; using TChunkList = TIntrusiveList; public: class IGrowPolicy { public: virtual ~IGrowPolicy() = default; virtual size_t Next(size_t prev) const noexcept = 0; }; class TLinearGrow: public IGrowPolicy { public: size_t Next(size_t prev) const noexcept override { return prev; } static IGrowPolicy* Instance() noexcept; }; class TExpGrow: public IGrowPolicy { public: size_t Next(size_t prev) const noexcept override { return prev * 2; } static IGrowPolicy* Instance() noexcept; }; struct TOptions { bool RoundUpToNextPowerOfTwo; TOptions() : RoundUpToNextPowerOfTwo(true) { } }; inline TMemoryPool(size_t initial, IGrowPolicy* grow = TExpGrow::Instance(), IAllocator* alloc = TDefaultAllocator::Instance(), const TOptions& options = TOptions()) : Current_(&Empty_) , BlockSize_(initial) , GrowPolicy_(grow) , Alloc_(alloc) , Options_(options) , Origin_(initial) , MemoryAllocatedBeforeCurrent_(0) , MemoryWasteBeforeCurrent_(0) { } inline ~TMemoryPool() { Clear(); } inline void* Allocate(size_t len) { return RawAllocate(AlignUp(len, PLATFORM_DATA_ALIGN)); } inline void* Allocate(size_t len, size_t align) { return RawAllocate(AlignUp(len, PLATFORM_DATA_ALIGN), align); } template inline T* Allocate() { return (T*)this->Allocate(sizeof(T), alignof(T)); } template inline T* Allocate(size_t align) { return (T*)this->Allocate(sizeof(T), Max(align, alignof(T))); } template inline T* AllocateArray(size_t count) { return (T*)this->Allocate(sizeof(T) * count, alignof(T)); } template inline T* AllocateArray(size_t count, size_t align) { return (T*)this->Allocate(sizeof(T) * count, Max(align, alignof(T))); } template inline T* AllocateZeroArray(size_t count) { T* ptr = AllocateArray(count); memset(ptr, 0, count * sizeof(T)); return ptr; } template inline T* AllocateZeroArray(size_t count, size_t align) { T* ptr = AllocateArray(count, align); memset(ptr, 0, count * sizeof(T)); return ptr; } template inline T* New(Args&&... args) { return new (Allocate()) T(std::forward(args)...); } template inline T* NewArray(size_t count) { T* arr = (T*)AllocateArray(count); for (T *ptr = arr, *end = arr + count; ptr != end; ++ptr) { new (ptr) T; } return arr; } template inline TChar* Append(const TChar* str) { return Append(str, std::char_traits::length(str) + 1); // include terminating zero byte } template inline TChar* Append(const TChar* str, size_t len) { TChar* ret = AllocateArray(len); std::char_traits::copy(ret, str, len); return ret; } template inline TBasicStringBuf AppendString(const TBasicStringBuf& buf) { return TBasicStringBuf(Append(buf.data(), buf.size()), buf.size()); } template inline TBasicStringBuf AppendCString(const TBasicStringBuf& buf) { TChar* ret = static_cast(Allocate((buf.size() + 1) * sizeof(TChar))); std::char_traits::copy(ret, buf.data(), buf.size()); *(ret + buf.size()) = 0; return TBasicStringBuf(ret, buf.size()); } inline size_t Available() const noexcept { return Current_->Left(); } inline void Clear() noexcept { DoClear(false); } inline void ClearKeepFirstChunk() noexcept { DoClear(true); } inline size_t ClearReturnUsedChunkCount(bool keepFirstChunk) noexcept { return DoClear(keepFirstChunk); } inline size_t MemoryAllocated() const noexcept { return MemoryAllocatedBeforeCurrent_ + (Current_ != &Empty_ ? Current_->Used() : 0); } inline size_t MemoryWaste() const noexcept { return MemoryWasteBeforeCurrent_ + (Current_ != &Empty_ ? Current_->Left() : 0); } template inline void Traverse(TOp& op) const noexcept { for (TChunkList::TConstIterator i = Chunks_.Begin(); i != Chunks_.End(); ++i) { op(i->Data(), i->DataSize()); } } inline IAllocator* RealAllocator() const noexcept { return Alloc_; } protected: inline void* RawAllocate(size_t len) { void* ret = Current_->Allocate(len); if (ret) { return ret; } AddChunk(len); return Current_->Allocate(len); } inline void* RawAllocate(size_t len, size_t align) { Y_ASSERT(align > 0); void* ret = Current_->Allocate(len, align); if (ret) { return ret; } AddChunk(len + align - 1); return Current_->Allocate(len, align); } private: void AddChunk(size_t hint); size_t DoClear(bool keepfirst) noexcept; private: TChunk Empty_; TChunk* Current_; size_t BlockSize_; IGrowPolicy* GrowPolicy_; IAllocator* Alloc_; TOptions Options_; TChunkList Chunks_; const size_t Origin_; size_t MemoryAllocatedBeforeCurrent_; size_t MemoryWasteBeforeCurrent_; }; template struct TPoolableBase { inline void* operator new(size_t bytes, TPool& pool) { return pool.Allocate(bytes); } inline void* operator new(size_t bytes, std::align_val_t align, TPool& pool) { return pool.Allocate(bytes, (size_t)align); } inline void operator delete(void*, size_t) noexcept { } inline void operator delete(void*, TPool&) noexcept { } private: inline void* operator new(size_t); // disallow default allocation }; struct TPoolable: public TPoolableBase { }; class TMemoryPoolAllocator: public IAllocator { public: inline TMemoryPoolAllocator(TMemoryPool* pool) : Pool_(pool) { } TBlock Allocate(size_t len) override { TBlock ret = {Pool_->Allocate(len), len}; return ret; } void Release(const TBlock& block) override { (void)block; } private: TMemoryPool* Pool_; }; template class TPoolAllocBase { public: using pointer = T*; using const_pointer = const T*; using reference = T&; using const_reference = const T&; using size_type = size_t; using difference_type = ptrdiff_t; using value_type = T; inline TPoolAllocBase(TPool* pool) : Pool_(pool) { } template inline TPoolAllocBase(const TPoolAllocBase& o) : Pool_(o.GetPool()) { } inline T* allocate(size_t n) { return (T*)Pool_->Allocate(n * sizeof(T), alignof(T)); } inline void deallocate(pointer /*p*/, size_t /*n*/) { } template struct rebind { using other = TPoolAllocBase; }; inline size_type max_size() const noexcept { return size_type(-1) / sizeof(T); } template inline void construct(pointer p, Args&&... args) { new (p) T(std::forward(args)...); } inline void destroy(pointer p) noexcept { (void)p; /* Make MSVC happy. */ p->~T(); } inline TPool* GetPool() const { return Pool_; } inline friend bool operator==(const TPoolAllocBase& l, const TPoolAllocBase& r) { return l.Pool_ == r.Pool_; } inline friend bool operator!=(const TPoolAllocBase& l, const TPoolAllocBase& r) { return !(l == r); } private: TPool* Pool_; }; template using TPoolAlloc = TPoolAllocBase; // Any type since it is supposed to be rebound anyway. using TPoolAllocator = TPoolAlloc; template inline bool operator==(const TPoolAlloc&, const TPoolAlloc&) noexcept { return true; } template inline bool operator!=(const TPoolAlloc&, const TPoolAlloc&) noexcept { return false; }