Compare commits

...
Sign in to create a new pull request.

159 commits
master ... safe

Author SHA1 Message Date
Ilusha
e6f535fe6e fix raytracer 2024-03-16 20:57:46 +03:00
Ilusha
1ead32d84a tmp 2024-03-16 12:51:09 +03:00
Ilusha
65cb26a627 tmp 2024-03-15 23:42:10 +03:00
Ilusha
00d8fa0886 Use UnitTest-Cpp library for testing 2024-03-15 17:22:33 +03:00
Ilusha
ecaa2bbdfb Recovering from github accound ban 2024-03-15 11:31:41 +03:00
Ilusha
c2ad9ce501 tmp 2024-03-14 09:00:14 +03:00
Ilusha
aa2d8f1200 new testing framework 2024-03-14 09:00:03 +03:00
Ilusha
5d94ddd774 tmp 2024-03-14 08:00:35 +03:00
Ilusha
da74d27135 tmp 2024-03-13 14:20:50 +03:00
Ilusha
37ae4b2abb add parser chech for precedence in arithmetic expressions 2024-03-13 14:10:18 +03:00
Ilusha
afe32d6014 Fixing lalr on windows 2024-03-12 07:33:11 -07:00
Ilusha
ffafeba6c2
Update cmake.yml 2024-03-09 10:49:15 +03:00
Ilusha
80fcbe4e2c
try clang 14 2024-03-09 10:44:51 +03:00
Ilusha
d7d809c222
try clang 16 in buildbot 2024-03-09 10:42:09 +03:00
Ilusha
8ac49dc50b
Update cmake.yml 2024-03-09 10:35:45 +03:00
Ilusha
cce9609ced Adding windows build information 2024-03-09 10:29:02 +03:00
Ilusha
ac4f08c67c Edit todo and status texts 2024-03-07 13:28:59 +03:00
Ilusha
23e7ecb812 Fix interval tree 2024-03-07 12:45:24 +03:00
Ilusha
20ac8620e6 stable 2024-03-07 12:33:57 +03:00
IlyaShurupov
b906e0e66c tmp 2024-03-07 12:33:57 +03:00
IlyaShurupov
49c67eb028 parser error handling 2024-03-07 12:33:57 +03:00
IlyaShurupov
fdb861b299 Parser done implementing 2024-03-07 12:33:57 +03:00
IlyaShurupov
828096de17 tmp 2024-03-07 12:33:57 +03:00
IlyaShurupov
e628e7c1e2 grammar done 2024-03-07 12:33:57 +03:00
IlyaShurupov
ad543c304d tmp 2024-03-07 12:33:57 +03:00
IlyaShurupov
ed3421321c tmp 2024-03-07 12:33:57 +03:00
Ilusha
f666416696 Replace object parser with lalr library 2024-03-07 12:33:46 +03:00
IlyaShurupov
3ba4bef93b small fix 2024-03-07 12:31:48 +03:00
IlyaShurupov
3bed74df87 parsing loop closed 2024-03-07 12:31:48 +03:00
IlyaShurupov
63b0560375 fix compile errors 2024-03-07 12:31:48 +03:00
IlyaShurupov
1d9ef2aa6c cfg automata ini 2024-03-07 12:31:48 +03:00
IlyaShurupov
35387395eb cfg accepting 2024-03-07 12:31:48 +03:00
IlyaShurupov
e6d77c639b refactor regular automata 2024-03-07 12:31:48 +03:00
IlyaShurupov
a9a810c197 CFG compiler ini 2024-03-07 12:31:48 +03:00
IlyaShurupov
4f88c45c9e tmp 2024-03-07 12:31:48 +03:00
IlyaShurupov
81a2247bf2 tmp 2024-03-07 12:31:48 +03:00
IlyaShurupov
dd6b63acb1 reg automata fixes 2024-03-07 12:31:48 +03:00
IlyaShurupov
41aa8daeb2 tmp 2024-03-07 12:31:48 +03:00
Ilusha
1f1dc001a1 tmp 2024-03-07 12:31:26 +03:00
IlyaShurupov
93b02416de refactor and reuse Automatas. TODO : add test for automatas 2024-03-07 12:27:48 +03:00
IlyaShurupov
312419936e tmp 2024-03-07 12:27:47 +03:00
IlyaShurupov
d2bb8cdd6a tmp 2024-03-07 12:27:47 +03:00
IlyaShurupov
7112002e30 Reuse Regular Automata functionality 2024-03-07 12:27:47 +03:00
IlyaShurupov
656da1fd76 Make parser template class 2024-03-07 12:27:47 +03:00
IlyaShurupov
38957e678b TODO : Make RE Grammar template independent. Difine Unified grammar format. 2024-03-07 12:27:47 +03:00
IlyaShurupov
7ed6b1f33c tmp 2024-03-07 12:27:47 +03:00
IlyaShurupov
060aaecbeb Adding implementation comments to Language 2024-03-07 12:27:47 +03:00
IlyaShurupov
fae4d83f97 Language Initial 2024-03-07 12:27:47 +03:00
IlyaShurupov
14cb588948 Fix avl tree destructor 2024-03-01 19:03:31 +03:00
IlyaShurupov
7d8cf66b27 Adding more tests for interval tree 2024-02-14 10:50:01 +03:00
IlyaShurupov
a3399fdc56 Interval Tree added 2024-02-13 19:07:46 +03:00
IlyaShurupov
7932887f43 Update ImGui 2024-02-03 10:46:29 +03:00
IlyaShurupov
3b50001daf Refactor lib view 2024-02-03 10:46:29 +03:00
Ilusha
83d51d3e83 ini 2024-02-03 10:46:29 +03:00
Ilusha
f7f0d6e5be Merge branch 'master' of https://github.com/IlyaShurupov/Modules 2024-01-17 22:49:56 +03:00
Ilusha
f146ddd307 Remove mutex class and use std 2024-01-17 22:42:48 +03:00
Ilusha
69cdf43909 Small fixes from windows 2024-01-17 16:14:47 -08:00
Ilusha
7298858c96
Update WindowsPlatform.yml 2024-01-17 08:37:51 -08:00
Ilusha
dec6a9dc73
Update WindowsPlatform.yml 2024-01-17 08:36:38 -08:00
Ilusha
db73ffb439
Create WindowsPlatform.yml 2024-01-17 08:34:25 -08:00
Ilusha
34a8d4eafd Adding support for windows and fixing some errors 2024-01-16 19:58:50 +03:00
IlyaShurupov
2722a25036 Fix build errors 2024-01-10 16:10:58 +03:00
IlyaShurupov
81e16e1c50 Exclude glew from manual build duo to errors. Using precompiled packages. 2024-01-10 15:47:47 +03:00
Ilusha
f7fd0c5942
Update cmake.yml change alsa package name 2024-01-10 10:57:43 +00:00
Ilusha
023c3908e0
Update cmake.yml add alsa dep 2024-01-10 10:54:08 +00:00
IlyaShurupov
a5f4d7149a Refactor! 2024-01-10 12:05:31 +03:00
IlyaShurupov
5d7a343a20 Filtering 2023-11-30 15:57:14 +03:00
IlyaShurupov
d5ff875497 Neet fixes 2023-11-30 15:19:40 +03:00
IlyaShurupov
231b30b2cb Adding Music Playback 2023-11-29 19:42:17 +03:00
IlyaShurupov
7b325244c5 Side panel added 2023-11-29 14:36:59 +03:00
IlyaShurupov
c7d3b15758 LibraryViewer 2023-11-28 23:22:04 +03:00
IlyaShurupov
5a55011911 CMake fixes 2023-10-28 15:56:52 +03:00
IlyaShurupov
b7a89b714e Num Rec app and save & loading 2023-10-26 16:27:43 +03:00
IlyaShurupov
fc20f3594d Number recognition Tuning 2023-10-26 00:52:40 +03:00
IlyaShurupov
826713c212 NumRec Fixes 2023-10-25 20:41:48 +03:00
IlyaShurupov
0542b7ba2e BackProp Fixes 2023-10-25 13:14:45 +03:00
IlyaShurupov
efbb09f8ba BackProp Initial 2023-10-23 13:38:00 +03:00
IlyaShurupov
744c01c5d0 Apply formating to all files. CLeanup 2023-10-22 20:23:29 +03:00
IlyaShurupov
43e374f269 Complex toy classes & DataAnalysis initial 2023-10-21 23:20:20 +03:00
IlyaShurupov
93a90e0291 RT Multisampling 2023-10-19 19:11:29 +03:00
IlyaShurupov
3396e69b47 Save multiple buffers from RayTracer 2023-10-18 20:13:07 +03:00
IlyaShurupov
7af12ee1a0 Adding simple test for RT 2023-10-18 19:05:51 +03:00
IlyaShurupov
2e0d0e0050 Adding progress bar for rt application 2023-10-18 13:45:47 +03:00
IlyaShurupov
414d4e6411 Primitive sampling 2023-10-18 12:05:01 +03:00
IlyaShurupov
97f79a6483 Adding normals 2023-10-17 20:51:25 +03:00
IlyaShurupov
cfb4c0cfcc RT Todo 2023-10-17 12:32:12 +03:00
IlyaShurupov
c284a12ade Fix issue with submodule mapping for lua 2023-10-16 23:10:14 +03:00
IlyaShurupov
3e9365b1ee Adding scene files 2023-10-16 22:38:45 +03:00
IlyaShurupov
517dc83c85 Adding lua scene description 2023-10-16 22:09:20 +03:00
IlyaShurupov
e00ab4059c tmp 2023-10-16 16:24:31 +03:00
IlyaShurupov
0e41a6e4fd Simple Normals Rendering 2023-10-16 13:38:56 +03:00
IlyaShurupov
97525efacb Bug fixes 2023-10-16 00:02:32 +03:00
IlyaShurupov
94d47ba390 RayTracer Initial Framework setup 2023-10-15 23:25:48 +03:00
IlyaShurupov
8d990a0132 Update Clang Format 2023-09-03 10:53:20 +03:00
IlushaShurupov
2810e06b3e Adding outdated source code into master 2023-08-20 11:27:47 +03:00
IlushaShurupov
5cf467a421 Updated TODO 2023-08-14 20:53:34 +03:00
IlushaShurupov
01b6f9099c LR(1) Parser Initial 2023-08-14 20:06:32 +03:00
IlushaShurupov
616441e049 Checking for non productive rules and loops in cfg 2023-08-12 09:39:06 +03:00
IlushaShurupov
b205438601 CFG remove unused rules 2023-08-12 08:27:50 +03:00
IlushaShurupov
e6d9439ba9 Adding simple test fot cf grammar 2023-08-12 08:08:47 +03:00
IlushaShurupov
0a886bd8a8 Fixing list copy and grammar sentence generation 2023-08-11 18:57:39 +03:00
IlushaShurupov
43fe34dcbf Improve module initialization log 2023-08-11 16:42:47 +03:00
IlushaShurupov
7d49642376 Fixin cfg 2023-08-11 16:37:51 +03:00
IlushaShurupov
01b7d96ebd Fixin map 2023-08-11 16:21:30 +03:00
IlushaShurupov
6fcfa075f8 Generate sentences from cf grammar. Fix list initialization from copy constructor 2023-08-11 16:04:02 +03:00
IlushaShurupov
d888aeb2b4 Fixing tokenizer bug 2023-08-10 18:36:56 +03:00
IlushaShurupov
92f34e2a99 CF Grammar improvements 2023-08-10 18:35:48 +03:00
IlushaShurupov
424787c735 CF Grammar Initial 2023-08-10 18:35:40 +03:00
IlushaShurupov
eb712a352d Parser Initial 2023-08-10 18:35:29 +03:00
IlushaShurupov
5fab9ee507 Adding some tokinizer checks for strings and comments 2023-08-10 18:35:17 +03:00
IlushaShurupov
9c8771a378 Editing TODO 2023-08-07 19:03:17 +03:00
Ilusha
9a7e44a96d
Update cmake.yml - sudo apt-get update before installing pacages 2023-08-07 18:56:03 +03:00
IlushaShurupov
112fea2f04 Adding objects GUI initial. Changing TODO. Some fixes along side 2023-08-07 18:37:11 +03:00
IlushaShurupov
f9d62c324d "Added new test 'Complex' and improved error debugging"
This revision adds a complex test to the 'TestInterpreter.cpp' file to test functionalities such as classes, variable initializations, loops, conditionals and methods in the interpreter. The token parser has also been updated with a reset method in the 'parser.cpp' file for cleaning up any previous state before parsing a new script. This ensures the parser starts fresh for each new parse request. It also improves the debugging process in 'function.cpp' by printing the description of parser errors. Few completed tasks are removed from the TODO list.
2023-08-03 21:15:27 +03:00
IlushaShurupov
55571ca3dd Load and destroy methods revised for proper reference counting
The update enhances object management by involving scope-based reference counting. The change affects loading and destruction operations in the Object and several other classes, ensuring the release or retention of objects adhere to their usage context. A function for logging type data has also been introduced to provide better clarity during debugging. Also, tests in script & interpreter have been altered to run in separate module initializations to avoid cross-test interference. Overall, it provides better memory management and dependable tests.
2023-08-01 20:06:34 +03:00
IlushaShurupov
b06e1da529 Add Interpreter tests and improve Parser error logging
A set of tests for the InterpreterObject has been added to enhance its reliability and maintainability. The tests check the essential functionalities such as its creation, execution, saving, loading, and destruction. Furthermore, the error logging for the Parser has been improved, now providing more precise location of detected errors. The changes improve the robustness and debugability of the code.
2023-08-01 18:57:36 +03:00
IlushaShurupov
0cefb47b66 Refactor save size calculation with SaveSizeCounter
SaveSizeCounter is introduced and used for calculating the save size of objects. The previous method of calculating save size directly in each class was replaced with a call to the SaveSizeCounter's calc method. This results in a cleaner, more maintainable code as the size calculation logic is now centralized in one class. It is more efficient and scalable as any changes to the calculation can be made in one place and will be reflected everywhere.
2023-08-01 07:41:09 +03:00
IlushaShurupov
a918c83ec1 Objects adding dict test & fixes 2023-07-31 22:10:46 +03:00
IlushaShurupov
2c8e1470d1 Adding Simple Objest test. Fixes some bugs 2023-07-31 20:16:05 +03:00
IlushaShurupov
64e1f78739 Objects Compiled 2023-07-31 08:04:14 +03:00
IlushaShurupov
ba95747ef9 Merge branch 'Archiver' 2023-07-30 08:32:21 +03:00
IlyaShurupov
3575cbc543 Archiver Initial 2023-07-30 08:28:31 +03:00
IlushaShurupov
01ba8160ef Generating permutations and small buffer checks 2023-07-26 23:30:38 +03:00
IlushaShurupov
939e365a4f Objects Initial 2023-07-25 19:13:27 +03:00
IlushaShurupov
803ce23169 Objects Initial 2023-07-25 08:11:19 +03:00
IlushaShurupov
00bc875846 Freeze on no memory available 2023-07-24 20:39:28 +03:00
IlushaShurupov
5f186164a0 Graphics Animations & Renaming 2023-07-23 22:11:14 +03:00
Ilusha
3a68e70f03 Update cmake.yml to build Graphics 2023-07-23 17:05:35 +03:00
IlushaShurupov
e9c6a97a07 Reorganize a little 2023-07-23 16:50:51 +03:00
IlushaShurupov
3770c72ec0 Adding nanovg 2023-07-23 15:32:02 +03:00
IlushaShurupov
c4a7f91d56 New placement header 2023-07-23 15:32:02 +03:00
IlushaShurupov
6db1406d68 Graphics module Initial (And some more fixes embedded) 2023-07-23 15:31:34 +03:00
Ilusha
e81449eddf
Update cmake.yml - checkout submodules 2023-07-22 20:36:06 +03:00
IlushaShurupov
7595ae3926 Small changes 2023-07-22 20:32:03 +03:00
IlushaShurupov
e9dbee6667 Restructure Storage Module -> Connection module 2023-07-21 19:55:31 +03:00
IlushaShurupov
fa4d1d72dc Merge branch 'Storage' 2023-07-21 18:57:17 +03:00
IlushaShurupov
0301f608bd Fixed bug in map 2023-07-20 22:31:36 +03:00
IlushaShurupov
a543568e1d Restructure 2023-07-20 00:10:29 +03:00
IlushaShurupov
576a3565f7 Command Line INterpreter with fixes 2023-07-19 21:29:57 +03:00
IlushaShurupov
62cb2bb8b7 Fix File test 2023-07-18 23:48:54 +03:00
IlushaShurupov
8e00882bb3 Networking Initial 2023-07-18 23:37:04 +03:00
IlushaShurupov
df3767df29 Storage initial 2023-07-18 23:36:51 +03:00
IlushaShurupov
90998e2279 CommandLine parser initial 2023-07-18 22:37:53 +03:00
IlushaShurupov
6bca7431b7 Adding Strings tests 2023-07-17 22:32:55 +03:00
Ilusha
91b00817e3
Update cmake.yml 2023-07-17 20:49:19 +03:00
Ilusha
d80ea40848
Update cmake.yml 2023-07-17 20:45:05 +03:00
Ilusha
bedd88be2e
Try coverage cmake.yml 2023-07-17 20:43:45 +03:00
IlushaShurupov
bc892da992 Fixes 2023-07-16 18:28:24 +03:00
IlushaShurupov
fabceb52d3 Update Todo 2023-07-15 20:43:24 +03:00
IlushaShurupov
b68539cc0c Tokenizer Test 2023-07-15 20:03:52 +03:00
IlushaShurupov
44bad77e93 Tokenizer (No tests) 2023-07-15 18:58:31 +03:00
IlushaShurupov
e63843c6bb Remove Cmd Arg parser for now 2023-07-15 13:43:55 +03:00
IlushaShurupov
692994c2d3 Tokenizer Initial 2023-07-15 13:43:55 +03:00
IlushaShurupov
facc17ec1b Buffer 2d (No tests) 2023-07-15 13:43:23 +03:00
IlushaShurupov
4c9691e192 Remove old testing 2023-07-15 11:15:54 +03:00
IlushaShurupov
c75cb9fb48 Testing 2023-07-15 11:12:10 +03:00
IlushaShurupov
d362d8c3b3 Removing TextEditor from strings for now 2023-07-15 11:12:10 +03:00
IlushaShurupov
eb9210de1c Strings Initial 2023-07-15 11:12:10 +03:00
EgorPanteleev
9cf541a6ba
Update README.MD (#1) 2023-07-13 20:14:38 +03:00
296 changed files with 276722 additions and 6153 deletions

View file

@ -1,66 +1,211 @@
# Generated from CLion C/C++ Code Style settings ---
BasedOnStyle: LLVM BasedOnStyle: LLVM
AccessModifierOffset: -2 AccessModifierOffset: -2
AlignAfterOpenBracket: Align AlignAfterOpenBracket: BlockIndent
AlignConsecutiveAssignments: None 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 AlignOperands: Align
AllowAllArgumentsOnNextLine: false AlignTrailingComments: true
AllowAllConstructorInitializersOnNextLine: false AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false AllowAllConstructorInitializersOnNextLine: true
AllowShortBlocksOnASingleLine: Always AllowAllParametersOfDeclarationOnNextLine: true
AllowShortCaseLabelsOnASingleLine: false AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: true
AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom AttributeMacros:
- __capability
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: Both
BraceWrapping: BraceWrapping:
AfterCaseLabel: false AfterCaseLabel: true
AfterClass: false AfterClass: true
AfterControlStatement: Never AfterControlStatement: Always
AfterEnum: false AfterEnum: false
AfterFunction: false AfterFunction: false
AfterNamespace: false AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false AfterUnion: false
AfterExternBlock: false
BeforeCatch: false BeforeCatch: false
BeforeElse: false BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
IndentBraces: false IndentBraces: false
SplitEmptyFunction: false SplitEmptyFunction: true
SplitEmptyRecord: true SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakAfterJavaFieldAnnotations: false
BreakBeforeBinaryOperators: None BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeConceptDeclarations: Always
BreakBeforeTernaryOperators: true BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon BreakConstructorInitializers: AfterColon
BreakInheritanceList: BeforeColon BreakInheritanceList: BeforeColon
ColumnLimit: 0 BreakStringLiterals: true
ColumnLimit: 120
CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false 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 IndentCaseLabels: true
IndentExternBlock: AfterExternBlock
IndentGotoLabels: true
IndentPPDirectives: None IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 2 IndentWidth: 2
IndentWrappedFunctionNames: false
InsertBraces: false
InsertTrailingCommas: None
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2 LambdaBodyIndentation: Signature
Language: Cpp
MacroBlockBegin: ""
MacroBlockEnd: ""
MaxEmptyLinesToKeep: 1
NamespaceIndentation: All NamespaceIndentation: All
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true 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 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 SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false SpaceAfterTemplateKeyword: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements 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 SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0 SpacesBeforeTrailingComments: 1
SpacesInAngles: false SpacesInAngles: Never
SpacesInCStyleCastParentheses: false SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInLineCommentPrefix:
Minimum: 1
Maximum: -1
SpacesInParentheses: false SpacesInParentheses: false
SpacesInSquareBrackets: false SpacesInSquareBrackets: false
Standard: Latest
StatementAttributeLikeMacros:
- Q_EMIT
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 2 TabWidth: 2
UseTab: Always UseCRLF: false
UseTab: ForContinuationAndIndentation
WhitespaceSensitiveMacros:
- STRINGIZE
- PP_STRINGIZE
- BOOST_PP_STRINGIZE
- NS_SWIFT_NAME
- CF_SWIFT_NAME

16
.cproject Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
<storageModule moduleId="org.eclipse.cdt.core.settings">
<cconfiguration id="org.eclipse.cdt.core.default.config.612128043">
<storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.612128043" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
<externalSettings/>
<extensions/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
</cconfiguration>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.pathentry">
<pathentry excluding="**/CMakeFiles/**" kind="out" path="build"/>
</storageModule>
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
</cproject>

View file

@ -0,0 +1,428 @@
<mxfile host="Electron" modified="2023-10-29T20:02:24.324Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.3 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="26yH0Gc3erZ3uKcA6DFE" version="22.0.3" type="device">
<diagram name="Page-1" id="8edUMiMEU_fADqxzhr4b">
<mxGraphModel dx="1062" dy="-128" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="850" pageHeight="1100" background="none" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="Knp0EVw5rfMEdcPhjhnq-158" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-22" target="Knp0EVw5rfMEdcPhjhnq-141" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="User Regular Language Rulles" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-22">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="380" y="1237" width="179" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-157" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-53" target="Knp0EVw5rfMEdcPhjhnq-146" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="User Context Free Language Rulles" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-53">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="482" y="1610" width="179" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-116" value="Compiler API Rulles To parse Regular grammar Rulles" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;startSize=23;" parent="1" vertex="1">
<mxGeometry x="175" y="1351" width="384" height="158" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<UserObject label="RE Compiler API Rulles" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-124">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-116" vertex="1">
<mxGeometry x="24" y="37.5" width="332" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<UserObject label="CF Compiler API Rulles" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-125">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-116" vertex="1">
<mxGeometry x="24" y="98.5" width="336" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-126" value="CF Parser" style="swimlane;whiteSpace=wrap;html=1;startSize=23;" parent="1" vertex="1">
<mxGeometry x="2269" y="1405" width="999" height="823" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-13" value="Acceptor" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="416" y="171" width="293" height="203" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-34" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="Knp0EVw5rfMEdcPhjhnq-13" source="Knp0EVw5rfMEdcPhjhnq-14" target="Knp0EVw5rfMEdcPhjhnq-19" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-35" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="Knp0EVw5rfMEdcPhjhnq-13" source="Knp0EVw5rfMEdcPhjhnq-14" target="Knp0EVw5rfMEdcPhjhnq-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-36" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="Knp0EVw5rfMEdcPhjhnq-13" source="Knp0EVw5rfMEdcPhjhnq-14" target="Knp0EVw5rfMEdcPhjhnq-20" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="Automation" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-14">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-13" vertex="1">
<mxGeometry x="92" y="81.5" width="120" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-18" value="SymbolStream" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-13" vertex="1">
<mxGeometry x="103.5" y="42" width="97" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-19" value="AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-13" vertex="1">
<mxGeometry x="131.5" y="159" width="41" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-20" value="Tables" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-13" vertex="1">
<mxGeometry x="15" y="98.5" width="53" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-54" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-37" target="Knp0EVw5rfMEdcPhjhnq-13" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-55" value="Get Symbol" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="Knp0EVw5rfMEdcPhjhnq-54" vertex="1" connectable="0">
<mxGeometry x="-0.5956" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-137" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-37" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="953.4782608695655" y="398.6521739130435" as="targetPoint" />
<Array as="points">
<mxPoint x="563" y="733" />
<mxPoint x="919" y="733" />
<mxPoint x="919" y="399" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-37" value="Acceptor" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="416" y="490" width="293" height="203" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-38" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="Knp0EVw5rfMEdcPhjhnq-37" source="Knp0EVw5rfMEdcPhjhnq-41" target="Knp0EVw5rfMEdcPhjhnq-43" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-39" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="Knp0EVw5rfMEdcPhjhnq-37" source="Knp0EVw5rfMEdcPhjhnq-41" target="Knp0EVw5rfMEdcPhjhnq-42" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-40" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="Knp0EVw5rfMEdcPhjhnq-37" source="Knp0EVw5rfMEdcPhjhnq-41" target="Knp0EVw5rfMEdcPhjhnq-44" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="Automation" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-41">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-37" vertex="1">
<mxGeometry x="92" y="81.5" width="120" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-42" value="SymbolStream" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-37" vertex="1">
<mxGeometry x="103.5" y="42" width="97" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-43" value="AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-37" vertex="1">
<mxGeometry x="131.5" y="159" width="41" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-44" value="Tables" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-37" vertex="1">
<mxGeometry x="15" y="98.5" width="53" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-59" value="Finate State Automation" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="721" y="175.5" width="146" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-60" value="Push Down Automation" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="726" y="490" width="143" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-131" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-82" target="Knp0EVw5rfMEdcPhjhnq-37" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-134" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-82" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="24.947368421052943" y="591.5" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-82" value="CFG Compiler" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="93" y="448.5" width="281" height="286" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-88" value="CF Comp Rules" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-82" vertex="1">
<mxGeometry x="7" y="130" width="103" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-90" value="Tables" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-82" vertex="1">
<mxGeometry x="210" y="132.5" width="53" height="26" as="geometry" />
</mxCell>
<UserObject label="LL" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-91">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-82" vertex="1">
<mxGeometry x="116" y="51.75" width="83" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<UserObject label="Canonical" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-92">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-82" vertex="1">
<mxGeometry x="116" y="119.75" width="83" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<UserObject label="LALR" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-93">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-82" vertex="1">
<mxGeometry x="116" y="187.75" width="83" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-132" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-94" target="Knp0EVw5rfMEdcPhjhnq-13" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-133" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-94" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="24.947368421052943" y="272.5" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-94" value="RE Compiler" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;startSize=23;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="122" y="228.25" width="260" height="88.5" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-95" value="RE Compiler Rulles" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-94" vertex="1">
<mxGeometry x="13" y="33.75" width="123" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-96" value="Tables" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-94" vertex="1">
<mxGeometry x="187" y="39.5" width="53" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-154" value="Sentence" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;" parent="Knp0EVw5rfMEdcPhjhnq-126" vertex="1">
<mxGeometry x="21" y="149.5" width="69" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-56" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;" parent="Knp0EVw5rfMEdcPhjhnq-126" source="Knp0EVw5rfMEdcPhjhnq-13" target="Knp0EVw5rfMEdcPhjhnq-154" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="94" y="166" as="targetPoint" />
<Array as="points">
<mxPoint x="626" y="104" />
<mxPoint x="281" y="104" />
<mxPoint x="281" y="166" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-57" value="Get Symbol" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="Knp0EVw5rfMEdcPhjhnq-56" vertex="1" connectable="0">
<mxGeometry x="-0.0483" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-127" value="Compiler API Rulles To parse Regular grammar Rulles" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;startSize=23;" parent="1" vertex="1">
<mxGeometry x="285" y="1720" width="385" height="158" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<UserObject label="RE Compiler API Rulles" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-128">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-127" vertex="1">
<mxGeometry x="24" y="37.5" width="340" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<UserObject label="CF Compiler API Rulles" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-129">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-127" vertex="1">
<mxGeometry x="24" y="98.5" width="340" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-162" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-130" target="Knp0EVw5rfMEdcPhjhnq-151" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="User Sentence" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-130">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="1063" y="1056" width="120" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-138" value="CF Parser" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="622" y="1273" width="337" height="173" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-139" value="CF AST Rulles for Tokenizer" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-138" vertex="1">
<mxGeometry x="19" y="80" width="168" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-140" value="CF AST Rulles for an acceptor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-138" vertex="1">
<mxGeometry x="19" y="125" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-141" value="CF Sentence" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-138" vertex="1">
<mxGeometry x="19" y="37" width="88" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-142" value="AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-138" vertex="1">
<mxGeometry x="265" y="73.5" width="41" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-143" value="CF Parser" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="714" y="1645" width="337" height="173" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-144" value="CF AST Rulles for Tokenizer" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-143" vertex="1">
<mxGeometry x="19" y="80" width="168" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-145" value="CF AST Rulles for an acceptor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-143" vertex="1">
<mxGeometry x="19" y="125" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-146" value="CF Sentence" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-143" vertex="1">
<mxGeometry x="19" y="37" width="88" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-147" value="AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-143" vertex="1">
<mxGeometry x="265" y="73.5" width="41" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-148" value="CF Parser" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" parent="1" vertex="1">
<mxGeometry x="1391" y="1401" width="337" height="173" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-149" value="RE Compiler API Rulles for FSA" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-148" vertex="1">
<mxGeometry x="36" y="79.75" width="189" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-150" value="CF Compiler API Rulles for an PDA acceptor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-148" vertex="1">
<mxGeometry x="36" y="124" width="255" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-151" value="CF Sentence" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-148" vertex="1">
<mxGeometry x="19" y="37" width="88" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-152" value="Sentence AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" parent="Knp0EVw5rfMEdcPhjhnq-148" vertex="1">
<mxGeometry x="229" y="35" width="95" height="26" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-155" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-129" target="Knp0EVw5rfMEdcPhjhnq-145" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-156" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-128" target="Knp0EVw5rfMEdcPhjhnq-144" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-159" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-124" target="Knp0EVw5rfMEdcPhjhnq-139" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-160" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-125" target="Knp0EVw5rfMEdcPhjhnq-140" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-165" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="B2JnEC1cqt8830IlIT0b-3" target="Knp0EVw5rfMEdcPhjhnq-149" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-166" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="B2JnEC1cqt8830IlIT0b-1" target="Knp0EVw5rfMEdcPhjhnq-150" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="Knp0EVw5rfMEdcPhjhnq-167" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" parent="1" source="Knp0EVw5rfMEdcPhjhnq-152" target="Knp0EVw5rfMEdcPhjhnq-168" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1909.9999999999995" y="1483" as="targetPoint" />
</mxGeometry>
</mxCell>
<UserObject label="User AST" treeRoot="1" id="Knp0EVw5rfMEdcPhjhnq-168">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="1977" y="1190" width="120" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-2" value="" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="Knp0EVw5rfMEdcPhjhnq-147" target="B2JnEC1cqt8830IlIT0b-1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1020" y="1732" as="sourcePoint" />
<mxPoint x="1408" y="1535" as="targetPoint" />
</mxGeometry>
</mxCell>
<UserObject label="AST to CF Compiler API" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-1">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" vertex="1" parent="1">
<mxGeometry x="1117" y="1613" width="197" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-4" value="" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="Knp0EVw5rfMEdcPhjhnq-142" target="B2JnEC1cqt8830IlIT0b-3">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1032" y="1311" as="sourcePoint" />
<mxPoint x="1408" y="1490" as="targetPoint" />
</mxGeometry>
</mxCell>
<UserObject label="AST to RE Compiler API" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-3">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" vertex="1" parent="1">
<mxGeometry x="1107" y="1470.5" width="197" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-41" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-42" target="B2JnEC1cqt8830IlIT0b-56">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="Language Grammar &lt;br&gt;(CF RE United Format)" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-42">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="422" y="2209" width="179" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-45" value="Grammar For CF RE United Grammar Format" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;startSize=23;" vertex="1" parent="1">
<mxGeometry x="253" y="2439" width="384" height="158" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<UserObject label="RE Compiler API Rulles" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-46">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-45">
<mxGeometry x="24" y="37.5" width="332" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<UserObject label="CF Compiler API Rulles" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-47">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-45">
<mxGeometry x="24" y="98.5" width="336" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-51" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-52" target="B2JnEC1cqt8830IlIT0b-66">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<UserObject label="User Sentence" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-52">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="422" y="2132" width="180" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-53" value="CF Parser" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="700" y="2361" width="337" height="173" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-54" value="CF AST Rulles for Tokenizer" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-53">
<mxGeometry x="19" y="80" width="168" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-55" value="CF AST Rulles for an acceptor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-53">
<mxGeometry x="19" y="125" width="180" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-56" value="CF Sentence" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-53">
<mxGeometry x="19" y="37" width="88" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-57" value="AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-53">
<mxGeometry x="265" y="73.5" width="41" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-63" value="CF Parser" style="swimlane;rounded=0;swimlaneLine=0;fontColor=default;fontSize=14;" vertex="1" parent="1">
<mxGeometry x="1415" y="2271" width="337" height="173" as="geometry">
<mxRectangle x="453" y="256" width="50" height="44" as="alternateBounds" />
</mxGeometry>
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-64" value="RE Compiler API Rulles for FSA" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-63">
<mxGeometry x="36" y="79.75" width="189" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-65" value="CF Compiler API Rulles for an PDA acceptor" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-63">
<mxGeometry x="36" y="124" width="255" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-66" value="CF Sentence" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-63">
<mxGeometry x="19" y="37" width="88" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-67" value="Sentence AST" style="text;html=1;align=center;verticalAlign=middle;resizable=0;points=[];autosize=1;strokeColor=none;fillColor=none;rounded=0;" vertex="1" parent="B2JnEC1cqt8830IlIT0b-63">
<mxGeometry x="229" y="35" width="95" height="26" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-70" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-46" target="B2JnEC1cqt8830IlIT0b-54">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-71" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;startArrow=none;endArrow=none;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-47" target="B2JnEC1cqt8830IlIT0b-55">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-72" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-79" target="B2JnEC1cqt8830IlIT0b-64">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-73" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-77" target="B2JnEC1cqt8830IlIT0b-65">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="B2JnEC1cqt8830IlIT0b-74" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-67" target="B2JnEC1cqt8830IlIT0b-75">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1933.9999999999995" y="2353" as="targetPoint" />
</mxGeometry>
</mxCell>
<UserObject label="User AST" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-75">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="1">
<mxGeometry x="2001" y="2060" width="120" height="60" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-76" value="" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-57" target="B2JnEC1cqt8830IlIT0b-77">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1044" y="2601.5" as="sourcePoint" />
<mxPoint x="1432" y="2405" as="targetPoint" />
</mxGeometry>
</mxCell>
<UserObject label="AST to CF Compiler API" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-77">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" vertex="1" parent="1">
<mxGeometry x="1141" y="2483" width="197" height="46.5" as="geometry" />
</mxCell>
</UserObject>
<mxCell id="B2JnEC1cqt8830IlIT0b-78" value="" style="edgeStyle=elbowEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;" edge="1" parent="1" source="B2JnEC1cqt8830IlIT0b-57" target="B2JnEC1cqt8830IlIT0b-79">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1056" y="2181" as="sourcePoint" />
<mxPoint x="1432" y="2360" as="targetPoint" />
</mxGeometry>
</mxCell>
<UserObject label="AST to RE Compiler API" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-79">
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={&quot;edgeStyle&quot;:&quot;elbowEdgeStyle&quot;,&quot;startArrow&quot;:&quot;none&quot;,&quot;endArrow&quot;:&quot;none&quot;};labelBackgroundColor=none;rounded=0;" vertex="1" parent="1">
<mxGeometry x="1151" y="2371" width="197" height="46.5" as="geometry" />
</mxCell>
</UserObject>
</root>
</mxGraphModel>
</diagram>
</mxfile>

17
.github/workflows/WindowsPlatform.yml vendored Normal file
View file

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

View file

@ -18,25 +18,47 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Install LLVM - name: Setup
run: sudo apt-get install -y llvm shell: bash
run: |
- name: Set LLVM Toolchain # If your submodules are configured to use SSH instead of HTTPS please uncomment the following line
run: | # git config --global url."https://github.com/".insteadOf "git@github.com:"
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 10 auth_header="$(git config --local --get http.https://github.com/.extraheader)"
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 20
- name: Configure CMake git submodule sync --recursive
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 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}}

