Compare commits

..

225 Commits

Author SHA1 Message Date
Christoffer Lerno
161280ce7d Add deprecation silence notice. 2026-01-30 13:57:52 +01:00
Christoffer Lerno
4e129d4ae2 Fix test. 2026-01-30 13:18:56 +01:00
Christoffer Lerno
2b6f1c061d Fix of evaluation order warning. 2026-01-30 13:10:54 +01:00
Christoffer Lerno
cb19c7d9e7 Test update 2026-01-29 20:08:58 +01:00
Christoffer Lerno
224b120745 Another fix error => warn 2026-01-29 20:07:42 +01:00
Christoffer Lerno
534dd42472 Update test. 2026-01-29 17:51:50 +01:00
Christoffer Lerno
163976f85f Fix of second warning triggered on failing to detect a method. 2026-01-29 17:42:51 +01:00
Christoffer Lerno
d79a8808d7 Update readme example and last version 2026-01-29 14:36:13 +01:00
Christoffer Lerno
1cba5a0774 Release candidate 2026-01-29 11:44:45 +01:00
Christoffer Lerno
6c76a7ce4e Make methods not fully determined a warning. 2026-01-29 11:42:58 +01:00
Christoffer Lerno
084d5cbc94 Set target in test. 2026-01-29 02:30:06 +01:00
Christoffer Lerno
32b1df0f86 Set target in test. 2026-01-29 00:43:53 +01:00
Christoffer Lerno
50718cb905 - Crash in slice expression when it contains a rethrow #2872
- Multiple issues when rethrowing inside of expressions #2873
2026-01-29 00:42:20 +01:00
Christoffer Lerno
414c0c9438 - Initializer did not correctly handle second rethrow #2870
- Crash encountering panic in if-else style switch #2871
2026-01-28 22:56:59 +01:00
Christoffer Lerno
362d5680e4 - Optional in initializer cause a crash #2864
- Negating a global address with offset was a counted as a global runtime constant #2865
- Converting static "make_slice" to array failed to be handled #2866
- Narrowing a not expression was incorrectly handled #2867
- Vector shift by optional scalar failed #2868
2026-01-28 21:59:40 +01:00
Christoffer Lerno
fed7a74a75 Fix assert in #2863 2026-01-28 19:02:27 +01:00
Christoffer Lerno
d276d3767f Incorrect handling when reporting fn with optional compile time type #2862 2026-01-28 18:51:21 +01:00
Christoffer Lerno
a07660f957 Improve tracing of liveness error reporting. 2026-01-28 11:44:37 +01:00
Christoffer Lerno
4d02ce4414 Additional fix for #2853 2026-01-27 23:43:14 +01:00
Christoffer Lerno
c1e3cfaacc - Crash when trying to create a const zero untyped list #2847 2026-01-27 23:36:00 +01:00
Christoffer Lerno
c11385cf49 Fix wasm int128 size on LLVM 17-19. 2026-01-27 16:26:35 +01:00
Christoffer Lerno
0b5064683f Regression: Generic type could not be found in lambda inside macro #2853 2026-01-27 14:23:41 +01:00
Christoffer Lerno
f3ede27f60 Regression: "'out' parameters may not be read" when writing to array #2852 2026-01-27 13:48:50 +01:00
Christoffer Lerno
4fbb42833e - Crash when creating $Type* where $Type is an optional type #2848
- Crashes when using `io::EOF~!` in various unhandled places. #2848
2026-01-27 13:32:08 +01:00
Christoffer Lerno
3e76b7ff1c Fixes to optional rethrow in expressions. 2026-01-26 04:10:38 +01:00
Christoffer Lerno
e901a3de55 - Fix alignment for uint128 to 16 with WASM targets.
- Incorrect assert in struct alignment checking #2841
- Packed structs sometimes not lowered as such.
2026-01-25 23:06:16 +01:00
Christoffer Lerno
4899ee14e2 Resolving &X.b when X is a const incorrectly checked for runtime constness #2842
Creating a generic instance fails if it is created after interface checking #2840
2026-01-25 21:24:27 +01:00
Christoffer Lerno
3c04a326f4 Resolving &X.b when X is a const incorrectly checked for runtime constness #2842 2026-01-25 20:28:58 +01:00
Christoffer Lerno
a1ff3b05ed - Lowering of optional in && was incorrect #2843 2026-01-25 19:50:28 +01:00
Christoffer Lerno
74e228688a - Packed .c3l files without compressions weren't unpacked correctly. 2026-01-25 19:13:42 +01:00
Zack Puhl
75d454b6a6 Add mem_allocator realloc_array Macros (#2760)
* Add mem_allocator `realloc_array` Macros

* test, ensure `realloc_array` to 0 size returns `null`

* update release notes

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-25 16:12:44 +01:00
m0tholith
6cffb888ea Add maximum memory usage tracking to tracking allocator (#2772)
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-25 16:11:46 +01:00
soerlemans
cf03215564 Added unit tests for Crc32 hashing. (#2833)
* Adding crc32 unit tests.

* Fix formatting in crc32 test cases

---------

Co-authored-by: soerlemans <sebasoerlemans+git@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2026-01-25 13:16:44 +01:00
Christoffer Lerno
5b90743120 Fix test compatibility 2026-01-25 13:15:26 +01:00
Christoffer Lerno
0fb91265b6 - Bug when initializing an inferred array with deep structure using designated init #2826 2026-01-25 13:13:59 +01:00
Christoffer Lerno
3cb7c489ee Updated releasenotes 2026-01-25 12:50:02 +01:00
Christoffer Lerno
c65c378b7f - Bitstruct as substruct fails to properly work with designated initializers. #2827 2026-01-25 12:48:36 +01:00
Christoffer Lerno
cf9784afee - Bitstruct accidentally allowed other arrays than char arrays #2836 2026-01-25 12:14:34 +01:00
Christoffer Lerno
8bd942c1b8 - Store of zero in lowering did not properly handle optionals in some cases #2837 2026-01-25 04:57:35 +01:00
Christoffer Lerno
109e15b5a0 - Empty enums would return the values as zero sized arrays #2838 2026-01-25 04:41:06 +01:00
Christoffer Lerno
0fdd6bdc81 - Early exit in macro call crashes codegen #2820 2026-01-25 00:52:57 +01:00
Christoffer Lerno
378b35265b - Raw vaargs with optional return not lowered correctly #2819 2026-01-24 23:54:20 +01:00
Christoffer Lerno
b5e25e3857 Constant deref of subscript had inserted checks #2818 2026-01-24 23:24:36 +01:00
Christoffer Lerno
9b2fc04959 ompile time dereference of a constant slice was too generous #2821 2026-01-24 22:42:04 +01:00
Christoffer Lerno
ce8167a102 - Incorrectly try compile time int check on vector #2815
- Generating typeid from function gives incorrect typeid #2816
- Recursive definitions not discovered when initializer is access on other const #2817
- Slice overrun detected late hit codegen assert #2822
2026-01-24 19:38:51 +01:00
Christoffer Lerno
397d065a74 - Constant shifting incorrectly doesn't flatten the underlying vector base #2825
- String not set as attributes resolved breaking has_tagof #2824
- Self referencing forward resolved const enum fails to be properly detected #2823
2026-01-24 18:32:26 +01:00
Christoffer Lerno
5e23817a3d - Comparing a flexible array member to another type would hit an assert. #2830
- Underlying slice type not checked correctly in $defined #2829
- Checking for exhaustive cases is done even in if-chain switch if all is enum #2828
2026-01-24 17:57:56 +01:00
Christoffer Lerno
396263f5c3 De-macro readline. 2026-01-23 22:45:58 +01:00
Christoffer Lerno
26d0760c0d Make counting sort normal. 2026-01-23 17:09:34 +01:00
Christoffer Lerno
11f090116f Allow disabling asserts. 2026-01-23 13:09:43 +01:00
Zack Puhl
5e1c343be4 Deprecate builtin EMPTY_MACRO_SLOT for optional macro arguments (#2805)
* add deprecations to macro slot builtins

* refactor all stdlib uses of now-deprecated EMPTY_MACRO_SLOT; release notes

* update incorrect releasenotes ref to this pr

* remove leftover comments from refactoring

* remove unnecessary `EmptySlot`-like type in countingsort; use private macro directly
2026-01-23 12:34:50 +01:00
Christoffer Lerno
0e69432e3d Improved error diagnostics for memory overflow. 2026-01-23 11:58:19 +01:00
Christoffer Lerno
94a26d9483 Add asserts, tracking Windows bug 2026-01-23 11:51:51 +01:00
Christoffer Lerno
efa5bdc6df Add better error message on VirtualAlloc failure on windows. 2026-01-23 11:45:00 +01:00
Christoffer Lerno
459969ddb2 Make sure we don't switch over bad things. 2026-01-23 01:22:58 +01:00
Christoffer Lerno
ae5047b73f Deprecating multi-level array length inference. int[*][*] is deprecated and will be removed 0.8.0. 2026-01-22 23:50:39 +01:00
Christoffer Lerno
17f3db835c - Remove dependency on test tmp library for stdlib compiler tests. #2800 2026-01-22 22:15:34 +01:00
Christoffer Lerno
1845a515ca - Empty ichar slice + byte concatenation hit an assert. #2789 2026-01-22 21:39:03 +01:00
Christoffer Lerno
bf50178eb3 $typeof(<type>) returns typeinfo, causing errors #2795 2026-01-22 20:04:09 +01:00
Christoffer Lerno
32675161c4 - Attrdef eval environment lacked rtype, causing error on invalid args #2797 2026-01-22 19:47:37 +01:00
Christoffer Lerno
e257500e03 Recursive definition of tag not detected with nested tag/tagof #2790 2026-01-22 19:16:54 +01:00
Christoffer Lerno
0add42b0a0 - Make foo.$abc implicitly mean foo.eval("$abc"). 2026-01-22 16:46:07 +01:00
Christoffer Lerno
b14053df41 - Too deeply nested scopes was a fatal crash and not a regular semantic error. #2796 2026-01-22 15:13:01 +01:00
Christoffer Lerno
6a78864b6c Instantiating an alias of a user-defined type was not properly caught #2798 2026-01-22 13:50:57 +01:00
Christoffer Lerno
b1fea45cd1 Using an optional type as generic parameter was not properly caught #2799 2026-01-22 13:34:16 +01:00
Christoffer Lerno
cef48482f1 Unable to access fields of a const inline enum with an aggregate underlying type. #2802 2026-01-21 13:22:46 +01:00
Christoffer Lerno
a126a25d66 Casting const bytes to vector with different element size was broken #2787 2026-01-21 12:56:25 +01:00
Christoffer Lerno
61c939059d int? ? was not correctly handled. #2786 2026-01-21 00:52:18 +01:00
Christoffer Lerno
5fa820a462 When a global const has invalid attributes, handling is incorrect, leading to a crash #2785. 2026-01-21 00:43:30 +01:00
Christoffer Lerno
472124dab3 Bug in sysv abi when passing union in with floats #2784 2026-01-21 00:34:37 +01:00
Christoffer Lerno
1e11b6c442 Inferring the size of a slice with an inner inferred array using {} isn't detected as error #2783 2026-01-20 23:58:45 +01:00
Christoffer Lerno
34a86852c6 Failed to reject void compile time variables, leading to crash. #2781 2026-01-20 23:07:11 +01:00
Christoffer Lerno
52afbdbde9 Recursive constant definition not properly detected, leading to assert #2780 2026-01-20 18:28:13 +01:00
Christoffer Lerno
888657cc97 - $typeof untyped list crashes when trying to create typeid from it. #2779 2026-01-20 17:54:05 +01:00
Christoffer Lerno
d7bfddf35e Broken cast from fault to array pointer #2778. 2026-01-20 17:43:16 +01:00
Christoffer Lerno
9c435352b9 - Second value in switch range not checked properly, causing an error on non-const values. #2777 2026-01-20 17:12:41 +01:00
Christoffer Lerno
3fe55b5e51 - Vectors not converted to arrays when passed as raw vaargs. #2776 2026-01-20 17:00:09 +01:00
Christoffer Lerno
cdabe8fd9e - Create optional with ~ instead of ?. return io::EOF?; becomes return io::EOF~.
- Deprecated use of `?` to create optional.
2026-01-20 16:10:28 +01:00
Christoffer Lerno
5390ca6250 - Eager evaluation of macro arguments would break inferred arrays on some platforms. #2771. 2026-01-20 13:02:14 +01:00
Christoffer Lerno
61e84e4d34 One last regression fix. 2026-01-20 11:47:17 +01:00
Christoffer Lerno
1b82b7d2f2 Fix regression with conditional methods. 2026-01-20 11:20:39 +01:00
Christoffer Lerno
49f59d82c9 Fix regression. 2026-01-20 01:26:29 +01:00
Christoffer Lerno
e84e23f7ec Fix Using @if with methods with generic params only inferred from the parent type fails #2770 2026-01-20 01:04:12 +01:00
Christoffer Lerno
0b9b49673e - Empty struct after @if processing was not detected, causing a crash instead of an error.
- Comparing an uint and int[<4>] was incorrectly assumed to be uint compared to int, causing a crash instead of an error.
- When an `int[*][6]` was given too few values, the compiler would assert instead of giving an error.
2026-01-20 00:04:18 +01:00
Christoffer Lerno
4512c6446d - Empty struct after @if processing was not detected, causing a crash instead of an error.
- Comparing an uint and int[<4>] was incorrectly assumed to be uint compared to int, causing a crash instead of an error.
- When an `int[*][6]` was given too few values, the compiler would assert instead of giving an error.
2026-01-19 15:01:08 +01:00
Christoffer Lerno
0fea6c6056 - Bitstruct with unevaluated user-defined type would cause a crash.
- Using named parameters with builtins would cause a crash.
- In some cases, using missing identifiers with builtins would cause a crash.
- Using `$defined` with function call missing arguments would cause a crash.
- Adding @nostrip to a test function would crash.
- Mixing struct splat, non-named params and named params would crash rather than to print an error.
- Creating a char vector from bytes would crash.
- Using $$wstr16 with an illegal argument would crash instead of printing an error.
2026-01-19 02:49:54 +01:00
Christoffer Lerno
dd8449576f - Passing a non-conststring to module attributes like @cname would trigger an assert rather than printing an error.
- Passing different types to arg 1 and 2 for $$matrix_transpose would trigger an assert.
- Zero init of optional compile time variable would crash the compiler.
- Using multiple declaration for generics in generic module would fail.
- Defining an extern const without a type would crash rather than print an error.
- Typedef followed by brace would trigger an assert.
- Union with too big member would trigger an assert.
2026-01-18 22:47:17 +01:00
Christoffer Lerno
c3b2694834 Generics with <>. Deprecation of {} generics. 2026-01-18 00:33:43 +01:00
Christoffer Lerno
d3ebd4a130 Migrate from @unaligned_load to mem::load 2026-01-17 19:12:32 +01:00
Christoffer Lerno
a326e31e57 Add missing fix. 2026-01-17 18:52:55 +01:00
Christoffer Lerno
945a3f3fc0 Add mem::store and mem::load which may combine both aligned and volatile operations. 2026-01-17 18:52:30 +01:00
Darvis
2a8fbb8fec Fix function signature for free to return void
free expects a return type void* but it should return void instead of void*
2026-01-17 18:52:11 +01:00
Book-reader
c0f1b02d0b native_file_size: use libc stat on more platforms
Co-authored-by: 김형근 (Nomota Hiongun KIM) <hiongun@gmail.com>
2026-01-17 16:09:21 +01:00
Christoffer Lerno
f2c557c3b7 Remove use of LLVMCreateBuilder #2666 2026-01-17 15:39:22 +01:00
Christoffer Lerno
d00a93f195 Update error on omitting end. 2026-01-17 15:37:30 +01:00
Christoffer Lerno
aa07969fc2 - HashSet.len() now returns usz instead of int. #2740 2026-01-17 15:12:31 +01:00
Christoffer Lerno
6dcd91c5ef - Assert on defining a const fault enum with enumerator and fault of the same name. #2732 2026-01-17 15:01:40 +01:00
Christoffer Lerno
c2e603ddd8 Expand inference. 2026-01-17 14:03:58 +01:00
Christoffer Lerno
70c4b24519 - In some cases, a type would not get implicitly converted to a typeid #2764. 2026-01-16 11:41:18 +01:00
Christoffer Lerno
51364a9d1b Another fix in immediately rethrowing a value in an assignment #2689 2026-01-15 23:33:26 +01:00
Christoffer Lerno
d9f4eb46d9 - Too little memory reserved when printing backtrace on Darwin #2698. 2026-01-15 22:50:12 +01:00
Book-reader
cd2d1a04d8 Use a Printable struct for ansi rgb formatting instead of explicit allocations (#2696)
* Use a `Printable` struct for ansi rgb formatting

* update release notes

* Some renaming.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-15 22:26:18 +01:00
Zack Puhl
3f7a547d8a Merge AsciiCharset CT/non-CT Functions (#2688)
* Merge AsciiCharset CT/non-CT Functions

* release notes

* incorporate helpful review feedback

* re-separate 'create_set' and 'contains' but keep 'combine_sets'; update tests

* tabs (annoying IDE)

* Restored old code verbatim for smaller diff. Split combine_sets into easier to macro/function for runtime / macro version, this also allows for more easy type checks.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-15 21:39:48 +01:00
Manu Linares
f254c27966 std/io/os/temp_directory.c3: fix INVALID_PATH in Win32 native_temp_directory (#2762)
* std/io/os/temp_directory.c3: fix INVALID_PATH in Windows native_temp_directory

- Use the actual length from GetTempPathW for Windows temp path slice
- We can remove the workaround in the test_suite_runner for WIN32 and
create all directories in %temp% now

* Updated releasenotes.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-15 21:23:30 +01:00
Jasper Wilmes
8a1c02c840 Rework libc signal constants (#2724)
* Move libc::SIG* constants to posix.c3

* Add missing libc::SIG* constants for Win32 systems

* Add missing POSIX signals

* Add missing Linux signals

* Add missing BSD signals

* Moved common signals back to libc

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-15 21:17:50 +01:00
Christoffer Lerno
7f297a9d27 Fix type inference overrides parameterization #2761. Remove support for ^io::EOF 2026-01-15 15:19:33 +01:00
VarunVF
64ef33f09b Require parenthesized assignment expressions in condition of 'if' statements #2716 (#2729)
* Require parenthesized assignment expressions in condition of 'if' statements #2716

* Move analysis to semantic checker and also check while. And update tests and release notes.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-15 00:43:58 +01:00
mikelgarai
cd4c586c3f Fixed macro hash_vec(vec): it was checking only the first bytes (4 or 8, not sure) (#2718)
* Fixed macro hash_vec(vec): it was checking only 8 bytes

* use $sizeof() directly as it is correct now

* Added unit test for vector types

Tests that hashes match for same values and that they don't match
when changing a single bit on any of the vector elements

* Changed hardcoded value for the compiler option

* Release note

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-14 23:22:10 +01:00
Kiana
0a9d4e398d Add $$VERSION and $$PRERELEASE (#2751)
* Add 64507VERSION and 64507PRERELEASE

* Migrate to compiler define method

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-14 23:15:55 +01:00
Christoffer Lerno
d61beef7e0 Fix String.replace not dropping temp mem. 2026-01-14 22:13:23 +01:00
Sergey Fedorov
7136b05019 common.h: define static_assert to _Static_assert when undefined 2026-01-14 14:57:19 +01:00
Christoffer Lerno
87fa253059 Add debug versions. 2026-01-14 02:00:07 +01:00
Manu Linares
04eb6fc451 CI: Major refactor, expanded coverage, and ~80% speedup (#2736)
* netbsd fail on error

- Group each BSD test in its own subshell. Without this, if an error
occurs, it exits, but we don't know "where" it failed
- added *usesh: true* to BSD for faster copying into the virtualbox VM

- Some test_suite_runners wheren't correctly printing. added
--no-terminal
- For testing I've changed *build-msvc* from Debug to RelWithDebInfo

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* separate runs

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* fix typo

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Split msvc-debug into separate workflow

Run msvc-debug only on maintainer pushes, keep CI fast and still catches
errors.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Fix build and test errors on NetBSD

* Set proper sizes for various types used for threads
* Add num_cpu function
* Add sysctl constants
* Allow testproject to build on NetBSD
* Tweaks to the linker code so that it adds correct flags and finds dynamic linker on NetBSD

* bsd: force system linker test

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* system linker on openbsd

remove lld from netbsd

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* use cc linker in openbsd, and re-add lld for netbsd

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* openbsd: fixes

- implement num_cpu() for OPENBSD
- testproject: add openbsd
- linker.c: fix for openbsd
- bsd refactor main.yml and more fixes

* openbsd: fix for unit tests and test_suite_runner

openbsd now passes all tests except the 'dynlib-test' which yields
'relocation error' when running `cc test.c -L. -ladd -Wl,-rpath=.`

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* openbsd: guard/disable unit test test_ct_intlog2()

* build-msys2-clang: add all tests that build-msvc has

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Fix Windows import library generation by adding /IMPLIB support and properly declaring static_lib_name()

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* build-msys2-mingw: add all tests that build-msvc has and enable it

configure CMake to link LLVM and LLD statically

* Revert "Revert windows"

This reverts commit 197f82d829.

* win-llvm 21.1.8

uses the RelWithDebInfo+Assertions

* refactor(CI): Centralize test logic and fix Docker execution

This commit refactors the GitHub Actions workflow to improve
maintainability and correct a flaw in the Docker build.

- Ensures Docker based tests run *inside* the generated container, not
on the host. This also fixes environment isolation and user permission
errors.
- Centralizes all common test steps (examples, libs, unit tests, etc)
into a single, auto-detecting Bash script: `scripts/tools/ci_tests.sh`.
- Centralize all release artifact packaging into a single
`scripts/tools/package_build.sh`
- fixes using correct paths for test_suite_runner when running on
windows under bash.

- This significantly reduces YAML duplication, making `main.yml` more
readable and easier to maintain.

This one supersedes pull/2677, and includes the fixes for:

- fix openbsd and netbsd
- added win-llvm 21.1.8 and fixed CMakeLists.txt
- added full unit-tests and suite for build-msys2-mingw and
build-msys2-clang

* macos: add/fix build for llvm (19,20,21)

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* ci: run tests in temp dir to avoid workspace pollution

Executes ci_tests.sh in a disposable temporary directory by copying
resources/ and test/ folders, preventing build artifacts from cluttering
the source tree.

This makes it easy to run `scripts/tools/ci_tests.sh` locally on any
platform.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* add ilammy/msvc-dev-cmd@ and Ninja

we can simplify the windows runner and run everything in bash

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* windows: drain subprocess stderr before join to avoid test_suite_runner hang

* move last test to `ci_tests.sh`

thus completing the centralization of tests in `ci_tests.sh`

* Fix Windows CI cache

Updates the cache key to include hashes of CMakeLists.txt and the
workflow file.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* `package_build.sh` runs in temp dir to avoid workspace pollution

- add cleanup

* nix: use `ci_tests.sh`

* remove annoying build-mac warnings

This now checks if package already installed trying to prevent
annoying warnings in the github ci:

Fixes:
```
build-mac (Release, 19)curl 8.17.0 is already installed and up-to-date.
To reinstall 8.17.0, run: brew reinstall curl
```

* refactor test_suite_runner

- less allocations, cleaner code, more maintainable.
- now can be killed and the temp dirs are correctly cleaned up, and the
output is correct.

* main.yml: bsd: change to using rsync, is twice as fast.

* default to `--wincrt=dynamic` when `--sanitize=address` on windows

Default to dynamic, as static ASan is removed in LLVM 21+ for Windows
https://github.com/llvm/llvm-project/pull/107899

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Formatting (indent with tab, align with space). Fix K&R braces. Style: keep all cases multiline if one is.

---------

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
Co-authored-by: Alex Garrett <limit.ordinal17@gmail.com>
2026-01-14 01:16:06 +01:00
Koni Marti
15fc435d92 collections: fix LinkedList.to_format
For-loop in LinkedList.to_format goes brrrrrr and prints the value of
the first node an infinte number of times because it does not properly
iterate the linked list.

Fix the list iterations and add the missing string format specifier.

Bug can be reproduced with:
```
import std;
fn void main() => @pool()
{
	io::printfn("%s", linkedlist::@tnew{usz}({1,2,3}));
}
```
2026-01-13 23:09:05 +01:00
Manuel Barrio Linares
9d54e9e3c4 a fix to generated C header global
test:
```c3
module my_module;

alias Something = fn void(int);
Something something @export("something") = &something_else;
fn void something_else(int a) => {};
```

would generate
```c
/* GLOBALS */
my_module__Somethingextern  something;
```

now correctly generates
```c
/* GLOBALS */
extern my_module__Something  something;
```

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
2026-01-13 23:07:49 +01:00
Book-reader
c73c7cb2a3 add Win32_CODEPAGES enum and enable utf8 console output on win32 (#2670)
* add Win32_CODEPAGES enum and enable utf8 console output on win32

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-13 23:00:01 +01:00
Gourmet
827ad18ef4 Ignore ctags and gtags files in .gitignore
This PR updates the .gitignore file to exclude automatically generated tags files from ctags and gtags.

Specifically, it ignores:
- ctags output file: tags
- GNU Global output files: GTAGS, GRTAGS, GPATH
2026-01-13 18:08:43 +01:00
Darvis
3fc562af6f Fix minor typos in README.md 2026-01-13 18:06:33 +01:00
Arnaud Duforat
87c42f1cd3 Add unit tests for HMAC 256 based on RFC 4231 (#2744)
* Add unit tests for HMAC 256 based on RFC 4231

* Formatting, shorten initializers.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-12 11:26:49 +01:00
Christoffer Lerno
e93f22fbda - bitorder::read and bitorder::write may fail because of unaligned access #2734. 2026-01-12 01:11:31 +01:00
Christoffer Lerno
8fa59cbb43 Creating recursive debug info for functions could cause assertions. 2026-01-07 23:50:56 +01:00
Christoffer Lerno
527766310f Update pointer type naming. 2026-01-07 23:12:09 +01:00
Christoffer Lerno
a02b0a1f4c Update LLVMHasUseList. 2026-01-07 20:29:55 +01:00
Christoffer Lerno
4d93db51ee Check properly has use list. 2026-01-07 19:59:40 +01:00
Christoffer Lerno
fa2e6e8189 Fix typo 2026-01-07 15:56:34 +01:00
Christoffer Lerno
0fc1871ddf - Assert on optional-returning-function in a comma expression. #2722 2026-01-07 15:53:54 +01:00
Christoffer Lerno
197f82d829 Revert windows 2026-01-07 03:36:48 +01:00
Christoffer Lerno
4ad11724d1 Update Windows flags 2026-01-07 03:31:03 +01:00
Christoffer Lerno
6d53fd6f6e Fix test. 2026-01-07 03:18:01 +01:00
Christoffer Lerno
db47b0555d Update windows LLVM version 2026-01-07 03:15:59 +01:00
Christoffer Lerno
ebd7b7243a Fix bug when evaluating a non-generic identifier. 2026-01-07 03:13:14 +01:00
Christoffer Lerno
e0771beabc Remove pointer names in debug, following Clang. 2026-01-07 02:35:05 +01:00
Christoffer Lerno
824d064710 Fix tests. 2026-01-07 00:55:30 +01:00
Christoffer Lerno
336ddb67c8 Incorrect alignment on typedef and local variable debug info. 2026-01-07 00:16:49 +01:00
Christoffer Lerno
9ad98beda7 - Crash when doing a type property lookup for const inline enums in some cases #2717. 2026-01-06 15:28:18 +01:00
Christoffer Lerno
702f836b40 io::read_fully now handles unbounded streams properly 2026-01-05 22:27:55 +01:00
Christoffer Lerno
d820a2356a Fix: Compiler assert when attempting to define multiple faults in a generic module. #2706 2026-01-05 21:04:23 +01:00
Christoffer Lerno
fa1951642b Add debug info 2026-01-05 00:11:48 +01:00
Christoffer Lerno
42dc2d541a Add testcase. 2026-01-04 22:05:01 +01:00
Chad Adams
fdbbe5c1aa improve c3c init error message (#2697)
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-03 17:17:12 +01:00
Christoffer Lerno
292bf1cbbc Designated initialization with ranges would not error on overflow by 1. 2026-01-03 02:32:40 +01:00
Christoffer Lerno
82769669ec Fix of generic with multiple parameters fail. #2702 2026-01-03 00:09:14 +01:00
Christoffer Lerno
29b211eefc - Fix the case where \u<unicode char> could crash the compiler on some platforms. 2026-01-01 21:44:37 +01:00
Zack Puhl
e4965ab408 Add BLAKE3 Hashing/XOF to stdlib (#2667)
* blake3: initial unit test passing!

* typo

* add key derivation macros; vanilla unit tests; working to test 18 atm

* mark it here - all tests passing o_o

* finish first-round unit tests - will add more if necessary

* add crypto shootout bench entertainment

* tests: add XOF unit w/ seek; assert NO finalization

* add another to finalizations unit test

* add all BLAKE3 scaffolding for later SIMD optimizations

* irksome

* tabs

* tabs2

* extra documentation / contracts

* extra detail

* try to make things a bit more arch-neutral

* release notes

* Formatting

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-01 21:11:33 +01:00
Christoffer Lerno
067a4f7cb1 Fixed codegen for vec->array in the boolean case. 2026-01-01 00:25:32 +01:00
Christoffer Lerno
739e91efa4 - Reduced memory usage for backtraces on Linux.
- `String.tokenize_all` would yield one too many empty tokens at the end.
- `String.replace` no longer depends on `String.split`.
2025-12-31 10:08:00 +01:00
Christoffer Lerno
26d733ef59 No optimization in test runner for MSVC 2025-12-31 00:45:35 +01:00
Christoffer Lerno
32d6025e29 Add panics just in case. 2025-12-31 00:43:19 +01:00
Christoffer Lerno
04706f2dcd Fix issue when removing test files on abort. 2025-12-31 00:13:38 +01:00
Christoffer Lerno
d8a7c57b56 - Miscompilation: global struct with vector could generate an incorrect initializer. 2025-12-30 23:54:00 +01:00
Christoffer Lerno
ad8769580a Methods registered with generic type will implicitly get templated. 2025-12-30 22:39:42 +01:00
Christoffer Lerno
d51dd09a1f Made contracts aggregate from types etc 2025-12-30 21:31:45 +01:00
Christoffer Lerno
5ed4f9519f - x'1234' +++ (ichar[1]) { 'A' } would fail due to missing const folding. 2025-12-30 16:06:28 +01:00
Christoffer Lerno
493a084745 Added additional type size limits. 2025-12-30 13:42:14 +01:00
Christoffer Lerno
9bd04526e8 foo.x was not always handled correctly when foo was optional. 2025-12-30 12:38:04 +01:00
Christoffer Lerno
90f0486334 - Assert when encountering a test function with raw vaarg parameters. 2025-12-30 12:28:03 +01:00
Christoffer Lerno
e4f1b57bd0 Assert when encountering a malformed module alias. 2025-12-29 22:41:42 +01:00
Christoffer Lerno
c949bd3108 Assert when struct size would exceed 4 GB. 2025-12-29 22:24:41 +01:00
Christoffer Lerno
56f8008d85 - Parse error in $defined was not handled correctly, leading to an assertion. 2025-12-29 22:09:39 +01:00
Christoffer Lerno
c6a96ad7f3 - i<n> suffixes were not caught when n < 8, causing an assert. 2025-12-29 21:57:06 +01:00
Christoffer Lerno
328e6f518c Fix bug when encountering Type{} where Type is a generic parameter. 2025-12-29 19:34:45 +01:00
Christoffer Lerno
8ec4a2cada Fix test. 2025-12-29 17:25:49 +01:00
Zack Puhl
9b318ec233 [stdlib] Impove SHA-256 Performance (#2671)
* [stdlib] Impove SHA-256 Performance

Cleaned up the code a bit. Seems to have improved performance anywhere from ~10-25%.

* trade-offs, trade-offs... reduce codegen

* Fix formatting

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-29 17:07:03 +01:00
Christoffer Lerno
d96624c578 Decoupled generics (#2695) 2025-12-29 17:01:03 +01:00
Christoffer Lerno
bf1d401566 Converting between simd/non-simd bool vector would hit a compiler assert. #2691 2025-12-29 16:59:34 +01:00
Christoffer Lerno
a2c886a2d9 - Compiler assert when passing returning CT failure immediately rethrown #2689. 2025-12-26 22:02:02 +01:00
Christoffer Lerno
e76278cfd7 Fix test 2025-12-25 22:50:06 +01:00
Christoffer Lerno
1028f85daa - Better error messages when slicing a pointer to a slice or vector. #2681 2025-12-25 22:01:15 +01:00
Christoffer Lerno
e706c914a8 Fix test. 2025-12-25 21:39:19 +01:00
Christoffer Lerno
f3b71ed7eb - $$MASK_TO_INT and $$INT_TO_MASK to create bool masks from integers and back.
- Fix bug when creating bool vectors in certain cases.
2025-12-25 20:55:11 +01:00
Christoffer Lerno
18b246c577 - Testing for the presence of methods at the top level is prohibited previous to method registration. 2025-12-24 15:32:21 +01:00
Christoffer Lerno
c205719563 Fix netbsd vm version 2025-12-23 11:43:53 +01:00
Josh Ring
02a5270c5a Add docstring to swizzle and swizzle2 (#2674)
* Add docstring to swizzle and swizzle2

* Fixup

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-23 11:38:04 +01:00
Manuel Barrio Linares
2ba244dd9c Split formatting options out of .clang-tidy into .clang-format
This splits out the .clang-format settings from the .clang-tidy file.

Right now, the .clang-tidy file has a bit of a mix: it contains both
clang-tidy checks and clang-format style options.
This actually works fine in some tools (like CLion which doesn't
completely fail), but newer versions of clangd-based tools (VSCode,
Neovim, etc.) are stricter and will outright reject the config because
they expect .clang-tidy to only contain tidy-related stuff.

So here's what I did:
- Moved all the formatting-related options into a brand new
.clang-format file
- Kept .clang-tidy focused only on checks, warnings-as-errors, and any
check-specific options

No changes to the actual style rules or tidy checks. Everything should
behave exactly the same as before.

This should now play nicely with clangd, VSCode/VSCodium, Neovim, and
other modern clang-based tools, while still working perfectly in CLion.

I did this because some/*my* PRs had some formatting errors that could
be prevented in the future, thus lessing the work while
reviewing/merging.

Test the old .clang-tidy with

```
clang-tidy --dump-config
./.clang-tidy:13:1: error: unknown key 'SpaceBeforeCaseColon'
... more errors
```

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
2025-12-23 11:34:53 +01:00
Manu Linares
d33d0a232b threaded test_suite_runner.c3 (#2642)
* threaded test_suite_runner.c3

- Added a simple threadpool
- Fixed the status line updates
- Implemented the #skip for tests
- Added ansi color to the final status line

It works as one expects reducing the total runner time by the allocated
number of threads.

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* fix thread_number and a test

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* added choice of "--thread [N]" or defaults to os::num_cpu()

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* added unique explicit --build-dir to the compiler

and also print the c3c command line for debugging

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* disable "run compiler tests" for msvc-debug build

it takes like 1:30hs

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* buffer printouts and correct ordering of tests

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* added progress bar

- removed some \r carrier return stuff

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Fix bug in fixed pool. Improve progress bar

* Add color to bar.

* Some renaming.

* fix some leaky leaks

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* each test output is printed immediately

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* Formatting, remove comment. Re-enable MSVC debug test.

---------

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-21 14:34:16 +01:00
Christoffer Lerno
5c4197debd Refactoring of decl_register 2025-12-21 14:10:06 +01:00
Christoffer Lerno
41261d40b2 - Incorrectly using LLVMStructType when emitting dynamic functions on MachO #2666 2025-12-20 21:20:35 +01:00
Zack Puhl
48ecceab54 Add Streebog-256 and -512 Hashing (aka "GOST-12") (#2659)
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-20 19:40:22 +01:00
Zack Puhl
0d9547a388 Add RIPEMD Hashing to stdlb (#2663)
* Add RIPE-MD Hashing to stdlib
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-20 19:39:12 +01:00
Manu Linares
d2f59c5b3f Linux: implement signal stacktrace using SA_SIGINFO + sigaltstack (#2653)
* Linux: implement signal stacktrace using SA_SIGINFO + sigaltstack

Adds proper signal handlers for SIGSEGV/SIGBUS/SIGILL on Linux,
enables backtraces from signal context, and exits with correct POSIX
signal codes (128+signal). Fixes missing "signal stacktrace" support

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* defer libc::dlclose(handle);

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* fix double backtrace on panic

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* add guards for Linux X86_64

- remove comments
- uncomment MContext_t for Linux AARCH64

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

* fix guards, missed in two places

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>

---------

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-20 03:07:23 +01:00
limit-ordinal
85166bc706 Add NetBSD Support (#2661)
* Add NetBSD Support

Includes:
- Hints to find non-compatibility libc functions
- Struct and constant definitions for sockets, polling, etc.
- Changes to the linker code to work around some quirks in the NetBSD dynamic linker
- A target triple for netbsd aarch64 so llvm builds/links the compiler properly on this platform

* Updated releasenotes and some compacting

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-19 19:23:06 +01:00
Christoffer Lerno
bf26898645 Fixed test. 2025-12-19 19:01:19 +01:00
Zack Puhl
1f2bcd5462 Implement BLAKE2 Hashing in stdlib (#2648)
* Implement BLAKE2 Hashing in stdlib

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-19 17:22:54 +01:00
Christoffer Lerno
4e6cd4283c - Remove use of LLVMGetGlobalContext for single module compilation.
- Fixed bug where constants would get modified when slicing them. #2660
2025-12-19 17:17:45 +01:00
Zack Puhl
9aec5de105 [stdlib] Add CT @in Macro/Builtin (#2662)
* [stdlib] Add CT `@in` Macro/Builtin
2025-12-19 15:55:20 +01:00
Manu Linares
dec49b05b8 Fix: Correct precision calculation for floating point formatting (#2657)
* Fix: Correct precision calculation for floating point formatting

---------

Signed-off-by: Manuel Barrio Linares <mbarriolinares@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-19 15:51:42 +01:00
Christoffer Lerno
29bcd2c96e Update libc test 2025-12-18 09:53:38 +01:00
Zack Puhl
97a9cab218 Fix ChaCha20 Alignment Issues 2025-12-18 09:07:51 +01:00
Christoffer Lerno
436af4dbca Re-enable OpenBSD 2025-12-17 20:11:14 +01:00
Christoffer Lerno
b08a2a2c0f Disable openbsd 2025-12-17 17:21:32 +01:00
Christoffer Lerno
b43dac24c4 Fix incorrect alignment in ChaCha20 2025-12-17 15:38:51 +01:00
Zack Puhl
bae0f0f579 Implement ChaCha20 Crypto in stdlib (#2643)
* ChaCha20 implementation, first pass

* fix bug with clone_slice when length is 0

* final ChaCha20 crypto tidying

* final adjustments; add benchmark

* add guards everywhere else or w/e

* stdlib 'i++' conformity

* release notes & security warning updates

* update tests; cleanup; default counter should be 0 not 1

* remove prints in test file

* add extra unit tests for unaligned buffers

Co-authored-by: Manu Linares <mbarriolinares@gmail.com>

* one final alignment test

* nice contraction of tests w/ some paranoia sprinkled in

* nearly double the efficiency of chacha20's transform

Co-authored-by: Manu Linares <mbarriolinares@gmail.com>

* fix memory leak in test case

* improve one of the unit tests to cover more cases

* greatly simplify chacha20 'transform'

Co-authored-by: Manu Linares <mbarriolinares@gmail.com>

---------

Co-authored-by: Manu Linares <mbarriolinares@gmail.com>
2025-12-17 15:10:45 +01:00
Zack Puhl
8055c340f6 Add Implicit @pool for Each Unit Test Invocation (#2654)
* Add Implicit `@pool` for Each Unit Test Invocation
2025-12-17 15:08:02 +01:00
Christoffer Lerno
00c1210625 Rename append_char_buffer 2025-12-16 22:11:58 +01:00
Christoffer Lerno
070b13c81c More fixes to $$LINE 2025-12-16 21:48:35 +01:00
Christoffer Lerno
415c9639e7 - Deprecated DString.append_chars, use DString.append_string
- Deprecated `DString.append_string` for DStrings, use `DString.append_dstring` instead.
- Added `DString.append_char_buffer`.
2025-12-16 20:17:54 +01:00
Christoffer Lerno
996f8a6a4d Shuffle names as <-> to 2025-12-16 16:15:01 +01:00
Christoffer Lerno
e49e3e32e7 - Fix error message when a method has the wrong type for the first argument.
- Add `any.to` and `any.as`.
2025-12-16 16:02:56 +01:00
Christoffer Lerno
86a05ba6c0 Fix issue with inlining span. 2025-12-16 14:22:25 +01:00
Christoffer Lerno
651735f9a0 - $$LINE would sometimes incorrectly be constant. 2025-12-16 13:30:51 +01:00
Michael Chernigin
759389066f Add Elf32_Shdr and Elf64_Shdr to std::os::linux 2025-12-16 00:17:14 +01:00
Christoffer Lerno
3577a2d6b8 Updated tests and error message for failed generic lookups. 2025-12-13 18:51:45 +01:00
Christoffer Lerno
b63886b879 Fix test 2025-12-13 18:06:16 +01:00
Christoffer Lerno
466d3bc1b6 - Hard limit of 127 characters for identifiers. 2025-12-13 03:51:39 +01:00
Lexi
2a2c0f5d91 Add a leniency flag to String.unescape(), and fix a memory leak in String.(un)escape() (#2640)
* fix memory leak in string_escape.c3
* add unescape_lenient and tunescape_lenient
* Optimize for the use of the temp allocator.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-13 00:49:01 +01:00
Christoffer Lerno
0c0d0ace4d Fix requirement to use prefix when using $defined 2025-12-13 00:38:19 +01:00
Zack Puhl
a0ab10c23e Add Poly1305 Universal Hashing Algorithm (MAC) (#2639)
* Add Poly1305 Universal Hashing Algorithm (MAC)
2025-12-12 23:08:49 +01:00
DylanDoesProgramming
0685260454 add --linux-libc=host option and set default to host (#2636)
* added `--linux-libc=host` and set default to `default_linux` from `src/build/builder.c`

* updated `resources/project_schema.json` for `linux-libc` host build option

* Update how libc is set.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-12 22:53:01 +01:00
Christoffer Lerno
15662bd32f Typedefs and structs with inline types supporting lengthof would not work with lengthof #2641. 2025-12-12 20:57:32 +01:00
Christoffer Lerno
475816251b - Strings assigned to longer arrays would crash codegen, e.g. char[10] x = "abcd. 2025-12-12 20:35:11 +01:00
Lexi
bf62d11a73 Improve the error message for miscasing types in the last argument of a method (#2629)
* Improve the error message for miscasing types in the last argument of a method
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-11 16:46:56 +01:00
Christoffer Lerno
37d42d555d - Compiler crash when concatenating structs and arrays to an untyped list. 2025-12-11 03:46:15 +01:00
Christoffer Lerno
1fc522ec9c - Subscripting of constant slices would sometimes be considered non-constant #2635. 2025-12-10 14:11:51 +01:00
Christoffer Lerno
3a8a6aa429 - Ignore const null check on deref in $defined and $sizeof #2633. 2025-12-10 00:02:24 +01:00
Technical Fowl
e15ee23925 Matrix math cleanup (#2620)
* Cleanup inconsistent matrix math functions
* Add more matrix unit tests
2025-12-07 00:08:51 +01:00
Savino Pio Liguori
85657c9200 Added "implement-libc" feature. (#2564)
This feature adds an option from project.json and build options to disable libc unreachable statements in order to be able to implement one for yourself. Useful feature for bare metal developing.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-07 00:07:54 +01:00
Sander van den Bosch
3f20e5af1d add join for ThreadPool without destroying the threads (#2579)
* add join for ThreadPool without destroying the threads
* Make the main Thread block waiting for the worker threads to finish instead of buzy looping and do proper initialization and freeing of all variables.
* Updated test to use `atomic_store` and  take into account the maximum queue size of the threadpool.
* - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads.
- Return of Thread/Mutex/CondVar `destroy()` is now "@maydiscard" and should be ignored. It will return void in 0.8.0.
- Return of Mutex `unlock()` and `lock()` is now "@maydiscard" and should be ignored. They will return void in 0.8.0.
- Return of ConditionVariable `signal()` `broadcast()` and `wait()` are now "@maydiscard". They will return void in 0.8.0.
- Return of Thread `detatch()` is now "@maydiscard". It will return void in 0.8.0.
- Buffered/UnbufferedChannel, and both ThreadPools have `@maydiscard` on a set of functions. They will retunr void in 0.8.0.
- Pthread bindings correctly return Errno instead of CInt.
- Return of Thread `join()` is now "@maydiscard".

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-06 23:54:04 +01:00
Christoffer Lerno
18e2838772 - Hex escapes like "\x80" would be incorrectly lowered. #2623 2025-12-06 18:24:15 +01:00
Walther Chen
f023db8638 fix ByteBuffer.grow (#2622)
* fix ByteBuffer.grow

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-12-06 17:57:11 +01:00
Christoffer Lerno
cb0b94c064 - Optional does not play well with bit ops #2618. 2025-12-06 00:46:42 +01:00
Christoffer Lerno
7087665ccc Updated grammar 2025-12-05 23:00:58 +01:00
Christoffer Lerno
1793dd7f0b Correctly detect incorrect use of ~ and ^ 2025-12-05 20:42:37 +01:00
Christoffer Lerno
a61fd6d280 - Casting bitstruct to wider base type should be single step #2616.
- Experimental accept of ~ and ^ for optionals.
2025-12-05 20:18:32 +01:00
Christoffer Lerno
034a048c8a - Regression with npot vector in struct triggering an assert #2219. 2025-12-05 17:26:22 +01:00
Christoffer Lerno
608fc4df9f Update to version 0.7.9. Updated grammar. 2025-12-05 15:30:50 +01:00
554 changed files with 16883 additions and 5474 deletions

15
.clang-format Normal file
View 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

View File

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

File diff suppressed because it is too large Load Diff

6
.gitignore vendored
View File

@@ -73,8 +73,12 @@ out/
/cmake-build-debug/
/cmake-build-release/
# Emacs files
# etags(Emacs), ctags, gtags
TAGS
GPATH
GRTAGS
GTAGS
tags
# Clangd LSP files
/.cache/

View File

@@ -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,6 +95,7 @@ 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(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")
@@ -142,11 +161,11 @@ if(C3_WITH_LLVM)
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
URL https://github.com/c3lang/win-llvm/releases/download/llvm_21_1_8/llvm-21.1.8-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
URL https://github.com/c3lang/win-llvm/releases/download/llvm_21_1_8/llvm-21.1.8-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
@@ -273,7 +292,12 @@ if(C3_WITH_LLVM)
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.
@@ -435,10 +459,18 @@ 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)
@@ -552,13 +584,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 +612,7 @@ else()
-Wno-unused-function
-Wno-unused-variable
-Wno-unused-parameter
-Wno-char-subscripts
)
target_link_options(c3c PRIVATE -pthread)
endif()

View File

@@ -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.8**.
The current stable version of the compiler is **version 0.7.9**.
The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis.
@@ -384,7 +384,7 @@ scoop install c3
#### Getting started with a "hello world"
Create a `main.c3` file with:
```c++
```c3
module hello_world;
import std::io;
@@ -513,7 +513,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
@@ -523,7 +523,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

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

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

View 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[..]);
}

View 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[..]);
}

View File

@@ -1,7 +1,7 @@
// 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 <Type>;
struct Atomic
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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;
@@ -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~;
}
<*
@@ -275,7 +275,7 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
fn void? HashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
if (!map.remove_entry_for_key(key)) return NOT_FOUND~;
}
fn void HashMap.clear(&map)

View File

@@ -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"
@@ -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 (!set.remove_entry_for_value(value)) return NOT_FOUND~;
}
fn usz HashSet.remove_all(&set, Value[] values)
@@ -634,7 +634,7 @@ fn Value? HashSetIterator.next(&self)
}
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn usz HashSetIterator.len(&self) @operator(len)

View File

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

View File

@@ -1,4 +1,4 @@
module std::collections::blockingqueue { Value };
module std::collections::blockingqueue <Value>;
import std::thread, std::time;
@@ -70,9 +70,9 @@ 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
@@ -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, {
@@ -137,7 +137,7 @@ fn void LinkedBlockingQueue.push(&self, Value value)
self.link_entry(entry);
// Signal that queue is no longer empty
self.not_empty.signal()!!;
self.not_empty.signal();
};
}
@@ -153,7 +153,7 @@ 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();
@@ -161,7 +161,7 @@ fn Value LinkedBlockingQueue.poll(&self)
allocator::free(self.allocator, entry);
if (self.capacity > 0)
{
self.not_full.signal()!!;
self.not_full.signal();
}
return value;
};
@@ -178,7 +178,7 @@ 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();
Value value = entry.value;
@@ -186,7 +186,7 @@ fn Value? LinkedBlockingQueue.pop(&self)
if (self.capacity > 0)
{
self.not_full.signal()!!;
self.not_full.signal();
}
return value;
};
@@ -214,7 +214,7 @@ 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();
@@ -224,7 +224,7 @@ fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
// Must signal not_full after removing an item
if (self.capacity > 0)
{
self.not_full.signal()!!;
self.not_full.signal();
}
return value;
};
@@ -266,7 +266,7 @@ 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,
@@ -274,7 +274,7 @@ fn void? LinkedBlockingQueue.try_push(&self, Value value)
.prev = null
});
self.link_entry(entry);
self.not_empty.signal()!!;
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, {
@@ -308,19 +308,19 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
.prev = null
});
self.link_entry(entry);
self.not_empty.signal()!!;
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~;
};
}

View File

@@ -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;
@@ -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~;
}
<*
@@ -248,7 +248,7 @@ fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
if (!map.remove_entry_for_key(key)) return NOT_FOUND~;
}
fn void LinkedHashMap.clear(&map)
@@ -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)

View File

@@ -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"
@@ -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 (!set.remove_entry_for_value(value)) return NOT_FOUND~;
}
fn usz LinkedHashSet.remove_all(&set, Value[] values)
@@ -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)

View File

@@ -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~;
}
@@ -296,7 +296,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
fn Type? LinkedList.pop(&self)
{
if (!self._last) return NO_MORE_ELEMENT?;
if (!self._last) return NO_MORE_ELEMENT~;
defer self.unlink_last();
return self._last.value;
}
@@ -308,20 +308,20 @@ fn bool LinkedList.is_empty(&self)
fn Type? LinkedList.pop_front(&self)
{
if (!self._first) return NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT~;
defer self.unlink_first();
return self._first.value;
}
fn void? LinkedList.remove_last(&self) @maydiscard
{
if (!self._first) return NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT~;
self.unlink_last();
}
fn void? LinkedList.remove_first(&self) @maydiscard
{
if (!self._first) return NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT~;
self.unlink_first();
}

View File

@@ -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);
@@ -115,7 +115,7 @@ fn void List.push(&self, Type element) @inline
fn Type? List.pop(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
defer self.set_size(self.size - 1);
return self.entries[self.size - 1];
}
@@ -127,7 +127,7 @@ fn void List.clear(&self)
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];
}
@@ -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?;
if (!self.size) return NO_MORE_ELEMENT~;
self.set_size(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];
}
@@ -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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,11 +63,10 @@ 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")
{
Type1 first;
Type2 second;
}
}

View File

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

View File

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

View File

@@ -153,7 +153,7 @@ fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
if (catch err = page)
{
allocator::free(self.backing_allocator, mem);
return err?;
return err~;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);

View File

@@ -23,22 +23,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,9 +49,9 @@ 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?;
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 (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return mem::OUT_OF_MEMORY~;
$switch:
$case env::DARWIN:
@@ -83,12 +83,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 +99,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 +122,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 +141,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~;
}

View File

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

View File

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

View File

@@ -1,4 +1,5 @@
module std::core::string::ansi;
import std::io;
enum Ansi : const inline String
{
@@ -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);
}

View File

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

View File

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

View File

@@ -90,33 +90,35 @@ bitstruct UInt128LE : uint128 @littleendian
<*
@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)

View File

@@ -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;
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
@@ -143,7 +181,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore, void *added_ba
{
backtraces[++backtraces_to_ignore] = added_backtrace;
}
@stack_mem(2048; Allocator mem)
@stack_mem(4096; Allocator mem)
{
BacktraceList? backtrace = backtrace::symbolize_backtrace(mem, backtraces);
if (catch backtrace) return false;
@@ -174,6 +212,7 @@ fn bool print_backtrace(String message, int backtraces_to_ignore, void *added_ba
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))
{
@@ -190,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)
{
@@ -315,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~;
}
<*
@@ -332,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~;
}
<*
@@ -416,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);
@@ -488,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;
}
@@ -532,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;
@@ -651,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]);
@@ -963,7 +1016,7 @@ fn void sig_bus_error(CInt i, void* info, void* context)
}
$endif
$endif
$$trap();
os::fastexit(128 + i);
}
fn void sig_segmentation_fault(CInt i, void* p1, void* context)
@@ -978,15 +1031,39 @@ fn void sig_segmentation_fault(CInt i, void* p1, void* context)
}
$endif
$endif
$$trap();
os::fastexit(128 + i);
}
fn void sig_illegal_instruction(CInt i, void* p1, void* context)
{
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)
{
$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);
}

View File

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

View File

@@ -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 $typeof(str) == String || $typeof(str) == DString : "Expected string or DString"
*>
macro void DString.append_string(&self, str)
{
$if $typeof(str) == String:
self.append_bytes(str);
$else
self.append_string_deprecated(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

View File

@@ -124,7 +124,8 @@ 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 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 +154,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
{

View File

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

View File

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

View File

@@ -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);
@@ -556,12 +583,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

View File

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

View File

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

View File

@@ -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, &section.sectname)) return section;
section++;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type)

View File

@@ -12,7 +12,10 @@ macro int @main_to_err_main(#m, int, char**)
if (catch #m()) return 1;
return 0;
}
macro int @main_to_int_main(#m, int, char**) => #m();
macro int @main_to_int_main(#m, int, char**)
{
return #m();
}
macro int @main_to_void_main(#m, int, char**)
{
#m();
@@ -63,6 +66,17 @@ 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
{
// 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);
}
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @cname("CommandLineToArgvW");
@@ -93,18 +107,26 @@ macro void release_wargs(String[] list) @private
macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
win32_set_utf8_codepage();
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_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
win32_set_utf8_codepage();
return #m();
}
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
win32_set_utf8_codepage();
#m();
return 0;
}
macro int @win_to_err_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;
@@ -113,6 +135,7 @@ macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd
macro int @win_to_int_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);
return #m(args);
@@ -120,6 +143,7 @@ macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd
macro int @win_to_void_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);
#m(args);
@@ -128,6 +152,7 @@ macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cm
macro int @win_to_err_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);
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
@@ -136,6 +161,7 @@ macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line
macro int @win_to_int_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(handle, prev_handle, args, show_cmd);
@@ -143,6 +169,7 @@ macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line
macro int @win_to_void_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);
#m(handle, prev_handle, args, show_cmd);
@@ -151,6 +178,7 @@ macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_lin
macro int @wmain_to_err_main_args(#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;
@@ -159,6 +187,7 @@ macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
{
win32_set_utf8_codepage();
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args);
@@ -166,6 +195,7 @@ macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
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;
@@ -173,6 +203,7 @@ macro int @_wmain_runner(#m, int argc, Char16** argv)
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
{
win32_set_utf8_codepage();
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
#m(args);

View File

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

View File

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

View File

@@ -27,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;
@@ -185,8 +187,6 @@ fn void unmute_output(bool has_error) @local
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.");
@@ -204,6 +204,8 @@ $endif
{
.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(),
@@ -218,9 +220,9 @@ $endif
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;
@@ -261,7 +263,7 @@ $endif
test_context = &context;
log::set_priority_all(test_context.log_level);
if (sort_tests)
if (context.sort)
{
quicksort(tests, &cmp_test_unit);
}
@@ -321,14 +323,17 @@ $endif
{
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())

View File

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

View File

@@ -120,7 +120,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..];
};
@@ -190,13 +190,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 +214,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 +368,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 +427,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 +445,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 +526,7 @@ fn usz? String.index_of_char(self, char character)
{
if (c == character) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -536,7 +549,7 @@ fn usz? String.index_of_chars(String self, char[] characters)
}
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -553,12 +566,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 +590,7 @@ fn usz? String.rindex_of_char(self, char character)
{
if (c == character) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -602,7 +615,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 +640,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 +1026,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 +1039,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 +1062,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,18 +1072,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)
{
value = value.overflow_mul(base_used).overflow_sub(c) ?? INTEGER_OVERFLOW?!;
value = value.overflow_mul(base_used).overflow_sub(c) ?? INTEGER_OVERFLOW~!;
break;
}
value = value.overflow_mul(base_used).overflow_add(c) ?? INTEGER_OVERFLOW?!;
value = value.overflow_mul(base_used).overflow_add(c) ?? INTEGER_OVERFLOW~!;
};
}
return value;
@@ -1184,16 +1197,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];
}
@@ -1205,7 +1223,7 @@ fn String? Splitter.next(&self)
if (!next && self.type == TOKENIZE) continue;
return remaining[:next];
}
self.current = len;
self.current = len + 1;
return remaining;
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -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();
@@ -329,8 +329,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 +345,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 +357,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,7 +367,7 @@ 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;

233
lib/std/crypto/chacha20.c3 Normal file
View 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(&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)
{
self.mutate_keystream();
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)
{
self.mutate_keystream();
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;

View File

@@ -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..];

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
module std::experimental::scheduler{Event};
module std::experimental::scheduler <Event>;
import std::collections, std::thread, std::time;
struct DelayedSchedulerEvent @local
@@ -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~;
};
}
}

View File

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

383
lib/std/hash/blake2.c3 Normal file
View File

@@ -0,0 +1,383 @@
// 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
if (#instance.last_node) #instance.f[1] = $typeof(#instance.h[0]).max;
#instance.f[0] = $typeof(#instance.h[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) @local @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)
=> @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) @local @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);

747
lib/std/hash/blake3.c3 Normal file
View File

@@ -0,0 +1,747 @@
// 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;
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 @init
{
$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. *>
enum Blake3Flags : const 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 Blake3.reset(&self) @local @inline
{
self.chunk.reset(self.key[..], 0);
self.cv_stack_len = 0;
}
<*
Private function to merge tree results.
*>
fn void Blake3.merge_cv_stack(&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) @local @inline
{
self.merge_cv_stack(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);
self.merge_cv_stack(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) @local @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) @local @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) @local @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) @local @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) @local @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) @local
{
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) @local @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) @local
{
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) @local
{
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;
@round(state, &block_words[0], 0);
@round(state, &block_words[0], 1);
@round(state, &block_words[0], 2);
@round(state, &block_words[0], 3);
@round(state, &block_words[0], 4);
@round(state, &block_words[0], 5);
@round(state, &block_words[0], 6);
}
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;
}

View File

@@ -0,0 +1,169 @@
// 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;
enum StreebogLength : const 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;
ulong[8] unprocessed_bits_count;
usz index = self.index >> 3;
usz shift = (self.index & 0b111) * 8;
unprocessed_bits_count[0] = self.index * 8;
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);
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;
}

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

View File

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

View File

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

View File

@@ -108,7 +108,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;
}

171
lib/std/hash/poly1305.c3 Normal file
View 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];
self.blocks(self.temp[..]);
input = input[rem..];
}
usz even_length = input.len - (input.len % BLOCK_SIZE);
if (even_length >= BLOCK_SIZE)
{
self.blocks(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
self.blocks(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 Poly1305.blocks(&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);
}
}

577
lib/std/hash/ripemd.c3 Normal file
View File

@@ -0,0 +1,577 @@
// 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 std::hash::ripemd;
const usz[4] PERMISSIBLE_SIZES_BITS = { 128, 160, 256, 320 };
<* Unchanging block size. *>
const BLOCK_SIZE = 64;
<* RIPE-MD initial values. *>
const uint[10] H = {
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0,
0x76543210, 0xfedcba98, 0x89abcdef, 0x01234567, 0x3c2d1e0f
};
<* RIPE-MD constant values. *>
const uint[9] K = {
0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e,
0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9
};
<*
@require @in(DIGEST_BITS, ...PERMISSIBLE_SIZES_BITS) : "Invalid DIGEST_BITS; must be one of {128, 160, 256, 320}."
*>
module std::hash::ripemd <DIGEST_BITS>;
<* Ultimate digest's size in bytes. *>
const DIGEST_BYTES = DIGEST_BITS / 8;
struct RipeMd
{
uint[10] state;
uint[16] buffer;
ulong byte_count;
}
<*
Compute the RIPE-MD hash of the given data.
@param [in] data : "The data to hash."
*>
macro char[*] hash(char[] data)
{
RipeMd r @noinit;
r.init();
r.update(data);
return r.final();
}
<*
Initialize the RIPE-MD hash context.
*>
fn void RipeMd.init(&self)
{
mem::zero_volatile(@as_char_view(*self));
self.state[..] = H[..];
// I don't care about the reason, I want to strangle whoever thought this was a good idea.
// I get it, you have to set the lane initializers correctly, but damn I want my hours back.
$if DIGEST_BITS == 256: self.state[4:4] = self.state[5:4]; $endif
}
<*
Update the RIPE-MD hash context with the given data.
@param [in] data : "The data to digest."
*>
fn void RipeMd.update(&self, char[] data)
{
usz $bufsz = $sizeof(self.buffer);
uint avail = $bufsz - (uint)(self.byte_count & 0x3f);
self.byte_count += data.len;
if (avail > data.len)
{
@as_char_view(self.buffer)[($bufsz - avail) : data.len] = data[..];
return;
}
@as_char_view(self.buffer)[($bufsz - avail) : avail] = data[:avail];
self.transform(self.buffer);
data = data[avail..];
for (bool is_aligned = 0 == (usz)data.ptr % uint.sizeof; data.len >= $bufsz; data = data[$bufsz..])
{
if (is_aligned) // when aligned, this optimization is a ~10-20% performance boost
{
self.transform(((uint*)data.ptr)[:$bufsz / uint.sizeof]);
}
else
{
@as_char_view(self.buffer)[:$bufsz] = data[:$bufsz];
self.transform(self.buffer);
}
}
@as_char_view(self.buffer)[:data.len] = data[..];
}
<*
Finalize the RIPE-MD hash context and return the final hash value.
This implicitly destroys the underlying hash structure for the sake of security.
*>
fn char[DIGEST_BYTES] RipeMd.final(&self)
{
char[DIGEST_BYTES] result @align(uint.sizeof);
static char[64] padding = { [0] = 0x80, [1..63] = 0x00 };
// Napkin maffs.
ulong bits = (ulong)self.byte_count << 3;
uint index = (uint)self.byte_count & 0x3f;
uint padlen = (index < 56) ? (56 - index) : ((64 + 56) - index);
// Update with padding, then append bit-length of digested message.
self.update(padding[:padlen]);
self.update(@as_char_view(bits));
// Clean up implicitly.
defer mem::zero_volatile(@as_char_view(*self));
// Copy and return.
((uint*)&result)[:DIGEST_BYTES / uint.sizeof] = self.state[:DIGEST_BYTES / uint.sizeof];
return result;
}
// State-based permutation functions given as a function pointer to each computation lane.
fn uint f1(uint x, uint y, uint z) @inline @local => x ^ y ^ z;
fn uint f2(uint x, uint y, uint z) @inline @local => z ^ (x & (y ^ z));
fn uint f3(uint x, uint y, uint z) @inline @local => (x | ~y) ^ z;
fn uint f4(uint x, uint y, uint z) @inline @local => y ^ (z & (x ^ y));
fn uint f5(uint x, uint y, uint z) @inline @local => x ^ (y | ~z);
// The primary workhorse of the digest function. Behavior only changes slightly.
// All 128/256 inputs will expand with 'e' as 0 - they don't use it anyway.
macro @round(#a, b, #c, d, e, #func, k, x, s) @local
{
#a += #func(b, #c, d) + x + k;
$switch:
$case DIGEST_BITS == 128 ||| DIGEST_BITS == 256:
#a = #a.rotl(s);
$case DIGEST_BITS == 160 ||| DIGEST_BITS == 320:
#a = #a.rotl(s) + e;
#c = #c.rotl(10);
$default:
$error "Invalid digest bits";
$endswitch
}
<*
Apply a transformation to some input data, but DO NOT inline the contents of the macro within the parent call.
@param in : "Data or message block to process."
*>
fn void RipeMd.transform(&self, uint[BLOCK_SIZE / uint.sizeof] in) @noinline => self.@transform(in);
<*
@param in : "Data or message block to process."
*>
macro RipeMd.@transform(&self, uint[BLOCK_SIZE / uint.sizeof] in) @local
{
uint aa, bb, cc, dd, ee, aaa, bbb, ccc, ddd, eee;
uint[] state = self.state[..];
// Run computations lanes based on the size of the resulting digest.
// The behavior is different enough for each that this can simply stay broken out into separate cases.
// Otherwise, lacing them all together is just not worth the effort and isn't as clear to other developers.
// (No shade, but see 'RustCrypto/hashes' RIPE-MD macros for an example of what I mean...)
$switch DIGEST_BITS:
$case 128:
aa = state[0];
bb = state[1];
cc = state[2];
dd = state[3];
aaa = state[0];
bbb = state[1];
ccc = state[2];
ddd = state[3];
@round(aa, bb, cc, dd, 0, f1, K[0], in[0], 11); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[5], 8);
@round(dd, aa, bb, cc, 0, f1, K[0], in[1], 14); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[14], 9);
@round(cc, dd, aa, bb, 0, f1, K[0], in[2], 15); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[7], 9);
@round(bb, cc, dd, aa, 0, f1, K[0], in[3], 12); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[0], 11);
@round(aa, bb, cc, dd, 0, f1, K[0], in[4], 5); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[9], 13);
@round(dd, aa, bb, cc, 0, f1, K[0], in[5], 8); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[2], 15);
@round(cc, dd, aa, bb, 0, f1, K[0], in[6], 7); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[11], 15);
@round(bb, cc, dd, aa, 0, f1, K[0], in[7], 9); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[4], 5);
@round(aa, bb, cc, dd, 0, f1, K[0], in[8], 11); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[13], 7);
@round(dd, aa, bb, cc, 0, f1, K[0], in[9], 13); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[6], 7);
@round(cc, dd, aa, bb, 0, f1, K[0], in[10], 14); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[15], 8);
@round(bb, cc, dd, aa, 0, f1, K[0], in[11], 15); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[8], 11);
@round(aa, bb, cc, dd, 0, f1, K[0], in[12], 6); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[1], 14);
@round(dd, aa, bb, cc, 0, f1, K[0], in[13], 7); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[10], 14);
@round(cc, dd, aa, bb, 0, f1, K[0], in[14], 9); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[3], 12);
@round(bb, cc, dd, aa, 0, f1, K[0], in[15], 8); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[12], 6);
@round(aa, bb, cc, dd, 0, f2, K[1], in[7], 7); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[6], 9);
@round(dd, aa, bb, cc, 0, f2, K[1], in[4], 6); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[11], 13);
@round(cc, dd, aa, bb, 0, f2, K[1], in[13], 8); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[3], 15);
@round(bb, cc, dd, aa, 0, f2, K[1], in[1], 13); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[7], 7);
@round(aa, bb, cc, dd, 0, f2, K[1], in[10], 11); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[0], 12);
@round(dd, aa, bb, cc, 0, f2, K[1], in[6], 9); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[13], 8);
@round(cc, dd, aa, bb, 0, f2, K[1], in[15], 7); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[5], 9);
@round(bb, cc, dd, aa, 0, f2, K[1], in[3], 15); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[10], 11);
@round(aa, bb, cc, dd, 0, f2, K[1], in[12], 7); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[14], 7);
@round(dd, aa, bb, cc, 0, f2, K[1], in[0], 12); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[15], 7);
@round(cc, dd, aa, bb, 0, f2, K[1], in[9], 15); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[8], 12);
@round(bb, cc, dd, aa, 0, f2, K[1], in[5], 9); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[12], 7);
@round(aa, bb, cc, dd, 0, f2, K[1], in[2], 11); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[4], 6);
@round(dd, aa, bb, cc, 0, f2, K[1], in[14], 7); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[9], 15);
@round(cc, dd, aa, bb, 0, f2, K[1], in[11], 13); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[1], 13);
@round(bb, cc, dd, aa, 0, f2, K[1], in[8], 12); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[2], 11);
@round(aa, bb, cc, dd, 0, f3, K[2], in[3], 11); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[15], 9);
@round(dd, aa, bb, cc, 0, f3, K[2], in[10], 13); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[5], 7);
@round(cc, dd, aa, bb, 0, f3, K[2], in[14], 6); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[1], 15);
@round(bb, cc, dd, aa, 0, f3, K[2], in[4], 7); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[3], 11);
@round(aa, bb, cc, dd, 0, f3, K[2], in[9], 14); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[7], 8);
@round(dd, aa, bb, cc, 0, f3, K[2], in[15], 9); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[14], 6);
@round(cc, dd, aa, bb, 0, f3, K[2], in[8], 13); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[6], 6);
@round(bb, cc, dd, aa, 0, f3, K[2], in[1], 15); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[9], 14);
@round(aa, bb, cc, dd, 0, f3, K[2], in[2], 14); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[11], 12);
@round(dd, aa, bb, cc, 0, f3, K[2], in[7], 8); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[8], 13);
@round(cc, dd, aa, bb, 0, f3, K[2], in[0], 13); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[12], 5);
@round(bb, cc, dd, aa, 0, f3, K[2], in[6], 6); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[2], 14);
@round(aa, bb, cc, dd, 0, f3, K[2], in[13], 5); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[10], 13);
@round(dd, aa, bb, cc, 0, f3, K[2], in[11], 12); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[0], 13);
@round(cc, dd, aa, bb, 0, f3, K[2], in[5], 7); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[4], 7);
@round(bb, cc, dd, aa, 0, f3, K[2], in[12], 5); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[13], 5);
@round(aa, bb, cc, dd, 0, f4, K[3], in[1], 11); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[8], 15);
@round(dd, aa, bb, cc, 0, f4, K[3], in[9], 12); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[6], 5);
@round(cc, dd, aa, bb, 0, f4, K[3], in[11], 14); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[4], 8);
@round(bb, cc, dd, aa, 0, f4, K[3], in[10], 15); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[1], 11);
@round(aa, bb, cc, dd, 0, f4, K[3], in[0], 14); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[3], 14);
@round(dd, aa, bb, cc, 0, f4, K[3], in[8], 15); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[11], 14);
@round(cc, dd, aa, bb, 0, f4, K[3], in[12], 9); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[15], 6);
@round(bb, cc, dd, aa, 0, f4, K[3], in[4], 8); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[0], 14);
@round(aa, bb, cc, dd, 0, f4, K[3], in[13], 9); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[5], 6);
@round(dd, aa, bb, cc, 0, f4, K[3], in[3], 14); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[12], 9);
@round(cc, dd, aa, bb, 0, f4, K[3], in[7], 5); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[2], 12);
@round(bb, cc, dd, aa, 0, f4, K[3], in[15], 6); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[13], 9);
@round(aa, bb, cc, dd, 0, f4, K[3], in[14], 8); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[9], 12);
@round(dd, aa, bb, cc, 0, f4, K[3], in[5], 6); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[7], 5);
@round(cc, dd, aa, bb, 0, f4, K[3], in[6], 5); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[10], 15);
@round(bb, cc, dd, aa, 0, f4, K[3], in[2], 12); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[14], 8);
ddd += cc + state[1];
state[1] = state[2] + dd + aaa;
state[2] = state[3] + aa + bbb;
state[3] = state[0] + bb + ccc;
state[0] = ddd;
$case 160:
aa = state[0];
bb = state[1];
cc = state[2];
dd = state[3];
ee = state[4];
aaa = state[0];
bbb = state[1];
ccc = state[2];
ddd = state[3];
eee = state[4];
@round(aa, bb, cc, dd, ee, f1, K[0], in[0], 11); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[5], 8);
@round(ee, aa, bb, cc, dd, f1, K[0], in[1], 14); @round(eee, aaa, bbb, ccc, ddd, f5, K[5], in[14], 9);
@round(dd, ee, aa, bb, cc, f1, K[0], in[2], 15); @round(ddd, eee, aaa, bbb, ccc, f5, K[5], in[7], 9);
@round(cc, dd, ee, aa, bb, f1, K[0], in[3], 12); @round(ccc, ddd, eee, aaa, bbb, f5, K[5], in[0], 11);
@round(bb, cc, dd, ee, aa, f1, K[0], in[4], 5); @round(bbb, ccc, ddd, eee, aaa, f5, K[5], in[9], 13);
@round(aa, bb, cc, dd, ee, f1, K[0], in[5], 8); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[2], 15);
@round(ee, aa, bb, cc, dd, f1, K[0], in[6], 7); @round(eee, aaa, bbb, ccc, ddd, f5, K[5], in[11], 15);
@round(dd, ee, aa, bb, cc, f1, K[0], in[7], 9); @round(ddd, eee, aaa, bbb, ccc, f5, K[5], in[4], 5);
@round(cc, dd, ee, aa, bb, f1, K[0], in[8], 11); @round(ccc, ddd, eee, aaa, bbb, f5, K[5], in[13], 7);
@round(bb, cc, dd, ee, aa, f1, K[0], in[9], 13); @round(bbb, ccc, ddd, eee, aaa, f5, K[5], in[6], 7);
@round(aa, bb, cc, dd, ee, f1, K[0], in[10], 14); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[15], 8);
@round(ee, aa, bb, cc, dd, f1, K[0], in[11], 15); @round(eee, aaa, bbb, ccc, ddd, f5, K[5], in[8], 11);
@round(dd, ee, aa, bb, cc, f1, K[0], in[12], 6); @round(ddd, eee, aaa, bbb, ccc, f5, K[5], in[1], 14);
@round(cc, dd, ee, aa, bb, f1, K[0], in[13], 7); @round(ccc, ddd, eee, aaa, bbb, f5, K[5], in[10], 14);
@round(bb, cc, dd, ee, aa, f1, K[0], in[14], 9); @round(bbb, ccc, ddd, eee, aaa, f5, K[5], in[3], 12);
@round(aa, bb, cc, dd, ee, f1, K[0], in[15], 8); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[12], 6);
@round(ee, aa, bb, cc, dd, f2, K[1], in[7], 7); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[6], 9);
@round(dd, ee, aa, bb, cc, f2, K[1], in[4], 6); @round(ddd, eee, aaa, bbb, ccc, f4, K[6], in[11], 13);
@round(cc, dd, ee, aa, bb, f2, K[1], in[13], 8); @round(ccc, ddd, eee, aaa, bbb, f4, K[6], in[3], 15);
@round(bb, cc, dd, ee, aa, f2, K[1], in[1], 13); @round(bbb, ccc, ddd, eee, aaa, f4, K[6], in[7], 7);
@round(aa, bb, cc, dd, ee, f2, K[1], in[10], 11); @round(aaa, bbb, ccc, ddd, eee, f4, K[6], in[0], 12);
@round(ee, aa, bb, cc, dd, f2, K[1], in[6], 9); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[13], 8);
@round(dd, ee, aa, bb, cc, f2, K[1], in[15], 7); @round(ddd, eee, aaa, bbb, ccc, f4, K[6], in[5], 9);
@round(cc, dd, ee, aa, bb, f2, K[1], in[3], 15); @round(ccc, ddd, eee, aaa, bbb, f4, K[6], in[10], 11);
@round(bb, cc, dd, ee, aa, f2, K[1], in[12], 7); @round(bbb, ccc, ddd, eee, aaa, f4, K[6], in[14], 7);
@round(aa, bb, cc, dd, ee, f2, K[1], in[0], 12); @round(aaa, bbb, ccc, ddd, eee, f4, K[6], in[15], 7);
@round(ee, aa, bb, cc, dd, f2, K[1], in[9], 15); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[8], 12);
@round(dd, ee, aa, bb, cc, f2, K[1], in[5], 9); @round(ddd, eee, aaa, bbb, ccc, f4, K[6], in[12], 7);
@round(cc, dd, ee, aa, bb, f2, K[1], in[2], 11); @round(ccc, ddd, eee, aaa, bbb, f4, K[6], in[4], 6);
@round(bb, cc, dd, ee, aa, f2, K[1], in[14], 7); @round(bbb, ccc, ddd, eee, aaa, f4, K[6], in[9], 15);
@round(aa, bb, cc, dd, ee, f2, K[1], in[11], 13); @round(aaa, bbb, ccc, ddd, eee, f4, K[6], in[1], 13);
@round(ee, aa, bb, cc, dd, f2, K[1], in[8], 12); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[2], 11);
@round(dd, ee, aa, bb, cc, f3, K[2], in[3], 11); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[15], 9);
@round(cc, dd, ee, aa, bb, f3, K[2], in[10], 13); @round(ccc, ddd, eee, aaa, bbb, f3, K[7], in[5], 7);
@round(bb, cc, dd, ee, aa, f3, K[2], in[14], 6); @round(bbb, ccc, ddd, eee, aaa, f3, K[7], in[1], 15);
@round(aa, bb, cc, dd, ee, f3, K[2], in[4], 7); @round(aaa, bbb, ccc, ddd, eee, f3, K[7], in[3], 11);
@round(ee, aa, bb, cc, dd, f3, K[2], in[9], 14); @round(eee, aaa, bbb, ccc, ddd, f3, K[7], in[7], 8);
@round(dd, ee, aa, bb, cc, f3, K[2], in[15], 9); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[14], 6);
@round(cc, dd, ee, aa, bb, f3, K[2], in[8], 13); @round(ccc, ddd, eee, aaa, bbb, f3, K[7], in[6], 6);
@round(bb, cc, dd, ee, aa, f3, K[2], in[1], 15); @round(bbb, ccc, ddd, eee, aaa, f3, K[7], in[9], 14);
@round(aa, bb, cc, dd, ee, f3, K[2], in[2], 14); @round(aaa, bbb, ccc, ddd, eee, f3, K[7], in[11], 12);
@round(ee, aa, bb, cc, dd, f3, K[2], in[7], 8); @round(eee, aaa, bbb, ccc, ddd, f3, K[7], in[8], 13);
@round(dd, ee, aa, bb, cc, f3, K[2], in[0], 13); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[12], 5);
@round(cc, dd, ee, aa, bb, f3, K[2], in[6], 6); @round(ccc, ddd, eee, aaa, bbb, f3, K[7], in[2], 14);
@round(bb, cc, dd, ee, aa, f3, K[2], in[13], 5); @round(bbb, ccc, ddd, eee, aaa, f3, K[7], in[10], 13);
@round(aa, bb, cc, dd, ee, f3, K[2], in[11], 12); @round(aaa, bbb, ccc, ddd, eee, f3, K[7], in[0], 13);
@round(ee, aa, bb, cc, dd, f3, K[2], in[5], 7); @round(eee, aaa, bbb, ccc, ddd, f3, K[7], in[4], 7);
@round(dd, ee, aa, bb, cc, f3, K[2], in[12], 5); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[13], 5);
@round(cc, dd, ee, aa, bb, f4, K[3], in[1], 11); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[8], 15);
@round(bb, cc, dd, ee, aa, f4, K[3], in[9], 12); @round(bbb, ccc, ddd, eee, aaa, f2, K[8], in[6], 5);
@round(aa, bb, cc, dd, ee, f4, K[3], in[11], 14); @round(aaa, bbb, ccc, ddd, eee, f2, K[8], in[4], 8);
@round(ee, aa, bb, cc, dd, f4, K[3], in[10], 15); @round(eee, aaa, bbb, ccc, ddd, f2, K[8], in[1], 11);
@round(dd, ee, aa, bb, cc, f4, K[3], in[0], 14); @round(ddd, eee, aaa, bbb, ccc, f2, K[8], in[3], 14);
@round(cc, dd, ee, aa, bb, f4, K[3], in[8], 15); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[11], 14);
@round(bb, cc, dd, ee, aa, f4, K[3], in[12], 9); @round(bbb, ccc, ddd, eee, aaa, f2, K[8], in[15], 6);
@round(aa, bb, cc, dd, ee, f4, K[3], in[4], 8); @round(aaa, bbb, ccc, ddd, eee, f2, K[8], in[0], 14);
@round(ee, aa, bb, cc, dd, f4, K[3], in[13], 9); @round(eee, aaa, bbb, ccc, ddd, f2, K[8], in[5], 6);
@round(dd, ee, aa, bb, cc, f4, K[3], in[3], 14); @round(ddd, eee, aaa, bbb, ccc, f2, K[8], in[12], 9);
@round(cc, dd, ee, aa, bb, f4, K[3], in[7], 5); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[2], 12);
@round(bb, cc, dd, ee, aa, f4, K[3], in[15], 6); @round(bbb, ccc, ddd, eee, aaa, f2, K[8], in[13], 9);
@round(aa, bb, cc, dd, ee, f4, K[3], in[14], 8); @round(aaa, bbb, ccc, ddd, eee, f2, K[8], in[9], 12);
@round(ee, aa, bb, cc, dd, f4, K[3], in[5], 6); @round(eee, aaa, bbb, ccc, ddd, f2, K[8], in[7], 5);
@round(dd, ee, aa, bb, cc, f4, K[3], in[6], 5); @round(ddd, eee, aaa, bbb, ccc, f2, K[8], in[10], 15);
@round(cc, dd, ee, aa, bb, f4, K[3], in[2], 12); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[14], 8);
@round(bb, cc, dd, ee, aa, f5, K[4], in[4], 9); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[12], 8);
@round(aa, bb, cc, dd, ee, f5, K[4], in[0], 15); @round(aaa, bbb, ccc, ddd, eee, f1, 0, in[15], 5);
@round(ee, aa, bb, cc, dd, f5, K[4], in[5], 5); @round(eee, aaa, bbb, ccc, ddd, f1, 0, in[10], 12);
@round(dd, ee, aa, bb, cc, f5, K[4], in[9], 11); @round(ddd, eee, aaa, bbb, ccc, f1, 0, in[4], 9);
@round(cc, dd, ee, aa, bb, f5, K[4], in[7], 6); @round(ccc, ddd, eee, aaa, bbb, f1, 0, in[1], 12);
@round(bb, cc, dd, ee, aa, f5, K[4], in[12], 8); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[5], 5);
@round(aa, bb, cc, dd, ee, f5, K[4], in[2], 13); @round(aaa, bbb, ccc, ddd, eee, f1, 0, in[8], 14);
@round(ee, aa, bb, cc, dd, f5, K[4], in[10], 12); @round(eee, aaa, bbb, ccc, ddd, f1, 0, in[7], 6);
@round(dd, ee, aa, bb, cc, f5, K[4], in[14], 5); @round(ddd, eee, aaa, bbb, ccc, f1, 0, in[6], 8);
@round(cc, dd, ee, aa, bb, f5, K[4], in[1], 12); @round(ccc, ddd, eee, aaa, bbb, f1, 0, in[2], 13);
@round(bb, cc, dd, ee, aa, f5, K[4], in[3], 13); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[13], 6);
@round(aa, bb, cc, dd, ee, f5, K[4], in[8], 14); @round(aaa, bbb, ccc, ddd, eee, f1, 0, in[14], 5);
@round(ee, aa, bb, cc, dd, f5, K[4], in[11], 11); @round(eee, aaa, bbb, ccc, ddd, f1, 0, in[0], 15);
@round(dd, ee, aa, bb, cc, f5, K[4], in[6], 8); @round(ddd, eee, aaa, bbb, ccc, f1, 0, in[3], 13);
@round(cc, dd, ee, aa, bb, f5, K[4], in[15], 5); @round(ccc, ddd, eee, aaa, bbb, f1, 0, in[9], 11);
@round(bb, cc, dd, ee, aa, f5, K[4], in[13], 6); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[11], 11);
ddd += cc + state[1];
state[1] = state[2] + dd + eee;
state[2] = state[3] + ee + aaa;
state[3] = state[4] + aa + bbb;
state[4] = state[0] + bb + ccc;
state[0] = ddd;
$case 256:
aa = state[0];
bb = state[1];
cc = state[2];
dd = state[3];
aaa = state[4];
bbb = state[5];
ccc = state[6];
ddd = state[7];
@round(aa, bb, cc, dd, 0, f1, K[0], in[0], 11); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[5], 8);
@round(dd, aa, bb, cc, 0, f1, K[0], in[1], 14); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[14], 9);
@round(cc, dd, aa, bb, 0, f1, K[0], in[2], 15); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[7], 9);
@round(bb, cc, dd, aa, 0, f1, K[0], in[3], 12); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[0], 11);
@round(aa, bb, cc, dd, 0, f1, K[0], in[4], 5); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[9], 13);
@round(dd, aa, bb, cc, 0, f1, K[0], in[5], 8); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[2], 15);
@round(cc, dd, aa, bb, 0, f1, K[0], in[6], 7); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[11], 15);
@round(bb, cc, dd, aa, 0, f1, K[0], in[7], 9); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[4], 5);
@round(aa, bb, cc, dd, 0, f1, K[0], in[8], 11); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[13], 7);
@round(dd, aa, bb, cc, 0, f1, K[0], in[9], 13); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[6], 7);
@round(cc, dd, aa, bb, 0, f1, K[0], in[10], 14); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[15], 8);
@round(bb, cc, dd, aa, 0, f1, K[0], in[11], 15); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[8], 11);
@round(aa, bb, cc, dd, 0, f1, K[0], in[12], 6); @round(aaa, bbb, ccc, ddd, 0, f4, K[5], in[1], 14);
@round(dd, aa, bb, cc, 0, f1, K[0], in[13], 7); @round(ddd, aaa, bbb, ccc, 0, f4, K[5], in[10], 14);
@round(cc, dd, aa, bb, 0, f1, K[0], in[14], 9); @round(ccc, ddd, aaa, bbb, 0, f4, K[5], in[3], 12);
@round(bb, cc, dd, aa, 0, f1, K[0], in[15], 8); @round(bbb, ccc, ddd, aaa, 0, f4, K[5], in[12], 6);
@swap(aa, aaa);
@round(aa, bb, cc, dd, 0, f2, K[1], in[7], 7); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[6], 9);
@round(dd, aa, bb, cc, 0, f2, K[1], in[4], 6); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[11], 13);
@round(cc, dd, aa, bb, 0, f2, K[1], in[13], 8); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[3], 15);
@round(bb, cc, dd, aa, 0, f2, K[1], in[1], 13); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[7], 7);
@round(aa, bb, cc, dd, 0, f2, K[1], in[10], 11); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[0], 12);
@round(dd, aa, bb, cc, 0, f2, K[1], in[6], 9); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[13], 8);
@round(cc, dd, aa, bb, 0, f2, K[1], in[15], 7); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[5], 9);
@round(bb, cc, dd, aa, 0, f2, K[1], in[3], 15); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[10], 11);
@round(aa, bb, cc, dd, 0, f2, K[1], in[12], 7); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[14], 7);
@round(dd, aa, bb, cc, 0, f2, K[1], in[0], 12); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[15], 7);
@round(cc, dd, aa, bb, 0, f2, K[1], in[9], 15); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[8], 12);
@round(bb, cc, dd, aa, 0, f2, K[1], in[5], 9); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[12], 7);
@round(aa, bb, cc, dd, 0, f2, K[1], in[2], 11); @round(aaa, bbb, ccc, ddd, 0, f3, K[6], in[4], 6);
@round(dd, aa, bb, cc, 0, f2, K[1], in[14], 7); @round(ddd, aaa, bbb, ccc, 0, f3, K[6], in[9], 15);
@round(cc, dd, aa, bb, 0, f2, K[1], in[11], 13); @round(ccc, ddd, aaa, bbb, 0, f3, K[6], in[1], 13);
@round(bb, cc, dd, aa, 0, f2, K[1], in[8], 12); @round(bbb, ccc, ddd, aaa, 0, f3, K[6], in[2], 11);
@swap(bb, bbb);
@round(aa, bb, cc, dd, 0, f3, K[2], in[3], 11); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[15], 9);
@round(dd, aa, bb, cc, 0, f3, K[2], in[10], 13); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[5], 7);
@round(cc, dd, aa, bb, 0, f3, K[2], in[14], 6); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[1], 15);
@round(bb, cc, dd, aa, 0, f3, K[2], in[4], 7); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[3], 11);
@round(aa, bb, cc, dd, 0, f3, K[2], in[9], 14); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[7], 8);
@round(dd, aa, bb, cc, 0, f3, K[2], in[15], 9); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[14], 6);
@round(cc, dd, aa, bb, 0, f3, K[2], in[8], 13); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[6], 6);
@round(bb, cc, dd, aa, 0, f3, K[2], in[1], 15); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[9], 14);
@round(aa, bb, cc, dd, 0, f3, K[2], in[2], 14); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[11], 12);
@round(dd, aa, bb, cc, 0, f3, K[2], in[7], 8); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[8], 13);
@round(cc, dd, aa, bb, 0, f3, K[2], in[0], 13); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[12], 5);
@round(bb, cc, dd, aa, 0, f3, K[2], in[6], 6); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[2], 14);
@round(aa, bb, cc, dd, 0, f3, K[2], in[13], 5); @round(aaa, bbb, ccc, ddd, 0, f2, K[7], in[10], 13);
@round(dd, aa, bb, cc, 0, f3, K[2], in[11], 12); @round(ddd, aaa, bbb, ccc, 0, f2, K[7], in[0], 13);
@round(cc, dd, aa, bb, 0, f3, K[2], in[5], 7); @round(ccc, ddd, aaa, bbb, 0, f2, K[7], in[4], 7);
@round(bb, cc, dd, aa, 0, f3, K[2], in[12], 5); @round(bbb, ccc, ddd, aaa, 0, f2, K[7], in[13], 5);
@swap(cc, ccc);
@round(aa, bb, cc, dd, 0, f4, K[3], in[1], 11); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[8], 15);
@round(dd, aa, bb, cc, 0, f4, K[3], in[9], 12); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[6], 5);
@round(cc, dd, aa, bb, 0, f4, K[3], in[11], 14); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[4], 8);
@round(bb, cc, dd, aa, 0, f4, K[3], in[10], 15); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[1], 11);
@round(aa, bb, cc, dd, 0, f4, K[3], in[0], 14); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[3], 14);
@round(dd, aa, bb, cc, 0, f4, K[3], in[8], 15); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[11], 14);
@round(cc, dd, aa, bb, 0, f4, K[3], in[12], 9); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[15], 6);
@round(bb, cc, dd, aa, 0, f4, K[3], in[4], 8); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[0], 14);
@round(aa, bb, cc, dd, 0, f4, K[3], in[13], 9); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[5], 6);
@round(dd, aa, bb, cc, 0, f4, K[3], in[3], 14); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[12], 9);
@round(cc, dd, aa, bb, 0, f4, K[3], in[7], 5); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[2], 12);
@round(bb, cc, dd, aa, 0, f4, K[3], in[15], 6); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[13], 9);
@round(aa, bb, cc, dd, 0, f4, K[3], in[14], 8); @round(aaa, bbb, ccc, ddd, 0, f1, 0, in[9], 12);
@round(dd, aa, bb, cc, 0, f4, K[3], in[5], 6); @round(ddd, aaa, bbb, ccc, 0, f1, 0, in[7], 5);
@round(cc, dd, aa, bb, 0, f4, K[3], in[6], 5); @round(ccc, ddd, aaa, bbb, 0, f1, 0, in[10], 15);
@round(bb, cc, dd, aa, 0, f4, K[3], in[2], 12); @round(bbb, ccc, ddd, aaa, 0, f1, 0, in[14], 8);
@swap(dd, ddd);
state[0] += aa;
state[1] += bb;
state[2] += cc;
state[3] += dd;
state[4] += aaa;
state[5] += bbb;
state[6] += ccc;
state[7] += ddd;
$case 320:
aa = state[0];
bb = state[1];
cc = state[2];
dd = state[3];
ee = state[4];
aaa = state[5];
bbb = state[6];
ccc = state[7];
ddd = state[8];
eee = state[9];
@round(aa, bb, cc, dd, ee, f1, K[0], in[0], 11); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[5], 8);
@round(ee, aa, bb, cc, dd, f1, K[0], in[1], 14); @round(eee, aaa, bbb, ccc, ddd, f5, K[5], in[14], 9);
@round(dd, ee, aa, bb, cc, f1, K[0], in[2], 15); @round(ddd, eee, aaa, bbb, ccc, f5, K[5], in[7], 9);
@round(cc, dd, ee, aa, bb, f1, K[0], in[3], 12); @round(ccc, ddd, eee, aaa, bbb, f5, K[5], in[0], 11);
@round(bb, cc, dd, ee, aa, f1, K[0], in[4], 5); @round(bbb, ccc, ddd, eee, aaa, f5, K[5], in[9], 13);
@round(aa, bb, cc, dd, ee, f1, K[0], in[5], 8); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[2], 15);
@round(ee, aa, bb, cc, dd, f1, K[0], in[6], 7); @round(eee, aaa, bbb, ccc, ddd, f5, K[5], in[11], 15);
@round(dd, ee, aa, bb, cc, f1, K[0], in[7], 9); @round(ddd, eee, aaa, bbb, ccc, f5, K[5], in[4], 5);
@round(cc, dd, ee, aa, bb, f1, K[0], in[8], 11); @round(ccc, ddd, eee, aaa, bbb, f5, K[5], in[13], 7);
@round(bb, cc, dd, ee, aa, f1, K[0], in[9], 13); @round(bbb, ccc, ddd, eee, aaa, f5, K[5], in[6], 7);
@round(aa, bb, cc, dd, ee, f1, K[0], in[10], 14); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[15], 8);
@round(ee, aa, bb, cc, dd, f1, K[0], in[11], 15); @round(eee, aaa, bbb, ccc, ddd, f5, K[5], in[8], 11);
@round(dd, ee, aa, bb, cc, f1, K[0], in[12], 6); @round(ddd, eee, aaa, bbb, ccc, f5, K[5], in[1], 14);
@round(cc, dd, ee, aa, bb, f1, K[0], in[13], 7); @round(ccc, ddd, eee, aaa, bbb, f5, K[5], in[10], 14);
@round(bb, cc, dd, ee, aa, f1, K[0], in[14], 9); @round(bbb, ccc, ddd, eee, aaa, f5, K[5], in[3], 12);
@round(aa, bb, cc, dd, ee, f1, K[0], in[15], 8); @round(aaa, bbb, ccc, ddd, eee, f5, K[5], in[12], 6);
@swap(aa, aaa);
@round(ee, aa, bb, cc, dd, f2, K[1], in[7], 7); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[6], 9);
@round(dd, ee, aa, bb, cc, f2, K[1], in[4], 6); @round(ddd, eee, aaa, bbb, ccc, f4, K[6], in[11], 13);
@round(cc, dd, ee, aa, bb, f2, K[1], in[13], 8); @round(ccc, ddd, eee, aaa, bbb, f4, K[6], in[3], 15);
@round(bb, cc, dd, ee, aa, f2, K[1], in[1], 13); @round(bbb, ccc, ddd, eee, aaa, f4, K[6], in[7], 7);
@round(aa, bb, cc, dd, ee, f2, K[1], in[10], 11); @round(aaa, bbb, ccc, ddd, eee, f4, K[6], in[0], 12);
@round(ee, aa, bb, cc, dd, f2, K[1], in[6], 9); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[13], 8);
@round(dd, ee, aa, bb, cc, f2, K[1], in[15], 7); @round(ddd, eee, aaa, bbb, ccc, f4, K[6], in[5], 9);
@round(cc, dd, ee, aa, bb, f2, K[1], in[3], 15); @round(ccc, ddd, eee, aaa, bbb, f4, K[6], in[10], 11);
@round(bb, cc, dd, ee, aa, f2, K[1], in[12], 7); @round(bbb, ccc, ddd, eee, aaa, f4, K[6], in[14], 7);
@round(aa, bb, cc, dd, ee, f2, K[1], in[0], 12); @round(aaa, bbb, ccc, ddd, eee, f4, K[6], in[15], 7);
@round(ee, aa, bb, cc, dd, f2, K[1], in[9], 15); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[8], 12);
@round(dd, ee, aa, bb, cc, f2, K[1], in[5], 9); @round(ddd, eee, aaa, bbb, ccc, f4, K[6], in[12], 7);
@round(cc, dd, ee, aa, bb, f2, K[1], in[2], 11); @round(ccc, ddd, eee, aaa, bbb, f4, K[6], in[4], 6);
@round(bb, cc, dd, ee, aa, f2, K[1], in[14], 7); @round(bbb, ccc, ddd, eee, aaa, f4, K[6], in[9], 15);
@round(aa, bb, cc, dd, ee, f2, K[1], in[11], 13); @round(aaa, bbb, ccc, ddd, eee, f4, K[6], in[1], 13);
@round(ee, aa, bb, cc, dd, f2, K[1], in[8], 12); @round(eee, aaa, bbb, ccc, ddd, f4, K[6], in[2], 11);
@swap(bb, bbb);
@round(dd, ee, aa, bb, cc, f3, K[2], in[3], 11); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[15], 9);
@round(cc, dd, ee, aa, bb, f3, K[2], in[10], 13); @round(ccc, ddd, eee, aaa, bbb, f3, K[7], in[5], 7);
@round(bb, cc, dd, ee, aa, f3, K[2], in[14], 6); @round(bbb, ccc, ddd, eee, aaa, f3, K[7], in[1], 15);
@round(aa, bb, cc, dd, ee, f3, K[2], in[4], 7); @round(aaa, bbb, ccc, ddd, eee, f3, K[7], in[3], 11);
@round(ee, aa, bb, cc, dd, f3, K[2], in[9], 14); @round(eee, aaa, bbb, ccc, ddd, f3, K[7], in[7], 8);
@round(dd, ee, aa, bb, cc, f3, K[2], in[15], 9); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[14], 6);
@round(cc, dd, ee, aa, bb, f3, K[2], in[8], 13); @round(ccc, ddd, eee, aaa, bbb, f3, K[7], in[6], 6);
@round(bb, cc, dd, ee, aa, f3, K[2], in[1], 15); @round(bbb, ccc, ddd, eee, aaa, f3, K[7], in[9], 14);
@round(aa, bb, cc, dd, ee, f3, K[2], in[2], 14); @round(aaa, bbb, ccc, ddd, eee, f3, K[7], in[11], 12);
@round(ee, aa, bb, cc, dd, f3, K[2], in[7], 8); @round(eee, aaa, bbb, ccc, ddd, f3, K[7], in[8], 13);
@round(dd, ee, aa, bb, cc, f3, K[2], in[0], 13); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[12], 5);
@round(cc, dd, ee, aa, bb, f3, K[2], in[6], 6); @round(ccc, ddd, eee, aaa, bbb, f3, K[7], in[2], 14);
@round(bb, cc, dd, ee, aa, f3, K[2], in[13], 5); @round(bbb, ccc, ddd, eee, aaa, f3, K[7], in[10], 13);
@round(aa, bb, cc, dd, ee, f3, K[2], in[11], 12); @round(aaa, bbb, ccc, ddd, eee, f3, K[7], in[0], 13);
@round(ee, aa, bb, cc, dd, f3, K[2], in[5], 7); @round(eee, aaa, bbb, ccc, ddd, f3, K[7], in[4], 7);
@round(dd, ee, aa, bb, cc, f3, K[2], in[12], 5); @round(ddd, eee, aaa, bbb, ccc, f3, K[7], in[13], 5);
@swap(cc, ccc);
@round(cc, dd, ee, aa, bb, f4, K[3], in[1], 11); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[8], 15);
@round(bb, cc, dd, ee, aa, f4, K[3], in[9], 12); @round(bbb, ccc, ddd, eee, aaa, f2, K[8], in[6], 5);
@round(aa, bb, cc, dd, ee, f4, K[3], in[11], 14); @round(aaa, bbb, ccc, ddd, eee, f2, K[8], in[4], 8);
@round(ee, aa, bb, cc, dd, f4, K[3], in[10], 15); @round(eee, aaa, bbb, ccc, ddd, f2, K[8], in[1], 11);
@round(dd, ee, aa, bb, cc, f4, K[3], in[0], 14); @round(ddd, eee, aaa, bbb, ccc, f2, K[8], in[3], 14);
@round(cc, dd, ee, aa, bb, f4, K[3], in[8], 15); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[11], 14);
@round(bb, cc, dd, ee, aa, f4, K[3], in[12], 9); @round(bbb, ccc, ddd, eee, aaa, f2, K[8], in[15], 6);
@round(aa, bb, cc, dd, ee, f4, K[3], in[4], 8); @round(aaa, bbb, ccc, ddd, eee, f2, K[8], in[0], 14);
@round(ee, aa, bb, cc, dd, f4, K[3], in[13], 9); @round(eee, aaa, bbb, ccc, ddd, f2, K[8], in[5], 6);
@round(dd, ee, aa, bb, cc, f4, K[3], in[3], 14); @round(ddd, eee, aaa, bbb, ccc, f2, K[8], in[12], 9);
@round(cc, dd, ee, aa, bb, f4, K[3], in[7], 5); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[2], 12);
@round(bb, cc, dd, ee, aa, f4, K[3], in[15], 6); @round(bbb, ccc, ddd, eee, aaa, f2, K[8], in[13], 9);
@round(aa, bb, cc, dd, ee, f4, K[3], in[14], 8); @round(aaa, bbb, ccc, ddd, eee, f2, K[8], in[9], 12);
@round(ee, aa, bb, cc, dd, f4, K[3], in[5], 6); @round(eee, aaa, bbb, ccc, ddd, f2, K[8], in[7], 5);
@round(dd, ee, aa, bb, cc, f4, K[3], in[6], 5); @round(ddd, eee, aaa, bbb, ccc, f2, K[8], in[10], 15);
@round(cc, dd, ee, aa, bb, f4, K[3], in[2], 12); @round(ccc, ddd, eee, aaa, bbb, f2, K[8], in[14], 8);
@swap(dd, ddd);
@round(bb, cc, dd, ee, aa, f5, K[4], in[4], 9); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[12], 8);
@round(aa, bb, cc, dd, ee, f5, K[4], in[0], 15); @round(aaa, bbb, ccc, ddd, eee, f1, 0, in[15], 5);
@round(ee, aa, bb, cc, dd, f5, K[4], in[5], 5); @round(eee, aaa, bbb, ccc, ddd, f1, 0, in[10], 12);
@round(dd, ee, aa, bb, cc, f5, K[4], in[9], 11); @round(ddd, eee, aaa, bbb, ccc, f1, 0, in[4], 9);
@round(cc, dd, ee, aa, bb, f5, K[4], in[7], 6); @round(ccc, ddd, eee, aaa, bbb, f1, 0, in[1], 12);
@round(bb, cc, dd, ee, aa, f5, K[4], in[12], 8); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[5], 5);
@round(aa, bb, cc, dd, ee, f5, K[4], in[2], 13); @round(aaa, bbb, ccc, ddd, eee, f1, 0, in[8], 14);
@round(ee, aa, bb, cc, dd, f5, K[4], in[10], 12); @round(eee, aaa, bbb, ccc, ddd, f1, 0, in[7], 6);
@round(dd, ee, aa, bb, cc, f5, K[4], in[14], 5); @round(ddd, eee, aaa, bbb, ccc, f1, 0, in[6], 8);
@round(cc, dd, ee, aa, bb, f5, K[4], in[1], 12); @round(ccc, ddd, eee, aaa, bbb, f1, 0, in[2], 13);
@round(bb, cc, dd, ee, aa, f5, K[4], in[3], 13); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[13], 6);
@round(aa, bb, cc, dd, ee, f5, K[4], in[8], 14); @round(aaa, bbb, ccc, ddd, eee, f1, 0, in[14], 5);
@round(ee, aa, bb, cc, dd, f5, K[4], in[11], 11); @round(eee, aaa, bbb, ccc, ddd, f1, 0, in[0], 15);
@round(dd, ee, aa, bb, cc, f5, K[4], in[6], 8); @round(ddd, eee, aaa, bbb, ccc, f1, 0, in[3], 13);
@round(cc, dd, ee, aa, bb, f5, K[4], in[15], 5); @round(ccc, ddd, eee, aaa, bbb, f1, 0, in[9], 11);
@round(bb, cc, dd, ee, aa, f5, K[4], in[13], 6); @round(bbb, ccc, ddd, eee, aaa, f1, 0, in[11], 11);
@swap(ee, eee);
state[0] += aa;
state[1] += bb;
state[2] += cc;
state[3] += dd;
state[4] += ee;
state[5] += aaa;
state[6] += bbb;
state[7] += ccc;
state[8] += ddd;
state[9] += eee;
$default:
$error "Invalid digest size";
$endswitch
}

View File

@@ -1,177 +1,175 @@
module std::hash::sha256;
import std::hash::hmac;
import std::bits, std::hash::hmac;
const BLOCK_SIZE = 64;
const HASH_SIZE = 32;
const uint[64] K @local = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
// Right rotate function
macro uint @rotr(uint x, uint n) @local => (((x) >> (n)) | ((x) << (32 - (n))));
// SHA-256 functions
macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z);
macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z);
macro uint @_sigma0(uint x) @local => @rotr(x, 2) ^ @rotr(x, 13) ^ @rotr(x, 22);
macro uint @_sigma1(uint x) @local => @rotr(x, 6) ^ @rotr(x, 11) ^ @rotr(x, 25);
macro uint @sigma0(uint x) @local => @rotr(x, 7) ^ @rotr(x, 18) ^ (x >> 3);
macro uint @sigma1(uint x) @local => @rotr(x, 17) ^ @rotr(x, 19) ^ (x >> 10);
struct Sha256
{
uint[8] state;
ulong bitcount;
char[BLOCK_SIZE] buffer;
}
alias HmacSha256 = Hmac{Sha256, HASH_SIZE, BLOCK_SIZE};
alias hmac = hmac::hash{Sha256, HASH_SIZE, BLOCK_SIZE};
alias pbkdf2 = hmac::pbkdf2{Sha256, HASH_SIZE, BLOCK_SIZE};
fn char[HASH_SIZE] hash(char[] data)
const uint[64] K @local = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};
macro uint @ch(uint x, uint y, uint z) @local => (x & y) ^ (~x & z);
macro uint @maj(uint x, uint y, uint z) @local => (x & y) ^ (x & z) ^ (y & z);
macro uint @_sigma0(uint x) @local => x.rotr(2) ^ x.rotr(13) ^ x.rotr(22);
macro uint @_sigma1(uint x) @local => x.rotr(6) ^ x.rotr(11) ^ x.rotr(25);
macro uint @sigma0(uint x) @local => x.rotr(7) ^ x.rotr(18) ^ (x >> 3);
macro uint @sigma1(uint x) @local => x.rotr(17) ^ x.rotr(19) ^ (x >> 10);
struct Sha256
{
Sha256 sha256 @noinit;
sha256.init();
sha256.update(data);
return sha256.final();
uint[8] state @align(usz.sizeof);
char[BLOCK_SIZE] buffer @align(ulong.sizeof); // must align along bitcount sizeof - see `final`
ulong bitcount;
}
fn void Sha256.init(&self)
<*
Compute and return a hash value.
@param [in] data : "The input data to hash."
*>
fn char[HASH_SIZE] hash(char[] data)
{
// Sha256 initialization constants
*self = {
.state = {
0x6A09E667,
0xBB67AE85,
0x3C6EF372,
0xA54FF53A,
0x510E527F,
0x9B05688C,
0x1F83D9AB,
0x5BE0CD19
}
};
Sha256 sha256 @noinit;
sha256.init();
sha256.update(data);
return sha256.final();
}
fn void Sha256.init(&self) => *self = {
.state = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
},
};
<*
@param [in] data
@require data.len <= uint.max
*>
fn void Sha256.update(&self, char[] data) {
uint i = 0;
uint len = data.len;
uint buffer_pos = (uint)(self.bitcount / 8) % BLOCK_SIZE;
self.bitcount += ((ulong)len * 8);
fn void Sha256.update(&self, char[] data)
{
uint buffer_pos = (uint)(self.bitcount >> 3) % BLOCK_SIZE;
self.bitcount += (ulong)data.len << 3; // always record ingested bits count immediately
while (len--) {
self.buffer[buffer_pos++] = data[i++];
if (buffer_pos == BLOCK_SIZE) {
sha256_transform(&self.state, &self.buffer);
buffer_pos = 0; // Reset buffer position
}
}
// Get the buffer position back to 0 if we're midway through consuming some data.
if (buffer_pos > 0 && buffer_pos < BLOCK_SIZE)
{
usz len = min(BLOCK_SIZE - buffer_pos, data.len);
self.buffer[buffer_pos:len] = data[:len];
data = data[len..];
if (buffer_pos + len == BLOCK_SIZE) self.transform();
}
// When the data pointer is aligned, we can disregard unaligned loading in the `transform` macro.
// We do this here from the outer call to reduce the expense of checking alignment on every single block.
if (0 == (usz)data.ptr % usz.sizeof)
{
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) self.transform((uint*)data.ptr);
}
else
{
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) self.transform_unaligned((uint*)data.ptr);
}
// Leftover data just gets stored away for the next update or final.
if (data.len)
{
self.buffer[..] = 0;
self.buffer[:data.len] = data[..];
}
}
fn char[HASH_SIZE] Sha256.final(&self) {
char[HASH_SIZE] hash;
ulong i = (self.bitcount / 8) % BLOCK_SIZE;
fn char[HASH_SIZE] Sha256.final(&self)
{
char[HASH_SIZE] hash @align(uint.sizeof);
ulong i = (self.bitcount / 8) % BLOCK_SIZE;
// Append 0x80 to the buffer
self.buffer[i++] = 0x80;
// Append 0x80 to the buffer
self.buffer[i++] = 0x80;
// Pad the buffer with zeros
if (i > BLOCK_SIZE - 8) {
while (i < BLOCK_SIZE) {
self.buffer[i++] = 0x00;
}
sha256_transform(&self.state, &self.buffer);
i = 0; // Reset buffer index after transformation
}
while (i < BLOCK_SIZE - 8) {
self.buffer[i++] = 0x00;
}
// Pad the buffer with zeros
if (i > BLOCK_SIZE - 8)
{
self.buffer[i..] = 0x00;
self.transform();
i = 0; // Reset buffer index after transformation
}
// Append the bitcount in big-endian format
for (int j = 0; j < 8; ++j) {
self.buffer[BLOCK_SIZE - 8 + j] = (char)((self.bitcount >> (56 - j * 8)) & 0xFF);
}
self.buffer[i..(BLOCK_SIZE - 8)] = 0x00;
sha256_transform(&self.state, &self.buffer);
// Append the bitcount in big-endian format
*(ulong*)(&self.buffer[BLOCK_SIZE - 8]) = env::BIG_ENDIAN ??? self.bitcount : bswap(self.bitcount);
// Convert state to the final hash
for (i = 0; i < 8; ++i) {
hash[i * 4] = (char)((self.state[i] >> 24) & 0xFF);
hash[i * 4 + 1] = (char)((self.state[i] >> 16) & 0xFF);
hash[i * 4 + 2] = (char)((self.state[i] >> 8) & 0xFF);
hash[i * 4 + 3] = (char)(self.state[i] & 0xFF);
}
return hash;
self.transform();
// Convert state to the final hash
foreach (x, s : self.state) *(uint*)(&hash[x * uint.sizeof]) = env::BIG_ENDIAN ??? s : bswap(s);
return hash;
}
<*
@param [&inout] state
@param [&in] buffer
*>
fn void sha256_transform(uint* state, char* buffer) @local {
uint a, b, c, d, e, f, g, h, t1, t2;
uint[64] m;
int i;
// These wrappers are necessary to significantly reduce code generation from macro expansions.
// Note that transformations on `self.buffer` (when incoming == null) should always be aligned.
fn void Sha256.transform(&self, uint* incoming = null) @local @noinline => self.do_transform(incoming, true);
fn void Sha256.transform_unaligned(&self, uint* incoming = null) @local @noinline => self.do_transform(incoming, false);
// Prepare the message schedule
for (i = 0; i < 16; ++i) {
m[i] = ((uint)buffer[i * 4] << 24) | ((uint)buffer[i * 4 + 1] << 16) |
((uint)buffer[i * 4 + 2] << 8) | ((uint)buffer[i * 4 + 3]); // Ensure values are cast to uint for correct shifts
}
for (i = 16; i < 64; ++i) {
m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16];
}
macro Sha256.do_transform(&self, uint* incoming = null, bool $aligned = true) @local
{
uint a, b, c, d, e, f, g, h, t1, t2 @noinit;
uint[64] m @noinit;
int i @noinit;
// Initialize working variables
a = state[0];
b = state[1];
c = state[2];
d = state[3];
e = state[4];
f = state[5];
g = state[6];
h = state[7];
if (!incoming) incoming = (uint*)&self.buffer;
// Perform the main SHA-256 compression function
for (i = 0; i < 64; ++i) {
t1 = h + @_sigma1(e) + @ch(e, f, g) + K[i] + m[i];
t2 = @_sigma0(a) + @maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
}
$if env::BIG_ENDIAN:
@as_char_view(m)[:BLOCK_SIZE] = @as_char_view(incoming)[:BLOCK_SIZE];
$else
// Unrolling this seems to make the hash slower.
for (i = 0; i < 16; ++i) m[i] = bswap($aligned ??? incoming[i] : @unaligned_load(incoming[i], 1));
$endif
// Update the state
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
state[4] += e;
state[5] += f;
state[6] += g;
state[7] += h;
a = b = c = d = e = f = g = h = t1 = t2 = i = 0;
m[:64] = buffer[:64] = 0;
for (i = 16; i < 64; i++) m[i] = @sigma1(m[i - 2]) + m[i - 7] + @sigma0(m[i - 15]) + m[i - 16];
a = self.state[0];
b = self.state[1];
c = self.state[2];
d = self.state[3];
e = self.state[4];
f = self.state[5];
g = self.state[6];
h = self.state[7];
$for usz $i = 0; $i < 64; $i++:
t1 = h + @_sigma1(e) + @ch(e, f, g) + K[$i] + m[$i];
t2 = @_sigma0(a) + @maj(a, b, c);
h = g;
g = f;
f = e;
e = d + t1;
d = c;
c = b;
b = a;
a = t1 + t2;
$endfor
self.state[0] += a;
self.state[1] += b;
self.state[2] += c;
self.state[3] += d;
self.state[4] += e;
self.state[5] += f;
self.state[6] += g;
self.state[7] += h;
}

View File

@@ -48,7 +48,7 @@ alias hash = siphash::hash { uint128, 4, 8 };
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
*>
module std::hash::siphash { OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS };
module std::hash::siphash <OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS>;
struct SipHash

View File

@@ -161,7 +161,7 @@ fn void Whirlpool.process_block(&self, char* block) @local
// NOTE: These loops are unrolled with C3's Chad-tier compile-time evaluation.
$for var $round = 0; $round < 8; $round++:
k[$round] = self.hash[$round];
state[$round] = $$bswap(@unaligned_load(((ulong*)block)[$round], 1)) ^ self.hash[$round];
state[$round] = $$bswap(mem::load((ulong*)block + $round, 1)) ^ self.hash[$round];
self.hash[$round] = state[$round];
$endfor

View File

@@ -34,8 +34,8 @@ fn ulong hash(char[] input, ulong seed = 0)
{
if (@likely(input.len >= 4))
{
a = (ulong)@unaligned_load(*(uint*)input.ptr, 1); // first 4 bytes widened to a u64
b = (ulong)@unaligned_load(*(uint*)&input[^4], 1); // a walking 4-byte window based on input.len
a = (ulong)mem::load((uint*)input.ptr, 1); // first 4 bytes widened to a u64
b = (ulong)mem::load((uint*)&input[^4], 1); // a walking 4-byte window based on input.len
}
else if (input.len > 0)
{
@@ -44,8 +44,8 @@ fn ulong hash(char[] input, ulong seed = 0)
}
else
{
a = @unaligned_load(*(ulong*)input.ptr, 1); // first 8 bytes
b = @unaligned_load(*(ulong*)&input[^8], 1); // a walking 8-byte window based on input.len
a = mem::load((ulong*)input.ptr, 1); // first 8 bytes
b = mem::load((ulong*)&input[^8], 1); // a walking 8-byte window based on input.len
}
uint128 r = ((uint128)a ^ 0xe703_7ed1_a0b4_28db) * ((uint128)b ^ seed);

View File

@@ -102,8 +102,8 @@ fn void? File.close(&self) @inline @dynamic
switch (libc::errno())
{
case errno::ECONNRESET:
case errno::EBADF: return io::FILE_NOT_VALID?;
case errno::EINTR: return io::INTERRUPTED?;
case errno::EBADF: return io::FILE_NOT_VALID~;
case errno::EINTR: return io::INTERRUPTED~;
case errno::EDQUOT:
case errno::EFAULT:
case errno::EAGAIN:
@@ -111,8 +111,8 @@ fn void? File.close(&self) @inline @dynamic
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return io::INCOMPLETE_WRITE?;
default: return io::UNKNOWN_ERROR?;
case errno::EIO: return io::INCOMPLETE_WRITE~;
default: return io::UNKNOWN_ERROR~;
}
}
self.file = null;
@@ -156,7 +156,7 @@ fn bool File.isatty(self) @if(env::LIBC)
fn char? File.read_byte(&self) @dynamic
{
int c = libc::fgetc(self.file);
if (c == -1) return io::EOF?;
if (c == -1) return io::EOF~;
return (char)c;
}
@@ -172,7 +172,7 @@ fn char[]? load_buffer(String filename, char[] buffer)
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
if (len > buffer.len) return io::OVERFLOW?;
if (len > buffer.len) return io::OVERFLOW~;
file.seek(0, SET)!;
usz read = 0;
while (read < len)

View File

@@ -26,8 +26,8 @@ fn void? FileMmap.destroy(&self) @maydiscard
{
fault err1 = @catch(self.file.close());
fault err2 = @catch(self.vm.destroy());
if (err1) return err1?;
if (err2) return err2?;
if (err1) return err1~;
if (err2) return err2~;
}
module std::io::file @if(env::LIBC &&& env::POSIX);

View File

@@ -2,6 +2,9 @@ module std::io;
import std::collections::map;
import libc;
// It is vitally important that the formatter doesn't do any allocations,
// or it ceases to be useful in constrained environments.
const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable
@@ -93,9 +96,9 @@ fn usz? Formatter.out(&self, char c) @private
{
if (catch err = self.out_fn(self.data, c))
{
if (self.first_fault) return self.first_fault?;
if (self.first_fault) return self.first_fault~;
self.first_fault = err;
return err?;
return err~;
}
return 1;
}
@@ -130,7 +133,7 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
if (!arg) return self.out_substr("(null)");
return self.out_substr(arg.to_constant_string());
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
@@ -337,7 +340,7 @@ fn void? out_null_fn(void* data @unused, char c @unused) @private
macro usz? @report_fault(Formatter* f, $fault)
{
(void)f.out_substr($fault);
return INVALID_FORMAT?;
return INVALID_FORMAT~;
}
macro usz? @wrap_bad(Formatter* f, #action)
@@ -349,11 +352,11 @@ macro usz? @wrap_bad(Formatter* f, #action)
{
case BUFFER_EXCEEDED:
case INTERNAL_BUFFER_EXCEEDED:
return f.first_err(err)?;
return f.first_err(err)~;
default:
err = f.first_err(INVALID_ARGUMENT);
f.out_substr("<INVALID>")!;
return err?;
return err~;
}
}
return len;
@@ -561,7 +564,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
if (self.first_fault) return self.first_fault?;
if (self.first_fault) return self.first_fault~;
return total_len;
}

View File

@@ -86,7 +86,7 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private
double d = *(double*)arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return BAD_FORMAT?;
return BAD_FORMAT~;
}
}
@@ -128,7 +128,7 @@ fn FloatType? float_from_any(any arg) @private
case double:
return (FloatType)*(double*)arg;
default:
return BAD_FORMAT?;
return BAD_FORMAT~;
}
}
@@ -283,7 +283,7 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
} while (y);
isz outlen = s - buf;
isz explen = ebuf - estr;
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED?;
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED~;
usz len;
usz l = p && outlen - 2 < p
? p + 2 + explen
@@ -364,7 +364,10 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
// Perform rounding: j is precision after the radix (possibly neg)
int j = (int)(p - (isz)(formatting == FLOAT ? 0 : e - (int)(formatting == ADAPTIVE && p)));
int j = (int)p;
if (formatting != FLOAT) j -= e;
if (formatting == ADAPTIVE && p != 0) j -= 1;
if (j < 9 * (z - r - 1))
{
uint x;
@@ -451,12 +454,12 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
}
}
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return INTERNAL_BUFFER_EXCEEDED?;
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return INTERNAL_BUFFER_EXCEEDED~;
int l = (int)(1 + p + (isz)(p || self.flags.hash));
char* estr @noinit;
if (formatting == FLOAT)
{
if (e > int.max - l) return INTERNAL_BUFFER_EXCEEDED?;
if (e > int.max - l) return INTERNAL_BUFFER_EXCEEDED~;
if (e > 0) l += e;
}
else
@@ -465,10 +468,10 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
while (ebuf - estr < 2) (--estr)[0] = '0';
*--estr = (e < 0 ? '-' : '+');
*--estr = self.flags.uppercase ? 'E' : 'e';
if (ebuf - estr > (isz)int.max - l) return INTERNAL_BUFFER_EXCEEDED?;
if (ebuf - estr > (isz)int.max - l) return INTERNAL_BUFFER_EXCEEDED~;
l += (int)(ebuf - estr);
}
if (l > int.max - pl) return INTERNAL_BUFFER_EXCEEDED?;
if (l > int.max - pl) return INTERNAL_BUFFER_EXCEEDED~;
usz len;
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
@@ -556,7 +559,7 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
case 2:
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value & 1;
value >>= 1;
}
@@ -564,13 +567,13 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
case 10:
if (!value)
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
break;
}
while (value >= 10)
{
if (len + 1 >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
if (len + 1 >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
char digit = (char)(value % 100);
buf[len:2] = DIGIT_PAIRS[2 * digit:2];
len += 2;
@@ -578,13 +581,13 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
}
if (value > 0)
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value;
}
case 16:
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
char digit = (char)value & 0xF;
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value >>= 4;
@@ -593,7 +596,7 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
case 8:
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value & 0x7;
value >>= 3;
}
@@ -613,12 +616,12 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
if (self.width && self.flags.zeropad && (negative || self.flags.plus || self.flags.space)) self.width--;
while (len < self.prec)
{
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
}
while (self.flags.zeropad && len < self.width)
{
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
}
}
@@ -633,7 +636,7 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
}
if (base != 10)
{
if (len + 1 >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
if (len + 1 >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
switch (base)
{
case 16:
@@ -652,13 +655,13 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
switch (true)
{
case negative:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '-';
case self.flags.plus:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '+';
case self.flags.space:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = ' ';
}
if (len) self.out_reverse(buf[:len])!;
@@ -738,10 +741,10 @@ fn int? printf_parse_format_field(
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
usz len = ++(*index_ptr);
if (len >= format_len) return BAD_FORMAT?;
if (*args_index_ptr >= args_len) return BAD_FORMAT?;
if (len >= format_len) return BAD_FORMAT~;
if (*args_index_ptr >= args_len) return BAD_FORMAT~;
any val = args_ptr[(*args_index_ptr)++];
if (!val.type.kindof.is_int()) return BAD_FORMAT?;
if (!val.type.kindof.is_int()) return BAD_FORMAT~;
uint? intval = types::any_to_int(val, int);
return intval ?? BAD_FORMAT?;
return intval ?? BAD_FORMAT~;
}

View File

@@ -57,6 +57,11 @@ faultdef
@return `The string containing the data read.`
*>
macro String? readline(Allocator allocator, stream = io::stdin())
{
return readline_impl{$typeof(stream)}(allocator, stream);
}
fn String? readline_impl(Allocator allocator, Stream stream) <Stream> @private
{
if (allocator == tmem)
{
@@ -99,16 +104,21 @@ macro String? treadline(stream = io::stdin())
*>
macro usz? readline_to_stream(out_stream, in_stream = io::stdin())
{
bool $is_stream = $typeof(in_stream) == InStream;
return readline_to_stream_impl{$typeof(in_stream), $typeof(out_stream)}(out_stream, in_stream);
}
fn usz? readline_to_stream_impl(OStream out_stream, IStream in_stream) <IStream, OStream> @private
{
bool $is_stream = IStream == InStream;
$if $is_stream:
var func = &in_stream.read_byte;
var func @safeinfer = &in_stream.read_byte;
char val = func((void*)in_stream)!;
$else
char val = in_stream.read_byte()!;
$endif
bool $is_out_stream = $typeof(out_stream) == OutStream;
bool $is_out_stream = OStream == OutStream;
$if $is_out_stream:
var out_func = &out_stream.write_byte;
var out_func @safeinfer = &out_stream.write_byte;
$endif
if (val == '\n') return 0;
@@ -132,7 +142,7 @@ macro usz? readline_to_stream(out_stream, in_stream = io::stdin())
if (catch err = c)
{
if (err == io::EOF) break;
return err?;
return err~;
}
if (c == '\r') continue;
if (c == '\n') break;
@@ -404,7 +414,7 @@ fn char[]? bprintf(char[] buffer, String format, args...) @maydiscard
fn void? out_buffer_fn(void *data, char c) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return BUFFER_EXCEEDED?;
if (buffer_data.written >= buffer_data.buffer.len) return BUFFER_EXCEEDED~;
buffer_data.buffer[buffer_data.written++] = c;
}

View File

@@ -11,20 +11,20 @@ macro void? native_chdir(Path path)
{
switch (libc::errno())
{
case errno::EACCES: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::ENOTDIR: return io::FILE_NOT_DIR?;
case errno::ENOENT: return io::FILE_NOT_FOUND?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
default: return io::GENERAL_ERROR?;
case errno::EACCES: return io::NO_PERMISSION~;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG~;
case errno::ENOTDIR: return io::FILE_NOT_DIR~;
case errno::ENOENT: return io::FILE_NOT_FOUND~;
case errno::ELOOP: return io::SYMLINK_FAILED~;
default: return io::GENERAL_ERROR~;
}
}
$case env::WIN32:
// TODO improve with better error handling.
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
$default:
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
$endswitch
};
}

View File

@@ -12,7 +12,7 @@ fn void*? native_fopen(String filename, String mode) @inline => @pool()
$else
void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy());
$endif
return file ?: file_open_errno()?;
return file ?: file_open_errno()~;
}
fn void? native_remove(String filename) => @pool()
@@ -27,10 +27,10 @@ fn void? native_remove(String filename) => @pool()
switch (libc::errno())
{
case errno::ENOENT:
return io::FILE_NOT_FOUND?;
return io::FILE_NOT_FOUND~;
case errno::EACCES:
default:
return io::FILE_CANNOT_DELETE?;
return io::FILE_CANNOT_DELETE~;
}
}
}
@@ -46,19 +46,19 @@ fn void*? native_freopen(void* file, String filename, String mode) @inline => @p
$else
file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file);
$endif
return file ?: file_open_errno()?;
return file ?: file_open_errno()~;
}
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?;
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()~;
}
fn usz? native_ftell(CFile file) @inline
{
long index = libc::ftell(file);
return index >= 0 ? (usz)index : file_seek_errno()?;
return index >= 0 ? (usz)index : file_seek_errno()~;
}
fn usz? native_fwrite(CFile file, char[] buffer) @inline
@@ -68,7 +68,7 @@ fn usz? native_fwrite(CFile file, char[] buffer) @inline
fn void? native_fputc(CInt c, CFile stream) @inline
{
if (libc::fputc(c, stream) == libc::EOF) return io::EOF?;
if (libc::fputc(c, stream) == libc::EOF) return io::EOF~;
}
fn usz? native_fread(CFile file, char[] buffer) @inline

View File

@@ -28,7 +28,7 @@ FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn));
fn void*? native_fopen(String filename, String mode) @inline
{
if (native_fopen_fn) return native_fopen_fn(filename, mode);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
<*
@@ -39,7 +39,7 @@ fn void*? native_fopen(String filename, String mode) @inline
fn void? native_remove(String filename) @inline
{
if (native_remove_fn) return native_remove_fn(filename);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
<*
@@ -49,35 +49,35 @@ fn void? native_remove(String filename) @inline
fn void*? native_freopen(void* file, String filename, String mode) @inline
{
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
fn usz? native_ftell(CFile file) @inline
{
if (native_ftell_fn) return native_ftell_fn(file);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
fn usz? native_fwrite(CFile file, char[] buffer) @inline
{
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
fn usz? native_fread(CFile file, char[] buffer) @inline
{
if (native_fread_fn) return native_fread_fn(file, buffer);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}
fn void? native_fputc(CInt c, CFile stream) @inline
{
if (native_fputc_fn) return native_fputc_fn(c, stream);
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
}

View File

@@ -14,25 +14,25 @@ fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || e
switch (libc::errno())
{
case errno::EBADF:
return io::FILE_NOT_VALID?;
return io::FILE_NOT_VALID~;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
case errno::EACCES:
return io::NO_PERMISSION?;
return io::NO_PERMISSION~;
case errno::ELOOP:
return io::NO_PERMISSION?;
return io::NO_PERMISSION~;
case errno::ENAMETOOLONG:
return io::NAME_TOO_LONG?;
return io::NAME_TOO_LONG~;
case errno::ENOENT:
return io::FILE_NOT_FOUND?;
return io::FILE_NOT_FOUND~;
case errno::ENOTDIR:
return io::FILE_NOT_DIR?;
return io::FILE_NOT_DIR~;
case errno::EOVERFLOW:
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
default:
return io::UNKNOWN_ERROR?;
return io::UNKNOWN_ERROR~;
}
}
}
@@ -47,14 +47,14 @@ fn usz? native_file_size(String path) @if(env::WIN32) => @pool()
return (usz)size.quadPart;
}
fn usz? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN)
fn usz? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN && !env::LINUX && !env::ANDROID && !env::BSD_FAMILY)
{
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)!;
}
fn usz? native_file_size(String path) @if(env::DARWIN)
fn usz? native_file_size(String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY)
{
Stat stat;
native_stat(&stat, path)!;

View File

@@ -12,7 +12,7 @@ macro String? getcwd(Allocator allocator)
defer if (free) libc::free(res);
if (!res)
{
if (libc::errno() != errno::ERANGE) return io::GENERAL_ERROR?;
if (libc::errno() != errno::ERANGE) return io::GENERAL_ERROR~;
res = win32::_wgetcwd(null, 0);
free = true;
}
@@ -27,7 +27,7 @@ macro String? getcwd(Allocator allocator)
if (!res)
{
// Improve error
if (libc::errno() != errno::ERANGE) return io::GENERAL_ERROR?;
if (libc::errno() != errno::ERANGE) return io::GENERAL_ERROR~;
res = posix::getcwd(null, 0);
free = true;
}
@@ -35,7 +35,7 @@ macro String? getcwd(Allocator allocator)
return res.copy(allocator);
$default:
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
$endswitch
}

View File

@@ -11,7 +11,7 @@ fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
directory = posix::opendir(dir.str_view() ? dir.str_view().zstr_tcopy() : (ZString)".");
};
defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)?;
if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)~;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
@@ -38,7 +38,7 @@ fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
WString result = dir.str_view().tconcat(`\*`).to_temp_wstring()!!;
Win32_WIN32_FIND_DATAW find_data;
Win32_HANDLE find = win32::findFirstFileW(result, &find_data);
if (find == win32::INVALID_HANDLE_VALUE) return io::CANNOT_READ_DIR?;
if (find == win32::INVALID_HANDLE_VALUE) return io::CANNOT_READ_DIR~;
defer win32::findClose(find);
do
{

View File

@@ -17,17 +17,17 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::EFAULT: return io::NO_PERMISSION~;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG~;
case errno::EDQUOT:
case errno::ENOSPC: return io::OUT_OF_SPACE?;
case errno::ENOSPC: return io::OUT_OF_SPACE~;
case errno::EISDIR:
case errno::EEXIST: return false;
case errno::ELOOP: return io::SYMLINK_FAILED?;
case errno::ENOTDIR: return io::FILE_NOT_FOUND?;
case errno::ENOENT: return io::PARENT_DIR_MISSING?;
case errno::ELOOP: return io::SYMLINK_FAILED~;
case errno::ENOTDIR: return io::FILE_NOT_FOUND~;
case errno::ENOENT: return io::PARENT_DIR_MISSING~;
default:
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
}
$case env::WIN32:
// TODO security attributes
@@ -35,18 +35,18 @@ macro bool? native_mkdir(Path path, MkdirPermissions permissions)
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return io::NO_PERMISSION?;
return io::NO_PERMISSION~;
case win32::ERROR_DISK_FULL:
return io::OUT_OF_SPACE?;
return io::OUT_OF_SPACE~;
case win32::ERROR_ALREADY_EXISTS:
return false;
case win32::ERROR_PATH_NOT_FOUND:
return io::FILE_NOT_FOUND?;
return io::FILE_NOT_FOUND~;
default:
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
}
$default:
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
$endswitch
};
}

View File

@@ -13,36 +13,36 @@ macro bool? native_rmdir(Path path)
if (!posix::rmdir(path.str_view().zstr_tcopy())) return true;
switch (libc::errno())
{
case errno::EBUSY: return io::BUSY?;
case errno::EBUSY: return io::BUSY~;
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::EFAULT: return io::NO_PERMISSION~;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG~;
case errno::ENOTDIR:
case errno::ENOENT: return false;
case errno::ENOTEMPTY: return io::DIR_NOT_EMPTY?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
default: return io::GENERAL_ERROR?;
case errno::ENOTEMPTY: return io::DIR_NOT_EMPTY~;
case errno::ELOOP: return io::SYMLINK_FAILED~;
default: return io::GENERAL_ERROR~;
}
$case env::WIN32:
if (win32::removeDirectoryW(path.str_view().to_temp_utf16()!!)) return true;
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return io::NO_PERMISSION?;
return io::NO_PERMISSION~;
case win32::ERROR_CURRENT_DIRECTORY:
return io::BUSY?;
return io::BUSY~;
case win32::ERROR_DIR_NOT_EMPTY:
return io::DIR_NOT_EMPTY?;
return io::DIR_NOT_EMPTY~;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
}
$default:
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
$endswitch
};
}

View File

@@ -10,7 +10,7 @@ fn void? native_rmtree(Path dir)
{
DIRPtr directory = posix::opendir(dir.str_view().zstr_tcopy());
defer if (directory) posix::closedir(directory);
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR~ : io::FILE_NOT_DIR~;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
@@ -27,7 +27,7 @@ fn void? native_rmtree(Path dir)
if (libc::remove(new_path.str_view().zstr_tcopy()))
{
// TODO improve
return io::GENERAL_ERROR?;
return io::GENERAL_ERROR~;
}
};
}
@@ -44,7 +44,7 @@ fn void? native_rmtree(Path path)
String s = path.str_view().tconcat("\\*");
Win32_HANDLE find = win32::findFirstFileW(s.to_temp_utf16(), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return io::CANNOT_READ_DIR?;
if (find == win32::INVALID_HANDLE_VALUE) return io::CANNOT_READ_DIR~;
defer win32::findClose(find);
do
{

View File

@@ -21,7 +21,7 @@ fn String? win32_get_known_folder_temp(Win32_REFKNOWNFOLDERID rfid) @private @if
{
Win32_PWSTR path;
Win32_HRESULT res = win32::shGetKnownFolderPath(rfid, 0x00008000 /* KF_FLAG_CREATE */, null, &path);
if (res) return io::PATH_COULD_NOT_BE_FOUND?;
if (res) return io::PATH_COULD_NOT_BE_FOUND~;
return string::from_wstring(tmem, (WString)path);
}
@@ -38,11 +38,11 @@ fn Path? native_home_directory(Allocator allocator) => @pool()
$case NETBSD:
$case OPENBSD:
$case HAIKU:
return path::new(allocator, env::tget_var("HOME")) ?? io::PATH_COULD_NOT_BE_FOUND?;
return path::new(allocator, env::tget_var("HOME")) ?? io::PATH_COULD_NOT_BE_FOUND~;
$case WIN32:
return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PROFILE));
$default:
return io::PATH_COULD_NOT_BE_FOUND?;
return io::PATH_COULD_NOT_BE_FOUND~;
$endswitch
}
@@ -67,7 +67,7 @@ fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => @poo
case TEMPLATES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "TEMPLATES"));
case SAVED_GAMES:
case SCREENSHOTS: nextcase;
default: return io::PATH_COULD_NOT_BE_FOUND?;
default: return io::PATH_COULD_NOT_BE_FOUND~;
}
$case IOS:
$case MACOS:
@@ -85,7 +85,7 @@ fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => @poo
case SAVED_GAMES:
case SCREENSHOTS:
case TEMPLATES: nextcase;
default: return io::PATH_COULD_NOT_BE_FOUND?;
default: return io::PATH_COULD_NOT_BE_FOUND~;
}
$case WIN32:
switch (dir)
@@ -100,10 +100,10 @@ fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => @poo
case SCREENSHOTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SCREENSHOTS));
case TEMPLATES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_TEMPLATES));
case PUBLIC_SHARE: nextcase;
default: return io::PATH_COULD_NOT_BE_FOUND?;
default: return io::PATH_COULD_NOT_BE_FOUND~;
}
$default:
return io::PATH_COULD_NOT_BE_FOUND?;
return io::PATH_COULD_NOT_BE_FOUND~;
$endswitch
}
@@ -121,15 +121,16 @@ fn Path? native_temp_directory(Allocator allocator) @if(!env::WIN32)
fn Path? native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool()
{
Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return io::GENERAL_ERROR?;
Char16[] buff = mem::talloc_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return io::GENERAL_ERROR?;
return path::new(allocator, string::tfrom_utf16(buff[:len]));
if (!len) return io::GENERAL_ERROR~;
Char16[] buff = mem::talloc_array(Char16, len);
Win32_DWORD res = win32::getTempPathW(len, buff);
if (!res) return io::GENERAL_ERROR~;
return path::new(allocator, string::tfrom_utf16(buff[:res]));
}
module std::io::os @if(env::NO_LIBC);
import std::io::path;
macro Path? native_home_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND?;
macro Path? native_temp_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND?;
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => io::PATH_COULD_NOT_BE_FOUND?;
macro Path? native_home_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND~;
macro Path? native_temp_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND~;
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => io::PATH_COULD_NOT_BE_FOUND~;

View File

@@ -86,7 +86,7 @@ fn PathList? ls(Allocator allocator, Path dir, bool no_dirs = false, bool no_sym
$if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
$else
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
$endif
}
@@ -137,11 +137,11 @@ macro bool? rmdir(path)
*>
fn void? rmtree(Path path)
{
if (!path.path_string.len) return INVALID_PATH?;
if (!path.path_string.len) return INVALID_PATH~;
$if $defined(os::native_rmtree):
return os::native_rmtree(path);
$else
return io::UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION~;
$endif
}
@@ -212,7 +212,7 @@ fn usz? start_of_base_name(String str, PathEnv path_env) @local
// Find the \ before "foo"
usz last_index = 2 + str[2..].index_of_char('\\')!;
// If they don't match, we're done
if (last_index > index) return INVALID_PATH?;
if (last_index > index) return INVALID_PATH~;
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
@@ -246,7 +246,7 @@ fn Path? String.to_absolute_path(self, Allocator allocator) => @pool()
fn Path? Path.absolute(self, Allocator allocator)
{
String path_str = self.str_view();
if (!path_str.len) return INVALID_PATH?;
if (!path_str.len) return INVALID_PATH~;
if (self.is_absolute()!) return new(allocator, path_str, self.env);
if (path_str == ".")
{
@@ -262,7 +262,7 @@ fn Path? Path.absolute(self, Allocator allocator)
const usz BUFFER_LEN = 4096;
WString buffer = (WString)mem::talloc_array(Char16, BUFFER_LEN);
buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN);
if (!buffer) return INVALID_PATH?;
if (!buffer) return INVALID_PATH~;
return { string::from_wstring(allocator, buffer), WIN32, allocator };
};
$else
@@ -371,10 +371,10 @@ fn usz? volume_name_len(String path, PathEnv path_env) @local
base_found = i;
continue;
}
if (is_reserved_win32_path_char(c)) return INVALID_PATH?;
if (is_reserved_win32_path_char(c)) return INVALID_PATH~;
}
if (base_found > 0 && base_found + 1 < len) return len;
return INVALID_PATH?;
return INVALID_PATH~;
case 'A'..'Z':
case 'a'..'z':
return path[1] == ':' ? 2 : 0;
@@ -392,7 +392,7 @@ fn usz? volume_name_len(String path, PathEnv path_env) @local
*>
fn Path? Path.parent(self)
{
if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return NO_PARENT?;
if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return NO_PARENT~;
foreach_r(i, c : self.path_string)
{
if (is_separator(c, self.env))
@@ -405,7 +405,7 @@ fn Path? Path.parent(self)
// Handle C:\foo
if (volume_len == i)
{
if (i + 1 == self.path_string.len) return NO_PARENT?;
if (i + 1 == self.path_string.len) return NO_PARENT~;
return { self.path_string[:i + 1], WIN32, null };
}
}
@@ -413,7 +413,7 @@ fn Path? Path.parent(self)
return { self.path_string[:i], self.env, null };
}
}
return NO_PARENT?;
return NO_PARENT~;
}
fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
@@ -455,7 +455,7 @@ fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
// The rest are names of the path elements, so check that the
// characters are valid.
if (is_reserved_path_char(c, path_env)) return INVALID_PATH?;
if (is_reserved_path_char(c, path_env)) return INVALID_PATH~;
// If we have '.' after a separator
if (c == '.' && previous_was_separator)
@@ -488,7 +488,7 @@ fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
continue;
case 2:
// This is an error: /a/../..
if (len == path_start && has_root) return INVALID_PATH?;
if (len == path_start && has_root) return INVALID_PATH~;
// If this .. at the start, or after ../? If so, we just copy ..
if (len == path_start ||
@@ -657,21 +657,21 @@ macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV)
}
fn bool? _mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) @private
{
if (!path.path_string.len) return INVALID_PATH?;
if (!path.path_string.len) return INVALID_PATH~;
if (is_dir(path)) return false;
if (exists(path)) return io::FILE_NOT_DIR?;
if (exists(path)) return io::FILE_NOT_DIR~;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return io::CANNOT_READ_DIR?;
if (!is_dir(path.parent()) ?? false) return io::CANNOT_READ_DIR~;
return os::native_mkdir(path, permissions);
}
fn bool? _rmdir(Path path) @private
{
if (!path.path_string.len) return INVALID_PATH?;
if (!path.path_string.len) return INVALID_PATH~;
return os::native_rmdir(path);
}

