From 5ef8c1f97b5135be7c9b8c6bf1e3746c1d685267 Mon Sep 17 00:00:00 2001 From: Jeongseok Lee Date: Wed, 5 Jan 2022 22:20:49 -0800 Subject: [PATCH] Add PoolAllocator class (#1645) --- CHANGELOG.md | 2 +- dart/common/CAllocator.cpp | 28 +- dart/common/CAllocator.hpp | 9 + dart/common/FreeListAllocator.cpp | 25 ++ dart/common/FreeListAllocator.hpp | 9 + dart/common/MemoryAllocator.hpp | 10 + dart/common/PoolAllocator.cpp | 326 +++++++++++++++++++ dart/common/PoolAllocator.hpp | 156 +++++++++ unittests/unit/common/test_PoolAllocator.cpp | 121 +++++++ 9 files changed, 684 insertions(+), 2 deletions(-) create mode 100644 dart/common/PoolAllocator.cpp create mode 100644 dart/common/PoolAllocator.hpp create mode 100644 unittests/unit/common/test_PoolAllocator.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index cf53b145e12ed..e340e508575c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ * Added Castable class: [#1634](https://github.com/dartsim/dart/pull/1634) * Added spdlog support as underlying logging framework: [#1633](https://github.com/dartsim/dart/pull/1633) - * Added custom memory allocators: [#1636](https://github.com/dartsim/dart/pull/1636), [#1637](https://github.com/dartsim/dart/pull/1637), [#1639](https://github.com/dartsim/dart/pull/1639) + * Added custom memory allocators: [#1636](https://github.com/dartsim/dart/pull/1636), [#1637](https://github.com/dartsim/dart/pull/1637), [#1639](https://github.com/dartsim/dart/pull/1639), [#1645](https://github.com/dartsim/dart/pull/1645) * Added Stopwatch class to replace Timer: [#1638](https://github.com/dartsim/dart/pull/1638) * Dynamics diff --git a/dart/common/CAllocator.cpp b/dart/common/CAllocator.cpp index c16f5ef488e42..15609d3599542 100644 --- a/dart/common/CAllocator.cpp +++ b/dart/common/CAllocator.cpp @@ -34,6 +34,7 @@ #include "dart/common/Console.hpp" #include "dart/common/Logging.hpp" +#include "dart/common/Macros.hpp" namespace dart::common { @@ -97,7 +98,7 @@ void* CAllocator::allocate(size_t size) noexcept void CAllocator::deallocate(void* pointer, size_t size) { (void)size; -#ifndef NDEBUG +#ifndef NDEBUG // debug std::lock_guard lock(mMutex); auto it = mMapPointerToSize.find(pointer); if (it != mMapPointerToSize.end()) @@ -132,6 +133,31 @@ void CAllocator::deallocate(void* pointer, size_t size) std::free(pointer); } +#ifndef NDEBUG +//============================================================================== +bool CAllocator::isAllocated(void* pointer, size_t size) const noexcept +{ + std::lock_guard lock(mMutex); + + const auto it = mMapPointerToSize.find(pointer); + if (it == mMapPointerToSize.end()) + return false; + + const auto& allocatedSize = it->second; + if (size != allocatedSize) + return false; + + return true; +} + +//============================================================================== +bool CAllocator::isEmpty() const noexcept +{ + std::lock_guard lock(mMutex); + return mMapPointerToSize.empty(); +} +#endif + //============================================================================== void CAllocator::print(std::ostream& os, int indent) const { diff --git a/dart/common/CAllocator.hpp b/dart/common/CAllocator.hpp index 9c790e9857396..6d644917f711b 100644 --- a/dart/common/CAllocator.hpp +++ b/dart/common/CAllocator.hpp @@ -61,6 +61,15 @@ class CAllocator : public MemoryAllocator // Documentation inherited void deallocate(void* pointer, size_t size) override; +#ifndef NDEBUG + // Documentation inherited + [[nodiscard]] bool isAllocated(void* pointer, size_t size) const + noexcept override; + + // Documentation inherited + [[nodiscard]] bool isEmpty() const noexcept override; +#endif + // Documentation inherited void print(std::ostream& os = std::cout, int indent = 0) const override; diff --git a/dart/common/FreeListAllocator.cpp b/dart/common/FreeListAllocator.cpp index 60107f94daed9..2dcd67d06ca5a 100644 --- a/dart/common/FreeListAllocator.cpp +++ b/dart/common/FreeListAllocator.cpp @@ -227,6 +227,31 @@ void FreeListAllocator::deallocate(void* pointer, size_t size) mFreeBlock = curr; } +#ifndef NDEBUG +//============================================================================== +bool FreeListAllocator::isAllocated(void* pointer, size_t size) const noexcept +{ + std::lock_guard lock(mMutex); + + const auto it = mMapPointerToSize.find(pointer); + if (it == mMapPointerToSize.end()) + return false; + + const auto& allocatedSize = it->second; + if (size != allocatedSize) + return false; + + return true; +} + +//============================================================================== +bool FreeListAllocator::isEmpty() const noexcept +{ + std::lock_guard lock(mMutex); + return mMapPointerToSize.empty(); +} +#endif + //============================================================================== bool FreeListAllocator::allocateMemoryBlock(size_t sizeToAllocate) { diff --git a/dart/common/FreeListAllocator.hpp b/dart/common/FreeListAllocator.hpp index c92d1360196b1..133b8d9092877 100644 --- a/dart/common/FreeListAllocator.hpp +++ b/dart/common/FreeListAllocator.hpp @@ -77,6 +77,15 @@ class FreeListAllocator : public MemoryAllocator // Documentation inherited void deallocate(void* pointer, size_t size) override; +#ifndef NDEBUG + // Documentation inherited + [[nodiscard]] bool isAllocated(void* pointer, size_t size) const + noexcept override; + + // Documentation inherited + [[nodiscard]] bool isEmpty() const noexcept override; +#endif + // Documentation inherited void print(std::ostream& os = std::cout, int indent = 0) const override; diff --git a/dart/common/MemoryAllocator.hpp b/dart/common/MemoryAllocator.hpp index e6333234d5e42..23a78178318a9 100644 --- a/dart/common/MemoryAllocator.hpp +++ b/dart/common/MemoryAllocator.hpp @@ -93,6 +93,16 @@ class MemoryAllocator : public Castable template void destroy(T* object) noexcept; +#ifndef NDEBUG + /// Returns true if a pointer is allocated by this allocator. + [[nodiscard]] virtual bool isAllocated(void* pointer, size_t size) const + noexcept + = 0; + + /// Returns true if there is no memory allocated by this allocator. + [[nodiscard]] virtual bool isEmpty() const noexcept = 0; +#endif + /// Prints state of the memory allocator virtual void print(std::ostream& os = std::cout, int indent = 0) const; diff --git a/dart/common/PoolAllocator.cpp b/dart/common/PoolAllocator.cpp new file mode 100644 index 0000000000000..1ba21ed80a6ad --- /dev/null +++ b/dart/common/PoolAllocator.cpp @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/common/PoolAllocator.hpp" + +#include + +#include "dart/common/Console.hpp" +#include "dart/common/Logging.hpp" +#include "dart/common/Macros.hpp" + +namespace dart::common { + +//============================================================================== +PoolAllocator::PoolAllocator(MemoryAllocator& base_allocator) + : mBaseAllocator(base_allocator) +{ + static_assert( + 8 <= sizeof(MemoryUnit), + "sizeof(MemoryUnit) should be equal to or greater than 8."); + + // Global setting + if (!mInitialized) + { + // Fill mUnitSizes from 8 to 1024 with 8 interval + for (auto i = 0u; i < HEAP_COUNT; ++i) + { + mUnitSizes[i] = (i + 1) * 8; + } + + auto j = 0u; + mMapSizeToHeapIndex[0] = -1; + for (auto i = 1u; i <= MAX_UNIT_SIZE; ++i) + { + if (i <= mUnitSizes[j]) + { + mMapSizeToHeapIndex[i] = j; + } + else + { + mMapSizeToHeapIndex[i] = ++j; + } + } + + mInitialized = true; + } + + mCurrentMemoryBlockIndex = 0; + + mMemoryBlocksSize = 64; + mMemoryBlocks = mBaseAllocator.allocateAs(mMemoryBlocksSize); + const size_t allocatedSize = mMemoryBlocksSize * sizeof(MemoryBlock); + std::memset(mMemoryBlocks, 0, allocatedSize); + + mFreeMemoryUnits.fill(nullptr); +} + +//============================================================================== +PoolAllocator::~PoolAllocator() +{ + // Lock the mutex + std::lock_guard lock(mMutex); + +#ifndef NDEBUG + if (!mMapPointerToSize.empty()) + { + size_t totalSize = 0; + for (auto it : mMapPointerToSize) + { + void* pointer = it.first; + size_t size = it.second; + totalSize += size; + DART_FATAL("Found memory leak of {} bytes at {}!", size, pointer); + } + DART_FATAL("Found potential memory leak of total {} bytes!", totalSize); + } +#endif + + for (int i = 0; i < mCurrentMemoryBlockIndex; ++i) + { + mBaseAllocator.deallocate(mMemoryBlocks[i].mMemoryUnits, BLOCK_SIZE); + } + mBaseAllocator.deallocate( + mMemoryBlocks, mMemoryBlocksSize * sizeof(MemoryBlock)); +} + +//============================================================================== +const MemoryAllocator& PoolAllocator::getBaseAllocator() const +{ + return mBaseAllocator; +} + +//============================================================================== +MemoryAllocator& PoolAllocator::getBaseAllocator() +{ + return mBaseAllocator; +} + +//============================================================================== +int PoolAllocator::getNumAllocatedMemoryBlocks() const +{ + return mCurrentMemoryBlockIndex; +} + +//============================================================================== +void* PoolAllocator::allocate(size_t size) noexcept +{ + // Cannot allocate zero bytes + if (size == 0) + { + return nullptr; + } + + // Use the default allocator to allocate memory that is greater than + // MAX_UNIT_SIZE + if (size > MAX_UNIT_SIZE) + { + DART_TRACE( + "Cannot allocate memory of size > {} using PoolAllocator.", + MAX_UNIT_SIZE); + return mBaseAllocator.allocate(size); + } + + // Lock the mutex + std::lock_guard lock(mMutex); + + const int heapIndex = mMapSizeToHeapIndex[size]; + + if (MemoryUnit* unit = mFreeMemoryUnits[heapIndex]) + { + mFreeMemoryUnits[heapIndex] = unit->mNext; +#ifndef NDEBUG + if (unit) + { + mSize += size; + mPeak = std::max(mPeak, mSize); + mMapPointerToSize[unit] = size; + } + return unit; +#else + return unit; +#endif + } + + if (mCurrentMemoryBlockIndex == mMemoryBlocksSize) + { + MemoryBlock* currentMemoryBlocks = mMemoryBlocks; + mMemoryBlocksSize += 64; + mMemoryBlocks = mBaseAllocator.allocateAs(mMemoryBlocksSize); + std::memcpy( + mMemoryBlocks, + currentMemoryBlocks, + mCurrentMemoryBlockIndex * sizeof(MemoryBlock)); + std::memset( + mMemoryBlocks + mCurrentMemoryBlockIndex, 0, 64 * sizeof(MemoryBlock)); + } + + MemoryBlock* newBlock = mMemoryBlocks + mCurrentMemoryBlockIndex; + newBlock->mMemoryUnits + = static_cast(mBaseAllocator.allocate(BLOCK_SIZE)); + const size_t unitSize = mUnitSizes[heapIndex]; + DART_ASSERT(unitSize > 0); + const unsigned int unitCount = BLOCK_SIZE / unitSize; + DART_ASSERT(unitCount > 0); + void* memoryUnitsBegin = static_cast(newBlock->mMemoryUnits); + char* memoryUnitsBeginChar = static_cast(memoryUnitsBegin); + for (size_t i = 0u; i < unitCount - 1; ++i) + { + void* unitPointer = static_cast(memoryUnitsBeginChar + unitSize * i); + void* nextUnitPointer + = static_cast(memoryUnitsBeginChar + unitSize * (i + 1)); + MemoryUnit* unit = static_cast(unitPointer); + MemoryUnit* nextUnit = static_cast(nextUnitPointer); + unit->mNext = nextUnit; + } + + void* lastUnitPointer + = static_cast(memoryUnitsBeginChar + unitSize * (unitCount - 1)); + MemoryUnit* lastUnit = static_cast(lastUnitPointer); + lastUnit->mNext = nullptr; + + mFreeMemoryUnits[heapIndex] = newBlock->mMemoryUnits->mNext; + mCurrentMemoryBlockIndex++; + +#ifndef NDEBUG + if (newBlock->mMemoryUnits) + { + mSize += size; + mPeak = std::max(mPeak, mSize); + mMapPointerToSize[newBlock->mMemoryUnits] = size; + } + return newBlock->mMemoryUnits; +#else + return newBlock->mMemoryUnits; +#endif +} + +//============================================================================== +void PoolAllocator::deallocate(void* pointer, size_t size) +{ + if (size > MAX_UNIT_SIZE) + { + mBaseAllocator.deallocate(pointer, size); + return; + } + + // Lock the mutex + std::lock_guard lock(mMutex); + +#ifndef NDEBUG // debug + auto it = mMapPointerToSize.find(pointer); + if (it != mMapPointerToSize.end()) + { + auto allocatedSize = it->second; + if (size != allocatedSize) + { + DART_FATAL( + "Cannot deallocated memory {} because the deallocating size {} is " + "different from the allocated size {}.", + pointer, + size, + allocatedSize); + return; + } + mSize -= size; + mMapPointerToSize.erase(it); + DART_TRACE("Deallocated {} bytes.", size); + } + else + { + DART_FATAL( + "Cannot deallocate memory {} that is not allocated by this allocator!", + pointer); + return; + } +#endif + + const int heapIndex = mMapSizeToHeapIndex[size]; + + MemoryUnit* releasedUnit = static_cast(pointer); + releasedUnit->mNext = mFreeMemoryUnits[heapIndex]; + mFreeMemoryUnits[heapIndex] = releasedUnit; +} + +#ifndef NDEBUG +//============================================================================== +bool PoolAllocator::isAllocated(void* pointer, size_t size) const noexcept +{ + if (size > MAX_UNIT_SIZE) + { + return mBaseAllocator.isAllocated(pointer, size); + } + + std::lock_guard lock(mMutex); + + const auto it = mMapPointerToSize.find(pointer); + if (it == mMapPointerToSize.end()) + return false; + + const auto& allocatedSize = it->second; + if (size != allocatedSize) + return false; + + return true; +} + +//============================================================================== +bool PoolAllocator::isEmpty() const noexcept +{ + std::lock_guard lock(mMutex); + return mMapPointerToSize.empty(); +} +#endif + +//============================================================================== +void PoolAllocator::print(std::ostream& os, int indent) const +{ + // Lock the mutex + std::lock_guard lock(mMutex); + + if (indent == 0) + { + os << "[PoolAllocator]\n"; + } + const std::string spaces(indent, ' '); + if (indent != 0) + { + os << spaces << "type: " << getType() << "\n"; + } + os << spaces << "allocated_memory_block_count: " << mMemoryBlocksSize << "\n"; + os << spaces << "current_memory_blocks_count: " << mCurrentMemoryBlockIndex + << "\n"; + os << spaces << "base_allocator:\n"; + mBaseAllocator.print(os, indent + 2); +} + +} // namespace dart::common diff --git a/dart/common/PoolAllocator.hpp b/dart/common/PoolAllocator.hpp new file mode 100644 index 0000000000000..1d87b911b3680 --- /dev/null +++ b/dart/common/PoolAllocator.hpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_COMMON_POOLALLOCATOR_HPP_ +#define DART_COMMON_POOLALLOCATOR_HPP_ + +#include +#include +#ifndef NDEBUG + #include +#endif + +#include "dart/common/MemoryAllocator.hpp" + +namespace dart::common { + +/// Memory allocator optimized for allocating many objects of the same or +/// similar sizes +class PoolAllocator : public MemoryAllocator +{ +public: + /// Constructor + /// + /// \param[in] baseAllocator: (optional) Base memory allocator. + /// \param[in] initialAllocation: (optional) Bytes to initially allocate. + explicit PoolAllocator( + MemoryAllocator& baseAllocator = MemoryAllocator::GetDefault()); + + /// Destructor + ~PoolAllocator() override; + + DART_STRING_TYPE(PoolAllocator); + + /// Returns the base allocator + const MemoryAllocator& getBaseAllocator() const; + + /// Returns the base allocator + MemoryAllocator& getBaseAllocator(); + + /// Returns the count of allocated memory blocks + int getNumAllocatedMemoryBlocks() const; + + // Documentation inherited + [[nodiscard]] void* allocate(size_t size) noexcept override; + + // Documentation inherited + void deallocate(void* pointer, size_t size) override; + +#ifndef NDEBUG + // Documentation inherited + [[nodiscard]] bool isAllocated(void* pointer, size_t size) const + noexcept override; + + // Documentation inherited + [[nodiscard]] bool isEmpty() const noexcept override; +#endif + + // Documentation inherited + void print(std::ostream& os = std::cout, int indent = 0) const override; + +private: + struct MemoryUnit + { + /// Pointer to next memory block + MemoryUnit* mNext; + }; + + struct MemoryBlock + { + /// Pointer to the first memory unit + MemoryUnit* mMemoryUnits; + }; + + inline static constexpr int HEAP_COUNT = 128; + + inline static constexpr size_t MAX_UNIT_SIZE = 1024; + + inline static constexpr size_t BLOCK_SIZE = 16 * MAX_UNIT_SIZE; + + inline static std::array mUnitSizes; + + inline static std::array mMapSizeToHeapIndex; + + inline static bool mInitialized = false; + + /// The base allocator to allocate memory chunk + MemoryAllocator& mBaseAllocator; + + /// Mutex for for mNumAllocatedMemoryBlocks, mNumMemoryBlocks, + /// mFreeMemoryUnits, and mAllocatedMemoryBlocks. + mutable std::mutex mMutex; + + /// The array of memory blocks. + /// + /// This array is a placeholder of allocated memory blocks. Initially this + /// contains nullptr as the elements. + MemoryBlock* mMemoryBlocks; + + /// The size of mMemoryBlocks. + /// + /// This is simply the current size of mMemoryBlocks. The value doesn't mean + /// the actual count of the allocated memory blocks. + int mMemoryBlocksSize; + + /// The count of the allocated memory blocks in use. + int mCurrentMemoryBlockIndex; + + /// List of free memory units. + /// + /// The size of mFreeMemoryUnits is fixed to HEAP_COUNT where each element is + /// for a specific memory size. For example, the first element has the free + /// memory unit for 8 bytes while the last element has the free memory unit + /// for 1024 bytes. + /// + /// The size must be the same of mUnitSizes. + std::array mFreeMemoryUnits; + +#ifndef NDEBUG + size_t mSize = 0; + size_t mPeak = 0; + std::unordered_map mMapPointerToSize; +#endif +}; + +} // namespace dart::common + +#endif // DART_COMMON_POOLALLOCATOR_HPP_ diff --git a/unittests/unit/common/test_PoolAllocator.cpp b/unittests/unit/common/test_PoolAllocator.cpp new file mode 100644 index 0000000000000..7cd77ea92c608 --- /dev/null +++ b/unittests/unit/common/test_PoolAllocator.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2011-2022, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "TestHelpers.hpp" + +using namespace dart; +using namespace common; + +//============================================================================== +TEST(PoolAllocatorTest, Constructors) +{ + auto a = PoolAllocator(); + EXPECT_EQ(&a.getBaseAllocator(), &MemoryAllocator::GetDefault()); + + auto b = PoolAllocator(MemoryAllocator::GetDefault()); + EXPECT_EQ(&b.getBaseAllocator(), &MemoryAllocator::GetDefault()); + + EXPECT_EQ(b.getNumAllocatedMemoryBlocks(), 0); + +#ifndef NDEBUG + EXPECT_TRUE(a.isEmpty()); + EXPECT_TRUE(b.isEmpty()); +#endif +} + +//============================================================================== +TEST(PoolAllocatorTest, Allocate) +{ + auto a = PoolAllocator(); + EXPECT_EQ(a.allocate(0), nullptr); + +#ifndef NDEBUG + EXPECT_TRUE(a.isEmpty()); +#endif + + // Allocate small memory + auto ptr1 = a.allocate(1); + EXPECT_NE(ptr1, nullptr); +#ifndef NDEBUG + EXPECT_TRUE(a.isAllocated(ptr1, 1)); + EXPECT_FALSE(a.isAllocated(0, 1)); // incorrect address + EXPECT_FALSE(a.isAllocated(ptr1, 1 * 2)); // incorrect size +#endif + EXPECT_EQ(a.getNumAllocatedMemoryBlocks(), 1); + + // Allocate the same size, which doesn't increase the number of memory block + auto ptr2 = a.allocate(1); + EXPECT_NE(ptr2, nullptr); + EXPECT_EQ(a.getNumAllocatedMemoryBlocks(), 1); + + // Allocate different size + auto ptr3 = a.allocate(64); + EXPECT_NE(ptr3, nullptr); + EXPECT_EQ(a.getNumAllocatedMemoryBlocks(), 2); + + // Allocate memory of the max size (= 1024) + auto ptr4 = a.allocate(1024); + EXPECT_NE(ptr4, nullptr); +#ifndef NDEBUG + EXPECT_TRUE(a.isAllocated(ptr4, 1024)); + EXPECT_FALSE(a.isAllocated(0, 1024)); + EXPECT_FALSE(a.isAllocated(ptr4, 1024 * 2)); +#endif + EXPECT_EQ(a.getNumAllocatedMemoryBlocks(), 3); + + // Allocate oversized memory (> 1024) + auto ptr5 = a.allocate(2048); + EXPECT_NE(ptr5, nullptr); +#ifndef NDEBUG + EXPECT_TRUE(a.isAllocated(ptr5, 2048)); + EXPECT_FALSE(a.isAllocated(0, 2048)); + EXPECT_FALSE(a.isAllocated(ptr5, 2048 * 2)); +#endif + EXPECT_EQ(a.getNumAllocatedMemoryBlocks(), 3); + +#ifndef NDEBUG + EXPECT_FALSE(a.isEmpty()); +#endif + + a.deallocate(ptr1, 1); + a.deallocate(ptr2, 1); + a.deallocate(ptr3, 64); + a.deallocate(ptr4, 1024); + a.deallocate(ptr5, 2048); + +#ifndef NDEBUG + EXPECT_TRUE(a.isEmpty()); +#endif +}