4
.gitignore vendored
View file

@ -5,3 +5,7 @@ build*
lib lib
install install
.vscode .vscode
out
.vs
*.bkp
*.$*

21
.gitmodules vendored Normal file
View file

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

20
.project Normal file
View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>Modules</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.cdt.core.cBuilder</name>
<triggers>clean,full,incremental,</triggers>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.cdt.core.cnature</nature>
<nature>org.eclipse.cdt.core.ccnature</nature>
<nature>org.eclipse.cdt.cmake.core.cmakeNature</nature>
</natures>
</projectDescription>

View file

@ -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)

View file

@ -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<Arg>& 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<Arg>& args) { addRule(Rule(id, args)); }
void ContextFreeGrammar::setStart(const String& startRule) { mStartTerminal = startRule; }

View file

@ -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);

View file

@ -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 <typename tAlphabetType, typename tStateType>
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<Transition>* getTransitions() const { return &mTransitions; }
private:
Buffer<Transition> mTransitions{};
tStateType mStateVal = tStateType();
bool mIsAccepting = false;
};
private:
List<State> mStates;
State* mStartState = nullptr;
Range<ualni> 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<State>* getStates() const { return &mStates; }
[[nodiscard]] Range<ualni> getAlphabetRange() const { return mAlphabetRange; }
private:
typedef AvlTree<AvlNumericKey<State*>, 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<State*> workingSet;
set.forEach([&](AvlNumericKey<State*>& 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<State*>& 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<AvlNumericKey<StatesSet*>, tAlphabetType> transitions;
State* newState = nullptr;
bool accepting = false;
tStateType stateVal = tStateType();
};
Buffer<StatesSet> groups = { {} };
Map<GroupKey, GroupInfo, DefaultAllocator, GroupKey::hash> groupInfos;
groups.first().insert(getStartState(), false);
expandSet(groups.first());
groupInfos.put({ &groups.first() }, { &groups.first() });
// 1) find new states
List<StatesSet*> 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<State*>& 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<StatesSet*> targetGroupKey, tAlphabetType symbol) {
GroupInfo* targetGroup = &groupInfos.get({ (StatesSet*) targetGroupKey.val });
addTransition(group->val.newState, targetGroup->newState, symbol);
};
group->val.transitions.forEach(functor);
}
return true;
}
};
}

View file

@ -0,0 +1,137 @@
#pragma once
#include "Strings.hpp"
#include "Automata.hpp"
#include "Buffer2D.hpp"
namespace tp {
template <typename tAlphabetType, typename tStateType>
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<StackItem*> 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<ualni>(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<tAlphabetType, tStateType> 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<const AutomataState*, ualni> 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<ualni>(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<Action> mTable;
Buffer<StackItem> mItems;
Buffer<StackItem*> mStack;
ualni mStartState = 0;
ualni mCurrentState = 0;
Range<ualni> mRange;
};
}

View file

@ -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<ContextFreeGrammar::Rule*> rules;
Map<String, NonTerminal*> references;
Map<String, NonTerminal*> referencing;
public:
[[nodiscard]] bool isProductive() const {
for (auto rule : rules) {
if (rule->isProductive()) return true;
}
return false;
}
[[nodiscard]] bool isLooped(Map<String, ualni>& 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<SymbolVal, Item>& automata) {
if (!init(grammar)) return false;
return true;
}
[[nodiscard]] const Buffer<Symbol>* 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<String, ualni> 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<String, NonTerminal> mNonTerminals;
Map<String, bool> mTerminals;
Buffer<Symbol> mSymbols;
Map<String, SymbolVal> mSymbolLookup;
};
}

View file

@ -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<Arg>& args);
public:
bool operator==(const Rule& in) const;
[[nodiscard]] bool isProductive() const;
[[nodiscard]] const String& getId() const { return mId; }
[[nodiscard]] const Buffer<Arg>* getArgs() const { return &mArgs; }
private:
String mId;
Buffer<Arg> mArgs;
};
public:
ContextFreeGrammar() = default;
public:
void addRule(const Rule& rule);
void addRule(const String& id, const InitialierList<Arg>& args);
void setStart(const String& startRule);
[[nodiscard]] const Buffer<Rule>* getRules() const { return &mRules; }
[[nodiscard]] const String& getStartTerminal() const { return mStartTerminal; }
public:
Buffer<Rule> mRules;
String mStartTerminal;
bool mIsLooped = false;
};
template <typename tAlphabetType, typename tTokType>
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<const Node*>& nodes) :
Node(Node::COMPOUND) {
mSequence = nodes;
}
~CompoundNode() override {
for (auto iter : mSequence) {
delete iter.data();
}
mSequence.clear();
}
public:
Buffer<const Node*> 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<Range<tAlphabetType>>& ranges, bool exclude = false) :
Node(Node::CLASS) {
mExclude = exclude;
mRanges = ranges;
}
~ClassNode() override { mRanges.removeAll(); }
public:
Buffer<Range<tAlphabetType>> 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<const Node*>& 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<Range<tAlphabetType>>& ranges, bool exclude = false) {
return new ClassNode(ranges, exclude);
}
public:
Buffer<Pair<const Node*, tTokType>> mRules;
};
}

View file

@ -0,0 +1,7 @@
#pragma once
#include "Module.hpp"
namespace tp {
extern ModuleManifest gModuleLanguage;
}

View file

@ -0,0 +1,107 @@
#pragma once
#include "RegularCompiler.hpp"
#include "RegularAutomata.hpp"
#include "ContextFreeCompiler.hpp"
#include "ContextFreeAutomata.hpp"
namespace tp {
template <typename tAlphabetType, typename tTokenType, tTokenType tInTransition, ualni MinSymbol, ualni MaxSymbol>
class Parser {
typedef RegularGrammar<tAlphabetType, tTokenType> RegularGrammar;
typedef RegularCompiler<tAlphabetType, tTokenType, tInTransition, MinSymbol, MaxSymbol> RegularCompiler;
typedef FiniteStateAutomation<tAlphabetType, tTokenType> RegularGraph;
typedef RegularAutomata<tAlphabetType, tTokenType> RegularAutomata;
// ContextFreeGrammar;
// ContextFreeCompiler;
typedef FiniteStateAutomation<ContextFreeCompiler::SymbolVal, ContextFreeCompiler::Item> ContextFreeGraph;
typedef ContextFreeAutomata<ContextFreeCompiler::SymbolVal, ContextFreeCompiler::Item> 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<String, tTokenType>& 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<ContextFreeCompiler::SymbolVal> 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<tTokenType, ContextFreeCompiler::SymbolVal> mGrammarGlue;
Map<ContextFreeCompiler::SymbolVal, String> mAstNames;
};
}

View file

@ -0,0 +1,101 @@
#pragma once
#include "Strings.hpp"
#include "Automata.hpp"
#include "Buffer2D.hpp"
namespace tp {
template <typename tAlphabetType, typename tStateType>
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<tAlphabetType, tStateType>& 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<ualni> mTable;
Buffer<Pair<bool, tStateType>> mStates;
ualni mCurrentState = 0;
ualni mStartState = 0;
Range<tAlphabetType> mSymbolRange = { 0, 0 };
};
}

View file

@ -0,0 +1,208 @@
#pragma once
#include "Grammar.hpp"
#include "Automata.hpp"
namespace tp {
template <typename tAlphabetType, typename tStateType, tStateType tInTransition, ualni tMinSymbol, ualni tMaxSymbol>
class RegularCompiler {
typedef FiniteStateAutomation<tAlphabetType, tStateType> Graph;
typedef typename Graph::State Vertex;
typedef RegularGrammar<tAlphabetType, tStateType> 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<ualni>(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<ualni> range, bool exclude) {
if (exclude) {
Range<ualni> first = { tMinSymbol, range.mBegin - 1 };
Range<ualni> 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); }
};
}

View file

@ -0,0 +1,69 @@
#pragma once
#include "Parser.hpp"
namespace tp {
// Gives ability to express grammar in the Unified Format as sentence
template <typename tAlphabetType>
class SimpleParser {
enum UGTokens : alni { InTransition = -1, TestSeq };
typedef Parser<tAlphabetType, UGTokens, InTransition, 0, 127> UGParser;
typedef Parser<tAlphabetType, alni, -1, 0, 127> 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<tAlphabetType, UGTokens> regularGrammar;
{
// this is basically ast from existing tokenizer
regularGrammar.addRule(regularGrammar.seq({ regularGrammar.val('a'), regularGrammar.val('b') }), TestSeq);
}
Map<String, UGTokens> 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<tAlphabetType, alni> 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<String, alni> 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;
};
}

View file

@ -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;
}
)";

View file

