Compare commits

..

114 Commits

Author SHA1 Message Date
Christoffer Lerno
5a3c484ceb Remove automatic win32 cleanup. RC2 2025-10-30 21:29:48 +01:00
Christoffer Lerno
331a77c1c2 Update 0.7.7 2025-10-30 11:01:01 +01:00
Christoffer Lerno
045053f6bf Fix for RISCV on LLVM 17 2025-10-28 18:22:32 +01:00
Christoffer Lerno
4809979898 - Add --riscv-cpu settings for RISC-V processors #2549. 2025-10-27 18:33:54 +01:00
Christoffer Lerno
a5b2636b2e Added cpu-flags to the command line help 2025-10-27 14:11:50 +01:00
Christoffer Lerno
c483c3b75f Update naming to cpu-flags 2025-10-27 14:08:21 +01:00
Christopher Coverdale
c10d449e43 Add local TcpSocketPair (#2526)
* Add extern fn socketpair() to posix
* Add extern fn getsockname() for local socketpair loopback in windows
* Add local TcpSocketPair
* Add unit test for TcpSocketPair
* Add implicit wsa startup

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-10-27 13:16:14 +01:00
Christoffer Lerno
54b110a367 Dev (#2547)
* Update feature handling for Wasm, RiscV, Aarch64, Arm
* - The option `--riscvfloat` renamed `--riscv-abi`.
- Add initial `--cpu-features` allowing fine grained control over CPU features.
2025-10-27 01:09:59 +01:00
Dmitry Atamanov
ee8dc3d681 Update Linux nolibc/hello_world (#2546) 2025-10-27 00:45:16 +01:00
Christoffer Lerno
a38a627a1d Allow (Foo)0 bitstruct casts even if type sizes do not match. 2025-10-25 20:33:47 +02:00
Christoffer Lerno
8aaf54e8b1 - Rename @extern to @cname, deprecating the old name #2493. 2025-10-25 15:55:25 +02:00
Christoffer Lerno
423152202f Dev (#2545)
* Optimize vector load / store. Fixes to alignment. Support typedef with `@simd` and `@align` #2543. Update vector ABI #2542
* Fix alignment issue with indirect arguments.
2025-10-25 12:31:06 +02:00
Christoffer Lerno
f37e7460aa Update OpenBSD llvm version. 2025-10-23 13:58:10 +02:00
Christoffer Lerno
8f5d5a0bb5 "Maybe-deref" subscripting foo.[i] += 1 #2540. 2025-10-23 00:42:38 +02:00
Christoffer Lerno
883052a6bb Improved generic inference in initializers #2541. 2025-10-22 23:48:32 +02:00
Christoffer Lerno
9cf271f5fb Refactoring codegen with Flat / Lowered types. Helpers for struct gep. type_get_indexed_type no longer returns the canonical type, fixes issues in #2534 2025-10-21 16:53:38 +02:00
Christoffer Lerno
5d8cad91b1 Fix lambda regression 2025-10-20 22:55:24 +02:00
Giuliano Macedo
614c6989d8 Fixed incorrect format strings when using error_exit. (#2530)
* Fixed incorrect format strings when using `error_exit`.
2025-10-20 11:24:07 +02:00
Tonis
03ad72afbb Quaternion math improvements (#2524)
* Add radians to deg function

* Quaternion math fixes

* Formatting, use splat/swizzling, divide into multiple tests.

---------

Co-authored-by: tonis2 <tanton@paysure.solutions>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-10-20 11:04:28 +02:00
Christoffer Lerno
b924ede71a Regression: Not printing backtrace when tests fail for MacOS #2536. 2025-10-20 02:50:06 +02:00
Christoffer Lerno
a81f857d8c Update to fix with splat. 2025-10-20 02:26:04 +02:00
Christoffer Lerno
6169d7acdf Correctly mention aliased type when method is not implemented #2534. 2025-10-20 00:19:51 +02:00
Christoffer Lerno
4af31da7ea Compiler segfault when getting a nonexistant member from an unnamed struct #2533. 2025-10-20 00:03:15 +02:00
Christoffer Lerno
0bd2c81757 Splatting optional compile-time macro parameter from inside lambda expression does not work #2532. 2025-10-19 23:05:50 +02:00
Book-reader
5ed1281451 fix nix hooks & patch phase 2025-10-15 22:06:10 +02:00
Christoffer Lerno
7b649314ec Fix tests. 2025-10-15 00:50:24 +02:00
Christoffer Lerno
e37343fbe3 Refactor the C ABI conversion to use frontend independent types. 2025-10-14 19:38:51 +02:00
Christoffer Lerno
7b02907830 Try to workaround tag lock. 2025-10-12 21:59:41 +02:00
Christoffer Lerno
6eee760239 Add --max-macro-iterations to set macro iteration limit. 2025-10-11 16:26:07 +02:00
Christoffer Lerno
ae33d1a206 Fix issue testing if something is global. Remove ScopeId. Adding comments to code. 2025-10-11 13:50:06 +02:00
Christoffer Lerno
3430240c2a Update readme with OpenBSD 2025-10-11 00:47:37 +02:00
Christoffer Lerno
6f11260a5c Disallow aliasing of @local symbols with a higher visibility in the alias. 2025-10-10 14:04:19 +02:00
Christoffer Lerno
df67b7dddd Allow .. ranges to use "a..a-1" in order to express zero length. 2025-10-10 00:34:30 +02:00
Christoffer Lerno
f3b7df2ab0 "build-dir" option now available for project.json, added to project. #2323 2025-10-09 23:41:58 +02:00
Christoffer Lerno
a000ae560a Add new builtins $$str_snakecase $$str_replace and $$str_pascalcase.
Added `@str_snakecase`, `@str_replace` and `@str_pascalcase` builtin compile time macros based on the `$$` builtins.
2025-10-09 22:13:59 +02:00
Christoffer Lerno
0d85caf21c Add splat defaults for designated initialization #2441.
Add ??? and +++= to list-precedence.
2025-10-09 12:45:55 +02:00
Christoffer Lerno
e34a26422f Change macro recursion depth to work on MSVC 2025-10-07 23:47:05 +02:00
Christoffer Lerno
fe70f10bcc Sorting functions correctly took slices by value, but also other types by value. Now, only slices are accepted by value, other containers are always by ref. 2025-10-07 22:43:40 +02:00
Christoffer Lerno
d6be1cbf65 Incorrect visibility on local globals with public aliases. #2519 2025-10-07 21:52:15 +02:00
Christoffer Lerno
04cd079d4e - Compiler segfault when accessing member of number cast to bitstruct #2516.
- Additional fix to #2515
- Compiler assert when getting a member of a `bitstruct : char @bigendian` #2517.
2025-10-07 00:12:41 +02:00
Christoffer Lerno
b4b14674b4 - Bitstruct truncated constant error escapes $defined #2515 2025-10-06 20:50:56 +02:00
Christoffer Lerno
5a1831c989 Error when using $vaarg/$vacount/$vasplat and similar in a macro without vaargs #2510. 2025-10-06 00:45:36 +02:00
Christoffer Lerno
e9ec421b3b Compiler fails to stop error print in recursive macro, and also prints unnecessary "inline at" #2513. 2025-10-06 00:31:27 +02:00
Christoffer Lerno
872f63eecc - Bitstruct value cannot be used to index a const array in compile time. #2512 2025-10-05 22:23:07 +02:00
Christoffer Lerno
1eb8c0ced1 Bug in io::write_using_write_byte. 2025-10-05 00:29:34 +02:00
Christoffer Lerno
b5ae2485a7 Update version to 0.7.7 2025-10-04 11:49:23 +02:00
Christoffer Lerno
fe6817f90d Update frontpage version. 2025-10-03 18:42:32 +02:00
Christoffer Lerno
98a72007f8 Releasenotes fixup 2025-10-03 18:41:11 +02:00
Christoffer Lerno
87c1e09a7a Compiler segfault when splatting variable that does not exist in untyped vaarg macro #2509 2025-10-03 14:08:19 +02:00
Christoffer Lerno
e0fbe31f00 Update release versions 2025-10-02 21:27:35 +02:00
Christoffer Lerno
7d6c844b99 Dead code analysis with labelled if did not work properly. 2025-10-02 21:24:05 +02:00
Christoffer Lerno
a03446a26d - Fix lambda-in-macro visibility, where lambdas would sometimes not correctly link if used through a macro. 2025-10-01 21:05:49 +02:00
Christoffer Lerno
a7e77fec78 $for int $a = 1; $a < 2; $a++ would not parse. 2025-10-01 14:53:52 +02:00
Christoffer Lerno
05c3fa1afd Update CI 2025-10-01 09:43:20 +02:00
Christoffer Lerno
30c8435669 Remove prerelease 2025-10-01 09:35:38 +02:00
Christoffer Lerno
94497c968b - Prevent foo.bar = {} when bar is a flexible array member. #2497
- Fix several issues relating to multi-level inference like `int[*][*]` #2505
2025-09-30 23:43:20 +02:00
Christoffer Lerno
281d4af464 Update contact information for CoC 2025-09-29 13:57:01 +02:00
Christoffer Lerno
cb2d0e798e Prevent foo.bar = {} when bar is a flexible array member. 2025-09-29 01:59:38 +02:00
Christoffer Lerno
da67cd4eb0 Assert when the binary doesn't get created and --run-once is used. #2502 2025-09-29 00:16:26 +02:00
Christoffer Lerno
7d06ca6d35 Crash during codegen when taking the typeid of an empty enum with associated values. 2025-09-29 00:01:20 +02:00
Christoffer Lerno
6d45450130 Renaming, and further refactoring. 2025-09-28 15:33:17 +02:00
Christoffer Lerno
27bbeaf79c Remove use of CheckType to simplify expression handling. 2025-09-28 11:41:32 +02:00
Christoffer Lerno
3af5a537da Cleanup around expressions. 2025-09-27 16:37:56 +02:00
Christoffer Lerno
6287e8dfbf Restore some out checking. 2025-09-26 21:19:38 +02:00
konimarti
1f49a5448e Add AES algorithm (#2496)
* crypto: add AES algorithm

* Some updates to the API

* Silence test.

* Fixed stdlib tests

* Some cleanup. Comments. Make internal methods functions.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-26 16:59:30 +02:00
m0tholith
ece4a2b6fb Make StderrLogger print file and line if FULL_LOG (#2500)
* Make StderrLogger print file and line if `FULL_LOG`

* Avoid inlining a lot of code by using a macro wrapper. Fix test.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-26 16:58:29 +02:00
Christoffer Lerno
e68bd0c57f Update test. 2025-09-25 14:48:00 +02:00
Christoffer Lerno
eaeafb7299 Issue not correctly aborting compilation on recursive generics. 2025-09-25 14:48:00 +02:00
Christoffer Lerno
44d736a537 Add +++= operator. 2025-09-25 14:48:00 +02:00
Christoffer Lerno
122dbb3668 Compiler assert with typed macro vaargs accessing a macro passed as vaarg #2498 2025-09-25 14:13:06 +02:00
Christoffer Lerno
c2abbe2e2f Loosen generic resolution. 2025-09-24 00:12:58 +02:00
Christoffer Lerno
3ccabd625c Renaming 2025-09-22 23:08:11 +02:00
Christoffer Lerno
cfe6534c15 Add shebang test. 2025-09-20 22:55:08 +02:00
Christoffer Lerno
f5090eb158 Support #! as a comment on the first line only. 2025-09-20 21:10:19 +02:00
Christoffer Lerno
d3db91536c Incorrect nameof on nested struct names. #2492 2025-09-20 15:00:44 +02:00
Arnaud Moura
9c42919e5a Install scripts (#2393)
* Add scripts to install c3
* Install script support debian binaries
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-19 22:24:05 +02:00
Book-reader
a6d33ec4af Update stdlib to use struct member docs from #2427 and other small changes (#2473)
* Doc comment improvements

* update `compression/qoi.c3` to use const enums

* revert sweeping doc comment changes that impacted readability for now

* Some tweaks.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-19 18:41:32 +02:00
Christoffer Lerno
b03ae8bb17 Alias and distinct types didn't check the underlying type wasn't compile time or optional. 2025-09-19 18:05:29 +02:00
Christoffer Lerno
59fd777198 Add exec timings to -vv output #2490 2025-09-19 17:20:56 +02:00
Christoffer Lerno
d8286fa2a5 Add 'loop-vectorize', 'slp-vectorize', 'unroll-loops' and 'merge-functions' optimization flags #2491. 2025-09-19 13:34:06 +02:00
Christoffer Lerno
3345e70c63 Comparing slices and arrays of user-defined types that implement == operator now works #2486. 2025-09-19 11:21:29 +02:00
BWindey
12eea4a98d [STDLIB] Add macro return types (#2487)
* add return types to macros where applicable
* std::time::clock::now() -> clock::now()
2025-09-18 14:06:58 +02:00
Christoffer Lerno
fdc20dc642 Taking .ordinal from an enum passed by pointer and then taking the address of this result would return the enum, not int. 2025-09-18 14:04:49 +02:00
Christoffer Lerno
c5e3a1b2da - Compiler segfault for invalid e-mails in project.json. #2488
- `env::PROJECT_VERSION` now returns the version in project.json.
2025-09-18 10:58:15 +02:00
Christoffer Lerno
35270fb0bf Fix unnecessary namespacing. 2025-09-18 00:16:08 +02:00
Christoffer Lerno
d782dad149 Fix compile time format check when the formatting string is a constant slice. 2025-09-17 14:31:00 +02:00
Christoffer Lerno
92aefb15f8 Generic inference (#2475)
* Change generic symbol resolution.
* Infer generic parameters lhs -> rhs: `List{int} x = list::NOHEAP`.
* Regression: Compiler segfault when assigning struct literal with too few members #2483
2025-09-16 18:05:21 +02:00
Christoffer Lerno
8342ac80d3 $alignof, $offsetof and $nameof can now be used in $defined. 2025-09-16 15:36:37 +02:00
Christoffer Lerno
c71444e7a0 Compile time switch over type would not correctly compare function pointer types. 2025-09-16 14:10:28 +02:00
Christian Brendlin
06e10bb69f Add Gentoo installation instructions to README
Added Gentoo installation instructions for c3c.
2025-09-15 14:38:23 +02:00
Velikiy Kirill
fabd96552f Implement write_to_stdin() in std::os::process (#2482)
* SubProcess: Add write_to_stdin

* SubProcess: Change unit test for windows support
2025-09-15 13:41:35 +02:00
m0tholith
2e99ae5ab9 For c3c run and friends, pass SIGINT to child process on Linux (#2480) 2025-09-13 18:51:33 +02:00
Christoffer Lerno
8fea6ee8ab Compiler segfault when modifying variable using an inline assembly block inside defer #2450. 2025-09-12 20:01:28 +02:00
Christoffer Lerno
e6b10ee00c Stack object size limit error on a static object. #2476 2025-09-12 17:11:25 +02:00
Christoffer Lerno
6aff6d66de Fix missing bitstruct member <* *> compatibility. 2025-09-10 23:39:04 +02:00
Christoffer Lerno
8035991ac3 ?? with void results on both sides cause a compiler crash #2472 2025-09-10 10:47:14 +02:00
Jonathan Nilsson
c0bd14cee7 Find cl and set INCLUDE env var outside of a visual studio command promt (#2467)
* Added search for cl

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-09 22:38:40 +02:00
Christoffer Lerno
3ba0beee96 Support for SysV for CVaList 2025-09-09 13:41:26 +02:00
Christoffer Lerno
8e6535f13c Fix of last checkin 2025-09-09 01:06:18 +02:00
Christoffer Lerno
0d8f9520e9 CVaList support on MacOS aarch64. 2025-09-09 01:05:20 +02:00
Christoffer Lerno
3caaf0a3e8 Compiler hang with unaligned load-store pair. #2470 2025-09-09 00:07:45 +02:00
Christoffer Lerno
a2206f1bcd int val = some_int + some_distinct_inline_int errors that int cannot be cast to DistinctInt #2468 2025-09-08 10:21:47 +02:00
Christoffer Lerno
7b5277d52c Any register allowed in X86_64 inline asm address. #2463 2025-09-07 00:03:49 +02:00
Christoffer Lerno
9f55a74d2e Remove use of find_len and len_from_list. Rename lenof to lengthof 2025-09-06 18:35:03 +02:00
Christoffer Lerno
3eb8f68ded - Add lenof() compile time function #2439
- Fix release notes
2025-09-06 18:17:17 +02:00
Christoffer Lerno
bd9bc118db Allow doc comments on individual struct members, faultdefs and enum values #2427. 2025-09-06 16:18:33 +02:00
Christoffer Lerno
95375a2591 Overloading &[] should be enough for foreach. #2466 2025-09-06 15:18:26 +02:00
Christoffer Lerno
b7115e9c70 Correctly silence "unsupported architecture" warning with --quiet #2465 2025-09-06 12:09:31 +02:00
Zack Puhl
078d9dc0b7 Add LinkedList Operators and Update Tests (#2438)
* Add LinkedList Operators and Update Tests
* add linkedlist printing and `@new` macros (single-line init and pool-capable)
* add linkedlist node and reg iterator; comparisons w/ ==
* Fix benchmarks. Drop random access to the linked list using []. Only return a direct array view.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-06 11:57:21 +02:00
Book-reader
79c0c8e082 Build linux binaries for releases with proper curl support (#2464)
* Build linux binaries with proper curl support

* Move vendor-fetch tests to a more appropriate location
2025-09-06 11:00:07 +02:00
Christoffer Lerno
69b3263a00 - Added path::home_directory, path::documents_directory, path::videos_directory, path::pictures_directory, path::desktop_directory, path::screenshots_directory,
`path::public_share_directory`, `path::templates_directory`, `path::saved_games_directory`, `path::music_directory`, `path::downloads_directory`.
  Fix codegen bug in expressions like `foo(x()) ?? io::EOF?` causing irregular crashes.
2025-09-06 02:27:10 +02:00
dimapaloskin
cbd415881b Support .m files in expand_csources 2025-09-06 01:48:20 +02:00
LowByteFox
6dbd81a6f9 add ability for TinyCC to compile c3c (#2459)
* add ability for TinyCC to compile c3c

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-09-06 01:47:48 +02:00
Christoffer Lerno
e605a21fd3 Revert "Revert 0.7.6 code for 0.7.5 re-release"
This reverts commit d1349c9cfb.
2025-09-05 23:30:35 +02:00
381 changed files with 13312 additions and 5713 deletions

View File

@@ -8,10 +8,10 @@ on:
env:
LLVM_RELEASE_VERSION_WINDOWS: 18
LLVM_RELEASE_VERSION_MAC: 17
LLVM_RELEASE_VERSION_LINUX: 17
LLVM_RELEASE_VERSION_MAC: 18
LLVM_RELEASE_VERSION_LINUX: 19
LLVM_RELEASE_VERSION_OPENBSD: 19
LLVM_RELEASE_VERSION_UBUNTU22: 17
LLVM_RELEASE_VERSION_UBUNTU22: 19
LLVM_DEV_VERSION: 22
jobs:
@@ -88,16 +88,16 @@ jobs:
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55 --print-linking examples\raylib\raylib_tetris.c3
- name: run compiler tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
- name: Compile run unit tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 -D SLOW_TESTS
- name: run compiler tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite/ --no-terminal
- name: Test python script
run: |
py msvc_build_libraries.py --accept-license
@@ -236,7 +236,8 @@ jobs:
- uses: actions/checkout@v4
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
sudo apt-get update
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev
- name: Install Clang ${{matrix.llvm_version}}
run: |
@@ -381,6 +382,10 @@ jobs:
./build/c3c init myproject
ls myproject
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib55
- name: run compiler tests
run: |
cd test
@@ -416,7 +421,8 @@ jobs:
- uses: actions/checkout@v4
- name: Install common deps
run: |
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl
sudo apt-get update
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build curl libcurl4-openssl-dev
- name: Install Clang ${{matrix.llvm_version}}
run: |
@@ -460,6 +466,7 @@ jobs:
-DLLVM_ENABLE_LIBXML2=OFF \
-DC3_LLVM_VERSION=${{matrix.llvm_version}}.1
cmake --build build
- name: Compile and run some examples
run: |
cd resources
@@ -506,6 +513,10 @@ jobs:
cd resources/testproject
../../build/c3c run -vvv --linker=builtin --trust=full
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib55
- name: run compiler tests
run: |
cd test
@@ -766,7 +777,7 @@ jobs:
uses: vmactions/openbsd-vm@main
with:
prepare: |
pkg_add cmake llvm-19.1.7p3 ninja
pkg_add cmake llvm-20.1.8p1 ninja
run: |
echo "CMake"
@@ -893,7 +904,7 @@ jobs:
- run: mv c3-ubuntu-22-Debug/c3-ubuntu-22-Debug.tar.gz c3-ubuntu-22-Debug/c3-ubuntu-22-debug.tar.gz
- run: mv c3-macos-Release/c3-macos-Release.zip c3-macos-Release/c3-macos.zip
- run: mv c3-macos-Debug/c3-macos-Debug.zip c3-macos-Debug/c3-macos-debug.zip
- run: gh release delete latest-prerelease --cleanup-tag -y || true
- run: gh release delete latest-prerelease-tag --cleanup-tag -y || true
- run: echo "RELEASE_NAME=latest-prerelease-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
- id: create_release
@@ -901,7 +912,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest-prerelease
tag_name: latest-prerelease-tag
name: ${{ env.RELEASE_NAME }}
draft: false
prerelease: true

View File

@@ -65,20 +65,21 @@ if(MSVC)
else()
add_compile_options(-gdwarf-3 -fno-exceptions)
# add_compile_options(-fsanitize=address,undefined)
# add_link_options(-fsanitize=address,undefined)
#add_compile_options(-fsanitize=address,undefined)
#add_link_options(-fsanitize=address,undefined)
endif()
# Options
set(C3_LINK_DYNAMIC OFF CACHE BOOL "Link dynamically with LLVM/LLD libs")
set(C3_WITH_LLVM ON CACHE BOOL "Build with LLVM")
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-in mimalloc")
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_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
set(C3_LINK_DYNAMIC OFF CACHE BOOL "Link dynamically with LLVM/LLD libs")
set(C3_WITH_LLVM ON CACHE BOOL "Build with LLVM")
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
set(C3_USE_MIMALLOC OFF CACHE BOOL "Use built-in mimalloc")
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_ENABLE_CLANGD_LSP OFF CACHE BOOL "Enable/Disable output of compile commands during generation")
set(LLVM_CRT_LIBRARY_DIR "" CACHE STRING "Use custom llvm's compiler-rt directory")
set(TCC_LIB_PATH "/usr/lib/tcc/libtcc1.a" CACHE STRING "Use custom libtcc1.a path")
set(C3_OPTIONS
C3_LINK_DYNAMIC
@@ -575,6 +576,17 @@ else()
target_link_options(c3c PRIVATE -pthread)
endif()
if(CMAKE_C_COMPILER_ID STREQUAL "TinyCC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--no-eh-frame-hdr -z noexecstack")
# Link the static tcc runtime archive if it exists
if(EXISTS "${TCC_LIB_PATH}")
target_link_libraries(c3c "${TCC_LIB_PATH}")
else()
message(FATAL_ERROR "TCC runtime not found at ${TCC_LIB_PATH}; Ensure the path is correct.")
endif()
endif()
install(TARGETS c3c DESTINATION bin)
install(DIRECTORY lib/ DESTINATION lib/c3)

View File

@@ -59,7 +59,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at . All
reported by contacting the project team at info@c3-lang.org. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.

View File

@@ -142,7 +142,7 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.7.5**.
The current stable version of the compiler is **version 0.7.7**.
The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis.
@@ -151,7 +151,7 @@ Follow the issues [here](https://github.com/c3lang/c3c/issues).
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
The compiler is currently verified to compile on Linux, Windows and MacOS.
The compiler is currently verified to compile on Linux, OpenBSD, Windows and MacOS.
**Support matrix**
@@ -209,12 +209,43 @@ This installs the latest prerelease build, as opposed to the latest released ver
3. If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
4. Run `c3c.exe`.
#### Installing on Windows with the install script
Open a PowerShell terminal (you may need to run it as an administrator) and run the following command:
```bash
iwr -useb https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.ps1 | iex
```
The script will inform you once the installation is successful and add the `~/.c3` directory to your PATH, which will allow you to run the c3c command from any location.
You can choose another version with option `C3_VERSION`.
For example, you can force the installation of the 0.7.4 version:
```bash
$env:C3_VERSION='0.7.4'; powershell -ExecutionPolicy Bypass -Command "iwr -useb https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.ps1 | iex"
```
If you don't have Visual Studio 17 installed you can either do so, or run the `msvc_build_libraries.py` Python script which will download the necessary files to compile on Windows.
#### Installing on Debian with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-linux-debug.tar.gz))
2. Unpack executable and standard lib.
3. Run `./c3c`.
#### Installing on Debian with the install script
Open a terminal and run the following command:
```bash
curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.sh | bash
```
The C3 compiler will be installed, and the script will also update your ~/.bashrc to include `~/.c3` in your PATH, allowing you to invoke the c3c command from anywhere. You might need to restart your terminal or source your shell for the changes to take effect.
You can choose another version with option `C3_VERSION`.
For example, you can force the installation of the 0.7.4 version:
```bash
curl -fsSL https://raw.githubusercontent.com/c3lang/c3c/refs/heads/master/install/install.sh | C3_VERSION=0.7.4 bash
```
#### Installing on Ubuntu with precompiled binaries
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20.tar.gz)
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest-prerelease/c3-ubuntu-20-debug.tar.gz))
@@ -296,6 +327,28 @@ You can access `c3c` via [flake.nix](./flake.nix), which will contain the latest
}
```
### Installing on Gentoo
`c3c` is available in the [Gentoo GURU overlay](https://wiki.gentoo.org/wiki/Project:GURU).
Enable and sync the GURU repository (if not already done):
```sh
sudo eselect repository enable guru
sudo emaint sync -r guru
```
Install `c3c` with:
```sh
sudo emerge -av dev-lang/c3c
```
* The compiler binary is installed to `/usr/bin/c3c`.
* The standard library is installed to `/usr/lib/c3`.
For Gentoo-specific issues, please use the [Gentoo Bugzilla](https://bugs.gentoo.org/) (Product: *GURU*).
#### Building via Docker
You can build `c3c` using an Ubuntu container. By default, the script will build through Ubuntu 22.04. You can specify the version by passing the `UBUNTU_VERSION` environment variable.

View File

@@ -107,7 +107,8 @@ fn void hash_speeds_of_many_random_values() => @pool()
foreach (&v : vwideints) *v = (uint128)random::next(&rand, uint.max);
char[48][] zstrs = allocator::new_array(tmem, char[48], $arrsz)[:$arrsz];
String[$arrsz] strs;
String[] strs = mem::temp_array(String, $arrsz);
foreach (x, &v : zstrs)
{
foreach (&c : (*v)[:random::next(&rand, 48)]) *c = (char)random::next(&rand, char.max);
@@ -195,7 +196,7 @@ fn void random_access_string_keys() => @pool()
v.tinit();
usz pseudo_checksum = 0;
String[5_000] saved;
String[] saved = mem::temp_array(String, 5_000);
for (usz i = 0; i < saved.len; ++i)
{

View File

@@ -0,0 +1,38 @@
module linkedlist_benchmarks;
import std::collections::linkedlist;
LinkedList{int} long_list;
const HAY = 2;
const NEEDLE = 1000;
fn void bench_setup() @init
{
set_benchmark_warmup_iterations(3);
set_benchmark_max_iterations(4096);
int[*] haystack = { [0..999] = HAY };
long_list = linkedlist::@new{int}(mem, haystack[..]);
long_list.push(NEEDLE);
long_list.push_all(haystack[..]);
}
// ==============================================================================================
module linkedlist_benchmarks @benchmark;
String die_str = "Failed to find the value `1`. Is something broken?";
fn void foreach_iterator()
{
foreach (v : long_list.array_view()) if (v == NEEDLE) return;
runtime::@kill_benchmark(die_str);
}
fn void foreach_r_iterator()
{
foreach_r (v : long_list.array_view()) if (v == NEEDLE) return;
runtime::@kill_benchmark(die_str);
}

View File

@@ -19,7 +19,7 @@ macro void trim_bench($trim_str, String $target = WHITESPACE_TARGET) => @pool()
$switch:
$case $typeof($trim_str) == String:
s1 = s2.trim($trim_str);
$case $typeof($$trim_str) == AsciiCharset:
$case $typeof($trim_str) == AsciiCharset:
s1 = s2.trim_charset($trim_str);
$default: $error "Unable to determine the right String `trim` operation to use.";
$endswitch

View File

@@ -0,0 +1,31 @@
module std::crypto::aes_bench;
import std::crypto::aes;
fn void init() @init
{
set_benchmark_warmup_iterations(5);
set_benchmark_max_iterations(10_000);
}
AesType aes = AES256;
char[] key = x"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4";
char[] text = x"6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710";
char[] cipher = x"601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c52b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6";
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
fn void bench_ctr_xcrypt() @benchmark
{
char[64] out;
Aes ctx;
// encrypt
ctx.init(aes, key, iv);
ctx.encrypt_buffer(text, &out);
// decrypt
ctx.init(aes, key, iv);
ctx.decrypt_buffer(cipher, &out);
}

View File

@@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
: ${DOCKER:=docker}
: ${IMAGE:="c3c-builder"}
@@ -41,4 +41,4 @@ exec $DOCKER run -i --rm \
-DCMAKE_DLLTOOL=llvm-dlltool-$LLVM_VERSION \
-DC3_LLVM_VERSION=auto && \
cmake --build build && \
cp -r build/c3c build/lib bin"
cp -r build/c3c build/lib bin"

View File

@@ -6,7 +6,7 @@ ENV LLVM_DEV_VERSION=20
ARG CMAKE_VERSION=3.20
RUN apt-get update && apt-get install -y wget gnupg software-properties-common zlib1g zlib1g-dev python3 ninja-build curl g++ && \
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 && \
mkdir -p /opt/cmake && \
sh cmake-${CMAKE_VERSION}-linux-x86_64.sh --prefix=/opt/cmake --skip-license && \
@@ -46,4 +46,4 @@ RUN groupadd -g 1337 c3c && \
USER c3c
ENV PATH="/opt/cmake/bin:${PATH}"
WORKDIR /home/c3c
WORKDIR /home/c3c

189
install/install.ps1 Normal file
View File

@@ -0,0 +1,189 @@
<#
.SYNOPSIS
C3 install script.
.DESCRIPTION
This script installs C3 on Windows from the command line.
.PARAMETER C3Version
Specifies the version of C3 to install.
Default is 'latest'. Can also be set via environment variable 'C3_VERSION'.
.PARAMETER C3Home
Specifies C3's installation directory.
Default is '$Env:USERPROFILE\.c3'. Can also be set via environment variable 'C3_HOME'.
.PARAMETER NoPathUpdate
If specified, the script will not modify the PATH environment variable.
.PARAMETER C3Repourl
Specifies the repository URL of C3.
Default is 'https://github.com/c3lang/c3c'. Can also be set via environment variable 'C3_REPOURL'.
.LINK
https://c3-lang.org/
.LINK
https://github.com/c3lang/c3c
#>
# Script parameters with defaults
param (
[string] $C3Version = 'latest',
[string] $C3Home = "$Env:USERPROFILE\.c3",
[switch] $NoPathUpdate,
[string] $C3Repourl = 'https://github.com/c3lang/c3c'
)
# Enable strict mode for better error handling
Set-StrictMode -Version Latest
# Function to broadcast environment variable changes to Windows system
function Publish-Env {
# Add P/Invoke type if it does not exist
if (-not ("Win32.NativeMethods" -as [Type])) {
Add-Type -Namespace Win32 -Name NativeMethods -MemberDefinition @"
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam,
uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);
"@
}
# Constants for broadcasting environment changes
$HWND_BROADCAST = [IntPtr] 0xffff
$WM_SETTINGCHANGE = 0x1a
$result = [UIntPtr]::Zero
# Broadcast the message to all windows
[Win32.Nativemethods]::SendMessageTimeout($HWND_BROADCAST,
$WM_SETTINGCHANGE,
[UIntPtr]::Zero,
"Environment",
2,
5000,
[ref] $result
) | Out-Null
}
# Function to write or update an environment variable in the registry
function Write-Env {
param(
[String] $name,
[String] $val,
[Switch] $global
)
# Determine the registry key based on scope (user or system)
$RegisterKey = if ($global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment', $true)
# If value is null, delete the variable
if ($null -eq $val) {
$EnvRegisterKey.DeleteValue($name)
} else {
# Determine the correct registry value type
$RegistryValueKind = if ($val.Contains('%')) {
[Microsoft.Win32.RegistryValueKind]::ExpandString
} elseif ($EnvRegisterKey.GetValue($name)) {
$EnvRegisterKey.GetValueKind($name)
} else {
[Microsoft.Win32.RegistryValueKind]::String
}
$EnvRegisterKey.SetValue($name, $val, $RegistryValueKind)
}
# Broadcast the change to the system
Publish-Env
}
# Function to get an environment variable from the registry
function Get-Env {
param(
[String] $name,
[Switch] $global
)
# Determine registry key based on scope
$RegisterKey = if ($global) {
Get-Item -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager'
} else {
Get-Item -Path 'HKCU:'
}
$EnvRegisterKey = $RegisterKey.OpenSubKey('Environment')
$RegistryValueOption = [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames
# Retrieve the value without expanding environment variables
$EnvRegisterKey.GetValue($name, $null, $RegistryValueOption)
}
# Override defaults if environment variables exist
if ($Env:C3_VERSION) { $C3Version = $Env:C3_VERSION }
if ($Env:C3_HOME) { $C3Home = $Env:C3_HOME }
if ($Env:C3_NO_PATH_UPDATE) { $NoPathUpdate = $true }
if ($Env:C3_REPOURL) { $C3Repourl = $Env:C3_REPOURL -replace '/$', '' }
# Set binary name
$BINARY = "c3-windows"
# Determine the download URL based on version
if ($C3Version -eq 'latest') {
$DOWNLOAD_URL = "$C3Repourl/releases/latest/download/$BINARY.zip"
} else {
# Ensure version starts with 'v'
$C3Version = "v" + ($C3Version -replace '^v', '')
$DOWNLOAD_URL = "$C3Repourl/releases/download/$C3Version/$BINARY.zip"
}
$BinDir = $C3Home
Write-Host "This script will automatically download and install C3 ($C3Version) for you."
Write-Host "Getting it from this url: $DOWNLOAD_URL"
Write-Host "The binary will be installed into '$BinDir'"
# Create temporary file for download
$TEMP_FILE = [System.IO.Path]::GetTempFileName()
try {
# Download the binary
Invoke-WebRequest -Uri $DOWNLOAD_URL -OutFile $TEMP_FILE
# Remove previous installation if it exists
if (Test-Path -Path $BinDir) {
Remove-Item -Path $BinDir -Recurse -Force | Out-Null
}
# Rename temp file to .zip
$ZIP_FILE = $TEMP_FILE + ".zip"
Rename-Item -Path $TEMP_FILE -NewName $ZIP_FILE
# Extract downloaded zip
Expand-Archive -Path $ZIP_FILE -DestinationPath $Env:USERPROFILE -Force
# Rename extracted folder to target installation directory
Rename-Item -Path "$Env:USERPROFILE/c3-windows-Release" -NewName $BinDir
} catch {
Write-Host "Error: '$DOWNLOAD_URL' is not available or failed to download"
exit 1
} finally {
# Cleanup temporary zip file
Remove-Item -Path $ZIP_FILE
}
# Update PATH environment variable if requested
if (!$NoPathUpdate) {
$PATH = Get-Env 'PATH'
if ($PATH -notlike "*$BinDir*") {
Write-Output "Adding $BinDir to PATH"
# Persist PATH for future sessions
Write-Env -name 'PATH' -val "$BinDir;$PATH"
# Update PATH for current session
$Env:PATH = "$BinDir;$PATH"
Write-Output "You may need to restart your shell"
} else {
Write-Output "$BinDir is already in PATH"
}
} else {
Write-Output "You may need to update your PATH manually to use c3"
}

137
install/install.sh Normal file
View File

@@ -0,0 +1,137 @@
#!/usr/bin/env bash
set -euo pipefail # Exit on error, unset variables, and fail pipelines on any error
__wrap__() {
# Version of C3 to install (default: latest)
VERSION="${C3_VERSION:-latest}"
# Installation directory (default: ~/.c3)
C3_HOME="${C3_HOME:-$HOME/.c3}"
# Expand '~' if present
C3_HOME="${C3_HOME/#\~/$HOME}"
BIN_DIR="$C3_HOME"
# C3 compiler repository URL
REPO="c3lang/c3c"
REPOURL="${C3_REPOURL:-https://github.com/$REPO}"
detect_platform() {
# Detects the operating system
local os_type
os_type="$(uname -s | tr '[:upper:]' '[:lower:]')"
case "$os_type" in
darwin) # macOS
echo "macos"
;;
msys*|mingw*|cygwin*) # Windows (Git Bash / MSYS / Cygwin)
IS_MSYS=true
echo "windows"
;;
*)
echo $os_type
;;
esac
}
# Determine platform string
PLATFORM="$(detect_platform)"
# File extension for the archive (ZIP for Windows, TAR.GZ for others)
EXT=".tar.gz"
BINARY="c3-${PLATFORM}"
if [[ "${IS_MSYS:-false}" == true ]]; then
EXT=".zip"
fi
# Determine the download URL (latest release or specific version)
if [[ "$VERSION" == "latest" ]]; then
URL="${REPOURL%/}/releases/latest/download/${BINARY}${EXT}"
else
URL="${REPOURL%/}/releases/download/v${VERSION#v}/${BINARY}${EXT}"
fi
# Temporary file for the downloaded archive
TEMP_FILE="$(mktemp "${TMPDIR:-/tmp}/.C3_install.XXXXXXXX")"
trap 'rm -f "$TEMP_FILE"' EXIT # Ensure temp file is deleted on exit
download_file() {
# Download the archive using curl or wget
# Check that the curl version is not 8.8.0, which is broken for --write-out
# https://github.com/curl/curl/issues/13845
if command -v curl >/dev/null && [[ "$(curl --version | awk 'NR==1{print $2}')" != "8.8.0" ]]; then
curl -SL "$URL" -o "$TEMP_FILE"
elif command -v wget >/dev/null; then
wget -O "$TEMP_FILE" "$URL"
else
echo "Error: curl or wget is required." >&2
exit 1
fi
}
echo "Downloading C3 ($VERSION) from $URL..."
download_file
# Remove existing installation and extract the new one
rm -rf "$BIN_DIR"
if [[ "$EXT" == ".zip" ]]; then
unzip "$TEMP_FILE" -d "$HOME"
else
tar -xzf "$TEMP_FILE" -C "$HOME"
fi
# Move extracted folder to installation directory
mv "$HOME/c3" "$BIN_DIR"
chmod +x "$BIN_DIR/c3c" # Ensure compiler binary is executable
echo "✅ Installation completed in $BIN_DIR"
# Update PATH unless suppressed by environment variable
if [ -n "${C3_NO_PATH_UPDATE:-}" ]; then
echo "No path update because C3_NO_PATH_UPDATE is set"
else
update_shell() {
FILE="$1"
LINE="$2"
# Create shell config file if missing
if [ ! -f "$FILE" ]; then
touch "$FILE"
fi
# Add the PATH line if not already present
if ! grep -Fxq "$LINE" "$FILE"; then
echo "Updating '${FILE}'"
echo "$LINE" >>"$FILE"
echo "Please restart or source your shell."
fi
}
# Detect the current shell and add C3 to its PATH
case "$(basename "${SHELL-}")" in
bash)
# Default to bashrc as that is used in non login shells instead of the profile.
LINE="export PATH=\"${BIN_DIR}:\$PATH\""
update_shell ~/.bashrc "$LINE"
;;
fish)
LINE="fish_add_path ${BIN_DIR}"
update_shell ~/.config/fish/config.fish "$LINE"
;;
zsh)
LINE="export PATH=\"${BIN_DIR}:\$PATH\""
update_shell ~/.zshrc "$LINE"
;;
tcsh)
LINE="set path = ( ${BIN_DIR} \$path )"
update_shell ~/.tcshrc "$LINE"
;;
'')
echo "warn: Could not detect shell type." >&2
echo " Please permanently add '${BIN_DIR}' to your \$PATH to enable the 'c3c' command." >&2
;;
*)
echo "warn: Could not update shell $(basename "$SHELL")" >&2
echo " Please permanently add '${BIN_DIR}' to your \$PATH to enable the 'c3c' command." >&2
;;
esac
fi
}
__wrap__

View File

@@ -58,7 +58,7 @@ fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired,
nextcase;
$endif
default:
unreachable("Unsuported size (%d) for atomic_compare_exchange", size);
unreachable("Unsupported size (%d) for atomic_compare_exchange", size);
}
return 0;
}
}

View File

@@ -2,10 +2,10 @@
// 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::anylist;
import std::io,std::math;
import std::collections::interfacelist;
alias AnyPredicate = fn bool(any value);
alias AnyTest = fn bool(any type, any context);
alias AnyPredicate = InterfacePredicate {any};
alias AnyTest = InterfaceTest {any};
<*
The AnyList contains a heterogenous set of types. Anything placed in the
@@ -18,282 +18,7 @@ alias AnyTest = fn bool(any type, any context);
If we're not doing pop, then things are easier, since we can just hand over
the existing any.
*>
struct AnyList (Printable)
{
usz size;
usz capacity;
Allocator allocator;
any* entries;
}
<*
Initialize the list. If not initialized then it will use the temp allocator
when something is pushed to it.
@param [&inout] allocator : "The allocator to use"
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
*>
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::alloc_array(allocator, any, initial_capacity);
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity : "The initial capacity to reserve"
*>
fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
{
return self.init(tmem, initial_capacity) @inline;
}
fn bool AnyList.is_initialized(&self) @inline => self.allocator != null;
<*
Push an element on the list by cloning it.
*>
macro void AnyList.push(&self, element)
{
if (!self.allocator) self.allocator = tmem;
self._append(allocator::clone(self.allocator, element));
}
<*
Free a retained element removed using *_retained.
*>
fn void AnyList.free_element(&self, any element) @inline
{
allocator::free(self.allocator, element.ptr);
}
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@param $Type : "The type we assume the value has"
@return "The last value as the type given"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop(&self, $Type)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return *anycast(self.entries[--self.size], $Type);
}
<*
Copy the last value, pop it and return the copy of it.
@param [&inout] allocator : "The allocator to use for copying"
@return "A copy of the last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.copy_pop(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
<*
Copy the last value, pop it and return the copy of it.
@return "A temp copy of the last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
<*
Pop the last value. It must later be released using `list.free_element()`.
@return "The last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.pop_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
<*
Remove all elements in the list.
*>
fn void AnyList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
self.free_element(self.entries[i]);
}
self.size = 0;
}
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@param $Type : "The type we assume the value has"
@return "The first value as the type given"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop_first(&self, $Type)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
<*
Pop the first value. It must later be released using `list.free_element()`.
@return "The first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.pop_first_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
Copy the first value, pop it and return the copy of it.
@param [&inout] allocator : "The allocator to use for copying"
@return "A copy of the first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
defer self.remove_at(0);
return allocator::clone_any(allocator, self.entries[0]);
}
<*
Copy the first value, pop it and return the temp copy of it.
@return "A temp copy of the first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
<*
Remove the element at the particular index.
@param index : "The index of the element to remove"
@require index < self.size
*>
fn void AnyList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
<*
Add all the elements in another AnyList.
@param [&in] other_list : "The list to add"
*>
fn void AnyList.add_all(&self, AnyList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
foreach (value : other_list)
{
self.entries[self.size++] = allocator::clone_any(self.allocator, value);
}
}
<*
Reverse the order of the elements in the list.
*>
fn void AnyList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
self.swap(i, end - i);
}
}
<*
Return a view of the data as a slice.
@return "The slice view"
*>
fn any[] AnyList.array_view(&self)
{
return self.entries[:self.size];
}
<*
Push an element to the front of the list.
@param value : "The value to push to the list"
*>
macro void AnyList.push_front(&self, value)
{
self.insert_at(0, value);
}
<*
Insert an element at a particular index.
@param index : "the index where the element should be inserted"
@param type : "the value to insert"
@require index <= self.size : "The index is out of bounds"
*>
macro void AnyList.insert_at(&self, usz index, type)
{
if (index == self.size)
{
self.push(type);
return;
}
any value = allocator::copy(self.allocator, type);
self._insert_at(self, index, value);
}
<*
Remove the last element in the list. The list may not be empty.
@require self.size > 0 : "The list was already empty"
*>
fn void AnyList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
<*
Remove the first element in the list, the list may not be empty.
@require self.size > 0
*>
fn void AnyList.remove_first(&self)
{
self.remove_at(0);
}
typedef AnyList = inline InterfaceList {any};
<*
Return the first element by value, assuming it is the given type.
@@ -313,10 +38,7 @@ macro AnyList.first(&self, $Type)
@return "The first element"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.first_any(&self) @inline
{
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
}
fn any? AnyList.first_any(&self) @inline => InterfaceList {any}.first(self);
<*
Return the last element by value, assuming it is the given type.
@@ -336,29 +58,36 @@ macro AnyList.last(&self, $Type)
@return "The last element"
@return? NO_MORE_ELEMENT
*>
fn any? AnyList.last_any(&self) @inline
fn any? AnyList.last_any(&self) @inline => InterfaceList {any}.last(self);
<*
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@param $Type : "The type we assume the value has"
@return "The last value as the type given"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop(&self, $Type)
{
return self.size ? self.entries[self.size - 1] : 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);
}
<*
Return whether the list is empty.
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@return "True if the list is empty"
@param $Type : "The type we assume the value has"
@return "The first value as the type given"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
fn bool AnyList.is_empty(&self) @inline
macro AnyList.pop_first(&self, $Type)
{
return !self.size;
}
<*
Return the length of the list.
@return "The number of elements in the list"
*>
fn usz AnyList.len(&self) @operator(len) @inline
{
return self.size;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
<*
@@ -383,222 +112,11 @@ macro AnyList.get(&self, usz index, $Type)
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
@require index < self.size : "Index out of range"
*>
fn any AnyList.get_any(&self, usz index) @inline @operator([])
{
return self.entries[index];
}
fn any AnyList.get_any(&self, usz index) @inline @operator([]) => InterfaceList {any}.get(self, index);
<*
Completely free and clear a list.
Return the length of the list.
@return "The number of elements in the list"
*>
fn void AnyList.free(&self)
{
if (!self.allocator) return;
self.clear();
allocator::free(self.allocator, self.entries);
self.capacity = 0;
self.entries = null;
}
<*
Swap two elements in a list.
@param i : "Index of one of the elements"
@param j : "Index of the other element"
@require i < self.size : "The first index is out of range"
@require j < self.size : "The second index is out of range"
*>
fn void AnyList.swap(&self, usz i, usz j)
{
any temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
<*
Print the list to a formatter.
*>
fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
<*
Remove any elements matching the predicate.
@param filter : "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz AnyList.remove_if(&self, AnyPredicate filter)
{
return self._remove_if(filter, false);
}
<*
Retain the elements matching the predicate.
@param selection : "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz AnyList.retain_if(&self, AnyPredicate selection)
{
return self._remove_if(selection, true);
}
<*
Remove any elements matching the predicate.
@param filter : "The function to determine if it should be removed or not"
@param context : "The context to the function"
@return "the number of deleted elements"
*>
fn usz AnyList.remove_using_test(&self, AnyTest filter, any context)
{
return self._remove_using_test(filter, false, context);
}
<*
Retain any elements matching the predicate.
@param selection : "The function to determine if it should be retained or not"
@param context : "The context to the function"
@return "the number of deleted elements"
*>
fn usz AnyList.retain_using_test(&self, AnyTest selection, any context)
{
return self._remove_using_test(selection, true, context);
}
<*
Reserve memory so that at least the `min_capacity` exists.
@param min_capacity : "The min capacity to hold"
*>
fn void AnyList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = tmem;
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, any.sizeof * min_capacity);
self.capacity = min_capacity;
}
<*
Set the element at any index.
@param index : "The index where to set the value."
@param value : "The value to set"
@require index <= self.size : "Index out of range"
*>
macro void AnyList.set(&self, usz index, value)
{
if (index == self.size)
{
self.push(value);
return;
}
self.free_element(self.entries[index]);
self.entries[index] = allocator::copy(self.allocator, value);
}
// -- private
fn void AnyList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
}
fn void AnyList._append(&self, any element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
<*
@require index < self.size
*>
fn void AnyList._insert_at(&self, usz index, any value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = value;
}
macro usz AnyList._remove_using_test(&self, AnyTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
macro usz AnyList._remove_if(&self, AnyPredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && filter(&self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(&self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(&self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}
fn usz AnyList.len(&self) @operator(len) @inline => InterfaceList {any}.len(self);

View File

@@ -11,7 +11,7 @@ alias ElementPredicate = fn bool(Type *type);
alias ElementTest = fn bool(Type *type, any context);
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
const ELEMENT_IS_POINTER = Type.kindof == POINTER;
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
macro bool type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct ElasticArray (Printable)
{
@@ -458,4 +458,4 @@ fn usz ElasticArray.compact_count(&self) @if(ELEMENT_IS_POINTER)
fn usz ElasticArray.compact(&self) @if(ELEMENT_IS_POINTER)
{
return list_common::list_compact(self);
}
}

View File

@@ -30,8 +30,10 @@ struct HashMap (Printable)
{
Entry*[] table;
Allocator allocator;
uint count; // Number of elements
uint threshold; // Resize limit
<* Last inserted LinkedEntry *>
uint count;
<* Resize limit *>
uint threshold;
float load_factor;
}
@@ -605,4 +607,4 @@ macro uint index_for(uint hash, uint capacity) @private
return hash & (capacity - 1);
}
int dummy @local;
int dummy @local;

View File

@@ -25,8 +25,10 @@ struct HashSet (Printable)
{
Entry*[] table;
Allocator allocator;
usz count; // Number of elements
usz threshold; // Resize limit
<* Number of elements *>
usz count;
<* Resize limit *>
usz threshold;
float load_factor;
}

View File

@@ -0,0 +1,539 @@
// Copyright (c) 2024-2025 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.
<*
@require Type.kindof == INTERFACE || Type.kindof == ANY : "The kind of an interfacelist must be an interface or `any`"
*>
module std::collections::interfacelist {Type};
import std::io,std::math;
alias InterfacePredicate = fn bool(Type value);
alias InterfaceTest = fn bool(Type type, Type context);
<*
The InterfaceList contains a heterogenous set of types implementing an interface. anything placed in the
list will shallowly copied in order to be stored as the interface. This means
that the list will copy and free its elements.
However, because we're getting interface values back when we pop, those operations
need to take an allocator, as we can only copy then pop then return the copy.
If we're not doing pop, then things are easier, since we can just hand over
the existing value.
*>
struct InterfaceList (Printable)
{
usz size;
usz capacity;
Allocator allocator;
Type* entries;
}
<*
Initialize the list. If not initialized then it will use the temp allocator
when something is pushed to it.
@param [&inout] allocator : "The allocator to use"
@param initial_capacity : "The initial capacity to reserve, defaults to 16"
*>
fn InterfaceList* InterfaceList.init(&self, Allocator allocator, usz initial_capacity = 16)
{
self.allocator = allocator;
self.size = 0;
if (initial_capacity > 0)
{
initial_capacity = math::next_power_of_2(initial_capacity);
self.entries = allocator::alloc_array(allocator, Type, initial_capacity);
}
else
{
self.entries = null;
}
self.capacity = initial_capacity;
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity : "The initial capacity to reserve"
*>
fn InterfaceList* InterfaceList.tinit(&self, usz initial_capacity = 16)
{
return self.init(tmem, initial_capacity) @inline;
}
fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null;
<*
Push an element on the list by cloning it.
@require $defined(Type t = &element) : "Element must implement the interface"
*>
macro void InterfaceList.push(&self, element)
{
if (!self.allocator) self.allocator = tmem;
self._append(allocator::clone(self.allocator, element));
}
<*
Free a retained element removed using *_retained.
*>
fn void InterfaceList.free_element(&self, Type element) @inline
{
allocator::free(self.allocator, element.ptr);
}
<*
Copy the last value, pop it and return the copy of it.
@param [&inout] allocator : "The allocator to use for copying"
@return "A copy of the last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.copy_pop(&self, Allocator allocator)
{
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]);
}
<*
Copy the last value, pop it and return the copy of it.
@return "A temp copy of the last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.tcopy_pop(&self) => self.copy_pop(tmem);
<*
Pop the last value. It must later be released using `list.free_element()`.
@return "The last value if it exists"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.pop_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
<*
Remove all elements in the list.
*>
fn void InterfaceList.clear(&self)
{
for (usz i = 0; i < self.size; i++)
{
self.free_element(self.entries[i]);
}
self.size = 0;
}
<*
Pop the first value. It must later be released using `list.free_element()`.
@return "The first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.pop_first_retained(&self)
{
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
Copy the first value, pop it and return the copy of it.
@param [&inout] allocator : "The allocator to use for copying"
@return "A copy of the first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.copy_pop_first(&self, Allocator allocator)
{
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]);
}
<*
Copy the first value, pop it and return the temp copy of it.
@return "A temp copy of the first value if it exists"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
<*
Remove the element at the particular index.
@param index : "The index of the element to remove"
@require index < self.size
*>
fn void InterfaceList.remove_at(&self, usz index)
{
if (!--self.size || index == self.size) return;
self.free_element(self.entries[index]);
self.entries[index .. self.size - 1] = self.entries[index + 1 .. self.size];
}
<*
Add all the elements in another InterfaceList.
@param [&in] other_list : "The list to add"
*>
fn void InterfaceList.add_all(&self, InterfaceList* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
foreach (value : other_list)
{
self.entries[self.size++] = (Type)allocator::clone_any(self.allocator, value);
}
}
<*
Reverse the order of the elements in the list.
*>
fn void InterfaceList.reverse(&self)
{
if (self.size < 2) return;
usz half = self.size / 2U;
usz end = self.size - 1;
for (usz i = 0; i < half; i++)
{
self.swap(i, end - i);
}
}
<*
Return a view of the data as a slice.
@return "The slice view"
*>
fn Type[] InterfaceList.array_view(&self)
{
return self.entries[:self.size];
}
<*
Push an element to the front of the list.
@param value : "The value to push to the list"
@require $defined(Type t = &value) : "Value must implement the interface"
*>
macro void InterfaceList.push_front(&self, value)
{
self.insert_at(0, value);
}
<*
Insert an element at a particular index.
@param index : "the index where the element should be inserted"
@param type : "the value to insert"
@require index <= self.size : "The index is out of bounds"
@require $defined(Type t = &type) : "Type must implement the interface"
*>
macro void InterfaceList.insert_at(&self, usz index, type)
{
if (index == self.size)
{
self.push(type);
return;
}
Type value = allocator::clone(self.allocator, type);
self._insert_at(self, index, value);
}
<*
Remove the last element in the list. The list may not be empty.
@require self.size > 0 : "The list was already empty"
*>
fn void InterfaceList.remove_last(&self)
{
self.free_element(self.entries[--self.size]);
}
<*
Remove the first element in the list, the list may not be empty.
@require self.size > 0
*>
fn void InterfaceList.remove_first(&self)
{
self.remove_at(0);
}
<*
Return the first element
@return "The first element"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.first(&self) @inline
{
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
}
<*
Return the last element
@return "The last element"
@return? NO_MORE_ELEMENT
*>
fn Type? InterfaceList.last(&self) @inline
{
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
}
<*
Return whether the list is empty.
@return "True if the list is empty"
*>
fn bool InterfaceList.is_empty(&self) @inline
{
return !self.size;
}
<*
Return the length of the list.
@return "The number of elements in the list"
*>
fn usz InterfaceList.len(&self) @operator(len) @inline
{
return self.size;
}
<*
Return an element in the list.
@param index : "The index of the element to retrieve"
@return "The element at the index"
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
@require index < self.size : "Index out of range"
*>
fn Type InterfaceList.get(&self, usz index) @inline @operator([])
{
return self.entries[index];
}
<*
Completely free and clear a list.
*>
fn void InterfaceList.free(&self)
{
if (!self.allocator) return;
self.clear();
allocator::free(self.allocator, self.entries);
self.capacity = 0;
self.entries = null;
}
<*
Swap two elements in a list.
@param i : "Index of one of the elements"
@param j : "Index of the other element"
@require i < self.size : "The first index is out of range"
@require j < self.size : "The second index is out of range"
*>
fn void InterfaceList.swap(&self, usz i, usz j)
{
Type temp = self.entries[i];
self.entries[i] = self.entries[j];
self.entries[j] = temp;
}
<*
Print the list to a formatter.
*>
fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
case 0:
return formatter.print("[]")!;
case 1:
return formatter.printf("[%s]", self.entries[0])!;
default:
usz n = formatter.print("[")!;
foreach (i, element : self.entries[:self.size])
{
if (i != 0) formatter.print(", ")!;
n += formatter.printf("%s", element)!;
}
n += formatter.print("]")!;
return n;
}
}
<*
Remove Type elements matching the predicate.
@param filter : "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
{
return self._remove_if(filter, false);
}
<*
Retain the elements matching the predicate.
@param selection : "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
{
return self._remove_if(selection, true);
}
<*
Remove Type elements matching the predicate.
@param filter : "The function to determine if it should be removed or not"
@param context : "The context to the function"
@return "the number of deleted elements"
*>
fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context)
{
return self._remove_using_test(filter, false, context);
}
<*
Retain Type elements matching the predicate.
@param selection : "The function to determine if it should be retained or not"
@param context : "The context to the function"
@return "the number of deleted elements"
*>
fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context)
{
return self._remove_using_test(selection, true, context);
}
<*
Reserve memory so that at least the `min_capacity` exists.
@param min_capacity : "The min capacity to hold"
*>
fn void InterfaceList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = tmem;
min_capacity = math::next_power_of_2(min_capacity);
self.entries = allocator::realloc(self.allocator, self.entries, Type.sizeof * min_capacity);
self.capacity = min_capacity;
}
<*
Set the element at Type index.
@param index : "The index where to set the value."
@param value : "The value to set"
@require index <= self.size : "Index out of range"
@require $defined(Type t = &value) : "Value must implement the interface"
*>
macro void InterfaceList.set(&self, usz index, value)
{
if (index == self.size)
{
self.push(value);
return;
}
self.free_element(self.entries[index]);
self.entries[index] = allocator::clone(self.allocator, value);
}
// -- private
fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.reserve(new_capacity);
}
fn void InterfaceList._append(&self, Type element) @local
{
self.ensure_capacity();
self.entries[self.size++] = element;
}
<*
@require index < self.size
*>
fn void InterfaceList._insert_at(&self, usz index, Type value) @local
{
self.ensure_capacity();
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
}
self.size++;
self.entries[index] = value;
}
macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && filter(self.entries[i - 1], ctx)) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(self.entries[i - 1], ctx)) i--;
$else
while (i > 0 && !filter(self.entries[i - 1], ctx)) i--;
$endif
}
return size - self.size;
}
macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
{
// Find last index of item to be deleted.
$if $invert:
while (i > 0 && !filter(self.entries[i - 1])) i--;
$else
while (i > 0 && filter(self.entries[i - 1])) i--;
$endif
// Remove the items from this index up to the one not to be deleted.
usz n = self.size - k;
for (usz j = i; j < k; j++) self.free_element(self.entries[j]);
self.entries[i:n] = self.entries[k:n];
self.size -= k - i;
// Find last index of item not to be deleted.
$if $invert:
while (i > 0 && filter(self.entries[i - 1])) i--;
$else
while (i > 0 && !filter(self.entries[i - 1])) i--;
$endif
}
return size - self.size;
}

View File

@@ -7,16 +7,22 @@ const INITIAL_CAPACITY = 16;
struct QueueEntry
{
Value value;
QueueEntry* next; // Next in queue order
QueueEntry* prev; // Previous in queue order
<* Next in queue order *>
QueueEntry* next;
<* Previous in queue order *>
QueueEntry* prev;
}
struct LinkedBlockingQueue
{
QueueEntry* head; // First element in queue
QueueEntry* tail; // Last element in queue
usz count; // Current number of elements
usz capacity; // Maximum capacity (0 for unbounded)
<* First element in queue *>
QueueEntry* head;
<* Last element in queue *>
QueueEntry* tail;
<* Current number of elements *>
usz count;
<* Maximum capacity (0 for unbounded) *>
usz capacity;
Mutex lock;
ConditionVariable not_empty;
ConditionVariable not_full;

View File

@@ -15,9 +15,12 @@ struct LinkedEntry
uint hash;
Key key;
Value value;
LinkedEntry* next; // For bucket chain
LinkedEntry* before; // Previous in insertion order
LinkedEntry* after; // Next in insertion order
<* For bucket chain *>
LinkedEntry* next;
<* Previous in insertion order *>
LinkedEntry* before;
<* Next in insertion order *>
LinkedEntry* after;
}
struct LinkedHashMap (Printable)
@@ -27,8 +30,10 @@ struct LinkedHashMap (Printable)
usz count;
usz threshold;
float load_factor;
LinkedEntry* head; // First inserted LinkedEntry
LinkedEntry* tail; // Last inserted LinkedEntry
<* First inserted LinkedEntry *>
LinkedEntry* head;
<* Last inserted LinkedEntry *>
LinkedEntry* tail;
}
@@ -249,7 +254,7 @@ fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
fn void LinkedHashMap.clear(&map)
{
if (!map.count) return;
LinkedEntry* entry = map.head;
while (entry)
{
@@ -257,12 +262,12 @@ fn void LinkedHashMap.clear(&map)
map.free_entry(entry);
entry = next;
}
foreach (LinkedEntry** &bucket : map.table)
{
*bucket = null;
}
map.count = 0;
map.head = null;
map.tail = null;
@@ -284,10 +289,10 @@ fn Key[] LinkedHashMap.tkeys(&self)
fn Key[] LinkedHashMap.keys(&self, Allocator allocator)
{
if (!self.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, self.count);
usz index = 0;
LinkedEntry* entry = self.head;
while (entry)
{
@@ -338,7 +343,7 @@ fn Value[] LinkedHashMap.values(&self, Allocator allocator)
fn bool LinkedHashMap.has_value(&map, Value v) @if(VALUE_IS_EQUATABLE)
{
if (!map.count) return false;
LinkedEntry* entry = map.head;
while (entry)
{
@@ -396,7 +401,7 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, {
.hash = hash,
.key = key,
@@ -405,10 +410,10 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
.before = map.tail,
.after = null
});
// Update bucket chain
map.table[bucket_index] = entry;
// Update linked list
if (map.tail)
{
@@ -420,7 +425,7 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
map.head = entry;
}
map.tail = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
@@ -431,28 +436,28 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
{
LinkedEntry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
LinkedEntry*[] new_table = allocator::new_array(map.allocator, LinkedEntry*, new_capacity);
map.table = new_table;
map.threshold = (uint)(new_capacity * map.load_factor);
// Rehash all entries - linked list order remains unchanged
foreach (uint i, LinkedEntry *e : old_table)
{
if (!e) continue;
// Split the bucket chain into two chains based on new bit
LinkedEntry* lo_head = null;
LinkedEntry* lo_tail = null;
LinkedEntry* hi_head = null;
LinkedEntry* hi_tail = null;
do
{
LinkedEntry* next = e.next;
@@ -484,7 +489,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
e = next;
}
while (e);
if (lo_tail)
{
lo_tail.next = null;
@@ -496,7 +501,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
new_table[i + old_capacity] = hi_head;
}
}
map.free_internal(old_table.ptr);
}
@@ -562,12 +567,12 @@ fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
LinkedEntry* prev = null;
LinkedEntry* e = map.table[i];
while (e)
{
if (e.hash == hash && equals(key, e.key))
@@ -580,7 +585,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
{
map.table[i] = e.next;
}
if (e.before)
{
e.before.after = e.after;
@@ -589,7 +594,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
{
map.head = e.after;
}
if (e.after)
{
e.after.before = e.before;
@@ -598,7 +603,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
{
map.tail = e.before;
}
map.count--;
map.free_entry(e);
return true;

View File

@@ -11,20 +11,27 @@ struct LinkedEntry
{
uint hash;
Value value;
LinkedEntry* next; // For bucket chain
LinkedEntry* before; // Previous in insertion order
LinkedEntry* after; // Next in insertion order
<* For bucket chain *>
LinkedEntry* next;
<* Previous in insertion order *>
LinkedEntry* before;
<* Next in insertion order *>
LinkedEntry* after;
}
struct LinkedHashSet (Printable)
{
LinkedEntry*[] table;
Allocator allocator;
usz count; // Number of elements
usz threshold; // Resize limit
<* Number of elements *>
usz count;
<* Resize limit *>
usz threshold;
float load_factor;
LinkedEntry* head; // First inserted LinkedEntry
LinkedEntry* tail; // Last inserted LinkedEntry
<* Resize limit *>
LinkedEntry* head;
<* First inserted LinkedEntry *>
LinkedEntry* tail;
}
fn int LinkedHashSet.len(&self) @operator(len) => (int) self.count;
@@ -43,7 +50,7 @@ fn LinkedHashSet* LinkedHashSet.init(&self, Allocator allocator, usz capacity =
self.threshold = (usz)(capacity * load_factor);
self.load_factor = load_factor;
self.table = allocator::new_array(allocator, LinkedEntry*, capacity);
self.head = null;
self.tail = null;
return self;
@@ -304,7 +311,7 @@ fn void LinkedHashSet.free(&set)
fn void LinkedHashSet.clear(&set)
{
if (!set.count) return;
LinkedEntry* entry = set.head;
while (entry)
{
@@ -312,12 +319,12 @@ fn void LinkedHashSet.clear(&set)
set.free_entry(entry);
entry = next;
}
foreach (LinkedEntry** &bucket : set.table)
{
*bucket = null;
}
set.count = 0;
set.head = null;
set.tail = null;
@@ -363,16 +370,16 @@ fn LinkedHashSet LinkedHashSet.intersection(&self, Allocator allocator, LinkedHa
{
LinkedHashSet result;
result.init(allocator, math::min(self.table.len, other.table.len), self.load_factor);
// Iterate through the smaller set for efficiency
LinkedHashSet* smaller = self.count <= other.count ? self : other;
LinkedHashSet* larger = self.count > other.count ? self : other;
smaller.@each(;Value value)
smaller.@each(;Value value)
{
if (larger.contains(value)) result.add(value);
};
return result;
}
@@ -435,7 +442,7 @@ fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
{
if (self.count == 0) return true;
if (self.count > other.count) return false;
self.@each(; Value value) {
if (!other.contains(value)) return false;
};
@@ -453,10 +460,10 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
.before = set.tail,
.after = null
});
// Update bucket chain
set.table[bucket_index] = entry;
// Update linked list
if (set.tail)
{
@@ -468,7 +475,7 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
set.head = entry;
}
set.tail = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
@@ -479,28 +486,28 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
{
LinkedEntry*[] old_table = set.table;
usz old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
set.threshold = uint.max;
return;
}
LinkedEntry*[] new_table = allocator::new_array(set.allocator, LinkedEntry*, new_capacity);
set.table = new_table;
set.threshold = (uint)(new_capacity * set.load_factor);
// Rehash all entries - linked list order remains unchanged
foreach (uint i, LinkedEntry *e : old_table)
{
if (!e) continue;
// Split the bucket chain into two chains based on new bit
LinkedEntry* lo_head = null;
LinkedEntry* lo_tail = null;
LinkedEntry* hi_head = null;
LinkedEntry* hi_tail = null;
do
{
LinkedEntry* next = e.next;
@@ -532,7 +539,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
e = next;
}
while (e);
if (lo_tail)
{
lo_tail.next = null;
@@ -544,7 +551,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
new_table[i + old_capacity] = hi_head;
}
}
set.free_internal(old_table.ptr);
}
@@ -601,16 +608,16 @@ fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
.value = value,
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
.value = value,
.next = set.table[bucket_index],
.before = set.tail,
.after = null
});
set.table[bucket_index] = entry;
// Update linked list
if (set.tail)
{
@@ -622,19 +629,19 @@ fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_inde
set.head = entry;
}
set.tail = entry;
set.count++;
}
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
LinkedEntry* prev = null;
LinkedEntry* e = set.table[i];
while (e)
{
if (e.hash == hash && equals(value, e.value))
@@ -647,7 +654,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
{
set.table[i] = e.next;
}
if (e.before)
{
e.before.after = e.after;
@@ -656,7 +663,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
{
set.head = e.after;
}
if (e.after)
{
e.after.before = e.before;
@@ -665,7 +672,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
{
set.tail = e.before;
}
set.count--;
set.free_entry(e);
return true;
@@ -715,9 +722,9 @@ fn bool LinkedHashSetIterator.has_next(&self)
return self.current && self.current.after != null;
}
fn usz LinkedHashSetIterator.len(&self) @operator(len)
fn usz LinkedHashSetIterator.len(&self) @operator(len)
{
return self.set.count;
}
int dummy @local;
int dummy @local;

View File

@@ -2,13 +2,14 @@
// 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};
import std::io;
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
struct Node @private
struct Node
{
Node *next;
Node *prev;
Node* next;
Node* prev;
Type value;
}
@@ -16,8 +17,31 @@ struct LinkedList
{
Allocator allocator;
usz size;
Node *_first;
Node *_last;
Node* _first;
Node* _last;
}
fn usz? LinkedList.to_format(&self, Formatter* f) @dynamic
{
usz len = f.print("{ ")!;
for (Node* node = self._first; node != null; node.next)
{
len += f.printf(node.next ? "%s, " : "s", node.value)!;
}
return len + f.print(" }");
}
macro LinkedList @new(Allocator allocator, Type[] #default_values = {})
{
LinkedList new_list;
new_list.init(allocator);
new_list.push_all(#default_values);
return new_list;
}
macro LinkedList @tnew(Type[] #default_values = {})
{
return @new(tmem, #default_values);
}
<*
@@ -68,6 +92,11 @@ fn void LinkedList.push_front(&self, Type value)
self.size++;
}
fn void LinkedList.push_front_all(&self, Type[] value)
{
foreach_r (v : value) self.push_front(v);
}
fn void LinkedList.push(&self, Type value)
{
Node *last = self._last;
@@ -85,6 +114,11 @@ fn void LinkedList.push(&self, Type value)
self.size++;
}
fn void LinkedList.push_all(&self, Type[] value)
{
foreach (v : value) self.push(v);
}
fn Type? LinkedList.peek(&self) => self.first() @inline;
fn Type? LinkedList.peek_last(&self) => self.last() @inline;
@@ -133,6 +167,7 @@ macro Node* LinkedList.node_at_index(&self, usz index)
while (index--) node = node.next;
return node;
}
<*
@require index < self.size
*>
@@ -141,6 +176,14 @@ fn Type LinkedList.get(&self, usz index)
return self.node_at_index(index).value;
}
<*
@require index < self.size
*>
fn Type* LinkedList.get_ref(&self, usz index)
{
return &self.node_at_index(index).value;
}
<*
@require index < self.size
*>
@@ -149,6 +192,26 @@ fn void LinkedList.set(&self, usz index, Type element)
self.node_at_index(index).value = element;
}
fn usz? LinkedList.index_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
for (Node* node = self._first, usz i = 0; node != null; node = node.next, ++i)
{
if (node.value == t) return i;
}
return NOT_FOUND?;
}
fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
for (Node* node = self._last, usz i = self.size - 1; node != null; node = node.prev, --i)
{
if (node.value == t) return i;
if (i == 0) break;
}
return NOT_FOUND?;
}
<*
@require index < self.size
*>
@@ -334,3 +397,69 @@ fn void LinkedList.unlink(&self, Node* x) @private
self.free_node(x);
self.size--;
}
macro bool LinkedList.eq(&self, other) @operator(==) @if(ELEMENT_IS_EQUATABLE)
{
Node* node1 = self._first;
Node* node2 = other._first;
while (true)
{
if (!node1) return node2 == null;
if (!node2) return false;
if (node1.value != node2.value) return false;
node1 = node1.next;
node2 = node2.next;
}
return true;
}
fn LinkedListArrayView LinkedList.array_view(&self)
{
return { .list = self, .current_node = self._first };
}
struct LinkedListArrayView
{
LinkedList* list;
Node* current_node;
usz current_index;
}
fn usz LinkedListArrayView.len(&self) @operator(len) => self.list.size;
<*
@require index < self.list.size
*>
fn Type LinkedListArrayView.get(&self, usz index) @operator([])
{
return *self.get_ref(index);
}
<*
@require index < self.list.size
*>
fn Type* LinkedListArrayView.get_ref(&self, usz index) @operator(&[])
{
if (index == self.list.size - 1)
{
self.current_node = self.list._last;
self.current_index = index;
}
while (self.current_index != index)
{
switch
{
case index < self.current_index: // reverse iteration
self.current_node = self.current_node.prev;
self.current_index--;
case index > self.current_index:
self.current_node = self.current_node.next;
self.current_index++;
}
}
return &self.current_node.value;
}

View File

@@ -13,7 +13,7 @@ const Allocator LIST_HEAP_ALLOCATOR = (Allocator)&dummy;
const List ONHEAP = { .allocator = LIST_HEAP_ALLOCATOR };
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
macro bool type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct List (Printable)
{

View File

@@ -7,10 +7,12 @@ 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 : char (char id)
enum QOIColorspace : const char
{
SRGB = 0, // sRGB with linear alpha
LINEAR = 1 // all channels linear
<* sRGB with linear alpha *>
SRGB = 0,
<* all channels linear *>
LINEAR = 1
}
<*
@@ -19,7 +21,7 @@ enum QOIColorspace : char (char id)
AUTO can be used when decoding to automatically determine
the channels from the file's header.
*>
enum QOIChannels : char (char id)
enum QOIChannels : const inline char
{
AUTO = 0,
RGB = 3,
@@ -132,7 +134,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
// check input data size
uint image_size = pixels * desc.channels.id;
uint image_size = pixels * desc.channels;
if (image_size != input.len) return INVALID_DATA?;
// allocate memory for encoded data (output)
@@ -146,13 +148,13 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
.be_magic = bswap('qoif'),
.be_width = bswap(desc.width),
.be_height = bswap(desc.height),
.channels = desc.channels.id,
.colorspace = desc.colorspace.id
.channels = desc.channels,
.colorspace = desc.colorspace
};
uint pos = Header.sizeof; // Current position in output
uint loc; // Current position in image (top-left corner)
uint loc_end = image_size - desc.channels.id; // End of image data
uint loc_end = image_size - desc.channels; // End of image data
char run_length = 0; // Length of the current run
Pixel[64] palette; // Zero-initialized by default
@@ -163,7 +165,7 @@ fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
ichar[<3>] luma; // ...and luma
// write chunks
for (loc = 0; loc < image_size; loc += desc.channels.id)
for (loc = 0; loc < image_size; loc += desc.channels)
{
// set previous pixel
prev = p;
@@ -292,8 +294,8 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
// copy header data to desc
desc.width = bswap(header.be_width);
desc.height = bswap(header.be_height);
desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid
desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid
desc.channels = header.channels;
desc.colorspace = header.colorspace;
if (desc.channels == AUTO) return INVALID_DATA?; // Channels must be specified in the header
// check width and height
@@ -314,11 +316,11 @@ fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels c
if (channels == AUTO) channels = desc.channels;
// allocate memory for image data
usz image_size = (usz)pixels * channels.id;
usz image_size = (usz)pixels * channels;
char[] image = allocator::alloc_array(allocator, char, image_size);
defer catch allocator::free(allocator, image);
for (loc = 0; loc < image_size; loc += channels.id)
for (loc = 0; loc < image_size; loc += channels)
{
// get chunk tag
tag = data[pos];
@@ -391,31 +393,22 @@ const OP_RUN = 0b11;
struct Header @packed
{
uint be_magic; // magic bytes "qoif"
uint be_width; // image width in pixels (BE)
uint be_height; // image height in pixels (BE)
<* magic bytes "qoif" *>
uint be_magic;
<* image width in pixels (BE) *>
uint be_width;
<* image height in pixels (BE) *>
uint be_height;
// informative fields
char channels; // 3 = RGB, 4 = RGB
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
<* 3 = RGB, 4 = RGB *>
QOIChannels channels;
<* 0 = sRGB with linear alpha, 1 = all channels linear *>
QOIColorspace colorspace;
}
const char[*] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
// inefficient, but it's only run once at a time
<*
@return? INVALID_DATA
*>
macro @enumcast($Type, raw)
{
foreach (value : $Type.values)
{
if (value.id == raw) return value;
}
return INVALID_DATA?;
}
typedef Pixel = inline char[<4>];
macro char Pixel.hash(Pixel p)
{

View File

@@ -13,7 +13,7 @@ import std::math;
The advantage over the BackedArenaAllocator, is that when allocating beyond the first "page", it will
retain the characteristics of an arena allocator (allocating a large piece of memory then handing off
memory from that memory), wheras the BackedArenaAllocator will have heap allocator characteristics.
memory from that memory), whereas the BackedArenaAllocator will have heap allocator characteristics.
*>
struct DynamicArenaAllocator (Allocator)
{

View File

@@ -19,7 +19,7 @@ alias AllocMap = HashMap { uptr, Allocation };
// It tracks allocations using a hash map but
// is not compatible with allocators that uses mark()
//
// It is also embarassingly single-threaded, so
// It is also embarrassingly single-threaded, so
// do not use it to track allocations that cross threads.
struct TrackingAllocator (Allocator)
@@ -216,4 +216,4 @@ fn void? TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
}
}
}
}
}

View File

@@ -22,9 +22,12 @@ struct Vmem (Allocator)
bitstruct VmemOptions : int
{
bool shrink_on_reset; // Release memory on reset
bool protect_unused_pages; // Protect unused pages on reset
bool scratch_released_data; // Overwrite released data with 0xAA
<* Release memory on reset *>
bool shrink_on_reset;
<* Protect unused pages on reset *>
bool protect_unused_pages;
<* Overwrite released data with 0xAA *>
bool scratch_released_data;
}
<*
@@ -38,11 +41,11 @@ bitstruct VmemOptions : int
fn void? Vmem.init(&self, usz preferred_size, usz reserve_page_size = 0, VmemOptions options = { true, true, env::COMPILER_SAFE_MODE }, usz min_size = 0)
{
static usz page_size = 0;
if (!page_size) page_size = mem::os_pagesize();
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?;
if (!page_size) page_size = mem::os_pagesize();
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?;
while (preferred_size >= min_size)
{
memory = vm::virtual_alloc(preferred_size, PROTECTED);

View File

@@ -3,7 +3,7 @@ import std::collections::pair, std::io;
<*
Returns true if the array contains at least one element, else false
@param [in] array
@param [in] element
@require $kindof(array) == SLICE || $kindof(array) == ARRAY
@@ -15,13 +15,13 @@ macro bool contains(array, element)
{
if (*item == element) return true;
}
return false;
return false;
}
<*
Return the first index of element found in the array, searching from the start.
@param [in] array
@param [in] element
@require $kindof(array) == SLICE || $kindof(array) == ARRAY
@@ -29,7 +29,7 @@ macro bool contains(array, element)
@return "the first index of the element"
@return? NOT_FOUND
*>
macro index_of(array, element)
macro usz? index_of(array, element)
{
foreach (i, &e : array)
{
@@ -63,13 +63,13 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
<*
Return the first index of element found in the array, searching in reverse from the end.
@param [in] array
@param [in] element
@return "the last index of the element"
@return? NOT_FOUND
*>
macro rindex_of(array, element)
macro usz? rindex_of(array, element)
{
foreach_r (i, &e : array)
{
@@ -142,7 +142,7 @@ macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
@param [in] array
@param identity
@param #operation : "The reduction/folding labmda function or function pointer to apply."
@param #operation : "The reduction/folding lambda function or function pointer to apply."
@require @is_valid_list(array) : "Expected a valid list"
@require $defined($typefrom(@reduce_fn(array, identity)) $func = #operation) : "Invalid lambda or function pointer type"
@@ -216,7 +216,7 @@ macro @product(array, identity_value = 1)
*>
macro usz[] @indices_of(Allocator allocator, array, #predicate)
{
usz[] results = allocator::new_array(allocator, usz, find_len(array));
usz[] results = allocator::new_array(allocator, usz, lengthof(array));
usz matches;
$typefrom(@predicate_fn(array)) $predicate = #predicate;
@@ -387,7 +387,6 @@ macro bool @all(array, #predicate)
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
@require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side"
@require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type."
*>
macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...) @nodiscard
{
@@ -400,8 +399,8 @@ macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...)
$Type = $typeof(#operation).returns;
$endif
usz left_len = find_len(left);
usz right_len = find_len(right);
usz left_len = lengthof(left);
usz right_len = lengthof(right);
$LeftType left_fill;
$RightType right_fill;
@@ -460,7 +459,6 @@ macro @zip(Allocator allocator, left, right, #operation = ..., fill_with = ...)
@require @is_valid_list(left) &&& @is_valid_list(right) : "Left and right sides must be integer indexable"
@require @is_valid_operation(left, right, ...#operation) : "The operator must take two parameters matching the elements of the left and right side"
@require @is_valid_fill(left, right, ...fill_with) : "The specified fill value does not match either the left or the right array's underlying type."
*>
macro @tzip(left, right, #operation = ..., fill_with = ...) @nodiscard
{
@@ -495,7 +493,7 @@ macro @tzip(left, right, #operation = ..., fill_with = ...) @nodiscard
@require @is_valid_list(left) : "Expected a valid list"
@require @is_valid_list(right) : "Expected a valid list"
@require find_len(right) >= find_len(left) : `Right side length must be >= the destination (left) side length; consider using a sub-array of data for the assignment.`
@require lengthof(right) >= lengthof(left) : `Right side length must be >= the destination (left) side length; consider using a sub-array of data for the assignment.`
@require $defined($typefrom(@zip_into_fn(left, right)) x = #operation) : "The functor must use the same types as the `left` and `right` inputs, and return a value of the `left` type."
*>
macro @zip_into(left, right, #operation)
@@ -539,7 +537,7 @@ macro bool @is_valid_operation(#left, #right, #operation = ...) @const
macro bool @is_valid_list(#expr) @const
{
return $defined(#expr[0]) &&& ($defined(#expr.len) ||| $defined(#expr.len()));
return $defined(#expr[0], lengthof(#expr));
}
macro bool @is_valid_fill(left, right, fill_with = ...)
@@ -547,11 +545,9 @@ macro bool @is_valid_fill(left, right, fill_with = ...)
$if !$defined(fill_with):
return true;
$else
usz left_len = $defined(left.len()) ??? left.len() : left.len;
usz right_len = $defined(right.len()) ??? right.len() : right.len;
usz left_len = lengthof(left);
usz right_len = lengthof(right);
if (left_len == right_len) return true;
return left_len > right_len ? $defined(($typeof(right[0]))fill_with) : $defined(($typeof(left[0]))fill_with);
$endif
}
macro usz find_len(list) => $defined(list.len()) ??? list.len() : list.len;

View File

@@ -119,7 +119,7 @@ macro write(x, bytes, $Type)
*($typeof(x)*)s.ptr = bitcast(x, $Type).val;
}
macro is_bitorder($Type)
macro bool is_bitorder($Type)
{
$switch $Type:
$case UShortLE:
@@ -181,4 +181,4 @@ macro bool @is_arrayptr_or_slice_of_char(#bytes) @const
$default:
return false;
$endswitch
}
}

View File

@@ -7,7 +7,7 @@ import libc, std::hash, std::io, std::os::backtrace;
<*
EMPTY_MACRO_SLOT is a value used for implementing optional arguments for macros in an efficient
way. It relies on the fact that distinct types are not implicitly convertable.
way. It relies on the fact that distinct types are not implicitly convertible.
You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether
the argument has been used or not.
@@ -27,8 +27,8 @@ macro foo(a, #b = EMPTY_MACRO_SLOT)
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
typedef EmptySlot = void*;
macro @is_empty_macro_slot(#arg) @const @builtin => $typeof(#arg) == EmptySlot;
macro @is_valid_macro_slot(#arg) @const @builtin => $typeof(#arg) != EmptySlot;
macro bool @is_empty_macro_slot(#arg) @const @builtin => $typeof(#arg) == EmptySlot;
macro bool @is_valid_macro_slot(#arg) @const @builtin => $typeof(#arg) != EmptySlot;
<*
Returns a random value at compile time.
@@ -108,7 +108,7 @@ macro anycast(any v, $Type) @builtin
return ($Type*)v.ptr;
}
macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$define($Type x = #foo)'") => $defined(*&&($Type){} = #foo);
macro bool @assignable_to(#foo, $Type) @const @builtin @deprecated("use '$defined($Type x = #foo)'") => $defined(*&&($Type){} = #foo);
macro @addr(#val) @builtin
{
@@ -430,7 +430,7 @@ macro swizzle2(v, v2, ...) @builtin
@require types::is_int($typeof($value)) : "Input value must be an integer"
@require $sizeof($value) * 8 <= 128 : "Input value must be 128 bits wide or lower"
*>
macro @clz($value) @builtin @const
macro uint @clz($value) @builtin @const
{
$if $value == 0:
return $sizeof($value) * 8; // it's all leading zeroes
@@ -547,6 +547,27 @@ macro isz @str_find(String $string, String $needle) @builtin => $$str_find($stri
macro String @str_upper(String $str) @builtin => $$str_upper($str);
macro String @str_lower(String $str) @builtin => $$str_lower($str);
macro uint @str_hash(String $str) @builtin => $$str_hash($str);
macro String @str_pascalcase(String $str) @builtin => $$str_pascalcase($str);
macro String @str_snakecase(String $str) @builtin => $$str_snakecase($str);
macro String @str_camelcase(String $str) @builtin => @str_capitalize($$str_pascalcase($str));
macro String @str_constantcase(String $str) @builtin => @str_upper($$str_snakecase($str));
macro String @str_replace(String $str, String $pattern, String $replace, uint $limit = 0) @builtin => $$str_replace($str, $pattern, $replace, $limit);
macro String @str_capitalize(String $str) @builtin
{
$switch $str.len:
$case 0: return $str;
$case 1: return $$str_upper($str);
$default: return $$str_upper($str[0:1]) +++ $str[1..];
$endswitch
}
macro String @str_uncapitalize(String $str) @builtin
{
$switch $str.len:
$case 0: return $str;
$case 1: return $$str_lower($str);
$default: return $$str_lower($str[0:1]) +++ $str[1..];
$endswitch
}
macro @generic_hash_core(h, value)
{
@@ -556,7 +577,7 @@ macro @generic_hash_core(h, value)
return h;
}
macro @generic_hash(value)
macro uint @generic_hash(value)
{
uint h = @generic_hash_core((uint)0x3efd4391, value);
$for var $cnt = 4; $cnt < $sizeof(value); $cnt += 4:

View File

@@ -6,7 +6,7 @@ module std::core::builtin;
<*
@require types::@comparable_value(a) && types::@comparable_value(b)
*>
macro less(a, b) @builtin
macro bool less(a, b) @builtin
{
$switch:
$case $defined(a.less):
@@ -21,7 +21,7 @@ macro less(a, b) @builtin
<*
@require types::@comparable_value(a) && types::@comparable_value(b)
*>
macro less_eq(a, b) @builtin
macro bool less_eq(a, b) @builtin
{
$switch:
$case $defined(a.less):
@@ -36,7 +36,7 @@ macro less_eq(a, b) @builtin
<*
@require types::@comparable_value(a) && types::@comparable_value(b)
*>
macro greater(a, b) @builtin
macro bool greater(a, b) @builtin
{
$switch:
$case $defined(a.less):
@@ -65,7 +65,7 @@ macro int compare_to(a, b) @builtin
<*
@require types::@comparable_value(a) && types::@comparable_value(b)
*>
macro greater_eq(a, b) @builtin
macro bool greater_eq(a, b) @builtin
{
$switch:
$case $defined(a.less):

View File

@@ -2,6 +2,8 @@
// 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::cinterop;
import std::core::env;
const C_INT_SIZE = $$C_INT_SIZE;
const C_LONG_SIZE = $$C_LONG_SIZE;
@@ -59,3 +61,57 @@ macro typeid unsigned_int_from_bitsize(usz $bitsize) @private
$default: $error("Invalid bitsize");
$endswitch
}
const USE_STACK_VALIST = env::ARCH_32_BIT || env::WIN32 || (env::DARWIN && env::AARCH64);
module std::core::cinterop @if(USE_STACK_VALIST);
typedef CVaList = void*;
macro CVaList.next(&self, $Type)
{
void *ptr = mem::aligned_pointer((void*)*self, max($Type.alignof, 8));
defer *self = (CVaList)(ptr + 1);
return *($Type*)ptr;
}
module std::core::cinterop @if(env::X86_64 && !env::WIN32);
struct CVaListData
{
uint gp_offset;
uint fp_offset;
void *overflow_arg_area;
void *reg_save_area;
}
typedef CVaList = CVaListData*;
macro CVaList.next(self, $Type)
{
CVaListData* data = (CVaListData*)self;
$switch:
$case $Type.kindof == FLOAT ||| ($Type.kindof == VECTOR && $Type.sizeof <= 16):
var $LoadType = $Type.sizeof < 8 ? double : $Type;
if (data.fp_offset < 6 * 8 + 8 * 16 )
{
defer data.fp_offset += (uint)mem::aligned_offset($Type.sizeof, 16);
return ($Type)*($LoadType*)(data.reg_save_area + data.fp_offset);
}
void* ptr = mem::aligned_pointer(data.overflow_arg_area, max(8, $Type.alignof));
defer data.overflow_arg_area = ptr + $Type.sizeof;
return ($Type)*($LoadType*)ptr;
$case $Type.kindof == SIGNED_INT || $Type.kindof == UNSIGNED_INT:
var $LoadType = $Type.sizeof < 4 ? int : $Type;
if (data.gp_offset < 6 * 8 && $Type.sizeof <= 8)
{
defer data.gp_offset += (uint)mem::aligned_offset($Type.sizeof, 8);
return ($Type)*($LoadType*)(data.reg_save_area + data.gp_offset);
}
void* ptr = mem::aligned_pointer(data.overflow_arg_area, max(8, $Type.alignof));
defer data.overflow_arg_area = ptr + $Type.sizeof;
return ($Type)*($LoadType*)ptr;
$default:
void* ptr = mem::aligned_pointer(data.overflow_arg_area, max(8, $Type.alignof));
defer data.overflow_arg_area = ptr + $Type.sizeof;
return *($Type*)ptr;
$endswitch
}

View File

@@ -206,5 +206,6 @@ macro bool os_is_posix() @const
}
const String[] AUTHORS = $$AUTHORS;
const String[] AUTHOR_EMAILS = $$AUTHOR_EMAILS;
const String PROJECT_VERSION = $$PROJECT_VERSION;
const BUILTIN_EXPECT_IS_DISABLED = $feature(DISABLE_BUILTIN_EXPECT);
const BUILTIN_PREFETCH_IS_DISABLED = $feature(DISABLE_BUILTIN_PREFETCH);

View File

@@ -134,7 +134,16 @@ macro void init()
log_init.call(fn () => (void)logger_mutex.init());
}
fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
macro void call_log(LogPriority prio, LogCategory category, String fmt, args...)
{
$if FULL_LOG:
call_log_internal(prio, category, $$FILE, $$FUNC, $$LINE, fmt, args);
$else
call_log_internal(prio, category, "", "", 0, fmt, args);
$endif
}
fn void call_log_internal(LogPriority prio, LogCategory category, String file, String func, int line, String fmt, any[] args)
{
LogPriority priority = mem::@atomic_load(config_priorities[category], UNORDERED);
if (priority > prio) return;
@@ -143,11 +152,7 @@ fn void call_log(LogPriority prio, LogCategory category, String fmt, args...)
Logger logger = current_logger;
LogFn logfn = current_logfn;
defer if (locked) (void)logger_mutex.unlock();
$if FULL_LOG:
logfn(logger.ptr, prio, category, current_tag, $$FILE, $$FUNC, $$LINE, fmt, args);
$else
logfn(logger.ptr, prio, category, current_tag, "", "", 0, fmt, args);
$endif
logfn(logger.ptr, prio, category, current_tag, file, func, line, fmt, args);
}
fn String? get_category_name(LogCategory category)
@@ -199,7 +204,11 @@ fn void StderrLogger.log(&self, LogPriority priority, LogCategory category, LogT
str.init(mem, 256);
str.appendf(fmt, ...args);
TzDateTime time = datetime::now().to_local();
$if FULL_LOG:
io::eprintfn("[%02d:%02d:%02d:%04d] %s:%d [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), file, line, priority, str);
$else
io::eprintfn("[%02d:%02d:%02d:%04d] [%s] %s", time.hour, time.min, time.sec, (time.usec / 1000), priority, str);
$endif
};
}

View File

@@ -72,6 +72,7 @@ macro masked_load(ptr, bool[<*>] mask, passthru)
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
@ensure $typeof(return) == $typeof(*ptr)
*>
macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
{

View File

@@ -64,7 +64,7 @@ macro int @main_to_void_main_args(#m, int argc, char** argv)
module std::core::main_stub @if(env::WIN32);
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW");
extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @cname("CommandLineToArgvW");
macro String[] win_command_line_to_strings(ushort* cmd_line) @private
{

View File

@@ -42,7 +42,7 @@ macro @enum_lookup_new($Type, $name, value)
module std::core::runtime @if(env::FREESTANDING_WASM);
extern fn void __wasm_call_ctors();
fn void wasm_initialize() @extern("_initialize") @wasm
fn void wasm_initialize() @cname("_initialize") @wasm
{
// The linker synthesizes this to call constructors.
__wasm_call_ctors();

View File

@@ -56,20 +56,27 @@ long cycle_stop @local;
DString benchmark_log @local;
bool benchmark_warming @local;
uint this_iteration @local;
bool benchmark_stop @local;
macro @start_benchmark()
macro void @start_benchmark()
{
benchmark_clock = std::time::clock::now();
benchmark_clock = clock::now();
cycle_start = $$sysclock();
}
macro @end_benchmark()
macro void @end_benchmark()
{
benchmark_nano_seconds = benchmark_clock.mark();
cycle_stop = $$sysclock();
}
macro @log_benchmark(msg, args...) => @pool()
macro void @kill_benchmark(String format, ...)
{
@log_benchmark(format, $vasplat);
benchmark_stop = true;
}
macro void @log_benchmark(msg, args...) => @pool()
{
if (benchmark_warming) return;
@@ -133,6 +140,7 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
@start_benchmark(); // can be overridden by calls inside the unit's func
unit.func() @inline;
if (benchmark_stop) return false;
if (benchmark_nano_seconds == (NanoDuration){}) @end_benchmark(); // only mark when it wasn't already by the unit.func

View File

@@ -14,9 +14,9 @@ TestContext* test_context @private;
struct TestContext
{
JmpBuf buf;
// Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add'
<* Allows filtering test cased or modules by substring, e.g. 'foo::', 'foo::test_add' *>
String test_filter;
// Triggers debugger breakpoint when assert or test:: checks failed
<* Triggers debugger breakpoint when assert or test:: checks failed *>
bool breakpoint_on_assert;
// internal state

View File

@@ -29,11 +29,11 @@ alias ErrorCallback = fn void (ZString);
@param addr : "Start of memory region."
@param size : "Size of memory region."
*>
macro poison_memory_region(void* addr, usz size)
macro void poison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
$if env::ADDRESS_SANITIZER:
__asan_poison_memory_region(addr, size);
$endif
$endif
}
<*
@@ -50,11 +50,11 @@ macro poison_memory_region(void* addr, usz size)
@param addr : "Start of memory region."
@param size : "Size of memory region."
*>
macro unpoison_memory_region(void* addr, usz size)
macro void unpoison_memory_region(void* addr, usz size)
{
$if env::ADDRESS_SANITIZER:
$if env::ADDRESS_SANITIZER:
__asan_unpoison_memory_region(addr, size);
$endif
$endif
}
<*

View File

@@ -115,7 +115,9 @@ fn bool TypeKind.is_int(kind) @inline
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
}
macro bool is_slice_convertable($Type)
macro bool is_slice_convertable($Type) @deprecated("Use is_slice_convertible") => is_slice_convertible($Type);
macro bool is_slice_convertible($Type)
{
$switch $Type.kindof:
$case SLICE:

650
lib/std/crypto/aes.c3 Normal file
View File

@@ -0,0 +1,650 @@
<*
This is an implementation of the AES algorithm with the ECB, CTR and CBC
modes. The key size can be chosen among AES128, AES192, AES256.
Ported from github.com/kokke/tiny-aes-c by Koni Marti.
The implementation is verified against the test vectors from the National
Institute of Standards and Technology Special Publication 800-38A 2001 ED.
Data length must be evenly divisible by 16 bytes (len % 16 == 0) unless CTR is
used. You should pad the end of the string with zeros or use PKCS7 if this is not the case.
For AES192/256 the key size is proportionally larger.
The following example demonstrates the AES encryption of a plaintext string
with an AES 128-bit key:
```
module app;
import std::crypto::aes, std::io;
fn void main()
{
char[] key = x"2b7e151628aed2a6abf7158809cf4f3c";
char[] text = x"6bc1bee22e409f96e93d7e117393172a";
char[16] iv = x"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff";
Aes aes;
aes.init(AES128, key, iv);
defer aes.destroy();
char[] cipher = aes.encrypt(mem, text);
defer free(cipher);
assert(cipher == x"874d6191b620e3261bef6864990db6ce");
}
```
*>
module std::crypto::aes;
<* Block length in bytes. AES is 128-bit blocks only. *>
const BLOCKLEN = 16;
<* Number of columns of a AES state. *>
const COLNUM = 4;
<*
Block modes:
ECB - Electronic Code Book (Not recommended, indata be 16 byte multiple)
CBC - Cipher Block Chaining (Indata be 16 byte multiple)
CTR - Counter Mode (Recommended, data may be any size)
*>
enum BlockMode
{
ECB,
CBC,
CTR,
}
<* 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 }
}
struct AesKey
{
<* Size of key in bits *>
usz key_size;
<* Size of key in bytes *>
int key_len;
<* Size of the expanded round_key *>
int key_exp_size; // expected size of round_key
<* Number of 32 bit words in key *>
usz nk;
<* Number of rounds in the cipher *>
usz nr;
}
struct Aes
{
<* The type, AES128, AES192 or AES256 *>
AesKey type;
<* Block mode: ECB, CBC or CTR *>
BlockMode mode;
<* Initialization Vector *>
char[BLOCKLEN] iv;
<* Internal key state *>
char[256] round_key;
<* Internal state *>
AesState state;
}
alias AesState = char[COLNUM][COLNUM];
<*
Initializes the AES crypto. The initialization vector should be securely random for each encryption
to mitigate things like replay attacks.
@param type : "The type or AES: 128, 192 or 256 bits"
@param [in] key : "The key to use, should be the same bit size as the type, so 16, 24 or 32 bytes"
@param iv : "The initialization vector"
@param mode : "The block mode: EBC, CBC, CTR. Defaults to CTR"
@require key.len == type.key.key_len : "Key does not match expected length."
*>
fn Aes* Aes.init(&self, AesType type, char[] key, char[BLOCKLEN] iv, BlockMode mode = CTR)
{
*self = { .type = type.key, .mode = mode, .iv = iv };
key_expansion(type, key, &self.round_key);
return self;
}
<*
Completely erases data stored in the context.
*>
fn void Aes.destroy(&self)
{
*self = {};
}
<*
Check if the length is valid using the given block mode. It has to be a multiple of 16 bytes unless CTR is used.
*>
macro bool is_valid_encryption_len(BlockMode mode, usz len)
{
switch (mode)
{
case CTR:
return true;
case ECB:
case CBC:
return len % BLOCKLEN == 0;
}
}
<*
@param [in] in : "Plaintext input."
@param [out] out : "Cipher output."
@require is_valid_encryption_len(self.mode, in.len) : "The input must be a multiple of 16 unless CTR is used"
@require out.len >= in.len : "Out buffer must be sufficiently large to hold the data"
*>
fn void Aes.encrypt_buffer(&self, char[] in, char[] out)
{
switch (self.mode)
{
case CTR: ctr_xcrypt_buffer(self, in, out);
case ECB: ecb_encrypt_buffer(self, in, out);
case CBC: cbc_encrypt_buffer(self, in, out);
}
}
<*
@param [in] in : "Cipher input."
@param [out] out : "Plaintext output."
@require is_valid_encryption_len(self.mode, in.len) : "The encrypted data must be a multiple of 16 unless CTR is used"
@require out.len >= in.len : "Out buffer must be sufficiently large to hold the data"
*>
fn void Aes.decrypt_buffer(&self, char[] in, char[] out)
{
switch (self.mode)
{
case ECB: ecb_decrypt_buffer(self, in, out);
case CBC: cbc_decrypt_buffer(self, in, out);
case CTR: ctr_xcrypt_buffer(self, in, out);
}
}
<*
Encrypt the data, allocating memory for the encrypted data.
@param [in] in : "Plaintext input."
@param [&inout] allocator : "The allocator to use for the output"
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
*>
fn char[] Aes.encrypt(&self, Allocator allocator, char[] in)
{
char[] out = allocator::alloc_array(allocator, char, in.len);
self.encrypt_buffer(in, out) @inline;
return out;
}
<*
Encrypt the data, allocating temp memory for the encrypted data.
@param [in] in : "Plaintext input."
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
*>
fn char[] Aes.tencrypt(&self, char[] in)
{
return self.encrypt(tmem, in);
}
<*
Decrypt the data, allocating memory for the decrypted data.
@param [in] in : "Encrypted input."
@param [&inout] allocator : "The allocator to use for the output"
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
*>
fn char[] Aes.decrypt(&self, Allocator allocator, char[] in)
{
char[] out = allocator::alloc_array(allocator, char, in.len);
self.decrypt_buffer(in, out) @inline;
return out;
}
<*
Decrypt the data, allocating temp memory for the decrypted data.
@param [in] in : "Encrypted input."
@require is_valid_encryption_len(self.mode, in.len) : "The in-data needs to be a multiple of 16 unless CTR is used"
*>
fn char[] Aes.tdecrypt(&self, char[] in)
{
return self.decrypt(tmem, in);
}
module std::crypto::aes @private;
<*
@param [&inout] aes : "AES context."
@param [in] in : "Plaintext input."
@param [out] out : "Cipher output."
*>
fn void ecb_encrypt_block(Aes *aes, char[BLOCKLEN]* in, char[BLOCKLEN]* out)
{
for (usz i = 0; i < 4; i++)
{
for (usz j = 0; j < 4; j++)
{
aes.state[i][j] = (*in)[i * 4 + j];
}
}
aes_cipher(aes, &aes.round_key);
for (usz i = 0; i < 4; i++)
{
for (usz j = 0; j < 4; j++)
{
(*out)[i * 4 + j] = aes.state[i][j];
}
}
}
<*
@param [&inout] aes : "AES context."
@param [in] in : "Cipher input."
@param [out] out : "Plaintext output."
*>
fn void ecb_decrypt_block(Aes *aes, char[BLOCKLEN]* in, char[BLOCKLEN]* out)
{
for (usz i = 0; i < 4; i++)
{
for (usz j = 0; j < 4; j++)
{
aes.state[i][j] = (*in)[i * 4 + j];
}
}
inv_cipher(aes, &aes.round_key);
for (usz i = 0; i < 4; i++)
{
for (usz j = 0; j < 4; j++)
{
(*out)[i * 4 + j] = aes.state[i][j];
}
}
}
<*
@param [&inout] aes : "AES context."
@param [in] in : "Cipher input."
@param [out] out : "Plaintext output."
@require out.len >= in.len : "out must be at least as large as buf"
*>
fn void ecb_decrypt_buffer(Aes *aes, char[] in, char[] out)
{
usz len = in.len;
for (usz i = 0; i < len; i += 4)
{
ecb_decrypt_block(aes, in[:BLOCKLEN], out[:BLOCKLEN]) @inline;
}
}
<*
@param [&inout] aes : "AES context."
@param [in] in : "Plaintext input."
@param [out] out : "Cipher output."
*>
fn void ecb_encrypt_buffer(Aes *aes, char[] in, char[] out)
{
usz len = in.len;
for (usz i = 0; i < len; i += BLOCKLEN)
{
ecb_encrypt_block(aes, in[i:BLOCKLEN], out[i:BLOCKLEN]) @inline;
}
}
fn void xor_with_iv(char[] buf, char[BLOCKLEN]* iv) @local
{
foreach (i, b : *iv)
{
buf[i] ^= b;
}
}
<*
@param [&inout] aes : "AES context."
@param [in] in : "Plaintext input."
@param [out] out : "Cipher output."
*>
fn void cbc_encrypt_buffer(Aes *aes, char[] in, char[] out)
{
char[] iv = aes.iv[..];
usz len = in.len;
char[BLOCKLEN] tmp;
char[BLOCKLEN] tmp2;
for (usz i = 0; i < len; i += BLOCKLEN)
{
tmp[:BLOCKLEN] = in[i:BLOCKLEN];
xor_with_iv(&tmp, iv);
ecb_encrypt_block(aes, &tmp, &tmp2);
out[i:BLOCKLEN] = tmp2[..];
iv = tmp2[..];
}
}
<*
@param [&inout] aes : "AES context."
@param [in] in : "Cipher input."
@param [out] out : "Plaintext output."
*>
fn void cbc_decrypt_buffer(Aes *aes, char[] in, char[] out)
{
char[BLOCKLEN] tmp;
usz len = in.len;
for (usz i = 0; i < len; i += BLOCKLEN)
{
ecb_decrypt_block(aes, in[i:BLOCKLEN], &tmp);
xor_with_iv(&tmp, aes.iv[..]);
aes.iv[:BLOCKLEN] = in[i:BLOCKLEN];
out[i:BLOCKLEN] = tmp[..];
}
}
<*
@param [&inout] aes : "AES context."
@param [in] in : "Plaintext/cipher input."
@param [out] out : "Cipher/plaintext output."
*>
fn void ctr_xcrypt_buffer(Aes *aes, char[] in, char[] out)
{
char[BLOCKLEN] buffer @noinit;
usz len = in.len;
for (int bi = BLOCKLEN, usz i = 0; i < len; i++)
{
if (bi == BLOCKLEN)
{
buffer = aes.iv;
ecb_encrypt_block(aes, &buffer, &buffer);
for LOOP: (bi = (BLOCKLEN - 1); bi >= 0; bi--)
{
if (aes.iv[bi] == 255)
{
aes.iv[bi] = 0;
continue;
}
aes.iv[bi]++;
break LOOP;
}
bi = 0;
}
out[i] = in[i] ^ buffer[bi];
bi++;
}
}
macro char get_sbox_value(num) => SBOX[num];
macro char get_sbox_invert(num) => RSBOX[num];
const char[256] SBOX =
x`637c777bf26b6fc53001672bfed7ab76
ca82c97dfa5947f0add4a2af9ca472c0
b7fd9326363ff7cc34a5e5f171d83115
04c723c31896059a071280e2eb27b275
09832c1a1b6e5aa0523bd6b329e32f84
53d100ed20fcb15b6acbbe394a4c58cf
d0efaafb434d338545f9027f503c9fa8
51a3408f929d38f5bcb6da2110fff3d2
cd0c13ec5f974417c4a77e3d645d1973
60814fdc222a908846eeb814de5e0bdb
e0323a0a4906245cc2d3ac629195e479
e7c8376d8dd54ea96c56f4ea657aae08
ba78252e1ca6b4c6e8dd741f4bbd8b8a
703eb5664803f60e613557b986c11d9e
e1f8981169d98e949b1e87e9ce5528df
8ca1890dbfe6426841992d0fb054bb16`;
const char[256] RSBOX =
x`52096ad53036a538bf40a39e81f3d7fb
7ce339829b2fff87348e4344c4dee9cb
547b9432a6c2233dee4c950b42fac34e
082ea16628d924b2765ba2496d8bd125
72f8f66486689816d4a45ccc5d65b692
6c704850fdedb9da5e154657a78d9d84
90d8ab008cbcd30af7e45805b8b34506
d02c1e8fca3f0f02c1afbd0301138a6b
3a9111414f67dcea97f2cfcef0b4e673
96ac7422e7ad3585e2f937e81c75df6e
47f11a711d29c5896fb7620eaa18be1b
fc563e4bc6d279209adbc0fe78cd5af4
1fdda8338807c731b11210592780ec5f
60517fa919b54a0d2de57a9f93c99cef
a0e03b4dae2af5b0c8ebbb3c83539961
172b047eba77d626e169146355210c7d`;
const char[11] RCON = x`8d01020408102040801b36`;
fn void add_round_key(Aes* aes, usz round, char[] round_key)
{
usz i, j;
for (i = 0; i < 4; i++)
{
for (j = 0; j < 4; j++)
{
aes.state[i][j] ^= round_key[(round * COLNUM * 4) + (i * COLNUM) + j];
}
}
}
fn void sub_bytes(Aes* aes)
{
for (usz i = 0; i < 4; i++)
{
for (usz j = 0; j < 4; j++)
{
aes.state[j][i] = get_sbox_value(aes.state[j][i]);
}
}
}
fn void shift_rows(Aes* aes)
{
char temp;
temp = aes.state[0][1];
aes.state[0][1] = aes.state[1][1];
aes.state[1][1] = aes.state[2][1];
aes.state[2][1] = aes.state[3][1];
aes.state[3][1] = temp;
temp = aes.state[0][2];
aes.state[0][2] = aes.state[2][2];
aes.state[2][2] = temp;
temp = aes.state[1][2];
aes.state[1][2] = aes.state[3][2];
aes.state[3][2] = temp;
temp = aes.state[0][3];
aes.state[0][3] = aes.state[3][3];
aes.state[3][3] = aes.state[2][3];
aes.state[2][3] = aes.state[1][3];
aes.state[1][3] = temp;
}
fn char xtime(char x) @local
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
fn void mix_columns(Aes* aes)
{
for (usz i = 0; i < 4; i++)
{
char t = aes.state[i][0];
char tmp = aes.state[i][0] ^ aes.state[i][1] ^ aes.state[i][2] ^ aes.state[i][3];
char tm = aes.state[i][0] ^ aes.state[i][1];
tm = xtime(tm);
aes.state[i][0] ^= tm ^ tmp;
tm = aes.state[i][1] ^ aes.state[i][2];
tm = xtime(tm);
aes.state[i][1] ^= tm ^ tmp;
tm = aes.state[i][2] ^ aes.state[i][3];
tm = xtime(tm);
aes.state[i][2] ^= tm ^ tmp;
tm = aes.state[i][3] ^ t;
tm = xtime(tm);
aes.state[i][3] ^= tm ^ tmp;
}
}
fn char multiply(char x, char y) @local
{
return (((y & 1) * x) ^
(((y>>1) & 1) * xtime(x)) ^
(((y>>2) & 1) * xtime(xtime(x))) ^
(((y>>3) & 1) * xtime(xtime(xtime(x)))) ^
(((y>>4) & 1) * xtime(xtime(xtime(xtime(x))))));
}
fn void inv_mix_columns(Aes* aes)
{
for (int i = 0; i < 4; i++)
{
char a = aes.state[i][0];
char b = aes.state[i][1];
char c = aes.state[i][2];
char d = aes.state[i][3];
aes.state[i][0] = multiply(a, 0x0e) ^ multiply(b, 0x0b) ^ multiply(c, 0x0d) ^ multiply(d, 0x09);
aes.state[i][1] = multiply(a, 0x09) ^ multiply(b, 0x0e) ^ multiply(c, 0x0b) ^ multiply(d, 0x0d);
aes.state[i][2] = multiply(a, 0x0d) ^ multiply(b, 0x09) ^ multiply(c, 0x0e) ^ multiply(d, 0x0b);
aes.state[i][3] = multiply(a, 0x0b) ^ multiply(b, 0x0d) ^ multiply(c, 0x09) ^ multiply(d, 0x0e);
}
}
fn void inv_sub_bytes(Aes* aes)
{
for (usz i = 0; i < 4; i++)
{
for (usz j = 0; j < 4; j++)
{
aes.state[j][i] = get_sbox_invert(aes.state[j][i]);
}
}
}
fn void inv_shift_rows(Aes* aes)
{
char temp;
temp = aes.state[3][1];
aes.state[3][1] = aes.state[2][1];
aes.state[2][1] = aes.state[1][1];
aes.state[1][1] = aes.state[0][1];
aes.state[0][1] = temp;
temp = aes.state[0][2];
aes.state[0][2] = aes.state[2][2];
aes.state[2][2] = temp;
temp = aes.state[1][2];
aes.state[1][2] = aes.state[3][2];
aes.state[3][2] = temp;
temp = aes.state[0][3];
aes.state[0][3] = aes.state[1][3];
aes.state[1][3] = aes.state[2][3];
aes.state[2][3] = aes.state[3][3];
aes.state[3][3] = temp;
}
fn void aes_cipher(Aes* aes, char[] round_key)
{
usz round = 0;
add_round_key(aes, 0, round_key);
for LOOP: (round = 1;; round++)
{
sub_bytes(aes);
shift_rows(aes);
if (round == aes.type.nr) break LOOP;
mix_columns(aes);
add_round_key(aes, round, round_key);
}
add_round_key(aes, aes.type.nr, round_key);
}
fn void inv_cipher(Aes* aes, char[] round_key)
{
add_round_key(aes, aes.type.nr, round_key);
for (usz round = aes.type.nr - 1; ; round--)
{
inv_shift_rows(aes);
inv_sub_bytes(aes);
add_round_key(aes, round, round_key);
if (!round) return;
inv_mix_columns(aes);
}
}
<*¨
@param type : "The AES variant to expant the key for"
@param [inout] round_key : "Key to expand into"
@param [in] key : "The key to expand"
@require key.len == type.key.key_len : "Key does not match expected length."
*>
fn void key_expansion(AesType type, char[] key, char[] round_key) @private
{
usz nk = type.key.nk;
for (usz i = 0; i < nk; i++)
{
round_key[(i * 4) + 0] = key[(i * 4) + 0];
round_key[(i * 4) + 1] = key[(i * 4) + 1];
round_key[(i * 4) + 2] = key[(i * 4) + 2];
round_key[(i * 4) + 3] = key[(i * 4) + 3];
}
for (usz i = nk; i < COLNUM * (type.key.nr + 1); i++)
{
usz k = (i - 1) * 4;
char[4] tempa @noinit;
tempa[0] = round_key[k + 0];
tempa[1] = round_key[k + 1];
tempa[2] = round_key[k + 2];
tempa[3] = round_key[k + 3];
if (i % nk == 0)
{
// rotword
char tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = tmp;
// subword
tempa[0] = get_sbox_value(tempa[0]);
tempa[1] = get_sbox_value(tempa[1]);
tempa[2] = get_sbox_value(tempa[2]);
tempa[3] = get_sbox_value(tempa[3]);
tempa[0] = tempa[0] ^ RCON[i / nk];
}
if (type.key.key_size == 256)
{
if (i % nk == 4)
{
// subword
tempa[0] = get_sbox_value(tempa[0]);
tempa[1] = get_sbox_value(tempa[1]);
tempa[2] = get_sbox_value(tempa[2]);
tempa[3] = get_sbox_value(tempa[3]);
}
}
usz j = i * 4;
k = (i - nk) * 4;
round_key[j + 0] = round_key[k + 0] ^ tempa[0];
round_key[j + 1] = round_key[k + 1] ^ tempa[1];
round_key[j + 2] = round_key[k + 2] ^ tempa[2];
round_key[j + 3] = round_key[k + 3] ^ tempa[3];
}
}

View File

@@ -0,0 +1,87 @@
// Experimental implementation
module std::crypto::aes128;
import std::crypto::aes;
fn char[] encrypt(Allocator allocator, char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
{
Aes aes @noinit;
aes.init(AES128, key, iv, CTR);
defer aes.destroy();
return aes.encrypt(allocator, data);
}
fn char[] tencrypt(char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
{
return encrypt(tmem, key, iv, data);
}
fn char[] decrypt(Allocator allocator, char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
{
Aes aes @noinit;
aes.init(AES128, key, iv, CTR);
defer aes.destroy();
return aes.decrypt(allocator, data);
}
fn char[] tdecrypt(char[16]* key, char[aes::BLOCKLEN] iv, char[] data)
{
return decrypt(tmem, key, iv, data);
}
module std::crypto::aes192;
import std::crypto::aes;
fn char[] encrypt(Allocator allocator, char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
{
Aes aes @noinit;
aes.init(AES192, key, iv, CTR);
defer aes.destroy();
return aes.encrypt(allocator, data);
}
fn char[] tencrypt(char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
{
return encrypt(tmem, key, iv, data);
}
fn char[] decrypt(Allocator allocator, char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
{
Aes aes @noinit;
aes.init(AES192, key, iv, CTR);
defer aes.destroy();
return aes.decrypt(allocator, data);
}
fn char[] tdecrypt(char[24]* key, char[aes::BLOCKLEN] iv, char[] data)
{
return decrypt(tmem, key, iv, data);
}
module std::crypto::aes256;
import std::crypto::aes;
fn char[] encrypt(Allocator allocator, char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
{
Aes aes @noinit;
aes.init(AES256, key, iv, CTR);
defer aes.destroy();
return aes.encrypt(allocator, data);
}
fn char[] tencrypt(char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
{
return encrypt(tmem, key, iv, data);
}
fn char[] decrypt(Allocator allocator, char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
{
Aes aes @noinit;
aes.init(AES256, key, iv, CTR);
defer aes.destroy();
return aes.decrypt(allocator, data);
}
fn char[] tdecrypt(char[32]* key, char[aes::BLOCKLEN] iv, char[] data)
{
return decrypt(tmem, key, iv, data);
}

View File

@@ -9,4 +9,3 @@ fn bool safe_compare(void* data1, void* data2, usz len)
}
return match == 0;
}

View File

@@ -212,7 +212,8 @@ fn F25519Int pack(Point* p)
struct Unpacking
{
Point point;
char on_curve; // Non-zero if true.
<* Non-zero if true. *>
char on_curve;
}
<*
@@ -365,7 +366,7 @@ fn void F25519Int.normalize(&s)
{
s.reduce_carry((*s)[^1] >> 7);
// Substract p
// Subtract p
F25519Int sub @noinit;
ushort c = 19;
foreach (i, v : (*s)[:^1])
@@ -399,7 +400,7 @@ fn char eq(F25519Int* a, F25519Int* b)
}
<*
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
Constant-time conditional selection. Result is undefined if condition is neither 0 nor 1.
@param [&in] zero : "selected if condition is 0"
@param [&in] one : "selected if condition is 1"
@@ -441,7 +442,7 @@ fn F25519Int F25519Int.add(&s, F25519Int* n) @operator(+)
macro F25519Int F25519Int.@sub(&s, F25519Int #n) @operator(-) => s.sub(@addr(#n));
<*
Substraction.
Subtraction.
@param [&in] s
@param [&in] n
@@ -638,7 +639,7 @@ fn FBaseInt from_bytes(char[] bytes)
}
<*
Constant-time conditonal selection. Result is undefined if condition is neither 0 nor 1.
Constant-time conditional selection. Result is undefined if condition is neither 0 nor 1.
@param [&in] zero : "selected if condition is 0"
@param [&in] one : "selected if condition is 1"
@@ -676,7 +677,7 @@ fn FBaseInt FBaseInt.add(&s, FBaseInt* n) @operator(+)
}
<*
Substraction if RHS is less than LHS else identity.
Subtraction if RHS is less than LHS else identity.
@param [&in] s
@param [&in] n

View File

@@ -33,7 +33,7 @@
module std::hash::a5hash;
macro @a5mul(#u, #v, #lo, #hi) @local
macro void @a5mul(#u, #v, #lo, #hi) @local
{
uint128 imd = (uint128)#u * (uint128)#v;
#lo = (ulong)imd;

View File

@@ -83,7 +83,7 @@ fn char[HASH_BYTES] Hmac.final(&self)
const IPAD @private = 0x36;
const OPAD @private = 0x5C;
macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
macro void @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[] out)
{
assert(out.len == HASH_BYTES);
char[HASH_BYTES] tmp @noinit;
@@ -104,4 +104,4 @@ macro @derive(Hmac *hmac_start, char[] salt, uint iterations, usz index, char[]
out[i] ^= v;
}
}
}
}

View File

@@ -33,7 +33,7 @@
module std::hash::komi;
macro @komimul(#u, #v, #lo, #hi) @local
macro void @komimul(#u, #v, #lo, #hi) @local
{
uint128 imd = (uint128)#u * (uint128)#v;
#lo = (ulong)imd;

View File

@@ -106,7 +106,7 @@ macro @h(x, y, z) => (x ^ y) ^ z;
macro @h2(x, y, z) => x ^ (y ^ z);
macro @i(x, y, z) => y ^ (x | ~z);
macro @step(#f, a, b, c, d, ptr, n, t, s)
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 = (*a << s) | ((*a & 0xffffffff) >> (32 - s));

View File

@@ -103,13 +103,13 @@ union Long16 @local
uint[16] l;
}
macro blk(Long16* block, i) @local
macro uint blk(Long16* block, i) @local
{
return (block.l[i & 15] = (block.l[(i + 13) & 15] ^ block.l[(i + 8) & 15]
^ block.l[(i + 2) & 15] ^ block.l[i & 15]).rotl(1));
}
macro blk0(Long16* block, i) @local
macro uint blk0(Long16* block, i) @local
{
$if env::BIG_ENDIAN:
return block.l[i];
@@ -119,35 +119,35 @@ macro blk0(Long16* block, i) @local
$endif
}
macro r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
macro void r0(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += ((w & (x ^ y)) ^ y) + blk0(block, i) + 0x5A827999 + v.rotl(5);
*wref = w.rotl(30);
}
macro r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
macro void r1(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += ((w & (x ^ y)) ^ y) + blk(block, i) + 0x5A827999 + v.rotl(5);
*wref = w.rotl(30);
}
macro r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
macro void r2(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += (w ^ x ^ y) + blk(block, i) + 0x6ED9EBA1 + v.rotl(5);
*wref = w.rotl(30);
}
macro r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
macro void r3(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += (((w | x) & y) | (w & x)) + blk(block, i) + 0x8F1BBCDC + v.rotl(5);
*wref = w.rotl(30);
}
macro r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
macro void r4(Long16* block, uint v, uint* wref, uint x, uint y, uint* z, uint i) @local
{
var w = *wref;
*z += (w ^ x ^ y) + blk(block, i) + 0xCA62C1D6 + v.rotl(5);
@@ -254,4 +254,4 @@ fn void sha1_transform(uint[5]* state, char* buffer) @local
(*state)[4] += e;
a = b = c = d = e = 0;
block = {};
}
}

View File

@@ -1,52 +1,52 @@
// 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.
//
// SipHash is a secure pseudorandom function (PRF) which digests a 128-bit key
// and a variable-length message to produce a 64- or 128-bit hash value.
//
// SipHash can be employed in numerous useful ways and structures, e.g.:
// - Hash Tables
// - Message Authentication Codes
// - Denial of Service (hash flooding) resistance
// - Bloom filters
// - Keyed runtime identifier derivation
//
// Read more: https://en.wikipedia.org/wiki/SipHash
//
//
// COMMON HASH VARIANTS.
// These two forms of SipHash (24 and 48) are the most widely
// used by many implementations.
// These provide typical 64-bit hash results.
// -- Best for performance-critical applications.
<*
Best for performance-critical applications.
See std::hash::siphash for more information.
*>
module std::hash::siphash24;
import std::hash::siphash;
alias SipHash24 = SipHash { ulong, 2, 4 };
alias hash = siphash::hash { ulong, 2, 4 };
// -- Best for conservative security applications.
<*
Best for security-focused applications.
See std::hash::siphash for more information.
*>
module std::hash::siphash48;
import std::hash::siphash;
alias SipHash48 = SipHash { ulong, 4, 8 };
alias hash = siphash::hash { ulong, 4, 8 };
// Exact same as above, but for 128-bit outputs. Algorithm internally changes slightly.
<* Exact same as siphash24, but for 128-bit outputs. Algorithm internally changes slightly. *>
module std::hash::siphash24_128;
import std::hash::siphash;
alias SipHash24_128 = SipHash { uint128, 2, 4 };
alias hash = siphash::hash { uint128, 2, 4 };
<* Exact same as siphash48, but for 128-bit outputs. Algorithm internally changes slightly. *>
module std::hash::siphash48_128;
import std::hash::siphash;
alias SipHash48_128 = SipHash { uint128, 4, 8 };
alias hash = siphash::hash { uint128, 4, 8 };
<*
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
SipHash is a secure pseudorandom function (PRF) which digests a 128-bit key
and a variable-length message to produce a 64- or 128-bit hash value.
SipHash can be employed in numerous useful ways and structures, e.g.:
- Hash Tables
- Message Authentication Codes
- Denial of Service (hash flooding) resistance
- Bloom filters
- Keyed runtime identifier derivation
Read more: https://en.wikipedia.org/wiki/SipHash
@require OutType.typeid == uint128.typeid || OutType.typeid == ulong.typeid : "Module OutType must be either uint128 or ulong."
*>
module std::hash::siphash { OutType, BLOCK_ROUNDS, FINALIZE_ROUNDS };

View File

@@ -20,7 +20,7 @@ fn char[] FileMmap.bytes(&self)
}
<*
Destroys the underlyng VirtualMemory object ie. calls munmap()"
Destroys the underlying VirtualMemory object ie. calls munmap()"
*>
fn void? FileMmap.destroy(&self) @maydiscard
{
@@ -41,7 +41,7 @@ module std::io::file @if(env::LIBC &&& env::POSIX);
@return? mem::OUT_OF_MEMORY, vm::ACCESS_DENIED, vm::RANGE_OVERFLOW, vm::INVALID_ARGS, vm::UNKNOWN_ERROR, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
@return "Memory mapped region. Must be released with FileMmap.destroy(). Provided File will not be closed"
*>
fn mmap::FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, vm::VirtualMemoryAccess access = READ, bool shared = false)
fn FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, VirtualMemoryAccess access = READ, bool shared = false)
{
if (len == 0)
{
@@ -78,7 +78,7 @@ fn mmap::FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, vm::Virtual
@return? mem::OUT_OF_MEMORY, vm::ACCESS_DENIED, vm::RANGE_OVERFLOW, vm::INVALID_ARGS, vm::UNKNOWN_ERROR, io::NO_PERMISSION, io::FILE_NOT_VALID, io::WOULD_BLOCK, io::FILE_NOT_FOUND
@return "Memory mapped region. Must be released with FileMmap.destroy()"
*>
fn mmap::FileMmap? mmap_open(String filename, String mode, usz offset = 0, usz len = 0, vm::VirtualMemoryAccess access = READ, bool shared = false)
fn FileMmap? mmap_open(String filename, String mode, usz offset = 0, usz len = 0, VirtualMemoryAccess access = READ, bool shared = false)
{
File file = open(filename, mode)!;
defer catch (void)file.close();

View File

@@ -22,7 +22,7 @@ fn usz? print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
return len;
}
macro Formatter.first_err(&self, fault f)
macro fault Formatter.first_err(&self, fault f)
{
if (self.first_fault) return self.first_fault;
self.first_fault = f;

View File

@@ -35,6 +35,7 @@ faultdef
NO_PERMISSION,
OUT_OF_SPACE,
OVERFLOW,
PATH_COULD_NOT_BE_FOUND,
READ_ONLY,
SYMLINK_FAILED,
TOO_MANY_DESCRIPTORS,

View File

@@ -1,5 +1,112 @@
module std::io::os;
enum NativeSystemDir
{
DESKTOP,
DOCUMENTS,
VIDEOS,
MUSIC,
DOWNLOADS,
PICTURES,
TEMPLATES,
PUBLIC_SHARE,
SAVED_GAMES,
SCREENSHOTS
}
module std::io::os @if(env::LIBC);
import std::io::path, std::os;
import std::io, std::os;
fn String? win32_get_known_folder_temp(Win32_REFKNOWNFOLDERID rfid) @private @if(env::WIN32)
{
Win32_PWSTR path;
Win32_HRESULT res = win32::shGetKnownFolderPath(rfid, 0x00008000 /* KF_FLAG_CREATE */, null, &path);
if (res) return io::PATH_COULD_NOT_BE_FOUND?;
return string::from_wstring(tmem, (WString)path);
}
fn Path? native_home_directory(Allocator allocator) => @pool()
{
$switch env::OS_TYPE:
$case IOS:
$case MACOS:
$case TVOS:
$case WATCHOS:
$case FREEBSD:
$case KFREEBSD:
$case LINUX:
$case NETBSD:
$case OPENBSD:
$case HAIKU:
return path::new(allocator, env::tget_var("HOME")) ?? io::PATH_COULD_NOT_BE_FOUND?;
$case WIN32:
return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PROFILE));
$default:
return io::PATH_COULD_NOT_BE_FOUND?;
$endswitch
}
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => @pool()
{
$switch env::OS_TYPE:
$case FREEBSD:
$case KFREEBSD:
$case LINUX:
$case NETBSD:
$case OPENBSD:
$case HAIKU:
switch (dir)
{
case DESKTOP: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DESKTOP"));
case DOWNLOADS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DOWNLOAD"));
case DOCUMENTS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "DOCUMENTS"));
case MUSIC: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "MUSIC"));
case VIDEOS: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "VIDEOS"));
case PICTURES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "PICTURES"));
case PUBLIC_SHARE: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "PUBLICSHARE"));
case TEMPLATES: return path::new(allocator, posix::xdg_user_dir_lookup(tmem, "TEMPLATES"));
case SAVED_GAMES:
case SCREENSHOTS: nextcase;
default: return io::PATH_COULD_NOT_BE_FOUND?;
}
$case IOS:
$case MACOS:
$case WATCHOS:
$case TVOS:
switch (dir)
{
case DESKTOP: return path::new(allocator, darwin::find_first_directory_temp(DESKTOP, USER));
case DOWNLOADS: return path::new(allocator, darwin::find_first_directory_temp(DOWNLOADS, USER));
case DOCUMENTS: return path::new(allocator, darwin::find_first_directory_temp(DOCUMENT, USER));
case MUSIC: return path::new(allocator, darwin::find_first_directory_temp(MUSIC, USER));
case VIDEOS: return path::new(allocator, darwin::find_first_directory_temp(MOVIES, USER));
case PICTURES: return path::new(allocator, darwin::find_first_directory_temp(PICTURES, USER));
case PUBLIC_SHARE: return path::new(allocator, darwin::find_first_directory_temp(SHARED_PUBLIC, USER));
case SAVED_GAMES:
case SCREENSHOTS:
case TEMPLATES: nextcase;
default: return io::PATH_COULD_NOT_BE_FOUND?;
}
$case WIN32:
switch (dir)
{
case DOWNLOADS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DOWNLOADS));
case DOCUMENTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DOCUMENTS));
case DESKTOP: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_DESKTOP));
case MUSIC: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_MUSIC));
case VIDEOS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_VIDEOS));
case PICTURES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_PICTURES));
case SAVED_GAMES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SAVED_GAMES));
case SCREENSHOTS: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_SCREENSHOTS));
case TEMPLATES: return path::new(allocator, win32_get_known_folder_temp(&win32::FOLDERID_TEMPLATES));
case PUBLIC_SHARE: nextcase;
default: return io::PATH_COULD_NOT_BE_FOUND?;
}
$default:
return io::PATH_COULD_NOT_BE_FOUND?;
$endswitch
}
fn Path? native_temp_directory(Allocator allocator) @if(!env::WIN32)
{
@@ -23,7 +130,6 @@ fn Path? native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool()
module std::io::os @if(env::NO_LIBC);
import std::io::path;
macro Path? native_temp_directory(Allocator allocator)
{
return io::UNSUPPORTED_OPERATION?;
}
macro Path? native_home_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND?;
macro Path? native_temp_directory(Allocator allocator) => io::PATH_COULD_NOT_BE_FOUND?;
fn Path? native_user_directory(Allocator allocator, NativeSystemDir dir) => io::PATH_COULD_NOT_BE_FOUND?;

View File

@@ -57,6 +57,18 @@ macro void? chdir(path)
fn Path? temp_directory(Allocator allocator) => os::native_temp_directory(allocator);
fn Path? home_directory(Allocator allocator) => os::native_home_directory(allocator);
fn Path? desktop_directory(Allocator allocator) => os::native_user_directory(allocator, DESKTOP);
fn Path? videos_directory(Allocator allocator) => os::native_user_directory(allocator, VIDEOS);
fn Path? music_directory(Allocator allocator) => os::native_user_directory(allocator, MUSIC);
fn Path? documents_directory(Allocator allocator) => os::native_user_directory(allocator, DOCUMENTS);
fn Path? screenshots_directory(Allocator allocator) => os::native_user_directory(allocator, SCREENSHOTS);
fn Path? saved_games_directory(Allocator allocator) => os::native_user_directory(allocator, SAVED_GAMES);
fn Path? downloads_directory(Allocator allocator) => os::native_user_directory(allocator, DOWNLOADS);
fn Path? pictures_directory(Allocator allocator) => os::native_user_directory(allocator, PICTURES);
fn Path? templates_directory(Allocator allocator) => os::native_user_directory(allocator, TEMPLATES);
fn Path? public_share_directory(Allocator allocator) => os::native_user_directory(allocator, PUBLIC_SHARE);
fn void? delete(Path path) => os::native_remove(path.str_view()) @inline;
macro bool @is_pathlike(#path) @const => $typeof(#path) == String ||| $typeof(#path) == Path;

View File

@@ -1,5 +1,7 @@
module std::io;
import std::math;
import std::core::env;
interface InStream
{
@@ -114,6 +116,9 @@ macro usz? write_all(stream, char[] buffer)
return n;
}
<*
@require @is_instream(s)
*>
macro usz? read_using_read_byte(s, char[] buffer)
{
usz len = 0;
@@ -131,12 +136,18 @@ macro usz? read_using_read_byte(s, char[] buffer)
return len;
}
<*
@require @is_outstream(s)
*>
macro void? write_byte_using_write(s, char c)
{
char[1] buff = { c };
s.write(&buff)!;
}
<*
@require @is_instream(s)
*>
macro char? read_byte_using_read(s)
{
char[1] buffer;
@@ -147,10 +158,12 @@ macro char? read_byte_using_read(s)
alias ReadByteFn = fn char?();
<*
@require @is_outstream(s)
*>
macro usz? write_using_write_byte(s, char[] bytes)
{
foreach (c : bytes) s.write_byte(self, c)!;
foreach (c : bytes) s.write_byte(c)!;
return bytes.len;
}
@@ -260,6 +273,16 @@ macro ushort? read_be_ushort(stream)
return (ushort)(hi_byte << 8 | lo_byte);
}
<*
@require @is_instream(stream)
*>
macro ushort? read_le_ushort(stream)
{
char lo_byte = stream.read_byte()!;
char hi_byte = stream.read_byte()!;
return (ushort)(hi_byte << 8 | lo_byte);
}
<*
@require @is_instream(stream)
*>
@@ -268,6 +291,14 @@ macro short? read_be_short(stream)
return read_be_ushort(stream);
}
<*
@require @is_instream(stream)
*>
macro short? read_le_short(stream)
{
return read_le_ushort(stream);
}
<*
@require @is_outstream(stream)
*>
@@ -277,6 +308,15 @@ macro void? write_be_short(stream, ushort s)
stream.write_byte((char)s)!;
}
<*
@require @is_outstream(stream)
*>
macro void? write_le_short(stream, ushort s)
{
stream.write_byte((char)s)!;
stream.write_byte((char)(s >> 8))!;
}
<*
@require @is_instream(stream)
*>
@@ -288,6 +328,17 @@ macro uint? read_be_uint(stream)
return val + stream.read_byte()!;
}
<*
@require @is_instream(stream)
*>
macro uint? read_le_uint(stream)
{
uint val = stream.read_byte()!;
val += stream.read_byte()! << 8;
val += stream.read_byte()! << 16;
return val + stream.read_byte()! << 24;
}
<*
@require @is_instream(stream)
*>
@@ -296,6 +347,14 @@ macro int? read_be_int(stream)
return read_be_uint(stream);
}
<*
@require @is_instream(stream)
*>
macro int? read_le_int(stream)
{
return read_le_uint(stream);
}
<*
@require @is_outstream(stream)
*>
@@ -307,6 +366,17 @@ macro void? write_be_int(stream, uint s)
stream.write_byte((char)s)!;
}
<*
@require @is_outstream(stream)
*>
macro void? write_le_int(stream, uint s)
{
stream.write_byte((char)s)!;
stream.write_byte((char)(s >> 8))!;
stream.write_byte((char)(s >> 16))!;
stream.write_byte((char)(s >> 24))!;
}
<*
@require @is_instream(stream)
*>
@@ -322,6 +392,21 @@ macro ulong? read_be_ulong(stream)
return val + stream.read_byte()!;
}
<*
@require @is_instream(stream)
*>
macro ulong? read_le_ulong(stream)
{
ulong val = (ulong)stream.read_byte()!;
val += (ulong)stream.read_byte()! << 8;
val += (ulong)stream.read_byte()! << 16;
val += (ulong)stream.read_byte()! << 24;
val += (ulong)stream.read_byte()! << 32;
val += (ulong)stream.read_byte()! << 40;
val += (ulong)stream.read_byte()! << 48;
return val + (ulong)stream.read_byte()! << 56;
}
<*
@require @is_instream(stream)
*>
@@ -330,6 +415,14 @@ macro long? read_be_long(stream)
return read_be_ulong(stream);
}
<*
@require @is_instream(stream)
*>
macro long? read_le_long(stream)
{
return read_le_ulong(stream);
}
<*
@require @is_outstream(stream)
*>
@@ -345,6 +438,21 @@ macro void? write_be_long(stream, ulong s)
stream.write_byte((char)s)!;
}
<*
@require @is_outstream(stream)
*>
macro void? write_le_long(stream, ulong s)
{
stream.write_byte((char)s)!;
stream.write_byte((char)(s >> 8))!;
stream.write_byte((char)(s >> 16))!;
stream.write_byte((char)(s >> 24))!;
stream.write_byte((char)(s >> 32))!;
stream.write_byte((char)(s >> 40))!;
stream.write_byte((char)(s >> 48))!;
stream.write_byte((char)(s >> 56))!;
}
<*
@require @is_instream(stream)
*>
@@ -368,6 +476,29 @@ macro uint128? read_be_uint128(stream)
return val + stream.read_byte()!;
}
<*
@require @is_instream(stream)
*>
macro uint128? read_le_uint128(stream)
{
uint128 val = stream.read_byte()!;
val += (uint128)stream.read_byte()! << 8;
val += (uint128)stream.read_byte()! << 16;
val += (uint128)stream.read_byte()! << 24;
val += (uint128)stream.read_byte()! << 32;
val += (uint128)stream.read_byte()! << 40;
val += (uint128)stream.read_byte()! << 48;
val += (uint128)stream.read_byte()! << 56;
val += (uint128)stream.read_byte()! << 64;
val += (uint128)stream.read_byte()! << 72;
val += (uint128)stream.read_byte()! << 80;
val += (uint128)stream.read_byte()! << 88;
val += (uint128)stream.read_byte()! << 96;
val += (uint128)stream.read_byte()! << 104;
val += (uint128)stream.read_byte()! << 112;
return val + (uint128)stream.read_byte()! << 120;
}
<*
@require @is_instream(stream)
*>
@@ -376,6 +507,14 @@ macro int128? read_be_int128(stream)
return read_be_uint128(stream);
}
<*
@require @is_instream(stream)
*>
macro int128? read_le_int128(stream)
{
return read_le_uint128(stream);
}
<*
@require @is_outstream(stream)
*>
@@ -399,6 +538,30 @@ macro void? write_be_int128(stream, uint128 s)
stream.write_byte((char)s)!;
}
<*
@require @is_outstream(stream)
*>
macro void? write_le_int128(stream, uint128 s)
{
stream.write_byte((char)s)!;
stream.write_byte((char)(s >> 8))!;
stream.write_byte((char)(s >> 16))!;
stream.write_byte((char)(s >> 24))!;
stream.write_byte((char)(s >> 32))!;
stream.write_byte((char)(s >> 40))!;
stream.write_byte((char)(s >> 48))!;
stream.write_byte((char)(s >> 56))!;
stream.write_byte((char)(s >> 64))!;
stream.write_byte((char)(s >> 72))!;
stream.write_byte((char)(s >> 80))!;
stream.write_byte((char)(s >> 88))!;
stream.write_byte((char)(s >> 96))!;
stream.write_byte((char)(s >> 104))!;
stream.write_byte((char)(s >> 112))!;
stream.write_byte((char)(s >> 120))!;
}
<*
@require @is_outstream(stream)
@require data.len < 256 : "Data exceeded 255"
@@ -443,6 +606,34 @@ macro char[]? read_short_bytearray(stream, Allocator allocator)
return data;
}
<*
@require @is_instream(stream)
*>
macro void? skip(stream, usz bytes)
{
if (!bytes) return;
$switch:
$case !$defined(stream.seek):
for (usz i = 0; i < bytes; i++)
{
stream.read()!;
}
return;
$case $typeof(stream) == InStream:
if (!&stream.seek)
{
for (usz i = 0; i < bytes; i++)
{
stream.read()!;
}
return;
}
stream.seek(bytes, CURSOR)!;
$default:
stream.seek(bytes, CURSOR)!;
$endswitch
}
<*
Wrap bytes for reading using io functions.
*>

View File

@@ -13,7 +13,7 @@ struct ByteBuffer (InStream, OutStream)
<*
ByteBuffer provides a streamable read/write buffer.
max_read defines how many bytes might be kept before its internal buffer is shrinked.
max_read defines how many bytes might be kept before its internal buffer is shrunk.
@require self.bytes.len == 0 : "Buffer already initialized."
*>
fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz initial_capacity = 16)
@@ -135,7 +135,7 @@ fn void ByteBuffer.grow(&self, usz n)
self.bytes = p[:n];
}
macro ByteBuffer.shrink(&self)
macro void ByteBuffer.shrink(&self)
{
if (self.read_idx >= self.max_read)
{
@@ -145,4 +145,4 @@ macro ByteBuffer.shrink(&self)
self.write_idx = 1 + readable;
self.read_idx = 1;
}
}
}

View File

@@ -100,7 +100,7 @@ extern fn CInt close(CInt fd) @if(!env::WIN32);
extern fn double difftime(Time_t time1, Time_t time2) @if(!env::WIN32);
extern fn DivResult div(CInt numer, CInt denom);
extern fn void exit(CInt status);
extern fn void _exit(CInt status) @extern("_Exit");
extern fn void _exit(CInt status) @cname("_Exit");
extern fn CInt fclose(CFile stream);
extern fn CFile fdopen(CInt fd, ZString mode) @if(!env::WIN32);
extern fn CInt feof(CFile stream);
@@ -211,9 +211,9 @@ const CInt STDOUT_FD = 1;
const CInt STDERR_FD = 2;
module libc @if(env::LINUX || env::ANDROID);
extern CFile __stdin @extern("stdin");
extern CFile __stdout @extern("stdout");
extern CFile __stderr @extern("stderr");
extern CFile __stdin @cname("stdin");
extern CFile __stdout @cname("stdout");
extern CFile __stderr @cname("stderr");
extern fn usz malloc_usable_size(void* ptr);
macro usz malloc_size(void* ptr) => malloc_usable_size(ptr);
extern fn void* aligned_alloc(usz align, usz size);
@@ -262,30 +262,30 @@ module libc @if(!env::LIBC);
import std::core::mem;
fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip
fn void longjmp(JmpBuf* buffer, CInt value) @weak @cname("longjmp") @nostrip
{
unreachable("longjmp unavailable");
}
fn CInt setjmp(JmpBuf* buffer) @weak @extern("setjmp") @nostrip
fn CInt setjmp(JmpBuf* buffer) @weak @cname("setjmp") @nostrip
{
unreachable("setjmp unavailable");
}
fn void* malloc(usz size) @weak @extern("malloc") @nostrip
fn void* malloc(usz size) @weak @cname("malloc") @nostrip
{
unreachable("malloc unavailable");
}
fn void* calloc(usz count, usz size) @weak @extern("calloc") @nostrip
fn void* calloc(usz count, usz size) @weak @cname("calloc") @nostrip
{
unreachable("calloc unavailable");
}
fn void* free(void*) @weak @extern("free")
fn void* free(void*) @weak @cname("free")
{
unreachable("free unavailable");
}
fn void* realloc(void* ptr, usz size) @weak @extern("realloc") @nostrip
fn void* realloc(void* ptr, usz size) @weak @cname("realloc") @nostrip
{
unreachable("realloc unavailable");
}
@@ -294,69 +294,69 @@ alias memcpy = mem::__memcpy;
alias memmove = mem::__memcpy;
alias memset = mem::__memset;
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @extern("fseek") @nostrip
fn int fseek(CFile stream, SeekIndex offset, int whence) @weak @cname("fseek") @nostrip
{
unreachable("'fseek' not available.");
}
fn CFile fopen(ZString filename, ZString mode) @weak @extern("fopen") @nostrip
fn CFile fopen(ZString filename, ZString mode) @weak @cname("fopen") @nostrip
{
unreachable("'fopen' not available.");
}
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @extern("fopen") @nostrip
fn CFile freopen(ZString filename, ZString mode, CFile stream) @weak @cname("fopen") @nostrip
{
unreachable("'freopen' not available.");
}
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fwrite") @nostrip
fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream) @weak @cname("fwrite") @nostrip
{
unreachable("'fwrite' not available.");
}
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @extern("fread") @nostrip
fn usz fread(void* ptr, usz size, usz nmemb, CFile stream) @weak @cname("fread") @nostrip
{
unreachable("'fread' not available.");
}
fn CFile fclose(CFile) @weak @extern("fclose") @nostrip
fn CFile fclose(CFile) @weak @cname("fclose") @nostrip
{
unreachable("'fclose' not available.");
}
fn int fflush(CFile stream) @weak @extern("fflush") @nostrip
fn int fflush(CFile stream) @weak @cname("fflush") @nostrip
{
unreachable("'fflush' not available.");
}
fn int fputc(int c, CFile stream) @weak @extern("fputc") @nostrip
fn int fputc(int c, CFile stream) @weak @cname("fputc") @nostrip
{
unreachable("'fputc' not available.");
}
fn char* fgets(ZString str, int n, CFile stream) @weak @extern("fgets") @nostrip
fn char* fgets(ZString str, int n, CFile stream) @weak @cname("fgets") @nostrip
{
unreachable("'fgets' not available.");
}
fn int fgetc(CFile stream) @weak @extern("fgetc") @nostrip
fn int fgetc(CFile stream) @weak @cname("fgetc") @nostrip
{
unreachable("'fgetc' not available.");
}
fn int feof(CFile stream) @weak @extern("feof") @nostrip
fn int feof(CFile stream) @weak @cname("feof") @nostrip
{
unreachable("'feof' not available.");
}
fn int putc(int c, CFile stream) @weak @extern("putc") @nostrip
fn int putc(int c, CFile stream) @weak @cname("putc") @nostrip
{
unreachable("'putc' not available.");
}
fn int putchar(int c) @weak @extern("putchar") @nostrip
fn int putchar(int c) @weak @cname("putchar") @nostrip
{
unreachable("'putchar' not available.");
}
fn int puts(ZString str) @weak @extern("puts") @nostrip
fn int puts(ZString str) @weak @cname("puts") @nostrip
{
unreachable("'puts' not available.");
}
@@ -407,17 +407,28 @@ alias SeekIndex = CLong;
struct Tm
{
CInt tm_sec; // seconds after the minute [0-60]
CInt tm_min; // minutes after the hour [0-59]
CInt tm_hour; // hours since midnight [0-23]
CInt tm_mday; // day of the month [1-31]
CInt tm_mon; // months since January [0-11]
CInt tm_year; // years since 1900
CInt tm_wday; // days since Sunday [0-6]
CInt tm_yday; // days since January 1 [0-365]
CInt tm_isdst; // Daylight Savings Time flag
TimeOffset tm_gmtoff @if(!env::WIN32); /* offset from UTC in seconds */
char* tm_zone @if(!env::WIN32); /* timezone abbreviation */
<* seconds after the minute [0-60] *>
CInt tm_sec;
<* minutes after the hour [0-59] *>
CInt tm_min;
<* hours since midnight [0-23] *>
CInt tm_hour;
<* day of the month [1-31] *>
CInt tm_mday;
<* months since January [0-11] *>
CInt tm_mon;
<* years since 1900 *>
CInt tm_year;
<* days since Sunday [0-6] *>
CInt tm_wday;
<* days since January 1 [0-365] *>
CInt tm_yday;
<* Daylight Savings Time flag *>
CInt tm_isdst;
<* offset from UTC in seconds *>
TimeOffset tm_gmtoff @if(!env::WIN32);
<* timezone abbreviation *>
char* tm_zone @if(!env::WIN32);
CInt tm_nsec @if(env::WASI);
}
@@ -433,12 +444,12 @@ alias Clock_t @if(env::WIN32) = int;
alias Clock_t @if(!env::WIN32) = CLong;
alias TimeOffset @if(env::WASI) = int;
alias TimeOffset @if(!env::WASI) = CLong ;
alias TimeOffset @if(!env::WASI) = CLong;
const int TIME_UTC = 1;
// This is a best-effort aproximation, but the C standard does not enforce
// This is a best-effort approximation, but the C standard does not enforce
// that this is a compile-time standard.
const CLOCKS_PER_SEC @if(env::WIN32) = 1000;
const CLOCKS_PER_SEC @if(!env::WIN32) = 1000000;

View File

@@ -17,19 +17,30 @@ struct Stat
Gid_t st_gid;
Dev_t st_rdev;
TimeSpec st_atimespec; // time of last access
TimeSpec st_mtimespec; // time of last data modification
TimeSpec st_ctimespec; // time of last status change
TimeSpec st_birthtimespec; // time of file creation(birth)
Off_t st_size; // file size, in bytes
Blkcnt_t st_blocks; // blocks allocated for file
Blksize_t st_blocksize; // optimal blocksize for I/O
uint st_flags; // user defined flags for file
uint st_gen; // file generation number
int st_lspare; // RESERVED
long[2] st_qspare; // RESERVED
<* time of last access *>
TimeSpec st_atimespec;
<* time of last data modification *>
TimeSpec st_mtimespec;
<* time of last status change *>
TimeSpec st_ctimespec;
<* time of file creation(birth) *>
TimeSpec st_birthtimespec;
<* file size, in bytes *>
Off_t st_size;
<* blocks allocated for file *>
Blkcnt_t st_blocks;
<* optimal blocksize for I/O *>
Blksize_t st_blocksize;
<* user defined flags for file *>
uint st_flags;
<* file generation number *>
uint st_gen;
<* RESERVED *>
int st_lspare;
<* RESERVED *>
long[2] st_qspare;
}
extern fn int stat(ZString str, Stat* stat) @extern("stat64");
extern fn int stat(ZString str, Stat* stat) @cname("stat64");
extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen);

View File

@@ -218,7 +218,7 @@ enum Speed : const CUInt
MAX_BAUD = B4000000,
}
enum Cc : const char
enum Cc : const inline char
{
VINTR = 0,
VQUIT = 1,

View File

@@ -62,7 +62,7 @@ struct SystemInfo
ushort wProcessorRevision;
}
extern fn CInt get_system_info(SystemInfo*) @extern("GetSystemInfo");
extern fn CInt get_system_info(SystemInfo*) @cname("GetSystemInfo");
// Aliases to simplify libc use
macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer);

View File

@@ -2,8 +2,8 @@ module std::math;
// Complex number aliases.
alias Complexf = Complex {float};
alias Complex = Complex {double};
alias Complexf = ComplexNumber {float};
alias Complex = ComplexNumber {double};
alias COMPLEX_IDENTITY @builtin = complex::IDENTITY {double};
alias COMPLEXF_IDENTITY @builtin = complex::IDENTITY {float};
alias IMAGINARY @builtin @deprecated("Use I") = complex::IMAGINARY { double };
@@ -19,7 +19,7 @@ alias I_F @builtin = complex::IMAGINARY { float };
module std::math::complex {Real};
import std::io;
union Complex (Printable)
union ComplexNumber (Printable)
{
struct
{
@@ -28,39 +28,39 @@ union Complex (Printable)
Real[<2>] v;
}
const Complex IDENTITY = { 1, 0 };
const Complex IMAGINARY = { 0, 1 };
const ComplexNumber IDENTITY = { 1, 0 };
const ComplexNumber IMAGINARY = { 0, 1 };
macro Complex Complex.add(self, Complex b) @operator(+) => { .v = self.v + b.v };
macro Complex Complex.add_this(&self, Complex b) @operator(+=) => { .v = self.v += b.v };
macro Complex Complex.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
macro Complex Complex.add_each(self, Real b) => { .v = self.v + b };
macro Complex Complex.sub(self, Complex b) @operator(-) => { .v = self.v - b.v };
macro Complex Complex.sub_this(&self, Complex b) @operator(-=) => { .v = self.v -= b.v };
macro Complex Complex.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
macro Complex Complex.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
macro Complex Complex.sub_each(self, Real b) => { .v = self.v - b };
macro Complex Complex.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
macro Complex Complex.mul(self, Complex b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
macro Complex Complex.div_real(self, Real r) @operator(/) => { .v = self.v / r };
macro Complex Complex.div_real_inverse(Complex c, Real r) @operator_r(/) => ((Complex) { .r = r }).div(c);
macro Complex Complex.div(self, Complex b) @operator(/)
macro ComplexNumber ComplexNumber.add(self, ComplexNumber b) @operator(+) => { .v = self.v + b.v };
macro ComplexNumber ComplexNumber.add_this(&self, ComplexNumber b) @operator(+=) => { .v = self.v += b.v };
macro ComplexNumber ComplexNumber.add_real(self, Real r) @operator_s(+) => { .v = self.v + (Real[<2>]) { r, 0 } };
macro ComplexNumber ComplexNumber.add_each(self, Real b) => { .v = self.v + b };
macro ComplexNumber ComplexNumber.sub(self, ComplexNumber b) @operator(-) => { .v = self.v - b.v };
macro ComplexNumber ComplexNumber.sub_this(&self, ComplexNumber b) @operator(-=) => { .v = self.v -= b.v };
macro ComplexNumber ComplexNumber.sub_real(self, Real r) @operator(-) => { .v = self.v - (Real[<2>]) { r, 0 } };
macro ComplexNumber ComplexNumber.sub_real_inverse(self, Real r) @operator_r(-) => { .v = (Real[<2>]) { r, 0 } - self.v };
macro ComplexNumber ComplexNumber.sub_each(self, Real b) => { .v = self.v - b };
macro ComplexNumber ComplexNumber.scale(self, Real r) @operator_s(*) => { .v = self.v * r };
macro ComplexNumber ComplexNumber.mul(self, ComplexNumber b)@operator(*) => { self.r * b.r - self.c * b.c, self.r * b.c + b.r * self.c };
macro ComplexNumber ComplexNumber.div_real(self, Real r) @operator(/) => { .v = self.v / r };
macro ComplexNumber ComplexNumber.div_real_inverse(ComplexNumber c, Real r) @operator_r(/) => ((ComplexNumber) { .r = r }).div(c);
macro ComplexNumber ComplexNumber.div(self, ComplexNumber b) @operator(/)
{
Real div = b.v.dot(b.v);
return { (self.r * b.r + self.c * b.c) / div, (self.c * b.r - self.r * b.c) / div };
}
macro Complex Complex.inverse(self)
macro ComplexNumber ComplexNumber.inverse(self)
{
Real sqr = self.v.dot(self.v);
return { self.r / sqr, -self.c / sqr };
}
macro Complex Complex.conjugate(self) => { .r = self.r, .c = -self.c };
macro Complex Complex.negate(self) @operator(-) => { .v = -self.v };
macro bool Complex.equals(self, Complex b) @operator(==) => self.v == b.v;
macro bool Complex.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
macro bool Complex.not_equals(self, Complex b) @operator(!=) => self.v != b.v;
macro ComplexNumber ComplexNumber.conjugate(self) => { .r = self.r, .c = -self.c };
macro ComplexNumber ComplexNumber.negate(self) @operator(-) => { .v = -self.v };
macro bool ComplexNumber.equals(self, ComplexNumber b) @operator(==) => self.v == b.v;
macro bool ComplexNumber.equals_real(self, Real r) @operator_s(==) => self.v == { r, 0 };
macro bool ComplexNumber.not_equals(self, ComplexNumber b) @operator(!=) => self.v != b.v;
fn usz? Complex.to_format(&self, Formatter* f) @dynamic
fn usz? ComplexNumber.to_format(&self, Formatter* f) @dynamic
{
return f.printf("%g%+gi", self.r, self.c);
}

View File

@@ -6,7 +6,7 @@ import std::math::complex;
import std::math::matrix;
import std::math::quaternion;
attrdef @MathLibc(name) = @extern(name), @link(env::POSIX, "m");
attrdef @MathLibc(name) = @cname(name), @link(env::POSIX, "m");
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
const LOG2E = 1.44269504088896340735992468100189214; // log2(e)
@@ -73,6 +73,11 @@ faultdef OVERFLOW, MATRIX_INVERSE_DOESNT_EXIST;
*>
macro deg_to_rad(x) => x * PI / 180;
<*
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
*>
macro rad_to_deg(x) => x * 180 / PI;
<*
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
*>
@@ -82,7 +87,7 @@ macro abs(x) => $$abs(x);
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
@require values::@is_int(y) || values::@is_float(y) : "Expected an integer or floating point value"
*>
macro is_approx(x, y, eps)
macro bool is_approx(x, y, eps)
{
if (x == y) return true;
if (is_nan(x) || is_nan(y)) return false;
@@ -93,7 +98,7 @@ macro is_approx(x, y, eps)
@require values::@is_int(x) || values::@is_float(x) : "Expected an integer or floating point value"
@require values::@is_int(y) || values::@is_float(y) : "Expected an integer or floating point value"
*>
macro is_approx_rel(x, y, eps)
macro bool is_approx_rel(x, y, eps)
{
if (x == y) return true;
if (is_nan(x) || is_nan(y)) return false;
@@ -132,7 +137,7 @@ macro atan2(x, y)
@require @typematch(sinp, cosp) : "Expected sinp and cosp to have the same type"
@require $defined(*sinp = x) : "Expected x and *sinp/*cosp to have the same type"
*>
macro sincos_ref(x, sinp, cosp)
macro void sincos_ref(x, sinp, cosp)
{
$if $typeof(sinp) == float*:
_sincosf(x, sinp, cosp);
@@ -252,8 +257,8 @@ macro @ceil($input) @const => $$ceil($input);
@return "lower if x < lower, upper if x > upper, otherwise return x."
@require types::is_numerical($typeof(x)) : `The input must be a numerical value or numerical vector`
@require $defined(x = lower) : `The lower bound must be convertable to the value type.`
@require $defined(x = upper) : `The upper bound must be convertable to the value type.`
@require $defined(x = lower) : `The lower bound must be convertible to the value type.`
@require $defined(x = upper) : `The upper bound must be convertible to the value type.`
*>
macro clamp(x, lower, upper) => $$max(($typeof(x))lower, $$min(x, ($typeof(x))upper));
@@ -540,7 +545,7 @@ macro bool is_finite(x)
<*
@require values::@is_promotable_to_float(x) : `The input must be a float`
*>
macro is_nan(x)
macro bool is_nan(x)
{
$switch $typeof(x):
$case float:
@@ -554,7 +559,7 @@ macro is_nan(x)
<*
@require values::@is_promotable_to_float(x) : `The input must be a float`
*>
macro is_inf(x)
macro bool is_inf(x)
{
$switch $typeof(x):
$case float:
@@ -1042,8 +1047,8 @@ extern fn void _sincosf(float, float*, float*) @MathLibc("__sincosf") @if(env::D
extern fn void _sincos(double, double*, double*) @MathLibc("sincos") @if(!env::DARWIN && !env::WIN32);
extern fn void _sincosf(float, float*, float*) @MathLibc("sincosf") @if(!env::DARWIN && !env::WIN32);
fn void _sincos(double a, double* s, double* c) @extern("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
fn void _sincosf(float a, float* s, float* c) @extern("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
fn void _sincos(double a, double* s, double* c) @cname("sincos") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
fn void _sincosf(float a, float* s, float* c) @cname("sincosf") @if(env::WIN32) { *s = sin(a); *c = cos(a); }
extern fn double _tan(double x) @MathLibc("tan");
extern fn float _tanf(float x) @MathLibc("tanf");

View File

@@ -12,7 +12,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* ====================================================
*/
fn double __cos(double x, double y) @extern("__cos") @weak @nostrip
fn double __cos(double x, double y) @cname("__cos") @weak @nostrip
{
const C1 = 4.16666666666666019037e-02; /* 0x3FA55555, 0x5555554C */
const C2 = -1.38888888888741095749e-03; /* 0xBF56C16C, 0x16C15177 */

View File

@@ -22,7 +22,7 @@ const double C1 @private = 0x155553e1053a42.0p-57; /* 0.0416666233237390631894
const double C2 @private = -0x16c087e80f1e27.0p-62; /* -0.00138867637746099294692 */
const double C3 @private = 0x199342e0ee5069.0p-68; /* 0.0000243904487962774090654 */
fn float __cosdf(double x) @extern("__cosdf") @weak @nostrip
fn float __cosdf(double x) @cname("__cosdf") @weak @nostrip
{
/* Try to optimize for parallel evaluation as in __tandf.c. */
double z = x * x;

View File

@@ -13,7 +13,7 @@ union FloatInternal
}
// Based on the musl implementation
fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
fn double fmod(double x, double y) @cname("fmod") @weak @nostrip
{
DoubleInternal ux = { .f = x };
DoubleInternal uy = { .f = y };
@@ -83,7 +83,7 @@ fn double fmod(double x, double y) @extern("fmod") @weak @nostrip
return ux.f;
}
fn float fmodf(float x, float y) @extern("fmodf") @weak @nostrip
fn float fmodf(float x, float y) @cname("fmodf") @weak @nostrip
{
FloatInternal ux = { .f = x };
FloatInternal uy = { .f = y };

View File

@@ -11,7 +11,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* is preserved.
* ====================================================
*/
fn double __sin(double x, double y, int iy) @extern("__sin") @weak @nostrip
fn double __sin(double x, double y, int iy) @cname("__sin") @weak @nostrip
{
const S1 = -1.66666666666666324348e-01; /* 0xBFC55555, 0x55555549 */

View File

@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* ====================================================
*/
// |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]).
fn float __sindf(double x) @extern("__sindf") @weak @nostrip
fn float __sindf(double x) @cname("__sindf") @weak @nostrip
{
const S1F = -0x15555554cbac77.0p-55; /* -0.166666666416265235595 */
const S2F = 0x111110896efbb2.0p-59; /* 0.0083333293858894631756 */

View File

@@ -27,7 +27,7 @@ const double[*] TAN_T = {
2.59073051863633712884e-05, /* 3EFB2A70, 74BF7AD4 */
};
fn double __tan(double x, double y, int odd) @extern("__tan") @weak @nostrip
fn double __tan(double x, double y, int odd) @cname("__tan") @weak @nostrip
{
const double PIO4 = 7.85398163397448278999e-01; /* 3FE921FB, 54442D18 */
const double PIO4LO = 3.06161699786838301793e-17; /* 3C81A626, 33145C07 */

View File

@@ -25,7 +25,7 @@ const double[*] TANDF = {
0x1362b9bf971bcd.0p-59, /* 0.00946564784943673166728 */
};
fn float __tandf(double x, int odd) @extern("__tandf") @weak @nostrip
fn float __tandf(double x, int odd) @cname("__tandf") @weak @nostrip
{
double z = x * x;
/*

View File

@@ -32,7 +32,7 @@ fn double _r(double z) @local
return p / q;
}
fn double _acos(double x) @weak @extern("acos") @nostrip
fn double _acos(double x) @weak @cname("acos") @nostrip
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
@@ -100,7 +100,7 @@ fn float _r_f(float z) @local
return p / q;
}
fn float _acosf(float x) @weak @extern("acosf") @nostrip
fn float _acosf(float x) @weak @cname("acosf") @nostrip
{
uint hx = bitcast(x, uint);
uint ix = hx & 0x7fffffff;

View File

@@ -32,7 +32,7 @@ fn double _r(double z) @local
return p / q;
}
fn double _asin(double x) @weak @extern("asin") @nostrip
fn double _asin(double x) @weak @cname("asin") @nostrip
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
@@ -102,7 +102,7 @@ fn float _r_f(float z) @local
return p / q;
}
fn float _asinf(float x) @weak @extern("asinf") @nostrip
fn float _asinf(float x) @weak @cname("asinf") @nostrip
{
uint hx = bitcast(x, uint);
uint ix = hx & 0x7fffffff;

View File

@@ -40,7 +40,7 @@ const double[*] AT @private = {
1.62858201153657823623e-02, /* 0x3F90AD3A, 0xE322DA11 */
};
fn double _atan(double x) @weak @extern("atan") @nostrip
fn double _atan(double x) @weak @cname("atan") @nostrip
{
int id @noinit;
uint ix = x.high_word();
@@ -138,7 +138,7 @@ const float[*] ATF @private = {
6.1687607318e-02,
};
fn float _atanf(float x) @weak @extern("atanf") @nostrip
fn float _atanf(float x) @weak @cname("atanf") @nostrip
{
int id @noinit;
uint ix = x.word();
@@ -217,7 +217,7 @@ fn float _atanf(float x) @weak @extern("atanf") @nostrip
const PI_LO @private = 1.2246467991473531772E-16; /* 0x3CA1A626, 0x33145C07 */
fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
fn double _atan2(double y, double x) @weak @cname("atan2") @nostrip
{
if (math::is_nan(x) || math::is_nan(y)) return x + y;
@@ -301,7 +301,7 @@ fn double _atan2(double y, double x) @weak @extern("atan2") @nostrip
const float PI_F @private = 3.1415927410e+00; /* 0x40490fdb */
const float PI_LO_F @private = -8.7422776573e-08; /* 0xb3bbbd2e */
fn float _atan2f(float y, float x) @weak @extern("atan2f") @nostrip
fn float _atan2f(float y, float x) @weak @cname("atan2f") @nostrip
{
if (math::is_nan(x) || math::is_nan(y)) return x + y;
uint ix = x.word();

View File

@@ -12,7 +12,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* ====================================================
*/
fn double _atanh(double x) @weak @extern("atanh") @nostrip
fn double _atanh(double x) @weak @cname("atanh") @nostrip
{
double t @noinit;
uint hx = x.high_word();
@@ -61,7 +61,7 @@ fn double _atanh(double x) @weak @extern("atanh") @nostrip
* ====================================================
*/
fn float _atanhf(float x) @weak @extern("atanhf") @nostrip
fn float _atanhf(float x) @weak @cname("atanhf") @nostrip
{
float t @noinit;
uint hx = bitcast(x, uint);

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _ceil(double x) @weak @extern("ceil") @nostrip
fn double _ceil(double x) @weak @cname("ceil") @nostrip
{
ulong ui = bitcast(x, ulong);
int e = (int)((ui >> 52) & 0x7ff);
@@ -17,7 +17,7 @@ fn double _ceil(double x) @weak @extern("ceil") @nostrip
}
fn float _ceilf(float x) @weak @extern("ceilf") @nostrip
fn float _ceilf(float x) @weak @cname("ceilf") @nostrip
{
uint u = bitcast(x, uint);
int e = (int)((u >> 23) & 0xff) - 0x7f;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn float _cosf(float x) @extern("cosf") @weak @nostrip
fn float _cosf(float x) @cname("cosf") @weak @nostrip
{
uint ix = x.word();
uint sign = ix >> 31;
@@ -51,7 +51,7 @@ fn float _cosf(float x) @extern("cosf") @weak @nostrip
* ====================================================
*/
fn double _cos(double x) @extern("cos") @weak @nostrip
fn double _cos(double x) @cname("cos") @weak @nostrip
{
// High word of x.
uint ix = x.high_word() & 0x7fffffff;

View File

@@ -17,7 +17,7 @@ const float EXPF_P2 = -2.7777778450e-03f;
const float EXPF_P3 = 6.6137559770e-05f;
const float EXPF_P4 = -1.6533901999e-06f;
fn double exp(double x) @extern("exp") @nostrip @weak
fn double exp(double x) @cname("exp") @nostrip @weak
{
if (x != x) return x;
if (x == double.inf) return double.inf;
@@ -38,7 +38,7 @@ fn double exp(double x) @extern("exp") @nostrip @weak
return ldexp(exp_r, (int)k);
}
fn float expf(float x) @extern("expf") @nostrip @weak
fn float expf(float x) @cname("expf") @nostrip @weak
{
if (x != x) return x;
if (x == float.inf) return float.inf;

View File

@@ -3,7 +3,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
macro uint _top12f(float x) @private => bitcast(x, uint) >> 20;
fn float _exp2f(float x) @extern("exp2f") @weak @nostrip
fn float _exp2f(float x) @cname("exp2f") @weak @nostrip
{
double xd = x;
uint abstop = _top12f(x) & 0x7ff;
@@ -80,7 +80,7 @@ macro uint _top12d(double x) @private
return (uint)(bitcast(x, ulong) >> 52);
}
fn double _exp2(double x) @extern("exp2") @weak @nostrip
fn double _exp2(double x) @cname("exp2") @weak @nostrip
{
uint abstop = _top12d(x) & 0x7ff;
ulong u = bitcast(x, ulong);

View File

@@ -1,13 +1,13 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _fabs(double x) @weak @extern("fabs") @nostrip
fn double _fabs(double x) @weak @cname("fabs") @nostrip
{
ulong ix = bitcast(x, ulong);
ix &= ~(1ul << 63);
return bitcast(ix, double);
}
fn float _fabsf(float x) @weak @extern("fabsf") @nostrip
fn float _fabsf(float x) @weak @cname("fabsf") @nostrip
{
uint ix = bitcast(x, uint);
ix &= 0x7fffffff;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _floor(double x) @weak @extern("floor") @nostrip
fn double _floor(double x) @weak @cname("floor") @nostrip
{
ulong ui = bitcast(x, ulong);
int e = (int)((ui >> 52) & 0x7ff);
@@ -17,7 +17,7 @@ fn double _floor(double x) @weak @extern("floor") @nostrip
}
fn float _floorf(float x) @weak @extern("floorf") @nostrip
fn float _floorf(float x) @weak @cname("floorf") @nostrip
{
uint u = bitcast(x, uint);
int e = (int)((u >> 23) & 0xff) - 0x7f;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double frexp(double x, int* exp) @extern("frexp")
fn double frexp(double x, int* exp) @cname("frexp")
{
uint hx = x.high_word();
uint ix = hx & 0x7fffffff;
@@ -31,7 +31,7 @@ fn double frexp(double x, int* exp) @extern("frexp")
}
}
fn float frexpf(float x, int* exp) @extern("frexpf")
fn float frexpf(float x, int* exp) @cname("frexpf")
{
uint ix = x.word();
uint hx = ix & 0x7fffffff;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double ldexp(double x, int exp) @extern("ldexp")
fn double ldexp(double x, int exp) @cname("ldexp")
{
uint hx = x.high_word();
int hexp = (int)((hx & 0x7ff00000) >> 20);
@@ -35,7 +35,7 @@ fn double ldexp(double x, int exp) @extern("ldexp")
}
}
fn float ldexpf(float x, int exp) @extern("ldexpf")
fn float ldexpf(float x, int exp) @cname("ldexpf")
{
uint ix = x.word();
int hexp = (int)((ix & 0x7f800000) >> 23);

View File

@@ -19,7 +19,7 @@ const float LOGF_L4 = 2.4279078841e-01f;
const double SQRT2 = 1.41421356237309504880;
const float SQRT2F = 1.41421356237309504880f;
fn double log(double x) @extern("log") @nostrip @weak
fn double log(double x) @cname("log") @nostrip @weak
{
if (x != x) return x;
if (x < 0.0) return double.nan;
@@ -50,7 +50,7 @@ fn double log(double x) @extern("log") @nostrip @weak
return k * LOG_LN2_HI - ((hfsq - (s * (hfsq + r) + k * LOG_LN2_LO)) - f);
}
fn float logf(float x) @extern("logf") @nostrip @weak
fn float logf(float x) @cname("logf") @nostrip @weak
{
if (x != x) return x;
if (x < 0.0f) return float.nan;

View File

@@ -48,7 +48,7 @@ const LG5 @local = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */
const LG6 @local = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */
const LG7 @local = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
fn double _log1p(double x) @weak @extern("log1p") @nostrip
fn double _log1p(double x) @weak @cname("log1p") @nostrip
{
uint hx = x.high_word();
int k = 1;
@@ -162,7 +162,7 @@ const float LG2_F @local = 0xccce13.0p-25; /* 0.40000972152 */
const float LG3_F @local = 0x91e9ee.0p-25; /* 0.28498786688 */
const float LG4_F @local = 0xf89e26.0p-26; /* 0.24279078841 */
fn float _log1pf(float x) @weak @extern("log1pf") @nostrip
fn float _log1pf(float x) @weak @cname("log1pf") @nostrip
{
uint ix = x.word();
int k = 1;

View File

@@ -55,7 +55,8 @@ struct Exp2Data @private
double shift;
double negln2hiN;
double negln2loN;
double[4] poly; // Last four coefficients.
<* Last four coefficients. *>
double[4] poly;
double exp2_shift;
double[EXP2_POLY_ORDER] exp2_poly;
ulong[2 * EXP_DATA_WIDTH] tab;
@@ -253,4 +254,4 @@ macro force_eval_add(x, v)
{
$typeof(x) temp @noinit;
@volatile_store(temp, x + v);
}
}

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double pow(double x, double y) @extern("pow")
fn double pow(double x, double y) @cname("pow")
{
if (x != x || y != y) return double.nan;
@@ -54,7 +54,7 @@ fn double pow(double x, double y) @extern("pow")
return result;
}
fn float powf(float x, float y) @extern("powf")
fn float powf(float x, float y) @cname("powf")
{
if (x != x || y != y) return float.nan;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _round(double x) @extern("round") @weak @nostrip
fn double _round(double x) @cname("round") @weak @nostrip
{
ulong u = bitcast(x, ulong);
int e = (int)((u >> 52) & 0x7ff);
@@ -26,7 +26,7 @@ fn double _round(double x) @extern("round") @weak @nostrip
return y;
}
fn float _roundf(float x) @extern("roundf") @weak @nostrip
fn float _roundf(float x) @cname("roundf") @weak @nostrip
{
uint u = bitcast(x, uint);
int e = (u >> 23) & 0xff;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _scalbn(double x, int n) @weak @extern("scalbn") @nostrip
fn double _scalbn(double x, int n) @weak @cname("scalbn") @nostrip
{
switch
{

View File

@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* ====================================================
*/
fn float _sinf(float x) @weak @extern("sinf") @nostrip
fn float _sinf(float x) @weak @cname("sinf") @nostrip
{
uint ix = x.word();
int sign = ix >> 31;
@@ -84,7 +84,7 @@ fn float _sinf(float x) @weak @extern("sinf") @nostrip
* ====================================================
*/
fn double sin(double x) @extern("sin") @weak @nostrip
fn double sin(double x) @cname("sin") @weak @nostrip
{
// High word of x.
uint ix = x.high_word() & 0x7fffffff;

View File

@@ -16,7 +16,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* ====================================================
*/
fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nostrip
fn void sincosf(float x, float *sin, float *cos) @cname("__sincosf") @weak @nostrip
{
uint ix = x.word();
uint sign = ix >> 31;
@@ -104,7 +104,7 @@ fn void sincosf(float x, float *sin, float *cos) @extern("__sincosf") @weak @nos
}
fn void sincos(double x, double *sin, double *cos) @extern("__sincos") @weak @nostrip
fn void sincos(double x, double *sin, double *cos) @cname("__sincos") @weak @nostrip
{
// High word of x.
uint ix = x.high_word() & 0x7fffffff;

View File

@@ -12,7 +12,7 @@ module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
* ====================================================
*/
fn double tan(double x) @extern("tan") @weak @nostrip
fn double tan(double x) @cname("tan") @weak @nostrip
{
uint ix = x.high_word();
ix &= 0x7fffffff;
@@ -57,7 +57,7 @@ fn double tan(double x) @extern("tan") @weak @nostrip
* ====================================================
*/
fn float tanf(float x) @extern("tanf") @weak @nostrip
fn float tanf(float x) @cname("tanf") @weak @nostrip
{
uint ix = x.word();
uint sign = ix >> 31;

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double sincos_broken(double x) @extern("sincos") @weak @nostrip
fn double sincos_broken(double x) @cname("sincos") @weak @nostrip
{
unreachable("'sinccos' not supported");
}

View File

@@ -1,6 +1,6 @@
module std::math::nolibc @if(env::NO_LIBC || $feature(C3_MATH));
fn double _trunc(double x) @weak @extern("trunc") @nostrip
fn double _trunc(double x) @weak @cname("trunc") @nostrip
{
ulong i = bitcast(x, ulong);
int e = (int)((i >> 52) & 0x7ff) - 0x3ff + 12;
@@ -13,7 +13,7 @@ fn double _trunc(double x) @weak @extern("trunc") @nostrip
return bitcast(i, double);
}
fn float _truncf(float x) @weak @extern("truncf") @nostrip
fn float _truncf(float x) @weak @cname("truncf") @nostrip
{
uint i = bitcast(x, uint);
int e = (int)((i >> 23) & 0xff) - 0x7f + 9;

View File

@@ -2,8 +2,8 @@ module std::math;
// Predefined quaternion aliases.
alias Quaternionf = Quaternion {float};
alias Quaternion = Quaternion {double};
alias Quaternionf = QuaternionNumber {float};
alias Quaternion = QuaternionNumber {double};
alias QUATERNION_IDENTITY @builtin = quaternion::IDENTITY {double};
alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
@@ -15,7 +15,7 @@ alias QUATERNIONF_IDENTITY @builtin = quaternion::IDENTITY {float};
module std::math::quaternion {Real};
import std::math::vector;
union Quaternion
union QuaternionNumber
{
struct
{
@@ -24,22 +24,25 @@ union Quaternion
Real[<4>] v;
}
const Quaternion IDENTITY = { 0, 0, 0, 1 };
const QuaternionNumber IDENTITY = { 0, 0, 0, 1 };
macro Quaternion Quaternion.add(self, Quaternion b) @operator(+) => { .v = self.v + b.v };
macro Quaternion Quaternion.add_each(self, Real b) => { .v = self.v + b };
macro Quaternion Quaternion.sub(self, Quaternion b) @operator(-) => { .v = self.v - b.v };
macro Quaternion Quaternion.negate(self) @operator(-) => { .v = -self.v };
macro Quaternion Quaternion.sub_each(self, Real b) => { .v = self.v - b };
macro Quaternion Quaternion.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
macro Quaternion Quaternion.normalize(self) => { .v = self.v.normalize() };
macro Real Quaternion.length(self) => self.v.length();
macro Quaternion Quaternion.lerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
macro Matrix4f Quaternion.to_matrixf(&self) => into_matrix(self, Matrix4f);
macro Matrix4 Quaternion.to_matrix(&self) => into_matrix(self, Matrix4);
fn Quaternion Quaternion.nlerp(self, Quaternion q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
macro QuaternionNumber QuaternionNumber.add(self, QuaternionNumber b) @operator(+) => { .v = self.v + b.v };
macro QuaternionNumber QuaternionNumber.add_each(self, Real b) => { .v = self.v + b };
macro QuaternionNumber QuaternionNumber.sub(self, QuaternionNumber b) @operator(-) => { .v = self.v - b.v };
macro QuaternionNumber QuaternionNumber.negate(self) @operator(-) => { .v = -self.v };
macro QuaternionNumber QuaternionNumber.sub_each(self, Real b) => { .v = self.v - b };
macro QuaternionNumber QuaternionNumber.scale(self, Real s) @operator_s(*) => { .v = self.v * s };
macro QuaternionNumber.to_angle(self) => 2 * math::acos(self.v.w);
macro QuaternionNumber QuaternionNumber.normalize(self) => { .v = self.v.normalize() };
macro Real QuaternionNumber.length(self) => self.v.length();
macro QuaternionNumber QuaternionNumber.lerp(self, QuaternionNumber q2, Real amount) => { .v = self.v.lerp(q2.v, amount) };
fn QuaternionNumber QuaternionNumber.nlerp(self, QuaternionNumber q2, Real amount) => { .v = self.v.lerp(q2.v, amount).normalize() };
fn Quaternion Quaternion.invert(self)
macro Matrix4f QuaternionNumber.to_matrixf(&self) => into_matrix(self, Matrix4f);
macro Matrix4 QuaternionNumber.to_matrix(&self) => into_matrix(self, Matrix4);
fn QuaternionNumber QuaternionNumber.invert(self)
{
Real length_sq = self.v.dot(self.v);
if (length_sq <= 0) return self;
@@ -47,9 +50,11 @@ fn Quaternion Quaternion.invert(self)
return { self.v[0] * -inv_length, self.v[1] * -inv_length, self.v[2] * -inv_length, self.v[3] * inv_length };
}
fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
fn QuaternionNumber QuaternionNumber.conjugate(&self) => { -self.v.x, -self.v.y, -self.v.z, self.v.w };
fn QuaternionNumber QuaternionNumber.slerp(self, QuaternionNumber q2, Real amount)
{
Quaternion result = {};
QuaternionNumber result = {};
Real[<4>] q2v = q2.v;
Real cos_half_theta = self.v.dot(q2v);
@@ -76,26 +81,48 @@ fn Quaternion Quaternion.slerp(self, Quaternion q2, Real amount)
return { .v = q1v * ratio_a + q2v * ratio_b };
}
fn Quaternion Quaternion.mul(self, Quaternion b) @operator(*)
{
return { self.i * b.l + self.l * b.i + self.j * b.k - self.k * b.j,
self.j * b.l + self.l * b.j + self.k * b.i - self.i * b.k,
self.k * b.l + self.l * b.k + self.i * b.j - self.j * b.i,
self.l * b.l - self.i * b.i - self.j * self.j - self.k * self.k };
fn QuaternionNumber QuaternionNumber.mul(self, QuaternionNumber b) @operator(*)
{
Real[<3>] q1_axis = { self.v.x, self.v.y, self.v.z };
Real[<3>] q2_axis = { b.v.x, b.v.y, b.v.z };
Real scalar = (self.v.w * b.v.w - q1_axis.dot(q2_axis));
Real[<3>] axis = self.v.w * q2_axis + b.v.w * q1_axis + q1_axis.cross(q2_axis);
return { ...axis, scalar };
}
macro into_matrix(Quaternion* q, $Type) @private
fn QuaternionNumber from_axis_angle(Real[<3>] axis, Real angle)
{
Quaternion rotation = q.normalize();
Real[<3>] normal_axis = axis.normalize();
Real half_angle = angle * 0.5;
Real sin_half = math::sin(half_angle);
return { ...(normal_axis * sin_half), math::cos(half_angle) };
}
fn Real[<3>] QuaternionNumber.rotate_vec3(self, Real[<3>] vector) @operator(*)
{
QuaternionNumber p = { ...vector, 0 };
QuaternionNumber result = self * p * self.conjugate();
return result.v.xyz;
}
macro into_matrix(QuaternionNumber* q, $Type) @private
{
QuaternionNumber rotation = q.normalize();
var x = rotation.i;
var y = rotation.j;
var z = rotation.k;
var w = rotation.l;
return ($Type) {
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w , 1 - 2*x*x - 2*y*y, 0,
0.0, 0.0, 0.0, 1.0,
1 - 2*y*y - 2*z*z, 2*x*y - 2*z*w, 2*x*z + 2*y*w, 0,
2*x*y + 2*z*w, 1 - 2*x*x - 2*z*z, 2*y*z - 2*x*w, 0,
2*x*z - 2*y*w, 2*y*z + 2*x*w, 1 - 2*x*x - 2*y*y, 0,
0.0, 0.0, 0.0, 1.0,
};
}

View File

@@ -140,7 +140,7 @@ macro ulong @int_to_long(#function) => (ulong)#function << 32 + #function;
macro uint @short_to_int(#function) => (uint)#function << 16 + #function;
macro ushort @char_to_short(#function) => (ushort)#function << 8 + #function;
macro @random_value_to_bytes(#function, char[] bytes)
macro void @random_value_to_bytes(#function, char[] bytes)
{
var $byte_size = $sizeof(#function());
usz len = bytes.len;
@@ -174,7 +174,7 @@ interface Random
}
macro init_default_random() @private
macro void init_default_random() @private
{
if (!default_random_initialized)
{

View File

@@ -1,6 +1,6 @@
module std::math::math_rt;
fn int128 __divti3(int128 a, int128 b) @extern("__divti3") @weak @nostrip
fn int128 __divti3(int128 a, int128 b) @cname("__divti3") @weak @nostrip
{
int128 sign_a = a >> 127; // -1 : 0
int128 sign_b = b >> 127; // -1 : 0
@@ -182,17 +182,17 @@ macro uint128 @__udivmodti4(uint128 a, uint128 b, bool $return_rem)
$endif
}
fn uint128 __umodti3(uint128 n, uint128 d) @extern("__umodti3") @weak @nostrip
fn uint128 __umodti3(uint128 n, uint128 d) @cname("__umodti3") @weak @nostrip
{
return @__udivmodti4(n, d, true);
}
fn uint128 __udivti3(uint128 n, uint128 d) @extern("__udivti3") @weak @nostrip
fn uint128 __udivti3(uint128 n, uint128 d) @cname("__udivti3") @weak @nostrip
{
return @__udivmodti4(n, d, false);
}
fn int128 __modti3(int128 a, int128 b) @extern("__modti3") @weak @nostrip
fn int128 __modti3(int128 a, int128 b) @cname("__modti3") @weak @nostrip
{
int128 sign = b >> 127;
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
@@ -212,7 +212,7 @@ union Int128bits @private
uint128 all;
}
fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip
fn uint128 __lshrti3(uint128 a, uint b) @cname("__lshrti3") @weak @nostrip
{
Int128bits result;
result.all = a;
@@ -230,7 +230,7 @@ fn uint128 __lshrti3(uint128 a, uint b) @extern("__lshrti3") @weak @nostrip
return result.all;
}
fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip
fn int128 __ashrti3(int128 a, uint b) @cname("__ashrti3") @weak @nostrip
{
Int128bits result;
result.all = a;
@@ -248,7 +248,7 @@ fn int128 __ashrti3(int128 a, uint b) @extern("__ashrti3") @weak @nostrip
return result.all;
}
fn int128 __ashlti3(int128 a, uint b) @extern("__ashlti3") @weak @nostrip
fn int128 __ashlti3(int128 a, uint b) @cname("__ashlti3") @weak @nostrip
{
Int128bits result;
result.all = a;
@@ -287,7 +287,7 @@ fn int128 __mulddi3(ulong a, ulong b) @private
return r.all;
}
fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip
fn int128 __multi3(int128 a, int128 b) @cname("__multi3") @weak @nostrip
{
Int128bits x = { .all = a };
Int128bits y = { .all = b };
@@ -296,14 +296,14 @@ fn int128 __multi3(int128 a, int128 b) @extern("__multi3") @weak @nostrip
return r.all;
}
fn float __floattisf(int128 a) @extern("__floattisf") @weak @nostrip => float_from_i128(float, a);
fn double __floattidf(int128 a) @extern("__floattidf") @weak @nostrip => float_from_i128(double, a);
fn float __floatuntisf(uint128 a) @extern("__floatuntisf") @weak @nostrip => float_from_u128(float, a);
fn double __floatuntidf(uint128 a) @extern("__floatuntidf") @weak @nostrip => float_from_u128(double, a);
fn uint128 __fixunsdfti(double a) @weak @extern("__fixunsdfti") @nostrip => fixuint(a);
fn uint128 __fixunssfti(float a) @weak @extern("__fixunssfti") @nostrip => fixuint(a);
fn int128 __fixdfti(double a) @weak @extern("__fixdfti") @nostrip => fixint(a);
fn int128 __fixsfti(float a) @weak @extern("__fixsfti") @nostrip => fixint(a);
fn float __floattisf(int128 a) @cname("__floattisf") @weak @nostrip => float_from_i128(float, a);
fn double __floattidf(int128 a) @cname("__floattidf") @weak @nostrip => float_from_i128(double, a);
fn float __floatuntisf(uint128 a) @cname("__floatuntisf") @weak @nostrip => float_from_u128(float, a);
fn double __floatuntidf(uint128 a) @cname("__floatuntidf") @weak @nostrip => float_from_u128(double, a);
fn uint128 __fixunsdfti(double a) @weak @cname("__fixunsdfti") @nostrip => fixuint(a);
fn uint128 __fixunssfti(float a) @weak @cname("__fixunssfti") @nostrip => fixuint(a);
fn int128 __fixdfti(double a) @weak @cname("__fixdfti") @nostrip => fixint(a);
fn int128 __fixsfti(float a) @weak @cname("__fixsfti") @nostrip => fixint(a);
macro float_from_i128($Type, a) @private

View File

@@ -8,7 +8,7 @@ macro force_eval_add(x, v)
@volatile_store(temp, x + v);
}
fn double __roundeven(double x) @extern("roundeven") @weak @nostrip
fn double __roundeven(double x) @cname("roundeven") @weak @nostrip
{
ulong u = bitcast(x, ulong);
int e = (int)((u >> 52) & 0x7ff);
@@ -42,7 +42,7 @@ fn double __roundeven(double x) @extern("roundeven") @weak @nostrip
return y;
}
fn float __roundevenf(float x) @extern("roundevenf") @weak @nostrip
fn float __roundevenf(float x) @cname("roundevenf") @weak @nostrip
{
uint u = bitcast(x, uint);
int e = (u >> 23) & 0xff;
@@ -75,7 +75,7 @@ fn float __roundevenf(float x) @extern("roundevenf") @weak @nostrip
return y;
}
fn double __powidf2(double a, int b) @extern("__powidf2") @weak @nostrip
fn double __powidf2(double a, int b) @cname("__powidf2") @weak @nostrip
{
bool recip = b < 0;
double r = 1;

View File

@@ -3,25 +3,25 @@
module std::math::vector;
import std::math;
macro double[<*>].sq_magnitude(self) => self.dot(self);
macro float[<*>].sq_magnitude(self) => self.dot(self);
macro double double[<*>].sq_magnitude(self) => self.dot(self);
macro float float[<*>].sq_magnitude(self) => self.dot(self);
macro double[<*>].distance_sq(self, double[<*>] v2) => (self - v2).sq_magnitude();
macro float[<*>].distance_sq(self, float[<*>] v2) => (self - v2).sq_magnitude();
macro double double[<*>].distance_sq(self, double[<*>] v2) => (self - v2).sq_magnitude();
macro float float[<*>].distance_sq(self, float[<*>] v2) => (self - v2).sq_magnitude();
macro float[<2>].transform(self, Matrix4f mat) => transform2(self, mat);
macro float[<2>].rotate(self, float angle) => rotate(self, angle);
macro float[<2>].angle(self, float[<2>] v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
macro float[<2>] float[<2>].transform(self, Matrix4f mat) => transform2(self, mat);
macro float[<2>] float[<2>].rotate(self, float angle) => rotate(self, angle);
macro float[<2>] float[<2>].angle(self, float[<2>] v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
macro double[<2>].transform(self, Matrix4 mat) => transform2(self, mat);
macro double[<2>].rotate(self, double angle) => rotate(self, angle);
macro double[<2>].angle(self, double[<2>] v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
macro double[<2>] double[<2>].transform(self, Matrix4 mat) => transform2(self, mat);
macro double[<2>] double[<2>].rotate(self, double angle) => rotate(self, angle);
macro double[<2>] double[<2>].angle(self, double[<2>] v2) => math::atan2(v2.y, v2.x) - math::atan2(self.y, self.x);
macro float[<*>].clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
macro double[<*>].clamp_mag(self, double min, double max) => clamp_magnitude(self, min, max);
macro float[<*>] float[<*>].clamp_mag(self, float min, float max) => clamp_magnitude(self, min, max);
macro double[<*>] double[<*>].clamp_mag(self, double min, double max) => clamp_magnitude(self, min, max);
macro float[<*>].towards(self, float[<*>] target, float max_distance) => towards(self, target, max_distance);
macro double[<*>].towards(self, double[<*>] target, double max_distance) => towards(self, target, max_distance);
macro float[<*>] float[<*>].towards(self, float[<*>] target, float max_distance) => towards(self, target, max_distance);
macro double[<*>] double[<*>].towards(self, double[<*>] target, double max_distance) => towards(self, target, max_distance);
fn float[<3>] float[<3>].cross(self, float[<3>] v2) => cross3(self, v2);
fn double[<3>] double[<3>].cross(self, double[<3>] v2) => cross3(self, v2);
@@ -41,8 +41,8 @@ fn double double[<3>].angle(self, double[<3>] v2) => angle3(self, v2);
fn float[<3>] float[<3>].refract(self, float[<3>] n, float r) => refract3(self, n, r);
fn double[<3>] double[<3>].refract(self, double[<3>] n, double r) => refract3(self, n, r);
fn float[<3>] float[<3>].rotate_quat(self, Quaternionf q) => rotate_by_quat3(self, q);
fn double[<3>] double[<3>].rotate_quat(self, Quaternion q) => rotate_by_quat3(self, q);
fn float[<3>] float[<3>].rotate_quat(self, Quaternionf q) => q * self;
fn double[<3>] double[<3>].rotate_quat(self, Quaternion q) => q * self;
fn float[<3>] float[<3>].rotate_axis(self, float[<3>] axis, float angle) => rotate_axis_angle(self, axis, angle);
fn double[<3>] double[<3>].rotate_axis(self, double[<3>] axis, double angle) => rotate_axis_angle(self, axis, angle);
@@ -144,21 +144,6 @@ macro void ortho_normalize3(v1, v2) @private
*v2 = v1n.cross(vn1);
}
macro rotate_by_quat3(v, q) @private
{
return ($typeof(v)){
v[0] * (q.i * q.i + q.l * q.l - q.j * q.j - q.k * q.k)
+ v[1] * (2 * q.i * q.j - 2 * q.l * q.k)
+ v[2] * (2 * q.i * q.k - 2 * q.l * q.j),
v[0] * (2 * q.l * q.k + 2 * q.i * q.j)
+ v[1] * (q.l * q.l - q.i * q.i + q.j * q.j - q.k * q.k)
+ v[2] * (-2 * q.l * q.i + 2 * q.j * q.k),
v[0] * (-2 * q.l * q.j + 2 * q.i * q.k)
+ v[1] * (2 * q.l * q.i + 2 * q.j * q.k)
+ v[2] * (q.l * q.l - q.i * q.i - q.j * q.j + q.k * q.k)
};
}
macro rotate_axis_angle(v, axis, angle) @private
{
axis = axis.normalize();

View File

@@ -67,7 +67,7 @@ const int SO_WANTMORE = 0x4000; // Apple: Give hint when more data re
const int SO_WANTOOBFLAG = 0x8000; // Apple: Want OOB in MSG_FLAG on receive
const int SO_SNDBUF = 0x1001; // Send buffer size
const int SO_RCVBUF = 0x1002; // Recieve buffer size
const int SO_RCVBUF = 0x1002; // Receive buffer size
const int SO_SNDLOWAT = 0x1003; // Send low-water mark
const int SO_RCVLOWAT = 0x1004; // Receive low-water mark
const int SO_SNDTIMEO = 0x1005; // Send timeout
@@ -94,4 +94,4 @@ const CShort POLLATTRIB = 0x0400; // file attributes may have changed
const CShort POLLNLINK = 0x0800; // (un)link/rename may have happened
const CShort POLLWRITE = 0x1000; // file's contents may have changed
const CInt MSG_PEEK = 0x0002;
const CInt MSG_PEEK = 0x0002;

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