View File

@@ -1,7 +1,5 @@
module std::io;
import std::math;
import std::core::env;
interface InStream
{
@@ -36,7 +34,7 @@ fn usz? available(InStream s)
s.seek(curr, Seek.SET)!;
return len - curr;
}
return 0;
return io::UNSUPPORTED_OPERATION~;
}
macro bool @is_instream(#expr) @const
@@ -85,26 +83,36 @@ macro usz? read_all(stream, char[] buffer)
{
if (buffer.len == 0) return 0;
usz n = stream.read(buffer)!;
if (n != buffer.len) return UNEXPECTED_EOF?;
if (n != buffer.len) return UNEXPECTED_EOF~;
return n;
}
<*
This function will read to the end of the stream.
@require @is_instream(stream)
*>
macro char[]? read_fully(Allocator allocator, stream)
{
usz len = available(stream)!;
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
// Efficient path if it is possible to pre-allocate
if (try len = available(stream))
{
read += stream.read(data[read:len - read])!;
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
{
read += stream.read(data[read:len - read])!;
}
return data[:len];
}
return data[:len];
ByteWriter writer;
writer.init(allocator);
copy_to(stream, &writer)!;
return writer.array_view();
}
<*
@require @is_outstream(stream)
*>
@@ -112,7 +120,7 @@ macro usz? write_all(stream, char[] buffer)
{
if (buffer.len == 0) return 0;
usz n = stream.write(buffer)!;
if (n != buffer.len) return INCOMPLETE_WRITE?;
if (n != buffer.len) return INCOMPLETE_WRITE~;
return n;
}
@@ -128,7 +136,7 @@ macro usz? read_using_read_byte(s, char[] buffer)
if (catch err = c)
{
if (err == io::EOF) return len;
return err?;
return err~;
}
*cptr = c;
len++;
@@ -152,7 +160,7 @@ macro char? read_byte_using_read(s)
{
char[1] buffer;
usz read = s.read(&buffer)!;
if (read != 1) return io::EOF?;
if (read != 1) return io::EOF~;
return buffer[0];
}
@@ -197,12 +205,12 @@ macro usz? copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
if (catch err = len)
{
if (err == io::EOF) return total_copied;
return err?;
return err~;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])!;
total_copied += len;
if (written != len) return INCOMPLETE_WRITE?;
if (written != len) return INCOMPLETE_WRITE~;
}
}
@@ -224,8 +232,8 @@ macro usz? read_varint(stream, x_ptr)
char? c = stream.read_byte();
if (catch err = c)
{
if (err == io::EOF) return io::UNEXPECTED_EOF?;
return err?;
if (err == io::EOF) return io::UNEXPECTED_EOF~;
return err~;
}
n++;
if (c & 0x80 == 0)
@@ -241,7 +249,7 @@ macro usz? read_varint(stream, x_ptr)
x |= (c & 0x7F) << shift;
shift += 7;
}
return math::OVERFLOW?;
return math::OVERFLOW~;
}
<*
@require @is_outstream(stream)

View File

@@ -49,7 +49,7 @@ fn usz? ReadBuffer.read(&self, char[] bytes) @dynamic
fn char? ReadBuffer.read_byte(&self) @dynamic
{
if (self.read_idx == self.write_idx) self.refill()!;
if (self.read_idx == self.write_idx) return io::EOF?;
if (self.read_idx == self.write_idx) return io::EOF~;
char c = self.bytes[self.read_idx];
self.read_idx++;
return c;
@@ -132,5 +132,5 @@ fn void? WriteBuffer.write_byte(&self, char c) @dynamic
fn void? WriteBuffer.write_pending(&self) @local
{
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
if (self.index != 0) return INCOMPLETE_WRITE?;
if (self.index != 0) return INCOMPLETE_WRITE~;
}

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