From db05d963be2267c7f1f4650a7b2bc6cf2f345f8d Mon Sep 17 00:00:00 2001 From: Ilusha <63184036+IlyaShurupov@users.noreply.github.com> Date: Sun, 28 May 2023 01:16:30 +0300 Subject: [PATCH] Initial --- .clang-format | 66 ++ .github/workflows/cmake.yml | 42 + .gitignore | 4 +- Allocators/CMakeLists.txt | 20 + README.md => Allocators/README.md | 2 +- .../private}/Allocators.cpp | 8 +- .../private}/ChunkAllocator.cpp | 7 +- .../private}/HeapAllocator.cpp | 20 +- .../private}/HeapAllocatorGlobal.cpp | 23 +- .../private}/PickAllocator.cpp | 0 .../private}/PoolAllocator.cpp | 23 +- .../private}/PrivateConfig.hpp | 2 - Allocators/public/Allocators.hpp | 29 + .../public}/ChunkAllocator.hpp | 13 +- .../public}/HeapAllocator.hpp | 6 +- .../public}/HeapAllocatorGlobal.hpp | 5 +- .../public}/PickAllocator.hpp | 0 .../public}/PoolAllocator.hpp | 0 Allocators/tests/Tests.cpp | 305 ++++++ .../tests/Tests.hpp | 0 BaseModule/CMakeLists.txt | 20 + .../patterns.cpp => BaseModule/README.md | 0 BaseModule/private/Assert.cpp | 26 + BaseModule/private/BaseModule.cpp | 81 ++ BaseModule/private/Common.cpp | 46 + BaseModule/private/Environment.cpp | 21 + BaseModule/public/Assert.hpp | 34 + BaseModule/public/BaseModule.hpp | 31 + BaseModule/public/Common.hpp | 59 ++ BaseModule/public/Environment.hpp | 198 ++++ BaseModule/public/TypeInfo.hpp | 927 ++++++++++++++++++ BaseModule/tests/Tests.cpp | 15 + CMakeLists.txt | 16 + Containers/CMakeLists.txt | 22 + Containers/private/Containers.cpp | 18 + Containers/public/AvlTree.hpp | 360 +++++++ Containers/public/ContainersCommon.hpp | 26 + Containers/public/List.hpp | 322 ++++++ Containers/public/Map.hpp | 397 ++++++++ Containers/tests/AvlTest.cpp | 31 + Containers/tests/ListTest.cpp | 88 ++ Containers/tests/MapTest.cpp | 137 +++ Containers/tests/Tests.cpp | 41 + Containers/tests/Tests.hpp | 73 ++ README.MD | 1 + Utils/CMakeLists.txt | 24 + Utils/private/Debugging.cpp | 207 ++++ Utils/private/Testing.cpp | 73 ++ Utils/private/Timing.cpp | 81 ++ Utils/private/Utils.cpp | 94 ++ Utils/public/Debugging.hpp | 122 +++ Utils/public/Sorting.hpp | 97 ++ Utils/public/Testing.hpp | 63 ++ Utils/public/Timing.hpp | 58 ++ Utils/public/Utils.hpp | 79 ++ Utils/tests/Tests.cpp | 67 ++ private/StackTrace.cpp | 186 ---- private/StackTrace.hpp | 43 - public/Allocators.hpp | 26 - tests/Tests.cpp | 18 - tests/alloctests.cpp | 291 ------ tests/alloctests.h | 3 - tools/MemBenchmark/benchmark_simple.cpp | 172 ---- tools/MemBenchmark/benchmarker.cpp | 563 ----------- tools/MemBenchmark/benchmarker.h | 100 -- tools/MemBenchmark/collector.cpp | 119 --- tools/MemBenchmark/collector.h | 43 - tools/MemBenchmark/patterns.h | 174 ---- tools/MemLeaks/MemleaksEntry.cpp | 93 -- tools/MemLeaks/memleaks.cpp | 403 -------- tools/MemLeaks/memleaks.h | 137 --- tools/MemUsage/MemUsageEntry.cpp | 1 - tools/MemUsage/memusage.cpp | 401 -------- tools/MemUsage/memusage.h | 401 -------- 74 files changed, 4473 insertions(+), 3231 deletions(-) create mode 100644 .clang-format create mode 100644 .github/workflows/cmake.yml create mode 100644 Allocators/CMakeLists.txt rename README.md => Allocators/README.md (84%) rename {private => Allocators/private}/Allocators.cpp (89%) rename {private => Allocators/private}/ChunkAllocator.cpp (90%) rename {private => Allocators/private}/HeapAllocator.cpp (72%) rename {private => Allocators/private}/HeapAllocatorGlobal.cpp (84%) rename {private => Allocators/private}/PickAllocator.cpp (100%) rename {private => Allocators/private}/PoolAllocator.cpp (85%) rename {private => Allocators/private}/PrivateConfig.hpp (94%) create mode 100644 Allocators/public/Allocators.hpp rename {public => Allocators/public}/ChunkAllocator.hpp (69%) rename {public => Allocators/public}/HeapAllocator.hpp (66%) rename {public => Allocators/public}/HeapAllocatorGlobal.hpp (72%) rename {public => Allocators/public}/PickAllocator.hpp (100%) rename {public => Allocators/public}/PoolAllocator.hpp (100%) create mode 100644 Allocators/tests/Tests.cpp rename tools/MemBenchmark/BenchmarkEntry.cpp => Allocators/tests/Tests.hpp (100%) create mode 100644 BaseModule/CMakeLists.txt rename tools/MemBenchmark/patterns.cpp => BaseModule/README.md (100%) create mode 100644 BaseModule/private/Assert.cpp create mode 100644 BaseModule/private/BaseModule.cpp create mode 100644 BaseModule/private/Common.cpp create mode 100644 BaseModule/private/Environment.cpp create mode 100644 BaseModule/public/Assert.hpp create mode 100644 BaseModule/public/BaseModule.hpp create mode 100644 BaseModule/public/Common.hpp create mode 100644 BaseModule/public/Environment.hpp create mode 100644 BaseModule/public/TypeInfo.hpp create mode 100644 BaseModule/tests/Tests.cpp create mode 100644 CMakeLists.txt create mode 100644 Containers/CMakeLists.txt create mode 100644 Containers/private/Containers.cpp create mode 100644 Containers/public/AvlTree.hpp create mode 100644 Containers/public/ContainersCommon.hpp create mode 100644 Containers/public/List.hpp create mode 100644 Containers/public/Map.hpp create mode 100644 Containers/tests/AvlTest.cpp create mode 100644 Containers/tests/ListTest.cpp create mode 100644 Containers/tests/MapTest.cpp create mode 100644 Containers/tests/Tests.cpp create mode 100644 Containers/tests/Tests.hpp create mode 100644 README.MD create mode 100644 Utils/CMakeLists.txt create mode 100644 Utils/private/Debugging.cpp create mode 100644 Utils/private/Testing.cpp create mode 100644 Utils/private/Timing.cpp create mode 100644 Utils/private/Utils.cpp create mode 100644 Utils/public/Debugging.hpp create mode 100644 Utils/public/Sorting.hpp create mode 100644 Utils/public/Testing.hpp create mode 100644 Utils/public/Timing.hpp create mode 100644 Utils/public/Utils.hpp create mode 100644 Utils/tests/Tests.cpp delete mode 100644 private/StackTrace.cpp delete mode 100644 private/StackTrace.hpp delete mode 100644 public/Allocators.hpp delete mode 100644 tests/Tests.cpp delete mode 100644 tests/alloctests.cpp delete mode 100644 tests/alloctests.h delete mode 100644 tools/MemBenchmark/benchmark_simple.cpp delete mode 100644 tools/MemBenchmark/benchmarker.cpp delete mode 100644 tools/MemBenchmark/benchmarker.h delete mode 100644 tools/MemBenchmark/collector.cpp delete mode 100644 tools/MemBenchmark/collector.h delete mode 100644 tools/MemBenchmark/patterns.h delete mode 100644 tools/MemLeaks/MemleaksEntry.cpp delete mode 100644 tools/MemLeaks/memleaks.cpp delete mode 100644 tools/MemLeaks/memleaks.h delete mode 100644 tools/MemUsage/MemUsageEntry.cpp delete mode 100644 tools/MemUsage/memusage.cpp delete mode 100644 tools/MemUsage/memusage.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1b9e01e --- /dev/null +++ b/.clang-format @@ -0,0 +1,66 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -2 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: None +AlignOperands: Align +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 0 +CompactNamespaces: false +ContinuationIndentWidth: 4 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 2 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Left +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 2 +UseTab: Never diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml new file mode 100644 index 0000000..0e85c3d --- /dev/null +++ b/.github/workflows/cmake.yml @@ -0,0 +1,42 @@ +name: CMake + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. + # You can convert this to a matrix build if you need cross-platform coverage. + # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Install LLVM + run: sudo apt-get install -y llvm + + - name: Set LLVM Toolchain + run: | + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 10 + sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 20 + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + # Execute tests defined by the CMake configuration. + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest -C ${{env.BUILD_TYPE}} + diff --git a/.gitignore b/.gitignore index f85b25e..ec1e45e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ .idea -tmp +*tmp* bin -build +build* lib install diff --git a/Allocators/CMakeLists.txt b/Allocators/CMakeLists.txt new file mode 100644 index 0000000..70ad8d9 --- /dev/null +++ b/Allocators/CMakeLists.txt @@ -0,0 +1,20 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(Allocator) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_link_libraries(${PROJECT_NAME} PUBLIC Utils) + +### -------------------------- Tests -------------------------- ### +enable_testing() +add_executable(${PROJECT_NAME}Tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Tests.cpp) +target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME}) +add_test(${PROJECT_NAME} ${PROJECT_NAME}Tests) + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) \ No newline at end of file diff --git a/README.md b/Allocators/README.md similarity index 84% rename from README.md rename to Allocators/README.md index 9e4f3e7..a2b4861 100644 --- a/README.md +++ b/Allocators/README.md @@ -30,7 +30,7 @@ If memory leaks were detected it will be loged in the output console. ![image](https://user-images.githubusercontent.com/63184036/222794298-3f238de4-c0b8-41fa-b7ec-c0c675da8f05.png) -Also debug.memleaks file will be generated in the working directory that can be viewved with MemLeaks Viewver. +Also debug.memleaks binary will be generated in the working directory that can be viewved with MemLeaks Viewver. ![image](https://user-images.githubusercontent.com/63184036/222793169-a405effe-72be-42fc-b375-bb06dce0a735.png) diff --git a/private/Allocators.cpp b/Allocators/private/Allocators.cpp similarity index 89% rename from private/Allocators.cpp rename to Allocators/private/Allocators.cpp index 86785fb..e4e38f4 100644 --- a/private/Allocators.cpp +++ b/Allocators/private/Allocators.cpp @@ -1,14 +1,10 @@ -#include "allocators.hpp" +#include "Allocators.hpp" -#include "filesystem.h" - -static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleFilesystem, NULL }; +static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleBase, NULL }; tp::ModuleManifest tp::gModuleAllocator = ModuleManifest("Allocators", NULL, NULL, sModuleDependencies); -//void* operator new(size_t aSize, void* aWhere) noexcept { return aWhere; } - 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); } diff --git a/private/ChunkAllocator.cpp b/Allocators/private/ChunkAllocator.cpp similarity index 90% rename from private/ChunkAllocator.cpp rename to Allocators/private/ChunkAllocator.cpp index a66a997..3ab8b28 100644 --- a/private/ChunkAllocator.cpp +++ b/Allocators/private/ChunkAllocator.cpp @@ -49,7 +49,7 @@ ChunkAlloc::ChunkAlloc(ualni aBlockSize, ualni aNBlocks) { } void* ChunkAlloc::allocate() { - RelAssert(mNFreeBlocks && "Out Of Memory"); + ASSERT(mNFreeBlocks && "Out Of Memory"); // 1) PreInitialize blocks if (mNInitBlocks < mNBlocks) { @@ -103,10 +103,11 @@ void ChunkAlloc::deallocate(void* aPtr) { mNFreeBlocks++; } -bool ChunkAlloc::isFull() { return !mNFreeBlocks; } -bool ChunkAlloc::isEmpty() { return mNFreeBlocks == mNBlocks; } +bool ChunkAlloc::isFull() const { return !mNFreeBlocks; } +bool ChunkAlloc::isEmpty() const { return mNFreeBlocks == mNBlocks; } ChunkAlloc::~ChunkAlloc() { + // TODO : check for leaks if (mOwnBuff) { HeapAllocGlobal::deallocate(mBuff); } diff --git a/private/HeapAllocator.cpp b/Allocators/private/HeapAllocator.cpp similarity index 72% rename from private/HeapAllocator.cpp rename to Allocators/private/HeapAllocator.cpp index b520957..c6e73b6 100644 --- a/private/HeapAllocator.cpp +++ b/Allocators/private/HeapAllocator.cpp @@ -1,5 +1,8 @@ -#include "heapallocator.hpp" + +#include "HeapAllocator.hpp" +#include "HeapAllocatorGlobal.hpp" + #include "PrivateConfig.hpp" #include @@ -8,10 +11,12 @@ using namespace tp; #if not defined(MEM_DEBUG) + // ----------------------- Release Implementation ---------------------------- // void* HeapAlloc::allocate(ualni aBlockSize) { return malloc(aBlockSize); } void HeapAlloc::deallocate(void* aPtr) { free(aPtr); } HeapAlloc::~HeapAlloc() {} + #else namespace tp { @@ -22,7 +27,7 @@ namespace tp { }; void* HeapAlloc::allocate(ualni aBlockSize) { - auto head = (MemHeadLocal*)mAlloc.allocate(aBlockSize + sizeof(MemHeadLocal)); + auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal)); auto out = head + 1; mNumAllocations++; @@ -33,8 +38,8 @@ void* HeapAlloc::allocate(ualni aBlockSize) { if (mEntry->mPrev) mEntry->mPrev->mNext = head; } else { - head->mNext = NULL; - head->mPrev = NULL; + head->mNext = nullptr; + head->mPrev = nullptr; } mEntry = head; @@ -52,18 +57,19 @@ void HeapAlloc::deallocate(void* aPtr) { mEntry = mEntry->mNext; } else { - mEntry = mEntry->mNext; + mEntry = mEntry->mPrev; } } - mAlloc.deallocate(head); + HeapAllocGlobal::deallocate(head); } HeapAlloc::~HeapAlloc() { if (mNumAllocations) { - DBG_BREAK("Destruction of not freed Allocator"); + DEBUG_BREAK("Destruction of not freed Allocator"); #ifdef MEM_STACK_TRACE + // TODO : log leaks and free them up #endif } } diff --git a/private/HeapAllocatorGlobal.cpp b/Allocators/private/HeapAllocatorGlobal.cpp similarity index 84% rename from private/HeapAllocatorGlobal.cpp rename to Allocators/private/HeapAllocatorGlobal.cpp index 4bade50..3a37688 100644 --- a/private/HeapAllocatorGlobal.cpp +++ b/Allocators/private/HeapAllocatorGlobal.cpp @@ -1,8 +1,9 @@ -#include "heapallocator.hpp" + +#include "HeapAllocatorGlobal.hpp" #include "PrivateConfig.hpp" -#include "StackTrace.hpp" +#include "Debugging.hpp" #include #include @@ -12,14 +13,15 @@ using namespace tp; #if not defined(MEM_DEBUG) // ----------------------- Release Implementation ---------------------------- // + void* HeapAllocGlobal::allocate(ualni aBlockSize) { return malloc(aBlockSize); } void HeapAllocGlobal::deallocate(void* aPtr) { free(aPtr); } -HeapAllocGlobal::~HeapAllocGlobal() {}; +HeapAllocGlobal::~HeapAllocGlobal() = default; #else -tp::MemHead* tp::HeapAllocGlobal::mEntry = NULL; -tp::ualni tp::HeapAllocGlobal::mNumAllocations = NULL; +tp::MemHead* tp::HeapAllocGlobal::mEntry = nullptr; +tp::ualni tp::HeapAllocGlobal::mNumAllocations = 0; // ----------------------- Debug Implementation ---------------------------- // // |----------------| @@ -66,7 +68,7 @@ void* HeapAllocGlobal::allocate(ualni aBlockSize) { auto data = wrap_top + WRAP_SIZE; auto wrap_bottom = data + aBlockSize; - if (!head) { return NULL; } + if (!head) { return nullptr; } head->mBlockSize = aBlockSize; // 2) Link with existing blocks @@ -78,8 +80,8 @@ void* HeapAllocGlobal::allocate(ualni aBlockSize) { if (mEntry->mPrev) mEntry->mPrev->mNext = head; } else { - head->mNext = NULL; - head->mPrev = NULL; + head->mNext = nullptr; + head->mPrev = nullptr; } mEntry = head; @@ -116,7 +118,7 @@ void HeapAllocGlobal::deallocate(void* aPtr) { mEntry = mEntry->mNext; } else { - mEntry = mEntry->mNext; + mEntry = mEntry->mPrev; } } @@ -135,9 +137,10 @@ void HeapAllocGlobal::deallocate(void* aPtr) { HeapAllocGlobal::~HeapAllocGlobal() { // 1) Check for not deallocated memory if (mNumAllocations) { - DBG_BREAK("Destruction of not freed Allocator"); + DEBUG_BREAK("Destruction of not freed Allocator"); #ifdef MEM_STACK_TRACE + // TODO: log leaks #endif } } diff --git a/private/PickAllocator.cpp b/Allocators/private/PickAllocator.cpp similarity index 100% rename from private/PickAllocator.cpp rename to Allocators/private/PickAllocator.cpp diff --git a/private/PoolAllocator.cpp b/Allocators/private/PoolAllocator.cpp similarity index 85% rename from private/PoolAllocator.cpp rename to Allocators/private/PoolAllocator.cpp index 6388811..ea4d52b 100644 --- a/private/PoolAllocator.cpp +++ b/Allocators/private/PoolAllocator.cpp @@ -3,19 +3,19 @@ * 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 deallocation in log time +* 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) ... * -* Deallocations: +* De-allocations: * 1) binary-search with delete pointer to find desired chunk * 2) ... * */ -#include "allocators.hpp" +#include "Allocators.hpp" #include "PrivateConfig.hpp" void tp::PoolAlloc::Chunks::add(Chunk* aChunk) { @@ -29,7 +29,7 @@ void tp::PoolAlloc::Chunks::add(Chunk* aChunk) { // check for buff overflow if (mUsedLen == mLen) { auto prevBuff = mBuff; - mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen * 2); + mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen * 2); memcp(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen); mLen *= 2; HeapAllocGlobal::deallocate(prevBuff); @@ -44,7 +44,7 @@ void tp::PoolAlloc::Chunks::remove(Chunk* aChunk) { mUsedLen--; // check for buff low usage - if ((halnf)mUsedLen / mLen < 0.25f) { + if ((halnf)mUsedLen / (halnf)mLen < 0.25f) { auto prevBuff = mBuff; mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2); memcp(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen); @@ -63,7 +63,7 @@ tp::PoolAlloc::Chunk* tp::PoolAlloc::Chunks::findNotFull() { return mBuff[idx]; } } - return NULL; + return nullptr; } tp::PoolAlloc::Chunk** tp::PoolAlloc::Chunks::findUtil(Chunk** aLeft, Chunk** aRight, void* aPtr) { @@ -84,7 +84,7 @@ void* tp::PoolAlloc::allocate() { if (!new_free_chunk) { new_free_chunk = new ((Chunk*)HeapAllocGlobal::allocate(sizeof(Chunk))) Chunk(mBlockSize, mChunkSize); - RelAssert(new_free_chunk); + ASSERT(new_free_chunk); mChunks.add(new_free_chunk); if (mFreeChunk) { @@ -94,8 +94,8 @@ void* tp::PoolAlloc::allocate() { if (mFreeChunk->mPrev) mFreeChunk->mPrev->mNext = new_free_chunk; } else { - new_free_chunk->mNext = NULL; - new_free_chunk->mPrev = NULL; + new_free_chunk->mNext = nullptr; + new_free_chunk->mPrev = nullptr; } mFreeChunk = new_free_chunk; @@ -111,7 +111,7 @@ void tp::PoolAlloc::deallocate(void* aPtr) { if (mFreeChunk->isEmpty()) { - Chunk* new_chunk = NULL; + 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]; @@ -128,5 +128,4 @@ void tp::PoolAlloc::deallocate(void* aPtr) { } } -tp::PoolAlloc::~PoolAlloc() { -} +tp::PoolAlloc::~PoolAlloc() = default; diff --git a/private/PrivateConfig.hpp b/Allocators/private/PrivateConfig.hpp similarity index 94% rename from private/PrivateConfig.hpp rename to Allocators/private/PrivateConfig.hpp index 4f09d53..beacb7e 100644 --- a/private/PrivateConfig.hpp +++ b/Allocators/private/PrivateConfig.hpp @@ -1,7 +1,5 @@ #pragma once -#include "PublicConfig.hpp" - #define MEM_WRAP_SIZE 8 // Wrapping Size in aligned units #define MEM_WRAP_FILL_VAL 0xBB // Wrapping Fill Value #define MEM_CLEAR_ON_ALLOC // Clear data on allocation diff --git a/Allocators/public/Allocators.hpp b/Allocators/public/Allocators.hpp new file mode 100644 index 0000000..75a0844 --- /dev/null +++ b/Allocators/public/Allocators.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "BaseModule.hpp" + +#include "HeapAllocatorGlobal.hpp" +#include "HeapAllocator.hpp" +#include "ChunkAllocator.hpp" +#include "PoolAllocator.hpp" + +namespace tp { + extern ModuleManifest gModuleAllocator; +}; + +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 delete(void* aPtr); +void operator delete[](void* aPtr); + +void* operator new(std::size_t aSize, tp::HeapAlloc& aAlloc); +void* operator new[](std::size_t aSize, tp::HeapAlloc& aAlloc); +void operator delete(void* aPtr, tp::HeapAlloc& aAlloc); +void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc); + +void* operator new(std::size_t aSize, tp::HeapAllocGlobal& aAlloc); +void* operator new[](std::size_t aSize, tp::HeapAllocGlobal& aAlloc); +void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc); +void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc); \ No newline at end of file diff --git a/public/ChunkAllocator.hpp b/Allocators/public/ChunkAllocator.hpp similarity index 69% rename from public/ChunkAllocator.hpp rename to Allocators/public/ChunkAllocator.hpp index dc9a9cb..ebc944d 100644 --- a/public/ChunkAllocator.hpp +++ b/Allocators/public/ChunkAllocator.hpp @@ -1,12 +1,11 @@ #pragma once -#include "common.h" -#include "PublicConfig.hpp" +#include "Environment.hpp" namespace tp { // Chunk Allocator - // Constant time allocations and deallocations 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. struct ChunkAlloc { @@ -15,8 +14,8 @@ namespace tp { void* allocate(); void deallocate(void* aPtr); - bool isFull(); - bool isEmpty(); + [[nodiscard]] bool isFull() const; + [[nodiscard]] bool isEmpty() const; ~ChunkAlloc(); @@ -24,8 +23,8 @@ namespace tp { ualni mBSize; // Size of data in aligned units ualni mNBlocks; - ualni* mBuff; - ualni* mNextBlock = 0; + ualni* mBuff = nullptr; + ualni* mNextBlock = nullptr; ualni mNFreeBlocks; ualni mNInitBlocks = 0; bool mOwnBuff = false; diff --git a/public/HeapAllocator.hpp b/Allocators/public/HeapAllocator.hpp similarity index 66% rename from public/HeapAllocator.hpp rename to Allocators/public/HeapAllocator.hpp index b6f84aa..80b2996 100644 --- a/public/HeapAllocator.hpp +++ b/Allocators/public/HeapAllocator.hpp @@ -1,16 +1,14 @@ #pragma once -#include "HeapAllocatorGlobal.hpp" +#include "Environment.hpp" namespace tp { struct HeapAlloc { #ifdef MEM_DEBUG - HeapAllocGlobal mAlloc; - ualni mNumAllocations = 0; - struct MemHeadLocal* mEntry = NULL; + struct MemHeadLocal* mEntry = nullptr; #endif void* allocate(ualni aBlockSize); diff --git a/public/HeapAllocatorGlobal.hpp b/Allocators/public/HeapAllocatorGlobal.hpp similarity index 72% rename from public/HeapAllocatorGlobal.hpp rename to Allocators/public/HeapAllocatorGlobal.hpp index 63f3875..7ada74c 100644 --- a/public/HeapAllocatorGlobal.hpp +++ b/Allocators/public/HeapAllocatorGlobal.hpp @@ -1,7 +1,6 @@ #pragma once -#include "common.h" -#include "PublicConfig.hpp" +#include "BaseModule.hpp" namespace tp { @@ -13,7 +12,7 @@ namespace tp { #endif static void* allocate(ualni aBlockSize); - static void deallocate(void* aPtr); + static void deallocate(void* aPtr); ~HeapAllocGlobal(); }; }; diff --git a/public/PickAllocator.hpp b/Allocators/public/PickAllocator.hpp similarity index 100% rename from public/PickAllocator.hpp rename to Allocators/public/PickAllocator.hpp diff --git a/public/PoolAllocator.hpp b/Allocators/public/PoolAllocator.hpp similarity index 100% rename from public/PoolAllocator.hpp rename to Allocators/public/PoolAllocator.hpp diff --git a/Allocators/tests/Tests.cpp b/Allocators/tests/Tests.cpp new file mode 100644 index 0000000..dfcc514 --- /dev/null +++ b/Allocators/tests/Tests.cpp @@ -0,0 +1,305 @@ + +#include "Allocators.hpp" + +#include +#include + +int main() { + + tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocators, NULL }; + tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies); + if (!TestModule.initialize()) { + return 1; + } + + tp::HeapAllocGlobal alloc; + + int* val = new(alloc) int(); + 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 +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 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(); +} \ No newline at end of file diff --git a/tools/MemBenchmark/BenchmarkEntry.cpp b/Allocators/tests/Tests.hpp similarity index 100% rename from tools/MemBenchmark/BenchmarkEntry.cpp rename to Allocators/tests/Tests.hpp diff --git a/BaseModule/CMakeLists.txt b/BaseModule/CMakeLists.txt new file mode 100644 index 0000000..7c4e3d0 --- /dev/null +++ b/BaseModule/CMakeLists.txt @@ -0,0 +1,20 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(BaseModule) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) + +### -------------------------- Tests -------------------------- ### +enable_testing() +add_executable(${PROJECT_NAME}Tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Tests.cpp) +target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME}) +add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) + + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) \ No newline at end of file diff --git a/tools/MemBenchmark/patterns.cpp b/BaseModule/README.md similarity index 100% rename from tools/MemBenchmark/patterns.cpp rename to BaseModule/README.md diff --git a/BaseModule/private/Assert.cpp b/BaseModule/private/Assert.cpp new file mode 100644 index 0000000..d74b726 --- /dev/null +++ b/BaseModule/private/Assert.cpp @@ -0,0 +1,26 @@ + +#include "Assert.hpp" + +#include +#include + +using namespace tp; + +void tp::_assert_(const char* exp, const char* file, int line) { + if (!exp) { + exp = "no info"; + } + printf("\nERROR: Assertion Failure - %s -- %s:%i\n", exp, file, line); + +#ifdef ENV_BUILD_DEBUG + DEBUG_BREAK(true); +#else + exit(1); +#endif + + +} + +void tp::terminate(tp::alni code) { + exit((int)code); +} \ No newline at end of file diff --git a/BaseModule/private/BaseModule.cpp b/BaseModule/private/BaseModule.cpp new file mode 100644 index 0000000..bd09cd7 --- /dev/null +++ b/BaseModule/private/BaseModule.cpp @@ -0,0 +1,81 @@ + +#include "BaseModule.hpp" + +#include + +using namespace tp; + +static bool init(const ModuleManifest* self) { + gEnvironment.log(); + return true; +} + +static ModuleManifest* deps[] = { nullptr }; +ModuleManifest tp::gModuleBase = ModuleManifest("Common", init, nullptr, deps); + +ModuleManifest::ModuleManifest(const char* aModuleName, ModuleInit aInit, ModuleDeinit aDeinit, ModuleManifest** aDependencies) { + mInit = aInit; + mDeinit = aDeinit; + mDependencies = aDependencies; + mModuleName = aModuleName; +} + +bool ModuleManifest::isInitialized() const { + return mInitialized; +} + +bool ModuleManifest::initialize() { + + mInitCount++; + + if (isInitialized()) { + return true; + } + + mInitialized = true; + + for (auto module = mDependencies; module && *module; module++) { + mInitialized &= (*module)->initialize(); + } + + std::cout << "====== Initializing \"" << mModuleName << "\"\n"; + + if (mInit) mInitialized &= mInit(this); + + if (!mInitialized) { + std::cout << "Failed to Initialize.\n"; + } + + return mInitialized; +} + +void ModuleManifest::deinitialize() { + mInitCount--; + + if (mInitCount > 0) { + return; + } + + if (!isInitialized()) { + return; + } + + if (mDeinit) mDeinit(this); + mInitialized = false; + + auto len = 0; + for (auto module = mDependencies; module && *module; module++) { + len++; + } + + for (auto i = 0; i < len; i++) { + auto module = mDependencies + (len - i - 1); + if ((*module)->isInitialized()) { + (*module)->deinitialize(); + } + } +} + +const char *ModuleManifest::getName() const { + return mModuleName; +} diff --git a/BaseModule/private/Common.cpp b/BaseModule/private/Common.cpp new file mode 100644 index 0000000..76c4625 --- /dev/null +++ b/BaseModule/private/Common.cpp @@ -0,0 +1,46 @@ + +#include "Common.hpp" + +namespace tp { + ualni next2pow(ualni v) { + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + return v + 1; + } + + uhalni next2pow(uhalni v) { + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + return v + 1; + } + + ufalni next2pow(ufalni v) { + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + return v + 1; + } + + ualni hash(const char* bytes) { + unsigned long hash = 5381; + int c; + while ((c = *bytes++)) { + hash = ((hash << 5) + hash) + c; + } + return hash; + } + + ualni hash(alni bytes) { return abs(bytes); } + ualni hash(alnf bytes) { return (alni)(abs(bytes)); } + ualni hash(halni bytes) { return hash(alni(bytes)); } + ualni hash(uhalni bytes) { return hash(alni(bytes)); } + ualni hash(ualni bytes) { return hash(alni(bytes)); } +} \ No newline at end of file diff --git a/BaseModule/private/Environment.cpp b/BaseModule/private/Environment.cpp new file mode 100644 index 0000000..17fe96c --- /dev/null +++ b/BaseModule/private/Environment.cpp @@ -0,0 +1,21 @@ + +#include "Environment.hpp" + +#include + +const char* ArchString[] = { "UNDEF", "INTEL", "ARM" }; +const char* BuildTypeString[] = { "UNDEF", "DEBUG", "RELEASE" }; +const char* ToolchainString[] = { "UNDEF", "GNU", "LLVM", "MSVC" }; +const char* OSString[] = { "UNDEF", "LINUX", "WINDOWS", "ANDROID", "IOS" }; +const char* ArchWidthString[] = { "UNDEF", "X64", "X32" }; + + +const tp::Environment tp::gEnvironment; + +void tp::Environment::log() const { + std::cout << "ARCH : " << ArchString[(int)mArch] << "\n"; + std::cout << "WIDTH : " << ArchWidthString[(int)mWidth] << "\n"; + std::cout << "CURRENT OS : " << OSString[(int)mOS] << "\n"; + std::cout << "TOOLCHAIN : " << ToolchainString[(int)mToolchain] << "\n"; + std::cout << "BUILD TYPE : " << BuildTypeString[(int)mBuildType] << "\n"; +} \ No newline at end of file diff --git a/BaseModule/public/Assert.hpp b/BaseModule/public/Assert.hpp new file mode 100644 index 0000000..6c7af04 --- /dev/null +++ b/BaseModule/public/Assert.hpp @@ -0,0 +1,34 @@ + +#pragma once + +#include "Environment.hpp" + +namespace tp { + void _assert_(const char* exp, const char* file, int line); + void terminate(tp::alni code = 0); +}; + +#define FAIL(exp) tp::_assert_(#exp, __FILE__, __LINE__); +#define ASSERT(exp) if (!(exp)) { FAIL(exp) } + +#undef assert +#ifdef ENV_BUILD_DEBUG +#define DEBUG_ASSERT(exp) ASSERT(exp) +#else +#define DEBUG_ASSERT(exp) {} +#endif + + + +#if defined(ENV_OS_WINDOWS) + #define DEBUG_BREAK(expr) if (expr) { __debugbreak(); } +#elif defined(ENV_OS_ANDROID) + #define DEBUG_BREAK(expr) if (expr) { __builtin_debugtrap(); } +#elif defined(ENV_OS_LINUX) + #define DEBUG_BREAK(expr) if (expr) { __builtin_trap(); } +#else + #define DEBUG_BREAK(expr) () +#endif + + +#define SWITCH_NO_DEF default : { FAIL("No Default Case Possible"); } \ No newline at end of file diff --git a/BaseModule/public/BaseModule.hpp b/BaseModule/public/BaseModule.hpp new file mode 100644 index 0000000..31ba4c5 --- /dev/null +++ b/BaseModule/public/BaseModule.hpp @@ -0,0 +1,31 @@ + +#pragma once + +#include "Common.hpp" +#include "Assert.hpp" + +#define MODULE_SANITY_CHECK(name) ASSERT(name.isInitialized() && "Module Is Not Initialized" && #name) + +namespace tp { + + class ModuleManifest { + public: + typedef bool (*ModuleInit)(const ModuleManifest*); + typedef void (*ModuleDeinit)(const ModuleManifest*); + + ModuleManifest(const char* aModuleName, ModuleInit aInit, ModuleDeinit aDeinit, ModuleManifest** aDependencies); + [[nodiscard]] bool isInitialized() const; + bool initialize(); + void deinitialize(); + [[nodiscard]] const char* getName() const; + private: + const char* mModuleName = nullptr; + ModuleManifest** mDependencies; // NULL terminated + bool mInitialized = false; + ModuleInit mInit = nullptr; + ModuleDeinit mDeinit = nullptr; + uhalni mInitCount = 0; + }; + + extern ModuleManifest gModuleBase; +}; diff --git a/BaseModule/public/Common.hpp b/BaseModule/public/Common.hpp new file mode 100644 index 0000000..a1821e5 --- /dev/null +++ b/BaseModule/public/Common.hpp @@ -0,0 +1,59 @@ + +#pragma once + +#include "Environment.hpp" +#include "TypeInfo.hpp" +#include + +namespace tp { + template + using init_list = std::initializer_list; + + // Selects whether to pass by constant reference or by value + template + using SelCopyArg = typename TypeSelect<(sizeof(tType) > sizeof(tp::alni)), const tType&, tType>::Result; + + ualni next2pow(ualni v); + uhalni next2pow(uhalni v); + ufalni next2pow(ufalni v); + + ualni hash(const char* bytes); + ualni hash(alni bytes); + ualni hash(halni bytes); + ualni hash(uhalni bytes); + ualni hash(ualni bytes); + ualni hash(alnf bytes); + + template + [[nodiscard]] T clamp(T v, T l, T u) { if (v < l) { v = l; } else if (v > u) { v = u; } return v; } + + template + [[nodiscard]] T max(T a, T b) { return (a > b) ? a : b; } + + template + [[nodiscard]] T min(T a, T b) { return (a < b) ? a : b; } + + template + [[nodiscard]] T abs(T v) { if (v < 0) { return -v; } return v; } + + template + inline void swap(T& t1, T& t2) { const T tmp = t1; t1 = t2; t2 = tmp; } + + // only for x > 0 and y > 0 + template + [[nodiscard]] T ceil_positive(T x, T y) { return T( 1 + ((x - 1) / (tp::alnf)y) ); } + + // power + template + [[nodiscard]] T pow(T x, uhalni n) { + T out = x; for (uhalni i = 0; i < n - 1; i++) { out = out * x; } + return out; + } + + template + halni nDig10(Type val) { + val = abs(val); halni out = 0; + while (val != 0) { val = halni(val / 10); out++; } + return out; + } +} \ No newline at end of file diff --git a/BaseModule/public/Environment.hpp b/BaseModule/public/Environment.hpp new file mode 100644 index 0000000..11a4133 --- /dev/null +++ b/BaseModule/public/Environment.hpp @@ -0,0 +1,198 @@ + +#pragma once + +#include +#include + +namespace tp { + + class Environment { + public: + + enum class Arch { UNDEF, INTEL, ARM } mArch = Arch::UNDEF; + + // Build Type + #if defined(__DEBUG__) || defined(_DEBUG) || defined(DEBUG) || !defined(NDEBUG) + #define ENV_BUILD_DEBUG + enum class BuildType { UNDEF, DEBUG, RELEASE } mBuildType = BuildType::DEBUG; + #else + #define ENV_BUILD_RELEASE + enum class BuildType { UNDEF, DEBUG, RELEASE } mBuildType = BuildType::RELEASE; + #endif + + // MCVS + #ifdef _MSC_VER + #define ENV_COMPILER_MSVC + enum class Toolchain { UNDEF, GNU, LLVM, MSVC } mToolchain = Toolchain::MSVC; + + // VERSION + // TODO + + // TARGET OS + #if defined(_WIN32) || defined(_WIN64) + #define ENV_OS_WINDOWS + enum class OS { UNDEF, LINUX, WINDOWS, ANDROID, IOS } mOS = OS::WINDOWS; + #else + enum class OS { UNDEF, LINUX, WINDOWS, ANDROID, IOS } mOS = OS::UNDEF; + #error "unexplored compilation to os target" + #endif + + // TARGET ALIGNED SIZE + #ifdef _WIN64 + enum class ArchWidth { UNDEF, X64, X32 } mWidth = ArchWidth::X64; + #define ENV_BITS_64 + #else + enum class ArchWidth { UNDEF, X64, X32 } mWidth = ArchWidth::X32; + #define ENV_BITS_32 + #endif + + #endif + + // GCC + #if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) + #define ENV_COMPILER_GCC + enum class Toolchain { UNDEF, GNU, LLVM, MSVC } mToolchain = Toolchain::GNU; + + // VERSION + #if (__GNUC___ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) + // TODO + #endif + + // TARGET OS + #if defined(__linux__) && !defined(__ANDROID__) + #define ENV_OS_LINUX + enum class OS { UNDEF, LINUX, WINDOWS, ANDROID, IOS } mOS = OS::LINUX; + #else + #error "unexplored compilation to os target" + #endif + + // TARGET ALIGNED SIZE + #if defined(__aarch64__) || defined(__x86_64__) || defined(__ARM_64BIT_STATE) + #define ENV_BITS_64 + enum class ArchWidth { UNDEF, X64, X32 } mWidth = ArchWidth::X64; + #elif defined(__x86_64__) || defined(i386) + #define ENV_BITS_32 + enum class ArchWidth { UNDEF, X64, X32 } mWidth = ArchWidth::X32; + #endif + + #endif + + // CLANG + #if defined(__clang__) && !defined(ENV_COMPILER_GCC) + #define ENV_COMPILER_CLANG + enum class Toolchain { UNDEF, GNU, LLVM, MSVC } mToolchain = Toolchain::LLVM; + + // VERSION + #if (__clang_major__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)) + // TODO + #endif + + // TARGET OS + #if defined(__linux__) && !defined(__ANDROID__) + #define ENV_OS_LINUX + enum class OS { UNDEF, LINUX, WINDOWS, ANDROID, IOS } mOS = OS::LINUX; + #elif defined(__ANDROID__) + enum class OS { UNDEF, LINUX, WINDOWS, ANDROID, IOS } mOS = OS::ANDOID; + #define ENV_OS_ANDROID + #else + #error "unexplored compilation to target os" + #endif + + // TARGET ALIGNED SIZE + #if defined(__aarch64__) || defined(__x86_64__) || defined(__ARM_64BIT_STATE) + #define ENV_BITS_64 + enum class ArchWidth { UNDEF, X64, X32 } mWidth = ArchWidth::X64; + #elif defined(__x86_64__) || defined(i386) + #define ENV_BITS_32 + enum class ArchWidth { UNDEF, X64, X32 } mWidth = ArchWidth::X32; + #endif + + #endif + + #if defined(__EMSCRIPTEN__) || defined(__MINGW32__) || defined(__MINGW32__) || defined(__MINGW64__) + #error "compiler is not supported" + #else + #if !(defined(ENV_COMPILER_MSVC) || defined(ENV_COMPILER_CLANG) || defined(ENV_COMPILER_GCC)) + // Linux and Linux - derived __linux__ + // Darwin(Mac OS X and iOS) __APPLE__ + // Akaros __ros__ + // NaCL __native_client__ + // AsmJS __asmjs__ + // Fuschia __Fuchsia__ + #error "unknown compiler" + #endif + #endif + + void log() const; + }; + + #ifndef ENV_BITS_64 + #error "ERROR - not 64 bit archytectures are out of support" + #endif + + const extern Environment gEnvironment; + + typedef char int1; + typedef unsigned char uint1; + typedef short int2; + typedef unsigned short uint2; + typedef unsigned int uint4; + typedef uint2 ufalni; + typedef uint4 uhalni; + typedef int int4; + typedef int4 halni; + typedef unsigned long long uint8; + typedef uint8 ualni; + typedef long long int8; + typedef int8 alni; + typedef double flt8; + typedef flt8 alnf; + typedef float flt4; + typedef flt4 halnf; + + #define ENV_INT1_MAX tp::int1( 0x7f) + #define ENV_INT1_MIN (-tp::int1(0x80)) + #define ENV_UINT1_MAX tp::uint1(0xff) + #define ENV_UINT1_MIN tp::uint1(0x00) + #define ENV_INT2_MAX tp::int2( 0x7fff) + #define ENV_INT2_MIN (-tp::int2(0x8000)) + #define ENV_UINT2_MAX tp::uint2(0xffff) + #define ENV_UINT2_MIN tp::uint2(0x0000) + #define ENV_UHALNI_MAX tp::uhalni(0xffffffff) + #define ENV_UHALNI_MIN tp::uhalni(0x00000000) + #define ENV_HALNI_MAX tp::halni( 0x7fffffff) + #define ENV_HALNI_MIN (-tp::halni(0x80000000)) + #define ENV_UALNI_MAX tp::ualni(0xffffffffffffffff) + #define ENV_UALNI_MIN tp::ualni(0x0000000000000000) + #define ENV_ALNI_MAX tp::alni( 0x7fffffffffffffff) + #define ENV_ALNI_MIN (-tp::alni(0x8000000000000000)) + #define ENV_ALNI_SIZE_B (8) + #define ENV_ALNF_DECIMAL_DIG DBL_DECIMAL_DIG + #define ENV_ALNF_DIG DBL_DIG + #define ENV_ALNF_EPSILON DBL_EPSILON + #define ENV_ALNF_HAS_SUBNORM DBL_HAS_SUBNORM + #define ENV_ALNF_MANT_DIG DBL_MANT_DIG + #define ENV_ALNF_MAX DBL_MAX + #define ENV_ALNF_MAX_10_EXP DBL_MAX_10_EXP + #define ENV_ALNF_MAX_EXP DBL_MAX_EXP + #define ENV_ALNF_MIN DBL_MIN + #define ENV_ALNF_MIN_10_EXP DBL_MIN_10_EXP + #define ENV_ALNF_MIN_EXP DBL_MIN_EXP + #define ENV_ALNF_RADIX _DBL_RADIX + #define ENV_ALNF_TRUE_MIN DBL_TRUE_MIN + #define ENV_HALNF_DECIMAL_DIG FLT_DECIMAL_DIG + #define ENV_HALNF_DIG FLT_DIG + #define ENV_HALNF_EPSILON FLT_EPSILON + #define ENV_HALNF_HAS_SUBNORM FLT_HAS_SUBNORM + #define ENV_HALNF_GUARD FLT_GUARD + #define ENV_HALNF_MANT_DIG FLT_MANT_DIG + #define ENV_HALNF_MAX FLT_MAX + #define ENV_HALNF_MAX_10_EXP FLT_MAX_10_EXP + #define ENV_HALNF_MAX_EXP FLT_MAX_EXP + #define ENV_HALNF_MIN FLT_MIN + #define ENV_HALNF_MIN_10_EXP FLT_MIN_10_EXP + #define ENV_HALNF_MIN_EXP FLT_MIN_EXP + #define ENV_HALNF_NORMALIZE FLT_NORMALIZE + #define ENV_HALNF_RADIX FLT_RADIX + #define ENV_HALNF_TRUE_MIN FLT_TRUE_MIN +}; \ No newline at end of file diff --git a/BaseModule/public/TypeInfo.hpp b/BaseModule/public/TypeInfo.hpp new file mode 100644 index 0000000..64e1e79 --- /dev/null +++ b/BaseModule/public/TypeInfo.hpp @@ -0,0 +1,927 @@ + +#pragma once + +//////////////////////////////////////////////////////////////////////////////// +// The Loki Library +// Copyright (c) 2001 by Andrei Alexandrescu +// This code accompanies the book: +// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design +// Patterns Applied". Copyright (c) 2001. Addison-Wesley. +// Code covered by the MIT License +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +//////////////////////////////////////////////////////////////////////////////// + + +namespace tp { + template + struct TypeSelect { + typedef T Result; + }; + template + struct TypeSelect { + typedef U Result; + }; +} + +namespace tp { +//////////////////////////////////////////////////////////////////////////////// +// class template IsSameType +// Return true iff two given types are the same +// Invocation: SameType::value +// where: +// T and U are types +// Result evaluates to true iff U == T (types equal) +//////////////////////////////////////////////////////////////////////////////// + + template + struct IsSameType { + enum { value = false }; + }; + + template + struct IsSameType { + enum { value = true }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// Helper types Small and Big - guarantee that sizeof(Small) < sizeof(Big) +//////////////////////////////////////////////////////////////////////////////// + + namespace Private { + template + struct ConversionHelper { + typedef char Small; + struct Big { char dummy[2]; }; + static Big Test(...); + static Small Test(U); + static T MakeT(); + }; + } + +//////////////////////////////////////////////////////////////////////////////// +// class template Conversion +// Figures out the conversion relationships between two types +// Invocations (T and U are types): +// a) Conversion::exists +// returns (at compile time) true if there is an implicit conversion from T +// to U (example: Derived to Base) +// b) Conversion::exists2Way +// returns (at compile time) true if there are both conversions from T +// to U and from U to T (example: int to char and back) +// c) Conversion::sameType +// returns (at compile time) true if T and U represent the same type +// +// Caveat: might not work if T and U are in a private inheritance hierarchy. +//////////////////////////////////////////////////////////////////////////////// + + template + struct Conversion { + typedef Private::ConversionHelper H; +#ifndef __MWERKS__ + enum { exists = sizeof(typename H::Small) == sizeof((H::Test(H::MakeT()))) }; +#else + enum { exists = false }; +#endif + enum { exists2Way = exists && Conversion::exists }; + enum { sameType = false }; + }; + + template + struct Conversion { + enum { exists = 1, exists2Way = 1, sameType = 1 }; + }; + + template + struct Conversion { + enum { exists = 0, exists2Way = 0, sameType = 0 }; + }; + + template + struct Conversion { + enum { exists = 0, exists2Way = 0, sameType = 0 }; + }; + + template <> + struct Conversion { + public: + enum { exists = 1, exists2Way = 1, sameType = 1 }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template SuperSubclass +// Invocation: SuperSubclass::value where B and D are types. +// Returns true if B is a public base of D, or if B and D are aliases of the +// same type. +// +// Caveat: might not work if T and U are in a private inheritance hierarchy. +//////////////////////////////////////////////////////////////////////////////// + + template + struct SuperSubclass { + enum { + value = (::tp::Conversion::exists && + !::tp::Conversion::sameType) + }; + +// Dummy enum to make sure that both classes are fully defined. + enum { dontUseWithIncompleteTypes = (sizeof(T) == sizeof(U)) }; + }; + + template <> + struct SuperSubclass { + enum { value = false }; + }; + + template + struct SuperSubclass { + enum { + value = (::tp::Conversion::exists && + !::tp::Conversion::sameType) + }; + +// Dummy enum to make sure that both classes are fully defined. + enum { dontUseWithIncompleteTypes = (0 == sizeof(U)) }; + }; + + template + struct SuperSubclass { + enum { + value = (::tp::Conversion::exists && + !::tp::Conversion::sameType) + }; + +// Dummy enum to make sure that both classes are fully defined. + enum { dontUseWithIncompleteTypes = (sizeof(T) == 0) }; + }; + + //////////////////////////////////////////////////////////////////////////////// + // class template SuperSubclassStrict + // Invocation: SuperSubclassStrict::value where B and D are types. + // Returns true if B is a public base of D. + // + // Caveat: might not work if T and U are in a private inheritance hierarchy. + //////////////////////////////////////////////////////////////////////////////// + + template + struct SuperSubclassStrict { + enum { + value = (::tp::Conversion::exists && + !::tp::Conversion::sameType && + !::tp::Conversion::sameType) + }; + +// Dummy enum to make sure that both classes are fully defined. + enum { dontUseWithIncompleteTypes = (sizeof(T) == sizeof(U)) }; + }; + + template<> + struct SuperSubclassStrict { + enum { value = false }; + }; + + template + struct SuperSubclassStrict { + enum { + value = (::tp::Conversion::exists && + !::tp::Conversion::sameType && + !::tp::Conversion::sameType) + }; + +// Dummy enum to make sure that both classes are fully defined. + enum { dontUseWithIncompleteTypes = (0 == sizeof(U)) }; + }; + + template + struct SuperSubclassStrict { + enum { + value = (::tp::Conversion::exists && + !::tp::Conversion::sameType && + !::tp::Conversion::sameType) + }; + +// Dummy enum to make sure that both classes are fully defined. + enum { dontUseWithIncompleteTypes = (sizeof(T) == 0) }; + }; + + +} // namespace tp + +//////////////////////////////////////////////////////////////////////////////// +// macro SUPERSUBCLASS +// Invocation: SUPERSUBCLASS(B, D) where B and D are types. +// Returns true if B is a public base of D, or if B and D are aliases of the +// same type. +// +// Caveat: might not work if T and U are in a private inheritance hierarchy. +// Deprecated: Use SuperSubclass class template instead. +//////////////////////////////////////////////////////////////////////////////// + +#define SUPERSUBCLASS(T, U) ::tp::SuperSubclass::value + +//////////////////////////////////////////////////////////////////////////////// +// macro SUPERSUBCLASS_STRICT +// Invocation: SUPERSUBCLASS(B, D) where B and D are types. +// Returns true if B is a public base of D. +// +// Caveat: might not work if T and U are in a private inheritance hierarchy. +// Deprecated: Use SuperSubclassStrict class template instead. +//////////////////////////////////////////////////////////////////////////////// + +#define SUPERSUBCLASS_STRICT(T, U) ::tp::SuperSubclassStrict::value + + + +namespace tp { + + struct NullType {}; + +//////////////////////////////////////////////////////////////////////////////// +// class template Typelist +// The building block of typelists of any length +// Use it through the LOKI_TYPELIST_NN macros +// Defines nested types: +// Head (first element, a non-typelist type by convention) +// Tail (second element, can be another typelist) +//////////////////////////////////////////////////////////////////////////////// + + template + struct Typelist { + typedef T Head; + typedef U Tail; + }; + +// Typelist utility algorithms + + namespace TL { + +//////////////////////////////////////////////////////////////////////////////// +// class template MakeTypelist +// Takes a number of arguments equal to its numeric suffix +// The arguments are type names. +// MakeTypelist::Result +// returns a typelist that is of T1, T2, ... +//////////////////////////////////////////////////////////////////////////////// + + template + < + typename T1 = NullType, typename T2 = NullType, typename T3 = NullType, + typename T4 = NullType, typename T5 = NullType, typename T6 = NullType, + typename T7 = NullType, typename T8 = NullType, typename T9 = NullType, + typename T10 = NullType, typename T11 = NullType, typename T12 = NullType, + typename T13 = NullType, typename T14 = NullType, typename T15 = NullType, + typename T16 = NullType, typename T17 = NullType, typename T18 = NullType + > + struct MakeTypelist { + private: + typedef typename MakeTypelist + < + T2, T3, T4, + T5, T6, T7, + T8, T9, T10, + T11, T12, T13, + T14, T15, T16, + T17, T18 + > + ::Result TailResult; + + public: + typedef Typelist Result; + }; + + template<> + struct MakeTypelist<> { + typedef NullType Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template Length +// Computes the length of a typelist +// Invocation (TList is a typelist): +// Length::value +// returns a compile-time constant containing the length of TList, not counting +// the end terminator (which by convention is NullType) +//////////////////////////////////////////////////////////////////////////////// + + template struct Length; + template <> struct Length { + enum { value = 0 }; + }; + + template + struct Length< Typelist > { + enum { value = 1 + Length::value }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template TypeAt +// Finds the type at a given index in a typelist +// Invocation (TList is a typelist and index is a compile-time integral +// constant): +// TypeAt::Result +// returns the type in position 'index' in TList +// If you pass an out-of-bounds index, the result is a compile-time error +//////////////////////////////////////////////////////////////////////////////// + + template struct TypeAt; + + template + struct TypeAt, 0> { + typedef Head Result; + }; + + template + struct TypeAt, i> { + typedef typename TypeAt::Result Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template TypeAtNonStrict +// Finds the type at a given index in a typelist +// Invocations (TList is a typelist and index is a compile-time integral +// constant): +// a) TypeAt::Result +// returns the type in position 'index' in TList, or NullType if index is +// out-of-bounds +// b) TypeAt::Result +// returns the type in position 'index' in TList, or D if index is out-of-bounds +//////////////////////////////////////////////////////////////////////////////// + + template + struct TypeAtNonStrict { + typedef DefaultType Result; + }; + + template + struct TypeAtNonStrict, 0, DefaultType> { + typedef Head Result; + }; + + template + struct TypeAtNonStrict, i, DefaultType> { + typedef typename + TypeAtNonStrict::Result Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template IndexOf +// Finds the index of a type in a typelist +// Invocation (TList is a typelist and T is a type): +// IndexOf::value +// returns the position of T in TList, or NullType if T is not found in TList +//////////////////////////////////////////////////////////////////////////////// + + template struct IndexOf; + + template + struct IndexOf { + enum { value = -1 }; + }; + + template + struct IndexOf, T> { + enum { value = 0 }; + }; + + template + struct IndexOf, T> { + private: + enum { temp = IndexOf::value }; + public: + enum { value = (temp == -1 ? -1 : 1 + temp) }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template Append +// Appends a type or a typelist to another +// Invocation (TList is a typelist and T is either a type or a typelist): +// Append::Result +// returns a typelist that is TList followed by T and NullType-terminated +//////////////////////////////////////////////////////////////////////////////// + + template struct Append; + + template <> struct Append { + typedef NullType Result; + }; + + template struct Append { + typedef Typelist Result; + }; + + template + struct Append > { + typedef Typelist Result; + }; + + template + struct Append, T> { + typedef Typelist::Result> + Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template Erase +// Erases the first occurence, if any, of a type in a typelist +// Invocation (TList is a typelist and T is a type): +// Erase::Result +// returns a typelist that is TList without the first occurence of T +//////////////////////////////////////////////////////////////////////////////// + + template struct Erase; + + template // Specialization 1 + struct Erase { + typedef NullType Result; + }; + + template // Specialization 2 + struct Erase, T> { + typedef Tail Result; + }; + + template // Specialization 3 + struct Erase, T> { + typedef Typelist::Result> + Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template EraseAll +// Erases all first occurences, if any, of a type in a typelist +// Invocation (TList is a typelist and T is a type): +// EraseAll::Result +// returns a typelist that is TList without any occurence of T +//////////////////////////////////////////////////////////////////////////////// + + template struct EraseAll; + template + struct EraseAll { + typedef NullType Result; + }; + template + struct EraseAll, T> { + // Go all the way down the list removing the type + typedef typename EraseAll::Result Result; + }; + template + struct EraseAll, T> { + // Go all the way down the list removing the type + typedef Typelist::Result> + Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template NoDuplicates +// Removes all duplicate types in a typelist +// Invocation (TList is a typelist): +// NoDuplicates::Result +//////////////////////////////////////////////////////////////////////////////// + + template struct NoDuplicates; + + template <> struct NoDuplicates { + typedef NullType Result; + }; + + template + struct NoDuplicates< Typelist > { + private: + typedef typename NoDuplicates::Result L1; + typedef typename Erase::Result L2; + public: + typedef Typelist Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template Replace +// Replaces the first occurence of a type in a typelist, with another type +// Invocation (TList is a typelist, T, U are types): +// Replace::Result +// returns a typelist in which the first occurence of T is replaced with U +//////////////////////////////////////////////////////////////////////////////// + + template struct Replace; + + template + struct Replace { + typedef NullType Result; + }; + + template + struct Replace, T, U> { + typedef Typelist Result; + }; + + template + struct Replace, T, U> { + typedef Typelist::Result> + Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template ReplaceAll +// Replaces all occurences of a type in a typelist, with another type +// Invocation (TList is a typelist, T, U are types): +// Replace::Result +// returns a typelist in which all occurences of T is replaced with U +//////////////////////////////////////////////////////////////////////////////// + + template struct ReplaceAll; + + template + struct ReplaceAll { + typedef NullType Result; + }; + + template + struct ReplaceAll, T, U> { + typedef Typelist::Result> Result; + }; + + template + struct ReplaceAll, T, U> { + typedef Typelist::Result> + Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template Reverse +// Reverses a typelist +// Invocation (TList is a typelist): +// Reverse::Result +// returns a typelist that is TList reversed +//////////////////////////////////////////////////////////////////////////////// + + template struct Reverse; + + template <> + struct Reverse { + typedef NullType Result; + }; + + template + struct Reverse< Typelist > { + typedef typename Append< + typename Reverse::Result, Head>::Result Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template MostDerived +// Finds the type in a typelist that is the most derived from a given type +// Invocation (TList is a typelist, T is a type): +// MostDerived::Result +// returns the type in TList that's the most derived from T +//////////////////////////////////////////////////////////////////////////////// + + template struct MostDerived; + + template + struct MostDerived { + typedef T Result; + }; + + template + struct MostDerived, T> { + private: + typedef typename MostDerived::Result Candidate; + public: + typedef typename TypeSelect< + SuperSubclass::value, + Head, Candidate>::Result Result; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template DerivedToFront +// Arranges the types in a typelist so that the most derived types appear first +// Invocation (TList is a typelist): +// DerivedToFront::Result +// returns the reordered TList +//////////////////////////////////////////////////////////////////////////////// + + template struct DerivedToFront; + + template <> + struct DerivedToFront { + typedef NullType Result; + }; + + template + struct DerivedToFront< Typelist > { + private: + typedef typename MostDerived::Result + TheMostDerived; + typedef typename Replace::Result Temp; + typedef typename DerivedToFront::Result L; + public: + typedef Typelist Result; + }; + + } // namespace TL + + + template < + class T01 = NullType, class T02 = NullType, class T03 = NullType, class T04 = NullType, class T05 = NullType, + class T06 = NullType, class T07 = NullType, class T08 = NullType, class T09 = NullType, class T10 = NullType, + class T11 = NullType, class T12 = NullType, class T13 = NullType, class T14 = NullType, class T15 = NullType, + class T16 = NullType, class T17 = NullType, class T18 = NullType, class T19 = NullType, class T20 = NullType + > + class Seq { + typedef typename Seq< T02, T03, T04, T05, T06, T07, T08, T09, T10, + T11, T12, T13, T14, T15, T16, T17, T18, T19, T20>::list TailResult; + public: + typedef Typelist list; + }; + + template<> + struct Seq<> { + typedef NullType list; + }; + +} // namespace tp + +#if 0 +#define TYPELIST1(T1) ::compiler::Typelist +#define TYPELIST2(T1, T2) ::compiler::Typelist +#define TYPELIST3(T1, T2, T3) ::compiler::Typelist +#define TYPELIST4(T1, T2, T3, T4) ::compiler::Typelist +#define TYPELIST5(T1, T2, T3, T4, T5) ::compiler::Typelist +#define TYPELIST6(T1, T2, T3, T4, T5, T6) ::compiler::Typelist +#define TYPELIST7(T1, T2, T3, T4, T5, T6, T7) ::compiler::Typelist +#define TYPELIST8(T1, T2, T3, T4, T5, T6, T7, T8) ::compiler::Typelist +#define TYPELIST9(T1, T2, T3, T4, T5, T6, T7, T8, T9) ::compiler::Typelist +#define TYPELIST10(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) ::compiler::Typelist +#endif + +#include + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4180 ) //qualifier applied to function type has no meaning; ignored +#endif + +namespace tp { +//////////////////////////////////////////////////////////////////////////////// +// class template IsCustomUnsignedInt +// Offers a means to integrate nonstandard built-in unsigned integral types +// (such as unsigned __int64 or unsigned long long int) with the TypeTraits +// class template defined below. +// Invocation: IsCustomUnsignedInt where T is any type +// Defines 'value', an enum that is 1 iff T is a custom built-in unsigned +// integral type +// Specialize this class template for nonstandard unsigned integral types +// and define value = 1 in those specializations +//////////////////////////////////////////////////////////////////////////////// + + template + struct IsCustomUnsignedInt { + enum { value = 0 }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template IsCustomSignedInt +// Offers a means to integrate nonstandard built-in unsigned integral types +// (such as unsigned __int64 or unsigned long long int) with the TypeTraits +// class template defined below. +// Invocation: IsCustomSignedInt where T is any type +// Defines 'value', an enum that is 1 iff T is a custom built-in signed +// integral type +// Specialize this class template for nonstandard unsigned integral types +// and define value = 1 in those specializations +//////////////////////////////////////////////////////////////////////////////// + + template + struct IsCustomSignedInt { + enum { value = 0 }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// class template IsCustomFloat +// Offers a means to integrate nonstandard floating point types with the +// TypeTraits class template defined below. +// Invocation: IsCustomFloat where T is any type +// Defines 'value', an enum that is 1 iff T is a custom built-in +// floating point type +// Specialize this class template for nonstandard unsigned integral types +// and define value = 1 in those specializations +//////////////////////////////////////////////////////////////////////////////// + + template + struct IsCustomFloat { + enum { value = 0 }; + }; + +//////////////////////////////////////////////////////////////////////////////// +// Helper types for class template TypeTraits defined below +//////////////////////////////////////////////////////////////////////////////// + + namespace Private { + + typedef Seq::list StdUnsignedInts; + typedef Seq::list StdSignedInts; + typedef Seq::list StdOtherInts; + typedef Seq::list StdFloats; + + template struct AddPointer { typedef U* Result; }; + template struct AddPointer { typedef U* Result; }; + + template struct AddReference { typedef U& Result; }; + template struct AddReference { typedef U& Result; }; + template <> struct AddReference { typedef NullType Result; }; + + template struct AddParameterType { typedef const U& Result; }; + template struct AddParameterType { typedef U& Result; }; + template <> struct AddParameterType { typedef NullType Result; }; + + }// namespace Private + +//////////////////////////////////////////////////////////////////////////////// +// class template TypeTraits +// +// Figures out at compile time various properties of any given type +// Invocations (T is a type, TypeTraits::Property): +// +// - isPointer : returns true if T is a pointer type +// - PointeeType : returns the type to which T points if T is a pointer +// type, NullType otherwise +// - isReference : returns true if T is a reference type +// - ReferredType : returns the type to which T refers if T is a reference +// type, NullType otherwise +// - isMemberPointer : returns true if T is a pointer to member type +// - isStdUnsignedInt: returns true if T is a standard unsigned integral type +// - isStdSignedInt : returns true if T is a standard signed integral type +// - isStdIntegral : returns true if T is a standard integral type +// - isStdFloat : returns true if T is a standard floating-point type +// - isStdArith : returns true if T is a standard arithmetic type +// - isStdFundamental: returns true if T is a standard fundamental type +// - isUnsignedInt : returns true if T is a unsigned integral type +// - isSignedInt : returns true if T is a signed integral type +// - isIntegral : returns true if T is a integral type +// - isFloat : returns true if T is a floating-point type +// - isArith : returns true if T is a arithmetic type +// - isFundamental : returns true if T is a fundamental type +// - ParameterType : returns the optimal type to be used as a parameter for +// functions that take Ts +// - isConst : returns true if T is a const-qualified type +// - NonConstType : Type with removed 'const' qualifier from T, if any +// - isVolatile : returns true if T is a volatile-qualified type +// - NonVolatileType : Type with removed 'volatile' qualifier from T, if any +// - UnqualifiedType : Type with removed 'const' and 'volatile' qualifiers from +// T, if any +// - ParameterType : returns the optimal type to be used as a parameter +// for functions that take 'const T's +// +//////////////////////////////////////////////////////////////////////////////// + + template + class TypeTraits { + private: + + template struct ReferenceTraits { + enum { result = false }; + typedef U ReferredType; + }; + + template struct ReferenceTraits { + enum { result = true }; + typedef U ReferredType; + }; + + template struct PointerTraits { + enum { result = false }; + typedef NullType PointeeType; + }; + + template struct PointerTraits { + enum { result = true }; + typedef U PointeeType; + }; + + template struct PointerTraits { + enum { result = true }; + typedef U PointeeType; + }; + + template struct PToMTraits { + enum { result = false }; + }; + + template struct PToMTraits { + enum { result = true }; + }; + + template struct PToMTraits { + enum { result = true }; + }; + + template struct UnConst { + typedef U Result; + enum { isConst = 0 }; + }; + + template struct UnConst { + typedef U Result; + enum { isConst = 1 }; + }; + + template struct UnConst { + typedef U& Result; + enum { isConst = 1 }; + }; + + template struct UnVolatile { + typedef U Result; + enum { isVolatile = 0 }; + }; + + template struct UnVolatile { + typedef U Result; + enum { isVolatile = 1 }; + }; + + template struct UnVolatile { + typedef U& Result; + enum { isVolatile = 1 }; + }; + + public: + typedef typename UnConst::Result + NonConstType; + typedef typename UnVolatile::Result + NonVolatileType; + typedef typename UnVolatile::Result>::Result + UnqualifiedType; + typedef typename PointerTraits::PointeeType + PointeeType; + typedef typename ReferenceTraits::ReferredType + ReferredType; + + enum { isConst = UnConst::isConst }; + enum { isVolatile = UnVolatile::isVolatile }; + enum { isReference = ReferenceTraits::result }; + enum { isMemberPointer = PToMTraits::ReferredType >::result }; + enum { isPointer = PointerTraits::ReferredType >::result }; + + enum { + isStdUnsignedInt = TL::IndexOf::value >= 0 || + TL::IndexOf::ReferredType>::value >= 0 + }; + enum { + isStdSignedInt = TL::IndexOf::value >= 0 || + TL::IndexOf::ReferredType>::value >= 0 + }; + enum { + isStdIntegral = isStdUnsignedInt || isStdSignedInt || + TL::IndexOf::value >= 0 || + TL::IndexOf::ReferredType>::value >= 0 + }; + enum { + isStdFloat = TL::IndexOf::value >= 0 || + TL::IndexOf::ReferredType>::value >= 0 + }; + + enum { isStdArith = isStdIntegral || isStdFloat }; + enum { isStdFundamental = isStdArith || isStdFloat || Conversion::sameType }; + + enum { isUnsignedInt = isStdUnsignedInt || IsCustomUnsignedInt::value }; + enum { isSignedInt = isStdSignedInt || IsCustomSignedInt::value }; + enum { isIntegral = isStdIntegral || isUnsignedInt || isSignedInt }; + enum { isFloat = isStdFloat || IsCustomFloat::value }; + enum { isArith = isIntegral || isFloat }; + enum { isFundamental = isStdFundamental || isArith }; + + typedef typename TypeSelect::Result>::Result + ParameterType; + }; +} + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif // _MSC_VER \ No newline at end of file diff --git a/BaseModule/tests/Tests.cpp b/BaseModule/tests/Tests.cpp new file mode 100644 index 0000000..4093d35 --- /dev/null +++ b/BaseModule/tests/Tests.cpp @@ -0,0 +1,15 @@ + +#include "BaseModule.hpp" + +int main() { + tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleBase, nullptr }; + tp::ModuleManifest TestModule("Test", nullptr, nullptr, ModuleDependencies); + + if (!TestModule.initialize()) { + return 1; + } + + ASSERT(tp::gEnvironment.mWidth == tp::Environment::ArchWidth::X64); + + TestModule.deinitialize(); +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2f2c109 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4") + +enable_testing() + +project(Types) + +add_compile_definitions(MEM_DEBUG) + +add_subdirectory(BaseModule) +add_subdirectory(Utils) +add_subdirectory(Containers) +#add_subdirectory(Allocators) \ No newline at end of file diff --git a/Containers/CMakeLists.txt b/Containers/CMakeLists.txt new file mode 100644 index 0000000..15823e9 --- /dev/null +++ b/Containers/CMakeLists.txt @@ -0,0 +1,22 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(Containers) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") +file(GLOB HEADERS "./public/*.hpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) +target_link_libraries(${PROJECT_NAME} PUBLIC BaseModule) + +### -------------------------- Tests -------------------------- ### +enable_testing() +file(GLOB TEST_SOURCES "./tests/*.cpp") +add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) +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) \ No newline at end of file diff --git a/Containers/private/Containers.cpp b/Containers/private/Containers.cpp new file mode 100644 index 0000000..fa729a5 --- /dev/null +++ b/Containers/private/Containers.cpp @@ -0,0 +1,18 @@ + +#include "ContainersCommon.hpp" + +#include + +namespace tp { + + static ModuleManifest* sModuleDependencies[] = { &gModuleBase,nullptr }; + ModuleManifest gModuleContainers = ModuleManifest("Containers", nullptr, nullptr, sModuleDependencies); + + void* DefaultAllocator::allocate(ualni size) { + return malloc(size); + } + + void DefaultAllocator::deallocate(void* p) { + free(p); + } +} \ No newline at end of file diff --git a/Containers/public/AvlTree.hpp b/Containers/public/AvlTree.hpp new file mode 100644 index 0000000..72a1ef5 --- /dev/null +++ b/Containers/public/AvlTree.hpp @@ -0,0 +1,360 @@ +#pragma once + +#include "ContainersCommon.hpp" + +namespace tp { + + template + struct AvlNumericKey { + + NumericType val; + + AvlNumericKey() = default; + AvlNumericKey(NumericType val) : val(val) {} + + inline bool descentRight(AvlNumericKey in) const { return in.val > val; } + inline bool descentLeft(AvlNumericKey in) const { return in.val < val; } + inline bool exactNode(AvlNumericKey in) const { return in.val == val; } + + inline AvlNumericKey getFindKey(/**/) const { return val; } + inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in.val; } + inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in.val; } + + inline void updateTreeCacheCallBack() {} + }; + + template + class AvlTree { + typedef SelCopyArg KeyArg; + typedef SelCopyArg DataArg; + + public: + class Node { + friend AvlTree; + + private: + Node(KeyArg aKey, DataArg aData) : key(aKey), data(aData) {} + + public: + Data data; + Key key; + + private: + Node* mLeft = nullptr; + Node* mRight = nullptr; + Node* mParent = nullptr; + ualni mHeight = 0; + + private: + inline bool descentRight(KeyArg aKey) const { return key.descentRight(aKey); } + inline bool descentLeft(KeyArg aKey) const { return key.descentRight(aKey); } + inline bool exactNode(KeyArg aKey) const { return key.exactNode(aKey); } + + inline KeyArg getFindKey(const Node* node = nullptr) const { return key.getFindKey(/*node*/); } + inline KeyArg keyInRightSubtree(KeyArg aKey) const { return key.keyInRightSubtree(aKey); } + inline KeyArg keyInLeftSubtree(KeyArg aKey) const { return key.keyInLeftSubtree(aKey); } + + inline void updateTreeCacheCallBack() { key.updateTreeCacheCallBack(); } + }; + + private: + Node* mRoot = nullptr; + ualni mSize = 0; + Allocator mAlloc; + + private: + + inline void deleteNode(Node* node) { + node->~Node(); + mAlloc.deallocate(node); + } + + inline Node* newNode(KeyArg key, DataArg data) { + return new (mAlloc.allocate(sizeof(Node))) Node(key, data); + } + + inline ualni getNodeHeight(const Node* node) const { + return node ? node->mHeight : -1; + } + + // returns new head + Node* rotateLeft(Node* pivot) { + DEBUG_ASSERT(pivot); + + Node* const head = pivot; + Node* const right = pivot->mRight; + Node* const right_left = right->mLeft; + Node* const parent = pivot->mParent; + + // parents + if (right_left) right_left->mParent = head; + head->mParent = right; + right->mParent = parent; + + // children + head->mRight = right_left; + right->mLeft = head; + + // heights + head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); + right->mHeight = 1 + max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight)); + + // cache + head->updateTreeCacheCallBack(); + right->updateTreeCacheCallBack(); + + return right; + } + + Node* rotateRight(Node* pivot) { + DEBUG_ASSERT(pivot); + + Node* const head = pivot; + Node* const left = pivot->mLeft; + Node* const left_right = left->mRight; + Node* const parent = pivot->mParent; + + // parents + if (left_right) left_right->mParent = head; + head->mParent = left; + left->mParent = parent; + + // children + head->mLeft = left_right; + left->mRight = head; + + // heights + head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); + left->mHeight = 1 + max(getNodeHeight(left->mLeft), getNodeHeight(left->mRight)); + + // cache + head->updateTreeCacheCallBack(); + left->updateTreeCacheCallBack(); + + return left; + } + + // recursively returns valid left or right child or root + Node* insertUtil(Node* head, KeyArg key, DataArg data) { + + Node* insertedNode; + + if (head == nullptr) { + mSize++; + Node* out = newNode(key, data); + out->updateTreeCacheCallBack(); + return out; + } + else if (head->exactNode(key)) { + return head; + } + else if (head->descentRight(key)) { + insertedNode = insertUtil(head->mRight, head->keyInRightSubtree(key), data); + head->mRight = insertedNode; + insertedNode->mParent = head; + } + else { + insertedNode = insertUtil(head->mLeft, head->keyInLeftSubtree(key), data); + head->mLeft = insertedNode; + insertedNode->mParent = head; + } + + // update height + head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); + + alni balance = alni(getNodeHeight(head->mRight) - getNodeHeight(head->mLeft)); + + if (balance > 1) { + if (head->mRight->descentRight(head->keyInRightSubtree(key))) { + return rotateLeft(head); + } + else { + head->mRight = rotateRight(head->mRight); + return rotateLeft(head); + } + } + else if (balance < -1) { + if (head->mLeft->descentLeft(head->keyInLeftSubtree(key))) { + return rotateRight(head); + } + else { + head->mLeft = rotateLeft(head->mLeft); + return rotateRight(head); + } + } + + head->updateTreeCacheCallBack(); + + return head; + } + + Node* removeUtil(Node* head, KeyArg key) { + if (head == nullptr) return head; + + if (head->exactNode(key)) { + if (head->mRight && head->mLeft) { + Node* min = minNode(head->mRight); + head->data = min->data; + head->mRight = removeUtil(head->mRight, min->getFindKey(head->mRight)); + } + else if (head->mRight) { + head->data = head->mRight->data; + deleteNode(head->mRight); + head->mRight = nullptr; + mSize--; + } + else if (head->mLeft) { + head->data = head->mLeft->data; + deleteNode(head->mLeft); + head->mLeft = nullptr; + mSize--; + } + else { + deleteNode(head); + mSize--; + head = nullptr; + } + } + else if (head->descentRight(key)) { + head->mRight = removeUtil(head->mRight, head->keyInRightSubtree(key)); + } + else if (head->descentLeft(key)) { + head->mLeft = removeUtil(head->mLeft, head->keyInLeftSubtree(key)); + } + + if (head == nullptr) return head; + + head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); + alni balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); + + if (balance < -1) { + if (getNodeHeight(head->mLeft->mLeft) >= getNodeHeight(head->mLeft->mRight)) { + return rotateRight(head); + } + else { + head->mLeft = rotateLeft(head->mLeft); + return rotateRight(head); + } + } + else if (balance > 1) { + if (getNodeHeight(head->mRight->mRight) >= getNodeHeight(head->mRight->mLeft)) { + return rotateLeft(head); + } + else { + head->mRight = rotateRight(head->mRight); + return rotateLeft(head); + } + } + + head->updateTreeCacheCallBack(); + + return head; + } + + public: + + AvlTree() { + MODULE_SANITY_CHECK(gModuleContainers) + } + + [[nodiscard]] ualni size() const { + return mSize; + } + + Node* head() const { + return this->mRoot; + } + + void insert(KeyArg key, DataArg data) { + mRoot = insertUtil(mRoot, key, data); + mRoot->mParent = nullptr; + } + + void remove(KeyArg key) { + mRoot = removeUtil(mRoot, key); + if (mRoot) mRoot->mParent = nullptr; + } + + Node* maxNode(Node* head) const { + if (!head) return nullptr; + while (head->mRight != nullptr) { + head = head->mRight; + } + return head; + } + + Node* minNode(Node* head) const { + if (!head) return nullptr; + while (head->mLeft != nullptr) { + head = head->mLeft; + } + return head; + } + + Node* find(KeyArg key) const { + Node* iter = mRoot; + while (true) { + if (!iter) return nullptr; + if (iter->exactNode(key)) return iter; + if (iter->descentLeft(key)) { + key = iter->keyInLeftSubtree(key); + iter = iter->mLeft; + } else { + key = iter->keyInRightSubtree(key); + iter = iter->mRight; + } + } + } + + Node* findLessOrEq(KeyArg key) const { + Node* iter = mRoot; + while (true) { + if (!iter) return nullptr; + if (iter->exactNode(key)) return iter; + if (iter->descentLeft(key)) { + if (iter->mLeft) { + key = iter->keyInLeftSubtree(key); + iter = iter->mLeft; + } else { + return iter; + } + } else { + if (iter->mRight) { + key = iter->keyInRightSubtree(key); + iter = iter->mRight; + } else { + return iter; + } + } + } + } + + // returns first invalid node + const Node* findInvalidNode(const Node* head) const { + if (head == nullptr) return nullptr; + + if (head->mLeft) { + // TODO: incomplete test + if (!head->descentLeft(head->mLeft->getFindKey(head))) return head; + if (head->mLeft->mParent != head) return head; + } + + if (head->mRight) { + if (!head->descentRight(head->mRight->getFindKey(head))) return head; + if (head->mRight->mParent != head) return head; + } + + int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); + + if (balance > 1 || balance < -1) return head; + + const Node* ret = findInvalidNode(head->mRight); + + if (ret) return ret; + + return findInvalidNode(head->mLeft); + } + + bool isValid() { return findInvalidNode(head()) == nullptr; } + }; +} \ No newline at end of file diff --git a/Containers/public/ContainersCommon.hpp b/Containers/public/ContainersCommon.hpp new file mode 100644 index 0000000..20e8fb1 --- /dev/null +++ b/Containers/public/ContainersCommon.hpp @@ -0,0 +1,26 @@ + +#pragma once + +#include "BaseModule.hpp" + +namespace tp { + extern ModuleManifest gModuleContainers; + + class DefaultAllocator { + public: + DefaultAllocator() = default; + static void *allocate(ualni); + static void deallocate(void *); + }; + + class DefaultSaverLoader { + public: + DefaultSaverLoader() = default; + + template + static void write(const Type&) {} + + template + static void read(Type&) {} + }; +} \ No newline at end of file diff --git a/Containers/public/List.hpp b/Containers/public/List.hpp new file mode 100644 index 0000000..3db6e81 --- /dev/null +++ b/Containers/public/List.hpp @@ -0,0 +1,322 @@ + +#pragma once + +#include "ContainersCommon.hpp" + +#include "TypeInfo.hpp" + +namespace tp { + + template + class List { + + typedef SelCopyArg TypeArg; + typedef ualni Index; + + public: + + struct Node { + Type data; + Node* next = nullptr; + Node* prev = nullptr; + Node() = default; + explicit Node(TypeArg p_data) : data(p_data) {} + Type& operator->() { return data; } + }; + + class IteratorPointer { + protected: + Node* mIter; + public: + IteratorPointer() = default; + Type& operator->() { return (mIter->data); } + const Type& operator->() const { return (mIter->data); } + }; + + class IteratorReference { + protected: + Node* mIter; + public: + IteratorReference() = default; + Type* operator->() { return &(mIter->data); } + const Type* operator->() const { return &(mIter->data); } + }; + + class Iterator : public TypeSelect::isPointer, IteratorPointer, IteratorReference>::Result { + public: + + explicit Iterator(Node* iter) { this->mIter = iter; } + + Node* node() { return this->mIter; } + Type& data() { return this->mIter->data; } + const Node* node() const { return this->mIter; } + const Type& data() const { return this->mIter->data; } + + const Iterator& operator*() const { return *this; } + + Iterator& operator++() { + this->mIter = this->mIter->next; + return *this; + } + + bool operator==(const Iterator& left) const { return left.mIter == this->mIter; } + bool operator!=(const Iterator& left) const { return left.mIter != this->mIter; } + }; + + private: + Node* mFirst = nullptr; + Node* mLast = nullptr; + Index mLength = 0; + Allocator mAlloc; + + public: + + List() = default; + + List(const init_list& list) { operator=(list); } + + [[nodiscard]] inline Node* first() const { return mFirst; } + [[nodiscard]] inline Node* last() const { return mLast; } + [[nodiscard]] inline Index length() const { return mLength; } + + [[nodiscard]] Node* newNode() { return new (mAlloc.allocate(sizeof(Node))) Node(); } + [[nodiscard]] Node* newNode(TypeArg arg) { return new (mAlloc.allocate(sizeof(Node))) Node(arg); } + [[nodiscard]] Node* newNodeNotConstructed() { + auto node = (Node*) mAlloc.allocate(sizeof(Node)); + node->next = node->prev = nullptr; + return node; + } + + [[nodiscard]] const Allocator& getAllocator() const { return mAlloc; } + + void deleteNode(Node* node) { + node->~Node(); + mAlloc.deallocate(node); + } + + Node* addNodeBack() { + auto const out = newNode(); + pushBack(out); + return out; + } + + Node* addNodeFront() { + auto const out = newNode(); + pushFront(out); + return out; + } + + void attach(Node* node, Node* node_to) { + if (node_to) { + if (node_to->next) { + node->next = node_to->next; + node->next->prev = node; + } + node_to->next = node; + node->prev = node_to; + if (node_to == mLast) { + mLast = node; + } + } else { + if (mFirst) { + mFirst->prev = node; + node->next = mFirst; + mFirst = node; + } else { + mFirst = mLast = node; + } + } + mLength++; + } + + void detach(Node* node) { + if (node->next) { + node->next->prev = node->prev; + } + if (node->prev) { + node->prev->next = node->next; + } + if (node == mLast) { + mLast = mLast->prev; + } + if (node == mFirst) { + mFirst = mFirst->next; + } + mLength--; + } + + [[nodiscard]] Node* findIdx(Index idx) const { + DEBUG_ASSERT(!mFirst || idx > mLength - 1) + Node* found = mFirst; + for (int i = 0; i != idx; i++) { + found = found->next; + } + return found; + } + + [[nodiscard]] Node* find(const TypeArg data) const { + Node* found = mFirst; + for (alni i = 0; data != found->data; i++) { + if (!found->next) { + return nullptr; + } + found = found->next; + } + return found; + } + + [[nodiscard]] inline const Type& operator[](Index idx) const { + DEBUG_ASSERT(idx < mLength) + return find(idx)->data; + } + + void pushBack(Node* new_node) { attach(new_node, mLast); } + void pushFront(Node* new_node) { attach(new_node, nullptr); } + void pushBack(TypeArg data) { pushBack(newNode(data)); } + void pushFront(TypeArg data) { pushFront(newNode(data)); } + + void popBack() { + DEBUG_ASSERT(mLast) + detach(mLast); + deleteNode(mLast); + } + + void popFront() { + DEBUG_ASSERT(mFirst) + auto temp = mFirst; + detach(mFirst); + deleteNode(temp); + } + + void insert(Node* node, Index idx) { + if (!mLength) { + attach(node, mLast); + } else if (idx >= mLength) { + attach(node, nullptr); + } else { + attach(node, find(idx)->prev); + } + } + + void insert(TypeArg data, Index idx) { + insert(newNode(data), idx); + } + + void removeNode(Node* node) { + detach(node); + deleteNode(node); + } + + void removeAll() { + while (mFirst) { + popFront(); + } + } + + // copies data + List& operator+=(const List& in) { + for (auto node : in) { + pushBack(node.data()); + } + return *this; + } + + List& operator+=(const init_list& list) { + for (auto item : list) { + pushBack(item); + } + return *this; + } + + List& operator=(const List& in) { + if (this == &in) { return *this; } + removeAll(); + (*this) += in; + return *this; + } + + List& operator=(const init_list& list) { + removeAll(); + *this += list; + return *this; + } + + [[nodiscard]] bool operator==(const List& in) const { + if (in == *this) { return true; } + if (in.length() != length()) { + return false; + } + Node* left = in.first(); + Node* right = first(); + while (left && right) { + if (left->data != right->data) { + return false; + } + } + if (left != right) { + return false; + } + return true; + } + + template + [[nodiscard]] Node* find(bool (*found)(Node* node, compare_val val), compare_val value) const { + for (Node* node = mFirst; node; node = node->next) { + if (found(node, value)) { + return node; + } + } + return nullptr; + } + + [[nodiscard]] Iterator begin() const { + Iterator out(mFirst); + return out; + } + + [[nodiscard]] Iterator end() const { + return Iterator(nullptr); + } + + void invert() { + Node* iter = mFirst; + Node* tmp; + while (iter) { + tmp = iter; + iter = iter->next; + swap(tmp->next, tmp->prev); + } + swap(mFirst, mLast); + } + + void detachAll() { + while (mFirst) { + detach(mFirst); + } + } + + template + void write(Saver& file) const { + file.write(mLength); + for (auto item : *this) { + file.write(item.data()); + } + } + + template + void read(Loader& file) { + removeAll(); + ualni len; + file.read(len); + for (auto i = len; i; i--) { + auto node = newNodeNotConstructed(); + file.read(node->data); + pushBack(node); + } + } + + ~List() { + removeAll(); + } + }; +} \ No newline at end of file diff --git a/Containers/public/Map.hpp b/Containers/public/Map.hpp new file mode 100644 index 0000000..b92e11e --- /dev/null +++ b/Containers/public/Map.hpp @@ -0,0 +1,397 @@ + +#pragma once + +#include "ContainersCommon.hpp" +#include "Common.hpp" + +namespace tp { + + template + ualni DefaultHashFunc(SelCopyArg key) { + return hash(key); + } + + template< + typename tKey, typename tVal, + class tAllocator = DefaultAllocator, + ualni(*tHashFunc)(SelCopyArg) = DefaultHashFunc, + int tTableInitialSize = 4 + > + class Map { + + enum { + MAP_PERTURB_SHIFT = 5, + MAP_MIN_SIZE = 4, + MAP_MAX_LOAD_PERCENTAGE = 66, + }; + + typedef SelCopyArg KeyArg; + typedef SelCopyArg ValArg; + + public: + + class Node { + friend Map; + Node(KeyArg aKey, ValArg aVal) : key(aKey), val(aVal) {} + public: + tKey key; + tVal val; + }; + + struct Idx { + alni idx = -1; + operator bool() { return idx != -1; } + }; + + private: + tAllocator mAlloc; + Node** mTable; + ualni mNSlots = 0; + ualni mNEntries = 0; + + private: + + constexpr halnf maxLoadFactor() { return halnf(MAP_MAX_LOAD_PERCENTAGE) / 100.f; } + + inline Node** newTable(const ualni len) { + return new(mAlloc.allocate(sizeof(Node*) * len)) Node*[len](); + } + + inline Node* newNode(KeyArg key, ValArg val) { + return new(mAlloc.allocate(sizeof(Node))) Node(key, val); + } + + inline Node* newNodeNotConstructed() { + return (Node*) mAlloc.allocate(sizeof(Node)); + } + + inline void deleteTable(Node** table) { + mAlloc.deallocate(table); + } + + inline void deleteNode(Node* p) { + p->~Node(); + mAlloc.deallocate(p); + } + + void markDeletedSlot(ualni idx) const { + mTable[idx] = (Node*)-1; + } + + static bool isDeletedNode(Node* node) { + return node == (Node*)-1; + } + + void rehash() { + alni nSlotsOld = mNSlots; + Node** tableOld = mTable; + + mNSlots = next2pow((uhalni)((1.f / (maxLoadFactor())) * mNEntries + 1)); + mTable = newTable(mNSlots); + mNEntries = 0; + + for (alni i = 0; i < nSlotsOld; i++) { + if (!tableOld[i] || isDeletedNode(tableOld[i])) { + continue; + } + + alni idx = findSlotWrite(tableOld[i]->key); + mTable[idx] = tableOld[i]; + mNEntries++; + } + + deleteTable(tableOld); + } + + alni findSlotRead(KeyArg key) const { + ualni const hashed_key = tHashFunc(key); + ualni const mask = mNSlots - 1; + ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1; + alni idx = hashed_key & mask; + NEXT: + if (isDeletedNode(mTable[idx])) { + goto SKIP; + } + if (!mTable[idx]) { + return -1; + } + if (mTable[idx]->key == key) { + return idx; + } + SKIP: + idx = ((5 * idx) + 1 + shift) & mask; + goto NEXT; + } + + // compares keys only when collisions occur + alni findSlotReadExisting(KeyArg key) const { + ualni const hashed_key = tHashFunc(key); + ualni const mask = mNSlots - 1; + ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1; + alni idx = hashed_key & mask; + NEXT: + if (isDeletedNode(mTable[idx])) { + goto SKIP; + } + if (!mTable[idx]) { + return -1; + } + if (mTable[((5 * idx) + 1 + shift) & mask] == nullptr) { + return idx; + } + if (mTable[idx]->key == key) { + return idx; + } + SKIP: + idx = ((5 * idx) + 1 + shift) & mask; + goto NEXT; + } + + ualni findSlotWrite(KeyArg key) const { + ualni const hashed_key = tHashFunc(key); + ualni const mask = mNSlots - 1; + ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1; + ualni idx = hashed_key & mask; + NEXT: + if (isDeletedNode(mTable[idx]) || !mTable[idx]) { + return idx; + } + if (mTable[idx]->key == key) { + return idx; + } + idx = ((5 * idx) + 1 + shift) & mask; + goto NEXT; + } + + void put(Node* node) { + const ualni idx = findSlotWrite(node->key); + + if (!mTable[idx] || isDeletedNode(mTable[idx])) { + mNEntries++; + } + + mTable[idx] = node; + + if ((halnf)mNEntries / mNSlots > maxLoadFactor()) { + rehash(); + } + } + + public: + + Map() { + MODULE_SANITY_CHECK(gModuleContainers) + mNSlots = next2pow(uhalni(tTableInitialSize - 1)); + mTable = newTable(mNSlots); + } + + Node** buff() const { + return mTable; + } + + [[nodiscard]] ualni size() const { + return mNEntries; + } + + [[nodiscard]] ualni slotsSize() const { + return mNEntries; + } + + [[nodiscard]] const tAllocator& getAllocator() const { + return mAlloc; + } + + void put(KeyArg key, ValArg val) { + const ualni idx = findSlotWrite(key); + if (!mTable[idx] || isDeletedNode(mTable[idx])) { + mTable[idx] = newNode(key, val); + mNEntries++; + } + mTable[idx]->val = val; + if ((halnf) mNEntries / mNSlots > maxLoadFactor()) { + rehash(); + } + } + + // undefined behavior if item is not presents + tVal& get(KeyArg key) { + DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error") + return mTable[findSlotReadExisting(key)]->val; + } + + const tVal& get(KeyArg key) const { + DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error") + return mTable[findSlotReadExisting(key)]->val; + } + + [[nodiscard]] Idx presents(KeyArg key) const { return { findSlotRead(key) }; } + + void remove(KeyArg key) { + DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error") + auto idx = findSlotReadExisting(key); + + deleteNode(mTable[idx]); + + markDeletedSlot(idx); + + mNEntries--; + if (halnf(mNEntries / mNSlots) < 1.f - maxLoadFactor()) { + rehash(); + } + } + + const tVal& getSlotVal(ualni slot) const { + DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error") + return mTable[slot]->val; + } + + tVal& getSlotVal(ualni slot) { + DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error") + return mTable[slot]->val; + } + + const tVal& getSlotVal(Idx slot) const { + DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error") + return mTable[slot]->val; + } + + tVal& getSlotVal(Idx slot) { + DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error") + return mTable[slot.idx]->val; + } + + Map& operator=(const Map& in) { + if (this == &in) { + return *this; + } + removeAll(); + mNSlots = in.mNSlots; + mTable = newTable(mNSlots); + for (alni i = 0; i < mNSlots; i++) { + if (in.mTable[i] && !isDeletedNode(in.mTable[i])) { + put(in.mTable[i]->key, in.mTable[i]->val); + } + } + return *this; + } + + [[nodiscard]] bool operator==(const Map& in) const { + if (this == &in) { + return true; + } + if (in.mNEntries != mNEntries) { + return false; + } + for (auto i : in) { + if (!presents(i->key) || get(i->key) != i->val) { + return false; + } + } + return true; + } + + void removeAll() { + for (ualni i = 0; i < mNSlots; i++) { + if (mTable[i] && !isDeletedNode(mTable[i])) { + deleteNode(mTable[i]); + } + } + deleteTable(mTable); + mTable = newTable(tTableInitialSize); + mNSlots = tTableInitialSize; + mNEntries = 0; + } + + [[nodiscard]] alni slotIdx(alni entry_idx_in) const { + alni entry_idx = -1; + for (alni slot_idx = 0; slot_idx < mNSlots; slot_idx++) { + if (mTable[slot_idx]) { + entry_idx++; + } + if (entry_idx == entry_idx_in) { + return slot_idx; + } + } + return -1; + } + + Node* GetEntry(ualni idx) { + auto slot = slotIdx(idx); + DEBUG_ASSERT(slot != -1 && "Key error") + return mTable[slot]; + } + + const Node* GetEntry(ualni idx) const { + auto slot = slotIdx(idx); + DEBUG_ASSERT(slot != -1 && "Key error") + return mTable[slot]; + } + + public: + + class Iterator { + const Map* map; + Node* mIter; + alni mSlot; + alni mEntry; + + friend Map; + explicit Iterator(const Map* _map) { + mSlot = -1; + mEntry = -1; + map = _map; + this->operator++(); + } + + public: + Node* operator->() { return mIter; } + const Node* operator->() const { return mIter; } + const Iterator& operator*() const { return *this; } + + bool operator!=(ualni idx) const { return mSlot != idx; } + + void operator++() { + mSlot++; + while ((map->isDeletedNode(map->mTable[mSlot]) || !map->mTable[mSlot]) && (mSlot != map->mNSlots)) { + mSlot++; + } + if (mSlot != map->mNSlots) { + mIter = map->mTable[mSlot]; + mEntry++; + } + } + }; + + [[nodiscard]] Iterator begin() const { + return Iterator(this); + } + + [[nodiscard]] ualni end() const { + return mNSlots; + } + + template + void write(Saver& file) { + file.write(mNEntries); + for (auto item : *this) { + file.write(item->val); + file.write(item->key); + } + } + + template + void read(Loader& file) { + removeAll(); + ualni len; + file.read(len); + for (auto i = len; i; i--) { + auto node = newNodeNotConstructed(); + file.read(node->val); + file.read(node->key); + put(node); + } + } + + ~Map() { removeAll(); } + }; +} \ No newline at end of file diff --git a/Containers/tests/AvlTest.cpp b/Containers/tests/AvlTest.cpp new file mode 100644 index 0000000..5c57729 --- /dev/null +++ b/Containers/tests/AvlTest.cpp @@ -0,0 +1,31 @@ + +#include "Tests.hpp" + +#include "AvlTree.hpp" + +#include "Testing.hpp" + +#include + +using namespace tp; + +TEST_DEF_STATIC(Simple) { + AvlTree, TestClass, TestAllocator> tree; + + TEST(tree.size() == 0); + TEST(tree.head() == nullptr); + + tree.insert(6, TestClass(6)); + TEST(tree.isValid()); + TEST(tree.size() == 1); + TEST(tree.head()->data == TestClass(6)); + + tree.remove(6); + TEST(tree.isValid()); + TEST(tree.size() == 0); + TEST(tree.head() == nullptr); +} + +TEST_DEF(Avl) { + testSimple(); +} \ No newline at end of file diff --git a/Containers/tests/ListTest.cpp b/Containers/tests/ListTest.cpp new file mode 100644 index 0000000..c33c5f1 --- /dev/null +++ b/Containers/tests/ListTest.cpp @@ -0,0 +1,88 @@ + +#include "Tests.hpp" +#include "Testing.hpp" + +#include + +using namespace tp; + +TEST_DEF_STATIC(SimpleReference) { + tp::List list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) }; + + list.pushBack(TestClass(5)); + list.pushFront(TestClass(0)); + + ualni i = -1; + for (auto iter : list) { + i++; + TEST_EQUAL(iter->getVal(), i); + } + + TEST(i == 5); + + list.removeAll(); + TEST(list.getAllocator().getAllocationsCount() == 0); +} + +TEST_DEF_STATIC(SimplePointer) { + tp::List list = { new TestClass(1), new TestClass(2), new TestClass(3), new TestClass(4) }; + + list.pushBack(new TestClass(5)); + list.pushFront(new TestClass(0)); + + ualni i = -1; + for (auto iter : list) { + i++; + TEST_EQUAL(iter->getVal(), i); + } + + TEST(i == 5); + + list.removeAll(); + + TEST(list.getAllocator().getAllocationsCount() == 0); +} + +TEST_DEF_STATIC(Copy) { + tp::List list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) }; + tp::List list2 = list; + + TEST_EQUAL(list, list2); + + list.removeAll(); + list2.removeAll(); + + TEST(list.getAllocator().getAllocationsCount() == 0); + TEST(list2.getAllocator().getAllocationsCount() == 0); +} + +TEST_DEF_STATIC(SaveLoad) { + tp::List list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) }; + + TestFile file; + + list.write(file); + + list.removeAll(); + + file.setAddress(0); + + list.read(file); + + ualni i = 0; + for (auto iter : list) { + i++; + TEST_EQUAL(iter->getVal(), i); + } + TEST(i == 4); + + list.removeAll(); + + TEST(list.getAllocator().getAllocationsCount() == 0); +} + +TEST_DEF(List) { + testSimplePointer(); + testSimpleReference(); + testSaveLoad(); +} \ No newline at end of file diff --git a/Containers/tests/MapTest.cpp b/Containers/tests/MapTest.cpp new file mode 100644 index 0000000..1940f22 --- /dev/null +++ b/Containers/tests/MapTest.cpp @@ -0,0 +1,137 @@ + +#include "Tests.hpp" +#include "Testing.hpp" + +#include "Map.hpp" + +#include + +using namespace tp; + +TEST_DEF_STATIC(SimpleReference) { + tp::Map map; + + for (auto i : Range(1000, 100000)) { + map.put(i, TestClass(i)); + } + + for (auto i : Range(1000, 100000)) { + TEST(map.presents(i)); + TEST_EQUAL(map.get(i).getVal(), i); + } + + for (auto i : Range(1000, 100000)) { + map.put(i, TestClass(i)); + } + + for (auto i : Range(1000, 2000)) { + TEST(map.presents(i)); + map.remove(i); + TEST(!map.presents(i)); + } + + for (auto i : Range(2000, 100000)) { + TEST(map.presents(i)); + TEST_EQUAL(map.get(i).getVal(), i); + } + + for (auto i : map) { + i->val.setVal(3); + } + + map.removeAll(); + + TEST(map.getAllocator().getAllocationsCount() == 1); +} + +TEST_DEF_STATIC(SimplePointer) { + tp::Map map; + + for (auto i : Range(1000)) { + map.put(i, new TestClass(i)); + } + + for (auto i : Range(1000)) { + TEST(map.presents(i)); + TEST_EQUAL(map.get(i)->getVal(), i); + } + + for (auto i : Range(1000)) { + map.put(i, new TestClass(i)); + } + + for (auto i : Range(900, 1000)) { + TEST(map.presents(i)); + map.remove(i); + TEST(!map.presents(i)); + } + + for (auto i : Range(900)) { + TEST(map.presents(i)); + TEST_EQUAL(map.get(i)->getVal(), i); + } + + for (auto i : map) { + i->val->setVal(3); + delete i->val; + } + + map.removeAll(); + + TEST(map.getAllocator().getAllocationsCount() == 1); +} + +TEST_DEF_STATIC(Copy) { + tp::Map map; + + for (auto i : Range(10)) { + map.put(i, TestClass(i)); + } + + tp::Map map2 = map; + + TEST_EQUAL(map, map2); + + map.removeAll(); + map2.removeAll(); + + TEST(map.getAllocator().getAllocationsCount() == 1); + TEST(map2.getAllocator().getAllocationsCount() == 1); +} + +TEST_DEF_STATIC(SaveLoad) { + tp::Map map; + + for (auto i : Range(10)) { + map.put(i, TestClass(i)); + } + + TestFile file; + + map.write(file); + + map.removeAll(); + + TEST(map.getAllocator().getAllocationsCount() == 1); + + file.setAddress(0); + + map.read(file); + + TEST(map.getAllocator().getAllocationsCount() == 11); + + for (auto i : Range(10)) { + TEST(map.presents(i)); + TEST_EQUAL(map.get(i).getVal(), i); + } + + map.removeAll(); + + TEST(map.getAllocator().getAllocationsCount() == 1); +} + +TEST_DEF(Map) { + testSimplePointer(); + testSimpleReference(); + testSaveLoad(); +} \ No newline at end of file diff --git a/Containers/tests/Tests.cpp b/Containers/tests/Tests.cpp new file mode 100644 index 0000000..b5d0fbf --- /dev/null +++ b/Containers/tests/Tests.cpp @@ -0,0 +1,41 @@ + +#include "Tests.hpp" + +#include "Testing.hpp" + +#include + +static bool init(const tp::ModuleManifest* self) { + tp::gTesting.setRootName(self->getName()); + return true; +} + +void* TestAllocator::allocate(tp::ualni size) { + nAllocations++; + return malloc(size); +} + +void TestAllocator::deallocate(void* p) { + nAllocations--; + free(p); +} + +tp::ualni TestAllocator::getAllocationsCount() const { + return nAllocations; +} + +int main() { + + tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr }; + tp::ModuleManifest testModule("ContainersTest", init, nullptr, deps); + + if (!testModule.initialize()) { + return 1; + } + + testList(); + testMap(); + testAvl(); + + testModule.deinitialize(); +} diff --git a/Containers/tests/Tests.hpp b/Containers/tests/Tests.hpp new file mode 100644 index 0000000..f4a7cc4 --- /dev/null +++ b/Containers/tests/Tests.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "Utils.hpp" + +class TestClass { + tp::ualni val2 = 0; + tp::ualni val1; +public: + + explicit TestClass(tp::ualni val) : val1(val) {} + + template + void write(Saver& file) const { + file.write(val1); + } + + template + void read(Loader& file) { + file.read(val1); + } + + [[nodiscard]] bool operator==(const TestClass& in) const { + return in.val1 == val1; + } + + [[nodiscard]] tp::ualni getVal() const { return val1; } + void setVal(tp::ualni val) { val1 = val; } +}; + +class TestAllocator { + tp::ualni nAllocations = 0; +public: + TestAllocator() = default; + void* allocate(tp::ualni size); + void deallocate(void* p); + [[nodiscard]] tp::ualni getAllocationsCount() const; +}; + +class TestFile { + tp::ualni mem[1024] = { 0 }; + tp::ualni address = 0; + +public: + TestFile() = default; + + template + void write(const Type& val) { + val.write(*this); + } + + template<> + void write(const tp::ualni& val) { + mem[address] = val; + address++; + } + + void setAddress(tp::ualni addr) { address = addr; } + + template + void read(Type& val) { + val.read(*this); + } + + template<> + void read(tp::ualni& val) { + val = mem[address]; + address++; + } +}; + +void testList(); +void testMap(); +void testAvl(); \ No newline at end of file diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..3e37552 --- /dev/null +++ b/README.MD @@ -0,0 +1 @@ +Modules diff --git a/Utils/CMakeLists.txt b/Utils/CMakeLists.txt new file mode 100644 index 0000000..5651c5d --- /dev/null +++ b/Utils/CMakeLists.txt @@ -0,0 +1,24 @@ + +cmake_minimum_required(VERSION 3.2) + +set(CMAKE_CXX_STANDARD 23) + +project(Utils) + +### ---------------------- Static Library --------------------- ### +file(GLOB SOURCES "./private/*.cpp") +add_library(${PROJECT_NAME} STATIC ${SOURCES}) +target_include_directories(${PROJECT_NAME} PUBLIC ./public/) + +### ---------------------- Dependencies --------------------- ### +target_link_libraries(${PROJECT_NAME} PUBLIC Containers) + +### -------------------------- Tests -------------------------- ### +enable_testing() +file(GLOB TEST_SOURCES "./tests/*.cpp") +add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) +target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME}) +add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) + + +install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib) \ No newline at end of file diff --git a/Utils/private/Debugging.cpp b/Utils/private/Debugging.cpp new file mode 100644 index 0000000..7097e55 --- /dev/null +++ b/Utils/private/Debugging.cpp @@ -0,0 +1,207 @@ + +#include "Debugging.hpp" +#include "Utils.hpp" + +#include + +using namespace tp; + +CallStackCapture* tp::gCSCapture = nullptr; + +void initializeCallStackCapture() { + gCSCapture = new CallStackCapture(); +} + +void deinitializeCallStackCapture() { + delete gCSCapture; +} + +ualni CallStackCapture::CallStack::getDepth() const { + ualni len = 0; + for (long long frame : frames) { + if (!frame) { break; } + len++; + } + ualni stripedLen = 0; + for (auto i = FRAMES_TO_SKIP_START; i < len - FRAMES_TO_SKIP_END; i++) { + if (!frames[i]) { break; } + stripedLen++; + } + return stripedLen; +} + +ualni CallStackCapture::hashCallStack(CallStackKey key) { + auto const cs = key.cs; + ualni out = 0; + for (ualni i = 0; cs->frames[i]; i++) { out += cs->frames[i]; } + return out; +} + +bool CallStackCapture::CallStackKey::operator==(const CallStackCapture::CallStackKey &in) const { + for (auto i : Range(MAX_CALL_DEPTH_CAPTURE)) { + if (cs->frames[i] != in.cs->frames[i]) { + return false; + } + if (cs->frames[i] == 0 && in.cs->frames[i] == 0) { + return true; + } + } + DEBUG_ASSERT(0 && "Must Not Happen") + return true; +} + +CallStackCapture::CallStackCapture() { + static_assert(MAX_CALL_DEPTH_CAPTURE >= 1); + static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB > 0); + static_assert(MAX_DEBUG_INFO_LEN > sizeof("unresolved")); + static_assert(FRAMES_TO_SKIP_END >= 0 && FRAMES_TO_SKIP_START >= 0); + static_assert(MAX_CALL_DEPTH_CAPTURE > FRAMES_TO_SKIP_START + FRAMES_TO_SKIP_END); + static_assert(MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024 > sizeof(CallStack)); + + MODULE_SANITY_CHECK(gModuleUtils) + mBuffLoad = 0; + mBuffLen = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack); + mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack)); +} + +const CallStackCapture::CallStack* CallStackCapture::getSnapshot() { + if (mBuffLoad > mBuffLen) { + static CallStack cs; + cs.frames[0] = 0; + return &cs; + } + + CallStack* cs = &mBuff[mBuffLoad]; + platformWriteStackTrace(cs); + + auto idx = mSnapshots.presents({ cs }); + if (idx) { + return mSnapshots.getSlotVal(idx); + } + + mSnapshots.put({ cs }, cs); + + mBuffLoad++; + return cs; +} + +const CallStackCapture::DebugSymbols* CallStackCapture::getSymbols(FramePointer frame) { + auto idx = mSymbols.presents(frame); + if (idx) { + return &mSymbols.getSlotVal(idx); + } + + mSymbols.put(frame, {}); + auto symbols = &mSymbols.get(frame); + + platformWriteDebugSymbols(frame, symbols); + return symbols; +} + +void CallStackCapture::clear() { + mBuffLoad = 0; + mSnapshots.removeAll(); + mSymbols.removeAll(); +} + +CallStackCapture::~CallStackCapture() { + free(mBuff); +} + +// ---------------------------------- Platform Depended ---------------------------------- // + +#if defined(ENV_OS_LINUX) + +#include +#include +#include +#include + +void CallStackCapture::platformWriteStackTrace(CallStack* stack) { + auto depth = backtrace((void**)stack->frames, (int) MAX_CALL_DEPTH_CAPTURE - 1); + stack->frames[depth] = 0; +} + +static void getGetSourceFromBinaryAddress(const char* binary, const char* address, char* file, ualni* line) { + static char buff[1024]; + + snprintf(buff, sizeof(buff), "addr2line -e %s -a %s", binary, address); + FILE* pipe = popen(buff, "r"); + if (pipe) { + fgets(buff, sizeof(buff), pipe); + fgets(buff, sizeof(buff), pipe); + pclose(pipe); + char* linePtr = strchr(buff, ':'); + + printf("%s\n", buff); + + if (linePtr != nullptr && buff[0] != '?' && buff[1] != '?' && buff[2] != ':') { + *linePtr = '\0'; + auto sourceLen = std::strlen(buff); + std::strcpy(file, buff + ((sourceLen > MAX_DEBUG_INFO_LEN) ? (sourceLen - MAX_DEBUG_INFO_LEN) : 0)); + *line = strtoul(linePtr + 1, nullptr, 10); + return; + } + } + + std::strcpy(file, "unresolved"); + *line = 0; +} + +static void getDemangledName(const char* func, char* out) { + int status; + size_t funcDemangledSize = MAX_DEBUG_INFO_LEN; + char* funcDemangled = (char*)malloc(funcDemangledSize); + char* ret = abi::__cxa_demangle(func, funcDemangled, &funcDemangledSize, &status); + if (status == 0) { + funcDemangled = ret; + auto funcLen = std::strlen(funcDemangled); + std::strcpy(out, funcDemangled + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0)); + free(ret); + return; + } + auto funcLen = std::strlen(func); + std::strcpy(out, func + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0)); + free(funcDemangled); +} + +void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) { + void* addrList[1] = { (void*) frame }; + auto symbolsArray = backtrace_symbols(addrList, 1); + + // 'bin(fun+addr)' + char* bin = *symbolsArray; + char* func = nullptr; + char* offset = nullptr; + + // 'bin fun+addr' + for (char *p = bin; *p; ++p) { + if (*p == '(') { *p = 0; func = p + 1; } + else if (*p == '+') { offset = p; } + else if (*p == ')' && offset) { *p = 0; } + } + + if (func && offset) { + getGetSourceFromBinaryAddress(bin, func, out->file, &out->line); + + if (offset != func) { + *offset = 0; + getDemangledName(func, out->function); + } else { + std::strcpy(out->function, "unresolved"); + } + } else { + std::strcpy(out->file, "unresolved"); + std::strcpy(out->function, "unresolved"); + } + + free(symbolsArray); +} + +#else +void CallStackCapture::platformWriteStackTrace(CallStack* stack) { stack->frames[0] = 0; } +void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) { + std::strcpy(out->file, "unresolved"); + std::strcpy(out->function, "unresolved"); +} +#endif \ No newline at end of file diff --git a/Utils/private/Testing.cpp b/Utils/private/Testing.cpp new file mode 100644 index 0000000..63b75e7 --- /dev/null +++ b/Utils/private/Testing.cpp @@ -0,0 +1,73 @@ + +#include "Testing.hpp" +#include "Utils.hpp" + +#include +#include + +using namespace tp; + +Testing tp::gTesting; + +void Testing::startTest(const char* name) { + MODULE_SANITY_CHECK(gModuleUtils) + mCurrent->mSubTests.pushBack(new TestingNode{ {}, {}, name, mCurrent }); + mCurrent = mCurrent->mSubTests.last()->data; +} + +void Testing::endTest() { + if (mCurrent->mParent) { + mCurrent = mCurrent->mParent; + } +} + +void Testing::addFailedCheck(const FailedCheck& info) { + mCurrent->mFailedChecks.pushBack(info); +} + +void Testing::reportState() { + mRootTest.updateState(); + + printf("\n"); + mRootTest.report(); +} + +bool Testing::hasFailed() { + mRootTest.updateState(); + return mRootTest.mHasFailed; +} + +void Testing::setRootName(const char* name) { + mRootTest.mName = name; +} + +void Testing::TestingNode::updateState() { + for (auto child : mSubTests) { + child->updateState(); + mHasFailed = child->mHasFailed; + if (mHasFailed) return; + } + mHasFailed = mFailedChecks.length(); +} + +void Testing::TestingNode::report(const char* path) const { + if (!mHasFailed) { + return; + } + + auto newPath = path ? std::string(path) + "/" + mName : std::string(mName); + + for (auto check : mFailedChecks) { + printf("%s Failed - (%s) %s:%llu\n", newPath.c_str(), check.data().expression, check.data().file, check.data().line); + } + + for (const auto& child : mSubTests) { + child->report(newPath.c_str()); + } +} + +Testing::TestingNode::~TestingNode() { + for (const auto& child : mSubTests) { + delete child.data(); + } +} diff --git a/Utils/private/Timing.cpp b/Utils/private/Timing.cpp new file mode 100644 index 0000000..cd02685 --- /dev/null +++ b/Utils/private/Timing.cpp @@ -0,0 +1,81 @@ + +#include +#include + +#include "Timing.hpp" +#include "Utils.hpp" + +#define GETTIMEMSC() \ + (time_ms)( \ + std::chrono::duration_cast( \ + std::chrono::time_point_cast(std::chrono::high_resolution_clock::now()) \ + .time_since_epoch()) \ + .count()) + +#define THREAD_SLEEP(time_ms) std::this_thread::sleep_for(std::chrono::milliseconds(time_ms)) + +tp::time_ms tp::gCurrentTime = tp::get_time(); + +namespace tp { + time_ms get_time() { + gCurrentTime = GETTIMEMSC(); + return gCurrentTime; + } + + void sleep(time_ms mDuration) { + THREAD_SLEEP(mDuration); + } + + + Timer::Timer() { + mDuration = 0; + mStart = GETTIMEMSC(); + } + + Timer::Timer(time_ms mDuration) { + mStart = GETTIMEMSC(); + this->mDuration = mDuration; + } + + bool Timer::isTimeout() { + return mDuration < GETTIMEMSC() - mStart; + } + + void Timer::reset() { + mStart = GETTIMEMSC(); + } + + time_ms Timer::timePassed() { + return GETTIMEMSC() - mStart; + } + + time_ms Timer::remainder() { + return mDuration - (GETTIMEMSC() - mStart); + } + + time_ms Timer::start() { return mStart; } + time_ms Timer::duration() { return mDuration; } + void Timer::setDuration(time_ms dur) { mDuration = dur; } + + void Timer::wait() { + if (!isTimeout()) { + sleep(remainder()); + } + } + + float Timer::easeIn(time_ms pDuration) { + if (!pDuration) { + pDuration = mDuration; + } + float x = (1.f / pDuration) * timePassed(); + return clamp((1.1f * x) / (x + 0.1f), 0.f, 1.f); + } + + float Timer::easeOut(time_ms pDuration) { + if (!pDuration) { + pDuration = mDuration; + } + float x = (1.f / pDuration) * timePassed(); + return clamp((0.1f * (1 - x)) / (x + 0.1f), 0.f, 1.f); + } +} \ No newline at end of file diff --git a/Utils/private/Utils.cpp b/Utils/private/Utils.cpp new file mode 100644 index 0000000..b330593 --- /dev/null +++ b/Utils/private/Utils.cpp @@ -0,0 +1,94 @@ + +#include "Utils.hpp" + +#include "ContainersCommon.hpp" + +#include "Testing.hpp" + +#include + +void initializeCallStackCapture(); +void deinitializeCallStackCapture(); + +static bool initialize(const tp::ModuleManifest* self) { + initializeCallStackCapture(); + return true; +} + +static void deinitialize(const tp::ModuleManifest* self) { + deinitializeCallStackCapture(); + tp::gTesting.reportState(); + if (tp::gTesting.hasFailed()) { exit(1); } +} + +namespace tp { + + static ModuleManifest* sModuleUtilsDeps[] = { &gModuleContainers, nullptr }; + ModuleManifest gModuleUtils = ModuleManifest("Utils", initialize, deinitialize, sModuleUtilsDeps); + + void memsetv(void* p, uhalni bytesize, uint1 val) { + MODULE_SANITY_CHECK(gModuleBase) + + alni alignedval = 0; + for (ualni idx = 0; idx < sizeof(alni); idx++) { + ((uint1*) &alignedval)[idx] = val; + } + + ualni alignedlen = bytesize / sizeof(alni); + for (ualni idx = 0; idx < alignedlen; idx++) { + ((alni*) p)[idx] = alignedval; + } + + 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) { + MODULE_SANITY_CHECK(gModuleBase) + + ualni alignedlen = len / sizeof(alni); + for (ualni idx = 0; idx < alignedlen; idx++) { + ((alni*) left)[idx] = ((alni*) right)[idx]; + } + + ualni unalignedlen = len - (alignedlen * sizeof(alni)); + for (ualni idx = 0; idx < unalignedlen; idx++) { + ((uint1*) left)[len - idx - 1] = ((uint1*) right)[len - idx - 1]; + } + } + + int1 memecomp(const void* left, const void* right, uhalni len) { + MODULE_SANITY_CHECK(gModuleBase) + if (!len) return 0; + + ualni alignedLength = len / sizeof(alni); + for (ualni idx = 0; idx < alignedLength; idx++) { + if (((alni*) left)[idx] == ((alni*) right)[idx]) { + continue; + } + if (((alni*) left)[idx] > ((alni*) right)[idx]) { + return 1; + } + return -1; + } + + ualni unalignedLength = len - (alignedLength * sizeof(alni)); + for (ualni idx = 0; idx < unalignedLength; idx++) { + if (((uint1*) left)[len - idx - 1] == ((uint1*) right)[len - idx - 1]) { + continue; + } + if (((uint1*) left)[len - idx - 1] > ((uint1*) right)[len - idx - 1]) { + return 1; + } + return -1; + } + return 0; + } + + alnf randf() { + alnf r = static_cast(std::rand()) / static_cast(RAND_MAX); + return r; + } +} \ No newline at end of file diff --git a/Utils/public/Debugging.hpp b/Utils/public/Debugging.hpp new file mode 100644 index 0000000..ef128af --- /dev/null +++ b/Utils/public/Debugging.hpp @@ -0,0 +1,122 @@ +#pragma once + +#include "Environment.hpp" +#include "Map.hpp" + +#define MAX_CALL_DEPTH_CAPTURE 16 +#define MAX_CALL_CAPTURES_MEM_SIZE_MB 16 +#define MAX_DEBUG_INFO_LEN 63 +#define FRAMES_TO_SKIP_START 2 +#define FRAMES_TO_SKIP_END 3 + +namespace tp { + + class CallStackCapture { + public: + typedef tp::alni FramePointer; + + class CallStack { + friend CallStackCapture; + FramePointer frames[MAX_CALL_DEPTH_CAPTURE]; + + public: + [[nodiscard]] ualni getDepth() const; + + class Iterator { + const FramePointer* mFrame; + public: + explicit Iterator(const FramePointer* frame) : mFrame(frame) {}; + FramePointer getFrame() { return *mFrame; } + bool operator==(const Iterator& in) const { return in.mFrame == mFrame; } + void operator++() { mFrame++; } + const Iterator& operator*() const { return *this; } + }; + + [[nodiscard]] Iterator begin() const { return Iterator(frames + FRAMES_TO_SKIP_START); } + [[nodiscard]] Iterator end() const { return Iterator(frames + FRAMES_TO_SKIP_START + getDepth()); } + }; + + class DebugSymbols { + friend CallStackCapture; + char function[MAX_DEBUG_INFO_LEN + 1] = { 0 }; + char file[MAX_DEBUG_INFO_LEN + 1] = { 0 }; + ualni line = 0; + public: + [[nodiscard]] const char* getFunc() const { return function; } + [[nodiscard]] const char* getFile() const { return file; } + [[nodiscard]] ualni getLine() const { return line; } + }; + + public: + CallStackCapture(); + ~CallStackCapture(); + + [[nodiscard]] const CallStack* getSnapshot(); + const DebugSymbols* getSymbols(FramePointer fp); + + public: + + template + void write(Saver& file) { + file.write(mBuffLoad); + for (auto cs : *this) { + file.write(cs.getCallStack()->getDepth()); + for (auto frame : *cs.getCallStack()) { + file.write((ualni) frame.getFrame()); + } + } + file.write(mSymbols); + } + + // independent of the configuration + template + void read(Loader& file) { + clear(); + ualni loadLen; + file.read(loadLen); + for (auto cs = loadLen; cs; cs--) { + ualni callStackLen; + file.read(callStackLen); + for (auto fp = callStackLen; fp; fp--) { + // -- + } + } + } + + public: + class Iterator { + const CallStack* mSnapshot; + public: + explicit Iterator(const CallStack* start) : mSnapshot(start) {}; + const CallStack* getCallStack() { return mSnapshot; } + bool operator==(const Iterator& in) const { return in.mSnapshot == mSnapshot; } + void operator++() { mSnapshot++; } + const Iterator& operator*() const { return *this; } + }; + + [[nodiscard]] Iterator begin() const { return Iterator(mBuff); } + [[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); } + + private: + + struct CallStackKey { + CallStack* cs; + bool operator==(const CallStackKey& in) const; + }; + + static void platformWriteStackTrace(CallStack* stack); + static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out); + [[nodiscard]] static ualni hashCallStack(CallStackKey key); + + void clear(); + + private: + ualni mBuffLen; + ualni mBuffLoad; + CallStack* mBuff; + Map mSnapshots; + Map mSymbols; + }; + + extern CallStackCapture* gCSCapture; +} diff --git a/Utils/public/Sorting.hpp b/Utils/public/Sorting.hpp new file mode 100644 index 0000000..c166674 --- /dev/null +++ b/Utils/public/Sorting.hpp @@ -0,0 +1,97 @@ + +#pragma once + +#include "Utils.hpp" + +namespace tp { + + template + inline bool compare(const Type& val1, const Type& val2) { + return val1 > val2; + } + + struct SortMerge { + + template + static void sort(Type* buff, int length, bool (*grater)(const Type& obj1, const Type& obj2) = &compare) { + mergeSort(buff, 0, length - 1, grater); + } + + private: + + template + static void merge(Type* buff, int left, int middle, int right, bool (*grater)(const Type& obj1, const Type& obj2)) { + int n1 = middle - left + 1; + int n2 = right - middle; + + Type* Left = new Type[n1]; + Type* Right = new Type[n2]; + + for (int i = 0; i < n1; i++) { + Left[i] = buff[left + i]; + } + + for (int j = 0; j < n2; j++) { + Right[j] = buff[middle + 1 + j]; + } + + int i = 0; + int j = 0; + int k = left; + + while (i < n1 && j < n2) { + if (!(grater(Left[i], Right[j]))) { + buff[k] = Left[i]; + i++; + } else { + buff[k] = Right[j]; + j++; + } + k++; + } + + while (i < n1) { + buff[k] = Left[i]; + i++; + k++; + } + + while (j < n2) { + buff[k] = Right[j]; + j++; + k++; + } + + delete[] Left; + delete[] Right; + } + + template + static void mergeSort(Type* buff, int left, int right, bool (*grater)(const Type& obj1, const Type& obj2)) { + + if (left >= right) { + return; + } + + int middle = left + (right - left) / 2; + + mergeSort(buff, left, middle, grater); + mergeSort(buff, middle + 1, right, grater); + merge(buff, left, middle, right, grater); + } + + }; + + struct SortInsert { + template + static void sort(Type* buff, int length, bool (*grater)(const Type& obj1, const Type& obj2) = &compare) { + for (int i = 0; i < length; i++) { + for (int j = i + 1; j < length; j++) { + if (grater(*buff[i], *buff[j])) { + swap(buff[i], buff[j]); + } + } + } + } + }; +} \ No newline at end of file diff --git a/Utils/public/Testing.hpp b/Utils/public/Testing.hpp new file mode 100644 index 0000000..9567513 --- /dev/null +++ b/Utils/public/Testing.hpp @@ -0,0 +1,63 @@ + +#pragma once + +#include "List.hpp" + +namespace tp { + class Testing { + public: + + struct FailedCheck { + const char* expression = nullptr; + const char* file = nullptr; + ualni line = 0; + }; + + Testing() = default; + void startTest(const char* name); + void endTest(); + void addFailedCheck(const FailedCheck& info); + void reportState(); + void setRootName(const char* name); + [[nodiscard]] bool hasFailed(); + + private: + struct TestingNode { + List mFailedChecks; + List mSubTests; + const char* mName = "Unnamed"; + TestingNode* mParent = nullptr; + bool mHasFailed = false; + + void report(const char* path = nullptr) const; + void updateState(); + ~TestingNode(); + }; + + TestingNode mRootTest; + TestingNode* mCurrent = &mRootTest; + }; + + extern Testing gTesting; +} + +#define TEST_DEF(Name)\ + static void Name##FunctorBody();\ + void test##Name() { \ + tp::gTesting.startTest(#Name);\ + Name##FunctorBody();\ + tp::gTesting.endTest();\ + } \ + void Name##FunctorBody() + +#define TEST_DEF_STATIC(Name)\ + static void Name##FunctorBody();\ + static void test##Name() { \ + tp::gTesting.startTest(#Name);\ + Name##FunctorBody();\ + tp::gTesting.endTest();\ + } \ + void Name##FunctorBody() + +#define TEST(expr) if (!(expr)) tp::gTesting.addFailedCheck({ #expr, __FILE__, __LINE__ }) +#define TEST_EQUAL(l, r) if (!((l) == (r))) tp::gTesting.addFailedCheck({ #l" == "#r, __FILE__, __LINE__ }) diff --git a/Utils/public/Timing.hpp b/Utils/public/Timing.hpp new file mode 100644 index 0000000..d58c4d6 --- /dev/null +++ b/Utils/public/Timing.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "Environment.hpp" + +namespace tp { + + typedef alni time_ms; + typedef alni time_ns; + + extern time_ms gCurrentTime; + + class Timer { + + time_ms mStart; + time_ms mDuration; + + public: + + Timer(); + explicit Timer(time_ms time); + + time_ms start(); + time_ms duration(); + void setDuration(time_ms dur); + + bool isTimeout(); + void reset(); + time_ms timePassed(); + time_ms remainder(); + void wait(); + + float easeIn(time_ms duration = 0); + float easeOut(time_ms duration = 0); + }; + + void sleep(time_ms duration); + time_ms get_time(); + + struct FpsCounter { + halni frames = 0; + Timer time; + halni fps = 0; + + FpsCounter() : time(1000) {} + + void update(bool log = true) { + frames++; + if (time.isTimeout()) { + fps = frames; + if (log) { + // printf("fps %i \n", fps); + } + frames = 0; + time.reset(); + } + } + }; +} \ No newline at end of file diff --git a/Utils/public/Utils.hpp b/Utils/public/Utils.hpp new file mode 100644 index 0000000..4c235a3 --- /dev/null +++ b/Utils/public/Utils.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "BaseModule.hpp" + +#define PTR_OFFSET(first, offset) (*((&first) + offset)) +#define MEMBER_OFFSET(s, m) (alni(&(((s*)0)->m))) + +namespace tp { + + extern ModuleManifest gModuleUtils; + + void memsetv(void* p, uhalni bytesize, uint1 val); + void memcp(void* left, const void* right, uhalni len); + int1 memequal(const void* left, const void* right, uhalni len); +} + +namespace tp { + [[nodiscard]] alnf randf(); +} + +namespace tp { + + template + class Pair { + public: + Pair() {} + Pair(T1 t1, T2 t2) : head(t1), tail(t2) {} + union { T1 t1; T1 head; T1 x; }; + union { T2 t2; T2 tail; T2 y; }; + }; + + template + class Bits { + Type mFlags = 0; + public: + Bits() = default; + explicit Bits(Type val) { mFlags = val; } + explicit Bits(bool val) { for (int bit = 0; bit < sizeof(Type); bit++) { set(bit, val); } } + bool get(int1 idx) { return mFlags & (1l << idx); } + void set(int1 idx, bool val) { + if (val) { + mFlags |= (1l << idx); + } else { + mFlags &= ~(1l << idx); + } + } + }; + + + template + class Range { + public: + class Iterator { + public: + tType mIndex; + explicit Iterator(tType pStartIndex) : mIndex(pStartIndex) {} + tType index() const { return mIndex; } + inline void operator++() { mIndex++; } + inline operator tType() const { return mIndex; } + inline bool operator==(Iterator pIndex) { return mIndex == pIndex.mIndex; } + inline bool operator!=(Iterator pIndex) { return mIndex != pIndex.mIndex; } + inline const Iterator& operator*() { return *this; } + }; + + tType mBegin = 0; + tType mEnd = 0; + + Range() = default; + explicit Range(tType pEndIndex) : mBegin(0), mEnd(pEndIndex) {} + Range(tType pStartIndex, tType pEndIndex) : mBegin(pStartIndex), mEnd(pEndIndex) {} + + bool valid() { return mBegin < mEnd; } + + tType idxBegin() const { return mBegin; } + tType idxEnd() const { return mEnd; } + Iterator begin() { return Iterator(mBegin); } + Iterator end() { return Iterator(mEnd); } + }; +} \ No newline at end of file diff --git a/Utils/tests/Tests.cpp b/Utils/tests/Tests.cpp new file mode 100644 index 0000000..d8b022a --- /dev/null +++ b/Utils/tests/Tests.cpp @@ -0,0 +1,67 @@ + +#include "Utils.hpp" +#include "Debugging.hpp" +#include "Testing.hpp" + +#include + +using namespace tp; + +void printSnapshot(const tp::CallStackCapture::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 common() { + gCSCapture->getSnapshot(); +} + +void first() { + common(); + common(); + common(); +} + +void second() { + common(); + common(); + common(); + common(); +} + +void third() { + common(); + common(); +} + +void root() { + first(); + second(); + third(); +} + +TEST_DEF(Debugging) { + root(); + + for (auto cs : *gCSCapture) { + printSnapshot(cs.getCallStack()); + } +} + +int main() { + + tp::ModuleManifest* deps[] = { &tp::gModuleUtils, nullptr }; + tp::ModuleManifest testModule("UtilsTest", nullptr, nullptr, deps); + + if (!testModule.initialize()) { + return 1; + } + + testDebugging(); + + testModule.deinitialize(); +} \ No newline at end of file diff --git a/private/StackTrace.cpp b/private/StackTrace.cpp deleted file mode 100644 index a3f7170..0000000 --- a/private/StackTrace.cpp +++ /dev/null @@ -1,186 +0,0 @@ - -#include "StackTrace.hpp" -#include "PrivateConfig.hpp" - -#include -#include -#include -#include - -#pragma comment(lib, "dbghelp.lib") - -using namespace tp; - -tp::CallStackSnapshots tp::gCallStackSnapshots; - -// ----------------------- Dict ----------------------- // - -CallStackSnapshots::SnapshotsDict::SnapshotsDict() { - mTable = (StackShapshot*)malloc(sizeof(StackShapshot) * mSize); - memsetv(mTable, sizeof(StackShapshot) * mSize, 0); -} - -CallStackSnapshots::StackShapshot CallStackSnapshots::SnapshotsDict::getSlot(ualni aIdx) { - return mTable[aIdx]; -} - -alni CallStackSnapshots::SnapshotsDict::presents(StackShapshot aPtr) { - return findSlotRead(aPtr); -} - -void CallStackSnapshots::SnapshotsDict::put(StackShapshot aPtr) { - auto idx = findSlotWrite(aPtr); - mTable[idx] = aPtr; - mEntries++; - - if ((halnf)mEntries / mSize > 2.f / 3.f) { - resize(); - } -} - -void CallStackSnapshots::SnapshotsDict::resize() { - alni nslots_old = mSize; - auto table_old = mTable; - - mSize *= 2; - mTable = (StackShapshot*)malloc(sizeof(StackShapshot) * mSize); - memsetv(mTable, sizeof(StackShapshot) * mSize, 0); - mEntries = 0; - - for (alni i = 0; i < nslots_old; i++) { - if (!table_old[i]) { - continue; - } - - alni idx = findSlotWrite(table_old[i]); - mTable[idx] = table_old[i]; - mEntries++; - } - - delete[] table_old; -} - -alni CallStackSnapshots::SnapshotsDict::findSlotRead(StackShapshot key) { - ualni const hased_key = hash(key); - ualni const mask = mSize - 1; - ualni const shift = (hased_key >> 5) & ~1; - alni idx = hased_key & mask; -NEXT: - if (!mTable[idx]) { - return -1; - } - if (compare(mTable[idx], key)) { - return idx; - } -SKIP: - idx = ((5 * idx) + 1 + shift) & mask; - goto NEXT; -} - -ualni CallStackSnapshots::SnapshotsDict::findSlotWrite(StackShapshot key) { - ualni const hased_key = hash(key); - ualni const mask = mSize - 1; - ualni const shift = (hased_key >> 5) & ~1; - ualni idx = hased_key & mask; -NEXT: - if (!mTable[idx]) { - return idx; - } - idx = ((5 * idx) + 1 + shift) & mask; - goto NEXT; -} - -CallStackSnapshots::StackShapshot CallStackSnapshots::SnapshotsDict::newStackSnapshot(ualni aDepth) { - auto const size = sizeof(FramePointer) * (aDepth + 1); - auto out = (StackShapshot*)malloc(size); - memsetv(out, size, 0); -} - -ualni CallStackSnapshots::SnapshotsDict::hash(StackShapshot snapshot) { - ualni out = 0; - for (FramePointer* iter = snapshot; iter; iter++) { out += *iter; } - return out; -} - -bool CallStackSnapshots::SnapshotsDict::compare(StackShapshot left, StackShapshot right) { - FramePointer* iter_left = left; - FramePointer* iter_right = right; - do { - if (*iter_left != *iter_right) { - return false; - } - iter_left++; - iter_right++; - } while (iter_left && iter_right); - if (*iter_left != *iter_right) { - return false; - } - return true; -} - -CallStackSnapshots::SnapshotsDict::~SnapshotsDict() { - for (ualni idx = 0; idx < mSize; idx++) { - if (mTable[idx]) { - free(mTable[idx]); - } - } - free(mTable); -} - -// ----------------------- CallStackSnapshots ----------------------- // - -CallStackSnapshots::StackShapshot CallStackSnapshots::getStack(ualni& len) { - enum { MAX_DEPTH = MEM_STACK_TRACE_MAX_DEPTH }; - static FramePointer pointers[MAX_DEPTH]; - len = 0; - - CONTEXT context; - RtlCaptureContext(&context); - - STACKFRAME64 stackFrame; - ZeroMemory(&stackFrame, sizeof(STACKFRAME64)); - stackFrame.AddrPC.Mode = AddrModeFlat; - stackFrame.AddrFrame.Mode = AddrModeFlat; - stackFrame.AddrStack.Mode = AddrModeFlat; - stackFrame.AddrPC.Offset = context.Rip; - stackFrame.AddrFrame.Offset = context.Rbp; - stackFrame.AddrStack.Offset = context.Rsp; - - HANDLE processHandle = GetCurrentProcess(); - HANDLE threadHandle = GetCurrentThread(); - - while ( - len < MAX_DEPTH && - StackWalk64(IMAGE_FILE_MACHINE_AMD64, processHandle, threadHandle, &stackFrame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL) - ) - { - pointers[len] = stackFrame.AddrFrame.Offset; - len++; - } - - return pointers; -} - -CallStackSnapshots::StackShapshot CallStackSnapshots::capture() { - ualni len; - auto stack = getStack(len); - auto idx = mSnapshots.presents(stack); - - if (idx == -1) { - auto new_snapshot = mSnapshots.newStackSnapshot(len + 1); - memcp(new_snapshot, stack, len * sizeof(FramePointer)); - new_snapshot[len] = 0; - mSnapshots.put(new_snapshot); - return new_snapshot; - } - - return mSnapshots.getSlot(idx); -} - -void CallStackSnapshots::saveToFile(StackShapshot* snapshots, const char* filepath) { - -} - -CallStackSnapshots::StackShapshot* CallStackSnapshots::loadFromFile(const char* filepath) { - -} \ No newline at end of file diff --git a/private/StackTrace.hpp b/private/StackTrace.hpp deleted file mode 100644 index 62c03a9..0000000 --- a/private/StackTrace.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "common.h" - -namespace tp { - - struct CallStackSnapshots { - - typedef alni FramePointer; - typedef FramePointer* StackShapshot; // NULL Terminated Frames - - StackShapshot capture(); - - void saveToFile(StackShapshot* snapshots, const char* filepath); - StackShapshot* loadFromFile(const char* filepath); - - private: - - struct SnapshotsDict { - SnapshotsDict(); - StackShapshot newStackSnapshot(ualni aDepth); - void put(StackShapshot aPtr); - StackShapshot getSlot(ualni aIdx); - alni presents(StackShapshot aPtr); - ~SnapshotsDict(); - - private: - StackShapshot* mTable = NULL; - uhalni mSize = 512; - uhalni mEntries = 0; - - ualni hash(StackShapshot snapshot); - bool compare(StackShapshot left, StackShapshot right); - alni findSlotRead(StackShapshot key); - ualni findSlotWrite(StackShapshot key); - void resize(); - } mSnapshots; - - StackShapshot getStack(ualni& len); - }; - - extern CallStackSnapshots gCallStackSnapshots; -}; diff --git a/public/Allocators.hpp b/public/Allocators.hpp deleted file mode 100644 index 506ae01..0000000 --- a/public/Allocators.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "HeapAllocator.hpp" -#include "ChunkAllocator.hpp" -#include "PoolAllocator.hpp" - -namespace tp { - extern ModuleManifest gModuleAllocator; -}; - -inline void* operator new(size_t aSize, void* aWhere) noexcept { return aWhere; } - -void* operator new(size_t aSize); -void* operator new[](size_t _Size); -void operator delete(void* aPtr); -void operator delete[](void* aPtr); - -void* operator new(size_t aSize, tp::HeapAlloc& aAlloc); -void* operator new[](size_t _Size, tp::HeapAlloc& aAlloc); -void operator delete(void* aPtr, tp::HeapAlloc& aAlloc); -void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc); - -void* operator new(size_t aSize, tp::HeapAllocGlobal& aAlloc); -void* operator new[](size_t _Size, tp::HeapAllocGlobal& aAlloc); -void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc); -void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc); \ No newline at end of file diff --git a/tests/Tests.cpp b/tests/Tests.cpp deleted file mode 100644 index 18339e3..0000000 --- a/tests/Tests.cpp +++ /dev/null @@ -1,18 +0,0 @@ - -#include "allocators.hpp" - -int main() { - - tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleAllocator, NULL }; - tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies); - if (!TestModule.initialize()) { - return 1; - } - - tp::HeapAllocGlobal alloc; - - int* val = new(alloc) int(); - delete(alloc, val); - - TestModule.deinitialize(); -} \ No newline at end of file diff --git a/tests/alloctests.cpp b/tests/alloctests.cpp deleted file mode 100644 index 44e935b..0000000 --- a/tests/alloctests.cpp +++ /dev/null @@ -1,291 +0,0 @@ - -#include -#include - -#include "allocators.h" - -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 -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 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(); -} \ No newline at end of file diff --git a/tests/alloctests.h b/tests/alloctests.h deleted file mode 100644 index 9a1ba89..0000000 --- a/tests/alloctests.h +++ /dev/null @@ -1,3 +0,0 @@ - -#pragma once -void allocators_test(); \ No newline at end of file diff --git a/tools/MemBenchmark/benchmark_simple.cpp b/tools/MemBenchmark/benchmark_simple.cpp deleted file mode 100644 index ed433d1..0000000 --- a/tools/MemBenchmark/benchmark_simple.cpp +++ /dev/null @@ -1,172 +0,0 @@ - - -#include "allocators.h" - -#include -#include "timer.h" - -struct AllocSpeedTets { - const char* test_desc = NULL; - virtual void exec() {}; -}; - -struct Test1 : AllocSpeedTets { - Test1() { - test_desc = "allocate lots of memory of same sizes, then free it all"; - } - - const int len = 1000; - int size = 100; - void* buff[1000]; - - void exec_util(tp::AbstractAllocator* alloc) { - for (size_t i = 0; i < len; i++) { - buff[i] = alloc->Alloc(size); - } - for (size_t i = 0; i < len; i++) { - alloc->Free(buff[i]); - } - } - - void exec() { - tp::time_ms start, end; - - tp::HeapAlloc malloc; - tp::PoolAlloc pool(0, 0); - tp::ChunkAlloc chunck(0, 0); - tp::PickAlloc pick; - - start = tp::get_time(); - - end = tp::get_time(); - printf("Malloc : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pool : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Chunk : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pick : %lli ms \n", end - start); - } -}; - -struct Test2 : AllocSpeedTets { - Test2() { - test_desc = "allocate lots of memory of different sizes, then free it all"; - } - - void exec() { - tp::time_ms start, end; - - tp::HeapAlloc malloc; - tp::PoolAlloc pool(0, 0); - tp::ChunkAlloc chunck(0, 0); - tp::PickAlloc pick; - - start = tp::get_time(); - end = tp::get_time(); - printf("Malloc : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pool : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Chunk : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pick : %lli ms \n", end - start); - } -}; - -struct Test3 : AllocSpeedTets { - Test3() { - test_desc = "allocate only a few blocks of memory, free them, and repeat this loop several times \ - \n (repeat for same - sized blocks and different - sized blocks)\n"; - } - - void exec() { - tp::time_ms start, end; - - tp::HeapAlloc malloc; - tp::PoolAlloc pool(0, 0); - tp::ChunkAlloc chunck(0, 0); - tp::PickAlloc pick; - - start = tp::get_time(); - end = tp::get_time(); - printf("Malloc : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pool : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Chunk : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pick : %lli ms \n", end - start); - } -}; - -struct Test4 : AllocSpeedTets { - Test4() { - test_desc = "allocate lots of memory of different sizes, free half of it(e.g.the even allocations), then allocateand free memory in a loop\n"; - } - - void exec() { - tp::time_ms start, end; - - tp::HeapAlloc malloc; - tp::PoolAlloc pool(0, 0); - tp::ChunkAlloc chunck(0, 0); - tp::PickAlloc pick; - - start = tp::get_time(); - end = tp::get_time(); - printf("Malloc : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pool : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Chunk : %lli ms \n", end - start); - - start = tp::get_time(); - end = tp::get_time(); - printf("Pick : %lli ms \n", end - start); - } -}; - -void banckmark() { - - tp::alloc_init(); - - const int tests_len = 1; - AllocSpeedTets* tests[] = { - new Test1(), - new Test2(), - new Test3(), - new Test4(), - }; - - for (int i = 0; i < tests_len; i++) { - printf("Test %i: \n %s\n", i, tests[i]->test_desc); - tests[i]->exec(); - - delete tests[i]; - } - - tp::alloc_uninit(); -} \ No newline at end of file diff --git a/tools/MemBenchmark/benchmarker.cpp b/tools/MemBenchmark/benchmarker.cpp deleted file mode 100644 index f4deadc..0000000 --- a/tools/MemBenchmark/benchmarker.cpp +++ /dev/null @@ -1,563 +0,0 @@ - -#include "benchmarker.h" - -#include "implot.h" -#include "ImGuiUtils.h" - -tp::alni hash(const tp::string& val) { - return tp::hash(val.cstr()); -} - -config glb_cfg = { - - .live_update = false, - - .heap = true, - .pool = true, - .chunk = true, - - .current_sizing_pattern = 0, - .current_loading_pattern = 0, - .current_ordering_pattern = 0, - - .update = 0, - - .time_per_instruction = true, - .mem_per_instruction = true, - .avreging = 1, - - .chunk_bsize = 100, - .chunk_blen = 100, - - .pool_bsize = 100, - .pool_blen = 100, -}; - -benchmarker::benchmarker() /*: ImGui::CompleteApp()*/ { - - i_count = 0; - pattern_out = NULL; - out.reserve(3); - - patterns.put("constant", new const_pattern()); - patterns.put("linear", new linear_pattern()); - patterns.put("random", new random_pattern()); -} - -void benchmarker::output_draw() { - if (ImGui::Begin("Overview")) { - - if (is_output) { - if (ImGui::TreeNode("total time")) { - for (auto& i : tp::Range(0, 3)) { - if (out[i]) { - if (out[i]->total_time < 1000) { - ImGui::Text("%s : %f 10e-9", out[i]->alloc_type, out[i]->total_time); - } else if (out[i]->total_time < 1000000) { - ImGui::Text("%s : %f 10e-6", out[i]->alloc_type, out[i]->total_time / 1000.f); - } else if (out[i]->total_time < 1000000000) { - ImGui::Text("%s : %f 10e-3", out[i]->alloc_type, out[i]->total_time / 1000000.f); - } else { - ImGui::Text("%s : %f ", out[i]->alloc_type, out[i]->total_time / 1000000000.f); - } - } - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("failures")) { - if ((out[0] && out[0]->failed) || (out[1] && out[1]->failed) || (out[2] && out[2]->failed)) { - for (auto& i : tp::Range(0, 3)) { - if (out[i] && out[i]->failed) - ImGui::Text("%s failed", out[i]->alloc_type); - } - } else { - ImGui::Text("all succeeded"); - } - ImGui::TreePop(); - } - } - } - ImGui::End(); - - - if (cfg.time_per_instruction) { - if (ImGui::Begin("Graphs")) { - if (is_output) { - if (ImPlot::BeginPlot("Time(ns) vs Alloc / Free inst")) { - if (cfg.heap) ImPlot::PlotLine("halloc", x_axis, out[0]->time.buff(), (int) i_count); - if (cfg.pool) ImPlot::PlotLine("pool", x_axis, out[1]->time.buff(), (int) i_count); - if (cfg.chunk) ImPlot::PlotLine("chunk", x_axis, out[2]->time.buff(), (int) i_count); - ImPlot::EndPlot(); - } - } - } - ImGui::End(); - } - - if (cfg.mem_per_instruction) { - if (ImGui::Begin("Graphs")) { - if (is_output) { - if (ImPlot::BeginPlot("Memory (bytes) at Instruction state")) { - if (cfg.heap) ImPlot::PlotLine("halloc", x_axis, out[0]->mem.buff(), (int) i_count); - if (cfg.pool) ImPlot::PlotLine("pool", x_axis, out[1]->mem.buff(), (int) i_count); - if (cfg.chunk) ImPlot::PlotLine("chunk", x_axis, out[2]->mem.buff(), (int) i_count); - ImPlot::EndPlot(); - } - } - } - ImGui::End(); - } - - if (ImGui::Begin("Pattern")) { - if (is_output) { - if (ImPlot::BeginPlot("Alloc/Free instruction info vs instruction idx")) { - if (cfg.current_sizing_pattern) ImPlot::PlotLine("allocated item size", x_axis, pattern_out->alloc_size.buff(), (int) i_count); - if (cfg.current_ordering_pattern) ImPlot::PlotLine("used data item idx", x_axis, pattern_out->data_idx.buff(), (int) i_count); - if (cfg.current_loading_pattern) ImPlot::PlotLine("total items allocated", x_axis, pattern_out->items_loaded.buff(), (int) i_count); - ImPlot::EndPlot(); - } - } - } - ImGui::End(); -} - -benchmarker::~benchmarker() { - for (auto iter : patterns) { - delete iter->val; - } - clear_out(); -} - -void benchmarker::select_pattern() { - if (patterns.size()) { - const char** pattern_names = new const char* [patterns.size()]; - - const char* current_opattern_name = NULL; - const char* current_lpattern_name = NULL; - const char* current_spattern_name = NULL; - - for (auto pattern_name : patterns) { - pattern_names[pattern_name.entry_idx] = pattern_name->key.cstr(); - if (pattern_name->val == glb_cfg.current_ordering_pattern) - current_opattern_name = pattern_name->key.cstr(); - if (pattern_name->val == glb_cfg.current_sizing_pattern) - current_spattern_name = pattern_name->key.cstr(); - if (pattern_name->val == glb_cfg.current_loading_pattern) - current_lpattern_name = pattern_name->key.cstr(); - } - - if (ImGui::BeginCombo("Ordering", current_opattern_name)) { - for (int n = 0; n < patterns.size(); n++) { - bool is_selected = (current_opattern_name == pattern_names[n]); - if (ImGui::Selectable(pattern_names[n], is_selected)) { - current_opattern_name = pattern_names[n]; - glb_cfg.current_ordering_pattern = patterns.get(pattern_names[n]); - } - } - ImGui::EndCombo(); - } - - if (ImGui::BeginCombo("Loading", current_lpattern_name)) { - for (int n = 0; n < patterns.size(); n++) { - bool is_selected = (current_lpattern_name == pattern_names[n]); - if (ImGui::Selectable(pattern_names[n], is_selected)) { - current_lpattern_name = pattern_names[n]; - glb_cfg.current_loading_pattern = patterns.get(pattern_names[n]); - } - } - ImGui::EndCombo(); - } - - if (ImGui::BeginCombo("Sizing", current_spattern_name)) { - for (int n = 0; n < patterns.size(); n++) { - bool is_selected = (current_spattern_name == pattern_names[n]); - if (ImGui::Selectable(pattern_names[n], is_selected)) { - current_spattern_name = pattern_names[n]; - glb_cfg.current_sizing_pattern = patterns.get(pattern_names[n]); - } - } - ImGui::EndCombo(); - } - delete pattern_names; - } -} - -void benchmarker::pattern_combo(const char*& current) { - if (patterns.size()) { - const char** pattern_names = new const char* [patterns.size()]; - - for (auto pattern_name : patterns) { - pattern_names[pattern_name.entry_idx] = pattern_name->key.cstr(); - } - - if (ImGui::BeginCombo("Patterns", current)) { - for (int n = 0; n < patterns.size(); n++) { - bool is_selected = (current == pattern_names[n]); - if (ImGui::Selectable(pattern_names[n], is_selected)) { - current = pattern_names[n]; - } - } - ImGui::EndCombo(); - } - delete pattern_names; - } -} - -void benchmarker::pattern_generator() { - - - static tp::alni selected_idx = -1; - static pattern* child_pattern_active = NULL; - - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.1f); - - pattern* pattern_edit = NULL; - if (ImGui::Begin("Pattern Generator")) { - - if (ImGui::SubMenuBegin("Selector", 1)) { - - const char* prev_pattern = pattern_generator_active; - pattern_combo(pattern_generator_active); - - child_pattern_active = (prev_pattern == pattern_generator_active) ? child_pattern_active : NULL; - - if (pattern_generator_active) { - tp::alni idx = patterns.presents(pattern_generator_active); - if (!MAP_VALID_IDX(idx)) { - pattern_edit = 0; - pattern_generator_active = 0; - } else { - pattern_edit = patterns[idx]; - } - } - - //ImGui::Separator(); - - static char create_name[100] = {"new pattern name"}; - ImGui::InputText("pattern name", create_name, 100); - - if (ImGui::Button("Create")) { - if (!patterns.presents(create_name)) { - pattern* new_pt = new pattern(); - new_pt->build_in = false; - new_pt->pattern_name = create_name; - tp::string id = create_name; - id.capture(); - patterns.put(id, new_pt); - } else { - ImGui::Notify("Such Pattern Already Exists", 3); - } - } - - if (pattern_edit) { - ImGui::SameLine(); - if (ImGui::Button("Delete")) { - if (pattern_edit->build_in) { - ImGui::Notify("Cant Remove Built-in Patterns", 3); - } else { - patterns.remove(pattern_generator_active); - delete pattern_edit; - pattern_edit = NULL; - pattern_generator_active = NULL; - } - } - - ImGui::SameLine(); - if (ImGui::Button("Rename")) { - if (pattern_edit->build_in) { - ImGui::Notify("Cant Rename Built-in Patterns", 3); - } else { - patterns.remove(pattern_generator_active); - patterns.put(create_name, pattern_edit); - } - } - } - ImGui::SubMenuEnd(1); - } - - if (pattern_generator_active) { - tp::alni idx = patterns.presents(pattern_generator_active); - if (!MAP_VALID_IDX(idx)) { - pattern_edit = 0; - pattern_generator_active = 0; - } else { - pattern_edit = patterns[idx]; - } - } - - if (!pattern_edit) { - ImGui::Text("Select pattern to edit or create one"); - ImGui::End(); - return; - } - - if (ImGui::SubMenuBegin("Preview", 1)) { - static float preview_res_scale = 0.05f; - int resolution = (int) (1000 * preview_res_scale); - CLAMP(resolution, 3, 10000); - float* x_axis = new float[resolution]; - float* y_axis = new float[resolution]; - - float x = 0; - float step = 1.f / (resolution - 1); - for (tp::alni idx = 0; idx < resolution; idx++) { - x_axis[idx] = x; - y_axis[idx] = (tp::flt4) pattern_edit->get_y(&patterns, x); - x += step; - } - - - if (ImPlot::BeginPlot("Pattern")) { - ImPlot::PlotLine("toggle", x_axis, y_axis, resolution); - ImPlot::EndPlot(); - } - - delete x_axis; - delete y_axis; - ImGui::SliderFloat("graph resolution", &preview_res_scale, 0.f, 1.f); - ImGui::SubMenuEnd(1); - } - - if (ImGui::SubMenuBegin("Compositor", 1)) { - - if (!pattern_edit->build_in) { - - if (ImGui::SubMenuBegin("Child Patterns", 2)) { - ImGui::BeginListBox(""); - for (tp::alni idx = 0; idx < pattern_edit->regions.length(); idx++) { - ImGui::PushID((int) idx); - if (ImGui::Button(pattern_edit->regions[idx].name.cstr())) { - child_pattern_active = patterns.get(pattern_edit->regions[idx].name); - selected_idx = idx; - } - if (selected_idx == idx) { - ImGui::SameLine(); ImGui::Text(" - Active"); - } - ImGui::PopID(); - } - ImGui::EndListBox(); - - if (selected_idx >= pattern_edit->regions.length()) { - selected_idx = -1; - } - - static const char* append_pattern = NULL; - bool add = ImGui::Button(" + "); - ImGui::SameLine(); - pattern_combo(append_pattern); - if (add) { - if (append_pattern) { - pattern_edit->regions.pushBack(child_pattern(append_pattern)); - } else { - ImGui::Notify("Select a Pattern to Append"); - } - } - - if (child_pattern_active && selected_idx != -1) { - - if (ImGui::Button(" Up ")) { - if (selected_idx > 0) { - tp::string tmp = pattern_edit->regions[selected_idx].name; - pattern_edit->regions[selected_idx] = pattern_edit->regions[selected_idx - 1]; - pattern_edit->regions[selected_idx - 1] = tmp; - selected_idx--; - } - } - ImGui::SameLine(); - if (ImGui::Button("Down")) { - if (selected_idx < pattern_edit->regions.length() - 1) { - tp::string tmp = pattern_edit->regions[selected_idx].name; - pattern_edit->regions[selected_idx] = pattern_edit->regions[selected_idx + 1]; - pattern_edit->regions[selected_idx + 1] = tmp; - selected_idx++; - } - } - ImGui::SameLine(); - if (ImGui::Button("Remove")) { - pattern_edit->regions.remove(selected_idx); - selected_idx = pattern_edit->regions.length() - 1; - } - } else { - ImGui::Text("Select Child Pattern"); - } - ImGui::SubMenuEnd(2); - } - - if (child_pattern_active && selected_idx != -1) { - if (ImGui::SubMenuBegin("child Pattern Properties", 2)) { - ImGui::SliderFloat("Point", &pattern_edit->regions[selected_idx].point, 0.f, 1.f); - ImGui::SliderFloat("Lower lim", &pattern_edit->regions[selected_idx].lowerlim, 0.f, 1.f); - ImGui::SliderFloat("Upper lim", &pattern_edit->regions[selected_idx].uppernlim, 0.f, 1.f); - if (child_pattern_active->build_in) { - } - ImGui::SubMenuEnd(2); - } - } - } else { - ImGui::Text("Can't Edit Built-In Patterns"); - } - ImGui::SubMenuEnd(1); - } - - ImGui::Separator(); - - } - ImGui::End(); -} - -void benchmarker::MainDrawTick() { - - analize(glb_cfg); - - - ImGui::BeginGroup(); - - if (ImGui::WindowEditor("Properties")) { - - ImGui::PushItemWidth(ImGui::GetContentRegionAvail().x * 0.5f); - - { - if (ImGui::Button("Run")) { - glb_cfg.update = true; - } - - bool prev_val = glb_cfg.live_update; - ImGui::SameLine(); ImGui::Checkbox("Live Update", &glb_cfg.live_update); - if (prev_val != glb_cfg.live_update) { - glb_cfg.update = true; - } - } - - if (ImGui::SubMenuBegin("General", 1)) { - ImGui::InputInt("Averaging", &glb_cfg.avreging, 1, 100); ImGui::ToolTip("Number of tests to be averaged"); - ImGui::Checkbox("Collect time per instruction", &glb_cfg.time_per_instruction); - ImGui::Checkbox("Collect mem per instruction", &glb_cfg.mem_per_instruction); - - ImGui::SubMenuEnd(1); - } - - if (ImGui::SubMenuBegin("Testing Pattern", 1)) { - - select_pattern(); - - ImGui::InputInt("size", &glb_cfg.pt_scale.size, 1, 100); ImGui::ToolTip("Y Scale of sizing pattern"); - ImGui::InputInt("items", &glb_cfg.pt_scale.items, 1, 100); ImGui::ToolTip("Y Scale of ordering and loading patterns"); - ImGui::InputInt("iterations", &glb_cfg.pt_scale.iterations, 1, 100); ImGui::ToolTip("X Scale of all patterns"); - - ImGui::SubMenuEnd(1); - } - - if (ImGui::SubMenuBegin("Allocators", 1)) { - ImGui::Checkbox("heap", &glb_cfg.heap); ImGui::SameLine(); ImGui::Checkbox("pool", &glb_cfg.pool); ImGui::SameLine(); ImGui::Checkbox("chunk", &glb_cfg.chunk); - - if (glb_cfg.chunk && ImGui::SubMenuBegin("Chunk", 2)) { - ImGui::InputInt("size", &glb_cfg.chunk_bsize, 1, 100); ImGui::ToolTip("Size of a slot in the chunk buffer"); - ImGui::InputInt("length", &glb_cfg.chunk_blen, 1, 100); ImGui::ToolTip("Number of slots in the chunk buffer"); - ImGui::SubMenuEnd(2); - } - if (glb_cfg.pool && ImGui::SubMenuBegin("Pool", 2)) { - ImGui::ToolTip("Generalized use of chunk allocator"); - ImGui::InputInt("size", &glb_cfg.pool_bsize, 1, 100); - ImGui::InputInt("length", &glb_cfg.pool_blen, 1, 100); - ImGui::SubMenuEnd(2); - } - ImGui::SubMenuEnd(1); - } - - //ImGui::PopItemWidth(); - } - ImGui::End(); - - output_draw(); - - pattern_generator(); - - ImGui::EndGroup(); -} - - -void benchmarker::analize(config pcfg) { - - if (!((pcfg.update) || (!(this->cfg == pcfg) && cfg.live_update))) { - return; - } - - this->cfg = pcfg; - - clear_out(); - - if (!pattern_analizer.init(&patterns, cfg.current_loading_pattern, cfg.current_ordering_pattern, cfg.current_sizing_pattern, &cfg.pt_scale)) { - if (pcfg.update) ImGui::Notify("invalid pattern configuration", 3); - glb_cfg.update = false; - cfg.update = false; - is_output = false; - return; - } - - reserve_out(&pattern_analizer); - - for (tp::alni iter = 0; iter < pcfg.avreging; iter++) { - init_allocators(pcfg); - if (cfg.heap) collect(&pattern_analizer, halloc, out[0]); - if (cfg.pool) collect(&pattern_analizer, palloc, out[1]); - if (cfg.chunk) collect(&pattern_analizer, calloc, out[2]); - dest_allocators(); - } - - for (auto& i : tp::Range(0, 3)) { - if (out[i]) out[i]->scale_all(1.f / pcfg.avreging); - } - - i_count = pattern_analizer.max_iterations(); - if (x_axis) { - delete x_axis; - x_axis = NULL; - } - x_axis = new tp::alnf[i_count]; - for (tp::alni iter = 0; iter < i_count; iter++) { - x_axis[iter] = (tp::alnf) iter; - } - - glb_cfg.update = false; - cfg.update = false; - is_output = true; -} - -void benchmarker::init_allocators(config& pcfg) { - if (cfg.heap) halloc = new tp::HeapAlloc(); - if (cfg.pool) palloc = new tp::PoolAlloc(pcfg.pool_bsize, pcfg.pool_blen); - if (cfg.chunk) calloc = new tp::ChunkAlloc(pcfg.chunk_bsize, pcfg.chunk_blen); -} - -void benchmarker::dest_allocators() { - try { - if (cfg.heap) delete halloc; - if (cfg.pool) delete palloc; - if (cfg.chunk) delete calloc; - } catch (...) { - } - - halloc = NULL; - palloc = NULL; - calloc = NULL; -} - -void benchmarker::clear_out() { - for (auto i : tp::Range(0, 3)) { - if (out[i]) { - delete out[i]; - out[i] = NULL; - } - } - - if (pattern_out) delete pattern_out; - pattern_out = NULL; -} - -void benchmarker::reserve_out(test_pattern* pattern) { - pattern_out = new pattern_histogram(pattern); - if (cfg.heap) out[0] = new allocator_histogram(pattern, "heap", cfg.time_per_instruction, cfg.mem_per_instruction); - if (cfg.pool) out[1] = new allocator_histogram(pattern, "pool", cfg.time_per_instruction, cfg.mem_per_instruction); - if (cfg.chunk) out[2] = new allocator_histogram(pattern, "chunk", cfg.time_per_instruction, cfg.mem_per_instruction); -} \ No newline at end of file diff --git a/tools/MemBenchmark/benchmarker.h b/tools/MemBenchmark/benchmarker.h deleted file mode 100644 index 96c42f0..0000000 --- a/tools/MemBenchmark/benchmarker.h +++ /dev/null @@ -1,100 +0,0 @@ - -#pragma once - -#include "patterns.h" - -#include "allocators.h" -#include "array.h" - -#include "gl.h" -#include "glcommon.h" -#include "window.h" - -enum class load_type { - LINEAR, - SINE, - STEPS, - RANDOM -}; - -enum class order_type { - LINEAR, - LINEAR_REVERSED, - RANDOM, -}; - -struct config { - // general - bool live_update = false; - - bool heap = true; - bool pool = true; - bool chunk = true; - - // pattern - pattern* current_sizing_pattern = 0; - pattern* current_loading_pattern = 0; - pattern* current_ordering_pattern = 0; - pattern_scale pt_scale; - - tp::alni update = 0; - bool time_per_instruction = 0; - bool mem_per_instruction = 0; - int avreging; - - // allocators - int chunk_bsize = 0; - int chunk_blen = 0; - - int pool_bsize = 0; - int pool_blen = 0; - - bool operator==(const config& in) { - return tp::memequal(this, (config*)(&in), sizeof(config)); - } -}; - -struct benchmarker { - - config cfg; - bool is_output = false; - - // allocators - tp::HeapAlloc* halloc = NULL; - tp::PoolAlloc* palloc = NULL; - tp::ChunkAlloc* calloc = NULL; - - - tp::Array out; - pattern_histogram* pattern_out; - - tp::alni i_count; - tp::alnf* x_axis = NULL; - - tp::HashMap patterns; - - pattern_reader pattern_analizer; - - const char* pattern_generator_active = NULL; - - benchmarker(); - ~benchmarker(); - - test_pattern* get_pattern(config& cfg); - - void pattern_combo(const char*&); - void analize(config cfg); - void select_pattern(); - void output_draw(); - - void MainDrawTick(); - - void pattern_generator(); - - void init_allocators(config& cfg); - - void dest_allocators(); - - void clear_out(); - void reserve_out(test_pattern* pattern); -}; \ No newline at end of file diff --git a/tools/MemBenchmark/collector.cpp b/tools/MemBenchmark/collector.cpp deleted file mode 100644 index 0afc0e0..0000000 --- a/tools/MemBenchmark/collector.cpp +++ /dev/null @@ -1,119 +0,0 @@ - -#include "collector.h" - -#include - -allocator_histogram::allocator_histogram(test_pattern* pt, const char* alloc_type, bool p_time_per_inst, bool p_mem_per_inst) { - this->alloc_type = alloc_type; - time_per_inst = p_time_per_inst; - mem_per_inst = p_mem_per_inst; - - if (time_per_inst) { - time.reserve(pt->max_iterations()); - } - if (mem_per_inst) { - mem.reserve(pt->max_iterations()); - } - data.reserve(pt->data_count()); - - total_time = 0; - failed = false; -} - -void allocator_histogram::scale_all(tp::alnf fac) { - for (auto& iter : time) { - iter.data() = fac * iter.data(); - } - for (auto& iter : mem) { - iter.data() = fac * iter.data(); - } - total_time = fac * total_time; -} - -allocator_histogram::~allocator_histogram() { -} - -void allocator_histogram::mark_resourses_usage(tp::alni idx, tp::alnf p_time, tp::alni p_mem, bool add) { - if (mem.length() > idx) - add ? mem[idx] += (tp::alnf)p_mem : mem[idx] = (tp::alnf)p_mem; - if (time.length() > idx) - add ? time[idx] += p_time : time[idx] = p_time; -} - -bool execute_instruction(tp::AbstractAllocator* alloc, bool load, tp::alni size, tp::uint1*& data) { - bool failed = false; - try { - if (load) { - if (!data) { - data = (tp::uint1*)alloc->Alloc(size); - if (!data) { - failed = true; - } - } - } - else { - if (data) { - alloc->Free(data); - data = NULL; - } - } - } - catch (...) { - failed = true; - } - return !failed; -} - - - -void collect(test_pattern* pattern, tp::AbstractAllocator* alloc, allocator_histogram* histogram) { - - tp::alni iter_idx = 0; - tp::alni nimtems_loaded = 0; - - auto total_st = std::chrono::high_resolution_clock::now(); - - for (iter_idx = 0; iter_idx < pattern->max_iterations(); iter_idx++) { - - tp::alni target_nimtems_loaded = pattern->pick_alloc_count(iter_idx); - tp::alni data_idx = pattern->pick_idx(iter_idx); - tp::alni load_size = pattern->pick_size(iter_idx); - - auto iter_st = std::chrono::high_resolution_clock::now(); - while (nimtems_loaded != target_nimtems_loaded) { - bool load = nimtems_loaded < target_nimtems_loaded; - if (!execute_instruction(alloc, load, load_size, histogram->data[data_idx])) { - histogram->mark_resourses_usage(iter_idx, 0, 0, 0); - histogram->failed = true; - break; - } - nimtems_loaded += (tp::alni)load + (-1 * (tp::alni)(!load)); - } - auto iter_nd = std::chrono::high_resolution_clock::now(); - tp::alni dur = std::chrono::duration_cast(iter_nd - iter_st).count(); - histogram->mark_resourses_usage(iter_idx, tp::alnf(dur), alloc->sizeReserved(), 1); - } - - // clear all out - for (iter_idx = 0; iter_idx < pattern->max_iterations(); iter_idx++) { - if (histogram->data[iter_idx]) { - execute_instruction(alloc, false, 0, histogram->data[iter_idx]); - } - } - - auto total_nd = std::chrono::high_resolution_clock::now(); - tp::alni dur = std::chrono::duration_cast(total_nd - total_st).count(); - histogram->total_time += dur; -} - -pattern_histogram::pattern_histogram(test_pattern* pt) { - alloc_size.reserve(pt->max_iterations()); - data_idx.reserve(pt->max_iterations()); - items_loaded.reserve(pt->max_iterations()); - - for (auto& i : tp::Range(0, pt->max_iterations())) { - alloc_size[i] = (tp::alnf) pt->pick_size(i); - data_idx[i] = (tp::alnf)pt->pick_idx(i); - items_loaded[i] = (tp::alnf)pt->pick_alloc_count(i); - } -} diff --git a/tools/MemBenchmark/collector.h b/tools/MemBenchmark/collector.h deleted file mode 100644 index abc0745..0000000 --- a/tools/MemBenchmark/collector.h +++ /dev/null @@ -1,43 +0,0 @@ - -#pragma once - -#include "array.h" - -class test_pattern { -public: - virtual tp::alni pick_size(tp::alni iter) { return 0; }; - virtual tp::alni max_size() { return 0; }; - virtual tp::alni pick_alloc_count(tp::alni iter) { return 0; }; - virtual tp::alni max_iterations() { return 0; }; - virtual tp::alni pick_idx(tp::alni iter) { return 0; }; - virtual tp::alni data_count() { return 0; }; -}; - -struct pattern_histogram { - tp::Array alloc_size; - tp::Array data_idx; - tp::Array items_loaded; - - pattern_histogram(test_pattern* pt); -}; - -struct allocator_histogram { - const char* alloc_type; - - tp::alnf total_time; - tp::Array time; - tp::Array mem; - bool failed; - - tp::Array data; - bool time_per_inst; - bool mem_per_inst; - - allocator_histogram(test_pattern* pt, const char* alloc_type, bool time_per_inst, bool mem_per_inst); - ~allocator_histogram(); - - void mark_resourses_usage(tp::alni idx, tp::alnf time, tp::alni mem, bool add); - void scale_all(tp::alnf fac); -}; - -void collect(test_pattern* pattern, tp::AbstractAllocator* alloc, allocator_histogram* histogram); \ No newline at end of file diff --git a/tools/MemBenchmark/patterns.h b/tools/MemBenchmark/patterns.h deleted file mode 100644 index 7fe33ec..0000000 --- a/tools/MemBenchmark/patterns.h +++ /dev/null @@ -1,174 +0,0 @@ - -#pragma once - -#include "strings.h" - -#include - -#include "collector.h" - -tp::alni hash(const tp::string& val); - -#include "map.h" - -enum class leav_pattern_type { - LINEAR, - RANDOM, - SINE, - CONST, -}; - -struct child_pattern { - child_pattern() {} - - child_pattern(tp::string _name) { name = _name; } - - float uppernlim = 1.f; - float lowerlim = 0.f; - float point = 1.f; - tp::string name; -}; - -struct pattern { - - leav_pattern_type type = leav_pattern_type::CONST; - tp::string pattern_name; - - tp::Array regions; - bool build_in = true; - - pattern() { - } - - tp::alnf get_y(tp::HashMap* patterns, tp::alnf x) { - assert(x <= 1.0001f && x >= -0.00001f); - - if (!regions.length()) { - return pure_get_y(x); - } - - float offset = 0.f; - for (tp::alni i = 0; i < regions.length(); i++) { - tp::alni idx = patterns->presents(regions[i].name); - if (!MAP_VALID_IDX(idx)) { - return 0.f; - } - pattern* child = (*patterns)[idx]; - assert(child); - - float range = regions[i].point * (1.f - offset); - if (offset + range > x) { - return regions[i].lowerlim + - (child->get_y(patterns, (x - offset) / range) * - (regions[i].uppernlim - regions[i].lowerlim)); - } - offset += range; - } - return 0; - } - - virtual tp::alnf pure_get_y(tp::alnf x) { return 0; } - - ~pattern() {} -}; - -// -------------------- build-in patterns ---------------------------- // - -struct const_pattern : pattern { - float val = 1.f; - const_pattern() { - type = leav_pattern_type::CONST; - pattern_name = "const"; - build_in = true; - } - - tp::alnf pure_get_y(tp::alnf x) override { return val; } -}; - -struct linear_pattern : pattern { - bool reversed = false; - linear_pattern() { - type = leav_pattern_type::LINEAR; - pattern_name = "linear"; - build_in = true; - } - - tp::alnf pure_get_y(tp::alnf x) override { return reversed ? 1.f - x : x; } -}; - -struct random_pattern : pattern { - random_pattern() { - type = leav_pattern_type::RANDOM; - build_in = true; - } - - tp::alnf pure_get_y(tp::alnf x) override { return tp::randf(); } -}; - -// -------------------- build-in patterns end ---------------------------- // - -struct pattern_scale { - int items = 0; - int size = 0; - int iterations = 0; -}; - -class pattern_reader : public test_pattern { - public: - bool init(tp::HashMap* p_patterns, pattern* p_lpattern, - pattern* p_opattern, pattern* p_spattern, pattern_scale* p_scale) { - lpattern = p_lpattern; - opattern = p_opattern; - spattern = p_spattern; - - scale = p_scale; - - patterns = p_patterns; - return verify_rulles(); - } - - bool verify_rulles() { - if (!lpattern || !opattern || !spattern) { - return false; - } - - bool out = true; - out &= scale->items > 0; - out &= scale->size > 0; - out &= scale->iterations > 0; - return out; - } - - tp::HashMap* patterns; - - pattern* lpattern = NULL; - pattern* opattern = NULL; - pattern* spattern = NULL; - - pattern_scale* scale = 0; - - tp::alnf get_x_val(tp::alni iter) { - tp::alnf out = iter / (tp::alnf)scale->iterations; - return out > 1 ? 1.f : out; - } - - tp::alni pick_size(tp::alni iter) override { - return (tp::alni)(scale->size * spattern->get_y(patterns, get_x_val(iter))); - } - - tp::alni pick_alloc_count(tp::alni iter) override { - return (tp::alni)(scale->items * lpattern->get_y(patterns, get_x_val(iter))); - } - - tp::alni pick_idx(tp::alni iter) override { - tp::alni out = (tp::alni) (scale->items * opattern->get_y(patterns, get_x_val(iter))); - CLAMP(out, 0, scale->items - 1); - return out; - } - - tp::alni max_size() override { return scale->size; } - - tp::alni max_iterations() override { return scale->iterations; } - - tp::alni data_count() override { return scale->iterations; } -}; diff --git a/tools/MemLeaks/MemleaksEntry.cpp b/tools/MemLeaks/MemleaksEntry.cpp deleted file mode 100644 index 56d6d3c..0000000 --- a/tools/MemLeaks/MemleaksEntry.cpp +++ /dev/null @@ -1,93 +0,0 @@ - -#include "SimpleGui.h" -#include "debugui.h" -#include "memleaks.h" -#include "timer.h" - -struct MemLeaksGUI { - - tp::glw::Window window; - tp::glw::DebugUI ui; - tp::glw::Canvas canvas; - tp::glw::WindowSimpleInputs inputs; - tp::glw::WindowsHeaderWidget header; - tp::MemLeaksData mLeaks; - tp::MemLeaksTreeView mView; - tp::halnf mHeaderHeight = 5.5f; - tp::Timer timer; - - MemLeaksGUI(const char* path) : window(), ui(window, debuguiCallBack, this), mLeaks(path), timer(10) { - canvas.setCol1({ 0.5f, 0.5f, 0.5f, 0.5f }); - window.mAppearence.mHiden = false; - mView.setTarget(&mLeaks); - } - - void proc() { - window.pollEvents(); - inputs.update(window, 1.f); - - header.header_rec = { (inputs.mWindowSizeMM.x - 60.f) / 2.f, 3.f, 60.f, mHeaderHeight }; - mView.rect = { 0.f, 0.f, inputs.mWindowSizeMM.x, inputs.mWindowSizeMM.y }; - - mView.proc(); - - if (inputs.MoveUp()) { - mView.zoom_factor -= 0.2f; - } - else if (inputs.MoveDown()) { - mView.zoom_factor += 0.2f; - } - mView.zoom_factor = tp::clamp(mView.zoom_factor, 0.f, 1.f); - - mView.vieport_crs = inputs.mCrs; - mView.vieport_crs_delta = inputs.mCrsmDelta * -1.f; - mView.mouse_down = inputs.Activated(); - mView.mouse_hold = inputs.Anticipating(); - mView.mouse_up = inputs.Selected(); - - header.proc(window, inputs); - } - - void draw() { - window.beginDraw(); { - canvas.beginDraw({ { 0, 0 }, inputs.mWindowSizeMM }, window.mDevice.mDPMM, 1.f); { - mView.draw(&canvas); - header.draw(canvas); - } canvas.endDraw(); - ui.drawDebugUI(window.mDevice.mDPMM); - } window.endDraw(); - } - - void run() { - while (!window.mEvents.mClose) { - proc(); - if (window.mEvents.mRedraw) { - draw(); - } - - timer.wait(); - timer.reset(); - } - } - - void debugui() {} - static void debuguiCallBack(void* self) { ((MemLeaksGUI*)self)->debugui(); } - ~MemLeaksGUI() {} -}; - -int main(char argc, char* argv[]) { - tp::set_working_dir(); - - tp::ModuleManifest* ModuleDependencies[] = { &tp::gModuleGlw, NULL }; - tp::ModuleManifest TestModule("Test", NULL, NULL, ModuleDependencies); - if (!TestModule.initialize()) { - return 1; - } - - { - MemLeaksGUI gui(argc > 1 ? argv[1] : "rsc/debug.memleaks"); - gui.run(); - } - - TestModule.deinitialize(); -} \ No newline at end of file diff --git a/tools/MemLeaks/memleaks.cpp b/tools/MemLeaks/memleaks.cpp deleted file mode 100644 index 529a47d..0000000 --- a/tools/MemLeaks/memleaks.cpp +++ /dev/null @@ -1,403 +0,0 @@ - -#include "memleaks.h" - -using namespace tp; - -void MemLeaksData::Frame::dfs(Frame& frame, void (*exec)(Frame& frame, void* custom), void* custom) { - if (frame.flag == Frame::Flags::INUSE) { return; } - frame.flag = Frame::Flags::INUSE; - exec(frame, custom); - for (auto call_info : frame.callers) { - dfs(*call_info->val.caller, exec, custom); - } - frame.flag = Frame::Flags::NONE; -} - -MemLeaksData::MemLeaksData(tp::string afilename) { - filename = afilename; - load_leaks(); - - rootframe.name = "root"; -} - -void MemLeaksData::increase_caller_count(Frame& frame, FrameId caller_id) { - auto idx = frame.callers.presents(caller_id); - if (idx) { - frame.callers.getSlotVal(idx).count++; - } - else { - Frame* caller_frame = &frames.get(caller_id); - frame.callers.put(caller_id, { caller_frame, 1 }); - } -} - -void MemLeaksData::calc_max_level(Frame& frame, tp::alni& max_level) { - frame.flag = Frame::Flags::INUSE; - - for (auto call_info : frame.callers) { - auto caller = call_info->val.caller; - if (caller->flag != Frame::Flags::INUSE) { - if (caller->depth_level < frame.depth_level + 1) { - caller->depth_level = frame.depth_level + 1; - max_level = MAX(caller->depth_level, max_level); - calc_max_level(*call_info->val.caller, max_level); - } - } - } - - frame.flag = Frame::Flags::NONE; -} - -void MemLeaksData::count_level_users(Frame& frame, tp::alni& max_level, tp::Array& levels) { - if (frame.flag2 == 1) { - return; - } - frame.flag2 = 1; - levels[frame.depth_level].count_users += 1; - for (auto call_info : frame.callers) { - count_level_users(*call_info->val.caller, max_level, levels); - } -} - -void MemLeaksData::apply_position(Frame& frame, tp::Array& levels) { - if (frame.flag2 == 1) { - return; - } - frame.flag2 = 1; - - - auto x = -(levels[frame.depth_level].count_users - 1) * sapacing.x / 2 + sapacing.x * levels[frame.depth_level].used_idx; - frame.tree_view_pos.assign(x, frame.depth_level * sapacing.y); - levels[frame.depth_level].used_idx++; - - for (auto call_info : frame.callers) { - apply_position(*call_info->val.caller, levels); - } -}; - -void MemLeaksData::construct_tree() { - using namespace tp; - - // construct tree - for (auto leak : leaks) { - increase_caller_count(rootframe, leak.data()[0]); - for (auto i : Range(leak->length() - 1)) { - increase_caller_count(frames.get(leak.data()[i]), leak.data()[i + 1]); - } - } - - tp::alni max_depth_level = 0; - calc_max_level(rootframe, max_depth_level); - - levels.reserve(max_depth_level + 1); - - count_level_users(rootframe, max_depth_level, levels); - Frame::dfs(rootframe, [](Frame& frame, void*) { frame.flag2 = 0; }); - - apply_position(rootframe, levels); - Frame::dfs(rootframe, [](Frame& frame, void*) { frame.flag2 = 0; }); -} - -void MemLeaksData::make_connections() { - tp::halni len = 0; - for (auto& frame : frames) { - len += frame.iter->val.callers.size(); - } - mConnections.reserve(len); - tp::halni idx = 0; - for (auto& frame : frames) { - for (auto& caller : frame.iter->val.callers) { - caller.iter->val.caller; - mConnections[idx] = { caller.iter->val.caller, &frame.iter->val, caller.iter->val.count }; - idx++; - } - } -} - -void MemLeaksData::load_leaks() { - using namespace tp; - - File log(filename.cstr(), osfile_openflags::LOAD); - - if (!log.opened) { - status = LoadStatus::INVALID_FILE_PATH; - return; - } - - log.Preload(); - - const char logo[] = "memleaks\0"; - const char logo_len = 10; - char logo_loaded[logo_len]; - - log.read_bytes(logo_loaded, logo_len); - - if (!tp::memequal(logo_loaded, logo, logo_len)) { - status = LoadStatus::INVALID_FILE_FORMAT; - return; - } - - try { - alni leaks_len; - log.read(&leaks_len); - - leaks.reserve(leaks_len); - - Frame frame; - - for (alni idx = 0; idx < leaks_len; idx++) { - - tp::alni frames_len; - log.read(&frames_len); - - leaks[idx].reserve(frames_len); - - for (alni frame_idx = 0; frame_idx < frames_len; frame_idx++) { - - FrameId id; - - - log.read(&id); - leaks[idx][frame_idx] = id; - - //if (frames.presents(id)) { - //continue; - //} - - frame.name.load(&log); - frame.file.load(&log); - log.read(&frame.line); - - frame.id = id; - - frames.put(id, frame); - } - } - - construct_tree(); - make_connections(); - - } - catch (...) { - status = LoadStatus::INTERNAL_ERROR; - return; - } - - status = LoadStatus::DONE; -} - - -// ------------------------- Tree View Drawer -------------------------------------- // - -void MemLeaksTreeView::setTarget(MemLeaksData* aLeaks) { - leaks = aLeaks; - selected_node = NULL; -} - -void MemLeaksTreeView::proc() { - if (!leaks) { - return; - } - - // handle selection - if (mouse_down) { - selected_node = NULL; - - void (*find_selected)(Frame & frame, void* vec) = [](Frame& frame, void* self_ptr) { - auto self = (MemLeaksTreeView*)self_ptr; - - auto node_rec = self->nodeBoundsScaled(frame); - //auto vieport = self->rect; - - if (node_rec.inside(self->vieport_crs)) { - self->selected_node = &frame; - } - }; - - Frame::dfs(leaks->rootframe, find_selected, this); - } - - // handle mouse drag - if (vieport_crs_delta != 0.f && rect.inside(vieport_crs) && mouse_hold) { - if (selected_node) { - selected_node->tree_view_pos -= vieport_crs_delta / (tp::halnf)scaleval; - } - else { - tree_view_pos -= vieport_crs_delta / (tp::halnf)scaleval; - } - } - - // calc scale fac - { - // zoom_factor : - // 0 - 3 nodes visiable - // 1 - all nodes visiable - - auto max_tree_size = MAX(tree_size.x, tree_size.y); - auto max_node_size = MAX(node_size.x, node_size.y); - auto min_view_size = MIN(rect.z, rect.w) - 100; - - auto max = min_view_size / max_tree_size; - auto min = min_view_size / max_node_size; - - scaleval = (max - min) * zoom_factor + min; - } - - // calc tree size - void (*execf)(Frame & frame, void* vec) = [](Frame& frame, void* vecp) { - auto tree_min_max_pos = (tp::rectf*)vecp; - // if smaller than min - if (tree_min_max_pos->v1.x > frame.tree_view_pos.x) { - tree_min_max_pos->v1.x = frame.tree_view_pos.x; - } - if (tree_min_max_pos->v1.y > frame.tree_view_pos.y) { - tree_min_max_pos->v1.y = frame.tree_view_pos.y; - } - // if bigger than max - if (tree_min_max_pos->v2.x < frame.tree_view_pos.x) { - tree_min_max_pos->v2.x = frame.tree_view_pos.x; - } - if (tree_min_max_pos->v2.y < frame.tree_view_pos.y) { - tree_min_max_pos->v2.y = frame.tree_view_pos.y; - } - }; - - tree_size = 0; - tp::rectf tree_min_max_pos = { FLT_MAX, FLT_MIN }; - Frame::dfs(leaks->rootframe, execf, &tree_min_max_pos); - tree_size = tree_min_max_pos.v2 - tree_min_max_pos.v1; - tree_size += node_size; -} - -void MemLeaksTreeView::draw(tp::glw::Canvas* drawer) { - - drawer->setCol1(col.bg); - drawer->rect(rect, 2.f); - - drawer->setCol1(col.text); - if (!leaks) { - drawer->text("Not Loaded", rect, 5); - return; - } - - switch (leaks->status) { - case MemLeaksData::LoadStatus::INVALID_FILE_PATH: - drawer->text("INVALID_FILE_PATH", rect, 5); - return; - case MemLeaksData::LoadStatus::INVALID_FILE_FORMAT: - drawer->text("INVALID_FILE_FORMAT", rect, 5); - return; - case MemLeaksData::LoadStatus::INTERNAL_ERROR: - drawer->text("INTERNAL_ERROR", rect, 5); - return; - } - - for (auto& frame : leaks->frames) { - drawNode(frame.iter->val, drawer); - } - - for (auto& connection : leaks->mConnections) { - drawConnection(*connection.data().caller, *connection.data().target, connection.data().count, drawer); - } - - if (selected_node) { - auto flag = tp::glw::Canvas::Align(tp::glw::Canvas::LEFT | tp::glw::Canvas::TOP); - tp::rectf info_rec = rect; - info_rec.pos.x += 5; - info_rec.pos.y += 25; - drawer->text(selected_node->name, info_rec, 4, flag); - info_rec.pos.y += 10; - drawer->text(selected_node->file, info_rec, 4, flag); - info_rec.pos.y += 10; - drawer->text(tp::halni(selected_node->line), info_rec, 4, flag); - } -} - -void MemLeaksTreeView::drawNode(Frame& node, tp::glw::Canvas* drawer) { - auto rec = nodeBoundsScaled(node); - - // outside - if (!rec.overlap(rect)) { - node.flag = Frame::Flags::NONE; - return; - } - - rec.clamp(rect); - - drawer->setCol1((&node == selected_node) ? col.node_active : col.node); - drawer->setCol2(col.node_outline); - drawer->rect(rec, node_rounding, outline_size); - - drawer->setCol1(col.text); - auto prev = drawer->mClamping; - rec.pos.x += 2; - rec.size.x -= 4; - drawer->setClamping(rec); - drawer->text(node.name, rec, text_size, tp::glw::Canvas::LEFT_MIDDLE); - drawer->setClamping(prev); -} - -void MemLeaksTreeView::drawConnection(Frame& from, Frame& to, tp::halni call_count, tp::glw::Canvas* drawer) { - - node_size *= 1.1; - auto rec1 = nodeBoundsScaled(from); - auto rec2 = nodeBoundsScaled(to); - node_size /= 1.1; - - auto c1 = scalePoint(from.tree_view_pos + tree_view_pos); - auto c2 = scalePoint(to.tree_view_pos + tree_view_pos); - - rec1.clamp_outside(c1, c2); - rec2.clamp_outside(c1, c2); - - auto viewport = rect; - - if (!viewport.clamp_inside(c1, c2)) { - return; - } - - auto dir = (c2 - c1).unitv(); - auto side = dir.normal() * arrow_size * scaleval; - - auto ab = c2 - dir * arrow_size * scaleval; - auto ae = c2; - auto al = ab + side; - auto ar = ab - side; - - if (&from == selected_node || &to == selected_node) { - //col = ImColor(ImGui::GetStyle().Colors[ImGuiCol_ButtonActive]); - } - - drawer->setCol1(col.arrow); - - drawer->trig({ ae.x, ae.y }, { al.x, al.y }, { ar.x, ar.y }); - drawer->line({ c1.x, c1.y }, { c2.x, c2.y }, line_thik); - - if (&from == selected_node || &to == selected_node) { - auto text_pos = c1 + (dir * (tp::halnf)(c1 - c2).length() * 0.8); - tp::string count = call_count; - drawer->setCol1(col.text); - drawer->text(count.cstr(), { text_pos.x, text_pos.y, 0, 0 }, text_size); - } -} - -tp::vec2f MemLeaksTreeView::scalePoint(const tp::vec2f in) { - return in * (tp::halnf)scaleval + rect.pos + rect.size / 2; -} - -tp::rectf MemLeaksTreeView::nodeBounds(Frame& node) { - tp::rectf out; - out.pos = node.tree_view_pos + tree_view_pos; - out.pos -= node_size / 2; - out.size = node_size; - return out; -} - -tp::rectf MemLeaksTreeView::nodeBoundsScaled(Frame& node) { - auto out = nodeBounds(node); - auto p1 = scalePoint(out.pos); - auto p3 = scalePoint(out.p3()); - out.pos = p1; - out.size = p3 - p1; - return out; -} \ No newline at end of file diff --git a/tools/MemLeaks/memleaks.h b/tools/MemLeaks/memleaks.h deleted file mode 100644 index 25c53c9..0000000 --- a/tools/MemLeaks/memleaks.h +++ /dev/null @@ -1,137 +0,0 @@ - -#include "map.h" -#include "array.h" -#include "stringt.h" -#include "rect.h" -#include "color.h" -#include "canvas.h" - -namespace tp { - - struct MemLeaksData { - - typedef tp::ualni FrameId; - typedef tp::Array MemLeak; - - struct Frame; - - struct CallerInfo { - Frame* caller = NULL; - tp::ualni count = 0; - }; - - struct Frame { - tp::string name; - tp::string file; - tp::ualni line = 0; - tp::ualni id = 0; - - tp::HashMap callers; - - tp::vec2f tree_view_pos = 0; - bool collapced = true; - - enum class Flags { - NONE, - INUSE, - } flag = Flags::NONE; - - tp::alni flag2 = 0; - tp::alni depth_level = 0; - - static void dfs(Frame& frame, void (*exec)(Frame& frame, void* custom), void* custom = 0); - }; - - Frame rootframe; - tp::HashMap frames; - tp::Array leaks; - - struct Connection { - Frame* caller = NULL; - Frame* target = NULL; - tp::ualni count = 0; - }; - - tp::Array mConnections; - - tp::string filename; - tp::vec2f sapacing = { 60.f, 15.f }; - - enum class LoadStatus { - DONE, - INVALID_FILE_PATH, - INVALID_FILE_FORMAT, - INTERNAL_ERROR, - } status; - - MemLeaksData(tp::string afilename); - - private: - - struct LevelInfo { - tp::alni count_users; - tp::alni used_idx; - }; - - tp::Array levels; - - void increase_caller_count(Frame& frame, FrameId caller_id); - void calc_max_level(Frame& frame, tp::alni& max_level); - void count_level_users(Frame& frame, tp::alni& max_level, tp::Array& levels); - void apply_position(Frame& frame, tp::Array& levels); - void construct_tree(); - void load_leaks(); - void make_connections(); - }; - - struct MemLeaksTreeView { - - // inputs - tp::halnf zoom_factor = 1.f; - tp::rectf rect = 0.f; - tp::vec2f vieport_crs = 0.f; - tp::vec2f vieport_crs_delta = 0.f; - bool mouse_down = false; - bool mouse_hold = false; - bool mouse_up = false; - - // appearence - struct Colors { - tp::rgba node = { 0.1f, 0.1f, 0.1f, 1.f }; - tp::rgba node_outline = { 0.5f, 0.5f, 0.5f, 1.f }; - tp::rgba node_active = { 0.2f, 0.2f, 0.2f, 1.f }; - tp::rgba bg = { 0.15f, 0.15f, 0.15f, 1.f }; - tp::rgba text = { 0.9f, 0.9f, 0.9f, 1.f }; - tp::rgba arrow = { 0.5f, 0.5f, 0.5f, 1.f }; - } col; - - tp::halnf text_size = 3; - tp::halnf arrow_size = 2; - tp::halnf outline_size = 0.5f; - tp::halnf line_thik = 0.2f; - tp::halnf node_rounding = 1; - - void setTarget(MemLeaksData* aLeaks); - - void proc(); - void draw(tp::glw::Canvas* drawer); - - private: - - typedef MemLeaksData::Frame Frame; - - MemLeaksData* leaks = NULL; - Frame* selected_node = NULL; - bool inside_node = false; - tp::alnf scaleval = 1.0; - tp::vec2f tree_size = 0.f; - tp::vec2f tree_view_pos = { 0.f, -50.f }; - tp::vec2f node_size = { 50, 10 }; - - void drawNode(Frame& node, tp::glw::Canvas* drawer); - void drawConnection(Frame& from, Frame& to, tp::halni call_count, tp::glw::Canvas* drawer); - tp::vec2f scalePoint(const tp::vec2f in); - tp::rectf nodeBounds(Frame& node); - tp::rectf nodeBoundsScaled(Frame& node); - }; -}; \ No newline at end of file diff --git a/tools/MemUsage/MemUsageEntry.cpp b/tools/MemUsage/MemUsageEntry.cpp deleted file mode 100644 index d3f5a12..0000000 --- a/tools/MemUsage/MemUsageEntry.cpp +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tools/MemUsage/memusage.cpp b/tools/MemUsage/memusage.cpp deleted file mode 100644 index 4f7a4ba..0000000 --- a/tools/MemUsage/memusage.cpp +++ /dev/null @@ -1,401 +0,0 @@ - - -#include "GuiWindow.h" - -#include "imgui.h" -#include "implot.h" -#include "nanovg.h" - -#include "strings.h" -#include "filesystem.h" -#include "array.h" -#include "list.h" -#include "map.h" - -#include "intersections.h" - -#include "pickalloc_analizer.h" -#include "pickalloc_cfg.h" - -#include - -class Memusage : public tp::GuiWindow { - - tp::string filename; - - static const char logo_len = 16; - const char logo[logo_len] = "memusage\0\0\0\0\0\0\0"; - char logo_loaded[logo_len]; - - PickAllocConfig cfg; - - enum class LoadStatus { - NONE, - DONE, - INVALID_FILE_PATH, - INVALID_FILE_FORMAT, - INTERNAL_ERROR, - } status; - - typedef tp::PickAllocDataAnalizer::Event Event; - typedef tp::alni SizeKey; - - struct Timeline { - tp::Array size; - tp::Array time; - tp::ualni peak = 0; - tp::ualni avereging = 0; - - void recordEvents(tp::Array& events) { - tp::ualni total_mem_usage = 0; - - size.extend(events.length()); - time.extend(events.length()); - - for (auto ev : events) { - - if (ev->dealloc) { - total_mem_usage -= ev->size; - } else { - total_mem_usage += ev->size; - } - - time[ev.idx()] = (tp::alnf) ev->time; - size[ev.idx()] = (tp::alnf) total_mem_usage; - - if (peak < total_mem_usage) { - peak = total_mem_usage; - } - } - - avereging = 1; - } - - void to_seconds() { - for (auto tm : time) { - tm.data() /= 1000; - } - } - }; - - // Contains events of fixed size - struct Group { - tp::ualni group_size = 0; - tp::Array events; - - tp::ualni flag = 0; - - Timeline timeline; - - Group(tp::ualni grp_size) : group_size(grp_size) {} - }; - - tp::HashMap groups; - tp::Array groups_sorted; - - public: - - Memusage(const char* filename) : GuiWindow() { - - this->mHeader.mAppTitle = "Mamusage Analizer"; - this->mHeader.mPadding = 10; - tp::string file_path = __FILE__; - tp::make_dir_str(file_path.get_writable()); - tp::make_dir_str(file_path.get_writable()); - this->mRscDirectory = file_path + "/"; - this->mGuiRootIsWindow = false; - LoadFonts(); - - this->filename = filename; - } - - ~Memusage() { - for (auto tl : groups) { - delete tl->val; - } - - if (cfg.mAllocationSizes) delete[] cfg.mAllocationSizes; - if (cfg.mChunkLengths) delete[] cfg.mChunkLengths; - } - - void run() { - - // display status - for (auto i : tp::Range(2)) { - procInputs(); - beginDraw(); - if (mNativeWindow.mEvents.mRedraw) { - DrawCallback(); - } - endDraw(); - mNativeWindow.mEvents.mRedraw = true; - } - - while (!mNativeWindow.mEvents.mTerminate) { - tp::Timer timer(5); - procInputs(); - beginDraw(); - if (mNativeWindow.mEvents.mRedraw) { - DrawCallback(); - } - endDraw(); - timer.wait(); - - if (status == LoadStatus::NONE) { - openFile(); - mNativeWindow.mEvents.mRedraw = true; - } - } - } - - private: - - tp::ualni fileContentSize(tp::File& file) { - return file.size() - fileContentStartIdx(); - } - - tp::ualni fileContentStartIdx() { - return logo_len; - } - - void readFile(tp::File& file) { - using namespace tp; - - // read cfg - file.read(&cfg.mCapacity); - cfg.mAllocationSizes = new tp::int4[cfg.mCapacity]; - cfg.mChunkLengths = new tp::int4[cfg.mCapacity]; - - file.read_bytes((tp::int1*)cfg.mAllocationSizes, sizeof(tp::int4) * cfg.mCapacity); - file.read_bytes((tp::int1*)cfg.mChunkLengths, sizeof(tp::int4) * cfg.mCapacity); - - // read evens - tp::ualni events_length; - file.read(&events_length); - - // one pass to collect data about groups in 'flag' - auto events_adress = file.adress; - for (tp::ualni i = 0; i < events_length; i++) { - Event evnt; - file.read(&evnt); - auto idx = groups.presents(evnt.size); - Group* group = NULL; - if (idx) { - group = groups.getSlotVal(idx); - } else { - group = new Group(evnt.size); - groups.put(evnt.size, group); - } - group->flag++; - } - for (auto grp : groups) { - grp->val->events.reserve(grp->val->flag); - grp->val->flag = 0; - } - file.adress = events_adress; - - // read actual events - for (tp::ualni idx = 0; idx < events_length; idx++) { - Event evnt; - file.read(&evnt); - auto grp = groups.get(evnt.size); - grp->events[grp->flag] = evnt; - grp->flag++; - } - - // initialize additional structure for sorting - groups_sorted.reserve(groups.size()); - for (auto grp : groups) { - groups_sorted[grp.entry_idx] = grp->val; - } - - // generate timelines - for (auto grp : groups_sorted) { - grp.data()->timeline.recordEvents(grp.data()->events); - grp.data()->timeline.to_seconds(); - } - } - - void openFile() { - using namespace tp; - File log(filename.cstr(), osfile_openflags::LOAD); - - if (!log.opened) { - status = LoadStatus::INVALID_FILE_PATH; - return; - } - - log.read_bytes(logo_loaded, logo_len); - if (!tp::memequal(logo_loaded, logo, logo_len)) { - status = LoadStatus::INVALID_FILE_FORMAT; - return; - } - - log.Preload(); - - try { - readFile(log); - status = LoadStatus::DONE; - } catch (...) { - status = LoadStatus::INTERNAL_ERROR; - return; - } - } - - void HeaderDrawCallback() override { - using namespace ImGui; - Button("Analize"); - Button("Save To Header File"); - } - - void DrawCallback() { - - auto pos = this->mNativeWindow.mAppearence.mSize / 2; - if (status != LoadStatus::DONE) { - - nvgFillColor(mNvg, nvgRGB(250, 250, 250)); - nvgFontSize(mNvg, 24); - nvgTextAlign(mNvg, NVG_ALIGN_MIDDLE | NVG_ALIGN_CENTER); - - switch (status) { - case LoadStatus::NONE: - nvgText(mNvg, pos.x, pos.y, "Loading...", 0); - break; - - case LoadStatus::INVALID_FILE_PATH: - nvgText(mNvg, pos.x, pos.y, "Invalid File Path", 0); - break; - - case LoadStatus::INVALID_FILE_FORMAT: - nvgText(mNvg, pos.x, pos.y, "Invalid File Format", 0); - break; - - case LoadStatus::INTERNAL_ERROR: - nvgText(mNvg, pos.x, pos.y, "Internal Error", 0); - } - return; - } - - drawEditor(); - } - - enum class GroupSorting { - LARGER_SIZE, - SMALLER_SIZE, - LARGER_TOTAL_SIZE, - SMALLER_TOTAL_SIZE, - } sorting = GroupSorting::LARGER_SIZE; - - void sortGroups(GroupSorting type) { - if (type == sorting) { - return; - } - - switch (type) { - case Memusage::GroupSorting::LARGER_SIZE: - groups_sorted.sort([](Group* const& i1, Group* const& i2) { - return i1->group_size > i2->group_size; - }); - break; - case Memusage::GroupSorting::SMALLER_SIZE: - groups_sorted.sort([](Group* const& i1, Group* const& i2) { - return i1->group_size < i2->group_size; - }); - break; - case Memusage::GroupSorting::LARGER_TOTAL_SIZE: - break; - case Memusage::GroupSorting::SMALLER_TOTAL_SIZE: - break; - default: - break; - } - - sorting = type; - } - - Group* mGroupActive = NULL; - - void groupView() { - using namespace ImGui; - int tmp; - - const char* names[] = { - {"Larger Group Size"}, - {"Smaller Group Size"}, - //{"LARGER_TOTAL_SIZE"}, - //{"SMALER_TOTAL_SIZE"}, - }; - - tmp = (int) sorting; - Combo(" ", &tmp, names, 2); - Separator(); - sortGroups((GroupSorting) tmp); - - for (auto grp : groups_sorted) { - if (Selectable(tp::string((tp::alni)grp.data()->group_size).cstr())) { - mGroupActive = grp.data(); - } - } - } - - void drawTimeline(const Timeline& timeline) { - if (ImPlot::BeginPlot(" Plot ", {-1, -1}, ImPlotFlags_CanvasOnly | ImPlotFlags_AntiAliased)) { - ImPlot::PlotLine("Memory Usage", timeline.time.buff(), timeline.size.buff(), (tp::halni) timeline.time.length()); - ImPlot::EndPlot(); - } - } - - void infoView() { - using namespace ImGui; - if (!mGroupActive) { - Text("No Group Selected"); - return; - } - - Text("Total Events : %i", mGroupActive->events.length()); - - Separator(); - - drawTimeline(mGroupActive->timeline); - } - - void drawEditor() { - using namespace ImGui; - - Begin("Groups View"); - groupView(); - End(); - - Begin("Info"); - infoView(); - End(); - } -}; - -int main(char argc, char* argv[]) { - - if (argc != 2) { - printf("invalid arguments"); - return 0; - } - - //ImPlot:: - - Memusage::InitializeTypes(); - - { - Memusage app(argv[1]); - - ImPlot::CreateContext(); - - app.run(); - - ImPlot::DestroyContext(); - - printf("internal error"); - } - - Memusage::UnInitializeTypes(); - - tp::terminate(); -} \ No newline at end of file diff --git a/tools/MemUsage/memusage.h b/tools/MemUsage/memusage.h deleted file mode 100644 index 4f7a4ba..0000000 --- a/tools/MemUsage/memusage.h +++ /dev/null @@ -1,401 +0,0 @@ - - -#include "GuiWindow.h" - -#include "imgui.h" -#include "implot.h" -#include "nanovg.h" - -#include "strings.h" -#include "filesystem.h" -#include "array.h" -#include "list.h" -#include "map.h" - -#include "intersections.h" - -#include "pickalloc_analizer.h" -#include "pickalloc_cfg.h" - -#include - -class Memusage : public tp::GuiWindow { - - tp::string filename; - - static const char logo_len = 16; - const char logo[logo_len] = "memusage\0\0\0\0\0\0\0"; - char logo_loaded[logo_len]; - - PickAllocConfig cfg; - - enum class LoadStatus { - NONE, - DONE, - INVALID_FILE_PATH, - INVALID_FILE_FORMAT, - INTERNAL_ERROR, - } status; - - typedef tp::PickAllocDataAnalizer::Event Event; - typedef tp::alni SizeKey; - - struct Timeline { - tp::Array size; - tp::Array time; - tp::ualni peak = 0; - tp::ualni avereging = 0; - - void recordEvents(tp::Array& events) { - tp::ualni total_mem_usage = 0; - - size.extend(events.length()); - time.extend(events.length()); - - for (auto ev : events) { - - if (ev->dealloc) { - total_mem_usage -= ev->size; - } else { - total_mem_usage += ev->size; - } - - time[ev.idx()] = (tp::alnf) ev->time; - size[ev.idx()] = (tp::alnf) total_mem_usage; - - if (peak < total_mem_usage) { - peak = total_mem_usage; - } - } - - avereging = 1; - } - - void to_seconds() { - for (auto tm : time) { - tm.data() /= 1000; - } - } - }; - - // Contains events of fixed size - struct Group { - tp::ualni group_size = 0; - tp::Array events; - - tp::ualni flag = 0; - - Timeline timeline; - - Group(tp::ualni grp_size) : group_size(grp_size) {} - }; - - tp::HashMap groups; - tp::Array groups_sorted; - - public: - - Memusage(const char* filename) : GuiWindow() { - - this->mHeader.mAppTitle = "Mamusage Analizer"; - this->mHeader.mPadding = 10; - tp::string file_path = __FILE__; - tp::make_dir_str(file_path.get_writable()); - tp::make_dir_str(file_path.get_writable()); - this->mRscDirectory = file_path + "/"; - this->mGuiRootIsWindow = false; - LoadFonts(); - - this->filename = filename; - } - - ~Memusage() { - for (auto tl : groups) { - delete tl->val; - } - - if (cfg.mAllocationSizes) delete[] cfg.mAllocationSizes; - if (cfg.mChunkLengths) delete[] cfg.mChunkLengths; - } - - void run() { - - // display status - for (auto i : tp::Range(2)) { - procInputs(); - beginDraw(); - if (mNativeWindow.mEvents.mRedraw) { - DrawCallback(); - } - endDraw(); - mNativeWindow.mEvents.mRedraw = true; - } - - while (!mNativeWindow.mEvents.mTerminate) { - tp::Timer timer(5); - procInputs(); - beginDraw(); - if (mNativeWindow.mEvents.mRedraw) { - DrawCallback(); - } - endDraw(); - timer.wait(); - - if (status == LoadStatus::NONE) { - openFile(); - mNativeWindow.mEvents.mRedraw = true; - } - } - } - - private: - - tp::ualni fileContentSize(tp::File& file) { - return file.size() - fileContentStartIdx(); - } - - tp::ualni fileContentStartIdx() { - return logo_len; - } - - void readFile(tp::File& file) { - using namespace tp; - - // read cfg - file.read(&cfg.mCapacity); - cfg.mAllocationSizes = new tp::int4[cfg.mCapacity]; - cfg.mChunkLengths = new tp::int4[cfg.mCapacity]; - - file.read_bytes((tp::int1*)cfg.mAllocationSizes, sizeof(tp::int4) * cfg.mCapacity); - file.read_bytes((tp::int1*)cfg.mChunkLengths, sizeof(tp::int4) * cfg.mCapacity); - - // read evens - tp::ualni events_length; - file.read(&events_length); - - // one pass to collect data about groups in 'flag' - auto events_adress = file.adress; - for (tp::ualni i = 0; i < events_length; i++) { - Event evnt; - file.read(&evnt); - auto idx = groups.presents(evnt.size); - Group* group = NULL; - if (idx) { - group = groups.getSlotVal(idx); - } else { - group = new Group(evnt.size); - groups.put(evnt.size, group); - } - group->flag++; - } - for (auto grp : groups) { - grp->val->events.reserve(grp->val->flag); - grp->val->flag = 0; - } - file.adress = events_adress; - - // read actual events - for (tp::ualni idx = 0; idx < events_length; idx++) { - Event evnt; - file.read(&evnt); - auto grp = groups.get(evnt.size); - grp->events[grp->flag] = evnt; - grp->flag++; - } - - // initialize additional structure for sorting - groups_sorted.reserve(groups.size()); - for (auto grp : groups) { - groups_sorted[grp.entry_idx] = grp->val; - } - - // generate timelines - for (auto grp : groups_sorted) { - grp.data()->timeline.recordEvents(grp.data()->events); - grp.data()->timeline.to_seconds(); - } - } - - void openFile() { - using namespace tp; - File log(filename.cstr(), osfile_openflags::LOAD); - - if (!log.opened) { - status = LoadStatus::INVALID_FILE_PATH; - return; - } - - log.read_bytes(logo_loaded, logo_len); - if (!tp::memequal(logo_loaded, logo, logo_len)) { - status = LoadStatus::INVALID_FILE_FORMAT; - return; - } - - log.Preload(); - - try { - readFile(log); - status = LoadStatus::DONE; - } catch (...) { - status = LoadStatus::INTERNAL_ERROR; - return; - } - } - - void HeaderDrawCallback() override { - using namespace ImGui; - Button("Analize"); - Button("Save To Header File"); - } - - void DrawCallback() { - - auto pos = this->mNativeWindow.mAppearence.mSize / 2; - if (status != LoadStatus::DONE) { - - nvgFillColor(mNvg, nvgRGB(250, 250, 250)); - nvgFontSize(mNvg, 24); - nvgTextAlign(mNvg, NVG_ALIGN_MIDDLE | NVG_ALIGN_CENTER); - - switch (status) { - case LoadStatus::NONE: - nvgText(mNvg, pos.x, pos.y, "Loading...", 0); - break; - - case LoadStatus::INVALID_FILE_PATH: - nvgText(mNvg, pos.x, pos.y, "Invalid File Path", 0); - break; - - case LoadStatus::INVALID_FILE_FORMAT: - nvgText(mNvg, pos.x, pos.y, "Invalid File Format", 0); - break; - - case LoadStatus::INTERNAL_ERROR: - nvgText(mNvg, pos.x, pos.y, "Internal Error", 0); - } - return; - } - - drawEditor(); - } - - enum class GroupSorting { - LARGER_SIZE, - SMALLER_SIZE, - LARGER_TOTAL_SIZE, - SMALLER_TOTAL_SIZE, - } sorting = GroupSorting::LARGER_SIZE; - - void sortGroups(GroupSorting type) { - if (type == sorting) { - return; - } - - switch (type) { - case Memusage::GroupSorting::LARGER_SIZE: - groups_sorted.sort([](Group* const& i1, Group* const& i2) { - return i1->group_size > i2->group_size; - }); - break; - case Memusage::GroupSorting::SMALLER_SIZE: - groups_sorted.sort([](Group* const& i1, Group* const& i2) { - return i1->group_size < i2->group_size; - }); - break; - case Memusage::GroupSorting::LARGER_TOTAL_SIZE: - break; - case Memusage::GroupSorting::SMALLER_TOTAL_SIZE: - break; - default: - break; - } - - sorting = type; - } - - Group* mGroupActive = NULL; - - void groupView() { - using namespace ImGui; - int tmp; - - const char* names[] = { - {"Larger Group Size"}, - {"Smaller Group Size"}, - //{"LARGER_TOTAL_SIZE"}, - //{"SMALER_TOTAL_SIZE"}, - }; - - tmp = (int) sorting; - Combo(" ", &tmp, names, 2); - Separator(); - sortGroups((GroupSorting) tmp); - - for (auto grp : groups_sorted) { - if (Selectable(tp::string((tp::alni)grp.data()->group_size).cstr())) { - mGroupActive = grp.data(); - } - } - } - - void drawTimeline(const Timeline& timeline) { - if (ImPlot::BeginPlot(" Plot ", {-1, -1}, ImPlotFlags_CanvasOnly | ImPlotFlags_AntiAliased)) { - ImPlot::PlotLine("Memory Usage", timeline.time.buff(), timeline.size.buff(), (tp::halni) timeline.time.length()); - ImPlot::EndPlot(); - } - } - - void infoView() { - using namespace ImGui; - if (!mGroupActive) { - Text("No Group Selected"); - return; - } - - Text("Total Events : %i", mGroupActive->events.length()); - - Separator(); - - drawTimeline(mGroupActive->timeline); - } - - void drawEditor() { - using namespace ImGui; - - Begin("Groups View"); - groupView(); - End(); - - Begin("Info"); - infoView(); - End(); - } -}; - -int main(char argc, char* argv[]) { - - if (argc != 2) { - printf("invalid arguments"); - return 0; - } - - //ImPlot:: - - Memusage::InitializeTypes(); - - { - Memusage app(argv[1]); - - ImPlot::CreateContext(); - - app.run(); - - ImPlot::DestroyContext(); - - printf("internal error"); - } - - Memusage::UnInitializeTypes(); - - tp::terminate(); -} \ No newline at end of file