diff --git a/.clang-format b/.clang-format
index 4d990a5..7f8e865 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,66 +1,211 @@
-# Generated from CLion C/C++ Code Style settings
+---
BasedOnStyle: LLVM
AccessModifierOffset: -2
-AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: None
+AlignAfterOpenBracket: BlockIndent
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveBitFields:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignEscapedNewlines: Left
AlignOperands: Align
-AllowAllArgumentsOnNextLine: false
-AllowAllConstructorInitializersOnNextLine: false
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortBlocksOnASingleLine: Always
-AllowShortCaseLabelsOnASingleLine: false
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: Always
+AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
-AllowShortLoopsOnASingleLine: true
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
-BreakBeforeBraces: Custom
+AttributeMacros:
+ - __capability
+BinPackArguments: false
+BinPackParameters: false
+BitFieldColonSpacing: Both
BraceWrapping:
- AfterCaseLabel: false
- AfterClass: false
- AfterControlStatement: Never
+ AfterCaseLabel: true
+ AfterClass: true
+ AfterControlStatement: Always
AfterEnum: false
AfterFunction: false
AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
AfterUnion: false
+ AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
IndentBraces: false
- SplitEmptyFunction: false
+ SplitEmptyFunction: true
SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakAfterJavaFieldAnnotations: false
BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeConceptDeclarations: Always
BreakBeforeTernaryOperators: true
-BreakConstructorInitializers: BeforeColon
+BreakConstructorInitializers: AfterColon
BreakInheritanceList: BeforeColon
-ColumnLimit: 0
+BreakStringLiterals: true
+ColumnLimit: 120
+CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
-ContinuationIndentWidth: 4
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: false
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: ^"(llvm|llvm-c|clang|clang-c)/
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: ^(<|"(gtest|gmock|isl|json)/)
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: .*
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: (Test)?$
+IncludeIsMainSourceRegex: ""
+IndentAccessModifiers: false
+IndentCaseBlocks: true
IndentCaseLabels: true
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
IndentPPDirectives: None
+IndentRequiresClause: true
IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertBraces: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
-MaxEmptyLinesToKeep: 2
+LambdaBodyIndentation: Signature
+Language: Cpp
+MacroBlockBegin: ""
+MacroBlockEnd: ""
+MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
+PPIndentWidth: -1
+PackConstructorInitializers: Never
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
+PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
-ReflowComments: false
+QualifierAlignment: Leave
+ReferenceAlignment: Pointer
+ReflowComments: true
+RemoveBracesLLVM: false
+RequiresClausePosition: OwnLine
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: Never
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: false
+SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
-SpaceBeforeRangeBasedForLoopColon: false
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDeclarationName: false
+ AfterFunctionDefinitionName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ AfterRequiresInClause: false
+ AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 0
-SpacesInAngles: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
SpacesInCStyleCastParentheses: false
-SpacesInContainerLiterals: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
TabWidth: 2
-UseTab: Always
+UseCRLF: false
+UseTab: ForContinuationAndIndentation
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
\ No newline at end of file
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..7891ac7
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.docs/Designs/Language.drawio b/.docs/Designs/Language.drawio
new file mode 100644
index 0000000..88238fd
--- /dev/null
+++ b/.docs/Designs/Language.drawio
@@ -0,0 +1,428 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/workflows/WindowsPlatform.yml b/.github/workflows/WindowsPlatform.yml
new file mode 100644
index 0000000..760ad45
--- /dev/null
+++ b/.github/workflows/WindowsPlatform.yml
@@ -0,0 +1,17 @@
+name: Windows
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+ runs-on: self-hosted
+
+ steps:
+ - uses: actions/checkout@v3
+
+ #- name: Build
+ #run: echo hello
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
index 0e85c3d..4041169 100644
--- a/.github/workflows/cmake.yml
+++ b/.github/workflows/cmake.yml
@@ -18,25 +18,47 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - 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: Setup
+ shell: bash
+ run: |
+ # If your submodules are configured to use SSH instead of HTTPS please uncomment the following line
+ # git config --global url."https://github.com/".insteadOf "git@github.com:"
+ auth_header="$(git config --local --get http.https://github.com/.extraheader)"
- - name: Configure CMake
- run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+ git submodule sync --recursive
+ git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
+
+ sudo apt-get update
+ sudo apt-get install python3 python-is-python3
+ sudo apt-get install -y libx11-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
+ sudo apt-get install -y clang-15
+ sudo apt-get install -y libasound2-dev libglew-dev
+ sudo apt-get install -y portaudio19-dev
+ #cd Externals/glew/
+ #make extensions
+ sudo apt update
+
+ #- 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: |
+ gcc -v
+ clang++-15 -v
+ CC=clang-15 CXX=clang++-15 cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+ - name: Build
+ run: CC=clang CXX=clang++ cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -v -j
+
+ - 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 -j -C ${{env.BUILD_TYPE}}
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- - name: Test
- working-directory: ${{github.workspace}}/build
- # Execute tests defined by the CMake configuration.
- # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
- run: ctest -C ${{env.BUILD_TYPE}}
diff --git a/.gitignore b/.gitignore
index b0247cb..5512493 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,7 @@ build*
lib
install
.vscode
+out
+.vs
+*.bkp
+*.$*
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9d1d1de
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,21 @@
+[submodule "Externals/imgui"]
+ path = Externals/imgui
+ url = https://github.com/elushaX/imgui.git
+[submodule "Externals/lua"]
+ path = Externals/lua
+ url = https://github.com/elushaX/lua.git
+[submodule "Externals/unittest-cpp"]
+ path = Externals/unittest-cpp
+ url = https://github.com/elushaX/unittest-cpp.git
+[submodule "Externals/glfw"]
+ path = Externals/glfw
+ url = https://github.com/elushaX/glfw.git
+[submodule "Externals/nanovg"]
+ path = Externals/nanovg
+ url = https://github.com/elushaX/nanovg.git
+[submodule "Externals/lalr"]
+ path = Externals/lalr
+ url = https://github.com/elushaX/lalr.git
+[submodule "Externals/asio"]
+ path = Externals/asio
+ url = https://github.com/elushaX/asio.git
diff --git a/.project b/.project
new file mode 100644
index 0000000..4dcf3c3
--- /dev/null
+++ b/.project
@@ -0,0 +1,20 @@
+
+
+ Modules
+
+
+
+
+
+ org.eclipse.cdt.core.cBuilder
+ clean,full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.core.ccnature
+ org.eclipse.cdt.cmake.core.cmakeNature
+
+
diff --git a/.wip/Language/CMakeLists.txt b/.wip/Language/CMakeLists.txt
new file mode 100644
index 0000000..2ac316c
--- /dev/null
+++ b/.wip/Language/CMakeLists.txt
@@ -0,0 +1,20 @@
+
+cmake_minimum_required(VERSION 3.2)
+
+set(CMAKE_CXX_STANDARD 23)
+
+project(Language)
+
+### ---------------------- Static Library --------------------- ###
+file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
+file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
+add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
+target_include_directories(${PROJECT_NAME} PUBLIC public/)
+target_link_libraries(${PROJECT_NAME} PUBLIC Strings)
+
+### -------------------------- Tests -------------------------- ###
+enable_testing()
+file(GLOB TEST_SOURCES "./tests/*.cpp")
+add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
+target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} UnitTest++)
+add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
\ No newline at end of file
diff --git a/.wip/Language/private/Grammar.cpp b/.wip/Language/private/Grammar.cpp
new file mode 100644
index 0000000..d6acecf
--- /dev/null
+++ b/.wip/Language/private/Grammar.cpp
@@ -0,0 +1,36 @@
+
+#include "Grammar.hpp"
+
+using namespace tp;
+
+ContextFreeGrammar::Arg::Arg(const String& id, bool terminal, bool epsilon) {
+ mId = id;
+ mIsTerminal = terminal;
+ mIsEpsilon = epsilon;
+}
+
+const String& ContextFreeGrammar::Arg::getId() const { return mId; }
+
+bool ContextFreeGrammar::Arg::operator==(const Arg& in) const {
+ return (mId == in.mId) && (mIsEpsilon == in.mIsEpsilon) && (mIsTerminal == in.mIsTerminal);
+}
+
+ContextFreeGrammar::Rule::Rule(const String& id, const InitialierList& args) {
+ mId = id;
+ mArgs = args;
+}
+
+bool ContextFreeGrammar::Rule::operator==(const Rule& in) const { return (mId == in.mId) && (mArgs == in.mArgs); }
+
+bool ContextFreeGrammar::Rule::isProductive() const {
+ for (auto arg : mArgs) {
+ if (arg->getId() == mId) return false;
+ }
+ return true;
+}
+
+void ContextFreeGrammar::addRule(const Rule& rule) { mRules.append(rule); }
+
+void ContextFreeGrammar::addRule(const String& id, const InitialierList& args) { addRule(Rule(id, args)); }
+
+void ContextFreeGrammar::setStart(const String& startRule) { mStartTerminal = startRule; }
\ No newline at end of file
diff --git a/.wip/Language/private/LanguageCommon.cpp b/.wip/Language/private/LanguageCommon.cpp
new file mode 100644
index 0000000..147536d
--- /dev/null
+++ b/.wip/Language/private/LanguageCommon.cpp
@@ -0,0 +1,8 @@
+
+#include "LanguageCommon.hpp"
+#include "Strings.hpp"
+
+using namespace tp;
+
+static ModuleManifest* sModuleDependencies[] = { &gModuleStrings, nullptr };
+ModuleManifest tp::gModuleLanguage = ModuleManifest("Language", nullptr, nullptr, sModuleDependencies);
diff --git a/.wip/Language/public/Automata.hpp b/.wip/Language/public/Automata.hpp
new file mode 100644
index 0000000..0e059b9
--- /dev/null
+++ b/.wip/Language/public/Automata.hpp
@@ -0,0 +1,249 @@
+
+#pragma once
+
+#include "Utils.hpp"
+#include "List.hpp"
+#include "Map.hpp"
+#include "Tree.hpp"
+
+namespace tp {
+
+ // Non-Deterministic Finite-State Automata
+ template
+ class FiniteStateAutomation {
+ public:
+ struct State;
+
+ public:
+ class Transition {
+ friend FiniteStateAutomation;
+
+ public:
+ enum Type { ANY, EPSILON, SYMBOL };
+
+ public:
+ Transition(Type type, State* state, tAlphabetType symbol = tAlphabetType()) {
+ mState = state;
+ mType = type;
+ mSymbol = symbol;
+ }
+
+ [[nodiscard]] bool isTransition(const tAlphabetType& symbol) const {
+ return (mType == ANY || mType == EPSILON) || (mSymbol == symbol);
+ }
+
+ [[nodiscard]] bool doesConsumes(const tAlphabetType& symbol) const {
+ return (mType == ANY || (mType == SYMBOL && mSymbol == symbol));
+ }
+
+ [[nodiscard]] bool isEpsilon() const { return mType == EPSILON; }
+
+ const State* getState() const { return mState; }
+ const tAlphabetType& getSymbol() const { return mSymbol; }
+
+ private:
+ State* mState = nullptr;
+ Type mType;
+ tAlphabetType mSymbol;
+ };
+
+ class State {
+ friend FiniteStateAutomation;
+
+ public:
+ State() = default;
+
+ public:
+ void setValue(const tStateType& stateValue) { mStateVal = stateValue; }
+ void setAcceptance(bool isAccepting) { mIsAccepting = isAccepting; }
+ [[nodiscard]] bool isAccepting() const { return mIsAccepting; }
+ const tStateType& getStateVal() const { return mStateVal; }
+ [[nodiscard]] const Buffer* getTransitions() const { return &mTransitions; }
+
+ private:
+ Buffer mTransitions{};
+ tStateType mStateVal = tStateType();
+ bool mIsAccepting = false;
+ };
+
+ private:
+ List mStates;
+ State* mStartState = nullptr;
+ Range mAlphabetRange = { ENV_UALNI_MAX, ENV_UALNI_MIN };
+
+ public:
+ FiniteStateAutomation() = default;
+
+ State* addState(const tStateType& state, bool accepting) {
+ auto node = mStates.newNode();
+ node->data.mIsAccepting = accepting;
+ node->data.mStateVal = state;
+ mStates.pushBack(node);
+ return &node->data;
+ }
+
+ void addTransition(State* from, State* to, const tAlphabetType& symbol) {
+ from->mTransitions.append(Transition(Transition::SYMBOL, to, symbol));
+ if (mAlphabetRange.mBegin < ualni(symbol)) mAlphabetRange.mBegin = ualni(symbol);
+ if (mAlphabetRange.mEnd > ualni(symbol)) mAlphabetRange.mEnd = ualni(symbol);
+ }
+
+ void addEpsilonTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::SYMBOL, to)); }
+
+ void addAnyTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::ANY, to)); }
+
+ void setStartState(State* start) { mStartState = start; }
+
+ [[nodiscard]] State* getStartState() const { return mStartState; }
+
+ [[nodiscard]] bool isValid() const {
+ if (!mStartState) {
+ return false;
+ }
+ return true;
+ }
+
+ [[nodiscard]] ualni numStates() const { return mStates.length(); }
+
+ [[nodiscard]] const List* getStates() const { return &mStates; }
+
+ [[nodiscard]] Range getAlphabetRange() const { return mAlphabetRange; }
+
+ private:
+ typedef AvlTree, bool> StatesSet;
+
+ // Expands initial set with states that are reachable from initial set with no input consumption (E-transitions)
+ static void expandSet(StatesSet& set) {
+ List workingSet;
+
+ set.forEach([&](AvlNumericKey& key, bool) { workingSet.pushBack(key.val); });
+
+ while (workingSet.length()) {
+ auto first = workingSet.first()->data;
+ set.insert(first, {});
+
+ for (auto transition : first->mTransitions) {
+ if (!transition->isEpsilon()) continue;
+ if (set.find(transition->mState)) continue;
+ workingSet.pushBack(transition->mState);
+ }
+
+ workingSet.popFront();
+ }
+ }
+
+ // States that are reachable from initial set with symbol transition
+ static void findMoveSet(StatesSet& from, StatesSet& moveSet, tAlphabetType symbol) {
+ from.forEach([&](AvlNumericKey& key, bool) {
+ for (auto transition : key.val->mTransitions) {
+ if (transition->isEpsilon()) continue;
+ if (!transition->isTransition(symbol)) continue;
+ if (moveSet.find(transition->mState)) continue;
+ moveSet.insert(transition->mState, {});
+ }
+ });
+ }
+
+ public:
+ bool makeDeterministic() {
+ if (!isValid()) return false;
+
+ struct GroupKey {
+ const StatesSet* group;
+ static ualni hash(GroupKey key) { return 0; }
+ bool operator==(const GroupKey& key) const { return false; }
+ };
+
+ struct GroupInfo {
+ StatesSet* group = nullptr;
+ AvlTree, tAlphabetType> transitions;
+ State* newState = nullptr;
+ bool accepting = false;
+ tStateType stateVal = tStateType();
+ };
+
+ Buffer groups = { {} };
+ Map groupInfos;
+
+ groups.first().insert(getStartState(), false);
+
+ expandSet(groups.first());
+
+ groupInfos.put({ &groups.first() }, { &groups.first() });
+
+ // 1) find new states
+ List workingSet;
+ workingSet.pushBack(&groups.first());
+
+ while (workingSet.length()) {
+ StatesSet* group = workingSet.first()->data;
+ GroupInfo* info = &groupInfos.get({ group });
+
+ for (auto symbol : getAlphabetRange()) {
+
+ // calculate new possible state
+ StatesSet potentialGroup;
+
+ findMoveSet(*group, potentialGroup, tAlphabetType(symbol));
+ expandSet(potentialGroup);
+
+ if (!potentialGroup.size()) continue;
+
+ // find existing or create group
+ StatesSet* targetGroup = nullptr;
+ auto iter = groupInfos.presents({ &potentialGroup });
+ if (iter) {
+ targetGroup = groupInfos.getSlotVal(iter).group;
+ } else {
+ targetGroup = &groups.append(potentialGroup);
+ groupInfos.put({ targetGroup }, { targetGroup });
+ workingSet.pushBack(targetGroup);
+ }
+
+ // assert transition is added
+ info->transitions.insert(targetGroup, tAlphabetType(symbol));
+ }
+
+ workingSet.popFront();
+ }
+
+ // 2) find new states termination values
+ for (auto group : groupInfos) {
+ GroupInfo* info = &group->val;
+ ualni accepting = 0;
+
+ info->group->forEach([&](AvlNumericKey& key, bool) {
+ if (key.val->mIsAccepting) {
+ accepting++;
+ info->accepting = true;
+ info->stateVal = key.val->mStateVal;
+ }
+ });
+
+ if (!accepting) {
+ info->accepting = false;
+ info->stateVal = info->group->head()->key.val->mStateVal;
+ }
+ }
+
+ // 3) transfer
+ mStates.removeAll();
+
+ // create states
+ for (auto group : groupInfos) {
+ group->val.newState = addState(group->val.stateVal, group->val.accepting);
+ }
+
+ // create transitions
+ for (auto group : groupInfos) {
+ auto functor = [&](AvlNumericKey targetGroupKey, tAlphabetType symbol) {
+ GroupInfo* targetGroup = &groupInfos.get({ (StatesSet*) targetGroupKey.val });
+ addTransition(group->val.newState, targetGroup->newState, symbol);
+ };
+ group->val.transitions.forEach(functor);
+ }
+
+ return true;
+ }
+ };
+}
\ No newline at end of file
diff --git a/.wip/Language/public/ContextFreeAutomata.hpp b/.wip/Language/public/ContextFreeAutomata.hpp
new file mode 100644
index 0000000..9c4f698
--- /dev/null
+++ b/.wip/Language/public/ContextFreeAutomata.hpp
@@ -0,0 +1,137 @@
+
+#pragma once
+
+#include "Strings.hpp"
+#include "Automata.hpp"
+#include "Buffer2D.hpp"
+
+namespace tp {
+
+ template
+ class ContextFreeAutomata {
+
+ struct Action {
+ enum Type { SHIFT, REDUCE, TRAP } type = TRAP;
+ ualni num = 0; // state to shift (shift action) or pop count (reduce action)
+ };
+
+ public:
+ struct StackItem {
+ ualni state = 0;
+ tAlphabetType symbol;
+ Buffer leafs;
+ };
+
+ struct AcceptResult {
+ bool accepted = false;
+ ualni advancedIdx = 0;
+ const StackItem* ast = nullptr;
+ };
+
+ public:
+ ContextFreeAutomata() = default;
+
+ AcceptResult accept(const tAlphabetType* stream, ualni size) {
+ mCurrentState = mStartState;
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+
+ ualni advancedIdx = 0;
+ while (advancedIdx < size) {
+ const tAlphabetType& symbol = *(stream + advancedIdx);
+
+ if (!(symbol >= mRange.mBegin && symbol < mRange.mEnd)) {
+ return { false, advancedIdx, nullptr };
+ }
+
+ const Action& action = mTable.get({ ualni(symbol - mRange.mBegin), mCurrentState });
+
+ if (action.type == Action::TRAP) {
+ return { false, advancedIdx, nullptr };
+ }
+
+ if (action.type == Action::SHIFT) {
+ mStack.last()->symbol = symbol;
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+ mCurrentState = action.num;
+ }
+
+ if (mTable.get({ 0, mCurrentState }).type == Action::REDUCE) {
+ StackItem* newItem = &mItems.append(StackItem{});
+ for (auto iter : Range(action.num)) {
+ newItem->leafs.append(mStack.last());
+ mCurrentState = mStack.last()->state;
+ mStack.pop();
+ }
+
+ if (!mStack.size()) {
+ if (advancedIdx == size) {
+ return { true, advancedIdx, newItem };
+ } else {
+ return { false, advancedIdx, {} };
+ }
+ }
+
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+ }
+
+ advancedIdx++;
+ }
+
+ return { false, advancedIdx, nullptr };
+ }
+
+ public:
+ typedef FiniteStateAutomation Automata;
+ typedef Automata::State AutomataState;
+
+ void construct(const Automata& automata) {
+ mRange = automata.getAlphabetRange();
+
+ const ualni numStates = automata.numStates();
+ const ualni numSymbols = mRange.idxDiff();
+
+ mTable.reserve({ numSymbols, numStates });
+ mTable.assign(Action{ Action::TRAP, 0 });
+
+ Map states;
+ ualni stateIndex = 0;
+ for (auto state : *automata.getStates()) {
+ states.put(&state.data(), { stateIndex });
+ stateIndex++;
+ }
+
+ stateIndex = 0;
+ for (auto state : *automata.getStates()) {
+ if (&state.data() == automata.getStartState()) {
+ mStartState = stateIndex;
+ }
+
+ if (state->isAccepting()) {
+ ASSERT(state->getTransitions()->size() == 0)
+ for (auto symbolIndex : Range(numSymbols)) {
+ mTable.set({ stateIndex, symbolIndex }, { Action::REDUCE, state->getStateVal().numArgs() });
+ }
+ } else {
+ for (auto transition : *state->getTransitions()) {
+ ualni symbolIndex = ualni(transition->getSymbol()) - mRange.mBegin;
+ ualni targetStateIndex = states.get(transition->getState());
+ mTable.set({ stateIndex, symbolIndex }, { Action::SHIFT, targetStateIndex });
+ }
+ }
+
+ stateIndex++;
+ }
+ }
+
+ private:
+ Buffer2D mTable;
+
+ Buffer mItems;
+ Buffer mStack;
+
+ ualni mStartState = 0;
+ ualni mCurrentState = 0;
+
+ Range mRange;
+ };
+}
diff --git a/.wip/Language/public/ContextFreeCompiler.hpp b/.wip/Language/public/ContextFreeCompiler.hpp
new file mode 100644
index 0000000..db05b61
--- /dev/null
+++ b/.wip/Language/public/ContextFreeCompiler.hpp
@@ -0,0 +1,181 @@
+
+#pragma once
+
+#include "Automata.hpp"
+#include "Grammar.hpp"
+
+namespace tp {
+
+ class ContextFreeCompiler {
+ public:
+ struct SymbolVal {
+ SymbolVal() = default;
+
+ SymbolVal(ualni aId, ualni aStart, ualni aLen) {
+ id = aId;
+ start = aStart;
+ len = aLen;
+ };
+
+ SymbolVal(ualni val) { id = val; }
+
+ operator ualni() const { return id; }
+
+ bool operator==(const SymbolVal& in) const { return in.id == id; }
+ SymbolVal& operator=(const SymbolVal& in) = default;
+
+ ualni id = 0;
+ ualni start = 0;
+ ualni len = 0;
+ };
+
+ struct Item {
+ const ContextFreeGrammar::Rule* mRule = nullptr;
+ ualni mAdvanceIdx = 0;
+ ualni numArgs() const { return 0; }
+ };
+
+ struct Symbol {
+ String mId;
+ bool mIsTerminal = false;
+ };
+
+ private:
+ struct NonTerminal {
+ Buffer rules;
+ Map references;
+ Map referencing;
+
+ public:
+ [[nodiscard]] bool isProductive() const {
+ for (auto rule : rules) {
+ if (rule->isProductive()) return true;
+ }
+ return false;
+ }
+
+ [[nodiscard]] bool isLooped(Map& processed, const String& id) const {
+ for (auto ref : referencing) {
+ if (processed.presents(ref->key)) return true;
+ }
+ processed.put(id, {});
+ for (auto ref : referencing) {
+ if (ref->val->isLooped(processed, ref->key)) return true;
+ }
+ return false;
+ }
+ };
+
+ public:
+ bool compile(const ContextFreeGrammar& grammar, FiniteStateAutomation& automata) {
+ if (!init(grammar)) return false;
+ return true;
+ }
+
+ [[nodiscard]] const Buffer* getSymbols() const { return &mSymbols; }
+ [[nodiscard]] SymbolVal getSymbolId(const String& name) const { return mSymbolLookup.get(name); }
+
+ private:
+ bool init(const ContextFreeGrammar& grammar) {
+
+ if (!grammar.getRules()->size()) {
+ return false;
+ }
+
+ for (auto rule : *grammar.getRules()) {
+ if (!rule->getArgs()->size()) {
+ return false;
+ }
+ }
+
+ findNonTerminals(grammar);
+
+ for (auto nonTerminal : mNonTerminals) {
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isTerminal() || arg->isEpsilon()) continue;
+
+ if (!mNonTerminals.presents(arg->getId())) {
+ printf("Referenced non-terminal '%s' is not defined\n", arg->getId().read());
+ return false;
+ }
+ }
+ }
+ }
+
+ findAllReferences(grammar);
+
+ for (auto nonTerminal : mNonTerminals) {
+ if (!nonTerminal->val.references.size() && nonTerminal->key != grammar.getStartTerminal()) {
+ printf("Non-terminal '%s' is defined but not used\n", nonTerminal->key.read());
+ return false;
+ }
+ }
+
+ for (auto nonTerminal : mNonTerminals) {
+ if (!nonTerminal->val.isProductive()) {
+ printf("Non-terminal '%s' is not productive\n", nonTerminal->val.rules.first()->getId().read());
+ return false;
+ }
+ }
+
+ Map processed;
+ if (mNonTerminals.get(grammar.getStartTerminal()).isLooped(processed, grammar.getStartTerminal())) {
+ printf("Note that grammar is looped.\n");
+ return false;
+ }
+
+ initSymbols(grammar);
+
+ return true;
+ }
+
+ void findNonTerminals(const ContextFreeGrammar& grammar) {
+ for (auto rule : *grammar.getRules()) {
+ if (!mNonTerminals.presents(rule->getId())) {
+ mNonTerminals.put(rule->getId(), {});
+ }
+ auto nonTerminal = &mNonTerminals.get(rule->getId());
+ nonTerminal->rules.append(&rule.data());
+ }
+ }
+
+ void findAllReferences(const ContextFreeGrammar& grammar) {
+ for (auto nonTerminal : mNonTerminals) {
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isTerminal() || arg->isEpsilon()) continue;
+
+ NonTerminal* reference = &mNonTerminals.get(arg->getId());
+ nonTerminal->val.referencing.put(arg->getId(), reference);
+ reference->references.put(nonTerminal->key, &nonTerminal->val);
+ }
+ }
+ }
+ }
+
+ void initSymbols(const ContextFreeGrammar& grammar) {
+ for (auto nonTerminal : mNonTerminals) {
+ mSymbols.append({ nonTerminal->key, false });
+ mSymbolLookup.put(nonTerminal->key, SymbolVal(mSymbols.size() - 1));
+
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isEpsilon() || arg->isTerminal()) continue;
+ if (mTerminals.presents(arg->getId())) continue;
+ mTerminals.put(arg->getId(), {});
+
+ mSymbols.append({ nonTerminal->key, true });
+ mSymbolLookup.put(nonTerminal->key, SymbolVal(mSymbols.size() - 1));
+ }
+ }
+ }
+ }
+
+ private:
+ Map mNonTerminals;
+ Map mTerminals;
+ Buffer mSymbols;
+ Map mSymbolLookup;
+ };
+}
diff --git a/.wip/Language/public/Grammar.hpp b/.wip/Language/public/Grammar.hpp
new file mode 100644
index 0000000..df1fba5
--- /dev/null
+++ b/.wip/Language/public/Grammar.hpp
@@ -0,0 +1,224 @@
+
+#pragma once
+
+#include "Buffer.hpp"
+#include "LanguageCommon.hpp"
+#include "Map.hpp"
+#include "Strings.hpp"
+
+namespace tp {
+
+ class ContextFreeGrammar {
+ public:
+ struct Arg {
+ friend class Rule;
+
+ public:
+ Arg() = default;
+ explicit Arg(const String& id, bool terminal = true, bool epsilon = false);
+
+ public:
+ bool operator==(const Arg& in) const;
+ [[nodiscard]] const String& getId() const;
+ [[nodiscard]] bool isTerminal() const { return mIsTerminal; }
+ [[nodiscard]] bool isEpsilon() const { return mIsEpsilon; }
+
+ private:
+ String mId;
+ bool mIsTerminal = false;
+ bool mIsEpsilon = false;
+ };
+
+ class Rule {
+ public:
+ Rule() = default;
+ Rule(const String& id, const InitialierList& args);
+
+ public:
+ bool operator==(const Rule& in) const;
+ [[nodiscard]] bool isProductive() const;
+ [[nodiscard]] const String& getId() const { return mId; }
+ [[nodiscard]] const Buffer* getArgs() const { return &mArgs; }
+
+ private:
+ String mId;
+ Buffer mArgs;
+ };
+
+ public:
+ ContextFreeGrammar() = default;
+
+ public:
+ void addRule(const Rule& rule);
+ void addRule(const String& id, const InitialierList& args);
+ void setStart(const String& startRule);
+ [[nodiscard]] const Buffer* getRules() const { return &mRules; }
+ [[nodiscard]] const String& getStartTerminal() const { return mStartTerminal; }
+
+ public:
+ Buffer mRules;
+ String mStartTerminal;
+ bool mIsLooped = false;
+ };
+
+ template
+ class RegularGrammar {
+ public:
+ struct Node {
+ enum Type {
+ NONE,
+ ANY,
+ OR,
+ IF,
+ CLASS,
+ COMPOUND,
+ REPEAT,
+ VAL,
+ } mType = NONE;
+
+ explicit Node(Type type) :
+ mType(type) {}
+
+ virtual ~Node() = default;
+ };
+
+ class ValueNode : public Node {
+ public:
+ explicit ValueNode(tAlphabetType val) :
+ mVal(val),
+ Node(Node::VAL) {}
+
+ ~ValueNode() override = default;
+
+ public:
+ tAlphabetType mVal;
+ };
+
+ class CompoundNode : public Node {
+ public:
+ CompoundNode() :
+ Node(Node::COMPOUND) {}
+
+ CompoundNode(const InitialierList& nodes) :
+ Node(Node::COMPOUND) {
+ mSequence = nodes;
+ }
+
+ ~CompoundNode() override {
+ for (auto iter : mSequence) {
+ delete iter.data();
+ }
+ mSequence.clear();
+ }
+
+ public:
+ Buffer mSequence;
+ };
+
+ class AlternationNode : public Node {
+ public:
+ AlternationNode() :
+ Node(Node::OR) {}
+
+ AlternationNode(const Node* a, const Node* b) :
+ Node(Node::OR) {
+ mFirst = a;
+ mSecond = b;
+ }
+
+ ~AlternationNode() override {
+ delete mFirst;
+ delete mSecond;
+ }
+
+ public:
+ const Node* mFirst = nullptr;
+ const Node* mSecond = nullptr;
+ };
+
+ class IfNode : public Node {
+ public:
+ IfNode() :
+ Node(Node::IF) {}
+
+ explicit IfNode(const Node* a) :
+ Node(Node::IF) {
+ mNode = a;
+ }
+
+ ~IfNode() override { delete mNode; }
+
+ public:
+ const Node* mNode = nullptr;
+ };
+
+ class AnyNode : public Node {
+ public:
+ AnyNode() :
+ Node(Node::ANY) {}
+
+ ~AnyNode() override = default;
+ };
+
+ class RepetitionNode : public Node {
+ public:
+ RepetitionNode() :
+ Node(Node::REPEAT) {}
+
+ explicit RepetitionNode(const Node* rep, bool plus = false) :
+ Node(Node::REPEAT) {
+ mNode = rep;
+ mPlus = plus;
+ }
+
+ ~RepetitionNode() override { delete mNode; }
+
+ public:
+ Node* mNode = nullptr;
+ bool mPlus = false;
+ };
+
+ class ClassNode : public Node {
+ public:
+ ClassNode() :
+ Node(Node::CLASS) {}
+
+ explicit ClassNode(const Buffer>& ranges, bool exclude = false) :
+ Node(Node::CLASS) {
+ mExclude = exclude;
+ mRanges = ranges;
+ }
+
+ ~ClassNode() override { mRanges.removeAll(); }
+
+ public:
+ Buffer> mRanges;
+ bool mExclude = false;
+ };
+
+ public:
+ RegularGrammar() = default;
+
+ ~RegularGrammar() {
+ for (auto rule : mRules) {
+ delete rule->t1;
+ }
+ }
+
+ void addRule(const Node* node, tTokType id) { mRules.append({ node, id }); }
+
+ public:
+ const Node* seq(const InitialierList& nodes) { return new CompoundNode(nodes); }
+ const Node* val(tAlphabetType in) { return new ValueNode(in); }
+ const Node* alt(const Node* a, const Node* b) { return new AlternationNode(a, b); }
+ const Node* may(const Node* a) { return new IfNode(a); }
+ const Node* any() { return new AnyNode(); }
+ const Node* rep(const Node* rep, bool plus = false) { return new RepetitionNode(rep, plus); }
+ const Node* ranges(const Buffer>& ranges, bool exclude = false) {
+ return new ClassNode(ranges, exclude);
+ }
+
+ public:
+ Buffer> mRules;
+ };
+}
diff --git a/.wip/Language/public/LanguageCommon.hpp b/.wip/Language/public/LanguageCommon.hpp
new file mode 100644
index 0000000..0429431
--- /dev/null
+++ b/.wip/Language/public/LanguageCommon.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "Module.hpp"
+
+namespace tp {
+ extern ModuleManifest gModuleLanguage;
+}
diff --git a/.wip/Language/public/Parser.hpp b/.wip/Language/public/Parser.hpp
new file mode 100644
index 0000000..10985ae
--- /dev/null
+++ b/.wip/Language/public/Parser.hpp
@@ -0,0 +1,107 @@
+
+#pragma once
+
+#include "RegularCompiler.hpp"
+#include "RegularAutomata.hpp"
+
+#include "ContextFreeCompiler.hpp"
+#include "ContextFreeAutomata.hpp"
+
+namespace tp {
+
+ template
+ class Parser {
+
+ typedef RegularGrammar RegularGrammar;
+ typedef RegularCompiler RegularCompiler;
+ typedef FiniteStateAutomation RegularGraph;
+ typedef RegularAutomata RegularAutomata;
+
+ // ContextFreeGrammar;
+ // ContextFreeCompiler;
+ typedef FiniteStateAutomation ContextFreeGraph;
+ typedef ContextFreeAutomata ContextFreeAutomata;
+
+ public:
+ struct ParseResult {
+ bool accepted = false;
+ const ContextFreeAutomata::StackItem* ast = nullptr;
+ };
+
+ public:
+ Parser() = default;
+
+ public:
+ bool compileTables(
+ const ContextFreeGrammar& cfGrammar,
+ const RegularGrammar& reGrammar,
+ const Map& contextFreeToRegular
+ ) {
+ // Compile Regular Grammar
+ {
+ RegularGraph graph;
+ RegularCompiler compiler;
+ compiler.compile(graph, reGrammar);
+ graph.makeDeterministic();
+ mRegularAutomata.construct(graph);
+ }
+
+ // compile context free grammar
+ {
+ ContextFreeGraph graph;
+ ContextFreeCompiler compiler;
+ compiler.compile(cfGrammar, graph);
+ graph.makeDeterministic();
+ mContextFreeAutomata.construct(graph);
+
+ // make glue
+ for (auto symbol : *compiler.getSymbols()) {
+ auto symbolId = compiler.getSymbolId(symbol->mId);
+ if (symbol->mIsTerminal) {
+ auto iter = contextFreeToRegular.presents(symbol->mId);
+ if (!iter) return false;
+ mGrammarGlue.put(contextFreeToRegular.getSlotVal(iter), symbolId);
+ } else {
+ mAstNames.put(symbolId, symbol->mId);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ParseResult parse(const tAlphabetType* sentence, ualni sentenceLength) {
+ // get tokens stream
+ Buffer tokens;
+
+ const tAlphabetType* sentenceIter = sentence;
+ ualni lengthIter = sentenceLength;
+
+ while (lengthIter) {
+ auto result = mRegularAutomata.accept(sentenceIter, lengthIter);
+
+ if (!result.accepted) {
+ return { false, nullptr };
+ }
+
+ tokens.append(ContextFreeCompiler::SymbolVal(
+ mGrammarGlue.get(result.state), ualni(sentenceIter - sentence), result.advancedIdx
+ ));
+
+ sentenceIter += result.advancedIdx;
+ lengthIter -= result.advancedIdx;
+ }
+
+ ContextFreeAutomata::AcceptResult result = mContextFreeAutomata.accept(tokens.getBuff(), tokens.size());
+ return { result.accepted, result.ast };
+ }
+
+ public:
+ // save load compiled tables
+ RegularAutomata mRegularAutomata;
+ ContextFreeAutomata mContextFreeAutomata;
+
+ Map mGrammarGlue;
+ Map mAstNames;
+ };
+}
diff --git a/.wip/Language/public/RegularAutomata.hpp b/.wip/Language/public/RegularAutomata.hpp
new file mode 100644
index 0000000..501dada
--- /dev/null
+++ b/.wip/Language/public/RegularAutomata.hpp
@@ -0,0 +1,101 @@
+
+#pragma once
+
+#include "Strings.hpp"
+#include "Automata.hpp"
+#include "Buffer2D.hpp"
+
+namespace tp {
+
+ template
+ class RegularAutomata {
+ public:
+ struct AcceptResult {
+ bool accepted = false;
+ ualni advancedIdx = 0;
+ tStateType state = tStateType();
+ };
+
+ public:
+ RegularAutomata() = default;
+
+ AcceptResult accept(const tAlphabetType* stream, ualni size) {
+ mCurrentState = mStartState;
+
+ ualni advancedIdx = 0;
+
+ while (advancedIdx < size) {
+ const tAlphabetType& symbol = *(stream + advancedIdx);
+
+ if (!(symbol >= mSymbolRange.mBegin && symbol < mSymbolRange.mEnd)) {
+ return { false, advancedIdx, {} };
+ }
+
+ mCurrentState = mTable.get({ (ualni) (symbol - mSymbolRange.mBegin), mCurrentState });
+
+ if (mCurrentState == mStates.size()) {
+ return { false, advancedIdx, {} };
+ }
+
+ if (mStates[mCurrentState].first) {
+ return { true, advancedIdx, mStates[mCurrentState].second };
+ }
+
+ advancedIdx++;
+ }
+
+ return { false, advancedIdx, {} };
+ }
+
+ public:
+ void construct(const FiniteStateAutomation& automata) {
+ const auto range = automata.getAlphabetRange();
+ mSymbolRange = { tAlphabetType(range.mBegin), tAlphabetType(range.mEnd) };
+
+ auto range_len = ualni(mSymbolRange.mEnd - mSymbolRange.mBegin);
+ auto sizeX = range_len ? range_len : 1;
+ auto sizeY = (ualni) (automata.numStates());
+
+ mTable.reserve({ sizeX, sizeY });
+ mTable.assign(automata.numStates());
+ mStates.reserve(sizeY);
+
+ ualni idx = 0;
+ for (auto state : *automata.getStates()) {
+ mStates[idx] = { state->isAccepting(), state->getStateVal() };
+ idx++;
+ }
+
+ idx = 0;
+ for (auto state : *automata.getStates()) {
+ if (&state.data() == automata.getStartState()) {
+ mStartState = mCurrentState = idx;
+ }
+ idx++;
+ }
+
+ ualni stateIdx = 0;
+ for (auto state : *automata.getStates()) {
+ for (auto transition : *state->getTransitions()) {
+ ualni stateIdx2 = 0;
+ for (auto state2 : *automata.getStates()) {
+ if (transition->getState() == &state2.data()) break;
+ stateIdx2++;
+ }
+ auto const code = transition->getSymbol();
+ mTable.set({ (ualni) (code - mSymbolRange.mBegin), (ualni) stateIdx }, stateIdx2);
+ }
+ stateIdx++;
+ }
+ }
+
+ private:
+ Buffer2D mTable;
+ Buffer> mStates;
+
+ ualni mCurrentState = 0;
+ ualni mStartState = 0;
+
+ Range mSymbolRange = { 0, 0 };
+ };
+}
diff --git a/.wip/Language/public/RegularCompiler.hpp b/.wip/Language/public/RegularCompiler.hpp
new file mode 100644
index 0000000..a27f464
--- /dev/null
+++ b/.wip/Language/public/RegularCompiler.hpp
@@ -0,0 +1,208 @@
+
+#pragma once
+
+#include "Grammar.hpp"
+#include "Automata.hpp"
+
+namespace tp {
+
+ template
+ class RegularCompiler {
+
+ typedef FiniteStateAutomation Graph;
+ typedef typename Graph::State Vertex;
+ typedef RegularGrammar Grammar;
+
+ struct Node {
+ Vertex* left = nullptr;
+ Vertex* right = nullptr;
+ };
+
+ private:
+ Graph* mGraph = nullptr;
+
+ public:
+ struct CompileError {
+ uhalni mRuleIndex = 0;
+ tStateType mRuleState;
+ const char* description = nullptr;
+ [[nodiscard]] bool isError() const { return description; }
+ };
+
+ CompileError mError;
+
+ void compile(Graph& graph, const tAlphabetType* regex, tStateType state) {
+ mGraph = &graph;
+ compileUtil(regex, state);
+ }
+
+ void compile(Graph& aGraph, const Grammar& grammar) {
+ mGraph = &aGraph;
+
+ auto left = addVertex();
+ auto right = addVertex();
+
+ halni idx = 0;
+ for (auto rule : grammar.mRules) {
+
+ auto node = compileUtil(rule.data().first, rule.data().second);
+
+ if (!(node.left && node.right)) {
+ mError.mRuleIndex = idx;
+ return;
+ }
+
+ transitionAny(left, node.left);
+ transitionAny(node.right, right);
+
+ idx++;
+ }
+
+ mGraph->setStartState(left);
+ }
+
+ private:
+ Node compileUtil(const Grammar::Node* astNode, tStateType state) {
+
+ auto node = compileNode(astNode, nullptr, nullptr);
+
+ node.right->setValue(state);
+ node.right->setAcceptance(true);
+
+ mGraph->setStartState(node.left);
+
+ return node;
+ }
+
+ Node compileVal(Grammar::ValueNode* val, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+ transitionVal(left, right, val->mVal);
+ return { left, right };
+ }
+
+ Node compileAlternation(const Grammar::AlternationNode* alt, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto first_node = compileNode(alt->mFirst, aLeft, aRight);
+ auto second_node = compileNode(alt->mSecond);
+ transitionAny(first_node.left, second_node.left);
+ transitionAny(second_node.right, first_node.right);
+ return first_node;
+ }
+
+ Node compileAny(const Grammar::AnyNode*, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+ transitionAny(left, right, true);
+ return { left, right };
+ }
+
+ Node compileRepeat(const Grammar::RepetitionNode* repeat, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ if (repeat->mPlus) {
+ auto middle = addVertex();
+
+ auto left_node = compileNode(repeat->mNode, aLeft, middle);
+
+ auto right_node = compileNode(repeat->mNode, middle, aRight);
+ transitionAny(right_node.right, right_node.left);
+ transitionAny(right_node.left, right_node.right);
+
+ return { left_node.left, right_node.right };
+ } else {
+ auto node = compileNode(repeat->mNode, aLeft, aRight);
+ transitionAny(node.right, node.left);
+ transitionAny(node.left, node.right);
+ return node;
+ }
+ }
+
+ Node compileIf(const Grammar::IfNode* ifNode, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto node = compileNode(ifNode->mNode, aLeft, aRight);
+ transitionAny(node.left, node.right);
+ return node;
+ }
+
+ Node compileClass(const Grammar::ClassNode* node, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+
+ if (node->mRanges.size() == 1) {
+ auto const& range = node->mRanges.first();
+ transitionRange(left, right, { ualni(range.mBegin), ualni(range.mEnd) }, node->mExclude);
+ return { left, right };
+ }
+
+ for (auto range : node->mRanges) {
+ auto middle = addVertex();
+ transitionRange(left, middle, { ualni(range->mBegin), ualni(range->mEnd) }, node->mExclude);
+ transitionAny(middle, right);
+ }
+ return { left, right };
+ }
+
+ Node compileCompound(const Grammar::CompoundNode* compound, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ Vertex* left = nullptr;
+ Vertex* rigth = nullptr;
+
+ ualni idx = 0;
+ for (auto child : compound->mSequence) {
+ auto pass_left = idx == 0 ? aLeft : rigth;
+ auto pass_right = idx == compound->mSequence.size() - 1 ? aRight : nullptr;
+ auto node = compileNode(child.data(), pass_left, pass_right);
+ if (!left) left = node.left;
+ rigth = node.right;
+ idx++;
+ }
+
+ return { left, rigth };
+ }
+
+ Node compileNode(const Grammar::Node* node, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ switch (node->mType) {
+ case Grammar::Node::CLASS: return compileClass((typename Grammar::ClassNode*) node, aLeft, aRight);
+ case Grammar::Node::COMPOUND: return compileCompound((typename Grammar::CompoundNode*) node, aLeft, aRight);
+ case Grammar::Node::IF: return compileIf((typename Grammar::IfNode*) node, aLeft, aRight);
+ case Grammar::Node::REPEAT: return compileRepeat((typename Grammar::RepetitionNode*) node, aLeft, aRight);
+ case Grammar::Node::ANY: return compileAny((typename Grammar::AnyNode*) node, aLeft, aRight);
+ case Grammar::Node::OR: return compileAlternation((typename Grammar::AlternationNode*) node, aLeft, aRight);
+ case Grammar::Node::VAL: return compileVal((typename Grammar::ValueNode*) node, aLeft, aRight);
+ case Grammar::Node::NONE: break;
+ }
+ ASSERT(0)
+ return {};
+ }
+
+ void transitionAny(Vertex* from, Vertex* to, bool consumes = false) {
+ for (auto symbol : Range(tMinSymbol, tMaxSymbol)) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ void transitionVal(Vertex* from, Vertex* to, tAlphabetType val) { mGraph->addTransition(from, to, val); }
+
+ void transitionRange(Vertex* from, Vertex* to, Range range, bool exclude) {
+ if (exclude) {
+ Range first = { tMinSymbol, range.mBegin - 1 };
+ Range second = { range.mEnd + 1, tMaxSymbol };
+
+ if (first.valid()) {
+ for (auto symbol : first) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ if (second.valid()) {
+ for (auto symbol : second) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ } else {
+ for (auto symbol : range) {
+ transitionVal(from, to, symbol);
+ }
+ }
+ }
+
+ Vertex* addVertex() { return mGraph->addState(tInTransition, false); }
+ };
+}
diff --git a/.wip/Language/public/SimpleParser.hpp b/.wip/Language/public/SimpleParser.hpp
new file mode 100644
index 0000000..ad1dc6e
--- /dev/null
+++ b/.wip/Language/public/SimpleParser.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "Parser.hpp"
+
+namespace tp {
+
+ // Gives ability to express grammar in the Unified Format as sentence
+ template
+ class SimpleParser {
+ enum UGTokens : alni { InTransition = -1, TestSeq };
+
+ typedef Parser UGParser;
+ typedef Parser UserParser;
+
+ public:
+ SimpleParser() {
+ // Grammar for unified grammar format sentence that tables compiled from
+
+ // Define Context-Free grammar
+ ContextFreeGrammar contextFreeGrammar;
+ {
+ // use existing CF grammar interface
+ contextFreeGrammar.addRule("a", { ContextFreeGrammar::Arg("") });
+ contextFreeGrammar.setStart("a");
+ }
+
+ // Define Regular grammar
+ RegularGrammar regularGrammar;
+ {
+ // this is basically ast from existing tokenizer
+ regularGrammar.addRule(regularGrammar.seq({ regularGrammar.val('a'), regularGrammar.val('b') }), TestSeq);
+ }
+
+ Map terminalsMap;
+ terminalsMap.put("TestSeq", TestSeq);
+
+ mUnifiedGrammarParser.compileTables(contextFreeGrammar, regularGrammar, terminalsMap);
+ }
+
+ public:
+ void compileTables(const tAlphabetType* grammar, ualni grammarLength) {
+ mUnifiedGrammarParser.parse(grammar, grammarLength);
+
+ // compile each ast into RegularGrammar and ContextFree Grammar api instructions
+ ContextFreeGrammar userContextFreeGrammar;
+ RegularGrammar userRegularGrammar;
+
+ // ...
+ // split ast into RE and CF part
+ // generate and execute grammar api commands
+ // use existing tokenizer code to create RE transition matrix
+ // ...
+ // compile tables from user grammar
+
+ Map terminalsMap;
+ terminalsMap.put("TestSeq", 0);
+
+ mUserParser.compileTables(userContextFreeGrammar, userRegularGrammar, terminalsMap);
+ }
+
+ UserParser::ParseResult parse(const tAlphabetType* grammar, ualni grammarLength) {
+ return mUserParser.parse(grammar, grammarLength);
+ }
+
+ private:
+ UGParser mUnifiedGrammarParser;
+ UserParser mUserParser;
+ };
+}
diff --git a/.wip/Language/tests/Test.hpp b/.wip/Language/tests/Test.hpp
new file mode 100644
index 0000000..f0b25f1
--- /dev/null
+++ b/.wip/Language/tests/Test.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+// #include "SimpleParser.hpp"
+
+const char* gGrammar = R"(
+# Grammar in the CF-RE United Format (Defined by language module)
+
+Rules : {
+ ScopeList : ScopeList Scope | Scope ;
+ Scope : \ScopeBegin StatementList \ScopeEnd;
+ StatementList : StatementList Statement \StatementEnd;
+ StatementList : Statement \StatementEnd;
+ Statement : \StatementBody;
+}
+
+Terminals : {
+ Space : " " | "\t" | "\n" | "\r";
+ ScopeBegin : "{";
+ ScopeEnd : "}";
+ StatementEnd : ";";
+ StatementBody : "a" | "b";
+}
+
+Start : Scope;
+Ignore : Space;
+)";
+
+const char* gSentence = R"(
+{}
+
+{ }
+
+{
+a;
+ a ; a ;
+
+ }
+
+ {
+a;
+ a;
+}
+)";
\ No newline at end of file
diff --git a/.wip/Language/tests/Tests.cpp b/.wip/Language/tests/Tests.cpp
new file mode 100644
index 0000000..ce4beb2
--- /dev/null
+++ b/.wip/Language/tests/Tests.cpp
@@ -0,0 +1,45 @@
+
+#include "Test.hpp"
+
+/*
+using namespace tp;
+
+void testAutomation() {
+
+ FiniteStateAutomation automata;
+
+ auto start = automata.addState(0, false);
+ auto end = automata.addState(1, true);
+
+ automata.addTransition(start, end, 'a');
+
+ automata.setStartState(start);
+
+ automata.makeDeterministic();
+}
+
+void test() {
+ auto parser = SimpleParser();
+
+ parser.compileTables(gGrammar, String::Logic::calcLength(gGrammar));
+ auto result = parser.parse(gSentence, String::Logic::calcLength(gSentence));
+}
+
+int main() {
+
+ tp::ModuleManifest* deps[] = { &tp::gModuleLanguage, nullptr };
+ tp::ModuleManifest testModule("Test", nullptr, nullptr, deps);
+
+ if (!testModule.initialize()) {
+ return 1;
+ }
+
+ testAutomation();
+ test();
+
+ testModule.deinitialize();
+}
+
+*/
+
+int main() { return 0; }
\ No newline at end of file
diff --git a/Allocators/CMakeLists.txt b/Allocators/CMakeLists.txt
index fae102e..8b59432 100644
--- a/Allocators/CMakeLists.txt
+++ b/Allocators/CMakeLists.txt
@@ -1,22 +1,15 @@
-
-cmake_minimum_required(VERSION 3.2)
-
-set(CMAKE_CXX_STANDARD 23)
-
-project(Allocator)
+project(Allocators)
### ---------------------- 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 Utils)
+target_link_libraries(${PROJECT_NAME} PUBLIC Callstack)
### -------------------------- Tests -------------------------- ###
enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp")
-add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
-target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
-add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
-
-install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
\ No newline at end of file
+add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
+target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
+add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
\ No newline at end of file
diff --git a/Allocators/README.md b/Allocators/README.md
index 6062b25..d2beeba 100644
--- a/Allocators/README.md
+++ b/Allocators/README.md
@@ -26,17 +26,11 @@ int main(char argc, char* argv[]) {
}
```
-If memory leaks were detected it will be loged in the output console.
+If memory leaks were detected it will be logged in the output console.

-Also debug.memleaks binary 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 viewed with MemLeaks Viewer.

-
-## Memory Usage Analisys
-Currently outdated
-
-## Benchmarks
-Currently outdated
diff --git a/Allocators/private/Allocators.cpp b/Allocators/private/Allocators.cpp
index 18bc13c..1b87345 100644
--- a/Allocators/private/Allocators.cpp
+++ b/Allocators/private/Allocators.cpp
@@ -1,23 +1,36 @@
#include "Allocators.hpp"
+#include "HeapAllocatorGlobal.hpp"
#include
+#include
-static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleBase, nullptr };
-tp::ModuleManifest tp::gModuleAllocators = ModuleManifest("Allocators", nullptr, nullptr, sModuleDependencies);
+static bool init(const tp::ModuleManifest* self) {
+ tp::HeapAllocGlobal::stopIgnore();
+ if (tp::HeapAllocGlobal::getNAllocations()) {
+ printf("Warning : Operator new was called outside module initialization!!\n\n");
+ }
+ return true;
+}
+static void deinit(const tp::ModuleManifest* self) {
+ tp::HeapAllocGlobal::checkLeaks();
+}
+
+static tp::ModuleManifest* sModuleDependencies[] = { nullptr };
+tp::ModuleManifest tp::gModuleAllocators = ModuleManifest("Allocators", init, deinit, sModuleDependencies);
void* operator new(size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
void* operator new[](size_t aSize) { return tp::HeapAllocGlobal::allocate(aSize); }
-void operator delete(void* aPtr) noexcept { tp::HeapAllocGlobal::deallocate(aPtr); }
-void operator delete[](void* aPtr) noexcept { tp::HeapAllocGlobal::deallocate(aPtr); }
+void operator delete(void* aPtr) noexcept { tp::HeapAllocGlobal::deallocate(aPtr); }
+void operator delete[](void* aPtr) noexcept { tp::HeapAllocGlobal::deallocate(aPtr); }
void* operator new(size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
void* operator new[](size_t aSize, tp::HeapAlloc& aAlloc) { return aAlloc.allocate(aSize); }
-void operator delete(void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
-void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
+void operator delete(void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
+void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc) { aAlloc.deallocate(aPtr); }
void* operator new(size_t aSize, tp::HeapAllocGlobal& aAlloc) { return tp::HeapAllocGlobal::allocate(aSize); }
void* operator new[](size_t aSize, tp::HeapAllocGlobal& aAlloc) { return tp::HeapAllocGlobal::allocate(aSize); }
-void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }
-void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }
\ No newline at end of file
+void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }
+void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }
\ No newline at end of file
diff --git a/Allocators/private/HeapAllocator.cpp b/Allocators/private/HeapAllocator.cpp
index a1994da..b6f76bc 100644
--- a/Allocators/private/HeapAllocator.cpp
+++ b/Allocators/private/HeapAllocator.cpp
@@ -1,70 +1,76 @@
-
-
-#include "HeapAllocator.hpp"
-#include "HeapAllocatorGlobal.hpp"
-
-#include "PrivateConfig.hpp"
-
-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 {
- struct MemHeadLocal {
- MemHeadLocal* mPrev;
- MemHeadLocal* mNext;
- };
-}
-
-void* HeapAlloc::allocate(ualni aBlockSize) {
- auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal));
- auto out = head + 1;
-
- mNumAllocations++;
- if (mEntry) {
- DEBUG_ASSERT(!mEntry->mNext)
- head->mNext = nullptr;
- head->mPrev = mEntry;
- mEntry->mNext = head;
- } else {
- head->mNext = nullptr;
- head->mPrev = nullptr;
- }
- mEntry = head;
-
- return out;
-}
-
-void HeapAlloc::deallocate(void* aPtr) {
- auto head = ((MemHeadLocal*)(aPtr)) - 1;
-
- mNumAllocations--;
- DEBUG_ASSERT(!mEntry->mNext)
- if (head->mNext) head->mNext->mPrev = head->mPrev;
- if (head->mPrev) head->mPrev->mNext = head->mNext;
- if (head == mEntry) {
- mEntry = head->mPrev;
- }
-
- HeapAllocGlobal::deallocate(head);
-}
-
-HeapAlloc::~HeapAlloc() {
- if (mNumAllocations) {
- DEBUG_BREAK("Destruction of not freed Allocator")
-
- #ifdef MEM_STACK_TRACE
- // TODO : log leaks and free them up
- #endif
- }
-}
-
-#endif
-
+
+
+#include "HeapAllocator.hpp"
+#include "HeapAllocatorGlobal.hpp"
+
+#include "PrivateConfig.hpp"
+
+#include
+
+using namespace tp;
+
+#if not defined(MEM_DEBUG)
+
+// ----------------------- Release Implementation ---------------------------- //
+void* HeapAlloc::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
+void HeapAlloc::deallocate(void* aPtr) {
+ if (!aPtr) return;
+ free(aPtr);
+}
+HeapAlloc::~HeapAlloc() {}
+
+#else
+
+namespace tp {
+ struct MemHeadLocal {
+ MemHeadLocal* mPrev;
+ MemHeadLocal* mNext;
+ };
+}
+
+void* HeapAlloc::allocate(ualni aBlockSize) {
+ auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal));
+ auto out = head + 1;
+
+ mNumAllocations++;
+ if (mEntry) {
+ DEBUG_ASSERT(!mEntry->mNext)
+ head->mNext = nullptr;
+ head->mPrev = mEntry;
+ mEntry->mNext = head;
+ } else {
+ head->mNext = nullptr;
+ head->mPrev = nullptr;
+ }
+ mEntry = head;
+
+ return out;
+}
+
+void HeapAlloc::deallocate(void* aPtr) {
+ if (!aPtr) return;
+
+ auto head = ((MemHeadLocal*) (aPtr)) - 1;
+
+ mNumAllocations--;
+ DEBUG_ASSERT(!mEntry->mNext)
+ if (head->mNext) head->mNext->mPrev = head->mPrev;
+ if (head->mPrev) head->mPrev->mNext = head->mNext;
+ if (head == mEntry) {
+ mEntry = head->mPrev;
+ }
+
+ HeapAllocGlobal::deallocate(head);
+}
+
+HeapAlloc::~HeapAlloc() {
+ if (mNumAllocations) {
+ DEBUG_ASSERT(0 && "Destruction of not freed Allocator")
+
+#ifdef MEM_STACK_TRACE
+ // TODO : log leaks and free them up
+#endif
+ }
+}
+
+#endif
diff --git a/Allocators/private/HeapAllocatorGlobal.cpp b/Allocators/private/HeapAllocatorGlobal.cpp
index da31e46..c46df45 100644
--- a/Allocators/private/HeapAllocatorGlobal.cpp
+++ b/Allocators/private/HeapAllocatorGlobal.cpp
@@ -1,150 +1,230 @@
-
-
-#include "HeapAllocatorGlobal.hpp"
-#include "PrivateConfig.hpp"
-
-#include "Utils.hpp"
-#include "Debugging.hpp"
-
-#include
-
-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() = default;
-
-#else
-
-tp::MemHead* tp::HeapAllocGlobal::mEntry = nullptr;
-tp::ualni tp::HeapAllocGlobal::mNumAllocations = 0;
-
-// ----------------------- Debug Implementation ---------------------------- //
-// |----------------|
-// | MemHead |
-// |----------------|
-// | wrap top |
-// |----------------| - Allocated Block Layout
-// | data |
-// |----------------|
-// | wrap bottom |
-// |----------------|
-
-namespace tp {
- struct MemHead {
- MemHead* mPrev;
- MemHead* mNext;
- ualni mBlockSize;
- #ifdef MEM_STACK_TRACE
- const CallStackCapture::CallStack* mCallStack;
- #endif
- };
-}
-
-enum : ualni {
- ALIGNED_SIZE = ENV_ALNI_SIZE_B,
- WRAP_SIZE = MEM_WRAP_SIZE * ALIGNED_SIZE,
- WRAP_VAL = MEM_WRAP_FILL_VAL,
- HEAD_SIZE = sizeof(MemHead),
- CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
- CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
-};
-
-void* HeapAllocGlobal::allocate(ualni aBlockSize) {
-
- static_assert(HEAD_SIZE % ALIGNED_SIZE == 0, "Heap Allocator Configuration Error");
-
- if (aBlockSize % ALIGNED_SIZE) {
- aBlockSize = (aBlockSize / ALIGNED_SIZE + 1) * ALIGNED_SIZE;
- }
-
- // 1) Allocate the block
- auto head = (MemHead*)malloc(aBlockSize + WRAP_SIZE * 2 + HEAD_SIZE);
- auto wrap_top = (int1*)(head + 1);
- auto data = wrap_top + WRAP_SIZE;
- auto wrap_bottom = data + aBlockSize;
-
- if (!head) { return nullptr; }
- head->mBlockSize = aBlockSize;
-
- // 2) Link with existing blocks
- mNumAllocations++;
- if (mEntry) {
- DEBUG_ASSERT(!mEntry->mNext)
- head->mNext = nullptr;
- head->mPrev = mEntry;
- mEntry->mNext = head;
- } else {
- head->mNext = nullptr;
- head->mPrev = nullptr;
- }
- mEntry = head;
-
- // 3) Wrap fill
- memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
- memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
-
- // 4) Trace the stack
- #ifdef MEM_STACK_TRACE
- head->mCallStack = gCSCapture->getSnapshot();
- #endif
-
- // 5) clear data
-#ifdef MEM_CLEAR_ON_ALLOC
- memSetVal(data, aBlockSize, CLEAR_ALLOC_VAL);
-#endif
-
- return data;
-}
-
-void HeapAllocGlobal::deallocate(void* aPtr) {
- // 1) Restore the pointers
- auto head = ((MemHead*)((int1*)aPtr - WRAP_SIZE)) - 1;
- auto wrap_top = (int1*)(head + 1);
- auto data = wrap_top + WRAP_SIZE;
- auto wrap_bottom = data + head->mBlockSize;
-
- // 2) Unlink with blocks
- mNumAllocations--;
- DEBUG_ASSERT(!mEntry->mNext)
- if (head->mNext) head->mNext->mPrev = head->mPrev;
- if (head->mPrev) head->mPrev->mNext = head->mNext;
- if (head == mEntry) {
- mEntry = head->mPrev;
- }
-
- // 3) Check the wrap
- ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
- ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
-
- // 4) clear data
-#ifdef MEM_CLEAR_ON_ALLOC
- memSetVal(data, head->mBlockSize, CLEAR_DEALLOC_VAL);
-#endif
-
- // 5) free the block
- free(head);
-}
-
-bool HeapAllocGlobal::checkLeaks() {
- // 1) Check for not deallocated memory
- if (mNumAllocations) {
-
- #ifdef MEM_STACK_TRACE
- gCSCapture->logLeaks();
- #endif
-
- DEBUG_BREAK("Destruction of not freed Allocator")
- return true;
- }
- return false;
-}
-
-HeapAllocGlobal::~HeapAllocGlobal() = default;
-
-#endif
-
+
+
+#include "HeapAllocatorGlobal.hpp"
+#include "PrivateConfig.hpp"
+
+#include "Callstack.hpp"
+#include "Utils.hpp"
+
+#include
+#include
+
+using namespace tp;
+
+#if not defined(MEM_DEBUG)
+
+// ----------------------- Release Implementation ---------------------------- //
+
+void* HeapAllocGlobal::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
+void HeapAllocGlobal::deallocate(void* aPtr) {
+ if (!aPtr) return;
+ free(aPtr);
+}
+HeapAllocGlobal::~HeapAllocGlobal() = default;
+bool HeapAllocGlobal::checkLeaks() { return false; }
+void HeapAllocGlobal::startIgnore() {}
+void HeapAllocGlobal::stopIgnore() {}
+ualni HeapAllocGlobal::getNAllocations() { return 0; }
+
+#else
+
+tp::MemHead* tp::HeapAllocGlobal::mEntry = nullptr;
+tp::ualni tp::HeapAllocGlobal::mNumAllocations = 0;
+std::mutex tp::HeapAllocGlobal::mMutex;
+bool tp::HeapAllocGlobal::mIgnore = true;
+bool tp::HeapAllocGlobal::mEnableCallstack = false;
+
+#ifdef MEM_STACK_TRACE
+tp::CallStackCapture tp::HeapAllocGlobal::mCallstack;
+#endif
+
+ // ----------------------- Debug Implementation ---------------------------- //
+// |----------------|
+// | MemHead |
+// |----------------|
+// | wrap top |
+// |----------------| - Allocated Block Layout
+// | data |
+// |----------------|
+// | wrap bottom |
+// |----------------|
+
+namespace tp {
+ struct MemHead {
+ MemHead* mPrev;
+ MemHead* mNext;
+ uhalni mBlockSize;
+ uhalni mIgnored;
+#ifdef MEM_STACK_TRACE
+ const CallStackCapture::CallStack* mCallStack;
+#endif
+ };
+}
+
+enum : ualni {
+ ALIGNED_SIZE = ENV_ALNI_SIZE_B,
+ WRAP_SIZE = MEM_WRAP_SIZE * ALIGNED_SIZE,
+ WRAP_VAL = MEM_WRAP_FILL_VAL,
+ HEAD_SIZE = sizeof(MemHead),
+ CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
+ CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
+};
+
+void* HeapAllocGlobal::allocate(ualni aBlockSize) {
+
+ static_assert(HEAD_SIZE % ALIGNED_SIZE == 0, "Heap Allocator Configuration Error");
+
+ if (aBlockSize % ALIGNED_SIZE) {
+ aBlockSize = (aBlockSize / ALIGNED_SIZE + 1) * ALIGNED_SIZE;
+ }
+
+// 1) Allocate the block
+ALLOCATE:
+ auto head = (MemHead*) malloc(aBlockSize + WRAP_SIZE * 2 + HEAD_SIZE);
+ if (!head) {
+ printf("WARNING : Cant allocate memory. Trying again\n");
+ goto ALLOCATE; // Just freeze if no memory is available
+ }
+
+ auto wrap_top = (int1*) (head + 1);
+ auto data = wrap_top + WRAP_SIZE;
+ auto wrap_bottom = data + aBlockSize;
+
+ head->mBlockSize = aBlockSize;
+ head->mIgnored = mIgnore;
+
+ // 2) Link with existing blocks
+ mMutex.lock();
+ mNumAllocations++;
+ if (mEntry) {
+ DEBUG_ASSERT(mEntry->mNext == nullptr)
+ head->mNext = nullptr;
+ head->mPrev = mEntry;
+ mEntry->mNext = head;
+ } else {
+ head->mNext = nullptr;
+ head->mPrev = nullptr;
+ }
+ mEntry = head;
+
+ // 3) Trace the stack
+#ifdef MEM_STACK_TRACE
+ // check if somewhat decides to call new within static variable initialization
+ head->mCallStack = (mEnableCallstack && mCallstack.initialized) ? mCallstack.getSnapshot() : nullptr;
+#endif
+
+ mMutex.unlock();
+
+ // 4) Wrap fill
+ memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
+ memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
+
+ // 5) clear data
+#ifdef MEM_CLEAR_ON_ALLOC
+ memSetVal(data, aBlockSize, CLEAR_ALLOC_VAL);
+#endif
+
+ return data;
+}
+
+void HeapAllocGlobal::deallocate(void* aPtr) {
+ if (!aPtr) return;
+
+ // 1) Restore the pointers
+ auto head = ((MemHead*) ((int1*) aPtr - WRAP_SIZE)) - 1;
+ auto wrap_top = (int1*) (head + 1);
+ auto data = wrap_top + WRAP_SIZE;
+ auto wrap_bottom = data + head->mBlockSize;
+
+ // 2) Unlink with blocks
+ mMutex.lock();
+ mNumAllocations--;
+ DEBUG_ASSERT(!mEntry->mNext)
+ if (head->mNext) head->mNext->mPrev = head->mPrev;
+ if (head->mPrev) head->mPrev->mNext = head->mNext;
+ if (head == mEntry) {
+ mEntry = head->mPrev;
+ }
+
+ if (!head->mIgnored) {
+ // 3) Check the wrap
+ if (memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL)) {
+#ifdef MEM_STACK_TRACE
+ if (head->mCallStack) mCallstack.printSnapshot(head->mCallStack);
+#endif
+ ASSERT(!"Allocated Block Wrap Corrupted!")
+ }
+
+ if (memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL)) {
+#ifdef MEM_STACK_TRACE
+ if (head->mCallStack) mCallstack.printSnapshot(head->mCallStack);
+#endif
+ ASSERT(!"Allocated Block Wrap Corrupted!")
+ }
+
+ // 4) clear data
+#ifdef MEM_CLEAR_ON_ALLOC
+ memSetVal(data, head->mBlockSize, CLEAR_DEALLOC_VAL);
+#endif
+ }
+
+ mMutex.unlock();
+
+ // 5) free the block
+ free(head);
+}
+
+bool HeapAllocGlobal::checkLeaks() {
+ ualni ignoredCount = 0;
+ for (auto iter = mEntry; iter; iter = iter->mPrev) {
+ ignoredCount += iter->mIgnored;
+ }
+
+ // 1) Check for not deallocated memory
+ if (mNumAllocations && ignoredCount < mNumAllocations) {
+
+#ifdef MEM_STACK_TRACE
+ for (auto iter = mEntry; iter; iter = iter->mPrev) {
+ if (!iter->mIgnored && iter->mCallStack) mCallstack.printSnapshot(iter->mCallStack);
+ }
+#endif
+
+ printf(" Count : %llu", mNumAllocations - ignoredCount);
+
+ ASSERT(!"Destruction of not freed Allocator")
+ return true;
+ }
+ return false;
+}
+
+void HeapAllocGlobal::startIgnore() {
+ mMutex.lock();
+ mIgnore = true;
+ mMutex.unlock();
+}
+
+void HeapAllocGlobal::stopIgnore() {
+ mMutex.lock();
+ mIgnore = false;
+ mMutex.unlock();
+}
+
+ualni HeapAllocGlobal::getNAllocations() {
+ return mNumAllocations;
+}
+
+void HeapAllocGlobal::enableCallstack() {
+ mMutex.lock();
+ mEnableCallstack = true;
+ mMutex.unlock();
+}
+
+void HeapAllocGlobal::disableCallstack() {
+ mMutex.lock();
+ mEnableCallstack = false;
+ mMutex.unlock();
+}
+
+HeapAllocGlobal::~HeapAllocGlobal() = default;
+
+#endif
diff --git a/Allocators/public/Allocators.hpp b/Allocators/public/Allocators.hpp
index da01ede..ad29c25 100644
--- a/Allocators/public/Allocators.hpp
+++ b/Allocators/public/Allocators.hpp
@@ -1,29 +1,27 @@
#pragma once
-#include "Module.hpp"
+#include "Utils.hpp"
-#include "HeapAllocatorGlobal.hpp"
-#include "HeapAllocator.hpp"
#include "ChunkAllocator.hpp"
+#include "HeapAllocator.hpp"
+#include "HeapAllocatorGlobal.hpp"
#include "PoolAllocator.hpp"
namespace tp {
extern ModuleManifest gModuleAllocators;
}
-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) noexcept;
-void operator delete[](void* aPtr) noexcept;
+void operator delete(void* aPtr) noexcept;
+void operator delete[](void* aPtr) noexcept;
void* operator new(std::size_t aSize, tp::HeapAlloc& aAlloc);
void* operator new[](std::size_t aSize, tp::HeapAlloc& aAlloc);
-void operator delete(void* aPtr, tp::HeapAlloc& aAlloc);
-void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc);
+void operator delete(void* aPtr, tp::HeapAlloc& aAlloc);
+void operator delete[](void* aPtr, tp::HeapAlloc& aAlloc);
void* operator new(std::size_t aSize, tp::HeapAllocGlobal& aAlloc);
void* operator new[](std::size_t aSize, tp::HeapAllocGlobal& aAlloc);
-void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc);
-void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc);
\ No newline at end of file
+void operator delete(void* aPtr, tp::HeapAllocGlobal& aAlloc);
+void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc);
\ No newline at end of file
diff --git a/Allocators/public/ChunkAllocator.hpp b/Allocators/public/ChunkAllocator.hpp
index 99247fa..42f84e6 100644
--- a/Allocators/public/ChunkAllocator.hpp
+++ b/Allocators/public/ChunkAllocator.hpp
@@ -15,6 +15,7 @@
*/
#include "Environment.hpp"
+#include "HeapAllocatorGlobal.hpp"
#include "PrivateConfig.hpp"
namespace tp {
@@ -22,7 +23,7 @@ namespace tp {
// Chunk Allocator
// Constant time allocations and de-allocations in any order.
// Memory blocks are fixed in size and number of blocks can not exceed given parameter.
- template
+ template
class ChunkAlloc {
enum : ualni {
@@ -75,24 +76,24 @@ namespace tp {
// 2) Find free block and update next free block
auto data = mNextBlock;
- mNextBlock = (ualni*)(*data);
+ mNextBlock = (ualni*) (*data);
mNumFreeBlocks--;
- #ifdef MEM_DEBUG
- // 3) Fill Wrap and offset data
- auto wrap_top = data;
- auto wrap_bottom = data + WRAP_SIZE_ALN + dataSize();
+#ifdef MEM_DEBUG
+ // 3) Fill Wrap and offset data
+ auto wrap_top = data;
+ auto wrap_bottom = data + WRAP_SIZE_ALN + dataSize();
- memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
- memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
+ memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
+ memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
- // 4) Clear data
- #ifdef MEM_CLEAR_ON_ALLOC
- memSetVal(data + WRAP_SIZE_ALN, dataSize() * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
- #endif
+// 4) Clear data
+#ifdef MEM_CLEAR_ON_ALLOC
+ memSetVal(data + WRAP_SIZE_ALN, dataSize() * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
+#endif
- data += WRAP_SIZE_ALN;
- #endif
+ data += WRAP_SIZE_ALN;
+#endif
return data;
}
@@ -100,26 +101,26 @@ namespace tp {
void deallocate(void* aPtr) {
DEBUG_ASSERT(aPtr >= mBuff && aPtr < mBuff + tNumBlocks * blockSize())
- auto block = (ualni*)aPtr;
+ auto block = (ualni*) aPtr;
- #ifdef MEM_DEBUG
- // 3) Check Wrap and offset data
- auto wrap_bottom = block + dataSize();
- auto wrap_top = block - WRAP_SIZE_ALN;
- block = wrap_top;
+#ifdef MEM_DEBUG
+ // 3) Check Wrap and offset data
+ auto wrap_bottom = block + dataSize();
+ auto wrap_top = block - WRAP_SIZE_ALN;
+ block = wrap_top;
- // 3) Check the wrap
- ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
- ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
+ // 3) Check the wrap
+ ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
+ ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
- // 4) Clear data
- #ifdef MEM_CLEAR_ON_ALLOC
- memSetVal(block, blockSize() * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
- #endif
+// 4) Clear data
+#ifdef MEM_CLEAR_ON_ALLOC
+ memSetVal(block, blockSize() * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
+#endif
- #endif
+#endif
- (*block) = (ualni)mNextBlock;
+ (*block) = (ualni) mNextBlock;
mNextBlock = block;
mNumFreeBlocks++;
}
diff --git a/Allocators/public/HeapAllocatorGlobal.hpp b/Allocators/public/HeapAllocatorGlobal.hpp
index 18cae0e..8c8b892 100644
--- a/Allocators/public/HeapAllocatorGlobal.hpp
+++ b/Allocators/public/HeapAllocatorGlobal.hpp
@@ -1,6 +1,7 @@
#pragma once
-#include "Module.hpp"
+#include "Callstack.hpp"
+#include
namespace tp {
@@ -9,6 +10,12 @@ namespace tp {
#ifdef MEM_DEBUG
static ualni mNumAllocations;
static struct MemHead* mEntry;
+ static std::mutex mMutex;
+ static bool mIgnore;
+ static bool mEnableCallstack;
+#ifdef MEM_STACK_TRACE // Save stack on allocation call
+ static CallStackCapture mCallstack;
+#endif
#endif
public:
@@ -20,6 +27,13 @@ namespace tp {
static void deallocate(void* aPtr);
static bool checkLeaks();
+ static void startIgnore();
+ static void stopIgnore();
+ static ualni getNAllocations();
+
+ static void enableCallstack();
+ static void disableCallstack();
+
public:
[[nodiscard]] bool checkWrap() const { return false; }
void checkValid() {}
diff --git a/Allocators/public/NewPlacement.hpp b/Allocators/public/NewPlacement.hpp
new file mode 100644
index 0000000..7641add
--- /dev/null
+++ b/Allocators/public/NewPlacement.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include "Common.hpp"
+
+#ifndef ENV_OS_WINDOWS
+inline void* operator new(std::size_t aSize, void* aWhere) noexcept { return aWhere; }
+inline void* operator new[](std::size_t aSize, void* aWhere) noexcept { return aWhere; }
+#endif
diff --git a/Allocators/public/PoolAllocator.hpp b/Allocators/public/PoolAllocator.hpp
index 98f1596..3fbe5d9 100644
--- a/Allocators/public/PoolAllocator.hpp
+++ b/Allocators/public/PoolAllocator.hpp
@@ -21,14 +21,14 @@ namespace tp {
// Pool Allocator
// Overcomes chunk allocator fixed number of max allocations
- template
+ template
class PoolAlloc {
typedef ChunkAlloc Chunk;
struct Chunks {
- void add(Chunk* aChunk){
+ void add(Chunk* aChunk) {
if (!mBuff) {
mLen = 16;
@@ -57,7 +57,7 @@ namespace tp {
}
}
- void remove(Chunk** del_address){
+ void remove(Chunk** del_address) {
if (mUsedLen == 1) {
mLen = 0;
mUsedLen = 0;
@@ -74,18 +74,16 @@ namespace tp {
mUsedLen--;
// check for buff low usage
- if ((halnf)mUsedLen / (halnf)mLen < 0.25f) {
+ if ((halnf) mUsedLen / (halnf) mLen < 0.25f) {
auto prevBuff = mBuff;
- mBuff = (Chunk**)HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2);
+ mBuff = (Chunk**) HeapAllocGlobal::allocate(sizeof(Chunk*) * mLen / 2);
memCopy(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
mLen /= 2;
HeapAllocGlobal::deallocate(prevBuff);
}
}
- [[nodiscard]] Chunk** find(void* aPtr) {
- return findUtil(mBuff, mBuff + mUsedLen, aPtr) - 1;
- }
+ [[nodiscard]] Chunk** find(void* aPtr) { return findUtil(mBuff, mBuff + mUsedLen, aPtr) - 1; }
[[nodiscard]] Chunk* findNotFull() const {
for (ualni idx = 0; idx < mUsedLen; idx++) {
@@ -134,12 +132,13 @@ namespace tp {
}
void deallocate(void* aPtr) {
+ if (!aPtr) return;
auto chunk = mChunks.find(aPtr);
(*chunk)->deallocate(aPtr);
if ((*chunk)->isEmpty()) {
- if (mFreeChunk == *chunk) mFreeChunk = nullptr;
- HeapAllocGlobal::deallocate(*chunk);
- mChunks.remove(chunk);
+ if (mFreeChunk == *chunk) mFreeChunk = nullptr;
+ HeapAllocGlobal::deallocate(*chunk);
+ mChunks.remove(chunk);
}
}
@@ -153,7 +152,7 @@ namespace tp {
if (i > j) {
ASSERT(mChunks.mBuff[i] > mChunks.mBuff[j])
} else if (i < j) {
- ASSERT(mChunks.mBuff[i] < mChunks.mBuff[j])
+ ASSERT(mChunks.mBuff[i] < mChunks.mBuff[j])
}
}
}
diff --git a/Allocators/public/PrivateConfig.hpp b/Allocators/public/PrivateConfig.hpp
index beacb7e..e8d735e 100644
--- a/Allocators/public/PrivateConfig.hpp
+++ b/Allocators/public/PrivateConfig.hpp
@@ -6,5 +6,4 @@
#define MEM_CLEAR_ON_DEALLOC // Clear data on free
#define MEM_CLEAR_ON_DEALLOC_VAL 0xAA // Clear data on free
#define MEM_CLEAR_ON_ALLOC_VAL 0xCC // Clear data on free
-#define MEM_STACK_TRACE // Save stack on allocation call
#define MEM_STACK_TRACE_MAX_DEPTH 32 // Call stack max depth
\ No newline at end of file
diff --git a/Allocators/tests/TestsAll.cpp b/Allocators/tests/Test.cpp
similarity index 66%
rename from Allocators/tests/TestsAll.cpp
rename to Allocators/tests/Test.cpp
index cb90f78..4b0563d 100644
--- a/Allocators/tests/TestsAll.cpp
+++ b/Allocators/tests/Test.cpp
@@ -1,248 +1,249 @@
-
-#include "Utils.hpp"
-#include "Testing.hpp"
-
-#include "HeapAllocatorGlobal.hpp"
-#include "HeapAllocator.hpp"
-#include "ChunkAllocator.hpp"
-#include "PoolAllocator.hpp"
-
-#include
-
-using namespace tp;
-
-struct TestStruct {
- alni val = 0;
-
- TestStruct() : val(0) {}
- explicit TestStruct(alni val) : val(val) {}
- TestStruct(const TestStruct& in) : val(in.val) {}
-
- ~TestStruct() { val = -1; }
-
- bool operator==(const TestStruct& in) const {
- return in.val == val;
- }
-};
-
-template
-class TestBenches {
-
- tAllocator mAlloc{};
- TestStruct mData[tSize]{};
- TestStruct* mLoaded[tSize]{};
- bool mIsLoaded[tSize]{};
- alni mLoadedNum = 0;
-
-public:
- TestBenches() {
- for (alni i = 0; i < tSize; i++) {
- mData[i].val = i;
- mIsLoaded[i] = false;
- mLoaded[i] = nullptr;
- }
- }
-
- void runTests() {
- try {
- test1();
- test2();
- test3();
- test4();
- test5();
- test6();
- } catch (...) {
- ASSERT(false)
- }
- }
-
-private:
- alni randomIdx(bool state, Range range = { 0, tSize }) {
- RAND:
- auto idx = alni(alnf(range.idxBegin()) + randomFloat() * alnf(range.idxDiff() + 1));
- idx = clamp(idx, alni(0), tSize - 1);
- if (state == mIsLoaded[idx]) goto RAND;
- return idx;
- }
-
- void verifyIntegrity() {
- mAlloc.checkValid();
- ASSERT(!mAlloc.checkWrap())
- for (alni i = 0; i < tSize; i++) {
- if (mIsLoaded[i]) {
- ASSERT(*mLoaded[i] == mData[i])
- }
- }
- }
-
- void loadItem(alni idx) {
- if (mIsLoaded[idx]) return;
- verifyIntegrity();
- mLoaded[idx] = new (mAlloc.allocate(sizeof(TestStruct))) TestStruct(mData[idx]);
- TEST(mLoaded[idx]);
- mIsLoaded[idx] = true;
- mLoadedNum++;
- verifyIntegrity();
- }
-
- void unloadItem(alni idx) {
- if (!mIsLoaded[idx]) return;
- verifyIntegrity();
- mLoaded[idx]->~TestStruct();
- mAlloc.deallocate(mLoaded[idx]);
- mIsLoaded[idx] = false;
- mLoadedNum--;
- verifyIntegrity();
- }
-
- void changeStates(Range rg, bool load, bool reversed = false, bool random = false) {
- for (auto i : rg) {
- alni idx = i;
- if (random) {
- idx = randomIdx(load, rg);
- } else if (reversed) {
- idx = rg.idxEnd() - i - 1;
- }
- (load) ? loadItem(idx) : unloadItem(idx);
- }
- }
-
- // full down-up load then up-down unload
- void test1() {
- changeStates({ 0, tSize }, true);
- changeStates({ 0, tSize }, false, true);
- }
-
- // full down-up load then down-up unload
- void test2() {
- changeStates({0, tSize}, true);
- changeStates({0, tSize}, false);
- }
-
- // full random load then random unload
- void test3() {
- changeStates({0, tSize}, true, false, true);
- changeStates({0, tSize}, false, false, true);
- }
-
- // combo tests 1-3
- void test4() {
- test1();
- test1();
-
- test2();
- test2();
-
- test3();
- test3();
- }
-
- static alnf sineUpFunction(alnf aSize, alnf aX, bool aReverse) {
- alnf end = 4 * 3.14159;
- alnf a = (2 / 7.f) * aSize;
- alnf b = end / aSize;
-
- alni c = ((-1 * aReverse) + (1 * !aReverse));
- alnf c1 = (aX - (end * aReverse)) / b;
- alnf c2 = (a * sin(aX - (end * aReverse)));
- alnf out = c1 + c2;
- return (alnf) c * out;
- }
-
- // sin load & sin unload with ~1/2 drop factor
- void test5() {
- alnf end = 4 * 3.14159;
- alnf step = end / 4.f;
-
- for (char i = 0; i < 2; i++) {
- for (alnf x = 0; x <= end; x += step) {
-
- alni target_alloc_count = (alni) ceil(sineUpFunction(tSize, x, i));
- target_alloc_count = clamp(target_alloc_count, alni(0), tSize);
-
- while (mLoadedNum > target_alloc_count) {
- unloadItem(randomIdx(0));
- }
- while (mLoadedNum < target_alloc_count) {
- loadItem(randomIdx(1));
- }
- }
- }
- }
-
- void checkWrap(ualni offset, bool after) {
- offset = clamp(offset, (ualni) 1, (ualni) MEM_WRAP_SIZE);
-
- TestStruct* ts = mLoaded[randomIdx(0)];
- ualni shift = (sizeof(TestStruct) * after) + (offset - 1) * after - offset * (!after);
- uint1* address = (((uint1*)ts) + shift);
-
- uint1 val = *address;
- *address = 5;
-
- TEST(!mAlloc.checkWrap());
-
- *address = val;
- }
-
- // mem guards test
- void test6() {
- changeStates({0, tSize}, 1);
- #ifdef MEM_DEBUG
- for (alni after = 0; after < 2; after++) {
- for (alni offset = 1; offset <= MEM_WRAP_SIZE; offset++) {
- checkWrap(offset, after);
- }
- }
- #endif
- changeStates({0, tSize}, 0);
- }
-};
-
-const ualni size = 1000;
-
-template
-void testAlloc() {
- try {
- TestBenches heapTests{};
- heapTests.runTests();
- } catch (...) {
- TEST(false);
- }
-}
-
-TEST_DEF_STATIC(GlobalHeap) {
- testAlloc();
-}
-
-TEST_DEF_STATIC(Heap) {
- testAlloc();
-}
-
-TEST_DEF_STATIC(Chunk) {
- testAlloc>();
- testAlloc>();
-}
-
-TEST_DEF_STATIC(Pool) {
- testAlloc>();
- testAlloc>();
- testAlloc>();
-}
-
-TEST_DEF_STATIC(Simple) {
- auto a = new TestStruct(-1);
- delete a;
-}
-
-TEST_DEF(All) {
- testSimple();
-
- testGlobalHeap();
- testHeap();
- testChunk();
- testPool();
-
- // TEST(HeapAllocGlobal::checkLeaks());
- // TEST(false);
+
+#include "UnitTest++/UnitTest++.h"
+
+#include "Allocators.hpp"
+#include "Utils.hpp"
+
+#include
+
+using namespace tp;
+
+struct TestStruct {
+ alni val = 0;
+
+ TestStruct() :
+ val(0) {}
+ explicit TestStruct(alni val) :
+ val(val) {}
+ TestStruct(const TestStruct& in) :
+ val(in.val) {}
+
+ ~TestStruct() { val = -1; }
+
+ bool operator==(const TestStruct& in) const { return in.val == val; }
+};
+
+template
+class TestBenches {
+
+ tAllocator mAlloc{};
+ TestStruct mData[tSize]{};
+ TestStruct* mLoaded[tSize]{};
+ bool mIsLoaded[tSize]{};
+ alni mLoadedNum = 0;
+
+public:
+ TestBenches() {
+ for (alni i = 0; i < tSize; i++) {
+ mData[i].val = i;
+ mIsLoaded[i] = false;
+ mLoaded[i] = nullptr;
+ }
+ }
+
+ void runTests() {
+ try {
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+ test6();
+ } catch (...) {
+ ASSERT(false)
+ }
+ }
+
+private:
+ alni randomIdx(bool state, Range range = { 0, tSize }) {
+ RAND:
+ auto idx = alni(alnf(range.idxBegin()) + randomFloat() * alnf(range.idxDiff() + 1));
+ idx = clamp(idx, alni(0), tSize - 1);
+ if (state == mIsLoaded[idx]) goto RAND;
+ return idx;
+ }
+
+ void verifyIntegrity() {
+ mAlloc.checkValid();
+ ASSERT(!mAlloc.checkWrap())
+ for (alni i = 0; i < tSize; i++) {
+ if (mIsLoaded[i]) {
+ ASSERT(*mLoaded[i] == mData[i])
+ }
+ }
+ }
+
+ void loadItem(alni idx) {
+ if (mIsLoaded[idx]) return;
+ verifyIntegrity();
+ mLoaded[idx] = new (mAlloc.allocate(sizeof(TestStruct))) TestStruct(mData[idx]);
+ ASSERT(mLoaded[idx]);
+ mIsLoaded[idx] = true;
+ mLoadedNum++;
+ verifyIntegrity();
+ }
+
+ void unloadItem(alni idx) {
+ if (!mIsLoaded[idx]) return;
+ verifyIntegrity();
+ mLoaded[idx]->~TestStruct();
+ mAlloc.deallocate(mLoaded[idx]);
+ mIsLoaded[idx] = false;
+ mLoadedNum--;
+ verifyIntegrity();
+ }
+
+ void changeStates(Range rg, bool load, bool reversed = false, bool random = false) {
+ for (auto i : rg) {
+ alni idx = i;
+ if (random) {
+ idx = randomIdx(load, rg);
+ } else if (reversed) {
+ idx = rg.idxEnd() - i - 1;
+ }
+ (load) ? loadItem(idx) : unloadItem(idx);
+ }
+ }
+
+ // full down-up load then up-down unload
+ void test1() {
+ changeStates({ 0, tSize }, true);
+ changeStates({ 0, tSize }, false, true);
+ }
+
+ // full down-up load then down-up unload
+ void test2() {
+ changeStates({ 0, tSize }, true);
+ changeStates({ 0, tSize }, false);
+ }
+
+ // full random load then random unload
+ void test3() {
+ changeStates({ 0, tSize }, true, false, true);
+ changeStates({ 0, tSize }, false, false, true);
+ }
+
+ // combo tests 1-3
+ void test4() {
+ test1();
+ test1();
+
+ test2();
+ test2();
+
+ test3();
+ test3();
+ }
+
+ static alnf sineUpFunction(alnf aSize, alnf aX, bool aReverse) {
+ alnf end = 4 * 3.14159;
+ alnf a = (2 / 7.f) * aSize;
+ alnf b = end / aSize;
+
+ alni c = ((-1 * aReverse) + (1 * !aReverse));
+ alnf c1 = (aX - (end * aReverse)) / b;
+ alnf c2 = (a * sin(aX - (end * aReverse)));
+ alnf out = c1 + c2;
+ return (alnf) c * out;
+ }
+
+ // sin load & sin unload with ~1/2 drop factor
+ void test5() {
+ alnf end = 4 * 3.14159;
+ alnf step = end / 4.f;
+
+ for (char i = 0; i < 2; i++) {
+ for (alnf x = 0; x <= end; x += step) {
+
+ alni target_alloc_count = (alni) ceil(sineUpFunction(tSize, x, i));
+ target_alloc_count = clamp(target_alloc_count, alni(0), tSize);
+
+ while (mLoadedNum > target_alloc_count) {
+ unloadItem(randomIdx(0));
+ }
+ while (mLoadedNum < target_alloc_count) {
+ loadItem(randomIdx(1));
+ }
+ }
+ }
+ }
+
+ void checkWrap(ualni offset, bool after) {
+ offset = clamp(offset, (ualni) 1, (ualni) MEM_WRAP_SIZE);
+
+ TestStruct* ts = mLoaded[randomIdx(0)];
+ ualni shift = (sizeof(TestStruct) * after) + (offset - 1) * after - offset * (!after);
+ uint1* address = (((uint1*) ts) + shift);
+
+ uint1 val = *address;
+ *address = 5;
+
+ ASSERT(!mAlloc.checkWrap());
+
+ *address = val;
+ }
+
+ // mem guards test
+ void test6() {
+ changeStates({ 0, tSize }, 1);
+#ifdef MEM_DEBUG
+ for (alni after = 0; after < 2; after++) {
+ for (alni offset = 1; offset <= MEM_WRAP_SIZE; offset++) {
+ checkWrap(offset, after);
+ }
+ }
+#endif
+ changeStates({ 0, tSize }, 0);
+ }
+};
+
+const ualni size = 500;
+
+template
+void testAlloc() {
+ try {
+ TestBenches heapTests{};
+ heapTests.runTests();
+ } catch (...) {
+ ASSERT(false);
+ }
+}
+
+SUITE(Allocators) {
+ TEST(GlobalHeap) { testAlloc(); }
+
+ TEST(Heap) { testAlloc(); }
+
+ TEST(Chunk) {
+ testAlloc>();
+ testAlloc>();
+ }
+
+ TEST(Pool) {
+ testAlloc>();
+ testAlloc>();
+ testAlloc>();
+ }
+
+ TEST(Simple) {
+ auto a = new TestStruct(-1);
+ delete a;
+ }
+}
+
+
+int main() {
+
+ tp::ModuleManifest* deps[] = { &tp::gModuleAllocators, nullptr };
+ tp::ModuleManifest testModule("AllocatorsTest", nullptr, nullptr, deps);
+
+ if (!testModule.initialize()) {
+ return 1;
+ }
+
+ bool res = UnitTest::RunAllTests();
+
+ testModule.deinitialize();
+
+ return res;
}
\ No newline at end of file
diff --git a/Allocators/tests/Tests.cpp b/Allocators/tests/Tests.cpp
deleted file mode 100644
index 760d6c5..0000000
--- a/Allocators/tests/Tests.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-
-#include "Allocators.hpp"
-
-#include "Tests.hpp"
-
-using namespace tp;
-
-int main() {
-
- tp::ModuleManifest* deps[] = { &tp::gModuleAllocators, &tp::gModuleUtils, nullptr };
- tp::ModuleManifest testModule("AllocatorsTest", nullptr, nullptr, deps);
-
- if (!testModule.initialize()) {
- return 1;
- }
-
- testAll();
-
- testModule.deinitialize();
-}
diff --git a/Allocators/tests/Tests.hpp b/Allocators/tests/Tests.hpp
deleted file mode 100644
index 8345a9d..0000000
--- a/Allocators/tests/Tests.hpp
+++ /dev/null
@@ -1,6 +0,0 @@
-#pragma once
-
-#include "Utils.hpp"
-#include "Testing.hpp"
-
-void testAll();
\ No newline at end of file
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d23e6cf..989e7e6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,17 +1,28 @@
-cmake_minimum_required(VERSION 3.2)
-set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/install)
+cmake_minimum_required(VERSION 3.5)
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -gdwarf-4")
-
-enable_testing()
-
-project(Types)
+set(CMAKE_CXX_STANDARD 20)
+# set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/./tmp/install)
+# set(CMAKE_CXX_FLAGS "-fuse-ld=lld-15 ${CMAKE_CXX_FLAGS} -gdwarf-4 ")
add_compile_definitions(MEM_DEBUG)
+project(ModulesRoot)
+
+include(CMakeOptions.txt)
+
add_subdirectory(Modules)
-add_subdirectory(Utils)
+add_subdirectory(Callstack)
add_subdirectory(Containers)
add_subdirectory(Math)
-add_subdirectory(Allocators)
\ No newline at end of file
+add_subdirectory(Strings)
+add_subdirectory(Allocators)
+# add_subdirectory(Language)
+add_subdirectory(Externals)
+add_subdirectory(Connection)
+add_subdirectory(Graphics)
+add_subdirectory(RayTracer)
+add_subdirectory(DataAnalysis)
+add_subdirectory(Objects)
+add_subdirectory(Widgets)
+add_subdirectory(LibraryViewer)
diff --git a/CMakeOptions.txt b/CMakeOptions.txt
new file mode 100644
index 0000000..5768530
--- /dev/null
+++ b/CMakeOptions.txt
@@ -0,0 +1,14 @@
+option(MODULES_MEMORY_DEBUG "Debug memory" ON)
+option(MODULES_MEMORY_DEBUG_STACK_TRACE "Record stack info on memory debug" ON)
+
+set(WINDOWS_LIBRARIES "../../ModulesWindowsLibraries" CACHE STRING "Svn repository with windows libraries https://svn.riouxsvn.com/moduleswindowsl")
+
+if (MODULES_MEMORY_DEBUG)
+ add_compile_definitions(MEM_DEBUG)
+endif ()
+
+if (MODULES_MEMORY_DEBUG_STACK_TRACE)
+ add_compile_definitions(MEM_STACK_TRACE)
+endif ()
+
+enable_testing()
\ No newline at end of file
diff --git a/CMakeSettings.json b/CMakeSettings.json
new file mode 100644
index 0000000..03cbf8d
--- /dev/null
+++ b/CMakeSettings.json
@@ -0,0 +1,15 @@
+{
+ "configurations": [
+ {
+ "name": "x64-Debug",
+ "generator": "Ninja",
+ "configurationType": "Debug",
+ "inheritEnvironments": [ "msvc_x64_x64" ],
+ "buildRoot": "${projectDir}\\out\\build\\${name}",
+ "installRoot": "${projectDir}\\out\\install\\${name}",
+ "cmakeCommandArgs": "",
+ "buildCommandArgs": "",
+ "ctestCommandArgs": ""
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Callstack/CMakeLists.txt b/Callstack/CMakeLists.txt
new file mode 100644
index 0000000..d6ce6cc
--- /dev/null
+++ b/Callstack/CMakeLists.txt
@@ -0,0 +1,14 @@
+project(Callstack)
+
+### ---------------------- 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 Containers)
+
+### -------------------------- Tests -------------------------- ###
+file(GLOB TEST_SOURCES "./tests/*.cpp")
+add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
+target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
+add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
\ No newline at end of file
diff --git a/Callstack/private/Callstack.cpp b/Callstack/private/Callstack.cpp
new file mode 100644
index 0000000..eb1f68c
--- /dev/null
+++ b/Callstack/private/Callstack.cpp
@@ -0,0 +1,332 @@
+
+#include "Callstack.hpp"
+
+#include
+
+using namespace tp;
+
+// frame to reference on no sapce left
+static void errorNoSpaceLeft() {
+ // to disable optimization of function
+ static int dummy;
+ dummy = 10;
+}
+
+ualni CallStackCapture::CallStack::getDepth() const {
+ ualni len = 0;
+ for (long long frame : frames) {
+ if (!frame) {
+ break;
+ }
+ len++;
+ }
+ return len++;
+}
+
+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 (ualni i = 0; i < MAX_CALL_DEPTH_CAPTURE; i++) {
+ 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(MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024 > sizeof(CallStack));
+
+ mBuffLoad = 0;
+ mBuffLen = STACKS_LENGTH;
+ mBuff = (CallStack*) malloc(mBuffLen * sizeof(CallStack));
+
+ mErrorSnapshot.frames[0] = (alni) & errorNoSpaceLeft;
+
+
+ platformInit();
+
+ initialized = true;
+}
+
+const CallStackCapture::CallStack* CallStackCapture::getSnapshot() {
+
+ if (mBuffLoad > mBuffLen) {
+ return &mErrorSnapshot;
+ }
+
+ 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);
+ initialized = false;
+}
+
+// ---------------------------------- Platform Depended ---------------------------------- //
+
+#if defined(ENV_OS_LINUX)
+
+#include
+#include
+#include
+#include
+
+void CallStackCapture::platformWriteStackTrace(CallStack* stack) {
+ auto depth = backtrace((void**) stack->frames, (int) MAX_CALL_DEPTH_CAPTURE - 1);
+ stack->frames[depth] = 0;
+}
+
+static void getGetSourceFromBinaryAddress(const char* binary, const char* address, char* file, ualni* line) {
+ static char buff[1024];
+
+ snprintf(buff, sizeof(buff), "addr2line -e %s -a %s", binary, address);
+ FILE* pipe = popen(buff, "r");
+ if (pipe) {
+ fgets(buff, sizeof(buff), pipe);
+ fgets(buff, sizeof(buff), pipe);
+ pclose(pipe);
+ char* linePtr = strchr(buff, ':');
+
+ printf("%s\n", buff);
+
+ if (linePtr != nullptr && buff[0] != '?' && buff[1] != '?' && buff[2] != ':') {
+ *linePtr = '\0';
+ auto sourceLen = std::strlen(buff);
+ std::strcpy(file, buff + ((sourceLen > MAX_DEBUG_INFO_LEN) ? (sourceLen - MAX_DEBUG_INFO_LEN) : 0));
+ *line = strtoul(linePtr + 1, nullptr, 10);
+ return;
+ }
+ }
+
+ std::strcpy(file, "unresolved");
+ *line = 0;
+}
+
+static void getDemangledName(const char* func, char* out) {
+ int status;
+ size_t funcDemangledSize = MAX_DEBUG_INFO_LEN;
+ char* funcDemangled = (char*) malloc(funcDemangledSize);
+ char* ret = abi::__cxa_demangle(func, funcDemangled, &funcDemangledSize, &status);
+ if (status == 0) {
+ funcDemangled = ret;
+ auto funcLen = std::strlen(funcDemangled);
+ std::strcpy(out, funcDemangled + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0));
+ free(ret);
+ return;
+ }
+ auto funcLen = std::strlen(func);
+ std::strcpy(out, func + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0));
+ free(funcDemangled);
+}
+
+void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
+ void* addrList[1] = { (void*) frame };
+ auto symbolsArray = backtrace_symbols(addrList, 1);
+
+ // 'bin(fun+addr)'
+ char* bin = *symbolsArray;
+ char* func = nullptr;
+ char* offset = nullptr;
+
+ // 'bin fun+addr'
+ for (char* p = bin; *p; ++p) {
+ if (*p == '(') {
+ *p = 0;
+ func = p + 1;
+ } else if (*p == '+') {
+ offset = p;
+ } else if (*p == ')' && offset) {
+ *p = 0;
+ }
+ }
+
+ if (func && offset) {
+ getGetSourceFromBinaryAddress(bin, func, out->file, &out->line);
+
+ if (offset != func) {
+ *offset = 0;
+ getDemangledName(func, out->function);
+ } else {
+ std::strcpy(out->function, "unresolved");
+ }
+ } else {
+ std::strcpy(out->file, "unresolved");
+ std::strcpy(out->function, "unresolved");
+ }
+
+ free(symbolsArray);
+}
+
+void CallStackCapture::printSnapshot(const CallStack* snapshot) {
+ printf("CallStack: \n");
+ if (snapshot) {
+ for (auto frame : *snapshot) {
+ auto symbols = getSymbols(frame.getFrame());
+ printf(" %s ----- %s:%llu\n", symbols->getFunc(), symbols->getFile(), symbols->getLine());
+ }
+ }
+ printf("\n");
+}
+
+void CallStackCapture::logAll() {
+ for (auto cs : *this) {
+ printSnapshot(cs.getCallStack());
+ }
+}
+
+#else
+#include
+#include
+#include
+
+#pragma comment(lib, "dbghelp.lib")
+
+void CallStackCapture::platformInit() {
+ HANDLE process = GetCurrentProcess();
+ SymInitialize(process, NULL, TRUE);
+}
+
+void CallStackCapture::platformDeinit() {
+ HANDLE process = GetCurrentProcess();
+ SymCleanup(process);
+}
+
+void CallStackCapture::platformWriteStackTrace(CallStack* stack) {
+ DWORD hash;
+ CONTEXT context;
+ memset(&context, 0, sizeof(CONTEXT));
+ context.ContextFlags = CONTEXT_FULL;
+ RtlCaptureContext(&context);
+
+ STACKFRAME64 stackFrame;
+ memset(&stackFrame, 0, sizeof(STACKFRAME64));
+#ifdef _M_IX86
+ hash = IMAGE_FILE_MACHINE_I386;
+ stackFrame.AddrPC.Offset = context.Eip;
+ stackFrame.AddrPC.Mode = AddrModeFlat;
+ stackFrame.AddrFrame.Offset = context.Ebp;
+ stackFrame.AddrFrame.Mode = AddrModeFlat;
+ stackFrame.AddrStack.Offset = context.Esp;
+ stackFrame.AddrStack.Mode = AddrModeFlat;
+#elif _M_X64
+ hash = IMAGE_FILE_MACHINE_AMD64;
+ stackFrame.AddrPC.Offset = context.Rip;
+ stackFrame.AddrPC.Mode = AddrModeFlat;
+ stackFrame.AddrFrame.Offset = context.Rbp;
+ stackFrame.AddrFrame.Mode = AddrModeFlat;
+ stackFrame.AddrStack.Offset = context.Rsp;
+ stackFrame.AddrStack.Mode = AddrModeFlat;
+#endif
+
+ for (int frameIndex = 0; frameIndex < MAX_CALL_DEPTH_CAPTURE; ++frameIndex) {
+ if (!StackWalk64(
+ hash,
+ GetCurrentProcess(),
+ GetCurrentThread(),
+ &stackFrame,
+ &context,
+ NULL,
+ SymFunctionTableAccess64,
+ SymGetModuleBase64,
+ NULL
+ )) {
+
+ stack->frames[frameIndex] = 0;
+ break;
+ }
+ stack->frames[frameIndex] = (alni) (void*) stackFrame.AddrPC.Offset;
+ }
+ stack->frames[MAX_CALL_DEPTH_CAPTURE - 1] = 0;
+}
+
+void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
+ HANDLE process = GetCurrentProcess();
+
+ IMAGEHLP_LINE64 lineInfo;
+ DWORD displacement;
+ lineInfo.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+ if (SymGetLineFromAddr64(process, (DWORD64) frame, &displacement, &lineInfo)) {
+ strcpy(out->file, lineInfo.FileName);
+ out->line = lineInfo.LineNumber;
+ } else {
+ strcpy(out->file, "unresolved");
+ out->line = 0;
+ }
+
+ DWORD64 displacementSym = 0;
+ char symbolBuffer[sizeof(IMAGEHLP_SYMBOL64) + MAX_PATH] = { 0 };
+ IMAGEHLP_SYMBOL64* symbol = (IMAGEHLP_SYMBOL64*) symbolBuffer;
+ symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
+ symbol->MaxNameLength = MAX_PATH;
+
+ if (SymGetSymFromAddr64(process, (DWORD64) frame, &displacementSym, symbol)) {
+ strcpy(out->function, symbol->Name);
+ } else {
+ strcpy(out->function, "unresolved");
+ }
+}
+
+void CallStackCapture::printSnapshot(const CallStack* snapshot) {
+ printf("CallStack: \n");
+ if (snapshot) {
+ for (int i = 0; i < snapshot->getDepth(); ++i) {
+ DebugSymbols symbols;
+ platformWriteDebugSymbols(snapshot->frames[i], &symbols);
+ printf(" %s ----- %s:%lu\n", symbols.function, symbols.file, symbols.line);
+ }
+ }
+ printf("\n");
+}
+
+void CallStackCapture::logAll() {
+ for (auto cs : *this) {
+ printSnapshot(cs.getCallStack());
+ }
+}
+#endif
\ No newline at end of file
diff --git a/Utils/public/Debugging.hpp b/Callstack/public/Callstack.hpp
similarity index 68%
rename from Utils/public/Debugging.hpp
rename to Callstack/public/Callstack.hpp
index 26f0cf8..aac0e3c 100644
--- a/Utils/public/Debugging.hpp
+++ b/Callstack/public/Callstack.hpp
@@ -3,18 +3,20 @@
#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;
+ bool initialized = false;
+
+ enum {
+ MAX_CALL_DEPTH_CAPTURE = 16,
+ MAX_CALL_CAPTURES_MEM_SIZE_MB = 32,
+ MAX_DEBUG_INFO_LEN = 64,
+ };
+
class CallStack {
friend CallStackCapture;
FramePointer frames[MAX_CALL_DEPTH_CAPTURE];
@@ -24,23 +26,27 @@ namespace tp {
class Iterator {
const FramePointer* mFrame;
+
public:
- explicit Iterator(const FramePointer* frame) : mFrame(frame) {};
+ 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()); }
+ [[nodiscard]] Iterator begin() const { return Iterator(frames); }
+ [[nodiscard]] Iterator end() const { return Iterator(frames + getDepth()); }
};
+ enum { STACKS_LENGTH = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack) };
+
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; }
@@ -54,41 +60,13 @@ namespace tp {
[[nodiscard]] const CallStack* getSnapshot();
const DebugSymbols* getSymbols(FramePointer fp);
- static void printSnapshot(const CallStack* snapshot);
- void logLeaks();
-
- public:
-
- template
- void write(Saver& file) {
- file.write(mBuffLoad);
- for (auto cs : *this) {
- file.write(cs.getCallStack()->getDepth());
- for (auto frame : *cs.getCallStack()) {
- file.write((ualni) frame.getFrame());
- }
- }
- file.write(mSymbols);
- }
-
- // independent of the configuration
- template
- void read(Loader& file) {
- clear();
- ualni loadLen;
- file.read(loadLen);
- for (auto cs = loadLen; cs; cs--) {
- ualni callStackLen;
- file.read(callStackLen);
- for (auto fp = callStackLen; fp; fp--) {
- // --
- }
- }
- }
+ void printSnapshot(const CallStack* snapshot);
+ void logAll();
public:
class Iterator {
const CallStack* mSnapshot;
+
public:
explicit Iterator(const CallStack* start) : mSnapshot(start) {};
const CallStack* getCallStack() { return mSnapshot; }
@@ -98,10 +76,9 @@ namespace tp {
};
[[nodiscard]] Iterator begin() const { return Iterator(mBuff); }
- [[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); }
+ [[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); }
private:
-
struct CallStackKey {
CallStack* cs;
bool operator==(const CallStackKey& in) const;
@@ -110,16 +87,19 @@ namespace tp {
static void platformWriteStackTrace(CallStack* stack);
static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out);
[[nodiscard]] static ualni hashCallStack(CallStackKey key);
+ void platformInit();
+ void platformDeinit();
void clear();
private:
+ CallStack mErrorSnapshot;
+
ualni mBuffLen;
ualni mBuffLoad;
CallStack* mBuff;
+
Map mSnapshots;
Map mSymbols;
};
-
- extern CallStackCapture* gCSCapture;
}
diff --git a/Callstack/tests/Test.cpp b/Callstack/tests/Test.cpp
new file mode 100644
index 0000000..e5ed3b7
--- /dev/null
+++ b/Callstack/tests/Test.cpp
@@ -0,0 +1,44 @@
+
+#include "UnitTest++/UnitTest++.h"
+
+#include "Callstack.hpp"
+
+#include
+
+using namespace tp;
+
+void common(CallStackCapture& cs) { auto tmp = cs.getSnapshot(); }
+
+void first(CallStackCapture& cs) {
+ common(cs);
+ common(cs);
+ common(cs);
+}
+
+void second(CallStackCapture& cs) {
+ common(cs);
+ common(cs);
+ common(cs);
+ common(cs);
+}
+
+void third(CallStackCapture& cs) {
+ common(cs);
+ common(cs);
+}
+
+void root(CallStackCapture& cs) {
+ first(cs);
+ second(cs);
+ third(cs);
+}
+
+SUITE(Utils) {
+ TEST(CallStackCapture) {
+ CallStackCapture callstack;
+ root(callstack);
+ callstack.logAll();
+ }
+}
+
+int main() { return UnitTest::RunAllTests(); }
\ No newline at end of file
diff --git a/Connection/CMakeLists.txt b/Connection/CMakeLists.txt
new file mode 100644
index 0000000..a4487b7
--- /dev/null
+++ b/Connection/CMakeLists.txt
@@ -0,0 +1,18 @@
+project(Connection)
+
+set(BINDINGS_INCLUDE ./../Externals/asio/asio/include)
+
+### ---------------------- Static Library --------------------- ###
+file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
+file(GLOB HEADERS "./public/*.hpp")
+add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
+target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
+target_include_directories(${PROJECT_NAME} PRIVATE ${BINDINGS_INCLUDE})
+target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
+
+### -------------------------- Tests -------------------------- ###
+enable_testing()
+file(GLOB TEST_SOURCES "./tests/*.cpp")
+add_executable(Test${PROJECT_NAME} ${TEST_SOURCES})
+target_link_libraries(Test${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
+add_test(NAME Test${PROJECT_NAME} COMMAND Test${PROJECT_NAME})
\ No newline at end of file
diff --git a/Connection/private/LocalConnection.cpp b/Connection/private/LocalConnection.cpp
new file mode 100644
index 0000000..efe7648
--- /dev/null
+++ b/Connection/private/LocalConnection.cpp
@@ -0,0 +1,84 @@
+#include "LocalConnection.hpp"
+
+#include "bindings/Disk.hpp"
+
+#include
+
+using namespace tp;
+
+bool LocalConnection::Location::exists() const {
+ // TODO : fixme
+ FILE* file = fopen(mLocation.c_str(), "r");
+ if (file) {
+ // File exists, close it and return 1
+ fclose(file);
+ return true;
+ }
+ return false;
+}
+
+bool LocalConnection::connect(const Location& location, const Type& connectionInfo) {
+ DEBUG_ASSERT(!mStatus.isOpened());
+ if (mStatus.isOpened()) return false;
+
+ auto handle = new LocalConnectionContext();
+
+ switch (connectionInfo.getType()) {
+ case Type::READ: handle->open(location.getLocation().c_str(), true); break;
+ case Type::WRITE: handle->open(location.getLocation().c_str(), false); break;
+ default: break;
+ };
+
+ if (!handle->isOpen()) {
+ mStatus.setStatus(Status::DENIED);
+ delete handle;
+ return false;
+ }
+
+ mStatus.setStatus(Status::OPENED);
+ mHandle = handle;
+ mConnectionType = connectionInfo;
+ return true;
+}
+
+bool LocalConnection::disconnect() {
+ DEBUG_ASSERT(mStatus.isOpened());
+ if (!mStatus.isOpened() || !mHandle) return false;
+ mHandle->close();
+ delete mHandle;
+ mStatus.setStatus(Status::CLOSED);
+ return true;
+}
+
+bool LocalConnection::setPointer(BytePointer pointer) {
+ DEBUG_ASSERT(mStatus.isOpened());
+ if (!mStatus.isOpened()) return false;
+ mPointer = pointer;
+ return true;
+}
+
+bool LocalConnection::readBytes(Byte* bytes, SizeBytes size) {
+ DEBUG_ASSERT(mStatus.isOpened() && mConnectionType.isRead());
+ if (!mStatus.isOpened() || !mConnectionType.isRead()) return false;
+
+ mHandle->seekp(mPointer);
+ mHandle->read(bytes, size);
+ mPointer += size;
+ return true;
+}
+
+bool LocalConnection::writeBytes(const Byte* bytes, SizeBytes size) {
+ DEBUG_ASSERT(mStatus.isOpened() && mConnectionType.isWrite());
+ if (!mStatus.isOpened() || !mConnectionType.isWrite()) return false;
+
+ mHandle->seekp(mPointer);
+ mHandle->write(bytes, size);
+ mPointer += size;
+ return true;
+}
+
+LocalConnection::SizeBytes LocalConnection::size() {
+ DEBUG_ASSERT(mStatus.isOpened());
+ if (!mStatus.isOpened() || !mHandle) return 0;
+ return mHandle->size();
+}
diff --git a/Connection/private/RemoteConnection.cpp b/Connection/private/RemoteConnection.cpp
new file mode 100644
index 0000000..9495b4f
--- /dev/null
+++ b/Connection/private/RemoteConnection.cpp
@@ -0,0 +1,2 @@
+
+#include "RemoteConnection.hpp"
diff --git a/Connection/private/bindings/Disk.cpp b/Connection/private/bindings/Disk.cpp
new file mode 100644
index 0000000..97d8cc9
--- /dev/null
+++ b/Connection/private/bindings/Disk.cpp
@@ -0,0 +1,53 @@
+#include "Disk.hpp"
+
+#include
+
+using namespace tp;
+
+ualni tp::LocalConnectionContext::size() {
+ auto strm = (std::fstream*) stream;
+ ualni out = 0;
+ strm->seekg(0, std::ios::beg);
+ out = strm->tellg();
+ strm->seekg(0, std::ios::end);
+ out = (ualni) strm->tellg() - out;
+ return out;
+}
+
+LocalConnectionContext::LocalConnectionContext() { stream = new std::fstream(); }
+
+LocalConnectionContext::~LocalConnectionContext() {
+ auto strm = (std::fstream*) stream;
+ delete strm;
+}
+
+bool LocalConnectionContext::isOpen() {
+ auto strm = (std::fstream*) stream;
+ return strm->is_open();
+}
+
+void LocalConnectionContext::close() {
+ auto strm = (std::fstream*) stream;
+ strm->close();
+}
+
+void LocalConnectionContext::seekp(ualni in) {
+ auto strm = (std::fstream*) stream;
+ strm->seekp(in);
+}
+
+void LocalConnectionContext::read(int1* in, ualni size) {
+ auto strm = (std::fstream*) stream;
+ strm->read(in, size);
+}
+
+void LocalConnectionContext::write(const int1* in, ualni size) {
+ auto strm = (std::fstream*) stream;
+ strm->write(in, size);
+}
+
+void LocalConnectionContext::open(const char* path, bool read) {
+ auto strm = (std::fstream*) stream;
+ if (read) strm->open(path, std::ios::in | std::ios::binary | std::ios::app);
+ else strm->open(path, std::ios::out | std::ios::binary | std::ios::trunc);
+}
diff --git a/Connection/private/bindings/Disk.hpp b/Connection/private/bindings/Disk.hpp
new file mode 100644
index 0000000..ad53c43
--- /dev/null
+++ b/Connection/private/bindings/Disk.hpp
@@ -0,0 +1,18 @@
+#include "Common.hpp"
+
+namespace tp {
+ class LocalConnectionContext {
+ void* stream;
+
+ public:
+ LocalConnectionContext();
+ ~LocalConnectionContext();
+ void open(const char*, bool);
+ bool isOpen();
+ void close();
+ void seekp(ualni);
+ void read(int1*, ualni);
+ void write(const int1*, ualni);
+ ualni size();
+ };
+}
\ No newline at end of file
diff --git a/Connection/private/bindings/Network.cpp b/Connection/private/bindings/Network.cpp
new file mode 100644
index 0000000..1d5a33f
--- /dev/null
+++ b/Connection/private/bindings/Network.cpp
@@ -0,0 +1,116 @@
+#include "Network.hpp"
+
+#include "asio.hpp"
+
+#include
+
+using namespace tp;
+
+class tp::ServerContext {
+ friend class Server;
+
+ asio::io_context context;
+ asio::ip::tcp::acceptor socket;
+ typedef asio::ip::tcp::socket Socket;
+
+ ServerContext() :
+ socket(context) {}
+
+ ~ServerContext() {
+ context.stop();
+ socket.close();
+ }
+};
+
+class tp::ClientContext {
+ friend Client;
+
+ asio::io_context context;
+ asio::ip::tcp::socket socket;
+
+public:
+ ClientContext() :
+ socket(context) {}
+
+ ~ClientContext() {
+ context.stop();
+ socket.close();
+ }
+};
+
+// --------------------------------------------------------------------------------------- //
+
+Server::Server() { mContext = new ServerContext(); }
+
+Server::~Server() { delete mContext; }
+
+void Server::start(ualni port) {
+ mContext->socket.open(asio::ip::tcp::v4());
+ mContext->socket.bind(asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port));
+ mContext->socket.listen();
+ std::cout << "Server listening on port " << port << std::endl;
+}
+
+Server::Socket Server::accept() {
+ auto clientSocket = new asio::ip::tcp::socket(mContext->context);
+ mContext->socket.accept(*clientSocket);
+ std::cout << "New client accepted " << std::endl;
+ return clientSocket;
+}
+
+bool Server::read(Socket client, int1* message, halni messageSize) {
+ try {
+ std::size_t bytesRead = asio::read(*(ServerContext::Socket*) client, asio::buffer(message, messageSize));
+ return bytesRead == messageSize;
+ } catch (const std::exception& e) {
+ std::cerr << "Error during read: " << e.what() << "\n";
+ return false;
+ }
+}
+
+bool Server::write(Socket client, const int1* message, halni messageSize) {
+ try {
+ std::size_t bytesWritten = asio::write(*(ServerContext::Socket*) client, asio::buffer(message, messageSize));
+ return bytesWritten == messageSize;
+ } catch (const std::exception& e) {
+ std::cerr << "Error during write: " << e.what() << "\n";
+ return false;
+ }
+}
+
+// --------------------------------------------------------------------------------------- //
+
+Client::Client() { mContext = new ClientContext(); }
+
+Client::~Client() { delete mContext; }
+
+bool Client::connect(const char* IP, ualni PORT) {
+ try {
+ asio::ip::tcp::endpoint endpoint(asio::ip::address::from_string(IP), PORT);
+ mContext->socket.connect(endpoint);
+ return true;
+ } catch (const std::exception& e) {
+ std::cerr << "Error during read: " << e.what() << "\n";
+ return false;
+ }
+}
+
+bool Client::read(int1* buff, halni size) {
+ try {
+ std::size_t bytesRead = asio::read(mContext->socket, asio::buffer(buff, size));
+ return bytesRead == size;
+ } catch (const std::exception& e) {
+ std::cerr << "Error during read: " << e.what() << "\n";
+ return false;
+ }
+}
+
+bool Client::write(const int1* message, halni messageSize) {
+ try {
+ std::size_t bytesWritten = asio::write(mContext->socket, asio::buffer(message, messageSize));
+ return bytesWritten == messageSize;
+ } catch (const std::exception& e) {
+ std::cerr << "Error during write: " << e.what() << "\n";
+ return false;
+ }
+}
diff --git a/Connection/public/ConnectionCommon.hpp b/Connection/public/ConnectionCommon.hpp
new file mode 100644
index 0000000..5014f4a
--- /dev/null
+++ b/Connection/public/ConnectionCommon.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "Module.hpp"
+#include
+
+namespace tp {
+ class Connection {
+ public:
+ typedef ualni SizeBytes;
+ typedef ualni BytePointer;
+ typedef int1 Byte;
+
+ class Status {
+ public:
+ enum State {
+ NONE,
+ OPENED,
+ CLOSED,
+ DENIED,
+ INVALID,
+ };
+
+ private:
+ State mStatus = NONE;
+
+ public:
+ Status() = default;
+ [[nodiscard]] State getStatus() const { return mStatus; }
+ void setStatus(State status) { mStatus = status; }
+ [[nodiscard]] bool isOpened() const { return mStatus == OPENED; }
+ };
+
+ class Type {
+ public:
+ enum State {
+ WRITE,
+ READ,
+ READ_WRITE,
+ NONE,
+ };
+
+ private:
+ State mHandleType = NONE;
+
+ public:
+ Type() = default;
+ explicit Type(bool read) :
+ mHandleType((State) read) {}
+ explicit Type(State handleType) :
+ mHandleType(handleType) {}
+ [[nodiscard]] State getType() const { return mHandleType; }
+ [[nodiscard]] bool isRead() const { return mHandleType == READ; }
+ [[nodiscard]] bool isWrite() const { return mHandleType == WRITE; }
+ };
+
+ protected:
+ Status mStatus;
+ Type mConnectionType;
+
+ public:
+ Connection() = default;
+
+ virtual const Status& getConnectionStatus() { return mStatus; }
+ virtual const Type& getConnectionType() { return mConnectionType; }
+ };
+}
\ No newline at end of file
diff --git a/Connection/public/LocalConnection.hpp b/Connection/public/LocalConnection.hpp
new file mode 100644
index 0000000..bdde90b
--- /dev/null
+++ b/Connection/public/LocalConnection.hpp
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "Archiver.hpp"
+#include "ConnectionCommon.hpp"
+
+namespace tp {
+ class LocalConnectionContext;
+
+ class LocalConnection : public Connection {
+ public:
+ class Location {
+ std::string mLocation;
+
+ public:
+ Location() :
+ mLocation("tmp"){};
+ explicit Location(const std::string& location) :
+ mLocation(location) {}
+ void setLocation(const std::string& location) { mLocation = location; }
+ [[nodiscard]] const std::string& getLocation() const { return mLocation; }
+ [[nodiscard]] bool exists() const;
+ };
+
+ public:
+ LocalConnection() = default;
+
+ virtual ~LocalConnection() {
+ if (mStatus.isOpened()) LocalConnection::disconnect();
+ }
+
+ public:
+ virtual bool connect(const Location& location, const Type& connectionInfo);
+ virtual bool disconnect();
+
+ public:
+ virtual const Status& getConnectionStatus() { return mStatus; }
+ virtual const Type& getConnectionType() { return mConnectionType; }
+ virtual const Location& getLocation() { return mLocation; }
+
+ public:
+ virtual bool setPointer(BytePointer pointer);
+ virtual bool readBytes(Byte* bytes, SizeBytes size);
+ virtual bool writeBytes(const Byte* bytes, SizeBytes size);
+
+ public:
+ virtual SizeBytes size();
+
+ private:
+ LocalConnectionContext* mHandle = nullptr;
+ Location mLocation;
+ BytePointer mPointer = 0;
+ };
+
+ template
+ class ArchiverLocalConnection : public ArchiverTemplate {
+ public:
+ ArchiverLocalConnection() = default;
+
+ protected:
+ void writeBytes(const int1* val, ualni size) override { connection.writeBytes(val, size); }
+ void readBytes(int1* val, ualni size) override { connection.readBytes(val, size); }
+
+ public:
+ LocalConnection connection;
+ };
+}
\ No newline at end of file
diff --git a/Connection/public/Network.hpp b/Connection/public/Network.hpp
new file mode 100644
index 0000000..215de31
--- /dev/null
+++ b/Connection/public/Network.hpp
@@ -0,0 +1,36 @@
+#pragma once
+
+#include "Module.hpp"
+
+namespace tp {
+ class ServerContext;
+ class ClientContext;
+
+ class Server {
+ ServerContext* mContext;
+
+ public:
+ typedef void* Socket;
+
+ public:
+ Server();
+ ~Server();
+
+ void start(ualni port);
+ Socket accept();
+ bool read(Socket client, int1* message, halni size);
+ bool write(Socket client, const int1* message, halni size);
+ };
+
+ class Client {
+ ClientContext* mContext;
+
+ public:
+ Client();
+ ~Client();
+
+ bool connect(const int1* IP, ualni PORT);
+ bool read(int1* buff, halni size);
+ bool write(const int1* message, halni size);
+ };
+}
\ No newline at end of file
diff --git a/Connection/public/RemoteConnection.hpp b/Connection/public/RemoteConnection.hpp
new file mode 100644
index 0000000..4bb7ed2
--- /dev/null
+++ b/Connection/public/RemoteConnection.hpp
@@ -0,0 +1,40 @@
+#pragma once
+
+#include "ConnectionCommon.hpp"
+
+namespace tp {
+
+ class RemoteConnectionContext;
+
+ class RemoteConnection : public Connection {
+ public:
+ class Location {
+ ualni mId = 0;
+
+ public:
+ Location() = default;
+ };
+
+ public:
+ RemoteConnection() = default;
+
+ virtual ~RemoteConnection() {
+ if (mStatus.isOpened()) RemoteConnection::disconnect();
+ }
+
+ public:
+ virtual bool connect(const Location& location, const Type& connectionInfo);
+ virtual bool disconnect();
+
+ public:
+ virtual const Location& getLocation() { return mLocation; }
+
+ public:
+ virtual bool readBytes(Byte* bytes, SizeBytes size);
+ virtual bool writeBytes(const Byte* bytes, SizeBytes size);
+
+ private:
+ RemoteConnectionContext* mHandle = nullptr;
+ Location mLocation;
+ };
+}
\ No newline at end of file
diff --git a/Connection/tests/Test.cpp b/Connection/tests/Test.cpp
new file mode 100644
index 0000000..5c180af
--- /dev/null
+++ b/Connection/tests/Test.cpp
@@ -0,0 +1,39 @@
+
+#include "ConnectionCommon.hpp"
+
+#include "LocalConnection.hpp"
+
+#include "UnitTest++/UnitTest++.h"
+
+using namespace tp;
+
+SUITE(Connection) {
+ TEST(LocalConnection) {
+ const int1* data = "abcde\0";
+ int1 dataRead[6]{};
+
+ {
+ LocalConnection file;
+ file.connect(LocalConnection::Location(std::string("tmp2.txt")), LocalConnection::Type(false));
+ file.writeBytes(data, 6);
+ file.disconnect();
+ }
+
+ {
+ LocalConnection file;
+ file.connect(LocalConnection::Location(std::string("tmp2.txt")), LocalConnection::Type(true));
+ file.readBytes(dataRead, 6);
+ file.disconnect();
+ }
+
+ for (auto i = 0; i < 5; i++) {
+ CHECK(data[i] == dataRead[i]);
+ }
+ }
+
+ TEST(NO_TESTS) { CHECK(false); }
+}
+
+int main() {
+ return UnitTest::RunAllTests();
+}
\ No newline at end of file
diff --git a/Containers/CMakeLists.txt b/Containers/CMakeLists.txt
index f63ea2f..bb9bb34 100644
--- a/Containers/CMakeLists.txt
+++ b/Containers/CMakeLists.txt
@@ -1,8 +1,3 @@
-
-cmake_minimum_required(VERSION 3.2)
-
-set(CMAKE_CXX_STANDARD 23)
-
project(Containers)
### ---------------------- Static Library --------------------- ###
@@ -13,10 +8,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
### -------------------------- Tests -------------------------- ###
-enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp")
-add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
-target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
-add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
-
-install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
\ No newline at end of file
+add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
+target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
+add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
\ No newline at end of file
diff --git a/Containers/private/Containers.cpp b/Containers/private/Containers.cpp
index b680dc5..20ed850 100644
--- a/Containers/private/Containers.cpp
+++ b/Containers/private/Containers.cpp
@@ -1,18 +1,11 @@
-
-#include "ContainersCommon.hpp"
-
-#include
-
-namespace tp {
-
- static ModuleManifest* sModuleDependencies[] = { &gModuleBase,nullptr };
- ModuleManifest gModuleContainers = ModuleManifest("Containers", nullptr, nullptr, sModuleDependencies);
-
- void* DefaultAllocator::allocate(ualni size) {
- return malloc(size);
- }
-
- void DefaultAllocator::deallocate(void* p) {
- free(p);
- }
+
+#include "ContainersCommon.hpp"
+
+#include
+
+namespace tp {
+
+ void* DefaultAllocator::allocate(ualni size) { return malloc(size); }
+
+ void DefaultAllocator::deallocate(void* p) { free(p); }
}
\ No newline at end of file
diff --git a/Containers/public/Buffer.hpp b/Containers/public/Buffer.hpp
index 1c9d3bf..441283e 100644
--- a/Containers/public/Buffer.hpp
+++ b/Containers/public/Buffer.hpp
@@ -5,68 +5,174 @@
namespace tp {
- template
- inline ualni BufferResizeScaling(ualni const size) { return size * tScale; }
+ template
+ inline ualni BufferResizeScaling(const ualni size) {
+ return size * tScale;
+ }
- template
- inline ualni BufferResizeScalingDown(ualni const size) { return size / tScale; }
+ template
+ inline ualni BufferResizeScalingDown(const ualni size) {
+ return size / tScale;
+ }
- template
- inline ualni BufferResizeAddition(ualni const size) { return size + tAddition; }
+ template
+ inline ualni BufferResizeAddition(const ualni size) {
+ return size + tAddition;
+ }
- template
- inline ualni BufferResizeAdditionDown(ualni const size) { return size - tAddition; }
+ template
+ inline ualni BufferResizeAdditionDown(const ualni size) {
+ return size - tAddition;
+ }
-
- template
+ template
class ConstSizeBuffer {
- SelCopyArg Arg;
+ typedef SelectValueOrReference Arg;
private:
tType mBuff[tSize];
+ ualni mLoad = 0;
public:
ConstSizeBuffer() = default;
+ ~ConstSizeBuffer() {
+ for (auto i = 0; i < mLoad; i++) {
+ mBuff[i].~tType();
+ }
+ }
+
public:
- [[nodiscard]] ualni size() const { return tSize; }
+ [[nodiscard]] ualni size() const { return mLoad; }
- tType& first() {
- return *mBuff;
- }
-
- const tType& first() const {
- return *mBuff;
+ [[nodiscard]] ualni getBuffSize() const { return tSize; }
+
+ tType& first() {
+ DEBUG_ASSERT(mLoad)
+ return *mBuff;
}
- tType& last() {
- return mBuff[tSize - 1];
+ const tType& first() const {
+ DEBUG_ASSERT(mLoad)
+ return *mBuff;
}
-
- const tType& last() const {
- return mBuff[tSize - 1];
+
+ tType& last() {
+ DEBUG_ASSERT(mLoad)
+ return mBuff[mLoad - 1];
+ }
+
+ const tType& last() const {
+ DEBUG_ASSERT(mLoad)
+ return mBuff[mLoad - 1];
}
tType& operator[](ualni aIdx) {
- DEBUG_ASSERT(aIdx >= 0 && aIdx < tSize)
+ DEBUG_ASSERT(aIdx >= 0 && aIdx < mLoad)
return mBuff[aIdx];
}
const tType& operator[](ualni aIdx) const {
- DEBUG_ASSERT(aIdx >= 0 && aIdx < tSize)
+ DEBUG_ASSERT(aIdx >= 0 && aIdx < mLoad)
return mBuff[aIdx];
}
+
+ void append(Arg data) {
+ ASSERT(mLoad != tSize)
+ new (&mBuff[mLoad]) tType(data);
+ mLoad++;
+ }
+
+ void pop() {
+ DEBUG_ASSERT(mLoad)
+ mBuff[mLoad].~tType();
+ mLoad--;
+ }
+
+ ConstSizeBuffer& operator=(const tp::InitialierList& init) {
+ for (auto arg : init) {
+ append(arg);
+ }
+ return *this;
+ }
+
+ void clear() {
+ this->~ConstSizeBuffer();
+ new (this) ConstSizeBuffer();
+ }
+
+ public:
+ class IteratorPointer {
+ protected:
+ tType* mIter;
+
+ public:
+ IteratorPointer() = default;
+
+ tType& operator->() { return *mIter; }
+
+ const tType& operator->() const { return *mIter; }
+ };
+
+ class IteratorReference {
+ protected:
+ tType* mIter;
+
+ public:
+ IteratorReference() = default;
+
+ tType* operator->() { return mIter; }
+
+ const tType* operator->() const { return mIter; }
+ };
+
+ class Iterator : public TypeSelect::isPointer, IteratorPointer, IteratorReference>::Result {
+ public:
+ explicit Iterator(tType* iter) { this->mIter = iter; }
+
+ const Iterator& operator*() const { return *this; }
+
+ Iterator& operator++() {
+ this->mIter++;
+ return *this;
+ }
+
+ bool operator==(const Iterator& left) const { return left.mIter == this->mIter; }
+
+ bool operator!=(const Iterator& left) const { return left.mIter != this->mIter; }
+ };
+
+ [[nodiscard]] Iterator begin() const { return Iterator(mBuff); }
+
+ [[nodiscard]] Iterator end() const { return Iterator(mBuff + mLoad); }
+
+ public:
+ template
+ void archiveWrite(tArchiver& ar) const {
+ ar << mLoad;
+ for (auto item : *this) {
+ ar << item.data();
+ }
+ }
+
+ template
+ void archiveRead(tArchiver& ar) {
+ clear();
+ ar >> mLoad;
+ for (auto i = 0; i < mLoad; i++) {
+ ar >> mBuff[i];
+ }
+ }
};
- template<
+ template <
typename tType,
class tAllocator = DefaultAllocator,
- ualni (tResizePolicy)(ualni) = BufferResizeScaling<2>,
- ualni (tResizePolicyDown)(ualni) = BufferResizeScalingDown<2>,
- ualni tMinSize = 4
- >
+ ualni(tResizePolicy)(ualni) = BufferResizeScaling<2>,
+ ualni(tResizePolicyDown)(ualni) = BufferResizeScalingDown<2>,
+ ualni tMinSize = 4>
class Buffer {
- typedef SelCopyArg Arg;
+ typedef SelectValueOrReference Arg;
private:
tType* mBuff;
@@ -75,58 +181,91 @@ namespace tp {
ualni mLoad;
public:
- Buffer() : mSize(tMinSize), mLoad(0) {
+ Buffer() :
+ mSize(tMinSize),
+ mLoad(0) {
mBuff = (tType*) mAllocator.allocate(sizeof(tType) * tMinSize);
}
- explicit Buffer(ualni size) : mSize(size), mLoad(0) {
- mBuff = (tType*) mAllocator.allocate(sizeof(tType) * tMinSize);
- }
-
- Buffer(const Buffer& in) : mSize(in.mSize), mLoad(in.mLoad) {
+ Buffer(const InitialierList& input) {
+ mSize = 0;
+ for (const auto& val : input) {
+ mSize++;
+ }
mBuff = (tType*) mAllocator.allocate(sizeof(tType) * mSize);
- for (ualni i = 0; i < mLoad; i++) new (&mBuff[i]) tType(in.mBuff[i]);
+ mLoad = 0;
+ for (const auto& val : input) {
+ mBuff[mLoad] = val;
+ mLoad++;
+ }
+ }
+
+ explicit Buffer(ualni size) {
+ mBuff = (tType*) mAllocator.allocate(sizeof(tType) * size);
+ mSize = size;
+ mLoad = size;
+ for (ualni i = 0; i < mLoad; i++) {
+ new (mBuff + i) tType();
+ }
+ }
+
+ Buffer(const Buffer& in) :
+ mSize(in.mSize),
+ mLoad(in.mLoad) {
+ mBuff = (tType*) mAllocator.allocate(sizeof(tType) * mSize);
+ for (ualni i = 0; i < mLoad; i++) {
+ new (&mBuff[i]) tType(in.mBuff[i]);
+ }
+ }
+
+ void clear() {
+ this->~Buffer();
+ new (this) Buffer();
}
Buffer& operator=(const Buffer& in) {
- if (this == &in) return *this;
+ if (this == &in) {
+ return *this;
+ }
this->~Buffer();
new (this) Buffer(in);
return *this;
}
~Buffer() {
- for (ualni i = 0; i < mLoad; i++) mBuff[i].~tType();
+ for (ualni i = 0; i < mLoad; i++) {
+ mBuff[i].~tType();
+ }
mAllocator.deallocate(mBuff);
}
public:
- [[nodiscard]] ualni size() const {
- return mLoad;
- }
+ [[nodiscard]] ualni size() const { return mLoad; }
+
+ [[nodiscard]] ualni getBuffSize() const { return mSize; }
+
+ [[nodiscard]] const tType* getBuff() const { return mBuff; }
+
+ [[nodiscard]] tType* getBuff() { return mBuff; }
- [[nodiscard]] ualni getBuffSize() const {
- return mSize;
- }
-
tType& first() {
- DEBUG_ASSERT(mLoad)
- return *mBuff;
+ DEBUG_ASSERT(mLoad)
+ return *mBuff;
}
const tType& first() const {
- DEBUG_ASSERT(mLoad)
- return *mBuff;
+ DEBUG_ASSERT(mLoad)
+ return *mBuff;
}
tType& last() {
- DEBUG_ASSERT(mLoad)
- return mBuff[mLoad - 1];
+ DEBUG_ASSERT(mLoad)
+ return mBuff[mLoad - 1];
}
const tType& last() const {
- DEBUG_ASSERT(mLoad)
- return mBuff[mLoad - 1];
+ DEBUG_ASSERT(mLoad)
+ return mBuff[mLoad - 1];
}
tType& operator[](ualni idx) {
@@ -141,50 +280,216 @@ namespace tp {
public:
bool operator==(const Buffer& in) const {
- if (this == &in) return true;
- if (mLoad != in.mLoad) return false;
+ if (this == &in) {
+ return true;
+ }
+ if (mLoad != in.mLoad) {
+ return false;
+ }
for (ualni i = 0; i < mLoad; i++) {
- if (mBuff[i] != in.mBuff[i]) return false;
+ if (mBuff[i] != in.mBuff[i]) {
+ return false;
+ }
}
return true;
}
public:
- void append(Arg data) {
- if (mLoad == mSize) resizeBuffer(tResizePolicy(mSize));
- new (&mBuff[mLoad]) tType(data);
- mLoad++;
+ Buffer& operator=(const tp::InitialierList& input) {
+ // TODO : optimize
+ for (ualni i = 0; i < mLoad; i++) {
+ mBuff[i].~tType();
+ }
+ mAllocator.deallocate(mBuff);
+
+ mSize = 0;
+ for (const auto& val : input) {
+ mSize++;
+ }
+
+ mBuff = (tType*) mAllocator.allocate(sizeof(tType) * mSize);
+ mLoad = 0;
+ for (const auto& val : input) {
+ new (&mBuff[mLoad]) tType(val);
+ mLoad++;
+ }
+
+ return *this;
}
- void append(const Buffer& in) {
- if (!in.mLoad) return;
- auto newLoad = mLoad + in.mLoad;
- auto newSize = mSize;
- while (newLoad >= newSize) newSize = tResizePolicy(newSize);
- if (newSize != mSize) resizeBuffer(newSize);
- for (auto i = mLoad; i < newLoad; i++) new (&mBuff[i]) tType(in.mBuff[i - mLoad]);
- mLoad = newLoad;
- }
+ tType& append(Arg data) {
+ if (mLoad == mSize) {
+ resizeBuffer(tResizePolicy(mSize));
+ }
+ new (&mBuff[mLoad]) tType(data);
+ mLoad++;
+ return mBuff[mLoad - 1];
+ }
+
+ void append(const Buffer& in) {
+ if (!in.mLoad) {
+ return;
+ }
+ auto newLoad = mLoad + in.mLoad;
+ auto newSize = mSize;
+ while (newLoad >= newSize) {
+ newSize = tResizePolicy(newSize);
+ }
+ if (newSize != mSize) {
+ resizeBuffer(newSize);
+ }
+ for (auto i = mLoad; i < newLoad; i++) {
+ new (&mBuff[i]) tType(in.mBuff[i - mLoad]);
+ }
+ mLoad = newLoad;
+ }
void pop() {
DEBUG_ASSERT(mLoad)
mBuff[mLoad].~tType();
mLoad--;
ualni prevSize = tResizePolicyDown(mSize);
- DEBUG_ASSERT(prevSize < mSize)
- if (prevSize > mLoad) resizeBuffer(prevSize);
+ DEBUG_ASSERT(prevSize < mSize)
+ if (prevSize > mLoad) {
+ resizeBuffer(prevSize);
+ }
}
+ void reserve(ualni aSize) {
+ for (ualni i = 0; i < mLoad; i++) {
+ mBuff[i].~tType();
+ }
+ mAllocator.deallocate(mBuff);
+ mBuff = (tType*) mAllocator.allocate(sizeof(tType) * aSize);
+ mSize = aSize;
+ mLoad = aSize;
+ for (ualni i = 0; i < mLoad; i++) {
+ new (mBuff + i) tType();
+ }
+ }
- private:
- void resizeBuffer(ualni newSize) {
- DEBUG_ASSERT(newSize >= mLoad)
- auto const oldBuff = mBuff;
- mBuff = (tType*) mAllocator.allocate(sizeof(tType) * newSize);
- for (ualni i = 0; i < mLoad; i++) new (&mBuff[i]) tType(oldBuff[i]);
- for (ualni i = 0; i < mLoad; i++) oldBuff[i].~tType();
- mAllocator.deallocate(oldBuff);
- mSize = newSize;
- }
+ public:
+ class IteratorPointer {
+ protected:
+ tType* mIter;
+
+ public:
+ IteratorPointer() = default;
+
+ tType& operator->() { return *mIter; }
+
+ const tType& operator->() const { return *mIter; }
+ };
+
+ class IteratorReference {
+ protected:
+ tType* mIter;
+
+ public:
+ IteratorReference() = default;
+
+ tType* operator->() { return mIter; }
+
+ const tType* operator->() const { return mIter; }
+ };
+
+ class Iterator : public TypeSelect::isPointer, IteratorPointer, IteratorReference>::Result {
+ public:
+ explicit Iterator(tType* iter) { this->mIter = iter; }
+
+ const Iterator& operator*() const { return *this; }
+
+ tType& data() { return *this->mIter; }
+
+ Iterator& operator++() {
+ this->mIter++;
+ return *this;
+ }
+
+ bool operator==(const Iterator& left) const { return left.mIter == this->mIter; }
+
+ bool operator!=(const Iterator& left) const { return left.mIter != this->mIter; }
+ };
+
+ [[nodiscard]] Iterator begin() const { return Iterator(mBuff); }
+
+ [[nodiscard]] Iterator end() const { return Iterator(mBuff + mLoad); }
+
+ public:
+ template
+ void archiveWrite(tArchiver& ar) const {
+ ar << mLoad;
+ for (auto item : *this) {
+ ar % item.data();
+ }
+ }
+
+ template
+ void archiveRead(tArchiver& ar) {
+ clear();
+ decltype(mLoad) len;
+ ar >> len;
+ for (auto i = len; i; i--) {
+ // TODO : optimize
+ if (mLoad == mSize) {
+ resizeBuffer(tResizePolicy(mSize));
+ }
+ new (mBuff + mLoad) tType();
+ ar % mBuff[mLoad];
+ mLoad++;
+ }
+ }
+
+ private:
+ void resizeBuffer(ualni newSize) {
+ DEBUG_ASSERT(newSize >= mLoad)
+ const auto oldBuff = mBuff;
+ mBuff = (tType*) mAllocator.allocate(sizeof(tType) * newSize);
+ for (ualni i = 0; i < mLoad; i++) {
+ new (&mBuff[i]) tType(oldBuff[i]);
+ }
+ for (ualni i = 0; i < mLoad; i++) {
+ oldBuff[i].~tType();
+ }
+ mAllocator.deallocate(oldBuff);
+ mSize = newSize;
+ }
};
-}
\ No newline at end of file
+
+ template
+ void generatePermutations(const Buffer>& in, Buffer>& out) {
+ typedef long long Idx;
+
+ // sanity check
+ for (const auto& vec : in) {
+ if (!vec.size()) {
+ return;
+ }
+ }
+
+ out.resize(in.size());
+ auto len = Idx(1);
+ for (const auto& vec : in) {
+ len *= (Idx) vec.size();
+ }
+ for (auto i = 0; i < in.size(); i++) {
+ out[i].resize(len);
+ }
+
+ auto dub = Idx(1);
+ for (auto power = (Idx) in.size() - 1; power >= 0; power--) {
+ auto& vec = in[power];
+ long long i = 0;
+ while (i < len) {
+ for (auto val : vec) {
+ for (auto j = 0; j < dub; j++) {
+ out[power][i] = val;
+ i++;
+ }
+ }
+ }
+ dub *= (Idx) vec.size();
+ }
+ }
+
+}
diff --git a/Containers/public/Buffer2D.hpp b/Containers/public/Buffer2D.hpp
new file mode 100644
index 0000000..e3d8e23
--- /dev/null
+++ b/Containers/public/Buffer2D.hpp
@@ -0,0 +1,131 @@
+
+#pragma once
+
+#include "Buffer.hpp"
+#include "Utils.hpp"
+
+namespace tp {
+
+ template
+ using ConstSizeBuffer2D = ConstSizeBuffer, tSizeY>;
+
+ typedef ualni Index;
+
+ struct Index2D {
+ Index x = 0;
+ Index y = 0;
+ };
+
+ template
+ class Buffer2D {
+ public:
+
+ typedef SelectValueOrReference tTypeArg;
+
+ private:
+ tAllocator mAlloc;
+ tType* mBuff = nullptr;
+ Index2D mSize = { 0, 0 };
+
+ void deleteBuffer() {
+ if (!mBuff) {
+ return;
+ }
+ for (ualni i = 0; i < mSize.x * mSize.y; i++) {
+ mBuff[i].~tType();
+ }
+ mAlloc.deallocate(mBuff);
+ }
+
+ void allocateBuffer(Index2D size) {
+ deleteBuffer();
+ mBuff = (tType*) mAlloc.allocate(sizeof(tType) * size.x * size.y);
+ for (ualni i = 0; i < mSize.x * mSize.y; i++) {
+ new (mBuff + i) tType();
+ }
+ }
+
+ public:
+ Buffer2D() = default;
+
+ ~Buffer2D() {
+ deleteBuffer();
+ mSize = { 0, 0 };
+ }
+
+ explicit Buffer2D(Index2D aSize) { reserve(aSize); }
+
+ [[nodiscard]] Index2D size() const { return { mSize.x, mSize.y }; }
+
+ tType* getBuff() const { return mBuff; }
+
+ inline tType& get(const Index2D& at) {
+ DEBUG_ASSERT(mBuff && at.x < mSize.x && at.y < mSize.y && at.x >= 0 && at.y >= 0)
+ return *(mBuff + mSize.x * at.y + at.x);
+ }
+
+ inline const tType& get(const Index2D& at) const {
+ DEBUG_ASSERT(mBuff && at.x < mSize.x && at.y < mSize.y && at.x >= 0 && at.y >= 0)
+ return *(mBuff + mSize.x * at.y + at.x);
+ }
+
+ inline void set(const Index2D& at, tTypeArg value) {
+ DEBUG_ASSERT(mBuff && at.x < mSize.x && at.y < mSize.y && at.x >= 0 && at.y >= 0)
+ *(mBuff + mSize.x * at.y + at.x) = value;
+ }
+
+ void reserve(const Index2D& newSize) {
+ if (mSize.x != newSize.x || mSize.y != newSize.y) {
+ allocateBuffer(newSize);
+ mSize = newSize;
+ }
+ }
+
+ void assign(tType value) {
+ DEBUG_ASSERT(mBuff);
+ Index len = mSize.x * mSize.y;
+ for (Index i = 0; i < len; i++) {
+ mBuff[i] = value;
+ }
+ }
+
+ bool operator==(const Buffer2D& in) const {
+ if (&in == this) {
+ return true;
+ }
+ if (in.size() != size()) {
+ return false;
+ }
+ for (auto i = 0; i < mSize.x * mSize.y; i++) {
+ if (mBuff[i] != in.mBuff[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public:
+ template
+ void archiveWrite(tArchiver& ar) const {
+ ar << mSize;
+ for (auto i = 0; i < mSize.x; i++) {
+ for (auto j = 0; j < mSize.y; j++) {
+ ar << get(i, j);
+ }
+ }
+ }
+
+ template
+ void archiveRead(tArchiver& ar) {
+ decltype(mSize) size;
+ deleteBuffer();
+ ar >> size;
+ reserve(size);
+ for (auto i = 0; i < mSize.x; i++) {
+ for (auto j = 0; j < mSize.y; j++) {
+ ar >> get(i, j);
+ }
+ }
+ }
+ };
+}
diff --git a/Containers/public/ContainersCommon.hpp b/Containers/public/ContainersCommon.hpp
index 43552d8..4021041 100644
--- a/Containers/public/ContainersCommon.hpp
+++ b/Containers/public/ContainersCommon.hpp
@@ -4,23 +4,22 @@
#include "Module.hpp"
namespace tp {
- extern ModuleManifest gModuleContainers;
class DefaultAllocator {
public:
DefaultAllocator() = default;
- static void *allocate(ualni);
- static void deallocate(void *);
+ static void* allocate(ualni);
+ static void deallocate(void*);
};
class DefaultSaverLoader {
public:
DefaultSaverLoader() = default;
- template
+ template
static void write(const Type&) {}
- template
+ template
static void read(Type&) {}
};
}
\ No newline at end of file
diff --git a/Containers/public/IntervalTree.hpp b/Containers/public/IntervalTree.hpp
new file mode 100644
index 0000000..261e402
--- /dev/null
+++ b/Containers/public/IntervalTree.hpp
@@ -0,0 +1,88 @@
+#pragma once
+
+#include "Tree.hpp"
+
+namespace tp {
+
+ template
+ class IntervalKey {
+ public:
+ IntervalKey() = default;
+
+ IntervalKey(tType start, tType end) {
+ mStart = start;
+ mEnd = end;
+ }
+
+ inline bool descentRight(const IntervalKey& in) const {
+ if (in.mStart != mStart) return in.mStart > mStart;
+ return in.mEnd > mEnd;
+ }
+
+ inline bool descentLeft(const IntervalKey& in) const {
+ if (in.mStart != mStart) return in.mStart < mStart;
+ return in.mEnd < mEnd;
+ }
+
+ inline bool exactNode(const IntervalKey& in) const { return in.mStart == mStart && in.mEnd == mEnd; }
+
+ inline const IntervalKey& getFindKey() const { return *this; }
+ inline const IntervalKey& keyInRightSubtree(const IntervalKey& in) const { return in; }
+ inline const IntervalKey& keyInLeftSubtree(const IntervalKey& in) const { return in; }
+
+ template
+ inline void updateTreeCacheCallBack(const tTreeNodeType& node) {
+ mMax = 0;
+ if (node.mRight && node.mRight->key.mMax > mMax) mMax = node.mRight->key.mMax;
+ if (node.mLeft && node.mLeft->key.mMax > mMax) mMax = node.mLeft->key.mMax;
+ if (mMax < mEnd) mMax = mEnd;
+ }
+
+ public:
+ tType mStart = tType();
+ tType mEnd = tType();
+ tType mMax = tType();
+ };
+
+ template
+ class IntervalTree : public AvlTree, tData> {
+ typedef typename AvlTree, tData>::Node Node;
+
+ public:
+ IntervalTree() = default;
+
+ template
+ ualni forEachIntersection(tType start, tType end, tFunctor functor) const {
+ ualni debug = 0;
+ forEachIntersectionUtil(this->head(), start, end, functor, debug);
+ return debug;
+ }
+
+ private:
+ template
+ void forEachIntersectionUtil(const Node* node, tType start, tType end, tFunctor functor, ualni& debug) const {
+ if (node == nullptr) return;
+
+ debug++;
+
+ // If 'start' is to the right of the rightmost point of any interval
+ // in this node and all children, there won't be any matches.
+ if (start > node->key.mMax) return;
+
+ // Search left children
+ forEachIntersectionUtil(node->mLeft, start, end, functor, debug);
+
+ // Check this node
+ if (start <= node->key.mEnd && end >= node->key.mStart) {
+ functor(node->key.mStart, node->key.mEnd, node->data);
+ }
+
+ // If end is to the left of the start of this interval,
+ // then it can't be in any child to the right.
+ if (end < node->key.mStart) return;
+
+ // Otherwise, search right children
+ forEachIntersectionUtil(node->mRight, start, end, functor, debug);
+ }
+ };
+}
\ No newline at end of file
diff --git a/Containers/public/List.hpp b/Containers/public/List.hpp
index 470781b..3a36de5 100644
--- a/Containers/public/List.hpp
+++ b/Containers/public/List.hpp
@@ -1,322 +1,333 @@
-
-#pragma once
-
-#include "ContainersCommon.hpp"
-
-#include "TypeInfo.hpp"
-
-namespace tp {
-
- template
- class List {
-
- typedef SelCopyArg TypeArg;
- typedef ualni Index;
-
- public:
-
- struct Node {
- Type data;
- Node* next = nullptr;
- Node* prev = nullptr;
- Node() = default;
- explicit Node(TypeArg p_data) : data(p_data) {}
- Type& operator->() { return data; }
- };
-
- class IteratorPointer {
- protected:
- Node* mIter;
- public:
- IteratorPointer() = default;
- Type& operator->() { return (mIter->data); }
- const Type& operator->() const { return (mIter->data); }
- };
-
- class IteratorReference {
- protected:
- Node* mIter;
- public:
- IteratorReference() = default;
- Type* operator->() { return &(mIter->data); }
- const Type* operator->() const { return &(mIter->data); }
- };
-
- class Iterator : public TypeSelect::isPointer, IteratorPointer, IteratorReference>::Result {
- public:
-
- explicit Iterator(Node* iter) { this->mIter = iter; }
-
- Node* node() { return this->mIter; }
- Type& data() { return this->mIter->data; }
- const Node* node() const { return this->mIter; }
- const Type& data() const { return this->mIter->data; }
-
- const Iterator& operator*() const { return *this; }
-
- Iterator& operator++() {
- this->mIter = this->mIter->next;
- return *this;
- }
-
- bool operator==(const Iterator& left) const { return left.mIter == this->mIter; }
- bool operator!=(const Iterator& left) const { return left.mIter != this->mIter; }
- };
-
- private:
- Node* mFirst = nullptr;
- Node* mLast = nullptr;
- Index mLength = 0;
- Allocator mAlloc;
-
- public:
-
- List() = default;
-
- List(const init_list& list) { operator=(list); }
-
- [[nodiscard]] inline Node* first() const { return mFirst; }
- [[nodiscard]] inline Node* last() const { return mLast; }
- [[nodiscard]] inline Index length() const { return mLength; }
-
- [[nodiscard]] Node* newNode() { return new (mAlloc.allocate(sizeof(Node))) Node(); }
- [[nodiscard]] Node* newNode(TypeArg arg) { return new (mAlloc.allocate(sizeof(Node))) Node(arg); }
- [[nodiscard]] Node* newNodeNotConstructed() {
- auto node = (Node*) mAlloc.allocate(sizeof(Node));
- node->next = node->prev = nullptr;
- return node;
- }
-
- [[nodiscard]] const Allocator& getAllocator() const { return mAlloc; }
-
- void deleteNode(Node* node) {
- node->~Node();
- mAlloc.deallocate(node);
- }
-
- Node* addNodeBack() {
- auto const out = newNode();
- pushBack(out);
- return out;
- }
-
- Node* addNodeFront() {
- auto const out = newNode();
- pushFront(out);
- return out;
- }
-
- void attach(Node* node, Node* node_to) {
- if (node_to) {
- if (node_to->next) {
- node->next = node_to->next;
- node->next->prev = node;
- }
- node_to->next = node;
- node->prev = node_to;
- if (node_to == mLast) {
- mLast = node;
- }
- } else {
- if (mFirst) {
- mFirst->prev = node;
- node->next = mFirst;
- mFirst = node;
- } else {
- mFirst = mLast = node;
- }
- }
- mLength++;
- }
-
- void detach(Node* node) {
- if (node->next) {
- node->next->prev = node->prev;
- }
- if (node->prev) {
- node->prev->next = node->next;
- }
- if (node == mLast) {
- mLast = mLast->prev;
- }
- if (node == mFirst) {
- mFirst = mFirst->next;
- }
- mLength--;
- }
-
- [[nodiscard]] Node* findIdx(Index idx) const {
- DEBUG_ASSERT(!mFirst || idx > mLength - 1)
- Node* found = mFirst;
- for (int i = 0; i != idx; i++) {
- found = found->next;
- }
- return found;
- }
-
- [[nodiscard]] Node* find(const TypeArg data) const {
- Node* found = mFirst;
- for (alni i = 0; data != found->data; i++) {
- if (!found->next) {
- return nullptr;
- }
- found = found->next;
- }
- return found;
- }
-
- [[nodiscard]] inline const Type& operator[](Index idx) const {
- DEBUG_ASSERT(idx < mLength)
- return find(idx)->data;
- }
-
- void pushBack(Node* new_node) { attach(new_node, mLast); }
- void pushFront(Node* new_node) { attach(new_node, nullptr); }
- void pushBack(TypeArg data) { pushBack(newNode(data)); }
- void pushFront(TypeArg data) { pushFront(newNode(data)); }
-
- void popBack() {
- DEBUG_ASSERT(mLast)
- detach(mLast);
- deleteNode(mLast);
- }
-
- void popFront() {
- DEBUG_ASSERT(mFirst)
- auto temp = mFirst;
- detach(mFirst);
- deleteNode(temp);
- }
-
- void insert(Node* node, Index idx) {
- if (!mLength) {
- attach(node, mLast);
- } else if (idx >= mLength) {
- attach(node, nullptr);
- } else {
- attach(node, find(idx)->prev);
- }
- }
-
- void insert(TypeArg data, Index idx) {
- insert(newNode(data), idx);
- }
-
- void removeNode(Node* node) {
- detach(node);
- deleteNode(node);
- }
-
- void removeAll() {
- while (mFirst) {
- popFront();
- }
- }
-
- // copies data
- List& operator+=(const List& in) {
- for (auto node : in) {
- pushBack(node.data());
- }
- return *this;
- }
-
- List& operator+=(const init_list& list) {
- for (auto item : list) {
- pushBack(item);
- }
- return *this;
- }
-
- List& operator=(const List& in) {
- if (this == &in) { return *this; }
- removeAll();
- (*this) += in;
- return *this;
- }
-
- List& operator=(const init_list& list) {
- removeAll();
- *this += list;
- return *this;
- }
-
- [[nodiscard]] bool operator==(const List& in) const {
- if (in == *this) { return true; }
- if (in.length() != length()) {
- return false;
- }
- Node* left = in.first();
- Node* right = first();
- while (left && right) {
- if (left->data != right->data) {
- return false;
- }
- }
- if (left != right) {
- return false;
- }
- return true;
- }
-
- template
- [[nodiscard]] Node* find(bool (*found)(Node* node, compare_val val), compare_val value) const {
- for (Node* node = mFirst; node; node = node->next) {
- if (found(node, value)) {
- return node;
- }
- }
- return nullptr;
- }
-
- [[nodiscard]] Iterator begin() const {
- Iterator out(mFirst);
- return out;
- }
-
- [[nodiscard]] Iterator end() const {
- return Iterator(nullptr);
- }
-
- void invert() {
- Node* iter = mFirst;
- Node* tmp;
- while (iter) {
- tmp = iter;
- iter = iter->next;
- swap(tmp->next, tmp->prev);
- }
- swap(mFirst, mLast);
- }
-
- void detachAll() {
- while (mFirst) {
- detach(mFirst);
- }
- }
-
- template
- void write(Saver& file) const {
- file.write(mLength);
- for (auto item : *this) {
- file.write(item.data());
- }
- }
-
- template
- void read(Loader& file) {
- removeAll();
- ualni len;
- file.read(len);
- for (auto i = len; i; i--) {
- auto node = newNodeNotConstructed();
- file.read(node->data);
- pushBack(node);
- }
- }
-
- ~List() {
- removeAll();
- }
- };
+
+#pragma once
+
+#include "ContainersCommon.hpp"
+
+#include "TypeInfo.hpp"
+
+namespace tp {
+
+ template
+ class List {
+
+ typedef SelectValueOrReference TypeArg;
+ typedef ualni Index;
+
+ public:
+ struct Node {
+ Type data;
+ Node* next = nullptr;
+ Node* prev = nullptr;
+ Node() { data = Type(); }
+ explicit Node(TypeArg p_data) :
+ data(p_data) {}
+ Type& operator->() { return data; }
+ };
+
+ class IteratorPointer {
+ protected:
+ Node* mIter;
+
+ public:
+ IteratorPointer() = default;
+ Type& operator->() { return (mIter->data); }
+ const Type& operator->() const { return (mIter->data); }
+ };
+
+ class IteratorReference {
+ protected:
+ Node* mIter;
+
+ public:
+ IteratorReference() = default;
+ Type* operator->() { return &(mIter->data); }
+ const Type* operator->() const { return &(mIter->data); }
+ };
+
+ class Iterator : public TypeSelect::isPointer, IteratorPointer, IteratorReference>::Result {
+ public:
+ explicit Iterator(Node* iter) { this->mIter = iter; }
+
+ Node* node() { return this->mIter; }
+ Type& data() { return this->mIter->data; }
+ const Node* node() const { return this->mIter; }
+ const Type& data() const { return this->mIter->data; }
+
+ const Iterator& operator*() const { return *this; }
+
+ Iterator& operator++() {
+ this->mIter = this->mIter->next;
+ return *this;
+ }
+
+ bool operator==(const Iterator& left) const { return left.mIter == this->mIter; }
+ bool operator!=(const Iterator& left) const { return left.mIter != this->mIter; }
+ };
+
+ private:
+ Node* mFirst = nullptr;
+ Node* mLast = nullptr;
+ Index mLength = 0;
+ Allocator mAlloc;
+
+ public:
+ List() = default;
+ List(const List& in) { this->operator=(in); }
+ List(const InitialierList& 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 && idx >= 0)
+ 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 (i == length()) return nullptr;
+ 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)
+ auto const tmp = mLast;
+ detach(mLast);
+ deleteNode(tmp);
+ }
+
+ 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 InitialierList& 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 InitialierList& 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;
+ }
+ left = left->next;
+ right = right->next;
+ }
+ if (left != right) {
+ return false;
+ }
+ return true;
+ }
+
+ template
+ [[nodiscard]] Node* find(bool (*found)(Node* node, compare_val val), compare_val value) const {
+ for (Node* node = mFirst; node; node = node->next) {
+ if (found(node, value)) {
+ return node;
+ }
+ }
+ return nullptr;
+ }
+
+ [[nodiscard]] Iterator begin() const {
+ Iterator out(mFirst);
+ return out;
+ }
+
+ [[nodiscard]] Iterator end() const { return Iterator(nullptr); }
+
+ void invert() {
+ Node* iter = mFirst;
+ Node* tmp;
+ while (iter) {
+ tmp = iter;
+ iter = iter->next;
+ swap(tmp->next, tmp->prev);
+ }
+ swap(mFirst, mLast);
+ }
+
+ void detachAll() {
+ while (mFirst) {
+ detach(mFirst);
+ }
+ }
+
+ void transferNodes(List in) {
+ removeAll();
+ for (auto node : in) {
+ attach(node);
+ }
+ in.detachAll();
+ }
+
+ public:
+ template
+ void archiveWrite(tArchiver& ar) const {
+ ar << mLength;
+ for (auto item : *this) {
+ ar << item.data();
+ }
+ }
+
+ template
+ void archiveRead(tArchiver& ar) {
+ removeAll();
+ decltype(mLength) len;
+ ar >> len;
+ for (auto i = len; i; i--) {
+ auto node = newNodeNotConstructed();
+ ar >> node->data;
+ pushBack(node);
+ }
+ }
+
+ ~List() { removeAll(); }
+ };
}
\ No newline at end of file
diff --git a/Containers/public/Map.hpp b/Containers/public/Map.hpp
index 2f6fb88..405ca9d 100644
--- a/Containers/public/Map.hpp
+++ b/Containers/public/Map.hpp
@@ -1,392 +1,384 @@
-
-#pragma once
-
-#include "ContainersCommon.hpp"
-#include "Common.hpp"
-
-namespace tp {
-
- template
- ualni DefaultHashFunc(SelCopyArg key) {
- return hash(key);
- }
-
- template) = DefaultHashFunc, int tTableInitialSize = 4>
- class Map {
-
- enum {
- MAP_PERTURB_SHIFT = 5,
- MAP_MIN_SIZE = 4,
- MAP_MAX_LOAD_PERCENTAGE = 66,
- };
-
- typedef SelCopyArg KeyArg;
- typedef SelCopyArg ValArg;
-
- public:
-
- class Node {
- friend Map;
- Node(KeyArg aKey, ValArg aVal) : key(aKey), val(aVal) {}
- public:
- tKey key;
- tVal val;
- };
-
- struct Idx {
- alni idx = -1;
- operator bool() { return idx != -1; }
- };
-
- private:
- tAllocator mAlloc;
- Node** mTable;
- ualni mNSlots = 0;
- ualni mNEntries = 0;
-
- private:
-
- constexpr halnf maxLoadFactor() { return halnf(MAP_MAX_LOAD_PERCENTAGE) / 100.f; }
-
- inline Node** newTable(const ualni len) {
- return new(mAlloc.allocate(sizeof(Node*) * len)) Node*[len]();
- }
-
- inline Node* newNode(KeyArg key, ValArg val) {
- return new(mAlloc.allocate(sizeof(Node))) Node(key, val);
- }
-
- inline Node* newNodeNotConstructed() {
- return (Node*) mAlloc.allocate(sizeof(Node));
- }
-
- inline void deleteTable(Node** table) {
- mAlloc.deallocate(table);
- }
-
- inline void deleteNode(Node* p) {
- p->~Node();
- mAlloc.deallocate(p);
- }
-
- void markDeletedSlot(ualni idx) const {
- mTable[idx] = (Node*)-1;
- }
-
- static bool isDeletedNode(Node* node) {
- return node == (Node*)-1;
- }
-
- void rehash() {
- alni nSlotsOld = mNSlots;
- Node** tableOld = mTable;
-
- mNSlots = next2pow((uhalni)((1.f / (maxLoadFactor())) * mNEntries + 1));
- mTable = newTable(mNSlots);
- mNEntries = 0;
-
- for (alni i = 0; i < nSlotsOld; i++) {
- if (!tableOld[i] || isDeletedNode(tableOld[i])) {
- continue;
- }
-
- alni idx = findSlotWrite(tableOld[i]->key);
- mTable[idx] = tableOld[i];
- mNEntries++;
- }
-
- deleteTable(tableOld);
- }
-
- alni findSlotRead(KeyArg key) const {
- ualni const hashed_key = tHashFunc(key);
- ualni const mask = mNSlots - 1;
- ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1;
- alni idx = hashed_key & mask;
- NEXT:
- if (isDeletedNode(mTable[idx])) {
- goto SKIP;
- }
- if (!mTable[idx]) {
- return -1;
- }
- if (mTable[idx]->key == key) {
- return idx;
- }
- SKIP:
- idx = ((5 * idx) + 1 + shift) & mask;
- goto NEXT;
- }
-
- // compares keys only when collisions occur
- alni findSlotReadExisting(KeyArg key) const {
- ualni const hashed_key = tHashFunc(key);
- ualni const mask = mNSlots - 1;
- ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1;
- alni idx = hashed_key & mask;
- NEXT:
- if (isDeletedNode(mTable[idx])) {
- goto SKIP;
- }
- if (!mTable[idx]) {
- return -1;
- }
- if (mTable[((5 * idx) + 1 + shift) & mask] == nullptr) {
- return idx;
- }
- if (mTable[idx]->key == key) {
- return idx;
- }
- SKIP:
- idx = ((5 * idx) + 1 + shift) & mask;
- goto NEXT;
- }
-
- ualni findSlotWrite(KeyArg key) const {
- ualni const hashed_key = tHashFunc(key);
- ualni const mask = mNSlots - 1;
- ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1;
- ualni idx = hashed_key & mask;
- NEXT:
- if (isDeletedNode(mTable[idx]) || !mTable[idx]) {
- return idx;
- }
- if (mTable[idx]->key == key) {
- return idx;
- }
- idx = ((5 * idx) + 1 + shift) & mask;
- goto NEXT;
- }
-
- void put(Node* node) {
- const ualni idx = findSlotWrite(node->key);
-
- if (!mTable[idx] || isDeletedNode(mTable[idx])) {
- mNEntries++;
- }
-
- mTable[idx] = node;
-
- if ((halnf)mNEntries / mNSlots > maxLoadFactor()) {
- rehash();
- }
- }
-
- public:
-
- Map() {
- MODULE_SANITY_CHECK(gModuleContainers)
- mNSlots = next2pow(uhalni(tTableInitialSize - 1));
- mTable = newTable(mNSlots);
- }
-
- Node** buff() const {
- return mTable;
- }
-
- [[nodiscard]] ualni size() const {
- return mNEntries;
- }
-
- [[nodiscard]] ualni slotsSize() const {
- return mNEntries;
- }
-
- [[nodiscard]] const tAllocator& getAllocator() const {
- return mAlloc;
- }
-
- void put(KeyArg key, ValArg val) {
- const ualni idx = findSlotWrite(key);
- if (!mTable[idx] || isDeletedNode(mTable[idx])) {
- mTable[idx] = newNode(key, val);
- mNEntries++;
- }
- mTable[idx]->val = val;
- if ((halnf) mNEntries / mNSlots > maxLoadFactor()) {
- rehash();
- }
- }
-
- // undefined behavior if item is not presents
- tVal& get(KeyArg key) {
- DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
- return mTable[findSlotReadExisting(key)]->val;
- }
-
- const tVal& get(KeyArg key) const {
- DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
- return mTable[findSlotReadExisting(key)]->val;
- }
-
- [[nodiscard]] Idx presents(KeyArg key) const { return { findSlotRead(key) }; }
-
- void remove(KeyArg key) {
- DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
- auto idx = findSlotReadExisting(key);
-
- deleteNode(mTable[idx]);
-
- markDeletedSlot(idx);
-
- mNEntries--;
- if (halnf(mNEntries / mNSlots) < 1.f - maxLoadFactor()) {
- rehash();
- }
- }
-
- const tVal& getSlotVal(ualni slot) const {
- DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error")
- return mTable[slot]->val;
- }
-
- tVal& getSlotVal(ualni slot) {
- DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error")
- return mTable[slot]->val;
- }
-
- const tVal& getSlotVal(Idx slot) const {
- DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error")
- return mTable[slot]->val;
- }
-
- tVal& getSlotVal(Idx slot) {
- DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error")
- return mTable[slot.idx]->val;
- }
-
- Map& operator=(const Map& in) {
- if (this == &in) {
- return *this;
- }
- removeAll();
- mNSlots = in.mNSlots;
- mTable = newTable(mNSlots);
- for (alni i = 0; i < mNSlots; i++) {
- if (in.mTable[i] && !isDeletedNode(in.mTable[i])) {
- put(in.mTable[i]->key, in.mTable[i]->val);
- }
- }
- return *this;
- }
-
- [[nodiscard]] bool operator==(const Map& in) const {
- if (this == &in) {
- return true;
- }
- if (in.mNEntries != mNEntries) {
- return false;
- }
- for (auto i : in) {
- if (!presents(i->key) || get(i->key) != i->val) {
- return false;
- }
- }
- return true;
- }
-
- void removeAll() {
- for (ualni i = 0; i < mNSlots; i++) {
- if (mTable[i] && !isDeletedNode(mTable[i])) {
- deleteNode(mTable[i]);
- }
- }
- deleteTable(mTable);
- mTable = newTable(tTableInitialSize);
- mNSlots = tTableInitialSize;
- mNEntries = 0;
- }
-
- [[nodiscard]] alni slotIdx(alni entry_idx_in) const {
- alni entry_idx = -1;
- for (alni slot_idx = 0; slot_idx < mNSlots; slot_idx++) {
- if (mTable[slot_idx]) {
- entry_idx++;
- }
- if (entry_idx == entry_idx_in) {
- return slot_idx;
- }
- }
- return -1;
- }
-
- Node* GetEntry(ualni idx) {
- auto slot = slotIdx(idx);
- DEBUG_ASSERT(slot != -1 && "Key error")
- return mTable[slot];
- }
-
- const Node* GetEntry(ualni idx) const {
- auto slot = slotIdx(idx);
- DEBUG_ASSERT(slot != -1 && "Key error")
- return mTable[slot];
- }
-
- public:
-
- class Iterator {
- const Map* map;
- Node* mIter;
- alni mSlot;
- alni mEntry;
-
- friend Map;
- explicit Iterator(const Map* _map) {
- mSlot = -1;
- mEntry = -1;
- map = _map;
- this->operator++();
- }
-
- public:
- Node* operator->() { return mIter; }
- const Node* operator->() const { return mIter; }
- const Iterator& operator*() const { return *this; }
-
- bool operator!=(ualni idx) const { return mSlot != idx; }
-
- void operator++() {
- mSlot++;
- while ((map->isDeletedNode(map->mTable[mSlot]) || !map->mTable[mSlot]) && (mSlot != map->mNSlots)) {
- mSlot++;
- }
- if (mSlot != map->mNSlots) {
- mIter = map->mTable[mSlot];
- mEntry++;
- }
- }
- };
-
- [[nodiscard]] Iterator begin() const {
- return Iterator(this);
- }
-
- [[nodiscard]] ualni end() const {
- return mNSlots;
- }
-
- template
- void write(Saver& file) {
- file.write(mNEntries);
- for (auto item : *this) {
- file.write(item->val);
- file.write(item->key);
- }
- }
-
- template
- void read(Loader& file) {
- removeAll();
- ualni len;
- file.read(len);
- for (auto i = len; i; i--) {
- auto node = newNodeNotConstructed();
- file.read(node->val);
- file.read(node->key);
- put(node);
- }
- }
-
- ~Map() { removeAll(); }
- };
+
+#pragma once
+
+#include "Common.hpp"
+#include "ContainersCommon.hpp"
+
+namespace tp {
+
+ template
+ ualni DefaultHashFunc(SelectValueOrReference key) {
+ return hash(key);
+ }
+
+ template ) = DefaultHashFunc, int tTableInitialSize = 4>
+ class Map {
+
+ enum {
+ MAP_PERTURB_SHIFT = 5,
+ MAP_MIN_SIZE = 4,
+ MAP_MAX_LOAD_PERCENTAGE = 66,
+ };
+
+ typedef SelectValueOrReference KeyArg;
+ typedef SelectValueOrReference 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;
+ [[nodiscard]] bool isValid() const { return bool(*this); }
+ explicit operator bool() const { return idx != -1; }
+ };
+
+ private:
+ tAllocator mAlloc;
+ Node** mTable = nullptr;
+ 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() {
+ mNSlots = next2pow(uhalni(tTableInitialSize - 1));
+ mTable = newTable(mNSlots);
+ }
+
+ Map(const Map& in) { this->operator=(in); }
+
+ 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++;
+ } else {
+ 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.idx]->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;
+ }
+
+ for (ualni i = 0; i < mNSlots; i++) {
+ if (mTable[i] && !isDeletedNode(mTable[i])) {
+ deleteNode(mTable[i]);
+ }
+ }
+ mNEntries = 0;
+ mNSlots = in.mNSlots;
+ deleteTable(mTable);
+ mTable = newTable(mNSlots);
+ for (alni i = 0; i < mNSlots; i++) {
+ if (in.mTable[i] && !isDeletedNode(in.mTable[i])) {
+ put(in.mTable[i]->key, in.mTable[i]->val);
+ }
+ }
+ return *this;
+ }
+
+ [[nodiscard]] bool operator==(const Map& in) const {
+ if (this == &in) {
+ return true;
+ }
+ if (in.mNEntries != mNEntries) {
+ return false;
+ }
+ for (auto i : in) {
+ if (!presents(i->key) || get(i->key) != i->val) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void removeAll() {
+ for (ualni i = 0; i < mNSlots; i++) {
+ if (mTable[i] && !isDeletedNode(mTable[i])) {
+ deleteNode(mTable[i]);
+ }
+ }
+ deleteTable(mTable);
+ mTable = newTable(tTableInitialSize);
+ mNSlots = tTableInitialSize;
+ mNEntries = 0;
+ }
+
+ [[nodiscard]] alni slotIdx(alni entry_idx_in) const {
+ alni entry_idx = -1;
+ for (alni slot_idx = 0; slot_idx < mNSlots; slot_idx++) {
+ if (mTable[slot_idx]) {
+ entry_idx++;
+ }
+ if (entry_idx == entry_idx_in) {
+ return slot_idx;
+ }
+ }
+ return -1;
+ }
+
+ Node* GetEntry(ualni idx) {
+ auto slot = slotIdx(idx);
+ DEBUG_ASSERT(slot != -1 && "Key error")
+ return mTable[slot];
+ }
+
+ const Node* GetEntry(ualni idx) const {
+ auto slot = slotIdx(idx);
+ DEBUG_ASSERT(slot != -1 && "Key error")
+ return mTable[slot];
+ }
+
+ public:
+ class Iterator {
+ const Map* map;
+ Node* mIter;
+ alni mSlot;
+ alni mEntry;
+
+ friend Map;
+ explicit Iterator(const Map* _map) {
+ mSlot = -1;
+ mEntry = -1;
+ map = _map;
+ this->operator++();
+ }
+
+ public:
+ Node* operator->() { return mIter; }
+ const Node* operator->() const { return mIter; }
+ const Iterator& operator*() const { return *this; }
+
+ bool operator!=(ualni idx) const { return mSlot != idx; }
+
+ void operator++() {
+ mSlot++;
+ while ((map->isDeletedNode(map->mTable[mSlot]) || !map->mTable[mSlot]) && (mSlot != map->mNSlots)) {
+ mSlot++;
+ }
+ if (mSlot != map->mNSlots) {
+ mIter = map->mTable[mSlot];
+ mEntry++;
+ }
+ }
+ };
+
+ [[nodiscard]] Iterator begin() const { return Iterator(this); }
+
+ [[nodiscard]] ualni end() const { return mNSlots; }
+
+ template
+ void archiveWrite(Archiver& ar) const {
+ ar << mNEntries;
+ for (auto item : *this) {
+ ar << item->val;
+ ar << item->key;
+ }
+ }
+
+ template
+ void archiveRead(Archiver& ar) {
+ removeAll();
+ decltype(mNSlots) len;
+ ar >> len;
+ for (auto i = len; i; i--) {
+ auto node = newNodeNotConstructed();
+ ar >> node->val;
+ ar >> node->key;
+ put(node);
+ }
+ }
+
+ ~Map() {
+ for (ualni i = 0; i < mNSlots; i++) {
+ if (mTable[i] && !isDeletedNode(mTable[i])) {
+ deleteNode(mTable[i]);
+ }
+ }
+ deleteTable(mTable);
+ }
+ };
}
\ No newline at end of file
diff --git a/Containers/public/Tree.hpp b/Containers/public/Tree.hpp
index dfeefc2..3b84fe8 100644
--- a/Containers/public/Tree.hpp
+++ b/Containers/public/Tree.hpp
@@ -1,373 +1,384 @@
-#pragma once
-
-#include "ContainersCommon.hpp"
-
-namespace tp {
-
- template
- struct AvlNumericKey {
-
- NumericType val;
-
- AvlNumericKey() = default;
- AvlNumericKey(NumericType val) : val(val) {}
-
- inline bool descentRight(AvlNumericKey in) const { return in.val > val; }
- inline bool descentLeft(AvlNumericKey in) const { return in.val < val; }
- inline bool exactNode(AvlNumericKey in) const { return in.val == val; }
-
- inline AvlNumericKey getFindKey(/**/) const { return *this; }
- inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; }
- inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; }
-
- inline void updateTreeCacheCallBack() {}
- };
-
- template
- class AvlTree {
- typedef SelCopyArg KeyArg;
- typedef SelCopyArg DataArg;
-
- public:
- class Node {
- friend AvlTree;
-
- private:
- Node(KeyArg aKey, DataArg aData) : key(aKey), data(aData) {}
-
- public:
- Data data;
- Key key;
-
- private:
- Node* mLeft = nullptr;
- Node* mRight = nullptr;
- Node* mParent = nullptr;
- ualni mHeight = 0;
-
- private:
- inline bool descentRight(KeyArg aKey) const { return key.descentRight(aKey); }
- inline bool descentLeft(KeyArg aKey) const { return key.descentLeft(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 void injectNodeInstead(Node* place, Node* inject) {
- // TODO : swap instead of copy
- place->data = inject->data;
- place->key = inject->key;
- }
-
- inline alni 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 isLeft or isRight 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);
- auto const& newKey = min->getFindKey(head->mRight);
- injectNodeInstead(head, min);
- head->mRight = removeUtil(head->mRight, newKey);
- }
- else if (head->mRight) {
- injectNodeInstead(head, head->mRight);
- deleteNode(head->mRight);
- head->mRight = nullptr;
- mSize--;
- }
- else if (head->mLeft) {
- injectNodeInstead(head, head->mLeft);
- 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 && head->mLeft->mHeight != head->mHeight - 1) return head;
- }
-
- if (head->mRight) {
- if (!head->descentRight(head->mRight->getFindKey(head))) return head;
- if (head->mRight->mParent != head) return head;
- if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) return head;
- }
-
- if (head->mLeft && head->mRight) {
- if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) 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; }
- };
+#pragma once
+
+#include "ContainersCommon.hpp"
+
+namespace tp {
+
+ template
+ struct AvlNumericKey {
+
+ NumericType val;
+
+ AvlNumericKey() = default;
+ AvlNumericKey(NumericType val) :
+ val(val) {}
+
+ inline bool descentRight(AvlNumericKey in) const { return in.val > val; }
+ inline bool descentLeft(AvlNumericKey in) const { return in.val < val; }
+ inline bool exactNode(AvlNumericKey in) const { return in.val == val; }
+
+ inline AvlNumericKey getFindKey(/**/) const { return *this; }
+ inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; }
+ inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; }
+
+ template
+ inline void updateTreeCacheCallBack(const NodeType&) {}
+ };
+
+ template
+ class AvlTree {
+ typedef SelectValueOrReference KeyArg;
+ typedef SelectValueOrReference DataArg;
+
+ public:
+ class Node {
+ friend AvlTree;
+
+ private:
+ Node(KeyArg aKey, DataArg aData) :
+ key(aKey),
+ data(aData) {}
+
+ public:
+ Data data;
+ Key key;
+
+ public:
+ 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.descentLeft(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(*this); }
+ };
+
+ public:
+ AvlTree() {}
+ ~AvlTree() { removeAll(); }
+
+ [[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 && head->mLeft->mHeight != head->mHeight - 1) return head;
+ }
+
+ if (head->mRight) {
+ if (!head->descentRight(head->mRight->getFindKey(head))) return head;
+ if (head->mRight->mParent != head) return head;
+ if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) return head;
+ }
+
+ if (head->mLeft && head->mRight) {
+ if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) 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; }
+
+ template
+ void traverse(Node* node, bool after, tFunctor functor) {
+ if (!after) functor(node);
+ if (node->mLeft) traverse(node->mLeft, after, functor);
+ if (node->mRight) traverse(node->mRight, after, functor);
+ if (after) functor(node);
+ }
+
+ void removeAll() {
+ if (!mRoot) return;
+ removeUtil(mRoot);
+ mRoot = nullptr;
+ mSize = 0;
+ }
+
+ void removeUtil(Node* node) {
+ if (node->mLeft) removeUtil(node->mLeft);
+ if (node->mRight) removeUtil(node->mRight);
+ deleteNode(node);
+ }
+
+ public:
+ template
+ void archiveWrite(tArchiver& file) const {
+ FAIL("not implemented")
+ }
+
+ template
+ void archiveRead(tArchiver&) {
+ FAIL("not implemented")
+ }
+
+ 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 void injectNodeInstead(Node* place, Node* inject) {
+ // TODO : swap instead of copy
+ place->data = inject->data;
+ place->key = inject->key;
+ }
+
+ inline alni 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 isLeft or isRight 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);
+ auto const& newKey = min->getFindKey(head->mRight);
+ injectNodeInstead(head, min);
+ head->mRight = removeUtil(head->mRight, newKey);
+ } else if (head->mRight) {
+ injectNodeInstead(head, head->mRight);
+ deleteNode(head->mRight);
+ head->mRight = nullptr;
+ mSize--;
+ } else if (head->mLeft) {
+ injectNodeInstead(head, head->mLeft);
+ 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;
+ }
+
+ private:
+ Node* mRoot = nullptr;
+ ualni mSize = 0;
+ Allocator mAlloc;
+ };
}
\ No newline at end of file
diff --git a/Containers/tests/Buffer2DTest.cpp b/Containers/tests/Buffer2DTest.cpp
new file mode 100644
index 0000000..202f731
--- /dev/null
+++ b/Containers/tests/Buffer2DTest.cpp
@@ -0,0 +1,19 @@
+#include "Buffer2D.hpp"
+#include "Tests.hpp"
+
+using namespace tp;
+
+const ualni size = 1000;
+
+SUITE(Buffer2D) {
+ TEST(Simple) {
+ Buffer2D buff;
+ buff.reserve({ 4, 4 });
+ buff.set({ 2, 2 }, 5);
+ CHECK(buff.get({ 2, 2 }) == 5);
+ }
+
+ TEST(NO_TESTS) {
+ CHECK(false);
+ }
+}
diff --git a/Containers/tests/BufferTest.cpp b/Containers/tests/BufferTest.cpp
index bcca3a8..685f285 100644
--- a/Containers/tests/BufferTest.cpp
+++ b/Containers/tests/BufferTest.cpp
@@ -1,37 +1,33 @@
-
-#include "Tests.hpp"
-
#include "Buffer.hpp"
-
-#include "Testing.hpp"
-
-#include
+#include "Tests.hpp"
using namespace tp;
const ualni size = 1000;
-TEST_DEF_STATIC(Simple1) {
- Buffer buff;
- TEST(buff.size() == 0);
- for (auto i : Range(size * 10)) {
- buff.append(TestClass(i));
- }
- TEST(buff.size() == size * 10);
- while (buff.size()) buff.pop();
- TEST(buff.size() == 0);
-}
+SUITE(Buffer) {
+ TEST(Simple1) {
+ Buffer buff;
+ CHECK(buff.size() == 0);
+ for (auto i : Range(size * 10)) {
+ buff.append(TestClass(i));
+ }
+ CHECK(buff.size() == size * 10);
+ while (buff.size())
+ buff.pop();
+ CHECK(buff.size() == 0);
+ }
-TEST_DEF_STATIC(Simple2) {
- Buffer buff(size);
- TEST(buff.size() == 0);
- for (auto i : Range(size * 10)) buff.append(TestClass(i));
- TEST(buff.size() == size * 10);
- while (buff.size()) buff.pop();
- TEST(buff.size() == 0);
-}
+ TEST(Simple2) {
+ Buffer buff(size);
+ CHECK(buff.size() == size);
+ for (auto i : Range(size * 10))
+ buff.append(TestClass(i));
+ CHECK(buff.size() == size + size * 10);
+ while (buff.size())
+ buff.pop();
+ CHECK(buff.size() == 0);
+ }
-TEST_DEF(Buffer) {
- testSimple1();
- testSimple2();
+ TEST(NO_TEST) { CHECK(false); }
}
\ No newline at end of file
diff --git a/Containers/tests/IntervalTreeTests.cpp b/Containers/tests/IntervalTreeTests.cpp
new file mode 100644
index 0000000..c139787
--- /dev/null
+++ b/Containers/tests/IntervalTreeTests.cpp
@@ -0,0 +1,274 @@
+#include "IntervalTree.hpp"
+#include "Tests.hpp"
+
+#include "Buffer.hpp"
+
+#include
+
+using namespace tp;
+
+struct Interval {
+
+ [[nodiscard]] bool overlaps(const Interval& in) const { return in.start <= end && in.end >= start; }
+
+ halnf start{};
+ halnf end{};
+ bool ignore = false;
+
+ void random(halnf span, halnf scale = 1.f) {
+ start = ((halnf) randomFloat()) * (span);
+ end = ((halnf) randomFloat()) * (span);
+ if (start > end) swap(start, end);
+
+ auto len = (end - start) * scale * 0.5f;
+ auto mid = (start + end) / 2.f;
+
+ start = mid - len;
+ end = mid + len;
+ }
+
+ void randomSized(halnf span, halnf size, halnf wobble) {
+ start = ((halnf) randomFloat()) * (span);
+ end = start + size + (halnf) (randomFloat() * wobble);
+ }
+};
+
+SUITE(IntervalTree) {
+ TEST(FunctionalitySimple) {
+
+ IntervalTree intervalTree;
+
+ intervalTree.insert({ 0, 10 }, 1);
+ intervalTree.insert({ 3, 6 }, 2);
+ intervalTree.insert({ 8, 12 }, 3);
+
+ intervalTree.forEachIntersection(4, 5, [](alni start, ualni end, ualni data) {
+ printf("%i", int(data));
+ printf("\n");
+ });
+ }
+
+ TEST(FunctionalityScale) {
+
+ const int NUM_TEST_INTERVALS = 1000;
+ const halnf SPAN = 100;
+
+ Buffer pool;
+ IntervalTree intervalTree;
+
+ Buffer testIntervals;
+
+ auto test = [&]() {
+ for (auto testInterval : testIntervals) {
+
+ Buffer correct;
+ Buffer result;
+
+ ualni idx = 0;
+ for (auto interval : pool) {
+ if (!interval->ignore && interval->overlaps(testInterval.data())) {
+ correct.append(idx);
+ }
+ idx++;
+ }
+
+ intervalTree.forEachIntersection(testInterval->start, testInterval->end, [&](alni start, ualni end, ualni data) {
+ result.append(data);
+ });
+
+ CHECK(correct.size() == result.size());
+
+ if (!(correct.size() == result.size())) {
+ printf("intersections - \n");
+
+ for (auto i : correct) {
+ printf(" %i", (int) i.data());
+ }
+ printf("\n");
+
+ for (auto i : result) {
+ printf(" %i", (int) i.data());
+ }
+ printf("\n\n");
+ }
+ // todo compare containers
+ }
+ };
+
+ // initialize
+ for (auto i : Range(NUM_TEST_INTERVALS)) {
+ auto interval = Interval();
+ interval.random(SPAN);
+
+ pool.append(interval);
+ intervalTree.insert({ interval.start, interval.end }, i);
+
+ interval.random(SPAN * 2.f, (halnf) randomFloat());
+
+ testIntervals.append(interval);
+ }
+
+ test();
+
+ // remove some
+ for (auto i : Range(NUM_TEST_INTERVALS / 2)) {
+ auto idx = ualni(randomFloat() * (alnf) pool.size());
+ pool[idx].ignore = true;
+ intervalTree.remove({ pool[idx].start, pool[idx].end });
+ }
+
+ test();
+ }
+
+ TEST(Efficency) {
+
+ struct Stat {
+ halnf numItems = 0;
+ halnf avgFound = 0;
+ halnf avgChecks = 0;
+ };
+
+ auto test = [&](ualni NUM_TEST_INTERVALS, ualni NUM_CHECKS) {
+ const auto SPAN = (halnf) (halnf(NUM_TEST_INTERVALS));
+ const auto SCALE = (halnf) (2.f);
+
+ IntervalTree intervalTree;
+ Buffer testIntervals;
+
+ auto WOBBLE = SPAN * 0;
+ for (auto i : Range(NUM_TEST_INTERVALS)) {
+ auto interval = Interval();
+ interval.randomSized(SPAN, SCALE, WOBBLE);
+ intervalTree.insert({ interval.start, interval.end }, i);
+ // WOBBLE -= 1.f;
+ }
+
+ for (auto i : Range(0))
+ intervalTree.insert({ (halnf) i * 0.01f, SPAN }, 0);
+
+ WOBBLE = 0;
+ for (auto i : Range(NUM_CHECKS)) {
+ auto interval = Interval();
+ interval.randomSized(SPAN, SCALE, WOBBLE);
+ testIntervals.append(interval);
+ }
+
+ ualni debugMaxChecks = 0;
+ ualni debugMaxFound = 0;
+ halnf debugAvgChecks = 0;
+ halnf debugAvgFound = 0;
+
+ for (auto testInterval : testIntervals) {
+ ualni debugFound = 0;
+ ualni debug = intervalTree.forEachIntersection(
+ testInterval->start,
+ testInterval->end,
+ [&](ualni start, ualni end, ualni data) {
+ debugFound++;
+ //
+ }
+ );
+
+ if (debug > debugMaxChecks) {
+ debugMaxChecks = debug;
+ debugMaxFound = debugFound;
+ }
+
+ debugMaxChecks = max(debug, debugMaxChecks);
+ debugAvgChecks += (halnf) debug;
+ debugAvgFound += (halnf) debugFound;
+ }
+
+ debugAvgChecks /= (halnf) testIntervals.size();
+ debugAvgFound /= (halnf) testIntervals.size();
+
+ printf("\nItems : %llu\n", NUM_TEST_INTERVALS);
+ printf("Avg(checks) : %f\n", debugAvgChecks);
+ printf("Avg(found) : %f\n", debugAvgFound);
+ printf("Max checks : %llu\n", debugMaxChecks);
+ printf("Max checks found : %llu\n", debugMaxFound);
+ printf("N(Avg(checks) / Avg(N(items)) : %f\n", debugAvgChecks / (halnf) intervalTree.size());
+ printf("N(Avg(found)) / Avg(N(items)) : %f\n", debugAvgFound / (halnf) intervalTree.size());
+ printf("Avg(found) / Avg(checks) : %f\n", debugAvgFound / debugAvgChecks);
+
+ return Stat{ (halnf) NUM_TEST_INTERVALS,
+ debugAvgFound / (halnf) intervalTree.size(),
+ debugAvgChecks / (halnf) intervalTree.size() };
+ };
+
+ Buffer stats;
+ for (auto i : Range(2, 5)) {
+ Stat stat = test(pow(10, i), 100);
+ stats.append(stat);
+ }
+
+ printf("\nChecks: ");
+ for (auto stat : stats)
+ printf("%e ", stat->avgChecks);
+
+ printf("\nHits: ");
+ for (auto stat : stats)
+ printf("%e ", stat->avgFound);
+
+ printf("\nItems: ");
+ for (auto stat : stats)
+ printf("%e ", stat->numItems);
+
+ printf("\n\n");
+ }
+
+ TEST(FunctionalityComplex) {
+ IntervalTree