diff --git a/CMakeLists.txt b/CMakeLists.txt index e4c7892..2564b94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,6 +150,7 @@ IF( CPPCORE_BUILD_UNITTESTS ) SET( cppcore_memory_test_src test/memory/TStackAllocatorTest.cpp test/memory/TPoolAllocatorTest.cpp + test/memory/TScratchAllocatorTest.cpp ) SET( cppcore_random_test_src @@ -163,7 +164,7 @@ IF( CPPCORE_BUILD_UNITTESTS ) SOURCE_GROUP( code\\container FILES ${cppcore_container_test_src} ) SOURCE_GROUP( code\\memory FILES ${cppcore_memory_test_src} ) SOURCE_GROUP( code\\random FILES ${cppcore_random_test_src} ) - + # Prevent overriding the parent project's compiler/linker # settings on Windows SET(gtest_force_shared_crt ON CACHE BOOL "" FORCE) diff --git a/doc/Common.md b/doc/Common.md index 6b99003..4e946ae 100644 --- a/doc/Common.md +++ b/doc/Common.md @@ -38,7 +38,7 @@ You need to manage singe bits and you want to avoid dealing with the OR-, AND- a void main() { TBitField bitfield(0); - + // The first bit is set bitfield.setBit(1); if (bitfield.getBit(1)) std::cout "First bit is set" << std:endl; @@ -53,12 +53,12 @@ void main() { ### Usecases ### Examples -## TSharedPtr +## TSharedPtr ### Introduction ### Usecases ### Examples -## Variant +## Variant ### Introduction ### Usecases ### Examples diff --git a/doc/Memory.md b/doc/Memory.md index 9b772cd..930c189 100644 --- a/doc/Memory.md +++ b/doc/Memory.md @@ -8,5 +8,37 @@ allocation scheme in all containers. ## CPPCore::TPoolAllocator This allocator can be use to create an initial pool of object at the program startup. +## CPPCore::TScratchAllocator +### Introduction +The scratch allocator preallocates a memory block which can be used in your program. You do not have to deallocate any of the allocations. +This will be done when clearing the allocator. All allocations will be invalidated. + +### Usecases +Common use cases include: +- Temporary allocations in algorithms (e.g., path finding, sorting) +- Frame-based memory management in games +- Scratch space for parsing and serialization +- Short-lived computational tasks with multiple dynamic allocations + +### Examples +```cpp +#include + +using namespace ::cppcore; + +int main() { + // Will work + ScratchAllocator myAllocator(1024); + char *ptr1 = myAllocator.alloc(512); + assert(ptr1 != nullptr); + + // Overrange shall get catched + char *ptr2 = myAllocator.alloc(600); + assert(ptr2 == nullptr); + + return 0; +} +``` + ## CPPCore::TStackAllocator The stack allocator preallocates a memory block which can be used in your program. When deallocating your memory you have to follow the first-in last-out rule. diff --git a/include/cppcore/IO/FileSystem.h b/include/cppcore/IO/FileSystem.h index 0072fef..79f11fd 100644 --- a/include/cppcore/IO/FileSystem.h +++ b/include/cppcore/IO/FileSystem.h @@ -49,13 +49,13 @@ class FileSystem { /// @brief The class constructor with the location. /// @param[in] location The root location. FileSystem(const char *location); - + /// @brief The class destructor. ~FileSystem() = default; - + /// @brief Will perform a refresh. void refresh(); - + /// @brief Will return the free disk info. /// @return the File-system space. FSSpace *getFreeDiskSpace(); diff --git a/include/cppcore/Memory/TDefaultAllocator.h b/include/cppcore/Memory/TDefaultAllocator.h index 4af1b93..64bbe13 100644 --- a/include/cppcore/Memory/TDefaultAllocator.h +++ b/include/cppcore/Memory/TDefaultAllocator.h @@ -129,4 +129,4 @@ inline void TDefaultAllocator::dumpAllocations(std::string &allocs) { // empty } -} // Namespace cppcore +} // namespace cppcore diff --git a/include/cppcore/Memory/TPoolAllocator.h b/include/cppcore/Memory/TPoolAllocator.h index d1a5c6e..427bf32 100644 --- a/include/cppcore/Memory/TPoolAllocator.h +++ b/include/cppcore/Memory/TPoolAllocator.h @@ -94,7 +94,7 @@ class TPoolAllocator { /// @brief Will reset the allocator. void reset(); - + /// No copying allowed CPPCORE_NONE_COPYING(TPoolAllocator) @@ -283,4 +283,4 @@ void TPoolAllocator::reset() { m_current = m_first; } -} // Namespace cppcore +} // namespace cppcore diff --git a/include/cppcore/Memory/TScratchAllocator.h b/include/cppcore/Memory/TScratchAllocator.h new file mode 100644 index 0000000..ed5f48e --- /dev/null +++ b/include/cppcore/Memory/TScratchAllocator.h @@ -0,0 +1,161 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014-2024 Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#pragma once + +#include +#include +#include + +namespace cppcore { + +//------------------------------------------------------------------------------------------------- +/// @class TScratchAllocator +/// @ingroup CPPCore +/// +/// @brief A simple scratch allocator. +/// +/// You can use it to manage all the dynamic allocations without caring about cleaning it up. All +/// Allocations will be a one-shot alloc, which do not need to get released. When calling clear +/// all allocation will be invalidated and cannot be used anymore. +//------------------------------------------------------------------------------------------------- +template +class TScratchAllocator { +public: + /// @brief The default class constructor. + TScratchAllocator(); + + /// @brief The class constructor with the pool size. + /// @param[in] numItems The buffer size. + explicit TScratchAllocator(size_t numItems); + + /// @brief The class destructor. + ~TScratchAllocator(); + + /// @brief Will allocate the number of items. + /// @param[in] numItems The number of items. + /// @return Pointr showing to the scratch buffer. + T *alloc(size_t numItems); + + /// @brief Will reserve the pool. + /// @apram[in] size The pool size to reserve. + void reserve(size_t size); + + /// @brief Will clear the pool, memory will be deallocated. + /// @note All instances which are currently is use will get invalid. PLease use with care. + void clear(); + + /// @brief Returns the capacity of items in the pool allocator. + /// @return The capacity. + size_t capacity() const; + + /// @brief Will return the reserved memory in bytes. + /// @return The reserved memory in bytes. + size_t reservedMem() const; + + /// @brief Will return the free memory in the pool in bytes. + /// @return The free memory in bytes. + size_t freeMem() const; + + /// No copying allowed + CPPCORE_NONE_COPYING(TScratchAllocator) + +private: + T *mBlock = nullptr; + size_t mSize; + size_t mIndex; +}; + +template +inline TScratchAllocator::TScratchAllocator() : + mSize(0u), mIndex(0u) { + // empty +} + +template +inline TScratchAllocator::TScratchAllocator(size_t numItems) : + mSize(numItems), mIndex(0u) { + reserve(numItems); +} + +template +inline TScratchAllocator::~TScratchAllocator() { + clear(); +} + +template +inline T *TScratchAllocator::alloc(size_t numItems) { + if (numItems == 0) { + return nullptr; + } + + // Check for overflow + if (numItems > std::numeric_limits::max() / sizeof(T)) { + return nullptr; + } + + if ((mIndex + numItems) > mSize) { + return nullptr; + } + + // Ensure alignment + size_t alignment = alignof(T); + mIndex = (mIndex + alignment - 1) & ~(alignment - 1); + + T *ptr = &mBlock[mIndex]; + mIndex += numItems; + return ptr; +} + +template +void TScratchAllocator::reserve(size_t size) { + clear(); + mBlock = new T[size]; + mSize = size; + mIndex = 0u; +} + +template +inline void TScratchAllocator::clear() { + delete [] mBlock; + mBlock = nullptr; + mSize = 0u; +} + +template +inline size_t TScratchAllocator::capacity() const { + return mSize; +} + +template +inline size_t TScratchAllocator::reservedMem() const { + return mIndex; +} + +template +inline size_t TScratchAllocator::freeMem() const { + return (mSize - mIndex); +} + +using ScratchAllocator = TScratchAllocator; + +} // namespace cppcore diff --git a/include/cppcore/Memory/TStackAllocator.h b/include/cppcore/Memory/TStackAllocator.h index 219b353..b556727 100644 --- a/include/cppcore/Memory/TStackAllocator.h +++ b/include/cppcore/Memory/TStackAllocator.h @@ -52,7 +52,7 @@ class TStackAllocator { /// @brief The class destructor. ~TStackAllocator(); - /// Will alloc the number of items from the stack. + /// Will allocate the number of items from the stack. /// @param size [in] The requested size of items. T *alloc(size_t size); @@ -184,11 +184,13 @@ inline void TStackAllocator::clear() { m_data = nullptr; m_capacity = 0; m_top = 0; + m_numAllocs = 0; } - + template inline void TStackAllocator::reset() { m_top = 0; + m_numAllocs = 0; } template diff --git a/test/Random/RandomGeneratorTest.cpp b/test/Random/RandomGeneratorTest.cpp index 6d32ade..a06656c 100644 --- a/test/Random/RandomGeneratorTest.cpp +++ b/test/Random/RandomGeneratorTest.cpp @@ -25,9 +25,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace cppcore; -class RandomGeneratorTest : public testing::Test { - // empty -}; +class RandomGeneratorTest : public testing::Test {}; TEST_F( RandomGeneratorTest, getTest ) { RandomGenerator generator; diff --git a/test/memory/TScratchAllocatorTest.cpp b/test/memory/TScratchAllocatorTest.cpp new file mode 100644 index 0000000..fe0f869 --- /dev/null +++ b/test/memory/TScratchAllocatorTest.cpp @@ -0,0 +1,66 @@ +/*----------------------------------------------------------------------------------------------- +The MIT License (MIT) + +Copyright (c) 2014-2024 Kim Kulling + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-----------------------------------------------------------------------------------------------*/ +#include + +#include + +using namespace cppcore; + +class TScratchAllocatorTest : public testing::Test { +public: + static constexpr size_t BufferSize = 1024u; +}; + +TEST_F(TScratchAllocatorTest, CreateTest) { + bool ok( true ); + try { + ScratchAllocator myAllocator(BufferSize); + } catch ( ... ) { + ok = false; + } + EXPECT_TRUE( ok ); +} + +TEST_F(TScratchAllocatorTest, AllocTest) { + ScratchAllocator myAllocator(BufferSize); + char *ptr1 = myAllocator.alloc(512); + EXPECT_NE(ptr1, nullptr); + EXPECT_EQ(myAllocator.capacity(), BufferSize); + EXPECT_EQ(myAllocator.freeMem(), 512); + EXPECT_EQ(myAllocator.reservedMem(), 512); + + char *ptr2 = myAllocator.alloc(600); + EXPECT_EQ(ptr2, nullptr); + + myAllocator.clear(); +} + +TEST_F(TScratchAllocatorTest, ClearTest) { + ScratchAllocator myAllocator(BufferSize); + EXPECT_EQ(myAllocator.capacity(), BufferSize); + EXPECT_EQ(myAllocator.freeMem(), BufferSize); + + myAllocator.clear(); + EXPECT_EQ(myAllocator.capacity(), 0u); + EXPECT_EQ(myAllocator.freeMem(), 0u); +} diff --git a/test/memory/TStackAllocatorTest.cpp b/test/memory/TStackAllocatorTest.cpp index 87dd4ba..dad0c99 100644 --- a/test/memory/TStackAllocatorTest.cpp +++ b/test/memory/TStackAllocatorTest.cpp @@ -26,9 +26,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace cppcore; -class TStackAllocatorTest : public testing::Test { -protected: -}; +class TStackAllocatorTest : public testing::Test {}; TEST_F( TStackAllocatorTest, CreateTest ) { bool ok( true );