@ -0,0 +1,45 @@
#include "Test.hpp"
/*
using namespace tp;
void testAutomation() {
FiniteStateAutomation<char, int> 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<int1>();
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; }

View file

@ -1,22 +1,15 @@
project(Allocators)
cmake_minimum_required(VERSION 3.2)
set(CMAKE_CXX_STANDARD 23)
project(Allocator)
### ---------------------- Static Library --------------------- ### ### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp") file(GLOB SOURCES "./private/*.cpp")
file(GLOB HEADERS "./public/*.hpp") file(GLOB HEADERS "./public/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS}) add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/) target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Utils) target_link_libraries(${PROJECT_NAME} PUBLIC Callstack)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing() enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp") file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils) target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)

View file

@ -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.
![image](https://user-images.githubusercontent.com/63184036/222794298-3f238de4-c0b8-41fa-b7ec-c0c675da8f05.png) ![image](https://user-images.githubusercontent.com/63184036/222794298-3f238de4-c0b8-41fa-b7ec-c0c675da8f05.png)
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.
![image](https://user-images.githubusercontent.com/63184036/222793169-a405effe-72be-42fc-b375-bb06dce0a735.png) ![image](https://user-images.githubusercontent.com/63184036/222793169-a405effe-72be-42fc-b375-bb06dce0a735.png)
## Memory Usage Analisys
Currently outdated
## Benchmarks
Currently outdated

View file

@ -1,23 +1,36 @@
#include "Allocators.hpp" #include "Allocators.hpp"
#include "HeapAllocatorGlobal.hpp"
#include <cstdlib> #include <cstdlib>
#include <stdio.h>
static tp::ModuleManifest* sModuleDependencies[] = { &tp::gModuleBase, nullptr }; static bool init(const tp::ModuleManifest* self) {
tp::ModuleManifest tp::gModuleAllocators = ModuleManifest("Allocators", nullptr, nullptr, sModuleDependencies); 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 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 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 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); }
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); } void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc) { tp::HeapAllocGlobal::deallocate(aPtr); }

View file

@ -1,70 +1,76 @@
#include "HeapAllocator.hpp" #include "HeapAllocator.hpp"
#include "HeapAllocatorGlobal.hpp" #include "HeapAllocatorGlobal.hpp"
#include "PrivateConfig.hpp" #include "PrivateConfig.hpp"
using namespace tp; #include <malloc.h>
#if not defined(MEM_DEBUG) using namespace tp;
// ----------------------- Release Implementation ---------------------------- // #if not defined(MEM_DEBUG)
void* HeapAlloc::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
void HeapAlloc::deallocate(void* aPtr) { free(aPtr); } // ----------------------- Release Implementation ---------------------------- //
HeapAlloc::~HeapAlloc() {} void* HeapAlloc::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
void HeapAlloc::deallocate(void* aPtr) {
#else if (!aPtr) return;
free(aPtr);
namespace tp { }
struct MemHeadLocal { HeapAlloc::~HeapAlloc() {}
MemHeadLocal* mPrev;
MemHeadLocal* mNext; #else
};
} namespace tp {
struct MemHeadLocal {
void* HeapAlloc::allocate(ualni aBlockSize) { MemHeadLocal* mPrev;
auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal)); MemHeadLocal* mNext;
auto out = head + 1; };
}
mNumAllocations++;
if (mEntry) { void* HeapAlloc::allocate(ualni aBlockSize) {
DEBUG_ASSERT(!mEntry->mNext) auto head = (MemHeadLocal*) HeapAllocGlobal::allocate(aBlockSize + sizeof(MemHeadLocal));
head->mNext = nullptr; auto out = head + 1;
head->mPrev = mEntry;
mEntry->mNext = head; mNumAllocations++;
} else { if (mEntry) {
head->mNext = nullptr; DEBUG_ASSERT(!mEntry->mNext)
head->mPrev = nullptr; head->mNext = nullptr;
} head->mPrev = mEntry;
mEntry = head; mEntry->mNext = head;
} else {
return out; head->mNext = nullptr;
} head->mPrev = nullptr;
}
void HeapAlloc::deallocate(void* aPtr) { mEntry = head;
auto head = ((MemHeadLocal*)(aPtr)) - 1;
return out;
mNumAllocations--; }
DEBUG_ASSERT(!mEntry->mNext)
if (head->mNext) head->mNext->mPrev = head->mPrev; void HeapAlloc::deallocate(void* aPtr) {
if (head->mPrev) head->mPrev->mNext = head->mNext; if (!aPtr) return;
if (head == mEntry) {
mEntry = head->mPrev; auto head = ((MemHeadLocal*) (aPtr)) - 1;
}
mNumAllocations--;
HeapAllocGlobal::deallocate(head); DEBUG_ASSERT(!mEntry->mNext)
} if (head->mNext) head->mNext->mPrev = head->mPrev;
if (head->mPrev) head->mPrev->mNext = head->mNext;
HeapAlloc::~HeapAlloc() { if (head == mEntry) {
if (mNumAllocations) { mEntry = head->mPrev;
DEBUG_BREAK("Destruction of not freed Allocator") }
#ifdef MEM_STACK_TRACE HeapAllocGlobal::deallocate(head);
// TODO : log leaks and free them up }
#endif
} HeapAlloc::~HeapAlloc() {
} if (mNumAllocations) {
DEBUG_ASSERT(0 && "Destruction of not freed Allocator")
#endif
#ifdef MEM_STACK_TRACE
// TODO : log leaks and free them up
#endif
}
}
#endif

View file

@ -1,150 +1,230 @@
#include "HeapAllocatorGlobal.hpp" #include "HeapAllocatorGlobal.hpp"
#include "PrivateConfig.hpp" #include "PrivateConfig.hpp"
#include "Utils.hpp" #include "Callstack.hpp"
#include "Debugging.hpp" #include "Utils.hpp"
#include <cstdlib> #include <cstdio>
#include <cstdlib>
using namespace tp;
using namespace tp;
#if not defined(MEM_DEBUG)
#if not defined(MEM_DEBUG)
// ----------------------- Release Implementation ---------------------------- //
// ----------------------- Release Implementation ---------------------------- //
void* HeapAllocGlobal::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
void HeapAllocGlobal::deallocate(void* aPtr) { free(aPtr); } void* HeapAllocGlobal::allocate(ualni aBlockSize) { return malloc(aBlockSize); }
HeapAllocGlobal::~HeapAllocGlobal() = default; void HeapAllocGlobal::deallocate(void* aPtr) {
if (!aPtr) return;
#else free(aPtr);
}
tp::MemHead* tp::HeapAllocGlobal::mEntry = nullptr; HeapAllocGlobal::~HeapAllocGlobal() = default;
tp::ualni tp::HeapAllocGlobal::mNumAllocations = 0; bool HeapAllocGlobal::checkLeaks() { return false; }
void HeapAllocGlobal::startIgnore() {}
// ----------------------- Debug Implementation ---------------------------- // void HeapAllocGlobal::stopIgnore() {}
// |----------------| ualni HeapAllocGlobal::getNAllocations() { return 0; }
// | MemHead |
// |----------------| #else
// | wrap top |
// |----------------| - Allocated Block Layout tp::MemHead* tp::HeapAllocGlobal::mEntry = nullptr;
// | data | tp::ualni tp::HeapAllocGlobal::mNumAllocations = 0;
// |----------------| std::mutex tp::HeapAllocGlobal::mMutex;
// | wrap bottom | bool tp::HeapAllocGlobal::mIgnore = true;
// |----------------| bool tp::HeapAllocGlobal::mEnableCallstack = false;
namespace tp { #ifdef MEM_STACK_TRACE
struct MemHead { tp::CallStackCapture tp::HeapAllocGlobal::mCallstack;
MemHead* mPrev; #endif
MemHead* mNext;
ualni mBlockSize; // ----------------------- Debug Implementation ---------------------------- //
#ifdef MEM_STACK_TRACE // |----------------|
const CallStackCapture::CallStack* mCallStack; // | MemHead |
#endif // |----------------|
}; // | wrap top |
} // |----------------| - Allocated Block Layout
// | data |
enum : ualni { // |----------------|
ALIGNED_SIZE = ENV_ALNI_SIZE_B, // | wrap bottom |
WRAP_SIZE = MEM_WRAP_SIZE * ALIGNED_SIZE, // |----------------|
WRAP_VAL = MEM_WRAP_FILL_VAL,
HEAD_SIZE = sizeof(MemHead), namespace tp {
CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL, struct MemHead {
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL, MemHead* mPrev;
}; MemHead* mNext;
uhalni mBlockSize;
void* HeapAllocGlobal::allocate(ualni aBlockSize) { uhalni mIgnored;
#ifdef MEM_STACK_TRACE
static_assert(HEAD_SIZE % ALIGNED_SIZE == 0, "Heap Allocator Configuration Error"); const CallStackCapture::CallStack* mCallStack;
#endif
if (aBlockSize % ALIGNED_SIZE) { };
aBlockSize = (aBlockSize / ALIGNED_SIZE + 1) * ALIGNED_SIZE; }
}
enum : ualni {
// 1) Allocate the block ALIGNED_SIZE = ENV_ALNI_SIZE_B,
auto head = (MemHead*)malloc(aBlockSize + WRAP_SIZE * 2 + HEAD_SIZE); WRAP_SIZE = MEM_WRAP_SIZE * ALIGNED_SIZE,
auto wrap_top = (int1*)(head + 1); WRAP_VAL = MEM_WRAP_FILL_VAL,
auto data = wrap_top + WRAP_SIZE; HEAD_SIZE = sizeof(MemHead),
auto wrap_bottom = data + aBlockSize; CLEAR_ALLOC_VAL = MEM_CLEAR_ON_ALLOC_VAL,
CLEAR_DEALLOC_VAL = MEM_CLEAR_ON_DEALLOC_VAL,
if (!head) { return nullptr; } };
head->mBlockSize = aBlockSize;
void* HeapAllocGlobal::allocate(ualni aBlockSize) {
// 2) Link with existing blocks
mNumAllocations++; static_assert(HEAD_SIZE % ALIGNED_SIZE == 0, "Heap Allocator Configuration Error");
if (mEntry) {
DEBUG_ASSERT(!mEntry->mNext) if (aBlockSize % ALIGNED_SIZE) {
head->mNext = nullptr; aBlockSize = (aBlockSize / ALIGNED_SIZE + 1) * ALIGNED_SIZE;
head->mPrev = mEntry; }
mEntry->mNext = head;
} else { // 1) Allocate the block
head->mNext = nullptr; ALLOCATE:
head->mPrev = nullptr; auto head = (MemHead*) malloc(aBlockSize + WRAP_SIZE * 2 + HEAD_SIZE);
} if (!head) {
mEntry = head; printf("WARNING : Cant allocate memory. Trying again\n");
goto ALLOCATE; // Just freeze if no memory is available
// 3) Wrap fill }
memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL); auto wrap_top = (int1*) (head + 1);
auto data = wrap_top + WRAP_SIZE;
// 4) Trace the stack auto wrap_bottom = data + aBlockSize;
#ifdef MEM_STACK_TRACE
head->mCallStack = gCSCapture->getSnapshot(); head->mBlockSize = aBlockSize;
#endif head->mIgnored = mIgnore;
// 5) clear data // 2) Link with existing blocks
#ifdef MEM_CLEAR_ON_ALLOC mMutex.lock();
memSetVal(data, aBlockSize, CLEAR_ALLOC_VAL); mNumAllocations++;
#endif if (mEntry) {
DEBUG_ASSERT(mEntry->mNext == nullptr)
return data; head->mNext = nullptr;
} head->mPrev = mEntry;
mEntry->mNext = head;
void HeapAllocGlobal::deallocate(void* aPtr) { } else {
// 1) Restore the pointers head->mNext = nullptr;
auto head = ((MemHead*)((int1*)aPtr - WRAP_SIZE)) - 1; head->mPrev = nullptr;
auto wrap_top = (int1*)(head + 1); }
auto data = wrap_top + WRAP_SIZE; mEntry = head;
auto wrap_bottom = data + head->mBlockSize;
// 3) Trace the stack
// 2) Unlink with blocks #ifdef MEM_STACK_TRACE
mNumAllocations--; // check if somewhat decides to call new within static variable initialization
DEBUG_ASSERT(!mEntry->mNext) head->mCallStack = (mEnableCallstack && mCallstack.initialized) ? mCallstack.getSnapshot() : nullptr;
if (head->mNext) head->mNext->mPrev = head->mPrev; #endif
if (head->mPrev) head->mPrev->mNext = head->mNext;
if (head == mEntry) { mMutex.unlock();
mEntry = head->mPrev;
} // 4) Wrap fill
memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
// 3) Check the wrap memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!") // 5) clear data
#ifdef MEM_CLEAR_ON_ALLOC
// 4) clear data memSetVal(data, aBlockSize, CLEAR_ALLOC_VAL);
#ifdef MEM_CLEAR_ON_ALLOC #endif
memSetVal(data, head->mBlockSize, CLEAR_DEALLOC_VAL);
#endif return data;
}
// 5) free the block
free(head); void HeapAllocGlobal::deallocate(void* aPtr) {
} if (!aPtr) return;
bool HeapAllocGlobal::checkLeaks() { // 1) Restore the pointers
// 1) Check for not deallocated memory auto head = ((MemHead*) ((int1*) aPtr - WRAP_SIZE)) - 1;
if (mNumAllocations) { auto wrap_top = (int1*) (head + 1);
auto data = wrap_top + WRAP_SIZE;
#ifdef MEM_STACK_TRACE auto wrap_bottom = data + head->mBlockSize;
gCSCapture->logLeaks();
#endif // 2) Unlink with blocks
mMutex.lock();
DEBUG_BREAK("Destruction of not freed Allocator") mNumAllocations--;
return true; DEBUG_ASSERT(!mEntry->mNext)
} if (head->mNext) head->mNext->mPrev = head->mPrev;
return false; if (head->mPrev) head->mPrev->mNext = head->mNext;
} if (head == mEntry) {
mEntry = head->mPrev;
HeapAllocGlobal::~HeapAllocGlobal() = default; }
#endif 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

View file

@ -1,29 +1,27 @@
#pragma once #pragma once
#include "Module.hpp" #include "Utils.hpp"
#include "HeapAllocatorGlobal.hpp"
#include "HeapAllocator.hpp"
#include "ChunkAllocator.hpp" #include "ChunkAllocator.hpp"
#include "HeapAllocator.hpp"
#include "HeapAllocatorGlobal.hpp"
#include "PoolAllocator.hpp" #include "PoolAllocator.hpp"
namespace tp { namespace tp {
extern ModuleManifest gModuleAllocators; 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 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 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 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);
void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc); void operator delete[](void* aPtr, tp::HeapAllocGlobal& aAlloc);

View file

@ -15,6 +15,7 @@
*/ */
#include "Environment.hpp" #include "Environment.hpp"
#include "HeapAllocatorGlobal.hpp"
#include "PrivateConfig.hpp" #include "PrivateConfig.hpp"
namespace tp { namespace tp {
@ -22,7 +23,7 @@ namespace tp {
// Chunk Allocator // Chunk Allocator
// Constant time allocations and de-allocations in any order. // Constant time allocations and de-allocations in any order.
// Memory blocks are fixed in size and number of blocks can not exceed given parameter. // Memory blocks are fixed in size and number of blocks can not exceed given parameter.
template<typename tType, ualni tNumBlocks> template <typename tType, ualni tNumBlocks>
class ChunkAlloc { class ChunkAlloc {
enum : ualni { enum : ualni {
@ -75,24 +76,24 @@ namespace tp {
// 2) Find free block and update next free block // 2) Find free block and update next free block
auto data = mNextBlock; auto data = mNextBlock;
mNextBlock = (ualni*)(*data); mNextBlock = (ualni*) (*data);
mNumFreeBlocks--; mNumFreeBlocks--;
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
// 3) Fill Wrap and offset data // 3) Fill Wrap and offset data
auto wrap_top = data; auto wrap_top = data;
auto wrap_bottom = data + WRAP_SIZE_ALN + dataSize(); auto wrap_bottom = data + WRAP_SIZE_ALN + dataSize();
memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL); memSetVal(wrap_top, WRAP_SIZE, WRAP_VAL);
memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL); memSetVal(wrap_bottom, WRAP_SIZE, WRAP_VAL);
// 4) Clear data // 4) Clear data
#ifdef MEM_CLEAR_ON_ALLOC #ifdef MEM_CLEAR_ON_ALLOC
memSetVal(data + WRAP_SIZE_ALN, dataSize() * ALIGNED_SIZE, CLEAR_ALLOC_VAL); memSetVal(data + WRAP_SIZE_ALN, dataSize() * ALIGNED_SIZE, CLEAR_ALLOC_VAL);
#endif #endif
data += WRAP_SIZE_ALN; data += WRAP_SIZE_ALN;
#endif #endif
return data; return data;
} }
@ -100,26 +101,26 @@ namespace tp {
void deallocate(void* aPtr) { void deallocate(void* aPtr) {
DEBUG_ASSERT(aPtr >= mBuff && aPtr < mBuff + tNumBlocks * blockSize()) DEBUG_ASSERT(aPtr >= mBuff && aPtr < mBuff + tNumBlocks * blockSize())
auto block = (ualni*)aPtr; auto block = (ualni*) aPtr;
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
// 3) Check Wrap and offset data // 3) Check Wrap and offset data
auto wrap_bottom = block + dataSize(); auto wrap_bottom = block + dataSize();
auto wrap_top = block - WRAP_SIZE_ALN; auto wrap_top = block - WRAP_SIZE_ALN;
block = wrap_top; block = wrap_top;
// 3) Check the wrap // 3) Check the wrap
ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!") ASSERT(!memCompareVal(wrap_top, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!") ASSERT(!memCompareVal(wrap_bottom, WRAP_SIZE, WRAP_VAL) && "Allocated Block Wrap Corrupted!")
// 4) Clear data // 4) Clear data
#ifdef MEM_CLEAR_ON_ALLOC #ifdef MEM_CLEAR_ON_ALLOC
memSetVal(block, blockSize() * ALIGNED_SIZE, CLEAR_DEALLOC_VAL); memSetVal(block, blockSize() * ALIGNED_SIZE, CLEAR_DEALLOC_VAL);
#endif #endif
#endif #endif
(*block) = (ualni)mNextBlock; (*block) = (ualni) mNextBlock;
mNextBlock = block; mNextBlock = block;
mNumFreeBlocks++; mNumFreeBlocks++;
} }

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "Module.hpp" #include "Callstack.hpp"
#include <mutex>
namespace tp { namespace tp {
@ -9,6 +10,12 @@ namespace tp {
#ifdef MEM_DEBUG #ifdef MEM_DEBUG
static ualni mNumAllocations; static ualni mNumAllocations;
static struct MemHead* mEntry; 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 #endif
public: public:
@ -20,6 +27,13 @@ namespace tp {
static void deallocate(void* aPtr); static void deallocate(void* aPtr);
static bool checkLeaks(); static bool checkLeaks();
static void startIgnore();
static void stopIgnore();
static ualni getNAllocations();
static void enableCallstack();
static void disableCallstack();
public: public:
[[nodiscard]] bool checkWrap() const { return false; } [[nodiscard]] bool checkWrap() const { return false; }
void checkValid() {} void checkValid() {}

View file

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

View file

@ -21,14 +21,14 @@ namespace tp {
// Pool Allocator // Pool Allocator
// Overcomes chunk allocator fixed number of max allocations // Overcomes chunk allocator fixed number of max allocations
template<typename tType, ualni tNumBlocks> template <typename tType, ualni tNumBlocks>
class PoolAlloc { class PoolAlloc {
typedef ChunkAlloc<tType, tNumBlocks> Chunk; typedef ChunkAlloc<tType, tNumBlocks> Chunk;
struct Chunks { struct Chunks {
void add(Chunk* aChunk){ void add(Chunk* aChunk) {
if (!mBuff) { if (!mBuff) {
mLen = 16; mLen = 16;
@ -57,7 +57,7 @@ namespace tp {
} }
} }
void remove(Chunk** del_address){ void remove(Chunk** del_address) {
if (mUsedLen == 1) { if (mUsedLen == 1) {
mLen = 0; mLen = 0;
mUsedLen = 0; mUsedLen = 0;
@ -74,18 +74,16 @@ namespace tp {
mUsedLen--; mUsedLen--;
// check for buff low usage // check for buff low usage
if ((halnf)mUsedLen / (halnf)mLen < 0.25f) { if ((halnf) mUsedLen / (halnf) mLen < 0.25f) {
auto prevBuff = mBuff; 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); memCopy(mBuff, prevBuff, sizeof(Chunk*) * mUsedLen);
mLen /= 2; mLen /= 2;
HeapAllocGlobal::deallocate(prevBuff); HeapAllocGlobal::deallocate(prevBuff);
} }
} }
[[nodiscard]] Chunk** find(void* aPtr) { [[nodiscard]] Chunk** find(void* aPtr) { return findUtil(mBuff, mBuff + mUsedLen, aPtr) - 1; }
return findUtil(mBuff, mBuff + mUsedLen, aPtr) - 1;
}
[[nodiscard]] Chunk* findNotFull() const { [[nodiscard]] Chunk* findNotFull() const {
for (ualni idx = 0; idx < mUsedLen; idx++) { for (ualni idx = 0; idx < mUsedLen; idx++) {
@ -134,12 +132,13 @@ namespace tp {
} }
void deallocate(void* aPtr) { void deallocate(void* aPtr) {
if (!aPtr) return;
auto chunk = mChunks.find(aPtr); auto chunk = mChunks.find(aPtr);
(*chunk)->deallocate(aPtr); (*chunk)->deallocate(aPtr);
if ((*chunk)->isEmpty()) { if ((*chunk)->isEmpty()) {
if (mFreeChunk == *chunk) mFreeChunk = nullptr; if (mFreeChunk == *chunk) mFreeChunk = nullptr;
HeapAllocGlobal::deallocate(*chunk); HeapAllocGlobal::deallocate(*chunk);
mChunks.remove(chunk); mChunks.remove(chunk);
} }
} }
@ -153,7 +152,7 @@ namespace tp {
if (i > j) { if (i > j) {
ASSERT(mChunks.mBuff[i] > mChunks.mBuff[j]) ASSERT(mChunks.mBuff[i] > mChunks.mBuff[j])
} else if (i < j) { } else if (i < j) {
ASSERT(mChunks.mBuff[i] < mChunks.mBuff[j]) ASSERT(mChunks.mBuff[i] < mChunks.mBuff[j])
} }
} }
} }

View file

@ -6,5 +6,4 @@
#define MEM_CLEAR_ON_DEALLOC // Clear data on free #define MEM_CLEAR_ON_DEALLOC // Clear data on free
#define MEM_CLEAR_ON_DEALLOC_VAL 0xAA // 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_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 #define MEM_STACK_TRACE_MAX_DEPTH 32 // Call stack max depth

View file

@ -1,248 +1,249 @@
#include "Utils.hpp" #include "UnitTest++/UnitTest++.h"
#include "Testing.hpp"
#include "Allocators.hpp"
#include "HeapAllocatorGlobal.hpp" #include "Utils.hpp"
#include "HeapAllocator.hpp"
#include "ChunkAllocator.hpp" #include <cmath>
#include "PoolAllocator.hpp"
using namespace tp;
#include <cmath>
struct TestStruct {
using namespace tp; alni val = 0;
struct TestStruct { TestStruct() :
alni val = 0; val(0) {}
explicit TestStruct(alni val) :
TestStruct() : val(0) {} val(val) {}
explicit TestStruct(alni val) : val(val) {} TestStruct(const TestStruct& in) :
TestStruct(const TestStruct& in) : val(in.val) {} val(in.val) {}
~TestStruct() { val = -1; } ~TestStruct() { val = -1; }
bool operator==(const TestStruct& in) const { bool operator==(const TestStruct& in) const { return in.val == val; }
return in.val == val; };
}
}; template <alni tSize, class tAllocator>
class TestBenches {
template <alni tSize, class tAllocator>
class TestBenches { tAllocator mAlloc{};
TestStruct mData[tSize]{};
tAllocator mAlloc{}; TestStruct* mLoaded[tSize]{};
TestStruct mData[tSize]{}; bool mIsLoaded[tSize]{};
TestStruct* mLoaded[tSize]{}; alni mLoadedNum = 0;
bool mIsLoaded[tSize]{};
alni mLoadedNum = 0; public:
TestBenches() {
public: for (alni i = 0; i < tSize; i++) {
TestBenches() { mData[i].val = i;
for (alni i = 0; i < tSize; i++) { mIsLoaded[i] = false;
mData[i].val = i; mLoaded[i] = nullptr;
mIsLoaded[i] = false; }
mLoaded[i] = nullptr; }
}
} void runTests() {
try {
void runTests() { test1();
try { test2();
test1(); test3();
test2(); test4();
test3(); test5();
test4(); test6();
test5(); } catch (...) {
test6(); ASSERT(false)
} catch (...) { }
ASSERT(false) }
}
} private:
alni randomIdx(bool state, Range<alni> range = { 0, tSize }) {
private: RAND:
alni randomIdx(bool state, Range<alni> range = { 0, tSize }) { auto idx = alni(alnf(range.idxBegin()) + randomFloat() * alnf(range.idxDiff() + 1));
RAND: idx = clamp(idx, alni(0), tSize - 1);
auto idx = alni(alnf(range.idxBegin()) + randomFloat() * alnf(range.idxDiff() + 1)); if (state == mIsLoaded[idx]) goto RAND;
idx = clamp(idx, alni(0), tSize - 1); return idx;
if (state == mIsLoaded[idx]) goto RAND; }
return idx;
} void verifyIntegrity() {
mAlloc.checkValid();
void verifyIntegrity() { ASSERT(!mAlloc.checkWrap())
mAlloc.checkValid(); for (alni i = 0; i < tSize; i++) {
ASSERT(!mAlloc.checkWrap()) if (mIsLoaded[i]) {
for (alni i = 0; i < tSize; i++) { ASSERT(*mLoaded[i] == mData[i])
if (mIsLoaded[i]) { }
ASSERT(*mLoaded[i] == mData[i]) }
} }
}
} void loadItem(alni idx) {
if (mIsLoaded[idx]) return;
void loadItem(alni idx) { verifyIntegrity();
if (mIsLoaded[idx]) return; mLoaded[idx] = new (mAlloc.allocate(sizeof(TestStruct))) TestStruct(mData[idx]);
verifyIntegrity(); ASSERT(mLoaded[idx]);
mLoaded[idx] = new (mAlloc.allocate(sizeof(TestStruct))) TestStruct(mData[idx]); mIsLoaded[idx] = true;
TEST(mLoaded[idx]); mLoadedNum++;
mIsLoaded[idx] = true; verifyIntegrity();
mLoadedNum++; }
verifyIntegrity();
} void unloadItem(alni idx) {
if (!mIsLoaded[idx]) return;
void unloadItem(alni idx) { verifyIntegrity();
if (!mIsLoaded[idx]) return; mLoaded[idx]->~TestStruct();
verifyIntegrity(); mAlloc.deallocate(mLoaded[idx]);
mLoaded[idx]->~TestStruct(); mIsLoaded[idx] = false;
mAlloc.deallocate(mLoaded[idx]); mLoadedNum--;
mIsLoaded[idx] = false; verifyIntegrity();
mLoadedNum--; }
verifyIntegrity();
} void changeStates(Range<alni> rg, bool load, bool reversed = false, bool random = false) {
for (auto i : rg) {
void changeStates(Range<alni> rg, bool load, bool reversed = false, bool random = false) { alni idx = i;
for (auto i : rg) { if (random) {
alni idx = i; idx = randomIdx(load, rg);
if (random) { } else if (reversed) {
idx = randomIdx(load, rg); idx = rg.idxEnd() - i - 1;
} else if (reversed) { }
idx = rg.idxEnd() - i - 1; (load) ? loadItem(idx) : unloadItem(idx);
} }
(load) ? loadItem(idx) : unloadItem(idx); }
}
} // full down-up load then up-down unload
void test1() {
// full down-up load then up-down unload changeStates({ 0, tSize }, true);
void test1() { changeStates({ 0, tSize }, false, true);
changeStates({ 0, tSize }, true); }
changeStates({ 0, tSize }, false, true);
} // full down-up load then down-up unload
void test2() {
// full down-up load then down-up unload changeStates({ 0, tSize }, true);
void test2() { changeStates({ 0, tSize }, false);
changeStates({0, tSize}, true); }
changeStates({0, tSize}, false);
} // full random load then random unload
void test3() {
// full random load then random unload changeStates({ 0, tSize }, true, false, true);
void test3() { changeStates({ 0, tSize }, false, false, true);
changeStates({0, tSize}, true, false, true); }
changeStates({0, tSize}, false, false, true);
} // combo tests 1-3
void test4() {
// combo tests 1-3 test1();
void test4() { test1();
test1();
test1(); test2();
test2();
test2();
test2(); test3();
test3();
test3(); }
test3();
} static alnf sineUpFunction(alnf aSize, alnf aX, bool aReverse) {
alnf end = 4 * 3.14159;
static alnf sineUpFunction(alnf aSize, alnf aX, bool aReverse) { alnf a = (2 / 7.f) * aSize;
alnf end = 4 * 3.14159; alnf b = end / aSize;
alnf a = (2 / 7.f) * aSize;
alnf b = end / aSize; alni c = ((-1 * aReverse) + (1 * !aReverse));
alnf c1 = (aX - (end * aReverse)) / b;
alni c = ((-1 * aReverse) + (1 * !aReverse)); alnf c2 = (a * sin(aX - (end * aReverse)));
alnf c1 = (aX - (end * aReverse)) / b; alnf out = c1 + c2;
alnf c2 = (a * sin(aX - (end * aReverse))); return (alnf) c * out;
alnf out = c1 + c2; }
return (alnf) c * out;
} // sin load & sin unload with ~1/2 drop factor
void test5() {
// sin load & sin unload with ~1/2 drop factor alnf end = 4 * 3.14159;
void test5() { alnf step = end / 4.f;
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) {
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);
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) { }
unloadItem(randomIdx(0)); while (mLoadedNum < target_alloc_count) {
} loadItem(randomIdx(1));
while (mLoadedNum < target_alloc_count) { }
loadItem(randomIdx(1)); }
} }
} }
}
} void checkWrap(ualni offset, bool after) {
offset = clamp(offset, (ualni) 1, (ualni) MEM_WRAP_SIZE);
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);
TestStruct* ts = mLoaded[randomIdx(0)]; uint1* address = (((uint1*) ts) + shift);
ualni shift = (sizeof(TestStruct) * after) + (offset - 1) * after - offset * (!after);
uint1* address = (((uint1*)ts) + shift); uint1 val = *address;
*address = 5;
uint1 val = *address;
*address = 5; ASSERT(!mAlloc.checkWrap());
TEST(!mAlloc.checkWrap()); *address = val;
}
*address = val;
} // mem guards test
void test6() {
// mem guards test changeStates({ 0, tSize }, 1);
void test6() { #ifdef MEM_DEBUG
changeStates({0, tSize}, 1); for (alni after = 0; after < 2; after++) {
#ifdef MEM_DEBUG for (alni offset = 1; offset <= MEM_WRAP_SIZE; offset++) {
for (alni after = 0; after < 2; after++) { checkWrap(offset, after);
for (alni offset = 1; offset <= MEM_WRAP_SIZE; offset++) { }
checkWrap(offset, after); }
} #endif
} changeStates({ 0, tSize }, 0);
#endif }
changeStates({0, tSize}, 0); };
}
}; const ualni size = 500;
const ualni size = 1000; template <typename Alloc>
void testAlloc() {
template<typename Alloc> try {
void testAlloc() { TestBenches<size, Alloc> heapTests{};
try { heapTests.runTests();
TestBenches<size, Alloc> heapTests{}; } catch (...) {
heapTests.runTests(); ASSERT(false);
} catch (...) { }
TEST(false); }
}
} SUITE(Allocators) {
TEST(GlobalHeap) { testAlloc<HeapAllocGlobal>(); }
TEST_DEF_STATIC(GlobalHeap) {
testAlloc<HeapAllocGlobal>(); TEST(Heap) { testAlloc<tp::HeapAlloc>(); }
}
TEST(Chunk) {
TEST_DEF_STATIC(Heap) { testAlloc<ChunkAlloc<TestStruct, size>>();
testAlloc<HeapAlloc>(); testAlloc<ChunkAlloc<TestStruct, size * 2>>();
} }
TEST_DEF_STATIC(Chunk) { TEST(Pool) {
testAlloc<ChunkAlloc<TestStruct, size>>(); testAlloc<PoolAlloc<TestStruct, 1>>();
testAlloc<ChunkAlloc<TestStruct, size * 2>>(); testAlloc<PoolAlloc<TestStruct, size / 100>>();
} testAlloc<PoolAlloc<TestStruct, size>>();
}
TEST_DEF_STATIC(Pool) {
testAlloc<PoolAlloc<TestStruct, 1>>(); TEST(Simple) {
testAlloc<PoolAlloc<TestStruct, size / 100>>(); auto a = new TestStruct(-1);
testAlloc<PoolAlloc<TestStruct, size>>(); delete a;
} }
}
TEST_DEF_STATIC(Simple) {
auto a = new TestStruct(-1);
delete a; int main() {
}
tp::ModuleManifest* deps[] = { &tp::gModuleAllocators, nullptr };
TEST_DEF(All) { tp::ModuleManifest testModule("AllocatorsTest", nullptr, nullptr, deps);
testSimple();
if (!testModule.initialize()) {
testGlobalHeap(); return 1;
testHeap(); }
testChunk();
testPool(); bool res = UnitTest::RunAllTests();
// TEST(HeapAllocGlobal::checkLeaks()); testModule.deinitialize();
// TEST(false);
return res;
} }

View file

@ -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();
}

View file

@ -1,6 +0,0 @@
#pragma once
#include "Utils.hpp"
#include "Testing.hpp"
void testAll();

View file

@ -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") set(CMAKE_CXX_STANDARD 20)
enable_testing()
project(Types)
# 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) add_compile_definitions(MEM_DEBUG)
project(ModulesRoot)
include(CMakeOptions.txt)
add_subdirectory(Modules) add_subdirectory(Modules)
add_subdirectory(Utils) add_subdirectory(Callstack)
add_subdirectory(Containers) add_subdirectory(Containers)
add_subdirectory(Math) add_subdirectory(Math)
add_subdirectory(Allocators) 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)

14
CMakeOptions.txt Normal file
View file

@ -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()

15
CMakeSettings.json Normal file
View file

@ -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": ""
}
]
}

14
Callstack/CMakeLists.txt Normal file
View file

@ -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})

