Initial
This commit is contained in:
parent
d4c558a59a
commit
db05d963be
74 changed files with 4473 additions and 3231 deletions
66
.clang-format
Normal file
66
.clang-format
Normal file
|
|
@ -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
|
||||
42
.github/workflows/cmake.yml
vendored
Normal file
42
.github/workflows/cmake.yml
vendored
Normal file
|
|
@ -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}}
|
||||
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,6 +1,6 @@
|
|||
.idea
|
||||
tmp
|
||||
*tmp*
|
||||
bin
|
||||
build
|
||||
build*
|
||||
lib
|
||||
install
|
||||
|
|
|
|||
20
Allocators/CMakeLists.txt
Normal file
20
Allocators/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
|
|
@ -30,7 +30,7 @@ If memory leaks were detected it will be loged in the output console.
|
|||
|
||||

|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
|
|
@ -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); }
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -1,5 +1,8 @@
|
|||
|
||||
#include "heapallocator.hpp"
|
||||
|
||||
#include "HeapAllocator.hpp"
|
||||
#include "HeapAllocatorGlobal.hpp"
|
||||
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
|
||||
#include "heapallocator.hpp"
|
||||
|
||||
#include "HeapAllocatorGlobal.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include "StackTrace.hpp"
|
||||
#include "Debugging.hpp"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <cstdlib>
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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) {
|
||||
|
|
@ -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;
|
||||
|
|
@ -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
|
||||
29
Allocators/public/Allocators.hpp
Normal file
29
Allocators/public/Allocators.hpp
Normal file
|
|
@ -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);
|
||||
|
|
@ -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;
|
||||
|
|
@ -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);
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "common.h"
|
||||
#include "PublicConfig.hpp"
|
||||
#include "BaseModule.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
305
Allocators/tests/Tests.cpp
Normal file
305
Allocators/tests/Tests.cpp
Normal file
|
|
@ -0,0 +1,305 @@
|
|||
|
||||
#include "Allocators.hpp"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
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 <tp::alni size>
|
||||
struct allocator_test {
|
||||
test_struct data[size];
|
||||
bool is_allocated[size];
|
||||
tp::alni n_loaded = 0;
|
||||
|
||||
test_struct* allocations[size];
|
||||
|
||||
tp::AbstractAllocator* alloc;
|
||||
tp::AbstractAllocator* parent_alloc;
|
||||
const char* allocator_name = NULL;
|
||||
|
||||
tp::alni rand_idx(bool state) {
|
||||
RAND:
|
||||
|
||||
tp::alni idx = (tp::alni)(tp::randf() * (size + 1));
|
||||
CLAMP(idx, 0, size - 1);
|
||||
|
||||
if (state == is_allocated[idx]) {
|
||||
goto RAND;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
allocator_test(tp::AbstractAllocator* palloc, const char* pallocator_name,
|
||||
tp::AbstractAllocator* p_parent_alloc) {
|
||||
allocator_name = pallocator_name;
|
||||
parent_alloc = p_parent_alloc;
|
||||
alloc = palloc;
|
||||
for (tp::alni i = 0; i < size; i++) {
|
||||
RAND:
|
||||
tp::alni val = tp::alni(tp::randf() * (size + 100.f));
|
||||
for (tp::alni check_idx = 0; check_idx < size; check_idx++) {
|
||||
if (data[check_idx].val == val) {
|
||||
goto RAND;
|
||||
}
|
||||
}
|
||||
|
||||
data[i].val = val;
|
||||
is_allocated[i] = false;
|
||||
allocations[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void verify_integrity() {
|
||||
// verify data integrity
|
||||
for (tp::alni i = 0; i < size; i++) {
|
||||
if (is_allocated[i]) {
|
||||
assert(*allocations[i] == data[i] && "data is currupted\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc->isWrapSupport()) assert(!alloc->isWrapCorrupted());
|
||||
if (parent_alloc && parent_alloc->isWrapSupport())
|
||||
assert(!parent_alloc->isWrapCorrupted());
|
||||
|
||||
verify_sizes();
|
||||
}
|
||||
|
||||
void verify_sizes() {
|
||||
return;
|
||||
#ifdef MEM_TRACE
|
||||
assert(alloc->sizeInuse() == n_loaded * sizeof(test_struct) &&
|
||||
"invalid inuse size\n");
|
||||
assert(alloc->sizeReserved() >= n_loaded * (tp::alni)sizeof(test_struct) &&
|
||||
"invalid reserved size\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void load_item(tp::alni idx) {
|
||||
if (!is_allocated[idx]) {
|
||||
allocations[idx] = new (alloc) test_struct();
|
||||
|
||||
assert(allocations[idx] && "allocator returned NULL");
|
||||
|
||||
allocations[idx]->val = data[idx].val;
|
||||
is_allocated[idx] = true;
|
||||
n_loaded++;
|
||||
verify_integrity();
|
||||
}
|
||||
}
|
||||
|
||||
void unload_item(tp::alni idx) {
|
||||
if (is_allocated[idx]) {
|
||||
verify_integrity();
|
||||
delete allocations[idx];
|
||||
is_allocated[idx] = false;
|
||||
n_loaded--;
|
||||
verify_integrity();
|
||||
}
|
||||
}
|
||||
|
||||
void change_states(tp::Range<tp::alni> rg, bool state, bool reversed = false,
|
||||
bool random = false) {
|
||||
for (auto i : rg) {
|
||||
tp::alni idx = i;
|
||||
|
||||
if (random) {
|
||||
idx = rand_idx(state);
|
||||
}
|
||||
else if (reversed) {
|
||||
idx = size - i - 1;
|
||||
}
|
||||
|
||||
(state) ? load_item(idx) : unload_item(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// full down-up load then up-down unload
|
||||
void test1() {
|
||||
change_states({ 0, size }, 1);
|
||||
change_states({ 0, size }, 0, true);
|
||||
}
|
||||
|
||||
// full down-up load then down-up unload
|
||||
void test2() {
|
||||
change_states({ 0, size }, 1);
|
||||
change_states({ 0, size }, 0);
|
||||
}
|
||||
|
||||
// full random load then random unload
|
||||
void test3() {
|
||||
change_states({ 0, size }, 1, 0, 1);
|
||||
change_states({ 0, size }, 0, 0, 1);
|
||||
}
|
||||
|
||||
// multipul tests 1-3
|
||||
void test4() {
|
||||
test1();
|
||||
test1();
|
||||
|
||||
test2();
|
||||
test2();
|
||||
|
||||
test3();
|
||||
test3();
|
||||
}
|
||||
|
||||
static tp::alnf sineupf(tp::alnf asize, tp::alnf x, bool reverse) {
|
||||
tp::alnf end = 4 * PI;
|
||||
tp::alnf a = (2 / 7.f) * asize;
|
||||
tp::alnf b = end / asize;
|
||||
|
||||
tp::alni c = ((-1 * reverse) + (1 * !reverse));
|
||||
tp::alnf c1 = (x - (end * reverse)) / b;
|
||||
tp::alnf c2 = (a * sin(x - (end * reverse)));
|
||||
tp::alnf out = c1 + c2;
|
||||
return c * out;
|
||||
}
|
||||
|
||||
// sin load & sin unload with ~1/2 drop factor
|
||||
void test5() {
|
||||
tp::alnf end = 4 * PI;
|
||||
tp::alnf step = end / 4.f;
|
||||
|
||||
for (char i = 0; i < 2; i++) {
|
||||
for (tp::alnf x = 0; x <= end; x += step) {
|
||||
tp::alni target_alloc_count = (tp::alni)ceil(sineupf(size, x, i));
|
||||
CLAMP(target_alloc_count, 0, size);
|
||||
|
||||
while (n_loaded > target_alloc_count) {
|
||||
unload_item(rand_idx(0));
|
||||
}
|
||||
while (n_loaded < target_alloc_count) {
|
||||
load_item(rand_idx(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MEM_WRAP
|
||||
void check_wrap(tp::alni offset, bool after) {
|
||||
CLAMP(offset, 1, WRAP_LEN);
|
||||
|
||||
test_struct* ts = allocations[rand_idx(0)];
|
||||
tp::alni shift = (sizeof(test_struct) * after) + (offset - 1) * after -
|
||||
offset * (!after);
|
||||
tp::uint1* address = (((tp::uint1*)ts) + shift);
|
||||
|
||||
tp::uint1 val = *address;
|
||||
*address = 5;
|
||||
assert(alloc->isWrapCorrupted());
|
||||
*address = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
// mem guards test
|
||||
void test6() {
|
||||
change_states({ 0, size }, 1);
|
||||
|
||||
#ifdef MEM_WRAP
|
||||
for (tp::alni after = 0; after < 2; after++) {
|
||||
for (tp::alni offset = 1; offset <= WRAP_LEN; offset++) {
|
||||
check_wrap(offset, after);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
change_states({ 0, size }, 0);
|
||||
}
|
||||
|
||||
void run_tests() {
|
||||
try {
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
if (alloc->isWrapSupport()) {
|
||||
test6();
|
||||
}
|
||||
|
||||
printf("%s - passed\n", allocator_name);
|
||||
if (!alloc->isWrapSupport()) {
|
||||
printf(" WARNING: %s has no wrap support!! \n", allocator_name);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
printf("%s - failed\n", allocator_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void heap_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
allocator_test<150> hatest(tp::heapalloc, "heap allocator", NULL);
|
||||
hatest.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("heap alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void chunk_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
tp::ChunkAlloc calloc(sizeof(test_struct), 50);
|
||||
allocator_test<50> ca_test(&calloc, "chunk allocator", tp::heapalloc);
|
||||
ca_test.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("chunk alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void pool_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
tp::PoolAlloc palloc(sizeof(test_struct), 50);
|
||||
allocator_test<150> pa_test(&palloc, "pool allocator", tp::heapalloc);
|
||||
pa_test.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("chunk alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void allocators_test() {
|
||||
|
||||
printf("running tests on alocators:\n");
|
||||
|
||||
heap_alloc_test();
|
||||
chunk_alloc_test();
|
||||
pool_alloc_test();
|
||||
}
|
||||
|
||||
|
||||
CROSSPLATFORM_MAIN;
|
||||
int main(int argc, char* argv[]) {
|
||||
tp::print_env_info();
|
||||
allocators_test();
|
||||
}
|
||||
20
BaseModule/CMakeLists.txt
Normal file
20
BaseModule/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
26
BaseModule/private/Assert.cpp
Normal file
26
BaseModule/private/Assert.cpp
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include "Assert.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
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);
|
||||
}
|
||||
81
BaseModule/private/BaseModule.cpp
Normal file
81
BaseModule/private/BaseModule.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
#include "BaseModule.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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;
|
||||
}
|
||||
46
BaseModule/private/Common.cpp
Normal file
46
BaseModule/private/Common.cpp
Normal file
|
|
@ -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)); }
|
||||
}
|
||||
21
BaseModule/private/Environment.cpp
Normal file
21
BaseModule/private/Environment.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
|
||||
#include "Environment.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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";
|
||||
}
|
||||
34
BaseModule/public/Assert.hpp
Normal file
34
BaseModule/public/Assert.hpp
Normal file
|
|
@ -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"); }
|
||||
31
BaseModule/public/BaseModule.hpp
Normal file
31
BaseModule/public/BaseModule.hpp
Normal file
|
|
@ -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;
|
||||
};
|
||||
59
BaseModule/public/Common.hpp
Normal file
59
BaseModule/public/Common.hpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Environment.hpp"
|
||||
#include "TypeInfo.hpp"
|
||||
#include <initializer_list>
|
||||
|
||||
namespace tp {
|
||||
template<typename Type>
|
||||
using init_list = std::initializer_list<Type>;
|
||||
|
||||
// Selects whether to pass by constant reference or by value
|
||||
template <typename tType>
|
||||
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 <typename T>
|
||||
[[nodiscard]] T clamp(T v, T l, T u) { if (v < l) { v = l; } else if (v > u) { v = u; } return v; }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T max(T a, T b) { return (a > b) ? a : b; }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T min(T a, T b) { return (a < b) ? a : b; }
|
||||
|
||||
template <typename T>
|
||||
[[nodiscard]] T abs(T v) { if (v < 0) { return -v; } return v; }
|
||||
|
||||
template <typename T>
|
||||
inline void swap(T& t1, T& t2) { const T tmp = t1; t1 = t2; t2 = tmp; }
|
||||
|
||||
// only for x > 0 and y > 0
|
||||
template <typename T>
|
||||
[[nodiscard]] T ceil_positive(T x, T y) { return T( 1 + ((x - 1) / (tp::alnf)y) ); }
|
||||
|
||||
// power
|
||||
template <typename T>
|
||||
[[nodiscard]] T pow(T x, uhalni n) {
|
||||
T out = x; for (uhalni i = 0; i < n - 1; i++) { out = out * x; }
|
||||
return out;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
halni nDig10(Type val) {
|
||||
val = abs(val); halni out = 0;
|
||||
while (val != 0) { val = halni(val / 10); out++; }
|
||||
return out;
|
||||
}
|
||||
}
|
||||
198
BaseModule/public/Environment.hpp
Normal file
198
BaseModule/public/Environment.hpp
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <cfloat>
|
||||
|
||||
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
|
||||
};
|
||||
927
BaseModule/public/TypeInfo.hpp
Normal file
927
BaseModule/public/TypeInfo.hpp
Normal file
|
|
@ -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 <bool flag, typename T, typename U>
|
||||
struct TypeSelect {
|
||||
typedef T Result;
|
||||
};
|
||||
template <typename T, typename U>
|
||||
struct TypeSelect<false, T, U> {
|
||||
typedef U Result;
|
||||
};
|
||||
}
|
||||
|
||||
namespace tp {
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class template IsSameType
|
||||
// Return true iff two given types are the same
|
||||
// Invocation: SameType<T, U>::value
|
||||
// where:
|
||||
// T and U are types
|
||||
// Result evaluates to true iff U == T (types equal)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <typename T, typename U>
|
||||
struct IsSameType {
|
||||
enum { value = false };
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IsSameType<T, T> {
|
||||
enum { value = true };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper types Small and Big - guarantee that sizeof(Small) < sizeof(Big)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Private {
|
||||
template <class T, class U>
|
||||
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<T, U>::exists
|
||||
// returns (at compile time) true if there is an implicit conversion from T
|
||||
// to U (example: Derived to Base)
|
||||
// b) Conversion<T, U>::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<T, U>::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 <class T, class U>
|
||||
struct Conversion {
|
||||
typedef Private::ConversionHelper<T, U> H;
|
||||
#ifndef __MWERKS__
|
||||
enum { exists = sizeof(typename H::Small) == sizeof((H::Test(H::MakeT()))) };
|
||||
#else
|
||||
enum { exists = false };
|
||||
#endif
|
||||
enum { exists2Way = exists && Conversion<U, T>::exists };
|
||||
enum { sameType = false };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Conversion<T, T> {
|
||||
enum { exists = 1, exists2Way = 1, sameType = 1 };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Conversion<void, T> {
|
||||
enum { exists = 0, exists2Way = 0, sameType = 0 };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct Conversion<T, void> {
|
||||
enum { exists = 0, exists2Way = 0, sameType = 0 };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct Conversion<void, void> {
|
||||
public:
|
||||
enum { exists = 1, exists2Way = 1, sameType = 1 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class template SuperSubclass
|
||||
// Invocation: SuperSubclass<B, D>::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 <class T, class U>
|
||||
struct SuperSubclass {
|
||||
enum {
|
||||
value = (::tp::Conversion<const volatile U*, const volatile T*>::exists &&
|
||||
!::tp::Conversion<const volatile T*, const volatile void*>::sameType)
|
||||
};
|
||||
|
||||
// Dummy enum to make sure that both classes are fully defined.
|
||||
enum { dontUseWithIncompleteTypes = (sizeof(T) == sizeof(U)) };
|
||||
};
|
||||
|
||||
template <>
|
||||
struct SuperSubclass<void, void> {
|
||||
enum { value = false };
|
||||
};
|
||||
|
||||
template <class U>
|
||||
struct SuperSubclass<void, U> {
|
||||
enum {
|
||||
value = (::tp::Conversion<const volatile U*, const volatile void*>::exists &&
|
||||
!::tp::Conversion<const volatile void*, const volatile void*>::sameType)
|
||||
};
|
||||
|
||||
// Dummy enum to make sure that both classes are fully defined.
|
||||
enum { dontUseWithIncompleteTypes = (0 == sizeof(U)) };
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct SuperSubclass<T, void> {
|
||||
enum {
|
||||
value = (::tp::Conversion<const volatile void*, const volatile T*>::exists &&
|
||||
!::tp::Conversion<const volatile T*, const volatile void*>::sameType)
|
||||
};
|
||||
|
||||
// Dummy enum to make sure that both classes are fully defined.
|
||||
enum { dontUseWithIncompleteTypes = (sizeof(T) == 0) };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class template SuperSubclassStrict
|
||||
// Invocation: SuperSubclassStrict<B, D>::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<class T, class U>
|
||||
struct SuperSubclassStrict {
|
||||
enum {
|
||||
value = (::tp::Conversion<const volatile U*, const volatile T*>::exists &&
|
||||
!::tp::Conversion<const volatile T*, const volatile void*>::sameType &&
|
||||
!::tp::Conversion<const volatile T*, const volatile U*>::sameType)
|
||||
};
|
||||
|
||||
// Dummy enum to make sure that both classes are fully defined.
|
||||
enum { dontUseWithIncompleteTypes = (sizeof(T) == sizeof(U)) };
|
||||
};
|
||||
|
||||
template<>
|
||||
struct SuperSubclassStrict<void, void> {
|
||||
enum { value = false };
|
||||
};
|
||||
|
||||
template<class U>
|
||||
struct SuperSubclassStrict<void, U> {
|
||||
enum {
|
||||
value = (::tp::Conversion<const volatile U*, const volatile void*>::exists &&
|
||||
!::tp::Conversion<const volatile void*, const volatile void*>::sameType &&
|
||||
!::tp::Conversion<const volatile void*, const volatile U*>::sameType)
|
||||
};
|
||||
|
||||
// Dummy enum to make sure that both classes are fully defined.
|
||||
enum { dontUseWithIncompleteTypes = (0 == sizeof(U)) };
|
||||
};
|
||||
|
||||
template<class T>
|
||||
struct SuperSubclassStrict<T, void> {
|
||||
enum {
|
||||
value = (::tp::Conversion<const volatile void*, const volatile T*>::exists &&
|
||||
!::tp::Conversion<const volatile T*, const volatile void*>::sameType &&
|
||||
!::tp::Conversion<const volatile T*, const volatile void*>::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<T,U>::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<T,U>::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 <class T, class U>
|
||||
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<T1, T2, ...>::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<T1, TailResult> Result;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct MakeTypelist<> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class template Length
|
||||
// Computes the length of a typelist
|
||||
// Invocation (TList is a typelist):
|
||||
// Length<TList>::value
|
||||
// returns a compile-time constant containing the length of TList, not counting
|
||||
// the end terminator (which by convention is NullType)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList> struct Length;
|
||||
template <> struct Length<NullType> {
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
struct Length< Typelist<T, U> > {
|
||||
enum { value = 1 + Length<U>::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<TList, index>::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 <class TList, unsigned int index> struct TypeAt;
|
||||
|
||||
template <class Head, class Tail>
|
||||
struct TypeAt<Typelist<Head, Tail>, 0> {
|
||||
typedef Head Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, unsigned int i>
|
||||
struct TypeAt<Typelist<Head, Tail>, i> {
|
||||
typedef typename TypeAt<Tail, i - 1>::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<TList, index>::Result
|
||||
// returns the type in position 'index' in TList, or NullType if index is
|
||||
// out-of-bounds
|
||||
// b) TypeAt<TList, index, D>::Result
|
||||
// returns the type in position 'index' in TList, or D if index is out-of-bounds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, unsigned int index,
|
||||
typename DefaultType = NullType>
|
||||
struct TypeAtNonStrict {
|
||||
typedef DefaultType Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, typename DefaultType>
|
||||
struct TypeAtNonStrict<Typelist<Head, Tail>, 0, DefaultType> {
|
||||
typedef Head Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, unsigned int i, typename DefaultType>
|
||||
struct TypeAtNonStrict<Typelist<Head, Tail>, i, DefaultType> {
|
||||
typedef typename
|
||||
TypeAtNonStrict<Tail, i - 1, DefaultType>::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<TList, T>::value
|
||||
// returns the position of T in TList, or NullType if T is not found in TList
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T> struct IndexOf;
|
||||
|
||||
template <class T>
|
||||
struct IndexOf<NullType, T> {
|
||||
enum { value = -1 };
|
||||
};
|
||||
|
||||
template <class T, class Tail>
|
||||
struct IndexOf<Typelist<T, Tail>, T> {
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template <class Head, class Tail, class T>
|
||||
struct IndexOf<Typelist<Head, Tail>, T> {
|
||||
private:
|
||||
enum { temp = IndexOf<Tail, T>::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<TList, T>::Result
|
||||
// returns a typelist that is TList followed by T and NullType-terminated
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T> struct Append;
|
||||
|
||||
template <> struct Append<NullType, NullType> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class T> struct Append<NullType, T> {
|
||||
typedef Typelist<T, NullType> Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail>
|
||||
struct Append<NullType, Typelist<Head, Tail> > {
|
||||
typedef Typelist<Head, Tail> Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, class T>
|
||||
struct Append<Typelist<Head, Tail>, T> {
|
||||
typedef Typelist<Head,
|
||||
typename Append<Tail, T>::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<TList, T>::Result
|
||||
// returns a typelist that is TList without the first occurence of T
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T> struct Erase;
|
||||
|
||||
template <class T> // Specialization 1
|
||||
struct Erase<NullType, T> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class T, class Tail> // Specialization 2
|
||||
struct Erase<Typelist<T, Tail>, T> {
|
||||
typedef Tail Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, class T> // Specialization 3
|
||||
struct Erase<Typelist<Head, Tail>, T> {
|
||||
typedef Typelist<Head,
|
||||
typename Erase<Tail, T>::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<TList, T>::Result
|
||||
// returns a typelist that is TList without any occurence of T
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T> struct EraseAll;
|
||||
template <class T>
|
||||
struct EraseAll<NullType, T> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
template <class T, class Tail>
|
||||
struct EraseAll<Typelist<T, Tail>, T> {
|
||||
// Go all the way down the list removing the type
|
||||
typedef typename EraseAll<Tail, T>::Result Result;
|
||||
};
|
||||
template <class Head, class Tail, class T>
|
||||
struct EraseAll<Typelist<Head, Tail>, T> {
|
||||
// Go all the way down the list removing the type
|
||||
typedef Typelist<Head,
|
||||
typename EraseAll<Tail, T>::Result>
|
||||
Result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class template NoDuplicates
|
||||
// Removes all duplicate types in a typelist
|
||||
// Invocation (TList is a typelist):
|
||||
// NoDuplicates<TList, T>::Result
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList> struct NoDuplicates;
|
||||
|
||||
template <> struct NoDuplicates<NullType> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail>
|
||||
struct NoDuplicates< Typelist<Head, Tail> > {
|
||||
private:
|
||||
typedef typename NoDuplicates<Tail>::Result L1;
|
||||
typedef typename Erase<L1, Head>::Result L2;
|
||||
public:
|
||||
typedef Typelist<Head, L2> 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<TList, T, U>::Result
|
||||
// returns a typelist in which the first occurence of T is replaced with U
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T, class U> struct Replace;
|
||||
|
||||
template <class T, class U>
|
||||
struct Replace<NullType, T, U> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class T, class Tail, class U>
|
||||
struct Replace<Typelist<T, Tail>, T, U> {
|
||||
typedef Typelist<U, Tail> Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, class T, class U>
|
||||
struct Replace<Typelist<Head, Tail>, T, U> {
|
||||
typedef Typelist<Head,
|
||||
typename Replace<Tail, T, U>::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<TList, T, U>::Result
|
||||
// returns a typelist in which all occurences of T is replaced with U
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T, class U> struct ReplaceAll;
|
||||
|
||||
template <class T, class U>
|
||||
struct ReplaceAll<NullType, T, U> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class T, class Tail, class U>
|
||||
struct ReplaceAll<Typelist<T, Tail>, T, U> {
|
||||
typedef Typelist<U, typename ReplaceAll<Tail, T, U>::Result> Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, class T, class U>
|
||||
struct ReplaceAll<Typelist<Head, Tail>, T, U> {
|
||||
typedef Typelist<Head,
|
||||
typename ReplaceAll<Tail, T, U>::Result>
|
||||
Result;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// class template Reverse
|
||||
// Reverses a typelist
|
||||
// Invocation (TList is a typelist):
|
||||
// Reverse<TList>::Result
|
||||
// returns a typelist that is TList reversed
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList> struct Reverse;
|
||||
|
||||
template <>
|
||||
struct Reverse<NullType> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail>
|
||||
struct Reverse< Typelist<Head, Tail> > {
|
||||
typedef typename Append<
|
||||
typename Reverse<Tail>::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<TList, T>::Result
|
||||
// returns the type in TList that's the most derived from T
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList, class T> struct MostDerived;
|
||||
|
||||
template <class T>
|
||||
struct MostDerived<NullType, T> {
|
||||
typedef T Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail, class T>
|
||||
struct MostDerived<Typelist<Head, Tail>, T> {
|
||||
private:
|
||||
typedef typename MostDerived<Tail, T>::Result Candidate;
|
||||
public:
|
||||
typedef typename TypeSelect<
|
||||
SuperSubclass<Candidate, Head>::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<TList>::Result
|
||||
// returns the reordered TList
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class TList> struct DerivedToFront;
|
||||
|
||||
template <>
|
||||
struct DerivedToFront<NullType> {
|
||||
typedef NullType Result;
|
||||
};
|
||||
|
||||
template <class Head, class Tail>
|
||||
struct DerivedToFront< Typelist<Head, Tail> > {
|
||||
private:
|
||||
typedef typename MostDerived<Tail, Head>::Result
|
||||
TheMostDerived;
|
||||
typedef typename Replace<Tail,
|
||||
TheMostDerived, Head>::Result Temp;
|
||||
typedef typename DerivedToFront<Temp>::Result L;
|
||||
public:
|
||||
typedef Typelist<TheMostDerived, L> 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<T01, TailResult> list;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Seq<> {
|
||||
typedef NullType list;
|
||||
};
|
||||
|
||||
} // namespace tp
|
||||
|
||||
#if 0
|
||||
#define TYPELIST1(T1) ::compiler::Typelist<T1, ::compiler::NullType>
|
||||
#define TYPELIST2(T1, T2) ::compiler::Typelist<T1, TYPELIST1(T2) >
|
||||
#define TYPELIST3(T1, T2, T3) ::compiler::Typelist<T1, TYPELIST2(T2, T3) >
|
||||
#define TYPELIST4(T1, T2, T3, T4) ::compiler::Typelist<T1, TYPELIST3(T2, T3, T4) >
|
||||
#define TYPELIST5(T1, T2, T3, T4, T5) ::compiler::Typelist<T1, TYPELIST4(T2, T3, T4, T5) >
|
||||
#define TYPELIST6(T1, T2, T3, T4, T5, T6) ::compiler::Typelist<T1, TYPELIST5(T2, T3, T4, T5, T6) >
|
||||
#define TYPELIST7(T1, T2, T3, T4, T5, T6, T7) ::compiler::Typelist<T1, TYPELIST6(T2, T3, T4, T5, T6, T7) >
|
||||
#define TYPELIST8(T1, T2, T3, T4, T5, T6, T7, T8) ::compiler::Typelist<T1, TYPELIST7(T2, T3, T4, T5, T6, T7, T8) >
|
||||
#define TYPELIST9(T1, T2, T3, T4, T5, T6, T7, T8, T9) ::compiler::Typelist<T1, TYPELIST8(T2, T3, T4, T5, T6, T7, T8, T9) >
|
||||
#define TYPELIST10(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) ::compiler::Typelist<T1, TYPELIST9(T2, T3, T4, T5, T6, T7, T8, T9, T10) >
|
||||
#endif
|
||||
|
||||
#include <limits>
|
||||
|
||||
#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<T> 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 <typename T>
|
||||
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<T> 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 <typename T>
|
||||
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<T> 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 <typename T>
|
||||
struct IsCustomFloat {
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helper types for class template TypeTraits defined below
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
namespace Private {
|
||||
|
||||
typedef Seq<unsigned char, unsigned short int, unsigned int, unsigned long int>::list StdUnsignedInts;
|
||||
typedef Seq<signed char, short int, int, long int>::list StdSignedInts;
|
||||
typedef Seq<bool, char, wchar_t>::list StdOtherInts;
|
||||
typedef Seq<float, double, long double>::list StdFloats;
|
||||
|
||||
template <typename U> struct AddPointer { typedef U* Result; };
|
||||
template <typename U> struct AddPointer<U&> { typedef U* Result; };
|
||||
|
||||
template <class U> struct AddReference { typedef U& Result; };
|
||||
template <class U> struct AddReference<U&> { typedef U& Result; };
|
||||
template <> struct AddReference<void> { typedef NullType Result; };
|
||||
|
||||
template <class U> struct AddParameterType { typedef const U& Result; };
|
||||
template <class U> struct AddParameterType<U&> { typedef U& Result; };
|
||||
template <> struct AddParameterType<void> { 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<T>::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 <typename T>
|
||||
class TypeTraits {
|
||||
private:
|
||||
|
||||
template <class U> struct ReferenceTraits {
|
||||
enum { result = false };
|
||||
typedef U ReferredType;
|
||||
};
|
||||
|
||||
template <class U> struct ReferenceTraits<U&> {
|
||||
enum { result = true };
|
||||
typedef U ReferredType;
|
||||
};
|
||||
|
||||
template <class U> struct PointerTraits {
|
||||
enum { result = false };
|
||||
typedef NullType PointeeType;
|
||||
};
|
||||
|
||||
template <class U> struct PointerTraits<U*> {
|
||||
enum { result = true };
|
||||
typedef U PointeeType;
|
||||
};
|
||||
|
||||
template <class U> struct PointerTraits<U*&> {
|
||||
enum { result = true };
|
||||
typedef U PointeeType;
|
||||
};
|
||||
|
||||
template <class U> struct PToMTraits {
|
||||
enum { result = false };
|
||||
};
|
||||
|
||||
template <class U, class V> struct PToMTraits<U V::*> {
|
||||
enum { result = true };
|
||||
};
|
||||
|
||||
template <class U, class V> struct PToMTraits<U V::*&> {
|
||||
enum { result = true };
|
||||
};
|
||||
|
||||
template <class U> struct UnConst {
|
||||
typedef U Result;
|
||||
enum { isConst = 0 };
|
||||
};
|
||||
|
||||
template <class U> struct UnConst<const U> {
|
||||
typedef U Result;
|
||||
enum { isConst = 1 };
|
||||
};
|
||||
|
||||
template <class U> struct UnConst<const U&> {
|
||||
typedef U& Result;
|
||||
enum { isConst = 1 };
|
||||
};
|
||||
|
||||
template <class U> struct UnVolatile {
|
||||
typedef U Result;
|
||||
enum { isVolatile = 0 };
|
||||
};
|
||||
|
||||
template <class U> struct UnVolatile<volatile U> {
|
||||
typedef U Result;
|
||||
enum { isVolatile = 1 };
|
||||
};
|
||||
|
||||
template <class U> struct UnVolatile<volatile U&> {
|
||||
typedef U& Result;
|
||||
enum { isVolatile = 1 };
|
||||
};
|
||||
|
||||
public:
|
||||
typedef typename UnConst<T>::Result
|
||||
NonConstType;
|
||||
typedef typename UnVolatile<T>::Result
|
||||
NonVolatileType;
|
||||
typedef typename UnVolatile<typename UnConst<T>::Result>::Result
|
||||
UnqualifiedType;
|
||||
typedef typename PointerTraits<UnqualifiedType>::PointeeType
|
||||
PointeeType;
|
||||
typedef typename ReferenceTraits<T>::ReferredType
|
||||
ReferredType;
|
||||
|
||||
enum { isConst = UnConst<T>::isConst };
|
||||
enum { isVolatile = UnVolatile<T>::isVolatile };
|
||||
enum { isReference = ReferenceTraits<UnqualifiedType>::result };
|
||||
enum { isMemberPointer = PToMTraits<typename ReferenceTraits<UnqualifiedType>::ReferredType >::result };
|
||||
enum { isPointer = PointerTraits<typename ReferenceTraits<UnqualifiedType>::ReferredType >::result };
|
||||
|
||||
enum {
|
||||
isStdUnsignedInt = TL::IndexOf<Private::StdUnsignedInts, UnqualifiedType>::value >= 0 ||
|
||||
TL::IndexOf<Private::StdUnsignedInts,
|
||||
typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0
|
||||
};
|
||||
enum {
|
||||
isStdSignedInt = TL::IndexOf<Private::StdSignedInts, UnqualifiedType>::value >= 0 ||
|
||||
TL::IndexOf<Private::StdSignedInts,
|
||||
typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0
|
||||
};
|
||||
enum {
|
||||
isStdIntegral = isStdUnsignedInt || isStdSignedInt ||
|
||||
TL::IndexOf<Private::StdOtherInts, UnqualifiedType>::value >= 0 ||
|
||||
TL::IndexOf<Private::StdOtherInts,
|
||||
typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0
|
||||
};
|
||||
enum {
|
||||
isStdFloat = TL::IndexOf<Private::StdFloats, UnqualifiedType>::value >= 0 ||
|
||||
TL::IndexOf<Private::StdFloats,
|
||||
typename ReferenceTraits<UnqualifiedType>::ReferredType>::value >= 0
|
||||
};
|
||||
|
||||
enum { isStdArith = isStdIntegral || isStdFloat };
|
||||
enum { isStdFundamental = isStdArith || isStdFloat || Conversion<T, void>::sameType };
|
||||
|
||||
enum { isUnsignedInt = isStdUnsignedInt || IsCustomUnsignedInt<UnqualifiedType>::value };
|
||||
enum { isSignedInt = isStdSignedInt || IsCustomSignedInt<UnqualifiedType>::value };
|
||||
enum { isIntegral = isStdIntegral || isUnsignedInt || isSignedInt };
|
||||
enum { isFloat = isStdFloat || IsCustomFloat<UnqualifiedType>::value };
|
||||
enum { isArith = isIntegral || isFloat };
|
||||
enum { isFundamental = isStdFundamental || isArith };
|
||||
|
||||
typedef typename TypeSelect<isStdArith || isPointer || isMemberPointer, T, typename Private::AddParameterType<T>::Result>::Result
|
||||
ParameterType;
|
||||
};
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning( pop )
|
||||
#endif // _MSC_VER
|
||||
15
BaseModule/tests/Tests.cpp
Normal file
15
BaseModule/tests/Tests.cpp
Normal file
|
|
@ -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();
|
||||
}
|
||||
16
CMakeLists.txt
Normal file
16
CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
22
Containers/CMakeLists.txt
Normal file
22
Containers/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
18
Containers/private/Containers.cpp
Normal file
18
Containers/private/Containers.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
#include "ContainersCommon.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
360
Containers/public/AvlTree.hpp
Normal file
360
Containers/public/AvlTree.hpp
Normal file
|
|
@ -0,0 +1,360 @@
|
|||
#pragma once
|
||||
|
||||
#include "ContainersCommon.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
template <typename NumericType>
|
||||
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 <typename Key, typename Data, class Allocator = DefaultAllocator>
|
||||
class AvlTree {
|
||||
typedef SelCopyArg<Key> KeyArg;
|
||||
typedef SelCopyArg<Data> 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; }
|
||||
};
|
||||
}
|
||||
26
Containers/public/ContainersCommon.hpp
Normal file
26
Containers/public/ContainersCommon.hpp
Normal file
|
|
@ -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<typename Type>
|
||||
static void write(const Type&) {}
|
||||
|
||||
template<typename Type>
|
||||
static void read(Type&) {}
|
||||
};
|
||||
}
|
||||
322
Containers/public/List.hpp
Normal file
322
Containers/public/List.hpp
Normal file
|
|
@ -0,0 +1,322 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ContainersCommon.hpp"
|
||||
|
||||
#include "TypeInfo.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
template <typename Type, class Allocator = DefaultAllocator>
|
||||
class List {
|
||||
|
||||
typedef SelCopyArg<Type> 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<TypeTraits<Type>::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<Type>& 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<Type>& 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<Type>& 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 <typename compare_val>
|
||||
[[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<class Saver>
|
||||
void write(Saver& file) const {
|
||||
file.write(mLength);
|
||||
for (auto item : *this) {
|
||||
file.write(item.data());
|
||||
}
|
||||
}
|
||||
|
||||
template<class Loader>
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
397
Containers/public/Map.hpp
Normal file
397
Containers/public/Map.hpp
Normal file
|
|
@ -0,0 +1,397 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "ContainersCommon.hpp"
|
||||
#include "Common.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
template<typename Key>
|
||||
ualni DefaultHashFunc(SelCopyArg<Key> key) {
|
||||
return hash(key);
|
||||
}
|
||||
|
||||
template<
|
||||
typename tKey, typename tVal,
|
||||
class tAllocator = DefaultAllocator,
|
||||
ualni(*tHashFunc)(SelCopyArg<tKey>) = DefaultHashFunc<tKey>,
|
||||
int tTableInitialSize = 4
|
||||
>
|
||||
class Map {
|
||||
|
||||
enum {
|
||||
MAP_PERTURB_SHIFT = 5,
|
||||
MAP_MIN_SIZE = 4,
|
||||
MAP_MAX_LOAD_PERCENTAGE = 66,
|
||||
};
|
||||
|
||||
typedef SelCopyArg<tKey> KeyArg;
|
||||
typedef SelCopyArg<tVal> 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<class Saver>
|
||||
void write(Saver& file) {
|
||||
file.write(mNEntries);
|
||||
for (auto item : *this) {
|
||||
file.write(item->val);
|
||||
file.write(item->key);
|
||||
}
|
||||
}
|
||||
|
||||
template<class Loader>
|
||||
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(); }
|
||||
};
|
||||
}
|
||||
31
Containers/tests/AvlTest.cpp
Normal file
31
Containers/tests/AvlTest.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
#include "Tests.hpp"
|
||||
|
||||
#include "AvlTree.hpp"
|
||||
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
TEST_DEF_STATIC(Simple) {
|
||||
AvlTree<AvlNumericKey<alni>, 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();
|
||||
}
|
||||
88
Containers/tests/ListTest.cpp
Normal file
88
Containers/tests/ListTest.cpp
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
|
||||
#include "Tests.hpp"
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
TEST_DEF_STATIC(SimpleReference) {
|
||||
tp::List<TestClass, TestAllocator> 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<TestClass*, TestAllocator> 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<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
|
||||
tp::List<TestClass, TestAllocator> 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<TestClass, TestAllocator> 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();
|
||||
}
|
||||
137
Containers/tests/MapTest.cpp
Normal file
137
Containers/tests/MapTest.cpp
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
|
||||
#include "Tests.hpp"
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include "Map.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace tp;
|
||||
|
||||
TEST_DEF_STATIC(SimpleReference) {
|
||||
tp::Map<tp::ualni, TestClass, TestAllocator> 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<tp::ualni, TestClass*, TestAllocator> 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<tp::ualni, TestClass, TestAllocator> map;
|
||||
|
||||
for (auto i : Range(10)) {
|
||||
map.put(i, TestClass(i));
|
||||
}
|
||||
|
||||
tp::Map<tp::ualni, TestClass, TestAllocator> 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<tp::ualni, TestClass, TestAllocator> 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();
|
||||
}
|
||||
41
Containers/tests/Tests.cpp
Normal file
41
Containers/tests/Tests.cpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
#include "Tests.hpp"
|
||||
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
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();
|
||||
}
|
||||
73
Containers/tests/Tests.hpp
Normal file
73
Containers/tests/Tests.hpp
Normal file
|
|
@ -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<class Saver>
|
||||
void write(Saver& file) const {
|
||||
file.write(val1);
|
||||
}
|
||||
|
||||
template<class Loader>
|
||||
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<typename Type>
|
||||
void write(const Type& val) {
|
||||
val.write(*this);
|
||||
}
|
||||
|
||||
template<>
|
||||
void write<tp::ualni>(const tp::ualni& val) {
|
||||
mem[address] = val;
|
||||
address++;
|
||||
}
|
||||
|
||||
void setAddress(tp::ualni addr) { address = addr; }
|
||||
|
||||
template<typename Type>
|
||||
void read(Type& val) {
|
||||
val.read(*this);
|
||||
}
|
||||
|
||||
template<>
|
||||
void read<tp::ualni>(tp::ualni& val) {
|
||||
val = mem[address];
|
||||
address++;
|
||||
}
|
||||
};
|
||||
|
||||
void testList();
|
||||
void testMap();
|
||||
void testAvl();
|
||||
1
README.MD
Normal file
1
README.MD
Normal file
|
|
@ -0,0 +1 @@
|
|||
Modules
|
||||
24
Utils/CMakeLists.txt
Normal file
24
Utils/CMakeLists.txt
Normal file
|
|
@ -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)
|
||||
207
Utils/private/Debugging.cpp
Normal file
207
Utils/private/Debugging.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
|
||||
#include "Debugging.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
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 <malloc.h>
|
||||
#include <execinfo.h>
|
||||
#include <cstring>
|
||||
#include <cxxabi.h>
|
||||
|
||||
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
|
||||
73
Utils/private/Testing.cpp
Normal file
73
Utils/private/Testing.cpp
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
#include "Testing.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
81
Utils/private/Timing.cpp
Normal file
81
Utils/private/Timing.cpp
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
#include "Timing.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
#define GETTIMEMSC() \
|
||||
(time_ms)( \
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>( \
|
||||
std::chrono::time_point_cast<std::chrono::milliseconds>(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);
|
||||
}
|
||||
}
|
||||
94
Utils/private/Utils.cpp
Normal file
94
Utils/private/Utils.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
|
||||
#include "Utils.hpp"
|
||||
|
||||
#include "ContainersCommon.hpp"
|
||||
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
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<alnf>(std::rand()) / static_cast<alnf>(RAND_MAX);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
122
Utils/public/Debugging.hpp
Normal file
122
Utils/public/Debugging.hpp
Normal file
|
|
@ -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<class Saver>
|
||||
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<class Loader>
|
||||
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<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots;
|
||||
Map<FramePointer, DebugSymbols> mSymbols;
|
||||
};
|
||||
|
||||
extern CallStackCapture* gCSCapture;
|
||||
}
|
||||
97
Utils/public/Sorting.hpp
Normal file
97
Utils/public/Sorting.hpp
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace tp {
|
||||
|
||||
template <typename Type>
|
||||
inline bool compare(const Type& val1, const Type& val2) {
|
||||
return val1 > val2;
|
||||
}
|
||||
|
||||
struct SortMerge {
|
||||
|
||||
template <typename Type>
|
||||
static void sort(Type* buff, int length, bool (*grater)(const Type& obj1, const Type& obj2) = &compare) {
|
||||
mergeSort(buff, 0, length - 1, grater);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
template <typename Type>
|
||||
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 <typename Type>
|
||||
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 <typename Type>
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
63
Utils/public/Testing.hpp
Normal file
63
Utils/public/Testing.hpp
Normal file
|
|
@ -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<FailedCheck> mFailedChecks;
|
||||
List<TestingNode*> 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__ })
|
||||
58
Utils/public/Timing.hpp
Normal file
58
Utils/public/Timing.hpp
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
79
Utils/public/Utils.hpp
Normal file
79
Utils/public/Utils.hpp
Normal file
|
|
@ -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 <typename T1, typename T2>
|
||||
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 <typename Type = alni>
|
||||
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<typename tType = ualni>
|
||||
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); }
|
||||
};
|
||||
}
|
||||
67
Utils/tests/Tests.cpp
Normal file
67
Utils/tests/Tests.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
|
||||
#include "Utils.hpp"
|
||||
#include "Debugging.hpp"
|
||||
#include "Testing.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
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();
|
||||
}
|
||||
|
|
@ -1,186 +0,0 @@
|
|||
|
||||
#include "StackTrace.hpp"
|
||||
#include "PrivateConfig.hpp"
|
||||
|
||||
#include <Windows.h>
|
||||
#include <DbgHelp.h>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
|
||||
#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) {
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -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);
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#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 <tp::alni size>
|
||||
struct allocator_test {
|
||||
test_struct data[size];
|
||||
bool is_allocated[size];
|
||||
tp::alni n_loaded = 0;
|
||||
|
||||
test_struct* allocations[size];
|
||||
|
||||
tp::AbstractAllocator* alloc;
|
||||
tp::AbstractAllocator* parent_alloc;
|
||||
const char* allocator_name = NULL;
|
||||
|
||||
tp::alni rand_idx(bool state) {
|
||||
RAND:
|
||||
|
||||
tp::alni idx = (tp::alni)(tp::randf() * (size + 1));
|
||||
CLAMP(idx, 0, size - 1);
|
||||
|
||||
if (state == is_allocated[idx]) {
|
||||
goto RAND;
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
allocator_test(tp::AbstractAllocator* palloc, const char* pallocator_name,
|
||||
tp::AbstractAllocator* p_parent_alloc) {
|
||||
allocator_name = pallocator_name;
|
||||
parent_alloc = p_parent_alloc;
|
||||
alloc = palloc;
|
||||
for (tp::alni i = 0; i < size; i++) {
|
||||
RAND:
|
||||
tp::alni val = tp::alni(tp::randf() * (size + 100.f));
|
||||
for (tp::alni check_idx = 0; check_idx < size; check_idx++) {
|
||||
if (data[check_idx].val == val) {
|
||||
goto RAND;
|
||||
}
|
||||
}
|
||||
|
||||
data[i].val = val;
|
||||
is_allocated[i] = false;
|
||||
allocations[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void verify_integrity() {
|
||||
// verify data integrity
|
||||
for (tp::alni i = 0; i < size; i++) {
|
||||
if (is_allocated[i]) {
|
||||
assert(*allocations[i] == data[i] && "data is currupted\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (alloc->isWrapSupport()) assert(!alloc->isWrapCorrupted());
|
||||
if (parent_alloc && parent_alloc->isWrapSupport())
|
||||
assert(!parent_alloc->isWrapCorrupted());
|
||||
|
||||
verify_sizes();
|
||||
}
|
||||
|
||||
void verify_sizes() {
|
||||
return;
|
||||
#ifdef MEM_TRACE
|
||||
assert(alloc->sizeInuse() == n_loaded * sizeof(test_struct) &&
|
||||
"invalid inuse size\n");
|
||||
assert(alloc->sizeReserved() >= n_loaded * (tp::alni)sizeof(test_struct) &&
|
||||
"invalid reserved size\n");
|
||||
#endif
|
||||
}
|
||||
|
||||
void load_item(tp::alni idx) {
|
||||
if (!is_allocated[idx]) {
|
||||
allocations[idx] = new (alloc) test_struct();
|
||||
|
||||
assert(allocations[idx] && "allocator returned NULL");
|
||||
|
||||
allocations[idx]->val = data[idx].val;
|
||||
is_allocated[idx] = true;
|
||||
n_loaded++;
|
||||
verify_integrity();
|
||||
}
|
||||
}
|
||||
|
||||
void unload_item(tp::alni idx) {
|
||||
if (is_allocated[idx]) {
|
||||
verify_integrity();
|
||||
delete allocations[idx];
|
||||
is_allocated[idx] = false;
|
||||
n_loaded--;
|
||||
verify_integrity();
|
||||
}
|
||||
}
|
||||
|
||||
void change_states(tp::Range<tp::alni> rg, bool state, bool reversed = false,
|
||||
bool random = false) {
|
||||
for (auto i : rg) {
|
||||
tp::alni idx = i;
|
||||
|
||||
if (random) {
|
||||
idx = rand_idx(state);
|
||||
}
|
||||
else if (reversed) {
|
||||
idx = size - i - 1;
|
||||
}
|
||||
|
||||
(state) ? load_item(idx) : unload_item(idx);
|
||||
}
|
||||
}
|
||||
|
||||
// full down-up load then up-down unload
|
||||
void test1() {
|
||||
change_states({ 0, size }, 1);
|
||||
change_states({ 0, size }, 0, true);
|
||||
}
|
||||
|
||||
// full down-up load then down-up unload
|
||||
void test2() {
|
||||
change_states({ 0, size }, 1);
|
||||
change_states({ 0, size }, 0);
|
||||
}
|
||||
|
||||
// full random load then random unload
|
||||
void test3() {
|
||||
change_states({ 0, size }, 1, 0, 1);
|
||||
change_states({ 0, size }, 0, 0, 1);
|
||||
}
|
||||
|
||||
// multipul tests 1-3
|
||||
void test4() {
|
||||
test1();
|
||||
test1();
|
||||
|
||||
test2();
|
||||
test2();
|
||||
|
||||
test3();
|
||||
test3();
|
||||
}
|
||||
|
||||
static tp::alnf sineupf(tp::alnf asize, tp::alnf x, bool reverse) {
|
||||
tp::alnf end = 4 * PI;
|
||||
tp::alnf a = (2 / 7.f) * asize;
|
||||
tp::alnf b = end / asize;
|
||||
|
||||
tp::alni c = ((-1 * reverse) + (1 * !reverse));
|
||||
tp::alnf c1 = (x - (end * reverse)) / b;
|
||||
tp::alnf c2 = (a * sin(x - (end * reverse)));
|
||||
tp::alnf out = c1 + c2;
|
||||
return c * out;
|
||||
}
|
||||
|
||||
// sin load & sin unload with ~1/2 drop factor
|
||||
void test5() {
|
||||
tp::alnf end = 4 * PI;
|
||||
tp::alnf step = end / 4.f;
|
||||
|
||||
for (char i = 0; i < 2; i++) {
|
||||
for (tp::alnf x = 0; x <= end; x += step) {
|
||||
tp::alni target_alloc_count = (tp::alni)ceil(sineupf(size, x, i));
|
||||
CLAMP(target_alloc_count, 0, size);
|
||||
|
||||
while (n_loaded > target_alloc_count) {
|
||||
unload_item(rand_idx(0));
|
||||
}
|
||||
while (n_loaded < target_alloc_count) {
|
||||
load_item(rand_idx(1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MEM_WRAP
|
||||
void check_wrap(tp::alni offset, bool after) {
|
||||
CLAMP(offset, 1, WRAP_LEN);
|
||||
|
||||
test_struct* ts = allocations[rand_idx(0)];
|
||||
tp::alni shift = (sizeof(test_struct) * after) + (offset - 1) * after -
|
||||
offset * (!after);
|
||||
tp::uint1* address = (((tp::uint1*)ts) + shift);
|
||||
|
||||
tp::uint1 val = *address;
|
||||
*address = 5;
|
||||
assert(alloc->isWrapCorrupted());
|
||||
*address = val;
|
||||
}
|
||||
#endif
|
||||
|
||||
// mem guards test
|
||||
void test6() {
|
||||
change_states({ 0, size }, 1);
|
||||
|
||||
#ifdef MEM_WRAP
|
||||
for (tp::alni after = 0; after < 2; after++) {
|
||||
for (tp::alni offset = 1; offset <= WRAP_LEN; offset++) {
|
||||
check_wrap(offset, after);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
change_states({ 0, size }, 0);
|
||||
}
|
||||
|
||||
void run_tests() {
|
||||
try {
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
test4();
|
||||
test5();
|
||||
if (alloc->isWrapSupport()) {
|
||||
test6();
|
||||
}
|
||||
|
||||
printf("%s - passed\n", allocator_name);
|
||||
if (!alloc->isWrapSupport()) {
|
||||
printf(" WARNING: %s has no wrap support!! \n", allocator_name);
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
printf("%s - failed\n", allocator_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void heap_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
allocator_test<150> hatest(tp::heapalloc, "heap allocator", NULL);
|
||||
hatest.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("heap alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void chunk_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
tp::ChunkAlloc calloc(sizeof(test_struct), 50);
|
||||
allocator_test<50> ca_test(&calloc, "chunk allocator", tp::heapalloc);
|
||||
ca_test.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("chunk alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void pool_alloc_test() {
|
||||
tp::alloc_init(); {
|
||||
try {
|
||||
tp::PoolAlloc palloc(sizeof(test_struct), 50);
|
||||
allocator_test<150> pa_test(&palloc, "pool allocator", tp::heapalloc);
|
||||
pa_test.run_tests();
|
||||
}
|
||||
catch (...) {
|
||||
printf("chunk alloc failed\n");
|
||||
}
|
||||
} tp::alloc_uninit();
|
||||
}
|
||||
|
||||
void allocators_test() {
|
||||
|
||||
printf("running tests on alocators:\n");
|
||||
|
||||
heap_alloc_test();
|
||||
chunk_alloc_test();
|
||||
pool_alloc_test();
|
||||
}
|
||||
|
||||
|
||||
CROSSPLATFORM_MAIN;
|
||||
int main(int argc, char* argv[]) {
|
||||
tp::print_env_info();
|
||||
allocators_test();
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
void allocators_test();
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
|
||||
|
||||
#include "allocators.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#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();
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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<allocator_histogram*> out;
|
||||
pattern_histogram* pattern_out;
|
||||
|
||||
tp::alni i_count;
|
||||
tp::alnf* x_axis = NULL;
|
||||
|
||||
tp::HashMap<pattern*, tp::string> 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);
|
||||
};
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
|
||||
#include "collector.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
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<std::chrono::nanoseconds>(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<std::chrono::nanoseconds>(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<tp::alnf> alloc_size;
|
||||
tp::Array<tp::alnf> data_idx;
|
||||
tp::Array<tp::alnf> items_loaded;
|
||||
|
||||
pattern_histogram(test_pattern* pt);
|
||||
};
|
||||
|
||||
struct allocator_histogram {
|
||||
const char* alloc_type;
|
||||
|
||||
tp::alnf total_time;
|
||||
tp::Array<tp::alnf> time;
|
||||
tp::Array<tp::alnf> mem;
|
||||
bool failed;
|
||||
|
||||
tp::Array<tp::uint1*> 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);
|
||||
|
|
@ -1,174 +0,0 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "strings.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#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<child_pattern> regions;
|
||||
bool build_in = true;
|
||||
|
||||
pattern() {
|
||||
}
|
||||
|
||||
tp::alnf get_y(tp::HashMap<pattern*, tp::string>* 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<pattern*, tp::string>* 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<pattern*, tp::string>* 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; }
|
||||
};
|
||||
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -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<LevelInfo>& 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<LevelInfo>& 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<alni>(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<alni>(&leaks_len);
|
||||
|
||||
leaks.reserve(leaks_len);
|
||||
|
||||
Frame frame;
|
||||
|
||||
for (alni idx = 0; idx < leaks_len; idx++) {
|
||||
|
||||
tp::alni frames_len;
|
||||
log.read<tp::alni>(&frames_len);
|
||||
|
||||
leaks[idx].reserve(frames_len);
|
||||
|
||||
for (alni frame_idx = 0; frame_idx < frames_len; frame_idx++) {
|
||||
|
||||
FrameId id;
|
||||
|
||||
|
||||
log.read<tp::ualni>(&id);
|
||||
leaks[idx][frame_idx] = id;
|
||||
|
||||
//if (frames.presents(id)) {
|
||||
//continue;
|
||||
//}
|
||||
|
||||
frame.name.load(&log);
|
||||
frame.file.load(&log);
|
||||
log.read<tp::ualni>(&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;
|
||||
}
|
||||
|
|
@ -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<FrameId> 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<CallerInfo, FrameId> 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<Frame, FrameId> frames;
|
||||
tp::Array<MemLeak> leaks;
|
||||
|
||||
struct Connection {
|
||||
Frame* caller = NULL;
|
||||
Frame* target = NULL;
|
||||
tp::ualni count = 0;
|
||||
};
|
||||
|
||||
tp::Array<Connection> 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<LevelInfo> 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<LevelInfo>& levels);
|
||||
void apply_position(Frame& frame, tp::Array<LevelInfo>& 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);
|
||||
};
|
||||
};
|
||||
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -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 <stdio.h>
|
||||
|
||||
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<tp::alnf> size;
|
||||
tp::Array<tp::alnf> time;
|
||||
tp::ualni peak = 0;
|
||||
tp::ualni avereging = 0;
|
||||
|
||||
void recordEvents(tp::Array<Event>& 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<Event> events;
|
||||
|
||||
tp::ualni flag = 0;
|
||||
|
||||
Timeline timeline;
|
||||
|
||||
Group(tp::ualni grp_size) : group_size(grp_size) {}
|
||||
};
|
||||
|
||||
tp::HashMap<Group*, SizeKey> groups;
|
||||
tp::Array<Group*> 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();
|
||||
}
|
||||
|
|
@ -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 <stdio.h>
|
||||
|
||||
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<tp::alnf> size;
|
||||
tp::Array<tp::alnf> time;
|
||||
tp::ualni peak = 0;
|
||||
tp::ualni avereging = 0;
|
||||
|
||||
void recordEvents(tp::Array<Event>& 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<Event> events;
|
||||
|
||||
tp::ualni flag = 0;
|
||||
|
||||
Timeline timeline;
|
||||
|
||||
Group(tp::ualni grp_size) : group_size(grp_size) {}
|
||||
};
|
||||
|
||||
tp::HashMap<Group*, SizeKey> groups;
|
||||
tp::Array<Group*> 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();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue