├── .gitignore ├── LICENSE ├── README.md ├── external └── catch-1.1.1 │ └── catch.hpp ├── include └── gin │ ├── allocator.h │ ├── allocator_frame.h │ ├── enum_flags_utils.h │ ├── kernel │ └── osx │ │ └── virtual_memory.h │ ├── linear_allocator.h │ ├── platforms.h │ ├── stack_frame_allocator.h │ ├── utils.h │ ├── virtual_memory.h │ ├── vmem_linear_allocator.h │ └── vmem_stack_frame_allocator.h ├── makefile ├── script ├── clean.py └── test.py └── test ├── main.cpp ├── test_linear_allocator.cpp ├── test_stack_frame_allocator.cpp ├── test_vmem_linear_allocator.cpp └── test_vmem_stack_frame_allocator.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | intermediate 3 | *.swp 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Nicholas Frechette 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gin 2 | A high performance C++ playground library 3 | -------------------------------------------------------------------------------- /include/gin/allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_ALLOCATOR_H 2 | #define GIN_ALLOCATOR_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include 29 | #include 30 | 31 | namespace gin 32 | { 33 | //////////////////////////////////////// 34 | // Base class for all memory allocators. 35 | // 36 | // It exposes an interface for common allocator functions 37 | // but it differs in that we store a function pointer to 38 | // a reallocate static function. This is to avoid a cache miss 39 | // on the vtable when using the most common functionality: 40 | // allocation and deallocation (both of which can be achieved 41 | // with this function). 42 | // 43 | // See here for more details: 44 | // http://nfrechette.github.io/2015/05/11/memory_allocator_interface/ 45 | //////////////////////////////////////// 46 | 47 | class Allocator 48 | { 49 | protected: 50 | typedef void* (*ReallocateFun)(Allocator*, void*, size_t, size_t, size_t); 51 | 52 | inline Allocator(ReallocateFun reallocateFun); 53 | 54 | public: 55 | virtual ~Allocator() {} 56 | 57 | // Not all allocators support per pointer freeing, maybe 58 | // these should be renamed? 59 | virtual void* Allocate(size_t size, size_t alignment) = 0; 60 | virtual void Deallocate(void* ptr, size_t size) = 0; 61 | 62 | inline void* Reallocate(void* oldPtr, size_t oldSize, size_t newSize, size_t alignment); 63 | 64 | virtual bool IsOwnerOf(void* ptr) const = 0; 65 | 66 | // TODO: Release() and IsInitialized() are good candidates to add here 67 | 68 | protected: 69 | ReallocateFun m_reallocateFun; 70 | }; 71 | 72 | //////////////////////////////////////// 73 | 74 | Allocator::Allocator(ReallocateFun reallocateFun) 75 | : m_reallocateFun(reallocateFun) 76 | { 77 | assert(reallocateFun); 78 | } 79 | 80 | void* Allocator::Reallocate(void* oldPtr, size_t oldSize, size_t newSize, size_t alignment) 81 | { 82 | assert(m_reallocateFun); 83 | return (*m_reallocateFun)(this, oldPtr, oldSize, newSize, alignment); 84 | } 85 | } 86 | 87 | #endif // GIN_ALLOCATOR_H 88 | 89 | -------------------------------------------------------------------------------- /include/gin/allocator_frame.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_ALLOCATOR_FRAME_H 2 | #define GIN_ALLOCATOR_FRAME_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include 29 | #include 30 | 31 | namespace gin 32 | { 33 | class Allocator; 34 | class AllocatorFrame; 35 | 36 | namespace internal 37 | { 38 | //////////////////////////////////////// 39 | // AllocatorFrameFactory is an adapter between our generic AllocatorFrame 40 | // and our custom allocators. It allows more natural AllocatorFrame construction: 41 | // 42 | // AllocatorFrame frame(someAllocator); 43 | // 44 | // Allocators are required to automatically coerce into the AllocatorFrameFactory 45 | // type for support. 46 | // Under normal circomstances, everything should be inlined and clean. 47 | //////////////////////////////////////// 48 | class AllocatorFrameFactory 49 | { 50 | public: 51 | typedef void (*PushFrameFun)(Allocator*, AllocatorFrame&); 52 | 53 | inline AllocatorFrameFactory(Allocator* allocator, PushFrameFun pushFun); 54 | 55 | private: 56 | inline void PushFrame(AllocatorFrame& outFrame) const; 57 | 58 | Allocator* m_allocator; 59 | PushFrameFun m_pushFun; 60 | 61 | friend AllocatorFrame; 62 | }; 63 | } 64 | 65 | //////////////////////////////////////// 66 | // AllocatorFrame represents a frame in allocators that support them. 67 | // This is meant to be a generic class, any allocator specific frame 68 | // data should be stored in the allocator itself referenced by m_allocatorData. 69 | // 70 | // See here for more details: 71 | // http://nfrechette.github.io/2016/05/09/greedy_stack_frame_allocator/ 72 | //////////////////////////////////////// 73 | 74 | class AllocatorFrame 75 | { 76 | public: 77 | typedef bool (*PopFrameFun)(Allocator*, void*); 78 | 79 | inline AllocatorFrame(); 80 | inline AllocatorFrame(Allocator* allocator, PopFrameFun popFun, void* allocatorData); 81 | inline AllocatorFrame(const internal::AllocatorFrameFactory&& factory); 82 | inline AllocatorFrame(AllocatorFrame&& frame); 83 | inline ~AllocatorFrame(); 84 | 85 | inline AllocatorFrame* operator=(AllocatorFrame&& frame); 86 | 87 | inline bool Pop(); 88 | 89 | inline bool CanPop() const; 90 | 91 | private: 92 | AllocatorFrame(const AllocatorFrame&) = delete; 93 | AllocatorFrame* operator=(const AllocatorFrame&) = delete; 94 | 95 | Allocator* m_allocator; 96 | PopFrameFun m_popFun; 97 | void* m_allocatorData; 98 | }; 99 | 100 | //////////////////////////////////////// 101 | 102 | namespace internal 103 | { 104 | AllocatorFrameFactory::AllocatorFrameFactory(Allocator* allocator, PushFrameFun pushFun) 105 | : m_allocator(allocator) 106 | , m_pushFun(pushFun) 107 | { 108 | } 109 | 110 | void AllocatorFrameFactory::PushFrame(AllocatorFrame& outFrame) const 111 | { 112 | (*m_pushFun)(m_allocator, outFrame); 113 | } 114 | } 115 | 116 | //////////////////////////////////////// 117 | 118 | AllocatorFrame::AllocatorFrame() 119 | : m_allocator(nullptr) 120 | , m_popFun(nullptr) 121 | , m_allocatorData(nullptr) 122 | { 123 | } 124 | 125 | AllocatorFrame::AllocatorFrame(Allocator* allocator, PopFrameFun popFun, void* allocatorData) 126 | : m_allocator(allocator) 127 | , m_popFun(popFun) 128 | , m_allocatorData(allocatorData) 129 | { 130 | } 131 | 132 | AllocatorFrame::AllocatorFrame(const internal::AllocatorFrameFactory&& factory) 133 | : AllocatorFrame() 134 | { 135 | factory.PushFrame(*this); 136 | } 137 | 138 | AllocatorFrame::AllocatorFrame(AllocatorFrame&& frame) 139 | : AllocatorFrame() 140 | { 141 | *this = std::move(frame); 142 | } 143 | 144 | AllocatorFrame::~AllocatorFrame() 145 | { 146 | // Always attempt to pop at destruction 147 | Pop(); 148 | } 149 | 150 | AllocatorFrame* AllocatorFrame::operator=(AllocatorFrame&& frame) 151 | { 152 | std::swap(m_allocator, frame.m_allocator); 153 | std::swap(m_popFun, frame.m_popFun); 154 | std::swap(m_allocatorData, frame.m_allocatorData); 155 | 156 | return this; 157 | } 158 | 159 | bool AllocatorFrame::Pop() 160 | { 161 | if (!CanPop()) 162 | { 163 | // Nothing to do, already popped or not initialized 164 | return false; 165 | } 166 | 167 | bool result = (*m_popFun)(m_allocator, m_allocatorData); 168 | 169 | // Reset our data pointer to prevent us from popping again 170 | // Only 'm_allocatorData' is used to tell if we are initialized. 171 | // Everything else is set when we initialize. 172 | // If we are not initialized, we cannot be popped. 173 | m_allocatorData = nullptr; 174 | 175 | return result; 176 | } 177 | 178 | bool AllocatorFrame::CanPop() const 179 | { 180 | // Only true if we are initialized and we haven't popped already 181 | return m_allocatorData != nullptr; 182 | } 183 | } 184 | 185 | #endif // GIN_ALLOCATOR_FRAME_H 186 | 187 | -------------------------------------------------------------------------------- /include/gin/enum_flags_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_ENUM_FLAGS_UTILS_H 2 | #define GIN_ENUM_FLAGS_UTILS_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include 29 | 30 | // Cannot make these 'constexpr' due to variable declarations. 31 | // Leaving it simply as 'inline' for now due to increased lisibility. 32 | 33 | #define IMPL_ENUM_FLAGS_OPERATORS(enumType) \ 34 | inline enumType operator|(enumType lhs, enumType rhs) \ 35 | { \ 36 | typedef std::underlying_type::type IntegralType; \ 37 | typedef std::make_unsigned::type RawType; \ 38 | RawType rawLHS = static_cast(lhs); \ 39 | RawType rawRHS = static_cast(rhs); \ 40 | RawType result = rawLHS | rawRHS; \ 41 | return static_cast(result); \ 42 | } \ 43 | inline enumType operator&(enumType lhs, enumType rhs) \ 44 | { \ 45 | typedef std::underlying_type::type IntegralType; \ 46 | typedef std::make_unsigned::type RawType; \ 47 | RawType rawLHS = static_cast(lhs); \ 48 | RawType rawRHS = static_cast(rhs); \ 49 | RawType result = rawLHS & rawRHS; \ 50 | return static_cast(result); \ 51 | } \ 52 | inline enumType operator^(enumType lhs, enumType rhs) \ 53 | { \ 54 | typedef std::underlying_type::type IntegralType; \ 55 | typedef std::make_unsigned::type RawType; \ 56 | RawType rawLHS = static_cast(lhs); \ 57 | RawType rawRHS = static_cast(rhs); \ 58 | RawType result = rawLHS ^ rawRHS; \ 59 | return static_cast(result); \ 60 | } \ 61 | inline enumType operator~(enumType rhs) \ 62 | { \ 63 | typedef std::underlying_type::type IntegralType; \ 64 | typedef std::make_unsigned::type RawType; \ 65 | RawType rawRHS = static_cast(rhs); \ 66 | RawType result = ~rawRHS; \ 67 | return static_cast(result); \ 68 | } \ 69 | 70 | #endif // GIN_ENUM_FLAGS_UTILS_H 71 | 72 | -------------------------------------------------------------------------------- /include/gin/kernel/osx/virtual_memory.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_KERNEL_OSX_VIRTUAL_MEMORY_H 2 | #define GIN_KERNEL_OSX_VIRTUAL_MEMORY_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include "../../platforms.h" 29 | #include "../../enum_flags_utils.h" 30 | 31 | #if GIN_OSX 32 | 33 | #include 34 | #include 35 | 36 | #define GIN_DEFAULT_VMEM_PROT (PROT_READ | PROT_WRITE) 37 | #define GIN_DEFAULT_VMEM_FLAGS (MAP_PRIVATE | MAP_ANON) 38 | 39 | // See comments below about commit/decommit. 40 | // Enabling this will incur a performance hit but will prevent accidental 41 | // paging of decommitted memory regions. 42 | 43 | #define GIN_VMEM_SAFE 1 44 | 45 | namespace gin 46 | { 47 | enum class MemoryAccessFlags 48 | { 49 | eCPU_None = PROT_NONE, 50 | eCPU_Read = PROT_READ, 51 | eCPU_Write = PROT_WRITE, 52 | eCPU_Exec = PROT_EXEC, 53 | 54 | eCPU_ReadWrite = PROT_READ | PROT_WRITE, 55 | eCPU_All = PROT_READ | PROT_WRITE | PROT_EXEC, 56 | }; 57 | 58 | IMPL_ENUM_FLAGS_OPERATORS(MemoryAccessFlags); 59 | 60 | enum class MemoryRegionFlags 61 | { 62 | ePrivate = MAP_PRIVATE, // Changes not visible to other processes 63 | eShared = MAP_SHARED, // Changes visible to other processes 64 | eAnonymous = MAP_ANON, // Not backed by a file descriptor 65 | }; 66 | 67 | IMPL_ENUM_FLAGS_OPERATORS(MemoryRegionFlags); 68 | 69 | inline void* VirtualReserve(size_t size, size_t alignment, 70 | MemoryAccessFlags accessFlags, 71 | MemoryRegionFlags regionFlags) 72 | { 73 | if (alignment != 1) 74 | { 75 | // Alignment is not supported 76 | return nullptr; 77 | } 78 | 79 | #if GIN_VMEM_SAFE 80 | int prot = PROT_NONE; 81 | #else 82 | int prot = static_cast(accessFlags); 83 | #endif 84 | 85 | int flags = static_cast(regionFlags); 86 | 87 | void* ptr = mmap(nullptr, size, prot, flags, -1, 0); 88 | return ptr; 89 | } 90 | 91 | inline void* VirtualReserve(size_t size, 92 | MemoryAccessFlags accessFlags, 93 | MemoryRegionFlags regionFlags) 94 | { 95 | return VirtualReserve(size, 1, accessFlags, regionFlags); 96 | } 97 | 98 | inline bool VirtualRelease(void* ptr, size_t size) 99 | { 100 | int result = munmap(ptr, size); 101 | return result == 0; 102 | } 103 | 104 | // This is complicated on OS X... 105 | // See: https://bugzilla.mozilla.org/show_bug.cgi?id=670596 106 | // Here we use the fact that OS X has on demand paging. 107 | // When safety is enabled, memory regions decommitted are always 108 | // marked with PROT_NONE to prevent access and accidental paging. 109 | // Decommitting is achieve with madvise but the memory usage reported 110 | // might not be accurate since the decommitted pages are only taken 111 | // away if there is memory pressure in the system. 112 | 113 | inline bool VirtualCommit(void* ptr, size_t size, 114 | MemoryAccessFlags accessFlags, 115 | MemoryRegionFlags regionFlags) 116 | { 117 | #if GIN_VMEM_SAFE 118 | int prot = static_cast(accessFlags); 119 | int result = mprotect(ptr, size, prot); 120 | return result == 0; 121 | #else 122 | return true; 123 | #endif 124 | } 125 | 126 | inline bool VirtualDecommit(void* ptr, size_t size) 127 | { 128 | int result = madvise(ptr, size, MADV_FREE); 129 | 130 | #if GIN_VMEM_SAFE 131 | if (result != 0) return false; 132 | result = mprotect(ptr, size, PROT_NONE); 133 | #endif 134 | 135 | return result == 0; 136 | } 137 | 138 | inline void* VirtualAlloc(size_t size, size_t alignment, 139 | MemoryAccessFlags accessFlags, 140 | MemoryRegionFlags regionFlags) 141 | { 142 | if (alignment != 1) 143 | { 144 | // Alignment is not supported 145 | return nullptr; 146 | } 147 | 148 | int prot = static_cast(accessFlags); 149 | int flags = static_cast(regionFlags); 150 | 151 | void* result = mmap(nullptr, size, prot, flags, -1, 0); 152 | return result; 153 | } 154 | 155 | inline void* VirtualAlloc(size_t size, 156 | MemoryAccessFlags accessFlags, 157 | MemoryRegionFlags regionFlags) 158 | { 159 | return VirtualAlloc(size, 1, accessFlags, regionFlags); 160 | } 161 | 162 | inline bool VirtualFree(void* ptr, size_t size) 163 | { 164 | int result = munmap(ptr, size); 165 | return result == 0; 166 | } 167 | } 168 | 169 | #endif // GIN_OSX 170 | 171 | #endif // GIN_KERNEL_OSX_VIRTUAL_MEMORY_H 172 | 173 | -------------------------------------------------------------------------------- /include/gin/linear_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_LINEAR_ALLOCATOR_H 2 | #define GIN_LINEAR_ALLOCATOR_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include "allocator.h" 29 | #include "utils.h" 30 | 31 | #include 32 | #include 33 | #include 34 | 35 | namespace gin 36 | { 37 | //////////////////////////////////////// 38 | // A simple linear allocator. 39 | // A pre-allocated buffer is provided and the allocator 40 | // will carve out allocations out of it. 41 | // 42 | // There is no per allocation overhead. 43 | // The buffer memory is not modified by the allocator. 44 | // The allocator is not thread-safe. 45 | // 46 | // See here for more details: 47 | // http://nfrechette.github.io/2015/05/21/linear_allocator/ 48 | //////////////////////////////////////// 49 | 50 | template 51 | class TLinearAllocator : public Allocator 52 | { 53 | public: 54 | inline TLinearAllocator(); 55 | inline TLinearAllocator(void* buffer, size_t bufferSize); 56 | inline ~TLinearAllocator(); 57 | 58 | virtual void* Allocate(size_t size, size_t alignment) override; 59 | virtual void Deallocate(void* ptr, size_t size) override; 60 | 61 | virtual bool IsOwnerOf(void* ptr) const override; 62 | 63 | void Initialize(void* buffer, size_t bufferSize); 64 | void Reset(); 65 | void Release(); 66 | 67 | inline bool IsInitialized() const; 68 | inline size_t GetAllocatedSize() const; 69 | 70 | private: 71 | TLinearAllocator(const TLinearAllocator&) = delete; 72 | TLinearAllocator(TLinearAllocator&&) = delete; 73 | TLinearAllocator& operator=(TLinearAllocator) = delete; 74 | 75 | void* AllocateImpl(size_t size, size_t alignment); 76 | static void* ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment); 77 | 78 | uintptr_t m_buffer; 79 | SizeType m_bufferSize; 80 | SizeType m_allocatedSize; 81 | SizeType m_lastAllocationOffset; // For realloc support only 82 | }; 83 | 84 | //////////////////////////////////////// 85 | 86 | template 87 | TLinearAllocator::TLinearAllocator() 88 | : Allocator(&TLinearAllocator::ReallocateImpl) 89 | , m_buffer(0) 90 | , m_bufferSize(0) 91 | , m_allocatedSize(0) 92 | , m_lastAllocationOffset(0) 93 | { 94 | } 95 | 96 | template 97 | TLinearAllocator::TLinearAllocator(void* buffer, size_t bufferSize) 98 | : Allocator(&TLinearAllocator::ReallocateImpl) 99 | , m_buffer(0) 100 | , m_bufferSize(0) 101 | , m_allocatedSize(0) 102 | , m_lastAllocationOffset(0) 103 | { 104 | Initialize(buffer, bufferSize); 105 | } 106 | 107 | template 108 | TLinearAllocator::~TLinearAllocator() 109 | { 110 | Release(); 111 | } 112 | 113 | template 114 | void TLinearAllocator::Initialize(void* buffer, size_t bufferSize) 115 | { 116 | //assert(!IsInitialized()); 117 | //assert(buffer != nullptr); 118 | //assert(bufferSize != 0); 119 | //assert(bufferSize <= static_cast(std::numeric_limits::max())); 120 | 121 | if (IsInitialized()) 122 | { 123 | // Invalid allocator state 124 | return; 125 | } 126 | 127 | if (buffer == nullptr 128 | || bufferSize == 0 129 | || bufferSize > static_cast(std::numeric_limits::max())) 130 | { 131 | // Invalid arguments 132 | return; 133 | } 134 | 135 | m_buffer = reinterpret_cast(buffer); 136 | m_bufferSize = static_cast(bufferSize); 137 | m_allocatedSize = 0; 138 | m_lastAllocationOffset = static_cast(bufferSize); 139 | } 140 | 141 | template 142 | void TLinearAllocator::Reset() 143 | { 144 | //assert(IsInitialized()); 145 | 146 | if (!IsInitialized()) 147 | { 148 | // Invalid allocator state 149 | return; 150 | } 151 | 152 | m_allocatedSize = 0; 153 | m_lastAllocationOffset = m_bufferSize; 154 | } 155 | 156 | template 157 | void TLinearAllocator::Release() 158 | { 159 | //assert(IsInitialized()); 160 | 161 | if (!IsInitialized()) 162 | { 163 | // Invalid allocator state 164 | return; 165 | } 166 | 167 | m_buffer = 0; 168 | m_bufferSize = 0; 169 | m_allocatedSize = 0; 170 | m_lastAllocationOffset = 0; 171 | } 172 | 173 | template 174 | bool TLinearAllocator::IsInitialized() const 175 | { 176 | return m_buffer != 0; 177 | } 178 | 179 | template 180 | size_t TLinearAllocator::GetAllocatedSize() const 181 | { 182 | return m_allocatedSize; 183 | } 184 | 185 | template 186 | void* TLinearAllocator::Allocate(size_t size, size_t alignment) 187 | { 188 | return AllocateImpl(size, alignment); 189 | } 190 | 191 | template 192 | void TLinearAllocator::Deallocate(void* ptr, size_t size) 193 | { 194 | // Not supported, does nothing 195 | } 196 | 197 | template 198 | bool TLinearAllocator::IsOwnerOf(void* ptr) const 199 | { 200 | //assert(IsInitialized()); 201 | 202 | if (!IsInitialized()) 203 | { 204 | // Invalid allocator state 205 | return false; 206 | } 207 | 208 | return IsPointerInBuffer(ptr, m_buffer, m_allocatedSize); 209 | } 210 | 211 | template 212 | void* TLinearAllocator::AllocateImpl(size_t size, size_t alignment) 213 | { 214 | //assert(IsInitialized()); 215 | //assert(size > 0); 216 | //assert(IsPowerOfTwo(alignment)); 217 | 218 | if (!IsInitialized()) 219 | { 220 | // Invalid allocator state 221 | return nullptr; 222 | } 223 | 224 | if (size == 0 || !IsPowerOfTwo(alignment)) 225 | { 226 | // Invalid arguments 227 | return nullptr; 228 | } 229 | 230 | if (!CanSatisfyAllocation(m_buffer, m_bufferSize, m_allocatedSize, size, alignment)) 231 | { 232 | return nullptr; 233 | } 234 | 235 | return AllocateFromBuffer(m_buffer, m_bufferSize, m_allocatedSize, size, alignment, m_lastAllocationOffset); 236 | } 237 | 238 | template 239 | void* TLinearAllocator::ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment) 240 | { 241 | //assert(allocator); 242 | 243 | TLinearAllocator* allocatorImpl = static_cast*>(allocator); 244 | 245 | //assert(allocatorImpl->IsInitialized()); 246 | //assert(newSize > 0); 247 | //assert(IsPowerOfTwo(alignment)); 248 | 249 | if (!allocatorImpl->IsInitialized()) 250 | { 251 | // Invalid allocator state 252 | return nullptr; 253 | } 254 | 255 | if (newSize == 0 || !IsPowerOfTwo(alignment)) 256 | { 257 | // Invalid arguments 258 | return nullptr; 259 | } 260 | 261 | // We do not support freeing 262 | SizeType lastAllocationOffset = allocatorImpl->m_lastAllocationOffset; 263 | uintptr_t lastAllocation = allocatorImpl->m_buffer + lastAllocationOffset; 264 | uintptr_t rawOldPtr = reinterpret_cast(oldPtr); 265 | 266 | if (lastAllocation == rawOldPtr) 267 | { 268 | // We are reallocating the last allocation 269 | SizeType allocatedSize = allocatorImpl->m_allocatedSize; 270 | SizeType bufferSize = allocatorImpl->m_bufferSize; 271 | 272 | // If we are shrinking the allocation, deltaSize 273 | // will be very large (negative) 274 | SizeType deltaSize = newSize - oldSize; 275 | 276 | // If deltaSize is very large (negative), we will wrap around 277 | // and newAllocatedSize should end up smaller than allocatedSize 278 | SizeType newAllocatedSize = allocatedSize + deltaSize; 279 | //assert(newAllocatedSize <= bufferSize); 280 | if (newAllocatedSize > bufferSize) 281 | { 282 | // Out of memory 283 | return nullptr; 284 | } 285 | 286 | allocatorImpl->m_allocatedSize = newAllocatedSize; 287 | 288 | // Nothing to copy since we re-use the same memory 289 | 290 | return oldPtr; 291 | } 292 | 293 | // We do not support reallocating an arbitrary allocation 294 | // we simply perform a new allocation and copy the contents 295 | void* ptr = allocatorImpl->AllocateImpl(newSize, alignment); 296 | 297 | if (ptr != nullptr) 298 | { 299 | size_t numBytesToCopy = newSize >= oldSize ? oldSize : newSize; 300 | memcpy(ptr, oldPtr, numBytesToCopy); 301 | } 302 | 303 | return ptr; 304 | } 305 | 306 | //////////////////////////////////////// 307 | 308 | typedef TLinearAllocator LinearAllocator; 309 | } 310 | 311 | #endif // GIN_LINEAR_ALLOCATOR_H 312 | 313 | -------------------------------------------------------------------------------- /include/gin/platforms.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_PLATFORMS_H 2 | #define GIN_PLATFORMS_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #if __APPLE__ 29 | #include "TargetConditionals.h" 30 | 31 | #if TARGET_OS_IPHONE && TARGET_IPHONE_SIMULATOR 32 | #define GIN_IOS_SIM 1 33 | #define GIN_IOS 1 34 | #elif TARGET_OS_IPHONE 35 | #define GIN_IOS 1 36 | #else 37 | #define GIN_OSX 1 38 | #endif 39 | #endif 40 | 41 | #endif // GIN_PLATFORMS_H 42 | 43 | -------------------------------------------------------------------------------- /include/gin/stack_frame_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_STACK_FRAME_ALLOCATOR_H 2 | #define GIN_STACK_FRAME_ALLOCATOR_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include "allocator.h" 29 | #include "allocator_frame.h" 30 | #include "utils.h" 31 | #include "virtual_memory.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | namespace gin 38 | { 39 | //////////////////////////////////////// 40 | // A simple stack frame allocator. 41 | // 42 | // The allocator is not thread-safe. 43 | // 44 | // See here for more details: 45 | // http://nfrechette.github.io/2016/05/09/greedy_stack_frame_allocator/ 46 | //////////////////////////////////////// 47 | 48 | template 49 | class TStackFrameAllocator : public Allocator 50 | { 51 | public: 52 | inline TStackFrameAllocator(); 53 | inline TStackFrameAllocator(size_t segmentSize); 54 | inline ~TStackFrameAllocator(); 55 | 56 | virtual void* Allocate(size_t size, size_t alignment) override; 57 | virtual void Deallocate(void* ptr, size_t size) override; 58 | 59 | virtual bool IsOwnerOf(void* ptr) const override; 60 | 61 | void RegisterSegment(void* buffer, size_t bufferSize); 62 | 63 | AllocatorFrame PushFrame(); 64 | bool PopFrame(AllocatorFrame& frame); 65 | inline operator internal::AllocatorFrameFactory(); 66 | 67 | void Initialize(size_t segmentSize); 68 | void Release(); 69 | 70 | inline bool IsInitialized() const; 71 | size_t GetAllocatedSize() const; 72 | inline bool HasLiveFrame() const; 73 | 74 | inline size_t GetFrameOverhead() const; 75 | inline size_t GetSegmentOverhead() const; 76 | 77 | private: 78 | struct SegmentDescription 79 | { 80 | static constexpr uintptr_t kMinAlignment = 8; 81 | static constexpr uintptr_t kFlagsMask = kMinAlignment - 1; 82 | static constexpr uintptr_t kIsExternallyManaged = 0x1; 83 | 84 | // Link in our segment list, either prev or next depending on context 85 | // We also pack some flags in the pointer least significant bits 86 | uintptr_t packed; 87 | 88 | SizeType segmentSize; 89 | SizeType allocatedSize; 90 | 91 | SegmentDescription(size_t size) 92 | : packed(0) 93 | , segmentSize(size) 94 | , allocatedSize(0) 95 | { 96 | } 97 | 98 | uintptr_t GetBuffer() const { return reinterpret_cast(this) + sizeof(SegmentDescription); } 99 | SizeType GetBufferSize() const { return segmentSize - sizeof(SegmentDescription); } 100 | 101 | void SetLink(SegmentDescription* segment) 102 | { 103 | packed = reinterpret_cast(segment) | (packed & kFlagsMask); 104 | } 105 | SegmentDescription* GetLink() const { return reinterpret_cast(packed & ~kFlagsMask); } 106 | 107 | bool IsExternallyManaged() const { return (packed & kIsExternallyManaged) != 0; } 108 | void SetExternallyManaged(bool value) 109 | { 110 | packed = (packed & ~kIsExternallyManaged) | (value ? kIsExternallyManaged : 0); 111 | } 112 | }; 113 | 114 | struct FrameDescription 115 | { 116 | FrameDescription* prevFrame; 117 | }; 118 | 119 | // Ensure that when we allocate a FrameDescription in a fresh new segment, that we 120 | // do not introduce padding due to alignment. 121 | static_assert(alignof(FrameDescription) == alignof(SegmentDescription), "Alignment must match!"); 122 | 123 | TStackFrameAllocator(const TStackFrameAllocator&) = delete; 124 | TStackFrameAllocator(TStackFrameAllocator&&) = delete; 125 | TStackFrameAllocator& operator=(TStackFrameAllocator) = delete; 126 | 127 | SegmentDescription* AllocateSegment(size_t size, size_t alignment); 128 | void ReleaseSegment(SegmentDescription* segment); 129 | SegmentDescription* FindFreeSegment(size_t size, size_t alignment); 130 | 131 | static bool CanSatisfyAllocation(const SegmentDescription* segment, size_t size, size_t alignment); 132 | 133 | void* AllocateImpl(size_t size, size_t alignment); 134 | static void* ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment); 135 | static void PushImpl(Allocator* allocator, AllocatorFrame& outFrame); 136 | static bool PopImpl(Allocator* allocator, void* allocatorData); 137 | 138 | SegmentDescription* m_liveSegment; 139 | FrameDescription* m_liveFrame; 140 | SegmentDescription* m_freeSegmentList; 141 | 142 | SizeType m_defaultSegmentSize; 143 | SizeType m_lastAllocationOffset; // For realloc support only 144 | }; 145 | 146 | //////////////////////////////////////// 147 | 148 | template 149 | TStackFrameAllocator::TStackFrameAllocator() 150 | : Allocator(&TStackFrameAllocator::ReallocateImpl) 151 | , m_liveSegment(nullptr) 152 | , m_liveFrame(nullptr) 153 | , m_freeSegmentList(nullptr) 154 | , m_defaultSegmentSize(0) 155 | , m_lastAllocationOffset(0) 156 | { 157 | } 158 | 159 | template 160 | TStackFrameAllocator::TStackFrameAllocator(size_t segmentSize) 161 | : Allocator(&TStackFrameAllocator::ReallocateImpl) 162 | , m_liveSegment(nullptr) 163 | , m_liveFrame(nullptr) 164 | , m_freeSegmentList(nullptr) 165 | , m_defaultSegmentSize(0) 166 | , m_lastAllocationOffset(0) 167 | { 168 | Initialize(segmentSize); 169 | } 170 | 171 | template 172 | TStackFrameAllocator::~TStackFrameAllocator() 173 | { 174 | Release(); 175 | } 176 | 177 | template 178 | void TStackFrameAllocator::Initialize(size_t segmentSize) 179 | { 180 | //assert(!IsInitialized()); 181 | //assert(buffer != nullptr); 182 | //assert(segmentSize != 0); 183 | //assert(segmentSize <= static_cast(std::numeric_limits::max())); 184 | 185 | if (IsInitialized()) 186 | { 187 | // Invalid allocator state 188 | return; 189 | } 190 | 191 | if (segmentSize == 0 192 | || segmentSize > static_cast(std::numeric_limits::max())) 193 | { 194 | // Invalid arguments 195 | return; 196 | } 197 | 198 | m_liveSegment = nullptr; 199 | m_liveFrame = nullptr; 200 | m_freeSegmentList = nullptr; 201 | 202 | m_defaultSegmentSize = static_cast(segmentSize); 203 | m_lastAllocationOffset = static_cast(segmentSize); 204 | } 205 | 206 | template 207 | void TStackFrameAllocator::Release() 208 | { 209 | //assert(IsInitialized()); 210 | //assert(!HasLiveFrame()); 211 | 212 | if (!IsInitialized()) 213 | { 214 | // Invalid allocator state 215 | return; 216 | } 217 | 218 | if (HasLiveFrame()) 219 | { 220 | // Cannot release the allocator if we have live frames, leak memory instead 221 | return; 222 | } 223 | 224 | //assert(!m_liveSegment); 225 | 226 | SegmentDescription* segment = m_freeSegmentList; 227 | while (segment != nullptr) 228 | { 229 | SegmentDescription* nextSegment = segment->GetLink(); 230 | 231 | if (!segment->IsExternallyManaged()) 232 | { 233 | // We allocated this internally, release it 234 | ReleaseSegment(segment); 235 | } 236 | 237 | segment = nextSegment; 238 | } 239 | 240 | // Only 'm_defaultSegmentSize' is used to tell if we are initialized. 241 | // Everything else is set when we initialize. 242 | // If we are not initialized, the allocator cannot be safely used. 243 | m_defaultSegmentSize = 0; 244 | } 245 | 246 | template 247 | bool TStackFrameAllocator::IsInitialized() const 248 | { 249 | return m_defaultSegmentSize != 0; 250 | } 251 | 252 | template 253 | size_t TStackFrameAllocator::GetAllocatedSize() const 254 | { 255 | size_t allocatedSize = 0; 256 | 257 | // This is slow, use at your own risk. We must iterate over all 258 | // live segments. 259 | SegmentDescription* segment = m_liveSegment; 260 | while (segment != nullptr) 261 | { 262 | allocatedSize += segment->allocatedSize; 263 | 264 | segment = segment->GetLink(); 265 | } 266 | 267 | return allocatedSize; 268 | } 269 | 270 | template 271 | bool TStackFrameAllocator::HasLiveFrame() const 272 | { 273 | return m_liveFrame != nullptr; 274 | } 275 | 276 | template 277 | size_t TStackFrameAllocator::GetFrameOverhead() const 278 | { 279 | return sizeof(FrameDescription); 280 | } 281 | 282 | template 283 | size_t TStackFrameAllocator::GetSegmentOverhead() const 284 | { 285 | return sizeof(SegmentDescription); 286 | } 287 | 288 | template 289 | void* TStackFrameAllocator::Allocate(size_t size, size_t alignment) 290 | { 291 | //assert(IsInitialized()); 292 | //assert(size > 0); 293 | //assert(IsPowerOfTwo(alignment)); 294 | 295 | if (!IsInitialized()) 296 | { 297 | // Invalid allocator state 298 | return nullptr; 299 | } 300 | 301 | if (size == 0 || !IsPowerOfTwo(alignment)) 302 | { 303 | // Invalid arguments 304 | return nullptr; 305 | } 306 | 307 | if (!HasLiveFrame()) 308 | { 309 | // Need at least a single live frame 310 | return nullptr; 311 | } 312 | 313 | return AllocateImpl(size, alignment); 314 | } 315 | 316 | template 317 | void TStackFrameAllocator::Deallocate(void* ptr, size_t size) 318 | { 319 | // Not supported, does nothing 320 | } 321 | 322 | template 323 | bool TStackFrameAllocator::IsOwnerOf(void* ptr) const 324 | { 325 | //assert(IsInitialized()); 326 | 327 | if (!IsInitialized()) 328 | { 329 | // Invalid allocator state 330 | return false; 331 | } 332 | 333 | // This is slow, use at your own risk. We must iterate over all 334 | // live segments. 335 | SegmentDescription* segment = m_liveSegment; 336 | while (segment != nullptr) 337 | { 338 | if (IsPointerInBuffer(ptr, segment->GetBuffer(), segment->allocatedSize)) 339 | { 340 | return true; 341 | } 342 | 343 | segment = segment->GetLink(); 344 | } 345 | 346 | return false; 347 | } 348 | 349 | template 350 | void TStackFrameAllocator::RegisterSegment(void* buffer, size_t bufferSize) 351 | { 352 | //assert(IsInitialized()); 353 | 354 | if (!IsInitialized()) 355 | { 356 | // Invalid allocator state 357 | return; 358 | } 359 | 360 | //assert(buffer != nullptr); 361 | //assert(bufferSize > sizeof(SegmentDescription)); 362 | //assert(IsAlignedTo(buffer, SegmentDescription::kMinAlignment)); 363 | 364 | if (buffer == nullptr || bufferSize <= sizeof(SegmentDescription) 365 | || !IsAlignedTo(buffer, SegmentDescription::kMinAlignment)) 366 | { 367 | // Invalid arguments 368 | return; 369 | } 370 | 371 | // Add our new segment to our free list 372 | SegmentDescription* segment = new(buffer) SegmentDescription(bufferSize); 373 | segment->SetLink(m_freeSegmentList); 374 | segment->SetExternallyManaged(true); 375 | 376 | m_freeSegmentList = segment; 377 | } 378 | 379 | template 380 | AllocatorFrame TStackFrameAllocator::PushFrame() 381 | { 382 | AllocatorFrame frame; 383 | 384 | PushImpl(this, frame); 385 | 386 | return frame; 387 | } 388 | 389 | template 390 | bool TStackFrameAllocator::PopFrame(AllocatorFrame& frame) 391 | { 392 | return frame.Pop(); 393 | } 394 | 395 | template 396 | TStackFrameAllocator::operator internal::AllocatorFrameFactory() 397 | { 398 | return internal::AllocatorFrameFactory(this, &PushImpl); 399 | } 400 | 401 | template 402 | typename TStackFrameAllocator::SegmentDescription* 403 | TStackFrameAllocator::AllocateSegment(size_t size, size_t alignment) 404 | { 405 | //assert(size > sizeof(SegmentDescription)); 406 | //assert(IsPowerOfTwo(alignment)); 407 | 408 | size_t desiredSize = AlignTo(size + alignment + sizeof(SegmentDescription), alignment); 409 | size_t segmentSize = std::max(desiredSize, m_defaultSegmentSize); 410 | 411 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 412 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 413 | 414 | void* ptr = VirtualAlloc(segmentSize, accessFlags, regionFlags); 415 | 416 | //assert(ptr != nullptr); 417 | if (ptr == nullptr) 418 | { 419 | // Failed to allocate a usable segment 420 | return nullptr; 421 | } 422 | 423 | //assert(IsAlignedTo(ptr, SegmentDescription::kMinAlignment)); 424 | 425 | SegmentDescription* segment = new(ptr) SegmentDescription(segmentSize); 426 | 427 | return segment; 428 | } 429 | 430 | template 431 | void TStackFrameAllocator::ReleaseSegment(SegmentDescription* segment) 432 | { 433 | //assert(segment); 434 | //assert(segment->segmentSize > 0); 435 | //assert(!segment->IsExternallyManaged()); 436 | 437 | VirtualFree(segment, segment->segmentSize); 438 | } 439 | 440 | template 441 | typename TStackFrameAllocator::SegmentDescription* 442 | TStackFrameAllocator::FindFreeSegment(size_t size, size_t alignment) 443 | { 444 | if (m_liveSegment != nullptr && CanSatisfyAllocation(m_liveSegment, size, alignment)) 445 | { 446 | return m_liveSegment; 447 | } 448 | 449 | SegmentDescription* segment = m_freeSegmentList; 450 | while (segment != nullptr) 451 | { 452 | SegmentDescription* nextSegment = segment->GetLink(); 453 | 454 | if (CanSatisfyAllocation(segment, size, alignment)) 455 | { 456 | segment->SetLink(m_liveSegment); 457 | m_liveSegment = segment; 458 | 459 | m_freeSegmentList = nextSegment; 460 | 461 | return segment; 462 | } 463 | 464 | // Try the next one 465 | segment = nextSegment; 466 | } 467 | 468 | // Failed to find a segment with enough space 469 | // Try to allocate a new one 470 | SegmentDescription* liveSegment = AllocateSegment(size, alignment); 471 | if (liveSegment != nullptr) 472 | { 473 | liveSegment->SetLink(m_liveSegment); 474 | m_liveSegment = liveSegment; 475 | } 476 | 477 | return liveSegment; 478 | } 479 | 480 | template 481 | bool TStackFrameAllocator::CanSatisfyAllocation(const SegmentDescription* segment, size_t size, size_t alignment) 482 | { 483 | return gin::CanSatisfyAllocation(segment->GetBuffer(), segment->GetBufferSize(), segment->allocatedSize, size, alignment); 484 | } 485 | 486 | template 487 | void* TStackFrameAllocator::AllocateImpl(size_t size, size_t alignment) 488 | { 489 | //assert(IsInitialized()); 490 | //assert(size > 0); 491 | //assert(IsPowerOfTwo(alignment)); 492 | 493 | SegmentDescription* liveSegment = FindFreeSegment(size, alignment); 494 | if (liveSegment == nullptr) 495 | { 496 | // Failed to allocate a segment, out of memory? 497 | return nullptr; 498 | } 499 | 500 | return AllocateFromBuffer(liveSegment->GetBuffer(), liveSegment->GetBufferSize(), liveSegment->allocatedSize, 501 | size, alignment, m_lastAllocationOffset); 502 | } 503 | 504 | template 505 | void* TStackFrameAllocator::ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment) 506 | { 507 | TStackFrameAllocator* allocatorImpl = static_cast*>(allocator); 508 | 509 | //assert(allocatorImpl->IsInitialized()); 510 | //assert(newSize > 0); 511 | //assert(IsPowerOfTwo(alignment)); 512 | 513 | if (!allocatorImpl->IsInitialized()) 514 | { 515 | // Invalid allocator state 516 | return nullptr; 517 | } 518 | 519 | if (newSize == 0 || !IsPowerOfTwo(alignment)) 520 | { 521 | // Invalid arguments 522 | return nullptr; 523 | } 524 | 525 | if (!allocatorImpl->HasLiveFrame()) 526 | { 527 | // Need at least a single live frame 528 | return nullptr; 529 | } 530 | 531 | // We do not support freeing 532 | SizeType lastAllocationOffset = allocatorImpl->m_lastAllocationOffset; 533 | SegmentDescription* liveSegment = allocatorImpl->m_liveSegment; 534 | uintptr_t lastAllocation = liveSegment->GetBuffer() + lastAllocationOffset; 535 | uintptr_t rawOldPtr = reinterpret_cast(oldPtr); 536 | 537 | if (lastAllocation == rawOldPtr) 538 | { 539 | // We are reallocating the last allocation 540 | SizeType allocatedSize = liveSegment->allocatedSize; 541 | SizeType bufferSize = liveSegment->GetBufferSize(); 542 | 543 | // If we are shrinking the allocation, deltaSize 544 | // will be very large (negative) 545 | SizeType deltaSize = newSize - oldSize; 546 | 547 | // If deltaSize is very large (negative), we will wrap around 548 | // and newAllocatedSize should end up smaller than allocatedSize 549 | SizeType newAllocatedSize = allocatedSize + deltaSize; 550 | //assert(newAllocatedSize <= bufferSize); 551 | if (newAllocatedSize <= bufferSize) 552 | { 553 | liveSegment->allocatedSize = newAllocatedSize; 554 | 555 | // Nothing to copy since we re-use the same memory 556 | 557 | return oldPtr; 558 | } 559 | 560 | // Not enough space in our current live segment, make 561 | // a new allocation and copy 562 | } 563 | 564 | // We do not support reallocating an arbitrary allocation 565 | // we simply perform a new allocation and copy the contents 566 | void* ptr = allocatorImpl->AllocateImpl(newSize, alignment); 567 | 568 | if (ptr != nullptr) 569 | { 570 | size_t numBytesToCopy = newSize >= oldSize ? oldSize : newSize; 571 | memcpy(ptr, oldPtr, numBytesToCopy); 572 | } 573 | 574 | return ptr; 575 | } 576 | 577 | template 578 | void TStackFrameAllocator::PushImpl(Allocator* allocator, AllocatorFrame& outFrame) 579 | { 580 | //assert(allocator); 581 | 582 | TStackFrameAllocator* allocatorImpl = static_cast*>(allocator); 583 | 584 | if (!allocatorImpl->IsInitialized()) 585 | { 586 | // Invalid allocator state 587 | outFrame = AllocatorFrame(); 588 | return; 589 | } 590 | 591 | void* ptr = allocatorImpl->AllocateImpl(sizeof(FrameDescription), alignof(FrameDescription)); 592 | if (ptr == nullptr) 593 | { 594 | // Failed to allocate our frame, out of memory? 595 | outFrame = AllocatorFrame(); 596 | return; 597 | } 598 | 599 | FrameDescription* frameDesc = reinterpret_cast(ptr); 600 | frameDesc->prevFrame = allocatorImpl->m_liveFrame; 601 | 602 | allocatorImpl->m_liveFrame = frameDesc; 603 | 604 | outFrame = AllocatorFrame(allocator, &PopImpl, frameDesc); 605 | } 606 | 607 | template 608 | bool TStackFrameAllocator::PopImpl(Allocator* allocator, void* allocatorData) 609 | { 610 | //assert(allocator); 611 | 612 | TStackFrameAllocator* allocatorImpl = static_cast*>(allocator); 613 | 614 | //assert(allocatorImpl->IsInitialized()); 615 | 616 | if (!allocatorImpl->IsInitialized()) 617 | { 618 | // Invalid allocator state 619 | return false; 620 | } 621 | 622 | const FrameDescription* frameDesc = static_cast(allocatorData); 623 | 624 | // We can only pop the top most frame 625 | //assert(frameDesc == allocatorImpl->m_liveFrame); 626 | if (frameDesc != allocatorImpl->m_liveFrame) 627 | { 628 | return false; 629 | } 630 | 631 | // Update our topmost frame 632 | allocatorImpl->m_liveFrame = frameDesc->prevFrame; 633 | 634 | // Pop everything 635 | SegmentDescription* liveSegment = allocatorImpl->m_liveSegment; 636 | SegmentDescription* freeSegmentList = allocatorImpl->m_freeSegmentList; 637 | 638 | while (liveSegment != nullptr) 639 | { 640 | SegmentDescription* nextSegment = liveSegment->GetLink(); 641 | 642 | uintptr_t buffer = liveSegment->GetBuffer(); 643 | if (IsPointerInBuffer(frameDesc, buffer, liveSegment->allocatedSize)) 644 | { 645 | // Reset our allocated size and stop 646 | // Note that this only works because allocating the frame on a fresh new segment 647 | // does not require any padding from alignment 648 | uintptr_t allocatedSize = reinterpret_cast(frameDesc) - buffer; 649 | if (allocatedSize == 0) 650 | { 651 | // The whole segment is popped, add it to the free list 652 | liveSegment->SetLink(freeSegmentList); 653 | liveSegment->allocatedSize = 0; 654 | freeSegmentList = liveSegment; 655 | 656 | // Update the segment so we can use the previous one as live below 657 | liveSegment = nextSegment; 658 | } 659 | else 660 | { 661 | liveSegment->allocatedSize = static_cast(allocatedSize); 662 | } 663 | 664 | break; 665 | } 666 | 667 | // Our frame wasn't in this segment, we can add it to the free list 668 | liveSegment->SetLink(freeSegmentList); 669 | liveSegment->allocatedSize = 0; 670 | freeSegmentList = liveSegment; 671 | 672 | liveSegment = nextSegment; 673 | } 674 | 675 | // Update our live segment and free list 676 | allocatorImpl->m_liveSegment = liveSegment; 677 | allocatorImpl->m_freeSegmentList = freeSegmentList; 678 | 679 | //assert((allocatorImpl->m_liveFrame != nullptr && allocatorImpl->m_liveSegment != nullptr) 680 | // || (allocatorImpl->m_liveFrame == nullptr && allocatorImpl->m_liveSegment == nullptr)); 681 | 682 | return true; 683 | } 684 | 685 | //////////////////////////////////////// 686 | 687 | typedef TStackFrameAllocator StackFrameAllocator; 688 | } 689 | 690 | #endif // GIN_STACK_FRAME_ALLOCATOR_H 691 | 692 | -------------------------------------------------------------------------------- /include/gin/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_UTILS_H 2 | #define GIN_UTILS_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include 29 | 30 | namespace gin 31 | { 32 | //////////////////////////////////////// 33 | // AlignTo(..) aligns an integral value or a pointer to 34 | // the specified alignment value by bumping up the input value 35 | // if required. 36 | //////////////////////////////////////// 37 | template 38 | constexpr IntegralType AlignTo(IntegralType value, size_t alignment) 39 | { 40 | #if 0 41 | // Readable variant, cannot make this constexpr due to 42 | // variable declaration being a C++1y extension 43 | size_t bumpedValue = static_cast(value) + (alignment - 1); 44 | size_t truncatedValue = bumpedValue & ~(alignment - 1); 45 | return static_cast(truncatedValue); 46 | #else 47 | return static_cast((static_cast(value) + (alignment - 1)) & ~(alignment - 1)); 48 | #endif 49 | } 50 | 51 | template 52 | constexpr PtrType* AlignTo(PtrType* value, size_t alignment) 53 | { 54 | return reinterpret_cast(AlignTo(reinterpret_cast(value), alignment)); 55 | } 56 | 57 | //////////////////////////////////////// 58 | // IsAlignedTo(..) returns 'true' if the input integral or pointer value 59 | // is aligned to the specified alignment. 60 | //////////////////////////////////////// 61 | template 62 | constexpr bool IsAlignedTo(IntegralType value, size_t alignment) 63 | { 64 | return (value & (alignment - 1)) == 0; 65 | } 66 | 67 | template 68 | constexpr bool IsAlignedTo(PtrType* value, size_t alignment) 69 | { 70 | return (reinterpret_cast(value) & (alignment - 1)) == 0; 71 | } 72 | 73 | //////////////////////////////////////// 74 | // IsPowerOfTwo(..) returns 'true' if the input value is a power of two. 75 | //////////////////////////////////////// 76 | constexpr bool IsPowerOfTwo(size_t value) 77 | { 78 | return value != 0 && (value & (value - 1)) == 0; 79 | } 80 | 81 | //////////////////////////////////////// 82 | // IsPointerInBuffer(..) returns 'true' if the input pointer 83 | // falls in the supplied buffer. 84 | //////////////////////////////////////// 85 | // Cannot use void* here, it fails to compile as a constexpr 86 | // TODO: Make this branchless? Subtract ptr with start/end buffer 87 | // to get 2 negative values IIF ptr is in the buffer, logical AND 88 | // the two negative numbers and shift the sign bit. 89 | template 90 | constexpr bool IsPointerInBuffer(PtrType* ptr, uintptr_t buffer, size_t bufferSize) 91 | { 92 | #if 0 93 | // Readable variant, cannot make this constexpr due to 94 | // variable declaration being a C++1y extension 95 | uintptr_t ptrValue = reinterpret_cast(ptr); 96 | uintptr_t bufferEnd = buffer + bufferSize; 97 | 98 | return ptrValue >= buffer && ptrValue < bufferEnd; 99 | #else 100 | return reinterpret_cast(ptr) >= buffer && reinterpret_cast(ptr) < (buffer + bufferSize); 101 | #endif 102 | } 103 | 104 | //////////////////////////////////////// 105 | // CanSatisfyAllocation(..) returns 'true' if the supplied buffer 106 | // still has space remaining to satisfy a given allocation and alignment. 107 | //////////////////////////////////////// 108 | // TODO: Make constexpr 109 | template 110 | bool CanSatisfyAllocation(uintptr_t buffer, SizeType bufferSize, SizeType allocatedSize, size_t size, size_t alignment) 111 | { 112 | uintptr_t bufferHead = buffer + allocatedSize; 113 | uintptr_t allocStart = AlignTo(bufferHead, alignment); 114 | 115 | //assert(allocStart >= bufferHead); 116 | if (allocStart < bufferHead) 117 | { 118 | // Alignment made us overflow 119 | return false; 120 | } 121 | 122 | uintptr_t allocEnd = allocStart + size; 123 | 124 | //assert(allocEnd > allocStart); 125 | if (allocEnd <= allocStart) 126 | { 127 | // Requested size made us overflow 128 | return false; 129 | } 130 | 131 | uintptr_t allocSize = allocEnd - bufferHead; 132 | SizeType newAllocatedSize = allocatedSize + allocSize; 133 | 134 | //assert(newAllocatedSize <= bufferSize); 135 | if (newAllocatedSize <= bufferSize) 136 | { 137 | // Still has free space, we fit 138 | return true; 139 | } 140 | 141 | // Not enough space 142 | return false; 143 | } 144 | 145 | //////////////////////////////////////// 146 | // AllocateFromBuffer(..) will perform the allocation from the supplied buffer. 147 | // 'allocatedSize' and 'outAllocationOffset' will be updated. 148 | //////////////////////////////////////// 149 | template 150 | void* AllocateFromBuffer(uintptr_t buffer, SizeType bufferSize, SizeType& allocatedSize, 151 | size_t size, size_t alignment, 152 | SizeType& outAllocationOffset) 153 | { 154 | uintptr_t bufferHead = buffer + allocatedSize; 155 | uintptr_t allocStart = AlignTo(bufferHead, alignment); 156 | //assert(allocStart >= bufferHead); 157 | 158 | uintptr_t allocEnd = allocStart + size; 159 | //assert(allocEnd > allocStart); 160 | 161 | uintptr_t allocSize = allocEnd - bufferHead; 162 | SizeType newAllocatedSize = allocatedSize + allocSize; 163 | //assert(newAllocatedSize <= bufferSize); 164 | 165 | allocatedSize = newAllocatedSize; 166 | outAllocationOffset = static_cast(allocStart - buffer); 167 | 168 | return reinterpret_cast(allocStart); 169 | } 170 | } 171 | 172 | #endif // GIN_UTILS_H 173 | 174 | -------------------------------------------------------------------------------- /include/gin/virtual_memory.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_VIRTUAL_MEMORY_H 2 | #define GIN_VIRTUAL_MEMORY_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include "kernel/osx/virtual_memory.h" 29 | 30 | #endif // GIN_VIRTUAL_MEMORY_H 31 | 32 | -------------------------------------------------------------------------------- /include/gin/vmem_linear_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_VMEM_LINEAR_ALLOCATOR_H 2 | #define GIN_VMEM_LINEAR_ALLOCATOR_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include "allocator.h" 29 | #include "utils.h" 30 | #include "virtual_memory.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | namespace gin 37 | { 38 | //////////////////////////////////////// 39 | // A simple linear allocator. 40 | // Unlike LineraAllocator, it does not accept a pre-allocated 41 | // buffer but instead allocates virtual memory and 42 | // commits/decommits it as necessary. 43 | // 44 | // There is no per allocation overhead. 45 | // The buffer memory is not modified by the allocator. 46 | // The allocator is not thread-safe. 47 | // 48 | // See here for more details: 49 | // http://nfrechette.github.io/2015/06/11/vmem_linear_allocator/ 50 | //////////////////////////////////////// 51 | 52 | template 53 | class TVMemLinearAllocator : public Allocator 54 | { 55 | public: 56 | inline TVMemLinearAllocator(); 57 | inline TVMemLinearAllocator(size_t bufferSize); 58 | inline ~TVMemLinearAllocator(); 59 | 60 | virtual void* Allocate(size_t size, size_t alignment) override; 61 | virtual void Deallocate(void* ptr, size_t size) override; 62 | 63 | virtual bool IsOwnerOf(void* ptr) const override; 64 | 65 | void Initialize(size_t bufferSize); 66 | void Reset(); 67 | void Release(); 68 | 69 | inline bool IsInitialized() const; 70 | inline size_t GetAllocatedSize() const; 71 | inline size_t GetCommittedSize() const; 72 | 73 | private: 74 | TVMemLinearAllocator(const TVMemLinearAllocator&) = delete; 75 | TVMemLinearAllocator(TVMemLinearAllocator&&) = delete; 76 | TVMemLinearAllocator& operator=(TVMemLinearAllocator) = delete; 77 | 78 | void* AllocateImpl(size_t size, size_t alignment); 79 | static void* ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment); 80 | 81 | uintptr_t m_buffer; 82 | SizeType m_bufferSize; 83 | SizeType m_allocatedSize; 84 | SizeType m_lastAllocationOffset; // For realloc support only 85 | SizeType m_committedSize; 86 | }; 87 | 88 | //////////////////////////////////////// 89 | 90 | template 91 | TVMemLinearAllocator::TVMemLinearAllocator() 92 | : Allocator(&TVMemLinearAllocator::ReallocateImpl) 93 | , m_buffer(0) 94 | , m_bufferSize(0) 95 | , m_allocatedSize(0) 96 | , m_lastAllocationOffset(0) 97 | , m_committedSize(0) 98 | { 99 | } 100 | 101 | template 102 | TVMemLinearAllocator::TVMemLinearAllocator(size_t bufferSize) 103 | : Allocator(&TVMemLinearAllocator::ReallocateImpl) 104 | , m_buffer(0) 105 | , m_bufferSize(0) 106 | , m_allocatedSize(0) 107 | , m_lastAllocationOffset(0) 108 | , m_committedSize(0) 109 | { 110 | Initialize(bufferSize); 111 | } 112 | 113 | template 114 | TVMemLinearAllocator::~TVMemLinearAllocator() 115 | { 116 | Release(); 117 | } 118 | 119 | template 120 | void TVMemLinearAllocator::Initialize(size_t bufferSize) 121 | { 122 | //assert(!IsInitialized()); 123 | //assert(bufferSize >= PAGE_SIZE); 124 | //assert(bufferSize <= static_cast(std::numeric_limits::max())); 125 | 126 | if (IsInitialized()) 127 | { 128 | // Invalid allocator state 129 | return; 130 | } 131 | 132 | if (bufferSize < (4 * 1024) // TODO: PAGE_SIZE 133 | || bufferSize > static_cast(std::numeric_limits::max())) 134 | { 135 | // Invalid arguments 136 | return; 137 | } 138 | 139 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 140 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 141 | 142 | void* ptr = VirtualReserve(bufferSize, accessFlags, regionFlags); 143 | //assert(ptr); 144 | if (!ptr) 145 | { 146 | // Failed to reserve virtual memory 147 | return; 148 | } 149 | 150 | m_buffer = reinterpret_cast(ptr); 151 | m_bufferSize = static_cast(bufferSize); 152 | m_allocatedSize = 0; 153 | m_lastAllocationOffset = static_cast(bufferSize); 154 | m_committedSize = 0; 155 | } 156 | 157 | template 158 | void TVMemLinearAllocator::Reset() 159 | { 160 | //assert(IsInitialized()); 161 | 162 | if (!IsInitialized()) 163 | { 164 | // Invalid allocator state 165 | return; 166 | } 167 | 168 | // TODO: Introduce a policy to handle slack 169 | 170 | if (m_committedSize != 0) 171 | { 172 | void* ptr = reinterpret_cast(m_buffer); 173 | bool success = VirtualDecommit(ptr, m_committedSize); 174 | //assert(success); 175 | if (!success) 176 | { 177 | // Failed to decommit virtual memory 178 | return; 179 | } 180 | } 181 | 182 | m_allocatedSize = 0; 183 | m_lastAllocationOffset = m_bufferSize; 184 | m_committedSize = 0; 185 | } 186 | 187 | template 188 | void TVMemLinearAllocator::Release() 189 | { 190 | //assert(IsInitialized()); 191 | 192 | if (!IsInitialized()) 193 | { 194 | // Invalid allocator state 195 | return; 196 | } 197 | 198 | // No need to decommit memory, release will take care of it 199 | 200 | void* ptr = reinterpret_cast(m_buffer); 201 | bool success = VirtualRelease(ptr, m_bufferSize); 202 | //assert(success); 203 | if (!success) 204 | { 205 | // Failed to release the virtual memory 206 | return; 207 | } 208 | 209 | m_buffer = 0; 210 | m_bufferSize = 0; 211 | m_allocatedSize = 0; 212 | m_lastAllocationOffset = 0; 213 | m_committedSize = 0; 214 | } 215 | 216 | template 217 | bool TVMemLinearAllocator::IsInitialized() const 218 | { 219 | return m_buffer != 0; 220 | } 221 | 222 | template 223 | size_t TVMemLinearAllocator::GetAllocatedSize() const 224 | { 225 | return m_allocatedSize; 226 | } 227 | 228 | template 229 | size_t TVMemLinearAllocator::GetCommittedSize() const 230 | { 231 | return m_committedSize; 232 | } 233 | 234 | template 235 | void* TVMemLinearAllocator::Allocate(size_t size, size_t alignment) 236 | { 237 | return AllocateImpl(size, alignment); 238 | } 239 | 240 | template 241 | void TVMemLinearAllocator::Deallocate(void* ptr, size_t size) 242 | { 243 | // Not supported, does nothing 244 | } 245 | 246 | template 247 | bool TVMemLinearAllocator::IsOwnerOf(void* ptr) const 248 | { 249 | //assert(IsInitialized()); 250 | 251 | if (!IsInitialized()) 252 | { 253 | // Invalid allocator state 254 | return false; 255 | } 256 | 257 | return IsPointerInBuffer(ptr, m_buffer, m_allocatedSize); 258 | } 259 | 260 | template 261 | void* TVMemLinearAllocator::AllocateImpl(size_t size, size_t alignment) 262 | { 263 | //assert(IsInitialized()); 264 | //assert(size > 0); 265 | //assert(IsPowerOfTwo(alignment)); 266 | 267 | if (!IsInitialized()) 268 | { 269 | // Invalid allocator state 270 | return nullptr; 271 | } 272 | 273 | if (size == 0 || !IsPowerOfTwo(alignment)) 274 | { 275 | // Invalid arguments 276 | return nullptr; 277 | } 278 | 279 | uintptr_t bufferStart = m_buffer; 280 | SizeType allocatedSize = m_allocatedSize; 281 | uintptr_t bufferHead = bufferStart + allocatedSize; 282 | uintptr_t allocStart = AlignTo(bufferHead, alignment); 283 | 284 | //assert(allocStart >= bufferHead); 285 | if (allocStart < bufferHead) 286 | { 287 | // Alignment made us overflow 288 | return nullptr; 289 | } 290 | 291 | uintptr_t allocEnd = allocStart + size; 292 | uintptr_t allocSize = allocEnd - bufferHead; 293 | 294 | //assert(allocEnd > allocStart); 295 | if (allocEnd <= allocStart) 296 | { 297 | // Requested size made us overflow 298 | return nullptr; 299 | } 300 | 301 | SizeType bufferSize = m_bufferSize; 302 | SizeType newAllocatedSize = allocatedSize + allocSize; 303 | //assert(newAllocatedSize <= bufferSize); 304 | if (newAllocatedSize > bufferSize) 305 | { 306 | // Out of memory 307 | return nullptr; 308 | } 309 | 310 | SizeType committedSize = m_committedSize; 311 | if (newAllocatedSize > committedSize) 312 | { 313 | // We need to commit more memory 314 | void* commitPtr = reinterpret_cast(bufferStart + committedSize); 315 | SizeType commitSize = AlignTo(newAllocatedSize - committedSize, 4096); // TODO: PAGE_SIZE 316 | 317 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 318 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 319 | 320 | bool success = VirtualCommit(commitPtr, commitSize, accessFlags, regionFlags); 321 | //assert(success); 322 | if (!success) 323 | { 324 | // Out of memory 325 | return nullptr; 326 | } 327 | 328 | m_committedSize = committedSize + commitSize; 329 | } 330 | 331 | m_allocatedSize = newAllocatedSize; 332 | m_lastAllocationOffset = static_cast(allocStart - bufferStart); 333 | 334 | return reinterpret_cast(allocStart); 335 | } 336 | 337 | template 338 | void* TVMemLinearAllocator::ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment) 339 | { 340 | //assert(allocator); 341 | 342 | TVMemLinearAllocator* allocatorImpl = static_cast*>(allocator); 343 | 344 | //assert(allocatorImpl->IsInitialized()); 345 | //assert(newSize > 0); 346 | //assert(IsPowerOfTwo(alignment)); 347 | 348 | if (!allocatorImpl->IsInitialized()) 349 | { 350 | // Invalid allocator state 351 | return nullptr; 352 | } 353 | 354 | if (newSize == 0 || !IsPowerOfTwo(alignment)) 355 | { 356 | // Invalid arguments 357 | return nullptr; 358 | } 359 | 360 | // We do not support freeing 361 | SizeType lastAllocationOffset = allocatorImpl->m_lastAllocationOffset; 362 | uintptr_t lastAllocation = allocatorImpl->m_buffer + lastAllocationOffset; 363 | uintptr_t rawOldPtr = reinterpret_cast(oldPtr); 364 | 365 | if (lastAllocation == rawOldPtr) 366 | { 367 | // We are reallocating the last allocation 368 | SizeType allocatedSize = allocatorImpl->m_allocatedSize; 369 | SizeType bufferSize = allocatorImpl->m_bufferSize; 370 | 371 | // If we are shrinking the allocation, deltaSize 372 | // will be very large (negative) 373 | SizeType deltaSize = newSize - oldSize; 374 | 375 | // If deltaSize is very large (negative), we will wrap around 376 | // and newAllocatedSize should end up smaller than allocatedSize 377 | SizeType newAllocatedSize = allocatedSize + deltaSize; 378 | //assert(newAllocatedSize <= bufferSize); 379 | if (newAllocatedSize > bufferSize) 380 | { 381 | // Out of memory 382 | return nullptr; 383 | } 384 | 385 | SizeType committedSize = allocatorImpl->m_committedSize; 386 | if (newAllocatedSize > committedSize) 387 | { 388 | // We need to commit more memory 389 | void* commitPtr = reinterpret_cast(allocatorImpl->m_buffer + committedSize); 390 | SizeType commitSize = AlignTo(newAllocatedSize - committedSize, 4096); // TODO: PAGE_SIZE 391 | 392 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 393 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 394 | 395 | bool success = VirtualCommit(commitPtr, commitSize, accessFlags, regionFlags); 396 | //assert(success); 397 | if (!success) 398 | { 399 | // Out of memory 400 | return nullptr; 401 | } 402 | 403 | allocatorImpl->m_committedSize = committedSize + commitSize; 404 | } 405 | 406 | allocatorImpl->m_allocatedSize = newAllocatedSize; 407 | 408 | // Nothing to copy since we re-use the same memory 409 | 410 | return oldPtr; 411 | } 412 | 413 | // We do not support reallocating an arbitrary allocation 414 | // we simply perform a new allocation and copy the contents 415 | void* ptr = allocatorImpl->AllocateImpl(newSize, alignment); 416 | 417 | if (ptr != nullptr) 418 | { 419 | size_t numBytesToCopy = newSize >= oldSize ? oldSize : newSize; 420 | memcpy(ptr, oldPtr, numBytesToCopy); 421 | } 422 | 423 | return ptr; 424 | } 425 | 426 | //////////////////////////////////////// 427 | 428 | typedef TVMemLinearAllocator VMemLinearAllocator; 429 | } 430 | 431 | #endif // GIN_VMEM_LINEAR_ALLOCATOR_H 432 | 433 | -------------------------------------------------------------------------------- /include/gin/vmem_stack_frame_allocator.h: -------------------------------------------------------------------------------- 1 | #ifndef GIN_VMEM_STACK_FRAME_ALLOCATOR_H 2 | #define GIN_VMEM_STACK_FRAME_ALLOCATOR_H 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // The MIT License (MIT) 6 | // 7 | // Copyright (c) 2015-2016 Nicholas Frechette 8 | // 9 | // Permission is hereby granted, free of charge, to any person obtaining a copy 10 | // of this software and associated documentation files (the "Software"), to deal 11 | // in the Software without restriction, including without limitation the rights 12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | // copies of the Software, and to permit persons to whom the Software is 14 | // furnished to do so, subject to the following conditions: 15 | // 16 | // The above copyright notice and this permission notice shall be included in all 17 | // copies or substantial portions of the Software. 18 | // 19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | // SOFTWARE. 26 | //////////////////////////////////////////////////////////////////////////////// 27 | 28 | #include "allocator.h" 29 | #include "allocator_frame.h" 30 | #include "utils.h" 31 | #include "virtual_memory.h" 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | namespace gin 38 | { 39 | //////////////////////////////////////// 40 | // A simple virtual memory aware stack frame allocator. 41 | // Unlike StackFrameAllocator, we do not allocate multiple segments, 42 | // and instead we allocate a single large virtual memory range 43 | // and commit/decommit when relevant. 44 | // 45 | // The allocator is not thread-safe. 46 | // 47 | // See here for more details: 48 | // http://nfrechette.github.io/todo/ 49 | //////////////////////////////////////// 50 | 51 | template 52 | class TVMemStackFrameAllocator : public Allocator 53 | { 54 | public: 55 | inline TVMemStackFrameAllocator(); 56 | inline TVMemStackFrameAllocator(size_t bufferSize); 57 | inline ~TVMemStackFrameAllocator(); 58 | 59 | virtual void* Allocate(size_t size, size_t alignment) override; 60 | virtual void Deallocate(void* ptr, size_t size) override; 61 | 62 | virtual bool IsOwnerOf(void* ptr) const override; 63 | 64 | AllocatorFrame PushFrame(); 65 | bool PopFrame(AllocatorFrame& frame); 66 | inline operator internal::AllocatorFrameFactory(); 67 | 68 | void Initialize(size_t bufferSize); 69 | void Release(); 70 | 71 | bool DecommitSlack(size_t minSlack); 72 | 73 | inline bool IsInitialized() const; 74 | inline size_t GetAllocatedSize() const; 75 | inline size_t GetCommittedSize() const; 76 | inline bool HasLiveFrame() const; 77 | 78 | inline size_t GetFrameOverhead() const; 79 | 80 | private: 81 | struct FrameDescription 82 | { 83 | FrameDescription* prevFrame; 84 | }; 85 | 86 | TVMemStackFrameAllocator(const TVMemStackFrameAllocator&) = delete; 87 | TVMemStackFrameAllocator(TVMemStackFrameAllocator&&) = delete; 88 | TVMemStackFrameAllocator& operator=(TVMemStackFrameAllocator) = delete; 89 | 90 | void* AllocateImpl(size_t size, size_t alignment); 91 | static void* ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment); 92 | static void PushImpl(Allocator* allocator, AllocatorFrame& outFrame); 93 | static bool PopImpl(Allocator* allocator, void* allocatorData); 94 | 95 | uintptr_t m_buffer; 96 | FrameDescription* m_liveFrame; 97 | 98 | SizeType m_bufferSize; 99 | SizeType m_allocatedSize; 100 | SizeType m_committedSize; 101 | SizeType m_lastAllocationOffset; // For realloc support only 102 | }; 103 | 104 | //////////////////////////////////////// 105 | 106 | template 107 | TVMemStackFrameAllocator::TVMemStackFrameAllocator() 108 | : Allocator(&TVMemStackFrameAllocator::ReallocateImpl) 109 | , m_buffer(0) 110 | , m_liveFrame(nullptr) 111 | , m_bufferSize(0) 112 | , m_allocatedSize(0) 113 | , m_committedSize(0) 114 | , m_lastAllocationOffset(0) 115 | { 116 | } 117 | 118 | template 119 | TVMemStackFrameAllocator::TVMemStackFrameAllocator(size_t bufferSize) 120 | : Allocator(&TVMemStackFrameAllocator::ReallocateImpl) 121 | , m_buffer(0) 122 | , m_liveFrame(nullptr) 123 | , m_bufferSize(0) 124 | , m_allocatedSize(0) 125 | , m_committedSize(0) 126 | , m_lastAllocationOffset(0) 127 | { 128 | Initialize(bufferSize); 129 | } 130 | 131 | template 132 | TVMemStackFrameAllocator::~TVMemStackFrameAllocator() 133 | { 134 | Release(); 135 | } 136 | 137 | template 138 | void TVMemStackFrameAllocator::Initialize(size_t bufferSize) 139 | { 140 | //assert(!IsInitialized()); 141 | //assert(bufferSize >= PAGE_SIZE); 142 | //assert(IsAlignedTo(bufferSize, PAGE_SIZE); 143 | //assert(bufferSize <= static_cast(std::numeric_limits::max())); 144 | 145 | if (IsInitialized()) 146 | { 147 | // Invalid allocator state 148 | return; 149 | } 150 | 151 | if (bufferSize < 4096 // TODO: PAGE_SIZE 152 | || !IsAlignedTo(bufferSize, 4096) 153 | || bufferSize > static_cast(std::numeric_limits::max())) 154 | { 155 | // Invalid arguments 156 | return; 157 | } 158 | 159 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 160 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 161 | 162 | void* ptr = VirtualReserve(bufferSize, accessFlags, regionFlags); 163 | //assert(ptr); 164 | if (!ptr) 165 | { 166 | // Failed to reserve virtual memory 167 | return; 168 | } 169 | 170 | m_buffer = reinterpret_cast(ptr); 171 | m_liveFrame = nullptr; 172 | m_bufferSize = static_cast(bufferSize); 173 | m_allocatedSize = 0; 174 | m_committedSize = 0; 175 | m_lastAllocationOffset = static_cast(bufferSize); 176 | } 177 | 178 | template 179 | void TVMemStackFrameAllocator::Release() 180 | { 181 | //assert(IsInitialized()); 182 | //assert(!HasLiveFrame()); 183 | 184 | if (!IsInitialized()) 185 | { 186 | // Invalid allocator state 187 | return; 188 | } 189 | 190 | if (HasLiveFrame()) 191 | { 192 | // Cannot release the allocator if we have live frames, leak memory instead 193 | return; 194 | } 195 | 196 | // No need to decommit memory, release will take care of it 197 | 198 | void* ptr = reinterpret_cast(m_buffer); 199 | bool success = VirtualRelease(ptr, m_bufferSize); 200 | //assert(success); 201 | if (!success) 202 | { 203 | // Failed to release the virtual memory 204 | return; 205 | } 206 | 207 | m_buffer = 0; 208 | m_liveFrame = nullptr; 209 | m_bufferSize = 0; 210 | m_allocatedSize = 0; 211 | m_committedSize = 0; 212 | m_lastAllocationOffset = 0; 213 | } 214 | 215 | template 216 | bool TVMemStackFrameAllocator::DecommitSlack(size_t minSlack) 217 | { 218 | //assert(IsInitialized()); 219 | //assert(IsAlignedTo(minSlack, PAGE_SIZE); 220 | //assert(minSlack <= static_cast(std::numeric_limits::max())); 221 | 222 | if (!IsInitialized()) 223 | { 224 | // Invalid allocator state 225 | return false; 226 | } 227 | 228 | if (!IsAlignedTo(minSlack, 4096) 229 | || minSlack > static_cast(std::numeric_limits::max())) 230 | { 231 | // Invalid arguments 232 | return false; 233 | } 234 | 235 | SizeType slack = m_committedSize - m_allocatedSize; 236 | 237 | // Round down decommit size to a multiple of the page size 238 | size_t decommitSize = (slack - minSlack) & ~(4096 - 1); // TODO: PAGE_SIZE 239 | 240 | if (slack > minSlack && decommitSize != 0) 241 | { 242 | void* ptr = reinterpret_cast(m_buffer); 243 | 244 | bool success = VirtualDecommit(ptr, decommitSize); 245 | //assert(success); 246 | 247 | if (success) 248 | { 249 | m_committedSize -= decommitSize; 250 | } 251 | 252 | return success; 253 | } 254 | 255 | return true; 256 | } 257 | 258 | template 259 | bool TVMemStackFrameAllocator::IsInitialized() const 260 | { 261 | return m_buffer != 0; 262 | } 263 | 264 | template 265 | size_t TVMemStackFrameAllocator::GetAllocatedSize() const 266 | { 267 | return m_allocatedSize; 268 | } 269 | 270 | template 271 | size_t TVMemStackFrameAllocator::GetCommittedSize() const 272 | { 273 | return m_committedSize; 274 | } 275 | 276 | template 277 | bool TVMemStackFrameAllocator::HasLiveFrame() const 278 | { 279 | return m_liveFrame != nullptr; 280 | } 281 | 282 | template 283 | size_t TVMemStackFrameAllocator::GetFrameOverhead() const 284 | { 285 | return sizeof(FrameDescription); 286 | } 287 | 288 | template 289 | void* TVMemStackFrameAllocator::Allocate(size_t size, size_t alignment) 290 | { 291 | //assert(IsInitialized()); 292 | //assert(size > 0); 293 | //assert(IsPowerOfTwo(alignment)); 294 | 295 | if (!IsInitialized()) 296 | { 297 | // Invalid allocator state 298 | return nullptr; 299 | } 300 | 301 | if (size == 0 || !IsPowerOfTwo(alignment)) 302 | { 303 | // Invalid arguments 304 | return nullptr; 305 | } 306 | 307 | if (!HasLiveFrame()) 308 | { 309 | // Need at least a single live frame 310 | return nullptr; 311 | } 312 | 313 | return AllocateImpl(size, alignment); 314 | } 315 | 316 | template 317 | void TVMemStackFrameAllocator::Deallocate(void* ptr, size_t size) 318 | { 319 | // Not supported, does nothing 320 | } 321 | 322 | template 323 | bool TVMemStackFrameAllocator::IsOwnerOf(void* ptr) const 324 | { 325 | //assert(IsInitialized()); 326 | 327 | if (!IsInitialized()) 328 | { 329 | // Invalid allocator state 330 | return false; 331 | } 332 | 333 | return IsPointerInBuffer(ptr, m_buffer, m_allocatedSize); 334 | } 335 | 336 | template 337 | AllocatorFrame TVMemStackFrameAllocator::PushFrame() 338 | { 339 | AllocatorFrame frame; 340 | 341 | PushImpl(this, frame); 342 | 343 | return frame; 344 | } 345 | 346 | template 347 | bool TVMemStackFrameAllocator::PopFrame(AllocatorFrame& frame) 348 | { 349 | return frame.Pop(); 350 | } 351 | 352 | template 353 | TVMemStackFrameAllocator::operator internal::AllocatorFrameFactory() 354 | { 355 | return internal::AllocatorFrameFactory(this, &PushImpl); 356 | } 357 | 358 | template 359 | void* TVMemStackFrameAllocator::AllocateImpl(size_t size, size_t alignment) 360 | { 361 | //assert(IsInitialized()); 362 | //assert(size > 0); 363 | //assert(IsPowerOfTwo(alignment)); 364 | 365 | if (!CanSatisfyAllocation(m_buffer, m_bufferSize, m_allocatedSize, size, alignment)) 366 | { 367 | // Out of memory or overflow 368 | return nullptr; 369 | } 370 | 371 | SizeType allocatedSize = m_allocatedSize; 372 | SizeType lastAllocationOffset = m_lastAllocationOffset; 373 | SizeType committedSize = m_committedSize; 374 | 375 | void* ptr = AllocateFromBuffer(m_buffer, m_bufferSize, allocatedSize, size, alignment, lastAllocationOffset); 376 | 377 | if (allocatedSize > committedSize) 378 | { 379 | // We need to commit more memory 380 | void* commitPtr = reinterpret_cast(m_buffer + committedSize); 381 | SizeType commitSize = AlignTo(allocatedSize - committedSize, 4096); // TODO: PAGE_SIZE 382 | 383 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 384 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 385 | 386 | bool success = VirtualCommit(commitPtr, commitSize, accessFlags, regionFlags); 387 | //assert(success); 388 | if (!success) 389 | { 390 | // Out of memory 391 | return nullptr; 392 | } 393 | 394 | m_committedSize = committedSize + commitSize; 395 | } 396 | 397 | m_allocatedSize = allocatedSize; 398 | m_lastAllocationOffset = lastAllocationOffset; 399 | 400 | return ptr; 401 | } 402 | 403 | template 404 | void* TVMemStackFrameAllocator::ReallocateImpl(Allocator* allocator, void* oldPtr, size_t oldSize, size_t newSize, size_t alignment) 405 | { 406 | TVMemStackFrameAllocator* allocatorImpl = static_cast*>(allocator); 407 | 408 | //assert(allocatorImpl->IsInitialized()); 409 | //assert(newSize > 0); 410 | //assert(IsPowerOfTwo(alignment)); 411 | 412 | if (!allocatorImpl->IsInitialized()) 413 | { 414 | // Invalid allocator state 415 | return nullptr; 416 | } 417 | 418 | if (newSize == 0 || !IsPowerOfTwo(alignment)) 419 | { 420 | // Invalid arguments 421 | return nullptr; 422 | } 423 | 424 | if (!allocatorImpl->HasLiveFrame()) 425 | { 426 | // Need at least a single live frame 427 | return nullptr; 428 | } 429 | 430 | // We do not support freeing 431 | SizeType lastAllocationOffset = allocatorImpl->m_lastAllocationOffset; 432 | uintptr_t lastAllocation = allocatorImpl->m_buffer + lastAllocationOffset; 433 | uintptr_t rawOldPtr = reinterpret_cast(oldPtr); 434 | 435 | if (lastAllocation == rawOldPtr) 436 | { 437 | // We are reallocating the last allocation 438 | SizeType allocatedSize = allocatorImpl->m_allocatedSize; 439 | SizeType bufferSize = allocatorImpl->m_bufferSize; 440 | 441 | // If we are shrinking the allocation, deltaSize 442 | // will be very large (negative) 443 | SizeType deltaSize = newSize - oldSize; 444 | 445 | // If deltaSize is very large (negative), we will wrap around 446 | // and newAllocatedSize should end up smaller than allocatedSize 447 | SizeType newAllocatedSize = allocatedSize + deltaSize; 448 | //assert(newAllocatedSize <= bufferSize); 449 | if (newAllocatedSize > bufferSize) 450 | { 451 | // Out of memory 452 | return nullptr; 453 | } 454 | 455 | SizeType committedSize = allocatorImpl->m_committedSize; 456 | if (newAllocatedSize > committedSize) 457 | { 458 | // We need to commit more memory 459 | void* commitPtr = reinterpret_cast(allocatorImpl->m_buffer + committedSize); 460 | SizeType commitSize = AlignTo(newAllocatedSize - committedSize, 4096); // TODO: PAGE_SIZE 461 | 462 | MemoryAccessFlags accessFlags = MemoryAccessFlags::eCPU_ReadWrite; 463 | MemoryRegionFlags regionFlags = MemoryRegionFlags::ePrivate | MemoryRegionFlags::eAnonymous; 464 | 465 | bool success = VirtualCommit(commitPtr, commitSize, accessFlags, regionFlags); 466 | //assert(success); 467 | if (!success) 468 | { 469 | // Out of memory 470 | return nullptr; 471 | } 472 | 473 | allocatorImpl->m_committedSize = committedSize + commitSize; 474 | } 475 | 476 | allocatorImpl->m_allocatedSize = newAllocatedSize; 477 | 478 | // Nothing to copy since we re-use the same memory 479 | 480 | return oldPtr; 481 | } 482 | 483 | // We do not support reallocating an arbitrary allocation 484 | // we simply perform a new allocation and copy the contents 485 | void* ptr = allocatorImpl->AllocateImpl(newSize, alignment); 486 | 487 | if (ptr != nullptr) 488 | { 489 | size_t numBytesToCopy = newSize >= oldSize ? oldSize : newSize; 490 | memcpy(ptr, oldPtr, numBytesToCopy); 491 | } 492 | 493 | return ptr; 494 | } 495 | 496 | template 497 | void TVMemStackFrameAllocator::PushImpl(Allocator* allocator, AllocatorFrame& outFrame) 498 | { 499 | //assert(allocator); 500 | 501 | TVMemStackFrameAllocator* allocatorImpl = static_cast*>(allocator); 502 | 503 | if (!allocatorImpl->IsInitialized()) 504 | { 505 | // Invalid allocator state 506 | outFrame = AllocatorFrame(); 507 | return; 508 | } 509 | 510 | void* ptr = allocatorImpl->AllocateImpl(sizeof(FrameDescription), alignof(FrameDescription)); 511 | if (ptr == nullptr) 512 | { 513 | // Failed to allocate our frame, out of memory? 514 | outFrame = AllocatorFrame(); 515 | return; 516 | } 517 | 518 | FrameDescription* frameDesc = reinterpret_cast(ptr); 519 | frameDesc->prevFrame = allocatorImpl->m_liveFrame; 520 | 521 | allocatorImpl->m_liveFrame = frameDesc; 522 | 523 | outFrame = AllocatorFrame(allocator, &PopImpl, frameDesc); 524 | } 525 | 526 | template 527 | bool TVMemStackFrameAllocator::PopImpl(Allocator* allocator, void* allocatorData) 528 | { 529 | //assert(allocator); 530 | 531 | TVMemStackFrameAllocator* allocatorImpl = static_cast*>(allocator); 532 | 533 | //assert(allocatorImpl->IsInitialized()); 534 | 535 | if (!allocatorImpl->IsInitialized()) 536 | { 537 | // Invalid allocator state 538 | return false; 539 | } 540 | 541 | const FrameDescription* frameDesc = static_cast(allocatorData); 542 | 543 | // We can only pop the top most frame 544 | //assert(frameDesc == allocatorImpl->m_liveFrame); 545 | if (frameDesc != allocatorImpl->m_liveFrame) 546 | { 547 | return false; 548 | } 549 | 550 | // Update our topmost frame 551 | allocatorImpl->m_liveFrame = frameDesc->prevFrame; 552 | 553 | // Popping is noop 554 | uintptr_t allocatedSize = reinterpret_cast(frameDesc) - allocatorImpl->m_buffer; 555 | allocatorImpl->m_allocatedSize = static_cast(allocatedSize); 556 | 557 | return true; 558 | } 559 | 560 | //////////////////////////////////////// 561 | 562 | typedef TVMemStackFrameAllocator VMemStackFrameAllocator; 563 | } 564 | 565 | #endif // GIN_VMEM_STACK_FRAME_ALLOCATOR_H 566 | 567 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | SCRIPTS=./script 2 | 3 | .PHONY: test 4 | test: 5 | python $(SCRIPTS)/test.py 6 | 7 | .PHONY: clean 8 | clean: 9 | python $(SCRIPTS)/clean.py 10 | 11 | -------------------------------------------------------------------------------- /script/clean.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import io 4 | import os 5 | import subprocess 6 | import sys 7 | 8 | GIN_ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 9 | GIN_INTERMEDIATE_DIR = os.path.join(GIN_ROOT_DIR, "intermediate") 10 | GIN_BIN_DIR = os.path.join(GIN_ROOT_DIR, "bin") 11 | 12 | def main(): 13 | subprocess.call("rm -rf " + GIN_INTERMEDIATE_DIR, shell=True) 14 | subprocess.call("rm -rf " + GIN_BIN_DIR, shell=True) 15 | 16 | if __name__ == '__main__': 17 | sys.exit(main()) 18 | 19 | -------------------------------------------------------------------------------- /script/test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import sys 5 | import subprocess 6 | 7 | GIN_ROOT_DIR = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 8 | GIN_EXT_DIR = os.path.join(GIN_ROOT_DIR, "external") 9 | GIN_INCLUDE_DIR = os.path.join(GIN_ROOT_DIR, "include") 10 | GIN_TEST_DIR = os.path.join(GIN_ROOT_DIR, "test") 11 | GIN_INTERMEDIATE_DIR = os.path.join(GIN_ROOT_DIR, "intermediate") 12 | GIN_BIN_DIR = os.path.join(GIN_ROOT_DIR, "bin") 13 | 14 | CATCH_INCLUDE_DIR = os.path.join(GIN_EXT_DIR, "catch-1.1.1") 15 | 16 | #COMPILER = "clang -x c++ -std=gnu++11 -stdlib=libc++" 17 | COMPILER = "clang++" 18 | LINKER = "clang++" 19 | WARNING_LIST = ["no-trigraphs", "no-missing-field-initializers", 20 | "no-missing-prototypes", "error=return-type", 21 | "unreachable-code", "no-non-virtual-dtor", 22 | "no-overloaded-virtual", "no-exit-time-destructors", 23 | "no-missing-braces", "parentheses", "switch", 24 | "unused-function", "no-unused-label", 25 | "no-unused-parameter", "unused-variable", 26 | "unused-value", "empty-body", "conditional-uninitialized", 27 | "no-unknown-pragmas", "no-shadow", "no-four-char-constants", 28 | "no-conversion", "constant-conversion", "int-conversion", 29 | "bool-conversion", "enum-conversion", "shorten-64-to-32", 30 | "no-newline-eof", "no-c++11-extensions", "no-sign-conversion", 31 | "deprecated-declarations", "invalid-offsetof"] 32 | FLAGS = ["no-rtti", "asm-blocks", "strict-aliasing", 33 | "visibility-inlines-hidden"] 34 | EXTRA_FLAGS = ["-std=c++11", "-c", "-g", "-DDEBUG=1", "-O0", "-MMD"] 35 | INCLUDE_DIRS = [CATCH_INCLUDE_DIR, GIN_INCLUDE_DIR] 36 | EXEC_NAME = "test" 37 | 38 | def parse_dependencies(obj): 39 | dep_path = os.path.join(GIN_INTERMEDIATE_DIR, obj.replace(".o", ".d")) 40 | if not os.path.exists(dep_path): 41 | print "No dependency file found at: {}".format(dep_path) 42 | return [] 43 | 44 | lines = [line.strip() for line in open(dep_path)] 45 | 46 | # Remove the first line which references the object file itself 47 | del lines[0] 48 | 49 | # Strip tailing ' \' stuff 50 | lines = [line.rstrip(" \\") for line in lines] 51 | 52 | return lines 53 | 54 | def should_compile(obj): 55 | obj_path = os.path.join(GIN_INTERMEDIATE_DIR, obj) 56 | if not os.path.exists(obj_path): 57 | # Object file doesn't exist, build it 58 | return True 59 | 60 | obj_mtime = os.path.getmtime(obj_path) 61 | 62 | deps = parse_dependencies(obj) 63 | for dep_path in deps: 64 | if not os.path.exists(dep_path): 65 | # Dependency file doesn't exist, assume we are dirty 66 | return True 67 | 68 | dep_mtime = os.path.getmtime(dep_path) 69 | if dep_mtime > obj_mtime: 70 | # Dependency modify time is newer than object modify time, we are dirty 71 | return True 72 | 73 | return False 74 | 75 | def main(): 76 | #print "gin root: %s" % GIN_ROOT_DIR 77 | #print "gin external: %s" % GIN_EXT_DIR 78 | #print "gin include: %s" % GIN_INCLUDE_DIR 79 | #print "gin test: %s" % GIN_TEST_DIR 80 | 81 | # Make sure output directories exists 82 | if not os.path.exists(GIN_BIN_DIR): 83 | os.makedirs(GIN_BIN_DIR) 84 | 85 | if not os.path.exists(GIN_INTERMEDIATE_DIR): 86 | os.makedirs(GIN_INTERMEDIATE_DIR) 87 | 88 | test_sources = [file for file in os.listdir(GIN_TEST_DIR) if os.path.isfile(os.path.join(GIN_TEST_DIR, file)) and file.endswith(".cpp")] 89 | 90 | test_obj = [file.replace(".cpp", ".o") for file in test_sources] 91 | 92 | test_exec_list = zip(test_sources, test_obj) 93 | 94 | warnings = " ".join(["-W{0}".format(item) for item in WARNING_LIST]) 95 | flags = " ".join(["-f{0}".format(item) for item in FLAGS]) 96 | extra_flags = " ".join(EXTRA_FLAGS) 97 | include_dirs = " ".join(["-I{0}".format(item) for item in INCLUDE_DIRS]) 98 | 99 | obj_list = [] 100 | compilation_failed = False 101 | is_exec_dirty = False 102 | 103 | # Compile everything into object files 104 | for src, obj in test_exec_list: 105 | src_path = os.path.join(GIN_TEST_DIR, src) 106 | obj_path = os.path.join(GIN_INTERMEDIATE_DIR, obj) 107 | obj_cmd = "-o " + obj_path 108 | 109 | obj_list.append(obj_path) 110 | 111 | if not should_compile(obj): 112 | print "Skipping '%s'..." % src_path 113 | continue 114 | 115 | compile_list = [COMPILER, warnings, flags, extra_flags, 116 | include_dirs, src_path, obj_cmd] 117 | compile_cmd = " ".join(compile_list) 118 | 119 | #print compile_cmd 120 | print "Compiling '%s'..." % src_path 121 | 122 | result = subprocess.call(compile_cmd, shell=True) 123 | compilation_failed = compilation_failed or (result != 0) 124 | is_exec_dirty = True 125 | 126 | if compilation_failed: 127 | return -1 128 | 129 | exec_path = os.path.join(GIN_BIN_DIR, EXEC_NAME) 130 | 131 | # Link everything 132 | if is_exec_dirty: 133 | objs = " ".join(obj_list) 134 | exec_out_cmd = "-o " + exec_path 135 | link_list = [LINKER, objs, exec_out_cmd] 136 | link_cmd = " ".join(link_list) 137 | 138 | #print link_cmd 139 | print "Linking '%s'..." % exec_path 140 | 141 | result = subprocess.call(link_cmd, shell=True) 142 | 143 | if result != 0: 144 | return -1 145 | 146 | # Execute 147 | print "Executing '%s'..." % exec_path 148 | 149 | subprocess.call(exec_path) 150 | 151 | if __name__ == '__main__': 152 | sys.exit(main()) 153 | 154 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include "catch.hpp" 3 | 4 | // Nothing here yet, just the entry point 5 | 6 | -------------------------------------------------------------------------------- /test/test_linear_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "gin/linear_allocator.h" 4 | #include "gin/utils.h" 5 | 6 | TEST_CASE("allocate and free from given buffer", "[LinearAllocator]") 7 | { 8 | using namespace gin; 9 | 10 | const size_t BUFFER_SIZE = 1024; 11 | 12 | uint8_t buffer[BUFFER_SIZE]; 13 | 14 | LinearAllocator alloc(&buffer[0], BUFFER_SIZE); 15 | 16 | REQUIRE(alloc.IsInitialized()); 17 | REQUIRE(alloc.GetAllocatedSize() == 0); 18 | 19 | SECTION("test IsOwnerOf()") 20 | { 21 | REQUIRE(!alloc.IsOwnerOf(nullptr)); 22 | REQUIRE(!alloc.IsOwnerOf(&buffer[0])); 23 | REQUIRE(!alloc.IsOwnerOf(&buffer[0] + 32768)); 24 | 25 | uint8_t* ptr0 = static_cast(alloc.Allocate(2, 1)); 26 | if (ptr0) memset(ptr0, 0xcd, 2); 27 | 28 | REQUIRE(alloc.IsOwnerOf(ptr0)); 29 | REQUIRE(alloc.IsOwnerOf(ptr0 + 1)); 30 | REQUIRE(!alloc.IsOwnerOf(ptr0 + 2)); 31 | } 32 | 33 | SECTION("test allocation") 34 | { 35 | void* ptr0 = alloc.Allocate(2, 1); 36 | if (ptr0) memset(ptr0, 0xcd, 2); 37 | 38 | REQUIRE(ptr0 == &buffer[0]); 39 | REQUIRE(alloc.IsOwnerOf(ptr0)); 40 | REQUIRE(alloc.GetAllocatedSize() == 2); 41 | 42 | void* ptr1 = alloc.Allocate(1022, 1); 43 | if (ptr1) memset(ptr1, 0xcd, 1022); 44 | 45 | REQUIRE(alloc.IsOwnerOf(ptr1)); 46 | REQUIRE(alloc.GetAllocatedSize() == 1024); 47 | REQUIRE(ptr0 != ptr1); 48 | 49 | void* ptr2 = alloc.Allocate(1, 1); 50 | if (ptr2) memset(ptr2, 0xcd, 1); 51 | 52 | REQUIRE(ptr2 == nullptr); 53 | REQUIRE(alloc.GetAllocatedSize() == 1024); 54 | } 55 | 56 | SECTION("test alignment") 57 | { 58 | uintptr_t bufferHead = reinterpret_cast(&buffer[0]); 59 | size_t allocatedSize = 0; 60 | 61 | void* ptr0 = alloc.Allocate(2, 8); 62 | if (ptr0) memset(ptr0, 0xcd, 2); 63 | size_t ptr0Size = AlignTo(bufferHead + allocatedSize, 8) - (bufferHead + allocatedSize) + 2; 64 | 65 | allocatedSize += ptr0Size; 66 | 67 | REQUIRE(alloc.IsOwnerOf(ptr0)); 68 | REQUIRE(alloc.GetAllocatedSize() == allocatedSize); 69 | REQUIRE(IsAlignedTo(ptr0, 8)); 70 | 71 | void* ptr1 = alloc.Allocate(2, 16); 72 | if (ptr1) memset(ptr1, 0xcd, 2); 73 | size_t ptr1Size = AlignTo(bufferHead + allocatedSize, 16) - (bufferHead + allocatedSize) + 2; 74 | 75 | allocatedSize += ptr1Size; 76 | 77 | REQUIRE(alloc.IsOwnerOf(ptr1)); 78 | REQUIRE(alloc.GetAllocatedSize() == allocatedSize); 79 | REQUIRE(IsAlignedTo(ptr1, 16)); 80 | REQUIRE(ptr0 != ptr1); 81 | } 82 | 83 | SECTION("test realloc") 84 | { 85 | void* ptr0 = alloc.Allocate(2, 1); 86 | if (ptr0) memset(ptr0, 0xcd, 2); 87 | 88 | void* ptr1 = alloc.Reallocate(ptr0, 2, 8, 1); 89 | if (ptr1) memset(ptr1, 0xcd, 8); 90 | 91 | REQUIRE(ptr0 == ptr1); 92 | REQUIRE(alloc.GetAllocatedSize() == 8); 93 | 94 | void* ptr2 = alloc.Reallocate(nullptr, 0, 4, 1); 95 | if (ptr2) memset(ptr2, 0xcd, 4); 96 | 97 | REQUIRE(ptr0 != ptr2); 98 | REQUIRE(alloc.GetAllocatedSize() == 12); 99 | 100 | void* ptr3 = alloc.Reallocate(ptr0, 8, 12, 1); 101 | if (ptr3) memset(ptr3, 0xcd, 12); 102 | 103 | REQUIRE(ptr0 != ptr3); 104 | REQUIRE(ptr2 != ptr3); 105 | REQUIRE(alloc.GetAllocatedSize() == 24); 106 | 107 | void* ptr4 = alloc.Reallocate(ptr3, 12, 4, 1); 108 | if (ptr4) memset(ptr4, 0xcd, 4); 109 | 110 | REQUIRE(ptr3 == ptr4); 111 | REQUIRE(alloc.GetAllocatedSize() == 16); 112 | 113 | void* ptr5 = alloc.Reallocate(ptr4, 4, 128 * 1024, 1); 114 | if (ptr5) memset(ptr5, 0xcd, 128 * 1024); 115 | 116 | REQUIRE(ptr5 == nullptr); 117 | REQUIRE(alloc.GetAllocatedSize() == 16); 118 | } 119 | 120 | SECTION("test nop free") 121 | { 122 | void* ptr0 = alloc.Allocate(2, 1); 123 | if (ptr0) memset(ptr0, 0xcd, 2); 124 | 125 | REQUIRE(alloc.GetAllocatedSize() == 2); 126 | 127 | alloc.Deallocate(ptr0, 2); 128 | 129 | REQUIRE(alloc.GetAllocatedSize() == 2); 130 | 131 | void* ptr1 = alloc.Allocate(2, 1); 132 | if (ptr1) memset(ptr1, 0xcd, 2); 133 | 134 | REQUIRE(ptr0 != ptr1); 135 | REQUIRE(alloc.GetAllocatedSize() == 4); 136 | } 137 | 138 | SECTION("test reset") 139 | { 140 | void* ptr0 = alloc.Allocate(2, 1); 141 | if (ptr0) memset(ptr0, 0xcd, 2); 142 | 143 | REQUIRE(alloc.GetAllocatedSize() == 2); 144 | 145 | alloc.Reset(); 146 | 147 | REQUIRE(alloc.GetAllocatedSize() == 0); 148 | 149 | void* ptr1 = alloc.Allocate(2, 1); 150 | if (ptr1) memset(ptr1, 0xcd, 2); 151 | 152 | REQUIRE(alloc.GetAllocatedSize() == 2); 153 | REQUIRE(ptr0 == ptr1); 154 | } 155 | } 156 | 157 | TEST_CASE("test invalid arguments", "[LinearAllocator]") 158 | { 159 | using namespace gin; 160 | 161 | SECTION("test initialization") 162 | { 163 | LinearAllocator alloc; 164 | 165 | REQUIRE(!alloc.IsInitialized()); 166 | 167 | alloc.Initialize(nullptr, 1024); 168 | 169 | REQUIRE(!alloc.IsInitialized()); 170 | 171 | uint8_t buffer[1]; 172 | alloc.Initialize(&buffer[0], 0); 173 | 174 | REQUIRE(!alloc.IsInitialized()); 175 | } 176 | 177 | SECTION("test out of memory") 178 | { 179 | const size_t BUFFER_SIZE = 1024; 180 | 181 | uint8_t buffer[BUFFER_SIZE]; 182 | 183 | LinearAllocator alloc(&buffer[0], BUFFER_SIZE); 184 | 185 | REQUIRE(alloc.IsInitialized()); 186 | REQUIRE(alloc.GetAllocatedSize() == 0); 187 | 188 | void* ptr0 = alloc.Allocate(BUFFER_SIZE + 1, 1); 189 | if (ptr0) memset(ptr0, 0xcd, BUFFER_SIZE + 1); 190 | 191 | REQUIRE(ptr0 == nullptr); 192 | } 193 | 194 | SECTION("test alignment overflow") 195 | { 196 | void* buffer = reinterpret_cast(~0u - 8); 197 | 198 | LinearAllocator alloc(buffer, 8); 199 | 200 | REQUIRE(alloc.IsInitialized()); 201 | REQUIRE(alloc.GetAllocatedSize() == 0); 202 | 203 | void* ptr0 = alloc.Allocate(1, 16); 204 | if (ptr0) memset(ptr0, 0xcd, 1); 205 | 206 | REQUIRE(ptr0 == nullptr); 207 | } 208 | 209 | SECTION("test size overflow") 210 | { 211 | void* buffer = reinterpret_cast(~0u - 8); 212 | 213 | LinearAllocator alloc(buffer, 8); 214 | 215 | REQUIRE(alloc.IsInitialized()); 216 | REQUIRE(alloc.GetAllocatedSize() == 0); 217 | 218 | void* ptr0 = alloc.Allocate(32, 1); 219 | if (ptr0) memset(ptr0, 0xcd, 32); 220 | 221 | REQUIRE(ptr0 == nullptr); 222 | } 223 | } 224 | 225 | -------------------------------------------------------------------------------- /test/test_stack_frame_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "gin/stack_frame_allocator.h" 4 | #include "gin/utils.h" 5 | 6 | TEST_CASE("allocate and free from stack frame allocator", "[StackFrameAllocator]") 7 | { 8 | using namespace gin; 9 | 10 | const size_t SEGMENT_SIZE = 1024; 11 | 12 | StackFrameAllocator alloc(SEGMENT_SIZE); 13 | 14 | size_t frameOverhead = alloc.GetFrameOverhead(); 15 | 16 | REQUIRE(alloc.IsInitialized()); 17 | REQUIRE(alloc.GetAllocatedSize() == 0); 18 | REQUIRE(!alloc.HasLiveFrame()); 19 | 20 | SECTION("test frame push/pop") 21 | { 22 | { 23 | AllocatorFrame frame = alloc.PushFrame(); 24 | 25 | REQUIRE(frame.CanPop()); 26 | REQUIRE(alloc.HasLiveFrame()); 27 | 28 | // Pop manually 29 | frame.Pop(); 30 | 31 | REQUIRE(!frame.CanPop()); 32 | REQUIRE(!alloc.HasLiveFrame()); 33 | } 34 | 35 | REQUIRE(!alloc.HasLiveFrame()); 36 | 37 | { 38 | AllocatorFrame frame(alloc); 39 | 40 | REQUIRE(frame.CanPop()); 41 | REQUIRE(alloc.HasLiveFrame()); 42 | 43 | // Pop automatically with the destructor 44 | } 45 | 46 | REQUIRE(!alloc.HasLiveFrame()); 47 | REQUIRE(alloc.GetAllocatedSize() == 0); 48 | } 49 | 50 | SECTION("test IsOwnerOf()") 51 | { 52 | uint8_t* ptr0; 53 | 54 | { 55 | AllocatorFrame frame(alloc); 56 | 57 | REQUIRE(!alloc.IsOwnerOf(nullptr)); 58 | 59 | ptr0 = static_cast(alloc.Allocate(2, 1)); 60 | if (ptr0) memset(ptr0, 0xcd, 2); 61 | 62 | REQUIRE(alloc.IsOwnerOf(ptr0)); 63 | REQUIRE(alloc.IsOwnerOf(ptr0 + 1)); 64 | REQUIRE(!alloc.IsOwnerOf(ptr0 + 2)); 65 | } 66 | 67 | REQUIRE(!alloc.IsOwnerOf(ptr0)); 68 | REQUIRE(alloc.GetAllocatedSize() == 0); 69 | } 70 | 71 | SECTION("test allocation") 72 | { 73 | { 74 | AllocatorFrame frame(alloc); 75 | 76 | void* ptr0 = alloc.Allocate(2, 1); 77 | if (ptr0) memset(ptr0, 0xcd, 2); 78 | 79 | REQUIRE(alloc.IsOwnerOf(ptr0)); 80 | REQUIRE(alloc.GetAllocatedSize() == 2 + frameOverhead); 81 | 82 | void* ptr1 = alloc.Allocate(1022, 1); 83 | if (ptr1) memset(ptr1, 0xcd, 1022); 84 | 85 | REQUIRE(alloc.IsOwnerOf(ptr1)); 86 | REQUIRE(alloc.GetAllocatedSize() == 1024 + frameOverhead); 87 | REQUIRE(ptr0 != ptr1); 88 | 89 | void* ptr2 = alloc.Allocate(2048, 1); 90 | if (ptr2) memset(ptr2, 0xcd, 2048); 91 | 92 | REQUIRE(alloc.IsOwnerOf(ptr2)); 93 | REQUIRE(alloc.GetAllocatedSize() == 1024 + 2048 + frameOverhead); 94 | REQUIRE(ptr1 != ptr2); 95 | } 96 | 97 | REQUIRE(alloc.GetAllocatedSize() == 0); 98 | } 99 | 100 | SECTION("test alignment") 101 | { 102 | AllocatorFrame frame(alloc); 103 | 104 | void* ptr0 = alloc.Allocate(2, 8); 105 | if (ptr0) memset(ptr0, 0xcd, 2); 106 | 107 | REQUIRE(alloc.IsOwnerOf(ptr0)); 108 | REQUIRE(IsAlignedTo(ptr0, 8)); 109 | 110 | void* ptr1 = alloc.Allocate(2, 16); 111 | if (ptr1) memset(ptr1, 0xcd, 2); 112 | 113 | REQUIRE(alloc.IsOwnerOf(ptr1)); 114 | REQUIRE(IsAlignedTo(ptr1, 16)); 115 | REQUIRE(ptr0 != ptr1); 116 | } 117 | 118 | SECTION("test realloc") 119 | { 120 | AllocatorFrame frame(alloc); 121 | 122 | void* ptr0 = alloc.Allocate(2, 1); 123 | if (ptr0) memset(ptr0, 0xcd, 2); 124 | 125 | void* ptr1 = alloc.Reallocate(ptr0, 2, 8, 1); 126 | if (ptr1) memset(ptr1, 0xcd, 8); 127 | 128 | REQUIRE(ptr0 == ptr1); 129 | REQUIRE(alloc.GetAllocatedSize() == 8 + frameOverhead); 130 | 131 | void* ptr2 = alloc.Reallocate(nullptr, 0, 4, 1); 132 | if (ptr2) memset(ptr2, 0xcd, 4); 133 | 134 | REQUIRE(ptr0 != ptr2); 135 | REQUIRE(alloc.GetAllocatedSize() == 12 + frameOverhead); 136 | 137 | void* ptr3 = alloc.Reallocate(ptr0, 8, 12, 1); 138 | if (ptr3) memset(ptr3, 0xcd, 12); 139 | 140 | REQUIRE(ptr0 != ptr3); 141 | REQUIRE(ptr2 != ptr3); 142 | REQUIRE(alloc.GetAllocatedSize() == 24 + frameOverhead); 143 | 144 | void* ptr4 = alloc.Reallocate(ptr3, 12, 4, 1); 145 | if (ptr4) memset(ptr4, 0xcd, 4); 146 | 147 | REQUIRE(ptr3 == ptr4); 148 | REQUIRE(alloc.GetAllocatedSize() == 16 + frameOverhead); 149 | 150 | void* ptr5 = alloc.Reallocate(ptr4, 4, 128 * 1024, 1); 151 | if (ptr5) memset(ptr5, 0xcd, 128 * 1024); 152 | 153 | REQUIRE(ptr4 != ptr5); 154 | REQUIRE(alloc.GetAllocatedSize() == 128 * 1024 + 16 + frameOverhead); 155 | } 156 | 157 | SECTION("test nop free") 158 | { 159 | AllocatorFrame frame(alloc); 160 | 161 | void* ptr0 = alloc.Allocate(2, 1); 162 | if (ptr0) memset(ptr0, 0xcd, 2); 163 | 164 | REQUIRE(alloc.GetAllocatedSize() == 2 + frameOverhead); 165 | 166 | alloc.Deallocate(ptr0, 2); 167 | 168 | REQUIRE(alloc.GetAllocatedSize() == 2 + frameOverhead); 169 | 170 | void* ptr1 = alloc.Allocate(2, 1); 171 | if (ptr1) memset(ptr1, 0xcd, 2); 172 | 173 | REQUIRE(ptr0 != ptr1); 174 | REQUIRE(alloc.GetAllocatedSize() == 4 + frameOverhead); 175 | } 176 | } 177 | 178 | TEST_CASE("test invalid arguments in stack frame allocator", "[StackFrameAllocator]") 179 | { 180 | using namespace gin; 181 | 182 | SECTION("test initialization") 183 | { 184 | StackFrameAllocator alloc; 185 | 186 | REQUIRE(!alloc.IsInitialized()); 187 | 188 | alloc.Initialize(0); 189 | 190 | REQUIRE(!alloc.IsInitialized()); 191 | } 192 | } 193 | 194 | -------------------------------------------------------------------------------- /test/test_vmem_linear_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "gin/vmem_linear_allocator.h" 4 | #include "gin/utils.h" 5 | 6 | TEST_CASE("allocate and free from VMemLinearAllocator", "[VMemLinearAllocator]") 7 | { 8 | using namespace gin; 9 | 10 | const size_t BUFFER_SIZE = 64 * 1024; 11 | 12 | VMemLinearAllocator alloc(BUFFER_SIZE); 13 | 14 | REQUIRE(alloc.IsInitialized()); 15 | REQUIRE(alloc.GetAllocatedSize() == 0); 16 | REQUIRE(alloc.GetCommittedSize() == 0); 17 | 18 | SECTION("test IsOwnerOf()") 19 | { 20 | REQUIRE(!alloc.IsOwnerOf(nullptr)); 21 | 22 | uint8_t* ptr0 = static_cast(alloc.Allocate(2, 1)); 23 | if (ptr0) memset(ptr0, 0xcd, 2); 24 | 25 | REQUIRE(alloc.IsOwnerOf(ptr0)); 26 | REQUIRE(alloc.IsOwnerOf(ptr0 + 1)); 27 | REQUIRE(!alloc.IsOwnerOf(ptr0 + 2)); 28 | } 29 | 30 | SECTION("test allocation") 31 | { 32 | void* ptr0 = alloc.Allocate(2, 1); 33 | if (ptr0) memset(ptr0, 0xcd, 2); 34 | 35 | REQUIRE(alloc.IsOwnerOf(ptr0)); 36 | REQUIRE(alloc.GetAllocatedSize() == 2); 37 | REQUIRE(alloc.GetCommittedSize() == 4 * 1024); 38 | 39 | void* ptr1 = alloc.Allocate(BUFFER_SIZE - 2, 1); 40 | if (ptr1) memset(ptr1, 0xcd, BUFFER_SIZE - 2); 41 | 42 | REQUIRE(alloc.IsOwnerOf(ptr1)); 43 | REQUIRE(alloc.GetAllocatedSize() == BUFFER_SIZE); 44 | REQUIRE(alloc.GetCommittedSize() == BUFFER_SIZE); 45 | REQUIRE(ptr0 != ptr1); 46 | 47 | void* ptr2 = alloc.Allocate(1, 1); 48 | if (ptr2) memset(ptr2, 0xcd, 1); 49 | 50 | REQUIRE(ptr2 == nullptr); 51 | REQUIRE(alloc.GetAllocatedSize() == BUFFER_SIZE); 52 | REQUIRE(alloc.GetCommittedSize() == BUFFER_SIZE); 53 | } 54 | 55 | SECTION("test alignment") 56 | { 57 | void* ptr0 = alloc.Allocate(2, 8); 58 | if (ptr0) memset(ptr0, 0xcd, 2); 59 | 60 | REQUIRE(alloc.IsOwnerOf(ptr0)); 61 | REQUIRE(IsAlignedTo(ptr0, 8)); 62 | 63 | void* ptr1 = alloc.Allocate(2, 16); 64 | if (ptr1) memset(ptr1, 0xcd, 2); 65 | 66 | REQUIRE(alloc.IsOwnerOf(ptr1)); 67 | REQUIRE(IsAlignedTo(ptr1, 16)); 68 | REQUIRE(ptr0 != ptr1); 69 | } 70 | 71 | SECTION("test realloc") 72 | { 73 | void* ptr0 = alloc.Allocate(2, 1); 74 | if (ptr0) memset(ptr0, 0xcd, 2); 75 | 76 | void* ptr1 = alloc.Reallocate(ptr0, 2, 8, 1); 77 | if (ptr1) memset(ptr1, 0xcd, 8); 78 | 79 | REQUIRE(ptr0 == ptr1); 80 | REQUIRE(alloc.GetAllocatedSize() == 8); 81 | 82 | void* ptr2 = alloc.Reallocate(nullptr, 0, 4, 1); 83 | if (ptr2) memset(ptr2, 0xcd, 4); 84 | 85 | REQUIRE(ptr0 != ptr2); 86 | REQUIRE(alloc.GetAllocatedSize() == 12); 87 | 88 | void* ptr3 = alloc.Reallocate(ptr0, 8, 12, 1); 89 | if (ptr3) memset(ptr3, 0xcd, 12); 90 | 91 | REQUIRE(ptr0 != ptr3); 92 | REQUIRE(ptr2 != ptr3); 93 | REQUIRE(alloc.GetAllocatedSize() == 24); 94 | 95 | void* ptr4 = alloc.Reallocate(ptr3, 12, 4, 1); 96 | if (ptr4) memset(ptr4, 0xcd, 4); 97 | 98 | REQUIRE(ptr3 == ptr4); 99 | REQUIRE(alloc.GetAllocatedSize() == 16); 100 | 101 | void* ptr5 = alloc.Reallocate(ptr4, 4, 128 * 1024, 1); 102 | if (ptr5) memset(ptr5, 0xcd, 128 * 1024); 103 | 104 | REQUIRE(ptr5 == nullptr); 105 | REQUIRE(alloc.GetAllocatedSize() == 16); 106 | } 107 | 108 | SECTION("test nop free") 109 | { 110 | void* ptr0 = alloc.Allocate(2, 1); 111 | if (ptr0) memset(ptr0, 0xcd, 2); 112 | 113 | REQUIRE(alloc.GetAllocatedSize() == 2); 114 | REQUIRE(alloc.GetCommittedSize() == 4 * 1024); 115 | 116 | alloc.Deallocate(ptr0, 2); 117 | 118 | REQUIRE(alloc.GetAllocatedSize() == 2); 119 | REQUIRE(alloc.GetCommittedSize() == 4 * 1024); 120 | 121 | void* ptr1 = alloc.Allocate(2, 1); 122 | if (ptr1) memset(ptr1, 0xcd, 2); 123 | 124 | REQUIRE(ptr0 != ptr1); 125 | REQUIRE(alloc.GetAllocatedSize() == 4); 126 | REQUIRE(alloc.GetCommittedSize() == 4 * 1024); 127 | } 128 | 129 | SECTION("test reset") 130 | { 131 | void* ptr0 = alloc.Allocate(2, 1); 132 | if (ptr0) memset(ptr0, 0xcd, 2); 133 | 134 | REQUIRE(alloc.GetAllocatedSize() == 2); 135 | REQUIRE(alloc.GetCommittedSize() == 4 * 1024); 136 | 137 | alloc.Reset(); 138 | 139 | REQUIRE(alloc.GetAllocatedSize() == 0); 140 | REQUIRE(alloc.GetCommittedSize() == 0); 141 | 142 | void* ptr1 = alloc.Allocate(2, 1); 143 | if (ptr1) memset(ptr1, 0xcd, 2); 144 | 145 | REQUIRE(alloc.GetAllocatedSize() == 2); 146 | REQUIRE(alloc.GetCommittedSize() == 4 * 1024); 147 | REQUIRE(ptr0 == ptr1); 148 | } 149 | } 150 | 151 | TEST_CASE("test invalid arguments to VMemLinearAllocator", "[VMemLinearAllocator]") 152 | { 153 | using namespace gin; 154 | 155 | SECTION("test initialization") 156 | { 157 | VMemLinearAllocator alloc; 158 | 159 | REQUIRE(!alloc.IsInitialized()); 160 | 161 | alloc.Initialize(1024); 162 | 163 | REQUIRE(!alloc.IsInitialized()); 164 | } 165 | 166 | SECTION("test out of memory") 167 | { 168 | const size_t BUFFER_SIZE = 64 * 1024; 169 | 170 | VMemLinearAllocator alloc(BUFFER_SIZE); 171 | 172 | REQUIRE(alloc.IsInitialized()); 173 | REQUIRE(alloc.GetAllocatedSize() == 0); 174 | REQUIRE(alloc.GetCommittedSize() == 0); 175 | 176 | void* ptr0 = alloc.Allocate(BUFFER_SIZE + 1, 1); 177 | if (ptr0) memset(ptr0, 0xcd, 2); 178 | 179 | REQUIRE(ptr0 == nullptr); 180 | REQUIRE(alloc.GetAllocatedSize() == 0); 181 | REQUIRE(alloc.GetCommittedSize() == 0); 182 | } 183 | 184 | SECTION("test alignment overflow") 185 | { 186 | } 187 | 188 | SECTION("test size overflow") 189 | { 190 | } 191 | } 192 | 193 | -------------------------------------------------------------------------------- /test/test_vmem_stack_frame_allocator.cpp: -------------------------------------------------------------------------------- 1 | #include "catch.hpp" 2 | 3 | #include "gin/vmem_stack_frame_allocator.h" 4 | #include "gin/utils.h" 5 | 6 | TEST_CASE("allocate and free from vmem stack frame allocator", "[VMemStackFrameAllocator]") 7 | { 8 | using namespace gin; 9 | 10 | const size_t BUFFER_SIZE = 16 * 1024; 11 | 12 | VMemStackFrameAllocator alloc(BUFFER_SIZE); 13 | 14 | size_t frameOverhead = alloc.GetFrameOverhead(); 15 | 16 | REQUIRE(alloc.IsInitialized()); 17 | REQUIRE(alloc.GetAllocatedSize() == 0); 18 | REQUIRE(!alloc.HasLiveFrame()); 19 | 20 | SECTION("test frame push/pop") 21 | { 22 | { 23 | AllocatorFrame frame = alloc.PushFrame(); 24 | 25 | REQUIRE(frame.CanPop()); 26 | REQUIRE(alloc.HasLiveFrame()); 27 | 28 | // Pop manually 29 | frame.Pop(); 30 | 31 | REQUIRE(!frame.CanPop()); 32 | REQUIRE(!alloc.HasLiveFrame()); 33 | } 34 | 35 | REQUIRE(!alloc.HasLiveFrame()); 36 | 37 | { 38 | AllocatorFrame frame(alloc); 39 | 40 | REQUIRE(frame.CanPop()); 41 | REQUIRE(alloc.HasLiveFrame()); 42 | 43 | // Pop automatically with the destructor 44 | } 45 | 46 | REQUIRE(!alloc.HasLiveFrame()); 47 | REQUIRE(alloc.GetAllocatedSize() == 0); 48 | } 49 | 50 | SECTION("test IsOwnerOf()") 51 | { 52 | uint8_t* ptr0; 53 | 54 | { 55 | AllocatorFrame frame(alloc); 56 | 57 | REQUIRE(!alloc.IsOwnerOf(nullptr)); 58 | 59 | ptr0 = static_cast(alloc.Allocate(2, 1)); 60 | if (ptr0) memset(ptr0, 0xcd, 2); 61 | 62 | REQUIRE(alloc.IsOwnerOf(ptr0)); 63 | REQUIRE(alloc.IsOwnerOf(ptr0 + 1)); 64 | REQUIRE(!alloc.IsOwnerOf(ptr0 + 2)); 65 | } 66 | 67 | REQUIRE(!alloc.IsOwnerOf(ptr0)); 68 | REQUIRE(alloc.GetAllocatedSize() == 0); 69 | } 70 | 71 | SECTION("test allocation") 72 | { 73 | { 74 | AllocatorFrame frame(alloc); 75 | 76 | void* ptr0 = alloc.Allocate(2, 1); 77 | if (ptr0) memset(ptr0, 0xcd, 2); 78 | 79 | REQUIRE(alloc.IsOwnerOf(ptr0)); 80 | REQUIRE(alloc.GetAllocatedSize() == 2 + frameOverhead); 81 | 82 | void* ptr1 = alloc.Allocate(1022, 1); 83 | if (ptr1) memset(ptr1, 0xcd, 1022); 84 | 85 | REQUIRE(alloc.IsOwnerOf(ptr1)); 86 | REQUIRE(alloc.GetAllocatedSize() == 1024 + frameOverhead); 87 | REQUIRE(ptr0 != ptr1); 88 | 89 | void* ptr2 = alloc.Allocate(2048, 1); 90 | if (ptr2) memset(ptr2, 0xcd, 2048); 91 | 92 | REQUIRE(alloc.IsOwnerOf(ptr2)); 93 | REQUIRE(alloc.GetAllocatedSize() == 1024 + 2048 + frameOverhead); 94 | REQUIRE(ptr1 != ptr2); 95 | } 96 | 97 | REQUIRE(alloc.GetAllocatedSize() == 0); 98 | } 99 | 100 | SECTION("test commit/decommit") 101 | { 102 | { 103 | AllocatorFrame frame(alloc); 104 | 105 | void* ptr0 = alloc.Allocate(1024, 1); 106 | if (ptr0) memset(ptr0, 0xcd, 1024); 107 | 108 | REQUIRE(alloc.GetCommittedSize() == 4096); 109 | 110 | void* ptr1 = alloc.Allocate(8192, 1); 111 | if (ptr1) memset(ptr1, 0xcd, 8192); 112 | 113 | REQUIRE(alloc.GetCommittedSize() == 4096 * 3); 114 | } 115 | 116 | alloc.DecommitSlack(8192); 117 | 118 | REQUIRE(alloc.GetCommittedSize() == 8192); 119 | 120 | alloc.DecommitSlack(0); 121 | 122 | REQUIRE(alloc.GetCommittedSize() == 0); 123 | 124 | { 125 | AllocatorFrame frame(alloc); 126 | 127 | void* ptr0 = alloc.Allocate(1024, 1); 128 | if (ptr0) memset(ptr0, 0xcd, 1024); 129 | 130 | REQUIRE(ptr0 != nullptr); 131 | REQUIRE(alloc.IsOwnerOf(ptr0)); 132 | REQUIRE(alloc.GetAllocatedSize() == 1024 + frameOverhead); 133 | } 134 | } 135 | 136 | SECTION("test alignment") 137 | { 138 | AllocatorFrame frame(alloc); 139 | 140 | void* ptr0 = alloc.Allocate(2, 8); 141 | if (ptr0) memset(ptr0, 0xcd, 2); 142 | 143 | REQUIRE(alloc.IsOwnerOf(ptr0)); 144 | REQUIRE(IsAlignedTo(ptr0, 8)); 145 | 146 | void* ptr1 = alloc.Allocate(2, 16); 147 | if (ptr1) memset(ptr1, 0xcd, 2); 148 | 149 | REQUIRE(alloc.IsOwnerOf(ptr1)); 150 | REQUIRE(IsAlignedTo(ptr1, 16)); 151 | REQUIRE(ptr0 != ptr1); 152 | } 153 | 154 | SECTION("test realloc") 155 | { 156 | AllocatorFrame frame(alloc); 157 | 158 | void* ptr0 = alloc.Allocate(2, 1); 159 | if (ptr0) memset(ptr0, 0xcd, 2); 160 | 161 | void* ptr1 = alloc.Reallocate(ptr0, 2, 8, 1); 162 | if (ptr1) memset(ptr1, 0xcd, 8); 163 | 164 | REQUIRE(ptr0 == ptr1); 165 | REQUIRE(alloc.GetAllocatedSize() == 8 + frameOverhead); 166 | 167 | void* ptr2 = alloc.Reallocate(nullptr, 0, 4, 1); 168 | if (ptr2) memset(ptr2, 0xcd, 4); 169 | 170 | REQUIRE(ptr0 != ptr2); 171 | REQUIRE(alloc.GetAllocatedSize() == 12 + frameOverhead); 172 | 173 | void* ptr3 = alloc.Reallocate(ptr0, 8, 12, 1); 174 | if (ptr3) memset(ptr3, 0xcd, 12); 175 | 176 | REQUIRE(ptr0 != ptr3); 177 | REQUIRE(ptr2 != ptr3); 178 | REQUIRE(alloc.GetAllocatedSize() == 24 + frameOverhead); 179 | 180 | void* ptr4 = alloc.Reallocate(ptr3, 12, 4, 1); 181 | if (ptr4) memset(ptr4, 0xcd, 4); 182 | 183 | REQUIRE(ptr3 == ptr4); 184 | REQUIRE(alloc.GetAllocatedSize() == 16 + frameOverhead); 185 | 186 | void* ptr5 = alloc.Reallocate(ptr4, 4, 128 * 1024, 1); 187 | if (ptr5) memset(ptr5, 0xcd, 128 * 1024); 188 | 189 | REQUIRE(ptr5 == nullptr); 190 | REQUIRE(alloc.GetAllocatedSize() == 16 + frameOverhead); 191 | } 192 | 193 | SECTION("test nop free") 194 | { 195 | AllocatorFrame frame(alloc); 196 | 197 | void* ptr0 = alloc.Allocate(2, 1); 198 | if (ptr0) memset(ptr0, 0xcd, 2); 199 | 200 | REQUIRE(alloc.GetAllocatedSize() == 2 + frameOverhead); 201 | 202 | alloc.Deallocate(ptr0, 2); 203 | 204 | REQUIRE(alloc.GetAllocatedSize() == 2 + frameOverhead); 205 | 206 | void* ptr1 = alloc.Allocate(2, 1); 207 | if (ptr1) memset(ptr1, 0xcd, 2); 208 | 209 | REQUIRE(ptr0 != ptr1); 210 | REQUIRE(alloc.GetAllocatedSize() == 4 + frameOverhead); 211 | } 212 | } 213 | 214 | TEST_CASE("test invalid arguments in vmem stack frame allocator", "[VMemStackFrameAllocator]") 215 | { 216 | using namespace gin; 217 | 218 | SECTION("test initialization") 219 | { 220 | VMemStackFrameAllocator alloc; 221 | 222 | REQUIRE(!alloc.IsInitialized()); 223 | 224 | alloc.Initialize(0); 225 | 226 | REQUIRE(!alloc.IsInitialized()); 227 | } 228 | } 229 | 230 | --------------------------------------------------------------------------------