View file

@ -0,0 +1,332 @@
#include "Callstack.hpp"
#include <iostream>
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 <cstring>
#include <cxxabi.h>
#include <execinfo.h>
#include <malloc.h>
void CallStackCapture::platformWriteStackTrace(CallStack* stack) {
auto depth = backtrace((void**) stack->frames, (int) MAX_CALL_DEPTH_CAPTURE - 1);
stack->frames[depth] = 0;
}
static void getGetSourceFromBinaryAddress(const char* binary, const char* address, char* file, ualni* line) {
static char buff[1024];
snprintf(buff, sizeof(buff), "addr2line -e %s -a %s", binary, address);
FILE* pipe = popen(buff, "r");
if (pipe) {
fgets(buff, sizeof(buff), pipe);
fgets(buff, sizeof(buff), pipe);
pclose(pipe);
char* linePtr = strchr(buff, ':');
printf("%s\n", buff);
if (linePtr != nullptr && buff[0] != '?' && buff[1] != '?' && buff[2] != ':') {
*linePtr = '\0';
auto sourceLen = std::strlen(buff);
std::strcpy(file, buff + ((sourceLen > MAX_DEBUG_INFO_LEN) ? (sourceLen - MAX_DEBUG_INFO_LEN) : 0));
*line = strtoul(linePtr + 1, nullptr, 10);
return;
}
}
std::strcpy(file, "unresolved");
*line = 0;
}
static void getDemangledName(const char* func, char* out) {
int status;
size_t funcDemangledSize = MAX_DEBUG_INFO_LEN;
char* funcDemangled = (char*) malloc(funcDemangledSize);
char* ret = abi::__cxa_demangle(func, funcDemangled, &funcDemangledSize, &status);
if (status == 0) {
funcDemangled = ret;
auto funcLen = std::strlen(funcDemangled);
std::strcpy(out, funcDemangled + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0));
free(ret);
return;
}
auto funcLen = std::strlen(func);
std::strcpy(out, func + ((funcLen > MAX_DEBUG_INFO_LEN) ? (funcLen - MAX_DEBUG_INFO_LEN) : 0));
free(funcDemangled);
}
void CallStackCapture::platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out) {
void* addrList[1] = { (void*) frame };
auto symbolsArray = backtrace_symbols(addrList, 1);
// 'bin(fun+addr)'
char* bin = *symbolsArray;
char* func = nullptr;
char* offset = nullptr;
// 'bin fun+addr'
for (char* p = bin; *p; ++p) {
if (*p == '(') {
*p = 0;
func = p + 1;
} else if (*p == '+') {
offset = p;
} else if (*p == ')' && offset) {
*p = 0;
}
}
if (func && offset) {
getGetSourceFromBinaryAddress(bin, func, out->file, &out->line);
if (offset != func) {
*offset = 0;
getDemangledName(func, out->function);
} else {
std::strcpy(out->function, "unresolved");
}
} else {
std::strcpy(out->file, "unresolved");
std::strcpy(out->function, "unresolved");
}
free(symbolsArray);
}
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 <windows.h>
#include <dbghelp.h>
#include <stdio.h>
#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

View file

@ -3,18 +3,20 @@
#include "Environment.hpp" #include "Environment.hpp"
#include "Map.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 { namespace tp {
class CallStackCapture { class CallStackCapture {
public: public:
typedef tp::alni FramePointer; 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 { class CallStack {
friend CallStackCapture; friend CallStackCapture;
FramePointer frames[MAX_CALL_DEPTH_CAPTURE]; FramePointer frames[MAX_CALL_DEPTH_CAPTURE];
@ -24,23 +26,27 @@ namespace tp {
class Iterator { class Iterator {
const FramePointer* mFrame; const FramePointer* mFrame;
public: public:
explicit Iterator(const FramePointer* frame) : mFrame(frame) {}; explicit Iterator(const FramePointer* frame) : mFrame(frame){};
FramePointer getFrame() { return *mFrame; } FramePointer getFrame() { return *mFrame; }
bool operator==(const Iterator& in) const { return in.mFrame == mFrame; } bool operator==(const Iterator& in) const { return in.mFrame == mFrame; }
void operator++() { mFrame++; } void operator++() { mFrame++; }
const Iterator& operator*() const { return *this; } const Iterator& operator*() const { return *this; }
}; };
[[nodiscard]] Iterator begin() const { return Iterator(frames + FRAMES_TO_SKIP_START); } [[nodiscard]] Iterator begin() const { return Iterator(frames); }
[[nodiscard]] Iterator end() const { return Iterator(frames + FRAMES_TO_SKIP_START + getDepth()); } [[nodiscard]] Iterator end() const { return Iterator(frames + getDepth()); }
}; };
enum { STACKS_LENGTH = (MAX_CALL_CAPTURES_MEM_SIZE_MB * 1024 * 1024) / sizeof(CallStack) };
class DebugSymbols { class DebugSymbols {
friend CallStackCapture; friend CallStackCapture;
char function[MAX_DEBUG_INFO_LEN + 1] = { 0 }; char function[MAX_DEBUG_INFO_LEN + 1] = { 0 };
char file[MAX_DEBUG_INFO_LEN + 1] = { 0 }; char file[MAX_DEBUG_INFO_LEN + 1] = { 0 };
ualni line = 0; ualni line = 0;
public: public:
[[nodiscard]] const char* getFunc() const { return function; } [[nodiscard]] const char* getFunc() const { return function; }
[[nodiscard]] const char* getFile() const { return file; } [[nodiscard]] const char* getFile() const { return file; }
@ -54,41 +60,13 @@ namespace tp {
[[nodiscard]] const CallStack* getSnapshot(); [[nodiscard]] const CallStack* getSnapshot();
const DebugSymbols* getSymbols(FramePointer fp); const DebugSymbols* getSymbols(FramePointer fp);
static void printSnapshot(const CallStack* snapshot); void printSnapshot(const CallStack* snapshot);
void logLeaks(); void logAll();
public:
template<class Saver>
void write(Saver& file) {
file.write(mBuffLoad);
for (auto cs : *this) {
file.write(cs.getCallStack()->getDepth());
for (auto frame : *cs.getCallStack()) {
file.write((ualni) frame.getFrame());
}
}
file.write(mSymbols);
}
// independent of the configuration
template<class Loader>
void read(Loader& file) {
clear();
ualni loadLen;
file.read(loadLen);
for (auto cs = loadLen; cs; cs--) {
ualni callStackLen;
file.read(callStackLen);
for (auto fp = callStackLen; fp; fp--) {
// --
}
}
}
public: public:
class Iterator { class Iterator {
const CallStack* mSnapshot; const CallStack* mSnapshot;
public: public:
explicit Iterator(const CallStack* start) : mSnapshot(start) {}; explicit Iterator(const CallStack* start) : mSnapshot(start) {};
const CallStack* getCallStack() { return mSnapshot; } const CallStack* getCallStack() { return mSnapshot; }
@ -98,10 +76,9 @@ namespace tp {
}; };
[[nodiscard]] Iterator begin() const { return Iterator(mBuff); } [[nodiscard]] Iterator begin() const { return Iterator(mBuff); }
[[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); } [[nodiscard]] Iterator end() const { return Iterator(mBuff + mBuffLoad); }
private: private:
struct CallStackKey { struct CallStackKey {
CallStack* cs; CallStack* cs;
bool operator==(const CallStackKey& in) const; bool operator==(const CallStackKey& in) const;
@ -110,16 +87,19 @@ namespace tp {
static void platformWriteStackTrace(CallStack* stack); static void platformWriteStackTrace(CallStack* stack);
static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out); static void platformWriteDebugSymbols(FramePointer frame, DebugSymbols* out);
[[nodiscard]] static ualni hashCallStack(CallStackKey key); [[nodiscard]] static ualni hashCallStack(CallStackKey key);
void platformInit();
void platformDeinit();
void clear(); void clear();
private: private:
CallStack mErrorSnapshot;
ualni mBuffLen; ualni mBuffLen;
ualni mBuffLoad; ualni mBuffLoad;
CallStack* mBuff; CallStack* mBuff;
Map<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots; Map<CallStackKey, CallStack*, DefaultAllocator, hashCallStack> mSnapshots;
Map<FramePointer, DebugSymbols> mSymbols; Map<FramePointer, DebugSymbols> mSymbols;
}; };
extern CallStackCapture* gCSCapture;
} }

44
Callstack/tests/Test.cpp Normal file
View file

@ -0,0 +1,44 @@
#include "UnitTest++/UnitTest++.h"
#include "Callstack.hpp"
#include <cstdio>
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(); }

18
Connection/CMakeLists.txt Normal file
View file

@ -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})

View file

@ -0,0 +1,84 @@
#include "LocalConnection.hpp"
#include "bindings/Disk.hpp"
#include <cstdio>
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();
}

View file

@ -0,0 +1,2 @@
#include "RemoteConnection.hpp"

View file

@ -0,0 +1,53 @@
#include "Disk.hpp"
#include <fstream>
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);
}

View file

@ -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();
};
}

View file

@ -0,0 +1,116 @@
#include "Network.hpp"
#include "asio.hpp"
#include <iostream>
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;
}
}

View file

@ -0,0 +1,66 @@
#pragma once
#include "Module.hpp"
#include <string>
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; }
};
}

View file

@ -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 <bool tRead>
class ArchiverLocalConnection : public ArchiverTemplate<tRead> {
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;
};
}

View file

@ -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);
};
}

View file

@ -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;
};
}

39
Connection/tests/Test.cpp Normal file
View file

@ -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();
}

View file

@ -1,8 +1,3 @@
cmake_minimum_required(VERSION 3.2)
set(CMAKE_CXX_STANDARD 23)
project(Containers) project(Containers)
### ---------------------- Static Library --------------------- ### ### ---------------------- Static Library --------------------- ###
@ -13,10 +8,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Modules) target_link_libraries(${PROJECT_NAME} PUBLIC Modules)
### -------------------------- Tests -------------------------- ### ### -------------------------- Tests -------------------------- ###
enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp") file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES}) add_executable(Tests${PROJECT_NAME} ${TEST_SOURCES})
target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils) target_link_libraries(Tests${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests) add_test(NAME Tests${PROJECT_NAME} COMMAND Tests${PROJECT_NAME})
install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)

View file

@ -1,18 +1,11 @@
#include "ContainersCommon.hpp" #include "ContainersCommon.hpp"
#include <cstdlib> #include <cstdlib>
namespace tp { namespace tp {
static ModuleManifest* sModuleDependencies[] = { &gModuleBase,nullptr }; void* DefaultAllocator::allocate(ualni size) { return malloc(size); }
ModuleManifest gModuleContainers = ModuleManifest("Containers", nullptr, nullptr, sModuleDependencies);
void DefaultAllocator::deallocate(void* p) { free(p); }
void* DefaultAllocator::allocate(ualni size) {
return malloc(size);
}
void DefaultAllocator::deallocate(void* p) {
free(p);
}
} }

View file

