mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
420 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a3cdc01ec | ||
|
|
808ce6d605 | ||
|
|
09606a2998 | ||
|
|
0a21323652 | ||
|
|
c990b5aaf1 | ||
|
|
98e0985a10 | ||
|
|
5974bf6cd0 | ||
|
|
992890065a | ||
|
|
d4dbe27072 | ||
|
|
56a11ae3ca | ||
|
|
f1d03c194f | ||
|
|
0085fe73ad | ||
|
|
a238b62483 | ||
|
|
58022e7cca | ||
|
|
d2f046780d | ||
|
|
ec65c5761e | ||
|
|
0ed917cdc2 | ||
|
|
8373ab0509 | ||
|
|
8f7610345d | ||
|
|
8bd963ecaf | ||
|
|
dc52478c09 | ||
|
|
e1ec4b1235 | ||
|
|
7c68396c0d | ||
|
|
eae7d0c4a1 | ||
|
|
5055e86518 | ||
|
|
4b13ad692a | ||
|
|
7c81bb35ca | ||
|
|
5a82f672b5 | ||
|
|
0387d7666d | ||
|
|
392c78860d | ||
|
|
585c66100d | ||
|
|
8bb974829d | ||
|
|
6b3139940c | ||
|
|
9efb9b90d1 | ||
|
|
152558f5bc | ||
|
|
7ae4c5a1ab | ||
|
|
9fbf6bc213 | ||
|
|
143fa70f87 | ||
|
|
3ac701be0e | ||
|
|
8cb23cff29 | ||
|
|
ec6ba8e7ca | ||
|
|
df030ac51c | ||
|
|
4b03a84b00 | ||
|
|
5d0c41da6b | ||
|
|
990e9685d2 | ||
|
|
a7309b217e | ||
|
|
d7cf8fa9ab | ||
|
|
a78a169a17 | ||
|
|
d80a8629a6 | ||
|
|
14779bd467 | ||
|
|
eb80776988 | ||
|
|
fac9054f1b | ||
|
|
768d24d580 | ||
|
|
e299a4b630 | ||
|
|
bbf89815d6 | ||
|
|
cc6d552e3d | ||
|
|
27ceded331 | ||
|
|
3f279b2f1c | ||
|
|
acc4a900f5 | ||
|
|
f079fa82b2 | ||
|
|
7665720264 | ||
|
|
83705ab340 | ||
|
|
adea3dd83f | ||
|
|
9b52be9ba6 | ||
|
|
202349d88f | ||
|
|
18ab18958b | ||
|
|
2237d3b836 | ||
|
|
e2f17a770b | ||
|
|
40e6a2c4a3 | ||
|
|
a80e40a798 | ||
|
|
de8a733c77 | ||
|
|
c5d7a03859 | ||
|
|
0b82537700 | ||
|
|
7b8299c8dd | ||
|
|
dcbd0f8f2d | ||
|
|
402c00e230 | ||
|
|
0542c05b88 | ||
|
|
67e224c62f | ||
|
|
565b08846f | ||
|
|
80ad0e02ad | ||
|
|
b8ee5a4150 | ||
|
|
445dd155b7 | ||
|
|
abb3efca00 | ||
|
|
de0b317461 | ||
|
|
21758476d4 | ||
|
|
b69f78be30 | ||
|
|
89b9f52f1e | ||
|
|
02a67254cc | ||
|
|
120f56ac5f | ||
|
|
5b93212f43 | ||
|
|
c57415ea78 | ||
|
|
36dfbdff45 | ||
|
|
d07e454dfe | ||
|
|
40aa4d4dcd | ||
|
|
0be291e0d7 | ||
|
|
35d84390bd | ||
|
|
124efb2684 | ||
|
|
5c158e481b | ||
|
|
42b79d19c1 | ||
|
|
4521a25284 | ||
|
|
1933d47ba1 | ||
|
|
bb9e9b54cf | ||
|
|
eddbfb8ba3 | ||
|
|
2c55d6e220 | ||
|
|
472c49de25 | ||
|
|
016254d38c | ||
|
|
054fcd2deb | ||
|
|
d71aa10f62 | ||
|
|
1d0aef4522 | ||
|
|
71b673d241 | ||
|
|
dcf695c726 | ||
|
|
5e656603a5 | ||
|
|
b4426c095b | ||
|
|
5f32c97094 | ||
|
|
1c8cb7fa11 | ||
|
|
6bc606a9b1 | ||
|
|
d509b4caa1 | ||
|
|
12975d07ac | ||
|
|
3d512abaf7 | ||
|
|
a9d93c93d5 | ||
|
|
1b7601fdbb | ||
|
|
373013046d | ||
|
|
10d369d766 | ||
|
|
161280ce7d | ||
|
|
4e129d4ae2 | ||
|
|
2b6f1c061d | ||
|
|
cb19c7d9e7 | ||
|
|
224b120745 | ||
|
|
534dd42472 | ||
|
|
163976f85f | ||
|
|
d79a8808d7 | ||
|
|
1cba5a0774 | ||
|
|
6c76a7ce4e | ||
|
|
084d5cbc94 | ||
|
|
32b1df0f86 | ||
|
|
50718cb905 | ||
|
|
414c0c9438 | ||
|
|
362d5680e4 | ||
|
|
fed7a74a75 | ||
|
|
d276d3767f | ||
|
|
a07660f957 | ||
|
|
4d02ce4414 | ||
|
|
c1e3cfaacc | ||
|
|
c11385cf49 | ||
|
|
0b5064683f | ||
|
|
f3ede27f60 | ||
|
|
4fbb42833e | ||
|
|
3e76b7ff1c | ||
|
|
e901a3de55 | ||
|
|
4899ee14e2 | ||
|
|
3c04a326f4 | ||
|
|
a1ff3b05ed | ||
|
|
74e228688a | ||
|
|
75d454b6a6 | ||
|
|
6cffb888ea | ||
|
|
cf03215564 | ||
|
|
5b90743120 | ||
|
|
0fb91265b6 | ||
|
|
3cb7c489ee | ||
|
|
c65c378b7f | ||
|
|
cf9784afee | ||
|
|
8bd942c1b8 | ||
|
|
109e15b5a0 | ||
|
|
0fdd6bdc81 | ||
|
|
378b35265b | ||
|
|
b5e25e3857 | ||
|
|
9b2fc04959 | ||
|
|
ce8167a102 | ||
|
|
397d065a74 | ||
|
|
5e23817a3d | ||
|
|
396263f5c3 | ||
|
|
26d0760c0d | ||
|
|
11f090116f | ||
|
|
5e1c343be4 | ||
|
|
0e69432e3d | ||
|
|
94a26d9483 | ||
|
|
efa5bdc6df | ||
|
|
459969ddb2 | ||
|
|
ae5047b73f | ||
|
|
17f3db835c | ||
|
|
1845a515ca | ||
|
|
bf50178eb3 | ||
|
|
32675161c4 | ||
|
|
e257500e03 | ||
|
|
0add42b0a0 | ||
|
|
b14053df41 | ||
|
|
6a78864b6c | ||
|
|
b1fea45cd1 | ||
|
|
cef48482f1 | ||
|
|
a126a25d66 | ||
|
|
61c939059d | ||
|
|
5fa820a462 | ||
|
|
472124dab3 | ||
|
|
1e11b6c442 | ||
|
|
34a86852c6 | ||
|
|
52afbdbde9 | ||
|
|
888657cc97 | ||
|
|
d7bfddf35e | ||
|
|
9c435352b9 | ||
|
|
3fe55b5e51 | ||
|
|
cdabe8fd9e | ||
|
|
5390ca6250 | ||
|
|
61e84e4d34 | ||
|
|
1b82b7d2f2 | ||
|
|
49f59d82c9 | ||
|
|
e84e23f7ec | ||
|
|
0b9b49673e | ||
|
|
4512c6446d | ||
|
|
0fea6c6056 | ||
|
|
dd8449576f | ||
|
|
c3b2694834 | ||
|
|
d3ebd4a130 | ||
|
|
a326e31e57 | ||
|
|
945a3f3fc0 | ||
|
|
2a8fbb8fec | ||
|
|
c0f1b02d0b | ||
|
|
f2c557c3b7 | ||
|
|
d00a93f195 | ||
|
|
aa07969fc2 | ||
|
|
6dcd91c5ef | ||
|
|
c2e603ddd8 | ||
|
|
70c4b24519 | ||
|
|
51364a9d1b | ||
|
|
d9f4eb46d9 | ||
|
|
cd2d1a04d8 | ||
|
|
3f7a547d8a | ||
|
|
f254c27966 | ||
|
|
8a1c02c840 | ||
|
|
7f297a9d27 | ||
|
|
64ef33f09b | ||
|
|
cd4c586c3f | ||
|
|
0a9d4e398d | ||
|
|
d61beef7e0 | ||
|
|
7136b05019 | ||
|
|
87fa253059 | ||
|
|
04eb6fc451 | ||
|
|
15fc435d92 | ||
|
|
9d54e9e3c4 | ||
|
|
c73c7cb2a3 | ||
|
|
827ad18ef4 | ||
|
|
3fc562af6f | ||
|
|
87c42f1cd3 | ||
|
|
e93f22fbda | ||
|
|
8fa59cbb43 | ||
|
|
527766310f | ||
|
|
a02b0a1f4c | ||
|
|
4d93db51ee | ||
|
|
fa2e6e8189 | ||
|
|
0fc1871ddf | ||
|
|
197f82d829 | ||
|
|
4ad11724d1 | ||
|
|
6d53fd6f6e | ||
|
|
db47b0555d | ||
|
|
ebd7b7243a | ||
|
|
e0771beabc | ||
|
|
824d064710 | ||
|
|
336ddb67c8 | ||
|
|
9ad98beda7 | ||
|
|
702f836b40 | ||
|
|
d820a2356a | ||
|
|
fa1951642b | ||
|
|
42dc2d541a | ||
|
|
fdbbe5c1aa | ||
|
|
292bf1cbbc | ||
|
|
82769669ec | ||
|
|
29b211eefc | ||
|
|
e4965ab408 | ||
|
|
067a4f7cb1 | ||
|
|
739e91efa4 | ||
|
|
26d733ef59 | ||
|
|
32d6025e29 | ||
|
|
04706f2dcd | ||
|
|
d8a7c57b56 | ||
|
|
ad8769580a | ||
|
|
d51dd09a1f | ||
|
|
5ed4f9519f | ||
|
|
493a084745 | ||
|
|
9bd04526e8 | ||
|
|
90f0486334 | ||
|
|
e4f1b57bd0 | ||
|
|
c949bd3108 | ||
|
|
56f8008d85 | ||
|
|
c6a96ad7f3 | ||
|
|
328e6f518c | ||
|
|
8ec4a2cada | ||
|
|
9b318ec233 | ||
|
|
d96624c578 | ||
|
|
bf1d401566 | ||
|
|
a2c886a2d9 | ||
|
|
e76278cfd7 | ||
|
|
1028f85daa | ||
|
|
e706c914a8 | ||
|
|
f3b71ed7eb | ||
|
|
18b246c577 | ||
|
|
c205719563 | ||
|
|
02a5270c5a | ||
|
|
2ba244dd9c | ||
|
|
d33d0a232b | ||
|
|
5c4197debd | ||
|
|
41261d40b2 | ||
|
|
48ecceab54 | ||
|
|
0d9547a388 | ||
|
|
d2f59c5b3f | ||
|
|
85166bc706 | ||
|
|
bf26898645 | ||
|
|
1f2bcd5462 | ||
|
|
4e6cd4283c | ||
|
|
9aec5de105 | ||
|
|
dec49b05b8 | ||
|
|
29bcd2c96e | ||
|
|
97a9cab218 | ||
|
|
436af4dbca | ||
|
|
b08a2a2c0f | ||
|
|
b43dac24c4 | ||
|
|
bae0f0f579 | ||
|
|
8055c340f6 | ||
|
|
00c1210625 | ||
|
|
070b13c81c | ||
|
|
415c9639e7 | ||
|
|
996f8a6a4d | ||
|
|
e49e3e32e7 | ||
|
|
86a05ba6c0 | ||
|
|
651735f9a0 | ||
|
|
759389066f | ||
|
|
3577a2d6b8 | ||
|
|
b63886b879 | ||
|
|
466d3bc1b6 | ||
|
|
2a2c0f5d91 | ||
|
|
0c0d0ace4d | ||
|
|
a0ab10c23e | ||
|
|
0685260454 | ||
|
|
15662bd32f | ||
|
|
475816251b | ||
|
|
bf62d11a73 | ||
|
|
37d42d555d | ||
|
|
1fc522ec9c | ||
|
|
3a8a6aa429 | ||
|
|
e15ee23925 | ||
|
|
85657c9200 | ||
|
|
3f20e5af1d | ||
|
|
18e2838772 | ||
|
|
f023db8638 | ||
|
|
cb0b94c064 | ||
|
|
7087665ccc | ||
|
|
1793dd7f0b | ||
|
|
a61fd6d280 | ||
|
|
034a048c8a | ||
|
|
608fc4df9f | ||
|
|
5961bcaf86 | ||
|
|
fd5489458a | ||
|
|
cf5dd5496a | ||
|
|
ccffa03de2 | ||
|
|
ce0ab62c78 | ||
|
|
0bc5cbca74 | ||
|
|
a2aa9fae6b | ||
|
|
9d79c3f33d | ||
|
|
a26055c932 | ||
|
|
b296875c05 | ||
|
|
a54658d37f | ||
|
|
0a0e097bdf | ||
|
|
2df51bfe07 | ||
|
|
cb065152ea | ||
|
|
07fa14f00b | ||
|
|
ff1b17d18b | ||
|
|
c3d5778ae0 | ||
|
|
373ad1a399 | ||
|
|
6deed2d4a4 | ||
|
|
6324d78c32 | ||
|
|
9e14338b77 | ||
|
|
6e4614b6a4 | ||
|
|
0b52819090 | ||
|
|
404a78943d | ||
|
|
7215a9fa12 | ||
|
|
463c6957fc | ||
|
|
8ec3a52ef7 | ||
|
|
ab1efdda73 | ||
|
|
4f3b6f922d | ||
|
|
869a1d93cb | ||
|
|
5d468ccbf0 | ||
|
|
887ed5b9e9 | ||
|
|
5c1a6d7623 | ||
|
|
1b49ebf855 | ||
|
|
98155b61f1 | ||
|
|
60cdea5292 | ||
|
|
49e836b1ab | ||
|
|
5b83108dd1 | ||
|
|
a50de26c5d | ||
|
|
7b50c87858 | ||
|
|
a816a78e98 | ||
|
|
39694e65c0 | ||
|
|
2a41fa6281 | ||
|
|
49b8cfe267 | ||
|
|
20dfdf5c5d | ||
|
|
1e543dc286 | ||
|
|
06884720e5 | ||
|
|
1ea181524e | ||
|
|
b16ee3119d | ||
|
|
4e66693065 | ||
|
|
5f96b8e4c6 | ||
|
|
748a2f6530 | ||
|
|
6360ddbc77 | ||
|
|
eccc6700dc | ||
|
|
52ececba37 | ||
|
|
ffc65bcbf4 | ||
|
|
0da6bf4455 | ||
|
|
7063e684ba | ||
|
|
07363c6ecd | ||
|
|
5070840da9 | ||
|
|
4a25bcc5ee | ||
|
|
d43d7100af | ||
|
|
791cbbfb62 | ||
|
|
9b05dfdef1 | ||
|
|
b072e88bb3 | ||
|
|
af33d2b1cc | ||
|
|
d438d7510e | ||
|
|
1673aef74f | ||
|
|
b3bce10699 | ||
|
|
3ff922e12b | ||
|
|
3b718335ec | ||
|
|
f25ad512a7 |
15
.clang-format
Normal file
15
.clang-format
Normal file
@@ -0,0 +1,15 @@
|
||||
IndentWidth: 4
|
||||
UseCRLF: false
|
||||
IndentCaseLabels: true
|
||||
UseTab: ForIndentation
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Allman
|
||||
AllowShortBlocksOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: WithoutElse
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
44
.clang-tidy
44
.clang-tidy
@@ -1,21 +1,7 @@
|
||||
---
|
||||
# Configure clang-tidy for this project.
|
||||
|
||||
IndentWidth: 4
|
||||
UseCRLF: false
|
||||
IndentCaseLabels: true
|
||||
UseTab: UT_ForIndentation
|
||||
TabWidth: 4
|
||||
BreakBeforeBraces: Allman
|
||||
AllowShortBlocksOnASingleLine: SBS_Empty
|
||||
AllowShortIfStatementsOnASingleLine: SIS_WithoutElse
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeParens: SBPO_ControlStatementsExceptControlMacros
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
|
||||
|
||||
# Disabled:
|
||||
# -google-readability-namespace-comments the *_CLIENT_NS is a macro, and
|
||||
@@ -40,17 +26,19 @@ Checks: >
|
||||
|
||||
# Turn all the warnings from the checks above into errors.
|
||||
WarningsAsErrors: "*"
|
||||
|
||||
CheckOptions:
|
||||
- { key: readability-function-cognitive-complexity.Threshold, value: 100 }
|
||||
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: k }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.GlobalConstantPrefix, value: k }
|
||||
- { key: readability-identifier-naming.StaticConstantCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.StaticConstantPrefix, value: k }
|
||||
- { key: readability-function-cognitive-complexity.Threshold, value: 100 }
|
||||
- { key: readability-identifier-naming.StructCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.FunctionCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.VariableCase, value: lower_case }
|
||||
- { key: readability-identifier-naming.MacroDefinitionCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.EnumConstantCase, value: UPPER_CASE }
|
||||
- { key: readability-identifier-naming.ConstexprVariableCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.ConstexprVariablePrefix, value: k }
|
||||
- { key: readability-identifier-naming.GlobalConstantCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.GlobalConstantPrefix, value: k }
|
||||
- { key: readability-identifier-naming.StaticConstantCase, value: CamelCase }
|
||||
- { key: readability-identifier-naming.StaticConstantPrefix, value: k }
|
||||
- { key: readability-identifier-naming.MinimumParameterNameLength, value: 0 }
|
||||
|
||||
MinimumParameterNameLength: 0
|
||||
|
||||
1249
.github/workflows/main.yml
vendored
1249
.github/workflows/main.yml
vendored
File diff suppressed because it is too large
Load Diff
18
.gitignore
vendored
18
.gitignore
vendored
@@ -73,8 +73,15 @@ out/
|
||||
/cmake-build-debug/
|
||||
/cmake-build-release/
|
||||
|
||||
# Emacs files
|
||||
CMakeFiles/cmake.check_cache
|
||||
CMakeCache.txt
|
||||
|
||||
# etags(Emacs), ctags, gtags
|
||||
TAGS
|
||||
GPATH
|
||||
GRTAGS
|
||||
GTAGS
|
||||
tags
|
||||
|
||||
# Clangd LSP files
|
||||
/.cache/
|
||||
@@ -90,5 +97,10 @@ result
|
||||
|
||||
# tests
|
||||
/test/tmp/*
|
||||
/test/testrun
|
||||
/test/test_suite_runner
|
||||
testrun
|
||||
test_suite_runner
|
||||
|
||||
# patches, originals and rejects
|
||||
*.patch
|
||||
*.rej
|
||||
*.orig
|
||||
|
||||
253
CMakeLists.txt
253
CMakeLists.txt
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
set(C3_LLVM_MIN_VERSION 17)
|
||||
set(C3_LLVM_MAX_VERSION 22)
|
||||
set(C3_LLVM_DEFAULT_VERSION 19)
|
||||
set(C3_LLVM_DEFAULT_VERSION 21)
|
||||
|
||||
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR)
|
||||
message(FATAL_ERROR "In-tree build detected, please build in a separate directory")
|
||||
@@ -56,12 +56,30 @@ set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
# Use /MT or /MTd
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
||||
if(MSVC)
|
||||
message(STATUS "MSVC version ${MSVC_VERSION}")
|
||||
add_compile_options(/utf-8)
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
FetchContent_GetProperties(LLVM_Windows)
|
||||
if(NOT LLVM_Windows_URL MATCHES "msvcrt")
|
||||
set(MSVC_CRT_SUFFIX "")
|
||||
message(STATUS "Detected STATIC LLVM (libcmt)")
|
||||
else()
|
||||
set(MSVC_CRT_SUFFIX "DLL")
|
||||
message(STATUS "Detected DYNAMIC LLVM (msvcrt)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Force the Runtime to Release (/MT or /MD) even in Debug
|
||||
# This is required to match our RelWithDebInfo LLVM builds
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
|
||||
set_property(GLOBAL PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
|
||||
|
||||
add_compile_definitions(_ITERATOR_DEBUG_LEVEL=0)
|
||||
|
||||
# Suppresses the LNK4098 mismatch warning in Debug builds
|
||||
set(CMAKE_EXE_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG} /NODEFAULTLIB:libcmtd /NODEFAULTLIB:msvcrtd")
|
||||
else()
|
||||
add_compile_options(-gdwarf-3 -fno-exceptions)
|
||||
|
||||
@@ -77,13 +95,18 @@ set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
set(C3_USE_TB OFF CACHE BOOL "Use TB")
|
||||
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
|
||||
set(C3_LLD_INCLUDE_DIR "" CACHE STRING "Use custom LLD include directory")
|
||||
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
|
||||
set(C3_FETCH_LLVM OFF CACHE BOOL "Automatically download LLVM artifacts")
|
||||
set(C3_LLVM_TAG "llvm_21.x" CACHE STRING "Tag/Branch to download LLVM from")
|
||||
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
|
||||
set(TCC_LIB_PATH "/usr/lib/tcc/libtcc1.a" CACHE STRING "Use custom libtcc1.a path")
|
||||
|
||||
set(C3_OPTIONS
|
||||
C3_LINK_DYNAMIC
|
||||
C3_WITH_LLVM
|
||||
C3_FETCH_LLVM
|
||||
C3_LLVM_TAG
|
||||
C3_LLVM_VERSION
|
||||
C3_USE_MIMALLOC
|
||||
C3_MIMALLOC_TAG
|
||||
@@ -106,9 +129,6 @@ if(C3_USE_MIMALLOC)
|
||||
)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
if (NOT WIN32)
|
||||
find_package(CURL)
|
||||
endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
|
||||
@@ -136,55 +156,83 @@ if(C3_ENABLE_CLANGD_LSP)
|
||||
endif(C3_ENABLE_CLANGD_LSP)
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION})
|
||||
if(C3_FETCH_LLVM)
|
||||
# 1. Determine local platform ID
|
||||
if (WIN32)
|
||||
set(C3_LLVM_PLATFORM "windows-amd64")
|
||||
elseif (APPLE)
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(C3_LLVM_PLATFORM "darwin-aarch64")
|
||||
else()
|
||||
set(C3_LLVM_PLATFORM "darwin-amd64")
|
||||
endif()
|
||||
else() # Linux
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64")
|
||||
set(C3_LLVM_PLATFORM "linux-aarch64")
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "riscv64")
|
||||
set(C3_LLVM_PLATFORM "linux-riscv64")
|
||||
else()
|
||||
set(C3_LLVM_PLATFORM "linux-amd64")
|
||||
endif()
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows_debug)
|
||||
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
|
||||
else()
|
||||
message("Loading Windows LLVM libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(llvm_dir ${llvm_windows_SOURCE_DIR})
|
||||
endif()
|
||||
message("Loaded Windows LLVM libraries into ${llvm_dir}")
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
# 2. Determine if we want Debug or Release
|
||||
set(C3_LLVM_SUFFIX "")
|
||||
set(C3_LLVM_TYPE "Release")
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(C3_LLVM_SUFFIX "-dbg")
|
||||
set(C3_LLVM_TYPE "Debug")
|
||||
endif()
|
||||
|
||||
# 3. Construct URL
|
||||
set(C3_LLVM_ARTIFACT_NAME "llvm-${C3_LLVM_PLATFORM}${C3_LLVM_SUFFIX}")
|
||||
#set(C3_LLVM_URL "https://github.com/c3lang/c3-llvm/releases/download/${C3_LLVM_TAG}/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
|
||||
#set(C3_LLVM_URL "https://github.com//ManuLinares/llvm-custom-builds/releases/download/${C3_LLVM_TAG}/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
|
||||
|
||||
# We could also just set "latest" here to always fetch the latest release
|
||||
set(C3_LLVM_URL "https://github.com/c3lang/llvm-for-c3/releases/latest/download/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
|
||||
|
||||
message(STATUS "Fetching ${C3_LLVM_TYPE} LLVM artifact for ${C3_LLVM_PLATFORM}...")
|
||||
message(STATUS "URL: ${C3_LLVM_URL}")
|
||||
|
||||
FetchContent_Declare(
|
||||
LLVM_Artifact
|
||||
URL ${C3_LLVM_URL}
|
||||
)
|
||||
FetchContent_MakeAvailable(LLVM_Artifact)
|
||||
|
||||
# 4. Point CMake to the fetched location
|
||||
set(llvm_dir ${llvm_artifact_SOURCE_DIR})
|
||||
set(CMAKE_PREFIX_PATH ${llvm_dir} ${CMAKE_PREFIX_PATH})
|
||||
set(LLVM_DIR "${llvm_dir}/lib/cmake/llvm")
|
||||
set(LLD_DIR "${llvm_dir}/lib/cmake/lld")
|
||||
|
||||
# TEST: For Windows, we might need to add the bin dir to prefix path for find_package to work well
|
||||
if (WIN32)
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
endif()
|
||||
|
||||
find_package(LLVM REQUIRED CONFIG NO_DEFAULT_PATH)
|
||||
find_package(LLD REQUIRED CONFIG NO_DEFAULT_PATH)
|
||||
else()
|
||||
# Add paths for LLVM CMake files of version 19 and higher as they follow a new installation
|
||||
# layout and are now in /usr/lib/llvm/*/lib/cmake/llvm/ rather than /usr/lib/cmake/llvm/
|
||||
#
|
||||
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
|
||||
# the newest version will always be found first.
|
||||
c3_print_variables(CMAKE_PREFIX_PATH)
|
||||
if (DEFINED LLVM_DIR)
|
||||
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
# Legacy MSVC path if someone explicitly disabled fetching but is on MSVC
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
# Default system search
|
||||
file (GLOB LLVM_CMAKE_PATHS "/usr/lib/llvm/*/lib/cmake/llvm/")
|
||||
list (APPEND CMAKE_PREFIX_PATH ${LLVM_CMAKE_PATHS} "/usr/lib/")
|
||||
message(STATUS "No LLVM_DIR specified, searching default directories ${CMAKE_PREFIX_PATH}")
|
||||
endif()
|
||||
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (EXISTS /opt/homebrew/lib)
|
||||
if (NOT C3_FETCH_LLVM AND EXISTS /opt/homebrew/lib)
|
||||
list(APPEND LLVM_LIBRARY_DIRS /opt/homebrew/lib)
|
||||
endif()
|
||||
|
||||
@@ -260,20 +308,25 @@ if(C3_WITH_LLVM)
|
||||
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a liblldCOFF.dylib lldCOFF.lib liblldCOFF.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a liblldCommon.dylib lldCommon.lib liblldCommon.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_ELF NAMES lldELF.a liblldELF.a liblldELF.dylib lldELF.lib liblldELF.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
|
||||
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a liblldMachO.dylib lldMachO.lib liblldMachO.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
set(LLD_MACHO "")
|
||||
endif()
|
||||
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a liblldMinGW.dylib lldMinGW.lib liblldMinGW.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a liblldWasm.dylib lldWasm.lib liblldWasm.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
else()
|
||||
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
#find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
|
||||
if(UNIX AND NOT WIN32)
|
||||
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
|
||||
else()
|
||||
find_library(LLVM NAMES libLLVM.a LLVM.lib PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
|
||||
endif()
|
||||
set(llvm_libs ${LLVM})
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
@@ -301,17 +354,36 @@ if(C3_WITH_LLVM)
|
||||
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
# Unused
|
||||
# ${RT_UBSAN_DYNAMIC}
|
||||
# ${RT_LSAN_DYNAMIC}
|
||||
)
|
||||
if (llvm_dir)
|
||||
file(GLOB_RECURSE RT_ASAN_DYNAMIC "${llvm_dir}/*libclang_rt.asan_osx_dynamic.dylib")
|
||||
file(GLOB_RECURSE RT_TSAN_DYNAMIC "${llvm_dir}/*libclang_rt.tsan_osx_dynamic.dylib")
|
||||
endif()
|
||||
|
||||
if (NOT RT_ASAN_DYNAMIC OR NOT RT_TSAN_DYNAMIC)
|
||||
# Fallback to searching in LLVM_LIBRARY_DIRS (for non-fetched LLVM)
|
||||
find_file(RT_ASAN_DYNAMIC_PATH NAMES libclang_rt.asan_osx_dynamic.dylib PATHS ${LLVM_LIBRARY_DIRS} PATH_SUFFIXES "clang/${LLVM_MAJOR_VERSION}/lib/darwin" "clang/${LLVM_PACKAGE_VERSION}/lib/darwin")
|
||||
find_file(RT_TSAN_DYNAMIC_PATH NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS ${LLVM_LIBRARY_DIRS} PATH_SUFFIXES "clang/${LLVM_MAJOR_VERSION}/lib/darwin" "clang/${LLVM_PACKAGE_VERSION}/lib/darwin")
|
||||
if (RT_ASAN_DYNAMIC_PATH)
|
||||
set(RT_ASAN_DYNAMIC ${RT_ASAN_DYNAMIC_PATH})
|
||||
endif()
|
||||
if (RT_TSAN_DYNAMIC_PATH)
|
||||
set(RT_TSAN_DYNAMIC ${RT_TSAN_DYNAMIC_PATH})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (RT_ASAN_DYNAMIC)
|
||||
list(GET RT_ASAN_DYNAMIC 0 RT_ASAN_DYNAMIC)
|
||||
endif()
|
||||
if (RT_TSAN_DYNAMIC)
|
||||
list(GET RT_TSAN_DYNAMIC 0 RT_TSAN_DYNAMIC)
|
||||
endif()
|
||||
|
||||
if (RT_ASAN_DYNAMIC AND RT_TSAN_DYNAMIC)
|
||||
set(sanitizer_runtime_libraries
|
||||
${RT_ASAN_DYNAMIC}
|
||||
${RT_TSAN_DYNAMIC}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Linking to llvm libs ${llvm_libs}")
|
||||
@@ -389,10 +461,12 @@ add_executable(c3c
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/utils/unzipper.c
|
||||
src/utils/msi.c
|
||||
src/compiler/c_codegen.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/methodtable.c
|
||||
src/compiler/mac_support.c
|
||||
src/utils/fetch_msvc.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c
|
||||
@@ -435,13 +509,22 @@ if(C3_WITH_LLVM)
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
if (MSVC)
|
||||
# Use the same detected CRT for the wrapper
|
||||
set_target_properties(c3c_wrappers PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
|
||||
set_target_properties(miniz PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
|
||||
|
||||
target_compile_options(c3c PRIVATE
|
||||
"$<$<CONFIG:Debug>:/EHa>"
|
||||
"$<$<CONFIG:Release>:/EHsc>")
|
||||
endif()
|
||||
|
||||
if(C3_LLD_INCLUDE_DIR)
|
||||
target_include_directories(c3c_wrappers PRIVATE ${C3_LLD_INCLUDE_DIR})
|
||||
endif()
|
||||
else()
|
||||
target_sources(c3c PRIVATE src/utils/hostinfo.c)
|
||||
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
|
||||
target_link_libraries(c3c m)
|
||||
endif()
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
@@ -491,7 +574,7 @@ else()
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
|
||||
target_link_libraries(c3c miniz c3c_wrappers)
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
@@ -499,11 +582,11 @@ if(C3_WITH_LLVM)
|
||||
target_include_directories(c3c_wrappers PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/wrapper/include/")
|
||||
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
target_link_libraries(c3c_wrappers PUBLIC ${lld_libs} ${llvm_libs})
|
||||
|
||||
else()
|
||||
|
||||
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
|
||||
target_link_libraries(c3c miniz ${lld_libs} ${llvm_libs})
|
||||
|
||||
endif()
|
||||
|
||||
@@ -520,12 +603,11 @@ if(MINGW)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608")
|
||||
endif ()
|
||||
|
||||
if (CURL_FOUND)
|
||||
target_link_libraries(c3c ${CURL_LIBRARIES})
|
||||
target_include_directories(c3c PRIVATE ${CURL_INCLUDE_DIRS})
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
|
||||
else()
|
||||
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
|
||||
if (NOT WIN32)
|
||||
# For dlopen support
|
||||
if (CMAKE_DL_LIBS)
|
||||
target_link_libraries(c3c ${CMAKE_DL_LIBS})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -552,13 +634,21 @@ if(MSVC)
|
||||
endif()
|
||||
|
||||
if(C3_WITH_LLVM)
|
||||
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
|
||||
set(sanitizer_runtime_libraries
|
||||
${clang_lib_dir}/clang_rt.asan-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
# The the sanitizer libs are in the folder "lib/clang/21/lib/windows/" but use the find anyway
|
||||
file(GLOB_RECURSE FOUND_ASAN_LIB "${llvm_dir}/*clang_rt.asan_dynamic-x86_64.lib")
|
||||
if(FOUND_ASAN_LIB)
|
||||
list(GET FOUND_ASAN_LIB 0 _asan_path)
|
||||
get_filename_component(_asan_dir "${_asan_path}" DIRECTORY)
|
||||
set(sanitizer_runtime_libraries
|
||||
${_asan_dir}/clang_rt.asan_dynamic-x86_64.lib
|
||||
${_asan_dir}/clang_rt.asan_dynamic-x86_64.dll
|
||||
${_asan_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
|
||||
message(STATUS "Found Sanitizer binaries at: ${_asan_dir}")
|
||||
else()
|
||||
message(WARNING "Could not find sanitizer runtime libraries in ${llvm_dir}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
else()
|
||||
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
|
||||
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
|
||||
@@ -572,6 +662,7 @@ else()
|
||||
-Wno-unused-function
|
||||
-Wno-unused-variable
|
||||
-Wno-unused-parameter
|
||||
-Wno-char-subscripts
|
||||
)
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
endif()
|
||||
@@ -611,8 +702,10 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
|
||||
|
||||
if (APPLE)
|
||||
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
|
||||
# We set DYLD_LIBRARY_PATH so the tools (which might be dynamic) can find libLLVM.dylib in the artifact
|
||||
string(REPLACE ";" ":" _dyld_path "${LLVM_LIBRARY_DIRS}")
|
||||
add_custom_command(TARGET c3c POST_BUILD
|
||||
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
COMMAND ${CMAKE_COMMAND} -E env "DYLD_LIBRARY_PATH=${_dyld_path}:$ENV{DYLD_LIBRARY_PATH}" find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
|
||||
54
README.md
54
README.md
@@ -8,11 +8,11 @@ for programmers who like C.
|
||||
|
||||
Precompiled binaries for the following operating systems are available:
|
||||
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
- OpenBSD x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz), [install instructions](#installing-on-openbsd-with-precompiled-binaries).
|
||||
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
|
||||
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
|
||||
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
|
||||
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
|
||||
- OpenBSD x64 [download](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd.tar.gz), [install instructions](#installing-on-openbsd-with-precompiled-binaries).
|
||||
|
||||
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
|
||||
|
||||
@@ -36,10 +36,10 @@ whole new language.
|
||||
|
||||
### Example code
|
||||
|
||||
The following code shows [generic modules](https://c3-lang.org/generic-programming/generics/) (more examples can be found at https://c3-lang.org/language-overview/examples/).
|
||||
The following code shows [generics](https://c3-lang.org/generic-programming/generics/) (more examples can be found at https://c3-lang.org/language-overview/examples/).
|
||||
|
||||
```cpp
|
||||
module stack {Type};
|
||||
```c3
|
||||
module stack <Type>;
|
||||
// Above: the parameterized type is applied to the entire module.
|
||||
|
||||
struct Stack
|
||||
@@ -78,7 +78,7 @@ fn bool Stack.empty(Stack* this)
|
||||
|
||||
Testing it out:
|
||||
|
||||
```cpp
|
||||
```c3
|
||||
import stack;
|
||||
|
||||
// Define our new types, the first will implicitly create
|
||||
@@ -142,7 +142,7 @@ fn void main()
|
||||
|
||||
### Current status
|
||||
|
||||
The current stable version of the compiler is **version 0.7.7**.
|
||||
The current stable version of the compiler is **version 0.7.10**.
|
||||
|
||||
The upcoming 0.7.x releases will focus on expanding the standard library,
|
||||
fixing bugs and improving compile time analysis.
|
||||
@@ -162,6 +162,8 @@ The compiler is currently verified to compile on Linux, OpenBSD, Windows and Mac
|
||||
| MacOS x64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
|
||||
| MacOS Aarch64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
|
||||
| iOS Aarch64 | No | Untested | Untested | Yes | Yes | Yes* |
|
||||
| Android Aarch64 | No | Untested | Untested | Untested | Untested | Yes* |
|
||||
| Android x64 | No | Untested | Untested | Untested | Untested | Yes* |
|
||||
| Linux x86 | Yes | Yes | Yes | Yes | Yes | Yes* |
|
||||
| Linux x64 | Yes | Yes | Yes | Yes | Yes | Yes* |
|
||||
| Linux Aarch64 | Yes | Yes | Yes | Yes | Yes | Yes* |
|
||||
@@ -172,6 +174,7 @@ The compiler is currently verified to compile on Linux, OpenBSD, Windows and Mac
|
||||
| ELF freestanding Aarch64 | No | Untested | No | No | No | Yes* |
|
||||
| ELF freestanding Riscv64 | No | Untested | No | No | No | Untested |
|
||||
| ELF freestanding Riscv32 | No | Untested | No | No | No | Untested |
|
||||
| ELF freestanding Xtensa* | No | Untested | No | No | No | Untested |
|
||||
| FreeBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| FreeBSD x64 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
| NetBSD x86 | Untested | Untested | No | Yes | Untested | Yes* |
|
||||
@@ -184,7 +187,8 @@ The compiler is currently verified to compile on Linux, OpenBSD, Windows and Mac
|
||||
|
||||
*\* Inline asm is still a work in progress*<br>
|
||||
*\* OpenBSD 7.7 is the only tested version*<br>
|
||||
*\* OpenBSD has limited stacktrace, needs to be tested further*
|
||||
*\* OpenBSD has limited stacktrace, needs to be tested further*<br>
|
||||
*\* Xtensa support is enabled by compiling with `-DXTENSA_ENABLE`. The [espressif llvm fork](https://github.com/espressif/llvm-project) is recommended for best compatibility*
|
||||
|
||||
More platforms will be supported in the future.
|
||||
|
||||
@@ -193,7 +197,7 @@ More platforms will be supported in the future.
|
||||
- If you wish to contribute with ideas, please file issues or discuss on Discord.
|
||||
- Interested in contributing to the stdlib? Please get in touch on Discord.
|
||||
- Compilation instructions for other Linux and Unix variants are appreciated.
|
||||
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
|
||||
- Would you like to contribute bindings to some library? It would be nice to have support for SDL3 and more. If you have created some bindings, please submit them to https://github.com/c3lang/vendor.
|
||||
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
|
||||
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
|
||||
- Do you have some specific area you have deep knowledge of and could help make C3 even better at doing? File or comment on issues.
|
||||
@@ -203,8 +207,8 @@ More platforms will be supported in the future.
|
||||
This installs the latest prerelease build, as opposed to the latest released version.
|
||||
|
||||
#### Installing on Windows with precompiled binaries
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-windows-debug.zip))
|
||||
1. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-windows-debug.zip))
|
||||
2. Unzip exe and standard lib.
|
||||
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
|
||||
4. Run `c3c.exe`.
|
||||
@@ -227,8 +231,8 @@ If you don't have Visual Studio 17 installed you can either do so, or run the `m
|
||||
|
||||
|
||||
#### Installing on Debian with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-linux-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
@@ -247,23 +251,23 @@ curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/instal
|
||||
```
|
||||
|
||||
#### Installing on Ubuntu with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-ubuntu-20-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
#### Installing on MacOS with precompiled binaries
|
||||
1. Make sure you have XCode with command line tools installed.
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-macos-debug.zip))
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-macos-debug.zip))
|
||||
3. Unzip executable and standard lib.
|
||||
4. Run `./c3c`.
|
||||
|
||||
(*Note that there is a known issue with debug symbol generation on MacOS 13, see [issue #1086](https://github.com/c3lang/c3c/issues/1086))
|
||||
|
||||
#### Installing on OpenBSD with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-openbsd-debug.tar.gz))
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease-tag/c3-openbsd-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
@@ -382,7 +386,7 @@ scoop install c3
|
||||
#### Getting started with a "hello world"
|
||||
|
||||
Create a `main.c3` file with:
|
||||
```c++
|
||||
```c3
|
||||
module hello_world;
|
||||
import std::io;
|
||||
|
||||
@@ -511,7 +515,7 @@ provide the link path to the LLVM CMake directories, e.g. `cmake -B build -S . -
|
||||
|
||||
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
|
||||
libcurl is needed. The CMake script should detect it if it is available. Note that
|
||||
this functionality is non-essential and it is perfectly fine to user the compiler without it.*
|
||||
this functionality is non-essential and it is perfectly fine to use the compiler without it.*
|
||||
|
||||
#### Licensing
|
||||
|
||||
@@ -521,7 +525,7 @@ which is licensed under LGPL 3.0.
|
||||
|
||||
This means you are free to use all parts of standard library,
|
||||
tests, benchmarks, grammar, examples and so on under the MIT license, including
|
||||
using those libraries and tests if your build your own C3 compiler.
|
||||
using those libraries and tests if you build your own C3 compiler.
|
||||
|
||||
#### Editor plugins
|
||||
|
||||
|
||||
51
benchmarks/stdlib/compression/deflate.c3
Normal file
51
benchmarks/stdlib/compression/deflate.c3
Normal file
@@ -0,0 +1,51 @@
|
||||
module deflate_benchmarks;
|
||||
import std::compression::deflate;
|
||||
|
||||
const uint SMALL_ITERATIONS = 50000;
|
||||
const uint LARGE_ITERATIONS = 100;
|
||||
|
||||
// Data to compress
|
||||
const char[] SMALL_DATA = { [0..1023] = 'A' };
|
||||
const char[] LARGE_DATA = { [0..1048575] = 'B' };
|
||||
|
||||
char[] small_compressed;
|
||||
char[] large_compressed;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
small_compressed = deflate::compress(mem, SMALL_DATA)!!;
|
||||
large_compressed = deflate::compress(mem, LARGE_DATA)!!;
|
||||
set_benchmark_warmup_iterations(2);
|
||||
set_benchmark_max_iterations(10);
|
||||
|
||||
set_benchmark_func_iterations($qnameof(deflate_compress_small), SMALL_ITERATIONS);
|
||||
set_benchmark_func_iterations($qnameof(deflate_decompress_small), SMALL_ITERATIONS);
|
||||
set_benchmark_func_iterations($qnameof(deflate_compress_large), LARGE_ITERATIONS);
|
||||
set_benchmark_func_iterations($qnameof(deflate_decompress_large), LARGE_ITERATIONS);
|
||||
}
|
||||
|
||||
// =======================================================================================
|
||||
module deflate_benchmarks @benchmark;
|
||||
|
||||
import std::compression::deflate;
|
||||
import std::core::mem;
|
||||
|
||||
fn void deflate_compress_small() => @pool()
|
||||
{
|
||||
char[]? compressed = deflate::compress(tmem, SMALL_DATA);
|
||||
}
|
||||
|
||||
fn void deflate_decompress_small() => @pool()
|
||||
{
|
||||
char[]? decompressed = deflate::decompress(tmem, small_compressed);
|
||||
}
|
||||
|
||||
fn void deflate_compress_large() => @pool()
|
||||
{
|
||||
char[]? compressed = deflate::compress(tmem, LARGE_DATA);
|
||||
}
|
||||
|
||||
fn void deflate_decompress_large() => @pool()
|
||||
{
|
||||
char[]? decompressed = deflate::decompress(tmem, large_compressed);
|
||||
}
|
||||
39
benchmarks/stdlib/crypto/chacha20.c3
Normal file
39
benchmarks/stdlib/crypto/chacha20.c3
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module chacha20_benchmarks;
|
||||
|
||||
import std::crypto::chacha20;
|
||||
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(1024);
|
||||
}
|
||||
|
||||
const char[] KEY = x'98bef1469be7269837a45bfbc92a5a6ac762507cf96443bf33b96b1bd4c6f8f6';
|
||||
const char[] NONCE = x'44e792d63335abb1582e9253';
|
||||
const uint COUNTER = 42;
|
||||
|
||||
char[] one_mb @align(ulong.sizeof) = { [0..1024*1024] = 0xA5 };
|
||||
|
||||
// This doesn't test both encryption + decryption, because it's a symmetric operation that shares
|
||||
// a single common data transformation. Testing one limb is enough.
|
||||
fn void gogo_chacha20() @benchmark
|
||||
{
|
||||
chacha20::encrypt_mut(one_mb[..], KEY, NONCE, COUNTER);
|
||||
}
|
||||
|
||||
// Check what the speed of an unligned buffer looks like.
|
||||
fn void gogo_chacha20_unaligned() @benchmark => @pool()
|
||||
{
|
||||
char[] copy = mem::talloc_array(char, one_mb.len + 3);
|
||||
char[] im_off_slightly = copy[3..];
|
||||
copy[3..] = one_mb[..];
|
||||
assert((usz)im_off_slightly.ptr % usz.sizeof > 0);
|
||||
|
||||
runtime::@start_benchmark();
|
||||
chacha20::encrypt_mut(im_off_slightly, KEY, NONCE, COUNTER);
|
||||
runtime::@end_benchmark();
|
||||
}
|
||||
113
benchmarks/stdlib/crypto/crypto_shootout.c3
Normal file
113
benchmarks/stdlib/crypto/crypto_shootout.c3
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module crypto_hash_benchmarks;
|
||||
|
||||
import std::collections::pair;
|
||||
|
||||
|
||||
const usz COMMON_ITERATIONS = 1 << 17;
|
||||
|
||||
char* common_1mib_ptr;
|
||||
char[] common_16;
|
||||
char[] common_256;
|
||||
char[] common_4kib;
|
||||
char[] common_1mib;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(COMMON_ITERATIONS + 3);
|
||||
|
||||
common_1mib_ptr = mem::alloc_array(char, 1024*1024);
|
||||
common_1mib = common_1mib_ptr[:1024*1024];
|
||||
common_1mib[..] = 0xA5;
|
||||
|
||||
common_16 = common_1mib[:16];
|
||||
common_256 = common_1mib[:256];
|
||||
common_4kib = common_1mib[:4096];
|
||||
|
||||
static String[] function_prefixes = {
|
||||
$qnameof(md5_16)[..^4],
|
||||
$qnameof(sha1_16)[..^4],
|
||||
$qnameof(sha2_256_16)[..^4],
|
||||
$qnameof(sha2_512_16)[..^4],
|
||||
//$qnameof(blake2s_256_16)[..^4],
|
||||
//$qnameof(blake2b_256_16)[..^4],
|
||||
$qnameof(blake3_16)[..^4],
|
||||
$qnameof(ripemd_160_16)[..^4],
|
||||
$qnameof(whirlpool_16)[..^4],
|
||||
$qnameof(streebog_256_16)[..^4],
|
||||
$qnameof(streebog_512_16)[..^4],
|
||||
};
|
||||
|
||||
static Pair{ String, uint }[] to_iters = {
|
||||
{ "_4kib", 1 << 15 },
|
||||
{ "_1mib", 1024 },
|
||||
};
|
||||
|
||||
foreach (p : to_iters)
|
||||
{
|
||||
foreach (name : function_prefixes) set_benchmark_func_iterations(name.tconcat(p.first), p.second);
|
||||
}
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(common_1mib_ptr);
|
||||
}
|
||||
|
||||
|
||||
// =======================================================================================
|
||||
module crypto_hash_benchmarks @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
|
||||
fn void md5_16() => md5::hash(common_16);
|
||||
fn void sha1_16() => sha1::hash(common_16);
|
||||
fn void sha2_256_16() => sha256::hash(common_16);
|
||||
fn void sha2_512_16() => sha512::hash(common_16);
|
||||
//fn void blake2s_256_16() => blake2::s(256, common_16);
|
||||
//fn void blake2b_256_16() => blake2::b(256, common_16);
|
||||
fn void blake3_16() => blake3::hash(common_16);
|
||||
fn void ripemd_160_16() => ripemd::hash{160}(common_16);
|
||||
fn void whirlpool_16() => whirlpool::hash(common_16);
|
||||
fn void streebog_256_16() => streebog::hash_256(common_16);
|
||||
fn void streebog_512_16() => streebog::hash_512(common_16);
|
||||
|
||||
fn void md5_256() => md5::hash(common_256);
|
||||
fn void sha1_256() => sha1::hash(common_256);
|
||||
fn void sha2_256_256() => sha256::hash(common_256);
|
||||
fn void sha2_512_256() => sha512::hash(common_256);
|
||||
//fn void blake2s_256_256() => blake2::s(256, common_256);
|
||||
//fn void blake2b_256_256() => blake2::b(256, common_256);
|
||||
fn void blake3_256() => blake3::hash(common_256);
|
||||
fn void ripemd_160_256() => ripemd::hash{160}(common_256);
|
||||
fn void whirlpool_256() => whirlpool::hash(common_256);
|
||||
fn void streebog_256_256() => streebog::hash_256(common_256);
|
||||
fn void streebog_512_256() => streebog::hash_512(common_256);
|
||||
|
||||
fn void md5_4kib() => md5::hash(common_4kib);
|
||||
fn void sha1_4kib() => sha1::hash(common_4kib);
|
||||
fn void sha2_256_4kib() => sha256::hash(common_4kib);
|
||||
fn void sha2_512_4kib() => sha512::hash(common_4kib);
|
||||
//fn void blake2s_256_4kib() => blake2::s(256, common_4kib);
|
||||
//fn void blake2b_256_4kib() => blake2::b(256, common_4kib);
|
||||
fn void blake3_4kib() => blake3::hash(common_4kib);
|
||||
fn void ripemd_160_4kib() => ripemd::hash{160}(common_4kib);
|
||||
fn void whirlpool_4kib() => whirlpool::hash(common_4kib);
|
||||
fn void streebog_256_4kib() => streebog::hash_256(common_4kib);
|
||||
fn void streebog_512_4kib() => streebog::hash_512(common_4kib);
|
||||
|
||||
fn void md5_1mib() => md5::hash(common_1mib);
|
||||
fn void sha1_1mib() => sha1::hash(common_1mib);
|
||||
fn void sha2_256_1mib() => sha256::hash(common_1mib);
|
||||
fn void sha2_512_1mib() => sha512::hash(common_1mib);
|
||||
//fn void blake2s_256_1mib() => blake2::s(256, common_1mib);
|
||||
//fn void blake2b_256_1mib() => blake2::b(256, common_1mib);
|
||||
fn void blake3_1mib() => blake3::hash(common_1mib);
|
||||
fn void ripemd_160_1mib() => ripemd::hash{160}(common_1mib);
|
||||
fn void whirlpool_1mib() => whirlpool::hash(common_1mib);
|
||||
fn void streebog_256_1mib() => streebog::hash_256(common_1mib);
|
||||
fn void streebog_512_1mib() => streebog::hash_512(common_1mib);
|
||||
57
benchmarks/stdlib/hash/blake3.c3
Normal file
57
benchmarks/stdlib/hash/blake3.c3
Normal file
@@ -0,0 +1,57 @@
|
||||
module blake3_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(128);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module blake3_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void blake3_hash()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = blake3::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha512::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_whirlpool()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = whirlpool::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
41
benchmarks/stdlib/hash/md5.c3
Normal file
41
benchmarks/stdlib/hash/md5.c3
Normal file
@@ -0,0 +1,41 @@
|
||||
module md5_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(128);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module md5_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void md5_hash()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = md5::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
76
benchmarks/stdlib/hash/ripemd.c3
Normal file
76
benchmarks/stdlib/hash/ripemd.c3
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module ripemd_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(256);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module ripemd_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void ripemd_128()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = ripemd::hash{128}(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void ripemd_160()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = ripemd::hash{160}(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void ripemd_256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = ripemd::hash{256}(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void ripemd_320()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = ripemd::hash{320}(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_whirlpool()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = whirlpool::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
51
benchmarks/stdlib/hash/streebog.c3
Normal file
51
benchmarks/stdlib/hash/streebog.c3
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module streebog_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(256);
|
||||
}
|
||||
|
||||
const char[] INPUT = { [0..1024*1024] = 0xA5 };
|
||||
const char[*] EXPECTED_256 = x'694676905b7cf099755db1cc186f741f0fd1877aaaa4badcbfb305537f986971';
|
||||
const char[*] EXPECTED_512 = x'fe21d08857ea97e79035d1e5c9ba5130786e8d1875bc74d628349560d94d6bdff0b0dcd2f6347eb8b3f0239b6cca76b5028c0ff45f631fcdf77b1d551dd079f3';
|
||||
|
||||
|
||||
module streebog_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void get_in_the_bog_256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = streebog::hash_256(INPUT);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void get_in_the_bog_512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = streebog::hash_512(INPUT);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(INPUT);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_whirlpool()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = whirlpool::hash(INPUT);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
57
benchmarks/stdlib/hash/whirlpool.c3
Normal file
57
benchmarks/stdlib/hash/whirlpool.c3
Normal file
@@ -0,0 +1,57 @@
|
||||
module whirlpool_bench;
|
||||
|
||||
fn void initialize_bench() @init
|
||||
{
|
||||
set_benchmark_warmup_iterations(3);
|
||||
set_benchmark_max_iterations(128);
|
||||
|
||||
input = mem::alloc_array(char, BUFSZ);
|
||||
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
|
||||
input_slice = input[:BUFSZ];
|
||||
}
|
||||
|
||||
fn void teardown_bench() @finalizer
|
||||
{
|
||||
mem::free(input);
|
||||
input = null;
|
||||
}
|
||||
|
||||
char* input;
|
||||
char[] input_slice;
|
||||
const usz BUFSZ = 1024 * 1024;
|
||||
|
||||
module whirlpool_bench @benchmark;
|
||||
|
||||
import std::hash;
|
||||
|
||||
fn void whirlpool_hash()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = whirlpool::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha256()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha256::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_sha512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = sha512::hash(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
|
||||
fn void compared_with_streebog_512()
|
||||
{
|
||||
runtime::@start_benchmark();
|
||||
char[*] myset = streebog::hash_512(input_slice);
|
||||
runtime::@end_benchmark();
|
||||
mem::zero_volatile(myset[..]);
|
||||
}
|
||||
@@ -30,7 +30,8 @@ chmod -R 777 build bin
|
||||
exec $DOCKER run -i --rm \
|
||||
-v "$PWD":/home/c3c/source \
|
||||
-w /home/c3c/source $IMAGE bash -c \
|
||||
"cmake -S . -B build \
|
||||
"git config --global --add safe.directory /home/c3c/source && \
|
||||
cmake -S . -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
|
||||
-DCMAKE_C_COMPILER=clang-$LLVM_VERSION \
|
||||
|
||||
@@ -2,48 +2,44 @@ ARG UBUNTU_VERSION=22.04
|
||||
FROM ubuntu:${UBUNTU_VERSION}
|
||||
|
||||
ARG LLVM_VERSION=18
|
||||
ENV LLVM_DEV_VERSION=20
|
||||
ARG CMAKE_VERSION=3.20.0
|
||||
|
||||
ARG CMAKE_VERSION=3.20
|
||||
# Prevent interactive prompts during apt install
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev && \
|
||||
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
|
||||
RUN for i in 1 2 3; do apt-get update && break || sleep 2; done && \
|
||||
apt-get install -y --fix-missing \
|
||||
wget gnupg software-properties-common lsb-release \
|
||||
zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev git && \
|
||||
CODENAME=$(lsb_release -cs) && \
|
||||
ARCH=$(uname -m) && \
|
||||
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-${ARCH}.sh && \
|
||||
mkdir -p /opt/cmake && \
|
||||
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
|
||||
rm cmake-${CMAKE_VERSION}-linux-x86_64.sh && \
|
||||
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
|
||||
sh cmake-${CMAKE_VERSION}-linux-${ARCH}.sh --prefix=/opt/cmake --skip-license && \
|
||||
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake && \
|
||||
rm cmake-${CMAKE_VERSION}-linux-${ARCH}.sh
|
||||
|
||||
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
|
||||
if [ "${LLVM_VERSION}" -lt 18 ]; then \
|
||||
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
|
||||
libpolly-${LLVM_VERSION}-dev \
|
||||
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev libmlir-${LLVM_VERSION} \
|
||||
libmlir-${LLVM_VERSION}-dev mlir-${LLVM_VERSION}-tools; \
|
||||
elif [ "${LLVM_VERSION}" -lt "${LLVM_DEV_VERSION}" ]; then \
|
||||
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
|
||||
libpolly-${LLVM_VERSION}-dev \
|
||||
clang-${LLVM_VERSION} clang++-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
|
||||
RUN CODENAME=$(lsb_release -cs) && \
|
||||
for i in 1 2; do wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key && break || sleep 2; done | apt-key add - && \
|
||||
if [ "${LLVM_VERSION}" -ge 16 ]; then \
|
||||
add-apt-repository "deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME}-${LLVM_VERSION} main"; \
|
||||
else \
|
||||
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" && \
|
||||
apt-get update && \
|
||||
apt-get install -y -t llvm-toolchain-focal \
|
||||
libpolly-${LLVM_VERSION}-dev \
|
||||
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
|
||||
add-apt-repository "deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME} main"; \
|
||||
fi && \
|
||||
for i in 1 2 3; do apt-get update && break || sleep 2; done && \
|
||||
apt-get install -y --fix-missing \
|
||||
clang-${LLVM_VERSION} \
|
||||
clang++-${LLVM_VERSION} \
|
||||
llvm-${LLVM_VERSION} \
|
||||
llvm-${LLVM_VERSION}-dev \
|
||||
lld-${LLVM_VERSION} \
|
||||
liblld-${LLVM_VERSION}-dev \
|
||||
libpolly-${LLVM_VERSION}-dev && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN groupadd -g 1337 c3c && \
|
||||
useradd -m -u 1337 -g c3c c3c
|
||||
|
||||
# Add cmake to PATH for user c3c
|
||||
USER c3c
|
||||
ENV PATH="/opt/cmake/bin:${PATH}"
|
||||
|
||||
WORKDIR /home/c3c
|
||||
@@ -1,9 +1,9 @@
|
||||
// Copyright (c) 2023-2025 Eduardo José Gómez Hernández. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::atomic::types{Type};
|
||||
module std::atomic::types;
|
||||
|
||||
struct Atomic
|
||||
struct Atomic <Type>
|
||||
{
|
||||
Type data;
|
||||
}
|
||||
@@ -11,147 +11,87 @@ struct Atomic
|
||||
<*
|
||||
Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
|
||||
@param ordering : "The ordering, cannot be release or acquire-release."
|
||||
@require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
|
||||
@param $ordering : "The ordering, cannot be release or acquire-release."
|
||||
@require $ordering != RELEASE && $ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
|
||||
*>
|
||||
macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.load(&self, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
switch(ordering)
|
||||
{
|
||||
case NOT_ATOMIC: return $$atomic_load(data, false, AtomicOrdering.NOT_ATOMIC.ordinal);
|
||||
case UNORDERED: return $$atomic_load(data, false, AtomicOrdering.UNORDERED.ordinal);
|
||||
case RELAXED: return $$atomic_load(data, false, AtomicOrdering.RELAXED.ordinal);
|
||||
case ACQUIRE: return $$atomic_load(data, false, AtomicOrdering.ACQUIRE.ordinal);
|
||||
case SEQ_CONSISTENT: return $$atomic_load(data, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
|
||||
case ACQUIRE_RELEASE:
|
||||
case RELEASE: unreachable("Invalid ordering.");
|
||||
}
|
||||
return $$atomic_load(&self.data, false, $ordering.ordinal);
|
||||
}
|
||||
<*
|
||||
Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
|
||||
|
||||
@param ordering : "The ordering, cannot be acquire or acquire-release."
|
||||
@require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
|
||||
@param $ordering : "The ordering, cannot be acquire or acquire-release."
|
||||
@require $ordering != ACQUIRE && $ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
|
||||
*>
|
||||
macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro void Atomic.store(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
switch(ordering)
|
||||
{
|
||||
case NOT_ATOMIC: $$atomic_store(data, value, false, AtomicOrdering.NOT_ATOMIC.ordinal);
|
||||
case UNORDERED: $$atomic_store(data, value, false, AtomicOrdering.UNORDERED.ordinal);
|
||||
case RELAXED: $$atomic_store(data, value, false, AtomicOrdering.RELAXED.ordinal);
|
||||
case RELEASE: $$atomic_store(data, value, false, AtomicOrdering.RELEASE.ordinal);
|
||||
case SEQ_CONSISTENT: $$atomic_store(data, value, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
|
||||
case ACQUIRE_RELEASE:
|
||||
case ACQUIRE: unreachable("Invalid ordering.");
|
||||
}
|
||||
$$atomic_store(&self.data, value, false, $ordering.ordinal);
|
||||
}
|
||||
|
||||
macro Type Atomic.add(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.add(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_add, data, value, ordering);
|
||||
return atomic::fetch_add(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.sub(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.sub(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_sub, data, value, ordering);
|
||||
return atomic::fetch_sub(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.mul(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.mul(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_mul, data, value, ordering);
|
||||
return atomic::fetch_mul(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.div(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_div, data, value, ordering);
|
||||
return atomic::fetch_div(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.max(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_max, data, value, ordering);
|
||||
return atomic::fetch_max(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
|
||||
macro Type Atomic.min(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_min, data, value, ordering);
|
||||
return atomic::fetch_min(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.or(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_or, data, value, ordering);
|
||||
return atomic::fetch_or(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.xor(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
|
||||
return atomic::fetch_xor(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.and(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.and(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_and, data, value, ordering);
|
||||
return atomic::fetch_and(&self.data, value, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.shr(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.shr(&self, Type amount, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering);
|
||||
return atomic::fetch_shift_right(&self.data, amount, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.shl(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
macro Type Atomic.shl(&self, Type amount, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering);
|
||||
return atomic::fetch_shift_left(&self.data, amount, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.set(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
macro Type Atomic.set(&self, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
return @atomic_exec_no_arg(atomic::flag_set, data, ordering);
|
||||
return atomic::flag_set(&self.data, $ordering);
|
||||
}
|
||||
|
||||
macro Type Atomic.clear(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
macro Type Atomic.clear(&self, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
|
||||
{
|
||||
Type* data = &self.data;
|
||||
|
||||
return @atomic_exec_no_arg(atomic::flag_clear, data, ordering);
|
||||
}
|
||||
|
||||
macro @atomic_exec(#func, data, value, ordering) @local
|
||||
{
|
||||
switch(ordering)
|
||||
{
|
||||
case RELAXED: return #func(data, value, RELAXED);
|
||||
case ACQUIRE: return #func(data, value, ACQUIRE);
|
||||
case RELEASE: return #func(data, value, RELEASE);
|
||||
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
|
||||
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
|
||||
default: unreachable("Ordering may not be non-atomic or unordered.");
|
||||
}
|
||||
}
|
||||
|
||||
macro @atomic_exec_no_arg(#func, data, ordering) @local
|
||||
{
|
||||
switch(ordering)
|
||||
{
|
||||
case RELAXED: return #func(data, RELAXED);
|
||||
case ACQUIRE: return #func(data, ACQUIRE);
|
||||
case RELEASE: return #func(data, RELEASE);
|
||||
case ACQUIRE_RELEASE: return #func(data, ACQUIRE_RELEASE);
|
||||
case SEQ_CONSISTENT: return #func(data, SEQ_CONSISTENT);
|
||||
default: unreachable("Ordering may not be non-atomic or unordered.");
|
||||
}
|
||||
return atomic::flag_clear(&self.data, $ordering);
|
||||
}
|
||||
|
||||
module std::atomic;
|
||||
@@ -175,8 +115,8 @@ macro bool is_native_atomic_type($Type)
|
||||
$case FLOAT:
|
||||
$case BOOL:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return is_native_atomic_type($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
@@ -272,7 +212,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
|
||||
@require $defined(*ptr) : "Expected a pointer"
|
||||
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
|
||||
@require $defined(*ptr * y) : "/ must be defined between the values."
|
||||
@require $defined(*ptr / y) : "/ must be defined between the values."
|
||||
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
|
||||
*>
|
||||
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
|
||||
|
||||
@@ -70,7 +70,7 @@ fn any? AnyList.last_any(&self) @inline => InterfaceList {any}.last(self);
|
||||
*>
|
||||
macro AnyList.pop(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return *anycast(self.entries[--self.size], $Type);
|
||||
}
|
||||
@@ -85,7 +85,7 @@ macro AnyList.pop(&self, $Type)
|
||||
*>
|
||||
macro AnyList.pop_first(&self, $Type)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.remove_at(0);
|
||||
return *anycast(self.entries[0], $Type);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<*
|
||||
@require SIZE > 0 : "The size of the bitset in bits must be at least 1"
|
||||
*>
|
||||
module std::collections::bitset {SIZE};
|
||||
module std::collections::bitset <SIZE>;
|
||||
|
||||
const BITS = uint.sizeof * 8;
|
||||
const SZ = (SIZE + BITS - 1) / BITS;
|
||||
@@ -172,7 +172,7 @@ fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
|
||||
<*
|
||||
@require Type.kindof == UNSIGNED_INT
|
||||
*>
|
||||
module std::collections::growablebitset{Type};
|
||||
module std::collections::growablebitset <Type>;
|
||||
import std::collections::list;
|
||||
|
||||
const BITS = Type.sizeof * 8;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<*
|
||||
@require MAX_SIZE >= 1 : `The size must be at least 1 element big.`
|
||||
*>
|
||||
module std::collections::elastic_array {Type, MAX_SIZE};
|
||||
module std::collections::elastic_array <Type, MAX_SIZE>;
|
||||
import std::io, std::math, std::collections::list_common;
|
||||
|
||||
alias ElementPredicate = fn bool(Type *type);
|
||||
@@ -46,7 +46,7 @@ fn String ElasticArray.to_tstring(&self)
|
||||
|
||||
fn void? ElasticArray.push_try(&self, Type element) @inline
|
||||
{
|
||||
if (self.size == MAX_SIZE) return mem::OUT_OF_MEMORY?;
|
||||
if (self.size == MAX_SIZE) return mem::OUT_OF_MEMORY~;
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ fn void ElasticArray.push(&self, Type element) @inline
|
||||
|
||||
fn Type? ElasticArray.pop(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ fn void ElasticArray.clear(&self)
|
||||
*>
|
||||
fn Type? ElasticArray.pop_first(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
@@ -241,7 +241,7 @@ fn void? ElasticArray.push_front_try(&self, Type type) @inline
|
||||
*>
|
||||
fn void? ElasticArray.insert_at_try(&self, usz index, Type value)
|
||||
{
|
||||
if (self.size == MAX_SIZE) return mem::OUT_OF_MEMORY?;
|
||||
if (self.size == MAX_SIZE) return mem::OUT_OF_MEMORY~;
|
||||
self.insert_at(index, value);
|
||||
}
|
||||
|
||||
@@ -269,25 +269,25 @@ fn void ElasticArray.set_at(&self, usz index, Type type)
|
||||
|
||||
fn void? ElasticArray.remove_last(&self) @maydiscard
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
self.size--;
|
||||
}
|
||||
|
||||
fn void? ElasticArray.remove_first(&self) @maydiscard
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
fn Type? ElasticArray.first(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
fn Type? ElasticArray.last(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
return self.entries[self.size - 1];
|
||||
}
|
||||
|
||||
@@ -368,7 +368,7 @@ fn usz? ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (equals(v, type)) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz? ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||
@@ -377,7 +377,7 @@ fn usz? ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (equals(v, type)) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<*
|
||||
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap"
|
||||
@require Enum.values.len > 0 : "Only non-empty enums may be used with enummap"
|
||||
*>
|
||||
module std::collections::enummap{Enum, ValueType};
|
||||
module std::collections::enummap <Enum, ValueType>;
|
||||
import std::io;
|
||||
|
||||
struct EnumMap (Printable)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<*
|
||||
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset"
|
||||
*>
|
||||
module std::collections::enumset{Enum};
|
||||
module std::collections::enumset <Enum>;
|
||||
import std::io;
|
||||
|
||||
const ENUM_COUNT @private = Enum.values.len;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<*
|
||||
@require $defined((Key){}.hash()) : `No .hash function found on the key`
|
||||
*>
|
||||
module std::collections::map{Key, Value};
|
||||
module std::collections::map <Key, Value>;
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
@@ -150,7 +150,7 @@ fn bool HashMap.is_initialized(&map)
|
||||
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
|
||||
{
|
||||
self.init(allocator, other_map.table.len, other_map.load_factor);
|
||||
self.put_all_for_create(other_map);
|
||||
hashmap_put_all_for_create(self, other_map);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -175,13 +175,13 @@ fn usz HashMap.len(&map) @inline
|
||||
|
||||
fn Value*? HashMap.get_ref(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
if (!map.count) return NOT_FOUND~;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn Value* HashMap.get_or_create_ref(&map, Key key) @operator(&[])
|
||||
@@ -204,13 +204,13 @@ fn Value* HashMap.get_or_create_ref(&map, Key key) @operator(&[])
|
||||
|
||||
fn Entry*? HashMap.get_entry(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
if (!map.count) return NOT_FOUND~;
|
||||
uint hash = rehash(key.hash());
|
||||
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -233,7 +233,7 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
|
||||
if (e.hash == hash && equals(key, e.key)) return e.value;
|
||||
}
|
||||
Value val = #expr;
|
||||
map.add_entry(hash, key, val, index);
|
||||
hashmap_add_entry(map, hash, key, val, index);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -269,13 +269,13 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
hashmap_add_entry(map, hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void? HashMap.remove(&map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
|
||||
if (!hashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(&map)
|
||||
@@ -290,9 +290,9 @@ fn void HashMap.clear(&map)
|
||||
{
|
||||
Entry *to_delete = next;
|
||||
next = next.next;
|
||||
map.free_entry(to_delete);
|
||||
hashmap_free_entry(map, to_delete);
|
||||
}
|
||||
map.free_entry(entry);
|
||||
hashmap_free_entry(map, entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
map.count = 0;
|
||||
@@ -302,7 +302,7 @@ fn void HashMap.free(&map)
|
||||
{
|
||||
if (!map.is_initialized()) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
hashmap_free_internal(map, map.table.ptr);
|
||||
map.table = {};
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ fn HashMapKeyIterator HashMap.key_iter(&self)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
fn void hashmap_add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
@@ -411,11 +411,11 @@ fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_ind
|
||||
map.table[bucket_index] = entry;
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
hashmap_resize(map, map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.resize(&map, uint new_capacity) @private
|
||||
fn void hashmap_resize(HashMap* map, uint new_capacity) @private
|
||||
{
|
||||
Entry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
@@ -425,9 +425,9 @@ fn void HashMap.resize(&map, uint new_capacity) @private
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
|
||||
map.transfer(new_table);
|
||||
hashmap_transfer(map, new_table);
|
||||
map.table = new_table;
|
||||
map.free_internal(old_table.ptr);
|
||||
hashmap_free_internal(map, old_table.ptr);
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
@@ -443,7 +443,7 @@ fn usz? HashMap.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
||||
fn void hashmap_transfer(HashMap* map, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -462,20 +462,20 @@ fn void HashMap.transfer(&map, Entry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
|
||||
fn void hashmap_put_all_for_create(HashMap* map, HashMap* other_map) @private
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
while (e)
|
||||
{
|
||||
map.put_for_create(e.key, e.value);
|
||||
hashmap_put_for_create(map, e.key, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
||||
fn void hashmap_put_for_create(HashMap* map, Key key, Value value) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
@@ -487,15 +487,15 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
hashmap_create_entry(map, hash, key, value, i);
|
||||
}
|
||||
|
||||
fn void HashMap.free_internal(&map, void* ptr) @inline @private
|
||||
fn void hashmap_free_internal(HashMap* map, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(map.allocator, ptr);
|
||||
}
|
||||
|
||||
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
fn bool hashmap_remove_entry_for_key(HashMap* map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
uint hash = rehash(key.hash());
|
||||
@@ -516,7 +516,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
map.free_entry(e);
|
||||
hashmap_free_entry(map, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -525,7 +525,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
fn void hashmap_create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
{
|
||||
Entry *e = map.table[bucket_index];
|
||||
$if COPY_KEYS:
|
||||
@@ -536,12 +536,12 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
|
||||
map.count++;
|
||||
}
|
||||
|
||||
fn void HashMap.free_entry(&self, Entry *entry) @local
|
||||
fn void hashmap_free_entry(HashMap* map, Entry *entry) @local
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
allocator::free(self.allocator, entry.key);
|
||||
allocator::free(map.allocator, entry.key);
|
||||
$endif
|
||||
self.free_internal(entry);
|
||||
hashmap_free_internal(map, entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<*
|
||||
@require $defined((Value){}.hash()) : `No .hash function found on the value`
|
||||
*>
|
||||
module std::collections::set {Value};
|
||||
module std::collections::set <Value>;
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
@@ -32,7 +32,7 @@ struct HashSet (Printable)
|
||||
float load_factor;
|
||||
}
|
||||
|
||||
fn int HashSet.len(&self) @operator(len) => (int) self.count;
|
||||
fn usz HashSet.len(&self) @operator(len) => self.count;
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@@ -135,7 +135,7 @@ fn bool HashSet.is_initialized(&set)
|
||||
fn HashSet* HashSet.init_from_set(&self, Allocator allocator, HashSet* other_set)
|
||||
{
|
||||
self.init(allocator, other_set.table.len, other_set.load_factor);
|
||||
self.put_all_for_create(other_set);
|
||||
hashset_put_all_for_create(self, other_set);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ fn bool HashSet.add(&set, Value value)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return false;
|
||||
}
|
||||
set.add_entry(hash, value, index);
|
||||
hashset_add_entry(set, hash, value, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -258,7 +258,7 @@ fn bool HashSet.contains(&set, Value value)
|
||||
*>
|
||||
fn void? HashSet.remove(&set, Value value) @maydiscard
|
||||
{
|
||||
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
|
||||
if (!hashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz HashSet.remove_all(&set, Value[] values)
|
||||
@@ -266,7 +266,7 @@ fn usz HashSet.remove_all(&set, Value[] values)
|
||||
usz total;
|
||||
foreach (v : values)
|
||||
{
|
||||
if (set.remove_entry_for_value(v)) total++;
|
||||
if (hashset_remove_entry_for_value(set, v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -279,7 +279,7 @@ fn usz HashSet.remove_all_from(&set, HashSet* other)
|
||||
usz total;
|
||||
other.@each(;Value val)
|
||||
{
|
||||
if (set.remove_entry_for_value(val)) total++;
|
||||
if (hashset_remove_entry_for_value(set, val)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
@@ -291,7 +291,7 @@ fn void HashSet.free(&set)
|
||||
{
|
||||
if (!set.is_initialized()) return;
|
||||
set.clear();
|
||||
set.free_internal(set.table.ptr);
|
||||
hashset_free_internal(set, set.table.ptr);
|
||||
*set = {};
|
||||
}
|
||||
|
||||
@@ -314,10 +314,10 @@ fn void HashSet.clear(&set)
|
||||
{
|
||||
Entry *to_delete = next;
|
||||
next = next.next;
|
||||
set.free_entry(to_delete);
|
||||
hashset_free_entry(set, to_delete);
|
||||
}
|
||||
|
||||
set.free_entry(entry);
|
||||
hashset_free_entry(set, entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
set.count = 0;
|
||||
@@ -327,7 +327,7 @@ fn void HashSet.reserve(&set, usz capacity)
|
||||
{
|
||||
if (capacity > set.threshold)
|
||||
{
|
||||
set.resize(math::next_power_of_2(capacity));
|
||||
hashset_resize(set, math::next_power_of_2(capacity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -464,17 +464,17 @@ fn bool HashSet.is_subset(&self, HashSet* other)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
|
||||
fn void hashset_add_entry(HashSet* set, uint hash, Value value, uint bucket_index) @private
|
||||
{
|
||||
Entry* entry = allocator::new(set.allocator, Entry, { .hash = hash, .value = value, .next = set.table[bucket_index] });
|
||||
set.table[bucket_index] = entry;
|
||||
if (set.count++ >= set.threshold)
|
||||
{
|
||||
set.resize(set.table.len * 2);
|
||||
hashset_resize(set, set.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.resize(&self, usz new_capacity) @private
|
||||
fn void hashset_resize(HashSet* self, usz new_capacity) @private
|
||||
{
|
||||
Entry*[] old_table = self.table;
|
||||
usz old_capacity = old_table.len;
|
||||
@@ -484,9 +484,9 @@ fn void HashSet.resize(&self, usz new_capacity) @private
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
|
||||
self.transfer(new_table);
|
||||
hashset_transfer(self, new_table);
|
||||
self.table = new_table;
|
||||
self.free_internal(old_table.ptr);
|
||||
hashset_free_internal(self, old_table.ptr);
|
||||
self.threshold = (uint)(new_capacity * self.load_factor);
|
||||
}
|
||||
|
||||
@@ -502,7 +502,7 @@ fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void HashSet.transfer(&self, Entry*[] new_table) @private
|
||||
fn void hashset_transfer(HashSet* self, Entry*[] new_table) @private
|
||||
{
|
||||
Entry*[] src = self.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -521,20 +521,20 @@ fn void HashSet.transfer(&self, Entry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
|
||||
fn void hashset_put_all_for_create(HashSet* set, HashSet* other_set) @private
|
||||
{
|
||||
if (!other_set.count) return;
|
||||
foreach (Entry *e : other_set.table)
|
||||
{
|
||||
while (e)
|
||||
{
|
||||
set.put_for_create(e.value);
|
||||
hashset_put_for_create(set, e.value);
|
||||
e = e.next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn void HashSet.put_for_create(&set, Value value) @private
|
||||
fn void hashset_put_for_create(HashSet* set, Value value) @private
|
||||
{
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
@@ -546,15 +546,15 @@ fn void HashSet.put_for_create(&set, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
set.create_entry(hash, value, i);
|
||||
hashset_create_entry(set, hash, value, i);
|
||||
}
|
||||
|
||||
fn void HashSet.free_internal(&self, void* ptr) @inline @private
|
||||
fn void hashset_free_internal(HashSet* self, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(self.allocator, ptr);
|
||||
}
|
||||
|
||||
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
|
||||
fn void hashset_create_entry(HashSet* set, uint hash, Value value, int bucket_index) @private
|
||||
{
|
||||
Entry* entry = allocator::new(set.allocator, Entry, {
|
||||
.hash = hash,
|
||||
@@ -569,7 +569,7 @@ fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @pr
|
||||
Removes the entry for the specified value if present
|
||||
@return "true if found and removed, false otherwise"
|
||||
*>
|
||||
fn bool HashSet.remove_entry_for_value(&set, Value value) @private
|
||||
fn bool hashset_remove_entry_for_value(HashSet* set, Value value) @private
|
||||
{
|
||||
if (!set.count) return false;
|
||||
uint hash = rehash(value.hash());
|
||||
@@ -590,7 +590,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
|
||||
{
|
||||
prev.next = next;
|
||||
}
|
||||
set.free_entry(e);
|
||||
hashset_free_entry(set, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -600,7 +600,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void HashSet.free_entry(&set, Entry *entry) @private
|
||||
fn void hashset_free_entry(HashSet* set, Entry *entry) @private
|
||||
{
|
||||
allocator::free(set.allocator, entry);
|
||||
}
|
||||
@@ -634,7 +634,7 @@ fn Value? HashSetIterator.next(&self)
|
||||
}
|
||||
}
|
||||
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz HashSetIterator.len(&self) @operator(len)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<*
|
||||
@require Type.kindof == INTERFACE || Type.kindof == ANY : "The kind of an interfacelist must be an interface or `any`"
|
||||
*>
|
||||
module std::collections::interfacelist {Type};
|
||||
module std::collections::interfacelist <Type>;
|
||||
import std::io,std::math;
|
||||
|
||||
alias InterfacePredicate = fn bool(Type value);
|
||||
@@ -73,7 +73,7 @@ fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null;
|
||||
macro void InterfaceList.push(&self, element)
|
||||
{
|
||||
if (!self.allocator) self.allocator = tmem;
|
||||
self._append(allocator::clone(self.allocator, element));
|
||||
interfacelist_append(self, allocator::clone(self.allocator, element));
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -93,7 +93,7 @@ fn void InterfaceList.free_element(&self, Type element) @inline
|
||||
*>
|
||||
fn Type? InterfaceList.copy_pop(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
return (Type)allocator::clone_any(allocator, self.entries[--self.size]);
|
||||
}
|
||||
@@ -114,7 +114,7 @@ fn Type? InterfaceList.tcopy_pop(&self) => self.copy_pop(tmem);
|
||||
*>
|
||||
fn Type? InterfaceList.pop_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
return self.entries[--self.size];
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ fn void InterfaceList.clear(&self)
|
||||
*>
|
||||
fn Type? InterfaceList.pop_first_retained(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
@@ -152,7 +152,7 @@ fn Type? InterfaceList.pop_first_retained(&self)
|
||||
*>
|
||||
fn Type? InterfaceList.copy_pop_first(&self, Allocator allocator)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.free_element(self.entries[self.size]);
|
||||
defer self.remove_at(0);
|
||||
return (Type)allocator::clone_any(allocator, self.entries[0]);
|
||||
@@ -276,7 +276,7 @@ fn void InterfaceList.remove_first(&self)
|
||||
*>
|
||||
fn Type? InterfaceList.first(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
|
||||
return self.size ? self.entries[0] : NO_MORE_ELEMENT~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -287,7 +287,7 @@ fn Type? InterfaceList.first(&self) @inline
|
||||
*>
|
||||
fn Type? InterfaceList.last(&self) @inline
|
||||
{
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
|
||||
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -381,7 +381,7 @@ fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic
|
||||
*>
|
||||
fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
|
||||
{
|
||||
return self._remove_if(filter, false);
|
||||
return interfacelist_remove_if(self, filter, false);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -392,7 +392,7 @@ fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
|
||||
*>
|
||||
fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
|
||||
{
|
||||
return self._remove_if(selection, true);
|
||||
return interfacelist_remove_if(self, selection, true);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -404,7 +404,7 @@ fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
|
||||
*>
|
||||
fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context)
|
||||
{
|
||||
return self._remove_using_test(filter, false, context);
|
||||
return interfacelist_remove_using_test(self, filter, false, context);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -416,7 +416,7 @@ fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context
|
||||
*>
|
||||
fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context)
|
||||
{
|
||||
return self._remove_using_test(selection, true, context);
|
||||
return interfacelist_remove_using_test(self, selection, true, context);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -455,7 +455,7 @@ macro void InterfaceList.set(&self, usz index, value)
|
||||
|
||||
// -- private
|
||||
|
||||
fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
fn void interfacelist_ensure_capacity(InterfaceList* self, usz added = 1) @inline @private
|
||||
{
|
||||
usz new_size = self.size + added;
|
||||
if (self.capacity >= new_size) return;
|
||||
@@ -466,18 +466,18 @@ fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
|
||||
self.reserve(new_capacity);
|
||||
}
|
||||
|
||||
fn void InterfaceList._append(&self, Type element) @local
|
||||
fn void interfacelist_append(InterfaceList* self, Type element) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
interfacelist_ensure_capacity(self);
|
||||
self.entries[self.size++] = element;
|
||||
}
|
||||
|
||||
<*
|
||||
@require index < self.size
|
||||
*>
|
||||
fn void InterfaceList._insert_at(&self, usz index, Type value) @local
|
||||
fn void interfacelist_insert_at(InterfaceList* self, usz index, Type value) @local
|
||||
{
|
||||
self.ensure_capacity();
|
||||
interfacelist_ensure_capacity(self);
|
||||
for (usz i = self.size; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
@@ -486,7 +486,7 @@ fn void InterfaceList._insert_at(&self, usz index, Type value) @local
|
||||
self.entries[index] = value;
|
||||
}
|
||||
|
||||
macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local
|
||||
macro usz interfacelist_remove_using_test(InterfaceList* self, InterfaceTest filter, bool $invert, ctx) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
@@ -512,7 +512,7 @@ macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $in
|
||||
return size - self.size;
|
||||
}
|
||||
|
||||
macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local
|
||||
macro usz interfacelist_remove_if(InterfaceList* self, InterfacePredicate filter, bool $invert) @local
|
||||
{
|
||||
usz size = self.size;
|
||||
for (usz i = size, usz k = size; k > 0; k = i)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::collections::blockingqueue { Value };
|
||||
module std::collections::blockingqueue <Value>;
|
||||
import std::thread, std::time;
|
||||
|
||||
|
||||
@@ -70,12 +70,12 @@ fn void LinkedBlockingQueue.free(&self)
|
||||
}
|
||||
};
|
||||
|
||||
(void)self.lock.destroy();
|
||||
(void)self.not_empty.destroy();
|
||||
(void)self.not_full.destroy();
|
||||
self.lock.destroy();
|
||||
self.not_empty.destroy();
|
||||
self.not_full.destroy();
|
||||
}
|
||||
|
||||
fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
|
||||
fn void linkedblockingqueue_link_entry(LinkedBlockingQueue* self, QueueEntry* entry) @private
|
||||
{
|
||||
entry.next = null;
|
||||
entry.prev = self.tail;
|
||||
@@ -95,7 +95,7 @@ fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
|
||||
}
|
||||
|
||||
|
||||
fn QueueEntry* LinkedBlockingQueue.unlink_head(&self) @private
|
||||
fn QueueEntry* linkedblockingqueue_unlink_head(LinkedBlockingQueue* self) @private
|
||||
{
|
||||
if (self.head == null) return null;
|
||||
|
||||
@@ -126,7 +126,7 @@ fn void LinkedBlockingQueue.push(&self, Value value)
|
||||
{
|
||||
while (self.capacity > 0 && self.count >= self.capacity)
|
||||
{
|
||||
self.not_full.wait(&self.lock)!!;
|
||||
self.not_full.wait(&self.lock);
|
||||
}
|
||||
|
||||
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
|
||||
@@ -134,10 +134,10 @@ fn void LinkedBlockingQueue.push(&self, Value value)
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
linkedblockingqueue_link_entry(self, entry);
|
||||
|
||||
// Signal that queue is no longer empty
|
||||
self.not_empty.signal()!!;
|
||||
self.not_empty.signal();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -153,15 +153,15 @@ fn Value LinkedBlockingQueue.poll(&self)
|
||||
{
|
||||
while (self.count == 0)
|
||||
{
|
||||
self.not_empty.wait(&self.lock)!!;
|
||||
self.not_empty.wait(&self.lock);
|
||||
}
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
if (self.capacity > 0)
|
||||
{
|
||||
self.not_full.signal()!!;
|
||||
self.not_full.signal();
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@@ -178,15 +178,15 @@ fn Value? LinkedBlockingQueue.pop(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
if (self.count == 0) return NO_MORE_ELEMENT?;
|
||||
if (self.count == 0) return NO_MORE_ELEMENT~;
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
|
||||
if (self.capacity > 0)
|
||||
{
|
||||
self.not_full.signal()!!;
|
||||
self.not_full.signal();
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@@ -214,17 +214,17 @@ fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
|
||||
if (end <= time::now()) break;
|
||||
if (catch self.not_empty.wait_until(&self.lock, end)) break;
|
||||
}
|
||||
if (!self.count) return NO_MORE_ELEMENT?;
|
||||
if (!self.count) return NO_MORE_ELEMENT~;
|
||||
}
|
||||
|
||||
QueueEntry* entry = self.unlink_head();
|
||||
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
|
||||
Value value = entry.value;
|
||||
allocator::free(self.allocator, entry);
|
||||
|
||||
// Must signal not_full after removing an item
|
||||
if (self.capacity > 0)
|
||||
{
|
||||
self.not_full.signal()!!;
|
||||
self.not_full.signal();
|
||||
}
|
||||
return value;
|
||||
};
|
||||
@@ -266,15 +266,15 @@ fn void? LinkedBlockingQueue.try_push(&self, Value value)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
|
||||
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED~;
|
||||
|
||||
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
|
||||
.value = value,
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
self.not_empty.signal()!!;
|
||||
linkedblockingqueue_link_entry(self, entry);
|
||||
self.not_empty.signal();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
|
||||
if (end <= time::now()) break;
|
||||
if (catch self.not_empty.wait_until(&self.lock, end)) break;
|
||||
}
|
||||
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
|
||||
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED~;
|
||||
}
|
||||
|
||||
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
|
||||
@@ -307,20 +307,20 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
|
||||
.next = null,
|
||||
.prev = null
|
||||
});
|
||||
self.link_entry(entry);
|
||||
self.not_empty.signal()!!;
|
||||
linkedblockingqueue_link_entry(self, entry);
|
||||
self.not_empty.signal();
|
||||
};
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.is_initialized() : "Queue must be initialized"
|
||||
@return "The head value or NO_MORE_ELEMENT? if queue is empty"
|
||||
@return "The head value or NO_MORE_ELEMENT~ if queue is empty"
|
||||
*>
|
||||
fn Value? LinkedBlockingQueue.peek(&self)
|
||||
{
|
||||
self.lock.@in_lock()
|
||||
{
|
||||
return (self.head != null) ? self.head.value : NO_MORE_ELEMENT?;
|
||||
return (self.head != null) ? self.head.value : NO_MORE_ELEMENT~;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<*
|
||||
@require $defined((Key){}.hash()) : `No .hash function found on the key`
|
||||
*>
|
||||
module std::collections::map{Key, Value};
|
||||
module std::collections::map <Key, Value>;
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
@@ -150,7 +150,7 @@ fn bool LinkedHashMap.is_initialized(&map)
|
||||
fn LinkedHashMap* LinkedHashMap.init_from_map(&self, Allocator allocator, LinkedHashMap* other_map)
|
||||
{
|
||||
self.init(allocator, other_map.table.len, other_map.load_factor);
|
||||
self.put_all_for_create(other_map);
|
||||
linkedhashmap_put_all_for_create(self, other_map);
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -172,24 +172,24 @@ fn usz LinkedHashMap.len(&map) @inline => map.count;
|
||||
|
||||
fn Value*? LinkedHashMap.get_ref(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
if (!map.count) return NOT_FOUND~;
|
||||
uint hash = rehash(key.hash());
|
||||
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return &e.value;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn LinkedEntry*? LinkedHashMap.get_entry(&map, Key key)
|
||||
{
|
||||
if (!map.count) return NOT_FOUND?;
|
||||
if (!map.count) return NOT_FOUND~;
|
||||
uint hash = rehash(key.hash());
|
||||
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
|
||||
{
|
||||
if (e.hash == hash && equals(key, e.key)) return e;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -242,13 +242,13 @@ fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
map.add_entry(hash, key, value, index);
|
||||
linkedhashmap_add_entry(map, hash, key, value, index);
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
|
||||
if (!linkedhashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.clear(&map)
|
||||
@@ -259,7 +259,7 @@ fn void LinkedHashMap.clear(&map)
|
||||
while (entry)
|
||||
{
|
||||
LinkedEntry* next = entry.after;
|
||||
map.free_entry(entry);
|
||||
linkedhashmap_free_entry(map, entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
@@ -277,7 +277,7 @@ fn void LinkedHashMap.free(&map)
|
||||
{
|
||||
if (!map.is_initialized()) return;
|
||||
map.clear();
|
||||
map.free_internal(map.table.ptr);
|
||||
linkedhashmap_free_internal(map, map.table.ptr);
|
||||
map.table = {};
|
||||
}
|
||||
|
||||
@@ -375,17 +375,17 @@ fn bool LinkedHashMapIterator.next(&self)
|
||||
|
||||
fn LinkedEntry*? LinkedHashMapIterator.get(&self)
|
||||
{
|
||||
return self.current ? self.current : NOT_FOUND?;
|
||||
return self.current ? self.current : NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn Value*? LinkedHashMapValueIterator.get(&self)
|
||||
{
|
||||
return self.current ? &self.current.value : NOT_FOUND?;
|
||||
return self.current ? &self.current.value : NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn Key*? LinkedHashMapKeyIterator.get(&self)
|
||||
{
|
||||
return self.current ? &self.current.key : NOT_FOUND?;
|
||||
return self.current ? &self.current.key : NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn bool LinkedHashMapIterator.has_next(&self)
|
||||
@@ -396,7 +396,7 @@ fn bool LinkedHashMapIterator.has_next(&self)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
fn void linkedhashmap_add_entry(LinkedHashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
@@ -428,11 +428,11 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
|
||||
|
||||
if (map.count++ >= map.threshold)
|
||||
{
|
||||
map.resize(map.table.len * 2);
|
||||
linkedhashmap_resize(map, map.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
fn void linkedhashmap_resize(LinkedHashMap* map, uint new_capacity) @private
|
||||
{
|
||||
LinkedEntry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
@@ -502,7 +502,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
|
||||
}
|
||||
}
|
||||
|
||||
map.free_internal(old_table.ptr);
|
||||
linkedhashmap_free_internal(map, old_table.ptr);
|
||||
}
|
||||
|
||||
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
|
||||
@@ -517,7 +517,7 @@ fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
|
||||
fn void linkedhashmap_transfer(LinkedHashMap* map, LinkedEntry*[] new_table) @private
|
||||
{
|
||||
LinkedEntry*[] src = map.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -536,7 +536,7 @@ fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
|
||||
fn void linkedhashmap_put_all_for_create(LinkedHashMap* map, LinkedHashMap* other_map) @private
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
other_map.@each(; Key key, Value value) {
|
||||
@@ -544,7 +544,7 @@ fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @privat
|
||||
};
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
|
||||
fn void linkedhashmap_put_for_create(LinkedHashMap* map, Key key, Value value) @private
|
||||
{
|
||||
uint hash = rehash(key.hash());
|
||||
uint i = index_for(hash, map.table.len);
|
||||
@@ -556,15 +556,15 @@ fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
map.create_entry(hash, key, value, i);
|
||||
linkedhashmap_create_entry(map, hash, key, value, i);
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
|
||||
fn void linkedhashmap_free_internal(LinkedHashMap* map, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(map.allocator, ptr);
|
||||
}
|
||||
|
||||
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
fn bool linkedhashmap_remove_entry_for_key(LinkedHashMap* map, Key key) @private
|
||||
{
|
||||
if (!map.count) return false;
|
||||
|
||||
@@ -605,7 +605,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
}
|
||||
|
||||
map.count--;
|
||||
map.free_entry(e);
|
||||
linkedhashmap_free_entry(map, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -614,23 +614,23 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
fn void linkedhashmap_create_entry(LinkedHashMap* map, uint hash, Key key, Value value, int bucket_index) @private
|
||||
{
|
||||
LinkedEntry *e = map.table[bucket_index];
|
||||
$if COPY_KEYS:
|
||||
key = key.copy(map.allocator);
|
||||
key = key.copy(map.allocator);
|
||||
$endif
|
||||
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
|
||||
map.table[bucket_index] = entry;
|
||||
map.count++;
|
||||
}
|
||||
|
||||
fn void LinkedHashMap.free_entry(&self, LinkedEntry *entry) @local
|
||||
fn void linkedhashmap_free_entry(LinkedHashMap* self, LinkedEntry *entry) @local
|
||||
{
|
||||
$if COPY_KEYS:
|
||||
allocator::free(self.allocator, entry.key);
|
||||
allocator::free(self.allocator, entry.key);
|
||||
$endif
|
||||
self.free_internal(entry);
|
||||
linkedhashmap_free_internal(self, entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<*
|
||||
@require $defined((Value){}.hash()) : `No .hash function found on the value`
|
||||
*>
|
||||
module std::collections::set {Value};
|
||||
module std::collections::set <Value>;
|
||||
import std::math;
|
||||
import std::io @norecurse;
|
||||
|
||||
@@ -34,7 +34,7 @@ struct LinkedHashSet (Printable)
|
||||
LinkedEntry* tail;
|
||||
}
|
||||
|
||||
fn int LinkedHashSet.len(&self) @operator(len) => (int) self.count;
|
||||
fn usz LinkedHashSet.len(&self) @operator(len) => self.count;
|
||||
|
||||
<*
|
||||
@param [&inout] allocator : "The allocator to use"
|
||||
@@ -143,7 +143,7 @@ fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, Linked
|
||||
LinkedEntry* entry = other_set.head;
|
||||
while (entry) // Save insertion order
|
||||
{
|
||||
self.put_for_create(entry.value);
|
||||
linkedhashset_put_for_create(self, entry.value);
|
||||
entry = entry.after;
|
||||
}
|
||||
return self;
|
||||
@@ -223,7 +223,7 @@ fn bool LinkedHashSet.add(&set, Value value)
|
||||
{
|
||||
if (e.hash == hash && equals(value, e.value)) return false;
|
||||
}
|
||||
set.add_entry(hash, value, index);
|
||||
linkedhashset_add_entry(set, hash, value, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ fn bool LinkedHashSet.contains(&set, Value value)
|
||||
*>
|
||||
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
|
||||
{
|
||||
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
|
||||
if (!linkedhashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz LinkedHashSet.remove_all(&set, Value[] values)
|
||||
@@ -274,7 +274,7 @@ fn usz LinkedHashSet.remove_all(&set, Value[] values)
|
||||
usz total;
|
||||
foreach (v : values)
|
||||
{
|
||||
if (set.remove_entry_for_value(v)) total++;
|
||||
if (linkedhashset_remove_entry_for_value(set, v)) total++;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
@@ -287,7 +287,7 @@ fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
|
||||
usz total;
|
||||
other.@each(;Value val)
|
||||
{
|
||||
if (set.remove_entry_for_value(val)) total++;
|
||||
if (linkedhashset_remove_entry_for_value(set, val)) total++;
|
||||
};
|
||||
return total;
|
||||
}
|
||||
@@ -299,7 +299,7 @@ fn void LinkedHashSet.free(&set)
|
||||
{
|
||||
if (!set.is_initialized()) return;
|
||||
set.clear();
|
||||
set.free_internal(set.table.ptr);
|
||||
linkedhashset_free_internal(set, set.table.ptr);
|
||||
set.table = {};
|
||||
}
|
||||
|
||||
@@ -316,7 +316,7 @@ fn void LinkedHashSet.clear(&set)
|
||||
while (entry)
|
||||
{
|
||||
LinkedEntry* next = entry.after;
|
||||
set.free_entry(entry);
|
||||
linkedhashset_free_entry(set, entry);
|
||||
entry = next;
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ fn void LinkedHashSet.reserve(&set, usz capacity)
|
||||
{
|
||||
if (capacity > set.threshold)
|
||||
{
|
||||
set.resize(math::next_power_of_2(capacity));
|
||||
linkedhashset_resize(set, math::next_power_of_2(capacity));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -451,7 +451,7 @@ fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
|
||||
|
||||
// --- private methods
|
||||
|
||||
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
|
||||
fn void linkedhashset_add_entry(LinkedHashSet* set, uint hash, Value value, uint bucket_index) @private
|
||||
{
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
@@ -478,11 +478,11 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
|
||||
|
||||
if (set.count++ >= set.threshold)
|
||||
{
|
||||
set.resize(set.table.len * 2);
|
||||
linkedhashset_resize(set, set.table.len * 2);
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
fn void linkedhashset_resize(LinkedHashSet* set, usz new_capacity) @private
|
||||
{
|
||||
LinkedEntry*[] old_table = set.table;
|
||||
usz old_capacity = old_table.len;
|
||||
@@ -552,7 +552,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
|
||||
}
|
||||
}
|
||||
|
||||
set.free_internal(old_table.ptr);
|
||||
linkedhashset_free_internal(set, old_table.ptr);
|
||||
}
|
||||
|
||||
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
|
||||
@@ -567,7 +567,7 @@ fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
|
||||
return len + f.print(" }");
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
|
||||
fn void linked_hashset_transfer(LinkedHashSet* set, LinkedEntry*[] new_table) @private
|
||||
{
|
||||
LinkedEntry*[] src = set.table;
|
||||
uint new_capacity = new_table.len;
|
||||
@@ -586,7 +586,7 @@ fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.put_for_create(&set, Value value) @private
|
||||
fn void linkedhashset_put_for_create(LinkedHashSet* set, Value value) @private
|
||||
{
|
||||
uint hash = rehash(value.hash());
|
||||
uint i = index_for(hash, set.table.len);
|
||||
@@ -598,15 +598,15 @@ fn void LinkedHashSet.put_for_create(&set, Value value) @private
|
||||
return;
|
||||
}
|
||||
}
|
||||
set.create_entry(hash, value, i);
|
||||
linkedhashset_create_entry(set, hash, value, i);
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
|
||||
fn void linkedhashset_free_internal(LinkedHashSet* set, void* ptr) @inline @private
|
||||
{
|
||||
allocator::free(set.allocator, ptr);
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
|
||||
fn void linkedhashset_create_entry(LinkedHashSet* set, uint hash, Value value, int bucket_index) @private
|
||||
{
|
||||
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
|
||||
.hash = hash,
|
||||
@@ -633,7 +633,7 @@ fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_inde
|
||||
set.count++;
|
||||
}
|
||||
|
||||
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
fn bool linkedhashset_remove_entry_for_value(LinkedHashSet* set, Value value) @private
|
||||
{
|
||||
if (!set.count) return false;
|
||||
|
||||
@@ -674,7 +674,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
}
|
||||
|
||||
set.count--;
|
||||
set.free_entry(e);
|
||||
linkedhashset_free_entry(set, e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
@@ -683,7 +683,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
|
||||
fn void linkedhashset_free_entry(LinkedHashSet* set, LinkedEntry *entry) @private
|
||||
{
|
||||
allocator::free(set.allocator, entry);
|
||||
}
|
||||
@@ -713,7 +713,7 @@ fn bool LinkedHashSetIterator.next(&self)
|
||||
|
||||
fn Value*? LinkedHashSetIterator.get(&self)
|
||||
{
|
||||
return self.current ? &self.current.value : NOT_FOUND?;
|
||||
return self.current ? &self.current.value : NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn bool LinkedHashSetIterator.has_next(&self)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::linkedlist{Type};
|
||||
module std::collections::linkedlist <Type>;
|
||||
import std::io;
|
||||
|
||||
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
|
||||
@@ -24,9 +24,9 @@ struct LinkedList
|
||||
fn usz? LinkedList.to_format(&self, Formatter* f) @dynamic
|
||||
{
|
||||
usz len = f.print("{ ")!;
|
||||
for (Node* node = self._first; node != null; node.next)
|
||||
for (Node* node = self._first; node != null; node = node.next)
|
||||
{
|
||||
len += f.printf(node.next ? "%s, " : "s", node.value)!;
|
||||
len += f.printf(node.next ? "%s, " : "%s", node.value)!;
|
||||
}
|
||||
return len + f.print(" }");
|
||||
}
|
||||
@@ -124,13 +124,13 @@ fn Type? LinkedList.peek_last(&self) => self.last() @inline;
|
||||
|
||||
fn Type? LinkedList.first(&self)
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT?;
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
return self._first.value;
|
||||
}
|
||||
|
||||
fn Type? LinkedList.last(&self)
|
||||
{
|
||||
if (!self._last) return NO_MORE_ELEMENT?;
|
||||
if (!self._last) return NO_MORE_ELEMENT~;
|
||||
return self._last.value;
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ fn usz? LinkedList.index_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (node.value == t) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
@@ -208,7 +208,7 @@ fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
if (node.value == t) return i;
|
||||
if (i == 0) break;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
*>
|
||||
fn void LinkedList.remove_at(&self, usz index)
|
||||
{
|
||||
self.unlink(self.node_at_index(index));
|
||||
linked_list_unlink(self, self.node_at_index(index));
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -232,47 +232,47 @@ fn void LinkedList.insert_at(&self, usz index, Type element)
|
||||
case self.size:
|
||||
self.push(element);
|
||||
default:
|
||||
self.link_before(self.node_at_index(index), element);
|
||||
linked_list_link_before(self, self.node_at_index(index), element);
|
||||
}
|
||||
}
|
||||
<*
|
||||
@require succ != null
|
||||
*>
|
||||
fn void LinkedList.link_before(&self, Node *succ, Type value) @private
|
||||
fn void linked_list_link_before(LinkedList* l, Node *succ, Type value) @private
|
||||
{
|
||||
Node* pred = succ.prev;
|
||||
Node* new_node = self.alloc_node();
|
||||
Node* new_node = l.alloc_node();
|
||||
*new_node = { .prev = pred, .next = succ, .value = value };
|
||||
succ.prev = new_node;
|
||||
if (!pred)
|
||||
{
|
||||
self._first = new_node;
|
||||
l._first = new_node;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred.next = new_node;
|
||||
}
|
||||
self.size++;
|
||||
l.size++;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self._first != null
|
||||
@require l._first != null
|
||||
*>
|
||||
fn void LinkedList.unlink_first(&self) @private
|
||||
fn void linked_list_unlink_first(LinkedList* l) @private
|
||||
{
|
||||
Node* f = self._first;
|
||||
Node* f = l._first;
|
||||
Node* next = f.next;
|
||||
self.free_node(f);
|
||||
self._first = next;
|
||||
l.free_node(f);
|
||||
l._first = next;
|
||||
if (!next)
|
||||
{
|
||||
self._last = null;
|
||||
l._last = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
next.prev = null;
|
||||
}
|
||||
self.size--;
|
||||
l.size--;
|
||||
}
|
||||
|
||||
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
@@ -285,7 +285,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
case equals(node.value, t):
|
||||
Node* next = node.next;
|
||||
self.unlink(node);
|
||||
linked_list_unlink(self, node);
|
||||
node = next;
|
||||
default:
|
||||
node = node.next;
|
||||
@@ -296,8 +296,8 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
|
||||
fn Type? LinkedList.pop(&self)
|
||||
{
|
||||
if (!self._last) return NO_MORE_ELEMENT?;
|
||||
defer self.unlink_last();
|
||||
if (!self._last) return NO_MORE_ELEMENT~;
|
||||
defer linked_list_unlink_last(self);
|
||||
return self._last.value;
|
||||
}
|
||||
|
||||
@@ -308,21 +308,21 @@ fn bool LinkedList.is_empty(&self)
|
||||
|
||||
fn Type? LinkedList.pop_front(&self)
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT?;
|
||||
defer self.unlink_first();
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
defer linked_list_unlink_first(self);
|
||||
return self._first.value;
|
||||
}
|
||||
|
||||
fn void? LinkedList.remove_last(&self) @maydiscard
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT?;
|
||||
self.unlink_last();
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
linked_list_unlink_last(self);
|
||||
}
|
||||
|
||||
fn void? LinkedList.remove_first(&self) @maydiscard
|
||||
{
|
||||
if (!self._first) return NO_MORE_ELEMENT?;
|
||||
self.unlink_first();
|
||||
if (!self._first) return NO_MORE_ELEMENT~;
|
||||
linked_list_unlink_first(self);
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
self.unlink(node);
|
||||
linked_list_unlink(self, node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -345,7 +345,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
if (node.value == t)
|
||||
{
|
||||
self.unlink(node);
|
||||
linked_list_unlink(self, node);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -354,7 +354,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
|
||||
<*
|
||||
@require self._last != null
|
||||
*>
|
||||
fn void LinkedList.unlink_last(&self) @inline @private
|
||||
fn void linked_list_unlink_last(LinkedList* self) @inline @private
|
||||
{
|
||||
Node* l = self._last;
|
||||
Node* prev = l.prev;
|
||||
@@ -374,7 +374,7 @@ fn void LinkedList.unlink_last(&self) @inline @private
|
||||
<*
|
||||
@require x != null
|
||||
*>
|
||||
fn void LinkedList.unlink(&self, Node* x) @private
|
||||
fn void linked_list_unlink(LinkedList* self, Node* x) @private
|
||||
{
|
||||
Node* next = x.next;
|
||||
Node* prev = x.prev;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
|
||||
// Use of self source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::collections::list{Type};
|
||||
module std::collections::list <Type>;
|
||||
import std::io, std::math, std::collections::list_common;
|
||||
|
||||
alias ElementPredicate = fn bool(Type *type);
|
||||
@@ -82,7 +82,7 @@ fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types)
|
||||
self.allocator = allocator;
|
||||
self.capacity = types.len;
|
||||
self.entries = types.ptr;
|
||||
self.set_size(types.len);
|
||||
list_set_size(self, types.len);
|
||||
}
|
||||
|
||||
fn bool List.is_initialized(&self) @inline => self.allocator != null && self.allocator != (Allocator)&dummy;
|
||||
@@ -110,24 +110,24 @@ fn usz? List.to_format(&self, Formatter* formatter) @dynamic
|
||||
fn void List.push(&self, Type element) @inline
|
||||
{
|
||||
self.reserve(1);
|
||||
self.entries[self.set_size(self.size + 1)] = element;
|
||||
self.entries[list_set_size(self, self.size + 1)] = element;
|
||||
}
|
||||
|
||||
fn Type? List.pop(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
defer self.set_size(self.size - 1);
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer list_set_size(self, self.size - 1);
|
||||
return self.entries[self.size - 1];
|
||||
}
|
||||
|
||||
fn void List.clear(&self)
|
||||
{
|
||||
self.set_size(0);
|
||||
list_set_size(self, 0);
|
||||
}
|
||||
|
||||
fn Type? List.pop_first(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
defer self.remove_at(0);
|
||||
return self.entries[0];
|
||||
}
|
||||
@@ -138,7 +138,7 @@ fn Type? List.pop_first(&self)
|
||||
fn void List.remove_at(&self, usz index)
|
||||
{
|
||||
usz new_size = self.size - 1;
|
||||
defer self.set_size(new_size);
|
||||
defer list_set_size(self, new_size);
|
||||
if (!new_size || index == new_size) return;
|
||||
self.entries[index .. new_size - 1] = self.entries[index + 1 .. new_size];
|
||||
}
|
||||
@@ -147,7 +147,7 @@ fn void List.add_all(&self, List* other_list)
|
||||
{
|
||||
if (!other_list.size) return;
|
||||
self.reserve(other_list.size);
|
||||
usz index = self.set_size(self.size + other_list.size);
|
||||
usz index = list_set_size(self, self.size + other_list.size);
|
||||
foreach (&value : other_list)
|
||||
{
|
||||
self.entries[index++] = *value;
|
||||
@@ -203,7 +203,7 @@ fn void List.add_array(&self, Type[] array) @deprecated("Use push_all")
|
||||
{
|
||||
if (!array.len) return;
|
||||
self.reserve(array.len);
|
||||
usz index = self.set_size(self.size + array.len);
|
||||
usz index = list_set_size(self, self.size + array.len);
|
||||
self.entries[index : array.len] = array[..];
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ fn void List.push_all(&self, Type[] array)
|
||||
{
|
||||
if (!array.len) return;
|
||||
self.reserve(array.len);
|
||||
usz index = self.set_size(self.size + array.len);
|
||||
usz index = list_set_size(self, self.size + array.len);
|
||||
self.entries[index : array.len] = array[..];
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ fn void List.push_front(&self, Type type) @inline
|
||||
fn void List.insert_at(&self, usz index, Type type)
|
||||
{
|
||||
self.reserve(1);
|
||||
self.set_size(self.size + 1);
|
||||
list_set_size(self, self.size + 1);
|
||||
for (isz i = self.size - 1; i > index; i--)
|
||||
{
|
||||
self.entries[i] = self.entries[i - 1];
|
||||
@@ -250,25 +250,25 @@ fn void List.set_at(&self, usz index, Type type)
|
||||
|
||||
fn void? List.remove_last(&self) @maydiscard
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
self.set_size(self.size - 1);
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
list_set_size(self, self.size - 1);
|
||||
}
|
||||
|
||||
fn void? List.remove_first(&self) @maydiscard
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
self.remove_at(0);
|
||||
}
|
||||
|
||||
fn Type? List.first(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
return self.entries[0];
|
||||
}
|
||||
|
||||
fn Type? List.last(&self)
|
||||
{
|
||||
if (!self.size) return NO_MORE_ELEMENT?;
|
||||
if (!self.size) return NO_MORE_ELEMENT~;
|
||||
return self.entries[self.size - 1];
|
||||
}
|
||||
|
||||
@@ -299,7 +299,7 @@ fn void List.free(&self)
|
||||
{
|
||||
if (!self.allocator || self.allocator.ptr == &dummy || !self.capacity) return;
|
||||
|
||||
self.pre_free(); // Remove sanitizer annotation
|
||||
list_pre_free(self); // Remove sanitizer annotation
|
||||
|
||||
$if type_is_overaligned():
|
||||
allocator::free_aligned(self.allocator, self.entries);
|
||||
@@ -358,7 +358,7 @@ fn usz List.retain_using_test(&self, ElementTest filter, any context)
|
||||
return list_common::list_remove_using_test(self, filter, true, context);
|
||||
}
|
||||
|
||||
fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
fn void list_ensure_capacity(List* self, usz min_capacity) @local
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (self.capacity >= min_capacity) return;
|
||||
@@ -374,7 +374,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
break;
|
||||
}
|
||||
|
||||
self.pre_free(); // Remove sanitizer annotation
|
||||
list_pre_free(self); // Remove sanitizer annotation
|
||||
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
$if type_is_overaligned():
|
||||
@@ -384,7 +384,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
|
||||
$endif;
|
||||
self.capacity = min_capacity;
|
||||
|
||||
self.post_alloc(); // Add sanitizer annotation
|
||||
list_post_alloc(self); // Add sanitizer annotation
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -419,7 +419,7 @@ fn void List.reserve(&self, usz added)
|
||||
assert(new_size < usz.max / 2U);
|
||||
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
|
||||
while (new_capacity < new_size) new_capacity *= 2U;
|
||||
self.ensure_capacity(new_capacity);
|
||||
list_ensure_capacity(self, new_capacity);
|
||||
}
|
||||
|
||||
fn void List._update_size_change(&self,usz old_size, usz new_size)
|
||||
@@ -436,7 +436,7 @@ fn void List._update_size_change(&self,usz old_size, usz new_size)
|
||||
<*
|
||||
@require new_size == 0 || self.capacity != 0
|
||||
*>
|
||||
fn usz List.set_size(&self, usz new_size) @inline @private
|
||||
fn usz list_set_size(List* self, usz new_size) @inline @private
|
||||
{
|
||||
usz old_size = self.size;
|
||||
self._update_size_change(old_size, new_size);
|
||||
@@ -444,7 +444,7 @@ fn usz List.set_size(&self, usz new_size) @inline @private
|
||||
return old_size;
|
||||
}
|
||||
|
||||
macro void List.pre_free(&self) @private
|
||||
macro void list_pre_free(List* self) @private
|
||||
{
|
||||
if (!self.capacity) return;
|
||||
self._update_size_change(self.size, self.capacity);
|
||||
@@ -453,7 +453,7 @@ macro void List.pre_free(&self) @private
|
||||
<*
|
||||
@require self.capacity > 0
|
||||
*>
|
||||
macro void List.post_alloc(&self) @private
|
||||
macro void list_post_alloc(List* self) @private
|
||||
{
|
||||
self._update_size_change(self.capacity, self.size);
|
||||
}
|
||||
@@ -461,22 +461,22 @@ macro void List.post_alloc(&self) @private
|
||||
// Functions for equatable types
|
||||
|
||||
|
||||
fn usz? List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||
fn usz? List.index_of(&self, Type type) @if (ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
foreach (i, v : self)
|
||||
{
|
||||
if (equals(v, type)) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn usz? List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
|
||||
fn usz? List.rindex_of(&self, Type type) @if (ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
foreach_r (i, v : self)
|
||||
{
|
||||
if (equals(v, type)) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE)
|
||||
@@ -532,7 +532,8 @@ fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
|
||||
{
|
||||
usz old_size = self.size;
|
||||
defer {
|
||||
defer
|
||||
{
|
||||
if (old_size != self.size) self._update_size_change(old_size, self.size);
|
||||
}
|
||||
return list_common::list_remove_item(self, value);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::collections::maybe{Type};
|
||||
module std::collections::maybe <Type>;
|
||||
import std::io;
|
||||
|
||||
struct Maybe (Printable)
|
||||
@@ -32,7 +32,7 @@ const Maybe EMPTY = { };
|
||||
|
||||
macro Type? Maybe.get(self)
|
||||
{
|
||||
return self.has_value ? self.value : NOT_FOUND?;
|
||||
return self.has_value ? self.value : NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn bool Maybe.equals(self, Maybe other) @operator(==) @if(types::is_equatable_type(Type))
|
||||
|
||||
@@ -151,7 +151,7 @@ fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array();
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn void Object.init_map_if_needed(&self) @private
|
||||
fn void object_init_map_if_needed(Object* self) @private
|
||||
{
|
||||
if (self.is_empty())
|
||||
{
|
||||
@@ -163,7 +163,7 @@ fn void Object.init_map_if_needed(&self) @private
|
||||
<*
|
||||
@require self.is_indexable()
|
||||
*>
|
||||
fn void Object.init_array_if_needed(&self) @private
|
||||
fn void object_init_array_if_needed(Object* self) @private
|
||||
{
|
||||
if (self.is_empty())
|
||||
{
|
||||
@@ -175,9 +175,9 @@ fn void Object.init_array_if_needed(&self) @private
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn void Object.set_object(&self, String key, Object* new_object) @private
|
||||
fn void object_set_object(Object* self, String key, Object* new_object) @private
|
||||
{
|
||||
self.init_map_if_needed();
|
||||
object_init_map_if_needed(self);
|
||||
Object*? val = self.map.get_entry(key).value;
|
||||
defer (void)val.free();
|
||||
self.map.set(key, new_object);
|
||||
@@ -214,7 +214,7 @@ macro Object* Object.object_from_value(&self, value) @private
|
||||
macro Object* Object.set(&self, String key, value)
|
||||
{
|
||||
Object* val = self.object_from_value(value);
|
||||
self.set_object(key, val);
|
||||
object_set_object(self, key, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -242,8 +242,7 @@ macro Object* Object.push(&self, value)
|
||||
<*
|
||||
@require self.is_keyable()
|
||||
*>
|
||||
fn Object*? Object.get(&self, String key) => self.is_empty() ? NOT_FOUND? : self.map.get(key);
|
||||
|
||||
fn Object*? Object.get(&self, String key) => self.is_empty() ? NOT_FOUND~ : self.map.get(key);
|
||||
|
||||
fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key);
|
||||
|
||||
@@ -268,7 +267,7 @@ fn usz Object.get_len(&self)
|
||||
*>
|
||||
fn void Object.push_object(&self, Object* to_append)
|
||||
{
|
||||
self.init_array_if_needed();
|
||||
object_init_array_if_needed(self);
|
||||
self.array.push(to_append);
|
||||
}
|
||||
|
||||
@@ -277,7 +276,7 @@ fn void Object.push_object(&self, Object* to_append)
|
||||
*>
|
||||
fn void Object.set_object_at(&self, usz index, Object* to_set)
|
||||
{
|
||||
self.init_array_if_needed();
|
||||
object_init_array_if_needed(self);
|
||||
while (self.array.len() < index)
|
||||
{
|
||||
self.array.push(&NULL_OBJECT);
|
||||
@@ -308,7 +307,7 @@ macro get_integer_value(Object* value, $Type)
|
||||
return ($Type)value.s.to_uint128();
|
||||
$endif
|
||||
}
|
||||
if (!value.is_int()) return string::MALFORMED_INTEGER?;
|
||||
if (!value.is_int()) return string::MALFORMED_INTEGER~;
|
||||
return ($Type)value.i;
|
||||
}
|
||||
|
||||
@@ -361,7 +360,7 @@ fn uint128? Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint1
|
||||
fn String? Object.get_string(&self, String key)
|
||||
{
|
||||
Object* value = self.get(key)!;
|
||||
if (!value.is_string()) return TYPE_MISMATCH?;
|
||||
if (!value.is_string()) return TYPE_MISMATCH~;
|
||||
return value.s;
|
||||
}
|
||||
|
||||
@@ -371,7 +370,7 @@ fn String? Object.get_string(&self, String key)
|
||||
fn String? Object.get_string_at(&self, usz index)
|
||||
{
|
||||
Object* value = self.get_at(index);
|
||||
if (!value.is_string()) return TYPE_MISMATCH?;
|
||||
if (!value.is_string()) return TYPE_MISMATCH~;
|
||||
return value.s;
|
||||
}
|
||||
|
||||
@@ -381,7 +380,7 @@ fn String? Object.get_string_at(&self, usz index)
|
||||
macro String? Object.get_enum(&self, $EnumType, String key)
|
||||
{
|
||||
Object value = self.get(key)!;
|
||||
if ($EnumType.typeid != value.type) return TYPE_MISMATCH?;
|
||||
if ($EnumType.typeid != value.type) return TYPE_MISMATCH~;
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
|
||||
@@ -391,7 +390,7 @@ macro String? Object.get_enum(&self, $EnumType, String key)
|
||||
macro String? Object.get_enum_at(&self, $EnumType, usz index)
|
||||
{
|
||||
Object value = self.get_at(index);
|
||||
if ($EnumType.typeid != value.type) return TYPE_MISMATCH?;
|
||||
if ($EnumType.typeid != value.type) return TYPE_MISMATCH~;
|
||||
return ($EnumType)value.i;
|
||||
}
|
||||
|
||||
@@ -401,7 +400,7 @@ macro String? Object.get_enum_at(&self, $EnumType, usz index)
|
||||
fn bool? Object.get_bool(&self, String key)
|
||||
{
|
||||
Object* value = self.get(key)!;
|
||||
if (!value.is_bool()) return TYPE_MISMATCH?;
|
||||
if (!value.is_bool()) return TYPE_MISMATCH~;
|
||||
return value.b;
|
||||
}
|
||||
|
||||
@@ -412,7 +411,7 @@ fn bool? Object.get_bool(&self, String key)
|
||||
fn bool? Object.get_bool_at(&self, usz index)
|
||||
{
|
||||
Object* value = self.get_at(index);
|
||||
if (!value.is_bool()) return TYPE_MISMATCH?;
|
||||
if (!value.is_bool()) return TYPE_MISMATCH~;
|
||||
return value.b;
|
||||
}
|
||||
|
||||
@@ -431,7 +430,7 @@ fn double? Object.get_float(&self, String key)
|
||||
case FLOAT:
|
||||
return value.f;
|
||||
default:
|
||||
return TYPE_MISMATCH?;
|
||||
return TYPE_MISMATCH~;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,7 +449,7 @@ fn double? Object.get_float_at(&self, usz index)
|
||||
case FLOAT:
|
||||
return value.f;
|
||||
default:
|
||||
return TYPE_MISMATCH?;
|
||||
return TYPE_MISMATCH~;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,16 +20,13 @@
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
module std::collections::priorityqueue{Type};
|
||||
import std::collections::priorityqueue::private;
|
||||
|
||||
typedef PriorityQueue = inline PrivatePriorityQueue{Type, false};
|
||||
typedef PriorityQueueMax = inline PrivatePriorityQueue{Type, true};
|
||||
|
||||
module std::collections::priorityqueue::private{Type, MAX};
|
||||
module std::collections::priorityqueue;
|
||||
import std::collections::list, std::io;
|
||||
|
||||
struct PrivatePriorityQueue (Printable)
|
||||
typedef PriorityQueue <Type> = inline PrivatePriorityQueue{Type, false};
|
||||
typedef PriorityQueueMax <Type> = inline PrivatePriorityQueue{Type, true};
|
||||
|
||||
struct PrivatePriorityQueue (Printable) <Type, MAX>
|
||||
{
|
||||
List{Type} heap;
|
||||
}
|
||||
@@ -86,7 +83,7 @@ fn Type? PrivatePriorityQueue.pop(&self)
|
||||
{
|
||||
usz i = 0;
|
||||
usz len = self.heap.len();
|
||||
if (!len) return NO_MORE_ELEMENT?;
|
||||
if (!len) return NO_MORE_ELEMENT~;
|
||||
usz new_count = len - 1;
|
||||
self.heap.swap(0, new_count);
|
||||
while OUTER: ((2 * i + 1) < new_count)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<*
|
||||
@require Type.is_ordered : "The type must be ordered"
|
||||
*>
|
||||
module std::collections::range{Type};
|
||||
module std::collections::range <Type>;
|
||||
import std::io;
|
||||
|
||||
struct Range (Printable)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<*
|
||||
@require Type.kindof == ARRAY : "Required an array type"
|
||||
*>
|
||||
module std::collections::ringbuffer{Type};
|
||||
module std::collections::ringbuffer <Type>;
|
||||
import std::io;
|
||||
|
||||
alias Element = $typeof((Type){}[0]);
|
||||
@@ -48,7 +48,7 @@ fn Element? RingBuffer.pop(&self)
|
||||
switch
|
||||
{
|
||||
case self.written == 0:
|
||||
return NO_MORE_ELEMENT?;
|
||||
return NO_MORE_ELEMENT~;
|
||||
case self.written < self.buf.len:
|
||||
self.written--;
|
||||
return self.buf[self.written];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::collections::pair{Type1, Type2};
|
||||
module std::collections::pair <Type1, Type2>;
|
||||
import std::io;
|
||||
|
||||
struct Pair (Printable)
|
||||
@@ -29,9 +29,7 @@ fn bool Pair.equal(self, Pair other) @operator(==) @if (types::has_equals(Type1)
|
||||
return self.first == other.first && self.second == other.second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
module std::collections::triple{Type1, Type2, Type3};
|
||||
module std::collections::triple <Type1, Type2, Type3>;
|
||||
import std::io;
|
||||
|
||||
struct Triple (Printable)
|
||||
@@ -65,8 +63,7 @@ fn bool Triple.equal(self, Triple other) @operator(==) @if (types::has_equals(Ty
|
||||
return self.first == other.first && self.second == other.second && self.third == other.third;
|
||||
}
|
||||
|
||||
|
||||
module std::collections::tuple{Type1, Type2};
|
||||
module std::collections::tuple <Type1, Type2>;
|
||||
|
||||
struct Tuple @deprecated("Use 'Pair' instead")
|
||||
{
|
||||
|
||||
1108
lib/std/compression/deflate.c3
Normal file
1108
lib/std/compression/deflate.c3
Normal file
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ const uint PIXELS_MAX = 400000000;
|
||||
Purely informative. It will be saved to the file header,
|
||||
but does not affect how chunks are en-/decoded.
|
||||
*>
|
||||
enum QOIColorspace : const char
|
||||
constdef QOIColorspace : char
|
||||
{
|
||||
<* sRGB with linear alpha *>
|
||||
SRGB = 0,
|
||||
@@ -21,7 +21,7 @@ enum QOIColorspace : const char
|
||||
AUTO can be used when decoding to automatically determine
|
||||
the channels from the file's header.
|
||||
*>
|
||||
enum QOIChannels : const inline char
|
||||
constdef QOIChannels : inline char
|
||||
{
|
||||
AUTO = 0,
|
||||
RGB = 3,
|
||||
@@ -100,7 +100,7 @@ fn usz? write(String filename, char[] input, QOIDesc* desc) => @pool()
|
||||
fn char[]? read(Allocator allocator, String filename, QOIDesc* desc, QOIChannels channels = AUTO) => @pool()
|
||||
{
|
||||
// read file
|
||||
char[] data = file::load_temp(filename) ?? FILE_OPEN_FAILED?!;
|
||||
char[] data = file::load_temp(filename) ?? FILE_OPEN_FAILED~!;
|
||||
// pass data to decode function
|
||||
return decode(allocator, data, desc, channels);
|
||||
}
|
||||
@@ -128,14 +128,14 @@ import std::bits;
|
||||
fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
{
|
||||
// check info in desc
|
||||
if (desc.width == 0 || desc.height == 0) return INVALID_PARAMETERS?;
|
||||
if (desc.channels == AUTO) return INVALID_PARAMETERS?;
|
||||
if (desc.width == 0 || desc.height == 0) return INVALID_PARAMETERS~;
|
||||
if (desc.channels == AUTO) return INVALID_PARAMETERS~;
|
||||
uint pixels = desc.width * desc.height;
|
||||
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
|
||||
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS~;
|
||||
|
||||
// check input data size
|
||||
uint image_size = pixels * desc.channels;
|
||||
if (image_size != input.len) return INVALID_DATA?;
|
||||
if (image_size != input.len) return INVALID_DATA~;
|
||||
|
||||
// allocate memory for encoded data (output)
|
||||
// header + chunk tag and RGB(A) data for each pixel + end of stream
|
||||
@@ -283,27 +283,27 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
|
||||
fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels channels = AUTO) @nodiscard
|
||||
{
|
||||
// check input data
|
||||
if (data.len < Header.sizeof + END_OF_STREAM.len) return INVALID_DATA?;
|
||||
if (data.len < Header.sizeof + END_OF_STREAM.len) return INVALID_DATA~;
|
||||
|
||||
// get header
|
||||
Header* header = (Header*)data.ptr;
|
||||
|
||||
// check magic bytes (FourCC)
|
||||
if (bswap(header.be_magic) != 'qoif') return INVALID_DATA?;
|
||||
if (bswap(header.be_magic) != 'qoif') return INVALID_DATA~;
|
||||
|
||||
// copy header data to desc
|
||||
desc.width = bswap(header.be_width);
|
||||
desc.height = bswap(header.be_height);
|
||||
desc.channels = header.channels;
|
||||
uint width = desc.width = bswap(header.be_width);
|
||||
uint height = desc.height = bswap(header.be_height);
|
||||
QOIChannels desc_channels = desc.channels = header.channels;
|
||||
desc.colorspace = header.colorspace;
|
||||
if (desc.channels == AUTO) return INVALID_DATA?; // Channels must be specified in the header
|
||||
if (desc_channels == AUTO) return INVALID_DATA~; // Channels must be specified in the header
|
||||
|
||||
// check width and height
|
||||
if (desc.width == 0 || desc.height == 0) return INVALID_DATA?;
|
||||
if (width == 0 || height == 0) return INVALID_DATA~;
|
||||
|
||||
// check pixel count
|
||||
ulong pixels = (ulong)desc.width * (ulong)desc.height;
|
||||
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
|
||||
ulong pixels = (ulong)width * (ulong)height;
|
||||
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS~;
|
||||
|
||||
uint pos = Header.sizeof; // Current position in data
|
||||
uint loc; // Current position in image (top-left corner)
|
||||
@@ -313,7 +313,7 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
|
||||
Pixel[64] palette; // Zero-initialized by default
|
||||
Pixel p = { 0, 0, 0, 255 };
|
||||
|
||||
if (channels == AUTO) channels = desc.channels;
|
||||
if (channels == AUTO) channels = desc_channels;
|
||||
|
||||
// allocate memory for image data
|
||||
usz image_size = (usz)pixels * channels;
|
||||
|
||||
1215
lib/std/compression/zip.c3
Normal file
1215
lib/std/compression/zip.c3
Normal file
File diff suppressed because it is too large
Load Diff
@@ -90,12 +90,12 @@ fn void*? ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usz total_len = self.data.len;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE~;
|
||||
void* start_mem = self.data.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof;
|
||||
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz end = (usz)(mem - self.data.ptr) + size;
|
||||
if (end > total_len) return mem::OUT_OF_MEMORY?;
|
||||
if (end > total_len) return mem::OUT_OF_MEMORY~;
|
||||
self.used = end;
|
||||
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
||||
header.size = size;
|
||||
@@ -117,7 +117,7 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
|
||||
usz total_len = self.data.len;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE~;
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
// Do last allocation and alignment match?
|
||||
@@ -130,7 +130,7 @@ fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
|
||||
else
|
||||
{
|
||||
usz new_used = self.used + size - old_size;
|
||||
if (new_used > total_len) return mem::OUT_OF_MEMORY?;
|
||||
if (new_used > total_len) return mem::OUT_OF_MEMORY~;
|
||||
self.used = new_used;
|
||||
}
|
||||
header.size = size;
|
||||
|
||||
@@ -56,7 +56,7 @@ fn BackedArenaAllocator*? new_backed_allocator(usz size, Allocator allocator)
|
||||
fn void BackedArenaAllocator.destroy(&self)
|
||||
{
|
||||
self.reset(0);
|
||||
if (self.last_page) (void)self._free_page(self.last_page);
|
||||
if (self.last_page) (void)_free_page(self, self.last_page);
|
||||
allocator::free(self.backing_allocator, self);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
|
||||
self.used = last_page.mark;
|
||||
ExtraPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
self._free_page(to_free)!!;
|
||||
_free_page(self, to_free)!!;
|
||||
}
|
||||
self.last_page = last_page;
|
||||
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
|
||||
@@ -98,13 +98,13 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
|
||||
self.used = mark;
|
||||
}
|
||||
|
||||
fn void? BackedArenaAllocator._free_page(&self, ExtraPage* page) @inline @local
|
||||
fn void? _free_page(BackedArenaAllocator* self, ExtraPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
return self.backing_allocator.release(mem, page.is_aligned());
|
||||
}
|
||||
|
||||
fn void*? BackedArenaAllocator._realloc_page(&self, ExtraPage* page, usz size, usz alignment) @inline @local
|
||||
fn void*? _realloc_page(BackedArenaAllocator* self, ExtraPage* page, usz size, usz alignment) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
@@ -133,7 +133,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
|
||||
assert(self.last_page, "Realloc of unrelated pointer");
|
||||
// First grab the page
|
||||
ExtraPage *page = pointer - ExtraPage.sizeof;
|
||||
return self._realloc_page(page, size, alignment);
|
||||
return _realloc_page(self, page, size, alignment);
|
||||
}
|
||||
|
||||
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
|
||||
@@ -137,36 +137,7 @@ fn void DynamicArenaAllocator.reset(&self)
|
||||
self.page = page;
|
||||
}
|
||||
|
||||
<*
|
||||
@require math::is_power_of_2(alignment)
|
||||
@require size > 0
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
|
||||
{
|
||||
// First, make sure that we can align it, extending the page size if needed.
|
||||
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
|
||||
assert(page_size > size + DynamicArenaChunk.sizeof);
|
||||
// Grab the page without alignment (we do it ourselves)
|
||||
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
|
||||
DynamicArenaPage*? page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
|
||||
if (catch err = page)
|
||||
{
|
||||
allocator::free(self.backing_allocator, mem);
|
||||
return err?;
|
||||
}
|
||||
page.memory = mem;
|
||||
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
|
||||
assert(mem_start + size < mem + page_size);
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
|
||||
chunk.size = size;
|
||||
page.prev_arena = self.page;
|
||||
page.total = page_size;
|
||||
page.used = mem_start + size - page.memory;
|
||||
self.page = page;
|
||||
page.current_stack_ptr = mem_start;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require size > 0 : `acquire expects size > 0`
|
||||
@@ -189,7 +160,7 @@ fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
|
||||
}
|
||||
if (!page)
|
||||
{
|
||||
ptr = self._alloc_new(size, alignment)!;
|
||||
ptr = _alloc_new(self, size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
|
||||
@@ -208,7 +179,7 @@ fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
ptr = self._alloc_new(size, alignment)!;
|
||||
ptr = _alloc_new(self, size, alignment)!;
|
||||
break SET_DONE;
|
||||
}
|
||||
page.used = new_used;
|
||||
@@ -220,3 +191,34 @@ fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
|
||||
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
<*
|
||||
@require math::is_power_of_2(alignment)
|
||||
@require size > 0
|
||||
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
|
||||
*>
|
||||
fn void*? _alloc_new(DynamicArenaAllocator* self, usz size, usz alignment) @local
|
||||
{
|
||||
// First, make sure that we can align it, extending the page size if needed.
|
||||
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
|
||||
assert(page_size > size + DynamicArenaChunk.sizeof);
|
||||
// Grab the page without alignment (we do it ourselves)
|
||||
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
|
||||
DynamicArenaPage*? page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
|
||||
if (catch err = page)
|
||||
{
|
||||
allocator::free(self.backing_allocator, mem);
|
||||
return err~;
|
||||
}
|
||||
page.memory = mem;
|
||||
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
|
||||
assert(mem_start + size < mem + page_size);
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
|
||||
chunk.size = size;
|
||||
page.prev_arena = self.page;
|
||||
page.total = page_size;
|
||||
page.used = mem_start + size - page.memory;
|
||||
self.page = page;
|
||||
page.current_stack_ptr = mem_start;
|
||||
return mem_start;
|
||||
}
|
||||
@@ -32,58 +32,58 @@ fn void*? SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type,
|
||||
{
|
||||
if (init_type == ZERO)
|
||||
{
|
||||
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
|
||||
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_calloc, size, alignment) : simple_alloc_calloc(self, size);
|
||||
}
|
||||
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
|
||||
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_alloc, size, alignment) : simple_alloc_alloc(self, size);
|
||||
}
|
||||
|
||||
fn void*? SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
return alignment > 0
|
||||
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
|
||||
: self._realloc(old_pointer, size);
|
||||
? @aligned_realloc_fn(self, simple_alloc_calloc, simple_alloc_free, old_pointer, size, alignment)
|
||||
: simple_alloc_realloc(self, old_pointer, size);
|
||||
}
|
||||
|
||||
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
|
||||
{
|
||||
if (aligned)
|
||||
{
|
||||
@aligned_free(self._free, old_pointer)!!;
|
||||
@aligned_free_fn(self, simple_alloc_free, old_pointer)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
self._free(old_pointer);
|
||||
simple_alloc_free(self, old_pointer);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require old_pointer && bytes > 0
|
||||
*>
|
||||
fn void*? SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
|
||||
fn void*? simple_alloc_realloc(SimpleHeapAllocator* self, void* old_pointer, usz bytes) @local
|
||||
{
|
||||
// Find the block header.
|
||||
Header* block = (Header*)old_pointer - 1;
|
||||
if (block.size >= bytes) return old_pointer;
|
||||
void* new = self._alloc(bytes)!;
|
||||
void* new = simple_alloc_alloc(self, bytes)!;
|
||||
usz max_to_copy = math::min(block.size, bytes);
|
||||
mem::copy(new, old_pointer, max_to_copy);
|
||||
self._free(old_pointer);
|
||||
simple_alloc_free(self, old_pointer);
|
||||
return new;
|
||||
}
|
||||
|
||||
fn void*? SimpleHeapAllocator._calloc(&self, usz bytes) @local
|
||||
fn void*? simple_alloc_calloc(SimpleHeapAllocator* self, usz bytes) @local
|
||||
{
|
||||
void* data = self._alloc(bytes)!;
|
||||
void* data = simple_alloc_alloc(self, bytes)!;
|
||||
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
fn void*? simple_alloc_alloc(SimpleHeapAllocator* self, usz bytes) @local
|
||||
{
|
||||
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
if (!self.free_list)
|
||||
{
|
||||
self.add_block(aligned_bytes)!;
|
||||
simple_alloc_add_block(self, aligned_bytes)!;
|
||||
}
|
||||
|
||||
Header* current = self.free_list;
|
||||
@@ -123,22 +123,22 @@ fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
|
||||
current = current.next;
|
||||
}
|
||||
}
|
||||
self.add_block(aligned_bytes)!;
|
||||
return self._alloc(aligned_bytes);
|
||||
simple_alloc_add_block(self, aligned_bytes)!;
|
||||
return simple_alloc_alloc(self, aligned_bytes);
|
||||
}
|
||||
|
||||
fn void? SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
|
||||
fn void? simple_alloc_add_block(SimpleHeapAllocator* self, usz aligned_bytes) @local
|
||||
{
|
||||
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
|
||||
char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!;
|
||||
Header* new_block = (Header*)result.ptr;
|
||||
new_block.size = result.len - Header.sizeof;
|
||||
new_block.next = null;
|
||||
self._free(new_block + 1);
|
||||
simple_alloc_free(self, new_block + 1);
|
||||
}
|
||||
|
||||
|
||||
fn void SimpleHeapAllocator._free(&self, void* ptr) @local
|
||||
fn void simple_alloc_free(SimpleHeapAllocator* self, void* ptr) @local
|
||||
{
|
||||
// Empty ptr -> do nothing.
|
||||
if (!ptr) return;
|
||||
|
||||
@@ -15,7 +15,6 @@ module std::core::mem::allocator @if(env::POSIX);
|
||||
import std::os;
|
||||
import libc;
|
||||
|
||||
|
||||
fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
if (init_type == ZERO)
|
||||
@@ -23,22 +22,22 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
void* data @noinit;
|
||||
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY?;
|
||||
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY~;
|
||||
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return data;
|
||||
}
|
||||
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY?;
|
||||
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* data @noinit;
|
||||
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY?;
|
||||
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(data = libc::malloc(bytes))) return mem::OUT_OF_MEMORY?;
|
||||
if (!(data = libc::malloc(bytes))) return mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
@@ -49,25 +48,32 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
|
||||
fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||
{
|
||||
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
|
||||
void* new_ptr;
|
||||
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return mem::OUT_OF_MEMORY?;
|
||||
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
|
||||
$switch:
|
||||
$case env::DARWIN:
|
||||
usz old_usable_size = darwin::malloc_size(old_ptr);
|
||||
$case env::LINUX:
|
||||
usz old_usable_size = linux::malloc_usable_size(old_ptr);
|
||||
$default:
|
||||
usz old_usable_size = new_bytes;
|
||||
$endswitch
|
||||
// Try realloc, even though it might be unaligned.
|
||||
void* new_ptr = libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~!;
|
||||
|
||||
// If it's aligned then we're done!
|
||||
uptr ptr_val = (uptr)new_ptr;
|
||||
if (ptr_val & (alignment - 1) == 0) return new_ptr;
|
||||
|
||||
// We failed, so we need to use memalign
|
||||
// We will free new_ptr before we exit.
|
||||
defer libc::free(new_ptr);
|
||||
|
||||
// Create a pointer which is sure to be aligned.
|
||||
void* aligned_ptr;
|
||||
if (posix::posix_memalign(&aligned_ptr, alignment, new_bytes))
|
||||
{
|
||||
return mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
// Now it is safe to copy the full range of data.
|
||||
mem::copy(aligned_ptr, new_ptr, new_bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
return aligned_ptr;
|
||||
|
||||
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
|
||||
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
libc::free(old_ptr);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
|
||||
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||
{
|
||||
libc::free(old_ptr);
|
||||
@@ -83,12 +89,12 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
{
|
||||
if (alignment > 0)
|
||||
{
|
||||
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: mem::OUT_OF_MEMORY?;
|
||||
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY?;
|
||||
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
|
||||
if (!data) return mem::OUT_OF_MEMORY?;
|
||||
if (!data) return mem::OUT_OF_MEMORY~;
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
@@ -99,9 +105,9 @@ fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
|
||||
{
|
||||
if (alignment)
|
||||
{
|
||||
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: mem::OUT_OF_MEMORY?;
|
||||
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
|
||||
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
|
||||
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||
@@ -122,12 +128,12 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
|
||||
if (init_type == ZERO)
|
||||
{
|
||||
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
|
||||
return data ?: mem::OUT_OF_MEMORY?;
|
||||
return data ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
else
|
||||
{
|
||||
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
|
||||
if (!data) return mem::OUT_OF_MEMORY?;
|
||||
if (!data) return mem::OUT_OF_MEMORY~;
|
||||
$if env::TESTING:
|
||||
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
|
||||
$endif
|
||||
@@ -141,9 +147,9 @@ fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
|
||||
if (alignment)
|
||||
{
|
||||
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
|
||||
return data ?: mem::OUT_OF_MEMORY?;
|
||||
return data ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
|
||||
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ fn void TempAllocator.reset(&self)
|
||||
{
|
||||
TempAllocator* old = child;
|
||||
child = old.derived;
|
||||
old.destroy();
|
||||
temp_allocator_destroy(old);
|
||||
}
|
||||
self.capacity = self.original_capacity;
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
@@ -142,17 +142,17 @@ fn void TempAllocator.reset(&self)
|
||||
fn void TempAllocator.free(&self)
|
||||
{
|
||||
self.reset();
|
||||
self.destroy();
|
||||
temp_allocator_destroy(self);
|
||||
}
|
||||
|
||||
fn void TempAllocator.destroy(&self) @local
|
||||
fn void temp_allocator_destroy(TempAllocator* self)
|
||||
{
|
||||
TempAllocatorPage *last_page = self.last_page;
|
||||
while (last_page)
|
||||
{
|
||||
TempAllocatorPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
self._free_page(to_free)!!;
|
||||
_free_page(self, to_free)!!;
|
||||
}
|
||||
if (self.allocated)
|
||||
{
|
||||
@@ -179,33 +179,6 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
|
||||
}
|
||||
|
||||
|
||||
fn void? TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
return self.backing_allocator.release(mem, page.is_aligned());
|
||||
}
|
||||
|
||||
fn void*? TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
|
||||
// Walk backwards to find the pointer to this page.
|
||||
TempAllocatorPage **pointer_to_prev = &self.last_page;
|
||||
// Remove the page from the list
|
||||
while (*pointer_to_prev != page)
|
||||
{
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
if (page_size > size) page_size = size;
|
||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
self.backing_allocator.release(real_pointer, page.is_aligned());
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
@@ -215,7 +188,7 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
|
||||
assert(self.last_page, "Realloc of non temp pointer");
|
||||
// First grab the page
|
||||
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
|
||||
return self._realloc_page(page, size, alignment);
|
||||
return _realloc_page(self, page, size, alignment);
|
||||
}
|
||||
bool is_realloc_of_last = chunk.size + pointer == &self.data[self.used];
|
||||
if (is_realloc_of_last)
|
||||
@@ -326,9 +299,39 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
|
||||
return &page.data[0];
|
||||
}
|
||||
|
||||
|
||||
fn void? _free_page(TempAllocator* self, TempAllocatorPage* page) @inline @local
|
||||
{
|
||||
void* mem = page.start;
|
||||
return self.backing_allocator.release(mem, page.is_aligned());
|
||||
}
|
||||
|
||||
fn void*? _realloc_page(TempAllocator* self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
|
||||
// Walk backwards to find the pointer to this page.
|
||||
TempAllocatorPage **pointer_to_prev = &self.last_page;
|
||||
// Remove the page from the list
|
||||
while (*pointer_to_prev != page)
|
||||
{
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = self.acquire(size, NO_ZERO, alignment)!;
|
||||
if (page_size > size) page_size = size;
|
||||
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
|
||||
self.backing_allocator.release(real_pointer, page.is_aligned());
|
||||
return data;
|
||||
}
|
||||
|
||||
module std::core::mem::allocator @if((env::POSIX || env::WIN32) && $feature(VMEM_TEMP));
|
||||
import std::math;
|
||||
|
||||
|
||||
tlocal VmemOptions temp_allocator_default_options = {
|
||||
.shrink_on_reset = env::MEMORY_ENV != NORMAL,
|
||||
.protect_unused_pages = env::COMPILER_OPT_LEVEL <= O1 || env::COMPILER_SAFE_MODE,
|
||||
@@ -383,10 +386,10 @@ fn void TempAllocator.reset(&self)
|
||||
}
|
||||
fn void TempAllocator.free(&self)
|
||||
{
|
||||
self.destroy();
|
||||
_destroy(self);
|
||||
}
|
||||
|
||||
fn void TempAllocator.destroy(&self) @local
|
||||
fn void _destroy(TempAllocator* self) @local
|
||||
{
|
||||
TempAllocator* child = self.derived;
|
||||
if (!child) return;
|
||||
|
||||
@@ -28,6 +28,8 @@ struct TrackingAllocator (Allocator)
|
||||
AllocMap map;
|
||||
usz mem_total;
|
||||
usz allocs_total;
|
||||
usz usage;
|
||||
usz max_usage;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -70,6 +72,11 @@ fn usz TrackingAllocator.total_allocated(&self) => self.mem_total;
|
||||
*>
|
||||
fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
|
||||
|
||||
<*
|
||||
@return "the maximum amount of memory allocated"
|
||||
*>
|
||||
fn usz TrackingAllocator.max_allocated(&self) => self.max_usage;
|
||||
|
||||
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
|
||||
{
|
||||
return self.map.tvalues();
|
||||
@@ -88,27 +95,34 @@ fn void*? TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, us
|
||||
backtrace::capture_current(&bt);
|
||||
self.map.set((uptr)data, { data, size, bt });
|
||||
self.mem_total += size;
|
||||
self.usage += size;
|
||||
if (self.usage > self.max_usage) self.max_usage = self.usage;
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void*? TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
void* data = self.inner_allocator.resize(old_pointer, size, alignment)!;
|
||||
self.usage -= self.map[(uptr)old_pointer]!!.size;
|
||||
self.map.remove((uptr)old_pointer);
|
||||
void*[MAX_BACKTRACE] bt;
|
||||
backtrace::capture_current(&bt);
|
||||
self.map.set((uptr)data, { data, size, bt });
|
||||
self.mem_total += size;
|
||||
self.usage += size;
|
||||
if (self.usage > self.max_usage) self.max_usage = self.usage;
|
||||
self.allocs_total++;
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void TrackingAllocator.release(&self, void* old_pointer, bool is_aligned) @dynamic
|
||||
{
|
||||
usz? old_size = self.map[(uptr)old_pointer].size;
|
||||
if (catch self.map.remove((uptr)old_pointer))
|
||||
{
|
||||
unreachable("Attempt to release untracked pointer %p, this is likely a bug.", old_pointer);
|
||||
}
|
||||
self.usage -= old_size!!;
|
||||
self.inner_allocator.release(old_pointer, is_aligned);
|
||||
}
|
||||
|
||||
@@ -177,6 +191,7 @@ fn void? TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
|
||||
io::fprintfn(out, "- Total current allocations: %d", entries)!;
|
||||
io::fprintfn(out, "- Total allocations (freed and retained): %d", self.allocs_total)!;
|
||||
io::fprintfn(out, "- Total allocated memory (freed and retained): %d", self.mem_total)!;
|
||||
io::fprintfn(out, "- Maximum memory usage: %d", self.max_usage)!;
|
||||
if (leaks)
|
||||
{
|
||||
io::fprintn(out)!;
|
||||
|
||||
@@ -45,7 +45,7 @@ fn void? Vmem.init(&self, usz preferred_size, usz reserve_page_size = 0, VmemOpt
|
||||
if (page_size < reserve_page_size) page_size = reserve_page_size;
|
||||
preferred_size = mem::aligned_offset(preferred_size, page_size);
|
||||
if (!min_size) min_size = max(preferred_size / 1024, 1);
|
||||
VirtualMemory? memory = mem::OUT_OF_MEMORY?;
|
||||
VirtualMemory? memory = mem::OUT_OF_MEMORY~;
|
||||
while (preferred_size >= min_size)
|
||||
{
|
||||
memory = vm::virtual_alloc(preferred_size, PROTECTED);
|
||||
@@ -62,7 +62,7 @@ fn void? Vmem.init(&self, usz preferred_size, usz reserve_page_size = 0, VmemOpt
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (catch memory) return VMEM_RESERVE_FAILED?;
|
||||
if (catch memory) return VMEM_RESERVE_FAILED~;
|
||||
if (page_size > preferred_size) page_size = preferred_size;
|
||||
$if env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(memory.ptr, memory.size);
|
||||
@@ -87,12 +87,12 @@ fn void*? Vmem.acquire(&self, usz size, AllocInitType init_type, usz alignment)
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usz total_len = self.memory.size;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
|
||||
if (size > total_len) return mem::INVALID_ALLOC_SIZE~;
|
||||
void* start_mem = self.memory.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + self.allocated + VmemHeader.sizeof;
|
||||
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz after = (usz)(mem - start_mem) + size;
|
||||
if (after > total_len) return mem::OUT_OF_MEMORY?;
|
||||
if (after > total_len) return mem::OUT_OF_MEMORY~;
|
||||
if (init_type == ZERO && self.high_water <= self.allocated)
|
||||
{
|
||||
init_type = NO_ZERO;
|
||||
@@ -119,7 +119,7 @@ fn bool Vmem.owns_pointer(&self, void* ptr) @inline
|
||||
*>
|
||||
fn void*? Vmem.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
|
||||
{
|
||||
if (size > self.memory.size) return mem::INVALID_ALLOC_SIZE?;
|
||||
if (size > self.memory.size) return mem::INVALID_ALLOC_SIZE~;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
assert(self.owns_pointer(old_pointer), "Pointer originates from a different allocator: %p, not in %p - %p", old_pointer, self.memory.ptr, self.memory.ptr + self.allocated);
|
||||
VmemHeader* header = old_pointer - VmemHeader.sizeof;
|
||||
@@ -135,7 +135,7 @@ fn void*? Vmem.resize(&self, void *old_pointer, usz size, usz alignment) @dynami
|
||||
else
|
||||
{
|
||||
usz allocated = self.allocated + size - old_size;
|
||||
if (allocated > self.memory.size) return mem::OUT_OF_MEMORY?;
|
||||
if (allocated > self.memory.size) return mem::OUT_OF_MEMORY~;
|
||||
protect(self, allocated)!;
|
||||
}
|
||||
header.size = size;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module std::core::string::ansi;
|
||||
import std::io;
|
||||
|
||||
enum Ansi : const inline String
|
||||
constdef Ansi : inline String
|
||||
{
|
||||
RESET = "\e[0m",
|
||||
BOLD = "\e[1m",
|
||||
@@ -56,6 +57,37 @@ enum Ansi : const inline String
|
||||
BG_BRIGHT_WHITE = "\e[107m",
|
||||
}
|
||||
|
||||
struct AnsiColor (Printable)
|
||||
{
|
||||
char r, g, b;
|
||||
bool bg;
|
||||
}
|
||||
|
||||
fn usz? AnsiColor.to_format(&self, Formatter* fmt) @dynamic
|
||||
{
|
||||
return fmt.printf("\e[%s8;2;%s;%s;%sm", self.bg ? 4 : 3, self.r, self.g, self.b);
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code
|
||||
|
||||
@return `A struct that, when formatted with '%s', sets the foreground or background colour to the specified rgb value`
|
||||
*>
|
||||
fn AnsiColor get_color_rgb(char r, char g, char b, bool bg = false)
|
||||
{
|
||||
return {r, g, b, bg};
|
||||
}
|
||||
|
||||
<*
|
||||
24-bit color code
|
||||
|
||||
@return `A struct that, when formatted with '%s', sets the foreground or background colour of printed text to the specified rgb value`
|
||||
*>
|
||||
fn AnsiColor get_color(uint rgb, bool bg = false)
|
||||
{
|
||||
return {(char)(rgb >> 16), (char)((rgb & 0x00FF00) >> 8), (char)rgb, bg};
|
||||
}
|
||||
|
||||
<*
|
||||
8-bit color code
|
||||
|
||||
@@ -96,7 +128,7 @@ macro String color(uint $rgb, bool $bg = false) @const
|
||||
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_color(Allocator mem, uint rgb, bool bg = false)
|
||||
fn String make_color(Allocator mem, uint rgb, bool bg = false) @deprecated("use get_color instead")
|
||||
{
|
||||
return make_color_rgb(mem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
|
||||
}
|
||||
@@ -107,7 +139,7 @@ fn String make_color(Allocator mem, uint rgb, bool bg = false)
|
||||
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_tcolor(uint rgb, bool bg = false)
|
||||
fn String make_tcolor(uint rgb, bool bg = false) @deprecated("use get_color instead")
|
||||
{
|
||||
return make_color_rgb(tmem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
|
||||
}
|
||||
@@ -117,7 +149,7 @@ fn String make_tcolor(uint rgb, bool bg = false)
|
||||
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
|
||||
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false) @deprecated("use get_color_rgb instead")
|
||||
{
|
||||
return string::format(mem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
|
||||
}
|
||||
@@ -127,7 +159,7 @@ fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
|
||||
|
||||
@return `the string char for the given foreground color`
|
||||
*>
|
||||
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false)
|
||||
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false) @deprecated("use get_color_rgb instead")
|
||||
{
|
||||
return string::format(tmem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ macro usz? index_of(array, element)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ macro usz? rindex_of(array, element)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
|
||||
@@ -329,6 +329,72 @@ macro bool @all(array, #predicate)
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Extract a copy of all even-index elements from an input array.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the return array."
|
||||
@param [in] array : "The array from which to extract all even elements."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined(array[:lengthof(array)]) : "Expected a sliceable list"
|
||||
*>
|
||||
macro even(Allocator allocator, array)
|
||||
{
|
||||
return unlace_impl{$typeof(array[0])}(allocator, array[:lengthof(array)]);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Extract a copy of all odd-index elements from an input array.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the return array."
|
||||
@param [in] array : "The array from which to extract all odd elements."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $defined(array[:lengthof(array)]) : "Expected a sliceable list"
|
||||
*>
|
||||
macro odd(Allocator allocator, array)
|
||||
{
|
||||
return unlace_impl{$typeof(array[0])}(allocator, lengthof(array) > 1 ? array[1..] : ($typeof(array[0])[]){});
|
||||
}
|
||||
|
||||
<*
|
||||
Private implementation of `even` and `odd` macros, expecting a slice and returning one as well.
|
||||
This function always extracts the even elements of the input slice.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the return array."
|
||||
@param [in] array : "The array from which to extract all odd elements."
|
||||
*>
|
||||
fn Type[] unlace_impl(Allocator allocator, Type[] array) <Type> @private
|
||||
{
|
||||
usz new_len = array.len / 2 + (array.len % 2 == 0 ? 0 : 1);
|
||||
if (new_len == 0) return (Type[]){};
|
||||
Type[] new_array = allocator::new_array(allocator, Type, new_len);
|
||||
foreach (x, &new : new_array) *new = types::implements_copy(Type) ??? array[x * 2].copy(allocator) : array[x * 2];
|
||||
return new_array[:new_len];
|
||||
}
|
||||
|
||||
<*
|
||||
Unlace or partition an input list into its component parts such that `[a, b, c, d, e]` becomes
|
||||
`[a, c, e]` and `[b, d]`. Returned arrays are allocated by the given allocator and are returned
|
||||
via two `out` parameters, `left` and `right`.
|
||||
|
||||
@param [&inout] allocator : "The allocator used to create the returned arrays."
|
||||
@param [in] array : "The input array to unlace."
|
||||
@param [out] left : "Stores a copy of all even-index array elements."
|
||||
@param [out] right : "Stores a copy of all odd-index array elements."
|
||||
|
||||
@require @is_valid_list(array) : "Expected a valid list"
|
||||
@require $typeof(left) == $typeof(array[0])[]*
|
||||
@require $typeof(right) == $typeof(array[0])[]*
|
||||
*>
|
||||
macro unlace(Allocator allocator, array, left, right)
|
||||
{
|
||||
if (left) *left = even(allocator, array);
|
||||
if (right) *right = odd(allocator, array);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Zip together two separate arrays/slices into a single array of Pairs or return values. Values will
|
||||
be collected up to the length of the shorter array if `fill_with` is left undefined; otherwise, they
|
||||
|
||||
@@ -130,9 +130,27 @@ fn AsciiCharset create_set(String string)
|
||||
return set;
|
||||
}
|
||||
|
||||
macro bool AsciiCharset.@contains($set, char $c) @const => !!($c < 128) & !!($set & (AsciiCharset)(1ULL << $c));
|
||||
|
||||
macro AsciiCharset @combine_sets(AsciiCharset $first, AsciiCharset... $sets) @const
|
||||
{
|
||||
var $res = $first;
|
||||
$foreach $c : $sets:
|
||||
$res |= $c;
|
||||
$endforeach
|
||||
return $res;
|
||||
}
|
||||
fn AsciiCharset combine_sets(AsciiCharset first, AsciiCharset... sets)
|
||||
{
|
||||
foreach (c : sets) first |= c;
|
||||
return first;
|
||||
}
|
||||
|
||||
macro bool AsciiCharset.contains(set, char c) => !!(c < 128) & !!(set & (AsciiCharset)(1ULL << c));
|
||||
|
||||
const AsciiCharset WHITESPACE_SET = @create_set("\t\n\v\f\r ");
|
||||
const AsciiCharset NUMBER_SET = @create_set("0123456789");
|
||||
|
||||
|
||||
const AsciiCharset ALPHA_UPPER_SET = @create_set("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
const AsciiCharset ALPHA_LOWER_SET = @create_set("abcdefghijklmnopqrstuvwxyz");
|
||||
const AsciiCharset ALPHA_SET = @combine_sets(ALPHA_UPPER_SET, ALPHA_LOWER_SET);
|
||||
const AsciiCharset ALPHANUMERIC_SET = @combine_sets(ALPHA_SET, NUMBER_SET);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::core::bitorder;
|
||||
|
||||
import std::bits;
|
||||
// This module contains types of different endianness.
|
||||
// *BE types represent big-endian types
|
||||
// *LE types represent little-endian types.
|
||||
@@ -87,36 +87,88 @@ bitstruct UInt128LE : uint128 @littleendian
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(*bytes) : "Pointer must be possible to dereference"
|
||||
@require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro load_be(bytes)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return mem::load(bytes, $align: 1);
|
||||
$else
|
||||
return bswap(mem::load(bytes, $align: 1));
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(*bytes) : "Pointer must be possible to dereference"
|
||||
@require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro load_le(bytes)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
return bswap(mem::load(bytes, $align: 1));
|
||||
$else
|
||||
return mem::load(bytes, $align: 1);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro void store_be(void* dst, value)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
mem::store(($typeof(value)*)dst, value, $align: 1);
|
||||
$else
|
||||
mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
|
||||
*>
|
||||
macro void store_le(void* dst, value)
|
||||
{
|
||||
$if env::BIG_ENDIAN:
|
||||
mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
|
||||
$else
|
||||
mem::store(($typeof(value)*)dst, value, $align: 1);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) : "type must be a bitorder integer"
|
||||
@require $defined(*bytes) ||| $defined(bytes[:$Type.sizeof]) : "Data is too short to contain value"
|
||||
*>
|
||||
macro read(bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
char *ptr;
|
||||
$switch $kindof(bytes):
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
ptr = bytes;
|
||||
$default:
|
||||
s = bytes[:$Type.sizeof];
|
||||
ptr = bytes[..].ptr;
|
||||
$endswitch
|
||||
return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val;
|
||||
return bitcast(mem::load((char[$Type.sizeof]*)ptr, $align: 1), $Type).val;
|
||||
}
|
||||
|
||||
<*
|
||||
@require @is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
|
||||
@require is_bitorder($Type) : "type must be a bitorder integer"
|
||||
@require $defined(*bytes) ||| $defined(bytes[:$Type.sizeof]) : "Data is not sufficent to hold value"
|
||||
*>
|
||||
macro write(x, bytes, $Type)
|
||||
{
|
||||
char[] s;
|
||||
char *ptr;
|
||||
$switch $kindof(bytes):
|
||||
$case POINTER:
|
||||
s = (*bytes)[:$Type.sizeof];
|
||||
ptr = bytes;
|
||||
$default:
|
||||
s = bytes[:$Type.sizeof];
|
||||
ptr = bytes[..].ptr;
|
||||
$endswitch
|
||||
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
|
||||
mem::store(($typeof(x)*)ptr, bitcast(x, $Type).val, 1);
|
||||
}
|
||||
|
||||
macro bool is_bitorder($Type)
|
||||
|
||||
@@ -24,11 +24,15 @@ macro foo(a, #b = EMPTY_MACRO_SLOT)
|
||||
$endif
|
||||
}
|
||||
*>
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
|
||||
const EmptySlot EMPTY_MACRO_SLOT @builtin @deprecated("Use `#arg = ...` instead.") = null;
|
||||
|
||||
typedef EmptySlot = void*;
|
||||
macro bool @is_empty_macro_slot(#arg) @const @builtin => $typeof(#arg) == EmptySlot;
|
||||
macro bool @is_valid_macro_slot(#arg) @const @builtin => $typeof(#arg) != EmptySlot;
|
||||
typedef EmptySlot @constinit = void*;
|
||||
macro bool @is_empty_macro_slot(#arg) @const @builtin
|
||||
@deprecated("Use `#arg = ...` to define an optional macro slot, and `$defined(#arg)` to detect whether the argument is set.")
|
||||
=> $typeof(#arg) == EmptySlot;
|
||||
macro bool @is_valid_macro_slot(#arg) @const @builtin
|
||||
@deprecated("Use `#arg = ...` to define an optional macro slot, and `$defined(#arg)` to detect whether the argument is set.")
|
||||
=> $typeof(#arg) != EmptySlot;
|
||||
|
||||
<*
|
||||
Returns a random value at compile time.
|
||||
@@ -93,6 +97,21 @@ macro usz bitsizeof($Type) @builtin @const => $Type.sizeof * 8u;
|
||||
|
||||
macro usz @bitsizeof(#expr) @builtin @const => $sizeof(#expr) * 8u;
|
||||
|
||||
<*
|
||||
Compile-time check for whether a set of constants contains a certain expression.
|
||||
|
||||
@param #needle : "The expression whose value should be located."
|
||||
*>
|
||||
macro bool @in(#needle, ...) @builtin @const
|
||||
{
|
||||
$for var $x = 0; $x < $vacount; $x++:
|
||||
$assert $defined(#needle == $vaconst[$x])
|
||||
: "Index %s: types '%s' (needle) and '%s' are not equatable", $x, $typeof(#needle), $typeof($vaconst[$x]);
|
||||
$if #needle == $vaconst[$x]: return true; $endif
|
||||
$endfor
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Convert an `any` type to a type, returning an failure if there is a type mismatch.
|
||||
|
||||
@@ -104,10 +123,29 @@ macro usz @bitsizeof(#expr) @builtin @const => $sizeof(#expr) * 8u;
|
||||
*>
|
||||
macro anycast(any v, $Type) @builtin
|
||||
{
|
||||
if (v.type != $Type.typeid) return TYPE_MISMATCH?;
|
||||
if (v.type != $Type.typeid) return TYPE_MISMATCH~;
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
<*
|
||||
@return "The value in the pointer"
|
||||
@return? TYPE_MISMATCH
|
||||
*>
|
||||
macro any.to(self, $Type)
|
||||
{
|
||||
if (self.type != $Type.typeid) return TYPE_MISMATCH~;
|
||||
return *($Type*)self.ptr;
|
||||
}
|
||||
|
||||
<*
|
||||
@require self.type == $Type : "The 'any' contained an unexpected type."
|
||||
@return "The value in the pointer"
|
||||
*>
|
||||
macro any.as(self, $Type)
|
||||
{
|
||||
return *($Type*)self.ptr;
|
||||
}
|
||||
|
||||
macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$defined($Type x = #foo)'") => $defined(*&&($Type){} = #foo);
|
||||
|
||||
macro @addr(#val) @builtin
|
||||
@@ -135,12 +173,15 @@ macro bool @typeis(#value, $Type) @const @builtin @deprecated("Use `$typeof(#val
|
||||
}
|
||||
|
||||
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
fn bool print_backtrace(String message, int backtraces_to_ignore, void *added_backtrace = null) @if (env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
|
||||
{
|
||||
void*[256] buffer;
|
||||
void*[] backtraces = backtrace::capture_current(&buffer);
|
||||
backtraces_to_ignore++;
|
||||
@stack_mem(2048; Allocator mem)
|
||||
if (added_backtrace)
|
||||
{
|
||||
backtraces[++backtraces_to_ignore] = added_backtrace;
|
||||
}
|
||||
@stack_mem(4096; Allocator mem)
|
||||
{
|
||||
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
|
||||
if (catch backtrace) return false;
|
||||
@@ -171,6 +212,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore) @if (env::NATI
|
||||
|
||||
fn void default_panic(String message, String file, String function, uint line) @if(env::NATIVE_STACKTRACE)
|
||||
{
|
||||
in_panic = true;
|
||||
$if $defined(io::stderr) && env::PANIC_MSG:
|
||||
if (!print_backtrace(message, 2))
|
||||
{
|
||||
@@ -187,7 +229,7 @@ macro void abort(String string = "Unrecoverable error reached", ...) @format(0)
|
||||
$$trap();
|
||||
}
|
||||
|
||||
bool in_panic @local = false;
|
||||
bool in_panic @private = false;
|
||||
|
||||
fn void default_panic(String message, String file, String function, uint line) @if (!env::NATIVE_STACKTRACE)
|
||||
{
|
||||
@@ -312,7 +354,7 @@ macro enum_by_name($Type, String enum_name) @builtin
|
||||
{
|
||||
if (name == enum_name) return $Type.from_ordinal(i);
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -329,7 +371,7 @@ macro @enum_from_value($Type, #value, value) @builtin @deprecated("Use Enum.look
|
||||
{
|
||||
if (e.#value == value) return e;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -413,11 +455,25 @@ macro @prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write =
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
Shuffle a vector by its index
|
||||
|
||||
int[<4>] a = { 1, 2, 3, 4 };
|
||||
assert(swizzle(a, 0, 1, 1, 3) == (int[<4>]) { 1, 2, 2, 4 });
|
||||
*>
|
||||
macro swizzle(v, ...) @builtin
|
||||
{
|
||||
return $$swizzle(v, $vasplat);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Shuffle two vectors by a common index from arranging the vectors sequentially in memory
|
||||
|
||||
int[<4>] a = { 1, 2, 3, 4 };
|
||||
int[<4>] b = { 100, 1000, 10000, 100000 };
|
||||
assert(swizzle2(a, b, 0, 1, 4, 6, 2) == (int[<5>]) { 1, 2, 100, 10000, 3 });
|
||||
*>
|
||||
macro swizzle2(v, v2, ...) @builtin
|
||||
{
|
||||
return $$swizzle2(v, v2, $vasplat);
|
||||
@@ -485,7 +541,7 @@ macro bool @ok(#expr) @builtin
|
||||
macro void? @try(#v, #expr) @builtin @maydiscard
|
||||
{
|
||||
var res = #expr;
|
||||
if (catch err = res) return err?;
|
||||
if (catch err = res) return err~;
|
||||
#v = res;
|
||||
}
|
||||
|
||||
@@ -500,7 +556,7 @@ macro void? @try(#v, #expr) @builtin @maydiscard
|
||||
{
|
||||
char[] data;
|
||||
// Read until end of file
|
||||
if (@try_catch(data, load_line(), io::EOF)) break;
|
||||
if (@try_catch(data, load_line(), io::EOF)!) break;
|
||||
.. use data ..
|
||||
}
|
||||
|
||||
@@ -529,7 +585,7 @@ macro bool? @try_catch(#v, #expr, fault expected_fault) @builtin
|
||||
var res = #expr;
|
||||
if (catch err = res)
|
||||
{
|
||||
return err == expected_fault ? true : err?;
|
||||
return err == expected_fault ? true : err~;
|
||||
}
|
||||
#v = res;
|
||||
return false;
|
||||
@@ -648,7 +704,7 @@ macro uint hash_array(array_ptr) @local
|
||||
*>
|
||||
macro uint hash_vec(vec) @local
|
||||
{
|
||||
var $len = $sizeof(vec.len * $typeof(vec).inner.sizeof);
|
||||
var $len = $sizeof(vec);
|
||||
|
||||
$if $len > 16:
|
||||
return (uint)komi::hash(((char*)&&vec)[:$len]);
|
||||
@@ -941,57 +997,73 @@ macro void* get_returnaddress(int n)
|
||||
}
|
||||
|
||||
module std::core::builtin @if((env::LINUX || env::ANDROID || env::DARWIN) && env::COMPILER_SAFE_MODE && env::DEBUG_SYMBOLS);
|
||||
import libc, std::io;
|
||||
import libc, std::io, std::os::posix;
|
||||
|
||||
fn void sig_panic(String message)
|
||||
{
|
||||
default_panic(message, "???", "???", 0);
|
||||
}
|
||||
|
||||
SignalFunction old_bus_error;
|
||||
SignalFunction old_segmentation_fault;
|
||||
|
||||
fn void sig_bus_error(CInt i)
|
||||
fn void sig_bus_error(CInt i, void* info, void* context)
|
||||
{
|
||||
$if !env::NATIVE_STACKTRACE:
|
||||
sig_panic("Illegal memory access.");
|
||||
$else
|
||||
$if $defined(io::stderr):
|
||||
if (!print_backtrace("Illegal memory access.", 1))
|
||||
if (!print_backtrace("Illegal memory access.", 2, posix::stack_instruction(context)))
|
||||
{
|
||||
io::eprintn("\nERROR: 'Illegal memory access'.");
|
||||
}
|
||||
$endif
|
||||
$endif
|
||||
$$trap();
|
||||
os::fastexit(128 + i);
|
||||
}
|
||||
|
||||
fn void sig_segmentation_fault(CInt i)
|
||||
fn void sig_segmentation_fault(CInt i, void* p1, void* context)
|
||||
{
|
||||
$if !env::NATIVE_STACKTRACE:
|
||||
sig_panic("Out of bounds memory access.");
|
||||
$else
|
||||
$if $defined(io::stderr):
|
||||
if (!print_backtrace("Out of bounds memory access.", 1))
|
||||
if (!print_backtrace("Out of bounds memory access.", 2, posix::stack_instruction(context)))
|
||||
{
|
||||
io::eprintn("\nERROR: Memory error without backtrace, possible stack overflow.");
|
||||
}
|
||||
$endif
|
||||
$endif
|
||||
$$trap();
|
||||
os::fastexit(128 + i);
|
||||
}
|
||||
|
||||
fn void install_signal_handler(CInt signal, SignalFunction func) @local
|
||||
fn void sig_illegal_instruction(CInt i, void* p1, void* context)
|
||||
{
|
||||
SignalFunction old = libc::signal(signal, func);
|
||||
// Restore
|
||||
if ((iptr)old > 1024) libc::signal(signal, old);
|
||||
if (in_panic) os::fastexit(128 + i);
|
||||
$if !env::NATIVE_STACKTRACE:
|
||||
sig_panic("Illegal instruction.");
|
||||
$else
|
||||
$if $defined(io::stderr):
|
||||
if (!print_backtrace("Illegal instruction.", 2, posix::stack_instruction(context)))
|
||||
{
|
||||
io::eprintn("\nERROR: Illegal instruction.");
|
||||
}
|
||||
$endif
|
||||
$endif
|
||||
os::fastexit(128 + i);
|
||||
}
|
||||
|
||||
char[64 * 1024] sig_stack @local @if(env::BACKTRACE && env::LINUX);
|
||||
|
||||
// Clean this up
|
||||
fn void install_signal_handlers() @init(101) @local @if(env::BACKTRACE)
|
||||
{
|
||||
install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||
install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||
}
|
||||
$if env::LINUX:
|
||||
Stack_t ss = {
|
||||
.ss_sp = &sig_stack,
|
||||
.ss_size = sig_stack.len
|
||||
};
|
||||
libc::sigaltstack(&ss, null);
|
||||
$endif
|
||||
|
||||
posix::install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||
posix::install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||
posix::install_signal_handler(libc::SIGILL, &sig_illegal_instruction);
|
||||
}
|
||||
|
||||
@@ -16,25 +16,25 @@ const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
|
||||
*>
|
||||
fn usz? char32_to_utf8(Char32 c, char[] output)
|
||||
{
|
||||
if (!output.len) return string::CONVERSION_FAILED?;
|
||||
if (!output.len) return string::CONVERSION_FAILED~;
|
||||
switch (true)
|
||||
{
|
||||
case c <= 0x7f:
|
||||
output[0] = (char)c;
|
||||
return 1;
|
||||
case c <= 0x7ff:
|
||||
if (output.len < 2) return string::CONVERSION_FAILED?;
|
||||
if (output.len < 2) return string::CONVERSION_FAILED~;
|
||||
output[0] = (char)(0xC0 | c >> 6);
|
||||
output[1] = (char)(0x80 | (c & 0x3F));
|
||||
return 2;
|
||||
case c <= 0xffff:
|
||||
if (output.len < 3) return string::CONVERSION_FAILED?;
|
||||
if (output.len < 3) return string::CONVERSION_FAILED~;
|
||||
output[0] = (char)(0xE0 | c >> 12);
|
||||
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c & 0x3F));
|
||||
return 3;
|
||||
case c <= 0x10ffff:
|
||||
if (output.len < 4) return string::CONVERSION_FAILED?;
|
||||
if (output.len < 4) return string::CONVERSION_FAILED~;
|
||||
output[0] = (char)(0xF0 | c >> 18);
|
||||
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
@@ -42,7 +42,7 @@ fn usz? char32_to_utf8(Char32 c, char[] output)
|
||||
return 4;
|
||||
default:
|
||||
// 0x10FFFF and above is not defined.
|
||||
return string::CONVERSION_FAILED?;
|
||||
return string::CONVERSION_FAILED~;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,15 +84,15 @@ fn void? char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
|
||||
return;
|
||||
}
|
||||
// Low surrogate first is an error
|
||||
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return string::INVALID_UTF16?;
|
||||
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return string::INVALID_UTF16~;
|
||||
|
||||
// Unmatched high surrogate is an error
|
||||
if (*available == 1) return string::INVALID_UTF16?;
|
||||
if (*available == 1) return string::INVALID_UTF16~;
|
||||
|
||||
Char16 low = ptr[1];
|
||||
|
||||
// Unmatched high surrogate, invalid
|
||||
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return string::INVALID_UTF16?;
|
||||
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return string::INVALID_UTF16~;
|
||||
|
||||
// The high bits of the codepoint are the value bits of the high surrogate
|
||||
// The low bits of the codepoint are the value bits of the low surrogate
|
||||
@@ -138,7 +138,7 @@ fn usz char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
fn Char32? utf8_to_char32(char* ptr, usz* size)
|
||||
{
|
||||
usz max_size = *size;
|
||||
if (max_size < 1) return string::INVALID_UTF8?;
|
||||
if (max_size < 1) return string::INVALID_UTF8~;
|
||||
char c = (ptr++)[0];
|
||||
|
||||
if ((c & 0x80) == 0)
|
||||
@@ -148,40 +148,40 @@ fn Char32? utf8_to_char32(char* ptr, usz* size)
|
||||
}
|
||||
if ((c & 0xE0) == 0xC0)
|
||||
{
|
||||
if (max_size < 2) return string::INVALID_UTF8?;
|
||||
if (max_size < 2) return string::INVALID_UTF8~;
|
||||
*size = 2;
|
||||
Char32 uc = (c & 0x1F) << 6;
|
||||
c = *ptr;
|
||||
// Overlong sequence or invalid second.
|
||||
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8?;
|
||||
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8~;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if ((c & 0xF0) == 0xE0)
|
||||
{
|
||||
if (max_size < 3) return string::INVALID_UTF8?;
|
||||
if (max_size < 3) return string::INVALID_UTF8~;
|
||||
*size = 3;
|
||||
Char32 uc = (c & 0x0F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return string::INVALID_UTF8?;
|
||||
if (c & 0xC0 != 0x80) return string::INVALID_UTF8~;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8?;
|
||||
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8~;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if (max_size < 4) return string::INVALID_UTF8?;
|
||||
if ((c & 0xF8) != 0xF0) return string::INVALID_UTF8?;
|
||||
if (max_size < 4) return string::INVALID_UTF8~;
|
||||
if ((c & 0xF8) != 0xF0) return string::INVALID_UTF8~;
|
||||
*size = 4;
|
||||
Char32 uc = (c & 0x07) << 18;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return string::INVALID_UTF8?;
|
||||
if (c & 0xC0 != 0x80) return string::INVALID_UTF8~;
|
||||
uc += (c & 0x3F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return string::INVALID_UTF8?;
|
||||
if (c & 0xC0 != 0x80) return string::INVALID_UTF8~;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8?;
|
||||
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8~;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
|
||||
@@ -329,7 +329,7 @@ fn usz? utf8to32(String utf8, Char32[] utf32_buffer)
|
||||
usz buf_len = utf32_buffer.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
if (len32 == buf_len) return string::CONVERSION_FAILED?;
|
||||
if (len32 == buf_len) return string::CONVERSION_FAILED~;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
|
||||
i += width;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module std::core::dstring;
|
||||
import std::io;
|
||||
import std::io, std::math;
|
||||
|
||||
<*
|
||||
The DString offers a dynamic string builder.
|
||||
@@ -95,7 +95,7 @@ fn void DString.replace(&self, String needle, String replacement)
|
||||
match++;
|
||||
if (match == needle_len)
|
||||
{
|
||||
self.append_chars(replacement);
|
||||
self.append_string(replacement);
|
||||
match = 0;
|
||||
continue;
|
||||
}
|
||||
@@ -103,12 +103,12 @@ fn void DString.replace(&self, String needle, String replacement)
|
||||
}
|
||||
if (match > 0)
|
||||
{
|
||||
self.append_chars(str[i - match:match]);
|
||||
self.append_string(str[i - match:match]);
|
||||
match = 0;
|
||||
}
|
||||
self.append_char(c);
|
||||
}
|
||||
if (match > 0) self.append_chars(str[^match:match]);
|
||||
if (match > 0) self.append_string(str[^match:match]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -305,18 +305,23 @@ fn bool DString.less(self, DString other_string)
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void DString.append_chars(&self, String str)
|
||||
fn void DString.append_chars(&self, String str) @deprecated("Use append_string")
|
||||
{
|
||||
usz other_len = str.len;
|
||||
self.append_bytes(str);
|
||||
}
|
||||
|
||||
fn void DString.append_bytes(&self, char[] bytes)
|
||||
{
|
||||
usz other_len = bytes.len;
|
||||
if (!other_len) return;
|
||||
if (!*self)
|
||||
{
|
||||
*self = temp(str);
|
||||
*self = temp((String)bytes);
|
||||
return;
|
||||
}
|
||||
self.reserve(other_len);
|
||||
StringData* data = self.data();
|
||||
mem::copy(&data.chars[data.len], str.ptr, other_len);
|
||||
mem::copy(&data.chars[data.len], bytes.ptr, other_len);
|
||||
data.len += other_len;
|
||||
}
|
||||
|
||||
@@ -325,7 +330,24 @@ fn Char32[] DString.copy_utf32(&self, Allocator allocator)
|
||||
return self.str_view().to_utf32(allocator) @inline!!;
|
||||
}
|
||||
|
||||
fn void DString.append_string(&self, DString str)
|
||||
<*
|
||||
@require $defined(String s = str) ||| $typeof(str) == DString : "Expected string or DString"
|
||||
*>
|
||||
macro void DString.append_string(&self, str)
|
||||
{
|
||||
$if $typeof(str) == DString:
|
||||
self.append_string_deprecated(str);
|
||||
$else
|
||||
self.append_bytes((String)str);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro void DString.append_string_deprecated(&self, DString str) @deprecated("Use .append_dstring()")
|
||||
{
|
||||
self.append_dstring(str);
|
||||
}
|
||||
|
||||
fn void DString.append_dstring(&self, DString str)
|
||||
{
|
||||
StringData* other = str.data();
|
||||
if (!other) return;
|
||||
@@ -340,7 +362,7 @@ fn void DString.clear(self)
|
||||
|
||||
fn usz? DString.write(&self, char[] buffer) @dynamic
|
||||
{
|
||||
self.append_chars((String)buffer);
|
||||
self.append_bytes(buffer);
|
||||
return buffer.len;
|
||||
}
|
||||
|
||||
@@ -400,9 +422,9 @@ macro void DString.append(&self, value)
|
||||
$case ichar:
|
||||
self.append_char(value);
|
||||
$case DString:
|
||||
self.append_string(value);
|
||||
self.append_dstring(value);
|
||||
$case String:
|
||||
self.append_chars(value);
|
||||
self.append_string(value);
|
||||
$case Char32:
|
||||
self.append_char32(value);
|
||||
$default:
|
||||
@@ -410,7 +432,7 @@ macro void DString.append(&self, value)
|
||||
$case $defined((Char32)value):
|
||||
self.append_char32((Char32)value);
|
||||
$case $defined((String)value):
|
||||
self.append_chars((String)value);
|
||||
self.append_string((String)value);
|
||||
$default:
|
||||
$error "Unsupported type for append – use appendf instead.";
|
||||
$endswitch
|
||||
@@ -609,7 +631,7 @@ fn void DString.reverse(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn StringData* DString.data(self) @inline @private
|
||||
fn StringData* DString.data(self) @inline
|
||||
{
|
||||
return (StringData*)self;
|
||||
}
|
||||
@@ -626,7 +648,7 @@ fn void DString.reserve(&self, usz addition)
|
||||
if (data.capacity >= len) return;
|
||||
usz new_capacity = data.capacity * 2;
|
||||
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
|
||||
while (new_capacity < len) new_capacity *= 2;
|
||||
if (new_capacity < len) new_capacity = math::next_power_of_2(len);
|
||||
data.capacity = new_capacity;
|
||||
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
|
||||
}
|
||||
@@ -636,9 +658,10 @@ fn usz? DString.read_from_stream(&self, InStream reader)
|
||||
if (&reader.available)
|
||||
{
|
||||
usz total_read = 0;
|
||||
while (usz available = reader.available()!)
|
||||
while (ulong available = reader.available()!)
|
||||
{
|
||||
self.reserve(available);
|
||||
if (available > isz.max) available = (ulong)isz.max;
|
||||
self.reserve((usz)available);
|
||||
StringData* data = self.data();
|
||||
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
|
||||
total_read += len;
|
||||
|
||||
@@ -124,7 +124,9 @@ const usz MAX_VECTOR_SIZE = $$MAX_VECTOR_SIZE;
|
||||
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
|
||||
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
|
||||
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
|
||||
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
|
||||
const bool NO_LIBC = !LIBC && !CUSTOM_LIBC;
|
||||
const bool CUSTOM_LIBC = $$CUSTOM_LIBC;
|
||||
const bool OLD_IO = $feature(OLD_IO);
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
@@ -153,19 +155,20 @@ const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
|
||||
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
|
||||
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD;
|
||||
const bool WASI = LIBC && OS_TYPE == WASI;
|
||||
const bool WASM = ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
||||
const bool ANDROID = LIBC && OS_TYPE == ANDROID;
|
||||
const bool WASM_NOLIBC @builtin @deprecated("Use 'FREESTANDING_WASM' instead") = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
|
||||
const bool FREESTANDING_PE32 = NO_LIBC && OS_TYPE == WIN32;
|
||||
const bool FREESTANDING_MACHO = NO_LIBC && OS_TYPE == MACOS;
|
||||
const bool FREESTANDING_ELF = NO_LIBC && !env::FREESTANDING_PE32 && !env::FREESTANDING_MACHO && !env::FREESTANDING_WASM;
|
||||
const bool FREESTANDING_WASM = NO_LIBC && (ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64);
|
||||
const bool FREESTANDING_WASM = NO_LIBC && WASM;
|
||||
const bool FREESTANDING = env::FREESTANDING_PE32 || env::FREESTANDING_MACHO || env::FREESTANDING_ELF || env::FREESTANDING_WASM;
|
||||
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
|
||||
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
|
||||
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
|
||||
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
|
||||
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
|
||||
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32;
|
||||
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32 || env::NETBSD;
|
||||
|
||||
macro bool os_is_darwin() @const
|
||||
{
|
||||
|
||||
@@ -6,22 +6,22 @@ const FULL_LOG = env::COMPILER_SAFE_MODE || $feature(FULL_LOG);
|
||||
typedef LogCategory = inline char;
|
||||
typedef LogTag = char[12];
|
||||
|
||||
const LogCategory CATEGORY_APPLICATION = 0;
|
||||
const LogCategory CATEGORY_SYSTEM = 1;
|
||||
const LogCategory CATEGORY_KERNEL = 2;
|
||||
const LogCategory CATEGORY_AUDIO = 3;
|
||||
const LogCategory CATEGORY_VIDEO = 4;
|
||||
const LogCategory CATEGORY_RENDER = 5;
|
||||
const LogCategory CATEGORY_INPUT = 6;
|
||||
const LogCategory CATEGORY_NETWORK = 7;
|
||||
const LogCategory CATEGORY_SOCKET = 8;
|
||||
const LogCategory CATEGORY_SECURITY = 9;
|
||||
const LogCategory CATEGORY_TEST = 10;
|
||||
const LogCategory CATEGORY_ERROR = 11;
|
||||
const LogCategory CATEGORY_ASSERT = 12;
|
||||
const LogCategory CATEGORY_CRASH = 13;
|
||||
const LogCategory CATEGORY_STATS = 14;
|
||||
const LogCategory CATEGORY_CUSTOM_START = 100;
|
||||
const LogCategory CATEGORY_APPLICATION = (LogCategory)0;
|
||||
const LogCategory CATEGORY_SYSTEM = (LogCategory)1;
|
||||
const LogCategory CATEGORY_KERNEL = (LogCategory)2;
|
||||
const LogCategory CATEGORY_AUDIO = (LogCategory)3;
|
||||
const LogCategory CATEGORY_VIDEO = (LogCategory)4;
|
||||
const LogCategory CATEGORY_RENDER = (LogCategory)5;
|
||||
const LogCategory CATEGORY_INPUT = (LogCategory)6;
|
||||
const LogCategory CATEGORY_NETWORK = (LogCategory)7;
|
||||
const LogCategory CATEGORY_SOCKET = (LogCategory)8;
|
||||
const LogCategory CATEGORY_SECURITY = (LogCategory)9;
|
||||
const LogCategory CATEGORY_TEST = (LogCategory)10;
|
||||
const LogCategory CATEGORY_ERROR = (LogCategory)11;
|
||||
const LogCategory CATEGORY_ASSERT = (LogCategory)12;
|
||||
const LogCategory CATEGORY_CRASH = (LogCategory)13;
|
||||
const LogCategory CATEGORY_STATS = (LogCategory)14;
|
||||
const LogCategory CATEGORY_CUSTOM_START = (LogCategory)100;
|
||||
|
||||
tlocal LogCategory default_category = CATEGORY_APPLICATION;
|
||||
tlocal LogTag current_tag;
|
||||
@@ -148,17 +148,18 @@ fn void call_log_internal(LogPriority prio, LogCategory category, String file, S
|
||||
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
|
||||
if (priority > prio) return;
|
||||
init();
|
||||
bool locked = logger_mutex.is_initialized() && @ok(logger_mutex.lock());
|
||||
bool locked = logger_mutex.is_initialized();
|
||||
if (locked) logger_mutex.lock();
|
||||
Logger logger = current_logger;
|
||||
LogFn logfn = current_logfn;
|
||||
defer if (locked) (void)logger_mutex.unlock();
|
||||
defer if (locked) logger_mutex.unlock();
|
||||
logfn(logger.ptr, prio, category, current_tag, file, func, line, fmt, args);
|
||||
}
|
||||
|
||||
fn String? get_category_name(LogCategory category)
|
||||
{
|
||||
String val = category_names[category];
|
||||
return val ?: NOT_FOUND?;
|
||||
return val ?: NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn void set_category_name(LogCategory category, String name)
|
||||
|
||||
@@ -7,7 +7,7 @@ import std::os::posix, std::os::win32;
|
||||
import std::math;
|
||||
|
||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
||||
const DEFAULT_MEM_ALIGNMENT = env::WASM ? 16 : (void*.alignof) * 2;
|
||||
const ulong KB = 1024;
|
||||
const ulong MB = KB * 1024;
|
||||
const ulong GB = MB * 1024;
|
||||
@@ -200,7 +200,7 @@ macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
|
||||
*>
|
||||
macro @unaligned_load(#x, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_load(&#x, $alignment);
|
||||
return $$unaligned_load(&#x, $alignment, false);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -215,9 +215,10 @@ macro @unaligned_load(#x, usz $alignment) @builtin
|
||||
*>
|
||||
macro @unaligned_store(#x, value, usz $alignment) @builtin
|
||||
{
|
||||
return $$unaligned_store(&#x, ($typeof(#x))value, $alignment);
|
||||
return $$unaligned_store(&#x, ($typeof(#x))value, $alignment, false);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@param #x : "The variable or dereferenced pointer to load."
|
||||
@return "The value of the variable"
|
||||
@@ -242,6 +243,36 @@ macro @volatile_store(#x, value) @builtin
|
||||
return $$volatile_store(&#x, ($typeof(#x))value);
|
||||
}
|
||||
|
||||
<*
|
||||
@param ptr : "The pointer to load from"
|
||||
@param $align : "The alignment to assume for the load"
|
||||
@param $volatile : "Whether the load is volatile or not, defaults to false"
|
||||
@return "The value of the variable"
|
||||
|
||||
@require $defined(*ptr) : "This must be a typed pointer"
|
||||
@require @constant_is_power_of_2($align) : "The alignment must be a power of two"
|
||||
*>
|
||||
macro load(ptr, usz $align, bool $volatile = false)
|
||||
{
|
||||
return $$unaligned_load(ptr, $align, $volatile);
|
||||
}
|
||||
|
||||
<*
|
||||
@param ptr : "The pointer to store to."
|
||||
@param value : "The value to store."
|
||||
@param $align : "The alignment to assume for the store"
|
||||
@param $volatile : "Whether the store is volatile, defaults to false"
|
||||
@return "The value stored"
|
||||
|
||||
@require $defined(*ptr) : "This must be a typed pointer"
|
||||
@require $defined(*ptr = value) : "The value doesn't match the variable"
|
||||
@require @constant_is_power_of_2($align) : "The alignment must be a power of two"
|
||||
*>
|
||||
macro store(ptr, value, usz $align, bool $volatile = false)
|
||||
{
|
||||
return $$unaligned_store(ptr, ($typeof(*ptr))value, $align, $volatile);
|
||||
}
|
||||
|
||||
<*
|
||||
All possible atomic orderings
|
||||
*>
|
||||
@@ -807,6 +838,7 @@ macro new_aligned($Type, #init = ...) @nodiscard @safemacro
|
||||
<*
|
||||
@param $Type : "The type to allocate"
|
||||
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
@return "A pointer to uninitialized data for the type $Type"
|
||||
*>
|
||||
@@ -819,7 +851,8 @@ macro alloc($Type) @nodiscard
|
||||
@param $Type : "The type to allocate"
|
||||
@param padding : "The padding to add after the allocation"
|
||||
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_aligned' instead"
|
||||
@return "A pointer to uninitialized data for the type $Type"
|
||||
*>
|
||||
macro alloc_with_padding($Type, usz padding) @nodiscard
|
||||
@@ -833,6 +866,7 @@ macro alloc_with_padding($Type, usz padding) @nodiscard
|
||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
|
||||
@param $Type : "The type to allocate"
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@return "A pointer to uninitialized data for the type $Type with the proper alignment"
|
||||
*>
|
||||
macro alloc_aligned($Type) @nodiscard
|
||||
@@ -844,6 +878,7 @@ macro alloc_aligned($Type) @nodiscard
|
||||
@param $Type : "The type to allocate"
|
||||
@param #init : "The optional initializer"
|
||||
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||
@return "A pointer to temporary data of type $Type."
|
||||
*>
|
||||
@@ -863,6 +898,7 @@ macro tnew($Type, #init = ...) @nodiscard @safemacro
|
||||
@param padding : "The padding to add after the allocation"
|
||||
@param #init : "The optional initializer"
|
||||
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@require !$defined(#init) ||| $defined($Type a = #init) : "#init must be an initializer for the type"
|
||||
@return "A pointer to temporary data of type $Type with added padding at the end."
|
||||
*>
|
||||
@@ -877,17 +913,24 @@ macro temp_with_padding($Type, usz padding, #init = ...) @nodiscard @safemacro
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
*>
|
||||
macro talloc($Type) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof, $Type.alignof);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
*>
|
||||
macro talloc_with_padding($Type, usz padding) @nodiscard
|
||||
{
|
||||
return tmalloc($Type.sizeof + padding, $Type.alignof);
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'new_array_aligned' instead"
|
||||
*>
|
||||
macro new_array($Type, usz elements) @nodiscard
|
||||
@@ -898,6 +941,8 @@ macro new_array($Type, usz elements) @nodiscard
|
||||
<*
|
||||
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
*>
|
||||
macro new_array_aligned($Type, usz elements) @nodiscard
|
||||
{
|
||||
@@ -905,6 +950,7 @@ macro new_array_aligned($Type, usz elements) @nodiscard
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
@require $Type.alignof <= DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
*>
|
||||
macro alloc_array($Type, usz elements) @nodiscard
|
||||
@@ -915,6 +961,8 @@ macro alloc_array($Type, usz elements) @nodiscard
|
||||
<*
|
||||
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
|
||||
@require $Type.kindof != OPTIONAL : "Expected a non-optional type"
|
||||
*>
|
||||
macro alloc_array_aligned($Type, usz elements) @nodiscard
|
||||
{
|
||||
@@ -1029,7 +1077,7 @@ fn void* __memcpy(void* dst, void* src, usz n) @weak @export("memcpy")
|
||||
}
|
||||
|
||||
|
||||
module std::core::mem::volatile { Type };
|
||||
module std::core::mem::volatile <Type>;
|
||||
|
||||
typedef Volatile @structlike = Type;
|
||||
|
||||
@@ -1046,7 +1094,7 @@ macro Type Volatile.set(&self, Type val)
|
||||
<*
|
||||
@require mem::@constant_is_power_of_2(ALIGNMENT) : "The alignment must be a power of 2"
|
||||
*>
|
||||
module std::core::mem::alignment { Type, ALIGNMENT };
|
||||
module std::core::mem::alignment <Type, ALIGNMENT>;
|
||||
import std::core::mem @public;
|
||||
|
||||
<*
|
||||
@@ -1056,10 +1104,10 @@ typedef UnalignedRef = Type*;
|
||||
|
||||
macro Type UnalignedRef.get(self)
|
||||
{
|
||||
return @unaligned_load(*(Type*)self, ALIGNMENT);
|
||||
return @unaligned_load(*(Type*)self, ALIGNMENT, false);
|
||||
}
|
||||
|
||||
macro Type UnalignedRef.set(&self, Type val)
|
||||
{
|
||||
return @unaligned_store(*(Type*)self, val, ALIGNMENT);
|
||||
return @unaligned_store(*(Type*)self, val, ALIGNMENT, false);
|
||||
}
|
||||
|
||||
@@ -304,6 +304,31 @@ macro alloc_array_try(Allocator allocator, $Type, usz elements) @nodiscard
|
||||
return (($Type*)malloc_try(allocator, $Type.sizeof * elements))[:elements];
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
*>
|
||||
macro realloc_array(Allocator allocator, void* ptr, $Type, usz elements) @nodiscard
|
||||
{
|
||||
return realloc_array_try(allocator, ptr, $Type, elements)!!;
|
||||
}
|
||||
|
||||
<*
|
||||
Allocate using an aligned allocation. This is necessary for types with a default memory alignment
|
||||
exceeding DEFAULT_MEM_ALIGNMENT. IMPORTANT! It must be freed using free_aligned.
|
||||
*>
|
||||
macro realloc_array_aligned(Allocator allocator, void* ptr, $Type, usz elements) @nodiscard
|
||||
{
|
||||
return (($Type*)realloc_aligned(allocator, ptr, $Type.sizeof * elements, $Type.alignof))[:elements]!!;
|
||||
}
|
||||
|
||||
<*
|
||||
@require $Type.alignof <= mem::DEFAULT_MEM_ALIGNMENT : "Types with alignment exceeding the default must use 'alloc_array_aligned' instead"
|
||||
*>
|
||||
macro realloc_array_try(Allocator allocator, void* ptr, $Type, usz elements) @nodiscard
|
||||
{
|
||||
return (($Type*)realloc_try(allocator, ptr, $Type.sizeof * elements))[:elements];
|
||||
}
|
||||
|
||||
<*
|
||||
Clone a value.
|
||||
|
||||
@@ -326,6 +351,8 @@ macro clone(Allocator allocator, value) @nodiscard
|
||||
*>
|
||||
macro clone_slice(Allocator allocator, slice) @nodiscard
|
||||
{
|
||||
if (!lengthof(slice)) return {};
|
||||
|
||||
var $Type = $typeof(slice[0]);
|
||||
|
||||
$Type[] new_arr = new_array(allocator, $Type, slice.len);
|
||||
@@ -377,6 +404,28 @@ macro void*? @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
|
||||
return mem;
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
@require bytes <= isz.max
|
||||
*>
|
||||
macro void*? @aligned_alloc_fn(context, #alloc_fn, usz bytes, usz alignment)
|
||||
{
|
||||
if (alignment < void*.alignof) alignment = void*.alignof;
|
||||
usz header = AlignedBlock.sizeof + alignment;
|
||||
usz alignsize = bytes + header;
|
||||
$if $kindof(#alloc_fn(context, bytes)) == OPTIONAL:
|
||||
void* data = #alloc_fn(context, alignsize)!;
|
||||
$else
|
||||
void* data = #alloc_fn(context, alignsize);
|
||||
$endif
|
||||
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
assert(mem > data);
|
||||
*desc = { bytes, data };
|
||||
return mem;
|
||||
}
|
||||
|
||||
struct AlignedBlock
|
||||
{
|
||||
usz len;
|
||||
@@ -393,6 +442,16 @@ macro void? @aligned_free(#free_fn, void* old_pointer)
|
||||
$endif
|
||||
}
|
||||
|
||||
macro void? @aligned_free_fn(context, #free_fn, void* old_pointer)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
$if $kindof(#free_fn(context, desc.start)) == OPTIONAL:
|
||||
#free_fn(context, desc.start)!;
|
||||
$else
|
||||
#free_fn(context, desc.start);
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
@@ -411,6 +470,23 @@ macro void*? @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
|
||||
return new_data;
|
||||
}
|
||||
|
||||
<*
|
||||
@require bytes > 0
|
||||
@require alignment > 0
|
||||
*>
|
||||
macro void*? @aligned_realloc_fn(context, #calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
void* data_start = desc.start;
|
||||
void* new_data = @aligned_alloc_fn(context, #calloc_fn, bytes, alignment)!;
|
||||
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
|
||||
$if $kindof(#free_fn(context, data_start)) == OPTIONAL:
|
||||
#free_fn(context, data_start)!;
|
||||
$else
|
||||
#free_fn(context, data_start);
|
||||
$endif
|
||||
return new_data;
|
||||
}
|
||||
|
||||
// All allocators
|
||||
alias mem @builtin = thread_allocator ;
|
||||
@@ -556,12 +632,12 @@ typedef NullAllocator (Allocator) = uptr;
|
||||
|
||||
fn void*? NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
|
||||
{
|
||||
return mem::OUT_OF_MEMORY?;
|
||||
return mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
|
||||
fn void*? NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
|
||||
{
|
||||
return mem::OUT_OF_MEMORY?;
|
||||
return mem::OUT_OF_MEMORY~;
|
||||
}
|
||||
|
||||
fn void NullAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
|
||||
|
||||
@@ -54,7 +54,7 @@ struct FixedBlockPool
|
||||
@require calculate_actual_capacity(capacity, block_size) * block_size >= block_size
|
||||
: "Total memory would overflow"
|
||||
*>
|
||||
macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
|
||||
fn FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
|
||||
{
|
||||
self.allocator = allocator;
|
||||
self.tail = &self.head;
|
||||
@@ -64,7 +64,7 @@ macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_
|
||||
self.alignment = allocator::alignment_for_allocation(alignment);
|
||||
self.page_size = capacity * self.block_size;
|
||||
assert(self.page_size >= self.block_size, "Total memory would overflow %d %d", block_size, capacity);
|
||||
self.head.buffer = self.allocate_page();
|
||||
self.head.buffer = fixedblockpool_allocate_page(self);
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(self.head.buffer, self.page_size);
|
||||
$endif
|
||||
@@ -119,7 +119,7 @@ fn void FixedBlockPool.free(&self)
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::unpoison_memory_region(self.head.buffer, self.page_size);
|
||||
$endif
|
||||
self.free_page(self.head.buffer);
|
||||
fixedblockpool_free_page(self, self.head.buffer);
|
||||
FixedBlockPoolNode* iter = self.head.next;
|
||||
|
||||
while (iter)
|
||||
@@ -127,7 +127,7 @@ fn void FixedBlockPool.free(&self)
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::unpoison_memory_region(iter.buffer, self.page_size);
|
||||
$endif
|
||||
self.free_page(iter.buffer);
|
||||
fixedblockpool_free_page(self, iter.buffer);
|
||||
FixedBlockPoolNode* current = iter;
|
||||
iter = iter.next;
|
||||
allocator::free(self.allocator, current);
|
||||
@@ -158,7 +158,7 @@ fn void* FixedBlockPool.alloc(&self)
|
||||
}
|
||||
|
||||
void* end = self.tail.buffer + (self.tail.capacity * self.block_size);
|
||||
if (self.next_free >= end) self.new_node();
|
||||
if (self.next_free >= end) fixedblockpool_new_node(self);
|
||||
void* ptr = self.next_free;
|
||||
self.next_free += self.block_size;
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
@@ -172,7 +172,7 @@ fn void* FixedBlockPool.alloc(&self)
|
||||
Deallocate a block from the block pool
|
||||
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
@require self.check_ptr(ptr) : "The pointer should be part of the pool"
|
||||
@require fixedblockpool_check_ptr(self, ptr) : "The pointer should be part of the pool"
|
||||
*>
|
||||
fn void FixedBlockPool.dealloc(&self, void* ptr)
|
||||
{
|
||||
@@ -193,7 +193,7 @@ fn void FixedBlockPool.dealloc(&self, void* ptr)
|
||||
<*
|
||||
@require self.initialized : "The block pool must be initialized"
|
||||
*>
|
||||
fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
|
||||
fn bool fixedblockpool_check_ptr(FixedBlockPool* self, void *ptr) @local
|
||||
{
|
||||
FixedBlockPoolNode* iter = &self.head;
|
||||
|
||||
@@ -210,10 +210,10 @@ fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
|
||||
<*
|
||||
@require self.grow_capacity > 0 : "How many blocks will it store"
|
||||
*>
|
||||
fn void FixedBlockPool.new_node(&self) @local
|
||||
fn void fixedblockpool_new_node(FixedBlockPool* self) @local
|
||||
{
|
||||
FixedBlockPoolNode* node = allocator::new(self.allocator, FixedBlockPoolNode);
|
||||
node.buffer = self.allocate_page();
|
||||
node.buffer = fixedblockpool_allocate_page(self);
|
||||
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
|
||||
asan::poison_memory_region(node.buffer, self.page_size);
|
||||
$endif
|
||||
@@ -224,14 +224,14 @@ fn void FixedBlockPool.new_node(&self) @local
|
||||
self.allocated += node.capacity;
|
||||
}
|
||||
|
||||
macro void* FixedBlockPool.allocate_page(&self) @private
|
||||
macro void* fixedblockpool_allocate_page(FixedBlockPool* self) @private
|
||||
{
|
||||
return self.alignment > mem::DEFAULT_MEM_ALIGNMENT
|
||||
? allocator::calloc_aligned(self.allocator, self.page_size, self.alignment)!!
|
||||
: allocator::calloc(self.allocator, self.page_size);
|
||||
}
|
||||
|
||||
macro void FixedBlockPool.free_page(&self, void* page) @private
|
||||
macro void fixedblockpool_free_page(FixedBlockPool* self, void* page) @private
|
||||
{
|
||||
if (self.alignment > mem::DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
|
||||
@@ -54,11 +54,11 @@ fn void*? alloc(usz size, VirtualMemoryAccess access)
|
||||
if (ptr != posix::MAP_FAILED) return ptr;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
|
||||
case errno::EPERM: return ACCESS_DENIED?;
|
||||
case errno::EINVAL: return INVALID_ARGS?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
case errno::ENOMEM: return mem::OUT_OF_MEMORY~;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW~;
|
||||
case errno::EPERM: return ACCESS_DENIED~;
|
||||
case errno::EINVAL: return INVALID_ARGS~;
|
||||
default: return UNKNOWN_ERROR~;
|
||||
}
|
||||
$case env::WIN32:
|
||||
void* ptr = win32::virtualAlloc(null, aligned_alloc_size(size), MEM_RESERVE, access.to_win32());
|
||||
@@ -66,8 +66,8 @@ fn void*? alloc(usz size, VirtualMemoryAccess access)
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY:
|
||||
case win32::ERROR_COMMITMENT_LIMIT: return mem::OUT_OF_MEMORY?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
case win32::ERROR_COMMITMENT_LIMIT: return mem::OUT_OF_MEMORY~;
|
||||
default: return UNKNOWN_ERROR~;
|
||||
}
|
||||
$default:
|
||||
unsupported("Virtual alloc only available on Win32 and Posix");
|
||||
@@ -89,18 +89,18 @@ fn void? release(void* ptr, usz size)
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EINVAL: return INVALID_ARGS?; // Not a valid mapping or size
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS?; // Address not mapped
|
||||
default: return RELEASE_FAILED?;
|
||||
case errno::EINVAL: return INVALID_ARGS~; // Not a valid mapping or size
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS~; // Address not mapped
|
||||
default: return RELEASE_FAILED~;
|
||||
}
|
||||
}
|
||||
$case env::WIN32:
|
||||
if (win32::virtualFree(ptr, 0, MEM_RELEASE)) return;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return INVALID_ARGS?;
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
|
||||
default: return RELEASE_FAILED?;
|
||||
case win32::ERROR_INVALID_ADDRESS: return INVALID_ARGS~;
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY~;
|
||||
default: return RELEASE_FAILED~;
|
||||
}
|
||||
$default:
|
||||
unsupported("Virtual free only available on Win32 and Posix");
|
||||
@@ -124,21 +124,21 @@ fn void? protect(void* ptr, usz len, VirtualMemoryAccess access)
|
||||
if (!posix::mprotect(ptr, len, access.to_posix())) return;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EACCES: return ACCESS_DENIED?;
|
||||
case errno::EINVAL: return UNALIGNED_ADDRESS?;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS?;
|
||||
default: return UPDATE_FAILED?;
|
||||
case errno::EACCES: return ACCESS_DENIED~;
|
||||
case errno::EINVAL: return UNALIGNED_ADDRESS~;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW~;
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS~;
|
||||
default: return UPDATE_FAILED~;
|
||||
}
|
||||
$case env::WIN32:
|
||||
Win32_Protect old;
|
||||
if (win32::virtualProtect(ptr, len, access.to_win32(), &old)) return;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
|
||||
default: return UPDATE_FAILED?;
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS~;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED~;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS~;
|
||||
default: return UPDATE_FAILED~;
|
||||
}
|
||||
$default:
|
||||
unsupported("'virtual_protect' is only available on Win32 and Posix.");
|
||||
@@ -165,12 +165,12 @@ fn void? commit(void* ptr, usz len, VirtualMemoryAccess access = READWRITE)
|
||||
if (result) return;
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS~;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED~;
|
||||
case win32::ERROR_COMMITMENT_LIMIT:
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY?;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
case win32::ERROR_NOT_ENOUGH_MEMORY: return mem::OUT_OF_MEMORY~;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS~;
|
||||
default: return UNKNOWN_ERROR~;
|
||||
}
|
||||
$default:
|
||||
unsupported("'virtual_commit' is only available on Win32 and Posix.");
|
||||
@@ -197,9 +197,9 @@ fn void? decommit(void* ptr, usz len, bool block = true)
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EINVAL: return UNALIGNED_ADDRESS?;
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS?;
|
||||
default: return UPDATE_FAILED?;
|
||||
case errno::EINVAL: return UNALIGNED_ADDRESS~;
|
||||
case errno::ENOMEM: return UNMAPPED_ACCESS~;
|
||||
default: return UPDATE_FAILED~;
|
||||
}
|
||||
}
|
||||
if (block) (void)protect(ptr, len, PROTECTED) @inline;
|
||||
@@ -208,10 +208,10 @@ fn void? decommit(void* ptr, usz len, bool block = true)
|
||||
{
|
||||
switch (win32::getLastError())
|
||||
{
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS?;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS?;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED?;
|
||||
default: return UPDATE_FAILED?;
|
||||
case win32::ERROR_INVALID_ADDRESS: return UNALIGNED_ADDRESS~;
|
||||
case win32::ERROR_INVALID_PARAMETER: return INVALID_ARGS~;
|
||||
case win32::ERROR_ACCESS_DENIED: return ACCESS_DENIED~;
|
||||
default: return UPDATE_FAILED~;
|
||||
}
|
||||
}
|
||||
$default:
|
||||
@@ -237,15 +237,15 @@ fn void*? mmap_file(Fd fd, usz size, usz offset = 0, VirtualMemoryAccess access
|
||||
if (ptr != posix::MAP_FAILED) return ptr;
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ENOMEM: return mem::OUT_OF_MEMORY?;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW?;
|
||||
case errno::EPERM: return ACCESS_DENIED?;
|
||||
case errno::EINVAL: return INVALID_ARGS?;
|
||||
case errno::EACCES: return io::NO_PERMISSION?;
|
||||
case errno::EBADF: return io::FILE_NOT_VALID?;
|
||||
case errno::EAGAIN: return io::WOULD_BLOCK?;
|
||||
case errno::ENXIO: return io::FILE_NOT_FOUND?;
|
||||
default: return UNKNOWN_ERROR?;
|
||||
case errno::ENOMEM: return mem::OUT_OF_MEMORY~;
|
||||
case errno::EOVERFLOW: return RANGE_OVERFLOW~;
|
||||
case errno::EPERM: return ACCESS_DENIED~;
|
||||
case errno::EINVAL: return INVALID_ARGS~;
|
||||
case errno::EACCES: return io::NO_PERMISSION~;
|
||||
case errno::EBADF: return io::FILE_NOT_VALID~;
|
||||
case errno::EAGAIN: return io::WOULD_BLOCK~;
|
||||
case errno::ENXIO: return io::FILE_NOT_FOUND~;
|
||||
default: return UNKNOWN_ERROR~;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ fn void? VirtualMemory.destroy(&self)
|
||||
return release(self.ptr, self.size);
|
||||
}
|
||||
|
||||
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
|
||||
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
@@ -336,7 +336,7 @@ fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
|
||||
}
|
||||
}
|
||||
|
||||
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
|
||||
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32)
|
||||
{
|
||||
switch (self)
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ fn char[]? WasmMemory.allocate_block(&self, usz bytes)
|
||||
}
|
||||
|
||||
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
|
||||
if ($$wasm_memory_grow(0, blocks_required) == -1) return mem::OUT_OF_MEMORY?;
|
||||
if ($$wasm_memory_grow(0, blocks_required) == -1) return mem::OUT_OF_MEMORY~;
|
||||
self.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
|
||||
defer self.use += bytes;
|
||||
return ((char*)self.use)[:bytes];
|
||||
|
||||
@@ -79,7 +79,7 @@ fn SegmentCommand64*? find_segment(MachHeader* header, char* segname)
|
||||
}
|
||||
command = (void*)command + command.cmdsize;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
fn Section64*? find_section(SegmentCommand64* command, char* sectname)
|
||||
{
|
||||
@@ -89,7 +89,7 @@ fn Section64*? find_section(SegmentCommand64* command, char* sectname)
|
||||
if (name_cmp(sectname, §ion.sectname)) return section;
|
||||
section++;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type)
|
||||
|
||||
@@ -7,16 +7,26 @@ macro usz _strlen(ptr) @private
|
||||
return len;
|
||||
}
|
||||
|
||||
macro int @main_to_err_main(#m, int, char**)
|
||||
macro int @main_no_args(#m, int, char**)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
$if $typeof(#m()) == void:
|
||||
#m();
|
||||
return 0;
|
||||
$else
|
||||
return #m();
|
||||
$endif
|
||||
}
|
||||
macro int @main_to_int_main(#m, int, char**) => #m();
|
||||
macro int @main_to_void_main(#m, int, char**)
|
||||
|
||||
macro int @main_args(#m, int argc, char** argv)
|
||||
{
|
||||
#m();
|
||||
return 0;
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
$if $typeof(#m(list)) == void:
|
||||
#m(list);
|
||||
return 0;
|
||||
$else
|
||||
return #m(list);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro String[] args_to_strings(int argc, char** argv) @private
|
||||
@@ -32,21 +42,6 @@ macro String[] args_to_strings(int argc, char** argv) @private
|
||||
}
|
||||
|
||||
|
||||
macro int @main_to_err_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
if (catch #m(list)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @main_to_int_main_args(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
return #m(list);
|
||||
}
|
||||
|
||||
macro int @_main_runner(#m, int argc, char** argv)
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
@@ -54,15 +49,18 @@ macro int @_main_runner(#m, int argc, char** argv)
|
||||
return #m(list) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @main_to_void_main_args(#m, int argc, char** argv)
|
||||
module std::core::main_stub @if(env::WIN32);
|
||||
import std::os::win32;
|
||||
|
||||
macro win32_set_utf8_codepage() @local
|
||||
{
|
||||
String[] list = args_to_strings(argc, argv);
|
||||
defer free(list.ptr);
|
||||
#m(list);
|
||||
return 0;
|
||||
// By default windows uses an OEM codepage that differs based on locale
|
||||
// and does not support printing utf-8 characters. This allows both
|
||||
// printing utf-8 characters from strings and reading them from stdin.
|
||||
win32::setConsoleCP(UTF8);
|
||||
win32::setConsoleOutputCP(UTF8);
|
||||
}
|
||||
|
||||
module std::core::main_stub @if(env::WIN32);
|
||||
|
||||
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @cname("CommandLineToArgvW");
|
||||
|
||||
@@ -91,90 +89,72 @@ macro void release_wargs(String[] list) @private
|
||||
free(list.ptr);
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
macro int @win_main_no_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
if (catch #m()) return 1;
|
||||
return 0;
|
||||
}
|
||||
macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) => #m();
|
||||
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
#m();
|
||||
return 0;
|
||||
win32_set_utf8_codepage();
|
||||
$if $typeof(#m()) == void:
|
||||
#m();
|
||||
return 0;
|
||||
$else
|
||||
return #m();
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
macro int @win_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(args)) return 1;
|
||||
return 0;
|
||||
$if $typeof(#m(args)) == void:
|
||||
#m(args);
|
||||
return 0;
|
||||
$else
|
||||
return #m(args);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
macro int @win_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return #m(args);
|
||||
$if $typeof(#m(handle, prev_handle, args, show_cmd)) == void:
|
||||
#m(handle, prev_handle, args, show_cmd);
|
||||
return 0;
|
||||
$else
|
||||
return #m(handle, prev_handle, args, show_cmd);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
#m(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
return #m(handle, prev_handle, args, show_cmd);
|
||||
}
|
||||
|
||||
macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
|
||||
{
|
||||
String[] args = win_command_line_to_strings(cmd_line);
|
||||
defer release_wargs(args);
|
||||
#m(handle, prev_handle, args, show_cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
|
||||
macro int @wmain_main(#m, int argc, Char16** argv)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
if (catch #m(args)) return 1;
|
||||
return 1;
|
||||
$if $typeof(#m(args)) == void:
|
||||
#m(args);
|
||||
return 0;
|
||||
$else
|
||||
return #m(args);
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
|
||||
macro int @wmain_main_no_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args);
|
||||
win32_set_utf8_codepage();
|
||||
$if $typeof(#m()) == void:
|
||||
#m();
|
||||
return 0;
|
||||
$else
|
||||
return #m();
|
||||
$endif
|
||||
}
|
||||
|
||||
macro int @_wmain_runner(#m, int argc, Char16** argv)
|
||||
{
|
||||
win32_set_utf8_codepage();
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
return #m(args) ? 0 : 1;
|
||||
}
|
||||
|
||||
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
|
||||
{
|
||||
String[] args = wargs_strings(argc, argv);
|
||||
defer release_wargs(args);
|
||||
#m(args);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
@require !$defined(Type.dealloc) ||| $defined(Type.dealloc(&&(Type){})) : "'dealloc' must only take a pointer to the underlying type"
|
||||
@require !$defined(Type.dealloc) ||| $typeof((Type){}.dealloc()) == void : "'dealloc' must return 'void'"
|
||||
*>
|
||||
module std::core::mem::ref { Type };
|
||||
module std::core::mem::ref <Type>;
|
||||
import std::thread, std::atomic;
|
||||
|
||||
const OVERALIGNED @private = Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
|
||||
|
||||
@@ -27,7 +27,7 @@ macro @enum_lookup($Type, #value, value)
|
||||
$foreach $val : $Type.values:
|
||||
if ($val.#value == value) return $val;
|
||||
$endforeach
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
macro @enum_lookup_new($Type, $name, value)
|
||||
@@ -35,7 +35,7 @@ macro @enum_lookup_new($Type, $name, value)
|
||||
$foreach $val : $Type.values:
|
||||
if ($val.$eval($name) == value) return $val;
|
||||
$endforeach
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -125,10 +125,11 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
|
||||
char[] perc_str = { [0..19] = ' ', [20] = 0 };
|
||||
int perc = 0;
|
||||
uint print_step = current_benchmark_iterations / 100;
|
||||
if (print_step == 0) print_step = 1;
|
||||
|
||||
for (this_iteration = 0; this_iteration < current_benchmark_iterations; ++this_iteration, benchmark_nano_seconds = {})
|
||||
{
|
||||
if (0 == this_iteration % print_step) // only print right about when the % will update
|
||||
if (this_iteration % print_step == 0) // only print right about when the % will update
|
||||
{
|
||||
perc_str[0..(uint)math::floor((this_iteration / (float)current_benchmark_iterations) * 20)] = '#';
|
||||
perc = (uint)math::ceil(100 * (this_iteration / (float)current_benchmark_iterations));
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
module std::core::runtime;
|
||||
import std::core::test @public;
|
||||
import std::core::mem::allocator @public;
|
||||
import libc, std::time, std::io, std::sort;
|
||||
import std::os::env;
|
||||
import libc, std::time, std::io, std::sort, std::os;
|
||||
|
||||
|
||||
alias TestFn = fn void();
|
||||
|
||||
@@ -18,6 +18,8 @@ struct TestContext
|
||||
String test_filter;
|
||||
<* Triggers debugger breakpoint when assert or test:: checks failed *>
|
||||
bool breakpoint_on_assert;
|
||||
<* Controls level of printed logs *>
|
||||
LogPriority log_level;
|
||||
|
||||
// internal state
|
||||
bool assert_print_backtrace;
|
||||
@@ -25,6 +27,8 @@ struct TestContext
|
||||
bool is_in_panic;
|
||||
bool is_quiet_mode;
|
||||
bool is_no_capture;
|
||||
bool sort;
|
||||
bool check_leaks;
|
||||
String current_test_name;
|
||||
TestFn setup_fn;
|
||||
TestFn teardown_fn;
|
||||
@@ -86,7 +90,21 @@ fn bool terminal_has_ansi_codes() @local => @pool()
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void sig_bus_error(CInt i, void*, void* context) @local @if(env::POSIX)
|
||||
{
|
||||
panic_test("Bus error", "Unknown", "Unknown", 1, posix::stack_instruction(context));
|
||||
}
|
||||
|
||||
fn void sig_segmentation_fault(CInt i, void*, void* context) @local @if(env::POSIX)
|
||||
{
|
||||
panic_test("Segmentation fault", "Unknown", "Unknown", 1, posix::stack_instruction(context));
|
||||
}
|
||||
|
||||
fn void test_panic(String message, String file, String function, uint line) @local
|
||||
{
|
||||
panic_test(message, file, function, line);
|
||||
}
|
||||
fn void panic_test(String message, String file, String function, uint line, void* extra_trace = null) @local
|
||||
{
|
||||
if (test_context.is_in_panic) return;
|
||||
test_context.is_in_panic = true;
|
||||
@@ -96,7 +114,7 @@ fn void test_panic(String message, String file, String function, uint line) @loc
|
||||
if (test_context.assert_print_backtrace)
|
||||
{
|
||||
$if env::NATIVE_STACKTRACE:
|
||||
builtin::print_backtrace(message, 0);
|
||||
builtin::print_backtrace(message, extra_trace ? 3 : 0, extra_trace);
|
||||
$endif
|
||||
}
|
||||
io::printf("\nTest failed ^^^ ( %s:%s ) %s\n", file, line, message);
|
||||
@@ -124,7 +142,7 @@ fn void mute_output() @local
|
||||
File* stderr = io::stderr();
|
||||
*stderr = test_context.fake_stdout;
|
||||
*stdout = test_context.fake_stdout;
|
||||
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
|
||||
(void)test_context.fake_stdout.set_cursor(0)!!;
|
||||
}
|
||||
|
||||
fn void unmute_output(bool has_error) @local
|
||||
@@ -137,7 +155,7 @@ fn void unmute_output(bool has_error) @local
|
||||
*stderr = test_context.stored.stderr;
|
||||
*stdout = test_context.stored.stdout;
|
||||
|
||||
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
|
||||
ulong log_size = test_context.fake_stdout.cursor()!!;
|
||||
if (has_error)
|
||||
{
|
||||
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
|
||||
@@ -147,7 +165,7 @@ fn void unmute_output(bool has_error) @local
|
||||
{
|
||||
test_context.fake_stdout.write_byte('\n')!!;
|
||||
test_context.fake_stdout.write_byte('\0')!!;
|
||||
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
|
||||
test_context.fake_stdout.set_cursor(0)!!;
|
||||
|
||||
io::printfn("\n========== TEST LOG ============");
|
||||
io::printfn("%s\n", test_context.current_test_name);
|
||||
@@ -165,16 +183,19 @@ fn void unmute_output(bool has_error) @local
|
||||
(void)stdout.flush();
|
||||
}
|
||||
|
||||
|
||||
fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
usz max_name;
|
||||
bool sort_tests = true;
|
||||
bool check_leaks = true;
|
||||
if (!tests.len)
|
||||
{
|
||||
io::printn("There are no test units to run.");
|
||||
return true; // no tests == technically a pass
|
||||
}
|
||||
$if !env::NO_LIBC && env::POSIX:
|
||||
posix::install_signal_handler(libc::SIGBUS, &sig_bus_error);
|
||||
posix::install_signal_handler(libc::SIGSEGV, &sig_segmentation_fault);
|
||||
$endif
|
||||
foreach (&unit : tests)
|
||||
{
|
||||
if (max_name < unit.name.len) max_name = unit.name.len;
|
||||
@@ -183,6 +204,9 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
.assert_print_backtrace = true,
|
||||
.breakpoint_on_assert = false,
|
||||
.sort = true,
|
||||
.check_leaks = true,
|
||||
.log_level = LogPriority.ERROR,
|
||||
.test_filter = "",
|
||||
.has_ansi_codes = terminal_has_ansi_codes(),
|
||||
.stored.allocator = mem,
|
||||
@@ -196,10 +220,11 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
case "--test-breakpoint":
|
||||
context.breakpoint_on_assert = true;
|
||||
case "--test-nosort":
|
||||
sort_tests = false;
|
||||
context.sort = false;
|
||||
case "--test-noleak":
|
||||
check_leaks = false;
|
||||
context.check_leaks = false;
|
||||
case "--test-nocapture":
|
||||
case "--test-show-output":
|
||||
context.is_no_capture = true;
|
||||
case "--noansi":
|
||||
context.has_ansi_codes = false;
|
||||
@@ -215,13 +240,30 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
}
|
||||
context.test_filter = args[i + 1];
|
||||
i++;
|
||||
case "--test-log-level":
|
||||
if (i == args.len - 1)
|
||||
{
|
||||
io::printn("Missing log level for argument `--test-log-level`.");
|
||||
return false;
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
String upper = args[i + 1].to_upper_copy(tmem);
|
||||
if (catch @try(context.log_level, enum_by_name(LogPriority, upper)))
|
||||
{
|
||||
io::printn("Log level given to `--test-log-level` is not one of verbose, debug, info, warn, error or critical.");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
i++;
|
||||
default:
|
||||
io::printfn("Unknown argument: %s", args[i]);
|
||||
}
|
||||
}
|
||||
test_context = &context;
|
||||
log::set_priority_all(test_context.log_level);
|
||||
|
||||
if (sort_tests)
|
||||
if (context.sort)
|
||||
{
|
||||
quicksort(tests, &cmp_test_unit);
|
||||
}
|
||||
@@ -281,14 +323,17 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
|
||||
{
|
||||
mute_output();
|
||||
mem.clear();
|
||||
if (check_leaks) allocator::thread_allocator = &mem;
|
||||
unit.func();
|
||||
if (context.check_leaks) allocator::thread_allocator = &mem;
|
||||
@pool()
|
||||
{
|
||||
unit.func();
|
||||
};
|
||||
// track cleanup that may take place in teardown_fn
|
||||
if (context.teardown_fn)
|
||||
{
|
||||
context.teardown_fn();
|
||||
}
|
||||
if (check_leaks) allocator::thread_allocator = context.stored.allocator;
|
||||
if (context.check_leaks) allocator::thread_allocator = context.stored.allocator;
|
||||
|
||||
unmute_output(false); // all good, discard output
|
||||
if (mem.has_leaks())
|
||||
|
||||
@@ -2,16 +2,16 @@ module std::core::sanitizer::tsan;
|
||||
|
||||
typedef MutexFlags = inline CUInt;
|
||||
|
||||
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
|
||||
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;
|
||||
const MutexFlags MUTEX_READ_REENTRANT = 1 << 2;
|
||||
const MutexFlags MUTEX_NOT_STATIC = 1 << 8;
|
||||
const MutexFlags MUTEX_READ_LOCK = 1 << 3;
|
||||
const MutexFlags MUTEX_TRY_LOCK = 1 << 4;
|
||||
const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5;
|
||||
const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6;
|
||||
const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7;
|
||||
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
|
||||
const MutexFlags MUTEX_LINKER_INIT = (MutexFlags)1 << 0;
|
||||
const MutexFlags MUTEX_WRITE_REENTRANT = (MutexFlags)1 << 1;
|
||||
const MutexFlags MUTEX_READ_REENTRANT = (MutexFlags)1 << 2;
|
||||
const MutexFlags MUTEX_NOT_STATIC = (MutexFlags)1 << 8;
|
||||
const MutexFlags MUTEX_READ_LOCK = (MutexFlags)1 << 3;
|
||||
const MutexFlags MUTEX_TRY_LOCK = (MutexFlags)1 << 4;
|
||||
const MutexFlags MUTEX_TRY_LOCK_FAILED = (MutexFlags)1 << 5;
|
||||
const MutexFlags MUTEX_RECURSIVE_LOCK = (MutexFlags)1 << 6;
|
||||
const MutexFlags MUTEX_RECURSIVE_UNLOCK = (MutexFlags)1 << 7;
|
||||
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
|
||||
const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED;
|
||||
|
||||
macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module std::core::array::slice {Type};
|
||||
module std::core::array;
|
||||
|
||||
<*
|
||||
A slice2d allows slicing an array like int[10][10] into an arbitrary "int[][]"-like counterpart
|
||||
Typically you'd use array::slice2d(...) to create one.
|
||||
*>
|
||||
struct Slice2d
|
||||
struct Slice2d <Type>
|
||||
{
|
||||
Type* ptr;
|
||||
usz inner_len;
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
module std::core::string;
|
||||
import std::io, std::ascii;
|
||||
import std::core::mem::allocator;
|
||||
|
||||
|
||||
typedef String @if(!$defined(String)) = inline char[];
|
||||
|
||||
typedef String @constinit @if(!$defined(String)) = inline char[];
|
||||
<*
|
||||
ZString is a pointer to a zero terminated array of chars.
|
||||
|
||||
Use ZString when you need to interop with C zero terminated strings.
|
||||
*>
|
||||
typedef ZString = inline char*;
|
||||
typedef ZString @constinit = inline char*;
|
||||
|
||||
<*
|
||||
WString is a pointer to a zero terminated array of Char16.
|
||||
@@ -70,7 +72,7 @@ macro WString @wstring(String $string) @builtin
|
||||
}
|
||||
|
||||
<*
|
||||
Create a slice of an UTF32 encoded string at compile time.
|
||||
Create a slice of an UTF16 encoded string at compile time.
|
||||
|
||||
@param $string : "The string to encode"
|
||||
*>
|
||||
@@ -96,7 +98,8 @@ fn ZString tformat_zstr(String fmt, args...) @format(0)
|
||||
}
|
||||
|
||||
<*
|
||||
Return a new String created using the formatting function.
|
||||
Return a new String created using the formatting function, this function will implicitly
|
||||
use the temp allocator.
|
||||
|
||||
@param [inout] allocator : `The allocator to use`
|
||||
@param [in] fmt : `The formatting string`
|
||||
@@ -120,7 +123,7 @@ fn String bformat(char[] buffer, String fmt, args...) @format(1)
|
||||
OutputFn format_fn = fn void?(void* buf, char c) {
|
||||
char[]* buffer_ref = buf;
|
||||
char[] buffer = *buffer_ref;
|
||||
if (buffer.len == 0) return io::BUFFER_EXCEEDED?;
|
||||
if (buffer.len == 0) return io::BUFFER_EXCEEDED~;
|
||||
buffer[0] = c;
|
||||
*buffer_ref = buffer[1..];
|
||||
};
|
||||
@@ -156,31 +159,44 @@ macro bool char_in_set(char c, String set)
|
||||
return false;
|
||||
}
|
||||
|
||||
<*
|
||||
Join together an array of strings via a "joiner" sequence, which is inserted between each element.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use."
|
||||
@param [in] s : "An array of strings to join in sequence."
|
||||
@param [in] joiner : "The string used to join each element of `s`."
|
||||
@return "A single string containing the result, allocated via `allocator`, safe to convert to a ZString."
|
||||
*>
|
||||
fn String join(Allocator allocator, String[] s, String joiner)
|
||||
{
|
||||
if (!s)
|
||||
{
|
||||
return (String)allocator::new_array(allocator, char, 2)[:0];
|
||||
}
|
||||
|
||||
usz total_size = joiner.len * s.len;
|
||||
usz joiner_len = joiner.len;
|
||||
usz total_size = joiner_len * (s.len - 1) + 1;
|
||||
foreach (String* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
@pool()
|
||||
char[] data = allocator::alloc_array(allocator, char, total_size);
|
||||
usz offset = s[0].len;
|
||||
data[:offset] = s[0][:offset];
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
DString res = dstring::temp_with_capacity(total_size);
|
||||
res.append(s[0]);
|
||||
foreach (String* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res.copy_str(allocator);
|
||||
data[offset:joiner_len] = joiner[:joiner_len];
|
||||
offset += joiner_len;
|
||||
usz len = str.len;
|
||||
data[offset:len] = str.[:len];
|
||||
offset += len;
|
||||
};
|
||||
data[offset] = 0;
|
||||
return (String)data[:offset];
|
||||
}
|
||||
|
||||
<* Alias for `string::join` using the temp allocator. *>
|
||||
macro String tjoin(String[] s, String joiner) => join(tmem, s, joiner);
|
||||
|
||||
<*
|
||||
Replace all instances of one substring with a different string.
|
||||
|
||||
@@ -190,13 +206,18 @@ fn String join(Allocator allocator, String[] s, String joiner)
|
||||
@param [&inout] allocator : `The allocator to use for the String`
|
||||
@return "The new string with the elements replaced"
|
||||
*>
|
||||
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard
|
||||
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard => @pool()
|
||||
{
|
||||
@pool()
|
||||
Splitter s = self.tokenize_all(needle);
|
||||
DString d;
|
||||
d.init(tmem, new_str.len * 2 + self.len + 16);
|
||||
(void)d.append(s.next());
|
||||
while (try element = s.next())
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).copy_str(allocator);
|
||||
};
|
||||
d.append(new_str);
|
||||
d.append(element);
|
||||
}
|
||||
return d.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -209,8 +230,16 @@ fn String String.replace(self, Allocator allocator, String needle, String new_st
|
||||
*>
|
||||
fn String String.treplace(self, String needle, String new_str)
|
||||
{
|
||||
String[] split = self.tsplit(needle);
|
||||
return dstring::join(tmem, split, new_str).str_view();
|
||||
Splitter s = self.tokenize_all(needle);
|
||||
DString d;
|
||||
d.init(tmem, new_str.len * 2 + self.len + 16);
|
||||
(void)d.append(s.next());
|
||||
while (try element = s.next())
|
||||
{
|
||||
d.append(new_str);
|
||||
d.append(element);
|
||||
}
|
||||
return d.str_view();
|
||||
}
|
||||
|
||||
|
||||
@@ -355,7 +384,7 @@ fn String[] String.split(self, Allocator allocator, String delimiter, usz max =
|
||||
bool no_more = false;
|
||||
while (!no_more)
|
||||
{
|
||||
usz? index = i == max - 1 ? NOT_FOUND? : self.index_of(delimiter);
|
||||
usz? index = i == max - 1 ? NOT_FOUND~ : self.index_of(delimiter);
|
||||
String res @noinit;
|
||||
if (try index)
|
||||
{
|
||||
@@ -414,7 +443,7 @@ fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz ma
|
||||
bool no_more = false;
|
||||
while (!no_more)
|
||||
{
|
||||
usz? index = i == max - 1 ? NOT_FOUND? : s.index_of(delimiter);
|
||||
usz? index = i == max - 1 ? NOT_FOUND~ : s.index_of(delimiter);
|
||||
String res @noinit;
|
||||
if (try index)
|
||||
{
|
||||
@@ -432,7 +461,7 @@ fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz ma
|
||||
}
|
||||
if (i == max_capacity)
|
||||
{
|
||||
return BUFFER_EXCEEDED?;
|
||||
return BUFFER_EXCEEDED~;
|
||||
}
|
||||
buffer[i++] = res;
|
||||
}
|
||||
@@ -513,7 +542,7 @@ fn usz? String.index_of_char(self, char character)
|
||||
{
|
||||
if (c == character) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -536,7 +565,7 @@ fn usz? String.index_of_chars(String self, char[] characters)
|
||||
}
|
||||
}
|
||||
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -553,12 +582,12 @@ fn usz? String.index_of_chars(String self, char[] characters)
|
||||
fn usz? String.index_of_char_from(self, char character, usz start_index)
|
||||
{
|
||||
usz len = self.len;
|
||||
if (len <= start_index) return NOT_FOUND?;
|
||||
if (len <= start_index) return NOT_FOUND~;
|
||||
for (usz i = start_index; i < len; i++)
|
||||
{
|
||||
if (self[i] == character) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -577,7 +606,7 @@ fn usz? String.rindex_of_char(self, char character)
|
||||
{
|
||||
if (c == character) return i;
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -602,7 +631,7 @@ fn usz? String.index_of(self, String substr)
|
||||
if (c == first && self[i : needed] == substr) return i;
|
||||
}
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -627,7 +656,7 @@ fn usz? String.rindex_of(self, String substr)
|
||||
if (c == first && self[i : needed] == substr) return i;
|
||||
}
|
||||
}
|
||||
return NOT_FOUND?;
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn bool ZString.eq(self, ZString other) @operator(==)
|
||||
@@ -1013,12 +1042,12 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
usz index = 0;
|
||||
char* ptr = self.ptr;
|
||||
while (index < len && ptr[index].is_blank()) index++;
|
||||
if (len == index) return EMPTY_STRING?;
|
||||
if (len == index) return EMPTY_STRING~;
|
||||
bool is_negative;
|
||||
switch (self[index])
|
||||
{
|
||||
case '-':
|
||||
if ($Type.min == 0) return NEGATIVE_VALUE?;
|
||||
if ($Type.min == 0) return NEGATIVE_VALUE~;
|
||||
is_negative = true;
|
||||
index++;
|
||||
case '+':
|
||||
@@ -1026,7 +1055,7 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return MALFORMED_INTEGER?;
|
||||
if (len == index) return MALFORMED_INTEGER~;
|
||||
$Type base_used = ($Type)base;
|
||||
if (self[index] == '0' && base == 10)
|
||||
{
|
||||
@@ -1049,7 +1078,7 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return MALFORMED_INTEGER?;
|
||||
if (len == index) return MALFORMED_INTEGER~;
|
||||
}
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
@@ -1059,22 +1088,18 @@ macro String.to_integer(self, $Type, int base = 10)
|
||||
{
|
||||
case base_used < 10 || c < 'A': c -= '0';
|
||||
case c <= 'F': c -= 'A' - 10;
|
||||
case c < 'a' || c > 'f': return MALFORMED_INTEGER?;
|
||||
case c < 'a' || c > 'f': return MALFORMED_INTEGER~;
|
||||
default: c -= 'a' - 10;
|
||||
}
|
||||
if (c >= base_used) return MALFORMED_INTEGER?;
|
||||
if (c >= base_used) return MALFORMED_INTEGER~;
|
||||
do
|
||||
{
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base_used - c;
|
||||
if (new_value > value) return INTEGER_OVERFLOW?;
|
||||
value = new_value;
|
||||
value = value.overflow_mul(base_used).overflow_sub(c) ?? INTEGER_OVERFLOW~!;
|
||||
break;
|
||||
}
|
||||
$Type new_value = value * base_used + c;
|
||||
if (new_value < value) return INTEGER_OVERFLOW?;
|
||||
value = new_value;
|
||||
value = value.overflow_mul(base_used).overflow_add(c) ?? INTEGER_OVERFLOW~!;
|
||||
};
|
||||
}
|
||||
return value;
|
||||
@@ -1188,16 +1213,21 @@ fn void Splitter.reset(&self)
|
||||
self.current = 0;
|
||||
}
|
||||
|
||||
fn bool Splitter.at_end(&self)
|
||||
{
|
||||
return self.current > self.string.len;
|
||||
}
|
||||
|
||||
fn String? Splitter.next(&self)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
usz len = self.string.len;
|
||||
usz current = self.current;
|
||||
if (current > len) return NO_MORE_ELEMENT?;
|
||||
if (current > len) return NO_MORE_ELEMENT~;
|
||||
if (current == len)
|
||||
{
|
||||
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT?;
|
||||
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT~;
|
||||
self.current++;
|
||||
return self.string[current - 1:0];
|
||||
}
|
||||
@@ -1209,7 +1239,7 @@ fn String? Splitter.next(&self)
|
||||
if (!next && self.type == TOKENIZE) continue;
|
||||
return remaining[:next];
|
||||
}
|
||||
self.current = len;
|
||||
self.current = len + 1;
|
||||
return remaining;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
|
||||
// Copyright (c) 2024-2025 Christoffer Lerno. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
|
||||
@@ -22,43 +22,57 @@ faultdef INVALID_ESCAPE_SEQUENCE, UNTERMINATED_STRING, INVALID_HEX_ESCAPE, INVAL
|
||||
*>
|
||||
fn String String.escape(String s, Allocator allocator, bool strip_quotes = true)
|
||||
{
|
||||
// Conservative allocation: most strings need minimal escaping
|
||||
usz initial_capacity = s.len + s.len / 5 + 2; // ~1.2x + quotes
|
||||
DString result = dstring::new_with_capacity(allocator, initial_capacity);
|
||||
// Conservative allocation: most strings need minimal escaping
|
||||
usz initial_capacity = s.len + s.len / 5 + 2; // ~1.2x + quotes
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"': result.append(`\"`);
|
||||
case '\\': result.append(`\\`);
|
||||
case '\b': result.append(`\b`);
|
||||
case '\f': result.append(`\f`);
|
||||
case '\n': result.append(`\n`);
|
||||
case '\r': result.append(`\r`);
|
||||
case '\t': result.append(`\t`);
|
||||
case '\v': result.append(`\v`);
|
||||
case '\0': result.append(`\0`);
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
// Printable ASCII
|
||||
result.append_char(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-printable, use hex escape
|
||||
result.appendf("\\x%02x", (uint)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
return result.copy_str(allocator);
|
||||
if (allocator == tmem)
|
||||
{
|
||||
DString result = dstring::new_with_capacity(tmem, initial_capacity);
|
||||
escape_dstring(s, result, strip_quotes);
|
||||
return result.str_view();
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
DString result = dstring::temp_with_capacity(initial_capacity);
|
||||
escape_dstring(s, result, strip_quotes);
|
||||
return result.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
fn void escape_dstring(String s, DString result, bool strip_quotes) @private
|
||||
{
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"': result.append(`\"`);
|
||||
case '\\': result.append(`\\`);
|
||||
case '\b': result.append(`\b`);
|
||||
case '\f': result.append(`\f`);
|
||||
case '\n': result.append(`\n`);
|
||||
case '\r': result.append(`\r`);
|
||||
case '\t': result.append(`\t`);
|
||||
case '\v': result.append(`\v`);
|
||||
case '\0': result.append(`\0`);
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
// Printable ASCII
|
||||
result.append_char(c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Non-printable, use hex escape
|
||||
result.appendf("\\x%02x", (uint)c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strip_quotes) result.append_char('"');
|
||||
|
||||
}
|
||||
<*
|
||||
Escape a string using the temp allocator.
|
||||
|
||||
@@ -76,33 +90,33 @@ fn String String.tescape(String s, bool strip_quotes = false) => s.escape(tmem,
|
||||
*>
|
||||
fn usz escape_len(String s)
|
||||
{
|
||||
usz len = 2; // For quotes
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
len += 2; // \X
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
len += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len += 4; // \xHH
|
||||
}
|
||||
}
|
||||
}
|
||||
return len;
|
||||
usz len = 2; // For quotes
|
||||
foreach (char c : s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
len += 2; // \X
|
||||
default:
|
||||
if (c >= 32 && c <= 126)
|
||||
{
|
||||
len += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len += 4; // \xHH
|
||||
}
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -111,90 +125,103 @@ fn usz escape_len(String s)
|
||||
@param allocator : "The allocator to use for the result"
|
||||
@param s : "The quoted string to unescape"
|
||||
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
|
||||
@param lenient : "Be lenient with escapes, resolving unknown sequences to the escape character, defaults to false"
|
||||
@return "The unescaped string without quotes, safe to convert to ZString"
|
||||
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
|
||||
*>
|
||||
fn String? String.unescape(String s, Allocator allocator, bool allow_unquoted = false)
|
||||
fn String? String.unescape(String s, Allocator allocator, bool allow_unquoted = false, bool lenient = false)
|
||||
{
|
||||
if (s.len >= 2 && s[0] == '"' && s[^1] == '"')
|
||||
{
|
||||
// Remove quotes.
|
||||
s = s[1:^2];
|
||||
}
|
||||
else if (!allow_unquoted) return UNTERMINATED_STRING?;
|
||||
else if (!allow_unquoted) return UNTERMINATED_STRING~;
|
||||
|
||||
// Handle empty string case
|
||||
if (!s.len)
|
||||
{
|
||||
return "".copy(allocator);
|
||||
}
|
||||
|
||||
DString result = dstring::new_with_capacity(allocator, s.len);
|
||||
// Handle empty string case
|
||||
if (!s.len)
|
||||
{
|
||||
return "".copy(allocator);
|
||||
}
|
||||
if (allocator == tmem)
|
||||
{
|
||||
DString result = dstring::new_with_capacity(tmem, s.len);
|
||||
unescape_dstring(s, result, allow_unquoted, lenient)!;
|
||||
return result.str_view();
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
DString result = dstring::temp_with_capacity(s.len);
|
||||
unescape_dstring(s, result, allow_unquoted, lenient)!;
|
||||
return result.copy_str(allocator);
|
||||
};
|
||||
}
|
||||
|
||||
fn void? unescape_dstring(String s, DString result, bool allow_unquoted = false, bool lenient = false) @private
|
||||
{
|
||||
usz len = s.len;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
char c = s[i];
|
||||
if (c != '\\')
|
||||
{
|
||||
result.append_char(c);
|
||||
continue;
|
||||
}
|
||||
if (c != '\\')
|
||||
{
|
||||
result.append_char(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle escape sequence
|
||||
if (i + 1 >= len) return INVALID_ESCAPE_SEQUENCE?;
|
||||
// Handle escape sequence
|
||||
if (i + 1 >= len) return INVALID_ESCAPE_SEQUENCE~;
|
||||
|
||||
char escape_char = s[++i];
|
||||
switch (escape_char)
|
||||
{
|
||||
case '"': result.append_char('"');
|
||||
case '\\': result.append_char('\\');
|
||||
case '/': result.append_char('/');
|
||||
case 'b': result.append_char('\b');
|
||||
case 'f': result.append_char('\f');
|
||||
case 'n': result.append_char('\n');
|
||||
case 'r': result.append_char('\r');
|
||||
case 't': result.append_char('\t');
|
||||
case 'v': result.append_char('\v');
|
||||
case '0': result.append_char('\0');
|
||||
case 'x':
|
||||
// Hex escape \xHH
|
||||
if (i + 2 >= len) return INVALID_HEX_ESCAPE?;
|
||||
char h1 = s[++i];
|
||||
char h2 = s[++i];
|
||||
if (!h1.is_xdigit() || !h2.is_xdigit()) return INVALID_HEX_ESCAPE?;
|
||||
uint val = h1 > '9' ? (h1 | 32) - 'a' + 10 : h1 - '0';
|
||||
val = val << 4;
|
||||
val += h2 > '9' ? (h2 | 32) - 'a' + 10 : h2 - '0';
|
||||
result.append_char((char)val);
|
||||
case 'u':
|
||||
// Unicode escape \uHHHH
|
||||
if (i + 4 >= len) return INVALID_UNICODE_ESCAPE?;
|
||||
uint val;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
case 'U':
|
||||
// Unicode escape \UHHHHHHHH
|
||||
if (i + 8 >= len) return INVALID_UNICODE_ESCAPE?;
|
||||
uint val;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE?;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
default:
|
||||
return INVALID_ESCAPE_SEQUENCE?;
|
||||
}
|
||||
}
|
||||
|
||||
return result.copy_str(allocator);
|
||||
char escape_char = s[++i];
|
||||
switch (escape_char)
|
||||
{
|
||||
case '"': result.append_char('"');
|
||||
case '\\': result.append_char('\\');
|
||||
case '/': result.append_char('/');
|
||||
case 'b': result.append_char('\b');
|
||||
case 'f': result.append_char('\f');
|
||||
case 'n': result.append_char('\n');
|
||||
case 'r': result.append_char('\r');
|
||||
case 't': result.append_char('\t');
|
||||
case 'v': result.append_char('\v');
|
||||
case '0': result.append_char('\0');
|
||||
case 'x':
|
||||
// Hex escape \xHH
|
||||
if (i + 2 >= len) return INVALID_HEX_ESCAPE~;
|
||||
char h1 = s[++i];
|
||||
char h2 = s[++i];
|
||||
if (!h1.is_xdigit() || !h2.is_xdigit()) return INVALID_HEX_ESCAPE~;
|
||||
uint val = h1 > '9' ? (h1 | 32) - 'a' + 10 : h1 - '0';
|
||||
val = val << 4;
|
||||
val += h2 > '9' ? (h2 | 32) - 'a' + 10 : h2 - '0';
|
||||
result.append_char((char)val);
|
||||
case 'u':
|
||||
// Unicode escape \uHHHH
|
||||
if (i + 4 >= len) return INVALID_UNICODE_ESCAPE~;
|
||||
uint val;
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE~;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
case 'U':
|
||||
// Unicode escape \UHHHHHHHH
|
||||
if (i + 8 >= len) return INVALID_UNICODE_ESCAPE~;
|
||||
uint val;
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
char hex_char = s[++i];
|
||||
if (!hex_char.is_xdigit()) return INVALID_UNICODE_ESCAPE~;
|
||||
val = val << 4 + (hex_char > '9' ? (hex_char | 32) - 'a' + 10 : hex_char - '0');
|
||||
}
|
||||
result.append_char32(val);
|
||||
default:
|
||||
if (!lenient) return INVALID_ESCAPE_SEQUENCE~;
|
||||
result.append_char(escape_char);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@@ -202,10 +229,11 @@ fn String? String.unescape(String s, Allocator allocator, bool allow_unquoted =
|
||||
|
||||
@param s : "The quoted string to unescape"
|
||||
@param allow_unquoted : "Set to true to unescape strings not surrounded by quotes, defaults to false"
|
||||
@param lenient : "Be lenient with escapes, resolving unknown sequences to the escape character, defaults to false"
|
||||
@return "The unescaped string without quotes"
|
||||
@return? UNTERMINATED_STRING, INVALID_ESCAPE_SEQUENCE, INVALID_HEX_ESCAPE, INVALID_UNICODE_ESCAPE
|
||||
*>
|
||||
fn String? String.tunescape(String s, bool allow_unquoted = false) => s.unescape(tmem, allow_unquoted);
|
||||
fn String? String.tunescape(String s, bool allow_unquoted = false, bool lenient = false) => s.unescape(tmem, allow_unquoted, lenient);
|
||||
|
||||
<*
|
||||
Check if a character needs to be escaped in a string literal.
|
||||
@@ -215,19 +243,19 @@ fn String? String.tunescape(String s, bool allow_unquoted = false) => s.unescape
|
||||
*>
|
||||
fn bool needs_escape(char c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
return true;
|
||||
default:
|
||||
return c < 32 || c > 126;
|
||||
}
|
||||
switch (c)
|
||||
{
|
||||
case '"':
|
||||
case '\\':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\0':
|
||||
return true;
|
||||
default:
|
||||
return c < 32 || c > 126;
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ fn Char32? StringIterator.next(&self)
|
||||
{
|
||||
usz len = self.utf8.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return NO_MORE_ELEMENT?;
|
||||
if (current >= len) return NO_MORE_ELEMENT~;
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
|
||||
self.current += read;
|
||||
@@ -26,7 +26,7 @@ fn Char32? StringIterator.peek(&self)
|
||||
{
|
||||
usz len = self.utf8.len;
|
||||
usz current = self.current;
|
||||
if (current >= len) return NO_MORE_ELEMENT?;
|
||||
if (current >= len) return NO_MORE_ELEMENT~;
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
|
||||
return res;
|
||||
@@ -43,7 +43,7 @@ fn Char32? StringIterator.get(&self)
|
||||
usz current = self.current;
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
usz index = current > read ? current - read : 0;
|
||||
if (index >= len) return NO_MORE_ELEMENT?;
|
||||
if (index >= len) return NO_MORE_ELEMENT~;
|
||||
Char32 res = conv::utf8_to_char32(&self.utf8[index], &read)!;
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
got_rad = true;
|
||||
if (index == last_char)
|
||||
{
|
||||
if (!got_digit) return MALFORMED_FLOAT?;
|
||||
if (!got_digit) return MALFORMED_FLOAT~;
|
||||
return sign * 0.0;
|
||||
}
|
||||
if (index != last_char && (c = chars[++index]) == '0')
|
||||
@@ -83,7 +83,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
switch
|
||||
{
|
||||
case c == '.':
|
||||
if (got_rad) return MALFORMED_FLOAT?;
|
||||
if (got_rad) return MALFORMED_FLOAT~;
|
||||
got_rad = true;
|
||||
lrp = dc;
|
||||
case k < KMAX - 3:
|
||||
@@ -113,24 +113,24 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
c = chars[++index];
|
||||
}
|
||||
if (!got_rad) lrp = dc;
|
||||
if (!got_digit) return MALFORMED_FLOAT?;
|
||||
if (!got_digit) return MALFORMED_FLOAT~;
|
||||
if ((c | 32) == 'e')
|
||||
{
|
||||
if (last_char == index) return MALFORMED_FLOAT?;
|
||||
long e10 = String.to_long((String)chars[index + 1..]) ?? MALFORMED_FLOAT?!;
|
||||
if (last_char == index) return MALFORMED_FLOAT~;
|
||||
long e10 = String.to_long((String)chars[index + 1..]) ?? MALFORMED_FLOAT~!;
|
||||
lrp += e10;
|
||||
}
|
||||
else if (index != last_char)
|
||||
{
|
||||
return MALFORMED_FLOAT?;
|
||||
return MALFORMED_FLOAT~;
|
||||
}
|
||||
// Handle zero specially to avoid nasty special cases later
|
||||
if (!x[0]) return sign * 0.0;
|
||||
|
||||
// Optimize small integers (w/no exponent) and over/under-flow
|
||||
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
|
||||
if (lrp > - $emin / 2) return FLOAT_OUT_OF_RANGE?;
|
||||
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE?;
|
||||
if (lrp > - $emin / 2) return FLOAT_OUT_OF_RANGE~;
|
||||
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE~;
|
||||
|
||||
// Align incomplete final B1B digit
|
||||
if (j)
|
||||
@@ -158,7 +158,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
if (rp % 9)
|
||||
{
|
||||
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
|
||||
int p10 = P10S[8 - rpm9];
|
||||
uint p10 = P10S[8 - rpm9];
|
||||
uint carry = 0;
|
||||
for (k = a; k != z; k++)
|
||||
{
|
||||
@@ -320,7 +320,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
y *= 0.5;
|
||||
e2++;
|
||||
}
|
||||
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return MALFORMED_FLOAT?;
|
||||
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return MALFORMED_FLOAT~;
|
||||
}
|
||||
return math::scalbn(y, e2);
|
||||
}
|
||||
@@ -351,7 +351,7 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
got_rad = true;
|
||||
if (index == last_char)
|
||||
{
|
||||
if (!got_digit) return MALFORMED_FLOAT?;
|
||||
if (!got_digit) return MALFORMED_FLOAT~;
|
||||
return sign * 0.0;
|
||||
}
|
||||
if (index != last_char && (c = chars[++index]) == '0')
|
||||
@@ -369,7 +369,7 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
{
|
||||
if (c == '.')
|
||||
{
|
||||
if (got_rad) return MALFORMED_FLOAT?;
|
||||
if (got_rad) return MALFORMED_FLOAT~;
|
||||
got_rad = true;
|
||||
rp = dc;
|
||||
}
|
||||
@@ -393,20 +393,20 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
if (index == last_char) break;
|
||||
c = chars[++index];
|
||||
}
|
||||
if (!got_digit) return MALFORMED_FLOAT?;
|
||||
if (!got_digit) return MALFORMED_FLOAT~;
|
||||
if (!got_rad) rp = dc;
|
||||
for (; dc < 8; dc++) x *= 16;
|
||||
|
||||
long e2;
|
||||
if ((c | 32) == 'p')
|
||||
{
|
||||
long e2val = String.to_long((String)chars[index + 1..]) ?? (MALFORMED_FLOAT?)!;
|
||||
long e2val = String.to_long((String)chars[index + 1..]) ?? MALFORMED_FLOAT~!;
|
||||
e2 = e2val;
|
||||
}
|
||||
e2 += 4 * rp - 32;
|
||||
if (!x) return sign * 0.0;
|
||||
if (e2 > -$emin) return FLOAT_OUT_OF_RANGE?;
|
||||
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE?;
|
||||
if (e2 > -$emin) return FLOAT_OUT_OF_RANGE~;
|
||||
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE~;
|
||||
|
||||
while (x < 0x80000000)
|
||||
{
|
||||
@@ -441,7 +441,7 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
|
||||
}
|
||||
y = bias + sign * (double)x + sign * y;
|
||||
y -= bias;
|
||||
if (!y) return FLOAT_OUT_OF_RANGE?;
|
||||
if (!y) return FLOAT_OUT_OF_RANGE~;
|
||||
|
||||
return math::scalbn(y, (int)e2);
|
||||
}
|
||||
@@ -463,7 +463,7 @@ macro String.to_real(chars, $Type) @private
|
||||
$endswitch
|
||||
|
||||
chars = chars.trim();
|
||||
if (!chars.len) return MALFORMED_FLOAT?;
|
||||
if (!chars.len) return MALFORMED_FLOAT~;
|
||||
|
||||
if (chars.len != 1)
|
||||
{
|
||||
@@ -477,7 +477,7 @@ macro String.to_real(chars, $Type) @private
|
||||
}
|
||||
}
|
||||
chars = chars.trim();
|
||||
if (!chars.len) return MALFORMED_FLOAT?;
|
||||
if (!chars.len) return MALFORMED_FLOAT~;
|
||||
|
||||
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
|
||||
if (chars == "NAN" || chars == "nan") return $Type.nan;
|
||||
|
||||
@@ -12,7 +12,7 @@ faultdef DIVISION_BY_ZERO;
|
||||
|
||||
fn double? divide(int a, int b)
|
||||
{
|
||||
if (b == 0) return MathError.DIVISION_BY_ZERO?;
|
||||
if (b == 0) return MathError.DIVISION_BY_ZERO~;
|
||||
return (double)(a) / (double)(b);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ fn void? test_div() @test
|
||||
test::le(2, 3);
|
||||
test::eq_approx(m::divide(1, 3)!, 0.333, places: 3);
|
||||
test::@check(2 == 2, "divide: %d", divide(6, 3)!);
|
||||
test::@error(m::divide(3, 0));
|
||||
test::@error(m::divide(3, 0), MathError.DIVISION_BY_ZERO);
|
||||
}
|
||||
|
||||
@@ -78,14 +79,15 @@ macro @check(#condition, String format = "", args...)
|
||||
}
|
||||
|
||||
<*
|
||||
Check if function returns specific error
|
||||
Check if function returns (specific) error
|
||||
|
||||
@param #funcresult : `result of function execution`
|
||||
@param error_expected : `expected error of function execution`
|
||||
@require runtime::test_context != null : "Only allowed in @test functions"
|
||||
*>
|
||||
macro @error(#funcresult, fault error_expected)
|
||||
macro @error(#funcresult, fault error_expected = ...)
|
||||
{
|
||||
$if $defined(error_expected):
|
||||
if (catch err = #funcresult)
|
||||
{
|
||||
if (err != error_expected)
|
||||
@@ -96,6 +98,10 @@ macro @error(#funcresult, fault error_expected)
|
||||
return;
|
||||
}
|
||||
print_panicf("`%s` error [%s] was not returned.", $stringify(#funcresult), error_expected);
|
||||
$else
|
||||
if (catch err = #funcresult) return;
|
||||
print_panicf("`%s` unexpectedly did not return error.", $stringify(#funcresult));
|
||||
$endif
|
||||
}
|
||||
|
||||
<*
|
||||
|
||||
@@ -29,47 +29,47 @@ macro any_to_int(any v, $Type)
|
||||
{
|
||||
case ichar:
|
||||
ichar c = *(char*)v.ptr;
|
||||
if (is_mixed_signed && c < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (is_mixed_signed && c < 0) return VALUE_OUT_OF_UNSIGNED_RANGE~;
|
||||
return ($Type)c;
|
||||
case short:
|
||||
short s = *(short*)v.ptr;
|
||||
if (is_mixed_signed && s < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (s > max || s < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (is_mixed_signed && s < 0) return VALUE_OUT_OF_UNSIGNED_RANGE~;
|
||||
if (s > max || s < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)s;
|
||||
case int:
|
||||
int i = *(int*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (is_mixed_signed && i < 0) return VALUE_OUT_OF_UNSIGNED_RANGE~;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)i;
|
||||
case long:
|
||||
long l = *(long*)v.ptr;
|
||||
if (is_mixed_signed && l < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (l > max || l < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (is_mixed_signed && l < 0) return VALUE_OUT_OF_UNSIGNED_RANGE~;
|
||||
if (l > max || l < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)l;
|
||||
case int128:
|
||||
int128 i = *(int128*)v.ptr;
|
||||
if (is_mixed_signed && i < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (is_mixed_signed && i < 0) return VALUE_OUT_OF_UNSIGNED_RANGE~;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)i;
|
||||
case char:
|
||||
char c = *(char*)v.ptr;
|
||||
if (c > max) return VALUE_OUT_OF_RANGE?;
|
||||
if (c > max) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)c;
|
||||
case ushort:
|
||||
ushort s = *(ushort*)v.ptr;
|
||||
if (s > max || s < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (s > max || s < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)s;
|
||||
case uint:
|
||||
uint i = *(uint*)v.ptr;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)i;
|
||||
case ulong:
|
||||
ulong l = *(ulong*)v.ptr;
|
||||
if (l > max || l < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (l > max || l < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)l;
|
||||
case uint128:
|
||||
uint128 i = *(uint128*)v.ptr;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
|
||||
if (i > max || i < min) return VALUE_OUT_OF_RANGE~;
|
||||
return ($Type)i;
|
||||
default:
|
||||
unreachable();
|
||||
@@ -97,8 +97,8 @@ macro bool is_subtype_of($Type, $OtherType)
|
||||
macro bool is_numerical($Type)
|
||||
{
|
||||
$switch $Type.kindof:
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return is_numerical($Type.inner);
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
@@ -112,7 +112,12 @@ macro bool is_numerical($Type)
|
||||
|
||||
fn bool TypeKind.is_int(kind) @inline
|
||||
{
|
||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||
return kind == SIGNED_INT || kind == UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool TypeKind.@is_int($kind) @const
|
||||
{
|
||||
return $kind == SIGNED_INT ||| $kind == UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool is_slice_convertable($Type) @deprecated("Use is_slice_convertible") => is_slice_convertible($Type);
|
||||
@@ -165,7 +170,7 @@ macro bool is_unsigned($Type) @const
|
||||
|
||||
macro typeid flat_type($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
|
||||
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
|
||||
return flat_type($Type.inner);
|
||||
$else
|
||||
return $Type.typeid;
|
||||
@@ -174,7 +179,7 @@ macro typeid flat_type($Type) @const
|
||||
|
||||
macro TypeKind flat_kind($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
|
||||
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
|
||||
return flat_type($Type.inner);
|
||||
$else
|
||||
return $Type.kindof;
|
||||
@@ -198,8 +203,8 @@ macro bool is_flat_intlike($Type) @const
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return is_flat_intlike($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
@@ -225,7 +230,7 @@ macro bool is_underlying_int($Type) @const
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case TYPEDEF:
|
||||
return is_underlying_int($Type.inner);
|
||||
$default:
|
||||
return false;
|
||||
@@ -253,7 +258,7 @@ macro bool is_vector($Type) @const
|
||||
|
||||
macro typeid inner_type($Type) @const
|
||||
{
|
||||
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
|
||||
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
|
||||
return inner_type($Type.inner);
|
||||
$else
|
||||
return $Type.typeid;
|
||||
@@ -293,7 +298,7 @@ macro bool may_load_atomic($Type) @const
|
||||
$case POINTER:
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case DISTINCT:
|
||||
$case TYPEDEF:
|
||||
$case ENUM:
|
||||
return may_load_atomic($Type.inner);
|
||||
$default:
|
||||
@@ -308,8 +313,8 @@ macro lower_to_atomic_compatible_type($Type) @const
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return $Type.typeid;
|
||||
$case DISTINCT:
|
||||
$case CONST_ENUM:
|
||||
$case TYPEDEF:
|
||||
$case CONSTDEF:
|
||||
return lower_to_atomic_compatible_type($Type.inner);
|
||||
$case FLOAT:
|
||||
$switch $Type:
|
||||
@@ -329,8 +334,8 @@ macro lower_to_atomic_compatible_type($Type) @const
|
||||
$endswitch
|
||||
}
|
||||
|
||||
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) || types::is_int($Type);
|
||||
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) || types::is_int($Type);
|
||||
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) ||| types::is_int($Type);
|
||||
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) ||| types::is_int($Type);
|
||||
|
||||
macro bool is_same_vector_type($Type1, $Type2) @const
|
||||
{
|
||||
@@ -345,7 +350,7 @@ macro bool has_equals($Type) @const => $defined(($Type){} == ($Type){});
|
||||
|
||||
macro bool is_equatable_type($Type) @const
|
||||
{
|
||||
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
|
||||
$if $defined($Type.less) ||| $defined($Type.compare_to) ||| $defined($Type.equals):
|
||||
return true;
|
||||
$else
|
||||
return $Type.is_eq;
|
||||
@@ -357,7 +362,7 @@ macro bool is_equatable_type($Type) @const
|
||||
*>
|
||||
macro bool implements_copy($Type) @const
|
||||
{
|
||||
return $defined($Type.copy) && $defined($Type.free);
|
||||
return $defined($Type.copy) &&& $defined($Type.free);
|
||||
}
|
||||
|
||||
macro bool @equatable_value(#value) @const
|
||||
@@ -367,13 +372,16 @@ macro bool @equatable_value(#value) @const
|
||||
|
||||
macro bool @comparable_value(#value) @const
|
||||
{
|
||||
$if $defined(#value.less) || $defined(#value.compare_to):
|
||||
$if $defined(#value.less) ||| $defined(#value.compare_to):
|
||||
return true;
|
||||
$else
|
||||
return $typeof(#value).is_ordered;
|
||||
$endif
|
||||
}
|
||||
|
||||
const CONST_ENUM @builtin @deprecated("Use TypeKind.CONSTDEF instead") = TypeKind.CONSTDEF;
|
||||
const DISTINCT @builtin @deprecated("Use TypeKind.TYPEDEF instead") = TypeKind.TYPEDEF;
|
||||
|
||||
enum TypeKind : char
|
||||
{
|
||||
VOID,
|
||||
@@ -385,7 +393,7 @@ enum TypeKind : char
|
||||
FAULT,
|
||||
ANY,
|
||||
ENUM,
|
||||
CONST_ENUM,
|
||||
CONSTDEF,
|
||||
STRUCT,
|
||||
UNION,
|
||||
BITSTRUCT,
|
||||
@@ -394,7 +402,7 @@ enum TypeKind : char
|
||||
ARRAY,
|
||||
SLICE,
|
||||
VECTOR,
|
||||
DISTINCT,
|
||||
TYPEDEF,
|
||||
POINTER,
|
||||
INTERFACE,
|
||||
}
|
||||
|
||||
@@ -56,9 +56,9 @@ enum BlockMode
|
||||
<* AES type: 128, 192 or 256 bits *>
|
||||
enum AesType : (AesKey key)
|
||||
{
|
||||
AES128 = { 128, 16, 176, 4, 10 },
|
||||
AES192 = { 192, 24, 208, 6, 12 },
|
||||
AES256 = { 256, 32, 240, 8, 14 }
|
||||
AES128 {{ 128, 16, 176, 4, 10 }},
|
||||
AES192 {{ 192, 24, 208, 6, 12 }},
|
||||
AES256 {{ 256, 32, 240, 8, 14 }}
|
||||
}
|
||||
|
||||
struct AesKey
|
||||
|
||||
233
lib/std/crypto/chacha20.c3
Normal file
233
lib/std/crypto/chacha20.c3
Normal file
@@ -0,0 +1,233 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// ChaCha20 code dedicated from repo: https://github.com/NotsoanoNimus/chacha20_aead.c3l (but massively cleaned)
|
||||
module std::crypto::chacha20;
|
||||
|
||||
|
||||
<* The typical cipher block size in bytes. *>
|
||||
const BLOCK_SIZE = 64;
|
||||
|
||||
<* Required key size in bytes. *>
|
||||
const KEY_SIZE = 32;
|
||||
|
||||
<* ChaCha20 "nonce" (initialization vector) size. *>
|
||||
const NONCE_SIZE = 12;
|
||||
|
||||
<* A required ChaCha20 "magic" value used for state initialization. *>
|
||||
const char[] MAGIC = "expand 32-byte k";
|
||||
|
||||
<*
|
||||
Once a single ChaCha20 context has processed this many bytes, a new nonce MUST be used,
|
||||
unless the static `permit_overflow` runtime module variable is set to true.
|
||||
*>
|
||||
const CHACHA20_NONCE_REUSE_LIMIT = 64 * (1ull << 32);
|
||||
|
||||
<*
|
||||
SECURITY WARNING:
|
||||
This boolean should always remain 'false'. If set to 'true', you accept the security
|
||||
implications of nonce re-use caused by an overflow in the cipher's 'counter' field.
|
||||
|
||||
This security warning is only applicable when a single ChaCha20 context is being used
|
||||
to process more than about 256 GiB of data.
|
||||
*>
|
||||
bool permit_overflow = false;
|
||||
|
||||
|
||||
<* A context structure used to track an ongoing ChaCha20 transformation. *>
|
||||
struct ChaCha20
|
||||
{
|
||||
<* The position within a block before permuting the rounds. *>
|
||||
usz position;
|
||||
<* Count of bytes processed. Useful to track an approach to the 256GiB limit of a single context. *>
|
||||
ulong bytes_processed;
|
||||
<* The key stream or state used during cipher block operations. *>
|
||||
uint[16] key_stream @align(ulong.sizeof);
|
||||
<* The secret key for the context. *>
|
||||
char[32] key;
|
||||
<* The one-time nonce (or IV - initialization vector) used for the context. *>
|
||||
char[12] nonce;
|
||||
<* Internal state of the cipher. *>
|
||||
uint[16] state;
|
||||
}
|
||||
|
||||
|
||||
<* The meat and potatoes of the ChaCha20 stream cipher. *>
|
||||
macro quarter_round(uint* x, int a, int b, int c, int d) @local
|
||||
{
|
||||
x[a] += x[b]; x[d] = (x[d] ^ x[a]).rotl(16);
|
||||
x[c] += x[d]; x[b] = (x[b] ^ x[c]).rotl(12);
|
||||
x[a] += x[b]; x[d] = (x[d] ^ x[a]).rotl(8);
|
||||
x[c] += x[d]; x[b] = (x[b] ^ x[c]).rotl(7);
|
||||
}
|
||||
|
||||
<* Process the next (or final) chunk of ingested data. *>
|
||||
fn void chacha20_mutate_keystream(ChaCha20* self) @local @inline
|
||||
{
|
||||
self.key_stream[..] = self.state[..];
|
||||
|
||||
for (usz i = 0; i < 10; i++) // unrolling this does not improve performance measurably
|
||||
{
|
||||
quarter_round(&self.key_stream[0], 0, 4, 8, 12);
|
||||
quarter_round(&self.key_stream[0], 1, 5, 9, 13);
|
||||
quarter_round(&self.key_stream[0], 2, 6, 10, 14);
|
||||
quarter_round(&self.key_stream[0], 3, 7, 11, 15);
|
||||
quarter_round(&self.key_stream[0], 0, 5, 10, 15);
|
||||
quarter_round(&self.key_stream[0], 1, 6, 11, 12);
|
||||
quarter_round(&self.key_stream[0], 2, 7, 8, 13);
|
||||
quarter_round(&self.key_stream[0], 3, 4, 9, 14);
|
||||
}
|
||||
|
||||
// NOTE: This would 'feel' like a performance hit, but testing the benchmark doesn't show any noticeable
|
||||
// difference on -O5 between this and a for-loop, or even an unrolled loop with compile-time '$for'.
|
||||
array::@zip_into(self.key_stream[..], self.state[..], fn (a, b) => a + b);
|
||||
|
||||
self.state[12]++; // increment the block counter (rollovers are ok)
|
||||
}
|
||||
|
||||
<*
|
||||
Initialize a ChaCha20 transformation context.
|
||||
|
||||
@param key : `The secret key used for the transformation operation.`
|
||||
@param nonce : `The one-time nonce to use for the transformation operation.`
|
||||
@param counter : `An optional counter value to adjust the stream's position.`
|
||||
|
||||
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
|
||||
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
|
||||
*>
|
||||
fn void ChaCha20.init(&self, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0)
|
||||
{
|
||||
// Init block.
|
||||
self.position = BLOCK_SIZE; // start at the "end" of a block on init
|
||||
self.bytes_processed = 0;
|
||||
self.key[..] = key[..];
|
||||
self.nonce[..] = nonce[..];
|
||||
((char*)&self.state[0])[:MAGIC.len] = MAGIC[..];
|
||||
((char*)&self.state[4])[:KEY_SIZE] = key[..];
|
||||
self.state[12] = counter;
|
||||
((char*)&self.state[13])[:NONCE_SIZE] = nonce[..];
|
||||
}
|
||||
|
||||
<*
|
||||
Transform some input data using the current context structure.
|
||||
|
||||
@param[inout] data : `The data to transform (encrypt or decrypt).`
|
||||
*>
|
||||
fn void ChaCha20.transform(&self, char[] data)
|
||||
{
|
||||
if (!data.len) return;
|
||||
|
||||
usz original_length = data.len;
|
||||
char[] key_stream = @as_char_view(self.key_stream);
|
||||
|
||||
// 1. Process remaining bytes in the current keystream block.
|
||||
if (self.position < BLOCK_SIZE)
|
||||
{
|
||||
usz len = data.len < (BLOCK_SIZE - self.position) ? data.len : (BLOCK_SIZE - self.position);
|
||||
for (usz i = 0; i < len; i++) data[i] ^= key_stream[self.position + i];
|
||||
self.position += len;
|
||||
data = data[len..];
|
||||
}
|
||||
|
||||
// 2. Get the amount of bytes offset from the nearest alignment boundary.
|
||||
// Process full blocks at a time, word by word according to the system's architecture.
|
||||
// Any extra bytes on each side are dynamically processed byte-by-byte.
|
||||
usz offset = usz.sizeof - (((usz)data.ptr % usz.sizeof) ?: usz.sizeof);
|
||||
|
||||
for (usz x = offset; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..], x = offset)
|
||||
{
|
||||
chacha20_mutate_keystream(self);
|
||||
if (offset) foreach (i, &b : data[:offset]) *b ^= key_stream[i];
|
||||
char[] aligned_data = data[offset..];
|
||||
for (; x <= (BLOCK_SIZE - usz.sizeof); x += usz.sizeof)
|
||||
{
|
||||
((usz*)aligned_data.ptr)[x / usz.sizeof] ^= mem::load((usz*)(&key_stream[x]), $align: 1);
|
||||
}
|
||||
for (; x < BLOCK_SIZE; x++) data[x] ^= key_stream[x];
|
||||
}
|
||||
|
||||
// 3. Process any remaining bytes.
|
||||
if (data.len > 0)
|
||||
{
|
||||
chacha20_mutate_keystream(self);
|
||||
for (usz i = 0; i < data.len; i++) data[i] ^= key_stream[i];
|
||||
self.position = data.len;
|
||||
}
|
||||
|
||||
// All done. Capture the transformed length of data and check limits.
|
||||
self.bytes_processed += original_length;
|
||||
if (@unlikely(self.bytes_processed >= CHACHA20_NONCE_REUSE_LIMIT && !permit_overflow))
|
||||
{
|
||||
abort(
|
||||
"ChaCha20 transform limit (~256 GiB) exceeded. You can set 'chacha20::permit_overflow = true;' at"
|
||||
" runtime to disable this panic, but you accept the terrible SECURITY IMPLICATIONS of doing so."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
<* Destroy the current context structure by zeroing all fields. *>
|
||||
fn void ChaCha20.destroy(&self) => mem::zero_volatile(@as_char_view(*self));
|
||||
|
||||
|
||||
<*
|
||||
Perform an in-place transformation of some data in a buffer, without cloning the data to a new buffer.
|
||||
|
||||
@param[inout] data : `The data to transform (encrypt or decrypt).`
|
||||
@param key : `The secret key used for the transformation operation.`
|
||||
@param nonce : `The one-time nonce to use for the transformation operation.`
|
||||
@param counter : `An optional counter value to adjust the stream's position.`
|
||||
|
||||
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
|
||||
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
|
||||
*>
|
||||
fn void crypt(char[] data, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0) @private
|
||||
{
|
||||
if (@unlikely(!data.len)) return;
|
||||
ChaCha20 c @noinit;
|
||||
defer c.destroy();
|
||||
c.init(key, nonce, counter);
|
||||
c.transform(data);
|
||||
}
|
||||
alias encrypt_mut = crypt;
|
||||
alias decrypt_mut = crypt;
|
||||
|
||||
<*
|
||||
Perform a transformation of some data cloned from a source buffer.
|
||||
|
||||
@param[&inout] allocator : `The memory allocator which controls allocation of the cloned input data.`
|
||||
@param[inout] data : `The data to transform (encrypt or decrypt).`
|
||||
@param key : `The secret key used for the transformation operation.`
|
||||
@param nonce : `The one-time nonce to use for the transformation operation.`
|
||||
@param counter : `An optional counter value to adjust the stream's position.`
|
||||
|
||||
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
|
||||
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
|
||||
*>
|
||||
fn char[] crypt_clone(Allocator allocator, char[] data, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0) @private
|
||||
{
|
||||
if (@unlikely(!data.len)) return {};
|
||||
char[] buff = allocator::clone_slice(allocator, data);
|
||||
crypt(buff, key, nonce, counter);
|
||||
return buff;
|
||||
}
|
||||
alias encrypt = crypt_clone;
|
||||
alias decrypt = crypt_clone;
|
||||
|
||||
<*
|
||||
Perform a transformation of some data cloned from a source buffer by the temp allocator.
|
||||
|
||||
@param[inout] data : `The data to transform (encrypt or decrypt).`
|
||||
@param key : `The secret key used for the transformation operation.`
|
||||
@param nonce : `The one-time nonce to use for the transformation operation.`
|
||||
@param counter : `An optional counter value to adjust the stream's position.`
|
||||
|
||||
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
|
||||
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
|
||||
*>
|
||||
fn char[] tcrypt_clone(char[] data, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0) @private
|
||||
{
|
||||
return crypt_clone(tmem, data, key, nonce, counter);
|
||||
}
|
||||
alias tencrypt = tcrypt_clone;
|
||||
alias tdecrypt = tcrypt_clone;
|
||||
@@ -332,7 +332,7 @@ fn Projection Projection.mul(&s, char[] n) @operator(*)
|
||||
|
||||
module std::crypto::ed25519 @private;
|
||||
|
||||
typedef F25519Int = inline char[32];
|
||||
typedef F25519Int @constinit = inline char[32];
|
||||
|
||||
const F25519Int ZERO = {};
|
||||
const F25519Int ONE = {[0] = 1};
|
||||
@@ -567,7 +567,7 @@ fn F25519Int F25519Int.inv(&s)
|
||||
|
||||
@param [&in] s
|
||||
*>
|
||||
fn F25519Int F25519Int.pow_2523(&s) @local
|
||||
fn F25519Int pow_2523(F25519Int* s) @local
|
||||
{
|
||||
F25519Int r = *s;
|
||||
|
||||
@@ -587,7 +587,7 @@ fn F25519Int F25519Int.pow_2523(&s) @local
|
||||
fn F25519Int F25519Int.sqrt(&s)
|
||||
{
|
||||
F25519Int twice = s.mul_s(2);
|
||||
F25519Int pow = twice.pow_2523();
|
||||
F25519Int pow = pow_2523(&twice);
|
||||
|
||||
return (twice * pow * pow - ONE) * s * pow;
|
||||
}
|
||||
@@ -601,7 +601,7 @@ fn F25519Int F25519Int.sqrt(&s)
|
||||
module std::crypto::ed25519 @private;
|
||||
import std::math;
|
||||
|
||||
typedef FBaseInt = inline char[32];
|
||||
typedef FBaseInt @constinit = inline char[32];
|
||||
|
||||
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
|
||||
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";
|
||||
|
||||
@@ -101,12 +101,12 @@ fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
|
||||
{
|
||||
if (src.len == 0)
|
||||
{
|
||||
if (padding > 0) return encoding::INVALID_PADDING?;
|
||||
if (padding > 0) return encoding::INVALID_PADDING~;
|
||||
break;
|
||||
}
|
||||
if (src[0] == padding) break;
|
||||
buf[i] = alphabet.reverse[src[0]];
|
||||
if (buf[i] == INVALID) return encoding::INVALID_CHARACTER?;
|
||||
if (buf[i] == INVALID) return encoding::INVALID_CHARACTER~;
|
||||
src = src[1..];
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
|
||||
dst[0] = buf[1] >> 2 | buf[0] << 3;
|
||||
n++;
|
||||
default:
|
||||
return encoding::INVALID_CHARACTER?;
|
||||
return encoding::INVALID_CHARACTER~;
|
||||
}
|
||||
if (dst.len < 5) break;
|
||||
dst = dst[5..];
|
||||
@@ -242,7 +242,7 @@ const char INVALID @private = 0xff;
|
||||
const int STD_PADDING = '=';
|
||||
const int NO_PADDING = -1;
|
||||
|
||||
typedef Alphabet = char[32];
|
||||
typedef Alphabet @constinit = char[32];
|
||||
// Standard base32 Alphabet
|
||||
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
|
||||
// Extended Hex Alphabet
|
||||
|
||||
@@ -87,11 +87,11 @@ fn usz? decode_len(usz n, char padding)
|
||||
usz trailing = n % 4;
|
||||
if (padding)
|
||||
{
|
||||
if (trailing != 0) return encoding::INVALID_PADDING?;
|
||||
if (trailing != 0) return encoding::INVALID_PADDING~;
|
||||
// source size is multiple of 4
|
||||
return dn;
|
||||
}
|
||||
if (trailing == 1) return encoding::INVALID_PADDING?;
|
||||
if (trailing == 1) return encoding::INVALID_PADDING~;
|
||||
return dn + trailing * 3 / 4;
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
|
||||
case c1:
|
||||
case c2:
|
||||
case c3:
|
||||
return encoding::INVALID_CHARACTER?;
|
||||
return encoding::INVALID_CHARACTER~;
|
||||
}
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
|
||||
dst[0] = (char)(group >> 16);
|
||||
@@ -211,7 +211,7 @@ fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
|
||||
src = src[^trailing..];
|
||||
char c0 = alphabet.reverse[src[0]];
|
||||
char c1 = alphabet.reverse[src[1]];
|
||||
if (c0 == 0xFF || c1 == 0xFF) return encoding::INVALID_PADDING?;
|
||||
if (c0 == 0xFF || c1 == 0xFF) return encoding::INVALID_PADDING~;
|
||||
if (!padding)
|
||||
{
|
||||
switch (src.len)
|
||||
@@ -221,7 +221,7 @@ fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
|
||||
dst[0] = (char)(group >> 16);
|
||||
case 3:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return encoding::INVALID_CHARACTER?;
|
||||
if (c2 == 0xFF) return encoding::INVALID_CHARACTER~;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
@@ -235,13 +235,13 @@ fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
|
||||
switch (padding)
|
||||
{
|
||||
case src[2]:
|
||||
if (src[3] != padding) return encoding::INVALID_PADDING?;
|
||||
if (src[3] != padding) return encoding::INVALID_PADDING~;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dn -= 2;
|
||||
case src[3]:
|
||||
char c2 = alphabet.reverse[src[2]];
|
||||
if (c2 == 0xFF) return encoding::INVALID_CHARACTER?;
|
||||
if (c2 == 0xFF) return encoding::INVALID_CHARACTER~;
|
||||
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
|
||||
dst[0] = (char)(group >> 16);
|
||||
dst[1] = (char)(group >> 8);
|
||||
|
||||
243
lib/std/encoding/codepage.c3
Normal file
243
lib/std/encoding/codepage.c3
Normal file
@@ -0,0 +1,243 @@
|
||||
// Copyright (c) 2026 Koni Marti. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license.
|
||||
<*
|
||||
Module providing generic single‑byte code page to UTF‑8 conversion.
|
||||
|
||||
This module implements a compact, table‑driven approach for single-byte
|
||||
(8‑bit) encodings (e.g. CP437, CP850, CP866, CP125x). It is designed so
|
||||
that each concrete code page only needs to supply a small, static
|
||||
mapping table; the conversion logic is shared.
|
||||
|
||||
The design has two main goals:
|
||||
|
||||
- Fast decode from code page to UTF‑8 with a single table lookup per byte.
|
||||
- Memory‑efficient encode from UTF‑8 to code page without a large
|
||||
Unicode‑to‑byte array (no 64k reverse map per code page).
|
||||
|
||||
The design of CodePageTable and the packed reverse mapping is conceptually
|
||||
similar to golang.org/x/text/encoding/charmap.
|
||||
|
||||
*>
|
||||
module std::encoding::codepage;
|
||||
import std::sort;
|
||||
|
||||
<*
|
||||
Default replacement byte used when encoding from UTF‑8 to a single‑byte
|
||||
code page and a Unicode scalar cannot be represented.
|
||||
|
||||
By convention, 0x1A is the ASCII/IBM SUB (substitute) control character.
|
||||
*>
|
||||
const char REPLACEMENT_CHAR = 0x1a;
|
||||
|
||||
<*
|
||||
CodePageTable contains the bidirectional mapping tables for a single‑byte code
|
||||
page in a compact packed form.
|
||||
|
||||
to_codepoint is the forward map from code‑page byte (0x00–0xFF) to its UTF‑8
|
||||
sequence. The array index is the raw byte value, each entry occupying 4 bytes:
|
||||
|
||||
- Byte 0 is the length of the UTF‑8 sequence (0–4)
|
||||
- Bytes 1:len are the UTF‑8 bytes for the mapped Unicode scalar
|
||||
|
||||
The table therefore uses 256 * 4 bytes and is stored as a flat
|
||||
char[1024] array, where entry i starts at offset i*4.
|
||||
|
||||
from_codepoint is the reverse map from Unicode scalar value to code‑page byte,
|
||||
also stored as a packed char[1024] array. It contains 256 entries of 4 bytes
|
||||
each, where each 4‑byte chunk is interpreted as a little‑endian uint
|
||||
with the following packing scheme:
|
||||
|
||||
high 8 bits = code‑page byte value (0x00–0xFF)
|
||||
low 24 bits = Unicode scalar value (code point)
|
||||
|
||||
In other words:
|
||||
|
||||
entry = (byte_value << 24) | codepoint;
|
||||
|
||||
Ordering:
|
||||
The 256 packed uint entries in from_codepoint are sorted by the low 24 bits
|
||||
(code points). This allows binary search over Unicode scalar values without
|
||||
a 64k reverse‑lookup table. For any given code page, there are at most
|
||||
256 mappings, so a log2(256) or 8 step search is sufficient.
|
||||
*>
|
||||
struct CodePageTable
|
||||
{
|
||||
char[1024] to_codepoint;
|
||||
char[1024] from_codepoint;
|
||||
}
|
||||
|
||||
enum CodePage : (String name, CodePageTable* table)
|
||||
{
|
||||
CP1250 { "cp1250", &codepage::CP1250 },
|
||||
CP1251 { "cp1251", &codepage::CP1251 },
|
||||
CP1252 { "cp1252", &codepage::CP1252 },
|
||||
CP1253 { "cp1253", &codepage::CP1253 },
|
||||
CP1254 { "cp1254", &codepage::CP1254 },
|
||||
CP1255 { "cp1255", &codepage::CP1255 },
|
||||
CP1256 { "cp1256", &codepage::CP1256 },
|
||||
CP1257 { "cp1257", &codepage::CP1257 },
|
||||
CP1258 { "cp1258", &codepage::CP1258 },
|
||||
CP437 { "cp437", &codepage::CP437 },
|
||||
CP737 { "cp737", &codepage::CP737 },
|
||||
CP775 { "cp775", &codepage::CP775 },
|
||||
CP850 { "cp850", &codepage::CP850 },
|
||||
CP852 { "cp852", &codepage::CP852 },
|
||||
CP855 { "cp855", &codepage::CP855 },
|
||||
CP857 { "cp857", &codepage::CP857 },
|
||||
CP860 { "cp860", &codepage::CP860 },
|
||||
CP861 { "cp861", &codepage::CP861 },
|
||||
CP862 { "cp862", &codepage::CP862 },
|
||||
CP863 { "cp863", &codepage::CP863 },
|
||||
CP864 { "cp864", &codepage::CP864 },
|
||||
CP865 { "cp865", &codepage::CP865 },
|
||||
CP866 { "cp866", &codepage::CP866 },
|
||||
CP869 { "cp869", &codepage::CP869 },
|
||||
CP874 { "cp874", &codepage::CP874 },
|
||||
ISO_8859_1 { "iso-8859-1", &codepage::ISO_8859_1 },
|
||||
ISO_8859_10 { "iso-8859-10", &codepage::ISO_8859_10 },
|
||||
ISO_8859_11 { "iso-8859-11", &codepage::ISO_8859_11 },
|
||||
ISO_8859_13 { "iso-8859-13", &codepage::ISO_8859_13 },
|
||||
ISO_8859_14 { "iso-8859-14", &codepage::ISO_8859_14 },
|
||||
ISO_8859_15 { "iso-8859-15", &codepage::ISO_8859_15 },
|
||||
ISO_8859_16 { "iso-8859-16", &codepage::ISO_8859_16 },
|
||||
ISO_8859_2 { "iso-8859-2", &codepage::ISO_8859_2 },
|
||||
ISO_8859_3 { "iso-8859-3", &codepage::ISO_8859_3 },
|
||||
ISO_8859_4 { "iso-8859-4", &codepage::ISO_8859_4 },
|
||||
ISO_8859_5 { "iso-8859-5", &codepage::ISO_8859_5 },
|
||||
ISO_8859_6 { "iso-8859-6", &codepage::ISO_8859_6 },
|
||||
ISO_8859_7 { "iso-8859-7", &codepage::ISO_8859_7 },
|
||||
ISO_8859_8 { "iso-8859-8", &codepage::ISO_8859_8 },
|
||||
ISO_8859_9 { "iso-8859-9", &codepage::ISO_8859_9 },
|
||||
US_ASCII { "us-ascii", &codepage::US_ASCII },
|
||||
}
|
||||
|
||||
<*
|
||||
Returns a CodePage for the given charset name.
|
||||
|
||||
@param [in] charset_name : "A name, case insensitive, using _ or - for separator"
|
||||
@return "The CodePage for the name"
|
||||
|
||||
@return? NOT_FOUND : "If the charset is unknown or unsupported"
|
||||
*>
|
||||
fn CodePage? by_name(String charset_name) => @pool()
|
||||
{
|
||||
String name = charset_name.treplace("_","-");
|
||||
name.convert_to_lower();
|
||||
foreach (page : CodePage.values)
|
||||
{
|
||||
if (page.name == charset_name) return page;
|
||||
}
|
||||
return NOT_FOUND~;
|
||||
}
|
||||
|
||||
fn String? decode(Allocator allocator, char[] src, CodePage code_page)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, decode_len(src, code_page));
|
||||
return decode_buffer(src, dst, code_page);
|
||||
}
|
||||
|
||||
<*
|
||||
Decode a code-page byte buffer into a UTF‑8 string.
|
||||
|
||||
@param src : "Input byte array in the given code page."
|
||||
@param dst : "Destination output string in UTF-8."
|
||||
@param code_page : "Code page for this encoding."
|
||||
@return "String in UTF-8."
|
||||
*>
|
||||
fn String? decode_buffer(char[] src, char[] dst, CodePage code_page)
|
||||
{
|
||||
usz n = 0;
|
||||
CodePageTable *table = code_page.table;
|
||||
foreach (c: src)
|
||||
{
|
||||
usz pos = (usz)c * 4;
|
||||
char len = table.to_codepoint[pos];
|
||||
|
||||
dst[n:len] = table.to_codepoint[pos+1:len];
|
||||
n += len;
|
||||
}
|
||||
|
||||
return (String)dst[:n];
|
||||
}
|
||||
|
||||
fn char[]? encode(Allocator allocator, char[] src, CodePage code_page, char replacement = REPLACEMENT_CHAR)
|
||||
{
|
||||
char[] dst = allocator::alloc_array(allocator, char, encode_len(src));
|
||||
return encode_buffer(src, dst, code_page, replacement);
|
||||
}
|
||||
|
||||
const uint MASK @private = (1u << 24) - 1;
|
||||
|
||||
<*
|
||||
Encode a UTF‑8 string into a single‑byte code page.
|
||||
|
||||
@param src : "Input byte array in UTF-8"
|
||||
@param dst : "Destination output byte array in the target code page"
|
||||
@param code_page : "Code page for this encoding."
|
||||
@param replacement : "Byte to emit when Unicode scalar cannot be represented in the target code page."
|
||||
@return "Byte array in the given code page."
|
||||
*>
|
||||
fn char[]? encode_buffer(char[] src, char[] dst, CodePage code_page, char replacement = REPLACEMENT_CHAR)
|
||||
{
|
||||
// Unpack the packed reverse table once into a local uint[256] view.
|
||||
uint[256] from_map;
|
||||
CodePageTable *table = code_page.table;
|
||||
for (usz i = 0; i < 256; i++)
|
||||
{
|
||||
UIntLE *val = (UIntLE*)&table.from_codepoint[i * 4];
|
||||
from_map[i] = mem::load(val, 1).val;
|
||||
}
|
||||
|
||||
usz out = 0;
|
||||
usz n = src.len;
|
||||
for (usz i = 0; i < n; )
|
||||
{
|
||||
usz rem = n - i;
|
||||
if (rem > 4) rem = 4;
|
||||
|
||||
Char32 codepoint = conv::utf8_to_char32(&src[i], &rem)!;
|
||||
i += rem;
|
||||
|
||||
// Binary search for codepoint in low 24 bits of each entry.
|
||||
// Returned index is between [0..from_map.len).
|
||||
usz index = sort::binarysearch(from_map[..], (uint)codepoint, fn int(uint lhs, uint rhs) => (int)(lhs & MASK) - (int)(rhs & MASK));
|
||||
|
||||
uint entry = from_map[index];
|
||||
if ((entry & MASK) == (uint)codepoint)
|
||||
{
|
||||
char b = (char)(entry >> 24);
|
||||
dst[out++] = b;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst[out++] = replacement;
|
||||
}
|
||||
}
|
||||
|
||||
return dst[:out];
|
||||
}
|
||||
|
||||
<*
|
||||
Compute the number of UTF‑8 bytes produced when decoding src with the given
|
||||
code page table.
|
||||
@param src : "Input byte array in the given code page."
|
||||
@param code_page : "Code page for this encoding."
|
||||
*>
|
||||
fn usz decode_len(char[] src, CodePage code_page) @inline
|
||||
{
|
||||
usz n;
|
||||
CodePageTable *table = code_page.table;
|
||||
foreach (usz c: src) n += table.to_codepoint[c *4 ];
|
||||
return n;
|
||||
}
|
||||
|
||||
<*
|
||||
Compute the number of output bytes produced when
|
||||
encoding src from UTF‑8 to a single‑byte code page.
|
||||
@param src : "Input byte array in UTF-8"
|
||||
*>
|
||||
fn usz encode_len(char[] src) @inline
|
||||
{
|
||||
return conv::utf8_codepoints((String)src);
|
||||
}
|
||||
|
||||
2234
lib/std/encoding/codepage_private.c3
Normal file
2234
lib/std/encoding/codepage_private.c3
Normal file
File diff suppressed because it is too large
Load Diff
@@ -79,7 +79,7 @@ macro void? @each_row(InStream stream, String separator = ",", int max_rows = in
|
||||
if (catch err = s)
|
||||
{
|
||||
if (err == io::EOF) return;
|
||||
return err?;
|
||||
return err~;
|
||||
}
|
||||
@body(s.split(mem, separator));
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ fn usz? decode_bytes(char[] src, char[] dst)
|
||||
{
|
||||
char a = HEXREVERSE[src[j - 1]];
|
||||
char b = HEXREVERSE[src[j]];
|
||||
if (a > 0x0f || b > 0x0f) return encoding::INVALID_CHARACTER?;
|
||||
if (a > 0x0f || b > 0x0f) return encoding::INVALID_CHARACTER~;
|
||||
dst[i] = (a << 4) | b;
|
||||
i++;
|
||||
}
|
||||
|
||||
@@ -85,13 +85,13 @@ fn Object*? parse_from_token(JsonContext* context, JsonTokenType token) @local
|
||||
case COMMA:
|
||||
case RBRACE:
|
||||
case RBRACKET:
|
||||
case COLON: return UNEXPECTED_CHARACTER?;
|
||||
case COLON: return UNEXPECTED_CHARACTER~;
|
||||
case STRING: return object::new_string(context.last_string.str_view(), context.allocator);
|
||||
case NUMBER: return object::new_float(context.last_number, context.allocator);
|
||||
case TRUE: return object::new_bool(true);
|
||||
case FALSE: return object::new_bool(false);
|
||||
case NULL: return object::new_null();
|
||||
case EOF: return io::EOF?;
|
||||
case EOF: return io::EOF~;
|
||||
}
|
||||
}
|
||||
fn Object*? parse_any(JsonContext* context) @local
|
||||
@@ -117,7 +117,7 @@ fn JsonTokenType? lex_number(JsonContext *context, char c) @local
|
||||
c = read_next(context)!;
|
||||
if (leading_zero)
|
||||
{
|
||||
if (c.is_digit()) return INVALID_NUMBER?;
|
||||
if (c.is_digit()) return INVALID_NUMBER~;
|
||||
leading_zero = false;
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ fn JsonTokenType? lex_number(JsonContext *context, char c) @local
|
||||
t.append(c);
|
||||
c = read_next(context)!;
|
||||
}
|
||||
if (!c.is_digit()) return INVALID_NUMBER?;
|
||||
if (!c.is_digit()) return INVALID_NUMBER~;
|
||||
while (c.is_digit())
|
||||
{
|
||||
t.append(c);
|
||||
@@ -148,7 +148,7 @@ fn JsonTokenType? lex_number(JsonContext *context, char c) @local
|
||||
}
|
||||
}
|
||||
pushback(context, c);
|
||||
double? d = t.str_view().to_double() ?? INVALID_NUMBER?;
|
||||
double? d = t.str_view().to_double() ?? INVALID_NUMBER~;
|
||||
context.last_number = d!;
|
||||
return NUMBER;
|
||||
};
|
||||
@@ -160,14 +160,14 @@ fn Object*? parse_map(JsonContext* context) @local
|
||||
defer catch map.free();
|
||||
JsonTokenType token = advance(context)!;
|
||||
defer context.depth--;
|
||||
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
|
||||
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED~;
|
||||
|
||||
@stack_mem(256; Allocator mem)
|
||||
{
|
||||
DString temp_key = dstring::new_with_capacity(mem, 32);
|
||||
while (token != JsonTokenType.RBRACE)
|
||||
{
|
||||
if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER?;
|
||||
if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER~;
|
||||
DString string = context.last_string;
|
||||
// Copy the key to our temp holder, since our
|
||||
// last_string may be used in parse_any
|
||||
@@ -182,7 +182,7 @@ fn Object*? parse_map(JsonContext* context) @local
|
||||
token = advance(context)!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACE) return UNEXPECTED_CHARACTER?;
|
||||
if (token != JsonTokenType.RBRACE) return UNEXPECTED_CHARACTER~;
|
||||
}
|
||||
return map;
|
||||
};
|
||||
@@ -193,7 +193,7 @@ fn Object*? parse_array(JsonContext* context) @local
|
||||
Object* list = object::new_obj(context.allocator);
|
||||
defer catch list.free();
|
||||
defer context.depth--;
|
||||
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED?;
|
||||
if (++context.depth >= max_depth) return json::MAX_DEPTH_REACHED~;
|
||||
JsonTokenType token = advance(context)!;
|
||||
while (token != JsonTokenType.RBRACKET)
|
||||
{
|
||||
@@ -205,7 +205,7 @@ fn Object*? parse_array(JsonContext* context) @local
|
||||
token = advance(context)!;
|
||||
continue;
|
||||
}
|
||||
if (token != JsonTokenType.RBRACKET) return UNEXPECTED_CHARACTER?;
|
||||
if (token != JsonTokenType.RBRACKET) return UNEXPECTED_CHARACTER~;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
@@ -236,7 +236,7 @@ fn char? read_next(JsonContext* context) @local
|
||||
context.reached_end = true;
|
||||
return '\0';
|
||||
}
|
||||
return err?;
|
||||
return err~;
|
||||
}
|
||||
if (c == 0)
|
||||
{
|
||||
@@ -249,7 +249,7 @@ fn JsonTokenType? advance(JsonContext* context) @local
|
||||
{
|
||||
char c;
|
||||
// Skip whitespace
|
||||
while WS: (c = read_next(context)!)
|
||||
while WS: ((c = read_next(context)!))
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
@@ -272,12 +272,12 @@ fn JsonTokenType? advance(JsonContext* context) @local
|
||||
while COMMENT: (true)
|
||||
{
|
||||
// Skip to */
|
||||
while (c = read_next(context)!)
|
||||
while ((c = read_next(context)!))
|
||||
{
|
||||
if (c == '\n') context.line++;
|
||||
if (c != '*') continue;
|
||||
// Skip through all the '*'
|
||||
while (c = read_next(context)!)
|
||||
while ((c = read_next(context)!))
|
||||
{
|
||||
if (c == '\n') context.line++;
|
||||
if (c != '*') break;
|
||||
@@ -293,7 +293,7 @@ fn JsonTokenType? advance(JsonContext* context) @local
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return io::EOF?;
|
||||
return io::EOF~;
|
||||
case '{':
|
||||
return LBRACE;
|
||||
case '}':
|
||||
@@ -321,7 +321,7 @@ fn JsonTokenType? advance(JsonContext* context) @local
|
||||
match(context, "ull")!;
|
||||
return NULL;
|
||||
default:
|
||||
return UNEXPECTED_CHARACTER?;
|
||||
return UNEXPECTED_CHARACTER~;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,13 +330,13 @@ fn void? match(JsonContext* context, String str) @local
|
||||
foreach (c : str)
|
||||
{
|
||||
char l = read_next(context)!;
|
||||
if (l != c) return UNEXPECTED_CHARACTER?;
|
||||
if (l != c) return UNEXPECTED_CHARACTER~;
|
||||
}
|
||||
}
|
||||
|
||||
fn void? parse_expected(JsonContext* context, JsonTokenType token) @local
|
||||
{
|
||||
if (advance(context)! != token) return UNEXPECTED_CHARACTER?;
|
||||
if (advance(context)! != token) return UNEXPECTED_CHARACTER~;
|
||||
}
|
||||
|
||||
fn JsonTokenType? lex_string(JsonContext* context)
|
||||
@@ -348,9 +348,9 @@ fn JsonTokenType? lex_string(JsonContext* context)
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return io::EOF?;
|
||||
return io::EOF~;
|
||||
case 1..31:
|
||||
return UNEXPECTED_CHARACTER?;
|
||||
return UNEXPECTED_CHARACTER~;
|
||||
case '"':
|
||||
break LOOP;
|
||||
case '\\':
|
||||
@@ -363,9 +363,9 @@ fn JsonTokenType? lex_string(JsonContext* context)
|
||||
switch (c)
|
||||
{
|
||||
case '\0':
|
||||
return io::EOF?;
|
||||
return io::EOF~;
|
||||
case 1..31:
|
||||
return UNEXPECTED_CHARACTER?;
|
||||
return UNEXPECTED_CHARACTER~;
|
||||
case '"':
|
||||
case '\\':
|
||||
case '/':
|
||||
@@ -385,13 +385,13 @@ fn JsonTokenType? lex_string(JsonContext* context)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
c = read_next(context)!;
|
||||
if (!c.is_xdigit()) return INVALID_ESCAPE_SEQUENCE?;
|
||||
if (!c.is_xdigit()) return INVALID_ESCAPE_SEQUENCE~;
|
||||
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
|
||||
}
|
||||
context.last_string.append_char32(val);
|
||||
continue;
|
||||
default:
|
||||
return INVALID_ESCAPE_SEQUENCE?;
|
||||
return INVALID_ESCAPE_SEQUENCE~;
|
||||
}
|
||||
context.last_string.append(c);
|
||||
}
|
||||
|
||||
355
lib/std/encoding/pem.c3
Normal file
355
lib/std/encoding/pem.c3
Normal file
@@ -0,0 +1,355 @@
|
||||
// Copyright (c) 2026 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// A module for encoding or decoding PEM blobs [mostly] in accordance with RFCs 1421-1424.
|
||||
// This implementation retains a lot of flexibility in parsing input PEM blobs.
|
||||
//
|
||||
module std::encoding::pem;
|
||||
|
||||
import std::collections, std::encoding::base64;
|
||||
|
||||
<* A safe, default tag to use per RFC 1421's rules. *>
|
||||
const String DEFAULT_TAG = "PRIVACY-ENHANCED MESSAGE";
|
||||
|
||||
<* The set of characters which are considered valid for PEM tags (which appear inside of Encapsulation Boundaries). *>
|
||||
const AsciiCharset TAG_SET @local = ascii::@combine_sets(ascii::ALPHA_UPPER_SET, ascii::NUMBER_SET, ascii::@create_set(" _-/+()"));
|
||||
<* The set of characters which are considered valid for optional PEM headers used. *>
|
||||
const AsciiCharset HEADER_KEY_SET @local = ascii::@combine_sets(ascii::ALPHANUMERIC_SET, ascii::@create_set("!#$%&'*+-.^_`|~"));
|
||||
|
||||
<* All PEM Encapsulation Boundaries must use this delimiter to demarcate the PEM from its surrounding content, if any. *>
|
||||
const String EB_DELIMITER @local = "-----";
|
||||
<* All PEM blobs will start with this Encapsulation Boundary prefix. *>
|
||||
const String PRE_EB_PREFIX @local = EB_DELIMITER +++ "BEGIN ";
|
||||
<* All PEM blobs will terminate with this Encapsulation Boundary prefix. *>
|
||||
const String POST_EB_PREFIX @local = EB_DELIMITER +++ "END ";
|
||||
|
||||
alias PemHeader = String[2];
|
||||
|
||||
<* Specify a set of possible PEM en/decoding faults. *>
|
||||
faultdef
|
||||
BODY_REQUIRED, // encoding: no body given (or too few of them)
|
||||
HEADERS_REQUIRED, // encoding: no headers given (or too few of them)
|
||||
HEADER_KEY_REQUIRED, // encoding: blank header keys are not allowed
|
||||
HEADER_VALUE_REQUIRED, // encoding: blank header values are not allowed
|
||||
INVALID_BODY, // decoding: invalid body, likely bad base64
|
||||
INVALID_FORMAT, // decoding: invalid input formatting - no pre-EB or just plain wrong
|
||||
INVALID_HEADER, // decoding: invalid headers
|
||||
INVALID_HEADER_KEY, // decoding: invalid or empty header key
|
||||
INVALID_PRE_EB, // decoding: invalid pre-EncapsBoundary BEFORE the PEM body
|
||||
INVALID_POST_EB, // decoding: invalid post-EncapsBoundary AFTER the PEM body
|
||||
INVALID_TAG, // decoding: invalid tag within an EB
|
||||
MISMATCHED_TAG, // decoding: the tag from the pre-EB doesn't match that of the post-EB
|
||||
MISSING_BODY, // decoding: missing PEM body base64
|
||||
MISSING_HEADER_KEY, // decoding: the header is missing its key
|
||||
MISSING_HEADER_VALUE, // decoding: the header is missing its value
|
||||
MISSING_POST_EB, // decoding: no post-EB was found to close off the PEM
|
||||
MISSING_TAG, // decoding: no tag was defined or parsed from the EB
|
||||
TAG_REQUIRED, // encoding: no/empty tag given (or too few of them)
|
||||
;
|
||||
|
||||
|
||||
<* Represents a PEM object in memory, with a reference to the body data, tag value, and optional headers. *>
|
||||
struct Pem
|
||||
{
|
||||
<* The allocator associated with the PEM's creation and destruction. *>
|
||||
Allocator allocator;
|
||||
<* A flexible 'tag' value used within the Encapsulation Boundary to denote the type of the PEM. *>
|
||||
String tag;
|
||||
<* A set of optional headers used to provide more context or information about the body of the PEM object. *>
|
||||
LinkedHashMap{String, String} headers;
|
||||
<* The core boy data of the PEM itself - the main values to be transmitted in this format. *>
|
||||
char[] data;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Create a new PEM object from a few inputs. Each input (i.e., tag, data, and headers) is copied to a new memory location.
|
||||
The PEM object itself is not allocated in-memory, but is a simple container that points to each value that _is_.
|
||||
|
||||
Key-Value pairs for headers are provided in sequence as variadic arguments: `"key", "value", "key2", "value2", ...`
|
||||
|
||||
Created PEMs that are not temporary should be destroyed with `Pem.free`.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when copying the provided PEM object's fields."
|
||||
@param [in] data : "The body data of the PEM."
|
||||
@param [in] tag : "The tag value to use within the PEM's Encapsulation Boundary."
|
||||
|
||||
@return "A new PEM object."
|
||||
*>
|
||||
fn Pem create(Allocator allocator, char[] data, String tag, PemHeader... args)
|
||||
{
|
||||
Pem result = {
|
||||
.allocator = allocator,
|
||||
.tag = tag.copy(allocator),
|
||||
.data = allocator::clone_slice(allocator, data),
|
||||
};
|
||||
result.headers.init(allocator, capacity: max(args.len, 16));
|
||||
foreach (arg : args)
|
||||
{
|
||||
result.add_header(arg[0], arg[1]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Duplicate a `Pem` container and allocate copies of its members using the given allocator.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when copying the `Pem` members."
|
||||
*>
|
||||
fn Pem Pem.copy(&self, Allocator allocator)
|
||||
{
|
||||
Pem result = create(allocator, self.data, self.tag);
|
||||
self.headers.@each(;String key, String value)
|
||||
{
|
||||
result.add_header(key, value);
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Safely destroys a `Pem` and deallocate all of its members. This should always be explicitly called when not using `tmem`.
|
||||
*>
|
||||
fn void Pem.free(&self)
|
||||
{
|
||||
mem::zero_volatile(self.data);
|
||||
if (self.allocator != tmem)
|
||||
{
|
||||
self.headers.@each(;String key, String value)
|
||||
{
|
||||
allocator::free(self.allocator, value);
|
||||
};
|
||||
self.headers.free();
|
||||
self.tag.free(self.allocator);
|
||||
allocator::free(self.allocator, self.data);
|
||||
}
|
||||
mem::zero_volatile(@as_char_view(*self));
|
||||
}
|
||||
|
||||
fn void Pem.add_header(&self, String key, String value)
|
||||
{
|
||||
(void)self.headers[key].free(self.allocator);
|
||||
self.headers[key] = value.copy(self.allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Attempt to decode an input string into one or more `Pem` objects. If the input contains any non-PEM or otherwise
|
||||
invalid data, then this will throw an error. Ideally, this function is used to decode PEM files explicitly, lest
|
||||
the caller need to be sure they're only providing PEM data +/- some intermediate whitespace.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when creating the `Pem` outputs and members."
|
||||
@param [in] input : "The string to parse one or more PEM blobs from."
|
||||
|
||||
@return "An array of decoded `Pem` objects, depending on how many were present in the input (separated optionally by whitespace)."
|
||||
*>
|
||||
fn Pem[]? decode(Allocator allocator, String input) => @pool()
|
||||
{
|
||||
List{Pem} pem_list;
|
||||
pem_list.tinit();
|
||||
|
||||
String[] lines = input.treplace("\r\n", "\n").tsplit("\n");
|
||||
foreach (&line : lines) *line = (*line).trim_right(); // remove any trailing whitespace as this can disrupt parsing (but shouldn't)
|
||||
while (lines.len > 0)
|
||||
{
|
||||
pem_list.push(_decode_single(allocator, &lines)!);
|
||||
while (lines.len > 0 && lines[0].trim().len == 0) lines = lines[1..]; // skip all empty lines in between or after PEM boundaries
|
||||
}
|
||||
return pem_list.to_array(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
INTERNAL ONLY: Decode one PEM at a time, from pre-EB to its discovered post-EB.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use during decoding to return the result."
|
||||
@param [&inout] lines_io : "A pointer to an input slice to modify as the single PEM is parsed from it."
|
||||
|
||||
*>
|
||||
fn Pem? _decode_single(Allocator allocator, String[]* lines_io) @local
|
||||
{
|
||||
String[] lines = *lines_io; // copy to local var
|
||||
Pem result = { .allocator = allocator };
|
||||
result.headers.init(allocator);
|
||||
defer catch result.free();
|
||||
|
||||
// Remove any preceding whitespace-only lines.
|
||||
while (lines[0].trim().len == 0) lines = lines[1..];
|
||||
|
||||
if (lines.len < 3) return INVALID_FORMAT~; // at least 3 lines (pre-EB, body, post-EB) are always required
|
||||
|
||||
// The Pre-Encapsulation-Boundary must be of the format: -----BEGIN TAG-----, where "TAG" can be any upper-case identifier [A-Z_ -/]
|
||||
String pre_eb = lines[0];
|
||||
if (pre_eb[0:11] != PRE_EB_PREFIX || pre_eb[^5..] != EB_DELIMITER) return INVALID_PRE_EB~;
|
||||
String tag = pre_eb[PRE_EB_PREFIX.len..^6];
|
||||
if (!tag.len || !tag.trim().len) return MISSING_TAG~;
|
||||
foreach (c : tag) if (!TAG_SET.contains(c)) return INVALID_TAG~;
|
||||
result.tag = tag.copy(allocator);
|
||||
|
||||
// The Post-Encapsulation-Boundary is the same, but uses "END", and the extracted tag must match.
|
||||
// Since the input might contain more than one PEM unit, we need to search for the ending encapsulation boundary dynamically.
|
||||
String post_eb;
|
||||
usz endl;
|
||||
for SEARCH_EB: (endl = 1; endl < lines.len; endl++)
|
||||
{
|
||||
if (lines[endl].len > POST_EB_PREFIX.len && lines[endl][0:EB_DELIMITER.len] == EB_DELIMITER)
|
||||
{
|
||||
post_eb = lines[endl];
|
||||
break SEARCH_EB;
|
||||
}
|
||||
}
|
||||
if (!post_eb.len) return MISSING_POST_EB~;
|
||||
if (post_eb[0:9] != POST_EB_PREFIX || post_eb[^5..] != EB_DELIMITER) return INVALID_POST_EB~;
|
||||
String post_tag = post_eb[POST_EB_PREFIX.len..^6];
|
||||
if (post_tag.len != tag.len || post_tag != tag) return MISMATCHED_TAG~;
|
||||
|
||||
// Now that the inner portion is decapsulated, tag is, strip off the boundaries.
|
||||
*lines_io = lines[endl+1..]; // update the iterated slice of lines from the calling context - see: `decode`
|
||||
lines = lines[1:endl-1];
|
||||
|
||||
// while there's a colon+space in the current line, we should assume that this is a key-value header pair
|
||||
while (lines[0].contains(": "))
|
||||
{
|
||||
if (!HEADER_KEY_SET.contains(lines[0][0])) return INVALID_HEADER~; // not a multiline header? error out if the first char is not appropriate
|
||||
String[] marker = lines; // temporary marker
|
||||
usz span = 1; // how many lines this header spans
|
||||
|
||||
// Search for multi-line key-value pairs, indicated by a whitespace character beginning the current line.
|
||||
for (lines = lines[1..]; lines[0].len > 0 && ascii::WHITESPACE_SET.contains(lines[0][0]); lines = lines[1..], span++);
|
||||
foreach (&line : marker[:span]) *line = (*line).trim(); // always trim on both sides
|
||||
|
||||
String full_header = string::tjoin(marker[:span], " "); // join the lines with a single space
|
||||
if (!full_header.contains(": ")) return INVALID_HEADER~; // reassert the presence of this
|
||||
|
||||
// Extract the key and value from the message, then validate.
|
||||
// The header name should match a valid set of characters, but the value doesn't need to conform to anything other than existing
|
||||
String[] kv = full_header.tsplit(": ", max: 2);
|
||||
if (!kv[0].len) return MISSING_HEADER_KEY~;
|
||||
if (!kv[1].len) return MISSING_HEADER_VALUE~;
|
||||
foreach (c : kv[0]) if (!HEADER_KEY_SET.contains(c)) return INVALID_HEADER_KEY~;
|
||||
|
||||
result.add_header(kv[0], kv[1]); // finally, push the values
|
||||
}
|
||||
|
||||
// if any headers were present, the line after the headers MUST BE EMPTY
|
||||
if (result.headers.len() > 0)
|
||||
{
|
||||
if (lines[0].trim().len > 0) return INVALID_FORMAT~; // but we are forgiving about whitespace here
|
||||
lines = lines[1..];
|
||||
}
|
||||
|
||||
// Here, we assume lines[0] is the start of base64 data. This means there must be at least 1 line, of course.
|
||||
if (lines.len < 1) return MISSING_BODY~;
|
||||
|
||||
// ... While the PEM format specifies a 64-character width on all but the last line of the base64 body,
|
||||
// this parser doesn't need to be particular about that as long as the base64 is ok
|
||||
// In this case, the rest of the lines in the set should be base64 and should decode accordingly
|
||||
String to_decode = string::tjoin(lines, "");
|
||||
if (!to_decode.len) return MISSING_BODY~; // paranoia
|
||||
result.data = (base64::decode(allocator, to_decode) ?? INVALID_BODY~)!;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Encodes a single `Pem` object into a new PEM-formatted string.
|
||||
|
||||
@param pem : "The pem object to encode"
|
||||
@param [&inout] allocator : "The allocator to use for allocating the final encoded string."
|
||||
*>
|
||||
fn String? encode_pem(Pem pem, Allocator allocator, bool use_crlf = false)
|
||||
{
|
||||
if (!pem.data.len) return BODY_REQUIRED~;
|
||||
if (!pem.tag.len) return TAG_REQUIRED~;
|
||||
|
||||
DString out;
|
||||
out.tinit();
|
||||
String line_ending = use_crlf ? "\r\n" : "\n";
|
||||
@pool()
|
||||
{
|
||||
out.appendf(PRE_EB_PREFIX +++ "%s" +++ EB_DELIMITER +++ "%s", pem.tag, line_ending);
|
||||
foreach KEY_ITER: (key : pem.headers.tkeys())
|
||||
{
|
||||
if (!key.len) return HEADER_KEY_REQUIRED~;
|
||||
String value = pem.headers[key]!!;
|
||||
if (!value.len) return HEADER_VALUE_REQUIRED~;
|
||||
usz first_line_length = 64 - 2 - key.len;
|
||||
if (value.len <= first_line_length)
|
||||
{
|
||||
out.appendf("%s: %s%s", key, value, line_ending);
|
||||
continue KEY_ITER;
|
||||
}
|
||||
out.appendf("%s: %s%s", key, value[:first_line_length].trim(), line_ending);
|
||||
value = value[first_line_length..];
|
||||
while (value.len > 0)
|
||||
{
|
||||
out.appendf(" %s%s", (value.len >= 63 ? value[:63] : value[..]).trim(), line_ending);
|
||||
value = value.len >= 63 ? value[63..] : {};
|
||||
}
|
||||
}
|
||||
if (pem.headers.len() > 0) out.append(line_ending);
|
||||
String body = base64::tencode(pem.data);
|
||||
while (body.len > 0)
|
||||
{
|
||||
out.appendf("%s%s", body.len >= 64 ? body[:64] : body[..], line_ending);
|
||||
body = body.len >= 64 ? body[64..] : {};
|
||||
}
|
||||
out.appendf(POST_EB_PREFIX +++ "%s" +++ EB_DELIMITER +++ "%s", pem.tag, line_ending);
|
||||
};
|
||||
|
||||
return allocator == tmem ? out.str_view() : out.copy_str(allocator);
|
||||
}
|
||||
|
||||
<*
|
||||
Encodes a set of input data into a `String` containing the PEM-encoded data.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when creating the final output string."
|
||||
@param [in] data : "The body data for the output PEM."
|
||||
@param [in] tag : "The tag "
|
||||
*>
|
||||
fn String? encode(Allocator allocator, char[] data, String tag, PemHeader... headers, bool use_crlf = false) => @pool()
|
||||
{
|
||||
if (!data.len) return BODY_REQUIRED~;
|
||||
return encode_pem(create(tmem, data, tag, ...headers), allocator, use_crlf);
|
||||
}
|
||||
|
||||
<*
|
||||
Encode many inputs to a single output string that represents chained/sequential PEM objects in the order they were provided.
|
||||
The length of the `bodies` and `tags` array must be equal.
|
||||
If headers are provided, they must be arrays of String objects, matching both the number of tags and the number of bodies.
|
||||
|
||||
@param [&inout] allocator : "The allocator to use when creating the final output string."
|
||||
@param [in] bodies : "An ordered array of binary arrays, each representing the body of a single PEM."
|
||||
@param [in] tags : "An ordered array of tag strings, each representing the tag of a single PEM."
|
||||
|
||||
@return "A new `String`, allocated with `allocator`, that contains all PEM objects in the order they were given."
|
||||
*>
|
||||
fn String? encode_many(Allocator allocator, char[][] bodies, String[] tags, PemHeader[]... pem_headers, bool use_crlf = false)
|
||||
{
|
||||
usz entries = max(bodies.len, tags.len, pem_headers.len);
|
||||
switch
|
||||
{
|
||||
case bodies.len < entries: return BODY_REQUIRED~;
|
||||
case tags.len < entries: return TAG_REQUIRED~;
|
||||
case pem_headers.len > 0 && pem_headers.len < entries: return HEADERS_REQUIRED~;
|
||||
}
|
||||
|
||||
DString out;
|
||||
out.tinit();
|
||||
|
||||
if (!pem_headers.len)
|
||||
{
|
||||
foreach (x, body : bodies) @pool()
|
||||
{
|
||||
out.append(encode(tmem, body, tags[x], use_crlf: use_crlf)!);
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (i, headers : pem_headers) @pool()
|
||||
{
|
||||
out.append(encode(tmem, bodies[i], tags[i], ...headers, use_crlf: use_crlf)!);
|
||||
};
|
||||
}
|
||||
return allocator == tmem ? out.str_view() : out.copy_str(allocator);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::experimental::scheduler{Event};
|
||||
module std::experimental::scheduler <Event>;
|
||||
import std::collections, std::thread, std::time;
|
||||
|
||||
struct DelayedSchedulerEvent @local
|
||||
@@ -7,7 +7,7 @@ struct DelayedSchedulerEvent @local
|
||||
Clock execution_time;
|
||||
}
|
||||
|
||||
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local
|
||||
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other)
|
||||
{
|
||||
switch
|
||||
{
|
||||
@@ -43,7 +43,7 @@ macro void FrameScheduler.@destroy(&self; @destruct(Event e))
|
||||
self.events.free();
|
||||
self.pending_events.free();
|
||||
self.delayed_events.free();
|
||||
(void)self.mtx.destroy();
|
||||
self.mtx.destroy();
|
||||
}
|
||||
|
||||
fn void FrameScheduler.queue_delayed_event(&self, Event event, Duration delay)
|
||||
@@ -76,7 +76,7 @@ fn Event? FrameScheduler.pop_event(&self)
|
||||
while (true)
|
||||
{
|
||||
if (try event = self.events.pop()) return event;
|
||||
if (!@atomic_load(self.pending)) return NO_MORE_ELEMENT?;
|
||||
if (!@atomic_load(self.pending)) return NO_MORE_ELEMENT~;
|
||||
self.mtx.@in_lock()
|
||||
{
|
||||
self.events.add_all(&self.pending_events);
|
||||
@@ -88,7 +88,7 @@ fn Event? FrameScheduler.pop_event(&self)
|
||||
self.events.push(self.delayed_events.pop()!!);
|
||||
}
|
||||
@atomic_store(self.pending, self.delayed_events.len() > 0);
|
||||
if (!self.events.len()) return NO_MORE_ELEMENT?;
|
||||
if (!self.events.len()) return NO_MORE_ELEMENT~;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,8 @@ fn ulong hash(char[] data, ulong seed = 0)
|
||||
for (; data.len > 16; data = data[16..])
|
||||
{
|
||||
@a5mul(
|
||||
@unaligned_load(((ulong*)data.ptr)[0], 1) ^ seed1,
|
||||
@unaligned_load(((ulong*)data.ptr)[1], 1) ^ seed2,
|
||||
mem::load((ulong*)data.ptr, 1) ^ seed1,
|
||||
mem::load((ulong*)data.ptr + 1, 1) ^ seed2,
|
||||
seed1, seed2
|
||||
);
|
||||
|
||||
@@ -71,16 +71,16 @@ fn ulong hash(char[] data, ulong seed = 0)
|
||||
seed2 += val10;
|
||||
}
|
||||
|
||||
a = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 16), 1);
|
||||
b = @unaligned_load(*(ulong*)(data.ptr + (uptr)data.len - 8), 1);
|
||||
a = mem::load((ulong*)(data.ptr + (uptr)data.len - 16), 1);
|
||||
b = mem::load((ulong*)(data.ptr + (uptr)data.len - 8), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
a = ((ulong)@unaligned_load(*(uint*)&data[0], 1) << 32)
|
||||
| @unaligned_load(*(uint*)&data[^4], 1);
|
||||
a = ((ulong)mem::load((uint*)&data[0], 1) << 32)
|
||||
| mem::load((uint*)&data[^4], 1);
|
||||
|
||||
b = ((ulong)@unaligned_load(*(uint*)&data[(data.len >> 3) * 4], 1) << 32)
|
||||
| @unaligned_load(*(uint*)(data.ptr + data.len - 4 - (data.len >> 3) * 4), 1);
|
||||
b = ((ulong)mem::load((uint*)&data[(data.len >> 3) * 4], 1) << 32)
|
||||
| mem::load((uint*)(data.ptr + data.len - 4 - (data.len >> 3) * 4), 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
|
||||
module std::hash::adler32;
|
||||
|
||||
const uint ADLER_CONST @private = 65521;
|
||||
const uint ADLER32_CONST @private = 65521;
|
||||
|
||||
|
||||
struct Adler32
|
||||
{
|
||||
@@ -19,19 +20,79 @@ fn void Adler32.init(&self)
|
||||
|
||||
fn void Adler32.updatec(&self, char c)
|
||||
{
|
||||
self.a = (self.a + c) % ADLER_CONST;
|
||||
self.b = (self.b + self.a) % ADLER_CONST;
|
||||
self.a = (self.a + c) % ADLER32_CONST;
|
||||
self.b = (self.b + self.a) % ADLER32_CONST;
|
||||
}
|
||||
|
||||
fn void Adler32.update(&self, char[] data)
|
||||
{
|
||||
// Safe chunking constant which is optimized for L1 cache on most systems 32768 (32 KB).
|
||||
// 0x8000 ~ (2^32 / 65521 / 2).
|
||||
// The division is done so that we are guarenteed to never overflow.
|
||||
const uint SAFE_CHUNKING_SIZE = 0x8000;
|
||||
|
||||
// In order
|
||||
const uint UNROLL_SIZE = 8;
|
||||
|
||||
uint a = self.a;
|
||||
uint b = self.b;
|
||||
foreach (char x : data)
|
||||
|
||||
char* buf = data;
|
||||
usz len = data.len;
|
||||
|
||||
// Align pointer traversing buffer pointer to the unroll alignment size.
|
||||
if (len % UNROLL_SIZE != 0)
|
||||
{
|
||||
a = (a + x) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
do
|
||||
{
|
||||
a += *buf;
|
||||
b += a;
|
||||
|
||||
buf++;
|
||||
len--;
|
||||
} while (len % UNROLL_SIZE != 0);
|
||||
|
||||
if (a >= ADLER32_CONST)
|
||||
{
|
||||
a -= ADLER32_CONST;
|
||||
}
|
||||
|
||||
b %= ADLER32_CONST;
|
||||
}
|
||||
|
||||
// Calculate rest of adler32 checksum.
|
||||
while (len > 0)
|
||||
{
|
||||
$for var $i = 0; $i < UNROLL_SIZE; $i++:
|
||||
a += buf[$i]; b += a;
|
||||
$endfor
|
||||
|
||||
len -= UNROLL_SIZE;
|
||||
buf += UNROLL_SIZE;
|
||||
|
||||
// Even with 8 max value (0xFF) bytes being additioned to a (0xFF * 8 = 2040 for worst case).
|
||||
// There is no chance that a will be > 2 * ADLER32_CONST, so modulo is not needed here.
|
||||
// So its more performant to use subtraction.
|
||||
if (a >= ADLER32_CONST)
|
||||
{
|
||||
a -= ADLER32_CONST;
|
||||
}
|
||||
|
||||
// We need to periodically chunk b because it accumulates a which is a sum, so it grows rapidly.
|
||||
// So every 4K of bytes we modulo in order to prevent uint integer overflow.
|
||||
if (len % SAFE_CHUNKING_SIZE == 0)
|
||||
{
|
||||
b %= ADLER32_CONST;
|
||||
}
|
||||
}
|
||||
|
||||
// No need to explicitely modulo after loop end with ADLER32_CONST.
|
||||
// As a and b are guarenteed to be under ADLER32_CONST.
|
||||
|
||||
// Do assert on debug.
|
||||
assert(a < ADLER32_CONST);
|
||||
assert(b < ADLER32_CONST);
|
||||
|
||||
*self = { a, b };
|
||||
}
|
||||
|
||||
@@ -42,12 +103,10 @@ fn uint Adler32.final(&self)
|
||||
|
||||
fn uint hash(char[] data)
|
||||
{
|
||||
uint a = 1;
|
||||
uint b = 0;
|
||||
foreach (char x : data)
|
||||
{
|
||||
a = (a + x) % ADLER_CONST;
|
||||
b = (b + a) % ADLER_CONST;
|
||||
}
|
||||
return (b << 16) | a;
|
||||
Adler32 adler;
|
||||
adler.init();
|
||||
|
||||
adler.update(data);
|
||||
|
||||
return adler.final();
|
||||
}
|
||||
387
lib/std/hash/blake2.c3
Normal file
387
lib/std/hash/blake2.c3
Normal file
@@ -0,0 +1,387 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// This is based on the original BLAKE2 reference implementation, but it's been
|
||||
// significantly changed. You can really see how C3's features shine over the
|
||||
// original C source.
|
||||
//
|
||||
// You'll note that this may have been better done with generic modules. I'm not
|
||||
// an expert, but I'd argue that it's unnecessary for this particular algorithm.
|
||||
// There are only two important versions of the algorithm to implement in a "common"
|
||||
// (generic) way, and the output size of each type is controlled rather naturally.
|
||||
//
|
||||
module std::hash::blake2;
|
||||
|
||||
|
||||
// Common hash output sizes. The library is NOT limited to these six output sizes, but
|
||||
// these instead stand in for what would have been "magic numbers" throughout the code.
|
||||
const SIZE_128 = 16;
|
||||
const SIZE_160 = 20;
|
||||
const SIZE_224 = 28;
|
||||
const SIZE_256 = 32;
|
||||
const SIZE_384 = 48;
|
||||
const SIZE_512 = 64;
|
||||
|
||||
|
||||
macro @g(r, m, $s, i, #a, #b, #c, #d) @local
|
||||
{
|
||||
// I really dislike using these conditional one-liners, but... it's the only difference between 2s and 2b besides buffer sizes (mainly).
|
||||
#a += #b + m[$s[r][2*i+0]];
|
||||
#d = (#d ^ #a).rotr($sizeof(#a) == ulong.sizeof ??? 32 : 16);
|
||||
#c += #d;
|
||||
#b = (#b ^ #c).rotr($sizeof(#a) == ulong.sizeof ??? 24 : 12);
|
||||
#a += #b + m[$s[r][2*i+1]];
|
||||
#d = (#d ^ #a).rotr($sizeof(#a) == ulong.sizeof ??? 16 : 8);
|
||||
#c += #d;
|
||||
#b = (#b ^ #c).rotr($sizeof(#a) == ulong.sizeof ??? 63 : 7);
|
||||
}
|
||||
|
||||
macro @round(r, m, $s, #v) @local
|
||||
{
|
||||
@g(r, m, $s, 0, #v[ 0], #v[ 4], #v[ 8], #v[12]);
|
||||
@g(r, m, $s, 1, #v[ 1], #v[ 5], #v[ 9], #v[13]);
|
||||
@g(r, m, $s, 2, #v[ 2], #v[ 6], #v[10], #v[14]);
|
||||
@g(r, m, $s, 3, #v[ 3], #v[ 7], #v[11], #v[15]);
|
||||
@g(r, m, $s, 4, #v[ 0], #v[ 5], #v[10], #v[15]);
|
||||
@g(r, m, $s, 5, #v[ 1], #v[ 6], #v[11], #v[12]);
|
||||
@g(r, m, $s, 6, #v[ 2], #v[ 7], #v[ 8], #v[13]);
|
||||
@g(r, m, $s, 7, #v[ 3], #v[ 4], #v[ 9], #v[14]);
|
||||
}
|
||||
|
||||
macro common_compress(instance, $rounds, $iv, $sigma, block) @local
|
||||
{
|
||||
$typeof(instance.h[0])[16] m, v;
|
||||
|
||||
((char*)&m)[:$sizeof(block)] = block[..];
|
||||
|
||||
v[:8] = instance.h[..];
|
||||
v[ 8] = $iv[0];
|
||||
v[ 9] = $iv[1];
|
||||
v[10] = $iv[2];
|
||||
v[11] = $iv[3];
|
||||
v[12] = $iv[4] ^ instance.t[0];
|
||||
v[13] = $iv[5] ^ instance.t[1];
|
||||
v[14] = $iv[6] ^ instance.f[0];
|
||||
v[15] = $iv[7] ^ instance.f[1];
|
||||
|
||||
$for usz $i = 0; $i < $rounds; $i++:
|
||||
@round($i, m, $sigma, v);
|
||||
$endfor
|
||||
|
||||
$for usz $i = 0; $i < 8; $i++:
|
||||
instance.h[$i] ^= v[$i] ^ v[$i + 8];
|
||||
$endfor
|
||||
}
|
||||
|
||||
macro _add_ctr(instance, usz amount) @local
|
||||
{
|
||||
instance.t[0] += ($typeof(instance.t[0]))amount;
|
||||
instance.t[1] += ($typeof(instance.t[0]))(instance.t[0] < amount); // adds 1 on overflow of [0]
|
||||
}
|
||||
|
||||
macro common_init(instance, $ParamType, $iv, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {}) @local
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*instance)); // explicitly because habits around hash init usually involve @noinit
|
||||
|
||||
instance.h[..] = $iv[..];
|
||||
instance.outlen = out_len;
|
||||
|
||||
$ParamType p = {
|
||||
.digest_length = (char)out_len,
|
||||
.key_length = (char)key.len,
|
||||
.fanout = 1,
|
||||
.depth = 1,
|
||||
};
|
||||
if (salt.len) p.salt[:salt.len] = salt[..];
|
||||
if (personal.len) p.personal[:personal.len] = personal[..];
|
||||
|
||||
array::@zip_into(((char*)&instance.h)[:$sizeof(p)], ((char*)&p)[:$sizeof(p)], fn (a, b) => a ^ b); // bytes(self.h) ^= bytes(p)
|
||||
|
||||
if (key.len)
|
||||
{
|
||||
char[$sizeof($iv[0])*16] dummy = {};
|
||||
dummy[:key.len] = key[..];
|
||||
instance.update(dummy[..]); // consume a FULL block
|
||||
mem::zero_volatile(dummy[..]); // do not optimize clearing this from the stack
|
||||
}
|
||||
}
|
||||
|
||||
macro common_update(instance, $block_size, char[] data) @local
|
||||
{
|
||||
if (@unlikely(!data.len)) return;
|
||||
|
||||
usz fill = $block_size - instance.buflen;
|
||||
if (data.len > fill)
|
||||
{
|
||||
instance.buf[instance.buflen:fill] = data[:fill];
|
||||
instance.buflen = 0;
|
||||
|
||||
_add_ctr(instance, $block_size);
|
||||
instance._compress(instance.buf);
|
||||
|
||||
data = data[fill..];
|
||||
|
||||
for (; data.len > $block_size; data = data[$block_size..])
|
||||
{
|
||||
_add_ctr(instance, $block_size);
|
||||
instance._compress(data[:$block_size]);
|
||||
}
|
||||
}
|
||||
instance.buf[instance.buflen:data.len] = data[..];
|
||||
instance.buflen += data.len;
|
||||
}
|
||||
|
||||
macro common_final(instance, $output_length) @local
|
||||
{
|
||||
char[$output_length] result = {};
|
||||
if ($output_length != instance.outlen) return result;
|
||||
|
||||
_add_ctr(instance, instance.buflen);
|
||||
if (instance.f[0]) return result; // technically an error return
|
||||
|
||||
var $max = $typeof(instance.h[0]).max;
|
||||
if (instance.last_node) instance.f[1] = $max;
|
||||
instance.f[0] = $max;
|
||||
|
||||
mem::zero_volatile(instance.buf[instance.buflen..]); // pad buffer with zeroes
|
||||
instance._compress(instance.buf);
|
||||
|
||||
defer mem::zero_volatile(@as_char_view(*instance)); // destroy the current context implicitly
|
||||
|
||||
result[:instance.outlen] = @as_char_view(instance.h)[:instance.outlen];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ======================================================================================
|
||||
// BEGIN Blake2b contents. Do not separate this from Blake2s: there's not really a point.
|
||||
//
|
||||
const BLAKE2B_BLOCKBYTES @local = 128;
|
||||
const BLAKE2B_OUTBYTES @local = 64;
|
||||
const BLAKE2B_KEYBYTES @local = 64;
|
||||
const BLAKE2B_SALTBYTES @local = 16;
|
||||
const BLAKE2B_PERSONALBYTES @local = 16;
|
||||
const ulong[8] BLAKE2B_IV @local = {
|
||||
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
|
||||
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
|
||||
};
|
||||
const char[16][12] BLAKE2B_SIGMA @local = {
|
||||
x'000102030405060708090a0b0c0d0e0f',
|
||||
x'0e0a0408090f0d06010c00020b070503',
|
||||
x'0b080c0005020f0d0a0e030607010904',
|
||||
x'070903010d0c0b0e0206050a04000f08',
|
||||
x'0900050702040a0f0e010b0c0608030d',
|
||||
x'020c060a000b0803040d07050f0e0109',
|
||||
x'0c05010f0e0d040a000706030902080b',
|
||||
x'0d0b070e0c01030905000f040806020a',
|
||||
x'060f0e090b0300080c020d0701040a05',
|
||||
x'0a020804070601050f0b090e030c0d00',
|
||||
x'000102030405060708090a0b0c0d0e0f',
|
||||
x'0e0a0408090f0d06010c00020b070503',
|
||||
};
|
||||
|
||||
struct Blake2b
|
||||
{
|
||||
ulong[8] h;
|
||||
ulong[2] t;
|
||||
ulong[2] f;
|
||||
char[BLAKE2B_BLOCKBYTES] buf;
|
||||
usz buflen;
|
||||
usz outlen;
|
||||
char last_node;
|
||||
}
|
||||
|
||||
struct Blake2bParam @packed @local
|
||||
{
|
||||
char digest_length; /* 1 */
|
||||
char key_length; /* 2 */
|
||||
char fanout; /* 3 */
|
||||
char depth; /* 4 */
|
||||
uint leaf_length; /* 8 */
|
||||
uint node_offset; /* 12 */
|
||||
uint xof_length; /* 16 */
|
||||
char node_depth; /* 17 */
|
||||
char inner_length; /* 18 */
|
||||
char[14] reserved; /* 32 */
|
||||
char[BLAKE2B_SALTBYTES] salt; /* 48 */
|
||||
char[BLAKE2B_PERSONALBYTES] personal; /* 64 */
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(data[0]) &&& $typeof(data[0]) == char : "Input data must be a char slice, char array, or string value."
|
||||
*>
|
||||
macro blake2b_hash($out_len, data, char[] key = {}, char[] salt = {})
|
||||
{
|
||||
Blake2b b @noinit;
|
||||
b.init($out_len, key, salt);
|
||||
b.update(data[..]);
|
||||
return b.final($out_len);
|
||||
}
|
||||
alias b = blake2b_hash;
|
||||
|
||||
// See RFC 7693, Section 4 for common parameter sets.
|
||||
macro blake2b_224(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_224, data, key, salt);
|
||||
macro blake2b_256(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_256, data, key, salt);
|
||||
macro blake2b_384(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_384, data, key, salt);
|
||||
macro blake2b_512(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_512, data, key, salt);
|
||||
|
||||
alias b_224 = blake2b_224;
|
||||
alias b_256 = blake2b_256;
|
||||
alias b_384 = blake2b_384;
|
||||
alias b_512 = blake2b_512;
|
||||
|
||||
<*
|
||||
Blake2b initialization method. Presents various options
|
||||
|
||||
@param out_len : "The desired output length from the hash function."
|
||||
@param[in] key : "An optional key value to use (keys the entire hash value to give HMAC-like functionality)."
|
||||
@param[in] salt : "An optional salt value to use when generating the hash."
|
||||
@param[in] personal : "An optional personalization value to use when generating the hash."
|
||||
|
||||
@require out_len > 0 && out_len <= BLAKE2B_OUTBYTES : "Blake2 output length must be within the proper range."
|
||||
@require !key.ptr || (key.len > 0 && key.len <= BLAKE2B_KEYBYTES) : "A specified key's length must be within the proper range."
|
||||
@require !salt.ptr || (salt.len > 0 && salt.len <= BLAKE2B_SALTBYTES) : "A specified salt's length must be within the proper range."
|
||||
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
|
||||
*>
|
||||
fn void Blake2b.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
|
||||
=> common_init(self, Blake2bParam, BLAKE2B_IV, out_len, key, salt, personal);
|
||||
|
||||
<*
|
||||
Core compression inline function for Blake2b.
|
||||
*>
|
||||
fn void Blake2b._compress(&self, char[BLAKE2B_BLOCKBYTES] block) @inline
|
||||
=> common_compress(self, 12, BLAKE2B_IV, BLAKE2B_SIGMA, block);
|
||||
|
||||
<*
|
||||
Add more data to the hash context or stream.
|
||||
|
||||
@param[in] data : "The data to ingest into the hash context."
|
||||
*>
|
||||
fn void Blake2b.update(&self, char[] data)
|
||||
=> common_update(self, BLAKE2B_BLOCKBYTES, data);
|
||||
|
||||
<*
|
||||
Finalize the hash context and return the hash result at the given size.
|
||||
|
||||
@param $output_length : "The length of the output array which is returned by value, instead of as a slice."
|
||||
|
||||
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
|
||||
*>
|
||||
macro char[*] Blake2b.final(&self, $output_length) => _blake2b_final{$output_length}(self);
|
||||
|
||||
fn char[OUTPUT_LENGTH] _blake2b_final(Blake2b* self) <OUTPUT_LENGTH> @local
|
||||
{
|
||||
return common_final(self, OUTPUT_LENGTH);
|
||||
}
|
||||
|
||||
// ======================================================================================
|
||||
// BEGIN Blake2s contents. Do not separate this from Blake2b: there's not really a point.
|
||||
//
|
||||
const BLAKE2S_BLOCKBYTES @local = BLAKE2B_BLOCKBYTES / 2;
|
||||
const BLAKE2S_OUTBYTES @local = BLAKE2B_OUTBYTES / 2;
|
||||
const BLAKE2S_KEYBYTES @local = BLAKE2B_KEYBYTES / 2;
|
||||
const BLAKE2S_SALTBYTES @local = BLAKE2B_SALTBYTES / 2;
|
||||
const BLAKE2S_PERSONALBYTES @local = BLAKE2B_PERSONALBYTES / 2;
|
||||
const uint[8] BLAKE2S_IV @local = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 };
|
||||
const char[16][10] BLAKE2S_SIGMA @local = {
|
||||
x'000102030405060708090a0b0c0d0e0f',
|
||||
x'0e0a0408090f0d06010c00020b070503',
|
||||
x'0b080c0005020f0d0a0e030607010904',
|
||||
x'070903010d0c0b0e0206050a04000f08',
|
||||
x'0900050702040a0f0e010b0c0608030d',
|
||||
x'020c060a000b0803040d07050f0e0109',
|
||||
x'0c05010f0e0d040a000706030902080b',
|
||||
x'0d0b070e0c01030905000f040806020a',
|
||||
x'060f0e090b0300080c020d0701040a05',
|
||||
x'0a020804070601050f0b090e030c0d00',
|
||||
};
|
||||
|
||||
struct Blake2s
|
||||
{
|
||||
uint[8] h;
|
||||
uint[2] t;
|
||||
uint[2] f;
|
||||
char[BLAKE2S_BLOCKBYTES] buf;
|
||||
usz buflen;
|
||||
usz outlen;
|
||||
char last_node;
|
||||
}
|
||||
|
||||
struct Blake2sParam @packed @local
|
||||
{
|
||||
char digest_length; /* 1 */
|
||||
char key_length; /* 2 */
|
||||
char fanout; /* 3 */
|
||||
char depth; /* 4 */
|
||||
uint leaf_length; /* 8 */
|
||||
uint node_offset; /* 12 */
|
||||
ushort xof_length; /* 14 */
|
||||
char node_depth; /* 15 */
|
||||
char inner_length; /* 16 */
|
||||
char[BLAKE2S_SALTBYTES] salt; /* 24 */
|
||||
char[BLAKE2S_PERSONALBYTES] personal; /* 32 */
|
||||
}
|
||||
|
||||
<*
|
||||
@require $defined(data[0]) &&& $typeof(data[0]) == char : "Input data must be a char slice, char array, or string value."
|
||||
*>
|
||||
macro blake2s_hash($out_len, data, char[] key = {}, char[] salt = {})
|
||||
{
|
||||
Blake2s b @noinit;
|
||||
b.init($out_len, key, salt);
|
||||
b.update(data[..]);
|
||||
return b.final($out_len);
|
||||
}
|
||||
alias s = blake2s_hash;
|
||||
|
||||
// See RFC 7693, Section 4 for common parameter sets.
|
||||
macro blake2s_128(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_128, data, key, salt);
|
||||
macro blake2s_160(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_160, data, key, salt);
|
||||
macro blake2s_224(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_224, data, key, salt);
|
||||
macro blake2s_256(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_256, data, key, salt);
|
||||
|
||||
alias s_128 = blake2s_128;
|
||||
alias s_160 = blake2s_160;
|
||||
alias s_224 = blake2s_224;
|
||||
alias s_256 = blake2s_256;
|
||||
|
||||
<*
|
||||
Blake2s initialization method. Presents various options
|
||||
|
||||
@param out_len : "The desired output length from the hash function."
|
||||
@param[in] key : "An optional key value to use (keys the entire hash value to give HMAC-like functionality)."
|
||||
@param[in] salt : "An optional salt value to use when generating the hash."
|
||||
@param[in] personal : "An optional personalization value to use when generating the hash."
|
||||
|
||||
@require out_len > 0 && out_len <= BLAKE2S_OUTBYTES : "Blake2 output length must be within the proper range."
|
||||
@require !key.ptr || (key.len > 0 && key.len <= BLAKE2S_KEYBYTES) : "A specified key's length must be within the proper range."
|
||||
@require !salt.ptr || (salt.len > 0 && salt.len <= BLAKE2S_SALTBYTES) : "A specified salt's length must be within the proper range."
|
||||
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
|
||||
*>
|
||||
fn void Blake2s.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
|
||||
=> common_init(self, Blake2sParam, BLAKE2S_IV, out_len, key, salt, personal);
|
||||
|
||||
<*
|
||||
Core compression inline function for Blake2s.
|
||||
*>
|
||||
fn void Blake2s._compress(&self, char[BLAKE2S_BLOCKBYTES] block) @inline
|
||||
=> common_compress(self, 10, BLAKE2S_IV, BLAKE2S_SIGMA, block);
|
||||
|
||||
<*
|
||||
Add more data to the hash context or stream.
|
||||
|
||||
@param[in] data : "The data to ingest into the hash context."
|
||||
*>
|
||||
fn void Blake2s.update(&self, char[] data)
|
||||
=> common_update(self, BLAKE2S_BLOCKBYTES, data);
|
||||
|
||||
<*
|
||||
Finalize the hash context and return the hash result at the given size.
|
||||
|
||||
@param $output_length : "The length of the output array which is returned by value, instead of as a slice."
|
||||
|
||||
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
|
||||
*>
|
||||
macro char[*] Blake2s.final(&self, $output_length)
|
||||
=> common_final(self, $output_length);
|
||||
748
lib/std/hash/blake3.c3
Normal file
748
lib/std/hash/blake3.c3
Normal file
@@ -0,0 +1,748 @@
|
||||
// Copyright (c) 2025-2026 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// This is based on the original BLAKE3 reference implementation:
|
||||
// https://github.com/BLAKE3-team/BLAKE3/blob/master
|
||||
//
|
||||
module std::hash::blake3;
|
||||
import std::thread;
|
||||
|
||||
const BLOCK_SIZE = 64;
|
||||
const CHUNK_SIZE = 1024;
|
||||
const KEY_SIZE = 32;
|
||||
const KEY_SIZE_WORDS = KEY_SIZE / uint.sizeof;
|
||||
const OUT_SIZE = 32;
|
||||
const MAX_DEPTH = 54;
|
||||
|
||||
const uint[8] IV = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
const char[16][7] MESSAGE_SCHEDULE = {
|
||||
x'000102030405060708090a0b0c0d0e0f',
|
||||
x'0206030a0700040d010b0c05090e0f08',
|
||||
x'03040a0c0d02070e060509000b0f0801',
|
||||
x'0a070c090e030d0f04000b0205080106',
|
||||
x'0c0d090b0f0a0e080702050300010604',
|
||||
x'090e0b05080c0f010d03000a02060407',
|
||||
x'0b0f0500010908060e0a020c0304070d',
|
||||
};
|
||||
|
||||
|
||||
// Get feature-based optimization options.
|
||||
// For now, none of these are used until there's a chance to explore BLAKE3's (necessary) vectorization optimizations.
|
||||
//
|
||||
<* When true, force the use of slow-but-portable BLAKE3 functions. Do not vectorize the hash function. *>
|
||||
const FORCE_PORTABLE = true; //$feature(BLAKE3_FORCE_PORTABLE); // this is statically set to TRUE for now
|
||||
<* AARCH64: When not big-endian, use Neon. *>
|
||||
const USE_NEON = !FORCE_PORTABLE &&& (env::AARCH64 &&& !env::BIG_ENDIAN);
|
||||
<* Bundling some architecture booleans into one. *>
|
||||
const IS_X86 = !FORCE_PORTABLE &&& (env::X86_64 ||| env::X86);
|
||||
<*
|
||||
The maximum possible degree of parallelization based on the current architecture.
|
||||
This doesn't represent the ACTUAL degree available.
|
||||
*>
|
||||
const MAX_SIMD_DEGREE = IS_X86 ??? 16 : (USE_NEON ??? 4 : 1);
|
||||
<* There are cases in BLAKE3 where, at compile-time, it's necessary to easily get the max degree, or a minimum of 2. *>
|
||||
const MAX_SIMD_DEGREE_OR_2 = @max(MAX_SIMD_DEGREE, 2);
|
||||
|
||||
|
||||
<* Always set to true once BLAKE3 caches some initial CPU details. *>
|
||||
bool cpuinfo_initd @local = false;
|
||||
|
||||
<*
|
||||
Cache some information at runtime about the current processor and platform, as needed for optimizations.
|
||||
*>
|
||||
fn void init_blake3() @local
|
||||
{
|
||||
static OnceFlag run_once;
|
||||
run_once.call(fn() {
|
||||
$if IS_X86:
|
||||
cpudetect::x86_initialize_cpu_features(); // query all x86 feature flags, one time
|
||||
$endif
|
||||
cpuinfo_initd = true;
|
||||
});
|
||||
}
|
||||
|
||||
<* Check whether a given CPU flag is set (x86/x86_64 only). *>
|
||||
macro bool @check_cpu_flag(X86Feature f) @local @if(IS_X86)
|
||||
=> !!(cpudetect::x86_features & f.ordinal);
|
||||
|
||||
<*
|
||||
Return the actual SIMD degree of the processor at runtime.
|
||||
*>
|
||||
macro @simd_degree() @local
|
||||
{
|
||||
if (!cpuinfo_initd) init_blake3();
|
||||
assert(cpuinfo_initd == true, "Failed to run required BLAKE3 initializations.");
|
||||
|
||||
$switch:
|
||||
$case IS_X86:
|
||||
if (@check_cpu_flag(AVX512F) && @check_cpu_flag(AVX512VL)) return 16;
|
||||
if (@check_cpu_flag(AVX2)) return 8;
|
||||
if (@check_cpu_flag(SSE4_1) || @check_cpu_flag(SSE2)) return 4;
|
||||
$case USE_NEON:
|
||||
return 4;
|
||||
$endswitch
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
<* Flags used during hash computation based on its state. *>
|
||||
constdef Blake3Flags : inline char
|
||||
{
|
||||
CHUNK_START = 1 << 0,
|
||||
CHUNK_END = 1 << 1,
|
||||
PARENT = 1 << 2,
|
||||
ROOT = 1 << 3,
|
||||
KEYED_HASH = 1 << 4,
|
||||
DERIVE_KEY_CONTEXT = 1 << 5,
|
||||
DERIVE_KEY_MATERIAL = 1 << 6,
|
||||
}
|
||||
|
||||
struct Blake3ChunkState @local
|
||||
{
|
||||
uint[8] cv;
|
||||
ulong chunk_counter;
|
||||
char[BLOCK_SIZE] buf;
|
||||
char buf_len;
|
||||
char blocks_compressed;
|
||||
char flags;
|
||||
}
|
||||
|
||||
struct Blake3Output @local
|
||||
{
|
||||
uint[KEY_SIZE_WORDS] input_cv;
|
||||
ulong counter;
|
||||
char[BLOCK_SIZE] block;
|
||||
char block_len;
|
||||
char flags;
|
||||
}
|
||||
|
||||
struct Blake3
|
||||
{
|
||||
uint[KEY_SIZE_WORDS] key;
|
||||
Blake3ChunkState chunk;
|
||||
char cv_stack_len;
|
||||
char[(MAX_DEPTH + 1) * OUT_SIZE] cv_stack;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Generate an XOF hash based on the given inputs.
|
||||
|
||||
Consider the output hash w/ `seek = 0` and `$out_size = 41`:
|
||||
```
|
||||
2cc39783c223154fea8dfb7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d905630c8be290dfcf3
|
||||
```
|
||||
|
||||
Computing with the same input `key` and input `data`, but with a `seek = 3` and `$out_size = 8` yields:
|
||||
```
|
||||
83c223154fea8dfb
|
||||
|
||||
which is a slice cut out from the above hash:
|
||||
2cc397 [83c223154fea8dfb] 7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d905630c8be290dfcf3
|
||||
```
|
||||
|
||||
In this way, the XOF primitive that BLAKE3 is built from allows the hash output to be a potentially
|
||||
limitless result that one may slice to their liking using the right parameters.
|
||||
|
||||
@param [in] data : "The data to hash."
|
||||
@param [in] key : "An optional 32-byte key to turn the result into a keyed hash."
|
||||
@param seek : "An optional value specifying the offset into the XOF's yield where the resultant hash should begin."
|
||||
@param $out_size : "An optional value specifying the desired length to slice from the XOF's yield."
|
||||
|
||||
@return "The hash as a character array of `$out_size` bytes."
|
||||
|
||||
@require !key.len || key.len == KEY_SIZE : "Key value must be empty or exactly 32 bytes."
|
||||
@require $out_size > 0 : "You cannot use a zero $out_size."
|
||||
*>
|
||||
macro char[*] hash(char[] data, char[] key = {}, usz seek = 0, usz $out_size = 32)
|
||||
{
|
||||
char[$out_size] result;
|
||||
Blake3 b @noinit;
|
||||
defer b.destroy();
|
||||
b.init(key);
|
||||
b.update(data);
|
||||
b.final(result[..], $out_size, seek);
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Generate a hash from a context string. This call allows one to use the "context" to
|
||||
auto-generate keying material for the resultant hash value. Effectively, this allows for
|
||||
hashes made from data with completely variable-length keys, rather than having a key fixed
|
||||
to 32 bytes. The 'context' nomenclature is from BLAKE3 itself, not my naming.
|
||||
|
||||
@param [in] data : "The data to hash."
|
||||
@param [in] context : "An optional key to turn the result into a keyed hash."
|
||||
@param seek : "An optional value specifying the offset into the XOF's yield where the resultant hash should begin."
|
||||
@param $out_size : "An optional value specifying the desired length to slice from the XOF's yield."
|
||||
|
||||
@return "The context-based hash as a character array of `$out_size` bytes."
|
||||
|
||||
@require $out_size > 0 : "You cannot use a zero $out_size."
|
||||
*>
|
||||
macro char[*] ctx_hash(char[] data, char[] context, usz seek = 0, usz $out_size = 32)
|
||||
{
|
||||
char[$out_size] result;
|
||||
Blake3 b = new_from_context(context);
|
||||
defer b.destroy();
|
||||
b.update(data);
|
||||
b.final(result[..], $out_size, seek);
|
||||
return result;
|
||||
}
|
||||
|
||||
<*
|
||||
Generate a new Blake3 hashing structure from the given context string. The context string
|
||||
acts as a variable-length key to seed the new hash structure, and makes it ready to ingest
|
||||
incoming data with `update`.
|
||||
|
||||
@param [in] context : "The context byte array used to seed the returned Blake3 context."
|
||||
*>
|
||||
macro Blake3 new_from_context(char[] context)
|
||||
{
|
||||
char[KEY_SIZE] context_based_key;
|
||||
defer mem::zero_volatile(context_based_key[..]);
|
||||
|
||||
Blake3 key_from_ctx @noinit;
|
||||
defer key_from_ctx.destroy();
|
||||
key_from_ctx.init(explicit_flags: Blake3Flags.DERIVE_KEY_CONTEXT);
|
||||
key_from_ctx.update(context);
|
||||
key_from_ctx.final(context_based_key[..], KEY_SIZE);
|
||||
|
||||
Blake3 b @noinit;
|
||||
b.init(key: context_based_key[..], explicit_flags: Blake3Flags.DERIVE_KEY_MATERIAL);
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Initialize a BLAKE3 context.
|
||||
|
||||
@param [in] key : "An optional key initializer to use."
|
||||
|
||||
@require !key.len || key.len == KEY_SIZE : "An explicit initialization key must be of KEY_SIZE (32 bytes)."
|
||||
*>
|
||||
fn void Blake3.init(&self, char[] key = {}, char explicit_flags = 0)
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*self));
|
||||
|
||||
if (key.len)
|
||||
{
|
||||
foreach (i, &w : self.key) *w = mem::load((uint*)&key[i * $sizeof(self.key[0])], 1);
|
||||
if (!explicit_flags) explicit_flags = Blake3Flags.KEYED_HASH;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.key[..] = IV[..];
|
||||
}
|
||||
|
||||
self.chunk.init(self.key[..], explicit_flags);
|
||||
}
|
||||
|
||||
<*
|
||||
Reset the state of the hashing context, in case it should be reused without reloading the key value.
|
||||
*>
|
||||
fn void _reset(Blake3* self) @local @inline
|
||||
{
|
||||
self.chunk.reset(self.key[..], 0);
|
||||
self.cv_stack_len = 0;
|
||||
}
|
||||
|
||||
<*
|
||||
Private function to merge tree results.
|
||||
*>
|
||||
fn void _merge_cv_stack(Blake3* self, ulong total_len) @local @inline
|
||||
{
|
||||
usz post_merge_stack_len = (usz)@popcnt(total_len);
|
||||
for (; self.cv_stack_len > post_merge_stack_len; self.cv_stack_len--)
|
||||
{
|
||||
char* parent_node = &self.cv_stack[(self.cv_stack_len - 2) * OUT_SIZE];
|
||||
Blake3Output o = parent_output(parent_node, self.key[..], self.chunk.flags);
|
||||
o.chaining_value(parent_node);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Private function to add a new tree onto the stack.
|
||||
*>
|
||||
fn void Blake3.push_cv(&self, char* new_cv, ulong chunk_counter) @inline
|
||||
{
|
||||
_merge_cv_stack(self, chunk_counter);
|
||||
self.cv_stack[self.cv_stack_len * OUT_SIZE : OUT_SIZE] = new_cv[:OUT_SIZE];
|
||||
self.cv_stack_len++;
|
||||
}
|
||||
|
||||
<*
|
||||
Update the hash context by consuming incoming data.
|
||||
|
||||
@param [in] input : "The slice of new data to digest."
|
||||
@param use_tbb : "Should remain `false` until other BLAKE3 optimizations are set up."
|
||||
*>
|
||||
fn void Blake3.update(&self, char[] input, bool use_tbb = false)
|
||||
{
|
||||
if (!input.len) return;
|
||||
|
||||
if (self.chunk.len() > 0)
|
||||
{
|
||||
usz take = min(CHUNK_SIZE - self.chunk.len(), input.len);
|
||||
self.chunk.update(input[:take]);
|
||||
input = input[take..];
|
||||
|
||||
if (!input.len) return;
|
||||
|
||||
char[KEY_SIZE] chunk_cv;
|
||||
Blake3Output o = self.chunk.output();
|
||||
o.chaining_value(&chunk_cv);
|
||||
self.push_cv(&chunk_cv, self.chunk.chunk_counter);
|
||||
self.chunk.reset(self.key[..], self.chunk.chunk_counter + 1);
|
||||
}
|
||||
|
||||
while (input.len > CHUNK_SIZE)
|
||||
{
|
||||
usz subtree_len = @round_down_to_power_of_2(input.len);
|
||||
ulong count_so_far = self.chunk.chunk_counter * CHUNK_SIZE;
|
||||
|
||||
while ((((ulong)(subtree_len - 1)) & count_so_far) != 0) subtree_len /= 2;
|
||||
|
||||
ulong subtree_chunks = subtree_len / CHUNK_SIZE;
|
||||
if (subtree_len <= CHUNK_SIZE)
|
||||
{
|
||||
Blake3ChunkState chunk_state;
|
||||
chunk_state.init(self.key[..], self.chunk.flags);
|
||||
chunk_state.chunk_counter = self.chunk.chunk_counter;
|
||||
chunk_state.update(input[:subtree_len]);
|
||||
char[OUT_SIZE] cv;
|
||||
Blake3Output o = chunk_state.output();
|
||||
o.chaining_value(&cv);
|
||||
self.push_cv(&cv, chunk_state.chunk_counter);
|
||||
}
|
||||
else
|
||||
{
|
||||
char[2 * OUT_SIZE] cv_pair;
|
||||
compress_subtree_to_parent_node(input[:subtree_len], self.key[..], self.chunk.chunk_counter, self.chunk.flags, cv_pair[..], use_tbb);
|
||||
self.push_cv(&cv_pair[0], self.chunk.chunk_counter);
|
||||
self.push_cv(&cv_pair[OUT_SIZE], self.chunk.chunk_counter + (subtree_chunks / 2));
|
||||
}
|
||||
self.chunk.chunk_counter += subtree_chunks;
|
||||
input = input[subtree_len..];
|
||||
}
|
||||
|
||||
if (input.len > 0)
|
||||
{
|
||||
self.chunk.update(input);
|
||||
_merge_cv_stack(self, self.chunk.chunk_counter);
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
Yield the results of the hash into a specified output buffer, at the specified length.
|
||||
Note that the `into` slice does not need to be properly cut to receive hash results; it
|
||||
just needs to be wide enough to accommodate `into_len` yielded bytes from the XOF.
|
||||
|
||||
@param [in] into : "The storage buffer for the output hash value. Must be >= `into_len` bytes."
|
||||
@param into_len : "How many bytes to receive from the XOF/hash output."
|
||||
@param seek : "How far into the XOF's yield to begin the stored byte sequence."
|
||||
|
||||
@require into.len >= into_len : "The requested output size must be equal to or less than the size of the output slice."
|
||||
*>
|
||||
fn void Blake3.final(&self, char[] into, usz into_len, usz seek = 0)
|
||||
{
|
||||
if (!into_len) return;
|
||||
|
||||
if (!self.cv_stack_len)
|
||||
{
|
||||
Blake3Output o = self.chunk.output();
|
||||
o.root_bytes(seek, into[:into_len]);
|
||||
return;
|
||||
}
|
||||
|
||||
Blake3Output o @noinit;
|
||||
usz cvs_remaining;
|
||||
if (self.chunk.len() > 0)
|
||||
{
|
||||
cvs_remaining = self.cv_stack_len;
|
||||
o = self.chunk.output();
|
||||
}
|
||||
else
|
||||
{
|
||||
cvs_remaining = (usz)self.cv_stack_len - 2;
|
||||
o = parent_output(&self.cv_stack[cvs_remaining * KEY_SIZE], self.key[..], self.chunk.flags);
|
||||
}
|
||||
|
||||
while (cvs_remaining > 0)
|
||||
{
|
||||
char[BLOCK_SIZE] parent_block;
|
||||
cvs_remaining--;
|
||||
parent_block[:32] = self.cv_stack[cvs_remaining * 32 : 32];
|
||||
o.chaining_value(&parent_block[32]);
|
||||
o = parent_output(&parent_block, self.key[..], self.chunk.flags);
|
||||
}
|
||||
|
||||
o.root_bytes(seek, into[:into_len]);
|
||||
}
|
||||
|
||||
<*
|
||||
Destroy a BLAKE3 hashing context.
|
||||
*>
|
||||
fn void Blake3.destroy(&self) @inline
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*self));
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Initialize a BLAKE3 chunk state.
|
||||
|
||||
@param [in] key
|
||||
@param flags
|
||||
*>
|
||||
fn void Blake3ChunkState.init(&self, uint[] key, char flags) @inline
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*self));
|
||||
self.cv[..] = key[..];
|
||||
self.flags = flags;
|
||||
}
|
||||
|
||||
<*
|
||||
Reset a BLAKE3 chunk state.
|
||||
|
||||
@param [in] key
|
||||
@param chunk_counter
|
||||
*>
|
||||
fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @inline
|
||||
{
|
||||
self.init(key, self.flags); // maintain its own flags
|
||||
self.chunk_counter = chunk_counter; // update chunk counter
|
||||
}
|
||||
|
||||
<*
|
||||
Get bytes length of consumed data.
|
||||
*>
|
||||
fn usz Blake3ChunkState.len(&self) @operator(len) @inline
|
||||
=> (BLOCK_SIZE * (usz)self.blocks_compressed) + (usz)self.buf_len;
|
||||
|
||||
<*
|
||||
Ingest an amount of bytes into the chunk's buffer. NOTE: Doesn't check for underflow.
|
||||
|
||||
@param [in] data : "Data to ingest."
|
||||
*>
|
||||
fn usz Blake3ChunkState.fill_buf(&self, char[] data) @inline
|
||||
{
|
||||
usz take = min(BLOCK_SIZE - (usz)self.buf_len, data.len);
|
||||
self.buf[self.buf_len:take] = data[:take];
|
||||
self.buf_len += (char)take;
|
||||
return take;
|
||||
}
|
||||
|
||||
<*
|
||||
Determine whether to set the CHUNK_START flag.
|
||||
*>
|
||||
fn char Blake3ChunkState.maybe_start_flag(&self) @inline
|
||||
=> !self.blocks_compressed ? Blake3Flags.CHUNK_START : 0;
|
||||
|
||||
<*
|
||||
Update the chunk with the provided input bytes.
|
||||
|
||||
@param [in] input : "Incoming bytes to update with."
|
||||
*>
|
||||
fn void Blake3ChunkState.update(&self, char[] input)
|
||||
{
|
||||
if (self.buf_len)
|
||||
{
|
||||
usz take = self.fill_buf(input);
|
||||
input = input[take..];
|
||||
if (input.len)
|
||||
{
|
||||
compress_in_place(self.cv[..], self.buf[..], BLOCK_SIZE, self.chunk_counter, self.flags | self.maybe_start_flag());
|
||||
self.blocks_compressed++;
|
||||
self.buf_len = 0;
|
||||
self.buf[..] = {};
|
||||
}
|
||||
}
|
||||
for (; input.len > BLOCK_SIZE; self.blocks_compressed++, input = input[BLOCK_SIZE..])
|
||||
{
|
||||
compress_in_place(self.cv[..], input[:BLOCK_SIZE], BLOCK_SIZE, self.chunk_counter, self.flags | self.maybe_start_flag());
|
||||
}
|
||||
self.fill_buf(input);
|
||||
}
|
||||
|
||||
<*
|
||||
Convert the chunk state to an "output" type with the right flags.
|
||||
*>
|
||||
fn Blake3Output Blake3ChunkState.output(&self) @inline
|
||||
=> make_output(self.cv[..], &self.buf, self.buf_len, self.chunk_counter, self.flags | self.maybe_start_flag() | Blake3Flags.CHUNK_END);
|
||||
|
||||
<*
|
||||
Generate and initialize an output structure with the provided parameters.
|
||||
|
||||
@param [in] key
|
||||
@param [&in] in_block
|
||||
@param block_len
|
||||
@param counter
|
||||
@param flags
|
||||
*>
|
||||
fn Blake3Output make_output(uint[] key, char* in_block, usz block_len, ulong counter, char flags) @local @noinline
|
||||
{
|
||||
Blake3Output o;
|
||||
o.input_cv[..] = key[..];
|
||||
o.block[..] = in_block[:BLOCK_SIZE];
|
||||
o.block_len = (char)block_len;
|
||||
o.counter = counter;
|
||||
o.flags = flags;
|
||||
return o;
|
||||
}
|
||||
|
||||
<*
|
||||
Auto-generate a parent output structure, pre-initialized with some constant identifiers.
|
||||
|
||||
@param [&in] block
|
||||
@param [in] key
|
||||
@param flags
|
||||
*>
|
||||
macro Blake3Output parent_output(char* block, uint[] key, char flags) @local
|
||||
=> make_output(key, block, BLOCK_SIZE, 0, flags | Blake3Flags.PARENT);
|
||||
|
||||
<*
|
||||
Compress then store the chaining value of the output structure.
|
||||
|
||||
@param [&inout] cv
|
||||
*>
|
||||
macro void Blake3Output.chaining_value(&self, char* cv)
|
||||
{
|
||||
uint[KEY_SIZE_WORDS] cv_words;
|
||||
cv_words[..] = self.input_cv[..];
|
||||
compress_in_place(cv_words[..], self.block, self.block_len, self.counter, self.flags);
|
||||
cv[:KEY_SIZE] = @as_char_view(cv_words)[:KEY_SIZE];
|
||||
}
|
||||
|
||||
<*
|
||||
Store the result of the output into the designated slice.
|
||||
|
||||
@param seek
|
||||
@param [inout] into
|
||||
*>
|
||||
fn void Blake3Output.root_bytes(&self, usz seek, char[] into)
|
||||
{
|
||||
if (!into.len) return;
|
||||
|
||||
ulong output_block_counter = seek / BLOCK_SIZE;
|
||||
usz offset_within_block = seek % BLOCK_SIZE;
|
||||
char[BLOCK_SIZE] wide_buf;
|
||||
|
||||
if (offset_within_block)
|
||||
{
|
||||
compress_xof(self.input_cv[..], self.block, self.block_len, output_block_counter, self.flags | Blake3Flags.ROOT, wide_buf[..]);
|
||||
usz avail = BLOCK_SIZE - offset_within_block;
|
||||
usz bytes = min(into.len, avail);
|
||||
into[:bytes] = wide_buf[offset_within_block:bytes];
|
||||
into = into[bytes..];
|
||||
output_block_counter++;
|
||||
}
|
||||
if (into.len / BLOCK_SIZE)
|
||||
{
|
||||
@xof_many(self.input_cv[..], self.block, self.block_len, output_block_counter, self.flags | Blake3Flags.ROOT, into, into.len / BLOCK_SIZE);
|
||||
}
|
||||
output_block_counter += into.len / 64;
|
||||
into = into[(usz)(into.len & -64ll) ..];
|
||||
if (into.len)
|
||||
{
|
||||
compress_xof(self.input_cv[..], self.block, self.block_len, output_block_counter, self.flags | Blake3Flags.ROOT, wide_buf[..]);
|
||||
into[..] = wide_buf[:into.len];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// =================================================================================================
|
||||
// =================================================================================================
|
||||
// =================================================================================================
|
||||
// WELCOME TO THE COMPUTATION GARDEN...
|
||||
//
|
||||
// You wanna understand BLAKE3? You gotta get through us.
|
||||
// ______________________________
|
||||
// ༼ ºل͟º ༼ ºل͟º ༼ ºل͟º ༽ ºل͟º ༽ ºل͟º ༽
|
||||
//
|
||||
//
|
||||
macro uint @popcnt(#x) @local => (uint)#x.popcount();
|
||||
macro uint @highest_one(#x) @local => 63 ^ (uint)#x.clz();
|
||||
macro usz @round_down_to_power_of_2(#x) @local => (usz)1 << @highest_one(#x | 1);
|
||||
|
||||
macro left_subtree_len(usz input_len) @local
|
||||
=> @round_down_to_power_of_2((input_len - 1) / CHUNK_SIZE) * CHUNK_SIZE;
|
||||
|
||||
|
||||
macro @g(#state, a, b, c, d, x, y) @local
|
||||
{
|
||||
#state[a] += #state[b] + x;
|
||||
#state[d] = (#state[d] ^ #state[a]).rotr(16);
|
||||
#state[c] += #state[d];
|
||||
#state[b] = (#state[b] ^ #state[c]).rotr(12);
|
||||
#state[a] += #state[b] + y;
|
||||
#state[d] = (#state[d] ^ #state[a]).rotr(8);
|
||||
#state[c] += #state[d];
|
||||
#state[b] = (#state[b] ^ #state[c]).rotr(7);
|
||||
}
|
||||
|
||||
macro @round(uint[] state, uint* msg, usz round) @local
|
||||
{
|
||||
char* schedule = &MESSAGE_SCHEDULE[round];
|
||||
@g(state, 0, 4, 8, 12, msg[schedule[0] ], msg[schedule[1] ]);
|
||||
@g(state, 1, 5, 9, 13, msg[schedule[2] ], msg[schedule[3] ]);
|
||||
@g(state, 2, 6, 10, 14, msg[schedule[4] ], msg[schedule[5] ]);
|
||||
@g(state, 3, 7, 11, 15, msg[schedule[6] ], msg[schedule[7] ]);
|
||||
@g(state, 0, 5, 10, 15, msg[schedule[8] ], msg[schedule[9] ]);
|
||||
@g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
|
||||
@g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
|
||||
@g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
|
||||
}
|
||||
|
||||
fn void compress_pre(uint[] state, uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags) @local @noinline
|
||||
{
|
||||
uint[16] block_words @noinit;
|
||||
foreach (i, &b : block_words) *b = mem::load((uint*)&block[i * 4], 1);
|
||||
state[0:8] = cv[0:8];
|
||||
state[8:4] = IV[0:4];
|
||||
state[12] = (uint)counter;
|
||||
state[13] = (uint)(counter >> 32);
|
||||
state[14] = (uint)block_len;
|
||||
state[15] = (uint)flags;
|
||||
|
||||
for (int i = 0; i < 7; i++)
|
||||
{
|
||||
@round(state, &block_words[0], (usz)i);
|
||||
}
|
||||
}
|
||||
|
||||
macro compress_in_place(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags) @local
|
||||
{
|
||||
uint[16] state @noinit;
|
||||
compress_pre(state[..], cv, block, block_len, counter, flags);
|
||||
for (usz i = 0; i < 8; i++) cv[i] = state[i] ^ state[i + 8];
|
||||
}
|
||||
|
||||
macro compress_xof(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags, char[] out) @local
|
||||
{
|
||||
uint[16] state @noinit;
|
||||
compress_pre(state[..], cv, block, block_len, counter, flags);
|
||||
$for usz $i = 0; $i < 8; $i++: mem::store((uint*)&out[4 * $i], state[$i] ^ state[$i + 8], 1); $endfor
|
||||
$for usz $i = 0; $i < 8; $i++: mem::store((uint*)&out[4 * (8 + $i)], state[$i + 8] ^ cv[$i], 1); $endfor
|
||||
}
|
||||
|
||||
macro @xof_many(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags, char[] out, usz out_blocks) @local
|
||||
{
|
||||
for (usz i = 0; i < out_blocks; i++, out = out[BLOCK_SIZE..]) compress_xof(cv, block, block_len, counter + i, flags, out);
|
||||
}
|
||||
|
||||
macro hash_one(char* input, usz blocks, uint[] key, ulong counter, char flags, char flags_start, char flags_end, char[] out) @local
|
||||
{
|
||||
uint[8] cv;
|
||||
cv[..] = key[..];
|
||||
char block_flags = flags | flags_start;
|
||||
for (; blocks > 0; input += BLOCK_SIZE, blocks--, block_flags = flags)
|
||||
{
|
||||
if (blocks == 1) block_flags |= flags_end;
|
||||
compress_in_place(cv[..], input[:BLOCK_SIZE], BLOCK_SIZE, counter, block_flags);
|
||||
}
|
||||
foreach (i, c : cv) mem::store((uint*)&out[i * 4], c, 1);
|
||||
}
|
||||
|
||||
macro hash_many(char*[] inputs, usz num_inputs, usz blocks, uint[] key, ulong counter, bool $increment_counter, char flags, char flags_start, char flags_end, char* out) @local
|
||||
{
|
||||
for (; num_inputs > 0; num_inputs--, inputs = inputs[1..], out += OUT_SIZE)
|
||||
{
|
||||
hash_one(inputs[0], blocks, key, counter, flags, flags_start, flags_end, out[:OUT_SIZE]);
|
||||
$if $increment_counter: counter++; $endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn void compress_subtree_to_parent_node(char[] input, uint[] key, ulong chunk_counter, char flags, char[] out, bool use_tbb) @local @noinline
|
||||
{
|
||||
char[MAX_SIMD_DEGREE_OR_2 * OUT_SIZE] cv_array;
|
||||
|
||||
usz num_cvs = compress_subtree_wide(input, key, chunk_counter, flags, cv_array[..], use_tbb);
|
||||
assert(num_cvs <= 2);
|
||||
|
||||
$if MAX_SIMD_DEGREE_OR_2 > 2:
|
||||
char[MAX_SIMD_DEGREE_OR_2 * OUT_SIZE / 2] out_array;
|
||||
while (num_cvs > 2) num_cvs = compress_parents_parallel(cv_array[..], num_cvs, key, flags, &out_array);
|
||||
$endif
|
||||
|
||||
out[..] = cv_array[:2 * OUT_SIZE];
|
||||
}
|
||||
|
||||
fn usz compress_subtree_wide(char[] input, uint[] key, ulong chunk_counter, char flags, char* out, bool use_tbb) @local @noinline
|
||||
{
|
||||
if (input.len <= @simd_degree() * CHUNK_SIZE) return compress_chunks_parallel(input, key, chunk_counter, flags, out);
|
||||
|
||||
usz left_input_len = left_subtree_len(input.len);
|
||||
usz right_input_len = input.len - left_input_len;
|
||||
char* right_input = &input[left_input_len];
|
||||
ulong right_chunk_counter = chunk_counter + (ulong)(left_input_len / CHUNK_SIZE);
|
||||
|
||||
char[2 * MAX_SIMD_DEGREE_OR_2 * OUT_SIZE] cv_array;
|
||||
usz degree = @simd_degree();
|
||||
if (left_input_len > CHUNK_SIZE && degree == 1) degree = 2;
|
||||
char* right_cvs = &cv_array[degree * OUT_SIZE];
|
||||
|
||||
usz left_n = compress_subtree_wide(input[:left_input_len], key, chunk_counter, flags, &cv_array, use_tbb);
|
||||
usz right_n = compress_subtree_wide(right_input[:right_input_len], key, right_chunk_counter, flags, right_cvs, use_tbb);
|
||||
|
||||
if (left_n == 1)
|
||||
{
|
||||
out[:2 * OUT_SIZE] = cv_array[:2 * OUT_SIZE];
|
||||
return 2;
|
||||
}
|
||||
|
||||
return compress_parents_parallel(cv_array[..], left_n + right_n, key, flags, out);
|
||||
}
|
||||
|
||||
fn usz compress_parents_parallel(char[] child_chaining_values, usz num_chaining_values, uint[] key, char flags, char* out) @local @noinline
|
||||
{
|
||||
char*[MAX_SIMD_DEGREE_OR_2] parents_array;
|
||||
usz parents_array_len = 0;
|
||||
|
||||
while (num_chaining_values - (2 * parents_array_len) >= 2)
|
||||
{
|
||||
parents_array[parents_array_len++] = &child_chaining_values[2 * parents_array_len * OUT_SIZE];
|
||||
}
|
||||
|
||||
hash_many(parents_array[:parents_array_len], parents_array_len, 1, key, 0, false, flags | Blake3Flags.PARENT, 0, 0, out);
|
||||
|
||||
if (num_chaining_values > 2 * parents_array_len)
|
||||
{
|
||||
out[parents_array_len * OUT_SIZE : OUT_SIZE] = child_chaining_values[2 * parents_array_len * OUT_SIZE : OUT_SIZE];
|
||||
return parents_array_len + 1;
|
||||
}
|
||||
|
||||
return parents_array_len;
|
||||
}
|
||||
|
||||
fn usz compress_chunks_parallel(char[] input, uint[] key, ulong chunk_counter, char flags, char* out) @local @noinline
|
||||
{
|
||||
char*[MAX_SIMD_DEGREE] chunks_array;
|
||||
usz input_position = 0;
|
||||
usz chunks_array_len = 0;
|
||||
|
||||
for (; input.len - input_position >= CHUNK_SIZE; input_position += CHUNK_SIZE)
|
||||
{
|
||||
chunks_array[chunks_array_len++] = &input[input_position];
|
||||
}
|
||||
|
||||
hash_many(chunks_array[:chunks_array_len], chunks_array_len, CHUNK_SIZE / BLOCK_SIZE, key, chunk_counter, true, flags, Blake3Flags.CHUNK_START, Blake3Flags.CHUNK_END, out);
|
||||
|
||||
if (input.len <= input_position) return chunks_array_len;
|
||||
|
||||
ulong counter = chunk_counter + (ulong)chunks_array_len;
|
||||
Blake3ChunkState chunk_state;
|
||||
chunk_state.init(key, flags);
|
||||
chunk_state.chunk_counter = counter;
|
||||
chunk_state.update(input[input_position : input.len - input_position]);
|
||||
Blake3Output o = chunk_state.output();
|
||||
o.chaining_value(&out[chunks_array_len * OUT_SIZE]);
|
||||
|
||||
return chunks_array_len + 1;
|
||||
}
|
||||
@@ -5,8 +5,8 @@ module std::hash::fnv32a;
|
||||
|
||||
typedef Fnv32a = uint;
|
||||
|
||||
const FNV32A_START @private = 0x811c9dc5;
|
||||
const FNV32A_MUL @private = 0x01000193;
|
||||
const FNV32A_START @private = (Fnv32a)0x811c9dc5;
|
||||
const FNV32A_MUL @private = (Fnv32a)0x01000193;
|
||||
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
|
||||
|
||||
@@ -32,10 +32,10 @@ macro void Fnv32a.update_char(&self, char c)
|
||||
|
||||
fn uint hash(char[] data)
|
||||
{
|
||||
uint h = FNV32A_START;
|
||||
Fnv32a h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
update(&h, x);
|
||||
}
|
||||
return h;
|
||||
return (uint)h;
|
||||
}
|
||||
|
||||
@@ -5,10 +5,10 @@ module std::hash::fnv64a;
|
||||
|
||||
typedef Fnv64a = ulong;
|
||||
|
||||
const FNV64A_START @private = 0xcbf29ce484222325;
|
||||
const FNV64A_MUL @private = 0x00000100000001b3;
|
||||
const Fnv64a FNV64A_START @private = (Fnv64a)0xcbf29ce484222325;
|
||||
const Fnv64a FNV64A_MUL @private = (Fnv64a)0x00000100000001b3;
|
||||
|
||||
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
|
||||
macro Fnv64a update(Fnv64a h, char x) @nodiscard @private => (h ^ (Fnv64a)x) * FNV64A_MUL;
|
||||
|
||||
fn void Fnv64a.init(&self)
|
||||
{
|
||||
@@ -20,22 +20,22 @@ fn void Fnv64a.update(&self, char[] data)
|
||||
Fnv64a h = *self;
|
||||
foreach (char x : data)
|
||||
{
|
||||
update(&h, x);
|
||||
h = update(h, x);
|
||||
}
|
||||
*self = h;
|
||||
}
|
||||
|
||||
macro void Fnv64a.update_char(&self, char c)
|
||||
{
|
||||
update(self, c);
|
||||
*self = update(*self, c);
|
||||
}
|
||||
|
||||
fn ulong hash(char[] data)
|
||||
{
|
||||
ulong h = FNV64A_START;
|
||||
Fnv64a h = FNV64A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
update(&h, x);
|
||||
h = update(h, x);
|
||||
}
|
||||
return h;
|
||||
return (ulong)h;
|
||||
}
|
||||
|
||||
172
lib/std/hash/gost/streebog.c3
Normal file
172
lib/std/hash/gost/streebog.c3
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// An implementation of Streebog-256 and Streebog-512, defined in the
|
||||
// Russian standard GOST R 34.11-2012. Also known as "GOST-12".
|
||||
//
|
||||
module std::hash::streebog;
|
||||
|
||||
|
||||
constdef StreebogLength : inline uint
|
||||
{
|
||||
SIZE_256 = 32,
|
||||
SIZE_512 = 64,
|
||||
}
|
||||
|
||||
struct Streebog
|
||||
{
|
||||
ulong[8] h;
|
||||
ulong[8] n;
|
||||
ulong[8] s;
|
||||
ulong[8] message;
|
||||
usz index;
|
||||
usz hash_size;
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
@require $defined(data[0]) &&& $typeof(data[0]) == char : "Input data must be a char slice, char array, or string value."
|
||||
*>
|
||||
macro char[*] hash(StreebogLength $hash_size, data)
|
||||
{
|
||||
// TODO: The speed of this hash hinges on use of >>> SIMD instructions <<<. It would be nice to have some added.
|
||||
Streebog s @noinit;
|
||||
s.init($hash_size);
|
||||
s.update(data);
|
||||
return s.final($hash_size);
|
||||
}
|
||||
|
||||
macro char[*] hash_256(char[] data) => hash(SIZE_256, data);
|
||||
macro char[*] hash_512(char[] data) => hash(SIZE_512, data);
|
||||
|
||||
|
||||
macro @xor_512(#x, #y, #z) @local
|
||||
{
|
||||
$for var $i = 0; $i < 8; $i++:
|
||||
#z[$i] = #x[$i] ^ #y[$i];
|
||||
$endfor
|
||||
}
|
||||
|
||||
macro @add_512(#sum, #x) @local
|
||||
{
|
||||
ulong carry = 0;
|
||||
$for usz $i = 0; $i < 8; $i++:
|
||||
#sum[$i] += #x[$i] + carry;
|
||||
carry = #sum[$i] < #x[$i] ? 1 : (#sum[$i] == #x[$i] ? carry : 0);
|
||||
$endfor
|
||||
}
|
||||
|
||||
macro @lpsx(#a, #b, #result) @local
|
||||
{
|
||||
ulong[8] z @noinit;
|
||||
@xor_512(#a, #b, z);
|
||||
// Do not unroll these loops - produces far too much code at compile-time.
|
||||
for (usz i = 0; i < 8; i++)
|
||||
{
|
||||
#result[i] = TR[0][(z[0] >> (i << 3)) & 0xff];
|
||||
for (usz j = 1; j < 8; j++) #result[i] ^= TR[j][(z[j] >> (i << 3)) & 0xff];
|
||||
}
|
||||
}
|
||||
|
||||
macro @g_n(#n, #h, #m) @local
|
||||
{
|
||||
ulong[8] k_i;
|
||||
ulong[8] state;
|
||||
|
||||
@lpsx(#h, #n, k_i);
|
||||
@lpsx(k_i, #m, state);
|
||||
// Do not unroll this loop - produces far too much code at compile-time.
|
||||
for (usz i = 0; i < 11; i++)
|
||||
{
|
||||
@lpsx(k_i, ITERATION_CONSTANTS[i], k_i);
|
||||
@lpsx(k_i, state, state);
|
||||
}
|
||||
@lpsx(k_i, ITERATION_CONSTANTS[11], k_i);
|
||||
@xor_512(k_i, state, state);
|
||||
@xor_512(state, #h, state);
|
||||
@xor_512(state, #m, #h);
|
||||
}
|
||||
|
||||
|
||||
macro Streebog.@stage2(&self, #m) @local
|
||||
{
|
||||
@g_n(self.n, self.h, #m);
|
||||
@add_512(self.n, STAGE2_512);
|
||||
@add_512(self.s, #m);
|
||||
}
|
||||
|
||||
macro void Streebog.init(&self, StreebogLength $hash_size)
|
||||
{
|
||||
mem::zero_volatile(@as_char_view(*self)); // explicitly initialize the entire state to 0
|
||||
self.hash_size = $hash_size;
|
||||
$if $hash_size != SIZE_512:
|
||||
@as_char_view(self.h)[..] = (char[*]){ [0..63] = 0x01 }[..];
|
||||
$endif
|
||||
}
|
||||
|
||||
fn void Streebog.update(&self, char[] data)
|
||||
{
|
||||
if (self.index)
|
||||
{
|
||||
usz rest = BLOCK_SIZE - self.index;
|
||||
usz size = data.len;
|
||||
usz len = size < rest ? size : rest;
|
||||
|
||||
@as_char_view(self.message)[self.index:len] = data[:len];
|
||||
self.index += size;
|
||||
if (size < rest) return;
|
||||
|
||||
self.@stage2(self.message);
|
||||
data = data[rest..];
|
||||
self.index = 0;
|
||||
}
|
||||
bool aligned = 0 == (usz)data.ptr % ulong.sizeof;
|
||||
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..])
|
||||
{
|
||||
if (aligned)
|
||||
{
|
||||
self.@stage2((ulong*)data.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@as_char_view(self.message)[:BLOCK_SIZE] = data[:BLOCK_SIZE];
|
||||
self.@stage2(self.message);
|
||||
}
|
||||
}
|
||||
if (data.len)
|
||||
{
|
||||
self.index = data.len;
|
||||
@as_char_view(self.message)[:data.len] = data[..];
|
||||
}
|
||||
}
|
||||
|
||||
<*
|
||||
@require $hash_size == self.hash_size : "You must use the same output hash size as was initialized with the context."
|
||||
*>
|
||||
macro char[*] Streebog.final(&self, StreebogLength $hash_size)
|
||||
{
|
||||
char[$hash_size] result;
|
||||
streebog_final_private(self);
|
||||
defer mem::zero_volatile(@as_char_view(*self)); // implicitly clear the structure when finalized
|
||||
result[..] = @as_char_view(self.h)[(BLOCK_SIZE - $hash_size)..];
|
||||
return result;
|
||||
}
|
||||
|
||||
fn void streebog_final_private(Streebog* self) @local
|
||||
{
|
||||
ulong[8] unprocessed_bits_count;
|
||||
usz index = self.index >> 3;
|
||||
usz shift = (self.index & 0b111) * 8;
|
||||
|
||||
unprocessed_bits_count[0] = self.index * 8ul;
|
||||
self.message[index] &= ~(ulong.max << shift);
|
||||
self.message[index++] ^= 1ul << shift;
|
||||
if (index < 8) self.message[index..] = {};
|
||||
|
||||
@g_n(self.n, self.h, self.message);
|
||||
@add_512(self.n, unprocessed_bits_count);
|
||||
@add_512(self.s, self.message);
|
||||
@g_n(ZERO_512, self.h, self.n);
|
||||
@g_n(ZERO_512, self.h, self.s);
|
||||
}
|
||||
305
lib/std/hash/gost/streebog_internal.c3
Normal file
305
lib/std/hash/gost/streebog_internal.c3
Normal file
@@ -0,0 +1,305 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// An implementation of Streebog-256 and Streebog-512, defined in the
|
||||
// Russian standard GOST R 34.11-2012. Also known as "GOST-12".
|
||||
//
|
||||
module std::hash::streebog @private;
|
||||
|
||||
|
||||
const usz BLOCK_SIZE = 64;
|
||||
|
||||
const ulong[8] ZERO_512 @align(ulong.sizeof) = {};
|
||||
|
||||
const ulong[8] STAGE2_512 @align(ulong.sizeof) = { [0] = 512ul, [1..7] = 0 };
|
||||
|
||||
const ulong[8][12] ITERATION_CONSTANTS @align(ulong.sizeof) = {
|
||||
{ 0xdd806559f2a64507, 0x05767436cc744d23, 0xa2422a08a460d315, 0x4b7ce09192676901, 0x714eb88d7585c4fc, 0x2f6a76432e45d016, 0xebcb2f81c0657c1f, 0xb1085bda1ecadae9 },
|
||||
{ 0xe679047021b19bb7, 0x55dda21bd7cbcd56, 0x5cb561c2db0aa7ca, 0x9ab5176b12d69958, 0x61d55e0f16b50131, 0xf3feea720a232b98, 0x4fe39d460f70b5d7, 0x6fa3b58aa99d2f1a },
|
||||
{ 0x991e96f50aba0ab2, 0xc2b6f443867adb31, 0xc1c93a376062db09, 0xd3e20fe490359eb1, 0xf2ea7514b1297b7b, 0x06f15e5f529c1f8b, 0x0a39fc286a3d8435, 0xf574dcac2bce2fc7 },
|
||||
{ 0x220cbebc84e3d12e, 0x3453eaa193e837f1, 0xd8b71333935203be, 0xa9d72c82ed03d675, 0x9d721cad685e353f, 0x488e857e335c3c7d, 0xf948e1a05d71e4dd, 0xef1fdfb3e81566d2 },
|
||||
{ 0x601758fd7c6cfe57, 0x7a56a27ea9ea63f5, 0xdfff00b723271a16, 0xbfcd1747253af5a3, 0x359e35d7800fffbd, 0x7f151c1f1686104a, 0x9a3f410c6ca92363, 0x4bea6bacad474799 },
|
||||
{ 0xfa68407a46647d6e, 0xbf71c57236904f35, 0x0af21f66c2bec6b6, 0xcffaa6b71c9ab7b4, 0x187f9ab49af08ec6, 0x2d66c4f95142a46c, 0x6fa4c33b7a3039c0, 0xae4faeae1d3ad3d9 },
|
||||
{ 0x8886564d3a14d493, 0x3517454ca23c4af3, 0x06476983284a0504, 0x0992abc52d822c37, 0xd3473e33197a93c9, 0x399ec6c7e6bf87c9, 0x51ac86febf240954, 0xf4c70e16eeaac5ec },
|
||||
{ 0xa47f0dd4bf02e71e, 0x36acc2355951a8d9, 0x69d18d2bd1a5c42f, 0xf4892bcb929b0690, 0x89b4443b4ddbc49a, 0x4eb7f8719c36de1e, 0x03e7aa020c6e4141, 0x9b1f5b424d93c9a7 },
|
||||
{ 0x7261445183235adb, 0x0e38dc92cb1f2a60, 0x7b2b8a9aa6079c54, 0x800a440bdbb2ceb1, 0x3cd955b7e00d0984, 0x3a7d3a1b25894224, 0x944c9ad8ec165fde, 0x378f5a541631229b },
|
||||
{ 0x74b4c7fb98459ced, 0x3698fad1153bb6c3, 0x7a1e6c303b7652f4, 0x9fe76702af69334b, 0x1fffe18a1b336103, 0x8941e71cff8a78db, 0x382ae548b2e4f3f3, 0xabbedea680056f52 },
|
||||
{ 0x6bcaa4cd81f32d1b, 0xdea2594ac06fd85d, 0xefbacd1d7d476e98, 0x8a1d71efea48b9ca, 0x2001802114846679, 0xd8fa6bbbebab0761, 0x3002c6cd635afe94, 0x7bcd9ed0efc889fb },
|
||||
{ 0x48bc924af11bd720, 0xfaf417d5d9b21b99, 0xe71da4aa88e12852, 0x5d80ef9d1891cc86, 0xf82012d430219f9b, 0xcda43c32bcdf1d77, 0xd21380b00449b17a, 0x378ee767f11631ba }
|
||||
};
|
||||
|
||||
const ulong[256][8] TR @align(ulong.sizeof) = {
|
||||
{
|
||||
0xd01f715b5c7ef8e6, 0x16fa240980778325, 0xa8a42e857ee049c8, 0x6ac1068fa186465b, 0x6e417bd7a2e9320b, 0x665c8167a437daab, 0x7666681aa89617f6, 0x4b959163700bdcf5,
|
||||
0xf14be6b78df36248, 0xc585bd689a625cff, 0x9557d7fca67d82cb, 0x89f0b969af6dd366, 0xb0833d48749f6c35, 0xa1998c23b1ecbc7c, 0x8d70c431ac02a736, 0xd6dfbc2fd0a8b69e,
|
||||
0x37aeb3e551fa198b, 0x0b7d128a40b5cf9c, 0x5a8f2008b5780cbc, 0xedec882284e333e5, 0xd25fc177d3c7c2ce, 0x5e0f5d50b61778ec, 0x1d873683c0c24cb9, 0xad040bcbb45d208c,
|
||||
0x2f89a0285b853c76, 0x5732fff6791b8d58, 0x3e9311439ef6ec3f, 0xc9183a809fd3c00f, 0x83adf3f5260a01ee, 0xa6791941f4e8ef10, 0x103ae97d0ca1cd5d, 0x2ce948121dee1b4a,
|
||||
0x39738421dbf2bf53, 0x093da2a6cf0cf5b4, 0xcd9847d89cbcb45f, 0xf9561c078b2d8ae8, 0x9c6a755a6971777f, 0xbc1ebaa0712ef0c5, 0x72e61542abf963a6, 0x78bb5fde229eb12e,
|
||||
0x14ba94250fceb90d, 0x844d6697630e5282, 0x98ea08026a1e032f, 0xf06bbea144217f5c, 0xdb6263d11ccb377a, 0x641c314b2b8ee083, 0x320e96ab9b4770cf, 0x1ee7deb986a96b85,
|
||||
0xe96cf57a878c47b5, 0xfdd6615f8842feb8, 0xc83862965601dd1b, 0x2ea9f83e92572162, 0xf876441142ff97fc, 0xeb2c455608357d9d, 0x5612a7e0b0c9904c, 0x6c01cbfb2d500823,
|
||||
0x4548a6a7fa037a2d, 0xabc4c6bf388b6ef4, 0xbade77d4fdf8bebd, 0x799b07c8eb4cac3a, 0x0c9d87e805b19cf0, 0xcb588aac106afa27, 0xea0c1d40c1e76089, 0x2869354a1e816f1a,
|
||||
0xff96d17307fbc490, 0x9f0a9d602f1a5043, 0x96373fc6e016a5f7, 0x5292dab8b3a6e41c, 0x9b8ae0382c752413, 0x4f15ec3b7364a8a5, 0x3fb349555724f12b, 0xc7c50d4415db66d7,
|
||||
0x92b7429ee379d1a7, 0xd37f99611a15dfda, 0x231427c05e34a086, 0xa439a96d7b51d538, 0xb403401077f01865, 0xdda2aea5901d7902, 0x0a5d4a9c8967d288, 0xc265280adf660f93,
|
||||
0x8bb0094520d4e94e, 0x2a29856691385532, 0x42a833c5bf072941, 0x73c64d54622b7eb2, 0x07e095624504536c, 0x8a905153e906f45a, 0x6f6123c16b3b2f1f, 0xc6e55552dc097bc3,
|
||||
0x4468feb133d16739, 0xe211e7f0c7398829, 0xa2f96419f7879b40, 0x19074bdbc3ad38e9, 0xf4ebc3f9474e0b0c, 0x43886bd376d53455, 0xd8028beb5aa01046, 0x51f23282f5cdc320,
|
||||
0xe7b1c2be0d84e16d, 0x081dfab006dee8a0, 0x3b33340d544b857b, 0x7f5bcabc679ae242, 0x0edd37c48a08a6d8, 0x81ed43d9a9b33bc6, 0xb1a3655ebd4d7121, 0x69a1eeb5e7ed6167,
|
||||
0xf6ab73d5c8f73124, 0x1a67a3e185c61fd5, 0x2dc91004d43c065e, 0x0240b02c8fb93a28, 0x90f7f2b26cc0eb8f, 0x3cd3a16f114fd617, 0xaae49ea9f15973e0, 0x06c0cd748cd64e78,
|
||||
0xda423bc7d5192a6e, 0xc345701c16b41287, 0x6d2193ede4821537, 0xfcf639494190e3ac, 0x7c3b228621f1c57e, 0xfb16ac2b0494b0c0, 0xbf7e529a3745d7f9, 0x6881b6a32e3f7c73,
|
||||
0xca78d2bad9b8e733, 0xbbfe2fc2342aa3a9, 0x0dbddffecc6381e4, 0x70a6a56e2440598e, 0xe4d12a844befc651, 0x8c509c2765d0ba22, 0xee8c6018c28814d9, 0x17da7c1f49a59e31,
|
||||
0x609c4c1328e194d3, 0xb3e3d57232f44b09, 0x91d7aaa4a512f69b, 0x0ffd6fd243dabbcc, 0x50d26a943c1fde34, 0x6be15e9968545b4f, 0x94778fea6faf9fdf, 0x2b09dd7058ea4826,
|
||||
0x677cd9716de5c7bf, 0x49d5214fffb2e6dd, 0x0360e83a466b273c, 0x1fc786af4f7b7691, 0xa0b9d435783ea168, 0xd49f0c035f118cb6, 0x01205816c9d21d14, 0xac2453dd7d8f3d98,
|
||||
0x545217cc3f70aa64, 0x26b4028e9489c9c2, 0xdec2469fd6765e3e, 0x04807d58036f7450, 0xe5f17292823ddb45, 0xf30b569b024a5860, 0x62dcfc3fa758aefb, 0xe84cad6c4e5e5aa1,
|
||||
0xccb81fce556ea94b, 0x53b282ae7a74f908, 0x1b47fbf74c1402c1, 0x368eebf39828049f, 0x7afbeff2ad278b06, 0xbe5e0a8cfe97caed, 0xcfd8f7f413058e77, 0xf78b2bc301252c30,
|
||||
0x4d555c17fcdd928d, 0x5f2f05467fc565f8, 0x24f4b2a21b30f3ea, 0x860dd6bbecb768aa, 0x4c750401350f8f99, 0x0000000000000000, 0xecccd0344d312ef1, 0xb5231806be220571,
|
||||
0xc105c030990d28af, 0x653c695de25cfd97, 0x159acc33c61ca419, 0xb89ec7f872418495, 0xa9847693b73254dc, 0x58cf90243ac13694, 0x59efc832f3132b80, 0x5c4fed7c39ae42c4,
|
||||
0x828dabe3efd81cfa, 0xd13f294d95ace5f2, 0x7d1b7a90e823d86a, 0xb643f03cf849224d, 0x3df3f979d89dcb03, 0x7426d836272f2dde, 0xdfe21e891fa4432a, 0x3a136c1b9d99986f,
|
||||
0xfa36f43dcd46add4, 0xc025982650df35bb, 0x856d3e81aadc4f96, 0xc4a5e57e53b041eb, 0x4708168b75ba4005, 0xaf44bbe73be41aa4, 0x971767d029c4b8e3, 0xb9be9feebb939981,
|
||||
0x215497ecd18d9aae, 0x316e7e91dd2c57f3, 0xcef8afe2dad79363, 0x3853dc371220a247, 0x35ee03c9de4323a3, 0xe6919aa8c456fc79, 0xe05157dc4880b201, 0x7bdbb7e464f59612,
|
||||
0x127a59518318f775, 0x332ecebd52956ddb, 0x8f30741d23bb9d1e, 0xd922d3fd93720d52, 0x7746300c61440ae2, 0x25d4eab4d2e2eefe, 0x75068020eefd30ca, 0x135a01474acaea61,
|
||||
0x304e268714fe4ae7, 0xa519f17bb283c82c, 0xdc82f6b359cf6416, 0x5baf781e7caa11a8, 0xb2c38d64fb26561d, 0x34ce5bdf17913eb7, 0x5d6fb56af07c5fd0, 0x182713cd0a7f25fd,
|
||||
0x9e2ac576e6c84d57, 0x9aaab82ee5a73907, 0xa3d93c0f3e558654, 0x7e7b92aaae48ff56, 0x872d8ead256575be, 0x41c8dbfff96c0e7d, 0x99ca5014a3cc1e3b, 0x40e883e930be1369,
|
||||
0x1ca76e95091051ad, 0x4e35b42dbab6b5b1, 0x05a0254ecabd6944, 0xe1710fca8152af15, 0xf22b0e8dcb984574, 0xb763a82a319b3f59, 0x63fca4296e8ab3ef, 0x9d4a2d4ca0a36a6b,
|
||||
0xe331bfe60eeb953d, 0xd5bf541596c391a2, 0xf5cb9bef8e9c1618, 0x46284e9dbc685d11, 0x2074cffa185f87ba, 0xbd3ee2b6b8fcedd1, 0xae64e3f1f23607b0, 0xfeb68965ce29d984,
|
||||
0x55724fdaf6a2b770, 0x29496d5cd753720e, 0xa75941573d3af204, 0x8e102c0bea69800a, 0x111ab16bc573d049, 0xd7ffe439197aab8a, 0xefac380e0b5a09cd, 0x48f579593660fbc9,
|
||||
0x22347fd697e6bd92, 0x61bc1405e13389c7, 0x4ab5c975b9d9c1e1, 0x80cd1bcf606126d2, 0x7186fd78ed92449a, 0x93971a882aabccb3, 0x88d0e17f66bfce72, 0x27945a985d5bd4d6
|
||||
},
|
||||
{
|
||||
0xde553f8c05a811c8, 0x1906b59631b4f565, 0x436e70d6b1964ff7, 0x36d343cb8b1e9d85, 0x843dfacc858aab5a, 0xfdfc95c299bfc7f9, 0x0f634bdea1d51fa2, 0x6d458b3b76efb3cd,
|
||||
0x85c3f77cf8593f80, 0x3c91315fbe737cb2, 0x2148b03366ace398, 0x18f8b8264c6761bf, 0xc830c1c495c9fb0f, 0x981a76102086a0aa, 0xaa16012142f35760, 0x35cc54060c763cf6,
|
||||
0x42907d66cc45db2d, 0x8203d44b965af4bc, 0x3d6f3cefc3a0e868, 0xbc73ff69d292bda7, 0x8722ed0102e20a29, 0x8f8185e8cd34deb7, 0x9b0561dda7ee01d9, 0x5335a0193227fad6,
|
||||
0xc9cecc74e81a6fd5, 0x54f5832e5c2431ea, 0x99e47ba05d553470, 0xf7bee756acd226ce, 0x384e05a5571816fd, 0xd1367452a47d0e6a, 0xf29fde1c386ad85b, 0x320c77316275f7ca,
|
||||
0xd0c879e2d9ae9ab0, 0xdb7406c69110ef5d, 0x45505e51a2461011, 0xfc029872e46c5323, 0xfa3cb6f5f7bc0cc5, 0x031f17cd8768a173, 0xbd8df2d9af41297d, 0x9d3b4f5ab43e5e3f,
|
||||
0x4071671b36feee84, 0x716207e7d3e3b83d, 0x48d20ff2f9283a1a, 0x27769eb4757cbc7e, 0x5c56ebc793f2e574, 0xa48b474f9ef5dc18, 0x52cbada94ff46e0c, 0x60c7da982d8199c6,
|
||||
0x0e9d466edc068b78, 0x4eec2175eaf865fc, 0x550b8e9e21f7a530, 0x6b7ba5bc653fec2b, 0x5eb7f1ba6949d0dd, 0x57ea94e3db4c9099, 0xf640eae6d101b214, 0xdd4a284182c0b0bb,
|
||||
0xff1d8fbf6304f250, 0xb8accb933bf9d7e8, 0xe8867c478eb68c4d, 0x3f8e2692391bddc1, 0xcb2fd60912a15a7c, 0xaec935dbab983d2f, 0xf55ffd2b56691367, 0x80e2ce366ce1c115,
|
||||
0x179bf3f8edb27e1d, 0x01fe0db07dd394da, 0xda8a0b76ecc37b87, 0x44ae53e1df9584cb, 0xb310b4b77347a205, 0xdfab323c787b8512, 0x3b511268d070b78e, 0x65e6e3d2b9396753,
|
||||
0x6864b271e2574d58, 0x259784c98fc789d7, 0x02e11a7dfabb35a9, 0x8841a6dfa337158b, 0x7ade78c39b5dcdd0, 0xb7cf804d9a2cc84a, 0x20b6bd831b7f7742, 0x75bd331d3a88d272,
|
||||
0x418f6aab4b2d7a5e, 0xd9951cbb6babdaf4, 0xb6318dfde7ff5c90, 0x1f389b112264aa83, 0x492c024284fbaec0, 0xe33a0363c608f9a0, 0x2688930408af28a4, 0xc7538a1a341ce4ad,
|
||||
0x5da8e677ee2171ae, 0x8c9e92254a5c7fc4, 0x63d8cd55aae938b5, 0x29ebd8daa97a3706, 0x959827b37be88aa1, 0x1484e4356adadf6e, 0xa7945082199d7d6b, 0xbf6ce8a455fa1cd4,
|
||||
0x9cc542eac9edcae5, 0x79c16f0e1c356ca3, 0x89bfab6fdee48151, 0xd4174d1830c5f0ff, 0x9258048415eb419d, 0x6139d72850520d1c, 0x6a85a80c18ec78f1, 0xcd11f88e0171059a,
|
||||
0xcceff53e7ca29140, 0xd229639f2315af19, 0x90b91ef9ef507434, 0x5977d28d074a1be1, 0x311360fce51d56b9, 0xc093a92d5a1f2f91, 0x1a19a25bb6dc5416, 0xeb996b8a09de2d3e,
|
||||
0xfee3820f1ed7668a, 0xd7085ad5b7ad518c, 0x7fff41890fe53345, 0xec5948bd67dde602, 0x2fd5f65dbaaa68e0, 0xa5754affe32648c2, 0xf8ddac880d07396c, 0x6fa491468c548664,
|
||||
0x0c7c5c1326bdbed1, 0x4a33158f03930fb3, 0x699abfc19f84d982, 0xe4fa2054a80b329c, 0x6707f9af438252fa, 0x08a368e9cfd6d49e, 0x47b1442c58fd25b8, 0xbbb3dc5ebc91769b,
|
||||
0x1665fe489061eac7, 0x33f27a811fa66310, 0x93a609346838d547, 0x30ed6d4c98cec263, 0x1dd9816cd8df9f2a, 0x94662a03063b1e7b, 0x83fdd9fbeb896066, 0x7b207573e68e590a,
|
||||
0x5f49fc0a149a4407, 0x343259b671a5a82c, 0xfbc2bb458a6f981f, 0xc272b350a0a41a38, 0x3aaf1fd8ada32354, 0x6cbb868b0b3c2717, 0xa2b569c88d2583fe, 0xf180c9d1bf027928,
|
||||
0xaf37386bd64ba9f5, 0x12bacab2790a8088, 0x4c0d3b0810435055, 0xb2eeb9070e9436df, 0xc5b29067cea7d104, 0xdcb425f1ff132461, 0x4f122cc5972bf126, 0xac282fa651230886,
|
||||
0xe7e537992f6393ef, 0xe61b3a2952b00735, 0x709c0a57ae302ce7, 0xe02514ae416058d3, 0xc44c9dd7b37445de, 0x5a68c5408022ba92, 0x1c278cdca50c0bf0, 0x6e5a9cf6f18712be,
|
||||
0x86dce0b17f319ef3, 0x2d34ec2040115d49, 0x4bcd183f7e409b69, 0x2815d56ad4a9a3dc, 0x24698979f2141d0d, 0x0000000000000000, 0x1ec696a15fb73e59, 0xd86b110b16784e2e,
|
||||
0x8e7f8858b0e74a6d, 0x063e2e8713d05fe6, 0xe2c40ed3bbdb6d7a, 0xb1f1aeca89fc97ac, 0xe1db191e3cb3cc09, 0x6418ee62c4eaf389, 0xc6ad87aa49cf7077, 0xd6f65765ca7ec556,
|
||||
0x9afb6c6dda3d9503, 0x7ce05644888d9236, 0x8d609f95378feb1e, 0x23a9aa4e9c17d631, 0x6226c0e5d73aac6f, 0x56149953a69f0443, 0xeeb852c09d66d3ab, 0x2b0ac2a753c102af,
|
||||
0x07c023376e03cb3c, 0x2ccae1903dc2c993, 0xd3d76e2f5ec63bc3, 0x9e2458973356ff4c, 0xa66a5d32644ee9b1, 0x0a427294356de137, 0x783f62be61e6f879, 0x1344c70204d91452,
|
||||
0x5b96c8f0fdf12e48, 0xa90916ecc59bf613, 0xbe92e5142829880e, 0x727d102a548b194e, 0x1be7afebcb0fc0cc, 0x3e702b2244c8491b, 0xd5e940a84d166425, 0x66f9f41f3e51c620,
|
||||
0xabe80c913f20c3ba, 0xf07ec461c2d1edf2, 0xf361d3ac45b94c81, 0x0521394a94b8fe95, 0xadd622162cf09c5c, 0xe97871f7f3651897, 0xf4a1f09b2bba87bd, 0x095d6559b2054044,
|
||||
0x0bbc7f2448be75ed, 0x2af4cf172e129675, 0x157ae98517094bb4, 0x9fda55274e856b96, 0x914713499283e0ee, 0xb952c623462a4332, 0x74433ead475b46a8, 0x8b5eb112245fb4f8,
|
||||
0xa34b6478f0f61724, 0x11a5dd7ffe6221fb, 0xc16da49d27ccbb4b, 0x76a224d0bde07301, 0x8aa0bca2598c2022, 0x4df336b86d90c48f, 0xea67663a740db9e4, 0xef465f70e0b54771,
|
||||
0x39b008152acb8227, 0x7d1e5bf4f55e06ec, 0x105bd0cf83b1b521, 0x775c2960c033e7db, 0x7e014c397236a79f, 0x811cc386113255cf, 0xeda7450d1a0e72d8, 0x5889df3d7a998f3b,
|
||||
0x2e2bfbedc779fc3a, 0xce0eef438619a4e9, 0x372d4e7bf6cd095f, 0x04df34fae96b6a4f, 0xf923a13870d4adb6, 0xa1aa7e050a4d228d, 0xa8f71b5cb84862c9, 0xb52e9a306097fde3,
|
||||
0x0d8251a35b6e2a0b, 0x2257a7fee1c442eb, 0x73831d9a29588d94, 0x51d4ba64c89ccf7f, 0x502ab7d4b54f5ba5, 0x97793dce8153bf08, 0xe5042de4d5d8a646, 0x9687307efc802bd2,
|
||||
0xa05473b5779eb657, 0xb4d097801d446939, 0xcff0e2f3fbca3033, 0xc38cbee0dd778ee2, 0x464f499c252eb162, 0xcad1dbb96f72cea6, 0xba4dd1eec142e241, 0xb00fa37af42f0376
|
||||
},
|
||||
{
|
||||
0xcce4cd3aa968b245, 0x089d5484e80b7faf, 0x638246c1b3548304, 0xd2fe0ec8c2355492, 0xa7fbdf7ff2374eee, 0x4df1600c92337a16, 0x84e503ea523b12fb, 0x0790bbfd53ab0c4a,
|
||||
0x198a780f38f6ea9d, 0x2ab30c8f55ec48cb, 0xe0f7fed6b2c49db5, 0xb6ecf3f422cadbdc, 0x409c9a541358df11, 0xd3ce8a56dfde3fe3, 0xc3e9224312c8c1a0, 0x0d6dfa58816ba507,
|
||||
0xddf3e1b179952777, 0x04c02a42748bb1d9, 0x94c2abff9f2decb8, 0x4f91752da8f8acf4, 0x78682befb169bf7b, 0xe1c77a48af2ff6c4, 0x0c5d7ec69c80ce76, 0x4cc1e4928fd81167,
|
||||
0xfeed3d24d9997b62, 0x518bb6dfc3a54a23, 0x6dbf2d26151f9b90, 0xb5bc624b05ea664f, 0xe86aaa525acfe21a, 0x4801ced0fb53a0be, 0xc91463e6c00868ed, 0x1027a815cd16fe43,
|
||||
0xf67069a0319204cd, 0xb04ccc976c8abce7, 0xc0b9b3fc35e87c33, 0xf380c77c58f2de65, 0x50bb3241de4e2152, 0xdf93f490435ef195, 0xf1e0d25d62390887, 0xaf668bfb1a3c3141,
|
||||
0xbc11b251f00a7291, 0x73a5eed47e427d47, 0x25bee3f6ee4c3b2e, 0x43cc0beb34786282, 0xc824e778dde3039c, 0xf97d86d98a327728, 0xf2b043e24519b514, 0xe297ebf7880f4b57,
|
||||
0x3a94a49a98fab688, 0x868516cb68f0c419, 0xeffa11af0964ee50, 0xa4ab4ec0d517f37d, 0xa9c6b498547c567a, 0x8e18424f80fbbbb6, 0x0bcdc53bcf2bc23c, 0x137739aaea3643d0,
|
||||
0x2c1333ec1bac2ff0, 0x8d48d3f0a7db0625, 0x1e1ac3f26b5de6d7, 0xf520f81f16b2b95e, 0x9f0f6ec450062e84, 0x0130849e1deb6b71, 0xd45e31ab8c7533a9, 0x652279a2fd14e43f,
|
||||
0x3209f01e70f1c927, 0xbe71a770cac1a473, 0x0e3d6be7a64b1894, 0x7ec8148cff29d840, 0xcb7476c7fac3be0f, 0x72956a4a63a91636, 0x37f95ec21991138f, 0x9e3fea5a4ded45f5,
|
||||
0x7b38ba50964902e8, 0x222e580bbde73764, 0x61e253e0899f55e6, 0xfc8d2805e352ad80, 0x35994be3235ac56d, 0x09add01af5e014de, 0x5e8659a6780539c6, 0xb17c48097161d796,
|
||||
0x026015213acbd6e2, 0xd1ae9f77e515e901, 0xb7dc776a3f21b0ad, 0xaba6a1b96eb78098, 0x9bcf4486248d9f5d, 0x582666c536455efd, 0xfdbdac9bfeb9c6f1, 0xc47999be4163cdea,
|
||||
0x765540081722a7ef, 0x3e548ed8ec710751, 0x3d041f67cb51bac2, 0x7958af71ac82d40a, 0x36c9da5c047a78fe, 0xed9a048e33af38b2, 0x26ee7249c96c86bd, 0x900281bdeba65d61,
|
||||
0x11172c8bd0fd9532, 0xea0abf73600434f8, 0x42fc8f75299309f3, 0x34a9cf7d3eb1ae1c, 0x2b838811480723ba, 0x5ce64c8742ceef24, 0x1adae9b01fd6570e, 0x3c349bf9d6bad1b3,
|
||||
0x82453c891c7b75c0, 0x97923a40b80d512b, 0x4a61dbf1c198765c, 0xb48ce6d518010d3e, 0xcfb45c858e480fd6, 0xd933cbf30d1e96ae, 0xd70ea014ab558e3a, 0xc189376228031742,
|
||||
0x9262949cd16d8b83, 0xeb3a3bed7def5f89, 0x49314a4ee6b8cbcf, 0xdcc3652f647e4c06, 0xda635a4c2a3e2b3d, 0x470c21a940f3d35b, 0x315961a157d174b4, 0x6672e81dda3459ac,
|
||||
0x5b76f77a1165e36e, 0x445cb01667d36ec8, 0xc5491d205c88a69b, 0x456c34887a3805b9, 0xffddb9bac4721013, 0x99af51a71e4649bf, 0xa15be01cbc7729d5, 0x52db2760e485f7b0,
|
||||
0x8c78576eba306d54, 0xae560f6507d75a30, 0x95f22f6182c687c9, 0x71c5fbf54489aba5, 0xca44f259e728d57e, 0x88b87d2ccebbdc8d, 0xbab18d32be4a15aa, 0x8be8ec93e99b611e,
|
||||
0x17b713e89ebdf209, 0xb31c5d284baa0174, 0xeeca9531148f8521, 0xb8d198138481c348, 0x8988f9b2d350b7fc, 0xb9e11c8d996aa839, 0x5a4673e40c8e881f, 0x1687977683569978,
|
||||
0xbf4123eed72acf02, 0x4ea1f1b3b513c785, 0xe767452be16f91ff, 0x7505d1b730021a7c, 0xa59bca5ec8fc980c, 0xad069eda20f7e7a3, 0x38f4b1bba231606a, 0x60d2d77e94743e97,
|
||||
0x9affc0183966f42c, 0x248e6768f3a7505f, 0xcdd449a4b483d934, 0x87b59255751baf68, 0x1bea6d2e023d3c7f, 0x6b1f12455b5ffcab, 0x743555292de9710d, 0xd8034f6d10f5fddf,
|
||||
0xc6198c9f7ba81b08, 0xbb8109aca3a17edb, 0xfa2d1766ad12cabb, 0xc729080166437079, 0x9c5fff7b77269317, 0x0000000000000000, 0x15d706c9a47624eb, 0x6fdf38072fd44d72,
|
||||
0x5fb6dd3865ee52b7, 0xa33bf53d86bcff37, 0xe657c1b5fc84fa8e, 0xaa962527735cebe9, 0x39c43525bfda0b1b, 0x204e4d2a872ce186, 0x7a083ece8ba26999, 0x554b9c9db72efbfa,
|
||||
0xb22cd9b656416a05, 0x96a2bedea5e63a5a, 0x802529a826b0a322, 0x8115ad363b5bc853, 0x8375b81701901eb1, 0x3069e53f4a3a1fc5, 0xbd2136cfede119e0, 0x18bafc91251d81ec,
|
||||
0x1d4a524d4c7d5b44, 0x05f0aedc6960daa8, 0x29e39d3072ccf558, 0x70f57f6b5962c0d4, 0x989fd53903ad22ce, 0xf84d024797d91c59, 0x547b1803aac5908b, 0xf0d056c37fd263f6,
|
||||
0xd56eb535919e58d8, 0x1c7ad6d351963035, 0x2e7326cd2167f912, 0xac361a443d1c8cd2, 0x697f076461942a49, 0x4b515f6fdc731d2d, 0x8ad8680df4700a6f, 0x41ac1eca0eb3b460,
|
||||
0x7d988533d80965d3, 0xa8f6300649973d0b, 0x7765c4960ac9cc9e, 0x7ca801adc5e20ea2, 0xdea3700e5eb59ae4, 0xa06b6482a19c42a4, 0x6a2f96db46b497da, 0x27def6d7d487edcc,
|
||||
0x463ca5375d18b82a, 0xa6cb5be1efdc259f, 0x53eba3fef96e9cc1, 0xce84d81b93a364a7, 0xf4107c810b59d22f, 0x333974806d1aa256, 0x0f0def79bba073e5, 0x231edc95a00c5c15,
|
||||
0xe437d494c64f2c6c, 0x91320523f64d3610, 0x67426c83c7df32dd, 0x6eefbc99323f2603, 0x9d6f7be56acdf866, 0x5916e25b2bae358c, 0x7ff89012e2c2b331, 0x035091bf2720bd93,
|
||||
0x561b0d22900e4669, 0x28d319ae6f279e29, 0x2f43a2533c8c9263, 0xd09e1be9f8fe8270, 0xf740ed3e2c796fbc, 0xdb53ded237d5404c, 0x62b2c25faebfe875, 0x0afd41a5d2c0a94d,
|
||||
0x6412fd3ce0ff8f4e, 0xe3a76f6995e42026, 0x6c8fa9b808f4f0e1, 0xc2d9a6dd0f23aad1, 0x8f28c6d19d10d0c7, 0x85d587744fd0798a, 0xa20b71a39b579446, 0x684f83fa7c7f4138,
|
||||
0xe507500adba4471d, 0x3f640a46f19a6c20, 0x1247bd34f7dd28a1, 0x2d23b77206474481, 0x93521002cc86e0f2, 0x572b89bc8de52d18, 0xfb1d93f8b0f9a1ca, 0xe95a2ecc4724896b,
|
||||
0x3ba420048511ddf9, 0xd63e248ab6bee54b, 0x5dd6c8195f258455, 0x06a03f634e40673b, 0x1f2a476c76b68da6, 0x217ec9b49ac78af7, 0xecaa80102e4453c3, 0x14e78257b99d4f9a
|
||||
},
|
||||
{
|
||||
0x20329b2cc87bba05, 0x4f5eb6f86546a531, 0xd4f44775f751b6b1, 0x8266a47b850dfa8b, 0xbb986aa15a6ca985, 0xc979eb08f9ae0f99, 0x2da6f447a2375ea1, 0x1e74275dcd7d8576,
|
||||
0xbc20180a800bc5f8, 0xb4a2f701b2dc65be, 0xe726946f981b6d66, 0x48e6c453bf21c94c, 0x42cad9930f0a4195, 0xefa47b64aacccd20, 0x71180a8960409a42, 0x8bb3329bf6a44e0c,
|
||||
0xd34c35de2d36dacc, 0xa92f5b7cbc23dc96, 0xb31a85aa68bb09c3, 0x13e04836a73161d2, 0xb24dfc4129c51d02, 0x8ae44b70b7da5acd, 0xe671ed84d96579a7, 0xa4bb3417d66f3832,
|
||||
0x4572ab38d56d2de8, 0xb1b47761ea47215c, 0xe81c09cf70aba15d, 0xffbdb872ce7f90ac, 0xa8782297fd5dc857, 0x0d946f6b6a4ce4a4, 0xe4df1f4f5b995138, 0x9ebc71edca8c5762,
|
||||
0x0a2c1dc0b02b88d9, 0x3b503c115d9d7b91, 0xc64376a8111ec3a2, 0xcec199a323c963e4, 0xdc76a87ec58616f7, 0x09d596e073a9b487, 0x14583a9d7d560daf, 0xf4c6dc593f2a0cb4,
|
||||
0xdd21d19584f80236, 0x4a4836983ddde1d3, 0xe58866a41ae745f9, 0xf591a5b27e541875, 0x891dc05074586693, 0x5b068c651810a89e, 0xa30346bc0c08544f, 0x3dbf3751c684032d,
|
||||
0x2a1e86ec785032dc, 0xf73f5779fca830ea, 0xb60c05ca30204d21, 0x0cc316802b32f065, 0x8770241bdd96be69, 0xb861e18199ee95db, 0xf805cad91418fcd1, 0x29e70dccbbd20e82,
|
||||
0xc7140f435060d763, 0x0f3a9da0e8b0cc3b, 0xa2543f574d76408e, 0xbd7761e1c175d139, 0x4b1f4f737ca3f512, 0x6dc2df1f2fc137ab, 0xf1d05c3967b14856, 0xa742bf3715ed046c,
|
||||
0x654030141d1697ed, 0x07b872abda676c7d, 0x3ce84eba87fa17ec, 0xc1fb0403cb79afdf, 0x3e46bc7105063f73, 0x278ae987121cd678, 0xa1adb4778ef47cd0, 0x26dd906c5362c2b9,
|
||||
0x05168060589b44e2, 0xfbfc41f9d79ac08f, 0x0e6de44ba9ced8fa, 0x9feb08068bf243a3, 0x7b341749d06b129b, 0x229c69e74a87929a, 0xe09ee6c4427c011b, 0x5692e30e725c4c3a,
|
||||
0xda99a33e5e9f6e4b, 0x353dd85af453a36b, 0x25241b4c90e0fee7, 0x5de987258309d022, 0xe230140fc0802984, 0x93281e86a0c0b3c6, 0xf229d719a4337408, 0x6f6c2dd4ad3d1f34,
|
||||
0x8ea5b2fbae3f0aee, 0x8331dd90c473ee4a, 0x346aa1b1b52db7aa, 0xdf8f235e06042aa9, 0xcc6f6b68a1354b7b, 0x6c95a6f46ebf236a, 0x52d31a856bb91c19, 0x1a35ded6d498d555,
|
||||
0xf37eaef2e54d60c9, 0x72e181a9a3c2a61c, 0x98537aad51952fde, 0x16f6c856ffaa2530, 0xd960281e9d1d5215, 0x3a0745fa1ce36f50, 0x0b7b642bf1559c18, 0x59a87eae9aec8001,
|
||||
0x5e100c05408bec7c, 0x0441f98b19e55023, 0xd70dcc5534d38aef, 0x927f676de1bea707, 0x9769e70db925e3e5, 0x7a636ea29115065a, 0x468b201816ef11b6, 0xab81a9b73edff409,
|
||||
0xc0ac7de88a07bb1e, 0x1f235eb68c0391b7, 0x6056b074458dd30f, 0xbe8eeac102f7ed67, 0xcd381283e04b5fba, 0x5cbefecec277c4e3, 0xd21b4c356c48ce0d, 0x1019c31664b35d8c,
|
||||
0x247362a7d19eea26, 0xebe582efb3299d03, 0x02aef2cb82fc289f, 0x86275df09ce8aaa8, 0x28b07427faac1a43, 0x38a9b7319e1f47cf, 0xc82e92e3b8d01b58, 0x06ef0b409b1978bc,
|
||||
0x62f842bfc771fb90, 0x9904034610eb3b1f, 0xded85ab5477a3e68, 0x90d195a663428f98, 0x5384636e2ac708d8, 0xcbd719c37b522706, 0xae9729d76644b0eb, 0x7c8c65e20a0c7ee6,
|
||||
0x80c856b007f1d214, 0x8c0b40302cc32271, 0xdbcedad51fe17a8a, 0x740e8ae938dbdea0, 0xa615c6dc549310ad, 0x19cc55f6171ae90b, 0x49b1bdb8fe5fdd8d, 0xed0a89af2830e5bf,
|
||||
0x6a7aadb4f5a65bd6, 0x7e22972988f05679, 0xf952b3325566e810, 0x39fecedadf61530e, 0x6101c99f04f3c7ce, 0x2e5f7f6761b562ff, 0xf08725d226cf5c97, 0x63af3b54860fef51,
|
||||
0x8ff2cb10ef411e2f, 0x884ab9bb35267252, 0x4df04433e7ba8dae, 0x9afd8866d3690741, 0x66b9bb34de94abb3, 0x9baaf18d92171380, 0x543c11c5f0a064a5, 0x17a1b1bdbed431f1,
|
||||
0xb5f58eeaf3a2717f, 0xc355f6c849858740, 0xec5df044694ef17e, 0xd83751f5dc6346d4, 0xfc4433520dfdacf2, 0x0000000000000000, 0x5a51f58e596ebc5f, 0x3285aaf12e34cf16,
|
||||
0x8d5c39db6dbd36b0, 0x12b731dde64f7513, 0x94906c2d7aa7dfbb, 0x302b583aacc8e789, 0x9d45facd090e6b3c, 0x2165e2c78905aec4, 0x68d45f7f775a7349, 0x189b2c1d5664fdca,
|
||||
0xe1c99f2f030215da, 0x6983269436246788, 0x8489af3b1e148237, 0xe94b702431d5b59c, 0x33d2d31a6f4adbd7, 0xbfd9932a4389f9a6, 0xb0e30e8aab39359d, 0xd1e2c715afcaf253,
|
||||
0x150f43763c28196e, 0xc4ed846393e2eb3d, 0x03f98b20c3823c5e, 0xfd134ab94c83b833, 0x556b682eb1de7064, 0x36c4537a37d19f35, 0x7559f30279a5ca61, 0x799ae58252973a04,
|
||||
0x9c12832648707ffd, 0x78cd9c6913e92ec5, 0x1d8dac7d0effb928, 0x439da0784e745554, 0x413352b3cc887dcb, 0xbacf134a1b12bd44, 0x114ebafd25cd494d, 0x2f08068c20cb763e,
|
||||
0x76a07822ba27f63f, 0xeab2fb04f25789c2, 0xe3676de481fe3d45, 0x1b62a73d95e6c194, 0x641749ff5c68832c, 0xa5ec4dfc97112cf3, 0xf6682e92bdd6242b, 0x3f11c59a44782bb2,
|
||||
0x317c21d1edb6f348, 0xd65ab5be75ad9e2e, 0x6b2dd45fb4d84f17, 0xfaab381296e4d44e, 0xd0b5befeeeb4e692, 0x0882ef0b32d7a046, 0x512a91a5a83b2047, 0x963e9ee6f85bf724,
|
||||
0x4e09cf132438b1f0, 0x77f701c9fb59e2fe, 0x7ddb1c094b726a27, 0x5f4775ee01f5f8bd, 0x9186ec4d223c9b59, 0xfeeac1998f01846d, 0xac39db1ce4b89874, 0xb75b7c21715e59e0,
|
||||
0xafc0503c273aa42a, 0x6e3b543fec430bf5, 0x704f7362213e8e83, 0x58ff0745db9294c0, 0x67eec2df9feabf72, 0xa0facd9ccf8a6811, 0xb936986ad890811a, 0x95c715c63bd9cb7a,
|
||||
0xca8060283a2c33c7, 0x507de84ee9453486, 0x85ded6d05f6a96f6, 0x1cdad5964f81ade9, 0xd5a33e9eb62fa270, 0x40642b588df6690a, 0x7f75eec2c98e42b8, 0x2cf18dace3494a60,
|
||||
0x23cb100c0bf9865b, 0xeef3028febb2d9e1, 0x4425d2d394133929, 0xaad6d05c7fa1e0c8, 0xad6ea2f7a5c68cb5, 0xc2028f2308fb9381, 0x819f2f5b468fc6d5, 0xc5bafd88d29cfffc,
|
||||
0x47dc59f357910577, 0x2b49ff07392e261d, 0x57c59ae5332258fb, 0x73b6f842e2bcb2dd, 0xcf96e04862b77725, 0x4ca73dd8a6c4996f, 0x015779eb417e14c1, 0x37932a9176af8bf4
|
||||
},
|
||||
{
|
||||
0x190a2c9b249df23e, 0x2f62f8b62263e1e9, 0x7a7f754740993655, 0x330b7ba4d5564d9f, 0x4c17a16a46672582, 0xb22f08eb7d05f5b8, 0x535f47f40bc148cc, 0x3aec5d27d4883037,
|
||||
0x10ed0a1825438f96, 0x516101f72c233d17, 0x13cc6f949fd04eae, 0x739853c441474bfd, 0x653793d90d3f5b1b, 0x5240647b96b0fc2f, 0x0c84890ad27623e0, 0xd7189b32703aaea3,
|
||||
0x2685de3523bd9c41, 0x99317c5b11bffefa, 0x0d9baa854f079703, 0x70b93648fbd48ac5, 0xa80441fce30bc6be, 0x7287704bdc36ff1e, 0xb65384ed33dc1f13, 0xd36417343ee34408,
|
||||
0x39cd38ab6e1bf10f, 0x5ab861770a1f3564, 0x0ebacf09f594563b, 0xd04572b884708530, 0x3cae9722bdb3af47, 0x4a556b6f2f5cbaf2, 0xe1704f1f76c4bd74, 0x5ec4ed7144c6dfcf,
|
||||
0x16afc01d4c7810e6, 0x283f113cd629ca7a, 0xaf59a8761741ed2d, 0xeed5a3991e215fac, 0x3bf37ea849f984d4, 0xe413e096a56ce33c, 0x2c439d3a98f020d1, 0x637559dc6404c46b,
|
||||
0x9e6c95d1e5f5d569, 0x24bb9836045fe99a, 0x44efa466dac8ecc9, 0xc6eab2a5c80895d6, 0x803b50c035220cc4, 0x0321658cba93c138, 0x8f9ebc465dc7ee1c, 0xd15a5137190131d3,
|
||||
0x0fa5ec8668e5e2d8, 0x91c979578d1037b1, 0x0642ca05693b9f70, 0xefca80168350eb4f, 0x38d21b24f36a45ec, 0xbeab81e1af73d658, 0x8cbfd9cae7542f24, 0xfd19cc0d81f11102,
|
||||
0x0ac6430fbb4dbc90, 0x1d76a09d6a441895, 0x2a01573ff1cbbfa1, 0xb572e161894fde2b, 0x8124734fa853b827, 0x614b1fdf43e6b1b0, 0x68ac395c4238cc18, 0x21d837bfd7f7b7d2,
|
||||
0x20c714304a860331, 0x5cfaab726324aa14, 0x74c5ba4eb50d606e, 0xf3a3030474654739, 0x23e671bcf015c209, 0x45f087e947b9582a, 0xd8bd77b418df4c7b, 0xe06f6c90ebb50997,
|
||||
0x0bd96080263c0873, 0x7e03f9410e40dcfe, 0xb8e94be4c6484928, 0xfb5b0608e8ca8e72, 0x1a2b49179e0e3306, 0x4e29e76961855059, 0x4f36c4e6fcf4e4ba, 0x49740ee395cf7bca,
|
||||
0xc2963ea386d17f7d, 0x90d65ad810618352, 0x12d34c1b02a1fa4d, 0xfa44258775bb3a91, 0x18150f14b9ec46dd, 0x1491861e6b9a653d, 0x9a1019d7ab2c3fc2, 0x3668d42d06fe13d7,
|
||||
0xdcc1fbb25606a6d0, 0x969490dd795a1c22, 0x3549b1a1bc6dd2ef, 0xc94f5e23a0ed770e, 0xb9f6686b5b39fdcb, 0xc4d4f4a6efeae00d, 0xe732851a1fff2204, 0x94aad6de5eb869f9,
|
||||
0x3f8ff2ae07206e7f, 0xfe38a9813b62d03a, 0xa7a1ad7a8bee2466, 0x7b6056c8dde882b6, 0x302a1e286fc58ca7, 0x8da0fa457a259bc7, 0xb3302b64e074415b, 0x5402ae7eff8b635f,
|
||||
0x08f8050c9cafc94b, 0xae468bf98a3059ce, 0x88c355cca98dc58f, 0xb10e6d67c7963480, 0xbad70de7e1aa3cf3, 0xbfb4a26e320262bb, 0xcb711820870f02d5, 0xce12b7a954a75c9d,
|
||||
0x563ce87dd8691684, 0x9f73b65e7884618a, 0x2b1e74b06cba0b42, 0x47cec1ea605b2df1, 0x1c698312f735ac76, 0x5fdbcefed9b76b2c, 0x831a354c8fb1cdfc, 0x820516c312c0791f,
|
||||
0xb74ca762aeadabf0, 0xfc06ef821c80a5e1, 0x5723cbf24518a267, 0x9d4df05d5f661451, 0x588627742dfd40bf, 0xda8331b73f3d39a0, 0x17b0e392d109a405, 0xf965400bcf28fba9,
|
||||
0x7c3dbf4229a2a925, 0x023e460327e275db, 0x6cd0b55a0ce126b3, 0xe62da695828e96e7, 0x42ad6e63b3f373b9, 0xe50cc319381d57df, 0xc5cbd729729b54ee, 0x46d1e265fd2a9912,
|
||||
0x6428b056904eeff8, 0x8be23040131e04b7, 0x6709d5da2add2ec0, 0x075de98af44a2b93, 0x8447dcc67bfbe66f, 0x6616f655b7ac9a23, 0xd607b8bded4b1a40, 0x0563af89d3a85e48,
|
||||
0x3db1b4ad20c21ba4, 0x11f22997b8323b75, 0x292032b34b587e99, 0x7f1cdace9331681d, 0x8e819fc9c0b65aff, 0xa1e3677fe2d5bb16, 0xcd33d225ee349da5, 0xd9a2543b85aef898,
|
||||
0x795e10cbfa0af76d, 0x25a4bbb9992e5d79, 0x78413344677b438e, 0xf0826688cef68601, 0xd27b34bba392f0eb, 0x551d8df162fad7bc, 0x1e57c511d0d7d9ad, 0xdeffbdb171e4d30b,
|
||||
0xf4feea8e802f6caa, 0xa480c8f6317de55e, 0xa0fc44f07fa40ff5, 0x95b5f551c3c9dd1a, 0x22f952336d6476ea, 0x0000000000000000, 0xa6be8ef5169f9085, 0xcc2cf1aa73452946,
|
||||
0x2e7ddb39bf12550a, 0xd526dd3157d8db78, 0x486b2d6c08becf29, 0x9b0f3a58365d8b21, 0xac78cdfaadd22c15, 0xbc95c7e28891a383, 0x6a927f5f65dab9c3, 0xc3891d2c1ba0cb9e,
|
||||
0xeaa92f9f50f8b507, 0xcf0d9426c9d6e87e, 0xca6e3baf1a7eb636, 0xab25247059980786, 0x69b31ad3df4978fb, 0xe2512a93cc577c4c, 0xff278a0ea61364d9, 0x71a615c766a53e26,
|
||||
0x89dc764334fc716c, 0xf87a638452594f4a, 0xf2bc208be914f3da, 0x8766b94ac1682757, 0xbbc82e687cdb8810, 0x626a7a53f9757088, 0xa2c202f358467a2e, 0x4d0882e5db169161,
|
||||
0x09e7268301de7da8, 0xe897699c771ac0dc, 0xc8507dac3d9cc3ed, 0xc0a878a0a1330aa6, 0x978bb352e42ba8c1, 0xe9884a13ea6b743f, 0x279afdbabecc28a2, 0x047c8c064ed9eaab,
|
||||
0x507e2278b15289f4, 0x599904fbb08cf45c, 0xbd8ae46d15e01760, 0x31353da7f2b43844, 0x8558ff49e68a528c, 0x76fbfc4d92ef15b5, 0x3456922e211c660c, 0x86799ac55c1993b4,
|
||||
0x3e90d1219a51da9c, 0x2d5cbeb505819432, 0x982e5fd48cce4a19, 0xdb9c1238a24c8d43, 0xd439febecaa96f9b, 0x418c0bef0960b281, 0x158ea591f6ebd1de, 0x1f48e69e4da66d4e,
|
||||
0x8afd13cf8e6fb054, 0xf5e1c9011d5ed849, 0xe34e091c5126c8af, 0xad67ee7530a398f6, 0x43b24dec2e82c75a, 0x75da99c1287cd48d, 0x92e81cdb3783f689, 0xa3dd217cc537cecd,
|
||||
0x60543c50de970553, 0x93f73f54aaf2426a, 0xa91b62737e7a725d, 0xf19d4507538732e2, 0x77e4dfc20f9ea156, 0x7d229ccdb4d31dc6, 0x1b346a98037f87e5, 0xedf4c615a4b29e94,
|
||||
0x4093286094110662, 0xb0114ee85ae78063, 0x6ff1d0d6b672e78b, 0x6dcf96d591909250, 0xdfe09e3eec9567e8, 0x3214582b4827f97c, 0xb46dc2ee143e6ac8, 0xf6c0ac8da7cd1971,
|
||||
0xebb60c10cd8901e4, 0xf7df8f023abcad92, 0x9c52d3d2c217a0b2, 0x6b8d5cd0f8ab0d20, 0x3777f7a29b8fa734, 0x011f238f9d71b4e3, 0xc1b75b2f3c42be45, 0x5de588fdfe551ef7,
|
||||
0x6eeef3592b035368, 0xaa3a07ffc4e9b365, 0xecebe59a39c32a77, 0x5ba742f8976e8187, 0x4b4a48e0b22d0e11, 0xddded83dcb771233, 0xa59feb79ac0c51bd, 0xc7f5912a55792135
|
||||
},
|
||||
{
|
||||
0x6d6ae04668a9b08a, 0x3ab3f04b0be8c743, 0xe51e166b54b3c908, 0xbe90a9eb35c2f139, 0xb2c7066637f2bec1, 0xaa6945613392202c, 0x9a28c36f3b5201eb, 0xddce5a93ab536994,
|
||||
0x0e34133ef6382827, 0x52a02ba1ec55048b, 0xa2f88f97c4b2a177, 0x8640e513ca2251a5, 0xcdf1d36258137622, 0xfe6cb708dedf8ddb, 0x8a174a9ec8121e5d, 0x679896036b81560e,
|
||||
0x59ed033395795fee, 0x1dd778ab8b74edaf, 0xee533ef92d9f926d, 0x2a8c79baf8a8d8f5, 0x6bcf398e69b119f6, 0xe20491742fafdd95, 0x276488e0809c2aec, 0xea955b82d88f5cce,
|
||||
0x7102c63a99d9e0c4, 0xf9763017a5c39946, 0x429fa2501f151b3d, 0x4659c72bea05d59e, 0x984b7fdccf5a6634, 0xf742232953fbb161, 0x3041860e08c021c7, 0x747bfd9616cd9386,
|
||||
0x4bb1367192312787, 0x1b72a1638a6c44d3, 0x4a0e68a6e8359a66, 0x169a5039f258b6ca, 0xb98a2ef44edee5a4, 0xd9083fe85e43a737, 0x967f6ce239624e13, 0x8874f62d3c1a7982,
|
||||
0x3c1629830af06e3f, 0x9165ebfd427e5a8e, 0xb5dd81794ceeaa5c, 0x0de8f15a7834f219, 0x70bd98ede3dd5d25, 0xaccc9ca9328a8950, 0x56664eda1945ca28, 0x221db34c0f8859ae,
|
||||
0x26dbd637fa98970d, 0x1acdffb4f068f932, 0x4585254f64090fa0, 0x72de245e17d53afa, 0x1546b25d7c546cf4, 0x207e0ffffb803e71, 0xfaaad2732bcf4378, 0xb462dfae36ea17bd,
|
||||
0xcf926fd1ac1b11fd, 0xe0672dc7dba7ba4a, 0xd3fa49ad5d6b41b3, 0x8ba81449b216a3bc, 0x14f9ec8a0650d115, 0x40fc1ee3eb1d7ce2, 0x23a2ed9b758ce44f, 0x782c521b14fddc7e,
|
||||
0x1c68267cf170504e, 0xbcf31558c1ca96e6, 0xa781b43b4ba6d235, 0xf6fd7dfe29ff0c80, 0xb0a4bad5c3fad91e, 0xd199f51ea963266c, 0x414340349119c103, 0x5405f269ed4dadf7,
|
||||
0xabd61bb649969dcd, 0x6813dbeae7bdc3c8, 0x65fb2ab09f8931d1, 0xf1e7fae152e3181d, 0xc1a67cef5a2339da, 0x7a4feea8e0f5bba1, 0x1e0b9acf05783791, 0x5b8ebf8061713831,
|
||||
0x80e53cdbcb3af8d9, 0x7e898bd315e57502, 0xc6bcfbf0213f2d47, 0x95a38e86b76e942d, 0x092e94218d243cba, 0x8339debf453622e7, 0xb11be402b9fe64ff, 0x57d9100d634177c9,
|
||||
0xcc4e8db52217cbc3, 0x3b0cae9c71ec7aa2, 0xfb158ca451cbfe99, 0x2b33276d82ac6514, 0x01bf5ed77a04bde1, 0xc5601994af33f779, 0x75c4a3416cc92e67, 0xf3844652a6eb7fc2,
|
||||
0x3487e375fdd0ef64, 0x18ae430704609eed, 0x4d14efb993298efb, 0x815a620cb13e4538, 0x125c354207487869, 0x9eeea614ce42cf48, 0xce2d3106d61fac1c, 0xbbe99247bad6827b,
|
||||
0x071a871f7b1c149d, 0x2e4a1cc10db81656, 0x77a71ff298c149b8, 0x06a5d9c80118a97c, 0xad73c27e488e34b1, 0x443a7b981e0db241, 0xe3bbcfa355ab6074, 0x0af276450328e684,
|
||||
0x73617a896dd1871b, 0x58525de4ef7de20f, 0xb7be3dcab8e6cd83, 0x19111dd07e64230c, 0x842359a03e2a367a, 0x103f89f1f3401fb6, 0xdc710444d157d475, 0xb835702334da5845,
|
||||
0x4320fc876511a6dc, 0xd026abc9d3679b8d, 0x17250eee885c0b2b, 0x90dab52a387ae76f, 0x31fed8d972c49c26, 0x89cba8fa461ec463, 0x2ff5421677bcabb7, 0x396f122f85e41d7d,
|
||||
0xa09b332430bac6a8, 0xc888e8ced7070560, 0xaeaf201ac682ee8f, 0x1180d7268944a257, 0xf058a43628e7a5fc, 0xbd4c4b8fbbce2b07, 0xa1246df34abe7b49, 0x7d5569b79be9af3c,
|
||||
0xa9b5a705bd9efa12, 0xdb6b835baa4bc0e8, 0x05793bac8f147342, 0x21c1512881848390, 0xfdb0556c50d357e5, 0x613d4fcb6a99ff72, 0x03dce2648e0cda3e, 0xe949b9e6568386f0,
|
||||
0xfc0f0bbb2ad7ea04, 0x6a70675913b5a417, 0x7f36d5046fe1c8e3, 0x0c57af8d02304ff8, 0x32223abdfcc84618, 0x0891caf6f720815b, 0xa63eeaec31a26fd4, 0x2507345374944d33,
|
||||
0x49d28ac266394058, 0xf5219f9aa7f3d6be, 0x2d96fea583b4cc68, 0x5a31e1571b7585d0, 0x8ed12fe53d02d0fe, 0xdfade6205f5b0e4b, 0x4cabb16ee92d331a, 0x04c6657bf510cea3,
|
||||
0xd73c2cd6a87b8f10, 0xe1d87310a1a307ab, 0x6cd5be9112ad0d6b, 0x97c032354366f3f2, 0xd4e0ceb22677552e, 0x0000000000000000, 0x29509bde76a402cb, 0xc27a9e8bd42fe3e4,
|
||||
0x5ef7842cee654b73, 0xaf107ecdbc86536e, 0x3fcacbe784fcb401, 0xd55f90655c73e8cf, 0xe6c2f40fdabf1336, 0xe8f6e7312c873b11, 0xeb2a0555a28be12f, 0xe4a148bc2eb774e9,
|
||||
0x9b979db84156bc0a, 0x6eb60222e6a56ab4, 0x87ffbbc4b026ec44, 0xc703a5275b3b90a6, 0x47e699fc9001687f, 0x9c8d1aa73a4aa897, 0x7cea3760e1ed12dd, 0x4ec80ddd1d2554c5,
|
||||
0x13e36b957d4cc588, 0x5d2b66486069914d, 0x92b90999cc7280b0, 0x517cc9c56259deb5, 0xc937b619ad03b881, 0xec30824ad997f5b2, 0xa45d565fc5aa080b, 0xd6837201d27f32f1,
|
||||
0x635ef3789e9198ad, 0x531f75769651b96a, 0x4f77530a6721e924, 0x486dd4151c3dfdb9, 0x5f48dafb9461f692, 0x375b011173dc355a, 0x3da9775470f4d3de, 0x8d0dcd81b30e0ac0,
|
||||
0x36e45fc609d888bb, 0x55baacbe97491016, 0x8cb29356c90ab721, 0x76184125e2c5f459, 0x99f4210bb55edbd5, 0x6f095cf59ca1d755, 0x9f51f8c3b44672a9, 0x3538bda287d45285,
|
||||
0x50c39712185d6354, 0xf23b1885dcefc223, 0x79930ccc6ef9619f, 0xed8fdc9da3934853, 0xcb540aaa590bdf5e, 0x5c94389f1a6d2cac, 0xe77daad8a0bbaed7, 0x28efc5090ca0bf2a,
|
||||
0xbf2ff73c4fc64cd8, 0xb37858b14df60320, 0xf8c96ec0dfc724a7, 0x828680683f329f06, 0x941cd051cd6a29cc, 0xc3c5c05cae2b5e05, 0xb601631dc2e27062, 0xc01922382027843b,
|
||||
0x24b86a840e90f0d2, 0xd245177a276ffc52, 0x0f8b4de98c3c95c6, 0x3e759530fef809e0, 0x0b4d2892792c5b65, 0xc4df4743d5374a98, 0xa5e20888bfaeb5ea, 0xba56cc90c0d23f9a,
|
||||
0x38d04cf8ffe0a09c, 0x62e1adafe495254c, 0x0263bcb3f40867df, 0xcaeb547d230f62bf, 0x6082111c109d4293, 0xdad4dd8cd04f7d09, 0xefec602e579b2f8c, 0x1fb4c4187f7c8a70,
|
||||
0xffd3e9dfa4db303a, 0x7bf0b07f9af10640, 0xf49ec14dddf76b5f, 0x8f6e713247066d1f, 0x339d646a86ccfbf9, 0x64447467e58d8c30, 0x2c29a072f9b07189, 0xd8b7613f24471ad6,
|
||||
0x6627c8d41185ebef, 0xa347d140beb61c96, 0xde12b8f7255fb3aa, 0x9d324470404e1576, 0x9306574eb6763d51, 0xa80af9d2c79a47f3, 0x859c0777442e8b9b, 0x69ac853d9db97e29
|
||||
},
|
||||
{
|
||||
0xc3407dfc2de6377e, 0x5b9e93eea4256f77, 0xadb58fdd50c845e0, 0x5219ff11a75bed86, 0x356b61cfd90b1de9, 0xfb8f406e25abe037, 0x7a5a0231c0f60796, 0x9d3cd216e1f5020b,
|
||||
0x0c6550fb6b48d8f3, 0xf57508c427ff1c62, 0x4ad35ffa71cb407d, 0x6290a2da1666aa6d, 0xe284ec2349355f9f, 0xb3c307c53d7c84ec, 0x05e23c0468365a02, 0x190bac4d6c9ebfa8,
|
||||
0x94bbbee9e28b80fa, 0xa34fc777529cb9b5, 0xcc7b39f095bcd978, 0x2426addb0ce532e3, 0x7e79329312ce4fc7, 0xab09a72eebec2917, 0xf8d15499f6b9d6c2, 0x1a55b8babf8c895d,
|
||||
0xdb8add17fb769a85, 0xb57f2f368658e81b, 0x8acd36f18f3f41f6, 0x5ce3b7bba50f11d3, 0x114dcc14d5ee2f0a, 0xb91a7fcded1030e8, 0x81d5425fe55de7a1, 0xb6213bc1554adeee,
|
||||
0x80144ef95f53f5f2, 0x1e7688186db4c10c, 0x3b912965db5fe1bc, 0xc281715a97e8252d, 0x54a5d7e21c7f8171, 0x4b12535ccbc5522e, 0x1d289cefbea6f7f9, 0x6ef5f2217d2e729e,
|
||||
0xe6a7dc819b0d17ce, 0x1b94b41c05829b0e, 0x33d7493c622f711e, 0xdcf7f942fa5ce421, 0x600fba8b7f7a8ecb, 0x46b60f011a83988e, 0x235b898e0dcf4c47, 0x957ab24f588592a9,
|
||||
0x4354330572b5c28c, 0xa5f3ef84e9b8d542, 0x8c711e02341b2d01, 0x0b1874ae6a62a657, 0x1213d8e306fc19ff, 0xfe6d7c6a4d9dba35, 0x65ed868f174cd4c9, 0x88522ea0e6236550,
|
||||
0x899322065c2d7703, 0xc01e690bfef4018b, 0x915982ed8abddaf8, 0xbe675b98ec3a4e4c, 0xa996bf7f82f00db1, 0xe1daf8d49a27696a, 0x2effd5d3dc8986e7, 0xd153a51f2b1a2e81,
|
||||
0x18caa0ebd690adfb, 0x390e3134b243c51a, 0x2778b92cdff70416, 0x029f1851691c24a6, 0x5e7cafeacc133575, 0xfa4e4cc89fa5f264, 0x5a5f9f481e2b7d24, 0x484c47ab18d764db,
|
||||
0x400a27f2a1a7f479, 0xaeeb9b2a83da7315, 0x721c626879869734, 0x042330a2d2384851, 0x85f672fd3765aff0, 0xba446b3a3e02061d, 0x73dd6ecec3888567, 0xffac70ccf793a866,
|
||||
0xdfa9edb5294ed2d4, 0x6c6aea7014325638, 0x834a5a0e8c41c307, 0xcdba35562fb2cb2b, 0x0ad97808d06cb404, 0x0f3b440cb85aee06, 0xe5f9c876481f213b, 0x98deee1289c35809,
|
||||
0x59018bbfcd394bd1, 0xe01bf47220297b39, 0xde68e1139340c087, 0x9fa3ca4788e926ad, 0xbb85679c840c144e, 0x53d8f3b71d55ffd5, 0x0da45c5dd146caa0, 0x6f34fe87c72060cd,
|
||||
0x57fbc315cf6db784, 0xcee421a1fca0fdde, 0x3d2d0196607b8d4b, 0x642c8a29ad42c69a, 0x14aff010bdd87508, 0xac74837beac657b3, 0x3216459ad821634d, 0x3fb219c70967a9ed,
|
||||
0x06bc28f3bb246cf7, 0xf2082c9126d562c6, 0x66b39278c45ee23c, 0xbd394f6f3f2878b9, 0xfd33689d9e8f8cc0, 0x37f4799eb017394f, 0x108cc0b26fe03d59, 0xda4bd1b1417888d6,
|
||||
0xb09d1332ee6eb219, 0x2f3ed975668794b4, 0x58c0871977375982, 0x7561463d78ace990, 0x09876cff037e82f1, 0x7fb83e35a8c05d94, 0x26b9b58a65f91645, 0xef20b07e9873953f,
|
||||
0x3148516d0b3355b8, 0x41cb2b541ba9e62a, 0x790416c613e43163, 0xa011d380818e8f40, 0x3a5025c36151f3ef, 0xd57095bdf92266d0, 0x498d4b0da2d97688, 0x8b0c3a57353153a5,
|
||||
0x21c491df64d368e1, 0x8f2f0af5e7091bf4, 0x2da1c1240f9bb012, 0xc43d59a92ccc49da, 0xbfa6573e56345c1f, 0x828b56a8364fd154, 0x9a41f643e0df7caf, 0xbcf843c985266aea,
|
||||
0x2b1de9d7b4bfdce5, 0x20059d79dedd7ab2, 0x6dabe6d6ae3c446b, 0x45e81bf6c991ae7b, 0x6351ae7cac68b83e, 0xa432e32253b6c711, 0xd092a9b991143cd2, 0xcac711032e98b58f,
|
||||
0xd8d4c9e02864ac70, 0xc5fc550f96c25b89, 0xd7ef8dec903e4276, 0x67729ede7e50f06f, 0xeac28c7af045cf3d, 0xb15c1f945460a04a, 0x9cfddeb05bfb1058, 0x93c69abce3a1fe5e,
|
||||
0xeb0380dc4a4bdd6e, 0xd20db1e8f8081874, 0x229a8528b7c15e14, 0x44291750739fbc28, 0xd3ccbd4e42060a27, 0xf62b1c33f4ed2a97, 0x86a8660ae4779905, 0xd62e814a2a305025,
|
||||
0x477703a7a08d8add, 0x7b9b0e977af815c5, 0x78c51a60a9ea2330, 0xa6adfb733aaae3b7, 0x97e5aa1e3199b60f, 0x0000000000000000, 0xf4b404629df10e31, 0x5564db44a6719322,
|
||||
0x9207961a59afec0d, 0x9624a6b88b97a45c, 0x363575380a192b1c, 0x2c60cd82b595a241, 0x7d272664c1dc7932, 0x7142769faa94a1c1, 0xa1d0df263b809d13, 0x1630e841d4c451ae,
|
||||
0xc1df65ad44fa13d8, 0x13d2d445bcf20bac, 0xd915c546926abe23, 0x38cf3d92084dd749, 0xe766d0272103059d, 0xc7634d5effde7f2f, 0x077d2455012a7ea4, 0xedbfa82ff16fb199,
|
||||
0xaf2a978c39d46146, 0x42953fa3c8bbd0df, 0xcb061da59496a7dc, 0x25e7a17db6eb20b0, 0x34aa6d6963050fba, 0xa76cf7d580a4f1e4, 0xf7ea10954ee338c4, 0xfcf2643b24819e93,
|
||||
0xcf252d0746aeef8d, 0x4ef06f58a3f3082c, 0x563acfb37563a5d7, 0x5086e740ce47c920, 0x2982f186dda3f843, 0x87696aac5e798b56, 0x5d22bb1d1f010380, 0x035e14f7d31236f5,
|
||||
0x3cec0d30da759f18, 0xf3c920379cdb7095, 0xb8db736b571e22bb, 0xdd36f5e44052f672, 0xaac8ab8851e23b44, 0xa857b3d938fe1fe2, 0x17f1e4e76eca43fd, 0xec7ea4894b61a3ca,
|
||||
0x9e62c6e132e734fe, 0xd4b1991b432c7483, 0x6ad6c283af163acf, 0x1ce9904904a8e5aa, 0x5fbda34c761d2726, 0xf910583f4cb7c491, 0xc6a241f845d06d7c, 0x4f3163fe19fd1a7f,
|
||||
0xe99c988d2357f9c8, 0x8eee06535d0709a7, 0x0efa48aa0254fc55, 0xb4be23903c56fa48, 0x763f52caabbedf65, 0xeee1bcd8227d876c, 0xe345e085f33b4dcc, 0x3e731561b369bbbe,
|
||||
0x2843fd2067adea10, 0x2adce5710eb1ceb6, 0xb7e03767ef44ccbd, 0x8db012a48e153f52, 0x61ceb62dc5749c98, 0xe85d942b9959eb9b, 0x4c6f7709caef2c8a, 0x84377e5b8d6bbda3,
|
||||
0x30895dcbb13d47eb, 0x74a04a9bc2a2fbc3, 0x6b17ce251518289c, 0xe438c4d0f2113368, 0x1fb784bed7bad35f, 0x9b80fae55ad16efc, 0x77fe5e6c11b0cd36, 0xc858095247849129,
|
||||
0x08466059b97090a2, 0x01c10ca6ba0e1253, 0x6988d6747c040c3a, 0x6849dad2c60a1e69, 0x5147ebe67449db73, 0xc99905f4fd8a837a, 0x991fe2b433cd4a5a, 0xf09734c04fc94660,
|
||||
0xa28ecbd1e892abe6, 0xf1563866f5c75433, 0x4dae7baf70e13ed9, 0x7ce62ac27bd26b61, 0x70837a39109ab392, 0x90988e4b30b3c8ab, 0xb2020b63877296bf, 0x156efcb607d6675b
|
||||
},
|
||||
{
|
||||
0xe63f55ce97c331d0, 0x25b506b0015bba16, 0xc8706e29e6ad9ba8, 0x5b43d3775d521f6a, 0x0bfa3d577035106e, 0xab95fc172afb0e66, 0xf64b63979e7a3276, 0xf58b4562649dad4b,
|
||||
0x48f7c3dbae0c83f1, 0xff31916642f5c8c5, 0xcbb048dc1c4a0495, 0x66b8f83cdf622989, 0x35c130e908e2b9b0, 0x7c761a61f0b34fa1, 0x3601161cf205268d, 0x9e54ccfe2219b7d6,
|
||||
0x8b7d90a538940837, 0x9cd403588ea35d0b, 0xbc3c6fea9ccc5b5a, 0xe5ff733b6d24aeed, 0xceed22de0f7eb8d2, 0xec8581cab1ab545e, 0xb96105e88ff8e71d, 0x8ca03501871a5ead,
|
||||
0x76ccce65d6db2a2f, 0x5883f582a7b58057, 0x3f7be4ed2e8adc3e, 0x0fe7be06355cd9c9, 0xee054e6c1d11be83, 0x1074365909b903a6, 0x5dde9f80b4813c10, 0x4a770c7d02b6692c,
|
||||
0x5379c8d5d7809039, 0xb4067448161ed409, 0x5f5e5026183bd6cd, 0xe898029bf4c29df9, 0x7fb63c940a54d09c, 0xc5171f897f4ba8bc, 0xa6f28db7b31d3d72, 0x2e4f3be7716eaa78,
|
||||
0x0d6771a099e63314, 0x82076254e41bf284, 0x2f0fd2b42733df98, 0x5c9e76d3e2dc49f0, 0x7aeb569619606cdb, 0x83478b07b2468764, 0xcfadcb8d5923cd32, 0x85dac7f05b95a41e,
|
||||
0xb5469d1b4043a1e9, 0xb821ecbbd9a592fd, 0x1b8e0b0e798c13c8, 0x62a57b6d9a0be02e, 0xfcf1b793b81257f8, 0x9d94ea0bd8fe28eb, 0x4cea408aeb654a56, 0x23284a47e888996c,
|
||||
0x2d8f1d128b893545, 0xf4cbac3132c0d8ab, 0xbd7c86b9ca912eba, 0x3a268eef3dbe6079, 0xf0d62f6077a9110c, 0x2735c916ade150cb, 0x89fd5f03942ee2ea, 0x1acee25d2fd16628,
|
||||
0x90f39bab41181bff, 0x430dfe8cde39939f, 0xf70b8ac4c8274796, 0x1c53aeaac6024552, 0x13b410acf35e9c9b, 0xa532ab4249faa24f, 0x2b1251e5625a163f, 0xd7e3e676da4841c7,
|
||||
0xa7b264e4e5404892, 0xda8497d643ae72d3, 0x861ae105a1723b23, 0x38a6414991048aa4, 0x6578dec92585b6b4, 0x0280cfa6acbaeadd, 0x88bdb650c273970a, 0x9333bd5ebbff84c2,
|
||||
0x4e6a8f2c47dfa08b, 0x321c954db76cef2a, 0x418d312a72837942, 0xb29b38bfffcdf773, 0x6c022c38f90a4c07, 0x5a033a240b0f6a8a, 0x1f93885f3ce5da6f, 0xc38a537e96988bc6,
|
||||
0x39e6a81ac759ff44, 0x29929e43cee0fce2, 0x40cdd87924de0ca2, 0xe9d8ebc8a29fe819, 0x0c2798f3cfbb46f4, 0x55e484223e53b343, 0x4650948ecd0d2fd8, 0x20e86cb2126f0651,
|
||||
0x6d42c56baf5739e7, 0xa06fc1405ace1e08, 0x7babbfc54f3d193b, 0x424d17df8864e67f, 0xd8045870ef14980e, 0xc6d7397c85ac3781, 0x21a885e1443273b1, 0x67f8116f893f5c69,
|
||||
0x24f5efe35706cff6, 0xd56329d076f2ab1a, 0x5e1eb9754e66a32d, 0x28d2771098bd8902, 0x8f6013f47dfdc190, 0x17a993fdb637553c, 0xe0a219397e1012aa, 0x786b9930b5da8606,
|
||||
0x6e82e39e55b0a6da, 0x875a0856f72f4ec3, 0x3741ff4fa458536d, 0xac4859b3957558fc, 0x7ef6d5c75c09a57c, 0xc04a758b6c7f14fb, 0xf9acdd91ab26ebbf, 0x7391a467c5ef9668,
|
||||
0x335c7c1ee1319aca, 0xa91533b18641e4bb, 0xe4bf9a683b79db0d, 0x8e20faa72ba0b470, 0x51f907737b3a7ae4, 0x2268a314bed5ec8c, 0xd944b123b949edee, 0x31dcb3b84d8b7017,
|
||||
0xd3fe65279f218860, 0x097af2f1dc8ffab3, 0x9b09a6fc312d0b91, 0xcc6ded78a3c4520f, 0x3481d9ba5ebfcc50, 0x4f2a667f1182d56b, 0xdfd9fdd4509ace94, 0x26752045fbbc252b,
|
||||
0xbffc491f662bc467, 0xdd593272fc202449, 0x3cbbc218d46d4303, 0x91b372f817456e1f, 0x681faf69bc6385a0, 0xb686bbeebaa43ed4, 0x1469b5084cd0ca01, 0x98c98009cbca94ac,
|
||||
0x6438379a73d8c354, 0xc2caba2dc0c5fe26, 0x3e3b0dbe78d7a9de, 0x50b9ee202d670f04, 0x4590b27b37eab0e5, 0x6025b4cb36b10af3, 0xfb2c1237079c0162, 0xa12f28130c936be8,
|
||||
0x4b37e52e54eb1ccc, 0x083a1ba28ad28f53, 0xc10a9cd83a22611b, 0x9f1425ad7444c236, 0x069d4cf7e9d3237a, 0xedc56899e7f621be, 0x778c273680865fcf, 0x309c5aeb1bd605f7,
|
||||
0x8de0dc52d1472b4d, 0xf8ec34c2fd7b9e5f, 0xea18cd3d58787724, 0xaad515447ca67b86, 0x9989695a9d97e14c, 0x0000000000000000, 0xf196c63321f464ec, 0x71116bc169557cb5,
|
||||
0xaf887f466f92c7c1, 0x972e3e0ffe964d65, 0x190ec4a8d536f915, 0x95aef1a9522ca7b8, 0xdc19db21aa7d51a9, 0x94ee18fa0471d258, 0x8087adf248a11859, 0xc457f6da2916dd5c,
|
||||
0xfa6cfb6451c17482, 0xf256e0c6db13fbd1, 0x6a9f60cf10d96f7d, 0x4daaa9d9bd383fb6, 0x03c026f5fae79f3d, 0xde99148706c7bb74, 0x2a52b8b6340763df, 0x6fc20acd03edd33a,
|
||||
0xd423c08320afdefa, 0xbbe1ca4e23420dc0, 0x966ed75ca8cb3885, 0xeb58246e0e2502c4, 0x055d6a021334bc47, 0xa47242111fa7d7af, 0xe3623fcc84f78d97, 0x81c744a11efc6db9,
|
||||
0xaec8961539cfb221, 0xf31609958d4e8e31, 0x63e5923ecc5695ce, 0x47107ddd9b505a38, 0xa3afe7b5a0298135, 0x792b7063e387f3e6, 0x0140e953565d75e0, 0x12f4f9ffa503e97b,
|
||||
0x750ce8902c3cb512, 0xdbc47e8515f30733, 0x1ed3610c6ab8af8f, 0x5239218681dde5d9, 0xe222d69fd2aaf877, 0xfe71783514a8bd25, 0xcaf0a18f4a177175, 0x61655d9860ec7f13,
|
||||
0xe77fbc9dc19e4430, 0x2ccff441ddd440a5, 0x16e97aaee06a20dc, 0xa855dae2d01c915b, 0x1d1347f9905f30b2, 0xb7c652bdecf94b34, 0xd03e43d265c6175d, 0xfdb15ec0ee4f2218,
|
||||
0x57644b8492e9599e, 0x07dda5a4bf8e569a, 0x54a46d71680ec6a3, 0x5624a2d7c4b42c7e, 0xbebca04c3076b187, 0x7d36f332a6ee3a41, 0x3b6667bc6be31599, 0x695f463aea3ef040,
|
||||
0xad08b0e0c3282d1c, 0xb15b1e4a052a684e, 0x44d05b2861b7c505, 0x15295c5b1a8dbfe1, 0x744c01c37a61c0f2, 0x59c31cd1f1e8f5b7, 0xef45a73f4b4ccb63, 0x6bdf899c46841a9d,
|
||||
0x3dfb2b4b823036e3, 0xa2ef0ee6f674f4d5, 0x184e2dfb836b8cf5, 0x1134df0a5fe47646, 0xbaa1231d751f7820, 0xd17eaa81339b62bd, 0xb01bf71953771dae, 0x849a2ea30dc8d1fe,
|
||||
0x705182923f080955, 0x0ea757556301ac29, 0x041d83514569c9a7, 0x0abad4042668658e, 0x49b72a88f851f611, 0x8a3d79f66ec97dd7, 0xcd2d042bf59927ef, 0xc930877ab0f0ee48,
|
||||
0x9273540deda2f122, 0xc797d02fd3f14261, 0xe1e2f06a284d674a, 0xd2be8c74c97cfd80, 0x9a494faf67707e71, 0xb3dbd1eca9908293, 0x72d14d3493b2e388, 0xd6a30f258c153427
|
||||
}
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
module std::hash::hmac{HashAlg, HASH_BYTES, BLOCK_BYTES};
|
||||
module std::hash::hmac <HashAlg, HASH_BYTES, BLOCK_BYTES>;
|
||||
import std::crypto;
|
||||
|
||||
struct Hmac
|
||||
|
||||
@@ -58,38 +58,38 @@ fn ulong hash(char[] data, ulong seed = 0)
|
||||
|
||||
if (@likely(data.len >= 8))
|
||||
{
|
||||
r1h ^= @unaligned_load(*(ulong*)data.ptr, 1);
|
||||
r1h ^= mem::load((ulong*)data.ptr, 1);
|
||||
|
||||
r2h ^= (data.len < 12)
|
||||
? ((data[data.len - 3] | ((ulong)data[data.len - 2] << 8) | ((ulong)data[data.len - 1] << 16) | ((ulong)1 << 24)) >> ((data.len * 8) ^ 88))
|
||||
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (128 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[8], 1));
|
||||
: (((mem::load((uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (128 - data.len * 8)) << 32 | mem::load((uint*)&data[8], 1));
|
||||
}
|
||||
else if (data.len != 0)
|
||||
{
|
||||
r1h ^= (data.len < 4)
|
||||
? (((ulong)1 << (data.len * 8)) ^ data[0] ^ (data.len > 1 ? (ulong)data[1] << 8 : 0) ^ (data.len > 2 ? (ulong)data[2] << 16 : 0))
|
||||
: (((@unaligned_load(*(uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (64 - data.len * 8)) << 32 | @unaligned_load(*(uint*)&data[0], 1));
|
||||
: (((mem::load((uint*)&data[^4], 1) | ((ulong)1 << 32)) >> (64 - data.len * 8)) << 32 | mem::load((uint*)&data[0], 1));
|
||||
}
|
||||
}
|
||||
else if (data.len < 32)
|
||||
{
|
||||
// HASH16
|
||||
@komimul(
|
||||
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
|
||||
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
|
||||
mem::load((ulong*)&data[0], 1) ^ seed1,
|
||||
mem::load((ulong*)&data[8], 1) ^ seed5,
|
||||
seed1, seed5
|
||||
);
|
||||
seed1 ^= seed5;
|
||||
|
||||
if (data.len < 24)
|
||||
{
|
||||
r1h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 184))) ^ seed1;
|
||||
r1h = (((mem::load((ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 184))) ^ seed1;
|
||||
r2h = seed5;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1h = @unaligned_load(*(ulong*)&data[16], 1) ^ seed1;
|
||||
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 248))) ^ seed5;
|
||||
r1h = mem::load((ulong*)&data[16], 1) ^ seed1;
|
||||
r2h = (((mem::load((ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> (((int)(data.len * 8) ^ 248))) ^ seed5;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -106,8 +106,8 @@ fn ulong hash(char[] data, ulong seed = 0)
|
||||
{
|
||||
$for var $x = 0; $x < 4; ++$x :
|
||||
@komimul(
|
||||
@unaligned_load(*(ulong*)&data[0 + ($x * 8)], 1) ^ seeds[$x],
|
||||
@unaligned_load(*(ulong*)&data[32 + ($x * 8)], 1) ^ seeds[4 + $x],
|
||||
mem::load((ulong*)&data[0 + ($x * 8)], 1) ^ seeds[$x],
|
||||
mem::load((ulong*)&data[32 + ($x * 8)], 1) ^ seeds[4 + $x],
|
||||
seeds[$x], seeds[4 + $x]
|
||||
);
|
||||
$endfor
|
||||
@@ -125,8 +125,8 @@ fn ulong hash(char[] data, ulong seed = 0)
|
||||
for (; data.len >= 16; data = data[16:^16])
|
||||
{
|
||||
@komimul(
|
||||
@unaligned_load(*(ulong*)&data[0], 1) ^ seed1,
|
||||
@unaligned_load(*(ulong*)&data[8], 1) ^ seed5,
|
||||
mem::load((ulong*)&data[0], 1) ^ seed1,
|
||||
mem::load((ulong*)&data[8], 1) ^ seed5,
|
||||
seed1, seed5
|
||||
);
|
||||
seed1 ^= seed5;
|
||||
@@ -137,13 +137,13 @@ fn ulong hash(char[] data, ulong seed = 0)
|
||||
// NOTE: This is translated from the original code. It grabs the last ulong off the buffer even though the
|
||||
// data slice is less than 8 bytes. This is possible because this branch only occurs in a loop where
|
||||
// the original data slice length is >= 32.
|
||||
r1h = (((@unaligned_load(*(ulong*)(data.ptr + data.len - 8), 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x38)) ^ seed1;
|
||||
r1h = (((mem::load((ulong*)(data.ptr + data.len - 8), 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x38)) ^ seed1;
|
||||
r2h = seed5;
|
||||
}
|
||||
else
|
||||
{
|
||||
r1h = @unaligned_load(*(ulong*)data.ptr, 1) ^ seed1;
|
||||
r2h = (((@unaligned_load(*(ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x78)) ^ seed5;
|
||||
r1h = mem::load((ulong*)data.ptr, 1) ^ seed1;
|
||||
r2h = (((mem::load((ulong*)&data[^8], 1) >> 8) | ((ulong)1 << 56)) >> ((data.len * 8) ^ 0x78)) ^ seed5;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,32 @@ fn char[HASH_BYTES] Md5.final(&ctx)
|
||||
|
||||
module std::hash::md5 @private;
|
||||
|
||||
const uint[64] MD5_T @private = {
|
||||
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
|
||||
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
|
||||
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
|
||||
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
|
||||
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
|
||||
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
|
||||
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
|
||||
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
|
||||
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
|
||||
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
|
||||
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
|
||||
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
|
||||
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
|
||||
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
|
||||
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
|
||||
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
|
||||
};
|
||||
|
||||
const int[16] MD5_S @private = {
|
||||
7, 12, 17, 22,
|
||||
5, 9, 14, 20,
|
||||
4, 11, 16, 23,
|
||||
6, 10, 15, 21
|
||||
};
|
||||
|
||||
// Implementation
|
||||
macro @f(x, y, z) => z ^ (x & (y ^ z));
|
||||
macro @g(x, y, z) => y ^ (z & (x ^ y));
|
||||
@@ -108,7 +134,7 @@ macro @i(x, y, z) => y ^ (x | ~z);
|
||||
|
||||
macro void @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
{
|
||||
*a += #f(b, c, d) + @unaligned_load(*(uint *)&ptr[n * 4], 2) + t;
|
||||
*a += #f(b, c, d) + mem::load((uint *)&ptr[n * 4], 2) + t;
|
||||
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
|
||||
*a += b;
|
||||
}
|
||||
@@ -116,93 +142,54 @@ macro void @step(#f, a, b, c, d, ptr, n, t, s)
|
||||
|
||||
fn char* body(Md5* ctx, void* data, usz size)
|
||||
{
|
||||
char* ptr;
|
||||
uint a, b, c, d;
|
||||
uint saved_a, saved_b, saved_c, saved_d;
|
||||
ptr = data;
|
||||
a = ctx.a;
|
||||
b = ctx.b;
|
||||
c = ctx.c;
|
||||
d = ctx.d;
|
||||
char* ptr = data;
|
||||
uint a = ctx.a;
|
||||
uint b = ctx.b;
|
||||
uint c = ctx.c;
|
||||
uint d = ctx.d;
|
||||
|
||||
do
|
||||
{
|
||||
saved_a = a;
|
||||
saved_b = b;
|
||||
saved_c = c;
|
||||
saved_d = d;
|
||||
uint saved_a = a;
|
||||
uint saved_b = b;
|
||||
uint saved_c = c;
|
||||
uint saved_d = d;
|
||||
|
||||
/* Round 1 */
|
||||
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
|
||||
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
|
||||
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
|
||||
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
|
||||
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
|
||||
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
|
||||
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
|
||||
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
|
||||
/* Round 1 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@f, &a, b, c, d, ptr, i + 0, MD5_T[i + 0], MD5_S[0]);
|
||||
@step(@f, &d, a, b, c, ptr, i + 1, MD5_T[i + 1], MD5_S[1]);
|
||||
@step(@f, &c, d, a, b, ptr, i + 2, MD5_T[i + 2], MD5_S[2]);
|
||||
@step(@f, &b, c, d, a, ptr, i + 3, MD5_T[i + 3], MD5_S[3]);
|
||||
}
|
||||
|
||||
/* Round 2 */
|
||||
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
|
||||
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
|
||||
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
|
||||
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
|
||||
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
|
||||
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
|
||||
/* Round 2 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@g, &a, b, c, d, ptr, (1 + 5 * (i + 0)) % 16, MD5_T[16 + i + 0], MD5_S[4]);
|
||||
@step(@g, &d, a, b, c, ptr, (1 + 5 * (i + 1)) % 16, MD5_T[16 + i + 1], MD5_S[5]);
|
||||
@step(@g, &c, d, a, b, ptr, (1 + 5 * (i + 2)) % 16, MD5_T[16 + i + 2], MD5_S[6]);
|
||||
@step(@g, &b, c, d, a, ptr, (1 + 5 * (i + 3)) % 16, MD5_T[16 + i + 3], MD5_S[7]);
|
||||
}
|
||||
|
||||
/* Round 3 */
|
||||
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
|
||||
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
|
||||
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
|
||||
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
|
||||
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
|
||||
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
|
||||
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
|
||||
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
|
||||
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
|
||||
/* Round 3 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@h, &a, b, c, d, ptr, (5 + 3 * (i + 0)) % 16, MD5_T[32 + i + 0], MD5_S[8]);
|
||||
@step(@h, &d, a, b, c, ptr, (5 + 3 * (i + 1)) % 16, MD5_T[32 + i + 1], MD5_S[9]);
|
||||
@step(@h, &c, d, a, b, ptr, (5 + 3 * (i + 2)) % 16, MD5_T[32 + i + 2], MD5_S[10]);
|
||||
@step(@h, &b, c, d, a, ptr, (5 + 3 * (i + 3)) % 16, MD5_T[32 + i + 3], MD5_S[11]);
|
||||
}
|
||||
|
||||
/* Round 4 */
|
||||
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
|
||||
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
|
||||
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
|
||||
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
|
||||
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
|
||||
/* Round 4 */
|
||||
for (int i = 0; i < 16; i += 4)
|
||||
{
|
||||
@step(@i, &a, b, c, d, ptr, (7 * (i + 0)) % 16, MD5_T[48 + i + 0], MD5_S[12]);
|
||||
@step(@i, &d, a, b, c, ptr, (7 * (i + 1)) % 16, MD5_T[48 + i + 1], MD5_S[13]);
|
||||
@step(@i, &c, d, a, b, ptr, (7 * (i + 2)) % 16, MD5_T[48 + i + 2], MD5_S[14]);
|
||||
@step(@i, &b, c, d, a, ptr, (7 * (i + 3)) % 16, MD5_T[48 + i + 3], MD5_S[15]);
|
||||
}
|
||||
|
||||
a += saved_a;
|
||||
b += saved_b;
|
||||
|
||||
243
lib/std/hash/murmur.c3
Normal file
243
lib/std/hash/murmur.c3
Normal file
@@ -0,0 +1,243 @@
|
||||
module std::hash::murmur3;
|
||||
|
||||
<*
|
||||
@param [in] data : "The data to hash"
|
||||
@param seed : "The seed to use for hashing"
|
||||
@require (data.len / 4) <= int.max : "Too much data"
|
||||
*>
|
||||
fn uint hash32(char[] data, uint seed)
|
||||
{
|
||||
int nblocks = (int)data.len / 4;
|
||||
uint h1 = seed;
|
||||
|
||||
const uint C1 = 0xcc9e2d51;
|
||||
const uint C2 = 0x1b873593;
|
||||
|
||||
uint* blocks = (uint *)(data.ptr + nblocks * 4);
|
||||
|
||||
for (int i = -nblocks; i != 0; i++)
|
||||
{
|
||||
uint k1 = getblock32(blocks, i);
|
||||
|
||||
k1 *= C1;
|
||||
k1 = k1.rotl(15);
|
||||
k1 *= C2;
|
||||
|
||||
h1 ^= k1;
|
||||
h1 = h1.rotl(13);
|
||||
h1 = h1 * 5U + 0xe6546b64;
|
||||
}
|
||||
|
||||
char* tail = data.ptr + nblocks * 4;
|
||||
|
||||
uint k1;
|
||||
|
||||
switch (data.len & 3)
|
||||
{
|
||||
case 3: k1 ^= tail[2] << 16; nextcase;
|
||||
case 2: k1 ^= tail[1] << 8; nextcase;
|
||||
case 1: k1 ^= tail[0]; k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= (uint)data.len;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] data : "The data to hash"
|
||||
@param seed : "The seed to use for hashing"
|
||||
@require (data.len / 16) <= int.max : "Too much data"
|
||||
|
||||
*>
|
||||
fn uint128 hash128_64(char[] data, uint seed)
|
||||
{
|
||||
ulong len = data.len;
|
||||
int nblocks = (int)(len / 16);
|
||||
|
||||
ulong h1 = seed;
|
||||
ulong h2 = seed;
|
||||
|
||||
const ulong C1 = 0x87c37b91114253d5UL;
|
||||
const ulong C2 = 0x4cf5ad432745937fUL;
|
||||
|
||||
ulong* blocks = (ulong*)data.ptr; // Unaligned!
|
||||
|
||||
for (int i = 0; i < nblocks; i++)
|
||||
{
|
||||
ulong k1 = getblock64(blocks, i * 2 + 0);
|
||||
ulong k2 = getblock64(blocks, i * 2 + 1);
|
||||
|
||||
k1 *= C1; k1 = k1.rotl(31); k1 *= C2; h1 ^= k1;
|
||||
|
||||
h1 = h1.rotl(27); h1 += h2; h1 = h1 * 5U + 0x52dce729;
|
||||
|
||||
k2 *= C2; k2 = k2.rotl(33); k2 *= C1; h2 ^= k2;
|
||||
|
||||
h2 = h2.rotl(31); h2 += h1; h2 = h2 * 5U + 0x38495ab5;
|
||||
}
|
||||
|
||||
char* tail = data.ptr + nblocks * 16;
|
||||
ulong k1, k2;
|
||||
|
||||
switch (len & 15)
|
||||
{
|
||||
case 15: k2 ^= ((ulong)tail[14]) << 48; nextcase;
|
||||
case 14: k2 ^= ((ulong)tail[13]) << 40; nextcase;
|
||||
case 13: k2 ^= ((ulong)tail[12]) << 32; nextcase;
|
||||
case 12: k2 ^= ((ulong)tail[11]) << 24; nextcase;
|
||||
case 11: k2 ^= ((ulong)tail[10]) << 16; nextcase;
|
||||
case 10: k2 ^= ((ulong)tail[ 9]) << 8; nextcase;
|
||||
case 9: k2 ^= ((ulong)tail[ 8]) << 0;
|
||||
k2 *= C2; k2 = k2.rotl(33); k2 *= C1; h2 ^= k2;
|
||||
nextcase;
|
||||
case 8: k1 ^= ((ulong)tail[ 7]) << 56; nextcase;
|
||||
case 7: k1 ^= ((ulong)tail[ 6]) << 48; nextcase;
|
||||
case 6: k1 ^= ((ulong)tail[ 5]) << 40; nextcase;
|
||||
case 5: k1 ^= ((ulong)tail[ 4]) << 32; nextcase;
|
||||
case 4: k1 ^= ((ulong)tail[ 3]) << 24; nextcase;
|
||||
case 3: k1 ^= ((ulong)tail[ 2]) << 16; nextcase;
|
||||
case 2: k1 ^= ((ulong)tail[ 1]) << 8; nextcase;
|
||||
case 1: k1 ^= ((ulong)tail[ 0]) << 0;
|
||||
k1 *= C1; k1 = k1.rotl(31); k1 *= C2; h1 ^= k1;
|
||||
}
|
||||
|
||||
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix64(h1);
|
||||
h2 = fmix64(h2);
|
||||
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
return h1 + (uint128)h2 << 64U;
|
||||
}
|
||||
|
||||
<*
|
||||
@param [in] data : "The data to hash"
|
||||
@param seed : "The seed to use for hashing"
|
||||
@require data.len <= uint.max : "Too much data"
|
||||
*>
|
||||
fn uint128 hash128_32(char[] data, uint seed)
|
||||
{
|
||||
uint len = data.len;
|
||||
int nblocks = (int)(len / 16);
|
||||
|
||||
uint h1 = seed;
|
||||
uint h2 = seed;
|
||||
uint h3 = seed;
|
||||
uint h4 = seed;
|
||||
|
||||
const uint C1 = 0x239b961b;
|
||||
const uint C2 = 0xab0e9789;
|
||||
const uint C3 = 0x38b34ae5;
|
||||
const uint C4 = 0xa1e38b93;
|
||||
|
||||
uint* blocks = (uint *)(data.ptr + nblocks * 16);
|
||||
|
||||
for (int i = -nblocks; i != 0; i++)
|
||||
{
|
||||
uint k1 = getblock32(blocks, i * 4 + 0);
|
||||
uint k2 = getblock32(blocks, i * 4 + 1);
|
||||
uint k3 = getblock32(blocks, i * 4 + 2);
|
||||
uint k4 = getblock32(blocks, i * 4 + 3);
|
||||
|
||||
k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
|
||||
h1 = h1.rotl(19); h1 += h2; h1 = h1 * 5U + 0x561ccd1b;
|
||||
k2 *= C2; k2 = k2.rotl(16); k2 *= C3; h2 ^= k2;
|
||||
h2 = h2.rotl(17); h2 += h3; h2 = h2 * 5U + 0x0bcaa747;
|
||||
k3 *= C3; k3 = k3.rotl(17); k3 *= C4; h3 ^= k3;
|
||||
h3 = h3.rotl(15); h3 += h4; h3 = h3 * 5U + 0x96cd1c35;
|
||||
k4 *= C4; k4 = k4.rotl(18); k4 *= C1; h4 ^= k4;
|
||||
h4 = h4.rotl(13); h4 += h1; h4 = h4 * 5U + 0x32ac3b17;
|
||||
}
|
||||
|
||||
char* tail = data.ptr + nblocks * 16;
|
||||
|
||||
uint k1, k2, k3, k4;
|
||||
|
||||
switch (len & 15)
|
||||
{
|
||||
case 15: k4 ^= tail[14] << 16; nextcase;
|
||||
case 14: k4 ^= tail[13] << 8; nextcase;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= C4; k4 = k4.rotl(18); k4 *= C1; h4 ^= k4;
|
||||
nextcase;
|
||||
case 12: k3 ^= tail[11] << 24; nextcase;
|
||||
case 11: k3 ^= tail[10] << 16; nextcase;
|
||||
case 10: k3 ^= tail[ 9] << 8; nextcase;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= C3; k3 = k3.rotl(17); k3 *= C4; h3 ^= k3;
|
||||
nextcase;
|
||||
case 8: k2 ^= tail[ 7] << 24; nextcase;
|
||||
case 7: k2 ^= tail[ 6] << 16; nextcase;
|
||||
case 6: k2 ^= tail[ 5] << 8; nextcase;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= C2; k2 = k2.rotl(16); k2 *= C3; h2 ^= k2;
|
||||
nextcase;
|
||||
case 4: k1 ^= tail[ 3] << 24; nextcase;
|
||||
case 3: k1 ^= tail[ 2] << 16; nextcase;
|
||||
case 2: k1 ^= tail[ 1] << 8; nextcase;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
h2 = fmix32(h2);
|
||||
h3 = fmix32(h3);
|
||||
h4 = fmix32(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
return h1 + (uint128)h2 << 32U + (uint128)h3 << 64U + (uint128)h4 << 96U;
|
||||
}
|
||||
|
||||
macro uint getblock32(uint* p, int i) @local
|
||||
{
|
||||
UIntLE* p_le = (UIntLE*)p + i;
|
||||
return mem::load(p_le, 1).val;
|
||||
}
|
||||
|
||||
macro ulong getblock64(ulong* p, int i) @local
|
||||
{
|
||||
ULongLE* p_le = (ULongLE*)p + i;
|
||||
return mem::load(p_le, 1).val;
|
||||
}
|
||||
|
||||
macro uint fmix32(uint h) @local
|
||||
{
|
||||
h ^= h >> 16UL;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13UL;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16UL;
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
macro ulong fmix64(ulong k) @local
|
||||
{
|
||||
k ^= k >> 33U;
|
||||
k *= 0xff51afd7ed558ccd;
|
||||
k ^= k >> 33U;
|
||||
k *= 0xc4ceb9fe1a85ec53;
|
||||
k ^= k >> 33U;
|
||||
|
||||
return k;
|
||||
}
|
||||
|
||||
171
lib/std/hash/poly1305.c3
Normal file
171
lib/std/hash/poly1305.c3
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
//
|
||||
// Poly1305 code dedicated from repo: https://github.com/NotsoanoNimus/chacha20_aead.c3l (but massively cleaned)
|
||||
module std::hash::poly1305;
|
||||
|
||||
|
||||
<* The fixed output length of Poly1305 tags (hashes). *>
|
||||
const TAG_SIZE = 16;
|
||||
|
||||
<* Fixed length of a Poly1305 block. *>
|
||||
const BLOCK_SIZE = 16;
|
||||
|
||||
<* Fixed length of the required Poly1305 key, *>
|
||||
const KEY_SIZE = 32;
|
||||
|
||||
|
||||
struct Poly1305
|
||||
{
|
||||
ulong[3] h; // hash internal state
|
||||
uint128 r; // secret portion of key
|
||||
uint128 nonce; // initialization vector, derived from key
|
||||
char[TAG_SIZE] temp; // last partial ingestion state
|
||||
usz num; // index into last partial ingestion
|
||||
// Additional, cached state information:
|
||||
ulong r0;
|
||||
ulong r1;
|
||||
ulong s1;
|
||||
}
|
||||
|
||||
|
||||
<* Constant-time carrying computation for block permutations. *>
|
||||
macro ulong constant_time_carry(ulong a, ulong b) @local
|
||||
{
|
||||
return (a ^ ((a ^ b) | ((a - b) ^ b))) >> (bitsizeof(ulong) - 1);
|
||||
}
|
||||
|
||||
|
||||
<*
|
||||
Compute a Poly1305 message authentication code for the input with the given key (secret + nonce) value.
|
||||
Note that this construct SHOULD NOT be used with the 'Hmac' module; it is its own MAC package.
|
||||
|
||||
@param[in] input
|
||||
@param key
|
||||
*>
|
||||
fn char[TAG_SIZE] hash(char[] input, char[KEY_SIZE] key)
|
||||
{
|
||||
Poly1305 p @noinit;
|
||||
p.init(key);
|
||||
p.update(input);
|
||||
return p.final();
|
||||
}
|
||||
|
||||
<* Alias for the `hash` function; Message Authentication Code. *>
|
||||
alias mac = hash;
|
||||
|
||||
<* Alias for the `hash` function; "tag" generation. *>
|
||||
alias tag = hash;
|
||||
|
||||
|
||||
fn void Poly1305.init(&self, char[KEY_SIZE] key)
|
||||
{
|
||||
*self = { // implicitly clears state as well
|
||||
.r = mem::load((uint128*)&key[ 0], 1) & 0x0ffffffc_0ffffffc_0ffffffc_0fffffff, // clamped per spec
|
||||
.nonce = mem::load((uint128*)&key[16], 1)
|
||||
};
|
||||
|
||||
self.r0 = @unaligned_load(((ulong*)&self.r)[0], 1);
|
||||
self.r1 = @unaligned_load(((ulong*)&self.r)[1], 1);
|
||||
|
||||
self.s1 = self.r1 + (self.r1 >> 2);
|
||||
}
|
||||
|
||||
|
||||
fn void Poly1305.update(&self, char[] input)
|
||||
{
|
||||
if (self.num) // currently between consuming full blocks?
|
||||
{
|
||||
usz rem = BLOCK_SIZE - self.num;
|
||||
if (input.len < rem)
|
||||
{
|
||||
self.temp[self.num:input.len] = input[..]; // saving another partial block
|
||||
self.num += input.len; // move index forward
|
||||
return;
|
||||
}
|
||||
// ingest up to a block size to finish the partial, then advance the slice ptr
|
||||
self.temp[self.num:rem] = input[:rem];
|
||||
_blocks(self, self.temp[..]);
|
||||
input = input[rem..];
|
||||
}
|
||||
|
||||
usz even_length = input.len - (input.len % BLOCK_SIZE);
|
||||
if (even_length >= BLOCK_SIZE)
|
||||
{
|
||||
_blocks(self, input[:even_length]); // consume blocks
|
||||
input = input[even_length..]; // scroll to end (remainder)
|
||||
}
|
||||
|
||||
if (input.len) self.temp[:input.len] = input[..]; // keep remainder (uneven block sizes)
|
||||
|
||||
self.num = input.len;
|
||||
}
|
||||
|
||||
|
||||
fn char[TAG_SIZE] Poly1305.final(&self)
|
||||
{
|
||||
if (self.num) // consume any leftovers
|
||||
{
|
||||
self.temp[self.num++] = 1; // partial blocks must end with 0x01
|
||||
self.temp[self.num..] = {}; // explicit zeros on the rest
|
||||
_blocks(self, self.temp[..], 0); // chomp
|
||||
}
|
||||
|
||||
uint128 t = (uint128)self.h[0] + 5;
|
||||
ulong g0 = (ulong)t;
|
||||
|
||||
t = (uint128)self.h[1] + (t >> 64);
|
||||
ulong g1 = (ulong)t;
|
||||
|
||||
ulong mask = 0 - ((self.h[2] + (ulong)(t >> 64)) >> 2);
|
||||
self.h[0] = (self.h[0] & ~mask) | (g0 & mask);
|
||||
self.h[1] = (self.h[1] & ~mask) | (g1 & mask);
|
||||
|
||||
t = (uint128)self.h[0] + (ulong)self.nonce;
|
||||
self.h[0] = (ulong)t;
|
||||
|
||||
t = (uint128)self.h[1] + (ulong)(self.nonce >> 64) + (t >> 64);
|
||||
self.h[1] = (ulong)t;
|
||||
|
||||
// Store, clear context, return.
|
||||
uint128 result = ((uint128)self.h[1] << 64) + self.h[0];
|
||||
*self = {};
|
||||
return @as_char_view(result)[:TAG_SIZE];
|
||||
}
|
||||
|
||||
|
||||
fn void _blocks(Poly1305* self, char[] input, ulong pad_bit = 1) @local
|
||||
{
|
||||
for (; input.len >= BLOCK_SIZE; input = input[BLOCK_SIZE..])
|
||||
{
|
||||
ulong i0 = mem::load((ulong*)&input[0], 1);
|
||||
ulong i1 = mem::load((ulong*)&input[8], 1);
|
||||
|
||||
uint128 d0 = (uint128)self.h[0] + i0;
|
||||
self.h[0] = (ulong)d0;
|
||||
|
||||
uint128 d1 = (uint128)self.h[1] + (d0 >> 64) + i1;
|
||||
self.h[1] = (ulong)d1;
|
||||
|
||||
self.h[2] += (ulong)(d1 >> 64) + pad_bit;
|
||||
|
||||
d0 = ((uint128)self.h[0] * self.r0) + ((uint128)self.h[1] * self.s1);
|
||||
d1 = ((uint128)self.h[0] * self.r1) + ((uint128)self.h[1] * self.r0) + ((uint128)self.h[2] * self.s1);
|
||||
self.h[2] = (self.h[2] * self.r0);
|
||||
|
||||
self.h[0] = (ulong)d0;
|
||||
|
||||
d1 = d1 + (d0 >> 64);
|
||||
self.h[1] = (ulong)d1;
|
||||
self.h[2] = self.h[2] + (ulong)(d1 >> 64);
|
||||
|
||||
ulong c = (self.h[2] >> 2) + (self.h[2] & ~(ulong)3);
|
||||
|
||||
self.h[2] &= 3;
|
||||
self.h[0] += c;
|
||||
c = constant_time_carry(self.h[0], c);
|
||||
self.h[1] += c;
|
||||
self.h[2] += constant_time_carry(self.h[1], c);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user