Compare commits

...

347 Commits

Author SHA1 Message Date
Christoffer Lerno
808ce6d605 Fix deprecation warnings not silenced. 2026-02-26 00:28:36 +01:00
Christoffer Lerno
09606a2998 Update hint for deprecation 2026-02-26 00:15:58 +01:00
Christoffer Lerno
0a21323652 Fixes 2026-02-26 00:11:55 +01:00
Christoffer Lerno
c990b5aaf1 Typekind enums are changed CONST_ENUM -> CONSTDEF, DISTINCT -> TYPEDEF. 2026-02-25 23:52:13 +01:00
Christoffer Lerno
98e0985a10 Fix error message on deprecated enum = 2026-02-25 23:27:22 +01:00
Christoffer Lerno
5974bf6cd0 Make first RC 0.7.10 2026-02-25 18:11:42 +01:00
Manu Linares
992890065a vendor-fetch lists available libraries (#2976)
* vendor-fetch lists available libraries

from `github.com/c3lang/vendor` when no library is specified.

* add release notes
2026-02-24 22:55:18 +01:00
Christoffer Lerno
d4dbe27072 Add mention of LLVM libs 2026-02-24 20:00:50 +01:00
Christoffer Lerno
56a11ae3ca Fix text in error message. 2026-02-24 19:07:38 +01:00
konimarti
f1d03c194f distributions: fix tests (#2975)
Remove stochastic elements in the distributions tests and ensure they
are deterministic.

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-24 16:18:53 +01:00
Christoffer Lerno
0085fe73ad Fix test 2026-02-24 15:18:57 +01:00
Christoffer Lerno
a238b62483 Typo 2026-02-24 14:58:28 +01:00
Book-reader
58022e7cca only enable emulated TLS on android compiling natively in termux (#2974) 2026-02-24 00:10:22 +01:00
Christoffer Lerno
d2f046780d - Compiler crash using ?? with a void? macro #2973 2026-02-23 23:28:32 +01:00
Christoffer Lerno
ec65c5761e Fixed test 2026-02-23 19:21:56 +01:00
Christoffer Lerno
0ed917cdc2 Improve error when trying to use an extern const as a compile time constant. #2969 2026-02-23 19:15:30 +01:00
Christoffer Lerno
8373ab0509 Typo in releasenotes. 2026-02-23 19:15:30 +01:00
srkkov
8f7610345d More x86 instructions (#2964)
* Added most x86 cryptographic instructions

* Fixed popcnt test

* Fixed asm_ops_x64_2.c3t test

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-23 15:27:36 +01:00
Zack Puhl
8bd963ecaf Allow Async w/ Subprocess for POSIX Systems (#2953)
* add native_fflush libc passthru

* add `pipe2` which combines `pipe` and `fcntl` - see docs

* POSIX subprocess: incorporate async reader capability and and fflush for writer

* use the real-name O_NONBLOCK since it's available

* typo

* ok... remove `pipe2` and do this with an existing method......

* Update releasenotes.md

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-22 01:59:44 +01:00
Christoffer Lerno
dc52478c09 - Warn on use of visibility modifiers on methods. #2962 2026-02-21 21:10:08 +01:00
Gantsev Denis
e1ec4b1235 add more x86 instructions 2026-02-20 21:23:35 +01:00
Lucas Alves
7c68396c0d Enable default NDK detection for Android x86_64 (#2910)
* android: detect NDK for x86_64 targets

* Treat Android as POSIX in is_posix

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-20 21:07:44 +01:00
Manu Linares
eae7d0c4a1 stdlib: std::compression::zip and std::compression::deflate (#2930)
* stdlib: implement `std::compression::zip` and `std::compression::deflate`

- C3 implementation of DEFLATE (RFC 1951) and ZIP archive handling.
- Support for reading and writing archives using STORE and DEFLATE
methods.
- Decompression supports both fixed and dynamic Huffman blocks.
- Compression using greedy LZ77 matching.
- Zero dependencies on libc.
- Stream-based entry reading and writing.
- Full unit test coverage.

NOTE: This is an initial implementation. Future improvements could be:

- Optimization of the LZ77 matching (lazy matching).
- Support for dynamic Huffman blocks in compression.
- ZIP64 support for large files/archives.
- Support for encryption and additional compression methods.

* optimizations+refactoring

deflate:
- replace linear search with hash-based match finding.
- implement support for dynamic Huffman blocks using the Package-Merge
algorithm.
- add streaming decompression.
- add buffered StreamBitReader.

zip:
- add ZIP64 support.
- add CP437 and UTF-8 filename encoding detection.
- add DOS date/time conversion and timestamp preservation.
- add ZipEntryReader for streaming entry reads.
- implement ZipArchive.extract and ZipArchive.recover helpers.

other:
- Add `set_modified_time` to std::io;
- Add benchmarks and a few more unit tests.

* zip: add archive comment support

add tests

* forgot to rename the benchmark :(

* detect utf8 names on weird zips

fix method not passed to open_writer

* another edge case where directory doesn't end with /

* testing utilities

- detect encrypted zip
- `ZipArchive.open_writer` default to DEFLATE

* fix zip64 creation, add tests

* fix ZIP header endianness for big-endian compatibility

Update ZipLFH, ZipCDH, ZipEOCD, Zip64EOCD, and Zip64Locator structs to
use little-endian bitstruct types from std::core::bitorder

* fix ZipEntryReader position tracking and seek logic ZIP_METHOD_STORE

added a test to track this

* add package-merge algorithm attribution

Thanks @konimarti

* standalone deflate_benchmark.c3 against `miniz`

* fix integer overflows, leaks and improve safety

* a few safety for 32-bit systems and tests

* deflate compress optimization

* improve match finding, hash updates, and buffer usage

* use ulong for zip offsets

* style changes (#18)

* style changes

* update tests

* style changes in `deflate.c3`

* fix typo

* Allocator first. Some changes to deflate to use `copy_to`

* Fix missing conversion on 32 bits.

* Fix deflate stream. Formatting. Prefer switch over if-elseif

* - Stream functions now use long/ulong rather than isz/usz for seek/available.
- `instream.seek` is replaced by `set_cursor` and `cursor`.
- `instream.available`, `cursor` etc are long/ulong rather than isz/usz to be correct on 32-bit.

* Update to constdef

* Fix test

---------

Co-authored-by: Book-reader <thevoid@outlook.co.nz>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-20 20:41:34 +01:00
mmoustafa8108
5055e86518 test: added comprehensive tests for in-string & complex nested comments (#2956)
* test: added comprehensive tests for in-string & complex nested comments

* Properly handle error

---------

Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2026-02-20 13:24:08 +01:00
Manu Linares
4b13ad692a fixes for RISC-V ABI Implementation #1567 (#2937)
* fixes for RISC-V ABI Implementation #1567

Fixed RISC-V floating-point ABI by correcting the target triple to
`riscv64-unknown-linux-gnu`, adding the `target-abi` module flag, and
ensuring ABI-required CPU features are enabled.

I tested this with:
```bash
build/c3c compile-only --target linux-riscv64 rv_hello.c3
readelf -h obj/linux-riscv64/rv_hello.o | grep Flags
# Output: Flags: 0x5, RVC, double-float ABI
```

```bash
# and qemu because I don't have a riscv machine :/
qemu-riscv64-static -L /usr/riscv64-linux-gnu ./rv_hello
```

---

@lerno I purposedly left these two failing tests to clearly see the
difference.

`test/test_suite/abi/riscv64-lp64-lp64f-abi-1.c3t`
`test/test_suite/abi/riscv64-lp64-abi.c3t`

* improve Linux cross-compilation, specifically for RISC-V

- Implement automatic sysroot and CRT object discovery for RISC-V.
- Fix dynamic linker paths and emulation flags for LLD.
- Link against libgcc to resolve required arithmetic symbols.

* Update tests.

* fix linker CRT detection by centralizing host arch check

use target_host_arch() in linker.c
move target_host_arch() from hostinfo.c to target.c

* missing debug info

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-20 01:53:29 +01:00
Christoffer Lerno
7c81bb35ca Refactored contracts (#2960)
* - Properly support `@deprecated` as contract.
- Support deprecating enum values.
- Refactor contracts

* Always copy enum constants.

* Bugfix.
2026-02-20 01:51:28 +01:00
Christoffer Lerno
5a82f672b5 Update to constdef 2026-02-20 01:13:20 +01:00
Christoffer Lerno
0387d7666d - Trying to slice an indexable type leads to misleading error message #2958 2026-02-20 00:13:45 +01:00
Christoffer Lerno
392c78860d Add bitorder functions store_le, load_le, store_be, store_le. 2026-02-19 23:59:55 +01:00
Christoffer Lerno
585c66100d - Member access on a struct returned by the assignment expression, cause crash #2947 2026-02-19 20:43:45 +01:00
konimarti
8bb974829d math: implement discrete and continuous distributions (#2955)
* math: implement discrete and continuous distributions

Implement a comprehensive set of continuous and discrete probability
distributions with support for PDF, CDF, inverse CDF, random sampling,
mean, and variance calculations.

The following distributions are implemented:
* Normal
* Uniform
* Exponential
* Chi-Squared
* F-Distribution
* Student t
* Binomial
* Poisson

* update releasenotes.md

* Formatting

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-19 20:09:11 +01:00
Christoffer Lerno
6b3139940c Formatting to be consistent. Make unrolling use macro. 2026-02-19 18:22:21 +01:00
Damien Wilson
9efb9b90d1 Minor attribute combination checking (#2918)
* Minor attribute combination checking

* Added tests to the test suite.
2026-02-19 17:58:39 +01:00
soerlemans
152558f5bc Optimized adler32 hashing algorithm. (#2948)
* Optimized adler32 implementations.

 - Adapted adler32 implementation from Crypto++ public domain library.
 - Added unit tests for adler32 hashing algorithm.

* tabified adler32 implementation to match stdlib.

* Formatting to be consistent. Make unrolling use macro.

---------

Co-authored-by: soerlemans <sebasoerlemans+git@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-19 17:51:33 +01:00
Manuel Barrio Linares
7ae4c5a1ab some refactoring of io formatter internals
extracted logic for hex-buffers (%h), string-padding (%s), hex-floats
(%a), and collection printing into helper functions.
2026-02-19 16:39:27 +01:00
Fernando López Guevara
9fbf6bc213 enforce buff channel size > 0 (#2952)
* fix(stdlib): enforce BufferedChannel size > 0 and add regression test

* chore(repo): ignore cmake/test artifacts

* Formatting

---------

Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2026-02-18 14:34:41 +01:00
Christoffer Lerno
143fa70f87 Remove init for Blake3 2026-02-17 17:43:54 +01:00
Christoffer Lerno
3ac701be0e Improve error message when providing alias with a typeid expression where a type was expected. #2944 2026-02-17 13:02:34 +01:00
Manu Linares
8cb23cff29 split test_hash_vector_macro into per-type functions (#2940)
* split `test_hash_vector_macro` into per-type functions

it went from a single 18367 instructions function, to eleven separate
functions with (1000 to 1600) instructions each
thats 15649 instructions total, 2718 less instructions overall.

```
$ build/c3c compile-test --suppress-run -O1 --print-large-functions -D
SLOW_TESTS test/unit/ 2>&1 | sort -n | grep "test_hash_vector"
    1041 instructions found in
std::core::builtins:test_hash_vector_int128
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1041 instructions found in
std::core::builtins:test_hash_vector_uint128
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1305 instructions found in std::core::builtins:test_hash_vector_long
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1305 instructions found in
std::core::builtins:test_hash_vector_ulong
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1453 instructions found in std::core::builtins:test_hash_vector_int
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1453 instructions found in std::core::builtins:test_hash_vector_uint
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1565 instructions found in
std::core::builtins:test_hash_vector_short
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1565 instructions found in
std::core::builtins:test_hash_vector_ushort
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1629 instructions found in std::core::builtins:test_hash_vector_char
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1629 instructions found in
std::core::builtins:test_hash_vector_ichar
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
    1663 instructions found in std::core::builtins:test_hash_vector_bool
(/home/mb/scripts/c3c/test/unit/stdlib/core/builtintests.c3) - function
is very long.
```

* made the `test_hash_vector_internal` generic
2026-02-17 02:30:30 +01:00
Manuel Barrio Linares
ec6ba8e7ca refactor md5.body function to reduce instruction count
- replaced manual unrolling with loop structures and constant arrays
- instruction count reduced from 12445 to 4016
- maybe about 1 to 2% performance loss on some benchs but take this
number with a grain of salt.
2026-02-16 14:12:54 +01:00
Manuel Barrio Linares
df030ac51c optimize blake3 using a runtime for loop
instruction count went from 60k to 9k
no difference in speed for -O2 or higher
2026-02-16 02:31:47 +01:00
Manu Linares
4b03a84b00 optimize test_ct_intlog2 test and whirlpool hash (#2938)
* optimize `test_ct_intlog2` while still covering all 128 bit positions

* refactor whirlpool to reduce code bloat

replaced the fully unrolled round loop with a runtime loop, reducing
instruction count by 80k in `process_block` and yielding aprox 30%
performance boost due to improved cache locality.

* use compile-time arrays for `test_ct_intlog2`
2026-02-16 01:54:46 +01:00
Christoffer Lerno
5d0c41da6b Refactor dylib name. 2026-02-16 01:21:05 +01:00
Christoffer Lerno
990e9685d2 - Adding the incorrect sized vector to a pointer vector would cause a crash. 2026-02-16 00:39:14 +01:00
Christoffer Lerno
a7309b217e Add --print-large-functions for checking which functions likely dominate the compile time. 2026-02-16 00:13:19 +01:00
Christoffer Lerno
d7cf8fa9ab Reduce macro inlining size. Remove non-const atomic ordering. 2026-02-15 00:43:29 +01:00
Christoffer Lerno
a78a169a17 Slim down the string_to_int tests by 800 MB. 2026-02-15 00:05:35 +01:00
Christoffer Lerno
d80a8629a6 - On assert known false, the message was not show for no-args.
- Fixup in socket_private.c3
2026-02-14 23:49:27 +01:00
Christoffer Lerno
14779bd467 Set CP_UTF8 to underlying number. 2026-02-14 01:49:03 +01:00
Christoffer Lerno
eb80776988 More fixes for typedef @constinit change 2026-02-14 01:37:53 +01:00
Samuel
fac9054f1b Fix array initializer analysis (#2925)
* Fix array initializer analysis: improved semantic checking for arrays with inferred or fixed length,
* Update phrasing

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-13 23:38:07 +01:00
Zack Puhl
768d24d580 [stdlib] Add array even, odd, and unlace macros 2026-02-13 23:19:31 +01:00
Christoffer Lerno
e299a4b630 - Change typedef and const enums to not convert from literals by default. (#2934)
- Add `@constinit` to allow old typedef behaviour.
2026-02-13 20:39:47 +01:00
Christoffer Lerno
bbf89815d6 - Individual warning settings added. 2026-02-13 18:25:19 +01:00
Christoffer Lerno
cc6d552e3d - --safe=no disabled compile-time errors on compile-time known runtime @require checks #2936 2026-02-13 16:34:45 +01:00
Christoffer Lerno
27ceded331 Add a few const char* 2026-02-12 16:51:34 +01:00
Christoffer Lerno
3f279b2f1c Refactoring: make CT_IDENT and unresolved expression. 2026-02-12 16:38:18 +01:00
Christoffer Lerno
acc4a900f5 - New const enum declaration syntax.
- New enum associated value syntax.
2026-02-12 14:43:56 +01:00
Fernando López Guevara
f079fa82b2 fix(std-io): make uint128 decimal formatting safe (#2924)
* fix(std-io): make uint128 decimal formatting safe and add all-base
numeric coverage
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-11 23:50:16 +01:00
Manuel Barrio Linares
7665720264 add cache for Embedded/QEMU Tests
I forgot to add this one
2026-02-11 23:48:18 +01:00
Christoffer Lerno
83705ab340 Fix chacha20. 2026-02-11 23:44:26 +01:00
Christoffer Lerno
adea3dd83f Fix chacha20 2026-02-11 23:39:47 +01:00
Christoffer Lerno
9b52be9ba6 - unsigned % signed and unsigned / signed is no longer allowed without explicit casts, except for const denominators. #2928 2026-02-11 23:18:08 +01:00
Christoffer Lerno
202349d88f - Improved underlining errors/warnings when unicode is used. #2887 2026-02-11 16:49:22 +01:00
Manuel Barrio Linares
18ab18958b CI: add retries and package caching across all platforms
- Add retry loops to all package manager commands (apt, apk, pacman,
pkg).
- Implement daily package caching to minimize external server hits.
- Enable Docker layer caching in CI using buildx and GHA backend.
  - Sync CI Docker build steps with the build-with-docker.sh logic.
  - We don't, or more accurately, can't actually use
`build-with-docker.sh` anymore in CI. Maybe we should move it to
scripts/tools/ to keep the root directory clean.
- add ramdisk for BSD, shave 30/60 seconds.
- change BDSs to cross-platform-actions/action for a cleaner ci and skip
SLOW_TESTS for BSDs: this shaves a few seconds
- set global CACHE_INVALIDATION_SEED
2026-02-11 10:01:14 +01:00
Christoffer Lerno
2237d3b836 Missing check 2026-02-11 02:55:07 +01:00
Christoffer Lerno
e2f17a770b Using [] or .foo on $$ functions would not raise error but instead crash #2919. 2026-02-11 02:53:17 +01:00
konimarti
40e6a2c4a3 codepage: add single-byte code page support (#2891)
* codepage: add single-byte code page support

Add std::encoding::codepage with a shared engine for converting between
single-byte code pages and UTF-8 using table-driven mappings.

Introduce generated tables and wrappers for several code pages[1] each
exposing encode/decode helpers built on a common CodePageTable
structure.

The mapping data is generated by cpgen[2] from the Unicode Consortium’s
published code page mapping files and follows the Unicode standard’s
interpretation of control characters (abstract characters) rather than
historical VGA glyph shapes.

[1] Code page overview/groups:

    DOS/OEM code pages (legacy PC):
    cp437 cp737 cp775 cp850 cp852 cp855 cp857 cp860 cp861 cp862 cp863
    cp864 cp865 cp866 cp869 cp874

    Windows code pages (ANSI/Windows):
    cp1250 cp1251 cp1252 cp1253 cp1254 cp1255 cp1256 cp1257 cp1258

    ISO/IEC 8859 series (Latin/Regional):
    iso_8859_1 iso_8859_2 iso_8859_3 iso_8859_4 iso_8859_5 iso_8859_6
    iso_8859_7 iso_8859_8 iso_8859_9 iso_8859_10 iso_8859_11 iso_8859_13
    iso_8859_14 iso_8859_15 iso_8859_16

[2] github.com/konimarti/cpgen

Signed-off-by: Koni Marti <koni.marti@gmail.com>

* codepage: change encoding format, streamline api

* Use enum to collect the data.

---------

Signed-off-by: Koni Marti <koni.marti@gmail.com>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-11 01:10:12 +01:00
g.denis
a80e40a798 fix fedora dyn lib (#2917)
* also look into /usr/lib64
2026-02-10 23:12:04 +01:00
Manuel Barrio Linares
de8a733c77 Implement lazy-loaded libcurl
- Uses `dlopen` to load libcurl at runtime for better portability.
- Replaces the FETCH_AVAILABLE macro with download_available() check.
- Provides descriptive error messages when libcurl is missing.
2026-02-10 13:05:43 +01:00
Christoffer Lerno
c5d7a03859 Remove unnecessary free. 2026-02-10 11:46:26 +01:00
Christoffer Lerno
0b82537700 Fix 2026-02-10 11:27:55 +01:00
Christoffer Lerno
7b8299c8dd - Reallocating overaligned memory with the LibcAllocator was unsafe. 2026-02-10 11:23:51 +01:00
Christoffer Lerno
dcbd0f8f2d Fix dwarf definitions 2026-02-10 10:23:53 +01:00
Boris Barbulovski
402c00e230 Add Android Aarch64 and x64 support status 2026-02-09 10:26:40 +01:00
Christoffer Lerno
0542c05b88 Update tests. 2026-02-09 10:23:03 +01:00
Christoffer Lerno
67e224c62f Fix windows main 2026-02-09 10:15:49 +01:00
Christoffer Lerno
565b08846f Remove use of tempnam. 2026-02-09 00:04:05 +01:00
Christoffer Lerno
80ad0e02ad Fix tests and error in returning error on function name. 2026-02-08 22:54:24 +01:00
Lucas Alves
b8ee5a4150 fix: add non-API Android NDK lib search path (#2884)
* fix: add sysroot/usr/lib/<triple> to Android linker search paths

* Fix library search order: move libc++_shared logic after API libc paths

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-08 15:02:37 +01:00
Christoffer Lerno
445dd155b7 Another #2814 2026-02-08 15:01:27 +01:00
Rauny
abb3efca00 fix: prevent Homebrew dylib contamination in macOS builds (#2908)
When C3_FETCH_LLVM=ON, the fetched LLVM artifact is self-contained.
Two issues caused the macOS release binary to embed hardcoded
/opt/homebrew paths, making it fail on systems without Homebrew:

1. /opt/homebrew/lib was unconditionally appended to LLVM_LIBRARY_DIRS,
   causing find_library to discover Homebrew's LLD dylibs (whose
   LC_ID_DYLIB points to /opt/homebrew/opt/lld/lib/...). Guard this
   behind NOT C3_FETCH_LLVM so it only applies when using a
   system-installed LLVM.

2. In the static linking block (C3_LINK_DYNAMIC=OFF), find_library NAMES
   listed .dylib before .a, so shared libraries were preferred even when
   static linking was intended. Reorder to prefer .a, keeping .dylib as
   a fallback for systems that only ship shared libraries.

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-08 13:57:07 +01:00
Christoffer Lerno
de0b317461 Fix default main template. 2026-02-08 13:38:43 +01:00
Christoffer Lerno
21758476d4 - For c3c init with library templates, provide example exported functions. #2898 2026-02-07 21:31:12 +01:00
Christoffer Lerno
b69f78be30 Flag --cpu-flags doesn't work if the first item is an exclusion. #2905 2026-02-07 21:21:57 +01:00
Christoffer Lerno
89b9f52f1e Add libc mktemp and tempnam. Update test. 2026-02-07 20:25:28 +01:00
Christoffer Lerno
02a67254cc Fix win forwarding functions. 2026-02-07 19:57:43 +01:00
Manuel Barrio Linares
120f56ac5f disable --fetch-msvc when no curl 2026-02-07 01:22:54 +01:00
Laura Kirsch
5b93212f43 Added Xorshiro128++ random number generator. (#2846)
* Added Xorshiro128++ random number generator.

* Fixes to xorshiro implementation + tests.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-06 20:14:56 +01:00
Zack Puhl
c57415ea78 Optional byte limit for readline
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2026-02-06 19:15:29 +01:00
Manuel Barrio Linares
36dfbdff45 implement progress bar for fetch-msvc
using deterministic milestones
2026-02-06 17:59:36 +01:00
Christoffer Lerno
d07e454dfe Fix missing ) 2026-02-06 17:56:40 +01:00
Christoffer Lerno
40aa4d4dcd Add Murmur3 hash 2026-02-06 17:53:19 +01:00
Manu Linares
0be291e0d7 compiler: added c3c fetch-msvc command (#2854)
* compiler: added `c3c fetch-msvc` command
ported `msvc_build_libraries.py` to built-in compiler 'fetch-msvc'
subcommand.
* fix alpine build
* fix windows build
* fix WinHTTP download to correctly handle full URLs by splitting hostname and path.
* fix old `cp` and escape args for `file_copy_file`
also cleanup the temp directory when we are finished
it would be better to use a cache :|
* remove msiextract dependency
* Auto-fetch `msvc_sdk` if missing
- auto trigger `msvc_sdk` download when compiling for Windows if no SDK
is detected.
- make native Windows SDK detection non-fatal to allow fallback to the
portable SDK
- add debug logging for diagnostics
* removed the LZX decompression logic
- moved `msvc_sdk` to user cache locations to avoid permission issues.
- refined terminal messaging for better accuracy and clarity.
* use %LOCALAPPDATA% on Windows
* Add support for C3_MSVC_SDK
* Update release notes.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-06 14:01:49 +01:00
Manuel Barrio Linares
35d84390bd Simplify CI, do more with less.
- Rename Docker job to **"LLVM ${{ matrix.llvm_version }}
Compatibility"** for better visibility.
- Limit `build_type` to **Debug** for MSYS2, OpenBSD, NetBSD, and Nix
jobs to reduce CI time.
- Refactor **Alpine Linux** job: updated to 3.23, simplified dependency
installation, and removed redundant LLVM matrix.
- Modernize **Docker** builds:
    - Added automatic architecture detection (x86_64/ARM) for CMake
downloads.
    - Generalized LLVM repository logic to support multiple Ubuntu
codenames (Focal/Jammy/Noble).
    - Fixed git permissions issues in containerized builds using
`safe.directory`.
- Standardized Android build packaging and added conditional testing for
Debug builds.
2026-02-06 12:00:00 +01:00
Christoffer Lerno
124efb2684 Improve error message on const int* 2026-02-06 02:00:26 +01:00
Christoffer Lerno
5c158e481b Always have wmain. Cleanup synthetic main generation. Some cleanup for generics. 2026-02-06 01:28:19 +01:00
Christoffer Lerno
42b79d19c1 Update all stub methods. 2026-02-06 00:55:57 +01:00
Zack Puhl
4521a25284 [stdlib] Add PEM Encoding/Decoding Module (#2858)
* [stdlib] Add PEM Encoding/Decoding Module
* release notes
* Removed some unnecessary macro usages. Fixed memory handling with headers.
* Make end of line a parameter. Internal encode method -> function. Use more tmem. Remove t-functions.
* Update API

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2026-02-06 00:01:07 +01:00
Foxy-Boxes
1933d47ba1 add Intel user interrupt instructions (#2767)
* add Intel user interrupt instructions

* Example code did not actually work, so was removed.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-05 20:56:35 +01:00
Book-reader
bb9e9b54cf Improve android support & add CI (#2664)
* Change context destruction order.

* enable emulated tls on termux

* Fix stdlib on android

* Add a CI workflow for android termux

* update release notes

* use the new unified CI tests on android

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-05 20:04:16 +01:00
Manu Linares
eddbfb8ba3 CMakeLists and CI: Use our custom llvm (#2893)
* c3-llvm: new CMakeLists and CI

uses custom llvm version 21.x from
"https://github.com//ManuLinares/llvm-custom-builds/"

* use c3lang/llvm-for-c3 latest release

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-05 18:55:09 +01:00
Christoffer Lerno
2c55d6e220 Further fix shadowing of generics 2026-02-05 18:43:54 +01:00
Christoffer Lerno
472c49de25 Differentiate errors on generic mismatch. 2026-02-05 16:20:17 +01:00
Christoffer Lerno
016254d38c Improve error message when using functions as values #2856 2026-02-05 16:03:56 +01:00
Christoffer Lerno
054fcd2deb Update advice when an enum is found with "= 2" and no associated values. 2026-02-05 13:57:35 +01:00
Christoffer Lerno
d71aa10f62 - Const inline enums would not always implicitly get converted to the underlying type.
- Update to dstring.append_string to take any type converting to String.
2026-02-05 13:04:57 +01:00
Christoffer Lerno
1d0aef4522 Remove unnecessary macro. 2026-02-05 02:07:42 +01:00
Christoffer Lerno
71b673d241 - Shadowing not detected for generic declarations #2876 2026-02-05 01:33:47 +01:00
Christoffer Lerno
dcf695c726 Fix list[0].i = 5 when list[0] returns a pointer. #2888 2026-02-05 00:34:17 +01:00
Christoffer Lerno
5e656603a5 - Remove dependency on temp allocator in File.open. 2026-02-04 14:27:52 +01:00
Christoffer Lerno
b4426c095b - Remove dependency on temp allocator in String.join. 2026-02-04 13:06:12 +01:00
Christoffer Lerno
5f32c97094 - Compiler crash when using arrays of vectors in lists. #2889 2026-02-04 12:40:16 +01:00
Christoffer Lerno
1c8cb7fa11 Method resolution and $define now works together well unless definitions are out of order for real. 2026-02-04 12:23:37 +01:00
Christoffer Lerno
6bc606a9b1 Removed analyse top pass. Remove sub module tracking. Fix errors where ? should be ~ 2026-02-02 12:20:34 +01:00
Christoffer Lerno
d509b4caa1 Updated info about contributing bindings. 2026-01-31 22:07:14 +01:00
Zack Puhl
12975d07ac [stdlib] Reduce inline code volume from sorting macros (#2831)
* reduce codegen in sorting macros

* remove testing file...

* Fix and some renaming, removing some sub-modules that should not be in use.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-31 20:19:57 +01:00
Laura Kirsch
3d512abaf7 Fix non-LLVM build. 2026-01-31 14:07:48 +01:00
Kiana
a9d93c93d5 Improve linker error message (#2880)
* Improve linker error message

* use file_util/file_is_dir instead

* cleanup unused function

* Some minor updates and merging latest master.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-31 04:55:47 +01:00
Manu Linares
1b7601fdbb stdlib: optimize math::next_power_of_2 to O(1) using hardware CLZ (#2839)
* stdlib: optimize math::next_power_of_2 to O(1) using hardware CLZ

- updated DString.reserve to use this

* bit smearing

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-01-31 00:20:58 +01:00
Smite Rust
373013046d Fix formatting of path assignment in msvc_build_libraries.py 2026-01-30 22:01:40 +01:00
Christoffer Lerno
10d369d766 Updated grammar, and 0.7.10 first commit 2026-01-30 21:59:20 +01:00
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
702 changed files with 35962 additions and 9210 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

13
.gitignore vendored
View File

@@ -73,8 +73,15 @@ out/
/cmake-build-debug/
/cmake-build-release/
# Emacs files
CMakeFiles/cmake.check_cache
CMakeCache.txt
# etags(Emacs), ctags, gtags
TAGS
GPATH
GRTAGS
GTAGS
tags
# Clangd LSP files
/.cache/
@@ -90,8 +97,8 @@ result
# tests
/test/tmp/*
/test/testrun
/test/test_suite_runner
testrun
test_suite_runner
# patches, originals and rejects
*.patch

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,13 +95,18 @@ set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
set(C3_USE_TB OFF CACHE BOOL "Use TB")
set(C3_LLD_DIR "" CACHE STRING "Use custom LLD directory")
set(C3_LLD_INCLUDE_DIR "" CACHE STRING "Use custom LLD include directory")
set(C3_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
set(C3_FETCH_LLVM OFF CACHE BOOL "Automatically download LLVM artifacts")
set(C3_LLVM_TAG "llvm_21.x" CACHE STRING "Tag/Branch to download LLVM from")
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
set(TCC_LIB_PATH "/usr/lib/tcc/libtcc1.a" CACHE STRING "Use custom libtcc1.a path")
set(C3_OPTIONS
C3_LINK_DYNAMIC
C3_WITH_LLVM
C3_FETCH_LLVM
C3_LLVM_TAG
C3_LLVM_VERSION
C3_USE_MIMALLOC
C3_MIMALLOC_TAG
@@ -106,9 +129,6 @@ if(C3_USE_MIMALLOC)
)
FetchContent_MakeAvailable(mimalloc)
endif()
if (NOT WIN32)
find_package(CURL)
endif()
find_package(Git QUIET)
if(C3_USE_TB AND GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
@@ -136,55 +156,83 @@ if(C3_ENABLE_CLANGD_LSP)
endif(C3_ENABLE_CLANGD_LSP)
if(C3_WITH_LLVM)
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
if (C3_LLVM_VERSION STREQUAL "auto")
set(C3_LLVM_VERSION ${C3_LLVM_DEFAULT_VERSION})
if(C3_FETCH_LLVM)
# 1. Determine local platform ID
if (WIN32)
set(C3_LLVM_PLATFORM "windows-amd64")
elseif (APPLE)
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
set(C3_LLVM_PLATFORM "darwin-aarch64")
else()
set(C3_LLVM_PLATFORM "darwin-amd64")
endif()
else() # Linux
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "aarch64")
set(C3_LLVM_PLATFORM "linux-aarch64")
elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "riscv64" OR CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "riscv64")
set(C3_LLVM_PLATFORM "linux-riscv64")
else()
set(C3_LLVM_PLATFORM "linux-amd64")
endif()
endif()
FetchContent_Declare(
LLVM_Windows
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt.7z
)
FetchContent_Declare(
LLVM_Windows_debug
URL https://github.com/c3lang/win-llvm/releases/download/llvm_19_1_5/llvm-19.1.5-windows-amd64-msvc17-libcmt-dbg.7z
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Loading Windows LLVM debug libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows_debug)
set(llvm_dir ${llvm_windows_debug_SOURCE_DIR})
else()
message("Loading Windows LLVM libraries, this may take a while...")
FetchContent_MakeAvailable(LLVM_Windows)
set(llvm_dir ${llvm_windows_SOURCE_DIR})
endif()
message("Loaded Windows LLVM libraries into ${llvm_dir}")
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
# 2. Determine if we want Debug or Release
set(C3_LLVM_SUFFIX "")
set(C3_LLVM_TYPE "Release")
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(C3_LLVM_SUFFIX "-dbg")
set(C3_LLVM_TYPE "Debug")
endif()
# 3. Construct URL
set(C3_LLVM_ARTIFACT_NAME "llvm-${C3_LLVM_PLATFORM}${C3_LLVM_SUFFIX}")
#set(C3_LLVM_URL "https://github.com/c3lang/c3-llvm/releases/download/${C3_LLVM_TAG}/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
#set(C3_LLVM_URL "https://github.com//ManuLinares/llvm-custom-builds/releases/download/${C3_LLVM_TAG}/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
# We could also just set "latest" here to always fetch the latest release
set(C3_LLVM_URL "https://github.com/c3lang/llvm-for-c3/releases/latest/download/${C3_LLVM_ARTIFACT_NAME}.tar.gz")
message(STATUS "Fetching ${C3_LLVM_TYPE} LLVM artifact for ${C3_LLVM_PLATFORM}...")
message(STATUS "URL: ${C3_LLVM_URL}")
FetchContent_Declare(
LLVM_Artifact
URL ${C3_LLVM_URL}
)
FetchContent_MakeAvailable(LLVM_Artifact)
# 4. Point CMake to the fetched location
set(llvm_dir ${llvm_artifact_SOURCE_DIR})
set(CMAKE_PREFIX_PATH ${llvm_dir} ${CMAKE_PREFIX_PATH})
set(LLVM_DIR "${llvm_dir}/lib/cmake/llvm")
set(LLD_DIR "${llvm_dir}/lib/cmake/lld")
# TEST: For Windows, we might need to add the bin dir to prefix path for find_package to work well
if (WIN32)
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_dir} ${CMAKE_SYSTEM_PREFIX_PATH})
endif()
find_package(LLVM REQUIRED CONFIG NO_DEFAULT_PATH)
find_package(LLD REQUIRED CONFIG NO_DEFAULT_PATH)
else()
# Add paths for LLVM CMake files of version 19 and higher as they follow a new installation
# layout and are now in /usr/lib/llvm/*/lib/cmake/llvm/ rather than /usr/lib/cmake/llvm/
#
# Because of CMAKE_FIND_PACKAGE_SORT_ORDER CMAKE_FIND_PACKAGE_SORT_DIRECTION,
# the newest version will always be found first.
c3_print_variables(CMAKE_PREFIX_PATH)
if (DEFINED LLVM_DIR)
message(STATUS "Looking for LLVM CMake files in user-specified directory ${LLVM_DIR}")
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
# Legacy MSVC path if someone explicitly disabled fetching but is on MSVC
find_package(LLVM REQUIRED CONFIG)
find_package(LLD REQUIRED CONFIG)
else()
# Default system search
file (GLOB LLVM_CMAKE_PATHS "/usr/lib/llvm/*/lib/cmake/llvm/")
list (APPEND CMAKE_PREFIX_PATH ${LLVM_CMAKE_PATHS} "/usr/lib/")
message(STATUS "No LLVM_DIR specified, searching default directories ${CMAKE_PREFIX_PATH}")
endif()
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
if (NOT C3_LLVM_VERSION STREQUAL "auto")
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
else()
find_package(LLVM REQUIRED CONFIG)
endif()
endif()
endif()
if (EXISTS /opt/homebrew/lib)
if (NOT C3_FETCH_LLVM AND EXISTS /opt/homebrew/lib)
list(APPEND LLVM_LIBRARY_DIRS /opt/homebrew/lib)
endif()
@@ -260,20 +308,25 @@ if(C3_WITH_LLVM)
message(STATUS "Looking for static lld libraries in ${LLVM_LIBRARY_DIRS}")
# These don't seem to be reliable on windows.
find_library(LLD_COFF NAMES liblldCOFF.dylib lldCOFF.lib lldCOFF.a liblldCOFF.dll.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_COMMON NAMES liblldCommon.dylib lldCommon.lib lldCommon.a liblldCommon.dll.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_ELF NAMES liblldELF.dylib lldELF.lib lldELF.a liblldELF.dll.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a liblldCOFF.dylib lldCOFF.lib liblldCOFF.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a liblldCommon.dylib lldCommon.lib liblldCommon.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_ELF NAMES lldELF.a liblldELF.a liblldELF.dylib lldELF.lib liblldELF.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
if (NOT ${CMAKE_SYSTEM_NAME} STREQUAL "OpenBSD")
find_library(LLD_MACHO NAMES liblldMachO.dylib lldMachO.lib lldMachO.a liblldMachO.dll.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a liblldMachO.dylib lldMachO.lib liblldMachO.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
else()
set(LLD_MACHO "")
endif()
find_library(LLD_MINGW NAMES liblldMinGW.dylib lldMinGW.lib lldMinGW.a liblldMinGW.dll.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_WASM NAMES liblldWasm.dylib lldWasm.lib lldWasm.a liblldWasm.dll.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a liblldMinGW.dylib lldMinGW.lib liblldMinGW.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a liblldWasm.dylib lldWasm.lib liblldWasm.dll.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
else()
message(STATUS "Looking for shared lld libraries in ${LLVM_LIBRARY_DIRS}")
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
#find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH REQUIRED)
if(UNIX AND NOT WIN32)
find_library(LLVM NAMES libLLVM.so PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
else()
find_library(LLVM NAMES libLLVM.a LLVM.lib PATHS ${LLVM_LIBRARY_DIRS} REQUIRED)
endif()
set(llvm_libs ${LLVM})
# These don't seem to be reliable on windows.
@@ -301,17 +354,36 @@ if(C3_WITH_LLVM)
if (APPLE)
set(lld_libs ${lld_libs} xar)
find_file(RT_ASAN_DYNAMIC NAMES libclang_rt.asan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
find_file(RT_TSAN_DYNAMIC NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
find_file(RT_UBSAN_DYNAMIC NAMES libclang_rt.ubsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
find_file(RT_LSAN_DYNAMIC NAMES libclang_rt.lsan_osx_dynamic.dylib PATHS "${LLVM_LIBRARY_DIR}/clang/${LLVM_MAJOR_VERSION}/lib/darwin" ${LLVM_CRT_LIBRARY_DIR})
set(sanitizer_runtime_libraries
${RT_ASAN_DYNAMIC}
${RT_TSAN_DYNAMIC}
# Unused
# ${RT_UBSAN_DYNAMIC}
# ${RT_LSAN_DYNAMIC}
)
if (llvm_dir)
file(GLOB_RECURSE RT_ASAN_DYNAMIC "${llvm_dir}/*libclang_rt.asan_osx_dynamic.dylib")
file(GLOB_RECURSE RT_TSAN_DYNAMIC "${llvm_dir}/*libclang_rt.tsan_osx_dynamic.dylib")
endif()
if (NOT RT_ASAN_DYNAMIC OR NOT RT_TSAN_DYNAMIC)
# Fallback to searching in LLVM_LIBRARY_DIRS (for non-fetched LLVM)
find_file(RT_ASAN_DYNAMIC_PATH NAMES libclang_rt.asan_osx_dynamic.dylib PATHS ${LLVM_LIBRARY_DIRS} PATH_SUFFIXES "clang/${LLVM_MAJOR_VERSION}/lib/darwin" "clang/${LLVM_PACKAGE_VERSION}/lib/darwin")
find_file(RT_TSAN_DYNAMIC_PATH NAMES libclang_rt.tsan_osx_dynamic.dylib PATHS ${LLVM_LIBRARY_DIRS} PATH_SUFFIXES "clang/${LLVM_MAJOR_VERSION}/lib/darwin" "clang/${LLVM_PACKAGE_VERSION}/lib/darwin")
if (RT_ASAN_DYNAMIC_PATH)
set(RT_ASAN_DYNAMIC ${RT_ASAN_DYNAMIC_PATH})
endif()
if (RT_TSAN_DYNAMIC_PATH)
set(RT_TSAN_DYNAMIC ${RT_TSAN_DYNAMIC_PATH})
endif()
endif()
if (RT_ASAN_DYNAMIC)
list(GET RT_ASAN_DYNAMIC 0 RT_ASAN_DYNAMIC)
endif()
if (RT_TSAN_DYNAMIC)
list(GET RT_TSAN_DYNAMIC 0 RT_TSAN_DYNAMIC)
endif()
if (RT_ASAN_DYNAMIC AND RT_TSAN_DYNAMIC)
set(sanitizer_runtime_libraries
${RT_ASAN_DYNAMIC}
${RT_TSAN_DYNAMIC}
)
endif()
endif()
message(STATUS "Linking to llvm libs ${llvm_libs}")
@@ -389,10 +461,12 @@ add_executable(c3c
src/utils/whereami.c
src/utils/cpus.c
src/utils/unzipper.c
src/utils/msi.c
src/compiler/c_codegen.c
src/compiler/decltable.c
src/compiler/methodtable.c
src/compiler/mac_support.c
src/utils/fetch_msvc.c
src/compiler/windows_support.c
src/compiler/codegen_asm.c
src/compiler/asm_target.c
@@ -435,13 +509,22 @@ if(C3_WITH_LLVM)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=1)
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
if (MSVC)
# Use the same detected CRT for the wrapper
set_target_properties(c3c_wrappers PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
set_target_properties(miniz PROPERTIES MSVC_RUNTIME_LIBRARY "MultiThreaded${MSVC_CRT_SUFFIX}")
target_compile_options(c3c PRIVATE
"$<$<CONFIG:Debug>:/EHa>"
"$<$<CONFIG:Release>:/EHsc>")
endif()
if(C3_LLD_INCLUDE_DIR)
target_include_directories(c3c_wrappers PRIVATE ${C3_LLD_INCLUDE_DIR})
endif()
else()
target_sources(c3c PRIVATE src/utils/hostinfo.c)
target_compile_definitions(c3c PUBLIC LLVM_AVAILABLE=0)
target_link_libraries(c3c m)
endif()
target_include_directories(c3c PRIVATE
@@ -491,7 +574,7 @@ else()
endif()
if(C3_WITH_LLVM)
target_link_libraries(c3c ${llvm_libs} miniz c3c_wrappers ${lld_libs})
target_link_libraries(c3c miniz c3c_wrappers)
target_include_directories(c3c PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/include/")
@@ -499,11 +582,11 @@ if(C3_WITH_LLVM)
target_include_directories(c3c_wrappers PRIVATE
"${CMAKE_SOURCE_DIR}/wrapper/include/")
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
target_link_libraries(c3c_wrappers PUBLIC ${lld_libs} ${llvm_libs})
else()
target_link_libraries(c3c ${llvm_libs} miniz ${lld_libs})
target_link_libraries(c3c miniz ${lld_libs} ${llvm_libs})
endif()
@@ -520,12 +603,11 @@ if(MINGW)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--stack,8388608")
endif ()
if (CURL_FOUND)
target_link_libraries(c3c ${CURL_LIBRARIES})
target_include_directories(c3c PRIVATE ${CURL_INCLUDE_DIRS})
target_compile_definitions(c3c PUBLIC CURL_FOUND=1)
else()
target_compile_definitions(c3c PUBLIC CURL_FOUND=0)
if (NOT WIN32)
# For dlopen support
if (CMAKE_DL_LIBS)
target_link_libraries(c3c ${CMAKE_DL_LIBS})
endif()
endif()
@@ -552,13 +634,21 @@ if(MSVC)
endif()
if(C3_WITH_LLVM)
set(clang_lib_dir ${llvm_dir}/lib/clang/${C3_LLVM_VERSION}/lib/windows)
set(sanitizer_runtime_libraries
${clang_lib_dir}/clang_rt.asan-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.lib
${clang_lib_dir}/clang_rt.asan_dynamic-x86_64.dll
${clang_lib_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
# The the sanitizer libs are in the folder "lib/clang/21/lib/windows/" but use the find anyway
file(GLOB_RECURSE FOUND_ASAN_LIB "${llvm_dir}/*clang_rt.asan_dynamic-x86_64.lib")
if(FOUND_ASAN_LIB)
list(GET FOUND_ASAN_LIB 0 _asan_path)
get_filename_component(_asan_dir "${_asan_path}" DIRECTORY)
set(sanitizer_runtime_libraries
${_asan_dir}/clang_rt.asan_dynamic-x86_64.lib
${_asan_dir}/clang_rt.asan_dynamic-x86_64.dll
${_asan_dir}/clang_rt.asan_dynamic_runtime_thunk-x86_64.lib)
message(STATUS "Found Sanitizer binaries at: ${_asan_dir}")
else()
message(WARNING "Could not find sanitizer runtime libraries in ${llvm_dir}")
endif()
endif()
else()
if (C3_WITH_LLVM AND NOT LLVM_ENABLE_RTTI)
target_compile_options(c3c_wrappers PRIVATE -fno-rtti)
@@ -572,6 +662,7 @@ else()
-Wno-unused-function
-Wno-unused-variable
-Wno-unused-parameter
-Wno-char-subscripts
)
target_link_options(c3c PRIVATE -pthread)
endif()
@@ -611,8 +702,10 @@ if (C3_WITH_LLVM AND DEFINED sanitizer_runtime_libraries)
if (APPLE)
# Change LC_ID_DYLIB to be rpath-based instead of having an absolute path
# We set DYLD_LIBRARY_PATH so the tools (which might be dynamic) can find libLLVM.dylib in the artifact
string(REPLACE ";" ":" _dyld_path "${LLVM_LIBRARY_DIRS}")
add_custom_command(TARGET c3c POST_BUILD
COMMAND find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
COMMAND ${CMAKE_COMMAND} -E env "DYLD_LIBRARY_PATH=${_dyld_path}:$ENV{DYLD_LIBRARY_PATH}" find $<TARGET_FILE_DIR:c3c>/c3c_rt -type f -name "*.dylib" -execdir ${LLVM_TOOLS_BINARY_DIR}/llvm-install-name-tool -id @rpath/{} {} $<SEMICOLON>
VERBATIM)
endif()

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.10**.
The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis.
@@ -162,6 +162,8 @@ The compiler is currently verified to compile on Linux, OpenBSD, Windows and Mac
| MacOS x64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
| MacOS Aarch64 | Yes | Yes + cross compilation | Yes | Yes | Yes | Yes* |
| iOS Aarch64 | No | Untested | Untested | Yes | Yes | Yes* |
| Android Aarch64 | No | Untested | Untested | Untested | Untested | Yes* |
| Android x64 | No | Untested | Untested | Untested | Untested | Yes* |
| Linux x86 | Yes | Yes | Yes | Yes | Yes | Yes* |
| Linux x64 | Yes | Yes | Yes | Yes | Yes | Yes* |
| Linux Aarch64 | Yes | Yes | Yes | Yes | Yes | Yes* |
@@ -195,7 +197,7 @@ More platforms will be supported in the future.
- If you wish to contribute with ideas, please file issues or discuss on Discord.
- Interested in contributing to the stdlib? Please get in touch on Discord.
- Compilation instructions for other Linux and Unix variants are appreciated.
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
- Would you like to contribute bindings to some library? It would be nice to have support for SDL3 and more. If you have created some bindings, please submit them to https://github.com/c3lang/vendor.
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
- Do you have some specific area you have deep knowledge of and could help make C3 even better at doing? File or comment on issues.
@@ -384,7 +386,7 @@ scoop install c3
#### Getting started with a "hello world"
Create a `main.c3` file with:
```c++
```c3
module hello_world;
import std::io;
@@ -513,7 +515,7 @@ provide the link path to the LLVM CMake directories, e.g. `cmake -B build -S . -
*A note on compiling for Linux/Unix/MacOS: to be able to fetch vendor libraries
libcurl is needed. The CMake script should detect it if it is available. Note that
this functionality is non-essential and it is perfectly fine to user the compiler without it.*
this functionality is non-essential and it is perfectly fine to use the compiler without it.*
#### Licensing
@@ -523,7 +525,7 @@ which is licensed under LGPL 3.0.
This means you are free to use all parts of standard library,
tests, benchmarks, grammar, examples and so on under the MIT license, including
using those libraries and tests if your build your own C3 compiler.
using those libraries and tests if you build your own C3 compiler.
#### Editor plugins

View File

@@ -0,0 +1,51 @@
module deflate_benchmarks;
import std::compression::deflate;
const uint SMALL_ITERATIONS = 50000;
const uint LARGE_ITERATIONS = 100;
// Data to compress
const char[] SMALL_DATA = { [0..1023] = 'A' };
const char[] LARGE_DATA = { [0..1048575] = 'B' };
char[] small_compressed;
char[] large_compressed;
fn void initialize_bench() @init
{
small_compressed = deflate::compress(mem, SMALL_DATA)!!;
large_compressed = deflate::compress(mem, LARGE_DATA)!!;
set_benchmark_warmup_iterations(2);
set_benchmark_max_iterations(10);
set_benchmark_func_iterations($qnameof(deflate_compress_small), SMALL_ITERATIONS);
set_benchmark_func_iterations($qnameof(deflate_decompress_small), SMALL_ITERATIONS);
set_benchmark_func_iterations($qnameof(deflate_compress_large), LARGE_ITERATIONS);
set_benchmark_func_iterations($qnameof(deflate_decompress_large), LARGE_ITERATIONS);
}
// =======================================================================================
module deflate_benchmarks @benchmark;
import std::compression::deflate;
import std::core::mem;
fn void deflate_compress_small() => @pool()
{
char[]? compressed = deflate::compress(tmem, SMALL_DATA);
}
fn void deflate_decompress_small() => @pool()
{
char[]? decompressed = deflate::decompress(tmem, small_compressed);
}
fn void deflate_compress_large() => @pool()
{
char[]? compressed = deflate::compress(tmem, LARGE_DATA);
}
fn void deflate_decompress_large() => @pool()
{
char[]? decompressed = deflate::decompress(tmem, large_compressed);
}

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,57 @@
module blake3_bench;
fn void initialize_bench() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(128);
input = mem::alloc_array(char, BUFSZ);
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
input_slice = input[:BUFSZ];
}
fn void teardown_bench() @finalizer
{
mem::free(input);
input = null;
}
char* input;
char[] input_slice;
const usz BUFSZ = 1024 * 1024;
module blake3_bench @benchmark;
import std::hash;
fn void blake3_hash()
{
runtime::@start_benchmark();
char[*] myset = blake3::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_sha256()
{
runtime::@start_benchmark();
char[*] myset = sha256::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_sha512()
{
runtime::@start_benchmark();
char[*] myset = sha512::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_whirlpool()
{
runtime::@start_benchmark();
char[*] myset = whirlpool::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}

View File

@@ -0,0 +1,41 @@
module md5_bench;
fn void initialize_bench() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(128);
input = mem::alloc_array(char, BUFSZ);
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
input_slice = input[:BUFSZ];
}
fn void teardown_bench() @finalizer
{
mem::free(input);
input = null;
}
char* input;
char[] input_slice;
const usz BUFSZ = 1024 * 1024;
module md5_bench @benchmark;
import std::hash;
fn void md5_hash()
{
runtime::@start_benchmark();
char[*] myset = md5::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_sha256()
{
runtime::@start_benchmark();
char[*] myset = sha256::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}

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

@@ -0,0 +1,57 @@
module whirlpool_bench;
fn void initialize_bench() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(128);
input = mem::alloc_array(char, BUFSZ);
input[:BUFSZ] = (char[]){ [0..BUFSZ-1] = 0xA5 }[..];
input_slice = input[:BUFSZ];
}
fn void teardown_bench() @finalizer
{
mem::free(input);
input = null;
}
char* input;
char[] input_slice;
const usz BUFSZ = 1024 * 1024;
module whirlpool_bench @benchmark;
import std::hash;
fn void whirlpool_hash()
{
runtime::@start_benchmark();
char[*] myset = whirlpool::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_sha256()
{
runtime::@start_benchmark();
char[*] myset = sha256::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_sha512()
{
runtime::@start_benchmark();
char[*] myset = sha512::hash(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}
fn void compared_with_streebog_512()
{
runtime::@start_benchmark();
char[*] myset = streebog::hash_512(input_slice);
runtime::@end_benchmark();
mem::zero_volatile(myset[..]);
}

View File

@@ -30,7 +30,8 @@ chmod -R 777 build bin
exec $DOCKER run -i --rm \
-v "$PWD":/home/c3c/source \
-w /home/c3c/source $IMAGE bash -c \
"cmake -S . -B build \
"git config --global --add safe.directory /home/c3c/source && \
cmake -S . -B build \
-G Ninja \
-DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE \
-DCMAKE_C_COMPILER=clang-$LLVM_VERSION \

View File

@@ -2,48 +2,44 @@ ARG UBUNTU_VERSION=22.04
FROM ubuntu:${UBUNTU_VERSION}
ARG LLVM_VERSION=18
ENV LLVM_DEV_VERSION=20
ARG CMAKE_VERSION=3.20.0
ARG CMAKE_VERSION=3.20
# Prevent interactive prompts during apt install
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev && \
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-$CMAKE_VERSION-linux-x86_64.sh && \
RUN for i in 1 2 3; do apt-get update && break || sleep 2; done && \
apt-get install -y --fix-missing \
wget gnupg software-properties-common lsb-release \
zlib1g zlib1g-dev python3 ninja-build curl g++ libcurl4-openssl-dev git && \
CODENAME=$(lsb_release -cs) && \
ARCH=$(uname -m) && \
wget https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-${ARCH}.sh && \
mkdir -p /opt/cmake && \
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
rm cmake-${CMAKE_VERSION}-linux-x86_64.sh && \
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake
sh cmake-${CMAKE_VERSION}-linux-${ARCH}.sh --prefix=/opt/cmake --skip-license && \
ln -s /opt/cmake/bin/cmake /usr/local/bin/cmake && \
rm cmake-${CMAKE_VERSION}-linux-${ARCH}.sh
RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - && \
if [ "${LLVM_VERSION}" -lt 18 ]; then \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev libmlir-${LLVM_VERSION} \
libmlir-${LLVM_VERSION}-dev mlir-${LLVM_VERSION}-tools; \
elif [ "${LLVM_VERSION}" -lt "${LLVM_DEV_VERSION}" ]; then \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_VERSION} main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal-${LLVM_VERSION} \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} clang++-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
RUN CODENAME=$(lsb_release -cs) && \
for i in 1 2; do wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key && break || sleep 2; done | apt-key add - && \
if [ "${LLVM_VERSION}" -ge 16 ]; then \
add-apt-repository "deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME}-${LLVM_VERSION} main"; \
else \
add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main" && \
apt-get update && \
apt-get install -y -t llvm-toolchain-focal \
libpolly-${LLVM_VERSION}-dev \
clang-${LLVM_VERSION} llvm-${LLVM_VERSION} llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} liblld-${LLVM_VERSION}-dev; \
add-apt-repository "deb http://apt.llvm.org/${CODENAME}/ llvm-toolchain-${CODENAME} main"; \
fi && \
for i in 1 2 3; do apt-get update && break || sleep 2; done && \
apt-get install -y --fix-missing \
clang-${LLVM_VERSION} \
clang++-${LLVM_VERSION} \
llvm-${LLVM_VERSION} \
llvm-${LLVM_VERSION}-dev \
lld-${LLVM_VERSION} \
liblld-${LLVM_VERSION}-dev \
libpolly-${LLVM_VERSION}-dev && \
rm -rf /var/lib/apt/lists/*
RUN groupadd -g 1337 c3c && \
useradd -m -u 1337 -g c3c c3c
# Add cmake to PATH for user c3c
USER c3c
ENV PATH="/opt/cmake/bin:${PATH}"
WORKDIR /home/c3c
WORKDIR /home/c3c

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2023-2025 Eduardo José Gómez Hernández. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::atomic::types{Type};
module std::atomic::types;
struct Atomic
struct Atomic <Type>
{
Type data;
}
@@ -11,147 +11,87 @@ struct Atomic
<*
Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
@param ordering : "The ordering, cannot be release or acquire-release."
@require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
@param $ordering : "The ordering, cannot be release or acquire-release."
@require $ordering != RELEASE && $ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
*>
macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.load(&self, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
switch(ordering)
{
case NOT_ATOMIC: return $$atomic_load(data, false, AtomicOrdering.NOT_ATOMIC.ordinal);
case UNORDERED: return $$atomic_load(data, false, AtomicOrdering.UNORDERED.ordinal);
case RELAXED: return $$atomic_load(data, false, AtomicOrdering.RELAXED.ordinal);
case ACQUIRE: return $$atomic_load(data, false, AtomicOrdering.ACQUIRE.ordinal);
case SEQ_CONSISTENT: return $$atomic_load(data, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
case ACQUIRE_RELEASE:
case RELEASE: unreachable("Invalid ordering.");
}
return $$atomic_load(&self.data, false, $ordering.ordinal);
}
<*
Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
@param ordering : "The ordering, cannot be acquire or acquire-release."
@require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
@param $ordering : "The ordering, cannot be acquire or acquire-release."
@require $ordering != ACQUIRE && $ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
*>
macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro void Atomic.store(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
switch(ordering)
{
case NOT_ATOMIC: $$atomic_store(data, value, false, AtomicOrdering.NOT_ATOMIC.ordinal);
case UNORDERED: $$atomic_store(data, value, false, AtomicOrdering.UNORDERED.ordinal);
case RELAXED: $$atomic_store(data, value, false, AtomicOrdering.RELAXED.ordinal);
case RELEASE: $$atomic_store(data, value, false, AtomicOrdering.RELEASE.ordinal);
case SEQ_CONSISTENT: $$atomic_store(data, value, false, AtomicOrdering.SEQ_CONSISTENT.ordinal);
case ACQUIRE_RELEASE:
case ACQUIRE: unreachable("Invalid ordering.");
}
$$atomic_store(&self.data, value, false, $ordering.ordinal);
}
macro Type Atomic.add(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.add(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_add, data, value, ordering);
return atomic::fetch_add(&self.data, value, $ordering);
}
macro Type Atomic.sub(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.sub(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_sub, data, value, ordering);
return atomic::fetch_sub(&self.data, value, $ordering);
}
macro Type Atomic.mul(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.mul(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_mul, data, value, ordering);
return atomic::fetch_mul(&self.data, value, $ordering);
}
macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.div(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_div, data, value, ordering);
return atomic::fetch_div(&self.data, value, $ordering);
}
macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.max(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_max, data, value, ordering);
return atomic::fetch_max(&self.data, value, $ordering);
}
macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
macro Type Atomic.min(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_min, data, value, ordering);
return atomic::fetch_min(&self.data, value, $ordering);
}
macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
macro Type Atomic.or(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_or, data, value, ordering);
return atomic::fetch_or(&self.data, value, $ordering);
}
macro Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
macro Type Atomic.xor(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
return atomic::fetch_xor(&self.data, value, $ordering);
}
macro Type Atomic.and(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
macro Type Atomic.and(&self, Type value, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_and, data, value, ordering);
return atomic::fetch_and(&self.data, value, $ordering);
}
macro Type Atomic.shr(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
macro Type Atomic.shr(&self, Type amount, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering);
return atomic::fetch_shift_right(&self.data, amount, $ordering);
}
macro Type Atomic.shl(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
macro Type Atomic.shl(&self, Type amount, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering);
return atomic::fetch_shift_left(&self.data, amount, $ordering);
}
macro Type Atomic.set(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
macro Type Atomic.set(&self, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
{
Type* data = &self.data;
return @atomic_exec_no_arg(atomic::flag_set, data, ordering);
return atomic::flag_set(&self.data, $ordering);
}
macro Type Atomic.clear(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
macro Type Atomic.clear(&self, AtomicOrdering $ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
{
Type* data = &self.data;
return @atomic_exec_no_arg(atomic::flag_clear, data, ordering);
}
macro @atomic_exec(#func, data, value, ordering) @local
{
switch(ordering)
{
case RELAXED: return #func(data, value, RELAXED);
case ACQUIRE: return #func(data, value, ACQUIRE);
case RELEASE: return #func(data, value, RELEASE);
case ACQUIRE_RELEASE: return #func(data, value, ACQUIRE_RELEASE);
case SEQ_CONSISTENT: return #func(data, value, SEQ_CONSISTENT);
default: unreachable("Ordering may not be non-atomic or unordered.");
}
}
macro @atomic_exec_no_arg(#func, data, ordering) @local
{
switch(ordering)
{
case RELAXED: return #func(data, RELAXED);
case ACQUIRE: return #func(data, ACQUIRE);
case RELEASE: return #func(data, RELEASE);
case ACQUIRE_RELEASE: return #func(data, ACQUIRE_RELEASE);
case SEQ_CONSISTENT: return #func(data, SEQ_CONSISTENT);
default: unreachable("Ordering may not be non-atomic or unordered.");
}
return atomic::flag_clear(&self.data, $ordering);
}
module std::atomic;
@@ -175,8 +115,8 @@ macro bool is_native_atomic_type($Type)
$case FLOAT:
$case BOOL:
return true;
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return is_native_atomic_type($Type.inner);
$default:
return false;
@@ -272,7 +212,7 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "/ must be defined between the values."
@require $defined(*ptr / y) : "/ must be defined between the values."
@require $ordering != NOT_ATOMIC && $ordering != UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)

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;
@@ -150,7 +150,7 @@ fn bool HashMap.is_initialized(&map)
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
hashmap_put_all_for_create(self, other_map);
return self;
}
@@ -175,13 +175,13 @@ fn usz HashMap.len(&map) @inline
fn Value*? HashMap.get_ref(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
if (!map.count) return NOT_FOUND~;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn Value* HashMap.get_or_create_ref(&map, Key key) @operator(&[])
@@ -204,13 +204,13 @@ fn Value* HashMap.get_or_create_ref(&map, Key key) @operator(&[])
fn Entry*? HashMap.get_entry(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
if (!map.count) return NOT_FOUND~;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -233,7 +233,7 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
hashmap_add_entry(map, hash, key, val, index);
return val;
}
@@ -269,13 +269,13 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
return true;
}
}
map.add_entry(hash, key, value, index);
hashmap_add_entry(map, hash, key, value, index);
return false;
}
fn void? HashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
if (!hashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
}
fn void HashMap.clear(&map)
@@ -290,9 +290,9 @@ fn void HashMap.clear(&map)
{
Entry *to_delete = next;
next = next.next;
map.free_entry(to_delete);
hashmap_free_entry(map, to_delete);
}
map.free_entry(entry);
hashmap_free_entry(map, entry);
*entry_ref = null;
}
map.count = 0;
@@ -302,7 +302,7 @@ fn void HashMap.free(&map)
{
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
hashmap_free_internal(map, map.table.ptr);
map.table = {};
}
@@ -402,7 +402,7 @@ fn HashMapKeyIterator HashMap.key_iter(&self)
// --- private methods
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
fn void hashmap_add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
@@ -411,11 +411,11 @@ fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_ind
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
hashmap_resize(map, map.table.len * 2);
}
}
fn void HashMap.resize(&map, uint new_capacity) @private
fn void hashmap_resize(HashMap* map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
@@ -425,9 +425,9 @@ fn void HashMap.resize(&map, uint new_capacity) @private
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map.transfer(new_table);
hashmap_transfer(map, new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
hashmap_free_internal(map, old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
@@ -443,7 +443,7 @@ fn usz? HashMap.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void HashMap.transfer(&map, Entry*[] new_table) @private
fn void hashmap_transfer(HashMap* map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
@@ -462,20 +462,20 @@ fn void HashMap.transfer(&map, Entry*[] new_table) @private
}
}
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
fn void hashmap_put_all_for_create(HashMap* map, HashMap* other_map) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
while (e)
{
map.put_for_create(e.key, e.value);
hashmap_put_for_create(map, e.key, e.value);
e = e.next;
}
}
}
fn void HashMap.put_for_create(&map, Key key, Value value) @private
fn void hashmap_put_for_create(HashMap* map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -487,15 +487,15 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
return;
}
}
map.create_entry(hash, key, value, i);
hashmap_create_entry(map, hash, key, value, i);
}
fn void HashMap.free_internal(&map, void* ptr) @inline @private
fn void hashmap_free_internal(HashMap* map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
fn bool hashmap_remove_entry_for_key(HashMap* map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
@@ -516,7 +516,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
{
prev.next = next;
}
map.free_entry(e);
hashmap_free_entry(map, e);
return true;
}
prev = e;
@@ -525,7 +525,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
return false;
}
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
fn void hashmap_create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
@@ -536,12 +536,12 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
map.count++;
}
fn void HashMap.free_entry(&self, Entry *entry) @local
fn void hashmap_free_entry(HashMap* map, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
allocator::free(map.allocator, entry.key);
$endif
self.free_internal(entry);
hashmap_free_internal(map, entry);
}

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"
@@ -135,7 +135,7 @@ fn bool HashSet.is_initialized(&set)
fn HashSet* HashSet.init_from_set(&self, Allocator allocator, HashSet* other_set)
{
self.init(allocator, other_set.table.len, other_set.load_factor);
self.put_all_for_create(other_set);
hashset_put_all_for_create(self, other_set);
return self;
}
@@ -213,7 +213,7 @@ fn bool HashSet.add(&set, Value value)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
hashset_add_entry(set, hash, value, index);
return true;
}
@@ -258,7 +258,7 @@ fn bool HashSet.contains(&set, Value value)
*>
fn void? HashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
if (!hashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
}
fn usz HashSet.remove_all(&set, Value[] values)
@@ -266,7 +266,7 @@ fn usz HashSet.remove_all(&set, Value[] values)
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
if (hashset_remove_entry_for_value(set, v)) total++;
}
return total;
}
@@ -279,7 +279,7 @@ fn usz HashSet.remove_all_from(&set, HashSet* other)
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
if (hashset_remove_entry_for_value(set, val)) total++;
};
return total;
}
@@ -291,7 +291,7 @@ fn void HashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
hashset_free_internal(set, set.table.ptr);
*set = {};
}
@@ -314,10 +314,10 @@ fn void HashSet.clear(&set)
{
Entry *to_delete = next;
next = next.next;
set.free_entry(to_delete);
hashset_free_entry(set, to_delete);
}
set.free_entry(entry);
hashset_free_entry(set, entry);
*entry_ref = null;
}
set.count = 0;
@@ -327,7 +327,7 @@ fn void HashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
hashset_resize(set, math::next_power_of_2(capacity));
}
}
@@ -464,17 +464,17 @@ fn bool HashSet.is_subset(&self, HashSet* other)
// --- private methods
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
fn void hashset_add_entry(HashSet* set, uint hash, Value value, uint bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, { .hash = hash, .value = value, .next = set.table[bucket_index] });
set.table[bucket_index] = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
hashset_resize(set, set.table.len * 2);
}
}
fn void HashSet.resize(&self, usz new_capacity) @private
fn void hashset_resize(HashSet* self, usz new_capacity) @private
{
Entry*[] old_table = self.table;
usz old_capacity = old_table.len;
@@ -484,9 +484,9 @@ fn void HashSet.resize(&self, usz new_capacity) @private
return;
}
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
self.transfer(new_table);
hashset_transfer(self, new_table);
self.table = new_table;
self.free_internal(old_table.ptr);
hashset_free_internal(self, old_table.ptr);
self.threshold = (uint)(new_capacity * self.load_factor);
}
@@ -502,7 +502,7 @@ fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void HashSet.transfer(&self, Entry*[] new_table) @private
fn void hashset_transfer(HashSet* self, Entry*[] new_table) @private
{
Entry*[] src = self.table;
uint new_capacity = new_table.len;
@@ -521,20 +521,20 @@ fn void HashSet.transfer(&self, Entry*[] new_table) @private
}
}
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
fn void hashset_put_all_for_create(HashSet* set, HashSet* other_set) @private
{
if (!other_set.count) return;
foreach (Entry *e : other_set.table)
{
while (e)
{
set.put_for_create(e.value);
hashset_put_for_create(set, e.value);
e = e.next;
}
}
}
fn void HashSet.put_for_create(&set, Value value) @private
fn void hashset_put_for_create(HashSet* set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
@@ -546,15 +546,15 @@ fn void HashSet.put_for_create(&set, Value value) @private
return;
}
}
set.create_entry(hash, value, i);
hashset_create_entry(set, hash, value, i);
}
fn void HashSet.free_internal(&self, void* ptr) @inline @private
fn void hashset_free_internal(HashSet* self, void* ptr) @inline @private
{
allocator::free(self.allocator, ptr);
}
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
fn void hashset_create_entry(HashSet* set, uint hash, Value value, int bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, {
.hash = hash,
@@ -569,7 +569,7 @@ fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @pr
Removes the entry for the specified value if present
@return "true if found and removed, false otherwise"
*>
fn bool HashSet.remove_entry_for_value(&set, Value value) @private
fn bool hashset_remove_entry_for_value(HashSet* set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
@@ -590,7 +590,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
{
prev.next = next;
}
set.free_entry(e);
hashset_free_entry(set, e);
return true;
}
prev = e;
@@ -600,7 +600,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
return false;
}
fn void HashSet.free_entry(&set, Entry *entry) @private
fn void hashset_free_entry(HashSet* set, Entry *entry) @private
{
allocator::free(set.allocator, entry);
}
@@ -634,7 +634,7 @@ fn Value? HashSetIterator.next(&self)
}
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn usz HashSetIterator.len(&self) @operator(len)

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);
@@ -73,7 +73,7 @@ fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null;
macro void InterfaceList.push(&self, element)
{
if (!self.allocator) self.allocator = tmem;
self._append(allocator::clone(self.allocator, element));
interfacelist_append(self, allocator::clone(self.allocator, element));
}
<*
@@ -93,7 +93,7 @@ fn void InterfaceList.free_element(&self, Type element) @inline
*>
fn Type? InterfaceList.copy_pop(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
defer self.free_element(self.entries[self.size]);
return (Type)allocator::clone_any(allocator, self.entries[--self.size]);
}
@@ -114,7 +114,7 @@ fn Type? InterfaceList.tcopy_pop(&self) => self.copy_pop(tmem);
*>
fn Type? InterfaceList.pop_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
return self.entries[--self.size];
}
@@ -138,7 +138,7 @@ fn void InterfaceList.clear(&self)
*>
fn Type? InterfaceList.pop_first_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
defer self.remove_at(0);
return self.entries[0];
}
@@ -152,7 +152,7 @@ fn Type? InterfaceList.pop_first_retained(&self)
*>
fn Type? InterfaceList.copy_pop_first(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
defer self.free_element(self.entries[self.size]);
defer self.remove_at(0);
return (Type)allocator::clone_any(allocator, self.entries[0]);
@@ -276,7 +276,7 @@ fn void InterfaceList.remove_first(&self)
*>
fn Type? InterfaceList.first(&self) @inline
{
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
return self.size ? self.entries[0] : NO_MORE_ELEMENT~;
}
<*
@@ -287,7 +287,7 @@ fn Type? InterfaceList.first(&self) @inline
*>
fn Type? InterfaceList.last(&self) @inline
{
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT~;
}
<*
@@ -381,7 +381,7 @@ fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic
*>
fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
{
return self._remove_if(filter, false);
return interfacelist_remove_if(self, filter, false);
}
<*
@@ -392,7 +392,7 @@ fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
*>
fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
{
return self._remove_if(selection, true);
return interfacelist_remove_if(self, selection, true);
}
<*
@@ -404,7 +404,7 @@ fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
*>
fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context)
{
return self._remove_using_test(filter, false, context);
return interfacelist_remove_using_test(self, filter, false, context);
}
<*
@@ -416,7 +416,7 @@ fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context
*>
fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context)
{
return self._remove_using_test(selection, true, context);
return interfacelist_remove_using_test(self, selection, true, context);
}
<*
@@ -455,7 +455,7 @@ macro void InterfaceList.set(&self, usz index, value)
// -- private
fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
fn void interfacelist_ensure_capacity(InterfaceList* self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
@@ -466,18 +466,18 @@ fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
self.reserve(new_capacity);
}
fn void InterfaceList._append(&self, Type element) @local
fn void interfacelist_append(InterfaceList* self, Type element) @local
{
self.ensure_capacity();
interfacelist_ensure_capacity(self);
self.entries[self.size++] = element;
}
<*
@require index < self.size
*>
fn void InterfaceList._insert_at(&self, usz index, Type value) @local
fn void interfacelist_insert_at(InterfaceList* self, usz index, Type value) @local
{
self.ensure_capacity();
interfacelist_ensure_capacity(self);
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
@@ -486,7 +486,7 @@ fn void InterfaceList._insert_at(&self, usz index, Type value) @local
self.entries[index] = value;
}
macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local
macro usz interfacelist_remove_using_test(InterfaceList* self, InterfaceTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
@@ -512,7 +512,7 @@ macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $in
return size - self.size;
}
macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local
macro usz interfacelist_remove_if(InterfaceList* self, InterfacePredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)

View File

@@ -1,4 +1,4 @@
module std::collections::blockingqueue { Value };
module std::collections::blockingqueue <Value>;
import std::thread, std::time;
@@ -70,12 +70,12 @@ fn void LinkedBlockingQueue.free(&self)
}
};
(void)self.lock.destroy();
(void)self.not_empty.destroy();
(void)self.not_full.destroy();
self.lock.destroy();
self.not_empty.destroy();
self.not_full.destroy();
}
fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
fn void linkedblockingqueue_link_entry(LinkedBlockingQueue* self, QueueEntry* entry) @private
{
entry.next = null;
entry.prev = self.tail;
@@ -95,7 +95,7 @@ fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
}
fn QueueEntry* LinkedBlockingQueue.unlink_head(&self) @private
fn QueueEntry* linkedblockingqueue_unlink_head(LinkedBlockingQueue* self) @private
{
if (self.head == null) return null;
@@ -126,7 +126,7 @@ fn void LinkedBlockingQueue.push(&self, Value value)
{
while (self.capacity > 0 && self.count >= self.capacity)
{
self.not_full.wait(&self.lock)!!;
self.not_full.wait(&self.lock);
}
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
@@ -134,10 +134,10 @@ fn void LinkedBlockingQueue.push(&self, Value value)
.next = null,
.prev = null
});
self.link_entry(entry);
linkedblockingqueue_link_entry(self, entry);
// Signal that queue is no longer empty
self.not_empty.signal()!!;
self.not_empty.signal();
};
}
@@ -153,15 +153,15 @@ fn Value LinkedBlockingQueue.poll(&self)
{
while (self.count == 0)
{
self.not_empty.wait(&self.lock)!!;
self.not_empty.wait(&self.lock);
}
QueueEntry* entry = self.unlink_head();
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
Value value = entry.value;
allocator::free(self.allocator, entry);
if (self.capacity > 0)
{
self.not_full.signal()!!;
self.not_full.signal();
}
return value;
};
@@ -178,15 +178,15 @@ fn Value? LinkedBlockingQueue.pop(&self)
{
self.lock.@in_lock()
{
if (self.count == 0) return NO_MORE_ELEMENT?;
if (self.count == 0) return NO_MORE_ELEMENT~;
QueueEntry* entry = self.unlink_head();
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
Value value = entry.value;
allocator::free(self.allocator, entry);
if (self.capacity > 0)
{
self.not_full.signal()!!;
self.not_full.signal();
}
return value;
};
@@ -214,17 +214,17 @@ fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
if (end <= time::now()) break;
if (catch self.not_empty.wait_until(&self.lock, end)) break;
}
if (!self.count) return NO_MORE_ELEMENT?;
if (!self.count) return NO_MORE_ELEMENT~;
}
QueueEntry* entry = self.unlink_head();
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
Value value = entry.value;
allocator::free(self.allocator, entry);
// Must signal not_full after removing an item
if (self.capacity > 0)
{
self.not_full.signal()!!;
self.not_full.signal();
}
return value;
};
@@ -266,15 +266,15 @@ fn void? LinkedBlockingQueue.try_push(&self, Value value)
{
self.lock.@in_lock()
{
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED~;
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
.value = value,
.next = null,
.prev = null
});
self.link_entry(entry);
self.not_empty.signal()!!;
linkedblockingqueue_link_entry(self, entry);
self.not_empty.signal();
};
}
@@ -299,7 +299,7 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
if (end <= time::now()) break;
if (catch self.not_empty.wait_until(&self.lock, end)) break;
}
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED?;
if (self.capacity > 0 && self.count >= self.capacity) return CAPACITY_EXCEEDED~;
}
QueueEntry* entry = allocator::new(self.allocator, QueueEntry, {
@@ -307,20 +307,20 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
.next = null,
.prev = null
});
self.link_entry(entry);
self.not_empty.signal()!!;
linkedblockingqueue_link_entry(self, entry);
self.not_empty.signal();
};
}
<*
@require self.is_initialized() : "Queue must be initialized"
@return "The head value or NO_MORE_ELEMENT? if queue is empty"
@return "The head value or NO_MORE_ELEMENT~ if queue is empty"
*>
fn Value? LinkedBlockingQueue.peek(&self)
{
self.lock.@in_lock()
{
return (self.head != null) ? self.head.value : NO_MORE_ELEMENT?;
return (self.head != null) ? self.head.value : NO_MORE_ELEMENT~;
};
}

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;
@@ -150,7 +150,7 @@ fn bool LinkedHashMap.is_initialized(&map)
fn LinkedHashMap* LinkedHashMap.init_from_map(&self, Allocator allocator, LinkedHashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
linkedhashmap_put_all_for_create(self, other_map);
return self;
}
@@ -172,24 +172,24 @@ fn usz LinkedHashMap.len(&map) @inline => map.count;
fn Value*? LinkedHashMap.get_ref(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
if (!map.count) return NOT_FOUND~;
uint hash = rehash(key.hash());
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn LinkedEntry*? LinkedHashMap.get_entry(&map, Key key)
{
if (!map.count) return NOT_FOUND?;
if (!map.count) return NOT_FOUND~;
uint hash = rehash(key.hash());
for (LinkedEntry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -242,13 +242,13 @@ fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
return true;
}
}
map.add_entry(hash, key, value, index);
linkedhashmap_add_entry(map, hash, key, value, index);
return false;
}
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
if (!linkedhashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
}
fn void LinkedHashMap.clear(&map)
@@ -259,7 +259,7 @@ fn void LinkedHashMap.clear(&map)
while (entry)
{
LinkedEntry* next = entry.after;
map.free_entry(entry);
linkedhashmap_free_entry(map, entry);
entry = next;
}
@@ -277,7 +277,7 @@ fn void LinkedHashMap.free(&map)
{
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
linkedhashmap_free_internal(map, map.table.ptr);
map.table = {};
}
@@ -375,17 +375,17 @@ fn bool LinkedHashMapIterator.next(&self)
fn LinkedEntry*? LinkedHashMapIterator.get(&self)
{
return self.current ? self.current : NOT_FOUND?;
return self.current ? self.current : NOT_FOUND~;
}
fn Value*? LinkedHashMapValueIterator.get(&self)
{
return self.current ? &self.current.value : NOT_FOUND?;
return self.current ? &self.current.value : NOT_FOUND~;
}
fn Key*? LinkedHashMapKeyIterator.get(&self)
{
return self.current ? &self.current.key : NOT_FOUND?;
return self.current ? &self.current.key : NOT_FOUND~;
}
fn bool LinkedHashMapIterator.has_next(&self)
@@ -396,7 +396,7 @@ fn bool LinkedHashMapIterator.has_next(&self)
// --- private methods
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
fn void linkedhashmap_add_entry(LinkedHashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
@@ -428,11 +428,11 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
linkedhashmap_resize(map, map.table.len * 2);
}
}
fn void LinkedHashMap.resize(&map, uint new_capacity) @private
fn void linkedhashmap_resize(LinkedHashMap* map, uint new_capacity) @private
{
LinkedEntry*[] old_table = map.table;
uint old_capacity = old_table.len;
@@ -502,7 +502,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
}
}
map.free_internal(old_table.ptr);
linkedhashmap_free_internal(map, old_table.ptr);
}
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
@@ -517,7 +517,7 @@ fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
fn void linkedhashmap_transfer(LinkedHashMap* map, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = map.table;
uint new_capacity = new_table.len;
@@ -536,7 +536,7 @@ fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
}
}
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
fn void linkedhashmap_put_all_for_create(LinkedHashMap* map, LinkedHashMap* other_map) @private
{
if (!other_map.count) return;
other_map.@each(; Key key, Value value) {
@@ -544,7 +544,7 @@ fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @privat
};
}
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
fn void linkedhashmap_put_for_create(LinkedHashMap* map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -556,15 +556,15 @@ fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
return;
}
}
map.create_entry(hash, key, value, i);
linkedhashmap_create_entry(map, hash, key, value, i);
}
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
fn void linkedhashmap_free_internal(LinkedHashMap* map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
fn bool linkedhashmap_remove_entry_for_key(LinkedHashMap* map, Key key) @private
{
if (!map.count) return false;
@@ -605,7 +605,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
}
map.count--;
map.free_entry(e);
linkedhashmap_free_entry(map, e);
return true;
}
prev = e;
@@ -614,23 +614,23 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
return false;
}
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
fn void linkedhashmap_create_entry(LinkedHashMap* map, uint hash, Key key, Value value, int bucket_index) @private
{
LinkedEntry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void LinkedHashMap.free_entry(&self, LinkedEntry *entry) @local
fn void linkedhashmap_free_entry(LinkedHashMap* self, LinkedEntry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
linkedhashmap_free_internal(self, entry);
}

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"
@@ -143,7 +143,7 @@ fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, Linked
LinkedEntry* entry = other_set.head;
while (entry) // Save insertion order
{
self.put_for_create(entry.value);
linkedhashset_put_for_create(self, entry.value);
entry = entry.after;
}
return self;
@@ -223,7 +223,7 @@ fn bool LinkedHashSet.add(&set, Value value)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
linkedhashset_add_entry(set, hash, value, index);
return true;
}
@@ -266,7 +266,7 @@ fn bool LinkedHashSet.contains(&set, Value value)
*>
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND?;
if (!linkedhashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
}
fn usz LinkedHashSet.remove_all(&set, Value[] values)
@@ -274,7 +274,7 @@ fn usz LinkedHashSet.remove_all(&set, Value[] values)
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
if (linkedhashset_remove_entry_for_value(set, v)) total++;
}
return total;
}
@@ -287,7 +287,7 @@ fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
if (linkedhashset_remove_entry_for_value(set, val)) total++;
};
return total;
}
@@ -299,7 +299,7 @@ fn void LinkedHashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
linkedhashset_free_internal(set, set.table.ptr);
set.table = {};
}
@@ -316,7 +316,7 @@ fn void LinkedHashSet.clear(&set)
while (entry)
{
LinkedEntry* next = entry.after;
set.free_entry(entry);
linkedhashset_free_entry(set, entry);
entry = next;
}
@@ -334,7 +334,7 @@ fn void LinkedHashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
linkedhashset_resize(set, math::next_power_of_2(capacity));
}
}
@@ -451,7 +451,7 @@ fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
// --- private methods
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
fn void linkedhashset_add_entry(LinkedHashSet* set, uint hash, Value value, uint bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
@@ -478,11 +478,11 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
linkedhashset_resize(set, set.table.len * 2);
}
}
fn void LinkedHashSet.resize(&set, usz new_capacity) @private
fn void linkedhashset_resize(LinkedHashSet* set, usz new_capacity) @private
{
LinkedEntry*[] old_table = set.table;
usz old_capacity = old_table.len;
@@ -552,7 +552,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
}
}
set.free_internal(old_table.ptr);
linkedhashset_free_internal(set, old_table.ptr);
}
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
@@ -567,7 +567,7 @@ fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
fn void linked_hashset_transfer(LinkedHashSet* set, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = set.table;
uint new_capacity = new_table.len;
@@ -586,7 +586,7 @@ fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
}
}
fn void LinkedHashSet.put_for_create(&set, Value value) @private
fn void linkedhashset_put_for_create(LinkedHashSet* set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
@@ -598,15 +598,15 @@ fn void LinkedHashSet.put_for_create(&set, Value value) @private
return;
}
}
set.create_entry(hash, value, i);
linkedhashset_create_entry(set, hash, value, i);
}
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
fn void linkedhashset_free_internal(LinkedHashSet* set, void* ptr) @inline @private
{
allocator::free(set.allocator, ptr);
}
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
fn void linkedhashset_create_entry(LinkedHashSet* set, uint hash, Value value, int bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
@@ -633,7 +633,7 @@ fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_inde
set.count++;
}
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
fn bool linkedhashset_remove_entry_for_value(LinkedHashSet* set, Value value) @private
{
if (!set.count) return false;
@@ -674,7 +674,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
}
set.count--;
set.free_entry(e);
linkedhashset_free_entry(set, e);
return true;
}
prev = e;
@@ -683,7 +683,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
return false;
}
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
fn void linkedhashset_free_entry(LinkedHashSet* set, LinkedEntry *entry) @private
{
allocator::free(set.allocator, entry);
}
@@ -713,7 +713,7 @@ fn bool LinkedHashSetIterator.next(&self)
fn Value*? LinkedHashSetIterator.get(&self)
{
return self.current ? &self.current.value : NOT_FOUND?;
return self.current ? &self.current.value : NOT_FOUND~;
}
fn bool LinkedHashSetIterator.has_next(&self)

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~;
}
@@ -217,7 +217,7 @@ fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
*>
fn void LinkedList.remove_at(&self, usz index)
{
self.unlink(self.node_at_index(index));
linked_list_unlink(self, self.node_at_index(index));
}
<*
@@ -232,47 +232,47 @@ fn void LinkedList.insert_at(&self, usz index, Type element)
case self.size:
self.push(element);
default:
self.link_before(self.node_at_index(index), element);
linked_list_link_before(self, self.node_at_index(index), element);
}
}
<*
@require succ != null
*>
fn void LinkedList.link_before(&self, Node *succ, Type value) @private
fn void linked_list_link_before(LinkedList* l, Node *succ, Type value) @private
{
Node* pred = succ.prev;
Node* new_node = self.alloc_node();
Node* new_node = l.alloc_node();
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
{
self._first = new_node;
l._first = new_node;
}
else
{
pred.next = new_node;
}
self.size++;
l.size++;
}
<*
@require self._first != null
@require l._first != null
*>
fn void LinkedList.unlink_first(&self) @private
fn void linked_list_unlink_first(LinkedList* l) @private
{
Node* f = self._first;
Node* f = l._first;
Node* next = f.next;
self.free_node(f);
self._first = next;
l.free_node(f);
l._first = next;
if (!next)
{
self._last = null;
l._last = null;
}
else
{
next.prev = null;
}
self.size--;
l.size--;
}
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
@@ -285,7 +285,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
case equals(node.value, t):
Node* next = node.next;
self.unlink(node);
linked_list_unlink(self, node);
node = next;
default:
node = node.next;
@@ -296,8 +296,8 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
fn Type? LinkedList.pop(&self)
{
if (!self._last) return NO_MORE_ELEMENT?;
defer self.unlink_last();
if (!self._last) return NO_MORE_ELEMENT~;
defer linked_list_unlink_last(self);
return self._last.value;
}
@@ -308,21 +308,21 @@ fn bool LinkedList.is_empty(&self)
fn Type? LinkedList.pop_front(&self)
{
if (!self._first) return NO_MORE_ELEMENT?;
defer self.unlink_first();
if (!self._first) return NO_MORE_ELEMENT~;
defer linked_list_unlink_first(self);
return self._first.value;
}
fn void? LinkedList.remove_last(&self) @maydiscard
{
if (!self._first) return NO_MORE_ELEMENT?;
self.unlink_last();
if (!self._first) return NO_MORE_ELEMENT~;
linked_list_unlink_last(self);
}
fn void? LinkedList.remove_first(&self) @maydiscard
{
if (!self._first) return NO_MORE_ELEMENT?;
self.unlink_first();
if (!self._first) return NO_MORE_ELEMENT~;
linked_list_unlink_first(self);
}
@@ -332,7 +332,7 @@ fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
if (node.value == t)
{
self.unlink(node);
linked_list_unlink(self, node);
return true;
}
}
@@ -345,7 +345,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
if (node.value == t)
{
self.unlink(node);
linked_list_unlink(self, node);
return true;
}
}
@@ -354,7 +354,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
<*
@require self._last != null
*>
fn void LinkedList.unlink_last(&self) @inline @private
fn void linked_list_unlink_last(LinkedList* self) @inline @private
{
Node* l = self._last;
Node* prev = l.prev;
@@ -374,7 +374,7 @@ fn void LinkedList.unlink_last(&self) @inline @private
<*
@require x != null
*>
fn void LinkedList.unlink(&self, Node* x) @private
fn void linked_list_unlink(LinkedList* self, Node* x) @private
{
Node* next = x.next;
Node* prev = x.prev;

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);
@@ -82,7 +82,7 @@ fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types)
self.allocator = allocator;
self.capacity = types.len;
self.entries = types.ptr;
self.set_size(types.len);
list_set_size(self, types.len);
}
fn bool List.is_initialized(&self) @inline => self.allocator != null && self.allocator != (Allocator)&dummy;
@@ -110,24 +110,24 @@ fn usz? List.to_format(&self, Formatter* formatter) @dynamic
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
self.entries[self.set_size(self.size + 1)] = element;
self.entries[list_set_size(self, self.size + 1)] = element;
}
fn Type? List.pop(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.set_size(self.size - 1);
if (!self.size) return NO_MORE_ELEMENT~;
defer list_set_size(self, self.size - 1);
return self.entries[self.size - 1];
}
fn void List.clear(&self)
{
self.set_size(0);
list_set_size(self, 0);
}
fn Type? List.pop_first(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
defer self.remove_at(0);
return self.entries[0];
}
@@ -138,7 +138,7 @@ fn Type? List.pop_first(&self)
fn void List.remove_at(&self, usz index)
{
usz new_size = self.size - 1;
defer self.set_size(new_size);
defer list_set_size(self, new_size);
if (!new_size || index == new_size) return;
self.entries[index .. new_size - 1] = self.entries[index + 1 .. new_size];
}
@@ -147,7 +147,7 @@ fn void List.add_all(&self, List* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
usz index = self.set_size(self.size + other_list.size);
usz index = list_set_size(self, self.size + other_list.size);
foreach (&value : other_list)
{
self.entries[index++] = *value;
@@ -203,7 +203,7 @@ fn void List.add_array(&self, Type[] array) @deprecated("Use push_all")
{
if (!array.len) return;
self.reserve(array.len);
usz index = self.set_size(self.size + array.len);
usz index = list_set_size(self, self.size + array.len);
self.entries[index : array.len] = array[..];
}
@@ -217,7 +217,7 @@ fn void List.push_all(&self, Type[] array)
{
if (!array.len) return;
self.reserve(array.len);
usz index = self.set_size(self.size + array.len);
usz index = list_set_size(self, self.size + array.len);
self.entries[index : array.len] = array[..];
}
@@ -232,7 +232,7 @@ fn void List.push_front(&self, Type type) @inline
fn void List.insert_at(&self, usz index, Type type)
{
self.reserve(1);
self.set_size(self.size + 1);
list_set_size(self, self.size + 1);
for (isz i = self.size - 1; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
@@ -250,25 +250,25 @@ fn void List.set_at(&self, usz index, Type type)
fn void? List.remove_last(&self) @maydiscard
{
if (!self.size) return NO_MORE_ELEMENT?;
self.set_size(self.size - 1);
if (!self.size) return NO_MORE_ELEMENT~;
list_set_size(self, self.size - 1);
}
fn void? List.remove_first(&self) @maydiscard
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
self.remove_at(0);
}
fn Type? List.first(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
return self.entries[0];
}
fn Type? List.last(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT~;
return self.entries[self.size - 1];
}
@@ -299,7 +299,7 @@ fn void List.free(&self)
{
if (!self.allocator || self.allocator.ptr == &dummy || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
list_pre_free(self); // Remove sanitizer annotation
$if type_is_overaligned():
allocator::free_aligned(self.allocator, self.entries);
@@ -358,7 +358,7 @@ fn usz List.retain_using_test(&self, ElementTest filter, any context)
return list_common::list_remove_using_test(self, filter, true, context);
}
fn void List.ensure_capacity(&self, usz min_capacity) @local
fn void list_ensure_capacity(List* self, usz min_capacity) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
@@ -374,7 +374,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
break;
}
self.pre_free(); // Remove sanitizer annotation
list_pre_free(self); // Remove sanitizer annotation
min_capacity = math::next_power_of_2(min_capacity);
$if type_is_overaligned():
@@ -384,7 +384,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
$endif;
self.capacity = min_capacity;
self.post_alloc(); // Add sanitizer annotation
list_post_alloc(self); // Add sanitizer annotation
}
<*
@@ -419,7 +419,7 @@ fn void List.reserve(&self, usz added)
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.ensure_capacity(new_capacity);
list_ensure_capacity(self, new_capacity);
}
fn void List._update_size_change(&self,usz old_size, usz new_size)
@@ -436,7 +436,7 @@ fn void List._update_size_change(&self,usz old_size, usz new_size)
<*
@require new_size == 0 || self.capacity != 0
*>
fn usz List.set_size(&self, usz new_size) @inline @private
fn usz list_set_size(List* self, usz new_size) @inline @private
{
usz old_size = self.size;
self._update_size_change(old_size, new_size);
@@ -444,7 +444,7 @@ fn usz List.set_size(&self, usz new_size) @inline @private
return old_size;
}
macro void List.pre_free(&self) @private
macro void list_pre_free(List* self) @private
{
if (!self.capacity) return;
self._update_size_change(self.size, self.capacity);
@@ -453,7 +453,7 @@ macro void List.pre_free(&self) @private
<*
@require self.capacity > 0
*>
macro void List.post_alloc(&self) @private
macro void list_post_alloc(List* self) @private
{
self._update_size_change(self.capacity, self.size);
}
@@ -461,22 +461,22 @@ macro void List.post_alloc(&self) @private
// Functions for equatable types
fn usz? List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
fn usz? List.index_of(&self, Type type) @if (ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, type)) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn usz? List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
fn usz? List.rindex_of(&self, Type type) @if (ELEMENT_IS_EQUATABLE)
{
foreach_r (i, v : self)
{
if (equals(v, type)) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE)

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

@@ -151,7 +151,7 @@ fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array();
<*
@require self.is_keyable()
*>
fn void Object.init_map_if_needed(&self) @private
fn void object_init_map_if_needed(Object* self) @private
{
if (self.is_empty())
{
@@ -163,7 +163,7 @@ fn void Object.init_map_if_needed(&self) @private
<*
@require self.is_indexable()
*>
fn void Object.init_array_if_needed(&self) @private
fn void object_init_array_if_needed(Object* self) @private
{
if (self.is_empty())
{
@@ -175,9 +175,9 @@ fn void Object.init_array_if_needed(&self) @private
<*
@require self.is_keyable()
*>
fn void Object.set_object(&self, String key, Object* new_object) @private
fn void object_set_object(Object* self, String key, Object* new_object) @private
{
self.init_map_if_needed();
object_init_map_if_needed(self);
Object*? val = self.map.get_entry(key).value;
defer (void)val.free();
self.map.set(key, new_object);
@@ -214,7 +214,7 @@ macro Object* Object.object_from_value(&self, value) @private
macro Object* Object.set(&self, String key, value)
{
Object* val = self.object_from_value(value);
self.set_object(key, val);
object_set_object(self, key, val);
return val;
}
@@ -242,8 +242,7 @@ macro Object* Object.push(&self, value)
<*
@require self.is_keyable()
*>
fn Object*? Object.get(&self, String key) => self.is_empty() ? NOT_FOUND? : self.map.get(key);
fn Object*? Object.get(&self, String key) => self.is_empty() ? NOT_FOUND~ : self.map.get(key);
fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key);
@@ -268,7 +267,7 @@ fn usz Object.get_len(&self)
*>
fn void Object.push_object(&self, Object* to_append)
{
self.init_array_if_needed();
object_init_array_if_needed(self);
self.array.push(to_append);
}
@@ -277,7 +276,7 @@ fn void Object.push_object(&self, Object* to_append)
*>
fn void Object.set_object_at(&self, usz index, Object* to_set)
{
self.init_array_if_needed();
object_init_array_if_needed(self);
while (self.array.len() < index)
{
self.array.push(&NULL_OBJECT);
@@ -308,7 +307,7 @@ macro get_integer_value(Object* value, $Type)
return ($Type)value.s.to_uint128();
$endif
}
if (!value.is_int()) return string::MALFORMED_INTEGER?;
if (!value.is_int()) return string::MALFORMED_INTEGER~;
return ($Type)value.i;
}
@@ -361,7 +360,7 @@ fn uint128? Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint1
fn String? Object.get_string(&self, String key)
{
Object* value = self.get(key)!;
if (!value.is_string()) return TYPE_MISMATCH?;
if (!value.is_string()) return TYPE_MISMATCH~;
return value.s;
}
@@ -371,7 +370,7 @@ fn String? Object.get_string(&self, String key)
fn String? Object.get_string_at(&self, usz index)
{
Object* value = self.get_at(index);
if (!value.is_string()) return TYPE_MISMATCH?;
if (!value.is_string()) return TYPE_MISMATCH~;
return value.s;
}
@@ -381,7 +380,7 @@ fn String? Object.get_string_at(&self, usz index)
macro String? Object.get_enum(&self, $EnumType, String key)
{
Object value = self.get(key)!;
if ($EnumType.typeid != value.type) return TYPE_MISMATCH?;
if ($EnumType.typeid != value.type) return TYPE_MISMATCH~;
return ($EnumType)value.i;
}
@@ -391,7 +390,7 @@ macro String? Object.get_enum(&self, $EnumType, String key)
macro String? Object.get_enum_at(&self, $EnumType, usz index)
{
Object value = self.get_at(index);
if ($EnumType.typeid != value.type) return TYPE_MISMATCH?;
if ($EnumType.typeid != value.type) return TYPE_MISMATCH~;
return ($EnumType)value.i;
}
@@ -401,7 +400,7 @@ macro String? Object.get_enum_at(&self, $EnumType, usz index)
fn bool? Object.get_bool(&self, String key)
{
Object* value = self.get(key)!;
if (!value.is_bool()) return TYPE_MISMATCH?;
if (!value.is_bool()) return TYPE_MISMATCH~;
return value.b;
}
@@ -412,7 +411,7 @@ fn bool? Object.get_bool(&self, String key)
fn bool? Object.get_bool_at(&self, usz index)
{
Object* value = self.get_at(index);
if (!value.is_bool()) return TYPE_MISMATCH?;
if (!value.is_bool()) return TYPE_MISMATCH~;
return value.b;
}
@@ -431,7 +430,7 @@ fn double? Object.get_float(&self, String key)
case FLOAT:
return value.f;
default:
return TYPE_MISMATCH?;
return TYPE_MISMATCH~;
}
}
@@ -450,7 +449,7 @@ fn double? Object.get_float_at(&self, usz index)
case FLOAT:
return value.f;
default:
return TYPE_MISMATCH?;
return TYPE_MISMATCH~;
}
}

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

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ const uint PIXELS_MAX = 400000000;
Purely informative. It will be saved to the file header,
but does not affect how chunks are en-/decoded.
*>
enum QOIColorspace : const char
constdef QOIColorspace : char
{
<* sRGB with linear alpha *>
SRGB = 0,
@@ -21,7 +21,7 @@ enum QOIColorspace : const char
AUTO can be used when decoding to automatically determine
the channels from the file's header.
*>
enum QOIChannels : const inline char
constdef QOIChannels : inline char
{
AUTO = 0,
RGB = 3,
@@ -100,7 +100,7 @@ fn usz? write(String filename, char[] input, QOIDesc* desc) => @pool()
fn char[]? read(Allocator allocator, String filename, QOIDesc* desc, QOIChannels channels = AUTO) => @pool()
{
// read file
char[] data = file::load_temp(filename) ?? FILE_OPEN_FAILED?!;
char[] data = file::load_temp(filename) ?? FILE_OPEN_FAILED~!;
// pass data to decode function
return decode(allocator, data, desc, channels);
}
@@ -128,14 +128,14 @@ import std::bits;
fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
{
// check info in desc
if (desc.width == 0 || desc.height == 0) return INVALID_PARAMETERS?;
if (desc.channels == AUTO) return INVALID_PARAMETERS?;
if (desc.width == 0 || desc.height == 0) return INVALID_PARAMETERS~;
if (desc.channels == AUTO) return INVALID_PARAMETERS~;
uint pixels = desc.width * desc.height;
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS~;
// check input data size
uint image_size = pixels * desc.channels;
if (image_size != input.len) return INVALID_DATA?;
if (image_size != input.len) return INVALID_DATA~;
// allocate memory for encoded data (output)
// header + chunk tag and RGB(A) data for each pixel + end of stream
@@ -283,27 +283,27 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels channels = AUTO) @nodiscard
{
// check input data
if (data.len < Header.sizeof + END_OF_STREAM.len) return INVALID_DATA?;
if (data.len < Header.sizeof + END_OF_STREAM.len) return INVALID_DATA~;
// get header
Header* header = (Header*)data.ptr;
// check magic bytes (FourCC)
if (bswap(header.be_magic) != 'qoif') return INVALID_DATA?;
if (bswap(header.be_magic) != 'qoif') return INVALID_DATA~;
// copy header data to desc
desc.width = bswap(header.be_width);
desc.height = bswap(header.be_height);
desc.channels = header.channels;
uint width = desc.width = bswap(header.be_width);
uint height = desc.height = bswap(header.be_height);
QOIChannels desc_channels = desc.channels = header.channels;
desc.colorspace = header.colorspace;
if (desc.channels == AUTO) return INVALID_DATA?; // Channels must be specified in the header
if (desc_channels == AUTO) return INVALID_DATA~; // Channels must be specified in the header
// check width and height
if (desc.width == 0 || desc.height == 0) return INVALID_DATA?;
if (width == 0 || height == 0) return INVALID_DATA~;
// check pixel count
ulong pixels = (ulong)desc.width * (ulong)desc.height;
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
ulong pixels = (ulong)width * (ulong)height;
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS~;
uint pos = Header.sizeof; // Current position in data
uint loc; // Current position in image (top-left corner)
@@ -313,7 +313,7 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
Pixel[64] palette; // Zero-initialized by default
Pixel p = { 0, 0, 0, 255 };
if (channels == AUTO) channels = desc.channels;
if (channels == AUTO) channels = desc_channels;
// allocate memory for image data
usz image_size = (usz)pixels * channels;

1215
lib/std/compression/zip.c3 Normal file

File diff suppressed because it is too large Load Diff

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

@@ -56,7 +56,7 @@ fn BackedArenaAllocator*? new_backed_allocator(usz size, Allocator allocator)
fn void BackedArenaAllocator.destroy(&self)
{
self.reset(0);
if (self.last_page) (void)self._free_page(self.last_page);
if (self.last_page) (void)_free_page(self, self.last_page);
allocator::free(self.backing_allocator, self);
}
@@ -79,7 +79,7 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
self.used = last_page.mark;
ExtraPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
_free_page(self, to_free)!!;
}
self.last_page = last_page;
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
@@ -98,13 +98,13 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
self.used = mark;
}
fn void? BackedArenaAllocator._free_page(&self, ExtraPage* page) @inline @local
fn void? _free_page(BackedArenaAllocator* self, ExtraPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*? BackedArenaAllocator._realloc_page(&self, ExtraPage* page, usz size, usz alignment) @inline @local
fn void*? _realloc_page(BackedArenaAllocator* self, ExtraPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -133,7 +133,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
assert(self.last_page, "Realloc of unrelated pointer");
// First grab the page
ExtraPage *page = pointer - ExtraPage.sizeof;
return self._realloc_page(page, size, alignment);
return _realloc_page(self, page, size, alignment);
}
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;

View File

@@ -137,36 +137,7 @@ fn void DynamicArenaAllocator.reset(&self)
self.page = page;
}
<*
@require math::is_power_of_2(alignment)
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
assert(page_size > size + DynamicArenaChunk.sizeof);
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*? page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
if (catch err = page)
{
allocator::free(self.backing_allocator, mem);
return err?;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
page.prev_arena = self.page;
page.total = page_size;
page.used = mem_start + size - page.memory;
self.page = page;
page.current_stack_ptr = mem_start;
return mem_start;
}
<*
@require size > 0 : `acquire expects size > 0`
@@ -189,7 +160,7 @@ fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
}
if (!page)
{
ptr = self._alloc_new(size, alignment)!;
ptr = _alloc_new(self, size, alignment)!;
break SET_DONE;
}
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
@@ -208,7 +179,7 @@ fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
break ALLOCATE_NEW;
}
}
ptr = self._alloc_new(size, alignment)!;
ptr = _alloc_new(self, size, alignment)!;
break SET_DONE;
}
page.used = new_used;
@@ -220,3 +191,34 @@ fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
return ptr;
}
<*
@require math::is_power_of_2(alignment)
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? _alloc_new(DynamicArenaAllocator* self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
assert(page_size > size + DynamicArenaChunk.sizeof);
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*? page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
if (catch err = page)
{
allocator::free(self.backing_allocator, mem);
return err~;
}
page.memory = mem;
void* mem_start = mem::aligned_pointer(mem + DynamicArenaChunk.sizeof, alignment);
assert(mem_start + size < mem + page_size);
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
chunk.size = size;
page.prev_arena = self.page;
page.total = page_size;
page.used = mem_start + size - page.memory;
self.page = page;
page.current_stack_ptr = mem_start;
return mem_start;
}

View File

@@ -32,58 +32,58 @@ fn void*? SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type,
{
if (init_type == ZERO)
{
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_calloc, size, alignment) : simple_alloc_calloc(self, size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_alloc, size, alignment) : simple_alloc_alloc(self, size);
}
fn void*? SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
: self._realloc(old_pointer, size);
? @aligned_realloc_fn(self, simple_alloc_calloc, simple_alloc_free, old_pointer, size, alignment)
: simple_alloc_realloc(self, old_pointer, size);
}
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (aligned)
{
@aligned_free(self._free, old_pointer)!!;
@aligned_free_fn(self, simple_alloc_free, old_pointer)!!;
}
else
{
self._free(old_pointer);
simple_alloc_free(self, old_pointer);
}
}
<*
@require old_pointer && bytes > 0
*>
fn void*? SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
fn void*? simple_alloc_realloc(SimpleHeapAllocator* self, void* old_pointer, usz bytes) @local
{
// Find the block header.
Header* block = (Header*)old_pointer - 1;
if (block.size >= bytes) return old_pointer;
void* new = self._alloc(bytes)!;
void* new = simple_alloc_alloc(self, bytes)!;
usz max_to_copy = math::min(block.size, bytes);
mem::copy(new, old_pointer, max_to_copy);
self._free(old_pointer);
simple_alloc_free(self, old_pointer);
return new;
}
fn void*? SimpleHeapAllocator._calloc(&self, usz bytes) @local
fn void*? simple_alloc_calloc(SimpleHeapAllocator* self, usz bytes) @local
{
void* data = self._alloc(bytes)!;
void* data = simple_alloc_alloc(self, bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
fn void*? simple_alloc_alloc(SimpleHeapAllocator* self, usz bytes) @local
{
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!self.free_list)
{
self.add_block(aligned_bytes)!;
simple_alloc_add_block(self, aligned_bytes)!;
}
Header* current = self.free_list;
@@ -123,22 +123,22 @@ fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
current = current.next;
}
}
self.add_block(aligned_bytes)!;
return self._alloc(aligned_bytes);
simple_alloc_add_block(self, aligned_bytes)!;
return simple_alloc_alloc(self, aligned_bytes);
}
fn void? SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
fn void? simple_alloc_add_block(SimpleHeapAllocator* self, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!;
Header* new_block = (Header*)result.ptr;
new_block.size = result.len - Header.sizeof;
new_block.next = null;
self._free(new_block + 1);
simple_alloc_free(self, new_block + 1);
}
fn void SimpleHeapAllocator._free(&self, void* ptr) @local
fn void simple_alloc_free(SimpleHeapAllocator* self, void* ptr) @local
{
// Empty ptr -> do nothing.
if (!ptr) return;

View File

@@ -15,7 +15,6 @@ module std::core::mem::allocator @if(env::POSIX);
import std::os;
import libc;
fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
@@ -23,22 +22,22 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY?;
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY~;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY?;
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY~;
}
else
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY?;
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY~;
}
else
{
if (!(data = libc::malloc(bytes))) return mem::OUT_OF_MEMORY?;
if (!(data = libc::malloc(bytes))) return mem::OUT_OF_MEMORY~;
}
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
@@ -49,25 +48,32 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
void* new_ptr;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return mem::OUT_OF_MEMORY?;
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
$switch:
$case env::DARWIN:
usz old_usable_size = darwin::malloc_size(old_ptr);
$case env::LINUX:
usz old_usable_size = linux::malloc_usable_size(old_ptr);
$default:
usz old_usable_size = new_bytes;
$endswitch
// Try realloc, even though it might be unaligned.
void* new_ptr = libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~!;
// If it's aligned then we're done!
uptr ptr_val = (uptr)new_ptr;
if (ptr_val & (alignment - 1) == 0) return new_ptr;
// We failed, so we need to use memalign
// We will free new_ptr before we exit.
defer libc::free(new_ptr);
// Create a pointer which is sure to be aligned.
void* aligned_ptr;
if (posix::posix_memalign(&aligned_ptr, alignment, new_bytes))
{
return mem::OUT_OF_MEMORY~;
}
// Now it is safe to copy the full range of data.
mem::copy(aligned_ptr, new_ptr, new_bytes, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return aligned_ptr;
usz copy_size = new_bytes < old_usable_size ? new_bytes : old_usable_size;
mem::copy(new_ptr, old_ptr, copy_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
libc::free(old_ptr);
return new_ptr;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
libc::free(old_ptr);
@@ -83,12 +89,12 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
{
if (alignment > 0)
{
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: mem::OUT_OF_MEMORY?;
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: mem::OUT_OF_MEMORY~;
}
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY?;
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY~;
}
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
if (!data) return mem::OUT_OF_MEMORY?;
if (!data) return mem::OUT_OF_MEMORY~;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
@@ -99,9 +105,9 @@ fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
{
if (alignment)
{
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: mem::OUT_OF_MEMORY?;
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: mem::OUT_OF_MEMORY~;
}
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
@@ -122,12 +128,12 @@ fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
if (init_type == ZERO)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
return data ?: mem::OUT_OF_MEMORY?;
return data ?: mem::OUT_OF_MEMORY~;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
if (!data) return mem::OUT_OF_MEMORY?;
if (!data) return mem::OUT_OF_MEMORY~;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
@@ -141,9 +147,9 @@ fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignmen
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
return data ?: mem::OUT_OF_MEMORY?;
return data ?: mem::OUT_OF_MEMORY~;
}
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY~;
}

View File

@@ -127,7 +127,7 @@ fn void TempAllocator.reset(&self)
{
TempAllocator* old = child;
child = old.derived;
old.destroy();
temp_allocator_destroy(old);
}
self.capacity = self.original_capacity;
$if env::ADDRESS_SANITIZER:
@@ -142,17 +142,17 @@ fn void TempAllocator.reset(&self)
fn void TempAllocator.free(&self)
{
self.reset();
self.destroy();
temp_allocator_destroy(self);
}
fn void TempAllocator.destroy(&self) @local
fn void temp_allocator_destroy(TempAllocator* self)
{
TempAllocatorPage *last_page = self.last_page;
while (last_page)
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
_free_page(self, to_free)!!;
}
if (self.allocated)
{
@@ -179,33 +179,6 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
}
fn void? TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*? TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
// Walk backwards to find the pointer to this page.
TempAllocatorPage **pointer_to_prev = &self.last_page;
// Remove the page from the list
while (*pointer_to_prev != page)
{
pointer_to_prev = &((*pointer_to_prev).prev_page);
}
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, NO_ZERO, alignment)!;
if (page_size > size) page_size = size;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
@@ -215,7 +188,7 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
assert(self.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment);
return _realloc_page(self, page, size, alignment);
}
bool is_realloc_of_last = chunk.size + pointer == &self.data[self.used];
if (is_realloc_of_last)
@@ -326,9 +299,39 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
return &page.data[0];
}
fn void? _free_page(TempAllocator* self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*? _realloc_page(TempAllocator* self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
// Walk backwards to find the pointer to this page.
TempAllocatorPage **pointer_to_prev = &self.last_page;
// Remove the page from the list
while (*pointer_to_prev != page)
{
pointer_to_prev = &((*pointer_to_prev).prev_page);
}
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, NO_ZERO, alignment)!;
if (page_size > size) page_size = size;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
module std::core::mem::allocator @if((env::POSIX || env::WIN32) && $feature(VMEM_TEMP));
import std::math;
tlocal VmemOptions temp_allocator_default_options = {
.shrink_on_reset = env::MEMORY_ENV != NORMAL,
.protect_unused_pages = env::COMPILER_OPT_LEVEL <= O1 || env::COMPILER_SAFE_MODE,
@@ -383,10 +386,10 @@ fn void TempAllocator.reset(&self)
}
fn void TempAllocator.free(&self)
{
self.destroy();
_destroy(self);
}
fn void TempAllocator.destroy(&self) @local
fn void _destroy(TempAllocator* self) @local
{
TempAllocator* child = self.derived;
if (!child) return;
@@ -403,4 +406,4 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
fn void TempAllocator.release(&self, void* old_pointer, bool b) @dynamic
{
self.vmem.release(old_pointer, b) @inline;
}
}

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,6 +1,7 @@
module std::core::string::ansi;
import std::io;
enum Ansi : const inline String
constdef Ansi : inline String
{
RESET = "\e[0m",
BOLD = "\e[1m",
@@ -56,6 +57,37 @@ enum Ansi : const inline String
BG_BRIGHT_WHITE = "\e[107m",
}
struct AnsiColor (Printable)
{
char r, g, b;
bool bg;
}
fn usz? AnsiColor.to_format(&self, Formatter* fmt) @dynamic
{
return fmt.printf("\e[%s8;2;%s;%s;%sm", self.bg ? 4 : 3, self.r, self.g, self.b);
}
<*
24-bit color code
@return `A struct that, when formatted with '%s', sets the foreground or background colour to the specified rgb value`
*>
fn AnsiColor get_color_rgb(char r, char g, char b, bool bg = false)
{
return {r, g, b, bg};
}
<*
24-bit color code
@return `A struct that, when formatted with '%s', sets the foreground or background colour of printed text to the specified rgb value`
*>
fn AnsiColor get_color(uint rgb, bool bg = false)
{
return {(char)(rgb >> 16), (char)((rgb & 0x00FF00) >> 8), (char)rgb, bg};
}
<*
8-bit color code
@@ -96,7 +128,7 @@ macro String color(uint $rgb, bool $bg = false) @const
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
fn String make_color(Allocator mem, uint rgb, bool bg = false)
fn String make_color(Allocator mem, uint rgb, bool bg = false) @deprecated("use get_color instead")
{
return make_color_rgb(mem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
}
@@ -107,7 +139,7 @@ fn String make_color(Allocator mem, uint rgb, bool bg = false)
@require rgb <= 0xFF_FF_FF : `Expected a 24 bit RGB value`
@return `the string char for the given foreground color`
*>
fn String make_tcolor(uint rgb, bool bg = false)
fn String make_tcolor(uint rgb, bool bg = false) @deprecated("use get_color instead")
{
return make_color_rgb(tmem, (char)(rgb >> 16), (char)((rgb & 0xFF00) >> 8), (char)rgb, bg);
}
@@ -117,7 +149,7 @@ fn String make_tcolor(uint rgb, bool bg = false)
@return `the string char for the given foreground color`
*>
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false) @deprecated("use get_color_rgb instead")
{
return string::format(mem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
}
@@ -127,7 +159,7 @@ fn String make_color_rgb(Allocator mem, char r, char g, char b, bool bg = false)
@return `the string char for the given foreground color`
*>
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false)
fn String make_tcolor_rgb(char r, char g, char b, bool bg = false) @deprecated("use get_color_rgb instead")
{
return string::format(tmem, "\e[%s8;2;%s;%s;%sm", bg ? 4 : 3, r, g, b);
}

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~;
}
@@ -329,6 +329,72 @@ macro bool @all(array, #predicate)
}
<*
Extract a copy of all even-index elements from an input array.
@param [&inout] allocator : "The allocator used to create the return array."
@param [in] array : "The array from which to extract all even elements."
@require @is_valid_list(array) : "Expected a valid list"
@require $defined(array[:lengthof(array)]) : "Expected a sliceable list"
*>
macro even(Allocator allocator, array)
{
return unlace_impl{$typeof(array[0])}(allocator, array[:lengthof(array)]);
}
<*
Extract a copy of all odd-index elements from an input array.
@param [&inout] allocator : "The allocator used to create the return array."
@param [in] array : "The array from which to extract all odd elements."
@require @is_valid_list(array) : "Expected a valid list"
@require $defined(array[:lengthof(array)]) : "Expected a sliceable list"
*>
macro odd(Allocator allocator, array)
{
return unlace_impl{$typeof(array[0])}(allocator, lengthof(array) > 1 ? array[1..] : ($typeof(array[0])[]){});
}
<*
Private implementation of `even` and `odd` macros, expecting a slice and returning one as well.
This function always extracts the even elements of the input slice.
@param [&inout] allocator : "The allocator used to create the return array."
@param [in] array : "The array from which to extract all odd elements."
*>
fn Type[] unlace_impl(Allocator allocator, Type[] array) <Type> @private
{
usz new_len = array.len / 2 + (array.len % 2 == 0 ? 0 : 1);
if (new_len == 0) return (Type[]){};
Type[] new_array = allocator::new_array(allocator, Type, new_len);
foreach (x, &new : new_array) *new = types::implements_copy(Type) ??? array[x * 2].copy(allocator) : array[x * 2];
return new_array[:new_len];
}
<*
Unlace or partition an input list into its component parts such that `[a, b, c, d, e]` becomes
`[a, c, e]` and `[b, d]`. Returned arrays are allocated by the given allocator and are returned
via two `out` parameters, `left` and `right`.
@param [&inout] allocator : "The allocator used to create the returned arrays."
@param [in] array : "The input array to unlace."
@param [out] left : "Stores a copy of all even-index array elements."
@param [out] right : "Stores a copy of all odd-index array elements."
@require @is_valid_list(array) : "Expected a valid list"
@require $typeof(left) == $typeof(array[0])[]*
@require $typeof(right) == $typeof(array[0])[]*
*>
macro unlace(Allocator allocator, array, left, right)
{
if (left) *left = even(allocator, array);
if (right) *right = odd(allocator, array);
}
<*
Zip together two separate arrays/slices into a single array of Pairs or return values. Values will
be collected up to the length of the shorter array if `fill_with` is left undefined; otherwise, they

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

@@ -2,7 +2,7 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::core::bitorder;
import std::bits;
// This module contains types of different endianness.
// *BE types represent big-endian types
// *LE types represent little-endian types.
@@ -87,36 +87,88 @@ bitstruct UInt128LE : uint128 @littleendian
uint128 val : 0..127;
}
<*
@require $defined(*bytes) : "Pointer must be possible to dereference"
@require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
*>
macro load_be(bytes)
{
$if env::BIG_ENDIAN:
return mem::load(bytes, $align: 1);
$else
return bswap(mem::load(bytes, $align: 1));
$endif
}
<*
@require $defined(*bytes) : "Pointer must be possible to dereference"
@require types::is_intlike($typeof(*bytes)) : "Type must be an integer or int vector"
*>
macro load_le(bytes)
{
$if env::BIG_ENDIAN:
return bswap(mem::load(bytes, $align: 1));
$else
return mem::load(bytes, $align: 1);
$endif
}
<*
@require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
*>
macro void store_be(void* dst, value)
{
$if env::BIG_ENDIAN:
mem::store(($typeof(value)*)dst, value, $align: 1);
$else
mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
$endif
}
<*
@require types::is_intlike($typeof(value)) : "Type must be an integer or int vector"
*>
macro void store_le(void* dst, value)
{
$if env::BIG_ENDIAN:
mem::store(($typeof(value)*)dst, bswap(value), $align: 1);
$else
mem::store(($typeof(value)*)dst, value, $align: 1);
$endif
}
<*
@require @is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
@require $defined(*bytes) ||| $defined(bytes[:$Type.sizeof]) : "Data is too short to contain value"
*>
macro read(bytes, $Type)
{
char[] s;
char *ptr;
$switch $kindof(bytes):
$case POINTER:
s = (*bytes)[:$Type.sizeof];
ptr = bytes;
$default:
s = bytes[:$Type.sizeof];
ptr = bytes[..].ptr;
$endswitch
return bitcast(*(char[$Type.sizeof]*)s.ptr, $Type).val;
return bitcast(mem::load((char[$Type.sizeof]*)ptr, $align: 1), $Type).val;
}
<*
@require @is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
@require $defined(*bytes) ||| $defined(bytes[:$Type.sizeof]) : "Data is not sufficent to hold value"
*>
macro write(x, bytes, $Type)
{
char[] s;
char *ptr;
$switch $kindof(bytes):
$case POINTER:
s = (*bytes)[:$Type.sizeof];
ptr = bytes;
$default:
s = bytes[:$Type.sizeof];
ptr = bytes[..].ptr;
$endswitch
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
mem::store(($typeof(x)*)ptr, bitcast(x, $Type).val, 1);
}
macro bool is_bitorder($Type)

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;
typedef EmptySlot @constinit = void*;
macro bool @is_empty_macro_slot(#arg) @const @builtin
@deprecated("Use `#arg = ...` to define an optional macro slot, and `$defined(#arg)` to detect whether the argument is set.")
=> $typeof(#arg) == EmptySlot;
macro bool @is_valid_macro_slot(#arg) @const @builtin
@deprecated("Use `#arg = ...` to define an optional macro slot, and `$defined(#arg)` to detect whether the argument is set.")
=> $typeof(#arg) != EmptySlot;
<*
Returns a random value at compile time.
@@ -93,6 +97,21 @@ macro usz bitsizeof($Type) @builtin @const => $Type.sizeof * 8u;
macro usz @bitsizeof(#expr) @builtin @const => $sizeof(#expr) * 8u;
<*
Compile-time check for whether a set of constants contains a certain expression.
@param #needle : "The expression whose value should be located."
*>
macro bool @in(#needle, ...) @builtin @const
{
$for var $x = 0; $x < $vacount; $x++:
$assert $defined(#needle == $vaconst[$x])
: "Index %s: types '%s' (needle) and '%s' are not equatable", $x, $typeof(#needle), $typeof($vaconst[$x]);
$if #needle == $vaconst[$x]: return true; $endif
$endfor
return false;
}
<*
Convert an `any` type to a type, returning an failure if there is a type mismatch.
@@ -104,10 +123,29 @@ macro usz @bitsizeof(#expr) @builtin @const => $sizeof(#expr) * 8u;
*>
macro anycast(any v, $Type) @builtin
{
if (v.type != $Type.typeid) return TYPE_MISMATCH?;
if (v.type != $Type.typeid) return TYPE_MISMATCH~;
return ($Type*)v.ptr;
}
<*
@return "The value in the pointer"
@return? TYPE_MISMATCH
*>
macro any.to(self, $Type)
{
if (self.type != $Type.typeid) return TYPE_MISMATCH~;
return *($Type*)self.ptr;
}
<*
@require self.type == $Type : "The 'any' contained an unexpected type."
@return "The value in the pointer"
*>
macro any.as(self, $Type)
{
return *($Type*)self.ptr;
}
macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$defined($Type x = #foo)'") => $defined(*&&($Type){} = #foo);
macro @addr(#val) @builtin
@@ -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

@@ -1,5 +1,5 @@
module std::core::dstring;
import std::io;
import std::io, std::math;
<*
The DString offers a dynamic string builder.
@@ -95,7 +95,7 @@ fn void DString.replace(&self, String needle, String replacement)
match++;
if (match == needle_len)
{
self.append_chars(replacement);
self.append_string(replacement);
match = 0;
continue;
}
@@ -103,12 +103,12 @@ fn void DString.replace(&self, String needle, String replacement)
}
if (match > 0)
{
self.append_chars(str[i - match:match]);
self.append_string(str[i - match:match]);
match = 0;
}
self.append_char(c);
}
if (match > 0) self.append_chars(str[^match:match]);
if (match > 0) self.append_string(str[^match:match]);
};
}
@@ -305,18 +305,23 @@ fn bool DString.less(self, DString other_string)
return true;
}
fn void DString.append_chars(&self, String str)
fn void DString.append_chars(&self, String str) @deprecated("Use append_string")
{
usz other_len = str.len;
self.append_bytes(str);
}
fn void DString.append_bytes(&self, char[] bytes)
{
usz other_len = bytes.len;
if (!other_len) return;
if (!*self)
{
*self = temp(str);
*self = temp((String)bytes);
return;
}
self.reserve(other_len);
StringData* data = self.data();
mem::copy(&data.chars[data.len], str.ptr, other_len);
mem::copy(&data.chars[data.len], bytes.ptr, other_len);
data.len += other_len;
}
@@ -325,7 +330,24 @@ fn Char32[] DString.copy_utf32(&self, Allocator allocator)
return self.str_view().to_utf32(allocator) @inline!!;
}
fn void DString.append_string(&self, DString str)
<*
@require $defined(String s = str) ||| $typeof(str) == DString : "Expected string or DString"
*>
macro void DString.append_string(&self, str)
{
$if $typeof(str) == DString:
self.append_string_deprecated(str);
$else
self.append_bytes((String)str);
$endif
}
macro void DString.append_string_deprecated(&self, DString str) @deprecated("Use .append_dstring()")
{
self.append_dstring(str);
}
fn void DString.append_dstring(&self, DString str)
{
StringData* other = str.data();
if (!other) return;
@@ -340,7 +362,7 @@ fn void DString.clear(self)
fn usz? DString.write(&self, char[] buffer) @dynamic
{
self.append_chars((String)buffer);
self.append_bytes(buffer);
return buffer.len;
}
@@ -400,9 +422,9 @@ macro void DString.append(&self, value)
$case ichar:
self.append_char(value);
$case DString:
self.append_string(value);
self.append_dstring(value);
$case String:
self.append_chars(value);
self.append_string(value);
$case Char32:
self.append_char32(value);
$default:
@@ -410,7 +432,7 @@ macro void DString.append(&self, value)
$case $defined((Char32)value):
self.append_char32((Char32)value);
$case $defined((String)value):
self.append_chars((String)value);
self.append_string((String)value);
$default:
$error "Unsupported type for append use appendf instead.";
$endswitch
@@ -609,7 +631,7 @@ fn void DString.reverse(self)
}
}
fn StringData* DString.data(self) @inline @private
fn StringData* DString.data(self) @inline
{
return (StringData*)self;
}
@@ -626,7 +648,7 @@ fn void DString.reserve(&self, usz addition)
if (data.capacity >= len) return;
usz new_capacity = data.capacity * 2;
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
while (new_capacity < len) new_capacity *= 2;
if (new_capacity < len) new_capacity = math::next_power_of_2(len);
data.capacity = new_capacity;
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
}
@@ -636,9 +658,10 @@ fn usz? DString.read_from_stream(&self, InStream reader)
if (&reader.available)
{
usz total_read = 0;
while (usz available = reader.available()!)
while (ulong available = reader.available()!)
{
self.reserve(available);
if (available > isz.max) available = (ulong)isz.max;
self.reserve((usz)available);
StringData* data = self.data();
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
total_read += len;

View File

@@ -124,7 +124,9 @@ const usz MAX_VECTOR_SIZE = $$MAX_VECTOR_SIZE;
const bool ARCH_32_BIT = $$REGISTER_SIZE == 32;
const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE;
const bool NO_LIBC = !LIBC && !CUSTOM_LIBC;
const bool CUSTOM_LIBC = $$CUSTOM_LIBC;
const bool OLD_IO = $feature(OLD_IO);
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
@@ -153,19 +155,20 @@ const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD;
const bool WASI = LIBC && OS_TYPE == WASI;
const bool WASM = ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
const bool ANDROID = LIBC && OS_TYPE == ANDROID;
const bool WASM_NOLIBC @builtin @deprecated("Use 'FREESTANDING_WASM' instead") = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
const bool FREESTANDING_PE32 = NO_LIBC && OS_TYPE == WIN32;
const bool FREESTANDING_MACHO = NO_LIBC && OS_TYPE == MACOS;
const bool FREESTANDING_ELF = NO_LIBC && !env::FREESTANDING_PE32 && !env::FREESTANDING_MACHO && !env::FREESTANDING_WASM;
const bool FREESTANDING_WASM = NO_LIBC && (ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64);
const bool FREESTANDING_WASM = NO_LIBC && WASM;
const bool FREESTANDING = env::FREESTANDING_PE32 || env::FREESTANDING_MACHO || env::FREESTANDING_ELF || env::FREESTANDING_WASM;
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32;
const bool HAS_NATIVE_ERRNO = env::LINUX || env::ANDROID || env::OPENBSD || env::DARWIN || env::WIN32 || env::NETBSD;
macro bool os_is_darwin() @const
{

View File

@@ -6,22 +6,22 @@ const FULL_LOG = env::COMPILER_SAFE_MODE || $feature(FULL_LOG);
typedef LogCategory = inline char;
typedef LogTag = char[12];
const LogCategory CATEGORY_APPLICATION = 0;
const LogCategory CATEGORY_SYSTEM = 1;
const LogCategory CATEGORY_KERNEL = 2;
const LogCategory CATEGORY_AUDIO = 3;
const LogCategory CATEGORY_VIDEO = 4;
const LogCategory CATEGORY_RENDER = 5;
const LogCategory CATEGORY_INPUT = 6;
const LogCategory CATEGORY_NETWORK = 7;
const LogCategory CATEGORY_SOCKET = 8;
const LogCategory CATEGORY_SECURITY = 9;
const LogCategory CATEGORY_TEST = 10;
const LogCategory CATEGORY_ERROR = 11;
const LogCategory CATEGORY_ASSERT = 12;
const LogCategory CATEGORY_CRASH = 13;
const LogCategory CATEGORY_STATS = 14;
const LogCategory CATEGORY_CUSTOM_START = 100;
const LogCategory CATEGORY_APPLICATION = (LogCategory)0;
const LogCategory CATEGORY_SYSTEM = (LogCategory)1;
const LogCategory CATEGORY_KERNEL = (LogCategory)2;
const LogCategory CATEGORY_AUDIO = (LogCategory)3;
const LogCategory CATEGORY_VIDEO = (LogCategory)4;
const LogCategory CATEGORY_RENDER = (LogCategory)5;
const LogCategory CATEGORY_INPUT = (LogCategory)6;
const LogCategory CATEGORY_NETWORK = (LogCategory)7;
const LogCategory CATEGORY_SOCKET = (LogCategory)8;
const LogCategory CATEGORY_SECURITY = (LogCategory)9;
const LogCategory CATEGORY_TEST = (LogCategory)10;
const LogCategory CATEGORY_ERROR = (LogCategory)11;
const LogCategory CATEGORY_ASSERT = (LogCategory)12;
const LogCategory CATEGORY_CRASH = (LogCategory)13;
const LogCategory CATEGORY_STATS = (LogCategory)14;
const LogCategory CATEGORY_CUSTOM_START = (LogCategory)100;
tlocal LogCategory default_category = CATEGORY_APPLICATION;
tlocal LogTag current_tag;
@@ -148,17 +148,18 @@ fn void call_log_internal(LogPriority prio, LogCategory category, String file, S
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
if (priority > prio) return;
init();
bool locked = logger_mutex.is_initialized() && @ok(logger_mutex.lock());
bool locked = logger_mutex.is_initialized();
if (locked) logger_mutex.lock();
Logger logger = current_logger;
LogFn logfn = current_logfn;
defer if (locked) (void)logger_mutex.unlock();
defer if (locked) logger_mutex.unlock();
logfn(logger.ptr, prio, category, current_tag, file, func, line, fmt, args);
}
fn String? get_category_name(LogCategory category)
{
String val = category_names[category];
return val ?: NOT_FOUND?;
return val ?: NOT_FOUND~;
}
fn void set_category_name(LogCategory category, String name)

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);
@@ -377,6 +404,28 @@ macro void*? @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
return mem;
}
<*
@require bytes > 0
@require alignment > 0
@require bytes <= isz.max
*>
macro void*? @aligned_alloc_fn(context, #alloc_fn, usz bytes, usz alignment)
{
if (alignment < void*.alignof) alignment = void*.alignof;
usz header = AlignedBlock.sizeof + alignment;
usz alignsize = bytes + header;
$if $kindof(#alloc_fn(context, bytes)) == OPTIONAL:
void* data = #alloc_fn(context, alignsize)!;
$else
void* data = #alloc_fn(context, alignsize);
$endif
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
struct AlignedBlock
{
usz len;
@@ -393,6 +442,16 @@ macro void? @aligned_free(#free_fn, void* old_pointer)
$endif
}
macro void? @aligned_free_fn(context, #free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if $kindof(#free_fn(context, desc.start)) == OPTIONAL:
#free_fn(context, desc.start)!;
$else
#free_fn(context, desc.start);
$endif
}
<*
@require bytes > 0
@require alignment > 0
@@ -411,6 +470,23 @@ macro void*? @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
return new_data;
}
<*
@require bytes > 0
@require alignment > 0
*>
macro void*? @aligned_realloc_fn(context, #calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_alloc_fn(context, #calloc_fn, bytes, alignment)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
$if $kindof(#free_fn(context, data_start)) == OPTIONAL:
#free_fn(context, data_start)!;
$else
#free_fn(context, data_start);
$endif
return new_data;
}
// All allocators
alias mem @builtin = thread_allocator ;
@@ -556,12 +632,12 @@ typedef NullAllocator (Allocator) = uptr;
fn void*? NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
return mem::OUT_OF_MEMORY?;
return mem::OUT_OF_MEMORY~;
}
fn void*? NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
return mem::OUT_OF_MEMORY?;
return mem::OUT_OF_MEMORY~;
}
fn void NullAllocator.release(&self, void* old_ptr, bool aligned) @dynamic

View File

@@ -54,7 +54,7 @@ struct FixedBlockPool
@require calculate_actual_capacity(capacity, block_size) * block_size >= block_size
: "Total memory would overflow"
*>
macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
fn FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
{
self.allocator = allocator;
self.tail = &self.head;
@@ -64,7 +64,7 @@ macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_
self.alignment = allocator::alignment_for_allocation(alignment);
self.page_size = capacity * self.block_size;
assert(self.page_size >= self.block_size, "Total memory would overflow %d %d", block_size, capacity);
self.head.buffer = self.allocate_page();
self.head.buffer = fixedblockpool_allocate_page(self);
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::poison_memory_region(self.head.buffer, self.page_size);
$endif
@@ -119,7 +119,7 @@ fn void FixedBlockPool.free(&self)
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(self.head.buffer, self.page_size);
$endif
self.free_page(self.head.buffer);
fixedblockpool_free_page(self, self.head.buffer);
FixedBlockPoolNode* iter = self.head.next;
while (iter)
@@ -127,7 +127,7 @@ fn void FixedBlockPool.free(&self)
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(iter.buffer, self.page_size);
$endif
self.free_page(iter.buffer);
fixedblockpool_free_page(self, iter.buffer);
FixedBlockPoolNode* current = iter;
iter = iter.next;
allocator::free(self.allocator, current);
@@ -158,7 +158,7 @@ fn void* FixedBlockPool.alloc(&self)
}
void* end = self.tail.buffer + (self.tail.capacity * self.block_size);
if (self.next_free >= end) self.new_node();
if (self.next_free >= end) fixedblockpool_new_node(self);
void* ptr = self.next_free;
self.next_free += self.block_size;
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
@@ -172,7 +172,7 @@ fn void* FixedBlockPool.alloc(&self)
Deallocate a block from the block pool
@require self.initialized : "The block pool must be initialized"
@require self.check_ptr(ptr) : "The pointer should be part of the pool"
@require fixedblockpool_check_ptr(self, ptr) : "The pointer should be part of the pool"
*>
fn void FixedBlockPool.dealloc(&self, void* ptr)
{
@@ -193,7 +193,7 @@ fn void FixedBlockPool.dealloc(&self, void* ptr)
<*
@require self.initialized : "The block pool must be initialized"
*>
fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
fn bool fixedblockpool_check_ptr(FixedBlockPool* self, void *ptr) @local
{
FixedBlockPoolNode* iter = &self.head;
@@ -210,10 +210,10 @@ fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
<*
@require self.grow_capacity > 0 : "How many blocks will it store"
*>
fn void FixedBlockPool.new_node(&self) @local
fn void fixedblockpool_new_node(FixedBlockPool* self) @local
{
FixedBlockPoolNode* node = allocator::new(self.allocator, FixedBlockPoolNode);
node.buffer = self.allocate_page();
node.buffer = fixedblockpool_allocate_page(self);
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::poison_memory_region(node.buffer, self.page_size);
$endif
@@ -224,14 +224,14 @@ fn void FixedBlockPool.new_node(&self) @local
self.allocated += node.capacity;
}
macro void* FixedBlockPool.allocate_page(&self) @private
macro void* fixedblockpool_allocate_page(FixedBlockPool* self) @private
{
return self.alignment > mem::DEFAULT_MEM_ALIGNMENT
? allocator::calloc_aligned(self.allocator, self.page_size, self.alignment)!!
: allocator::calloc(self.allocator, self.page_size);
}
macro void FixedBlockPool.free_page(&self, void* page) @private
macro void fixedblockpool_free_page(FixedBlockPool* self, void* page) @private
{
if (self.alignment > mem::DEFAULT_MEM_ALIGNMENT)
{

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~;
}
}
@@ -321,7 +321,7 @@ fn void? VirtualMemory.destroy(&self)
return release(self.ptr, self.size);
}
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX)
{
switch (self)
{
@@ -336,7 +336,7 @@ fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
}
}
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32)
{
switch (self)
{

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

@@ -7,16 +7,26 @@ macro usz _strlen(ptr) @private
return len;
}
macro int @main_to_err_main(#m, int, char**)
macro int @main_no_args(#m, int, char**)
{
if (catch #m()) return 1;
return 0;
$if $typeof(#m()) == void:
#m();
return 0;
$else
return #m();
$endif
}
macro int @main_to_int_main(#m, int, char**) => #m();
macro int @main_to_void_main(#m, int, char**)
macro int @main_args(#m, int argc, char** argv)
{
#m();
return 0;
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
$if $typeof(#m(list)) == void:
#m(list);
return 0;
$else
return #m(list);
$endif
}
macro String[] args_to_strings(int argc, char** argv) @private
@@ -32,21 +42,6 @@ macro String[] args_to_strings(int argc, char** argv) @private
}
macro int @main_to_err_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
if (catch #m(list)) return 1;
return 0;
}
macro int @main_to_int_main_args(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
return #m(list);
}
macro int @_main_runner(#m, int argc, char** argv)
{
String[] list = args_to_strings(argc, argv);
@@ -54,15 +49,18 @@ macro int @_main_runner(#m, int argc, char** argv)
return #m(list) ? 0 : 1;
}
macro int @main_to_void_main_args(#m, int argc, char** argv)
module std::core::main_stub @if(env::WIN32);
import std::os::win32;
macro win32_set_utf8_codepage() @local
{
String[] list = args_to_strings(argc, argv);
defer free(list.ptr);
#m(list);
return 0;
// By default windows uses an OEM codepage that differs based on locale
// and does not support printing utf-8 characters. This allows both
// printing utf-8 characters from strings and reading them from stdin.
win32::setConsoleCP(UTF8);
win32::setConsoleOutputCP(UTF8);
}
module std::core::main_stub @if(env::WIN32);
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @cname("CommandLineToArgvW");
@@ -91,90 +89,72 @@ macro void release_wargs(String[] list) @private
free(list.ptr);
}
macro int @win_to_err_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
macro int @win_main_no_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
if (catch #m()) return 1;
return 0;
}
macro int @win_to_int_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd) => #m();
macro int @win_to_void_main_noargs(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
#m();
return 0;
win32_set_utf8_codepage();
$if $typeof(#m()) == void:
#m();
return 0;
$else
return #m();
$endif
}
macro int @win_to_err_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
macro int @win_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
win32_set_utf8_codepage();
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(args)) return 1;
return 0;
$if $typeof(#m(args)) == void:
#m(args);
return 0;
$else
return #m(args);
$endif
}
macro int @win_to_int_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
macro int @win_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
win32_set_utf8_codepage();
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(args);
$if $typeof(#m(handle, prev_handle, args, show_cmd)) == void:
#m(handle, prev_handle, args, show_cmd);
return 0;
$else
return #m(handle, prev_handle, args, show_cmd);
$endif
}
macro int @win_to_void_main_args(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(args);
return 0;
}
macro int @win_to_err_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
if (catch #m(handle, prev_handle, args, show_cmd)) return 1;
return 0;
}
macro int @win_to_int_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
return #m(handle, prev_handle, args, show_cmd);
}
macro int @win_to_void_main(#m, void* handle, void* prev_handle, Char16* cmd_line, int show_cmd)
{
String[] args = win_command_line_to_strings(cmd_line);
defer release_wargs(args);
#m(handle, prev_handle, args, show_cmd);
return 0;
}
macro int @wmain_to_err_main_args(#m, int argc, Char16** argv)
macro int @wmain_main(#m, int argc, Char16** argv)
{
win32_set_utf8_codepage();
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
if (catch #m(args)) return 1;
return 1;
$if $typeof(#m(args)) == void:
#m(args);
return 0;
$else
return #m(args);
$endif
}
macro int @wmain_to_int_main_args(#m, int argc, Char16** argv)
macro int @wmain_main_no_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args);
win32_set_utf8_codepage();
$if $typeof(#m()) == void:
#m();
return 0;
$else
return #m();
$endif
}
macro int @_wmain_runner(#m, int argc, Char16** argv)
{
win32_set_utf8_codepage();
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
return #m(args) ? 0 : 1;
}
macro int @wmain_to_void_main_args(#m, int argc, Char16** argv)
{
String[] args = wargs_strings(argc, argv);
defer release_wargs(args);
#m(args);
return 0;
}

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

@@ -125,10 +125,11 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
char[] perc_str = { [0..19] = ' ', [20] = 0 };
int perc = 0;
uint print_step = current_benchmark_iterations / 100;
if (print_step == 0) print_step = 1;
for (this_iteration = 0; this_iteration < current_benchmark_iterations; ++this_iteration, benchmark_nano_seconds = {})
{
if (0 == this_iteration % print_step) // only print right about when the % will update
if (this_iteration % print_step == 0) // only print right about when the % will update
{
perc_str[0..(uint)math::floor((this_iteration / (float)current_benchmark_iterations) * 20)] = '#';
perc = (uint)math::ceil(100 * (this_iteration / (float)current_benchmark_iterations));

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;
@@ -140,7 +142,7 @@ fn void mute_output() @local
File* stderr = io::stderr();
*stderr = test_context.fake_stdout;
*stdout = test_context.fake_stdout;
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
(void)test_context.fake_stdout.set_cursor(0)!!;
}
fn void unmute_output(bool has_error) @local
@@ -153,7 +155,7 @@ fn void unmute_output(bool has_error) @local
*stderr = test_context.stored.stderr;
*stdout = test_context.stored.stdout;
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
ulong log_size = test_context.fake_stdout.cursor()!!;
if (has_error)
{
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
@@ -163,7 +165,7 @@ fn void unmute_output(bool has_error) @local
{
test_context.fake_stdout.write_byte('\n')!!;
test_context.fake_stdout.write_byte('\0')!!;
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
test_context.fake_stdout.set_cursor(0)!!;
io::printfn("\n========== TEST LOG ============");
io::printfn("%s\n", test_context.current_test_name);
@@ -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

@@ -2,16 +2,16 @@ module std::core::sanitizer::tsan;
typedef MutexFlags = inline CUInt;
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;
const MutexFlags MUTEX_READ_REENTRANT = 1 << 2;
const MutexFlags MUTEX_NOT_STATIC = 1 << 8;
const MutexFlags MUTEX_READ_LOCK = 1 << 3;
const MutexFlags MUTEX_TRY_LOCK = 1 << 4;
const MutexFlags MUTEX_TRY_LOCK_FAILED = 1 << 5;
const MutexFlags MUTEX_RECURSIVE_LOCK = 1 << 6;
const MutexFlags MUTEX_RECURSIVE_UNLOCK = 1 << 7;
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
const MutexFlags MUTEX_LINKER_INIT = (MutexFlags)1 << 0;
const MutexFlags MUTEX_WRITE_REENTRANT = (MutexFlags)1 << 1;
const MutexFlags MUTEX_READ_REENTRANT = (MutexFlags)1 << 2;
const MutexFlags MUTEX_NOT_STATIC = (MutexFlags)1 << 8;
const MutexFlags MUTEX_READ_LOCK = (MutexFlags)1 << 3;
const MutexFlags MUTEX_TRY_LOCK = (MutexFlags)1 << 4;
const MutexFlags MUTEX_TRY_LOCK_FAILED = (MutexFlags)1 << 5;
const MutexFlags MUTEX_RECURSIVE_LOCK = (MutexFlags)1 << 6;
const MutexFlags MUTEX_RECURSIVE_UNLOCK = (MutexFlags)1 << 7;
const MutexFlags MUTEX_TRY_READ_LOCK = MUTEX_READ_LOCK | MUTEX_TRY_LOCK;
const MutexFlags MUTEX_TRY_READ_LOCK_FAILED = MUTEX_TRY_READ_LOCK | MUTEX_TRY_LOCK_FAILED;
macro void mutex_create(void* addr, MutexFlags flags) { $if env::THREAD_SANITIZER: __tsan_mutex_create(addr, flags); $endif }

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

@@ -1,14 +1,16 @@
module std::core::string;
import std::io, std::ascii;
import std::core::mem::allocator;
typedef String @if(!$defined(String)) = inline char[];
typedef String @constinit @if(!$defined(String)) = inline char[];
<*
ZString is a pointer to a zero terminated array of chars.
Use ZString when you need to interop with C zero terminated strings.
*>
typedef ZString = inline char*;
typedef ZString @constinit = inline char*;
<*
WString is a pointer to a zero terminated array of Char16.
@@ -96,7 +98,8 @@ fn ZString tformat_zstr(String fmt, args...) @format(0)
}
<*
Return a new String created using the formatting function.
Return a new String created using the formatting function, this function will implicitly
use the temp allocator.
@param [inout] allocator : `The allocator to use`
@param [in] fmt : `The formatting string`
@@ -120,7 +123,7 @@ fn String bformat(char[] buffer, String fmt, args...) @format(1)
OutputFn format_fn = fn void?(void* buf, char c) {
char[]* buffer_ref = buf;
char[] buffer = *buffer_ref;
if (buffer.len == 0) return io::BUFFER_EXCEEDED?;
if (buffer.len == 0) return io::BUFFER_EXCEEDED~;
buffer[0] = c;
*buffer_ref = buffer[1..];
};
@@ -156,31 +159,44 @@ macro bool char_in_set(char c, String set)
return false;
}
<*
Join together an array of strings via a "joiner" sequence, which is inserted between each element.
@param [&inout] allocator : "The allocator to use."
@param [in] s : "An array of strings to join in sequence."
@param [in] joiner : "The string used to join each element of `s`."
@return "A single string containing the result, allocated via `allocator`, safe to convert to a ZString."
*>
fn String join(Allocator allocator, String[] s, String joiner)
{
if (!s)
{
return (String)allocator::new_array(allocator, char, 2)[:0];
}
usz total_size = joiner.len * s.len;
usz joiner_len = joiner.len;
usz total_size = joiner_len * (s.len - 1) + 1;
foreach (String* &str : s)
{
total_size += str.len;
}
@pool()
char[] data = allocator::alloc_array(allocator, char, total_size);
usz offset = s[0].len;
data[:offset] = s[0][:offset];
foreach (String* &str : s[1..])
{
DString res = dstring::temp_with_capacity(total_size);
res.append(s[0]);
foreach (String* &str : s[1..])
{
res.append(joiner);
res.append(*str);
}
return res.copy_str(allocator);
data[offset:joiner_len] = joiner[:joiner_len];
offset += joiner_len;
usz len = str.len;
data[offset:len] = str.[:len];
offset += len;
};
data[offset] = 0;
return (String)data[:offset];
}
<* Alias for `string::join` using the temp allocator. *>
macro String tjoin(String[] s, String joiner) => join(tmem, s, joiner);
<*
Replace all instances of one substring with a different string.
@@ -190,13 +206,18 @@ fn String join(Allocator allocator, String[] s, String joiner)
@param [&inout] allocator : `The allocator to use for the String`
@return "The new string with the elements replaced"
*>
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard
fn String String.replace(self, Allocator allocator, String needle, String new_str) @nodiscard => @pool()
{
@pool()
Splitter s = self.tokenize_all(needle);
DString d;
d.init(tmem, new_str.len * 2 + self.len + 16);
(void)d.append(s.next());
while (try element = s.next())
{
String[] split = self.tsplit(needle);
return dstring::join(tmem, split, new_str).copy_str(allocator);
};
d.append(new_str);
d.append(element);
}
return d.copy_str(allocator);
}
<*
@@ -209,8 +230,16 @@ fn String String.replace(self, Allocator allocator, String needle, String new_st
*>
fn String String.treplace(self, String needle, String new_str)
{
String[] split = self.tsplit(needle);
return dstring::join(tmem, split, new_str).str_view();
Splitter s = self.tokenize_all(needle);
DString d;
d.init(tmem, new_str.len * 2 + self.len + 16);
(void)d.append(s.next());
while (try element = s.next())
{
d.append(new_str);
d.append(element);
}
return d.str_view();
}
@@ -355,7 +384,7 @@ fn String[] String.split(self, Allocator allocator, String delimiter, usz max =
bool no_more = false;
while (!no_more)
{
usz? index = i == max - 1 ? NOT_FOUND? : self.index_of(delimiter);
usz? index = i == max - 1 ? NOT_FOUND~ : self.index_of(delimiter);
String res @noinit;
if (try index)
{
@@ -414,7 +443,7 @@ fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz ma
bool no_more = false;
while (!no_more)
{
usz? index = i == max - 1 ? NOT_FOUND? : s.index_of(delimiter);
usz? index = i == max - 1 ? NOT_FOUND~ : s.index_of(delimiter);
String res @noinit;
if (try index)
{
@@ -432,7 +461,7 @@ fn String[]? String.split_to_buffer(s, String delimiter, String[] buffer, usz ma
}
if (i == max_capacity)
{
return BUFFER_EXCEEDED?;
return BUFFER_EXCEEDED~;
}
buffer[i++] = res;
}
@@ -513,7 +542,7 @@ fn usz? String.index_of_char(self, char character)
{
if (c == character) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -536,7 +565,7 @@ fn usz? String.index_of_chars(String self, char[] characters)
}
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -553,12 +582,12 @@ fn usz? String.index_of_chars(String self, char[] characters)
fn usz? String.index_of_char_from(self, char character, usz start_index)
{
usz len = self.len;
if (len <= start_index) return NOT_FOUND?;
if (len <= start_index) return NOT_FOUND~;
for (usz i = start_index; i < len; i++)
{
if (self[i] == character) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -577,7 +606,7 @@ fn usz? String.rindex_of_char(self, char character)
{
if (c == character) return i;
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -602,7 +631,7 @@ fn usz? String.index_of(self, String substr)
if (c == first && self[i : needed] == substr) return i;
}
}
return NOT_FOUND?;
return NOT_FOUND~;
}
<*
@@ -627,7 +656,7 @@ fn usz? String.rindex_of(self, String substr)
if (c == first && self[i : needed] == substr) return i;
}
}
return NOT_FOUND?;
return NOT_FOUND~;
}
fn bool ZString.eq(self, ZString other) @operator(==)
@@ -1013,12 +1042,12 @@ macro String.to_integer(self, $Type, int base = 10)
usz index = 0;
char* ptr = self.ptr;
while (index < len && ptr[index].is_blank()) index++;
if (len == index) return EMPTY_STRING?;
if (len == index) return EMPTY_STRING~;
bool is_negative;
switch (self[index])
{
case '-':
if ($Type.min == 0) return NEGATIVE_VALUE?;
if ($Type.min == 0) return NEGATIVE_VALUE~;
is_negative = true;
index++;
case '+':
@@ -1026,7 +1055,7 @@ macro String.to_integer(self, $Type, int base = 10)
default:
break;
}
if (len == index) return MALFORMED_INTEGER?;
if (len == index) return MALFORMED_INTEGER~;
$Type base_used = ($Type)base;
if (self[index] == '0' && base == 10)
{
@@ -1049,7 +1078,7 @@ macro String.to_integer(self, $Type, int base = 10)
default:
break;
}
if (len == index) return MALFORMED_INTEGER?;
if (len == index) return MALFORMED_INTEGER~;
}
$Type value = 0;
while (index != len)
@@ -1059,18 +1088,18 @@ macro String.to_integer(self, $Type, int base = 10)
{
case base_used < 10 || c < 'A': c -= '0';
case c <= 'F': c -= 'A' - 10;
case c < 'a' || c > 'f': return MALFORMED_INTEGER?;
case c < 'a' || c > 'f': return MALFORMED_INTEGER~;
default: c -= 'a' - 10;
}
if (c >= base_used) return MALFORMED_INTEGER?;
if (c >= base_used) return MALFORMED_INTEGER~;
do
{
if (is_negative)
{
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 +1213,21 @@ fn void Splitter.reset(&self)
self.current = 0;
}
fn bool Splitter.at_end(&self)
{
return self.current > self.string.len;
}
fn String? Splitter.next(&self)
{
while (true)
{
usz len = self.string.len;
usz current = self.current;
if (current > len) return NO_MORE_ELEMENT?;
if (current > len) return NO_MORE_ELEMENT~;
if (current == len)
{
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT?;
if (self.type != TOKENIZE_ALL) return NO_MORE_ELEMENT~;
self.current++;
return self.string[current - 1:0];
}
@@ -1205,7 +1239,7 @@ fn String? Splitter.next(&self)
if (!next && self.type == TOKENIZE) continue;
return remaining[:next];
}
self.current = len;
self.current = len + 1;
return remaining;
}
}

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)
@@ -158,7 +158,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
if (rp % 9)
{
long rpm9 = rp >= 0 ? rp % 9 : rp % 9 + 9;
int p10 = P10S[8 - rpm9];
uint p10 = P10S[8 - rpm9];
uint carry = 0;
for (k = a; k != z; k++)
{
@@ -320,7 +320,7 @@ macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
y *= 0.5;
e2++;
}
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return MALFORMED_FLOAT?;
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return MALFORMED_FLOAT~;
}
return math::scalbn(y, e2);
}
@@ -351,7 +351,7 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
got_rad = true;
if (index == last_char)
{
if (!got_digit) return MALFORMED_FLOAT?;
if (!got_digit) return MALFORMED_FLOAT~;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
@@ -369,7 +369,7 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
{
if (c == '.')
{
if (got_rad) return MALFORMED_FLOAT?;
if (got_rad) return MALFORMED_FLOAT~;
got_rad = true;
rp = dc;
}
@@ -393,20 +393,20 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
if (index == last_char) break;
c = chars[++index];
}
if (!got_digit) return MALFORMED_FLOAT?;
if (!got_digit) return MALFORMED_FLOAT~;
if (!got_rad) rp = dc;
for (; dc < 8; dc++) x *= 16;
long e2;
if ((c | 32) == 'p')
{
long e2val = String.to_long((String)chars[index + 1..]) ?? (MALFORMED_FLOAT?)!;
long e2val = String.to_long((String)chars[index + 1..]) ?? MALFORMED_FLOAT~!;
e2 = e2val;
}
e2 += 4 * rp - 32;
if (!x) return sign * 0.0;
if (e2 > -$emin) return FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE?;
if (e2 > -$emin) return FLOAT_OUT_OF_RANGE~;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE~;
while (x < 0x80000000)
{
@@ -441,7 +441,7 @@ macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
}
y = bias + sign * (double)x + sign * y;
y -= bias;
if (!y) return FLOAT_OUT_OF_RANGE?;
if (!y) return FLOAT_OUT_OF_RANGE~;
return math::scalbn(y, (int)e2);
}
@@ -463,7 +463,7 @@ macro String.to_real(chars, $Type) @private
$endswitch
chars = chars.trim();
if (!chars.len) return MALFORMED_FLOAT?;
if (!chars.len) return MALFORMED_FLOAT~;
if (chars.len != 1)
{
@@ -477,7 +477,7 @@ macro String.to_real(chars, $Type) @private
}
}
chars = chars.trim();
if (!chars.len) return MALFORMED_FLOAT?;
if (!chars.len) return MALFORMED_FLOAT~;
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
if (chars == "NAN" || chars == "nan") return $Type.nan;

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();
@@ -97,8 +97,8 @@ macro bool is_subtype_of($Type, $OtherType)
macro bool is_numerical($Type)
{
$switch $Type.kindof:
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return is_numerical($Type.inner);
$case SIGNED_INT:
$case UNSIGNED_INT:
@@ -112,7 +112,12 @@ macro bool is_numerical($Type)
fn bool TypeKind.is_int(kind) @inline
{
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
return kind == SIGNED_INT || kind == UNSIGNED_INT;
}
macro bool TypeKind.@is_int($kind) @const
{
return $kind == SIGNED_INT ||| $kind == UNSIGNED_INT;
}
macro bool is_slice_convertable($Type) @deprecated("Use is_slice_convertible") => is_slice_convertible($Type);
@@ -165,7 +170,7 @@ macro bool is_unsigned($Type) @const
macro typeid flat_type($Type) @const
{
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
return flat_type($Type.inner);
$else
return $Type.typeid;
@@ -174,7 +179,7 @@ macro typeid flat_type($Type) @const
macro TypeKind flat_kind($Type) @const
{
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
return flat_type($Type.inner);
$else
return $Type.kindof;
@@ -198,8 +203,8 @@ macro bool is_flat_intlike($Type) @const
$case UNSIGNED_INT:
return true;
$case VECTOR:
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return is_flat_intlike($Type.inner);
$default:
return false;
@@ -225,7 +230,7 @@ macro bool is_underlying_int($Type) @const
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
$case DISTINCT:
$case TYPEDEF:
return is_underlying_int($Type.inner);
$default:
return false;
@@ -253,7 +258,7 @@ macro bool is_vector($Type) @const
macro typeid inner_type($Type) @const
{
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
return inner_type($Type.inner);
$else
return $Type.typeid;
@@ -293,7 +298,7 @@ macro bool may_load_atomic($Type) @const
$case POINTER:
$case FLOAT:
return true;
$case DISTINCT:
$case TYPEDEF:
$case ENUM:
return may_load_atomic($Type.inner);
$default:
@@ -308,8 +313,8 @@ macro lower_to_atomic_compatible_type($Type) @const
$case SIGNED_INT:
$case UNSIGNED_INT:
return $Type.typeid;
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return lower_to_atomic_compatible_type($Type.inner);
$case FLOAT:
$switch $Type:
@@ -329,8 +334,8 @@ macro lower_to_atomic_compatible_type($Type) @const
$endswitch
}
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) || types::is_int($Type);
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) || types::is_int($Type);
macro bool is_promotable_to_floatlike($Type) @const => types::is_floatlike($Type) ||| types::is_int($Type);
macro bool is_promotable_to_float($Type) @const => types::is_float($Type) ||| types::is_int($Type);
macro bool is_same_vector_type($Type1, $Type2) @const
{
@@ -345,7 +350,7 @@ macro bool has_equals($Type) @const => $defined(($Type){} == ($Type){});
macro bool is_equatable_type($Type) @const
{
$if $defined($Type.less) || $defined($Type.compare_to) || $defined($Type.equals):
$if $defined($Type.less) ||| $defined($Type.compare_to) ||| $defined($Type.equals):
return true;
$else
return $Type.is_eq;
@@ -357,7 +362,7 @@ macro bool is_equatable_type($Type) @const
*>
macro bool implements_copy($Type) @const
{
return $defined($Type.copy) && $defined($Type.free);
return $defined($Type.copy) &&& $defined($Type.free);
}
macro bool @equatable_value(#value) @const
@@ -367,13 +372,16 @@ macro bool @equatable_value(#value) @const
macro bool @comparable_value(#value) @const
{
$if $defined(#value.less) || $defined(#value.compare_to):
$if $defined(#value.less) ||| $defined(#value.compare_to):
return true;
$else
return $typeof(#value).is_ordered;
$endif
}
const CONST_ENUM @builtin @deprecated("Use TypeKind.CONSTDEF instead") = TypeKind.CONSTDEF;
const DISTINCT @builtin @deprecated("Use TypeKind.TYPEDEF instead") = TypeKind.TYPEDEF;
enum TypeKind : char
{
VOID,
@@ -385,7 +393,7 @@ enum TypeKind : char
FAULT,
ANY,
ENUM,
CONST_ENUM,
CONSTDEF,
STRUCT,
UNION,
BITSTRUCT,
@@ -394,7 +402,7 @@ enum TypeKind : char
ARRAY,
SLICE,
VECTOR,
DISTINCT,
TYPEDEF,
POINTER,
INTERFACE,
}

View File

@@ -56,9 +56,9 @@ enum BlockMode
<* AES type: 128, 192 or 256 bits *>
enum AesType : (AesKey key)
{
AES128 = { 128, 16, 176, 4, 10 },
AES192 = { 192, 24, 208, 6, 12 },
AES256 = { 256, 32, 240, 8, 14 }
AES128 {{ 128, 16, 176, 4, 10 }},
AES192 {{ 192, 24, 208, 6, 12 }},
AES256 {{ 256, 32, 240, 8, 14 }}
}
struct AesKey

233
lib/std/crypto/chacha20.c3 Normal file
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(ChaCha20* self) @local @inline
{
self.key_stream[..] = self.state[..];
for (usz i = 0; i < 10; i++) // unrolling this does not improve performance measurably
{
quarter_round(&self.key_stream[0], 0, 4, 8, 12);
quarter_round(&self.key_stream[0], 1, 5, 9, 13);
quarter_round(&self.key_stream[0], 2, 6, 10, 14);
quarter_round(&self.key_stream[0], 3, 7, 11, 15);
quarter_round(&self.key_stream[0], 0, 5, 10, 15);
quarter_round(&self.key_stream[0], 1, 6, 11, 12);
quarter_round(&self.key_stream[0], 2, 7, 8, 13);
quarter_round(&self.key_stream[0], 3, 4, 9, 14);
}
// NOTE: This would 'feel' like a performance hit, but testing the benchmark doesn't show any noticeable
// difference on -O5 between this and a for-loop, or even an unrolled loop with compile-time '$for'.
array::@zip_into(self.key_stream[..], self.state[..], fn (a, b) => a + b);
self.state[12]++; // increment the block counter (rollovers are ok)
}
<*
Initialize a ChaCha20 transformation context.
@param key : `The secret key used for the transformation operation.`
@param nonce : `The one-time nonce to use for the transformation operation.`
@param counter : `An optional counter value to adjust the stream's position.`
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
*>
fn void ChaCha20.init(&self, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0)
{
// Init block.
self.position = BLOCK_SIZE; // start at the "end" of a block on init
self.bytes_processed = 0;
self.key[..] = key[..];
self.nonce[..] = nonce[..];
((char*)&self.state[0])[:MAGIC.len] = MAGIC[..];
((char*)&self.state[4])[:KEY_SIZE] = key[..];
self.state[12] = counter;
((char*)&self.state[13])[:NONCE_SIZE] = nonce[..];
}
<*
Transform some input data using the current context structure.
@param[inout] data : `The data to transform (encrypt or decrypt).`
*>
fn void ChaCha20.transform(&self, char[] data)
{
if (!data.len) return;
usz original_length = data.len;
char[] key_stream = @as_char_view(self.key_stream);
// 1. Process remaining bytes in the current keystream block.
if (self.position < BLOCK_SIZE)
{
usz len = data.len < (BLOCK_SIZE - self.position) ? data.len : (BLOCK_SIZE - self.position);
for (usz i = 0; i < len; i++) data[i] ^= key_stream[self.position + i];
self.position += len;
data = data[len..];
}
// 2. Get the amount of bytes offset from the nearest alignment boundary.
// Process full blocks at a time, word by word according to the system's architecture.
// Any extra bytes on each side are dynamically processed byte-by-byte.
usz offset = usz.sizeof - (((usz)data.ptr % usz.sizeof) ?: usz.sizeof);
for (usz x = offset; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..], x = offset)
{
chacha20_mutate_keystream(self);
if (offset) foreach (i, &b : data[:offset]) *b ^= key_stream[i];
char[] aligned_data = data[offset..];
for (; x <= (BLOCK_SIZE - usz.sizeof); x += usz.sizeof)
{
((usz*)aligned_data.ptr)[x / usz.sizeof] ^= mem::load((usz*)(&key_stream[x]), $align: 1);
}
for (; x < BLOCK_SIZE; x++) data[x] ^= key_stream[x];
}
// 3. Process any remaining bytes.
if (data.len > 0)
{
chacha20_mutate_keystream(self);
for (usz i = 0; i < data.len; i++) data[i] ^= key_stream[i];
self.position = data.len;
}
// All done. Capture the transformed length of data and check limits.
self.bytes_processed += original_length;
if (@unlikely(self.bytes_processed >= CHACHA20_NONCE_REUSE_LIMIT && !permit_overflow))
{
abort(
"ChaCha20 transform limit (~256 GiB) exceeded. You can set 'chacha20::permit_overflow = true;' at"
" runtime to disable this panic, but you accept the terrible SECURITY IMPLICATIONS of doing so."
);
}
}
<* Destroy the current context structure by zeroing all fields. *>
fn void ChaCha20.destroy(&self) => mem::zero_volatile(@as_char_view(*self));
<*
Perform an in-place transformation of some data in a buffer, without cloning the data to a new buffer.
@param[inout] data : `The data to transform (encrypt or decrypt).`
@param key : `The secret key used for the transformation operation.`
@param nonce : `The one-time nonce to use for the transformation operation.`
@param counter : `An optional counter value to adjust the stream's position.`
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
*>
fn void crypt(char[] data, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0) @private
{
if (@unlikely(!data.len)) return;
ChaCha20 c @noinit;
defer c.destroy();
c.init(key, nonce, counter);
c.transform(data);
}
alias encrypt_mut = crypt;
alias decrypt_mut = crypt;
<*
Perform a transformation of some data cloned from a source buffer.
@param[&inout] allocator : `The memory allocator which controls allocation of the cloned input data.`
@param[inout] data : `The data to transform (encrypt or decrypt).`
@param key : `The secret key used for the transformation operation.`
@param nonce : `The one-time nonce to use for the transformation operation.`
@param counter : `An optional counter value to adjust the stream's position.`
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
*>
fn char[] crypt_clone(Allocator allocator, char[] data, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0) @private
{
if (@unlikely(!data.len)) return {};
char[] buff = allocator::clone_slice(allocator, data);
crypt(buff, key, nonce, counter);
return buff;
}
alias encrypt = crypt_clone;
alias decrypt = crypt_clone;
<*
Perform a transformation of some data cloned from a source buffer by the temp allocator.
@param[inout] data : `The data to transform (encrypt or decrypt).`
@param key : `The secret key used for the transformation operation.`
@param nonce : `The one-time nonce to use for the transformation operation.`
@param counter : `An optional counter value to adjust the stream's position.`
@require key.len == KEY_SIZE : `Input key slice is not the correct length (32 bytes).`
@require nonce.len == NONCE_SIZE : `Input nonce slice is not the correct length (12 bytes).`
*>
fn char[] tcrypt_clone(char[] data, char[KEY_SIZE] key, char[NONCE_SIZE] nonce, uint counter = 0) @private
{
return crypt_clone(tmem, data, key, nonce, counter);
}
alias tencrypt = tcrypt_clone;
alias tdecrypt = tcrypt_clone;

View File

@@ -332,7 +332,7 @@ fn Projection Projection.mul(&s, char[] n) @operator(*)
module std::crypto::ed25519 @private;
typedef F25519Int = inline char[32];
typedef F25519Int @constinit = inline char[32];
const F25519Int ZERO = {};
const F25519Int ONE = {[0] = 1};
@@ -567,7 +567,7 @@ fn F25519Int F25519Int.inv(&s)
@param [&in] s
*>
fn F25519Int F25519Int.pow_2523(&s) @local
fn F25519Int pow_2523(F25519Int* s) @local
{
F25519Int r = *s;
@@ -587,7 +587,7 @@ fn F25519Int F25519Int.pow_2523(&s) @local
fn F25519Int F25519Int.sqrt(&s)
{
F25519Int twice = s.mul_s(2);
F25519Int pow = twice.pow_2523();
F25519Int pow = pow_2523(&twice);
return (twice * pow * pow - ONE) * s * pow;
}
@@ -601,7 +601,7 @@ fn F25519Int F25519Int.sqrt(&s)
module std::crypto::ed25519 @private;
import std::math;
typedef FBaseInt = inline char[32];
typedef FBaseInt @constinit = inline char[32];
// Order of the field : 2^252+0x14def9dea2f79cd65812631a5cf5d3ed
const FBaseInt ORDER = x"edd3f55c1a631258 d69cf7a2def9de14 0000000000000000 0000000000000010";

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..];
@@ -242,7 +242,7 @@ const char INVALID @private = 0xff;
const int STD_PADDING = '=';
const int NO_PADDING = -1;
typedef Alphabet = char[32];
typedef Alphabet @constinit = char[32];
// Standard base32 Alphabet
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
// Extended Hex Alphabet

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

@@ -0,0 +1,243 @@
// Copyright (c) 2026 Koni Marti. All rights reserved.
// Use of this source code is governed by the MIT license.
<*
Module providing generic singlebyte code page to UTF8 conversion.
This module implements a compact, tabledriven approach for single-byte
(8bit) encodings (e.g. CP437, CP850, CP866, CP125x). It is designed so
that each concrete code page only needs to supply a small, static
mapping table; the conversion logic is shared.
The design has two main goals:
- Fast decode from code page to UTF8 with a single table lookup per byte.
- Memoryefficient encode from UTF8 to code page without a large
Unicodetobyte array (no 64k reverse map per code page).
The design of CodePageTable and the packed reverse mapping is conceptually
similar to golang.org/x/text/encoding/charmap.
*>
module std::encoding::codepage;
import std::sort;
<*
Default replacement byte used when encoding from UTF8 to a singlebyte
code page and a Unicode scalar cannot be represented.
By convention, 0x1A is the ASCII/IBM SUB (substitute) control character.
*>
const char REPLACEMENT_CHAR = 0x1a;
<*
CodePageTable contains the bidirectional mapping tables for a singlebyte code
page in a compact packed form.
to_codepoint is the forward map from codepage byte (0x000xFF) to its UTF8
sequence. The array index is the raw byte value, each entry occupying 4 bytes:
- Byte 0 is the length of the UTF8 sequence (04)
- Bytes 1:len are the UTF8 bytes for the mapped Unicode scalar
The table therefore uses 256 * 4 bytes and is stored as a flat
char[1024] array, where entry i starts at offset i*4.
from_codepoint is the reverse map from Unicode scalar value to codepage byte,
also stored as a packed char[1024] array. It contains 256 entries of 4 bytes
each, where each 4byte chunk is interpreted as a littleendian uint
with the following packing scheme:
high 8 bits = codepage byte value (0x000xFF)
low 24 bits = Unicode scalar value (code point)
In other words:
entry = (byte_value << 24) | codepoint;
Ordering:
The 256 packed uint entries in from_codepoint are sorted by the low 24 bits
(code points). This allows binary search over Unicode scalar values without
a 64k reverselookup table. For any given code page, there are at most
256 mappings, so a log2(256) or 8 step search is sufficient.
*>
struct CodePageTable
{
char[1024] to_codepoint;
char[1024] from_codepoint;
}
enum CodePage : (String name, CodePageTable* table)
{
CP1250 { "cp1250", &codepage::CP1250 },
CP1251 { "cp1251", &codepage::CP1251 },
CP1252 { "cp1252", &codepage::CP1252 },
CP1253 { "cp1253", &codepage::CP1253 },
CP1254 { "cp1254", &codepage::CP1254 },
CP1255 { "cp1255", &codepage::CP1255 },
CP1256 { "cp1256", &codepage::CP1256 },
CP1257 { "cp1257", &codepage::CP1257 },
CP1258 { "cp1258", &codepage::CP1258 },
CP437 { "cp437", &codepage::CP437 },
CP737 { "cp737", &codepage::CP737 },
CP775 { "cp775", &codepage::CP775 },
CP850 { "cp850", &codepage::CP850 },
CP852 { "cp852", &codepage::CP852 },
CP855 { "cp855", &codepage::CP855 },
CP857 { "cp857", &codepage::CP857 },
CP860 { "cp860", &codepage::CP860 },
CP861 { "cp861", &codepage::CP861 },
CP862 { "cp862", &codepage::CP862 },
CP863 { "cp863", &codepage::CP863 },
CP864 { "cp864", &codepage::CP864 },
CP865 { "cp865", &codepage::CP865 },
CP866 { "cp866", &codepage::CP866 },
CP869 { "cp869", &codepage::CP869 },
CP874 { "cp874", &codepage::CP874 },
ISO_8859_1 { "iso-8859-1", &codepage::ISO_8859_1 },
ISO_8859_10 { "iso-8859-10", &codepage::ISO_8859_10 },
ISO_8859_11 { "iso-8859-11", &codepage::ISO_8859_11 },
ISO_8859_13 { "iso-8859-13", &codepage::ISO_8859_13 },
ISO_8859_14 { "iso-8859-14", &codepage::ISO_8859_14 },
ISO_8859_15 { "iso-8859-15", &codepage::ISO_8859_15 },
ISO_8859_16 { "iso-8859-16", &codepage::ISO_8859_16 },
ISO_8859_2 { "iso-8859-2", &codepage::ISO_8859_2 },
ISO_8859_3 { "iso-8859-3", &codepage::ISO_8859_3 },
ISO_8859_4 { "iso-8859-4", &codepage::ISO_8859_4 },
ISO_8859_5 { "iso-8859-5", &codepage::ISO_8859_5 },
ISO_8859_6 { "iso-8859-6", &codepage::ISO_8859_6 },
ISO_8859_7 { "iso-8859-7", &codepage::ISO_8859_7 },
ISO_8859_8 { "iso-8859-8", &codepage::ISO_8859_8 },
ISO_8859_9 { "iso-8859-9", &codepage::ISO_8859_9 },
US_ASCII { "us-ascii", &codepage::US_ASCII },
}
<*
Returns a CodePage for the given charset name.
@param [in] charset_name : "A name, case insensitive, using _ or - for separator"
@return "The CodePage for the name"
@return? NOT_FOUND : "If the charset is unknown or unsupported"
*>
fn CodePage? by_name(String charset_name) => @pool()
{
String name = charset_name.treplace("_","-");
name.convert_to_lower();
foreach (page : CodePage.values)
{
if (page.name == charset_name) return page;
}
return NOT_FOUND~;
}
fn String? decode(Allocator allocator, char[] src, CodePage code_page)
{
char[] dst = allocator::alloc_array(allocator, char, decode_len(src, code_page));
return decode_buffer(src, dst, code_page);
}
<*
Decode a code-page byte buffer into a UTF8 string.
@param src : "Input byte array in the given code page."
@param dst : "Destination output string in UTF-8."
@param code_page : "Code page for this encoding."
@return "String in UTF-8."
*>
fn String? decode_buffer(char[] src, char[] dst, CodePage code_page)
{
usz n = 0;
CodePageTable *table = code_page.table;
foreach (c: src)
{
usz pos = (usz)c * 4;
char len = table.to_codepoint[pos];
dst[n:len] = table.to_codepoint[pos+1:len];
n += len;
}
return (String)dst[:n];
}
fn char[]? encode(Allocator allocator, char[] src, CodePage code_page, char replacement = REPLACEMENT_CHAR)
{
char[] dst = allocator::alloc_array(allocator, char, encode_len(src));
return encode_buffer(src, dst, code_page, replacement);
}
const uint MASK @private = (1u << 24) - 1;
<*
Encode a UTF8 string into a singlebyte code page.
@param src : "Input byte array in UTF-8"
@param dst : "Destination output byte array in the target code page"
@param code_page : "Code page for this encoding."
@param replacement : "Byte to emit when Unicode scalar cannot be represented in the target code page."
@return "Byte array in the given code page."
*>
fn char[]? encode_buffer(char[] src, char[] dst, CodePage code_page, char replacement = REPLACEMENT_CHAR)
{
// Unpack the packed reverse table once into a local uint[256] view.
uint[256] from_map;
CodePageTable *table = code_page.table;
for (usz i = 0; i < 256; i++)
{
UIntLE *val = (UIntLE*)&table.from_codepoint[i * 4];
from_map[i] = mem::load(val, 1).val;
}
usz out = 0;
usz n = src.len;
for (usz i = 0; i < n; )
{
usz rem = n - i;
if (rem > 4) rem = 4;
Char32 codepoint = conv::utf8_to_char32(&src[i], &rem)!;
i += rem;
// Binary search for codepoint in low 24 bits of each entry.
// Returned index is between [0..from_map.len).
usz index = sort::binarysearch(from_map[..], (uint)codepoint, fn int(uint lhs, uint rhs) => (int)(lhs & MASK) - (int)(rhs & MASK));
uint entry = from_map[index];
if ((entry & MASK) == (uint)codepoint)
{
char b = (char)(entry >> 24);
dst[out++] = b;
}
else
{
dst[out++] = replacement;
}
}
return dst[:out];
}
<*
Compute the number of UTF8 bytes produced when decoding src with the given
code page table.
@param src : "Input byte array in the given code page."
@param code_page : "Code page for this encoding."
*>
fn usz decode_len(char[] src, CodePage code_page) @inline
{
usz n;
CodePageTable *table = code_page.table;
foreach (usz c: src) n += table.to_codepoint[c *4 ];
return n;
}
<*
Compute the number of output bytes produced when
encoding src from UTF8 to a singlebyte code page.
@param src : "Input byte array in UTF-8"
*>
fn usz encode_len(char[] src) @inline
{
return conv::utf8_codepoints((String)src);
}

File diff suppressed because it is too large Load Diff

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

355
lib/std/encoding/pem.c3 Normal file
View File

@@ -0,0 +1,355 @@
// Copyright (c) 2026 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// A module for encoding or decoding PEM blobs [mostly] in accordance with RFCs 1421-1424.
// This implementation retains a lot of flexibility in parsing input PEM blobs.
//
module std::encoding::pem;
import std::collections, std::encoding::base64;
<* A safe, default tag to use per RFC 1421's rules. *>
const String DEFAULT_TAG = "PRIVACY-ENHANCED MESSAGE";
<* The set of characters which are considered valid for PEM tags (which appear inside of Encapsulation Boundaries). *>
const AsciiCharset TAG_SET @local = ascii::@combine_sets(ascii::ALPHA_UPPER_SET, ascii::NUMBER_SET, ascii::@create_set(" _-/+()"));
<* The set of characters which are considered valid for optional PEM headers used. *>
const AsciiCharset HEADER_KEY_SET @local = ascii::@combine_sets(ascii::ALPHANUMERIC_SET, ascii::@create_set("!#$%&'*+-.^_`|~"));
<* All PEM Encapsulation Boundaries must use this delimiter to demarcate the PEM from its surrounding content, if any. *>
const String EB_DELIMITER @local = "-----";
<* All PEM blobs will start with this Encapsulation Boundary prefix. *>
const String PRE_EB_PREFIX @local = EB_DELIMITER +++ "BEGIN ";
<* All PEM blobs will terminate with this Encapsulation Boundary prefix. *>
const String POST_EB_PREFIX @local = EB_DELIMITER +++ "END ";
alias PemHeader = String[2];
<* Specify a set of possible PEM en/decoding faults. *>
faultdef
BODY_REQUIRED, // encoding: no body given (or too few of them)
HEADERS_REQUIRED, // encoding: no headers given (or too few of them)
HEADER_KEY_REQUIRED, // encoding: blank header keys are not allowed
HEADER_VALUE_REQUIRED, // encoding: blank header values are not allowed
INVALID_BODY, // decoding: invalid body, likely bad base64
INVALID_FORMAT, // decoding: invalid input formatting - no pre-EB or just plain wrong
INVALID_HEADER, // decoding: invalid headers
INVALID_HEADER_KEY, // decoding: invalid or empty header key
INVALID_PRE_EB, // decoding: invalid pre-EncapsBoundary BEFORE the PEM body
INVALID_POST_EB, // decoding: invalid post-EncapsBoundary AFTER the PEM body
INVALID_TAG, // decoding: invalid tag within an EB
MISMATCHED_TAG, // decoding: the tag from the pre-EB doesn't match that of the post-EB
MISSING_BODY, // decoding: missing PEM body base64
MISSING_HEADER_KEY, // decoding: the header is missing its key
MISSING_HEADER_VALUE, // decoding: the header is missing its value
MISSING_POST_EB, // decoding: no post-EB was found to close off the PEM
MISSING_TAG, // decoding: no tag was defined or parsed from the EB
TAG_REQUIRED, // encoding: no/empty tag given (or too few of them)
;
<* Represents a PEM object in memory, with a reference to the body data, tag value, and optional headers. *>
struct Pem
{
<* The allocator associated with the PEM's creation and destruction. *>
Allocator allocator;
<* A flexible 'tag' value used within the Encapsulation Boundary to denote the type of the PEM. *>
String tag;
<* A set of optional headers used to provide more context or information about the body of the PEM object. *>
LinkedHashMap{String, String} headers;
<* The core boy data of the PEM itself - the main values to be transmitted in this format. *>
char[] data;
}
<*
Create a new PEM object from a few inputs. Each input (i.e., tag, data, and headers) is copied to a new memory location.
The PEM object itself is not allocated in-memory, but is a simple container that points to each value that _is_.
Key-Value pairs for headers are provided in sequence as variadic arguments: `"key", "value", "key2", "value2", ...`
Created PEMs that are not temporary should be destroyed with `Pem.free`.
@param [&inout] allocator : "The allocator to use when copying the provided PEM object's fields."
@param [in] data : "The body data of the PEM."
@param [in] tag : "The tag value to use within the PEM's Encapsulation Boundary."
@return "A new PEM object."
*>
fn Pem create(Allocator allocator, char[] data, String tag, PemHeader... args)
{
Pem result = {
.allocator = allocator,
.tag = tag.copy(allocator),
.data = allocator::clone_slice(allocator, data),
};
result.headers.init(allocator, capacity: max(args.len, 16));
foreach (arg : args)
{
result.add_header(arg[0], arg[1]);
}
return result;
}
<*
Duplicate a `Pem` container and allocate copies of its members using the given allocator.
@param [&inout] allocator : "The allocator to use when copying the `Pem` members."
*>
fn Pem Pem.copy(&self, Allocator allocator)
{
Pem result = create(allocator, self.data, self.tag);
self.headers.@each(;String key, String value)
{
result.add_header(key, value);
};
return result;
}
<*
Safely destroys a `Pem` and deallocate all of its members. This should always be explicitly called when not using `tmem`.
*>
fn void Pem.free(&self)
{
mem::zero_volatile(self.data);
if (self.allocator != tmem)
{
self.headers.@each(;String key, String value)
{
allocator::free(self.allocator, value);
};
self.headers.free();
self.tag.free(self.allocator);
allocator::free(self.allocator, self.data);
}
mem::zero_volatile(@as_char_view(*self));
}
fn void Pem.add_header(&self, String key, String value)
{
(void)self.headers[key].free(self.allocator);
self.headers[key] = value.copy(self.allocator);
}
<*
Attempt to decode an input string into one or more `Pem` objects. If the input contains any non-PEM or otherwise
invalid data, then this will throw an error. Ideally, this function is used to decode PEM files explicitly, lest
the caller need to be sure they're only providing PEM data +/- some intermediate whitespace.
@param [&inout] allocator : "The allocator to use when creating the `Pem` outputs and members."
@param [in] input : "The string to parse one or more PEM blobs from."
@return "An array of decoded `Pem` objects, depending on how many were present in the input (separated optionally by whitespace)."
*>
fn Pem[]? decode(Allocator allocator, String input) => @pool()
{
List{Pem} pem_list;
pem_list.tinit();
String[] lines = input.treplace("\r\n", "\n").tsplit("\n");
foreach (&line : lines) *line = (*line).trim_right(); // remove any trailing whitespace as this can disrupt parsing (but shouldn't)
while (lines.len > 0)
{
pem_list.push(_decode_single(allocator, &lines)!);
while (lines.len > 0 && lines[0].trim().len == 0) lines = lines[1..]; // skip all empty lines in between or after PEM boundaries
}
return pem_list.to_array(allocator);
}
<*
INTERNAL ONLY: Decode one PEM at a time, from pre-EB to its discovered post-EB.
@param [&inout] allocator : "The allocator to use during decoding to return the result."
@param [&inout] lines_io : "A pointer to an input slice to modify as the single PEM is parsed from it."
*>
fn Pem? _decode_single(Allocator allocator, String[]* lines_io) @local
{
String[] lines = *lines_io; // copy to local var
Pem result = { .allocator = allocator };
result.headers.init(allocator);
defer catch result.free();
// Remove any preceding whitespace-only lines.
while (lines[0].trim().len == 0) lines = lines[1..];
if (lines.len < 3) return INVALID_FORMAT~; // at least 3 lines (pre-EB, body, post-EB) are always required
// The Pre-Encapsulation-Boundary must be of the format: -----BEGIN TAG-----, where "TAG" can be any upper-case identifier [A-Z_ -/]
String pre_eb = lines[0];
if (pre_eb[0:11] != PRE_EB_PREFIX || pre_eb[^5..] != EB_DELIMITER) return INVALID_PRE_EB~;
String tag = pre_eb[PRE_EB_PREFIX.len..^6];
if (!tag.len || !tag.trim().len) return MISSING_TAG~;
foreach (c : tag) if (!TAG_SET.contains(c)) return INVALID_TAG~;
result.tag = tag.copy(allocator);
// The Post-Encapsulation-Boundary is the same, but uses "END", and the extracted tag must match.
// Since the input might contain more than one PEM unit, we need to search for the ending encapsulation boundary dynamically.
String post_eb;
usz endl;
for SEARCH_EB: (endl = 1; endl < lines.len; endl++)
{
if (lines[endl].len > POST_EB_PREFIX.len && lines[endl][0:EB_DELIMITER.len] == EB_DELIMITER)
{
post_eb = lines[endl];
break SEARCH_EB;
}
}
if (!post_eb.len) return MISSING_POST_EB~;
if (post_eb[0:9] != POST_EB_PREFIX || post_eb[^5..] != EB_DELIMITER) return INVALID_POST_EB~;
String post_tag = post_eb[POST_EB_PREFIX.len..^6];
if (post_tag.len != tag.len || post_tag != tag) return MISMATCHED_TAG~;
// Now that the inner portion is decapsulated, tag is, strip off the boundaries.
*lines_io = lines[endl+1..]; // update the iterated slice of lines from the calling context - see: `decode`
lines = lines[1:endl-1];
// while there's a colon+space in the current line, we should assume that this is a key-value header pair
while (lines[0].contains(": "))
{
if (!HEADER_KEY_SET.contains(lines[0][0])) return INVALID_HEADER~; // not a multiline header? error out if the first char is not appropriate
String[] marker = lines; // temporary marker
usz span = 1; // how many lines this header spans
// Search for multi-line key-value pairs, indicated by a whitespace character beginning the current line.
for (lines = lines[1..]; lines[0].len > 0 && ascii::WHITESPACE_SET.contains(lines[0][0]); lines = lines[1..], span++);
foreach (&line : marker[:span]) *line = (*line).trim(); // always trim on both sides
String full_header = string::tjoin(marker[:span], " "); // join the lines with a single space
if (!full_header.contains(": ")) return INVALID_HEADER~; // reassert the presence of this
// Extract the key and value from the message, then validate.
// The header name should match a valid set of characters, but the value doesn't need to conform to anything other than existing
String[] kv = full_header.tsplit(": ", max: 2);
if (!kv[0].len) return MISSING_HEADER_KEY~;
if (!kv[1].len) return MISSING_HEADER_VALUE~;
foreach (c : kv[0]) if (!HEADER_KEY_SET.contains(c)) return INVALID_HEADER_KEY~;
result.add_header(kv[0], kv[1]); // finally, push the values
}
// if any headers were present, the line after the headers MUST BE EMPTY
if (result.headers.len() > 0)
{
if (lines[0].trim().len > 0) return INVALID_FORMAT~; // but we are forgiving about whitespace here
lines = lines[1..];
}
// Here, we assume lines[0] is the start of base64 data. This means there must be at least 1 line, of course.
if (lines.len < 1) return MISSING_BODY~;
// ... While the PEM format specifies a 64-character width on all but the last line of the base64 body,
// this parser doesn't need to be particular about that as long as the base64 is ok
// In this case, the rest of the lines in the set should be base64 and should decode accordingly
String to_decode = string::tjoin(lines, "");
if (!to_decode.len) return MISSING_BODY~; // paranoia
result.data = (base64::decode(allocator, to_decode) ?? INVALID_BODY~)!;
return result;
}
<*
Encodes a single `Pem` object into a new PEM-formatted string.
@param pem : "The pem object to encode"
@param [&inout] allocator : "The allocator to use for allocating the final encoded string."
*>
fn String? encode_pem(Pem pem, Allocator allocator, bool use_crlf = false)
{
if (!pem.data.len) return BODY_REQUIRED~;
if (!pem.tag.len) return TAG_REQUIRED~;
DString out;
out.tinit();
String line_ending = use_crlf ? "\r\n" : "\n";
@pool()
{
out.appendf(PRE_EB_PREFIX +++ "%s" +++ EB_DELIMITER +++ "%s", pem.tag, line_ending);
foreach KEY_ITER: (key : pem.headers.tkeys())
{
if (!key.len) return HEADER_KEY_REQUIRED~;
String value = pem.headers[key]!!;
if (!value.len) return HEADER_VALUE_REQUIRED~;
usz first_line_length = 64 - 2 - key.len;
if (value.len <= first_line_length)
{
out.appendf("%s: %s%s", key, value, line_ending);
continue KEY_ITER;
}
out.appendf("%s: %s%s", key, value[:first_line_length].trim(), line_ending);
value = value[first_line_length..];
while (value.len > 0)
{
out.appendf(" %s%s", (value.len >= 63 ? value[:63] : value[..]).trim(), line_ending);
value = value.len >= 63 ? value[63..] : {};
}
}
if (pem.headers.len() > 0) out.append(line_ending);
String body = base64::tencode(pem.data);
while (body.len > 0)
{
out.appendf("%s%s", body.len >= 64 ? body[:64] : body[..], line_ending);
body = body.len >= 64 ? body[64..] : {};
}
out.appendf(POST_EB_PREFIX +++ "%s" +++ EB_DELIMITER +++ "%s", pem.tag, line_ending);
};
return allocator == tmem ? out.str_view() : out.copy_str(allocator);
}
<*
Encodes a set of input data into a `String` containing the PEM-encoded data.
@param [&inout] allocator : "The allocator to use when creating the final output string."
@param [in] data : "The body data for the output PEM."
@param [in] tag : "The tag "
*>
fn String? encode(Allocator allocator, char[] data, String tag, PemHeader... headers, bool use_crlf = false) => @pool()
{
if (!data.len) return BODY_REQUIRED~;
return encode_pem(create(tmem, data, tag, ...headers), allocator, use_crlf);
}
<*
Encode many inputs to a single output string that represents chained/sequential PEM objects in the order they were provided.
The length of the `bodies` and `tags` array must be equal.
If headers are provided, they must be arrays of String objects, matching both the number of tags and the number of bodies.
@param [&inout] allocator : "The allocator to use when creating the final output string."
@param [in] bodies : "An ordered array of binary arrays, each representing the body of a single PEM."
@param [in] tags : "An ordered array of tag strings, each representing the tag of a single PEM."
@return "A new `String`, allocated with `allocator`, that contains all PEM objects in the order they were given."
*>
fn String? encode_many(Allocator allocator, char[][] bodies, String[] tags, PemHeader[]... pem_headers, bool use_crlf = false)
{
usz entries = max(bodies.len, tags.len, pem_headers.len);
switch
{
case bodies.len < entries: return BODY_REQUIRED~;
case tags.len < entries: return TAG_REQUIRED~;
case pem_headers.len > 0 && pem_headers.len < entries: return HEADERS_REQUIRED~;
}
DString out;
out.tinit();
if (!pem_headers.len)
{
foreach (x, body : bodies) @pool()
{
out.append(encode(tmem, body, tags[x], use_crlf: use_crlf)!);
};
}
else
{
foreach (i, headers : pem_headers) @pool()
{
out.append(encode(tmem, bodies[i], tags[i], ...headers, use_crlf: use_crlf)!);
};
}
return allocator == tmem ? out.str_view() : out.copy_str(allocator);
}

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
@@ -7,7 +7,7 @@ struct DelayedSchedulerEvent @local
Clock execution_time;
}
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other)
{
switch
{
@@ -43,7 +43,7 @@ macro void FrameScheduler.@destroy(&self; @destruct(Event e))
self.events.free();
self.pending_events.free();
self.delayed_events.free();
(void)self.mtx.destroy();
self.mtx.destroy();
}
fn void FrameScheduler.queue_delayed_event(&self, Event event, Duration delay)
@@ -76,7 +76,7 @@ fn Event? FrameScheduler.pop_event(&self)
while (true)
{
if (try event = self.events.pop()) return event;
if (!@atomic_load(self.pending)) return NO_MORE_ELEMENT?;
if (!@atomic_load(self.pending)) return NO_MORE_ELEMENT~;
self.mtx.@in_lock()
{
self.events.add_all(&self.pending_events);
@@ -88,7 +88,7 @@ fn Event? FrameScheduler.pop_event(&self)
self.events.push(self.delayed_events.pop()!!);
}
@atomic_store(self.pending, self.delayed_events.len() > 0);
if (!self.events.len()) return NO_MORE_ELEMENT?;
if (!self.events.len()) return NO_MORE_ELEMENT~;
};
}
}

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

View File

@@ -4,7 +4,8 @@
module std::hash::adler32;
const uint ADLER_CONST @private = 65521;
const uint ADLER32_CONST @private = 65521;
struct Adler32
{
@@ -19,19 +20,79 @@ fn void Adler32.init(&self)
fn void Adler32.updatec(&self, char c)
{
self.a = (self.a + c) % ADLER_CONST;
self.b = (self.b + self.a) % ADLER_CONST;
self.a = (self.a + c) % ADLER32_CONST;
self.b = (self.b + self.a) % ADLER32_CONST;
}
fn void Adler32.update(&self, char[] data)
{
// Safe chunking constant which is optimized for L1 cache on most systems 32768 (32 KB).
// 0x8000 ~ (2^32 / 65521 / 2).
// The division is done so that we are guarenteed to never overflow.
const uint SAFE_CHUNKING_SIZE = 0x8000;
// In order
const uint UNROLL_SIZE = 8;
uint a = self.a;
uint b = self.b;
foreach (char x : data)
char* buf = data;
usz len = data.len;
// Align pointer traversing buffer pointer to the unroll alignment size.
if (len % UNROLL_SIZE != 0)
{
a = (a + x) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
do
{
a += *buf;
b += a;
buf++;
len--;
} while (len % UNROLL_SIZE != 0);
if (a >= ADLER32_CONST)
{
a -= ADLER32_CONST;
}
b %= ADLER32_CONST;
}
// Calculate rest of adler32 checksum.
while (len > 0)
{
$for var $i = 0; $i < UNROLL_SIZE; $i++:
a += buf[$i]; b += a;
$endfor
len -= UNROLL_SIZE;
buf += UNROLL_SIZE;
// Even with 8 max value (0xFF) bytes being additioned to a (0xFF * 8 = 2040 for worst case).
// There is no chance that a will be > 2 * ADLER32_CONST, so modulo is not needed here.
// So its more performant to use subtraction.
if (a >= ADLER32_CONST)
{
a -= ADLER32_CONST;
}
// We need to periodically chunk b because it accumulates a which is a sum, so it grows rapidly.
// So every 4K of bytes we modulo in order to prevent uint integer overflow.
if (len % SAFE_CHUNKING_SIZE == 0)
{
b %= ADLER32_CONST;
}
}
// No need to explicitely modulo after loop end with ADLER32_CONST.
// As a and b are guarenteed to be under ADLER32_CONST.
// Do assert on debug.
assert(a < ADLER32_CONST);
assert(b < ADLER32_CONST);
*self = { a, b };
}
@@ -42,12 +103,10 @@ fn uint Adler32.final(&self)
fn uint hash(char[] data)
{
uint a = 1;
uint b = 0;
foreach (char x : data)
{
a = (a + x) % ADLER_CONST;
b = (b + a) % ADLER_CONST;
}
return (b << 16) | a;
Adler32 adler;
adler.init();
adler.update(data);
return adler.final();
}

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

@@ -0,0 +1,387 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// This is based on the original BLAKE2 reference implementation, but it's been
// significantly changed. You can really see how C3's features shine over the
// original C source.
//
// You'll note that this may have been better done with generic modules. I'm not
// an expert, but I'd argue that it's unnecessary for this particular algorithm.
// There are only two important versions of the algorithm to implement in a "common"
// (generic) way, and the output size of each type is controlled rather naturally.
//
module std::hash::blake2;
// Common hash output sizes. The library is NOT limited to these six output sizes, but
// these instead stand in for what would have been "magic numbers" throughout the code.
const SIZE_128 = 16;
const SIZE_160 = 20;
const SIZE_224 = 28;
const SIZE_256 = 32;
const SIZE_384 = 48;
const SIZE_512 = 64;
macro @g(r, m, $s, i, #a, #b, #c, #d) @local
{
// I really dislike using these conditional one-liners, but... it's the only difference between 2s and 2b besides buffer sizes (mainly).
#a += #b + m[$s[r][2*i+0]];
#d = (#d ^ #a).rotr($sizeof(#a) == ulong.sizeof ??? 32 : 16);
#c += #d;
#b = (#b ^ #c).rotr($sizeof(#a) == ulong.sizeof ??? 24 : 12);
#a += #b + m[$s[r][2*i+1]];
#d = (#d ^ #a).rotr($sizeof(#a) == ulong.sizeof ??? 16 : 8);
#c += #d;
#b = (#b ^ #c).rotr($sizeof(#a) == ulong.sizeof ??? 63 : 7);
}
macro @round(r, m, $s, #v) @local
{
@g(r, m, $s, 0, #v[ 0], #v[ 4], #v[ 8], #v[12]);
@g(r, m, $s, 1, #v[ 1], #v[ 5], #v[ 9], #v[13]);
@g(r, m, $s, 2, #v[ 2], #v[ 6], #v[10], #v[14]);
@g(r, m, $s, 3, #v[ 3], #v[ 7], #v[11], #v[15]);
@g(r, m, $s, 4, #v[ 0], #v[ 5], #v[10], #v[15]);
@g(r, m, $s, 5, #v[ 1], #v[ 6], #v[11], #v[12]);
@g(r, m, $s, 6, #v[ 2], #v[ 7], #v[ 8], #v[13]);
@g(r, m, $s, 7, #v[ 3], #v[ 4], #v[ 9], #v[14]);
}
macro common_compress(instance, $rounds, $iv, $sigma, block) @local
{
$typeof(instance.h[0])[16] m, v;
((char*)&m)[:$sizeof(block)] = block[..];
v[:8] = instance.h[..];
v[ 8] = $iv[0];
v[ 9] = $iv[1];
v[10] = $iv[2];
v[11] = $iv[3];
v[12] = $iv[4] ^ instance.t[0];
v[13] = $iv[5] ^ instance.t[1];
v[14] = $iv[6] ^ instance.f[0];
v[15] = $iv[7] ^ instance.f[1];
$for usz $i = 0; $i < $rounds; $i++:
@round($i, m, $sigma, v);
$endfor
$for usz $i = 0; $i < 8; $i++:
instance.h[$i] ^= v[$i] ^ v[$i + 8];
$endfor
}
macro _add_ctr(instance, usz amount) @local
{
instance.t[0] += ($typeof(instance.t[0]))amount;
instance.t[1] += ($typeof(instance.t[0]))(instance.t[0] < amount); // adds 1 on overflow of [0]
}
macro common_init(instance, $ParamType, $iv, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {}) @local
{
mem::zero_volatile(@as_char_view(*instance)); // explicitly because habits around hash init usually involve @noinit
instance.h[..] = $iv[..];
instance.outlen = out_len;
$ParamType p = {
.digest_length = (char)out_len,
.key_length = (char)key.len,
.fanout = 1,
.depth = 1,
};
if (salt.len) p.salt[:salt.len] = salt[..];
if (personal.len) p.personal[:personal.len] = personal[..];
array::@zip_into(((char*)&instance.h)[:$sizeof(p)], ((char*)&p)[:$sizeof(p)], fn (a, b) => a ^ b); // bytes(self.h) ^= bytes(p)
if (key.len)
{
char[$sizeof($iv[0])*16] dummy = {};
dummy[:key.len] = key[..];
instance.update(dummy[..]); // consume a FULL block
mem::zero_volatile(dummy[..]); // do not optimize clearing this from the stack
}
}
macro common_update(instance, $block_size, char[] data) @local
{
if (@unlikely(!data.len)) return;
usz fill = $block_size - instance.buflen;
if (data.len > fill)
{
instance.buf[instance.buflen:fill] = data[:fill];
instance.buflen = 0;
_add_ctr(instance, $block_size);
instance._compress(instance.buf);
data = data[fill..];
for (; data.len > $block_size; data = data[$block_size..])
{
_add_ctr(instance, $block_size);
instance._compress(data[:$block_size]);
}
}
instance.buf[instance.buflen:data.len] = data[..];
instance.buflen += data.len;
}
macro common_final(instance, $output_length) @local
{
char[$output_length] result = {};
if ($output_length != instance.outlen) return result;
_add_ctr(instance, instance.buflen);
if (instance.f[0]) return result; // technically an error return
var $max = $typeof(instance.h[0]).max;
if (instance.last_node) instance.f[1] = $max;
instance.f[0] = $max;
mem::zero_volatile(instance.buf[instance.buflen..]); // pad buffer with zeroes
instance._compress(instance.buf);
defer mem::zero_volatile(@as_char_view(*instance)); // destroy the current context implicitly
result[:instance.outlen] = @as_char_view(instance.h)[:instance.outlen];
return result;
}
// ======================================================================================
// BEGIN Blake2b contents. Do not separate this from Blake2s: there's not really a point.
//
const BLAKE2B_BLOCKBYTES @local = 128;
const BLAKE2B_OUTBYTES @local = 64;
const BLAKE2B_KEYBYTES @local = 64;
const BLAKE2B_SALTBYTES @local = 16;
const BLAKE2B_PERSONALBYTES @local = 16;
const ulong[8] BLAKE2B_IV @local = {
0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
0x510e527fade682d1, 0x9b05688c2b3e6c1f, 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
};
const char[16][12] BLAKE2B_SIGMA @local = {
x'000102030405060708090a0b0c0d0e0f',
x'0e0a0408090f0d06010c00020b070503',
x'0b080c0005020f0d0a0e030607010904',
x'070903010d0c0b0e0206050a04000f08',
x'0900050702040a0f0e010b0c0608030d',
x'020c060a000b0803040d07050f0e0109',
x'0c05010f0e0d040a000706030902080b',
x'0d0b070e0c01030905000f040806020a',
x'060f0e090b0300080c020d0701040a05',
x'0a020804070601050f0b090e030c0d00',
x'000102030405060708090a0b0c0d0e0f',
x'0e0a0408090f0d06010c00020b070503',
};
struct Blake2b
{
ulong[8] h;
ulong[2] t;
ulong[2] f;
char[BLAKE2B_BLOCKBYTES] buf;
usz buflen;
usz outlen;
char last_node;
}
struct Blake2bParam @packed @local
{
char digest_length; /* 1 */
char key_length; /* 2 */
char fanout; /* 3 */
char depth; /* 4 */
uint leaf_length; /* 8 */
uint node_offset; /* 12 */
uint xof_length; /* 16 */
char node_depth; /* 17 */
char inner_length; /* 18 */
char[14] reserved; /* 32 */
char[BLAKE2B_SALTBYTES] salt; /* 48 */
char[BLAKE2B_PERSONALBYTES] personal; /* 64 */
}
<*
@require $defined(data[0]) &&& $typeof(data[0]) == char : "Input data must be a char slice, char array, or string value."
*>
macro blake2b_hash($out_len, data, char[] key = {}, char[] salt = {})
{
Blake2b b @noinit;
b.init($out_len, key, salt);
b.update(data[..]);
return b.final($out_len);
}
alias b = blake2b_hash;
// See RFC 7693, Section 4 for common parameter sets.
macro blake2b_224(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_224, data, key, salt);
macro blake2b_256(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_256, data, key, salt);
macro blake2b_384(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_384, data, key, salt);
macro blake2b_512(data, char[] key = {}, char[] salt = {}) => blake2b_hash(SIZE_512, data, key, salt);
alias b_224 = blake2b_224;
alias b_256 = blake2b_256;
alias b_384 = blake2b_384;
alias b_512 = blake2b_512;
<*
Blake2b initialization method. Presents various options
@param out_len : "The desired output length from the hash function."
@param[in] key : "An optional key value to use (keys the entire hash value to give HMAC-like functionality)."
@param[in] salt : "An optional salt value to use when generating the hash."
@param[in] personal : "An optional personalization value to use when generating the hash."
@require out_len > 0 && out_len <= BLAKE2B_OUTBYTES : "Blake2 output length must be within the proper range."
@require !key.ptr || (key.len > 0 && key.len <= BLAKE2B_KEYBYTES) : "A specified key's length must be within the proper range."
@require !salt.ptr || (salt.len > 0 && salt.len <= BLAKE2B_SALTBYTES) : "A specified salt's length must be within the proper range."
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
*>
fn void Blake2b.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
=> common_init(self, Blake2bParam, BLAKE2B_IV, out_len, key, salt, personal);
<*
Core compression inline function for Blake2b.
*>
fn void Blake2b._compress(&self, char[BLAKE2B_BLOCKBYTES] block) @inline
=> common_compress(self, 12, BLAKE2B_IV, BLAKE2B_SIGMA, block);
<*
Add more data to the hash context or stream.
@param[in] data : "The data to ingest into the hash context."
*>
fn void Blake2b.update(&self, char[] data)
=> common_update(self, BLAKE2B_BLOCKBYTES, data);
<*
Finalize the hash context and return the hash result at the given size.
@param $output_length : "The length of the output array which is returned by value, instead of as a slice."
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
*>
macro char[*] Blake2b.final(&self, $output_length) => _blake2b_final{$output_length}(self);
fn char[OUTPUT_LENGTH] _blake2b_final(Blake2b* self) <OUTPUT_LENGTH> @local
{
return common_final(self, OUTPUT_LENGTH);
}
// ======================================================================================
// BEGIN Blake2s contents. Do not separate this from Blake2b: there's not really a point.
//
const BLAKE2S_BLOCKBYTES @local = BLAKE2B_BLOCKBYTES / 2;
const BLAKE2S_OUTBYTES @local = BLAKE2B_OUTBYTES / 2;
const BLAKE2S_KEYBYTES @local = BLAKE2B_KEYBYTES / 2;
const BLAKE2S_SALTBYTES @local = BLAKE2B_SALTBYTES / 2;
const BLAKE2S_PERSONALBYTES @local = BLAKE2B_PERSONALBYTES / 2;
const uint[8] BLAKE2S_IV @local = { 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 };
const char[16][10] BLAKE2S_SIGMA @local = {
x'000102030405060708090a0b0c0d0e0f',
x'0e0a0408090f0d06010c00020b070503',
x'0b080c0005020f0d0a0e030607010904',
x'070903010d0c0b0e0206050a04000f08',
x'0900050702040a0f0e010b0c0608030d',
x'020c060a000b0803040d07050f0e0109',
x'0c05010f0e0d040a000706030902080b',
x'0d0b070e0c01030905000f040806020a',
x'060f0e090b0300080c020d0701040a05',
x'0a020804070601050f0b090e030c0d00',
};
struct Blake2s
{
uint[8] h;
uint[2] t;
uint[2] f;
char[BLAKE2S_BLOCKBYTES] buf;
usz buflen;
usz outlen;
char last_node;
}
struct Blake2sParam @packed @local
{
char digest_length; /* 1 */
char key_length; /* 2 */
char fanout; /* 3 */
char depth; /* 4 */
uint leaf_length; /* 8 */
uint node_offset; /* 12 */
ushort xof_length; /* 14 */
char node_depth; /* 15 */
char inner_length; /* 16 */
char[BLAKE2S_SALTBYTES] salt; /* 24 */
char[BLAKE2S_PERSONALBYTES] personal; /* 32 */
}
<*
@require $defined(data[0]) &&& $typeof(data[0]) == char : "Input data must be a char slice, char array, or string value."
*>
macro blake2s_hash($out_len, data, char[] key = {}, char[] salt = {})
{
Blake2s b @noinit;
b.init($out_len, key, salt);
b.update(data[..]);
return b.final($out_len);
}
alias s = blake2s_hash;
// See RFC 7693, Section 4 for common parameter sets.
macro blake2s_128(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_128, data, key, salt);
macro blake2s_160(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_160, data, key, salt);
macro blake2s_224(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_224, data, key, salt);
macro blake2s_256(data, char[] key = {}, char[] salt = {}) => blake2s_hash(SIZE_256, data, key, salt);
alias s_128 = blake2s_128;
alias s_160 = blake2s_160;
alias s_224 = blake2s_224;
alias s_256 = blake2s_256;
<*
Blake2s initialization method. Presents various options
@param out_len : "The desired output length from the hash function."
@param[in] key : "An optional key value to use (keys the entire hash value to give HMAC-like functionality)."
@param[in] salt : "An optional salt value to use when generating the hash."
@param[in] personal : "An optional personalization value to use when generating the hash."
@require out_len > 0 && out_len <= BLAKE2S_OUTBYTES : "Blake2 output length must be within the proper range."
@require !key.ptr || (key.len > 0 && key.len <= BLAKE2S_KEYBYTES) : "A specified key's length must be within the proper range."
@require !salt.ptr || (salt.len > 0 && salt.len <= BLAKE2S_SALTBYTES) : "A specified salt's length must be within the proper range."
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
*>
fn void Blake2s.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
=> common_init(self, Blake2sParam, BLAKE2S_IV, out_len, key, salt, personal);
<*
Core compression inline function for Blake2s.
*>
fn void Blake2s._compress(&self, char[BLAKE2S_BLOCKBYTES] block) @inline
=> common_compress(self, 10, BLAKE2S_IV, BLAKE2S_SIGMA, block);
<*
Add more data to the hash context or stream.
@param[in] data : "The data to ingest into the hash context."
*>
fn void Blake2s.update(&self, char[] data)
=> common_update(self, BLAKE2S_BLOCKBYTES, data);
<*
Finalize the hash context and return the hash result at the given size.
@param $output_length : "The length of the output array which is returned by value, instead of as a slice."
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
*>
macro char[*] Blake2s.final(&self, $output_length)
=> common_final(self, $output_length);

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

@@ -0,0 +1,748 @@
// Copyright (c) 2025-2026 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// This is based on the original BLAKE3 reference implementation:
// https://github.com/BLAKE3-team/BLAKE3/blob/master
//
module std::hash::blake3;
import std::thread;
const BLOCK_SIZE = 64;
const CHUNK_SIZE = 1024;
const KEY_SIZE = 32;
const KEY_SIZE_WORDS = KEY_SIZE / uint.sizeof;
const OUT_SIZE = 32;
const MAX_DEPTH = 54;
const uint[8] IV = {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
};
const char[16][7] MESSAGE_SCHEDULE = {
x'000102030405060708090a0b0c0d0e0f',
x'0206030a0700040d010b0c05090e0f08',
x'03040a0c0d02070e060509000b0f0801',
x'0a070c090e030d0f04000b0205080106',
x'0c0d090b0f0a0e080702050300010604',
x'090e0b05080c0f010d03000a02060407',
x'0b0f0500010908060e0a020c0304070d',
};
// Get feature-based optimization options.
// For now, none of these are used until there's a chance to explore BLAKE3's (necessary) vectorization optimizations.
//
<* When true, force the use of slow-but-portable BLAKE3 functions. Do not vectorize the hash function. *>
const FORCE_PORTABLE = true; //$feature(BLAKE3_FORCE_PORTABLE); // this is statically set to TRUE for now
<* AARCH64: When not big-endian, use Neon. *>
const USE_NEON = !FORCE_PORTABLE &&& (env::AARCH64 &&& !env::BIG_ENDIAN);
<* Bundling some architecture booleans into one. *>
const IS_X86 = !FORCE_PORTABLE &&& (env::X86_64 ||| env::X86);
<*
The maximum possible degree of parallelization based on the current architecture.
This doesn't represent the ACTUAL degree available.
*>
const MAX_SIMD_DEGREE = IS_X86 ??? 16 : (USE_NEON ??? 4 : 1);
<* There are cases in BLAKE3 where, at compile-time, it's necessary to easily get the max degree, or a minimum of 2. *>
const MAX_SIMD_DEGREE_OR_2 = @max(MAX_SIMD_DEGREE, 2);
<* Always set to true once BLAKE3 caches some initial CPU details. *>
bool cpuinfo_initd @local = false;
<*
Cache some information at runtime about the current processor and platform, as needed for optimizations.
*>
fn void init_blake3() @local
{
static OnceFlag run_once;
run_once.call(fn() {
$if IS_X86:
cpudetect::x86_initialize_cpu_features(); // query all x86 feature flags, one time
$endif
cpuinfo_initd = true;
});
}
<* Check whether a given CPU flag is set (x86/x86_64 only). *>
macro bool @check_cpu_flag(X86Feature f) @local @if(IS_X86)
=> !!(cpudetect::x86_features & f.ordinal);
<*
Return the actual SIMD degree of the processor at runtime.
*>
macro @simd_degree() @local
{
if (!cpuinfo_initd) init_blake3();
assert(cpuinfo_initd == true, "Failed to run required BLAKE3 initializations.");
$switch:
$case IS_X86:
if (@check_cpu_flag(AVX512F) && @check_cpu_flag(AVX512VL)) return 16;
if (@check_cpu_flag(AVX2)) return 8;
if (@check_cpu_flag(SSE4_1) || @check_cpu_flag(SSE2)) return 4;
$case USE_NEON:
return 4;
$endswitch
return 1;
}
<* Flags used during hash computation based on its state. *>
constdef Blake3Flags : inline char
{
CHUNK_START = 1 << 0,
CHUNK_END = 1 << 1,
PARENT = 1 << 2,
ROOT = 1 << 3,
KEYED_HASH = 1 << 4,
DERIVE_KEY_CONTEXT = 1 << 5,
DERIVE_KEY_MATERIAL = 1 << 6,
}
struct Blake3ChunkState @local
{
uint[8] cv;
ulong chunk_counter;
char[BLOCK_SIZE] buf;
char buf_len;
char blocks_compressed;
char flags;
}
struct Blake3Output @local
{
uint[KEY_SIZE_WORDS] input_cv;
ulong counter;
char[BLOCK_SIZE] block;
char block_len;
char flags;
}
struct Blake3
{
uint[KEY_SIZE_WORDS] key;
Blake3ChunkState chunk;
char cv_stack_len;
char[(MAX_DEPTH + 1) * OUT_SIZE] cv_stack;
}
<*
Generate an XOF hash based on the given inputs.
Consider the output hash w/ `seek = 0` and `$out_size = 41`:
```
2cc39783c223154fea8dfb7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d905630c8be290dfcf3
```
Computing with the same input `key` and input `data`, but with a `seek = 3` and `$out_size = 8` yields:
```
83c223154fea8dfb
which is a slice cut out from the above hash:
2cc397 [83c223154fea8dfb] 7c1b1660f2ac2dcbd1c1de8277b0b0dd39b7e50d7d905630c8be290dfcf3
```
In this way, the XOF primitive that BLAKE3 is built from allows the hash output to be a potentially
limitless result that one may slice to their liking using the right parameters.
@param [in] data : "The data to hash."
@param [in] key : "An optional 32-byte key to turn the result into a keyed hash."
@param seek : "An optional value specifying the offset into the XOF's yield where the resultant hash should begin."
@param $out_size : "An optional value specifying the desired length to slice from the XOF's yield."
@return "The hash as a character array of `$out_size` bytes."
@require !key.len || key.len == KEY_SIZE : "Key value must be empty or exactly 32 bytes."
@require $out_size > 0 : "You cannot use a zero $out_size."
*>
macro char[*] hash(char[] data, char[] key = {}, usz seek = 0, usz $out_size = 32)
{
char[$out_size] result;
Blake3 b @noinit;
defer b.destroy();
b.init(key);
b.update(data);
b.final(result[..], $out_size, seek);
return result;
}
<*
Generate a hash from a context string. This call allows one to use the "context" to
auto-generate keying material for the resultant hash value. Effectively, this allows for
hashes made from data with completely variable-length keys, rather than having a key fixed
to 32 bytes. The 'context' nomenclature is from BLAKE3 itself, not my naming.
@param [in] data : "The data to hash."
@param [in] context : "An optional key to turn the result into a keyed hash."
@param seek : "An optional value specifying the offset into the XOF's yield where the resultant hash should begin."
@param $out_size : "An optional value specifying the desired length to slice from the XOF's yield."
@return "The context-based hash as a character array of `$out_size` bytes."
@require $out_size > 0 : "You cannot use a zero $out_size."
*>
macro char[*] ctx_hash(char[] data, char[] context, usz seek = 0, usz $out_size = 32)
{
char[$out_size] result;
Blake3 b = new_from_context(context);
defer b.destroy();
b.update(data);
b.final(result[..], $out_size, seek);
return result;
}
<*
Generate a new Blake3 hashing structure from the given context string. The context string
acts as a variable-length key to seed the new hash structure, and makes it ready to ingest
incoming data with `update`.
@param [in] context : "The context byte array used to seed the returned Blake3 context."
*>
macro Blake3 new_from_context(char[] context)
{
char[KEY_SIZE] context_based_key;
defer mem::zero_volatile(context_based_key[..]);
Blake3 key_from_ctx @noinit;
defer key_from_ctx.destroy();
key_from_ctx.init(explicit_flags: Blake3Flags.DERIVE_KEY_CONTEXT);
key_from_ctx.update(context);
key_from_ctx.final(context_based_key[..], KEY_SIZE);
Blake3 b @noinit;
b.init(key: context_based_key[..], explicit_flags: Blake3Flags.DERIVE_KEY_MATERIAL);
return b;
}
<*
Initialize a BLAKE3 context.
@param [in] key : "An optional key initializer to use."
@require !key.len || key.len == KEY_SIZE : "An explicit initialization key must be of KEY_SIZE (32 bytes)."
*>
fn void Blake3.init(&self, char[] key = {}, char explicit_flags = 0)
{
mem::zero_volatile(@as_char_view(*self));
if (key.len)
{
foreach (i, &w : self.key) *w = mem::load((uint*)&key[i * $sizeof(self.key[0])], 1);
if (!explicit_flags) explicit_flags = Blake3Flags.KEYED_HASH;
}
else
{
self.key[..] = IV[..];
}
self.chunk.init(self.key[..], explicit_flags);
}
<*
Reset the state of the hashing context, in case it should be reused without reloading the key value.
*>
fn void _reset(Blake3* self) @local @inline
{
self.chunk.reset(self.key[..], 0);
self.cv_stack_len = 0;
}
<*
Private function to merge tree results.
*>
fn void _merge_cv_stack(Blake3* self, ulong total_len) @local @inline
{
usz post_merge_stack_len = (usz)@popcnt(total_len);
for (; self.cv_stack_len > post_merge_stack_len; self.cv_stack_len--)
{
char* parent_node = &self.cv_stack[(self.cv_stack_len - 2) * OUT_SIZE];
Blake3Output o = parent_output(parent_node, self.key[..], self.chunk.flags);
o.chaining_value(parent_node);
}
}
<*
Private function to add a new tree onto the stack.
*>
fn void Blake3.push_cv(&self, char* new_cv, ulong chunk_counter) @inline
{
_merge_cv_stack(self, chunk_counter);
self.cv_stack[self.cv_stack_len * OUT_SIZE : OUT_SIZE] = new_cv[:OUT_SIZE];
self.cv_stack_len++;
}
<*
Update the hash context by consuming incoming data.
@param [in] input : "The slice of new data to digest."
@param use_tbb : "Should remain `false` until other BLAKE3 optimizations are set up."
*>
fn void Blake3.update(&self, char[] input, bool use_tbb = false)
{
if (!input.len) return;
if (self.chunk.len() > 0)
{
usz take = min(CHUNK_SIZE - self.chunk.len(), input.len);
self.chunk.update(input[:take]);
input = input[take..];
if (!input.len) return;
char[KEY_SIZE] chunk_cv;
Blake3Output o = self.chunk.output();
o.chaining_value(&chunk_cv);
self.push_cv(&chunk_cv, self.chunk.chunk_counter);
self.chunk.reset(self.key[..], self.chunk.chunk_counter + 1);
}
while (input.len > CHUNK_SIZE)
{
usz subtree_len = @round_down_to_power_of_2(input.len);
ulong count_so_far = self.chunk.chunk_counter * CHUNK_SIZE;
while ((((ulong)(subtree_len - 1)) & count_so_far) != 0) subtree_len /= 2;
ulong subtree_chunks = subtree_len / CHUNK_SIZE;
if (subtree_len <= CHUNK_SIZE)
{
Blake3ChunkState chunk_state;
chunk_state.init(self.key[..], self.chunk.flags);
chunk_state.chunk_counter = self.chunk.chunk_counter;
chunk_state.update(input[:subtree_len]);
char[OUT_SIZE] cv;
Blake3Output o = chunk_state.output();
o.chaining_value(&cv);
self.push_cv(&cv, chunk_state.chunk_counter);
}
else
{
char[2 * OUT_SIZE] cv_pair;
compress_subtree_to_parent_node(input[:subtree_len], self.key[..], self.chunk.chunk_counter, self.chunk.flags, cv_pair[..], use_tbb);
self.push_cv(&cv_pair[0], self.chunk.chunk_counter);
self.push_cv(&cv_pair[OUT_SIZE], self.chunk.chunk_counter + (subtree_chunks / 2));
}
self.chunk.chunk_counter += subtree_chunks;
input = input[subtree_len..];
}
if (input.len > 0)
{
self.chunk.update(input);
_merge_cv_stack(self, self.chunk.chunk_counter);
}
}
<*
Yield the results of the hash into a specified output buffer, at the specified length.
Note that the `into` slice does not need to be properly cut to receive hash results; it
just needs to be wide enough to accommodate `into_len` yielded bytes from the XOF.
@param [in] into : "The storage buffer for the output hash value. Must be >= `into_len` bytes."
@param into_len : "How many bytes to receive from the XOF/hash output."
@param seek : "How far into the XOF's yield to begin the stored byte sequence."
@require into.len >= into_len : "The requested output size must be equal to or less than the size of the output slice."
*>
fn void Blake3.final(&self, char[] into, usz into_len, usz seek = 0)
{
if (!into_len) return;
if (!self.cv_stack_len)
{
Blake3Output o = self.chunk.output();
o.root_bytes(seek, into[:into_len]);
return;
}
Blake3Output o @noinit;
usz cvs_remaining;
if (self.chunk.len() > 0)
{
cvs_remaining = self.cv_stack_len;
o = self.chunk.output();
}
else
{
cvs_remaining = (usz)self.cv_stack_len - 2;
o = parent_output(&self.cv_stack[cvs_remaining * KEY_SIZE], self.key[..], self.chunk.flags);
}
while (cvs_remaining > 0)
{
char[BLOCK_SIZE] parent_block;
cvs_remaining--;
parent_block[:32] = self.cv_stack[cvs_remaining * 32 : 32];
o.chaining_value(&parent_block[32]);
o = parent_output(&parent_block, self.key[..], self.chunk.flags);
}
o.root_bytes(seek, into[:into_len]);
}
<*
Destroy a BLAKE3 hashing context.
*>
fn void Blake3.destroy(&self) @inline
{
mem::zero_volatile(@as_char_view(*self));
}
<*
Initialize a BLAKE3 chunk state.
@param [in] key
@param flags
*>
fn void Blake3ChunkState.init(&self, uint[] key, char flags) @inline
{
mem::zero_volatile(@as_char_view(*self));
self.cv[..] = key[..];
self.flags = flags;
}
<*
Reset a BLAKE3 chunk state.
@param [in] key
@param chunk_counter
*>
fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @inline
{
self.init(key, self.flags); // maintain its own flags
self.chunk_counter = chunk_counter; // update chunk counter
}
<*
Get bytes length of consumed data.
*>
fn usz Blake3ChunkState.len(&self) @operator(len) @inline
=> (BLOCK_SIZE * (usz)self.blocks_compressed) + (usz)self.buf_len;
<*
Ingest an amount of bytes into the chunk's buffer. NOTE: Doesn't check for underflow.
@param [in] data : "Data to ingest."
*>
fn usz Blake3ChunkState.fill_buf(&self, char[] data) @inline
{
usz take = min(BLOCK_SIZE - (usz)self.buf_len, data.len);
self.buf[self.buf_len:take] = data[:take];
self.buf_len += (char)take;
return take;
}
<*
Determine whether to set the CHUNK_START flag.
*>
fn char Blake3ChunkState.maybe_start_flag(&self) @inline
=> !self.blocks_compressed ? Blake3Flags.CHUNK_START : 0;
<*
Update the chunk with the provided input bytes.
@param [in] input : "Incoming bytes to update with."
*>
fn void Blake3ChunkState.update(&self, char[] input)
{
if (self.buf_len)
{
usz take = self.fill_buf(input);
input = input[take..];
if (input.len)
{
compress_in_place(self.cv[..], self.buf[..], BLOCK_SIZE, self.chunk_counter, self.flags | self.maybe_start_flag());
self.blocks_compressed++;
self.buf_len = 0;
self.buf[..] = {};
}
}
for (; input.len > BLOCK_SIZE; self.blocks_compressed++, input = input[BLOCK_SIZE..])
{
compress_in_place(self.cv[..], input[:BLOCK_SIZE], BLOCK_SIZE, self.chunk_counter, self.flags | self.maybe_start_flag());
}
self.fill_buf(input);
}
<*
Convert the chunk state to an "output" type with the right flags.
*>
fn Blake3Output Blake3ChunkState.output(&self) @inline
=> make_output(self.cv[..], &self.buf, self.buf_len, self.chunk_counter, self.flags | self.maybe_start_flag() | Blake3Flags.CHUNK_END);
<*
Generate and initialize an output structure with the provided parameters.
@param [in] key
@param [&in] in_block
@param block_len
@param counter
@param flags
*>
fn Blake3Output make_output(uint[] key, char* in_block, usz block_len, ulong counter, char flags) @local @noinline
{
Blake3Output o;
o.input_cv[..] = key[..];
o.block[..] = in_block[:BLOCK_SIZE];
o.block_len = (char)block_len;
o.counter = counter;
o.flags = flags;
return o;
}
<*
Auto-generate a parent output structure, pre-initialized with some constant identifiers.
@param [&in] block
@param [in] key
@param flags
*>
macro Blake3Output parent_output(char* block, uint[] key, char flags) @local
=> make_output(key, block, BLOCK_SIZE, 0, flags | Blake3Flags.PARENT);
<*
Compress then store the chaining value of the output structure.
@param [&inout] cv
*>
macro void Blake3Output.chaining_value(&self, char* cv)
{
uint[KEY_SIZE_WORDS] cv_words;
cv_words[..] = self.input_cv[..];
compress_in_place(cv_words[..], self.block, self.block_len, self.counter, self.flags);
cv[:KEY_SIZE] = @as_char_view(cv_words)[:KEY_SIZE];
}
<*
Store the result of the output into the designated slice.
@param seek
@param [inout] into
*>
fn void Blake3Output.root_bytes(&self, usz seek, char[] into)
{
if (!into.len) return;
ulong output_block_counter = seek / BLOCK_SIZE;
usz offset_within_block = seek % BLOCK_SIZE;
char[BLOCK_SIZE] wide_buf;
if (offset_within_block)
{
compress_xof(self.input_cv[..], self.block, self.block_len, output_block_counter, self.flags | Blake3Flags.ROOT, wide_buf[..]);
usz avail = BLOCK_SIZE - offset_within_block;
usz bytes = min(into.len, avail);
into[:bytes] = wide_buf[offset_within_block:bytes];
into = into[bytes..];
output_block_counter++;
}
if (into.len / BLOCK_SIZE)
{
@xof_many(self.input_cv[..], self.block, self.block_len, output_block_counter, self.flags | Blake3Flags.ROOT, into, into.len / BLOCK_SIZE);
}
output_block_counter += into.len / 64;
into = into[(usz)(into.len & -64ll) ..];
if (into.len)
{
compress_xof(self.input_cv[..], self.block, self.block_len, output_block_counter, self.flags | Blake3Flags.ROOT, wide_buf[..]);
into[..] = wide_buf[:into.len];
}
}
// =================================================================================================
// =================================================================================================
// =================================================================================================
// WELCOME TO THE COMPUTATION GARDEN...
//
// You wanna understand BLAKE3? You gotta get through us.
// ______________________________
// ༼ ºل͟º ༼ ºل͟º ༼ ºل͟º ༽ ºل͟º ༽ ºل͟º ༽
//
//
macro uint @popcnt(#x) @local => (uint)#x.popcount();
macro uint @highest_one(#x) @local => 63 ^ (uint)#x.clz();
macro usz @round_down_to_power_of_2(#x) @local => (usz)1 << @highest_one(#x | 1);
macro left_subtree_len(usz input_len) @local
=> @round_down_to_power_of_2((input_len - 1) / CHUNK_SIZE) * CHUNK_SIZE;
macro @g(#state, a, b, c, d, x, y) @local
{
#state[a] += #state[b] + x;
#state[d] = (#state[d] ^ #state[a]).rotr(16);
#state[c] += #state[d];
#state[b] = (#state[b] ^ #state[c]).rotr(12);
#state[a] += #state[b] + y;
#state[d] = (#state[d] ^ #state[a]).rotr(8);
#state[c] += #state[d];
#state[b] = (#state[b] ^ #state[c]).rotr(7);
}
macro @round(uint[] state, uint* msg, usz round) @local
{
char* schedule = &MESSAGE_SCHEDULE[round];
@g(state, 0, 4, 8, 12, msg[schedule[0] ], msg[schedule[1] ]);
@g(state, 1, 5, 9, 13, msg[schedule[2] ], msg[schedule[3] ]);
@g(state, 2, 6, 10, 14, msg[schedule[4] ], msg[schedule[5] ]);
@g(state, 3, 7, 11, 15, msg[schedule[6] ], msg[schedule[7] ]);
@g(state, 0, 5, 10, 15, msg[schedule[8] ], msg[schedule[9] ]);
@g(state, 1, 6, 11, 12, msg[schedule[10]], msg[schedule[11]]);
@g(state, 2, 7, 8, 13, msg[schedule[12]], msg[schedule[13]]);
@g(state, 3, 4, 9, 14, msg[schedule[14]], msg[schedule[15]]);
}
fn void compress_pre(uint[] state, uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags) @local @noinline
{
uint[16] block_words @noinit;
foreach (i, &b : block_words) *b = mem::load((uint*)&block[i * 4], 1);
state[0:8] = cv[0:8];
state[8:4] = IV[0:4];
state[12] = (uint)counter;
state[13] = (uint)(counter >> 32);
state[14] = (uint)block_len;
state[15] = (uint)flags;
for (int i = 0; i < 7; i++)
{
@round(state, &block_words[0], (usz)i);
}
}
macro compress_in_place(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags) @local
{
uint[16] state @noinit;
compress_pre(state[..], cv, block, block_len, counter, flags);
for (usz i = 0; i < 8; i++) cv[i] = state[i] ^ state[i + 8];
}
macro compress_xof(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags, char[] out) @local
{
uint[16] state @noinit;
compress_pre(state[..], cv, block, block_len, counter, flags);
$for usz $i = 0; $i < 8; $i++: mem::store((uint*)&out[4 * $i], state[$i] ^ state[$i + 8], 1); $endfor
$for usz $i = 0; $i < 8; $i++: mem::store((uint*)&out[4 * (8 + $i)], state[$i + 8] ^ cv[$i], 1); $endfor
}
macro @xof_many(uint[] cv, char[BLOCK_SIZE] block, usz block_len, ulong counter, char flags, char[] out, usz out_blocks) @local
{
for (usz i = 0; i < out_blocks; i++, out = out[BLOCK_SIZE..]) compress_xof(cv, block, block_len, counter + i, flags, out);
}
macro hash_one(char* input, usz blocks, uint[] key, ulong counter, char flags, char flags_start, char flags_end, char[] out) @local
{
uint[8] cv;
cv[..] = key[..];
char block_flags = flags | flags_start;
for (; blocks > 0; input += BLOCK_SIZE, blocks--, block_flags = flags)
{
if (blocks == 1) block_flags |= flags_end;
compress_in_place(cv[..], input[:BLOCK_SIZE], BLOCK_SIZE, counter, block_flags);
}
foreach (i, c : cv) mem::store((uint*)&out[i * 4], c, 1);
}
macro hash_many(char*[] inputs, usz num_inputs, usz blocks, uint[] key, ulong counter, bool $increment_counter, char flags, char flags_start, char flags_end, char* out) @local
{
for (; num_inputs > 0; num_inputs--, inputs = inputs[1..], out += OUT_SIZE)
{
hash_one(inputs[0], blocks, key, counter, flags, flags_start, flags_end, out[:OUT_SIZE]);
$if $increment_counter: counter++; $endif
}
}
fn void compress_subtree_to_parent_node(char[] input, uint[] key, ulong chunk_counter, char flags, char[] out, bool use_tbb) @local @noinline
{
char[MAX_SIMD_DEGREE_OR_2 * OUT_SIZE] cv_array;
usz num_cvs = compress_subtree_wide(input, key, chunk_counter, flags, cv_array[..], use_tbb);
assert(num_cvs <= 2);
$if MAX_SIMD_DEGREE_OR_2 > 2:
char[MAX_SIMD_DEGREE_OR_2 * OUT_SIZE / 2] out_array;
while (num_cvs > 2) num_cvs = compress_parents_parallel(cv_array[..], num_cvs, key, flags, &out_array);
$endif
out[..] = cv_array[:2 * OUT_SIZE];
}
fn usz compress_subtree_wide(char[] input, uint[] key, ulong chunk_counter, char flags, char* out, bool use_tbb) @local @noinline
{
if (input.len <= @simd_degree() * CHUNK_SIZE) return compress_chunks_parallel(input, key, chunk_counter, flags, out);
usz left_input_len = left_subtree_len(input.len);
usz right_input_len = input.len - left_input_len;
char* right_input = &input[left_input_len];
ulong right_chunk_counter = chunk_counter + (ulong)(left_input_len / CHUNK_SIZE);
char[2 * MAX_SIMD_DEGREE_OR_2 * OUT_SIZE] cv_array;
usz degree = @simd_degree();
if (left_input_len > CHUNK_SIZE && degree == 1) degree = 2;
char* right_cvs = &cv_array[degree * OUT_SIZE];
usz left_n = compress_subtree_wide(input[:left_input_len], key, chunk_counter, flags, &cv_array, use_tbb);
usz right_n = compress_subtree_wide(right_input[:right_input_len], key, right_chunk_counter, flags, right_cvs, use_tbb);
if (left_n == 1)
{
out[:2 * OUT_SIZE] = cv_array[:2 * OUT_SIZE];
return 2;
}
return compress_parents_parallel(cv_array[..], left_n + right_n, key, flags, out);
}
fn usz compress_parents_parallel(char[] child_chaining_values, usz num_chaining_values, uint[] key, char flags, char* out) @local @noinline
{
char*[MAX_SIMD_DEGREE_OR_2] parents_array;
usz parents_array_len = 0;
while (num_chaining_values - (2 * parents_array_len) >= 2)
{
parents_array[parents_array_len++] = &child_chaining_values[2 * parents_array_len * OUT_SIZE];
}
hash_many(parents_array[:parents_array_len], parents_array_len, 1, key, 0, false, flags | Blake3Flags.PARENT, 0, 0, out);
if (num_chaining_values > 2 * parents_array_len)
{
out[parents_array_len * OUT_SIZE : OUT_SIZE] = child_chaining_values[2 * parents_array_len * OUT_SIZE : OUT_SIZE];
return parents_array_len + 1;
}
return parents_array_len;
}
fn usz compress_chunks_parallel(char[] input, uint[] key, ulong chunk_counter, char flags, char* out) @local @noinline
{
char*[MAX_SIMD_DEGREE] chunks_array;
usz input_position = 0;
usz chunks_array_len = 0;
for (; input.len - input_position >= CHUNK_SIZE; input_position += CHUNK_SIZE)
{
chunks_array[chunks_array_len++] = &input[input_position];
}
hash_many(chunks_array[:chunks_array_len], chunks_array_len, CHUNK_SIZE / BLOCK_SIZE, key, chunk_counter, true, flags, Blake3Flags.CHUNK_START, Blake3Flags.CHUNK_END, out);
if (input.len <= input_position) return chunks_array_len;
ulong counter = chunk_counter + (ulong)chunks_array_len;
Blake3ChunkState chunk_state;
chunk_state.init(key, flags);
chunk_state.chunk_counter = counter;
chunk_state.update(input[input_position : input.len - input_position]);
Blake3Output o = chunk_state.output();
o.chaining_value(&out[chunks_array_len * OUT_SIZE]);
return chunks_array_len + 1;
}

View File

@@ -5,8 +5,8 @@ module std::hash::fnv32a;
typedef Fnv32a = uint;
const FNV32A_START @private = 0x811c9dc5;
const FNV32A_MUL @private = 0x01000193;
const FNV32A_START @private = (Fnv32a)0x811c9dc5;
const FNV32A_MUL @private = (Fnv32a)0x01000193;
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV32A_MUL;
@@ -32,10 +32,10 @@ macro void Fnv32a.update_char(&self, char c)
fn uint hash(char[] data)
{
uint h = FNV32A_START;
Fnv32a h = FNV32A_START;
foreach (char x : data)
{
update(&h, x);
}
return h;
return (uint)h;
}

View File

@@ -5,10 +5,10 @@ module std::hash::fnv64a;
typedef Fnv64a = ulong;
const FNV64A_START @private = 0xcbf29ce484222325;
const FNV64A_MUL @private = 0x00000100000001b3;
const Fnv64a FNV64A_START @private = (Fnv64a)0xcbf29ce484222325;
const Fnv64a FNV64A_MUL @private = (Fnv64a)0x00000100000001b3;
macro void update(h, char x) @private => *h = (*h ^ ($typeof(*h))x) * FNV64A_MUL;
macro Fnv64a update(Fnv64a h, char x) @nodiscard @private => (h ^ (Fnv64a)x) * FNV64A_MUL;
fn void Fnv64a.init(&self)
{
@@ -20,22 +20,22 @@ fn void Fnv64a.update(&self, char[] data)
Fnv64a h = *self;
foreach (char x : data)
{
update(&h, x);
h = update(h, x);
}
*self = h;
}
macro void Fnv64a.update_char(&self, char c)
{
update(self, c);
*self = update(*self, c);
}
fn ulong hash(char[] data)
{
ulong h = FNV64A_START;
Fnv64a h = FNV64A_START;
foreach (char x : data)
{
update(&h, x);
h = update(h, x);
}
return h;
return (ulong)h;
}

View File

@@ -0,0 +1,172 @@
// Copyright (c) 2025 Zack Puhl <github@xmit.xyz>. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
//
// An implementation of Streebog-256 and Streebog-512, defined in the
// Russian standard GOST R 34.11-2012. Also known as "GOST-12".
//
module std::hash::streebog;
constdef StreebogLength : inline uint
{
SIZE_256 = 32,
SIZE_512 = 64,
}
struct Streebog
{
ulong[8] h;
ulong[8] n;
ulong[8] s;
ulong[8] message;
usz index;
usz hash_size;
}
<*
@require $defined(data[0]) &&& $typeof(data[0]) == char : "Input data must be a char slice, char array, or string value."
*>
macro char[*] hash(StreebogLength $hash_size, data)
{
// TODO: The speed of this hash hinges on use of >>> SIMD instructions <<<. It would be nice to have some added.
Streebog s @noinit;
s.init($hash_size);
s.update(data);
return s.final($hash_size);
}
macro char[*] hash_256(char[] data) => hash(SIZE_256, data);
macro char[*] hash_512(char[] data) => hash(SIZE_512, data);
macro @xor_512(#x, #y, #z) @local
{
$for var $i = 0; $i < 8; $i++:
#z[$i] = #x[$i] ^ #y[$i];
$endfor
}
macro @add_512(#sum, #x) @local
{
ulong carry = 0;
$for usz $i = 0; $i < 8; $i++:
#sum[$i] += #x[$i] + carry;
carry = #sum[$i] < #x[$i] ? 1 : (#sum[$i] == #x[$i] ? carry : 0);
$endfor
}
macro @lpsx(#a, #b, #result) @local
{
ulong[8] z @noinit;
@xor_512(#a, #b, z);
// Do not unroll these loops - produces far too much code at compile-time.
for (usz i = 0; i < 8; i++)
{
#result[i] = TR[0][(z[0] >> (i << 3)) & 0xff];
for (usz j = 1; j < 8; j++) #result[i] ^= TR[j][(z[j] >> (i << 3)) & 0xff];
}
}
macro @g_n(#n, #h, #m) @local
{
ulong[8] k_i;
ulong[8] state;
@lpsx(#h, #n, k_i);
@lpsx(k_i, #m, state);
// Do not unroll this loop - produces far too much code at compile-time.
for (usz i = 0; i < 11; i++)
{
@lpsx(k_i, ITERATION_CONSTANTS[i], k_i);
@lpsx(k_i, state, state);
}
@lpsx(k_i, ITERATION_CONSTANTS[11], k_i);
@xor_512(k_i, state, state);
@xor_512(state, #h, state);
@xor_512(state, #m, #h);
}
macro Streebog.@stage2(&self, #m) @local
{
@g_n(self.n, self.h, #m);
@add_512(self.n, STAGE2_512);
@add_512(self.s, #m);
}
macro void Streebog.init(&self, StreebogLength $hash_size)
{
mem::zero_volatile(@as_char_view(*self)); // explicitly initialize the entire state to 0
self.hash_size = $hash_size;
$if $hash_size != SIZE_512:
@as_char_view(self.h)[..] = (char[*]){ [0..63] = 0x01 }[..];
$endif
}
fn void Streebog.update(&self, char[] data)
{
if (self.index)
{
usz rest = BLOCK_SIZE - self.index;
usz size = data.len;
usz len = size < rest ? size : rest;
@as_char_view(self.message)[self.index:len] = data[:len];
self.index += size;
if (size < rest) return;
self.@stage2(self.message);
data = data[rest..];
self.index = 0;
}
bool aligned = 0 == (usz)data.ptr % ulong.sizeof;
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..])
{
if (aligned)
{
self.@stage2((ulong*)data.ptr);
}
else
{
@as_char_view(self.message)[:BLOCK_SIZE] = data[:BLOCK_SIZE];
self.@stage2(self.message);
}
}
if (data.len)
{
self.index = data.len;
@as_char_view(self.message)[:data.len] = data[..];
}
}
<*
@require $hash_size == self.hash_size : "You must use the same output hash size as was initialized with the context."
*>
macro char[*] Streebog.final(&self, StreebogLength $hash_size)
{
char[$hash_size] result;
streebog_final_private(self);
defer mem::zero_volatile(@as_char_view(*self)); // implicitly clear the structure when finalized
result[..] = @as_char_view(self.h)[(BLOCK_SIZE - $hash_size)..];
return result;
}
fn void streebog_final_private(Streebog* self) @local
{
ulong[8] unprocessed_bits_count;
usz index = self.index >> 3;
usz shift = (self.index & 0b111) * 8;
unprocessed_bits_count[0] = self.index * 8ul;
self.message[index] &= ~(ulong.max << shift);
self.message[index++] ^= 1ul << shift;
if (index < 8) self.message[index..] = {};
@g_n(self.n, self.h, self.message);
@add_512(self.n, unprocessed_bits_count);
@add_512(self.s, self.message);
@g_n(ZERO_512, self.h, self.n);
@g_n(ZERO_512, self.h, self.s);
}

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

@@ -99,6 +99,32 @@ fn char[HASH_BYTES] Md5.final(&ctx)
module std::hash::md5 @private;
const uint[64] MD5_T @private = {
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
};
const int[16] MD5_S @private = {
7, 12, 17, 22,
5, 9, 14, 20,
4, 11, 16, 23,
6, 10, 15, 21
};
// Implementation
macro @f(x, y, z) => z ^ (x & (y ^ z));
macro @g(x, y, z) => y ^ (z & (x ^ y));
@@ -108,7 +134,7 @@ macro @i(x, y, z) => y ^ (x | ~z);
macro void @step(#f, a, b, c, d, ptr, n, t, s)
{
*a += #f(b, c, d) + @unaligned_load(*(uint *)&ptr[n * 4], 2) + t;
*a += #f(b, c, d) + mem::load((uint *)&ptr[n * 4], 2) + t;
*a = (*a << s) | ((*a & 0xffffffff) >> (32 - s));
*a += b;
}
@@ -116,93 +142,54 @@ macro void @step(#f, a, b, c, d, ptr, n, t, s)
fn char* body(Md5* ctx, void* data, usz size)
{
char* ptr;
uint a, b, c, d;
uint saved_a, saved_b, saved_c, saved_d;
ptr = data;
a = ctx.a;
b = ctx.b;
c = ctx.c;
d = ctx.d;
char* ptr = data;
uint a = ctx.a;
uint b = ctx.b;
uint c = ctx.c;
uint d = ctx.d;
do
{
saved_a = a;
saved_b = b;
saved_c = c;
saved_d = d;
uint saved_a = a;
uint saved_b = b;
uint saved_c = c;
uint saved_d = d;
/* Round 1 */
@step(@f, &a, b, c, d, ptr, 0, 0xd76aa478, 7) ;
@step(@f, &d, a, b, c, ptr, 1, 0xe8c7b756, 12) ;
@step(@f, &c, d, a, b, ptr, 2, 0x242070db, 17) ;
@step(@f, &b, c, d, a, ptr, 3, 0xc1bdceee, 22) ;
@step(@f, &a, b, c, d, ptr, 4, 0xf57c0faf, 7) ;
@step(@f, &d, a, b, c, ptr, 5, 0x4787c62a, 12) ;
@step(@f, &c, d, a, b, ptr, 6, 0xa8304613, 17) ;
@step(@f, &b, c, d, a, ptr, 7, 0xfd469501, 22) ;
@step(@f, &a, b, c, d, ptr, 8, 0x698098d8, 7) ;
@step(@f, &d, a, b, c, ptr, 9, 0x8b44f7af, 12) ;
@step(@f, &c, d, a, b, ptr, 10, 0xffff5bb1, 17);
@step(@f, &b, c, d, a, ptr, 11, 0x895cd7be, 22);
@step(@f, &a, b, c, d, ptr, 12, 0x6b901122, 7) ;
@step(@f, &d, a, b, c, ptr, 13, 0xfd987193, 12);
@step(@f, &c, d, a, b, ptr, 14, 0xa679438e, 17);
@step(@f, &b, c, d, a, ptr, 15, 0x49b40821, 22);
/* Round 1 */
for (int i = 0; i < 16; i += 4)
{
@step(@f, &a, b, c, d, ptr, i + 0, MD5_T[i + 0], MD5_S[0]);
@step(@f, &d, a, b, c, ptr, i + 1, MD5_T[i + 1], MD5_S[1]);
@step(@f, &c, d, a, b, ptr, i + 2, MD5_T[i + 2], MD5_S[2]);
@step(@f, &b, c, d, a, ptr, i + 3, MD5_T[i + 3], MD5_S[3]);
}
/* Round 2 */
@step(@g, &a, b, c, d, ptr, 1, 0xf61e2562, 5) ;
@step(@g, &d, a, b, c, ptr, 6, 0xc040b340, 9) ;
@step(@g, &c, d, a, b, ptr, 11, 0x265e5a51, 14);
@step(@g, &b, c, d, a, ptr, 0, 0xe9b6c7aa, 20) ;
@step(@g, &a, b, c, d, ptr, 5, 0xd62f105d, 5) ;
@step(@g, &d, a, b, c, ptr, 10, 0x02441453, 9) ;
@step(@g, &c, d, a, b, ptr, 15, 0xd8a1e681, 14);
@step(@g, &b, c, d, a, ptr, 4, 0xe7d3fbc8, 20) ;
@step(@g, &a, b, c, d, ptr, 9, 0x21e1cde6, 5) ;
@step(@g, &d, a, b, c, ptr, 14, 0xc33707d6, 9) ;
@step(@g, &c, d, a, b, ptr, 3, 0xf4d50d87, 14) ;
@step(@g, &b, c, d, a, ptr, 8, 0x455a14ed, 20) ;
@step(@g, &a, b, c, d, ptr, 13, 0xa9e3e905, 5) ;
@step(@g, &d, a, b, c, ptr, 2, 0xfcefa3f8, 9) ;
@step(@g, &c, d, a, b, ptr, 7, 0x676f02d9, 14) ;
@step(@g, &b, c, d, a, ptr, 12, 0x8d2a4c8a, 20);
/* Round 2 */
for (int i = 0; i < 16; i += 4)
{
@step(@g, &a, b, c, d, ptr, (1 + 5 * (i + 0)) % 16, MD5_T[16 + i + 0], MD5_S[4]);
@step(@g, &d, a, b, c, ptr, (1 + 5 * (i + 1)) % 16, MD5_T[16 + i + 1], MD5_S[5]);
@step(@g, &c, d, a, b, ptr, (1 + 5 * (i + 2)) % 16, MD5_T[16 + i + 2], MD5_S[6]);
@step(@g, &b, c, d, a, ptr, (1 + 5 * (i + 3)) % 16, MD5_T[16 + i + 3], MD5_S[7]);
}
/* Round 3 */
@step(@h, &a, b, c, d, ptr, 5, 0xfffa3942, 4);
@step(@h2, &d, a, b, c, ptr, 8, 0x8771f681, 11);
@step(@h, &c, d, a, b, ptr, 11, 0x6d9d6122, 16);
@step(@h2, &b, c, d, a, ptr, 14, 0xfde5380c, 23);
@step(@h, &a, b, c, d, ptr, 1, 0xa4beea44, 4);
@step(@h2, &d, a, b, c, ptr, 4, 0x4bdecfa9, 11);
@step(@h, &c, d, a, b, ptr, 7, 0xf6bb4b60, 16);
@step(@h2, &b, c, d, a, ptr, 10, 0xbebfbc70, 23);
@step(@h, &a, b, c, d, ptr, 13, 0x289b7ec6, 4) ;
@step(@h2, &d, a, b, c, ptr, 0, 0xeaa127fa, 11) ;
@step(@h, &c, d, a, b, ptr, 3, 0xd4ef3085, 16) ;
@step(@h2, &b, c, d, a, ptr, 6, 0x04881d05, 23) ;
@step(@h, &a, b, c, d, ptr, 9, 0xd9d4d039, 4) ;
@step(@h2, &d, a, b, c, ptr, 12, 0xe6db99e5, 11) ;
@step(@h, &c, d, a, b, ptr, 15, 0x1fa27cf8, 16) ;
@step(@h2, &b, c, d, a, ptr, 2, 0xc4ac5665, 23) ;
/* Round 3 */
for (int i = 0; i < 16; i += 4)
{
@step(@h, &a, b, c, d, ptr, (5 + 3 * (i + 0)) % 16, MD5_T[32 + i + 0], MD5_S[8]);
@step(@h, &d, a, b, c, ptr, (5 + 3 * (i + 1)) % 16, MD5_T[32 + i + 1], MD5_S[9]);
@step(@h, &c, d, a, b, ptr, (5 + 3 * (i + 2)) % 16, MD5_T[32 + i + 2], MD5_S[10]);
@step(@h, &b, c, d, a, ptr, (5 + 3 * (i + 3)) % 16, MD5_T[32 + i + 3], MD5_S[11]);
}
/* Round 4 */
@step(@i, &a, b, c, d, ptr, 0, 0xf4292244, 6) ;
@step(@i, &d, a, b, c, ptr, 7, 0x432aff97, 10) ;
@step(@i, &c, d, a, b, ptr, 14, 0xab9423a7, 15) ;
@step(@i, &b, c, d, a, ptr, 5, 0xfc93a039, 21) ;
@step(@i, &a, b, c, d, ptr, 12, 0x655b59c3, 6) ;
@step(@i, &d, a, b, c, ptr, 3, 0x8f0ccc92, 10) ;
@step(@i, &c, d, a, b, ptr, 10, 0xffeff47d, 15) ;
@step(@i, &b, c, d, a, ptr, 1, 0x85845dd1, 21) ;
@step(@i, &a, b, c, d, ptr, 8, 0x6fa87e4f, 6) ;
@step(@i, &d, a, b, c, ptr, 15, 0xfe2ce6e0, 10) ;
@step(@i, &c, d, a, b, ptr, 6, 0xa3014314, 15) ;
@step(@i, &b, c, d, a, ptr, 13, 0x4e0811a1, 21) ;
@step(@i, &a, b, c, d, ptr, 4, 0xf7537e82, 6) ;
@step(@i, &d, a, b, c, ptr, 11, 0xbd3af235, 10) ;
@step(@i, &c, d, a, b, ptr, 2, 0x2ad7d2bb, 15) ;
@step(@i, &b, c, d, a, ptr, 9, 0xeb86d391, 21) ;
/* Round 4 */
for (int i = 0; i < 16; i += 4)
{
@step(@i, &a, b, c, d, ptr, (7 * (i + 0)) % 16, MD5_T[48 + i + 0], MD5_S[12]);
@step(@i, &d, a, b, c, ptr, (7 * (i + 1)) % 16, MD5_T[48 + i + 1], MD5_S[13]);
@step(@i, &c, d, a, b, ptr, (7 * (i + 2)) % 16, MD5_T[48 + i + 2], MD5_S[14]);
@step(@i, &b, c, d, a, ptr, (7 * (i + 3)) % 16, MD5_T[48 + i + 3], MD5_S[15]);
}
a += saved_a;
b += saved_b;

243
lib/std/hash/murmur.c3 Normal file
View File

@@ -0,0 +1,243 @@
module std::hash::murmur3;
<*
@param [in] data : "The data to hash"
@param seed : "The seed to use for hashing"
@require (data.len / 4) <= int.max : "Too much data"
*>
fn uint hash32(char[] data, uint seed)
{
int nblocks = (int)data.len / 4;
uint h1 = seed;
const uint C1 = 0xcc9e2d51;
const uint C2 = 0x1b873593;
uint* blocks = (uint *)(data.ptr + nblocks * 4);
for (int i = -nblocks; i != 0; i++)
{
uint k1 = getblock32(blocks, i);
k1 *= C1;
k1 = k1.rotl(15);
k1 *= C2;
h1 ^= k1;
h1 = h1.rotl(13);
h1 = h1 * 5U + 0xe6546b64;
}
char* tail = data.ptr + nblocks * 4;
uint k1;
switch (data.len & 3)
{
case 3: k1 ^= tail[2] << 16; nextcase;
case 2: k1 ^= tail[1] << 8; nextcase;
case 1: k1 ^= tail[0]; k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
}
h1 ^= (uint)data.len;
h1 = fmix32(h1);
return h1;
}
<*
@param [in] data : "The data to hash"
@param seed : "The seed to use for hashing"
@require (data.len / 16) <= int.max : "Too much data"
*>
fn uint128 hash128_64(char[] data, uint seed)
{
ulong len = data.len;
int nblocks = (int)(len / 16);
ulong h1 = seed;
ulong h2 = seed;
const ulong C1 = 0x87c37b91114253d5UL;
const ulong C2 = 0x4cf5ad432745937fUL;
ulong* blocks = (ulong*)data.ptr; // Unaligned!
for (int i = 0; i < nblocks; i++)
{
ulong k1 = getblock64(blocks, i * 2 + 0);
ulong k2 = getblock64(blocks, i * 2 + 1);
k1 *= C1; k1 = k1.rotl(31); k1 *= C2; h1 ^= k1;
h1 = h1.rotl(27); h1 += h2; h1 = h1 * 5U + 0x52dce729;
k2 *= C2; k2 = k2.rotl(33); k2 *= C1; h2 ^= k2;
h2 = h2.rotl(31); h2 += h1; h2 = h2 * 5U + 0x38495ab5;
}
char* tail = data.ptr + nblocks * 16;
ulong k1, k2;
switch (len & 15)
{
case 15: k2 ^= ((ulong)tail[14]) << 48; nextcase;
case 14: k2 ^= ((ulong)tail[13]) << 40; nextcase;
case 13: k2 ^= ((ulong)tail[12]) << 32; nextcase;
case 12: k2 ^= ((ulong)tail[11]) << 24; nextcase;
case 11: k2 ^= ((ulong)tail[10]) << 16; nextcase;
case 10: k2 ^= ((ulong)tail[ 9]) << 8; nextcase;
case 9: k2 ^= ((ulong)tail[ 8]) << 0;
k2 *= C2; k2 = k2.rotl(33); k2 *= C1; h2 ^= k2;
nextcase;
case 8: k1 ^= ((ulong)tail[ 7]) << 56; nextcase;
case 7: k1 ^= ((ulong)tail[ 6]) << 48; nextcase;
case 6: k1 ^= ((ulong)tail[ 5]) << 40; nextcase;
case 5: k1 ^= ((ulong)tail[ 4]) << 32; nextcase;
case 4: k1 ^= ((ulong)tail[ 3]) << 24; nextcase;
case 3: k1 ^= ((ulong)tail[ 2]) << 16; nextcase;
case 2: k1 ^= ((ulong)tail[ 1]) << 8; nextcase;
case 1: k1 ^= ((ulong)tail[ 0]) << 0;
k1 *= C1; k1 = k1.rotl(31); k1 *= C2; h1 ^= k1;
}
h1 ^= len;
h2 ^= len;
h1 += h2;
h2 += h1;
h1 = fmix64(h1);
h2 = fmix64(h2);
h1 += h2;
h2 += h1;
return h1 + (uint128)h2 << 64U;
}
<*
@param [in] data : "The data to hash"
@param seed : "The seed to use for hashing"
@require data.len <= uint.max : "Too much data"
*>
fn uint128 hash128_32(char[] data, uint seed)
{
uint len = data.len;
int nblocks = (int)(len / 16);
uint h1 = seed;
uint h2 = seed;
uint h3 = seed;
uint h4 = seed;
const uint C1 = 0x239b961b;
const uint C2 = 0xab0e9789;
const uint C3 = 0x38b34ae5;
const uint C4 = 0xa1e38b93;
uint* blocks = (uint *)(data.ptr + nblocks * 16);
for (int i = -nblocks; i != 0; i++)
{
uint k1 = getblock32(blocks, i * 4 + 0);
uint k2 = getblock32(blocks, i * 4 + 1);
uint k3 = getblock32(blocks, i * 4 + 2);
uint k4 = getblock32(blocks, i * 4 + 3);
k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
h1 = h1.rotl(19); h1 += h2; h1 = h1 * 5U + 0x561ccd1b;
k2 *= C2; k2 = k2.rotl(16); k2 *= C3; h2 ^= k2;
h2 = h2.rotl(17); h2 += h3; h2 = h2 * 5U + 0x0bcaa747;
k3 *= C3; k3 = k3.rotl(17); k3 *= C4; h3 ^= k3;
h3 = h3.rotl(15); h3 += h4; h3 = h3 * 5U + 0x96cd1c35;
k4 *= C4; k4 = k4.rotl(18); k4 *= C1; h4 ^= k4;
h4 = h4.rotl(13); h4 += h1; h4 = h4 * 5U + 0x32ac3b17;
}
char* tail = data.ptr + nblocks * 16;
uint k1, k2, k3, k4;
switch (len & 15)
{
case 15: k4 ^= tail[14] << 16; nextcase;
case 14: k4 ^= tail[13] << 8; nextcase;
case 13: k4 ^= tail[12] << 0;
k4 *= C4; k4 = k4.rotl(18); k4 *= C1; h4 ^= k4;
nextcase;
case 12: k3 ^= tail[11] << 24; nextcase;
case 11: k3 ^= tail[10] << 16; nextcase;
case 10: k3 ^= tail[ 9] << 8; nextcase;
case 9: k3 ^= tail[ 8] << 0;
k3 *= C3; k3 = k3.rotl(17); k3 *= C4; h3 ^= k3;
nextcase;
case 8: k2 ^= tail[ 7] << 24; nextcase;
case 7: k2 ^= tail[ 6] << 16; nextcase;
case 6: k2 ^= tail[ 5] << 8; nextcase;
case 5: k2 ^= tail[ 4] << 0;
k2 *= C2; k2 = k2.rotl(16); k2 *= C3; h2 ^= k2;
nextcase;
case 4: k1 ^= tail[ 3] << 24; nextcase;
case 3: k1 ^= tail[ 2] << 16; nextcase;
case 2: k1 ^= tail[ 1] << 8; nextcase;
case 1: k1 ^= tail[ 0] << 0;
k1 *= C1; k1 = k1.rotl(15); k1 *= C2; h1 ^= k1;
}
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
h1 = fmix32(h1);
h2 = fmix32(h2);
h3 = fmix32(h3);
h4 = fmix32(h4);
h1 += h2; h1 += h3; h1 += h4;
h2 += h1; h3 += h1; h4 += h1;
return h1 + (uint128)h2 << 32U + (uint128)h3 << 64U + (uint128)h4 << 96U;
}
macro uint getblock32(uint* p, int i) @local
{
UIntLE* p_le = (UIntLE*)p + i;
return mem::load(p_le, 1).val;
}
macro ulong getblock64(ulong* p, int i) @local
{
ULongLE* p_le = (ULongLE*)p + i;
return mem::load(p_le, 1).val;
}
macro uint fmix32(uint h) @local
{
h ^= h >> 16UL;
h *= 0x85ebca6b;
h ^= h >> 13UL;
h *= 0xc2b2ae35;
h ^= h >> 16UL;
return h;
}
macro ulong fmix64(ulong k) @local
{
k ^= k >> 33U;
k *= 0xff51afd7ed558ccd;
k ^= k >> 33U;
k *= 0xc4ceb9fe1a85ec53;
k ^= k >> 33U;
return k;
}

171
lib/std/hash/poly1305.c3 Normal file
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];
_blocks(self, self.temp[..]);
input = input[rem..];
}
usz even_length = input.len - (input.len % BLOCK_SIZE);
if (even_length >= BLOCK_SIZE)
{
_blocks(self, input[:even_length]); // consume blocks
input = input[even_length..]; // scroll to end (remainder)
}
if (input.len) self.temp[:input.len] = input[..]; // keep remainder (uneven block sizes)
self.num = input.len;
}
fn char[TAG_SIZE] Poly1305.final(&self)
{
if (self.num) // consume any leftovers
{
self.temp[self.num++] = 1; // partial blocks must end with 0x01
self.temp[self.num..] = {}; // explicit zeros on the rest
_blocks(self, self.temp[..], 0); // chomp
}
uint128 t = (uint128)self.h[0] + 5;
ulong g0 = (ulong)t;
t = (uint128)self.h[1] + (t >> 64);
ulong g1 = (ulong)t;
ulong mask = 0 - ((self.h[2] + (ulong)(t >> 64)) >> 2);
self.h[0] = (self.h[0] & ~mask) | (g0 & mask);
self.h[1] = (self.h[1] & ~mask) | (g1 & mask);
t = (uint128)self.h[0] + (ulong)self.nonce;
self.h[0] = (ulong)t;
t = (uint128)self.h[1] + (ulong)(self.nonce >> 64) + (t >> 64);
self.h[1] = (ulong)t;
// Store, clear context, return.
uint128 result = ((uint128)self.h[1] << 64) + self.h[0];
*self = {};
return @as_char_view(result)[:TAG_SIZE];
}
fn void _blocks(Poly1305* self, char[] input, ulong pad_bit = 1) @local
{
for (; input.len >= BLOCK_SIZE; input = input[BLOCK_SIZE..])
{
ulong i0 = mem::load((ulong*)&input[0], 1);
ulong i1 = mem::load((ulong*)&input[8], 1);
uint128 d0 = (uint128)self.h[0] + i0;
self.h[0] = (ulong)d0;
uint128 d1 = (uint128)self.h[1] + (d0 >> 64) + i1;
self.h[1] = (ulong)d1;
self.h[2] += (ulong)(d1 >> 64) + pad_bit;
d0 = ((uint128)self.h[0] * self.r0) + ((uint128)self.h[1] * self.s1);
d1 = ((uint128)self.h[0] * self.r1) + ((uint128)self.h[1] * self.r0) + ((uint128)self.h[2] * self.s1);
self.h[2] = (self.h[2] * self.r0);
self.h[0] = (ulong)d0;
d1 = d1 + (d0 >> 64);
self.h[1] = (ulong)d1;
self.h[2] = self.h[2] + (ulong)(d1 >> 64);
ulong c = (self.h[2] >> 2) + (self.h[2] & ~(ulong)3);
self.h[2] &= 3;
self.h[0] += c;
c = constant_time_carry(self.h[0], c);
self.h[1] += c;
self.h[2] += constant_time_carry(self.h[1], c);
}
}

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