@ -5,68 +5,174 @@
namespace tp { namespace tp {
template<ualni tScale = 2> template <ualni tScale = 2>
inline ualni BufferResizeScaling(ualni const size) { return size * tScale; } inline ualni BufferResizeScaling(const ualni size) {
return size * tScale;
}
template<ualni tScale = 2> template <ualni tScale = 2>
inline ualni BufferResizeScalingDown(ualni const size) { return size / tScale; } inline ualni BufferResizeScalingDown(const ualni size) {
return size / tScale;
}
template<ualni tAddition = 1> template <ualni tAddition = 1>
inline ualni BufferResizeAddition(ualni const size) { return size + tAddition; } inline ualni BufferResizeAddition(const ualni size) {
return size + tAddition;
}
template<ualni tAddition = 1> template <ualni tAddition = 1>
inline ualni BufferResizeAdditionDown(ualni const size) { return size - tAddition; } inline ualni BufferResizeAdditionDown(const ualni size) {
return size - tAddition;
}
template <typename tType, ualni tSize>
template<typename tType, ualni tSize>
class ConstSizeBuffer { class ConstSizeBuffer {
SelCopyArg<tType> Arg; typedef SelectValueOrReference<tType> Arg;
private: private:
tType mBuff[tSize]; tType mBuff[tSize];
ualni mLoad = 0;
public: public:
ConstSizeBuffer() = default; ConstSizeBuffer() = default;
~ConstSizeBuffer() {
for (auto i = 0; i < mLoad; i++) {
mBuff[i].~tType();
}
}
public: public:
[[nodiscard]] ualni size() const { return tSize; } [[nodiscard]] ualni size() const { return mLoad; }
tType& first() { [[nodiscard]] ualni getBuffSize() const { return tSize; }
return *mBuff;
} tType& first() {
DEBUG_ASSERT(mLoad)
const tType& first() const { return *mBuff;
return *mBuff;
} }
tType& last() { const tType& first() const {
return mBuff[tSize - 1]; DEBUG_ASSERT(mLoad)
return *mBuff;
} }
const tType& last() const { tType& last() {
return mBuff[tSize - 1]; DEBUG_ASSERT(mLoad)
return mBuff[mLoad - 1];
}
const tType& last() const {
DEBUG_ASSERT(mLoad)
return mBuff[mLoad - 1];
} }
tType& operator[](ualni aIdx) { tType& operator[](ualni aIdx) {
DEBUG_ASSERT(aIdx >= 0 && aIdx < tSize) DEBUG_ASSERT(aIdx >= 0 && aIdx < mLoad)
return mBuff[aIdx]; return mBuff[aIdx];
} }
const tType& operator[](ualni aIdx) const { const tType& operator[](ualni aIdx) const {
DEBUG_ASSERT(aIdx >= 0 && aIdx < tSize) DEBUG_ASSERT(aIdx >= 0 && aIdx < mLoad)
return mBuff[aIdx]; 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<tType>& 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<TypeTraits<tType>::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 <class tArchiver>
void archiveWrite(tArchiver& ar) const {
ar << mLoad;
for (auto item : *this) {
ar << item.data();
}
}
template <class tArchiver>
void archiveRead(tArchiver& ar) {
clear();
ar >> mLoad;
for (auto i = 0; i < mLoad; i++) {
ar >> mBuff[i];
}
}
}; };
template< template <
typename tType, typename tType,
class tAllocator = DefaultAllocator, class tAllocator = DefaultAllocator,
ualni (tResizePolicy)(ualni) = BufferResizeScaling<2>, ualni(tResizePolicy)(ualni) = BufferResizeScaling<2>,
ualni (tResizePolicyDown)(ualni) = BufferResizeScalingDown<2>, ualni(tResizePolicyDown)(ualni) = BufferResizeScalingDown<2>,
ualni tMinSize = 4 ualni tMinSize = 4>
>
class Buffer { class Buffer {
typedef SelCopyArg<tType> Arg; typedef SelectValueOrReference<tType> Arg;
private: private:
tType* mBuff; tType* mBuff;
@ -75,58 +181,91 @@ namespace tp {
ualni mLoad; ualni mLoad;
public: public:
Buffer() : mSize(tMinSize), mLoad(0) { Buffer() :
mSize(tMinSize),
mLoad(0) {
mBuff = (tType*) mAllocator.allocate(sizeof(tType) * tMinSize); mBuff = (tType*) mAllocator.allocate(sizeof(tType) * tMinSize);
} }
explicit Buffer(ualni size) : mSize(size), mLoad(0) { Buffer(const InitialierList<tType>& input) {
mBuff = (tType*) mAllocator.allocate(sizeof(tType) * tMinSize); mSize = 0;
} for (const auto& val : input) {
mSize++;
Buffer(const Buffer& in) : mSize(in.mSize), mLoad(in.mLoad) { }
mBuff = (tType*) mAllocator.allocate(sizeof(tType) * 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) { Buffer& operator=(const Buffer& in) {
if (this == &in) return *this; if (this == &in) {
return *this;
}
this->~Buffer(); this->~Buffer();
new (this) Buffer(in); new (this) Buffer(in);
return *this; return *this;
} }
~Buffer() { ~Buffer() {
for (ualni i = 0; i < mLoad; i++) mBuff[i].~tType(); for (ualni i = 0; i < mLoad; i++) {
mBuff[i].~tType();
}
mAllocator.deallocate(mBuff); mAllocator.deallocate(mBuff);
} }
public: public:
[[nodiscard]] ualni size() const { [[nodiscard]] ualni size() const { return mLoad; }
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() { tType& first() {
DEBUG_ASSERT(mLoad) DEBUG_ASSERT(mLoad)
return *mBuff; return *mBuff;
} }
const tType& first() const { const tType& first() const {
DEBUG_ASSERT(mLoad) DEBUG_ASSERT(mLoad)
return *mBuff; return *mBuff;
} }
tType& last() { tType& last() {
DEBUG_ASSERT(mLoad) DEBUG_ASSERT(mLoad)
return mBuff[mLoad - 1]; return mBuff[mLoad - 1];
} }
const tType& last() const { const tType& last() const {
DEBUG_ASSERT(mLoad) DEBUG_ASSERT(mLoad)
return mBuff[mLoad - 1]; return mBuff[mLoad - 1];
} }
tType& operator[](ualni idx) { tType& operator[](ualni idx) {
@ -141,50 +280,216 @@ namespace tp {
public: public:
bool operator==(const Buffer& in) const { bool operator==(const Buffer& in) const {
if (this == &in) return true; if (this == &in) {
if (mLoad != in.mLoad) return false; return true;
}
if (mLoad != in.mLoad) {
return false;
}
for (ualni i = 0; i < mLoad; i++) { 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; return true;
} }
public: public:
void append(Arg data) { Buffer& operator=(const tp::InitialierList<tType>& input) {
if (mLoad == mSize) resizeBuffer(tResizePolicy(mSize)); // TODO : optimize
new (&mBuff[mLoad]) tType(data); for (ualni i = 0; i < mLoad; i++) {
mLoad++; 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) { tType& append(Arg data) {
if (!in.mLoad) return; if (mLoad == mSize) {
auto newLoad = mLoad + in.mLoad; resizeBuffer(tResizePolicy(mSize));
auto newSize = mSize; }
while (newLoad >= newSize) newSize = tResizePolicy(newSize); new (&mBuff[mLoad]) tType(data);
if (newSize != mSize) resizeBuffer(newSize); mLoad++;
for (auto i = mLoad; i < newLoad; i++) new (&mBuff[i]) tType(in.mBuff[i - mLoad]); return mBuff[mLoad - 1];
mLoad = newLoad; }
}
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() { void pop() {
DEBUG_ASSERT(mLoad) DEBUG_ASSERT(mLoad)
mBuff[mLoad].~tType(); mBuff[mLoad].~tType();
mLoad--; mLoad--;
ualni prevSize = tResizePolicyDown(mSize); ualni prevSize = tResizePolicyDown(mSize);
DEBUG_ASSERT(prevSize < mSize) DEBUG_ASSERT(prevSize < mSize)
if (prevSize > mLoad) resizeBuffer(prevSize); 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: public:
void resizeBuffer(ualni newSize) { class IteratorPointer {
DEBUG_ASSERT(newSize >= mLoad) protected:
auto const oldBuff = mBuff; tType* mIter;
mBuff = (tType*) mAllocator.allocate(sizeof(tType) * newSize);
for (ualni i = 0; i < mLoad; i++) new (&mBuff[i]) tType(oldBuff[i]); public:
for (ualni i = 0; i < mLoad; i++) oldBuff[i].~tType(); IteratorPointer() = default;
mAllocator.deallocate(oldBuff);
mSize = newSize; 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<TypeTraits<tType>::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 <class tArchiver>
void archiveWrite(tArchiver& ar) const {
ar << mLoad;
for (auto item : *this) {
ar % item.data();
}
}
template <class tArchiver>
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;
}
}; };
}
template <typename tType>
void generatePermutations(const Buffer<Buffer<tType>>& in, Buffer<Buffer<tType>>& 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();
}
}
}

View file

@ -0,0 +1,131 @@
#pragma once
#include "Buffer.hpp"
#include "Utils.hpp"
namespace tp {
template <typename tType, ualni tSizeX, ualni tSizeY>
using ConstSizeBuffer2D = ConstSizeBuffer<ConstSizeBuffer<tType, tSizeX>, tSizeY>;
typedef ualni Index;
struct Index2D {
Index x = 0;
Index y = 0;
};
template <typename tType, class tAllocator = DefaultAllocator>
class Buffer2D {
public:
typedef SelectValueOrReference<tType> 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 <class tArchiver>
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 <class tArchiver>
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);
}
}
}
};
}

View file

@ -4,23 +4,22 @@
#include "Module.hpp" #include "Module.hpp"
namespace tp { namespace tp {
extern ModuleManifest gModuleContainers;
class DefaultAllocator { class DefaultAllocator {
public: public:
DefaultAllocator() = default; DefaultAllocator() = default;
static void *allocate(ualni); static void* allocate(ualni);
static void deallocate(void *); static void deallocate(void*);
}; };
class DefaultSaverLoader { class DefaultSaverLoader {
public: public:
DefaultSaverLoader() = default; DefaultSaverLoader() = default;
template<typename Type> template <typename Type>
static void write(const Type&) {} static void write(const Type&) {}
template<typename Type> template <typename Type>
static void read(Type&) {} static void read(Type&) {}
}; };
} }

View file

@ -0,0 +1,88 @@
#pragma once
#include "Tree.hpp"
namespace tp {
template <typename tType>
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 <typename tTreeNodeType>
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 <typename tType, typename tData>
class IntervalTree : public AvlTree<IntervalKey<tType>, tData> {
typedef typename AvlTree<IntervalKey<tType>, tData>::Node Node;
public:
IntervalTree() = default;
template <typename tFunctor>
ualni forEachIntersection(tType start, tType end, tFunctor functor) const {
ualni debug = 0;
forEachIntersectionUtil(this->head(), start, end, functor, debug);
return debug;
}
private:
template <typename tFunctor>
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);
}
};
}

View file

@ -1,322 +1,333 @@
#pragma once #pragma once
#include "ContainersCommon.hpp" #include "ContainersCommon.hpp"
#include "TypeInfo.hpp" #include "TypeInfo.hpp"
namespace tp { namespace tp {
template <typename Type, class Allocator = DefaultAllocator> template <typename Type, class Allocator = DefaultAllocator>
class List { class List {
typedef SelCopyArg<Type> TypeArg; typedef SelectValueOrReference<Type> TypeArg;
typedef ualni Index; typedef ualni Index;
public: public:
struct Node {
struct Node { Type data;
Type data; Node* next = nullptr;
Node* next = nullptr; Node* prev = nullptr;
Node* prev = nullptr; Node() { data = Type(); }
Node() = default; explicit Node(TypeArg p_data) :
explicit Node(TypeArg p_data) : data(p_data) {} data(p_data) {}
Type& operator->() { return data; } Type& operator->() { return data; }
}; };
class IteratorPointer { class IteratorPointer {
protected: protected:
Node* mIter; Node* mIter;
public:
IteratorPointer() = default; public:
Type& operator->() { return (mIter->data); } IteratorPointer() = default;
const Type& operator->() const { return (mIter->data); } Type& operator->() { return (mIter->data); }
}; const Type& operator->() const { return (mIter->data); }
};
class IteratorReference {
protected: class IteratorReference {
Node* mIter; protected:
public: Node* mIter;
IteratorReference() = default;
Type* operator->() { return &(mIter->data); } public:
const Type* operator->() const { return &(mIter->data); } IteratorReference() = default;
}; Type* operator->() { return &(mIter->data); }
const Type* operator->() const { return &(mIter->data); }
class Iterator : public TypeSelect<TypeTraits<Type>::isPointer, IteratorPointer, IteratorReference>::Result { };
public:
class Iterator : public TypeSelect<TypeTraits<Type>::isPointer, IteratorPointer, IteratorReference>::Result {
explicit Iterator(Node* iter) { this->mIter = iter; } public:
explicit Iterator(Node* iter) { this->mIter = iter; }
Node* node() { return this->mIter; }
Type& data() { return this->mIter->data; } Node* node() { return this->mIter; }
const Node* node() const { return this->mIter; } Type& data() { return this->mIter->data; }
const Type& data() const { 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; }
const Iterator& operator*() const { return *this; }
Iterator& operator++() {
this->mIter = this->mIter->next; Iterator& operator++() {
return *this; 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; } 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; private:
Node* mLast = nullptr; Node* mFirst = nullptr;
Index mLength = 0; Node* mLast = nullptr;
Allocator mAlloc; Index mLength = 0;
Allocator mAlloc;
public:
public:
List() = default; List() = default;
List(const List& in) { this->operator=(in); }
List(const init_list<Type>& list) { operator=(list); } List(const InitialierList<Type>& list) { operator=(list); }
[[nodiscard]] inline Node* first() const { return mFirst; } [[nodiscard]] inline Node* first() const { return mFirst; }
[[nodiscard]] inline Node* last() const { return mLast; } [[nodiscard]] inline Node* last() const { return mLast; }
[[nodiscard]] inline Index length() const { return mLength; } [[nodiscard]] inline Index length() const { return mLength; }
[[nodiscard]] Node* newNode() { return new (mAlloc.allocate(sizeof(Node))) Node(); } [[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* newNode(TypeArg arg) { return new (mAlloc.allocate(sizeof(Node))) Node(arg); }
[[nodiscard]] Node* newNodeNotConstructed() { [[nodiscard]] Node* newNodeNotConstructed() {
auto node = (Node*) mAlloc.allocate(sizeof(Node)); auto node = (Node*) mAlloc.allocate(sizeof(Node));
node->next = node->prev = nullptr; node->next = node->prev = nullptr;
return node; return node;
} }
[[nodiscard]] const Allocator& getAllocator() const { return mAlloc; } [[nodiscard]] const Allocator& getAllocator() const { return mAlloc; }
void deleteNode(Node* node) { void deleteNode(Node* node) {
node->~Node(); node->~Node();
mAlloc.deallocate(node); mAlloc.deallocate(node);
} }
Node* addNodeBack() { Node* addNodeBack() {
auto const out = newNode(); auto const out = newNode();
pushBack(out); pushBack(out);
return out; return out;
} }
Node* addNodeFront() { Node* addNodeFront() {
auto const out = newNode(); auto const out = newNode();
pushFront(out); pushFront(out);
return out; return out;
} }
void attach(Node* node, Node* node_to) { void attach(Node* node, Node* node_to) {
if (node_to) { if (node_to) {
if (node_to->next) { if (node_to->next) {
node->next = node_to->next; node->next = node_to->next;
node->next->prev = node; node->next->prev = node;
} }
node_to->next = node; node_to->next = node;
node->prev = node_to; node->prev = node_to;
if (node_to == mLast) { if (node_to == mLast) {
mLast = node; mLast = node;
} }
} else { } else {
if (mFirst) { if (mFirst) {
mFirst->prev = node; mFirst->prev = node;
node->next = mFirst; node->next = mFirst;
mFirst = node; mFirst = node;
} else { } else {
mFirst = mLast = node; mFirst = mLast = node;
} }
} }
mLength++; mLength++;
} }
void detach(Node* node) { void detach(Node* node) {
if (node->next) { if (node->next) {
node->next->prev = node->prev; node->next->prev = node->prev;
} }
if (node->prev) { if (node->prev) {
node->prev->next = node->next; node->prev->next = node->next;
} }
if (node == mLast) { if (node == mLast) {
mLast = mLast->prev; mLast = mLast->prev;
} }
if (node == mFirst) { if (node == mFirst) {
mFirst = mFirst->next; mFirst = mFirst->next;
} }
mLength--; mLength--;
} }
[[nodiscard]] Node* findIdx(Index idx) const { [[nodiscard]] Node* findIdx(Index idx) const {
DEBUG_ASSERT(!mFirst || idx > mLength - 1) DEBUG_ASSERT(mFirst && idx < mLength && idx >= 0)
Node* found = mFirst; Node* found = mFirst;
for (int i = 0; i != idx; i++) { for (int i = 0; i != idx; i++) {
found = found->next; found = found->next;
} }
return found; return found;
} }
[[nodiscard]] Node* find(const TypeArg data) const { [[nodiscard]] Node* find(const TypeArg data) const {
Node* found = mFirst; Node* found = mFirst;
for (alni i = 0; data != found->data; i++) { for (alni i = 0; data != found->data; i++) {
if (!found->next) { if (i == length()) return nullptr;
return nullptr; if (!found->next) {
} return nullptr;
found = found->next; }
} found = found->next;
return found; }
} return found;
}
[[nodiscard]] inline const Type& operator[](Index idx) const {
DEBUG_ASSERT(idx < mLength) [[nodiscard]] inline const Type& operator[](Index idx) const {
return find(idx)->data; 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(Node* new_node) { attach(new_node, mLast); }
void pushBack(TypeArg data) { pushBack(newNode(data)); } void pushFront(Node* new_node) { attach(new_node, nullptr); }
void pushFront(TypeArg data) { pushFront(newNode(data)); } void pushBack(TypeArg data) { pushBack(newNode(data)); }
void pushFront(TypeArg data) { pushFront(newNode(data)); }
void popBack() {
DEBUG_ASSERT(mLast) void popBack() {
detach(mLast); DEBUG_ASSERT(mLast)
deleteNode(mLast); auto const tmp = mLast;
} detach(mLast);
deleteNode(tmp);
void popFront() { }
DEBUG_ASSERT(mFirst)
auto temp = mFirst; void popFront() {
detach(mFirst); DEBUG_ASSERT(mFirst)
deleteNode(temp); auto temp = mFirst;
} detach(mFirst);
deleteNode(temp);
void insert(Node* node, Index idx) { }
if (!mLength) {
attach(node, mLast); void insert(Node* node, Index idx) {
} else if (idx >= mLength) { if (!mLength) {
attach(node, nullptr); attach(node, mLast);
} else { } else if (idx >= mLength) {
attach(node, find(idx)->prev); attach(node, nullptr);
} } else {
} attach(node, find(idx)->prev);
}
void insert(TypeArg data, Index idx) { }
insert(newNode(data), idx);
} void insert(TypeArg data, Index idx) { insert(newNode(data), idx); }
void removeNode(Node* node) { void removeNode(Node* node) {
detach(node); detach(node);
deleteNode(node); deleteNode(node);
} }
void removeAll() { void removeAll() {
while (mFirst) { while (mFirst) {
popFront(); popFront();
} }
} }
// copies data // copies data
List& operator+=(const List& in) { List& operator+=(const List& in) {
for (auto node : in) { for (auto node : in) {
pushBack(node.data()); pushBack(node.data());
} }
return *this; return *this;
} }
List& operator+=(const init_list<Type>& list) { List& operator+=(const InitialierList<Type>& list) {
for (auto item : list) { for (auto item : list) {
pushBack(item); pushBack(item);
} }
return *this; return *this;
} }
List& operator=(const List& in) { List& operator=(const List& in) {
if (this == &in) { return *this; } if (this == &in) {
removeAll(); return *this;
(*this) += in; }
return *this; removeAll();
} (*this) += in;
return *this;
List& operator=(const init_list<Type>& list) { }
removeAll();
*this += list; List& operator=(const InitialierList<Type>& list) {
return *this; removeAll();
} *this += list;
return *this;
[[nodiscard]] bool operator==(const List& in) const { }
if (in == *this) { return true; }
if (in.length() != length()) { [[nodiscard]] bool operator==(const List& in) const {
return false; if (&in == this) {
} return true;
Node* left = in.first(); }
Node* right = first(); if (in.length() != length()) {
while (left && right) { return false;
if (left->data != right->data) { }
return false; Node* left = in.first();
} Node* right = first();
} while (left && right) {
if (left != right) { if (left->data != right->data) {
return false; return false;
} }
return true; left = left->next;
} right = right->next;
}
template <typename compare_val> if (left != right) {
[[nodiscard]] Node* find(bool (*found)(Node* node, compare_val val), compare_val value) const { return false;
for (Node* node = mFirst; node; node = node->next) { }
if (found(node, value)) { return true;
return node; }
}
} template <typename compare_val>
return nullptr; [[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)) {
[[nodiscard]] Iterator begin() const { return node;
Iterator out(mFirst); }
return out; }
} return nullptr;
}
[[nodiscard]] Iterator end() const {
return Iterator(nullptr); [[nodiscard]] Iterator begin() const {
} Iterator out(mFirst);
return out;
void invert() { }
Node* iter = mFirst;
Node* tmp; [[nodiscard]] Iterator end() const { return Iterator(nullptr); }
while (iter) {
tmp = iter; void invert() {
iter = iter->next; Node* iter = mFirst;
swap(tmp->next, tmp->prev); Node* tmp;
} while (iter) {
swap(mFirst, mLast); tmp = iter;
} iter = iter->next;
swap(tmp->next, tmp->prev);
void detachAll() { }
while (mFirst) { swap(mFirst, mLast);
detach(mFirst); }
}
} void detachAll() {
while (mFirst) {
template<class Saver> detach(mFirst);
void write(Saver& file) const { }
file.write(mLength); }
for (auto item : *this) {
file.write(item.data()); void transferNodes(List in) {
} removeAll();
} for (auto node : in) {
attach(node);
template<class Loader> }
void read(Loader& file) { in.detachAll();
removeAll(); }
ualni len;
file.read(len); public:
for (auto i = len; i; i--) { template <class tArchiver>
auto node = newNodeNotConstructed(); void archiveWrite(tArchiver& ar) const {
file.read(node->data); ar << mLength;
pushBack(node); for (auto item : *this) {
} ar << item.data();
} }
}
~List() {
removeAll(); template <class tArchiver>
} 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(); }
};
} }

View file

@ -1,392 +1,384 @@
#pragma once #pragma once
#include "ContainersCommon.hpp" #include "Common.hpp"
#include "Common.hpp" #include "ContainersCommon.hpp"
namespace tp { namespace tp {
template<typename Key> template <typename Key>
ualni DefaultHashFunc(SelCopyArg<Key> key) { ualni DefaultHashFunc(SelectValueOrReference<Key> key) {
return hash(key); return hash(key);
} }
template<typename tKey, typename tVal, class tAllocator = DefaultAllocator, ualni(*tHashFunc)(SelCopyArg<tKey>) = DefaultHashFunc<tKey>, int tTableInitialSize = 4> template <typename tKey, typename tVal, class tAllocator = DefaultAllocator, ualni (*tHashFunc)(SelectValueOrReference<tKey>) = DefaultHashFunc<tKey>, int tTableInitialSize = 4>
class Map { class Map {
enum { enum {
MAP_PERTURB_SHIFT = 5, MAP_PERTURB_SHIFT = 5,
MAP_MIN_SIZE = 4, MAP_MIN_SIZE = 4,
MAP_MAX_LOAD_PERCENTAGE = 66, MAP_MAX_LOAD_PERCENTAGE = 66,
}; };
typedef SelCopyArg<tKey> KeyArg; typedef SelectValueOrReference<tKey> KeyArg;
typedef SelCopyArg<tVal> ValArg; typedef SelectValueOrReference<tVal> ValArg;
public: public:
class Node {
class Node { friend Map;
friend Map; Node(KeyArg aKey, ValArg aVal) :
Node(KeyArg aKey, ValArg aVal) : key(aKey), val(aVal) {} key(aKey),
public: val(aVal) {}
tKey key;
tVal val; public:
}; tKey key;
tVal val;
struct Idx { };
alni idx = -1;
operator bool() { return idx != -1; } struct Idx {
}; alni idx = -1;
[[nodiscard]] bool isValid() const { return bool(*this); }
private: explicit operator bool() const { return idx != -1; }
tAllocator mAlloc; };
Node** mTable;
ualni mNSlots = 0; private:
ualni mNEntries = 0; tAllocator mAlloc;
Node** mTable = nullptr;
private: ualni mNSlots = 0;
ualni mNEntries = 0;
constexpr halnf maxLoadFactor() { return halnf(MAP_MAX_LOAD_PERCENTAGE) / 100.f; }
private:
inline Node** newTable(const ualni len) { constexpr halnf maxLoadFactor() { return halnf(MAP_MAX_LOAD_PERCENTAGE) / 100.f; }
return new(mAlloc.allocate(sizeof(Node*) * len)) Node*[len]();
} inline Node** newTable(const ualni len) { return new (mAlloc.allocate(sizeof(Node*) * len)) Node*[len](); }
inline Node* newNode(KeyArg key, ValArg val) { inline Node* newNode(KeyArg key, ValArg val) { return new (mAlloc.allocate(sizeof(Node))) Node(key, val); }
return new(mAlloc.allocate(sizeof(Node))) Node(key, val);
} inline Node* newNodeNotConstructed() { return (Node*) mAlloc.allocate(sizeof(Node)); }
inline Node* newNodeNotConstructed() { inline void deleteTable(Node** table) { mAlloc.deallocate(table); }
return (Node*) mAlloc.allocate(sizeof(Node));
} inline void deleteNode(Node* p) {
p->~Node();
inline void deleteTable(Node** table) { mAlloc.deallocate(p);
mAlloc.deallocate(table); }
}
void markDeletedSlot(ualni idx) const { mTable[idx] = (Node*) -1; }
inline void deleteNode(Node* p) {
p->~Node(); static bool isDeletedNode(Node* node) { return node == (Node*) -1; }
mAlloc.deallocate(p);
} void rehash() {
alni nSlotsOld = mNSlots;
void markDeletedSlot(ualni idx) const { Node** tableOld = mTable;
mTable[idx] = (Node*)-1;
} mNSlots = next2pow((uhalni) ((1.f / (maxLoadFactor())) * mNEntries + 1));
mTable = newTable(mNSlots);
static bool isDeletedNode(Node* node) { mNEntries = 0;
return node == (Node*)-1;
} for (alni i = 0; i < nSlotsOld; i++) {
if (!tableOld[i] || isDeletedNode(tableOld[i])) {
void rehash() { continue;
alni nSlotsOld = mNSlots; }
Node** tableOld = mTable;
alni idx = findSlotWrite(tableOld[i]->key);
mNSlots = next2pow((uhalni)((1.f / (maxLoadFactor())) * mNEntries + 1)); mTable[idx] = tableOld[i];
mTable = newTable(mNSlots); mNEntries++;
mNEntries = 0; }
for (alni i = 0; i < nSlotsOld; i++) { deleteTable(tableOld);
if (!tableOld[i] || isDeletedNode(tableOld[i])) { }
continue;
} alni findSlotRead(KeyArg key) const {
ualni const hashed_key = tHashFunc(key);
alni idx = findSlotWrite(tableOld[i]->key); ualni const mask = mNSlots - 1;
mTable[idx] = tableOld[i]; ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1;
mNEntries++; alni idx = hashed_key & mask;
} NEXT:
if (isDeletedNode(mTable[idx])) {
deleteTable(tableOld); goto SKIP;
} }
if (!mTable[idx]) {
alni findSlotRead(KeyArg key) const { return -1;
ualni const hashed_key = tHashFunc(key); }
ualni const mask = mNSlots - 1; if (mTable[idx]->key == key) {
ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1; return idx;
alni idx = hashed_key & mask; }
NEXT: SKIP:
if (isDeletedNode(mTable[idx])) { idx = ((5 * idx) + 1 + shift) & mask;
goto SKIP; goto NEXT;
} }
if (!mTable[idx]) {
return -1; // compares keys only when collisions occur
} alni findSlotReadExisting(KeyArg key) const {
if (mTable[idx]->key == key) { ualni const hashed_key = tHashFunc(key);
return idx; ualni const mask = mNSlots - 1;
} ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1;
SKIP: alni idx = hashed_key & mask;
idx = ((5 * idx) + 1 + shift) & mask; NEXT:
goto NEXT; if (isDeletedNode(mTable[idx])) {
} goto SKIP;
}
// compares keys only when collisions occur if (!mTable[idx]) {
alni findSlotReadExisting(KeyArg key) const { return -1;
ualni const hashed_key = tHashFunc(key); }
ualni const mask = mNSlots - 1; if (mTable[((5 * idx) + 1 + shift) & mask] == nullptr) {
ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1; return idx;
alni idx = hashed_key & mask; }
NEXT: if (mTable[idx]->key == key) {
if (isDeletedNode(mTable[idx])) { return idx;
goto SKIP; }
} SKIP:
if (!mTable[idx]) { idx = ((5 * idx) + 1 + shift) & mask;
return -1; goto NEXT;
} }
if (mTable[((5 * idx) + 1 + shift) & mask] == nullptr) {
return idx; ualni findSlotWrite(KeyArg key) const {
} ualni const hashed_key = tHashFunc(key);
if (mTable[idx]->key == key) { ualni const mask = mNSlots - 1;
return idx; ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1;
} ualni idx = hashed_key & mask;
SKIP: NEXT:
idx = ((5 * idx) + 1 + shift) & mask; if (isDeletedNode(mTable[idx]) || !mTable[idx]) {
goto NEXT; return idx;
} }
if (mTable[idx]->key == key) {
ualni findSlotWrite(KeyArg key) const { return idx;
ualni const hashed_key = tHashFunc(key); }
ualni const mask = mNSlots - 1; idx = ((5 * idx) + 1 + shift) & mask;
ualni const shift = (hashed_key >> MAP_PERTURB_SHIFT) & ~1; goto NEXT;
ualni idx = hashed_key & mask; }
NEXT:
if (isDeletedNode(mTable[idx]) || !mTable[idx]) { void put(Node* node) {
return idx; const ualni idx = findSlotWrite(node->key);
}
if (mTable[idx]->key == key) { if (!mTable[idx] || isDeletedNode(mTable[idx])) {
return idx; mNEntries++;
} }
idx = ((5 * idx) + 1 + shift) & mask;
goto NEXT; mTable[idx] = node;
}
if ((halnf) mNEntries / mNSlots > maxLoadFactor()) {
void put(Node* node) { rehash();
const ualni idx = findSlotWrite(node->key); }
}
if (!mTable[idx] || isDeletedNode(mTable[idx])) {
mNEntries++; public:
} Map() {
mNSlots = next2pow(uhalni(tTableInitialSize - 1));
mTable[idx] = node; mTable = newTable(mNSlots);
}
if ((halnf)mNEntries / mNSlots > maxLoadFactor()) {
rehash(); Map(const Map& in) { this->operator=(in); }
}
} Node** buff() const { return mTable; }
public: [[nodiscard]] ualni size() const { return mNEntries; }
Map() { [[nodiscard]] ualni slotsSize() const { return mNEntries; }
MODULE_SANITY_CHECK(gModuleContainers)
mNSlots = next2pow(uhalni(tTableInitialSize - 1)); [[nodiscard]] const tAllocator& getAllocator() const { return mAlloc; }
mTable = newTable(mNSlots);
} void put(KeyArg key, ValArg val) {
const ualni idx = findSlotWrite(key);
Node** buff() const { if (!mTable[idx] || isDeletedNode(mTable[idx])) {
return mTable; mTable[idx] = newNode(key, val);
} mNEntries++;
} else {
[[nodiscard]] ualni size() const { mTable[idx]->val = val;
return mNEntries; }
} if ((halnf) mNEntries / mNSlots > maxLoadFactor()) {
rehash();
[[nodiscard]] ualni slotsSize() const { }
return mNEntries; }
}
// undefined behavior if item is not presents
[[nodiscard]] const tAllocator& getAllocator() const { tVal& get(KeyArg key) {
return mAlloc; DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
} return mTable[findSlotReadExisting(key)]->val;
}
void put(KeyArg key, ValArg val) {
const ualni idx = findSlotWrite(key); const tVal& get(KeyArg key) const {
if (!mTable[idx] || isDeletedNode(mTable[idx])) { DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
mTable[idx] = newNode(key, val); return mTable[findSlotReadExisting(key)]->val;
mNEntries++; }
}
mTable[idx]->val = val; [[nodiscard]] Idx presents(KeyArg key) const { return { findSlotRead(key) }; }
if ((halnf) mNEntries / mNSlots > maxLoadFactor()) {
rehash(); void remove(KeyArg key) {
} DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
} auto idx = findSlotReadExisting(key);
// undefined behavior if item is not presents deleteNode(mTable[idx]);
tVal& get(KeyArg key) {
DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error") markDeletedSlot(idx);
return mTable[findSlotReadExisting(key)]->val;
} mNEntries--;
if (halnf(mNEntries / mNSlots) < 1.f - maxLoadFactor()) {
const tVal& get(KeyArg key) const { rehash();
DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error") }
return mTable[findSlotReadExisting(key)]->val; }
}
const tVal& getSlotVal(ualni slot) const {
[[nodiscard]] Idx presents(KeyArg key) const { return { findSlotRead(key) }; } DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error")
return mTable[slot]->val;
void remove(KeyArg key) { }
DEBUG_ASSERT(findSlotRead(key) != -1 && "Key Error")
auto idx = findSlotReadExisting(key); tVal& getSlotVal(ualni slot) {
DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error")
deleteNode(mTable[idx]); return mTable[slot]->val;
}
markDeletedSlot(idx);
const tVal& getSlotVal(Idx slot) const {
mNEntries--; DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error")
if (halnf(mNEntries / mNSlots) < 1.f - maxLoadFactor()) { return mTable[slot.idx]->val;
rehash(); }
}
} tVal& getSlotVal(Idx slot) {
DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error")
const tVal& getSlotVal(ualni slot) const { return mTable[slot.idx]->val;
DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error") }
return mTable[slot]->val;
} Map& operator=(const Map& in) {
if (this == &in) {
tVal& getSlotVal(ualni slot) { return *this;
DEBUG_ASSERT(slot < mNSlots && (mTable[slot] && !isDeletedNode(mTable[slot])) && "Key Error") }
return mTable[slot]->val;
} for (ualni i = 0; i < mNSlots; i++) {
if (mTable[i] && !isDeletedNode(mTable[i])) {
const tVal& getSlotVal(Idx slot) const { deleteNode(mTable[i]);
DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error") }
return mTable[slot]->val; }
} mNEntries = 0;
mNSlots = in.mNSlots;
tVal& getSlotVal(Idx slot) { deleteTable(mTable);
DEBUG_ASSERT(slot.idx < mNSlots && (mTable[slot.idx] && !isDeletedNode(mTable[slot.idx])) && "Key Error") mTable = newTable(mNSlots);
return mTable[slot.idx]->val; for (alni i = 0; i < mNSlots; i++) {
} if (in.mTable[i] && !isDeletedNode(in.mTable[i])) {
put(in.mTable[i]->key, in.mTable[i]->val);
Map& operator=(const Map& in) { }
if (this == &in) { }
return *this; return *this;
} }
removeAll();
mNSlots = in.mNSlots; [[nodiscard]] bool operator==(const Map& in) const {
mTable = newTable(mNSlots); if (this == &in) {
for (alni i = 0; i < mNSlots; i++) { return true;
if (in.mTable[i] && !isDeletedNode(in.mTable[i])) { }
put(in.mTable[i]->key, in.mTable[i]->val); if (in.mNEntries != mNEntries) {
} return false;
} }
return *this; for (auto i : in) {
} if (!presents(i->key) || get(i->key) != i->val) {
return false;
[[nodiscard]] bool operator==(const Map& in) const { }
if (this == &in) { }
return true; return true;
} }
if (in.mNEntries != mNEntries) {
return false; void removeAll() {
} for (ualni i = 0; i < mNSlots; i++) {
for (auto i : in) { if (mTable[i] && !isDeletedNode(mTable[i])) {
if (!presents(i->key) || get(i->key) != i->val) { deleteNode(mTable[i]);
return false; }
} }
} deleteTable(mTable);
return true; mTable = newTable(tTableInitialSize);
} mNSlots = tTableInitialSize;
mNEntries = 0;
void removeAll() { }
for (ualni i = 0; i < mNSlots; i++) {
if (mTable[i] && !isDeletedNode(mTable[i])) { [[nodiscard]] alni slotIdx(alni entry_idx_in) const {
deleteNode(mTable[i]); alni entry_idx = -1;
} for (alni slot_idx = 0; slot_idx < mNSlots; slot_idx++) {
} if (mTable[slot_idx]) {
deleteTable(mTable); entry_idx++;
mTable = newTable(tTableInitialSize); }
mNSlots = tTableInitialSize; if (entry_idx == entry_idx_in) {
mNEntries = 0; return slot_idx;
} }
}
[[nodiscard]] alni slotIdx(alni entry_idx_in) const { return -1;
alni entry_idx = -1; }
for (alni slot_idx = 0; slot_idx < mNSlots; slot_idx++) {
if (mTable[slot_idx]) { Node* GetEntry(ualni idx) {
entry_idx++; auto slot = slotIdx(idx);
} DEBUG_ASSERT(slot != -1 && "Key error")
if (entry_idx == entry_idx_in) { return mTable[slot];
return slot_idx; }
}
} const Node* GetEntry(ualni idx) const {
return -1; auto slot = slotIdx(idx);
} DEBUG_ASSERT(slot != -1 && "Key error")
return mTable[slot];
Node* GetEntry(ualni idx) { }
auto slot = slotIdx(idx);
DEBUG_ASSERT(slot != -1 && "Key error") public:
return mTable[slot]; class Iterator {
} const Map* map;
Node* mIter;
const Node* GetEntry(ualni idx) const { alni mSlot;
auto slot = slotIdx(idx); alni mEntry;
DEBUG_ASSERT(slot != -1 && "Key error")
return mTable[slot]; friend Map;
} explicit Iterator(const Map* _map) {
mSlot = -1;
public: mEntry = -1;
map = _map;
class Iterator { this->operator++();
const Map* map; }
Node* mIter;
alni mSlot; public:
alni mEntry; Node* operator->() { return mIter; }
const Node* operator->() const { return mIter; }
friend Map; const Iterator& operator*() const { return *this; }
explicit Iterator(const Map* _map) {
mSlot = -1; bool operator!=(ualni idx) const { return mSlot != idx; }
mEntry = -1;
map = _map; void operator++() {
this->operator++(); mSlot++;
} while ((map->isDeletedNode(map->mTable[mSlot]) || !map->mTable[mSlot]) && (mSlot != map->mNSlots)) {
mSlot++;
public: }
Node* operator->() { return mIter; } if (mSlot != map->mNSlots) {
const Node* operator->() const { return mIter; } mIter = map->mTable[mSlot];
const Iterator& operator*() const { return *this; } mEntry++;
}
bool operator!=(ualni idx) const { return mSlot != idx; } }
};
void operator++() {
mSlot++; [[nodiscard]] Iterator begin() const { return Iterator(this); }
while ((map->isDeletedNode(map->mTable[mSlot]) || !map->mTable[mSlot]) && (mSlot != map->mNSlots)) {
mSlot++; [[nodiscard]] ualni end() const { return mNSlots; }
}
if (mSlot != map->mNSlots) { template <class Archiver>
mIter = map->mTable[mSlot]; void archiveWrite(Archiver& ar) const {
mEntry++; ar << mNEntries;
} for (auto item : *this) {
} ar << item->val;
}; ar << item->key;
}
[[nodiscard]] Iterator begin() const { }
return Iterator(this);
} template <class Archiver>
void archiveRead(Archiver& ar) {
[[nodiscard]] ualni end() const { removeAll();
return mNSlots; decltype(mNSlots) len;
} ar >> len;
for (auto i = len; i; i--) {
template<class Saver> auto node = newNodeNotConstructed();
void write(Saver& file) { ar >> node->val;
file.write(mNEntries); ar >> node->key;
for (auto item : *this) { put(node);
file.write(item->val); }
file.write(item->key); }
}
} ~Map() {
for (ualni i = 0; i < mNSlots; i++) {
template<class Loader> if (mTable[i] && !isDeletedNode(mTable[i])) {
void read(Loader& file) { deleteNode(mTable[i]);
removeAll(); }
ualni len; }
file.read(len); deleteTable(mTable);
for (auto i = len; i; i--) { }
auto node = newNodeNotConstructed(); };
file.read(node->val);
file.read(node->key);
put(node);
}
}
~Map() { removeAll(); }
};
} }

View file

@ -1,373 +1,384 @@
#pragma once #pragma once
#include "ContainersCommon.hpp" #include "ContainersCommon.hpp"
namespace tp { namespace tp {
template <typename NumericType> template <typename NumericType>
struct AvlNumericKey { struct AvlNumericKey {
NumericType val; NumericType val;
AvlNumericKey() = default; AvlNumericKey() = default;
AvlNumericKey(NumericType val) : val(val) {} 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 descentRight(AvlNumericKey in) const { return in.val > val; }
inline bool exactNode(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 getFindKey(/**/) const { return *this; }
inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; } inline AvlNumericKey keyInRightSubtree(AvlNumericKey in) const { return in; }
inline AvlNumericKey keyInLeftSubtree(AvlNumericKey in) const { return in; }
inline void updateTreeCacheCallBack() {}
}; template <typename NodeType>
inline void updateTreeCacheCallBack(const NodeType&) {}
template <typename Key, typename Data, class Allocator = DefaultAllocator> };
class AvlTree {
typedef SelCopyArg<Key> KeyArg; template <typename Key, typename Data, class Allocator = DefaultAllocator>
typedef SelCopyArg<Data> DataArg; class AvlTree {
typedef SelectValueOrReference<Key> KeyArg;
public: typedef SelectValueOrReference<Data> DataArg;
class Node {
friend AvlTree; public:
class Node {
private: friend AvlTree;
Node(KeyArg aKey, DataArg aData) : key(aKey), data(aData) {}
private:
public: Node(KeyArg aKey, DataArg aData) :
Data data; key(aKey),
Key key; data(aData) {}
private: public:
Node* mLeft = nullptr; Data data;
Node* mRight = nullptr; Key key;
Node* mParent = nullptr;
ualni mHeight = 0; public:
Node* mLeft = nullptr;
private: Node* mRight = nullptr;
inline bool descentRight(KeyArg aKey) const { return key.descentRight(aKey); } Node* mParent = nullptr;
inline bool descentLeft(KeyArg aKey) const { return key.descentLeft(aKey); } ualni mHeight = 0;
inline bool exactNode(KeyArg aKey) const { return key.exactNode(aKey); }
private:
inline KeyArg getFindKey(const Node* node = nullptr) const { return key.getFindKey(/*node*/); } inline bool descentRight(KeyArg aKey) const { return key.descentRight(aKey); }
inline KeyArg keyInRightSubtree(KeyArg aKey) const { return key.keyInRightSubtree(aKey); } inline bool descentLeft(KeyArg aKey) const { return key.descentLeft(aKey); }
inline KeyArg keyInLeftSubtree(KeyArg aKey) const { return key.keyInLeftSubtree(aKey); } inline bool exactNode(KeyArg aKey) const { return key.exactNode(aKey); }
inline void updateTreeCacheCallBack() { key.updateTreeCacheCallBack(); } 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); }
private:
Node* mRoot = nullptr; inline void updateTreeCacheCallBack() { key.updateTreeCacheCallBack(*this); }
ualni mSize = 0; };
Allocator mAlloc;
public:
private: AvlTree() {}
~AvlTree() { removeAll(); }
inline void deleteNode(Node* node) {
node->~Node(); [[nodiscard]] ualni size() const { return mSize; }
mAlloc.deallocate(node);
} Node* head() const { return this->mRoot; }
inline Node* newNode(KeyArg key, DataArg data) { void insert(KeyArg key, DataArg data) {
return new (mAlloc.allocate(sizeof(Node))) Node(key, data); mRoot = insertUtil(mRoot, key, data);
} mRoot->mParent = nullptr;
}
inline void injectNodeInstead(Node* place, Node* inject) {
// TODO : swap instead of copy void remove(KeyArg key) {
place->data = inject->data; mRoot = removeUtil(mRoot, key);
place->key = inject->key; if (mRoot) mRoot->mParent = nullptr;
} }
inline alni getNodeHeight(const Node* node) const { Node* maxNode(Node* head) const {
return node ? node->mHeight : -1; if (!head) return nullptr;
} while (head->mRight != nullptr) {
head = head->mRight;
// returns new head }
Node* rotateLeft(Node* pivot) { return head;
DEBUG_ASSERT(pivot); }
Node* const head = pivot; Node* minNode(Node* head) const {
Node* const right = pivot->mRight; if (!head) return nullptr;
Node* const right_left = right->mLeft; while (head->mLeft != nullptr) {
Node* const parent = pivot->mParent; head = head->mLeft;
}
// parents return head;
if (right_left) right_left->mParent = head; }
head->mParent = right;
right->mParent = parent; Node* find(KeyArg key) const {
Node* iter = mRoot;
// children while (true) {
head->mRight = right_left; if (!iter) return nullptr;
right->mLeft = head; if (iter->exactNode(key)) return iter;
if (iter->descentLeft(key)) {
// heights key = iter->keyInLeftSubtree(key);
head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); iter = iter->mLeft;
right->mHeight = 1 + max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight)); } else {
key = iter->keyInRightSubtree(key);
// cache iter = iter->mRight;
head->updateTreeCacheCallBack(); }
right->updateTreeCacheCallBack(); }
}
return right;
} Node* findLessOrEq(KeyArg key) const {
Node* iter = mRoot;
Node* rotateRight(Node* pivot) { while (true) {
DEBUG_ASSERT(pivot); if (!iter) return nullptr;
if (iter->exactNode(key)) return iter;
Node* const head = pivot; if (iter->descentLeft(key)) {
Node* const left = pivot->mLeft; if (iter->mLeft) {
Node* const left_right = left->mRight; key = iter->keyInLeftSubtree(key);
Node* const parent = pivot->mParent; iter = iter->mLeft;
} else {
// parents return iter;
if (left_right) left_right->mParent = head; }
head->mParent = left; } else {
left->mParent = parent; if (iter->mRight) {
key = iter->keyInRightSubtree(key);
// children iter = iter->mRight;
head->mLeft = left_right; } else {
left->mRight = head; return iter;
}
// heights }
head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight)); }
left->mHeight = 1 + max(getNodeHeight(left->mLeft), getNodeHeight(left->mRight)); }
// cache // returns first invalid node
head->updateTreeCacheCallBack(); const Node* findInvalidNode(const Node* head) const {
left->updateTreeCacheCallBack(); if (head == nullptr) return nullptr;
return left; if (head->mLeft) {
} // TODO: incomplete test
if (!head->descentLeft(head->mLeft->getFindKey(head))) return head;
// recursively returns valid isLeft or isRight child or root if (head->mLeft->mParent != head) return head;
Node* insertUtil(Node* head, KeyArg key, DataArg data) { if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) return head;
}
Node* insertedNode;
if (head->mRight) {
if (head == nullptr) { if (!head->descentRight(head->mRight->getFindKey(head))) return head;
mSize++; if (head->mRight->mParent != head) return head;
Node* out = newNode(key, data); if (!head->mLeft && head->mRight->mHeight != head->mHeight - 1) return head;
out->updateTreeCacheCallBack(); }
return out;
} if (head->mLeft && head->mRight) {
else if (head->exactNode(key)) { if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) return head;
return head; }
}
else if (head->descentRight(key)) { int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
insertedNode = insertUtil(head->mRight, head->keyInRightSubtree(key), data);
head->mRight = insertedNode; if (balance > 1 || balance < -1) return head;
insertedNode->mParent = head;
} const Node* ret = findInvalidNode(head->mRight);
else {
insertedNode = insertUtil(head->mLeft, head->keyInLeftSubtree(key), data); if (ret) return ret;
head->mLeft = insertedNode;
insertedNode->mParent = head; return findInvalidNode(head->mLeft);
} }
// update height bool isValid() { return findInvalidNode(head()) == nullptr; }
head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
template <typename tFunctor>
alni balance = alni(getNodeHeight(head->mRight) - getNodeHeight(head->mLeft)); void traverse(Node* node, bool after, tFunctor functor) {
if (!after) functor(node);
if (balance > 1) { if (node->mLeft) traverse(node->mLeft, after, functor);
if (head->mRight->descentRight(head->keyInRightSubtree(key))) { if (node->mRight) traverse(node->mRight, after, functor);
return rotateLeft(head); if (after) functor(node);
} }
else {
head->mRight = rotateRight(head->mRight); void removeAll() {
return rotateLeft(head); if (!mRoot) return;
} removeUtil(mRoot);
} mRoot = nullptr;
else if (balance < -1) { mSize = 0;
if (head->mLeft->descentLeft(head->keyInLeftSubtree(key))) { }
return rotateRight(head);
} void removeUtil(Node* node) {
else { if (node->mLeft) removeUtil(node->mLeft);
head->mLeft = rotateLeft(head->mLeft); if (node->mRight) removeUtil(node->mRight);
return rotateRight(head); deleteNode(node);
} }
}
public:
head->updateTreeCacheCallBack(); template <class tArchiver>
void archiveWrite(tArchiver& file) const {
return head; FAIL("not implemented")
} }
Node* removeUtil(Node* head, KeyArg key) { template <class tArchiver>
if (head == nullptr) return head; void archiveRead(tArchiver&) {
FAIL("not implemented")
if (head->exactNode(key)) { }
if (head->mRight && head->mLeft) {
Node* min = minNode(head->mRight); private:
auto const& newKey = min->getFindKey(head->mRight); inline void deleteNode(Node* node) {
injectNodeInstead(head, min); node->~Node();
head->mRight = removeUtil(head->mRight, newKey); mAlloc.deallocate(node);
} }
else if (head->mRight) {
injectNodeInstead(head, head->mRight); inline Node* newNode(KeyArg key, DataArg data) { return new (mAlloc.allocate(sizeof(Node))) Node(key, data); }
deleteNode(head->mRight);
head->mRight = nullptr; inline void injectNodeInstead(Node* place, Node* inject) {
mSize--; // TODO : swap instead of copy
} place->data = inject->data;
else if (head->mLeft) { place->key = inject->key;
injectNodeInstead(head, head->mLeft); }
deleteNode(head->mLeft);
head->mLeft = nullptr; inline alni getNodeHeight(const Node* node) const { return node ? node->mHeight : -1; }
mSize--;
} // returns new head
else { Node* rotateLeft(Node* pivot) {
deleteNode(head); DEBUG_ASSERT(pivot);
mSize--;
head = nullptr; Node* const head = pivot;
} Node* const right = pivot->mRight;
} Node* const right_left = right->mLeft;
else if (head->descentRight(key)) { Node* const parent = pivot->mParent;
head->mRight = removeUtil(head->mRight, head->keyInRightSubtree(key));
} // parents
else if (head->descentLeft(key)) { if (right_left) right_left->mParent = head;
head->mLeft = removeUtil(head->mLeft, head->keyInLeftSubtree(key)); head->mParent = right;
} right->mParent = parent;
if (head == nullptr) return head; // children
head->mRight = right_left;
head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft)); right->mLeft = head;
alni balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
// heights
if (balance < -1) { head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight));
if (getNodeHeight(head->mLeft->mLeft) >= getNodeHeight(head->mLeft->mRight)) { right->mHeight = 1 + max(getNodeHeight(right->mLeft), getNodeHeight(right->mRight));
return rotateRight(head);
} // cache
else { head->updateTreeCacheCallBack();
head->mLeft = rotateLeft(head->mLeft); right->updateTreeCacheCallBack();
return rotateRight(head);
} return right;
} }
else if (balance > 1) {
if (getNodeHeight(head->mRight->mRight) >= getNodeHeight(head->mRight->mLeft)) { Node* rotateRight(Node* pivot) {
return rotateLeft(head); DEBUG_ASSERT(pivot);
}
else { Node* const head = pivot;
head->mRight = rotateRight(head->mRight); Node* const left = pivot->mLeft;
return rotateLeft(head); Node* const left_right = left->mRight;
} Node* const parent = pivot->mParent;
}
// parents
head->updateTreeCacheCallBack(); if (left_right) left_right->mParent = head;
head->mParent = left;
return head; left->mParent = parent;
}
// children
public: head->mLeft = left_right;
left->mRight = head;
AvlTree() {
MODULE_SANITY_CHECK(gModuleContainers) // heights
} head->mHeight = 1 + max(getNodeHeight(head->mLeft), getNodeHeight(head->mRight));
left->mHeight = 1 + max(getNodeHeight(left->mLeft), getNodeHeight(left->mRight));
[[nodiscard]] ualni size() const {
return mSize; // cache
} head->updateTreeCacheCallBack();
left->updateTreeCacheCallBack();
Node* head() const {
return this->mRoot; return left;
} }
void insert(KeyArg key, DataArg data) { // recursively returns valid isLeft or isRight child or root
mRoot = insertUtil(mRoot, key, data); Node* insertUtil(Node* head, KeyArg key, DataArg data) {
mRoot->mParent = nullptr;
} Node* insertedNode;
void remove(KeyArg key) { if (head == nullptr) {
mRoot = removeUtil(mRoot, key); mSize++;
if (mRoot) mRoot->mParent = nullptr; Node* out = newNode(key, data);
} out->updateTreeCacheCallBack();
return out;
Node* maxNode(Node* head) const { } else if (head->exactNode(key)) {
if (!head) return nullptr; return head;
while (head->mRight != nullptr) { } else if (head->descentRight(key)) {
head = head->mRight; insertedNode = insertUtil(head->mRight, head->keyInRightSubtree(key), data);
} head->mRight = insertedNode;
return head; insertedNode->mParent = head;
} } else {
insertedNode = insertUtil(head->mLeft, head->keyInLeftSubtree(key), data);
Node* minNode(Node* head) const { head->mLeft = insertedNode;
if (!head) return nullptr; insertedNode->mParent = head;
while (head->mLeft != nullptr) { }
head = head->mLeft;
} // update height
return head; head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
}
alni balance = alni(getNodeHeight(head->mRight) - getNodeHeight(head->mLeft));
Node* find(KeyArg key) const {
Node* iter = mRoot; if (balance > 1) {
while (true) { if (head->mRight->descentRight(head->keyInRightSubtree(key))) {
if (!iter) return nullptr; return rotateLeft(head);
if (iter->exactNode(key)) return iter; } else {
if (iter->descentLeft(key)) { head->mRight = rotateRight(head->mRight);
key = iter->keyInLeftSubtree(key); return rotateLeft(head);
iter = iter->mLeft; }
} else { } else if (balance < -1) {
key = iter->keyInRightSubtree(key); if (head->mLeft->descentLeft(head->keyInLeftSubtree(key))) {
iter = iter->mRight; return rotateRight(head);
} } else {
} head->mLeft = rotateLeft(head->mLeft);
} return rotateRight(head);
}
Node* findLessOrEq(KeyArg key) const { }
Node* iter = mRoot;
while (true) { head->updateTreeCacheCallBack();
if (!iter) return nullptr;
if (iter->exactNode(key)) return iter; return head;
if (iter->descentLeft(key)) { }
if (iter->mLeft) {
key = iter->keyInLeftSubtree(key); Node* removeUtil(Node* head, KeyArg key) {
iter = iter->mLeft; if (head == nullptr) return head;
} else {
return iter; if (head->exactNode(key)) {
} if (head->mRight && head->mLeft) {
} else { Node* min = minNode(head->mRight);
if (iter->mRight) { auto const& newKey = min->getFindKey(head->mRight);
key = iter->keyInRightSubtree(key); injectNodeInstead(head, min);
iter = iter->mRight; head->mRight = removeUtil(head->mRight, newKey);
} else { } else if (head->mRight) {
return iter; injectNodeInstead(head, head->mRight);
} deleteNode(head->mRight);
} head->mRight = nullptr;
} mSize--;
} } else if (head->mLeft) {
injectNodeInstead(head, head->mLeft);
// returns first invalid node deleteNode(head->mLeft);
const Node* findInvalidNode(const Node* head) const { head->mLeft = nullptr;
if (head == nullptr) return nullptr; mSize--;
} else {
if (head->mLeft) { deleteNode(head);
// TODO: incomplete test mSize--;
if (!head->descentLeft(head->mLeft->getFindKey(head))) return head; head = nullptr;
if (head->mLeft->mParent != head) return head; }
if (!head->mRight && head->mLeft->mHeight != head->mHeight - 1) return head; } else if (head->descentRight(key)) {
} head->mRight = removeUtil(head->mRight, head->keyInRightSubtree(key));
} else if (head->descentLeft(key)) {
if (head->mRight) { head->mLeft = removeUtil(head->mLeft, head->keyInLeftSubtree(key));
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 == nullptr) return head;
}
head->mHeight = 1 + max(getNodeHeight(head->mRight), getNodeHeight(head->mLeft));
if (head->mLeft && head->mRight) { alni balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft);
if (max(head->mLeft->mHeight, head->mRight->mHeight) != head->mHeight - 1) return head;
} if (balance < -1) {
if (getNodeHeight(head->mLeft->mLeft) >= getNodeHeight(head->mLeft->mRight)) {
int balance = getNodeHeight(head->mRight) - getNodeHeight(head->mLeft); return rotateRight(head);
} else {
if (balance > 1 || balance < -1) return head; head->mLeft = rotateLeft(head->mLeft);
return rotateRight(head);
const Node* ret = findInvalidNode(head->mRight); }
} else if (balance > 1) {
if (ret) return ret; if (getNodeHeight(head->mRight->mRight) >= getNodeHeight(head->mRight->mLeft)) {
return rotateLeft(head);
return findInvalidNode(head->mLeft); } else {
} head->mRight = rotateRight(head->mRight);
return rotateLeft(head);
bool isValid() { return findInvalidNode(head()) == nullptr; } }
}; }
head->updateTreeCacheCallBack();
return head;
}
private:
Node* mRoot = nullptr;
ualni mSize = 0;
Allocator mAlloc;
};
} }

View file

@ -0,0 +1,19 @@
#include "Buffer2D.hpp"
#include "Tests.hpp"
using namespace tp;
const ualni size = 1000;
SUITE(Buffer2D) {
TEST(Simple) {
Buffer2D<int, TestAllocator> buff;
buff.reserve({ 4, 4 });
buff.set({ 2, 2 }, 5);
CHECK(buff.get({ 2, 2 }) == 5);
}
TEST(NO_TESTS) {
CHECK(false);
}
}

View file

@ -1,37 +1,33 @@
#include "Tests.hpp"
#include "Buffer.hpp" #include "Buffer.hpp"
#include "Tests.hpp"
#include "Testing.hpp"
#include <iostream>
using namespace tp; using namespace tp;
const ualni size = 1000; const ualni size = 1000;
TEST_DEF_STATIC(Simple1) { SUITE(Buffer) {
Buffer<TestClass> buff; TEST(Simple1) {
TEST(buff.size() == 0); Buffer<TestClass, TestAllocator> buff;
for (auto i : Range(size * 10)) { CHECK(buff.size() == 0);
buff.append(TestClass(i)); for (auto i : Range(size * 10)) {
} buff.append(TestClass(i));
TEST(buff.size() == size * 10); }
while (buff.size()) buff.pop(); CHECK(buff.size() == size * 10);
TEST(buff.size() == 0); while (buff.size())
} buff.pop();
CHECK(buff.size() == 0);
}
TEST_DEF_STATIC(Simple2) { TEST(Simple2) {
Buffer<TestClass> buff(size); Buffer<TestClass, TestAllocator> buff(size);
TEST(buff.size() == 0); CHECK(buff.size() == size);
for (auto i : Range(size * 10)) buff.append(TestClass(i)); for (auto i : Range(size * 10))
TEST(buff.size() == size * 10); buff.append(TestClass(i));
while (buff.size()) buff.pop(); CHECK(buff.size() == size + size * 10);
TEST(buff.size() == 0); while (buff.size())
} buff.pop();
CHECK(buff.size() == 0);
}
TEST_DEF(Buffer) { TEST(NO_TEST) { CHECK(false); }
testSimple1();
testSimple2();
} }

View file

@ -0,0 +1,274 @@
#include "IntervalTree.hpp"
#include "Tests.hpp"
#include "Buffer.hpp"
#include <cstdio>
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<ualni, ualni> 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<Interval> pool;
IntervalTree<halnf, ualni> intervalTree;
Buffer<Interval> testIntervals;
auto test = [&]() {
for (auto testInterval : testIntervals) {
Buffer<ualni> correct;
Buffer<ualni> 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<ualni>(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<ualni>(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<halnf, ualni> intervalTree;
Buffer<Interval> testIntervals;
auto WOBBLE = SPAN * 0;
for (auto i : Range<ualni>(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<ualni>(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<Stat> 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<ualni, ualni> intervals;
struct QueryResult {
ualni numFound = 0;
ualni lastDataFound = 0;
[[nodiscard]] bool isSingleData(ualni aData) const { return numFound == 1 && lastDataFound == aData; }
[[nodiscard]] bool notFound() const { return numFound == 0; }
[[nodiscard]] bool found(ualni num) const { return numFound == num; }
};
auto makeQuery = [&](ualni aStart, ualni aEnd) {
QueryResult out;
intervals.forEachIntersection(aStart, aEnd, [&](alni start, ualni end, ualni data) {
out.numFound++;
out.lastDataFound = data;
});
return out;
};
intervals.insert({ 2, 5 }, 1);
intervals.insert({ 12, 15 }, 2);
intervals.insert({ 22, 25 }, 3);
CHECK(makeQuery(1, 6).isSingleData(1));
CHECK(makeQuery(1, 3).isSingleData(1));
CHECK(makeQuery(3, 6).isSingleData(1));
CHECK(makeQuery(0, 1).notFound());
CHECK(makeQuery(7, 8).notFound());
CHECK(makeQuery(3, 4).isSingleData(1));
CHECK(makeQuery(13, 14).isSingleData(2));
CHECK(makeQuery(1, 35).found(3));
CHECK(makeQuery(11, 35).found(2));
// check opened
CHECK(makeQuery(5, 12).found(2));
CHECK(makeQuery(15, 22).found(2));
CHECK(makeQuery(1, 2).isSingleData(1));
CHECK(makeQuery(25, 35).isSingleData(3));
intervals.removeAll();
intervals.insert({ 0, 3 }, 1);
intervals.insert({ 0, 13 }, 2);
CHECK(makeQuery(0, 1).found(2));
}
}

View file

@ -1,88 +1,82 @@
#include "Archiver.hpp"
#include "Tests.hpp" #include "Tests.hpp"
#include "Testing.hpp" #include "List.hpp"
#include <iostream>
using namespace tp; using namespace tp;
TEST_DEF_STATIC(SimpleReference) { SUITE(DoubleLinkedList) {
tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
list.pushBack(TestClass(5)); TEST(SimpleReference) {
list.pushFront(TestClass(0)); tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
ualni i = -1; list.pushBack(TestClass(5));
for (auto iter : list) { list.pushFront(TestClass(0));
i++;
TEST_EQUAL(iter->getVal(), i); ualni i = -1;
for (auto iter : list) {
i++;
CHECK_EQUAL(iter->getVal(), i);
}
CHECK(i == 5);
list.removeAll();
} }
TEST(i == 5); TEST(SimplePointer) {
tp::List<TestClass*, TestAllocator> list = {
new TestClass(1), new TestClass(2), new TestClass(3), new TestClass(4)
};
list.removeAll(); list.pushBack(new TestClass(5));
TEST(list.getAllocator().getAllocationsCount() == 0); list.pushFront(new TestClass(0));
}
TEST_DEF_STATIC(SimplePointer) { ualni i = -1;
tp::List<TestClass*, TestAllocator> list = { new TestClass(1), new TestClass(2), new TestClass(3), new TestClass(4) }; for (auto iter : list) {
i++;
CHECK_EQUAL(iter->getVal(), i);
}
list.pushBack(new TestClass(5)); CHECK(i == 5);
list.pushFront(new TestClass(0));
ualni i = -1; for (auto iter : list) {
for (auto iter : list) { delete iter.data();
i++; }
TEST_EQUAL(iter->getVal(), i);
list.removeAll();
} }
TEST(i == 5); TEST(Copy) {
tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
tp::List<TestClass, TestAllocator> list2 = list;
list.removeAll(); CHECK(list == list2);
TEST(list.getAllocator().getAllocationsCount() == 0); list.removeAll();
} list2.removeAll();
TEST_DEF_STATIC(Copy) {
tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
tp::List<TestClass, TestAllocator> list2 = list;
TEST_EQUAL(list, list2);
list.removeAll();
list2.removeAll();
TEST(list.getAllocator().getAllocationsCount() == 0);
TEST(list2.getAllocator().getAllocationsCount() == 0);
}
TEST_DEF_STATIC(SaveLoad) {
tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
TestFile file;
list.write(file);
list.removeAll();
file.setAddress(0);
list.read(file);
ualni i = 0;
for (auto iter : list) {
i++;
TEST_EQUAL(iter->getVal(), i);
} }
TEST(i == 4);
list.removeAll(); TEST(Serialization) {
tp::List<TestClass, TestAllocator> list = { TestClass(1), TestClass(2), TestClass(3), TestClass(4) };
TEST(list.getAllocator().getAllocationsCount() == 0); ArchiverExample<1024, false> write;
} ArchiverExample<1024, true> read;
TEST_DEF(List) { write % list;
testSimplePointer();
testSimpleReference(); list.removeAll();
testSaveLoad();
memCopy(read.mBuff, write.mBuff, sizeof(write.mBuff));
read % list;
ualni i = 0;
for (auto iter : list) {
i++;
CHECK(iter->getVal() == i);
}
CHECK(i == 4);
list.removeAll();
}
} }

View file

@ -1,137 +1,124 @@
#include "Archiver.hpp"
#include "Tests.hpp"
#include "Testing.hpp"
#include "Map.hpp" #include "Map.hpp"
#include "Tests.hpp"
#include <iostream>
using namespace tp; using namespace tp;
TEST_DEF_STATIC(SimpleReference) { SUITE(HashTable) {
tp::Map<tp::ualni, TestClass, TestAllocator> map; TEST(SimpleReference) {
tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(1000, 100000)) { for (auto i : Range(1000, 100000)) {
map.put(i, TestClass(i)); map.put(i, TestClass(i));
}
for (auto i : Range(1000, 100000)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i).getVal(), (tp::ualni) i);
}
for (auto i : Range(1000, 100000)) {
map.put(i, TestClass(i));
}
for (auto i : Range(1000, 2000)) {
CHECK(map.presents(i));
map.remove(i);
CHECK(!map.presents(i));
}
for (auto i : Range(2000, 100000)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i).getVal(), (tp::ualni) i);
}
for (auto i : map) {
i->val.setVal(3);
}
map.removeAll();
} }
for (auto i : Range(1000, 100000)) { TEST(SimplePointer) {
TEST(map.presents(i)); tp::Map<tp::ualni, TestClass*, TestAllocator> map;
TEST_EQUAL(map.get(i).getVal(), i);
for (auto i : Range(1000)) {
map.put(i, new TestClass(i));
}
for (auto i : Range(1000)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i)->getVal(), (tp::ualni) i);
}
for (auto i : Range(1000)) {
auto del = map.get(i);
map.put(i, new TestClass(i));
delete del;
}
for (auto i : Range(900, 1000)) {
CHECK(map.presents(i));
delete map.get(i);
map.remove(i);
CHECK(!map.presents(i));
}
for (auto i : Range(900)) {
CHECK(map.presents(i));
CHECK_EQUAL((tp::ualni) map.get(i)->getVal(), (tp::ualni) i);
}
for (auto i : map) {
i->val->setVal(3);
delete i->val;
}
map.removeAll();
} }
for (auto i : Range(1000, 100000)) { TEST(Copy) {
map.put(i, TestClass(i)); tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) {
map.put(i, TestClass(i));
}
tp::Map<tp::ualni, TestClass, TestAllocator> map2 = map;
CHECK(map == map2);
map.removeAll();
map2.removeAll();
} }
for (auto i : Range(1000, 2000)) { TEST(SaveLoad) {
TEST(map.presents(i)); tp::Map<tp::ualni, TestClass, TestAllocator> map;
map.remove(i);
TEST(!map.presents(i)); for (auto i : Range(10)) {
map.put(i, TestClass(i));
}
ArchiverExample<1024, false> write;
ArchiverExample<1024, true> read;
write % map;
map.removeAll();
CHECK(map.size() == 0);
memCopy(read.mBuff, write.mBuff, sizeof(write.mBuff));
read % map;
CHECK(map.size() == 10);
for (auto i : Range(10)) {
CHECK(map.presents(i));
CHECK_EQUAL(map.get(i).getVal(), i);
}
map.removeAll();
} }
for (auto i : Range(2000, 100000)) {
TEST(map.presents(i));
TEST_EQUAL(map.get(i).getVal(), i);
}
for (auto i : map) {
i->val.setVal(3);
}
map.removeAll();
TEST(map.getAllocator().getAllocationsCount() == 1);
}
TEST_DEF_STATIC(SimplePointer) {
tp::Map<tp::ualni, TestClass*, TestAllocator> map;
for (auto i : Range(1000)) {
map.put(i, new TestClass(i));
}
for (auto i : Range(1000)) {
TEST(map.presents(i));
TEST_EQUAL(map.get(i)->getVal(), i);
}
for (auto i : Range(1000)) {
map.put(i, new TestClass(i));
}
for (auto i : Range(900, 1000)) {
TEST(map.presents(i));
map.remove(i);
TEST(!map.presents(i));
}
for (auto i : Range(900)) {
TEST(map.presents(i));
TEST_EQUAL(map.get(i)->getVal(), i);
}
for (auto i : map) {
i->val->setVal(3);
delete i->val;
}
map.removeAll();
TEST(map.getAllocator().getAllocationsCount() == 1);
}
TEST_DEF_STATIC(Copy) {
tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) {
map.put(i, TestClass(i));
}
tp::Map<tp::ualni, TestClass, TestAllocator> map2 = map;
TEST_EQUAL(map, map2);
map.removeAll();
map2.removeAll();
TEST(map.getAllocator().getAllocationsCount() == 1);
TEST(map2.getAllocator().getAllocationsCount() == 1);
}
TEST_DEF_STATIC(SaveLoad) {
tp::Map<tp::ualni, TestClass, TestAllocator> map;
for (auto i : Range(10)) {
map.put(i, TestClass(i));
}
TestFile file;
map.write(file);
map.removeAll();
TEST(map.getAllocator().getAllocationsCount() == 1);
file.setAddress(0);
map.read(file);
TEST(map.getAllocator().getAllocationsCount() == 11);
for (auto i : Range(10)) {
TEST(map.presents(i));
TEST_EQUAL(map.get(i).getVal(), i);
}
map.removeAll();
TEST(map.getAllocator().getAllocationsCount() == 1);
}
TEST_DEF(Map) {
testSimplePointer();
testSimpleReference();
testSaveLoad();
} }

