Allocators Testing Added. Pull and Chunk allocators updates.
This commit is contained in:
parent
4a2ab6f5d0
commit
9e5ac1e975
30 changed files with 704 additions and 701 deletions
|
|
@ -7,14 +7,16 @@ project(Allocator)
|
||||||
|
|
||||||
### ---------------------- Static Library --------------------- ###
|
### ---------------------- Static Library --------------------- ###
|
||||||
file(GLOB SOURCES "./private/*.cpp")
|
file(GLOB SOURCES "./private/*.cpp")
|
||||||
add_library(${PROJECT_NAME} STATIC ${SOURCES})
|
file(GLOB HEADERS "./public/*.hpp")
|
||||||
|
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
|
||||||
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
|
||||||
target_link_libraries(${PROJECT_NAME} PUBLIC Utils)
|
target_link_libraries(${PROJECT_NAME} PUBLIC Utils)
|
||||||
|
|
||||||
### -------------------------- Tests -------------------------- ###
|
### -------------------------- Tests -------------------------- ###
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_executable(${PROJECT_NAME}Tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Tests.cpp)
|
file(GLOB TEST_SOURCES "./tests/*.cpp")
|
||||||
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME})
|
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
|
||||||
add_test(${PROJECT_NAME} ${PROJECT_NAME}Tests)
|
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
|
||||||
|
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
|
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
|
||||||
|
|
@ -16,7 +16,7 @@ void test_call11() {
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(char argc, char* argv[]) {
|
int main(char argc, char* argv[]) {
|
||||||
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocator, NULL };
|
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocators, NULL };
|
||||||
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
|
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
|
||||||
TestModule.initialize();
|
TestModule.initialize();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,23 @@
|
||||||
|
|
||||||
#include "Allocators.hpp"
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleBase, NULL };
|
#include <cstdlib>
|
||||||
tp::ModuleManifest tp::gModuleAllocator = ModuleManifest("Allocators", NULL, NULL, sModuleDependencies);
|
|
||||||
|
static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleBase, nullptr };
|
||||||
|
tp::ModuleManifest tp::gModuleAllocators = ModuleManifest("Allocators", nullptr, nullptr, sModuleDependencies);
|
||||||
|
|
||||||
|
|
||||||
void* operator new(size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
|
void* operator new(size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
|
||||||
void* operator new[](size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
|
void* operator new[](size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
|
||||||
void operator delete(void* aPtr) { tp::HeapAllocGlobal::deallocate(aPtr); }
|
void operator delete(void* aPtr) noexcept { tp::HeapAllocGlobal::deallocate(aPtr); }
|
||||||
void operator delete[](void* aPtr) { tp::HeapAllocGlobal::deallocate(aPtr); }
|
void operator delete[](void* aPtr) noexcept { tp::HeapAllocGlobal::deallocate(aPtr); }
|
||||||
|
|
||||||
void* operator new(size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
|
void* operator new(size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
|
||||||
void* operator new[](size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
|
void* operator new[](size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
|
||||||
void operator delete(void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
|
void operator delete(void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
|
||||||
void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
|
void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
|
||||||
|
|
||||||
void* operator new(size_t aSize, tp::HeapAllocGlobal& aAlloc) { return aAlloc.allocate(aSize); }
|
void* operator new(size_t aSize, tp::HeapAllocGlobal& aAlloc) { return tp::HeapAllocGlobal::allocate(aSize); }
|
||||||
void* operator new[](size_t aSize, tp::HeapAllocGlobal& aAlloc) { return aAlloc.allocate(aSize); }
|
void* operator new[](size_t aSize, tp::HeapAllocGlobal& aAlloc) { return tp::HeapAllocGlobal::allocate(aSize); }
|
||||||
void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc) { aAlloc.deallocate(aPtr); }
|
void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }
|
||||||
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { aAlloc.deallocate(aPtr); }
|
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Implementation uses embedded one-directional linked list to track free blocks.
|
|
||||||
* The embedded part ensures that there is no memory overhead on block specifically.
|
|
||||||
* Linked list is initialized iteratively on each allocation if it has not been already.
|
|
||||||
|
|
||||||
* Allocating:
|
|
||||||
* 1) updating list entry to stored in the entry itself next free pointer
|
|
||||||
* 2) returning entry before (1).
|
|
||||||
*
|
|
||||||
* Deallocating:
|
|
||||||
* 1) assigning list entry value to the deleted block
|
|
||||||
* 2) updating list entry to that block.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "ChunkAllocator.hpp"
|
|
||||||
#include "PrivateConfig.hpp"
|
|
||||||
|
|
||||||
#include "HeapAllocatorGlobal.hpp"
|
|
||||||
|
|
||||||
using namespace tp;
|
|
||||||
|
|
||||||
enum : ualni {
|
|
||||||
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
|
||||||
WRAP_SIZE_ALN = MEM_WRAP_SIZE,
|
|
||||||
WRAP_SIZE = WRAP_SIZE_ALN * ALIGNED_SIZE,
|
|
||||||
WRAP_VAL = MEM_WRAP_FILL_VAL,
|
|
||||||
CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
|
|
||||||
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
|
|
||||||
};
|
|
||||||
|
|
||||||
ChunkAlloc::ChunkAlloc(ualni aBlockSize, void* aMemory, ualni aMemSize) {
|
|
||||||
auto const temp = aBlockSize / ALIGNED_SIZE;
|
|
||||||
mBSize = ((aBlockSize % ALIGNED_SIZE) ? temp + 1 : temp) + WRAP_SIZE_ALN * 2;
|
|
||||||
mBuff = (ualni*) aMemory;
|
|
||||||
mNBlocks = (aMemSize / ALIGNED_SIZE) / mBSize;
|
|
||||||
mNFreeBlocks = mNBlocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
ChunkAlloc::ChunkAlloc(ualni aBlockSize, ualni aNBlocks) {
|
|
||||||
auto const temp = aBlockSize / ALIGNED_SIZE;
|
|
||||||
mBSize = ((aBlockSize % ALIGNED_SIZE) ? temp + 1 : temp) + WRAP_SIZE_ALN * 2;
|
|
||||||
mNBlocks = aNBlocks;
|
|
||||||
mNFreeBlocks = mNBlocks;
|
|
||||||
mBuff = (ualni*)HeapAllocGlobal::allocate(mNBlocks * mBSize * ALIGNED_SIZE);
|
|
||||||
mOwnBuff = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* ChunkAlloc::allocate() {
|
|
||||||
ASSERT(mNFreeBlocks && "Out Of Memory");
|
|
||||||
|
|
||||||
// 1) PreInitialize blocks
|
|
||||||
if (mNInitBlocks < mNBlocks) {
|
|
||||||
*(mBuff + mNInitBlocks * mBSize) = (ualni)(mBuff + (mNInitBlocks++) * mBSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2) Find free block and update next free block
|
|
||||||
auto data = mNextBlock;
|
|
||||||
mNextBlock = (ualni*)(*data);
|
|
||||||
mNFreeBlocks--;
|
|
||||||
|
|
||||||
#ifdef MEM_DEBUG
|
|
||||||
// 3) Fill Wrap and offset data
|
|
||||||
auto wrap_top = data;
|
|
||||||
data += WRAP_SIZE_ALN;
|
|
||||||
auto wrap_bottom = data + mBSize;
|
|
||||||
|
|
||||||
memsetv(wrap_top, WRAP_SIZE, WRAP_VAL);
|
|
||||||
memsetv(wrap_bottom, WRAP_SIZE, WRAP_VAL);
|
|
||||||
|
|
||||||
// 4) Clear data
|
|
||||||
#ifdef MEM_CLEAR_ON_ALLOC
|
|
||||||
memsetv(data, mBSize * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ChunkAlloc::deallocate(void* aPtr) {
|
|
||||||
auto block = (ualni*)aPtr;
|
|
||||||
|
|
||||||
#ifdef MEM_DEBUG
|
|
||||||
// 3) Check Wrap and offset data
|
|
||||||
auto wrap_bottom = block + mBSize;
|
|
||||||
auto wrap_top = block - WRAP_SIZE_ALN;
|
|
||||||
block = wrap_top;
|
|
||||||
|
|
||||||
// 3) Check the wrap
|
|
||||||
RelAssert(memequalv(wrap_top, WRAP_SIZE, WRAP_VAL) && memequalv(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!");
|
|
||||||
|
|
||||||
// 4) Clear data
|
|
||||||
#ifdef MEM_CLEAR_ON_ALLOC
|
|
||||||
memsetv(aPtr, mBSize * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
(*block) = (ualni)mNextBlock;
|
|
||||||
mNextBlock = block;
|
|
||||||
mNFreeBlocks++;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ChunkAlloc::isFull() const { return !mNFreeBlocks; }
|
|
||||||
bool ChunkAlloc::isEmpty() const { return mNFreeBlocks == mNBlocks; }
|
|
||||||
|
|
||||||
ChunkAlloc::~ChunkAlloc() {
|
|
||||||
// TODO : check for leaks
|
|
||||||
if (mOwnBuff) {
|
|
||||||
HeapAllocGlobal::deallocate(mBuff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -5,9 +5,6 @@
|
||||||
|
|
||||||
#include "PrivateConfig.hpp"
|
#include "PrivateConfig.hpp"
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
using namespace tp;
|
using namespace tp;
|
||||||
|
|
||||||
#if not defined(MEM_DEBUG)
|
#if not defined(MEM_DEBUG)
|
||||||
|
|
@ -24,7 +21,7 @@ namespace tp {
|
||||||
MemHeadLocal* mPrev;
|
MemHeadLocal* mPrev;
|
||||||
MemHeadLocal* mNext;
|
MemHeadLocal* mNext;
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
void* HeapAlloc::allocate(ualni aBlockSize) {
|
void* HeapAlloc::allocate(ualni aBlockSize) {
|
||||||
auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal));
|
auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal));
|
||||||
|
|
@ -32,12 +29,11 @@ void* HeapAlloc::allocate(ualni aBlockSize) {
|
||||||
|
|
||||||
mNumAllocations++;
|
mNumAllocations++;
|
||||||
if (mEntry) {
|
if (mEntry) {
|
||||||
head->mNext = mEntry->mNext;
|
DEBUG_ASSERT(!mEntry->mNext)
|
||||||
head->mPrev = mEntry->mPrev;
|
head->mNext = nullptr;
|
||||||
if (mEntry->mNext) mEntry->mNext->mPrev = head;
|
head->mPrev = mEntry;
|
||||||
if (mEntry->mPrev) mEntry->mPrev->mNext = head;
|
mEntry->mNext = head;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
head->mNext = nullptr;
|
head->mNext = nullptr;
|
||||||
head->mPrev = nullptr;
|
head->mPrev = nullptr;
|
||||||
}
|
}
|
||||||
|
|
@ -50,15 +46,11 @@ void HeapAlloc::deallocate(void* aPtr) {
|
||||||
auto head = ((MemHeadLocal*)(aPtr)) - 1;
|
auto head = ((MemHeadLocal*)(aPtr)) - 1;
|
||||||
|
|
||||||
mNumAllocations--;
|
mNumAllocations--;
|
||||||
if (mEntry->mNext) mEntry->mNext->mPrev = mEntry->mPrev;
|
DEBUG_ASSERT(!mEntry->mNext)
|
||||||
if (mEntry->mPrev) mEntry->mPrev->mNext = mEntry->mNext;
|
if (head->mNext) head->mNext->mPrev = head->mPrev;
|
||||||
|
if (head->mPrev) head->mPrev->mNext = head->mNext;
|
||||||
if (head == mEntry) {
|
if (head == mEntry) {
|
||||||
if (mEntry->mNext) {
|
mEntry = head->mPrev;
|
||||||
mEntry = mEntry->mNext;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mEntry = mEntry->mPrev;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
HeapAllocGlobal::deallocate(head);
|
HeapAllocGlobal::deallocate(head);
|
||||||
|
|
@ -66,7 +58,7 @@ void HeapAlloc::deallocate(void* aPtr) {
|
||||||
|
|
||||||
HeapAlloc::~HeapAlloc() {
|
HeapAlloc::~HeapAlloc() {
|
||||||
if (mNumAllocations) {
|
if (mNumAllocations) {
|
||||||
DEBUG_BREAK("Destruction of not freed Allocator");
|
DEBUG_BREAK("Destruction of not freed Allocator")
|
||||||
|
|
||||||
#ifdef MEM_STACK_TRACE
|
#ifdef MEM_STACK_TRACE
|
||||||
// TODO : log leaks and free them up
|
// TODO : log leaks and free them up
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,9 @@
|
||||||
#include "HeapAllocatorGlobal.hpp"
|
#include "HeapAllocatorGlobal.hpp"
|
||||||
#include "PrivateConfig.hpp"
|
#include "PrivateConfig.hpp"
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
#include "Debugging.hpp"
|
#include "Debugging.hpp"
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
|
||||||
using namespace tp;
|
using namespace tp;
|
||||||
|
|
@ -40,10 +40,10 @@ namespace tp {
|
||||||
MemHead* mNext;
|
MemHead* mNext;
|
||||||
ualni mBlockSize;
|
ualni mBlockSize;
|
||||||
#ifdef MEM_STACK_TRACE
|
#ifdef MEM_STACK_TRACE
|
||||||
CallStackSnapshots::StackShapshot mCallStack;
|
const CallStackCapture::CallStack* mCallStack;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
enum : ualni {
|
enum : ualni {
|
||||||
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
||||||
|
|
@ -74,29 +74,28 @@ void* HeapAllocGlobal::allocate(ualni aBlockSize) {
|
||||||
// 2) Link with existing blocks
|
// 2) Link with existing blocks
|
||||||
mNumAllocations++;
|
mNumAllocations++;
|
||||||
if (mEntry) {
|
if (mEntry) {
|
||||||
head->mNext = mEntry->mNext;
|
DEBUG_ASSERT(!mEntry->mNext)
|
||||||
head->mPrev = mEntry->mPrev;
|
head->mNext = nullptr;
|
||||||
if (mEntry->mNext) mEntry->mNext->mPrev = head;
|
head->mPrev = mEntry;
|
||||||
if (mEntry->mPrev) mEntry->mPrev->mNext = head;
|
mEntry->mNext = head;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
head->mNext = nullptr;
|
head->mNext = nullptr;
|
||||||
head->mPrev = nullptr;
|
head->mPrev = nullptr;
|
||||||
}
|
}
|
||||||
mEntry = head;
|
mEntry = head;
|
||||||
|
|
||||||
// 3) Wrap fill
|
// 3) Wrap fill
|
||||||
memsetv(wrap_top, WRAP_SIZE, WRAP_VAL);
|
memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
|
||||||
memsetv(wrap_bottom, WRAP_SIZE, WRAP_VAL);
|
memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
|
||||||
|
|
||||||
// 4) Trace the stack
|
// 4) Trace the stack
|
||||||
#ifdef MEM_STACK_TRACE
|
#ifdef MEM_STACK_TRACE
|
||||||
head->mCallStack = gCallStackSnapshots.capture();
|
head->mCallStack = gCSCapture->getSnapshot();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 5) clear data
|
// 5) clear data
|
||||||
#ifdef MEM_CLEAR_ON_ALLOC
|
#ifdef MEM_CLEAR_ON_ALLOC
|
||||||
memsetv(data, aBlockSize, CLEAR_ALLOC_VAL);
|
memSetVal(data, aBlockSize, CLEAR_ALLOC_VAL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
|
@ -104,46 +103,48 @@ void* HeapAllocGlobal::allocate(ualni aBlockSize) {
|
||||||
|
|
||||||
void HeapAllocGlobal::deallocate(void* aPtr) {
|
void HeapAllocGlobal::deallocate(void* aPtr) {
|
||||||
// 1) Restore the pointers
|
// 1) Restore the pointers
|
||||||
auto head = ((MemHead*)(aPtr)) - 1;
|
auto head = ((MemHead*)((int1*)aPtr - WRAP_SIZE)) - 1;
|
||||||
auto wrap_top = (int1*)(head + 1);
|
auto wrap_top = (int1*)(head + 1);
|
||||||
auto data = wrap_top + WRAP_SIZE;
|
auto data = wrap_top + WRAP_SIZE;
|
||||||
auto wrap_bottom = data + head->mBlockSize;
|
auto wrap_bottom = data + head->mBlockSize;
|
||||||
|
|
||||||
// 2) Unlink with blocks
|
// 2) Unlink with blocks
|
||||||
mNumAllocations--;
|
mNumAllocations--;
|
||||||
if (mEntry->mNext) mEntry->mNext->mPrev = mEntry->mPrev;
|
DEBUG_ASSERT(!mEntry->mNext)
|
||||||
if (mEntry->mPrev) mEntry->mPrev->mNext = mEntry->mNext;
|
if (head->mNext) head->mNext->mPrev = head->mPrev;
|
||||||
|
if (head->mPrev) head->mPrev->mNext = head->mNext;
|
||||||
if (head == mEntry) {
|
if (head == mEntry) {
|
||||||
if (mEntry->mNext) {
|
mEntry = head->mPrev;
|
||||||
mEntry = mEntry->mNext;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
mEntry = mEntry->mPrev;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3) Check the wrap
|
// 3) Check the wrap
|
||||||
RelAssert(memequalv(wrap_top, WRAP_SIZE, WRAP_VAL) && memequalv(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!");
|
ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
|
||||||
|
ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
|
||||||
|
|
||||||
// 4) clear data
|
// 4) clear data
|
||||||
#ifdef MEM_CLEAR_ON_ALLOC
|
#ifdef MEM_CLEAR_ON_ALLOC
|
||||||
memsetv(data, head->mBlockSize, CLEAR_DEALLOC_VAL);
|
memSetVal(data, head->mBlockSize, CLEAR_DEALLOC_VAL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 5) free the block
|
// 5) free the block
|
||||||
free(aPtr);
|
free(head);
|
||||||
}
|
}
|
||||||
|
|
||||||
HeapAllocGlobal::~HeapAllocGlobal() {
|
bool HeapAllocGlobal::checkLeaks() {
|
||||||
// 1) Check for not deallocated memory
|
// 1) Check for not deallocated memory
|
||||||
if (mNumAllocations) {
|
if (mNumAllocations) {
|
||||||
DEBUG_BREAK("Destruction of not freed Allocator");
|
|
||||||
|
|
||||||
#ifdef MEM_STACK_TRACE
|
#ifdef MEM_STACK_TRACE
|
||||||
// TODO: log leaks
|
gCSCapture->logLeaks();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
DEBUG_BREAK("Destruction of not freed Allocator")
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HeapAllocGlobal::~HeapAllocGlobal() = default;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
|
|
||||||
|
|
@ -1,131 +0,0 @@
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
Implementation:
|
|
||||||
* Adding chunk pointer to each chunk to form one-directional list that keeps track of free chunk
|
|
||||||
* Storing ordered pointers to chunks in order to find desired chunk from delete pointer on de-allocation in log time
|
|
||||||
*
|
|
||||||
* Allocations:
|
|
||||||
* 1) allocate with chunk stored in list entry
|
|
||||||
* 2) ...
|
|
||||||
*
|
|
||||||
* De-allocations:
|
|
||||||
* 1) binary-search with delete pointer to find desired chunk
|
|
||||||
* 2) ...
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "Allocators.hpp"
|
|
||||||
#include "PrivateConfig.hpp"
|
|
||||||
|
|
||||||
void tp::PoolAlloc::Chunks::add(Chunk* aChunk) {
|
|
||||||
// ensure order
|
|
||||||
auto smaller_address = findUtil(mBuff, mBuff + mUsedLen, aChunk);
|
|
||||||
for (auto iter = mBuff + mUsedLen; iter != smaller_address; iter--) { *(iter + 1) = *iter; }
|
|
||||||
|
|
||||||
*(smaller_address + 1) = aChunk;
|
|
||||||
mUsedLen++;
|
|
||||||
|
|
||||||
// check for buff overflow
|
|
||||||
if (mUsedLen == mLen) {
|
|
||||||
auto prevBuff = mBuff;
|
|
||||||
mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen * 2);
|
|
||||||
memcp(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
|
|
||||||
mLen *= 2;
|
|
||||||
HeapAllocGlobal::deallocate(prevBuff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void tp::PoolAlloc::Chunks::remove(Chunk* aChunk) {
|
|
||||||
// ensure order
|
|
||||||
auto del_address = findUtil(mBuff, mBuff + mUsedLen, aChunk);
|
|
||||||
for (auto iter = del_address; iter != mBuff + mUsedLen; iter++) { *iter = *(iter + 1); }
|
|
||||||
|
|
||||||
mUsedLen--;
|
|
||||||
|
|
||||||
// check for buff low usage
|
|
||||||
if ((halnf)mUsedLen / (halnf)mLen < 0.25f) {
|
|
||||||
auto prevBuff = mBuff;
|
|
||||||
mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2);
|
|
||||||
memcp(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
|
|
||||||
mLen /= 2;
|
|
||||||
HeapAllocGlobal::deallocate(prevBuff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tp::PoolAlloc::Chunk* tp::PoolAlloc::Chunks::find(void* aPtr) {
|
|
||||||
return *findUtil(mBuff, mBuff + mUsedLen, aPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
tp::PoolAlloc::Chunk* tp::PoolAlloc::Chunks::findNotFull() {
|
|
||||||
for (ualni idx = 0; idx < mUsedLen; idx++) {
|
|
||||||
if (!mBuff[idx]->isFull()) {
|
|
||||||
return mBuff[idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
tp::PoolAlloc::Chunk** tp::PoolAlloc::Chunks::findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr) {
|
|
||||||
auto range = ualni(aRight - aLeft);
|
|
||||||
if (range == 1) { return aLeft; }
|
|
||||||
auto middle = aLeft + range / 2;
|
|
||||||
return (aPtr > *middle) ? findUtil(middle, aRight, aPtr) : findUtil(aLeft, middle, aPtr);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
tp::PoolAlloc::PoolAlloc(ualni aBlockSize, ualni aChunkSize) : mBlockSize(aBlockSize), mChunkSize(aChunkSize) {}
|
|
||||||
|
|
||||||
void* tp::PoolAlloc::allocate() {
|
|
||||||
|
|
||||||
if (!mFreeChunk || mFreeChunk->isFull()) {
|
|
||||||
auto new_free_chunk = mChunks.findNotFull();
|
|
||||||
|
|
||||||
if (!new_free_chunk) {
|
|
||||||
|
|
||||||
new_free_chunk = new ((Chunk*)HeapAllocGlobal::allocate(sizeof(Chunk))) Chunk(mBlockSize, mChunkSize);
|
|
||||||
ASSERT(new_free_chunk);
|
|
||||||
mChunks.add(new_free_chunk);
|
|
||||||
|
|
||||||
if (mFreeChunk) {
|
|
||||||
new_free_chunk->mNext = mFreeChunk->mNext;
|
|
||||||
new_free_chunk->mPrev = mFreeChunk->mPrev;
|
|
||||||
if (mFreeChunk->mNext) mFreeChunk->mNext->mPrev = new_free_chunk;
|
|
||||||
if (mFreeChunk->mPrev) mFreeChunk->mPrev->mNext = new_free_chunk;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
new_free_chunk->mNext = nullptr;
|
|
||||||
new_free_chunk->mPrev = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
mFreeChunk = new_free_chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mFreeChunk->allocate();
|
|
||||||
}
|
|
||||||
|
|
||||||
void tp::PoolAlloc::deallocate(void* aPtr) {
|
|
||||||
auto chunk = mChunks.find(aPtr);
|
|
||||||
chunk->deallocate(aPtr);
|
|
||||||
|
|
||||||
if (mFreeChunk->isEmpty()) {
|
|
||||||
|
|
||||||
Chunk* new_chunk = nullptr;
|
|
||||||
for (ualni idx = 0; idx < mChunks.mUsedLen; idx++) {
|
|
||||||
if (!mChunks.mBuff[idx]->isFull() && new_chunk != mFreeChunk) {
|
|
||||||
new_chunk = mChunks.mBuff[idx];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (new_chunk) {
|
|
||||||
if (mFreeChunk->mNext) mFreeChunk->mNext->mPrev = mFreeChunk->mPrev;
|
|
||||||
if (mFreeChunk->mPrev) mFreeChunk->mPrev->mNext = mFreeChunk->mNext;
|
|
||||||
mChunks.remove(mFreeChunk);
|
|
||||||
HeapAllocGlobal::deallocate(mFreeChunk);
|
|
||||||
mFreeChunk = new_chunk;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tp::PoolAlloc::~PoolAlloc() = default;
|
|
||||||
|
|
@ -8,15 +8,15 @@
|
||||||
#include "PoolAllocator.hpp"
|
#include "PoolAllocator.hpp"
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
extern ModuleManifest gModuleAllocator;
|
extern ModuleManifest gModuleAllocators;
|
||||||
};
|
}
|
||||||
|
|
||||||
inline void* operator new(std::size_t aSize, void* aWhere) noexcept { return aWhere; }
|
inline void* operator new(std::size_t aSize, void* aWhere) noexcept { return aWhere; }
|
||||||
|
|
||||||
void* operator new(std::size_t aSize);
|
void* operator new(std::size_t aSize);
|
||||||
void* operator new[](std::size_t aSize);
|
void* operator new[](std::size_t aSize);
|
||||||
void operator delete(void* aPtr);
|
void operator delete(void* aPtr) noexcept;
|
||||||
void operator delete[](void* aPtr);
|
void operator delete[](void* aPtr) noexcept;
|
||||||
|
|
||||||
void* operator new(std::size_t aSize, tp::HeapAlloc& aAlloc);
|
void* operator new(std::size_t aSize, tp::HeapAlloc& aAlloc);
|
||||||
void* operator new[](std::size_t aSize, tp::HeapAlloc& aAlloc);
|
void* operator new[](std::size_t aSize, tp::HeapAlloc& aAlloc);
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,135 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implementation uses embedded one-directional linked list to track free blocks.
|
||||||
|
* The embedded part ensures that there is no memory overhead on block specifically.
|
||||||
|
* Linked list is initialized iteratively on each allocation if it has not been already.
|
||||||
|
|
||||||
|
* Allocating:
|
||||||
|
* 1) updating list entry to stored in the entry itself next free pointer
|
||||||
|
* 2) returning entry before (1).
|
||||||
|
*
|
||||||
|
* Deallocating:
|
||||||
|
* 1) assigning list entry value to the deleted block
|
||||||
|
* 2) updating list entry to that block.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "Environment.hpp"
|
#include "Environment.hpp"
|
||||||
|
#include "PrivateConfig.hpp"
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
// Chunk Allocator
|
// Chunk Allocator
|
||||||
// Constant time allocations and de-allocations in any order.
|
// Constant time allocations and de-allocations in any order.
|
||||||
// Memory blocks are fixed in size and number of blocks can not exceed given parameter.
|
// Memory blocks are fixed in size and number of blocks can not exceed given parameter.
|
||||||
struct ChunkAlloc {
|
template<typename tType, ualni tNumBlocks>
|
||||||
|
class ChunkAlloc {
|
||||||
|
|
||||||
ChunkAlloc(ualni aBlockSize, void* aMemory, ualni aMemSize);
|
enum : ualni {
|
||||||
ChunkAlloc(ualni aBlockSize, ualni aNBlocks);
|
ALIGNED_SIZE = ENV_ALNI_SIZE_B,
|
||||||
|
|
||||||
void* allocate();
|
WRAP_SIZE_ALN = MEM_WRAP_SIZE / 2,
|
||||||
void deallocate(void* aPtr);
|
WRAP_SIZE = WRAP_SIZE_ALN * ALIGNED_SIZE,
|
||||||
[[nodiscard]] bool isFull() const;
|
|
||||||
[[nodiscard]] bool isEmpty() const;
|
|
||||||
|
|
||||||
~ChunkAlloc();
|
WRAP_VAL = MEM_WRAP_FILL_VAL,
|
||||||
|
CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
|
||||||
|
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr ualni dataSize() {
|
||||||
|
auto BLOCK_SIZE_BYTES = sizeof(tType);
|
||||||
|
auto BLOCK_SIZE_ALIGNED = BLOCK_SIZE_BYTES / ALIGNED_SIZE;
|
||||||
|
return BLOCK_SIZE_ALIGNED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr ualni blockSize() {
|
||||||
|
auto BLOCK_SIZE_BYTES = sizeof(tType);
|
||||||
|
auto BLOCK_SIZE = dataSize() + bool(BLOCK_SIZE_BYTES % ALIGNED_SIZE) + WRAP_SIZE_ALN * 2;
|
||||||
|
return BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ualni mBSize; // Size of data in aligned units
|
ualni* mNextBlock;
|
||||||
ualni mNBlocks;
|
ualni mNumFreeBlocks;
|
||||||
|
ualni mNumInitBlocks;
|
||||||
|
ualni mBuff[tNumBlocks * blockSize() * ALIGNED_SIZE];
|
||||||
|
|
||||||
ualni* mBuff = nullptr;
|
public:
|
||||||
ualni* mNextBlock = nullptr;
|
ChunkAlloc() {
|
||||||
ualni mNFreeBlocks;
|
mNumFreeBlocks = tNumBlocks;
|
||||||
ualni mNInitBlocks = 0;
|
mNumInitBlocks = 0;
|
||||||
bool mOwnBuff = false;
|
mNextBlock = mBuff;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
~ChunkAlloc() = default; // TODO : check for leaks
|
||||||
|
|
||||||
|
public:
|
||||||
|
void* allocate(ualni) {
|
||||||
|
DEBUG_ASSERT(mNumFreeBlocks && "Out Of Memory")
|
||||||
|
|
||||||
|
// 1) PreInitialize blocks
|
||||||
|
if (mNumInitBlocks < tNumBlocks) {
|
||||||
|
mBuff[mNumInitBlocks * blockSize()] = (ualni) (mBuff + (mNumInitBlocks + 1) * blockSize());
|
||||||
|
mNumInitBlocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) Find free block and update next free block
|
||||||
|
auto data = mNextBlock;
|
||||||
|
mNextBlock = (ualni*)(*data);
|
||||||
|
mNumFreeBlocks--;
|
||||||
|
|
||||||
|
#ifdef MEM_DEBUG
|
||||||
|
// 3) Fill Wrap and offset data
|
||||||
|
auto wrap_top = data;
|
||||||
|
auto wrap_bottom = data + WRAP_SIZE_ALN + dataSize();
|
||||||
|
|
||||||
|
memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
|
||||||
|
memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
|
||||||
|
|
||||||
|
// 4) Clear data
|
||||||
|
#ifdef MEM_CLEAR_ON_ALLOC
|
||||||
|
memSetVal(data + WRAP_SIZE_ALN, dataSize() * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
data += WRAP_SIZE_ALN;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(void* aPtr) {
|
||||||
|
DEBUG_ASSERT(aPtr >= mBuff && aPtr < mBuff + tNumBlocks * blockSize())
|
||||||
|
|
||||||
|
auto block = (ualni*)aPtr;
|
||||||
|
|
||||||
|
#ifdef MEM_DEBUG
|
||||||
|
// 3) Check Wrap and offset data
|
||||||
|
auto wrap_bottom = block + dataSize();
|
||||||
|
auto wrap_top = block - WRAP_SIZE_ALN;
|
||||||
|
block = wrap_top;
|
||||||
|
|
||||||
|
// 3) Check the wrap
|
||||||
|
ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
|
||||||
|
ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
|
||||||
|
|
||||||
|
// 4) Clear data
|
||||||
|
#ifdef MEM_CLEAR_ON_ALLOC
|
||||||
|
memSetVal(block, blockSize() * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
(*block) = (ualni)mNextBlock;
|
||||||
|
mNextBlock = block;
|
||||||
|
mNumFreeBlocks++;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool checkWrap() const { return false; }
|
||||||
|
void checkValid() {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool isFull() const { return !mNumFreeBlocks; }
|
||||||
|
[[nodiscard]] bool isEmpty() const { return mNumFreeBlocks == tNumBlocks; }
|
||||||
|
[[nodiscard]] const ualni* getBuff() const { return mBuff; }
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
@ -4,15 +4,23 @@
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
struct HeapAlloc {
|
class HeapAlloc {
|
||||||
|
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
ualni mNumAllocations = 0;
|
ualni mNumAllocations = 0;
|
||||||
struct MemHeadLocal* mEntry = nullptr;
|
struct MemHeadLocal* mEntry = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
HeapAlloc() = default;
|
||||||
|
~HeapAlloc();
|
||||||
|
|
||||||
|
public:
|
||||||
void* allocate(ualni aBlockSize);
|
void* allocate(ualni aBlockSize);
|
||||||
void deallocate(void* aPtr);
|
void deallocate(void* aPtr);
|
||||||
~HeapAlloc();
|
|
||||||
};
|
public:
|
||||||
|
[[nodiscard]] bool checkWrap() const { return false; }
|
||||||
|
void checkValid() {}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,24 @@
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
struct HeapAllocGlobal {
|
class HeapAllocGlobal {
|
||||||
|
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
static ualni mNumAllocations;
|
static ualni mNumAllocations;
|
||||||
static struct MemHead* mEntry;
|
static struct MemHead* mEntry;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
HeapAllocGlobal() = default;
|
||||||
|
~HeapAllocGlobal();
|
||||||
|
|
||||||
|
public:
|
||||||
static void* allocate(ualni aBlockSize);
|
static void* allocate(ualni aBlockSize);
|
||||||
static void deallocate(void* aPtr);
|
static void deallocate(void* aPtr);
|
||||||
~HeapAllocGlobal();
|
|
||||||
};
|
static bool checkLeaks();
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool checkWrap() const { return false; }
|
||||||
|
void checkValid() {}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "HeapAllocatorGlobal.hpp"
|
|
||||||
|
|
||||||
namespace tp {
|
|
||||||
|
|
||||||
struct PickAlloc {
|
|
||||||
PickAlloc();
|
|
||||||
void* allocate(ualni aBlockSize);
|
|
||||||
void deallocate(void* aPtr);
|
|
||||||
~PickAlloc();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
@ -1,42 +1,162 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
Implementation:
|
||||||
|
* Adding chunk pointer to each chunk to form one-directional list that keeps track of free chunk
|
||||||
|
* Storing ordered pointers to chunks in order to find desired chunk from delete pointer on de-allocation in log time
|
||||||
|
*
|
||||||
|
* Allocations:
|
||||||
|
* 1) allocate with chunk stored in list entry
|
||||||
|
* 2) ...
|
||||||
|
*
|
||||||
|
* De-allocations:
|
||||||
|
* 1) binary-search with delete pointer to find desired chunk
|
||||||
|
* 2) ...
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
#include "ChunkAllocator.hpp"
|
#include "ChunkAllocator.hpp"
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
// Pool Allocator
|
// Pool Allocator
|
||||||
// Overcomes chunk allocator fixed number of max allocations
|
// Overcomes chunk allocator fixed number of max allocations
|
||||||
struct PoolAlloc {
|
template<typename tType, ualni tNumBlocks>
|
||||||
|
class PoolAlloc {
|
||||||
|
|
||||||
PoolAlloc(ualni aBlockSize, ualni aChunkSize);
|
typedef ChunkAlloc<tType, tNumBlocks> Chunk;
|
||||||
void* allocate();
|
|
||||||
void deallocate(void* aPtr);
|
|
||||||
~PoolAlloc();
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
struct Chunk : public ChunkAlloc {
|
|
||||||
Chunk(ualni aBlockSize, ualni aChunlSize) : ChunkAlloc(aBlockSize, aChunlSize) {}
|
|
||||||
Chunk* mNext = NULL;
|
|
||||||
Chunk* mPrev = NULL;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Chunks {
|
struct Chunks {
|
||||||
void add(Chunk*);
|
|
||||||
void remove(Chunk*);
|
void add(Chunk* aChunk){
|
||||||
Chunk* find(void* aPtr);
|
|
||||||
Chunk* findNotFull();
|
if (!mBuff) {
|
||||||
Chunk** mBuff = NULL;
|
mLen = 16;
|
||||||
|
mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen);
|
||||||
|
mUsedLen = 1;
|
||||||
|
mBuff[0] = aChunk;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure order
|
||||||
|
auto smaller_address = findUtil(mBuff, mBuff + mUsedLen, aChunk);
|
||||||
|
for (auto iter = mBuff + mUsedLen; iter != smaller_address; iter--) {
|
||||||
|
*iter = *(iter - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
*(smaller_address) = aChunk;
|
||||||
|
mUsedLen++;
|
||||||
|
|
||||||
|
// check for buff overflow
|
||||||
|
if (mUsedLen == mLen) {
|
||||||
|
auto prevBuff = mBuff;
|
||||||
|
mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen * 2);
|
||||||
|
memCopy(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
|
||||||
|
mLen *= 2;
|
||||||
|
HeapAllocGlobal::deallocate(prevBuff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(Chunk** del_address){
|
||||||
|
if (mUsedLen == 1) {
|
||||||
|
mLen = 0;
|
||||||
|
mUsedLen = 0;
|
||||||
|
HeapAllocGlobal::deallocate(mBuff);
|
||||||
|
mBuff = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure order
|
||||||
|
for (auto iter = del_address; iter != mBuff + mUsedLen - 1; iter++) {
|
||||||
|
*iter = *(iter + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
mUsedLen--;
|
||||||
|
|
||||||
|
// check for buff low usage
|
||||||
|
if ((halnf)mUsedLen / (halnf)mLen < 0.25f) {
|
||||||
|
auto prevBuff = mBuff;
|
||||||
|
mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2);
|
||||||
|
memCopy(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
|
||||||
|
mLen /= 2;
|
||||||
|
HeapAllocGlobal::deallocate(prevBuff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Chunk** find(void* aPtr) {
|
||||||
|
return findUtil(mBuff, mBuff + mUsedLen, aPtr) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Chunk* findNotFull() const {
|
||||||
|
for (ualni idx = 0; idx < mUsedLen; idx++) {
|
||||||
|
if (!mBuff[idx]->isFull()) {
|
||||||
|
return mBuff[idx];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Chunk** mBuff = nullptr;
|
||||||
ualni mUsedLen = 0;
|
ualni mUsedLen = 0;
|
||||||
ualni mLen = 0;
|
ualni mLen = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Chunk** findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr);
|
Chunk** findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr) {
|
||||||
|
auto range = ualni(aRight - aLeft);
|
||||||
|
if (range == 1) {
|
||||||
|
return (aPtr < *aLeft) ? aLeft : aRight;
|
||||||
|
}
|
||||||
|
auto middle = aLeft + range / 2;
|
||||||
|
return (aPtr >= (*middle)) ? findUtil(middle, aRight, aPtr) : findUtil(aLeft, middle, aPtr);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
Chunks mChunks;
|
Chunks mChunks;
|
||||||
Chunk* mFreeChunk = NULL;
|
Chunk* mFreeChunk = nullptr;
|
||||||
ualni mBlockSize;
|
|
||||||
ualni mChunkSize;
|
public:
|
||||||
};
|
PoolAlloc() = default;
|
||||||
|
~PoolAlloc() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void* allocate(ualni) {
|
||||||
|
if (!mFreeChunk || mFreeChunk->isFull()) {
|
||||||
|
auto new_free_chunk = mChunks.findNotFull();
|
||||||
|
if (!new_free_chunk) {
|
||||||
|
new_free_chunk = new (HeapAllocGlobal::allocate(sizeof(Chunk))) Chunk();
|
||||||
|
DEBUG_ASSERT(new_free_chunk)
|
||||||
|
mChunks.add(new_free_chunk);
|
||||||
|
}
|
||||||
|
mFreeChunk = new_free_chunk;
|
||||||
|
}
|
||||||
|
return mFreeChunk->allocate(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void deallocate(void* aPtr) {
|
||||||
|
auto chunk = mChunks.find(aPtr);
|
||||||
|
(*chunk)->deallocate(aPtr);
|
||||||
|
if ((*chunk)->isEmpty()) {
|
||||||
|
if (mFreeChunk == *chunk) mFreeChunk = nullptr;
|
||||||
|
HeapAllocGlobal::deallocate(*chunk);
|
||||||
|
mChunks.remove(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool checkWrap() const { return false; }
|
||||||
|
|
||||||
|
void checkValid() {
|
||||||
|
return;
|
||||||
|
for (auto i = 0; i < mChunks.mUsedLen; i++) {
|
||||||
|
for (auto j = 0; j < mChunks.mUsedLen; j++) {
|
||||||
|
if (i > j) {
|
||||||
|
ASSERT(mChunks.mBuff[i] > mChunks.mBuff[j])
|
||||||
|
} else if (i < j) {
|
||||||
|
ASSERT(mChunks.mBuff[i] < mChunks.mBuff[j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,302 +1,20 @@
|
||||||
|
|
||||||
#include "Allocators.hpp"
|
#include "Allocators.hpp"
|
||||||
|
|
||||||
#include <math.h>
|
#include "Tests.hpp"
|
||||||
#include <stdio.h>
|
|
||||||
|
using namespace tp;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocators, NULL };
|
tp::ModuleManifest* deps[] = { &tp::gModuleAllocators, &tp::gModuleUtils, nullptr };
|
||||||
tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies);
|
tp::ModuleManifest testModule("AllocatorsTest", nullptr, nullptr, deps);
|
||||||
if (!TestModule.initialize()) {
|
|
||||||
|
if (!testModule.initialize()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
tp::HeapAllocGlobal alloc;
|
testAll();
|
||||||
|
|
||||||
int* val = new(alloc) int();
|
testModule.deinitialize();
|
||||||
delete(alloc, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct test_struct {
|
|
||||||
tp::alni val = 0;
|
|
||||||
|
|
||||||
test_struct() { val = 1; }
|
|
||||||
~test_struct() { val = -1; }
|
|
||||||
|
|
||||||
bool operator==(const test_struct& in) { return in.val == val; }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <tp::alni size>
|
|
||||||
struct allocator_test {
|
|
||||||
test_struct data[size];
|
|
||||||
bool is_allocated[size];
|
|
||||||
tp::alni n_loaded = 0;
|
|
||||||
|
|
||||||
test_struct* allocations[size];
|
|
||||||
|
|
||||||
tp::AbstractAllocator* alloc;
|
|
||||||
tp::AbstractAllocator* parent_alloc;
|
|
||||||
const char* allocator_name = NULL;
|
|
||||||
|
|
||||||
tp::alni rand_idx(bool state) {
|
|
||||||
RAND:
|
|
||||||
|
|
||||||
tp::alni idx = (tp::alni)(tp::randf() * (size + 1));
|
|
||||||
CLAMP(idx, 0, size - 1);
|
|
||||||
|
|
||||||
if (state == is_allocated[idx]) {
|
|
||||||
goto RAND;
|
|
||||||
}
|
|
||||||
return idx;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator_test(tp::AbstractAllocator* palloc, const char* pallocator_name, tp::AbstractAllocator* p_parent_alloc) {
|
|
||||||
allocator_name = pallocator_name;
|
|
||||||
parent_alloc = p_parent_alloc;
|
|
||||||
alloc = palloc;
|
|
||||||
for (tp::alni i = 0; i < size; i++) {
|
|
||||||
RAND:
|
|
||||||
tp::alni val = tp::alni(tp::randf() * (size + 100.f));
|
|
||||||
for (tp::alni check_idx = 0; check_idx < size; check_idx++) {
|
|
||||||
if (data[check_idx].val == val) {
|
|
||||||
goto RAND;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data[i].val = val;
|
|
||||||
is_allocated[i] = false;
|
|
||||||
allocations[i] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void verify_integrity() {
|
|
||||||
// verify data integrity
|
|
||||||
for (tp::alni i = 0; i < size; i++) {
|
|
||||||
if (is_allocated[i]) {
|
|
||||||
assert(*allocations[i] == data[i] && "data is currupted\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (alloc->isWrapSupport()) assert(!alloc->isWrapCorrupted());
|
|
||||||
if (parent_alloc && parent_alloc->isWrapSupport())
|
|
||||||
assert(!parent_alloc->isWrapCorrupted());
|
|
||||||
|
|
||||||
verify_sizes();
|
|
||||||
}
|
|
||||||
|
|
||||||
void verify_sizes() {
|
|
||||||
return;
|
|
||||||
#ifdef MEM_TRACE
|
|
||||||
assert(alloc->sizeInuse() == n_loaded * sizeof(test_struct) &&
|
|
||||||
"invalid inuse size\n");
|
|
||||||
assert(alloc->sizeReserved() >= n_loaded * (tp::alni)sizeof(test_struct) &&
|
|
||||||
"invalid reserved size\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void load_item(tp::alni idx) {
|
|
||||||
if (!is_allocated[idx]) {
|
|
||||||
allocations[idx] = new (alloc) test_struct();
|
|
||||||
|
|
||||||
assert(allocations[idx] && "allocator returned NULL");
|
|
||||||
|
|
||||||
allocations[idx]->val = data[idx].val;
|
|
||||||
is_allocated[idx] = true;
|
|
||||||
n_loaded++;
|
|
||||||
verify_integrity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void unload_item(tp::alni idx) {
|
|
||||||
if (is_allocated[idx]) {
|
|
||||||
verify_integrity();
|
|
||||||
delete allocations[idx];
|
|
||||||
is_allocated[idx] = false;
|
|
||||||
n_loaded--;
|
|
||||||
verify_integrity();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void change_states(tp::Range<tp::alni> rg, bool state, bool reversed = false, bool random = false) {
|
|
||||||
for (auto i : rg) {
|
|
||||||
tp::alni idx = i;
|
|
||||||
|
|
||||||
if (random) {
|
|
||||||
idx = rand_idx(state);
|
|
||||||
}
|
|
||||||
else if (reversed) {
|
|
||||||
idx = size - i - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(state) ? load_item(idx) : unload_item(idx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// full down-up load then up-down unload
|
|
||||||
void test1() {
|
|
||||||
change_states({ 0, size }, 1);
|
|
||||||
change_states({ 0, size }, 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// full down-up load then down-up unload
|
|
||||||
void test2() {
|
|
||||||
change_states({ 0, size }, 1);
|
|
||||||
change_states({ 0, size }, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// full random load then random unload
|
|
||||||
void test3() {
|
|
||||||
change_states({ 0, size }, 1, 0, 1);
|
|
||||||
change_states({ 0, size }, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// multipul tests 1-3
|
|
||||||
void test4() {
|
|
||||||
test1();
|
|
||||||
test1();
|
|
||||||
|
|
||||||
test2();
|
|
||||||
test2();
|
|
||||||
|
|
||||||
test3();
|
|
||||||
test3();
|
|
||||||
}
|
|
||||||
|
|
||||||
static tp::alnf sineupf(tp::alnf asize, tp::alnf x, bool reverse) {
|
|
||||||
tp::alnf end = 4 * PI;
|
|
||||||
tp::alnf a = (2 / 7.f) * asize;
|
|
||||||
tp::alnf b = end / asize;
|
|
||||||
|
|
||||||
tp::alni c = ((-1 * reverse) + (1 * !reverse));
|
|
||||||
tp::alnf c1 = (x - (end * reverse)) / b;
|
|
||||||
tp::alnf c2 = (a * sin(x - (end * reverse)));
|
|
||||||
tp::alnf out = c1 + c2;
|
|
||||||
return c * out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sin load & sin unload with ~1/2 drop factor
|
|
||||||
void test5() {
|
|
||||||
tp::alnf end = 4 * PI;
|
|
||||||
tp::alnf step = end / 4.f;
|
|
||||||
|
|
||||||
for (char i = 0; i < 2; i++) {
|
|
||||||
for (tp::alnf x = 0; x <= end; x += step) {
|
|
||||||
tp::alni target_alloc_count = (tp::alni)ceil(sineupf(size, x, i));
|
|
||||||
CLAMP(target_alloc_count, 0, size);
|
|
||||||
|
|
||||||
while (n_loaded > target_alloc_count) {
|
|
||||||
unload_item(rand_idx(0));
|
|
||||||
}
|
|
||||||
while (n_loaded < target_alloc_count) {
|
|
||||||
load_item(rand_idx(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef MEM_WRAP
|
|
||||||
void check_wrap(tp::alni offset, bool after) {
|
|
||||||
CLAMP(offset, 1, WRAP_LEN);
|
|
||||||
|
|
||||||
test_struct* ts = allocations[rand_idx(0)];
|
|
||||||
tp::alni shift = (sizeof(test_struct) * after) + (offset - 1) * after -
|
|
||||||
offset * (!after);
|
|
||||||
tp::uint1* address = (((tp::uint1*)ts) + shift);
|
|
||||||
|
|
||||||
tp::uint1 val = *address;
|
|
||||||
*address = 5;
|
|
||||||
assert(alloc->isWrapCorrupted());
|
|
||||||
*address = val;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// mem guards test
|
|
||||||
void test6() {
|
|
||||||
change_states({ 0, size }, 1);
|
|
||||||
|
|
||||||
#ifdef MEM_WRAP
|
|
||||||
for (tp::alni after = 0; after < 2; after++) {
|
|
||||||
for (tp::alni offset = 1; offset <= WRAP_LEN; offset++) {
|
|
||||||
check_wrap(offset, after);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
change_states({ 0, size }, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void run_tests() {
|
|
||||||
try {
|
|
||||||
test1();
|
|
||||||
test2();
|
|
||||||
test3();
|
|
||||||
test4();
|
|
||||||
test5();
|
|
||||||
if (alloc->isWrapSupport()) {
|
|
||||||
test6();
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%s - passed\n", allocator_name);
|
|
||||||
if (!alloc->isWrapSupport()) {
|
|
||||||
printf(" WARNING: %s has no wrap support!! \n", allocator_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
printf("%s - failed\n", allocator_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void heap_alloc_test() {
|
|
||||||
tp::alloc_init(); {
|
|
||||||
try {
|
|
||||||
allocator_test<150> hatest(tp::heapalloc, "heap allocator", NULL);
|
|
||||||
hatest.run_tests();
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
printf("heap alloc failed\n");
|
|
||||||
}
|
|
||||||
} tp::alloc_uninit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void chunk_alloc_test() {
|
|
||||||
tp::alloc_init(); {
|
|
||||||
try {
|
|
||||||
tp::ChunkAlloc calloc(sizeof(test_struct), 50);
|
|
||||||
allocator_test<50> ca_test(&calloc, "chunk allocator", tp::heapalloc);
|
|
||||||
ca_test.run_tests();
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
printf("chunk alloc failed\n");
|
|
||||||
}
|
|
||||||
} tp::alloc_uninit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pool_alloc_test() {
|
|
||||||
tp::alloc_init(); {
|
|
||||||
try {
|
|
||||||
tp::PoolAlloc palloc(sizeof(test_struct), 50);
|
|
||||||
allocator_test<150> pa_test(&palloc, "pool allocator", tp::heapalloc);
|
|
||||||
pa_test.run_tests();
|
|
||||||
}
|
|
||||||
catch (...) {
|
|
||||||
printf("chunk alloc failed\n");
|
|
||||||
}
|
|
||||||
} tp::alloc_uninit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocators_test() {
|
|
||||||
printf("running tests on alocators:\n");
|
|
||||||
|
|
||||||
heap_alloc_test();
|
|
||||||
chunk_alloc_test();
|
|
||||||
pool_alloc_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
CROSSPLATFORM_MAIN;
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
tp::print_env_info();
|
|
||||||
allocators_test();
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
#include "Testing.hpp"
|
||||||
|
|
||||||
|
void testAll();
|
||||||
248
Allocators/tests/TestsAll.cpp
Normal file
248
Allocators/tests/TestsAll.cpp
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
|
||||||
|
#include "Utils.hpp"
|
||||||
|
#include "Testing.hpp"
|
||||||
|
|
||||||
|
#include "HeapAllocatorGlobal.hpp"
|
||||||
|
#include "HeapAllocator.hpp"
|
||||||
|
#include "ChunkAllocator.hpp"
|
||||||
|
#include "PoolAllocator.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
using namespace tp;
|
||||||
|
|
||||||
|
struct TestStruct {
|
||||||
|
alni val = 0;
|
||||||
|
|
||||||
|
TestStruct() : val(0) {}
|
||||||
|
explicit TestStruct(alni val) : val(val) {}
|
||||||
|
TestStruct(const TestStruct& in) : val(in.val) {}
|
||||||
|
|
||||||
|
~TestStruct() { val = -1; }
|
||||||
|
|
||||||
|
bool operator==(const TestStruct& in) const {
|
||||||
|
return in.val == val;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <alni tSize, class tAllocator>
|
||||||
|
class TestBenches {
|
||||||
|
|
||||||
|
tAllocator mAlloc{};
|
||||||
|
TestStruct mData[tSize]{};
|
||||||
|
TestStruct* mLoaded[tSize]{};
|
||||||
|
bool mIsLoaded[tSize]{};
|
||||||
|
alni mLoadedNum = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TestBenches() {
|
||||||
|
for (alni i = 0; i < tSize; i++) {
|
||||||
|
mData[i].val = i;
|
||||||
|
mIsLoaded[i] = false;
|
||||||
|
mLoaded[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void runTests() {
|
||||||
|
try {
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
test4();
|
||||||
|
test5();
|
||||||
|
test6();
|
||||||
|
} catch (...) {
|
||||||
|
ASSERT(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
alni randomIdx(bool state, Range<alni> range = { 0, tSize }) {
|
||||||
|
RAND:
|
||||||
|
auto idx = alni(alnf(range.idxBegin()) + randomFloat() * alnf(range.idxDiff() + 1));
|
||||||
|
idx = clamp(idx, alni(0), tSize - 1);
|
||||||
|
if (state == mIsLoaded[idx]) goto RAND;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void verifyIntegrity() {
|
||||||
|
mAlloc.checkValid();
|
||||||
|
ASSERT(!mAlloc.checkWrap())
|
||||||
|
for (alni i = 0; i < tSize; i++) {
|
||||||
|
if (mIsLoaded[i]) {
|
||||||
|
ASSERT(*mLoaded[i] == mData[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadItem(alni idx) {
|
||||||
|
if (mIsLoaded[idx]) return;
|
||||||
|
verifyIntegrity();
|
||||||
|
mLoaded[idx] = new (mAlloc.allocate(sizeof(TestStruct))) TestStruct(mData[idx]);
|
||||||
|
TEST(mLoaded[idx]);
|
||||||
|
mIsLoaded[idx] = true;
|
||||||
|
mLoadedNum++;
|
||||||
|
verifyIntegrity();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unloadItem(alni idx) {
|
||||||
|
if (!mIsLoaded[idx]) return;
|
||||||
|
verifyIntegrity();
|
||||||
|
mLoaded[idx]->~TestStruct();
|
||||||
|
mAlloc.deallocate(mLoaded[idx]);
|
||||||
|
mIsLoaded[idx] = false;
|
||||||
|
mLoadedNum--;
|
||||||
|
verifyIntegrity();
|
||||||
|
}
|
||||||
|
|
||||||
|
void changeStates(Range<alni> rg, bool load, bool reversed = false, bool random = false) {
|
||||||
|
for (auto i : rg) {
|
||||||
|
alni idx = i;
|
||||||
|
if (random) {
|
||||||
|
idx = randomIdx(load, rg);
|
||||||
|
} else if (reversed) {
|
||||||
|
idx = rg.idxEnd() - i - 1;
|
||||||
|
}
|
||||||
|
(load) ? loadItem(idx) : unloadItem(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// full down-up load then up-down unload
|
||||||
|
void test1() {
|
||||||
|
changeStates({ 0, tSize }, true);
|
||||||
|
changeStates({ 0, tSize }, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// full down-up load then down-up unload
|
||||||
|
void test2() {
|
||||||
|
changeStates({0, tSize}, true);
|
||||||
|
changeStates({0, tSize}, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// full random load then random unload
|
||||||
|
void test3() {
|
||||||
|
changeStates({0, tSize}, true, false, true);
|
||||||
|
changeStates({0, tSize}, false, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// combo tests 1-3
|
||||||
|
void test4() {
|
||||||
|
test1();
|
||||||
|
test1();
|
||||||
|
|
||||||
|
test2();
|
||||||
|
test2();
|
||||||
|
|
||||||
|
test3();
|
||||||
|
test3();
|
||||||
|
}
|
||||||
|
|
||||||
|
static alnf sineUpFunction(alnf aSize, alnf aX, bool aReverse) {
|
||||||
|
alnf end = 4 * 3.14159;
|
||||||
|
alnf a = (2 / 7.f) * aSize;
|
||||||
|
alnf b = end / aSize;
|
||||||
|
|
||||||
|
alni c = ((-1 * aReverse) + (1 * !aReverse));
|
||||||
|
alnf c1 = (aX - (end * aReverse)) / b;
|
||||||
|
alnf c2 = (a * sin(aX - (end * aReverse)));
|
||||||
|
alnf out = c1 + c2;
|
||||||
|
return (alnf) c * out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sin load & sin unload with ~1/2 drop factor
|
||||||
|
void test5() {
|
||||||
|
alnf end = 4 * 3.14159;
|
||||||
|
alnf step = end / 4.f;
|
||||||
|
|
||||||
|
for (char i = 0; i < 2; i++) {
|
||||||
|
for (alnf x = 0; x <= end; x += step) {
|
||||||
|
|
||||||
|
alni target_alloc_count = (alni) ceil(sineUpFunction(tSize, x, i));
|
||||||
|
target_alloc_count = clamp(target_alloc_count, alni(0), tSize);
|
||||||
|
|
||||||
|
while (mLoadedNum > target_alloc_count) {
|
||||||
|
unloadItem(randomIdx(0));
|
||||||
|
}
|
||||||
|
while (mLoadedNum < target_alloc_count) {
|
||||||
|
loadItem(randomIdx(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void checkWrap(ualni offset, bool after) {
|
||||||
|
offset = clamp(offset, (ualni) 1, (ualni) MEM_WRAP_SIZE);
|
||||||
|
|
||||||
|
TestStruct* ts = mLoaded[randomIdx(0)];
|
||||||
|
ualni shift = (sizeof(TestStruct) * after) + (offset - 1) * after - offset * (!after);
|
||||||
|
uint1* address = (((uint1*)ts) + shift);
|
||||||
|
|
||||||
|
uint1 val = *address;
|
||||||
|
*address = 5;
|
||||||
|
|
||||||
|
TEST(!mAlloc.checkWrap());
|
||||||
|
|
||||||
|
*address = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mem guards test
|
||||||
|
void test6() {
|
||||||
|
changeStates({0, tSize}, 1);
|
||||||
|
#ifdef MEM_DEBUG
|
||||||
|
for (alni after = 0; after < 2; after++) {
|
||||||
|
for (alni offset = 1; offset <= MEM_WRAP_SIZE; offset++) {
|
||||||
|
checkWrap(offset, after);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
changeStates({0, tSize}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const ualni size = 1000;
|
||||||
|
|
||||||
|
template<typename Alloc>
|
||||||
|
void testAlloc() {
|
||||||
|
try {
|
||||||
|
TestBenches<size, Alloc> heapTests{};
|
||||||
|
heapTests.runTests();
|
||||||
|
} catch (...) {
|
||||||
|
TEST(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DEF_STATIC(GlobalHeap) {
|
||||||
|
testAlloc<HeapAllocGlobal>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DEF_STATIC(Heap) {
|
||||||
|
testAlloc<HeapAlloc>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DEF_STATIC(Chunk) {
|
||||||
|
testAlloc<ChunkAlloc<TestStruct, size>>();
|
||||||
|
testAlloc<ChunkAlloc<TestStruct, size * 2>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DEF_STATIC(Pool) {
|
||||||
|
testAlloc<PoolAlloc<TestStruct, 1>>();
|
||||||
|
testAlloc<PoolAlloc<TestStruct, size / 100>>();
|
||||||
|
testAlloc<PoolAlloc<TestStruct, size>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DEF_STATIC(Simple) {
|
||||||
|
auto a = new TestStruct(-1);
|
||||||
|
delete a;
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_DEF(All) {
|
||||||
|
testSimple();
|
||||||
|
|
||||||
|
testGlobalHeap();
|
||||||
|
testHeap();
|
||||||
|
testChunk();
|
||||||
|
testPool();
|
||||||
|
|
||||||
|
// TEST(HeapAllocGlobal::checkLeaks());
|
||||||
|
// TEST(false);
|
||||||
|
}
|
||||||
|
|
@ -14,4 +14,4 @@ add_subdirectory(Modules)
|
||||||
add_subdirectory(Utils)
|
add_subdirectory(Utils)
|
||||||
add_subdirectory(Containers)
|
add_subdirectory(Containers)
|
||||||
add_subdirectory(Math)
|
add_subdirectory(Math)
|
||||||
#add_subdirectory(Allocators)
|
add_subdirectory(Allocators)
|
||||||
|
|
@ -26,7 +26,7 @@ tp::ualni TestAllocator::getAllocationsCount() const {
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
||||||
tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr };
|
tp::ModuleManifest* deps[] = { &tp::gModuleContainers, &tp::gModuleUtils, nullptr };
|
||||||
tp::ModuleManifest testModule("ContainersTest", init, nullptr, deps);
|
tp::ModuleManifest testModule("ContainersTest", init, nullptr, deps);
|
||||||
|
|
||||||
if (!testModule.initialize()) {
|
if (!testModule.initialize()) {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ namespace tp {
|
||||||
|
|
||||||
Mat& operator=(const Mat& in) {
|
Mat& operator=(const Mat& in) {
|
||||||
if (&in == this) return *this;
|
if (&in == this) return *this;
|
||||||
memcp(this, &in, sizeof(Mat<Type, tNRows, tNColoumns>));
|
memCopy(this, &in, sizeof(Mat<Type, tNRows, tNColoumns>));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -341,7 +341,7 @@ namespace tp {
|
||||||
}
|
}
|
||||||
|
|
||||||
Mat& operator=(const Mat& in) {
|
Mat& operator=(const Mat& in) {
|
||||||
memcp(this, &in, sizeof(Mat2<Type>));
|
memCopy(this, &in, sizeof(Mat2<Type>));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ namespace tp {
|
||||||
}
|
}
|
||||||
|
|
||||||
Vec(const Vec& in) {
|
Vec(const Vec& in) {
|
||||||
memcp(mBuff, in.mBuff, sizeof(Type) * tSize);
|
memCopy(mBuff, in.mBuff, sizeof(Type) * tSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
Type& operator[](ualni i) {
|
Type& operator[](ualni i) {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
#include "Common.hpp"
|
#include "Common.hpp"
|
||||||
#include "Assert.hpp"
|
#include "Assert.hpp"
|
||||||
|
|
||||||
#define MODULE_SANITY_CHECK(name) ASSERT(name.isInitialized() && "Modules Is Not Initialized" && #name)
|
#define MODULE_SANITY_CHECK(name) DEBUG_ASSERT(name.isInitialized() && "Modules Is Not Initialized" && #name)
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
||||||
|
|
|
||||||
4
TODO
4
TODO
|
|
@ -1,4 +1,5 @@
|
||||||
All:
|
All:
|
||||||
|
Testing
|
||||||
Serialization
|
Serialization
|
||||||
|
|
||||||
Containers:
|
Containers:
|
||||||
|
|
@ -11,9 +12,6 @@ Containers:
|
||||||
Strings:
|
Strings:
|
||||||
Implement
|
Implement
|
||||||
|
|
||||||
Math:
|
|
||||||
Testing
|
|
||||||
|
|
||||||
Utils:
|
Utils:
|
||||||
Stack trace fixes
|
Stack trace fixes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,12 @@ using namespace tp;
|
||||||
CallStackCapture* tp::gCSCapture = nullptr;
|
CallStackCapture* tp::gCSCapture = nullptr;
|
||||||
|
|
||||||
void initializeCallStackCapture() {
|
void initializeCallStackCapture() {
|
||||||
gCSCapture = new CallStackCapture();
|
gCSCapture = new (malloc(sizeof(CallStackCapture))) CallStackCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
void deinitializeCallStackCapture() {
|
void deinitializeCallStackCapture() {
|
||||||
delete gCSCapture;
|
gCSCapture->~CallStackCapture();
|
||||||
|
free(gCSCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
ualni CallStackCapture::CallStack::getDepth() const {
|
ualni CallStackCapture::CallStack::getDepth() const {
|
||||||
|
|
@ -198,6 +199,21 @@ void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbol
|
||||||
free(symbolsArray);
|
free(symbolsArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::printSnapshot(const CallStack* snapshot) {
|
||||||
|
printf("CallStack: \n");
|
||||||
|
for (auto frame : *snapshot) {
|
||||||
|
auto symbols = gCSCapture->getSymbols(frame.getFrame());
|
||||||
|
printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine());
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void CallStackCapture::logLeaks() {
|
||||||
|
for (auto cs : *this) {
|
||||||
|
printSnapshot(cs.getCallStack());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
void CallStackCapture::platformWriteStackTrace(CallStack* stack) { stack->frames[0] = 0; }
|
void CallStackCapture::platformWriteStackTrace(CallStack* stack) { stack->frames[0] = 0; }
|
||||||
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
|
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,8 @@ Testing tp::gTesting;
|
||||||
|
|
||||||
void Testing::startTest(const char* name) {
|
void Testing::startTest(const char* name) {
|
||||||
MODULE_SANITY_CHECK(gModuleUtils)
|
MODULE_SANITY_CHECK(gModuleUtils)
|
||||||
mCurrent->mSubTests.pushBack(new TestingNode{ {}, {}, name, mCurrent });
|
auto newNode = new (malloc(sizeof(TestingNode))) TestingNode{ {}, {}, name, mCurrent };
|
||||||
|
mCurrent->mSubTests.pushBack(newNode);
|
||||||
mCurrent = mCurrent->mSubTests.last()->data;
|
mCurrent = mCurrent->mSubTests.last()->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,6 +77,7 @@ void Testing::TestingNode::report(const char* path) const {
|
||||||
|
|
||||||
Testing::TestingNode::~TestingNode() {
|
Testing::TestingNode::~TestingNode() {
|
||||||
for (const auto& child : mSubTests) {
|
for (const auto& child : mSubTests) {
|
||||||
delete child.data();
|
child.data()->~TestingNode();
|
||||||
|
free(child.data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,12 @@
|
||||||
void initializeCallStackCapture();
|
void initializeCallStackCapture();
|
||||||
void deinitializeCallStackCapture();
|
void deinitializeCallStackCapture();
|
||||||
|
|
||||||
static bool initialize(const tp::ModuleManifest* self) {
|
static bool initialize(const tp::ModuleManifest*) {
|
||||||
initializeCallStackCapture();
|
initializeCallStackCapture();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deinitialize(const tp::ModuleManifest* self) {
|
static void deinitialize(const tp::ModuleManifest*) {
|
||||||
deinitializeCallStackCapture();
|
deinitializeCallStackCapture();
|
||||||
tp::gTesting.reportState();
|
tp::gTesting.reportState();
|
||||||
if (tp::gTesting.hasFailed()) { exit(1); }
|
if (tp::gTesting.hasFailed()) { exit(1); }
|
||||||
|
|
@ -26,40 +26,40 @@ namespace tp {
|
||||||
static ModuleManifest* sModuleUtilsDeps[] = { &gModuleContainers, nullptr };
|
static ModuleManifest* sModuleUtilsDeps[] = { &gModuleContainers, nullptr };
|
||||||
ModuleManifest gModuleUtils = ModuleManifest("Utils", initialize, deinitialize, sModuleUtilsDeps);
|
ModuleManifest gModuleUtils = ModuleManifest("Utils", initialize, deinitialize, sModuleUtilsDeps);
|
||||||
|
|
||||||
void memsetv(void* p, uhalni bytesize, uint1 val) {
|
void memSetVal(void* p, uhalni byteSize, uint1 val) {
|
||||||
MODULE_SANITY_CHECK(gModuleBase)
|
MODULE_SANITY_CHECK(gModuleBase)
|
||||||
|
|
||||||
alni alignedval = 0;
|
alni alignedVal = val;
|
||||||
for (ualni idx = 0; idx < sizeof(alni); idx++) {
|
alignedVal = (alignedVal << 8) | alignedVal;
|
||||||
((uint1*) &alignedval)[idx] = val;
|
alignedVal = (alignedVal << 16) | alignedVal;
|
||||||
|
alignedVal = (alignedVal << 32) | alignedVal;
|
||||||
|
|
||||||
|
ualni alignedLen = byteSize / sizeof(alni);
|
||||||
|
for (ualni idx = 0; idx < alignedLen; idx++) {
|
||||||
|
((alni*) p)[idx] = alignedVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
ualni alignedlen = bytesize / sizeof(alni);
|
ualni unalignedLen = byteSize - (alignedLen * sizeof(alni));
|
||||||
for (ualni idx = 0; idx < alignedlen; idx++) {
|
for (ualni idx = 0; idx < unalignedLen; idx++) {
|
||||||
((alni*) p)[idx] = alignedval;
|
((uint1*) p)[byteSize - idx - 1] = val;
|
||||||
}
|
|
||||||
|
|
||||||
ualni unalignedlen = bytesize - (alignedlen * sizeof(alni));
|
|
||||||
for (ualni idx = 0; idx < unalignedlen; idx++) {
|
|
||||||
((uint1*) p)[bytesize - idx - 1] = val;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void memcp(void* left, const void* right, uhalni len) {
|
void memCopy(void* left, const void* right, uhalni len) {
|
||||||
MODULE_SANITY_CHECK(gModuleBase)
|
MODULE_SANITY_CHECK(gModuleBase)
|
||||||
|
|
||||||
ualni alignedlen = len / sizeof(alni);
|
ualni alignedLen = len / sizeof(alni);
|
||||||
for (ualni idx = 0; idx < alignedlen; idx++) {
|
for (ualni idx = 0; idx < alignedLen; idx++) {
|
||||||
((alni*) left)[idx] = ((alni*) right)[idx];
|
((alni*) left)[idx] = ((alni*) right)[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
ualni unalignedlen = len - (alignedlen * sizeof(alni));
|
ualni unalignedLen = len - (alignedLen * sizeof(alni));
|
||||||
for (ualni idx = 0; idx < unalignedlen; idx++) {
|
for (ualni idx = 0; idx < unalignedLen; idx++) {
|
||||||
((uint1*) left)[len - idx - 1] = ((uint1*) right)[len - idx - 1];
|
((uint1*) left)[len - idx - 1] = ((uint1*) right)[len - idx - 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int1 memecomp(const void* left, const void* right, uhalni len) {
|
int1 memCompare(const void* left, const void* right, uhalni len) {
|
||||||
MODULE_SANITY_CHECK(gModuleBase)
|
MODULE_SANITY_CHECK(gModuleBase)
|
||||||
if (!len) return 0;
|
if (!len) return 0;
|
||||||
|
|
||||||
|
|
@ -87,7 +87,41 @@ namespace tp {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
alnf randf() {
|
|
||||||
|
int1 memCompareVal(const void* left, uhalni len, uint1 val) {
|
||||||
|
MODULE_SANITY_CHECK(gModuleBase)
|
||||||
|
if (!len) return 0;
|
||||||
|
|
||||||
|
alni valAligned = val;
|
||||||
|
valAligned = (valAligned << 8) | valAligned;
|
||||||
|
valAligned = (valAligned << 16) | valAligned;
|
||||||
|
valAligned = (valAligned << 32) | valAligned;
|
||||||
|
|
||||||
|
ualni alignedLength = len / sizeof(alni);
|
||||||
|
for (ualni idx = 0; idx < alignedLength; idx++) {
|
||||||
|
if (((alni*) left)[idx] == valAligned) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (((alni*) left)[idx] > valAligned) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ualni unalignedLength = len - (alignedLength * sizeof(alni));
|
||||||
|
for (ualni idx = 0; idx < unalignedLength; idx++) {
|
||||||
|
if (((uint1*) left)[len - idx - 1] == val) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (((uint1*) left)[len - idx - 1] > val) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
alnf randomFloat() {
|
||||||
alnf r = static_cast<alnf>(std::rand()) / static_cast<alnf>(RAND_MAX);
|
alnf r = static_cast<alnf>(std::rand()) / static_cast<alnf>(RAND_MAX);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ namespace tp {
|
||||||
[[nodiscard]] const CallStack* getSnapshot();
|
[[nodiscard]] const CallStack* getSnapshot();
|
||||||
const DebugSymbols* getSymbols(FramePointer fp);
|
const DebugSymbols* getSymbols(FramePointer fp);
|
||||||
|
|
||||||
|
static void printSnapshot(const CallStack* snapshot);
|
||||||
|
void logLeaks();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
template<class Saver>
|
template<class Saver>
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,14 @@ namespace tp {
|
||||||
|
|
||||||
extern ModuleManifest gModuleUtils;
|
extern ModuleManifest gModuleUtils;
|
||||||
|
|
||||||
void memsetv(void* p, uhalni bytesize, uint1 val);
|
void memSetVal(void* p, uhalni byteSize, uint1 val);
|
||||||
void memcp(void* left, const void* right, uhalni len);
|
void memCopy(void* left, const void* right, uhalni len);
|
||||||
int1 memequal(const void* left, const void* right, uhalni len);
|
int1 memCompare(const void* left, const void* right, uhalni len);
|
||||||
|
int1 memCompareVal(const void* left, uhalni len, uint1 val);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
[[nodiscard]] alnf randf();
|
[[nodiscard]] alnf randomFloat();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace tp {
|
namespace tp {
|
||||||
|
|
@ -73,6 +74,7 @@ namespace tp {
|
||||||
|
|
||||||
tType idxBegin() const { return mBegin; }
|
tType idxBegin() const { return mBegin; }
|
||||||
tType idxEnd() const { return mEnd; }
|
tType idxEnd() const { return mEnd; }
|
||||||
|
tType idxDiff() const { return mEnd - mBegin; }
|
||||||
Iterator begin() { return Iterator(mBegin); }
|
Iterator begin() { return Iterator(mBegin); }
|
||||||
Iterator end() { return Iterator(mEnd); }
|
Iterator end() { return Iterator(mEnd); }
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -47,9 +47,7 @@ void root() {
|
||||||
TEST_DEF(Debugging) {
|
TEST_DEF(Debugging) {
|
||||||
root();
|
root();
|
||||||
|
|
||||||
for (auto cs : *gCSCapture) {
|
gCSCapture->logLeaks();
|
||||||
printSnapshot(cs.getCallStack());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue