#include "pool.h" #include #include class TCheckedAllocator: public TDefaultAllocator { public: inline TCheckedAllocator() : Alloced_(0) , Released_(0) , Allocs_(0) , Frees_(0) { } TBlock Allocate(size_t len) override { Check(); Alloced_ += len; ++Allocs_; return TDefaultAllocator::Allocate(len); } void Release(const TBlock& block) override { Released_ += block.Len; ++Frees_; Check(); TDefaultAllocator::Release(block); } inline void CheckAtEnd() { UNIT_ASSERT_EQUAL(Alloced_, Released_); UNIT_ASSERT_EQUAL(Allocs_, Frees_); } private: inline void Check() { UNIT_ASSERT(Alloced_ >= Released_); UNIT_ASSERT(Allocs_ >= Frees_); } private: size_t Alloced_; size_t Released_; size_t Allocs_; size_t Frees_; }; class TErrorOnCopy { public: TErrorOnCopy() = default; TErrorOnCopy(TErrorOnCopy&&) = default; TErrorOnCopy(const TErrorOnCopy&) { UNIT_ASSERT(false); } }; class TNoCopy { public: TNoCopy() = default; TNoCopy(TNoCopy&&) = default; TNoCopy(const TNoCopy&) = delete; }; class TNoMove { public: TNoMove() = default; TNoMove(const TNoMove&) = default; TNoMove(TNoMove&&) = delete; }; class TMemPoolTest: public TTestBase { UNIT_TEST_SUITE(TMemPoolTest); UNIT_TEST(TestMemPool) UNIT_TEST(TestAlign) UNIT_TEST(TestZeroArray) UNIT_TEST(TestLargeStartingAlign) UNIT_TEST(TestMoveAlloc) UNIT_TEST(TestRoundUpToNextPowerOfTwoOption) UNIT_TEST_SUITE_END(); private: inline void TestMemPool() { TCheckedAllocator alloc; { TMemoryPool pool(123, TMemoryPool::TExpGrow::Instance(), &alloc); for (size_t i = 0; i < 1000; ++i) { UNIT_ASSERT(pool.Allocate(i)); } } alloc.CheckAtEnd(); { TMemoryPool pool(150, TMemoryPool::TExpGrow::Instance(), &alloc); pool.Allocate(8); size_t memavail = pool.Available(); size_t memwaste = pool.MemoryWaste(); size_t memalloc = pool.MemoryAllocated(); for (size_t i = 0; i < 1000; ++i) { void* m = pool.Allocate(i); UNIT_ASSERT(m); memset(m, 0, i); } UNIT_ASSERT_VALUES_EQUAL(pool.ClearReturnUsedChunkCount(true), 11); UNIT_ASSERT_VALUES_EQUAL(memalloc - 8, pool.MemoryAllocated()); UNIT_ASSERT_VALUES_EQUAL(memwaste + 8, pool.MemoryWaste()); UNIT_ASSERT_VALUES_EQUAL(memavail + 8, pool.Available()); for (size_t i = 0; i < 1000; ++i) { void* m = pool.Allocate(i); UNIT_ASSERT(m); memset(m, 0, i); } UNIT_ASSERT_VALUES_EQUAL(pool.ClearReturnUsedChunkCount(false), 12); UNIT_ASSERT_VALUES_EQUAL(0, pool.MemoryAllocated()); UNIT_ASSERT_VALUES_EQUAL(0, pool.MemoryWaste()); UNIT_ASSERT_VALUES_EQUAL(0, pool.Available()); } alloc.CheckAtEnd(); struct TConstructorTest { int ConstructorType; TConstructorTest() : ConstructorType(1) { } TConstructorTest(int) : ConstructorType(2) { } TConstructorTest(const TString&, const TString&) : ConstructorType(3) { } TConstructorTest(TString&&, TString&&) : ConstructorType(4) { } }; { TMemoryPool pool(123, TMemoryPool::TExpGrow::Instance(), &alloc); THolder data1{pool.New()}; THolder data2{pool.New(42)}; THolder data3{pool.New("hello", "world")}; UNIT_ASSERT_VALUES_EQUAL(data1->ConstructorType, 1); UNIT_ASSERT_VALUES_EQUAL(data2->ConstructorType, 2); UNIT_ASSERT_VALUES_EQUAL(data3->ConstructorType, 4); } alloc.CheckAtEnd(); } inline void TestAlign() { TMemoryPool pool(1); void* aligned16 = pool.Allocate(3, 16); void* aligned2 = pool.Allocate(3, 2); void* aligned128 = pool.Allocate(3, 128); void* aligned4 = pool.Allocate(3, 4); void* aligned256 = pool.Allocate(3, 256); void* aligned8 = pool.Allocate(3, 8); void* aligned1024 = pool.Allocate(3, 1024); UNIT_ASSERT_VALUES_UNEQUAL(aligned16, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned2, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned128, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned4, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned256, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned8, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned1024, nullptr); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned2) & 1, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned4) & 3, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned8) & 7, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned16) & 15, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned128) & 127, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned256) & 255, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned1024) & 1023, 0); } void TestZeroArray() { TMemoryPool pool(1); size_t size = 10; i32* intArray = pool.AllocateZeroArray(size); for (size_t i = 0; i < size; ++i) { UNIT_ASSERT(intArray[i] == 0); } size_t align = 256; ui8* byteArray = pool.AllocateZeroArray(size, align); UNIT_ASSERT(size_t(byteArray) % align == 0); for (size_t i = 0; i < size; ++i) { UNIT_ASSERT(byteArray[i] == 0); } } void TestLargeStartingAlign() { TMemoryPool pool(1); void* aligned4k1 = pool.Allocate(1, 4096); void* aligned4k2 = pool.Allocate(1, 4096); UNIT_ASSERT_VALUES_UNEQUAL(aligned4k1, nullptr); UNIT_ASSERT_VALUES_UNEQUAL(aligned4k2, nullptr); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned4k1) & 4095, 0); UNIT_ASSERT_VALUES_EQUAL(reinterpret_cast(aligned4k2) & 4095, 0); } template void CheckMoveAlloc() { TMemoryPool pool(10 * sizeof(T)); TVector elems(&pool); elems.reserve(1); elems.emplace_back(); elems.resize(100); } void TestMoveAlloc() { CheckMoveAlloc(); CheckMoveAlloc(); CheckMoveAlloc(); } void TestRoundUpToNextPowerOfTwoOption() { const size_t MEMORY_POOL_BLOCK_SIZE = (1024 - 16) * 4096 - 16 - 16 - 32; class TFixedBlockSizeMemoryPoolPolicy final: public TMemoryPool::IGrowPolicy { public: size_t Next(size_t /*prev*/) const noexcept override { return MEMORY_POOL_BLOCK_SIZE; } }; TFixedBlockSizeMemoryPoolPolicy allocationPolicy; class TTestAllocator final: public TDefaultAllocator { public: TBlock Allocate(size_t len) override { Size_ += len; return TDefaultAllocator::Allocate(len); } size_t GetSize() const { return Size_; } private: size_t Size_ = 0; }; TTestAllocator allocator; TMemoryPool::TOptions options; options.RoundUpToNextPowerOfTwo = false; constexpr size_t EXPECTED_ALLOCATION_SIZE = MEMORY_POOL_BLOCK_SIZE + 32; TMemoryPool pool(MEMORY_POOL_BLOCK_SIZE, &allocationPolicy, &allocator, options); pool.Allocate(MEMORY_POOL_BLOCK_SIZE); UNIT_ASSERT_VALUES_EQUAL(EXPECTED_ALLOCATION_SIZE, allocator.GetSize()); pool.Allocate(1); UNIT_ASSERT_VALUES_EQUAL(2 * EXPECTED_ALLOCATION_SIZE, allocator.GetSize()); } }; UNIT_TEST_SUITE_REGISTRATION(TMemPoolTest);