View file

@ -1,42 +1,24 @@
#include "Tests.hpp" #include "Tests.hpp"
#include "UnitTest++/UnitTest++.h"
#include "Testing.hpp"
void* TestAllocator::allocate(tp::ualni size) {
#include <cstdlib> count++;
return malloc(size);
static bool init(const tp::ModuleManifest* self) { }
tp::gTesting.setRootName(self->getName());
return true; void TestAllocator::deallocate(void* p) {
} if (p) {
free(p);
void* TestAllocator::allocate(tp::ualni size) { count--;
nAllocations++; }
return malloc(size); }
}
void TestAllocator::deallocate(void* p) { TestAllocator::~TestAllocator() {
nAllocations--; ASSERT(!count);
free(p); }
}
int main() {
tp::ualni TestAllocator::getAllocationsCount() const { return UnitTest::RunAllTests();
return nAllocations; }
}
int main() {
tp::ModuleManifest* deps[] = { &tp::gModuleContainers, &tp::gModuleUtils, nullptr };
tp::ModuleManifest testModule("ContainersTest", init, nullptr, deps);
if (!testModule.initialize()) {
return 1;
}
testList();
testMap();
testAvl();
testBuffer();
testModule.deinitialize();
}

View file

@ -1,74 +1,43 @@
#pragma once #pragma once
#include "UnitTest++/UnitTest++.h"
#include "Utils.hpp" #include "Utils.hpp"
// counting number of allocations and deallocations
struct TestAllocator {
TestAllocator() { count = 0; };
tp::ualni count = 0;
void* allocate(tp::ualni size);
void deallocate(void*);
~TestAllocator();
};
class TestClass { class TestClass {
tp::ualni val2 = 0; tp::ualni val2 = 0;
tp::ualni val1; tp::ualni val1;
public: public:
TestClass() { val1 = 0; }
explicit TestClass(tp::ualni val) : val1(val) {} explicit TestClass(tp::ualni val) :
val1(val) {}
template<class Saver> template <class Saver>
void write(Saver& file) const { void write(Saver& file) const {
file.write(val1); file.write(val1);
} }
template<class Loader> template <class Loader>
void read(Loader& file) { void read(Loader& file) {
file.read(val1); file.read(val1);
} }
[[nodiscard]] bool operator==(const TestClass& in) const { [[nodiscard]] bool operator==(const TestClass& in) const { return in.val1 == val1; }
return in.val1 == val1;
}
[[nodiscard]] tp::ualni getVal() const { return val1; } [[nodiscard]] tp::ualni getVal() const { return val1; }
void setVal(tp::ualni val) { val1 = val; } void setVal(tp::ualni val) { val1 = val; }
}; };
class TestAllocator {
tp::ualni nAllocations = 0;
public:
TestAllocator() = default;
void* allocate(tp::ualni size);
void deallocate(void* p);
[[nodiscard]] tp::ualni getAllocationsCount() const;
};
class TestFile {
tp::ualni mem[1024] = { 0 };
tp::ualni address = 0;
public:
TestFile() = default;
template<typename Type>
void write(const Type& val) {
val.write(*this);
}
template<>
void write<tp::ualni>(const tp::ualni& val) {
mem[address] = val;
address++;
}
void setAddress(tp::ualni addr) { address = addr; }
template<typename Type>
void read(Type& val) {
val.read(*this);
}
template<>
void read<tp::ualni>(tp::ualni& val) {
val = mem[address];
address++;
}
};
void testList();
void testMap();
void testAvl();
void testBuffer();

