Compare commits
159 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6f535fe6e | ||
|
|
1ead32d84a | ||
|
|
65cb26a627 | ||
|
|
00d8fa0886 | ||
|
|
ecaa2bbdfb | ||
|
|
c2ad9ce501 | ||
|
|
aa2d8f1200 | ||
|
|
5d94ddd774 | ||
|
|
da74d27135 | ||
|
|
37ae4b2abb | ||
|
|
afe32d6014 | ||
|
|
ffafeba6c2 | ||
|
|
80fcbe4e2c | ||
|
|
d7d809c222 | ||
|
|
8ac49dc50b | ||
|
|
cce9609ced | ||
|
|
ac4f08c67c | ||
|
|
23e7ecb812 | ||
|
|
20ac8620e6 | ||
|
|
b906e0e66c | ||
|
|
49c67eb028 | ||
|
|
fdb861b299 | ||
|
|
828096de17 | ||
|
|
e628e7c1e2 | ||
|
|
ad543c304d | ||
|
|
ed3421321c | ||
|
|
f666416696 | ||
|
|
3ba4bef93b | ||
|
|
3bed74df87 | ||
|
|
63b0560375 | ||
|
|
1d9ef2aa6c | ||
|
|
35387395eb | ||
|
|
e6d77c639b | ||
|
|
a9a810c197 | ||
|
|
4f88c45c9e | ||
|
|
81a2247bf2 | ||
|
|
dd6b63acb1 | ||
|
|
41aa8daeb2 | ||
|
|
1f1dc001a1 | ||
|
|
93b02416de | ||
|
|
312419936e | ||
|
|
d2bb8cdd6a | ||
|
|
7112002e30 | ||
|
|
656da1fd76 | ||
|
|
38957e678b | ||
|
|
7ed6b1f33c | ||
|
|
060aaecbeb | ||
|
|
fae4d83f97 | ||
|
|
14cb588948 | ||
|
|
7d8cf66b27 | ||
|
|
a3399fdc56 | ||
|
|
7932887f43 | ||
|
|
3b50001daf | ||
|
|
83d51d3e83 | ||
|
|
f7f0d6e5be | ||
|
|
f146ddd307 | ||
|
|
69cdf43909 | ||
|
|
7298858c96 | ||
|
|
dec6a9dc73 | ||
|
|
db73ffb439 | ||
|
|
34a8d4eafd | ||
|
|
2722a25036 | ||
|
|
81e16e1c50 | ||
|
|
f7fd0c5942 | ||
|
|
023c3908e0 | ||
|
|
a5f4d7149a | ||
|
|
5d7a343a20 | ||
|
|
d5ff875497 | ||
|
|
231b30b2cb | ||
|
|
7b325244c5 | ||
|
|
c7d3b15758 | ||
|
|
5a55011911 | ||
|
|
b7a89b714e | ||
|
|
fc20f3594d | ||
|
|
826713c212 | ||
|
|
0542b7ba2e | ||
|
|
efbb09f8ba | ||
|
|
744c01c5d0 | ||
|
|
43e374f269 | ||
|
|
93a90e0291 | ||
|
|
3396e69b47 | ||
|
|
7af12ee1a0 | ||
|
|
2e0d0e0050 | ||
|
|
414d4e6411 | ||
|
|
97f79a6483 | ||
|
|
cfb4c0cfcc | ||
|
|
c284a12ade | ||
|
|
3e9365b1ee | ||
|
|
517dc83c85 | ||
|
|
e00ab4059c | ||
|
|
0e41a6e4fd | ||
|
|
97525efacb | ||
|
|
94d47ba390 | ||
|
|
8d990a0132 | ||
|
|
2810e06b3e | ||
|
|
5cf467a421 | ||
|
|
01b6f9099c | ||
|
|
616441e049 | ||
|
|
b205438601 | ||
|
|
e6d9439ba9 | ||
|
|
0a886bd8a8 | ||
|
|
43fe34dcbf | ||
|
|
7d49642376 | ||
|
|
01b7d96ebd | ||
|
|
6fcfa075f8 | ||
|
|
d888aeb2b4 | ||
|
|
92f34e2a99 | ||
|
|
424787c735 | ||
|
|
eb712a352d | ||
|
|
5fab9ee507 | ||
|
|
9c8771a378 | ||
|
|
9a7e44a96d | ||
|
|
112fea2f04 | ||
|
|
f9d62c324d | ||
|
|
55571ca3dd | ||
|
|
b06e1da529 | ||
|
|
0cefb47b66 | ||
|
|
a918c83ec1 | ||
|
|
2c8e1470d1 | ||
|
|
64e1f78739 | ||
|
|
ba95747ef9 | ||
|
|
3575cbc543 | ||
|
|
01ba8160ef | ||
|
|
939e365a4f | ||
|
|
803ce23169 | ||
|
|
00bc875846 | ||
|
|
5f186164a0 | ||
|
|
3a68e70f03 | ||
|
|
e9c6a97a07 | ||
|
|
3770c72ec0 | ||
|
|
c4a7f91d56 | ||
|
|
6db1406d68 | ||
|
|
e81449eddf | ||
|
|
7595ae3926 | ||
|
|
e9dbee6667 | ||
|
|
fa4d1d72dc | ||
|
|
0301f608bd | ||
|
|
a543568e1d | ||
|
|
576a3565f7 | ||
|
|
62cb2bb8b7 | ||
|
|
8e00882bb3 | ||
|
|
df3767df29 | ||
|
|
90998e2279 | ||
|
|
6bca7431b7 | ||
|
|
91b00817e3 | ||
|
|
d80ea40848 | ||
|
|
bedd88be2e | ||
|
|
bc892da992 | ||
|
|
fabceb52d3 | ||
|
|
b68539cc0c | ||
|
|
44bad77e93 | ||
|
|
e63843c6bb | ||
|
|
692994c2d3 | ||
|
|
facc17ec1b | ||
|
|
4c9691e192 | ||
|
|
c75cb9fb48 | ||
|
|
d362d8c3b3 | ||
|
|
eb9210de1c | ||
|
|
9cf541a6ba |
296 changed files with 276722 additions and 6153 deletions
197
.clang-format
197
.clang-format
|
|
@ -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
16
.cproject
Normal 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>
|
||||||
428
.docs/Designs/Language.drawio
Normal file
428
.docs/Designs/Language.drawio
Normal 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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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 <br>(CF RE United Format)" treeRoot="1" id="B2JnEC1cqt8830IlIT0b-42">
|
||||||
|
<mxCell style="whiteSpace=wrap;html=1;align=center;treeFolding=1;treeMoving=1;newEdgeStyle={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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={"edgeStyle":"elbowEdgeStyle","startArrow":"none","endArrow":"none"};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
17
.github/workflows/WindowsPlatform.yml
vendored
Normal 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
|
||||||
56
.github/workflows/cmake.yml
vendored
56
.github/workflows/cmake.yml
vendored
|
|
@ -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
4
.gitignore
vendored
|
|
@ -5,3 +5,7 @@ build*
|
||||||
lib
|
lib
|
||||||
install
|
install
|
||||||
.vscode
|
.vscode
|
||||||
|
out
|
||||||
|
.vs
|
||||||
|
*.bkp
|
||||||
|
*.$*
|
||||||
21
.gitmodules
vendored
Normal file
21
.gitmodules
vendored
Normal 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
20
.project
Normal 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>
|
||||||
20
.wip/Language/CMakeLists.txt
Normal file
20
.wip/Language/CMakeLists.txt
Normal 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)
|
||||||
36
.wip/Language/private/Grammar.cpp
Normal file
36
.wip/Language/private/Grammar.cpp
Normal 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; }
|
||||||
8
.wip/Language/private/LanguageCommon.cpp
Normal file
8
.wip/Language/private/LanguageCommon.cpp
Normal 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);
|
||||||
249
.wip/Language/public/Automata.hpp
Normal file
249
.wip/Language/public/Automata.hpp
Normal 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
137
.wip/Language/public/ContextFreeAutomata.hpp
Normal file
137
.wip/Language/public/ContextFreeAutomata.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
181
.wip/Language/public/ContextFreeCompiler.hpp
Normal file
181
.wip/Language/public/ContextFreeCompiler.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
224
.wip/Language/public/Grammar.hpp
Normal file
224
.wip/Language/public/Grammar.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
7
.wip/Language/public/LanguageCommon.hpp
Normal file
7
.wip/Language/public/LanguageCommon.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Module.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
extern ModuleManifest gModuleLanguage;
|
||||||
|
}
|
||||||
107
.wip/Language/public/Parser.hpp
Normal file
107
.wip/Language/public/Parser.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
101
.wip/Language/public/RegularAutomata.hpp
Normal file
101
.wip/Language/public/RegularAutomata.hpp
Normal 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 };
|
||||||
|
};
|
||||||
|
}
|
||||||
208
.wip/Language/public/RegularCompiler.hpp
Normal file
208
.wip/Language/public/RegularCompiler.hpp
Normal 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); }
|
||||||
|
};
|
||||||
|
}
|
||||||
69
.wip/Language/public/SimpleParser.hpp
Normal file
69
.wip/Language/public/SimpleParser.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
43
.wip/Language/tests/Test.hpp
Normal file
43
.wip/Language/tests/Test.hpp
Normal 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;
|
||||||
|
}
|
||||||
|
)";
|
||||||
45
.wip/Language/tests/Tests.cpp
Normal file
45
.wip/Language/tests/Tests.cpp
Normal 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; }
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -26,17 +26,11 @@ int main(char argc, char* argv[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
If memory leaks were detected it will be loged in the output console.
|
If memory leaks were detected it will be logged in the output console.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Also debug.memleaks binary will be generated in the working directory that can be viewved with MemLeaks Viewver.
|
Also debug.memleaks binary will be generated in the working directory that can be viewed with MemLeaks Viewer.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Memory Usage Analisys
|
|
||||||
Currently outdated
|
|
||||||
|
|
||||||
## Benchmarks
|
|
||||||
Currently outdated
|
|
||||||
|
|
|
||||||
|
|
@ -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); }
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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++;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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() {}
|
||||||
|
|
|
||||||
8
Allocators/public/NewPlacement.hpp
Normal file
8
Allocators/public/NewPlacement.hpp
Normal 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
|
||||||
|
|
@ -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])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Utils.hpp"
|
|
||||||
#include "Testing.hpp"
|
|
||||||
|
|
||||||
void testAll();
|
|
||||||
|
|
@ -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
14
CMakeOptions.txt
Normal 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
15
CMakeSettings.json
Normal 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
14
Callstack/CMakeLists.txt
Normal 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})
|
||||||
332
Callstack/private/Callstack.cpp
Normal file
332
Callstack/private/Callstack.cpp
Normal 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
|
||||||
|
|
@ -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
44
Callstack/tests/Test.cpp
Normal 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
18
Connection/CMakeLists.txt
Normal 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})
|
||||||
84
Connection/private/LocalConnection.cpp
Normal file
84
Connection/private/LocalConnection.cpp
Normal 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();
|
||||||
|
}
|
||||||
2
Connection/private/RemoteConnection.cpp
Normal file
2
Connection/private/RemoteConnection.cpp
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
|
||||||
|
#include "RemoteConnection.hpp"
|
||||||
53
Connection/private/bindings/Disk.cpp
Normal file
53
Connection/private/bindings/Disk.cpp
Normal 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);
|
||||||
|
}
|
||||||
18
Connection/private/bindings/Disk.hpp
Normal file
18
Connection/private/bindings/Disk.hpp
Normal 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();
|
||||||
|
};
|
||||||
|
}
|
||||||
116
Connection/private/bindings/Network.cpp
Normal file
116
Connection/private/bindings/Network.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Connection/public/ConnectionCommon.hpp
Normal file
66
Connection/public/ConnectionCommon.hpp
Normal 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; }
|
||||||
|
};
|
||||||
|
}
|
||||||
66
Connection/public/LocalConnection.hpp
Normal file
66
Connection/public/LocalConnection.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
36
Connection/public/Network.hpp
Normal file
36
Connection/public/Network.hpp
Normal 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);
|
||||||
|
};
|
||||||
|
}
|
||||||
40
Connection/public/RemoteConnection.hpp
Normal file
40
Connection/public/RemoteConnection.hpp
Normal 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
39
Connection/tests/Test.cpp
Normal 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();
|
||||||
|
}
|
||||||
|
|
@ -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)
|
|
||||||
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
||||||
131
Containers/public/Buffer2D.hpp
Normal file
131
Containers/public/Buffer2D.hpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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&) {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
88
Containers/public/IntervalTree.hpp
Normal file
88
Containers/public/IntervalTree.hpp
Normal 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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(); }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -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(); }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
@ -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;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
19
Containers/tests/Buffer2DTest.cpp
Normal file
19
Containers/tests/Buffer2DTest.cpp
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
274
Containers/tests/IntervalTreeTests.cpp
Normal file
274
Containers/tests/IntervalTreeTests.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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();
|
|
||||||
|
|
@ -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();
|
|
||||||
}
|
}
|
||||||
29
DataAnalysis/CMakeLists.txt
Normal file
29
DataAnalysis/CMakeLists.txt
Normal 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})
|
||||||
68
DataAnalysis/applications/NumRecApp.cpp
Normal file
68
DataAnalysis/applications/NumRecApp.cpp
Normal 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;
|
||||||
|
}
|
||||||
244
DataAnalysis/applications/NumRecTraining.cpp
Normal file
244
DataAnalysis/applications/NumRecTraining.cpp
Normal 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());
|
||||||
|
}
|
||||||
BIN
DataAnalysis/applications/rsc/mnist.gz
Normal file
BIN
DataAnalysis/applications/rsc/mnist.gz
Normal file
Binary file not shown.
9
DataAnalysis/private/DataAnalysisCommon.cpp
Normal file
9
DataAnalysis/private/DataAnalysisCommon.cpp
Normal 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);
|
||||||
|
}
|
||||||
158
DataAnalysis/private/FCNN.cpp
Normal file
158
DataAnalysis/private/FCNN.cpp
Normal 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;
|
||||||
|
}
|
||||||
7
DataAnalysis/public/DataAnalysisCommon.hpp
Normal file
7
DataAnalysis/public/DataAnalysisCommon.hpp
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Module.hpp"
|
||||||
|
|
||||||
|
namespace tp {
|
||||||
|
extern ModuleManifest gModuleDataAnalysis;
|
||||||
|
}
|
||||||
77
DataAnalysis/public/FCNN.hpp
Normal file
77
DataAnalysis/public/FCNN.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
};
|
||||||
59
DataAnalysis/tests/Tests.cpp
Normal file
59
DataAnalysis/tests/Tests.cpp
Normal 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
82
Externals/CMakeLists.txt
vendored
Normal 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
1
Externals/asio
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit ed5db1b50136bace796062c1a6eab0df9a74f8fa
|
||||||
1
Externals/glfw
vendored
Submodule
1
Externals/glfw
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 3eaf1255b29fdf5c2895856c7be7d7185ef2b241
|
||||||
7987
Externals/imageIO/stb_image.h
vendored
Normal file
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
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
1
Externals/imgui
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 4144d7d772a800f08693b8b13f7c81f1f22a73c4
|
||||||
1
Externals/lalr
vendored
Submodule
1
Externals/lalr
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6d16a6bb60490a2ee4e86f689c86e647b300c7fb
|
||||||
1
Externals/lua
vendored
Submodule
1
Externals/lua
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 6baee9ef9d5657ab582c8a4b9f885ec58ed502d0
|
||||||
1
Externals/nanovg
vendored
Submodule
1
Externals/nanovg
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 7544c114e83db7cf67bd1c9e012349b70caacc2f
|
||||||
1
Externals/unittest-cpp
vendored
Submodule
1
Externals/unittest-cpp
vendored
Submodule
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 10e50ad70c696002b1d5bbefd0ea04b3ea92a03b
|
||||||
23
Graphics/CMakeLists.txt
Normal file
23
Graphics/CMakeLists.txt
Normal 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}/")
|
||||||
|
|
||||||
44
Graphics/examples/Example.cpp
Normal file
44
Graphics/examples/Example.cpp
Normal 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
BIN
Graphics/examples/Font.ttf
Normal file
Binary file not shown.
7
Graphics/private/GraphicsCommon.cpp
Normal file
7
Graphics/private/GraphicsCommon.cpp
Normal 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);
|
||||||
|
}
|
||||||
117
Graphics/private/bindings/Canvas.cpp
Normal file
117
Graphics/private/bindings/Canvas.cpp
Normal 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); }
|
||||||
60
Graphics/private/bindings/DebugGUI.cpp
Normal file
60
Graphics/private/bindings/DebugGUI.cpp
Normal 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());
|
||||||
|
}
|
||||||
188
Graphics/private/bindings/Window.cpp
Normal file
188
Graphics/private/bindings/Window.cpp
Normal 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(); }
|
||||||
117
Graphics/private/bindings/WindowContext.hpp
Normal file
117
Graphics/private/bindings/WindowContext.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
113
Graphics/public/Graphics.hpp
Normal file
113
Graphics/public/Graphics.hpp
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
11
Graphics/public/GraphicsCommon.hpp
Normal file
11
Graphics/public/GraphicsCommon.hpp
Normal 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
Loading…
Add table
Add a link
Reference in a new issue