View file

@ -1,131 +1,124 @@
#include "Tests.hpp" #include "Tests.hpp"
#include "Tree.hpp" #include "Tree.hpp"
#include "Testing.hpp"
#include <iostream>
using namespace tp; using namespace tp;
TEST_DEF_STATIC(Simple) { SUITE(AvlTree) {
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree; TEST(Simple) {
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
TEST(tree.size() == 0); CHECK(tree.size() == 0);
TEST(tree.head() == nullptr); CHECK(tree.head() == nullptr);
tree.insert(6, TestClass(6)); tree.insert(6, TestClass(6));
TEST(tree.isValid()); CHECK(tree.isValid());
TEST(tree.size() == 1); CHECK(tree.size() == 1);
TEST(tree.head()->data == TestClass(6)); CHECK(tree.head()->data == TestClass(6));
tree.remove(6); tree.remove(6);
TEST(tree.isValid()); CHECK(tree.isValid());
TEST(tree.size() == 0); CHECK(tree.size() == 0);
TEST(tree.head() == nullptr); CHECK(tree.head() == nullptr);
}
TEST_DEF_STATIC(Persistance) {
AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
const auto size = 1000;
struct Item {
Item() : data(0) {}
bool presents = false;
TestClass data;
};
Item buff[size];
for (auto i : Range(size)) {
buff[i].data.setVal(i);
} }
// random load TEST(Persistance) {
ualni loadSize = 0; AvlTree<AvlNumericKey<alni>, TestClass, TestAllocator> tree;
while (loadSize < size / 2) {
ualni idx = rand() % (size - 1);
DEBUG_ASSERT(idx < size)
if (!buff[idx].presents) {
tree.insert((alni) buff[idx].data.getVal(), buff[idx].data);
loadSize++;
buff[idx].presents = true;
TEST(tree.isValid()); const auto size = 1000;
TEST(tree.size() == loadSize);
struct Item {
Item() :
data(0) {}
bool presents = false;
TestClass data;
};
Item buff[size];
for (auto i : Range(size)) {
buff[i].data.setVal(i);
} }
}
for (auto& item : buff) { // random load
if (item.presents) continue; ualni loadSize = 0;
tree.insert((alni) item.data.getVal(), item.data); while (loadSize < size / 2) {
loadSize++; auto idx = ualni(randomFloat() * (size - 1));
item.presents = true; DEBUG_ASSERT(idx < size)
if (!buff[idx].presents) {
tree.insert((alni) buff[idx].data.getVal(), buff[idx].data);
loadSize++;
buff[idx].presents = true;
TEST(tree.isValid()); CHECK(tree.isValid());
TEST(tree.size() == loadSize); CHECK(tree.size() == loadSize);
}
TEST(tree.size() == size);
TEST(tree.maxNode(tree.head())->data.getVal() == size - 1);
TEST(tree.minNode(tree.head())->data.getVal() == 0);
// find
for (auto item : buff) {
auto node = tree.find((alni) item.data.getVal());
TEST(node);
if (!node) continue;
TEST(node->data.getVal() == item.data.getVal());
}
TEST(!tree.find(size + 1));
TEST(!tree.find(-1));
// random unload
ualni unloadSize = 0;
while (unloadSize < size / 2) {
ualni idx = rand() % (size - 1);
if (buff[idx].presents) {
tree.remove((alni) buff[idx].data.getVal());
unloadSize++;
buff[idx].presents = false;
// find
for (auto item : buff) {
if (!item.presents) continue;
auto node = tree.find((alni) item.data.getVal());
TEST(node);
if (!node) continue;
TEST(node->data.getVal() == item.data.getVal());
} }
TEST(tree.isValid());
TEST(tree.size() == size - unloadSize);
} }
for (auto& item : buff) {
if (item.presents) continue;
tree.insert((alni) item.data.getVal(), item.data);
loadSize++;
item.presents = true;
CHECK(tree.isValid());
CHECK(tree.size() == loadSize);
}
CHECK(tree.size() == size);
CHECK(tree.maxNode(tree.head())->data.getVal() == size - 1);
CHECK(tree.minNode(tree.head())->data.getVal() == 0);
// find
for (auto item : buff) {
auto node = tree.find((alni) item.data.getVal());
CHECK(node);
if (!node) continue;
CHECK(node->data.getVal() == item.data.getVal());
}
CHECK(!tree.find(size + 1));
CHECK(!tree.find(-1));
// random unload
ualni unloadSize = 0;
while (unloadSize < size / 2) {
auto idx = ualni(randomFloat() * (size - 1));
if (buff[idx].presents) {
tree.remove((alni) buff[idx].data.getVal());
unloadSize++;
buff[idx].presents = false;
// find
for (auto item : buff) {
if (!item.presents) continue;
auto node = tree.find((alni) item.data.getVal());
CHECK(node);
if (!node) continue;
CHECK(node->data.getVal() == item.data.getVal());
}
CHECK(tree.isValid());
CHECK(tree.size() == size - unloadSize);
}
}
for (auto& item : buff) {
if (item.presents) {
tree.remove((alni) item.data.getVal());
unloadSize++;
item.presents = false;
CHECK(tree.isValid());
CHECK(tree.size() == size - unloadSize);
}
}
CHECK(tree.size() == 0);
CHECK(tree.head() == nullptr);
CHECK(tree.maxNode(tree.head()) == nullptr);
CHECK(tree.minNode(tree.head()) == nullptr);
} }
for (auto& item : buff) {
if (item.presents) {
tree.remove((alni) item.data.getVal());
unloadSize++;
item.presents = false;
TEST(tree.isValid());
TEST(tree.size() == size - unloadSize);
}
}
TEST(tree.size() == 0);
TEST(tree.head() == nullptr);
TEST(tree.maxNode(tree.head()) == nullptr);
TEST(tree.minNode(tree.head()) == nullptr);
}
TEST_DEF(Avl) {
testSimple();
testPersistance();
} }

View file

@ -0,0 +1,29 @@
project(DataAnalysis)
### ---------------------- Externals --------------------- ###
set(BINDINGS_INCLUDE ${EXTERNALS}/glfw/include ${EXTERNALS}/glew/include)
set(BINDINGS_LIBS glfw glew_s Imgui Nanovg)
### ---------------------- Static Library --------------------- ###
file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp" "./applications/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
target_link_libraries(${PROJECT_NAME} PUBLIC Math Containers Allocators)
### -------------------------- Applications -------------------------- ###
add_executable(NumRecTrain applications/NumRecTraining.cpp)
target_link_libraries(NumRecTrain ${PROJECT_NAME} Connection ImageIO)
file(COPY "applications/rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
add_executable(NumRecApp applications/NumRecApp.cpp)
target_link_libraries(NumRecApp ${PROJECT_NAME} Connection ImageIO)
### -------------------------- Tests -------------------------- ###
file(GLOB TEST_SOURCES "./tests/*.cpp" "./tests/*/*.cpp")
add_executable(Test${PROJECT_NAME} ${TEST_SOURCES})
target_include_directories(Test${PROJECT_NAME} PUBLIC ./applications/)
target_link_libraries(Test${PROJECT_NAME} ${PROJECT_NAME} UnitTest++)
add_test(NAME Test${PROJECT_NAME} COMMAND Test${PROJECT_NAME})

View file

@ -0,0 +1,68 @@
#include "FCNN.hpp"
#include "LocalConnection.hpp"
// #include "NewPlacement.hpp"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "stb_image_write.h"
using namespace tp;
void loadImage(Buffer<halnf>& output, const char* name) {
int x, y, channels_in_file;
unsigned char* loadedImage = stbi_load(name, &x, &y, &channels_in_file, 4);
if (!loadedImage) return;
output.reserve(x * y);
for (auto i : Range(output.size())) {
output[i] = loadedImage[i * 4] / 255.f;
}
stbi_image_free(loadedImage);
}
void loadNN(FCNN& nn) {
ArchiverLocalConnection<true> archiver;
archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(true));
if (archiver.connection.getConnectionStatus().isOpened()) {
archiver % nn;
} else {
Buffer<halni> layers = { 784, 10 };
nn.initializeRandom(layers);
}
}
void executeCmd(const char* imageName) {
FCNN nn;
Buffer<halnf> output(10);
Buffer<halnf> input;
loadNN(nn);
loadImage(input, imageName);
nn.evaluate(input, output);
printf("Output - ");
for (auto val : output) {
printf("%f ", val.data());
}
printf("\n\n");
}
int main(int argc, char** argv) {
const char* imageName = "digit.png";
if (argc == 2) {
imageName = argv[1];
}
executeCmd(imageName);
return 0;
}

View file

@ -0,0 +1,244 @@
#include "FCNN.hpp"
#include "LocalConnection.hpp"
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"
using namespace tp;
void writeImage(const Buffer<halnf>& image, const char* name) {
struct Tmp {
uint1 r, g, b, a;
};
Buffer<Tmp> converted;
converted.reserve(image.size());
for (auto i = 0; i < image.size(); i++) {
auto val = uint1(image[i] * 255);
converted[i] = { val, val, val, 255 };
}
stbi_write_png(name, 28, 28, 4, converted.getBuff(), 28 * 4);
}
struct Dataset {
ualni length = 0;
std::pair<ualni, ualni> imageSize = { 0, 0 };
Buffer<uint1> labels;
Buffer<Buffer<uint1>> images;
};
bool loadDataset(Dataset& out, const std::string& location) {
LocalConnection dataset;
dataset.connect(LocalConnection::Location(location), LocalConnection::Type(true));
if (!dataset.getConnectionStatus().isOpened()) {
return false;
}
LocalConnection::Byte length;
dataset.readBytes(&length, 1);
LocalConnection::Byte sizeX;
dataset.readBytes(&sizeX, 1);
LocalConnection::Byte sizeY;
dataset.readBytes(&sizeY, 1);
out.length = ((ualni) length) * 1000;
out.imageSize = { sizeX, sizeY };
out.labels.reserve(out.length);
out.images.reserve(out.length);
for (auto i : Range(out.length)) {
auto& image = out.images[i];
image.reserve(sizeX * sizeY);
dataset.readBytes((LocalConnection::Byte*) image.getBuff(), sizeX * sizeY);
}
LocalConnection::Byte label;
dataset.readBytes((LocalConnection::Byte*) out.labels.getBuff(), out.length);
return true;
}
struct NumberRec {
NumberRec() {
// try to load wb file
{
ArchiverLocalConnection<true> archiver;
archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(true));
if (archiver.connection.getConnectionStatus().isOpened()) {
archiver % nn;
} else {
Buffer<halni> layers = { 784, 10 };
nn.initializeRandom(layers);
}
}
Dataset dataset;
if (!loadDataset(dataset, "rsc/mnist")) {
printf("Cant Load Mnist Dataset\n");
return;
}
mTestcases.reserve(dataset.images.size());
for (auto i : Range(dataset.images.size())) {
auto& image = dataset.images[i];
auto label = dataset.labels[i];
auto& testcase = mTestcases[i];
testcase.output.reserve(10);
for (auto dig : Range(10)) {
testcase.output[dig] = label == dig ? 1 : 0;
}
testcase.input.reserve(image.size());
for (auto pxl : Range(image.size())) {
testcase.input[pxl] = (halnf) image[pxl] / 255.f;
}
}
output.reserve(10);
writeImage(mTestcases.first().input, "tmp1.png");
writeImage(mTestcases.last().input, "tmp2.png");
}
~NumberRec() {
// save aas wb file
{
ArchiverLocalConnection<false> archiver;
archiver.connection.connect(LocalConnection::Location("NumRec.wb"), LocalConnection::Type(false));
if (archiver.connection.getConnectionStatus().isOpened()) {
archiver % nn;
}
}
}
halnf eval(ualni idx) {
nn.evaluate(mTestcases[idx].input, output);
return nn.calcCost(mTestcases[idx].output);
}
void applyGrad(ualni idx) {
nn.calcGrad(mTestcases[idx].output);
nn.applyGrad(step);
}
static halni getMaxIdx(const Buffer<halnf>& in) {
halni out = 0;
for (auto i : Range(in.size())) {
if (in[i] > in[out]) {
out = i;
}
}
return out;
}
bool testIncorrect(ualni idx) {
nn.evaluate(mTestcases[idx].input, output);
return getMaxIdx(mTestcases[idx].output) != getMaxIdx(output);
}
void debLog(halni idx) {
printf("\n Got %i - ", getMaxIdx(output));
for (auto val : output) {
printf("%f ", val.data());
}
printf("\n Expected %i - ", getMaxIdx(mTestcases[idx].output));
for (auto val : mTestcases[idx].output) {
printf("%f ", val.data());
}
printf("\n\n");
}
void displayImage(ualni idx) {
auto& testcase = mTestcases[idx];
printf("Image : %i\n", int(getMaxIdx(testcase.output)));
for (auto i : Range(28)) {
for (auto j : Range(28)) {
printf("%c", char(testcase.input[j * 28 + i] * 255));
}
printf("\n");
}
}
halnf test(const Range<halni>& range) {
halnf avgCost = 0;
for (auto i : range) {
avgCost += eval(i);
}
avgCost /= (halnf) range.idxDiff();
return avgCost;
}
void trainStep(const Range<halni>& range) {
nn.clearGrad();
for (auto i : range) {
nn.evaluate(mTestcases[i].input, output);
nn.calcGrad(mTestcases[i].output);
}
nn.applyGrad(step);
}
public:
struct Image {
Buffer<halnf> input;
Buffer<halnf> output;
};
public:
Buffer<Image> mTestcases;
FCNN nn;
Buffer<halnf> output;
halnf step = 1.f;
};
int main() {
NumberRec app;
auto numBatches = 10;
auto trainRange = Range(0, 50000);
auto testRange = Range(50000, 70000);
auto batchSize = trainRange.idxDiff() / numBatches;
for (auto epoch : Range(1)) {
printf("Epoch %i\n", epoch.index());
for (auto batchIdx : Range(trainRange.idxDiff() / batchSize)) {
printf(" - Batch :%i \n", batchIdx.index());
auto batchRange = Range(trainRange.idxBegin() + batchSize * batchIdx, trainRange.idxBegin() + batchSize * (batchIdx + 1));
app.trainStep(batchRange);
printf("Cost on batch data : %f\n", app.test(batchRange));
}
printf("Cost on test data : %f\n\n", app.test(testRange));
}
auto errors = 0;
for (auto i : testRange) {
if (app.testIncorrect(i)) {
errors++;
}
// app.debLog(i);
// app.displayImage(i);
}
printf("\n\nIncorrect - %i out of %i (%f)\n\n", errors, testRange.idxDiff(), (halnf) errors / (halnf) testRange.idxDiff());
}

Binary file not shown.

View file

@ -0,0 +1,9 @@
#include "DataAnalysisCommon.hpp"
#include "Allocators.hpp"
#include "ContainersCommon.hpp"
#include "MathCommon.hpp"
namespace tp {
static ModuleManifest* deps[] = { &gModuleAllocators, nullptr };
ModuleManifest gModuleDataAnalysis = ModuleManifest("DataAnalysis", nullptr, nullptr, deps);
}

View file

@ -0,0 +1,158 @@
// #include "NewPlacement.hpp"
#include "FCNN.hpp"
#include "Utils.hpp"
#include <cmath>
using namespace tp;
static halnf linearFun(halnf val) { return val; }
static halnf linearFunDerivative(halnf val) { return 1; }
static halnf sigmoid(double val) { return 1.0f / (1.0f + (halnf) exp(-val)); }
static halnf sigmoidDerivative(double val) {
halnf sigmoid_val = sigmoid(val);
return sigmoid_val * (1.0f - sigmoid_val);
}
static halnf relu(halnf val) { return val < 0 ? 0 : val; }
static halnf reluDerivative(halnf val) { return val < 0 ? 0 : 1; }
static halnf activationFunction(halnf val) { return sigmoid(val); }
static halnf activationFunctionDerivative(halnf val) { return sigmoidDerivative(val); }
FCNN::FCNN(const Buffer<halni>& description) { initializeRandom(description); }
void FCNN::initializeRandom(const Buffer<halni>& description) {
ASSERT(description.size() > 1);
mLayers.reserve(description.size());
for (auto i : Range<halni>(0, (halni) description.size())) {
mLayers[i].neurons.reserve(description[i]);
if (i == 0) {
continue;
}
for (auto neuron : mLayers[i].neurons) {
neuron->weights.reserve(description[i - 1]);
for (auto weight : neuron->weights) {
weight->val = (halnf) (randomFloat() - 0.5) * 2;
}
neuron->bias.val = (halnf) (randomFloat() - 0.5) * 2;
}
}
}
void FCNN::evaluate(const Buffer<halnf>& input, Buffer<halnf>& output) {
ASSERT(output.size() == mLayers.last().neurons.size() && input.size() == mLayers.first().neurons.size())
for (auto idx : Range(input.size())) {
mLayers.first().neurons[idx].activationValue = input[idx];
}
for (auto layerIdx : Range<halni>(1, (halni) mLayers.size())) {
auto& layer = mLayers[layerIdx];
auto& layerPrev = mLayers[layerIdx - 1];
for (auto neuron : layer.neurons) {
neuron->activationValue = 0;
for (auto connectionIdx : Range(neuron->weights.size())) {
neuron->activationValue += neuron->weights[connectionIdx].val * layerPrev.neurons[connectionIdx].activationValue;
}
neuron->activationValue += neuron->bias.val;
neuron->activationValueLinear = neuron->activationValue;
neuron->activationValue = activationFunction(neuron->activationValue);
}
}
for (auto idx : Range(output.size())) {
output[idx] = mLayers.last().neurons[idx].activationValue;
}
}
halnf FCNN::calcCost(const Buffer<halnf>& output) {
halnf out = 0;
for (auto neuronIdx : Range(mLayers.last().neurons.size())) {
out += pow(output[neuronIdx] - mLayers.last().neurons[neuronIdx].activationValue, 2);
}
return out;
}
void FCNN::clearGrad() {
for (auto layIdx : Range<halni>(1, (halni) mLayers.size())) {
auto& layer = mLayers[layIdx];
for (auto neuron : layer.neurons) {
neuron->bias.grad = 0;
for (auto weightIdx : Range(neuron->weights.size())) {
neuron->weights[weightIdx].grad = 0;
}
}
}
mAvgCount = 0;
}
void FCNN::calcGrad(const Buffer<halnf>& output) {
ASSERT(mLayers.last().neurons.size() == output.size())
auto& lastLayer = mLayers.last();
// calculate chaining cache value for each neuron in last layer
for (auto neuronIdx : Range(lastLayer.neurons.size())) {
auto& neuron = lastLayer.neurons[neuronIdx];
neuron.cache = 2 * (neuron.activationValue - output[neuronIdx]);
}
// calculate rest of the layers
for (auto layerIdx = mLayers.size() - 1; layerIdx > 0; layerIdx--) {
auto& currentLayer = mLayers[layerIdx];
auto& inputLayer = mLayers[layerIdx - 1];
for (auto currentNeuronIdx : Range(currentLayer.neurons.size())) {
auto& currentNeuron = currentLayer.neurons[currentNeuronIdx];
// calculate cache value (chaining)
if (layerIdx != mLayers.size() - 1) {
auto& userLayer = mLayers[layerIdx + 1];
currentNeuron.cache = 0;
for (auto userNeuron : userLayer.neurons) {
currentNeuron.cache += userNeuron->weights[currentNeuronIdx].val * userNeuron->cache;
}
currentNeuron.cache *= activationFunctionDerivative(currentNeuron.activationValueLinear);
}
// gradient for current neuron bias
currentNeuron.bias.grad += currentNeuron.cache;
// calculate gradient for weights of current neuron
for (auto weightIdx : Range(currentNeuron.weights.size())) {
currentNeuron.weights[weightIdx].grad += inputLayer.neurons[weightIdx].activationValue * currentNeuron.cache;
}
}
}
mAvgCount++;
}
void FCNN::applyGrad(halnf step) {
for (auto layIdx : Range<halni>(1, (halni) mLayers.size())) {
auto& layer = mLayers[layIdx];
for (auto neuron : layer.neurons) {
neuron->bias.val -= neuron->bias.grad / (halnf) mAvgCount * step;
for (auto weightIdx : Range(neuron->weights.size())) {
neuron->weights[weightIdx].val -= (neuron->weights[weightIdx].grad / (halnf) mAvgCount) * step;
}
}
}
mAvgCount = 0;
}

View file

@ -0,0 +1,7 @@
#pragma once
#include "Module.hpp"
namespace tp {
extern ModuleManifest gModuleDataAnalysis;
}

View file

@ -0,0 +1,77 @@
#pragma once
#include "Buffer.hpp"
#include "DataAnalysisCommon.hpp"
namespace tp {
// Fully connected neural network
class FCNN {
struct Layer {
struct Neuron {
struct Parameter {
Parameter() = default;
Parameter(halnf in) :
val(in) {}
halnf val = 0;
halnf grad = 0;
public:
template <class tArchiver>
void archive(tArchiver& ar) {
ar % val;
}
};
Parameter bias;
Buffer<Parameter> weights;
halnf activationValue = 0;
halnf activationValueLinear = 0;
halnf cache;
public:
template <class tArchiver>
void archive(tArchiver& ar) {
ar % bias;
ar % weights;
}
};
Buffer<Neuron> neurons;
public:
template <class tArchiver>
void archive(tArchiver& ar) {
ar % neurons;
}
};
public:
FCNN() = default;
explicit FCNN(const Buffer<halni>& description);
void initializeRandom(const Buffer<halni>& description);
void evaluate(const Buffer<halnf>& input, Buffer<halnf>& output);
halnf calcCost(const Buffer<halnf>& output);
void clearGrad();
void calcGrad(const Buffer<halnf>& output);
void applyGrad(halnf step);
public:
template <class tArchiver>
void archive(tArchiver& ar) {
ar % mLayers;
}
public:
Buffer<Layer> mLayers;
halni mAvgCount = 0;
};
};

View file

@ -0,0 +1,59 @@
#include "UnitTest++/UnitTest++.h"
#include "FCNN.hpp"
#include "Utils.hpp"
#include <cstdio>
using namespace tp;
SUITE(FCNN) {
TEST(Basic) {
Buffer<halni> layers = { 100, 70, 50, 30, 20 };
Buffer<halnf> input(layers.first());
Buffer<halnf> outputExpected(layers.last());
Buffer<halnf> output(layers.last());
for (auto inputVal : Range(layers.first())) {
input[inputVal] = (halnf) randomFloat() * 100;
}
for (auto outIdx : Range(layers.last())) {
outputExpected[outIdx] = (halnf) randomFloat();
}
FCNN nn(layers);
halnf steppingValue = 0.01;
halnf cost = 0;
for (auto i : Range(150)) {
nn.evaluate(input, output);
nn.calcGrad(outputExpected);
nn.applyGrad(steppingValue);
cost = nn.calcCost(outputExpected);
printf("Loss %f \n", nn.calcCost(outputExpected));
}
CHECK(cost < 0.1f);
}
}
int main() {
tp::ModuleManifest* deps[] = { &tp::gModuleDataAnalysis, nullptr };
tp::ModuleManifest testModule("DataAnalysisTest", nullptr, nullptr, deps);
if (!testModule.initialize()) {
return 1;
}
bool res = UnitTest::RunAllTests();
testModule.deinitialize();
return res;
}

82
Externals/CMakeLists.txt vendored Normal file
View file

@ -0,0 +1,82 @@
# find_package(ALSA REQUIRED)
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
message("Configuring for windows...")
message("Libraries path is - ${WINDOWS_LIBRARIES}")
set(CMAKE_PREFIX_PATH "${WINDOWS_LIBRARIES}/glew-2.1.0")
find_package(GLEW REQUIRED)
set(GLEW_LIB ${GLEW_STATIC_LIBRARY_RELEASE} opengl32.lib PARENT_SCOPE)
# Your relative paths
set(RELATIVE_INCLUDE_DIR "${WINDOWS_LIBRARIES}/portaudio/include")
set(RELATIVE_LIB "${WINDOWS_LIBRARIES}/portaudio_build/Debug/portaudio.lib")
# Convert to absolute paths
get_filename_component(ABSOLUTE_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${RELATIVE_INCLUDE_DIR}" ABSOLUTE)
get_filename_component(ABSOLUTE_LIB "${CMAKE_CURRENT_SOURCE_DIR}/${RELATIVE_LIB}" ABSOLUTE)
# Set absolute paths with respect to the parent scope
set(PORTAUDIO_INCLUDE_DIR "${ABSOLUTE_INCLUDE_DIR}" PARENT_SCOPE)
set(PORTAUDIO_LIB "${ABSOLUTE_LIB}" PARENT_SCOPE)
else()
find_package(GLEW REQUIRED)
set(GLEW_LIB ${GLEW_LIBRARIES} GL PARENT_SCOPE)
set(PORTAUDIO_LIB portaudio PARENT_SCOPE)
endif()
#add_subdirectory(glew/build/cmake/)
#target_compile_definitions(glew_s PUBLIC GLEW_NO_GLU)
#add_subdirectory(unittest-cpp)
add_subdirectory(lalr)
add_subdirectory(glfw)
project(Imgui)
set(${PROJECT_NAME}_SOURCES
imgui/imgui.cpp
imgui/imgui_draw.cpp
imgui/imgui_tables.cpp
imgui/imgui_widgets.cpp
imgui/backends/imgui_impl_glfw.cpp
imgui/backends/imgui_impl_opengl3.cpp
)
add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_SOURCES})
include_directories(${PROJECT_NAME} ./glfw/include)
target_include_directories(${PROJECT_NAME} PUBLIC ./imgui/ ./imgui/backends/)
project(Nanovg)
set(${PROJECT_NAME}_SOURCES
nanovg/src/nanovg.c
)
add_library(${PROJECT_NAME} STATIC ${${PROJECT_NAME}_SOURCES})
target_include_directories(${PROJECT_NAME} PUBLIC ./nanovg/src/ ./nanovg/obsolete/)
project(Lua)
# Add the Lua source files
#file(GLOB ${PROJECT_NAME}_SOURCES "./lua/luaone.c")
# list(FILTER ${PROJECT_NAME}_SOURCES EXCLUDE REGEX "./lua/lua.c$")
# Create the Lua library
add_library(${PROJECT_NAME} STATIC "lua/onelua.c")
target_compile_definitions(${PROJECT_NAME} PUBLIC MAKE_LIB)
# Include directories for Lua headers
target_include_directories(${PROJECT_NAME} PUBLIC "./lua")
# Set compilation options, e.g., for compilation with C99
set_target_properties(${PROJECT_NAME} PROPERTIES
C_STANDARD 99
C_STANDARD_REQUIRED ON
)
project(ImageIO)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(${PROJECT_NAME} INTERFACE ./imageIO/)

1
Externals/asio vendored Submodule

@ -0,0 +1 @@
Subproject commit ed5db1b50136bace796062c1a6eab0df9a74f8fa

1
Externals/glfw vendored Submodule

@ -0,0 +1 @@
Subproject commit 3eaf1255b29fdf5c2895856c7be7d7185ef2b241

7987
Externals/imageIO/stb_image.h vendored Normal file

File diff suppressed because it is too large Load diff

1724
Externals/imageIO/stb_image_write.h vendored Normal file

File diff suppressed because it is too large Load diff

1
Externals/imgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 4144d7d772a800f08693b8b13f7c81f1f22a73c4

1
Externals/lalr vendored Submodule

@ -0,0 +1 @@
Subproject commit 6d16a6bb60490a2ee4e86f689c86e647b300c7fb

1
Externals/lua vendored Submodule

@ -0,0 +1 @@
Subproject commit 6baee9ef9d5657ab582c8a4b9f885ec58ed502d0

1
Externals/nanovg vendored Submodule

@ -0,0 +1 @@
Subproject commit 7544c114e83db7cf67bd1c9e012349b70caacc2f

1
Externals/unittest-cpp vendored Submodule

@ -0,0 +1 @@
Subproject commit 10e50ad70c696002b1d5bbefd0ea04b3ea92a03b

23
Graphics/CMakeLists.txt Normal file
View file

@ -0,0 +1,23 @@
project(Graphics)
### ---------------------- Externals --------------------- ###
set(BINDINGS_INCLUDE ../Externals/glfw/include ${GLEW_INCLUDE_DIR})
set(BINDINGS_LIBS glfw Imgui Nanovg ${GLEW_LIB})
### ---------------------- 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 Strings Math Allocators)
target_link_libraries(${PROJECT_NAME} PRIVATE ${BINDINGS_LIBS})
### -------------------------- Examples -------------------------- ###
add_executable(Example${PROJECT_NAME} ./examples/Example.cpp)
target_link_libraries(Example${PROJECT_NAME} ${PROJECT_NAME} ${BINDINGS_LIBS})
target_include_directories(Example${PROJECT_NAME} PRIVATE ${BINDINGS_INCLUDE})
file(COPY "examples/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")

View file

@ -0,0 +1,44 @@
#include "Window.hpp"
#include "Timing.hpp"
#include "imgui.h"
int main() {
tp::ModuleManifest* deps[] = { &tp::gModuleGraphics, nullptr };
tp::ModuleManifest testModule("Example", nullptr, nullptr, deps);
if (!testModule.initialize()) {
return 1;
}
int frames = 0;
int fps = 0;
tp::Timer timer(1000);
{
auto window = tp::Window::createWindow(800, 600, "Window 1");
if (window) {
while (!window->shouldClose()) {
window->processEvents();
ImGui::Text("fps: %i", fps);
window->draw();
if (timer.isTimeout()) {
fps = frames;
frames = 0;
timer.reset();
}
frames++;
}
}
tp::Window::destroyWindow(window);
}
testModule.deinitialize();
}

BIN
Graphics/examples/Font.ttf Normal file

Binary file not shown.

View file

@ -0,0 +1,7 @@
#include "GraphicsCommon.hpp"
#include "MathCommon.hpp"
namespace tp {
static ModuleManifest* deps[] = { &gModuleAllocators, nullptr };
ModuleManifest gModuleGraphics = ModuleManifest("Graphics", nullptr, nullptr, deps);
}

View file

@ -0,0 +1,117 @@
#include "Window.hpp"
// -------- OpenGL -------- //
#define GLEW_STATIC
#include <GL/glew.h>
#include "WindowContext.hpp"
// -------- Canvas -------- //
#define NANOVG_GL3_IMPLEMENTATION
#include <nanovg.h>
#include <nanovg_gl.h>
namespace tp {
class Graphics::Canvas::Context {
public:
NVGcontext* vg;
};
}
using namespace tp;
Graphics::Canvas::Canvas() { mContext = new Context(); }
Graphics::Canvas::~Canvas() { delete mContext; }
void Graphics::Canvas::init() {
mContext->vg = nvgCreateGL3(NVG_ANTIALIAS | NVG_STENCIL_STROKES);
if (nvgCreateFont(mContext->vg, "default", "Font.ttf") == -1) {
// TODO
}
}
void Graphics::Canvas::deinit() { nvgDeleteGL3(mContext->vg); }
RectF Graphics::Canvas::getAvaliableArea() { return { (halnf) 0, (halnf) 0, mWidth, mHeight }; }
void Graphics::Canvas::rect(const RectF& rec, const RGBA& col, halnf round) {
nvgBeginPath(mContext->vg);
if (round == 0) {
nvgRect(mContext->vg, rec.x, rec.y, rec.z, rec.w);
} else {
nvgRoundedRect(mContext->vg, rec.x, rec.y, rec.z, rec.w, round);
}
nvgFillColor(mContext->vg, { col.r, col.g, col.b, col.a });
nvgFill(mContext->vg);
}
void Graphics::Canvas::pushClamp(const RectF& rec) {
RectF intersection = rec;
if (mScissors.size()) {
mScissors.last().calcIntersection(rec, intersection);
}
nvgScissor(mContext->vg, intersection.x, intersection.y, intersection.z, intersection.w);
mScissors.append(rec);
}
void Graphics::Canvas::popClamp() {
DEBUG_ASSERT(mScissors.size());
mIsClamping = false;
mScissors.pop();
if (mScissors.size()) {
const auto& rec = mScissors.last();
nvgScissor(mContext->vg, rec.x, rec.y, rec.z, rec.w);
} else {
nvgResetScissor(mContext->vg);
}
}
void Graphics::Canvas::text(
const char* string, const RectF& aRec, halnf size, Align align, halnf marging, const RGBA& col
) {
RectF rec = { aRec.x + marging, aRec.y + marging, aRec.z - marging * 2, aRec.w - marging * 2 };
pushClamp(rec);
nvgFontSize(mContext->vg, size);
nvgFontFace(mContext->vg, "default");
nvgFillColor(mContext->vg, { col.r, col.g, col.b, col.a });
float centerX = rec.x;
float centerY = rec.y;
int alignNVG = 0;
if (((int1*) &align)[1] == 0x00) { // center x
alignNVG |= NVG_ALIGN_CENTER;
centerX += rec.z * 0.5f;
} else if (((int1*) &align)[1] == 0x01) { // left x
alignNVG |= NVG_ALIGN_LEFT;
} else if (((int1*) &align)[1] == 0x02) { // right x
alignNVG |= NVG_ALIGN_RIGHT;
centerX += rec.z;
}
if (((int1*) &align)[0] == 0x00) { // center y
alignNVG |= NVG_ALIGN_MIDDLE;
centerY += rec.w * 0.5f;
} else if (((int1*) &align)[0] == 0x01) { // top y
alignNVG |= NVG_ALIGN_TOP;
centerY += rec.w;
} else if (((int1*) &align)[0] == 0x02) { // bottom y
alignNVG |= NVG_ALIGN_BOTTOM;
}
nvgTextAlign(mContext->vg, alignNVG);
nvgText(mContext->vg, centerX, centerY, string, nullptr);
popClamp();
}
void Graphics::Canvas::proc() { nvgBeginFrame(mContext->vg, mWidth, mHeight, 1.0); }
void Graphics::Canvas::draw() { nvgEndFrame(mContext->vg); }

View file

@ -0,0 +1,60 @@
#include "Window.hpp"
#include "WindowContext.hpp"
// -------- Debug UI -------- //
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <imgui_internal.h>
namespace tp {
class Graphics::GUI::Context {
public:
ImGuiIO* io{};
ImGuiContext* ctx{};
};
}
using namespace tp;
Graphics::GUI::GUI() { mContext = new Context(); }
Graphics::GUI::~GUI() { delete mContext; }
void Graphics::GUI::init(Window* window) {
IMGUI_CHECKVERSION();
mContext->ctx = ImGui::CreateContext();
mContext->io = &ImGui::GetIO();
// Initialize ImGui with GLFW and OpenGL
ImGui_ImplGlfw_InitForOpenGL(window->getContext()->window, true);
// ImGui_ImplGlfw_GetBackendData();
ImGui_ImplOpenGL3_Init("#version 330");
ImGuiIO& io = ImGui::GetIO();
io.Fonts->AddFontFromFileTTF("Font.ttf", 20.f);
}
void Graphics::GUI::deinit() {
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
}
void Graphics::GUI::proc() {
tp::HeapAllocGlobal::startIgnore();
ImGui_ImplOpenGL3_NewFrame();
ImGui_ImplGlfw_NewFrame();
ImGui::NewFrame();
tp::HeapAllocGlobal::stopIgnore();
}
void Graphics::GUI::draw() {
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}

View file

@ -0,0 +1,188 @@
#include "Window.hpp"
// -------- OpenGL -------- //
#include <GL/glew.h>
#include "WindowContext.hpp"
#include <cstdio>
namespace tp {
class Graphics::GL::Context {
public:
// Showoff data
GLuint vao{};
GLuint vbo{};
GLfloat vertices[9] = {
-0.0f, -0.0f, 0.0f, // Left vertex
1.0f, 0.0f, 0.0f, // Right vertex
0.5f, 1.0f, 0.0f // Top vertex
};
};
}
using namespace tp;
Graphics::GL::GL() { mContext = new Context(); }
Graphics::GL::~GL() { delete mContext; }
void Graphics::GL::init() {
// Create a Vertex Array Object (VAO)
glGenVertexArrays(1, &mContext->vao);
glBindVertexArray(mContext->vao);
// Create a Vertex Buffer Object (VBO)
glGenBuffers(1, &mContext->vbo);
glBindBuffer(GL_ARRAY_BUFFER, mContext->vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(mContext->vertices), mContext->vertices, GL_STATIC_DRAW);
// Set up vertex attributes
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (void*) nullptr);
glEnableVertexAttribArray(0);
}
void Graphics::GL::deinit() {
glDeleteBuffers(1, &mContext->vbo);
glDeleteVertexArrays(1, &mContext->vao);
}
void Graphics::GL::proc() {}
void Graphics::GL::draw() {
glClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, mWidth, mHeight);
// glDrawArrays(GL_TRIANGLES, 0, 3);
}
void Graphics::init(Window* window) {
mGl.init();
mGui.init(window);
mCanvas.init();
}
void Graphics::deinit() {
mCanvas.deinit();
mGui.deinit();
mGl.deinit();
}
void Graphics::proc() {
mGl.proc();
mCanvas.proc();
mGui.proc();
}
void Graphics::draw() {
mGl.draw();
mCanvas.draw();
mGui.draw();
}
Window::Window(int width, int height, const char* title) {
mContext = new Context();
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, 1);
// Create a window and OpenGL context
mContext->window = glfwCreateWindow(width, height, title, nullptr, nullptr);
if (!mContext->window) {
printf("Failed to create GLFW window\n");
return;
}
glfwMakeContextCurrent(mContext->window);
// Initialize GLEW
if (glewInit() != GLEW_OK) {
printf("Failed to initialize GLEW\n");
return;
}
mContext->inputManager.init(mContext->window);
mGraphics.init(this);
mEvents.mContext = mContext;
}
Window::~Window() {
mGraphics.deinit();
glfwDestroyWindow(mContext->window);
delete mContext;
}
Window* Window::createWindow(int width, int height, const char* title) {
tp::HeapAllocGlobal::startIgnore();
static int count = 1;
if (!count) {
printf("Window class is a singleton\n");
return nullptr;
}
count--;
// Initialize GLFW
if (!glfwInit()) {
printf("Failed to initialize GLFW\n");
return nullptr;
}
// Set the GLFW error callback
glfwSetErrorCallback([](int error, const char* description) { printf("GLFW Error: %i %s\n", error, description); });
auto out = new Window(width, height, title);
tp::HeapAllocGlobal::stopIgnore();
return out;
}
void Window::destroyWindow(Window* window) {
delete window;
glfwTerminate();
}
bool Window::shouldClose() const { return glfwWindowShouldClose(mContext->window); }
void Window::processEvents() {
mContext->inputManager.isEvent = false;
glfwPollEvents();
int w, h;
glfwGetWindowSize(mContext->window, &w, &h);
mGraphics.mCanvas.mWidth = w;
mGraphics.mCanvas.mHeight = h;
mGraphics.mGl.mWidth = w;
mGraphics.mGl.mHeight = h;
mGraphics.proc();
mContext->inputManager.update();
get_time();
}
void Window::draw() {
mGraphics.draw();
glfwSwapBuffers(mContext->window);
mContext->inputManager.resetScroll();
}
auto Window::getContext() -> Context* { return mContext; }
Graphics::Canvas& Window::getCanvas() { return mGraphics.mCanvas; }
const Window::Events& Window::getEvents() { return mEvents; }
const Vec2F& Window::Events::getPos() const { return mContext->inputManager.getPos(); }
bool Window::Events::isPressed() const { return mContext->inputManager.isPressed(); }
bool Window::Events::isReleased() const { return mContext->inputManager.isReleased(); }
bool Window::Events::isDown() const { return mContext->inputManager.isDown(); }
halnf Window::Events::getScrollY() const { return mContext->inputManager.getScrollY(); }
bool Window::Events::isEvent() const { return mContext->inputManager.isEvents(); }

View file

@ -0,0 +1,117 @@
#pragma once
// -------- Window Context -------- //
#include <GLFW/glfw3.h>
#include <array>
namespace tp {
class Window;
class InputManager {
public:
InputManager() = default;
void init(GLFWwindow* aWindow) {
window = aWindow;
// Set up key and mouse button callbacks
glfwSetWindowUserPointer(window, this);
glfwSetKeyCallback(window, keyCallback);
glfwSetMouseButtonCallback(window, mouseButtonCallback);
glfwSetScrollCallback(window, scrollCallback);
}
const Vec2F& getPos() const { return mousePos; }
bool isKeyPressed(int key) const { return keyStates[key] == GLFW_PRESS; }
bool isKeyDown(int key) const { return keyStates[key] == GLFW_REPEAT || keyStates[key] == GLFW_PRESS; }
bool isPressed() const { return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS; }
bool isReleased() const { return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_RELEASE; }
bool isDown() const { return mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_REPEAT || mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS; }
bool isEvents() const { return isEvent; }
double getScrollY() const { return scrollY; }
// bool isScrollDown() const { return scrollY < 0.0; }
private:
friend Window;
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
InputManager* inputManager = static_cast<InputManager*>(glfwGetWindowUserPointer(window));
if (!inputManager) {
// Update key state
if (action == GLFW_PRESS) {
inputManager->keyStates[key] = GLFW_PRESS;
} else if (action == GLFW_RELEASE) {
inputManager->keyStates[key] = GLFW_RELEASE;
}
inputManager->isEvent = true;
}
}
static void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
InputManager* inputManager = static_cast<InputManager*>(glfwGetWindowUserPointer(window));
if (inputManager) {
// Update mouse button state
inputManager->mouseButtonStates[button] = action;
inputManager->isEvent = true;
}
}
static void scrollCallback(GLFWwindow* window, double xoffset, double yoffset) {
InputManager* inputManager = static_cast<InputManager*>(glfwGetWindowUserPointer(window));
if (inputManager) {
// Update scroll state
inputManager->scrollX = xoffset;
inputManager->scrollY = yoffset;
inputManager->isEvent = true;
}
}
void update() {
double x, y;
glfwGetCursorPos(window, &x, &y);
mousePosPrev = mousePos;
mousePos = { x, y };
if ((mousePosPrev - mousePos).length2()) {
isEvent = true;
}
}
void resetScroll() {
scrollX = 0.0;
scrollY = 0.0;
mouseButtonStates[GLFW_MOUSE_BUTTON_LEFT] = GLFW_REPEAT;
}
bool isEvent = false;
double scrollX = 0.0;
double scrollY = 0.0;
GLFWwindow* window;
Vec2F mousePos;
Vec2F mousePosPrev;
std::array<int, GLFW_KEY_LAST + 1> keyStates = { GLFW_RELEASE };
std::array<int, GLFW_MOUSE_BUTTON_LAST + 1> mouseButtonStates = { GLFW_RELEASE };
};
class Window::Context {
friend Graphics::GL;
friend Graphics::GUI;
friend Graphics::Canvas;
public:
GLFWwindow* window;
InputManager inputManager;
};
}

View file

@ -0,0 +1,113 @@
#pragma once
#include "GraphicsCommon.hpp"
// TODO : ugly
namespace tp {
class Window;
class Graphics {
public:
class GL {
class Context;
Context* mContext;
private:
friend Graphics;
friend Window;
GL();
~GL();
void init();
void deinit();
void proc();
void draw();
public:
// TODO : API
private:
halnf mWidth = 100;
halnf mHeight = 100;
};
class GUI {
class Context;
Context* mContext;
private:
friend Graphics;
GUI();
~GUI();
void init(Window* window);
void deinit();
void proc();
void draw();
public:
// TODO : API
};
class Canvas {
class Context;
Context* mContext;
private:
friend Graphics;
friend Window;
Canvas();
~Canvas();
void init();
void deinit();
void proc();
void draw();
public:
enum Align : int2 {
CC = 0x0000,
CT = 0x0001,
CB = 0x0002,
LC = 0x0100,
LT = 0x0101,
LB = 0x0102,
RC = 0x0200,
RT = 0x0201,
RB = 0x0202,
};
RectF getAvaliableArea();
void pushClamp(const RectF& rec);
void popClamp();
void rect(const RectF& rec, const RGBA& col, halnf round = 0);
void text(const char*, const RectF&, halnf size, Align, halnf marging, const RGBA&);
// TODO : API
private:
halnf mWidth = 600;
halnf mHeight = 600;
Buffer<RectF> mScissors;
bool mIsClamping = false;
};
private:
friend Window;
Graphics() = default;
void init(Window* window);
void deinit();
void draw();
void proc();
private:
GUI mGui;
GL mGl;
Canvas mCanvas;
};
}

View file

@ -0,0 +1,11 @@
#pragma once
#include "Color.hpp"
#include "Rect.hpp"
#include "Timing.hpp"
#include "Allocators.hpp"
namespace tp {
extern ModuleManifest gModuleGraphics;
}

Some files were not shown because too many files have changed in this diff Show more