Compare commits

..

103 Commits

Author SHA1 Message Date
Christoffer Lerno
83e5a0c2ab Fix CI 2025-03-30 12:51:15 +02:00
Christoffer Lerno
c058c50aef Add releasenotes to zip. 2025-03-30 12:34:04 +02:00
walther chen
46d3e3dc97 readme example typo 2025-03-29 23:08:20 +01:00
Christoffer Lerno
40ff6b1315 Release candidate 0.7.0 2025-03-29 22:36:28 +01:00
Christoffer Lerno
8453270921 Added Wumpus 2025-03-29 00:25:20 +01:00
Christoffer Lerno
6739de3a10 Re-enable everything and use the new release flow. 2025-03-28 11:18:40 +01:00
Christoffer Lerno
010a77816b Test new CI 2025-03-28 01:26:57 +01:00
Christoffer Lerno
61113a8471 Update name 2025-03-28 01:12:13 +01:00
Christoffer Lerno
7b0cc85b2c Update github token 2025-03-28 00:51:43 +01:00
Christoffer Lerno
ea5fec80b0 Try deleting old releases. 2025-03-28 00:37:39 +01:00
Christoffer Lerno
9db316ddac Restore the checkout. 2025-03-27 22:41:28 +01:00
Christoffer Lerno
cb164e2ca2 Remove what seems to be useless tags. 2025-03-27 20:35:54 +01:00
Christoffer Lerno
83ff1da80c Update latest name 2025-03-27 15:12:21 +01:00
Christoffer Lerno
d626dea52a Update timestamp name 2025-03-27 00:04:56 +01:00
Christoffer Lerno
5d026268a7 "Single module" was not enforced when creating a static library using as a project target. 2025-03-27 00:02:51 +01:00
Christoffer Lerno
2ab318a178 Generate tag name update 2025-03-26 21:30:12 +01:00
Christoffer Lerno
638d5332ff Generate tag name 2025-03-26 18:31:08 +01:00
Christoffer Lerno
5c46b0c2a0 Add @each_row for the csv module. 2025-03-26 18:19:19 +01:00
Christoffer Lerno
4538a1f50d - Correctly errors when a generic module contains a self-generic type. 2025-03-26 18:04:00 +01:00
Christoffer Lerno
e0f1919849 Rename latest build 2025-03-26 14:15:46 +01:00
Christoffer Lerno
df175dd48c Added some direction for stdlib contributions. 2025-03-26 11:59:28 +01:00
Christoffer Lerno
6e340f22af Add star history 2025-03-25 23:53:36 +01:00
Christoffer Lerno
ff2809a3ac Fix of regression in @assert_leak. 2025-03-25 22:11:47 +01:00
Maxime Beaudoin
a8554b4233 String.c3 function parameters ambiguous (#2061)
* Some tweaks. Fixes regression in `format`

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-25 14:25:23 +01:00
Christoffer Lerno
fa707db078 Do not implicitly convert enums to ordinal in formatting functions. 2025-03-25 12:27:51 +01:00
Christoffer Lerno
439349ceb8 Move dynamic dispatch lowering. 2025-03-25 00:20:01 +01:00
Christoffer Lerno
d760378b02 - Added @format attribute for compile time printf validation #2057.
- Bug when printing a boolean value as an integer using printf.
2025-03-24 13:32:44 +01:00
Christoffer Lerno
50d7919fec - Compile test and benchmark functions when invoking --lsp #2058. 2025-03-23 23:30:50 +01:00
Christoffer Lerno
f53f8bf423 Fix help message for asm-out 2025-03-23 23:19:31 +01:00
Christoffer Lerno
82f1b543ed &self not runtime null-checked in macro #1827. Regression in temp allocators. 2025-03-23 22:50:09 +01:00
walther chen
b48588ca8f improve error message for uninitialized temp allocator pool on new
threads
2025-03-23 22:15:42 +01:00
Christoffer Lerno
a03d821602 - Use @pool_init() to set up a temp pool on a thread. Only the main thread has implicit temp pool setup.
- `tmem` is now a variable.
2025-03-21 17:08:58 +01:00
[ Taha. Dostifam‍ ]
fab00f21a6 Added a progress bar to vendor-fetch in compiler section (#2055)
* added a progress bar to vendor-fetch of compiler section

* Handle ansi settings.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-20 21:53:02 +01:00
Christoffer Lerno
49033320e2 Stable path for unpacked libraries. 2025-03-20 20:51:36 +01:00
Christoffer Lerno
207bcfea02 os::exit and os::fastexit added. 2025-03-20 13:36:59 +01:00
Christoffer Lerno
d2c44717f1 Exclude 21 due to packaging errors of LLVM. 2025-03-20 10:09:48 +01:00
Christoffer Lerno
0beb30c979 Updated error message #2037 2025-03-19 23:25:56 +01:00
Christoffer Lerno
de74e97ab1 Fixes to temp allocator. 2025-03-18 19:58:16 +01:00
Christoffer Lerno
7e100472e7 - AnyList now also defaults to the temp allocator.
- `os::getcwd` and `os::get_home_dir` requires an explicit allocator.
- `file::load_new` and `file::load_path_new` removed.
2025-03-18 18:34:52 +01:00
Christoffer Lerno
cfc87a9d66 Update example. 2025-03-18 15:41:37 +01:00
Christoffer Lerno
84753bde6d - Allow inferred type on body parameters. E.g. @stack_mem(1024; alloc) { ... }; 2025-03-18 15:40:26 +01:00
Christoffer Lerno
72608ce01d - Temp allocator now supports more than 2 in-flight stacks.
- Printing stacktrace uses its own temp allocator.
- `@pool` no longer takes an argument.
- `Allocator` interface removes `mark` and `reset`.
- DynamicArenaAllocator has changed init function.
- Added `BackedArenaAllocator` which is allocated to a fixed size, then allocates on the backing allocator and supports mark/reset.
2025-03-18 15:16:22 +01:00
Christoffer Lerno
82cc49b388 - !!foo now works same as as ! ! foo.
- Incorrectly allowed getting pointer to a macro #2049.
2025-03-16 23:57:30 +01:00
Christoffer Lerno
425676a98d Bug due to missing cast when doing $i[$x] = $z.
Added `math::iota`.
2025-03-16 10:58:13 +01:00
Christoffer Lerno
5c77c9a754 - Change distinct -> typedef.
- Order of attribute declaration is changed for `alias`.
- Added `LANGUAGE_DEV_VERSION` env constant.
- Rename `anyfault` -> `fault`.
- Changed `fault` -> `faultdef`.
- Added `attrdef` instead of `alias` for attribute aliases.
2025-03-15 20:10:47 +01:00
Christoffer Lerno
fc5615a7a1 Restore def so that libraries work. 2025-03-13 11:51:21 +01:00
Christoffer Lerno
9707a9694f Temporarily turn off raylib 2025-03-13 11:46:38 +01:00
Christoffer Lerno
8b49e6c14d Rename def to alias. 2025-03-13 11:22:27 +01:00
Christoffer Lerno
ae76839347 Fix @return? parsing. 2025-03-13 11:03:19 +01:00
Christoffer Lerno
b8ae2b06d6 Rename @return! to @return? 2025-03-12 21:40:30 +01:00
Christoffer Lerno
c9dbd86d82 Crash resolving a method on Foo[2] when Foo is distinct #2042. 2025-03-12 15:45:43 +01:00
Christoffer Lerno
d5b211a786 Fault nameof prefixes the first last module path, for instance std::io::EOF is rendered as io::EOF. 2025-03-11 22:46:02 +01:00
Christoffer Lerno
f5d02cd0d2 Crash when trying to convert a struct slice to a vector #2039. 2025-03-11 21:18:19 +01:00
Christoffer Lerno
8c23c5028d Make @public import recursive. #2018
`import` can now both be @public and @norecurse.
2025-03-10 15:33:29 +01:00
Christoffer Lerno
461bd43a22 Fix Android 2 2025-03-10 11:04:43 +01:00
Christoffer Lerno
fbc8168bb9 Improve error message on foo ?? io::EOF with missing '?' #2036. Fix build on Android 2025-03-10 11:02:43 +01:00
Christoffer Lerno
ff75f2c21f Update naming of enum const. 2025-03-10 00:15:50 +01:00
Christoffer Lerno
25bccf4883 New faults and syntax (#2034)
- Remove `[?]` syntax.
- Change `int!` to `int?` syntax.
- New `fault` declarations.
- Enum associated values can reference the calling enum.
2025-03-10 00:11:35 +01:00
Boris Barbulovski
fefce25081 C3 Android logcat interface. (#2030)
* C3 Android logcat interface.
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-09 15:52:24 +01:00
Boris Barbulovski
f21cc02320 Initial Android support. (#2024)
* Initial Android support.

* Add Android x86_64

* - Typo api_verion
- Typo in DEFAULT_TARGETS
- Removed unnecessary snprintf

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-08 23:38:52 +01:00
BWindey
1461414128 [TEST] Add unit tests for libc (#1974)
Some tests for libc + fixes
---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-08 22:25:21 +01:00
m0tholith
dc8b35e62f Added is_initalized function to list, linkedlist, and anylist types (#2027)
* Added `is_initalized` function to list, linkedlist, and anylist types
2025-03-08 21:49:17 +01:00
Christoffer Lerno
1dca90b89d Fix tests. 2025-03-07 23:54:01 +01:00
Christoffer Lerno
a088a5057a Remove if (catch foo) { case ... } syntax. 2025-03-07 22:48:28 +01:00
Christoffer Lerno
facaa75083 Remove use of if-catch-case 2025-03-07 13:57:05 +01:00
Christoffer Lerno
cce097fdef Disable mingw compilation. 2025-03-07 12:36:19 +01:00
Christoffer Lerno
5a6884b708 Set linker for mingw64 2025-03-07 12:34:00 +01:00
Christoffer Lerno
5898cad98d Fix typo giving the wrong mem on error, closes #2023. 2025-03-07 12:14:01 +01:00
Christoffer Lerno
5482107ca8 Some formatting and updated test. Updated releasenotes. 2025-03-07 00:14:41 +01:00
Christoffer Lerno
c790ecbca5 Update msys in CI 2025-03-07 00:04:03 +01:00
Stephen Molloy
1fba9a7993 String.to_float("+") fails (#2021)
* When stripping +/- from the start of a string in String.to_real: only do this if the string length is greater than 1.
* Added a test for the String.to_float("+") bug
2025-03-07 00:02:13 +01:00
Christoffer Lerno
ca87ff066b Fix hello world. 2025-03-05 19:28:12 +01:00
Christoffer Lerno
c0b80eccad Change @return! syntax to require ":" after faults. Update all contracts to consistently use ":" before the description. 2025-03-05 17:11:45 +01:00
Christoffer Lerno
cf0405930e Fix case where occasionally atomic_load would miscompile. 2025-03-05 01:58:07 +01:00
Christoffer Lerno
28b1e4d182 Fix issue when doing boolean atomic operations. 2025-03-04 16:58:27 +01:00
Christoffer Lerno
9c65098a91 Fix missing "$switch" update. 2025-03-04 16:39:20 +01:00
Christoffer Lerno
2439405e70 - $foreach "()" replaced by trailing ":" $foreach ($x, $y : $foo) -> $foreach $x, $y : $foo:
- `$for` "()" replaced by trailing ":" `$for (var $x = 0; $x < FOO; $x++)` -> `$for var $x = 0; $x < FOO; $x++:`
- `$switch` "()" replaced by trailing ":" `$switch ($Type)` -> `$switch $Type:`
- Empty `$switch` requires trailing ":" `$switch` -> `$switch:`
2025-03-04 16:13:47 +01:00
Jonas
46b52ec9ce Fix Atomic.max (#2011)
* Fix atomic max and comments in std::atomic
* Updates to `Atomic` to handle distinct types and booleans.

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2025-03-04 15:39:03 +01:00
Nikita Pivkin
c6c78e7709 fix broken link to macOS installation section
Signed-off-by: Nikita Pivkin <nikita.pivkin@smartforce.io>
2025-03-04 14:03:14 +01:00
Zack Puhl
177df6321a add test file for wide string constants
add full UTF-8 to wide-str language capabilities
move wstr language constant expressions to builtins and adjust test cases
2025-03-04 13:46:59 +01:00
Christoffer Lerno
d2a461d270 Updated release notes. 2025-03-04 10:31:47 +01:00
Nikita Pivkin
7a848416f7 docs: update 'Building via Docker' section
Signed-off-by: Nikita Pivkin <nikita.pivkin@smartforce.io>
2025-03-04 10:30:32 +01:00
Erik Bäckman
83bc24f58c Fix libc::stat on FreeBSD (x86_64) 2025-03-04 10:20:13 +01:00
Christoffer Lerno
0ef99c23a8 Allow swizzling assign, eg. abc.xz += { 5, 10 }; 2025-03-04 02:18:24 +01:00
Christoffer Lerno
0a905d8458 Change all hash functions to have a common hash function. 2025-03-03 20:07:02 +01:00
Christoffer Lerno
261bfb97c6 Rename length_sq to sq_magnitude 2025-03-03 18:47:55 +01:00
Christoffer Lerno
cc94199131 Remove Vec2 and other aliases from std::math. 2025-03-03 18:13:39 +01:00
Christoffer Lerno
0925010c07 Removal of any-switches 2025-03-03 15:02:25 +01:00
Christoffer Lerno
13f824e349 Add ONHEAP for List and HashMap 2025-03-03 12:20:18 +01:00
Christoffer Lerno
3d6f28919c Aliases were incorrectly considered compile time constants. 2025-03-03 11:35:18 +01:00
Christoffer Lerno
910fc6e364 Post and pre-decrement operators switched places for vector elements. #2010. 2025-03-03 01:21:01 +01:00
Christoffer Lerno
c40198b016 - new_* functions in general moved to version without new_ prefix.
- `string::new_from_*` changed to `string::from_*`.
- `String.to_utf16_copy` and related changed to `String.to_utf16`.
- `String.to_utf16_tcopy` and related changed to `String.to_temp_utf16`
- `mem::temp_new` changed to `mem::tnew`.
- `mem::temp_alloc` and related changed to `mem::talloc`.
- `mem::temp_new_array` changed to `mem::temp_array`.
2025-03-03 00:32:20 +01:00
Christoffer Lerno
b35aafd3d5 Update for NoSanitize on globals. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
222bfb158b Remove deprecated functions. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
61c67c8f23 Fix address sanitizer to work on MachO targets (e.g. MacOS). 2025-03-03 00:32:20 +01:00
Christoffer Lerno
2b90500c22 Remove container sanitizer unless using the LIBC allocator. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
74a6e9f0c0 Add struct swizzle test. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
fbac2d6df3 Formatting updates. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
8f0de40b3d Update nix. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
8bb99c6f81 Fixes to examples and MSVC compilation. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
cc5f9c6aab Update CI for 0.7 2025-03-03 00:32:20 +01:00
Christoffer Lerno
fb6b048bd0 Remove operator(@construct). Fix sample. 2025-03-03 00:32:20 +01:00
Christoffer Lerno
2a895ec7be First 0.7 update, removing all deprecated features. 2025-03-03 00:32:20 +01:00
2098 changed files with 11544 additions and 123206 deletions

View File

@@ -77,15 +77,15 @@ jobs:
- name: Vendor-fetch
run: |
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
- name: Try raylib5
run: |
cd resources
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib5
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_tetris.c3
..\build\${{ matrix.build_type }}\c3c.exe vendor-fetch raylib55_v7
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_arkanoid.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_snake.c3
..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib55_v7 --print-linking examples\raylib\raylib_tetris.c3
- name: Compile run unit tests
run: |
@@ -95,7 +95,7 @@ jobs:
- name: run compiler tests
run: |
cd test
..\build\${{ matrix.build_type }}\c3c.exe compile-run -O1 src/test_suite_runner.c3 --stdlib ..\lib7 --enable-new-generics -- ..\build\${{ matrix.build_type }}\c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
..\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: |
@@ -112,7 +112,7 @@ jobs:
build-msys2-mingw:
runs-on: windows-latest
# if: ${{ false }}
if: ${{ false }}
strategy:
# Don't abort runners if a single one fails
fail-fast: false
@@ -132,11 +132,11 @@ jobs:
install: git binutils mingw-w64-x86_64-clang mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-toolchain mingw-w64-x86_64-python
- shell: msys2 {0}
run: |
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-19.1.7-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-19.1.7-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-20.1.0-1-any.pkg.tar.zst
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-20.1.0-1-any.pkg.tar.zst
- name: CMake
run: |
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_LINKER=lld
cmake --build build
- name: Compile and run some examples
@@ -158,7 +158,7 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib5
./build/c3c vendor-fetch raylib55_v7
- name: Build testproject lib
run: |
@@ -168,8 +168,8 @@ jobs:
- name: run compiler tests
run: |
cd test
../build/c3c.exe compile --target windows-x64 -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
./test_suite_runner.exe ../build/c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
../build/c3c.exe compile --target windows-x64 -O1 src/test_suite_runner.c3
./test_suite_runner.exe ../build/c3c.exe test_suite/ --no-terminal
build-msys2-clang:
runs-on: windows-latest
@@ -220,7 +220,7 @@ jobs:
- name: run compiler tests
run: |
cd test
../build/c3c.exe compile-run -O1 --stdlib ../lib7 src/test_suite_runner.c3 --enable-new-generics -- ../build/c3c.exe test_suite7/ --stdlib ../lib7 --no-terminal
../build/c3c.exe compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c.exe test_suite/ --no-terminal
build-linux:
runs-on: ubuntu-22.04
@@ -229,7 +229,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20, 21]
llvm_version: [17, 18, 19, 20]
steps:
- uses: actions/checkout@v4
@@ -381,7 +381,7 @@ jobs:
- name: run compiler tests
run: |
cd test
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_LINUX
@@ -391,6 +391,7 @@ jobs:
cp msvc_build_libraries.py c3
cp build/c3c c3
cp README.md c3
cp releasenotes.md c3
tar czf c3-linux-${{matrix.build_type}}.tar.gz c3
- name: upload artifacts
@@ -407,7 +408,7 @@ jobs:
fail-fast: false
matrix:
build_type: [Release, Debug]
llvm_version: [17, 18, 19, 20, 21]
llvm_version: [17, 18, 19, 20]
steps:
- uses: actions/checkout@v4
- name: Install common deps
@@ -503,7 +504,7 @@ jobs:
- name: run compiler tests
run: |
cd test
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_UBUNTU20
@@ -511,6 +512,7 @@ jobs:
mkdir c3
cp -r lib c3
cp README.md c3
cp releasenotes.md c3
cp msvc_build_libraries.py c3
cp build/c3c c3
tar czf c3-ubuntu-20-${{matrix.build_type}}.tar.gz c3
@@ -608,7 +610,7 @@ jobs:
- name: run compiler tests
run: |
cd test
../build/c3c compile-run -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics -- ../build/c3c test_suite7/ --stdlib ../lib7
../build/c3c compile-run -O1 src/test_suite_runner.c3 -- ../build/c3c test_suite/
build-mac:
runs-on: macos-latest
@@ -640,7 +642,7 @@ jobs:
- name: Vendor-fetch
run: |
./build/c3c vendor-fetch raylib5
./build/c3c vendor-fetch raylib55_v7
- name: Compile and run some examples
run: |
@@ -688,13 +690,13 @@ jobs:
- name: run compiler tests
run: |
cd test
../build/c3c compile -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
./test_suite_runner ../build/c3c test_suite7/ --stdlib ../lib7
../build/c3c compile -O1 src/test_suite_runner.c3
./test_suite_runner ../build/c3c test_suite/
- name: run build test suite runner
run: |
cd test
../build/c3c compile -O1 src/test_suite_runner.c3 --stdlib ../lib7 --enable-new-generics
../build/c3c compile -O1 src/test_suite_runner.c3
- name: bundle_output
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION_MAC
@@ -703,6 +705,7 @@ jobs:
cp -r lib macos
cp msvc_build_libraries.py macos
cp README.md macos
cp releasenotes.md macos
cp build/c3c macos
zip -r c3-macos-${{matrix.build_type}}.zip macos
@@ -748,31 +751,11 @@ jobs:
runs-on: ubuntu-22.04
needs: [build-msvc, build-linux, build-mac, build-linux-ubuntu20]
if: github.ref == 'refs/heads/master'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@v4
- name: delete tag
continue-on-error: true
uses: actions/github-script@v6
with:
script: |
github.rest.git.deleteRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'tags/latest',
sha: context.sha
})
- name: create tag
uses: actions/github-script@v6
with:
script: |
github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: 'refs/tags/latest',
sha: context.sha
})
- uses: actions/download-artifact@v4
- run: cp -r lib c3-windows-Release
- run: cp -r lib c3-windows-Debug
@@ -780,6 +763,8 @@ jobs:
- run: cp msvc_build_libraries.py c3-windows-Debug
- run: cp README.md c3-windows-Release
- run: cp README.md c3-windows-Debug
- run: cp releasenotes.md c3-windows-Release
- run: cp releasenotes.md c3-windows-Debug
- run: zip -r c3-windows.zip c3-windows-Release
- run: zip -r c3-windows-debug.zip c3-windows-Debug
- run: mv c3-linux-Release/c3-linux-Release.tar.gz c3-linux-Release/c3-linux.tar.gz
@@ -788,14 +773,16 @@ jobs:
- run: mv c3-ubuntu-20-Debug/c3-ubuntu-20-Debug.tar.gz c3-ubuntu-20-Debug/c3-ubuntu-20-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-0.7.0-prerelease --cleanup-tag -y || true
- run: echo "RELEASE_NAME=latest-0.7.0-rc-$(date +'%Y%m%d-%H%M')" >> $GITHUB_ENV
- id: create_release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: latest
release_name: latest
tag_name: latest-0.7.0-prerelease
name: ${{ env.RELEASE_NAME }}
draft: false
prerelease: true
files: |

1
.gitignore vendored
View File

@@ -85,3 +85,4 @@ result
# tests
/test/tmp/*
/test/testrun
/test/test_suite_runner

View File

@@ -74,7 +74,7 @@ No space inside parenthesis:
### Tab vs spaces
Recommendation: tabs, 4 spaces wide. No CRLF in the source.
Use tabs for indentation, no CRLF in the source.
### If, braces and new lines
@@ -147,4 +147,113 @@ Iterating over the elements are done using `VECEACH`.
### Scratch buffer for strings.
There is a scratch buffer for strings in the `global_context` prefer using that
one with related functions when working on temporary strings.
one with related functions when working on temporary strings.
# C3 Standard library style guide.
When contributing to the standard librairy please to your best to follow the following style requirements
as to ensure a consistent style in the stdlib and also make accepting PRs more quickly.
### Braces are placed on the next line
**NO:**
```c
fn void foo(String bar) {
@pool() {
...
};
}
```
**YES:**
```c
fn void foo(String bar)
{
@pool()
{
...
};
}
```
### Indentation with tabs
Use tab for indentation, not spaces, no CRLF in the sources
### Type names
Use `PascalCase` not `Ada_Case` for type names.
**YES:**
```c
enum MyEnum
{
ABC,
DEF
}
```
**NO:**
```c
enum My_Enum
{
ABC,
DEF
}
```
### Type names when binding to OS libraries
When doing bindings (for instance, adding declarations referring to Win32 APIs),
try to retain the original name when possible. If it isn't possible use (consistently)
one of two options:
1. Prefix: `HANDLE` -> `Win32_HANDLE`
2. Change the first letter to upper case: `mode_t` -> `Mode_t`
### Variables, function, methods and globals
Use `snake_case`, not `camelCase`.
**YES:**
```c
int some_global = 1;
fn void open_file(String special_file)
{
...
}
```
**NO:**
```c
int someGlobal = 1;
fn void openFile(String specialFile)
{
...
}
```
### Variables, function, methods and globals when binding to OS libraries
When doing bindings (for instance, adding declarations referring to Win32 APIs),
try to retain the original name when possible. If it isn't possible use (consistently)
one of two options:
1. Prefix: `win32_GetWindowLongPtrW`. However, this is usually only recommended if it is builtin.
2. Change first character to lower case: `GetWindowLongPtrW` -> `getWindowLongPtrW`
### Use `self` as the first method argument
Unless there is a strong reason not to, use `self` for the first parameter in a method.
### The allocator argument
Prefer always calling the allocator parameter `allocator`, and make it the first regular
argument.
## Add tests to your changes
If you add or fix things, then there should always be tests in `test/unit/stdlib` to verify
the functionality.

View File

@@ -11,7 +11,7 @@ Precompiled binaries for the following operating systems are available:
- Windows x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip), [install instructions](#installing-on-windows-with-precompiled-binaries).
- Debian x64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz), [install instructions](#installing-on-debian-with-precompiled-binaries).
- Ubuntu x86 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-ubuntu-20.tar.gz), [install instructions](#installing-on-ubuntu-with-precompiled-binaries).
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-mac-with-precompiled-binaries).
- MacOS Arm64 [download](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip), [install instructions](#installing-on-macos-with-precompiled-binaries).
The manual for C3 can be found at [www.c3-lang.org](http://www.c3-lang.org).
@@ -36,7 +36,7 @@ whole new language.
The following code shows [generic modules](https://c3-lang.org/references/docs/generics/) (more examples can be found at https://c3-lang.org/references/docs/examples/).
```cpp
module stack (<Type>);
module stack {Type};
// Above: the parameterized type is applied to the entire module.
struct Stack
@@ -80,13 +80,13 @@ import stack;
// Define our new types, the first will implicitly create
// a complete copy of the entire Stack module with "Type" set to "int"
def IntStack = Stack(<int>);
alias IntStack = Stack {int};
// The second creates another copy with "Type" set to "double"
def DoubleStack = Stack(<double>);
alias DoubleStack = Stack {double};
// If we had added "define IntStack2 = Stack(<int>)"
// If we had added "alias IntStack2 = Stack {int}"
// no additional copy would have been made (since we already
// have an parameterization of Stack(<int>)) so it would
// have an parameterization of Stack {int} so it would
// be same as declaring IntStack2 an alias of IntStack
// Importing an external C function is straightforward
@@ -249,20 +249,13 @@ makepkg -si
#### Building via Docker
You can build `c3c` using either an Ubuntu 18.04 or 20.04 container:
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.
```
./build-with-docker.sh 18
UBUNTU_VERSION=20.04 ./build-with-docker.sh
```
Replace `18` with `20` to build through Ubuntu 20.04.
For a release build specify:
```
./build-with-docker.sh 20 Release
```
A `c3c` executable will be found under `bin/`.
See the `build-with-docker.sh` script for more information on other configurable environment variables.
#### Installing on OS X using Homebrew
@@ -410,3 +403,7 @@ Editor plugins can be found at https://github.com/c3lang/editor-plugins.
A huge **THANK YOU** goes out to all contributors and sponsors.
A special thank you to sponsors [Caleb-o](https://github.com/Caleb-o) and [devdad](https://github.com/devdad) for going the extra mile.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=c3lang/c3c&type=Date)](https://www.star-history.com/#c3lang/c3c&Date)

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2023 Eduardo José Gómez Hernández. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::atomic::types(<Type>);
module std::atomic::types{Type};
struct Atomic
{
@@ -11,7 +11,7 @@ struct Atomic
<*
Loads data atomically, by default this uses SEQ_CONSISTENT ordering.
@param ordering "The ordering, cannot be release or acquire-release."
@param ordering : "The ordering, cannot be release or acquire-release."
@require ordering != RELEASE && ordering != ACQUIRE_RELEASE : "Release and acquire-release are not valid for load"
*>
macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
@@ -31,7 +31,7 @@ macro Type Atomic.load(&self, AtomicOrdering ordering = SEQ_CONSISTENT)
<*
Stores data atomically, by default this uses SEQ_CONSISTENT ordering.
@param ordering "The ordering, cannot be acquire or acquire-release."
@param ordering : "The ordering, cannot be acquire or acquire-release."
@require ordering != ACQUIRE && ordering != ACQUIRE_RELEASE : "Acquire and acquire-release are not valid for store"
*>
macro void Atomic.store(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
@@ -76,7 +76,7 @@ macro Type Atomic.div(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTEN
macro Type Atomic.max(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_div, data, value, ordering);
return @atomic_exec(atomic::fetch_max, data, value, ordering);
}
macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT)
@@ -85,36 +85,49 @@ macro Type Atomic.min(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTEN
return @atomic_exec(atomic::fetch_min, data, value, ordering);
}
macro Type Atomic.or(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
macro Type Atomic.or(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_or, data, value, ordering);
}
fn Type Atomic.xor(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
fn Type Atomic.xor(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_xor, data, value, ordering);
}
macro Type Atomic.and(&self, uint value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
macro Type Atomic.and(&self, Type value, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_and, data, value, ordering);
}
macro Type Atomic.shift_right(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
macro Type Atomic.shr(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_shift_right, data, amount, ordering);
}
macro Type Atomic.shift_left(&self, uint amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(!types::is_float(Type))
macro Type Atomic.shl(&self, Type amount, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) != FLOAT)
{
Type* data = &self.data;
return @atomic_exec(atomic::fetch_shift_left, data, amount, ordering);
}
macro Type Atomic.set(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
{
Type* data = &self.data;
return @atomic_exec_no_arg(atomic::flag_set, data, ordering);
}
macro Type Atomic.clear(&self, AtomicOrdering ordering = SEQ_CONSISTENT) @if(types::flat_kind(Type) == BOOL)
{
Type* data = &self.data;
return @atomic_exec_no_arg(atomic::flag_clear, data, ordering);
}
macro @atomic_exec(#func, data, value, ordering) @local
{
switch(ordering)
@@ -128,18 +141,58 @@ macro @atomic_exec(#func, data, value, ordering) @local
}
}
macro @atomic_exec_no_arg(#func, data, ordering) @local
{
switch(ordering)
{
case RELAXED: return #func(data, RELAXED);
case ACQUIRE: return #func(data, ACQUIRE);
case RELEASE: return #func(data, RELEASE);
case ACQUIRE_RELEASE: return #func(data, ACQUIRE_RELEASE);
case SEQ_CONSISTENT: return #func(data, SEQ_CONSISTENT);
default: unreachable("Ordering may not be non-atomic or unordered.");
}
}
module std::atomic;
import std::math;
macro bool @is_native_atomic_value(#value) @private
{
return is_native_atomic_type($typeof(#value));
}
macro bool is_native_atomic_type($Type)
{
$if $Type.sizeof > void*.sizeof:
return false;
$else
$switch $Type.kindof:
$case SIGNED_INT:
$case UNSIGNED_INT:
$case POINTER:
$case FLOAT:
$case BOOL:
return true;
$case DISTINCT:
return is_native_atomic_type($typefrom($Type.inner));
$default:
return false;
$endswitch
$endif
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be added to ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require !$alignment || math::is_power_of_2($alignment) : "Alignment must be a power of two."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr + y) : "+ must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -150,14 +203,16 @@ macro fetch_add(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be subtracted from ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require !$alignment || math::is_power_of_2($alignment) : "Alignment must be a power of two."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr - y) : "- must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -168,13 +223,15 @@ macro fetch_sub(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be multiplied with ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "* must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
@@ -193,25 +250,28 @@ macro fetch_mul(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$StorageType storage_old_value;
$StorageType storage_new_value;
do {
do
{
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = old_value * y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
}
while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to divide ptr by."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr * y) : "/ must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
@@ -230,154 +290,80 @@ macro fetch_div(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$StorageType storage_old_value;
$StorageType storage_new_value;
do {
do
{
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = old_value / y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
}
while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to perform a bitwise or with."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require !$alignment || math::is_power_of_2($alignment) : "Alignment must be a power of two."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr | y) : "| must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_or(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if types::is_int($typeof(*ptr)):
return $$atomic_fetch_or(ptr, y, $volatile, $ordering.ordinal, $alignment);
$endif
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value | storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
return $$atomic_fetch_or(ptr, y, $volatile, $ordering.ordinal, $alignment);
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to perform a bitwise xor with."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require !$alignment || math::is_power_of_2($alignment) : "Alignment must be a power of two."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr ^ y) : "^ must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_xor(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if types::is_int($typeof(*ptr)):
return $$atomic_fetch_xor(ptr, y, $volatile, $ordering.ordinal, $alignment);
$endif
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value ^ storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
return $$atomic_fetch_xor(ptr, y, $volatile, $ordering.ordinal, $alignment);
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to perform a bitwise and with."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require !$alignment || math::is_power_of_2($alignment) "Alignment must be a power of two."
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require !$alignment || math::is_power_of_2($alignment) : "Alignment must be a power of two."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr ^ y) : "& must be defined between the values."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_and(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
$if types::is_int($typeof(*ptr)):
return $$atomic_fetch_and(ptr, y, $volatile, $ordering.ordinal, $alignment);
$endif
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
var $StorageType = $typefrom(types::lower_to_atomic_compatible_type($typeof(*ptr)));
$StorageType* storage_ptr = ($StorageType*)ptr;
$typeof(*ptr) old_value;
$typeof(*ptr) new_value;
$StorageType storage_old_value;
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value & storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
return $$atomic_fetch_and(ptr, y, $volatile, $ordering.ordinal, $alignment);
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to shift ptr by."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
@require types::is_int($typeof(y)) : "The value for shift right must be an integer"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
@@ -397,25 +383,29 @@ macro fetch_shift_right(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
do
{
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value >> storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
}
while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to shift ptr by."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require types::is_int($typeof(y)) "The value for or must be an int"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::is_int($typeof(*ptr)) : "Only integer pointers may be used."
@require types::is_int($typeof(y)) : "The value for shift left must be an integer"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
@@ -435,64 +425,83 @@ macro fetch_shift_left(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT)
$StorageType storage_new_value;
$StorageType storage_y = ($StorageType)y;
do {
do
{
storage_old_value = $$atomic_load(storage_ptr, false, $load_ordering.ordinal);
old_value = bitcast(storage_old_value, $typeof(*ptr));
new_value = storage_old_value << storage_y;
storage_new_value = bitcast(new_value, $StorageType);
} while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
}
while (mem::compare_exchange(storage_ptr, storage_old_value, storage_new_value, $ordering, $load_ordering) != storage_old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro flag_set(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
$typeof(*ptr) old_value;
$typeof(*ptr) new_value = true;
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
do
{
old_value = $$atomic_load(ptr, false, $load_ordering.ordinal);
}
while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value);
do {
old_value = $$atomic_load(ptr, false, $ordering.ordinal);
} while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) "Only integer pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require types::flat_kind($typeof(*ptr)) == BOOL : "Only bool pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro flag_clear(ptr, AtomicOrdering $ordering = SEQ_CONSISTENT)
{
$typeof(*ptr) old_value;
$typeof(*ptr) new_value = false;
do {
old_value = $$atomic_load(ptr, false, $ordering.ordinal);
} while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value);
var $load_ordering = $ordering;
$if $ordering == RELEASE || $ordering == ACQUIRE_RELEASE:
$load_ordering = AtomicOrdering.SEQ_CONSISTENT;
$endif
do
{
old_value = $$atomic_load(ptr, false, $load_ordering.ordinal);
}
while (mem::compare_exchange(ptr, old_value, new_value, $ordering, $load_ordering) != old_value);
return old_value;
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be compared to ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr > y) : "Only values that are comparable with > may be used"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{
@@ -503,13 +512,15 @@ macro fetch_max(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatil
}
<*
@param [&in] ptr "the variable or dereferenced pointer to the data."
@param [in] y "the value to be added to ptr."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param [&in] ptr : "the variable or dereferenced pointer to the data."
@param [in] y : "the value to be compared to ptr."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@return "returns the old value of ptr"
@require types::is_int($typeof(*ptr)) || types::is_float($typeof(*ptr)) "Only integer/float pointers may be used."
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $defined(*ptr) : "Expected a pointer"
@require @is_native_atomic_value(*ptr) : "Only types that are native atomic may be used."
@require $defined(*ptr > y) : "Only values that are comparable with > may be used"
@require $ordering != AtomicOrdering.NOT_ATOMIC && $ordering != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
*>
macro fetch_min(ptr, y, AtomicOrdering $ordering = SEQ_CONSISTENT, bool $volatile = false, usz $alignment = 0)
{

View File

@@ -3,8 +3,9 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::atomic;
macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $success, failure, $alignment) {
switch(failure)
macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $success, failure, $alignment)
{
switch (failure)
{
case AtomicOrdering.RELAXED.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.RELAXED.ordinal, $alignment);
case AtomicOrdering.ACQUIRE.ordinal: return $$compare_exchange(ptr, expected, desired, false, false, $success, AtomicOrdering.ACQUIRE.ordinal, $alignment);
@@ -16,7 +17,7 @@ macro @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, $succe
macro @__atomic_compare_exchange_ordering_success(ptr, expected, desired, success, failure, $alignment)
{
switch(success)
switch (success)
{
case AtomicOrdering.RELAXED.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.RELAXED.ordinal, failure, $alignment);
case AtomicOrdering.ACQUIRE.ordinal: return @__atomic_compare_exchange_ordering_failure(ptr, expected, desired, AtomicOrdering.ACQUIRE.ordinal, failure, $alignment);
@@ -28,7 +29,7 @@ macro @__atomic_compare_exchange_ordering_success(ptr, expected, desired, succes
return 0;
}
fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, CInt success, CInt failure) @extern("__atomic_compare_exchange") @export
fn CInt __atomic_compare_exchange(CInt size, any ptr, any expected, any desired, CInt success, CInt failure) @weak @export("__atomic_compare_exchange")
{
switch (size)
{

View File

@@ -1,94 +1,94 @@
module std::bits;
<*
@require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
@require types::is_intlike($typeof(i)) : `The input must be an integer or integer vector`
*>
macro reverse(i) => $$bitreverse(i);
<*
@require types::is_intlike($typeof(i)) `The input must be an integer or integer vector`
@require types::is_intlike($typeof(i)) : `The input must be an integer or integer vector`
*>
macro bswap(i) @builtin => $$bswap(i);
macro uint[<?>].popcount(self) => $$popcount(self);
macro uint[<?>].ctz(self) => $$ctz(self);
macro uint[<?>].clz(self) => $$clz(self);
macro uint[<?>] uint[<?>].fshl(hi, uint[<?>] lo, uint[<?>] shift) => $$fshl(hi, lo, shift);
macro uint[<?>] uint[<?>].fshr(hi, uint[<?>] lo, uint[<?>] shift) => $$fshr(hi, lo, shift);
macro uint[<?>] uint[<?>].rotl(self, uint[<?>] shift) => $$fshl(self, self, shift);
macro uint[<?>] uint[<?>].rotr(self, uint[<?>] shift) => $$fshr(self, self, shift);
macro uint[<*>].popcount(self) => $$popcount(self);
macro uint[<*>].ctz(self) => $$ctz(self);
macro uint[<*>].clz(self) => $$clz(self);
macro uint[<*>] uint[<*>].fshl(hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
macro uint[<*>] uint[<*>].fshr(hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
macro uint[<*>] uint[<*>].rotl(self, uint[<*>] shift) => $$fshl(self, self, shift);
macro uint[<*>] uint[<*>].rotr(self, uint[<*>] shift) => $$fshr(self, self, shift);
macro int[<?>].popcount(self) => $$popcount(self);
macro int[<?>].ctz(self) => $$ctz(self);
macro int[<?>].clz(self) => $$clz(self);
macro int[<?>] int[<?>].fshl(hi, int[<?>] lo, int[<?>] shift) => $$fshl(hi, lo, shift);
macro int[<?>] int[<?>].fshr(hi, int[<?>] lo, int[<?>] shift) => $$fshr(hi, lo, shift);
macro int[<?>] int[<?>].rotl(self, int[<?>] shift) => $$fshl(self, self, shift);
macro int[<?>] int[<?>].rotr(self, int[<?>] shift) => $$fshr(self, self, shift);
macro int[<*>].popcount(self) => $$popcount(self);
macro int[<*>].ctz(self) => $$ctz(self);
macro int[<*>].clz(self) => $$clz(self);
macro int[<*>] int[<*>].fshl(hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
macro int[<*>] int[<*>].fshr(hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
macro int[<*>] int[<*>].rotl(self, int[<*>] shift) => $$fshl(self, self, shift);
macro int[<*>] int[<*>].rotr(self, int[<*>] shift) => $$fshr(self, self, shift);
macro ushort[<?>].popcount(self) => $$popcount(self);
macro ushort[<?>].ctz(self) => $$ctz(self);
macro ushort[<?>].clz(self) => $$clz(self);
macro ushort[<?>] ushort[<?>].fshl(hi, ushort[<?>] lo, ushort[<?>] shift) => $$fshl(hi, lo, shift);
macro ushort[<?>] ushort[<?>].fshr(hi, ushort[<?>] lo, ushort[<?>] shift) => $$fshr(hi, lo, shift);
macro ushort[<?>] ushort[<?>].rotl(self, ushort[<?>] shift) => $$fshl(self, self, shift);
macro ushort[<?>] ushort[<?>].rotr(self, ushort[<?>] shift) => $$fshr(self, self, shift);
macro ushort[<*>].popcount(self) => $$popcount(self);
macro ushort[<*>].ctz(self) => $$ctz(self);
macro ushort[<*>].clz(self) => $$clz(self);
macro ushort[<*>] ushort[<*>].fshl(hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
macro ushort[<*>] ushort[<*>].fshr(hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
macro ushort[<*>] ushort[<*>].rotl(self, ushort[<*>] shift) => $$fshl(self, self, shift);
macro ushort[<*>] ushort[<*>].rotr(self, ushort[<*>] shift) => $$fshr(self, self, shift);
macro short[<?>].popcount(self) => $$popcount(self);
macro short[<?>].ctz(self) => $$ctz(self);
macro short[<?>].clz(self) => $$clz(self);
macro short[<?>] short[<?>].fshl(hi, short[<?>] lo, short[<?>] shift) => $$fshl(hi, lo, shift);
macro short[<?>] short[<?>].fshr(hi, short[<?>] lo, short[<?>] shift) => $$fshr(hi, lo, shift);
macro short[<?>] short[<?>].rotl(self, short[<?>] shift) => $$fshl(self, self, shift);
macro short[<?>] short[<?>].rotr(self, short[<?>] shift) => $$fshr(self, self, shift);
macro short[<*>].popcount(self) => $$popcount(self);
macro short[<*>].ctz(self) => $$ctz(self);
macro short[<*>].clz(self) => $$clz(self);
macro short[<*>] short[<*>].fshl(hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
macro short[<*>] short[<*>].fshr(hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
macro short[<*>] short[<*>].rotl(self, short[<*>] shift) => $$fshl(self, self, shift);
macro short[<*>] short[<*>].rotr(self, short[<*>] shift) => $$fshr(self, self, shift);
macro char[<?>].popcount(self) => $$popcount(self);
macro char[<?>].ctz(self) => $$ctz(self);
macro char[<?>].clz(self) => $$clz(self);
macro char[<?>] char[<?>].fshl(hi, char[<?>] lo, char[<?>] shift) => $$fshl(hi, lo, shift);
macro char[<?>] char[<?>].fshr(hi, char[<?>] lo, char[<?>] shift) => $$fshr(hi, lo, shift);
macro char[<?>] char[<?>].rotl(self, char[<?>] shift) => $$fshl(self, self, shift);
macro char[<?>] char[<?>].rotr(self, char[<?>] shift) => $$fshr(self, self, shift);
macro char[<*>].popcount(self) => $$popcount(self);
macro char[<*>].ctz(self) => $$ctz(self);
macro char[<*>].clz(self) => $$clz(self);
macro char[<*>] char[<*>].fshl(hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
macro char[<*>] char[<*>].fshr(hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
macro char[<*>] char[<*>].rotl(self, char[<*>] shift) => $$fshl(self, self, shift);
macro char[<*>] char[<*>].rotr(self, char[<*>] shift) => $$fshr(self, self, shift);
macro ichar[<?>].popcount(self) => $$popcount(self);
macro ichar[<?>].ctz(self) => $$ctz(self);
macro ichar[<?>].clz(self) => $$clz(self);
macro ichar[<?>] ichar[<?>].fshl(hi, ichar[<?>] lo, ichar[<?>] shift) => $$fshl(hi, lo, shift);
macro ichar[<?>] ichar[<?>].fshr(hi, ichar[<?>] lo, ichar[<?>] shift) => $$fshr(hi, lo, shift);
macro ichar[<?>] ichar[<?>].rotl(self, ichar[<?>] shift) => $$fshl(self, self, shift);
macro ichar[<?>] ichar[<?>].rotr(self, ichar[<?>] shift) => $$fshr(self, self, shift);
macro ichar[<*>].popcount(self) => $$popcount(self);
macro ichar[<*>].ctz(self) => $$ctz(self);
macro ichar[<*>].clz(self) => $$clz(self);
macro ichar[<*>] ichar[<*>].fshl(hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
macro ichar[<*>] ichar[<*>].fshr(hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
macro ichar[<*>] ichar[<*>].rotl(self, ichar[<*>] shift) => $$fshl(self, self, shift);
macro ichar[<*>] ichar[<*>].rotr(self, ichar[<*>] shift) => $$fshr(self, self, shift);
macro ulong[<?>].popcount(self) => $$popcount(self);
macro ulong[<?>].ctz(self) => $$ctz(self);
macro ulong[<?>].clz(self) => $$clz(self);
macro ulong[<?>] ulong[<?>].fshl(hi, ulong[<?>] lo, ulong[<?>] shift) => $$fshl(hi, lo, shift);
macro ulong[<?>] ulong[<?>].fshr(hi, ulong[<?>] lo, ulong[<?>] shift) => $$fshr(hi, lo, shift);
macro ulong[<?>] ulong[<?>].rotl(self, ulong[<?>] shift) => $$fshl(self, self, shift);
macro ulong[<?>] ulong[<?>].rotr(self, ulong[<?>] shift) => $$fshr(self, self, shift);
macro ulong[<*>].popcount(self) => $$popcount(self);
macro ulong[<*>].ctz(self) => $$ctz(self);
macro ulong[<*>].clz(self) => $$clz(self);
macro ulong[<*>] ulong[<*>].fshl(hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
macro ulong[<*>] ulong[<*>].fshr(hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
macro ulong[<*>] ulong[<*>].rotl(self, ulong[<*>] shift) => $$fshl(self, self, shift);
macro ulong[<*>] ulong[<*>].rotr(self, ulong[<*>] shift) => $$fshr(self, self, shift);
macro long[<?>].popcount(self) => $$popcount(self);
macro long[<?>].ctz(self) => $$ctz(self);
macro long[<?>].clz(self) => $$clz(self);
macro long[<?>] long[<?>].fshl(hi, long[<?>] lo, long[<?>] shift) => $$fshl(hi, lo, shift);
macro long[<?>] long[<?>].fshr(hi, long[<?>] lo, long[<?>] shift) => $$fshr(hi, lo, shift);
macro long[<?>] long[<?>].rotl(self, long[<?>] shift) => $$fshl(self, self, shift);
macro long[<?>] long[<?>].rotr(self, long[<?>] shift) => $$fshr(self, self, shift);
macro long[<*>].popcount(self) => $$popcount(self);
macro long[<*>].ctz(self) => $$ctz(self);
macro long[<*>].clz(self) => $$clz(self);
macro long[<*>] long[<*>].fshl(hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
macro long[<*>] long[<*>].fshr(hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
macro long[<*>] long[<*>].rotl(self, long[<*>] shift) => $$fshl(self, self, shift);
macro long[<*>] long[<*>].rotr(self, long[<*>] shift) => $$fshr(self, self, shift);
macro uint128[<?>].popcount(self) => $$popcount(self);
macro uint128[<?>].ctz(self) => $$ctz(self);
macro uint128[<?>].clz(self) => $$clz(self);
macro uint128[<?>] uint128[<?>].fshl(hi, uint128[<?>] lo, uint128[<?>] shift) => $$fshl(hi, lo, shift);
macro uint128[<?>] uint128[<?>].fshr(hi, uint128[<?>] lo, uint128[<?>] shift) => $$fshr(hi, lo, shift);
macro uint128[<?>] uint128[<?>].rotl(self, uint128[<?>] shift) => $$fshl(self, self, shift);
macro uint128[<?>] uint128[<?>].rotr(self, uint128[<?>] shift) => $$fshr(self, self, shift);
macro uint128[<*>].popcount(self) => $$popcount(self);
macro uint128[<*>].ctz(self) => $$ctz(self);
macro uint128[<*>].clz(self) => $$clz(self);
macro uint128[<*>] uint128[<*>].fshl(hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
macro uint128[<*>] uint128[<*>].fshr(hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
macro uint128[<*>] uint128[<*>].rotl(self, uint128[<*>] shift) => $$fshl(self, self, shift);
macro uint128[<*>] uint128[<*>].rotr(self, uint128[<*>] shift) => $$fshr(self, self, shift);
macro int128[<?>].popcount(self) => $$popcount(self);
macro int128[<?>].ctz(self) => $$ctz(self);
macro int128[<?>].clz(self) => $$clz(self);
macro int128[<?>] int128[<?>].fshl(hi, int128[<?>] lo, int128[<?>] shift) => $$fshl(hi, lo, shift);
macro int128[<?>] int128[<?>].fshr(hi, int128[<?>] lo, int128[<?>] shift) => $$fshr(hi, lo, shift);
macro int128[<?>] int128[<?>].rotl(self, int128[<?>] shift) => $$fshl(self, self, shift);
macro int128[<?>] int128[<?>].rotr(self, int128[<?>] shift) => $$fshr(self, self, shift);
macro int128[<*>].popcount(self) => $$popcount(self);
macro int128[<*>].ctz(self) => $$ctz(self);
macro int128[<*>].clz(self) => $$clz(self);
macro int128[<*>] int128[<*>].fshl(hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
macro int128[<*>] int128[<*>].fshr(hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
macro int128[<*>] int128[<*>].rotl(self, int128[<*>] shift) => $$fshl(self, self, shift);
macro int128[<*>] int128[<*>].rotr(self, int128[<*>] shift) => $$fshr(self, self, shift);
macro uint.popcount(self) => $$popcount(self);
macro uint.ctz(self) => $$ctz(self);

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2024 Christoffer Lerno. All rights reserved.
// 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.
module std::collections::anylist;
import std::io,std::math;
def AnyPredicate = fn bool(any value);
def AnyTest = fn bool(any type, any context);
alias AnyPredicate = fn bool(any value);
alias AnyTest = fn bool(any type, any context);
struct AnyList (Printable)
{
@@ -17,18 +17,8 @@ struct AnyList (Printable)
<*
Use `init` for to use a custom allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.new_init(&self, usz initial_capacity = 16, Allocator allocator = null) @deprecated("Use init(mem)")
{
return self.init(allocator ?: allocator::heap(), initial_capacity) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator : "The allocator to use"
@param initial_capacity : "The initial capacity to reserve"
*>
fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
{
@@ -50,25 +40,16 @@ fn AnyList* AnyList.init(&self, Allocator allocator, usz initial_capacity = 16)
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn AnyList* AnyList.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
@param initial_capacity : "The initial capacity to reserve"
*>
fn AnyList* AnyList.tinit(&self, usz initial_capacity = 16)
{
return self.init(allocator::temp(), initial_capacity) @inline;
return self.init(tmem, initial_capacity) @inline;
}
fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
fn bool AnyList.is_initialized(&self) @inline => self.allocator != null;
fn usz? AnyList.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
@@ -88,25 +69,12 @@ fn usz! AnyList.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String AnyList.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String AnyList.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String AnyList.to_tstring(&self) => string::tformat("%s", *self);
<*
Push an element on the list by cloning it.
*>
macro void AnyList.push(&self, element)
{
if (!self.allocator) self.allocator = allocator::heap();
if (!self.allocator) self.allocator = tmem;
self.append_internal(allocator::clone(self.allocator, element));
}
@@ -128,56 +96,40 @@ fn void AnyList.free_element(&self, any element) @inline
Pop a value who's type is known. If the type is incorrect, this
will still pop the element.
@return! CastResult.TYPE_MISMATCH, IteratorResult.NO_MORE_ELEMENT
@return? TYPE_MISMATCH, NO_MORE_ELEMENT
*>
macro AnyList.pop(&self, $Type)
{
if (!self.size) return IteratorResult.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);
}
<*
Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT
@return? NO_MORE_ELEMENT
*>
fn any! AnyList.copy_pop(&self, Allocator allocator = allocator::heap())
fn any? AnyList.copy_pop(&self, Allocator allocator)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.free_element(self.entries[self.size]);
return allocator::clone_any(allocator, self.entries[--self.size]);
}
<*
Pop the last value and allocate the copy using the given allocator.
@return! IteratorResult.NO_MORE_ELEMENT
@deprecated `use copy_pop`
*>
fn any! AnyList.new_pop(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop(allocator);
}
<*
Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT
@deprecated `use tcopy_pop`
@return? NO_MORE_ELEMENT
*>
fn any! AnyList.temp_pop(&self) => self.copy_pop(allocator::temp());
<*
Pop the last value and allocate the copy using the temp allocator
@return! IteratorResult.NO_MORE_ELEMENT
*>
fn any! AnyList.tcopy_pop(&self) => self.copy_pop(allocator::temp());
fn any? AnyList.tcopy_pop(&self) => self.copy_pop(tmem);
<*
Pop the last value. It must later be released using list.free_element()
@return! IteratorResult.NO_MORE_ELEMENT
@return? NO_MORE_ELEMENT
*>
fn any! AnyList.pop_retained(&self)
fn any? AnyList.pop_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
@@ -195,7 +147,7 @@ fn void AnyList.clear(&self)
*>
macro AnyList.pop_first(&self, $Type)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return *anycast(self.entries[0], $Type);
}
@@ -203,28 +155,20 @@ macro AnyList.pop_first(&self, $Type)
<*
Same as pop_retained() but pops the first value instead.
*>
fn any! AnyList.pop_first_retained(&self)
fn any? AnyList.pop_first_retained(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
Same as new_pop() but pops the first value instead.
@deprecated `use copy_pop_first`
*>
fn any! AnyList.new_pop_first(&self, Allocator allocator = allocator::heap())
{
return self.copy_pop_first(allocator) @inline;
}
<*
Same as new_pop() but pops the first value instead.
Same as copy_pop() but pops the first value instead.
*>
fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
fn any? AnyList.copy_pop_first(&self, Allocator allocator)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
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]);
@@ -233,13 +177,7 @@ fn any! AnyList.copy_pop_first(&self, Allocator allocator = allocator::heap())
<*
Same as temp_pop() but pops the first value instead.
*>
fn any! AnyList.tcopy_pop_first(&self) => self.copy_pop_first(allocator::temp());
<*
Same as temp_pop() but pops the first value instead.
@deprecated `use tcopy_pop_first`
*>
fn any! AnyList.temp_pop_first(&self) => self.new_pop_first(allocator::temp());
fn any? AnyList.tcopy_pop_first(&self) => self.copy_pop_first(tmem);
<*
@require index < self.size
@@ -333,9 +271,9 @@ macro AnyList.first(&self, $Type)
return *anycast(self.first_any(), $Type);
}
fn any! AnyList.first_any(&self) @inline
fn any? AnyList.first_any(&self) @inline
{
return self.size ? self.entries[0] : IteratorResult.NO_MORE_ELEMENT?;
return self.size ? self.entries[0] : NO_MORE_ELEMENT?;
}
macro AnyList.last(&self, $Type)
@@ -343,9 +281,9 @@ macro AnyList.last(&self, $Type)
return *anycast(self.last_any(), $Type);
}
fn any! AnyList.last_any(&self) @inline
fn any? AnyList.last_any(&self) @inline
{
return self.size ? self.entries[self.size - 1] : IteratorResult.NO_MORE_ELEMENT?;
return self.size ? self.entries[self.size - 1] : NO_MORE_ELEMENT?;
}
fn bool AnyList.is_empty(&self) @inline
@@ -359,7 +297,7 @@ fn usz AnyList.len(&self) @operator(len) @inline
}
<*
@require index < self.size "Index out of range"
@require index < self.size : "Index out of range"
*>
macro AnyList.get(&self, usz index, $Type)
{
@@ -367,7 +305,7 @@ macro AnyList.get(&self, usz index, $Type)
}
<*
@require index < self.size "Index out of range"
@require index < self.size : "Index out of range"
*>
fn any AnyList.get_any(&self, usz index) @inline
{
@@ -391,7 +329,7 @@ fn void AnyList.swap(&self, usz i, usz j)
}
<*
@param filter "The function to determine if it should be removed or not"
@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)
@@ -400,7 +338,7 @@ fn usz AnyList.remove_if(&self, AnyPredicate filter)
}
<*
@param selection "The function to determine if it should be kept or not"
@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)
@@ -477,7 +415,7 @@ fn void AnyList.reserve(&self, usz min_capacity)
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
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;
@@ -489,7 +427,7 @@ macro any AnyList.@item_at(&self, usz index) @operator([])
}
<*
@require index <= self.size "Index out of range"
@require index <= self.size : "Index out of range"
*>
macro void AnyList.set(&self, usz index, value)
{

View File

@@ -1,9 +1,9 @@
<*
@require SIZE > 0
*>
module std::collections::bitset(<SIZE>);
module std::collections::bitset{SIZE};
def Type = uint;
alias Type = uint;
const BITS = Type.sizeof * 8;
const SZ = (SIZE + BITS - 1) / BITS;
@@ -70,12 +70,12 @@ fn void BitSet.set_bool(&self, usz i, bool value) @operator([]=) @inline
<*
@require Type.kindof == UNSIGNED_INT
*>
module std::collections::growablebitset(<Type>);
module std::collections::growablebitset{Type};
import std::collections::list;
const BITS = Type.sizeof * 8;
def GrowableBitSetList = List(<Type>);
alias GrowableBitSetList = List{Type};
struct GrowableBitSet
{
@@ -84,17 +84,7 @@ struct GrowableBitSet
<*
@param initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn GrowableBitSet* GrowableBitSet.new_init(&self, usz initial_capacity = 1, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
self.data.init(allocator, initial_capacity);
return self;
}
<*
@param initial_capacity
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
@param [&inout] allocator : "The allocator to use, defaults to the heap allocator"
*>
fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_capacity = 1)
{
@@ -102,14 +92,9 @@ fn GrowableBitSet* GrowableBitSet.init(&self, Allocator allocator, usz initial_c
return self;
}
fn GrowableBitSet* GrowableBitSet.temp_init(&self, usz initial_capacity = 1) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
fn GrowableBitSet* GrowableBitSet.tinit(&self, usz initial_capacity = 1)
{
return self.init(allocator::temp(), initial_capacity) @inline;
return self.init(tmem, initial_capacity) @inline;
}
fn void GrowableBitSet.free(&self)

View File

@@ -2,13 +2,13 @@
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
@require MAX_SIZE >= 1 `The size must be at least 1 element big.`
@require MAX_SIZE >= 1 : `The size must be at least 1 element big.`
*>
module std::collections::elastic_array(<Type, MAX_SIZE>);
module std::collections::elastic_array {Type, MAX_SIZE};
import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any context);
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;
@@ -19,7 +19,7 @@ struct ElasticArray (Printable)
Type[MAX_SIZE] entries;
}
fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
fn usz? ElasticArray.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
@@ -39,38 +39,28 @@ fn usz! ElasticArray.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String ElasticArray.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String ElasticArray.to_new_string(&self, Allocator allocator = nul) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String ElasticArray.to_tstring(&self)
{
return string::tformat("%s", *self);
}
fn void! ElasticArray.push_try(&self, Type element) @inline
fn void? ElasticArray.push_try(&self, Type element) @inline
{
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
if (self.size == MAX_SIZE) return mem::OUT_OF_MEMORY?;
self.entries[self.size++] = element;
}
<*
@require self.size < MAX_SIZE `Tried to exceed the max size`
@require self.size < MAX_SIZE : `Tried to exceed the max size`
*>
fn void ElasticArray.push(&self, Type element) @inline
{
self.entries[self.size++] = element;
}
fn Type! ElasticArray.pop(&self)
fn Type? ElasticArray.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[--self.size];
}
@@ -82,9 +72,9 @@ fn void ElasticArray.clear(&self)
<*
@require self.size > 0
*>
fn Type! ElasticArray.pop_first(&self)
fn Type? ElasticArray.pop_first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
@@ -146,7 +136,7 @@ fn usz ElasticArray.add_array_to_limit(&self, Type[] array)
Add the values of an array to this list.
@param [in] array
@require array.len + self.size <= MAX_SIZE `Size would exceed max.`
@require array.len + self.size <= MAX_SIZE : `Size would exceed max.`
@ensure self.size >= array.len
*>
fn void ElasticArray.add_array(&self, Type[] array)
@@ -160,28 +150,12 @@ fn void ElasticArray.add_array(&self, Type[] array)
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] ElasticArray.to_new_aligned_array(&self)
{
return list_common::list_to_new_aligned_array(Type, self, allocator::heap());
}
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] ElasticArray.to_aligned_array(&self, Allocator allocator)
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] ElasticArray.to_new_array(&self)
{
return list_common::list_to_array(Type, self, allocator::heap());
return list_common::list_to_aligned_array(Type, self, allocator);
}
<*
@@ -189,15 +163,15 @@ macro Type[] ElasticArray.to_new_array(&self)
*>
macro Type[] ElasticArray.to_array(&self, Allocator allocator)
{
return list_common::list_to_new_array(Type, self, allocator);
return list_common::list_to_array(Type, self, allocator);
}
fn Type[] ElasticArray.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_aligned_array(allocator::temp());
return self.to_aligned_array(tmem);
$else
return self.to_array(allocator::temp());
return self.to_array(tmem);
$endif;
}
@@ -215,7 +189,7 @@ fn Type[] ElasticArray.array_view(&self)
}
<*
@require self.size < MAX_SIZE `List would exceed max size`
@require self.size < MAX_SIZE : `List would exceed max size`
*>
fn void ElasticArray.push_front(&self, Type type) @inline
{
@@ -223,9 +197,9 @@ fn void ElasticArray.push_front(&self, Type type) @inline
}
<*
@require self.size < MAX_SIZE `List would exceed max size`
@require self.size < MAX_SIZE : `List would exceed max size`
*>
fn void! ElasticArray.push_front_try(&self, Type type) @inline
fn void? ElasticArray.push_front_try(&self, Type type) @inline
{
return self.insert_at_try(0, type);
}
@@ -233,14 +207,14 @@ fn void! ElasticArray.push_front_try(&self, Type type) @inline
<*
@require index <= self.size
*>
fn void! ElasticArray.insert_at_try(&self, usz index, Type value)
fn void? ElasticArray.insert_at_try(&self, usz index, Type value)
{
if (self.size == MAX_SIZE) return AllocationFailure.OUT_OF_MEMORY?;
if (self.size == MAX_SIZE) return mem::OUT_OF_MEMORY?;
self.insert_at(index, value);
}
<*
@require self.size < MAX_SIZE `List would exceed max size`
@require self.size < MAX_SIZE : `List would exceed max size`
@require index <= self.size
*>
fn void ElasticArray.insert_at(&self, usz index, Type type)
@@ -261,27 +235,27 @@ fn void ElasticArray.set_at(&self, usz index, Type type)
self.entries[index] = type;
}
fn void! ElasticArray.remove_last(&self) @maydiscard
fn void? ElasticArray.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
self.size--;
}
fn void! ElasticArray.remove_first(&self) @maydiscard
fn void? ElasticArray.remove_first(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
self.remove_at(0);
}
fn Type! ElasticArray.first(&self)
fn Type? ElasticArray.first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[0];
}
fn Type! ElasticArray.last(&self)
fn Type? ElasticArray.last(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[self.size - 1];
}
@@ -311,7 +285,7 @@ fn void ElasticArray.swap(&self, usz i, usz j)
}
<*
@param filter "The function to determine if it should be removed or not"
@param filter : "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz ElasticArray.remove_if(&self, ElementPredicate filter)
@@ -320,7 +294,7 @@ fn usz ElasticArray.remove_if(&self, ElementPredicate filter)
}
<*
@param selection "The function to determine if it should be kept or not"
@param selection : "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz ElasticArray.retain_if(&self, ElementPredicate selection)
@@ -356,22 +330,22 @@ fn void ElasticArray.set(&self, usz index, Type value) @operator([]=)
// Functions for equatable types
fn usz! ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
fn usz? ElasticArray.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
fn usz! ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
fn usz? ElasticArray.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach_r (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUATABLE)
@@ -387,8 +361,8 @@ fn bool ElasticArray.equals(&self, ElasticArray other_list) @if(ELEMENT_IS_EQUAT
<*
Check for presence of a value in a list.
@param [&in] self "the list to find elements in"
@param value "The value to search for"
@param [&in] self : "the list to find elements in"
@param value : "The value to search for"
@return "True if the value is found, false otherwise"
*>
fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -401,8 +375,8 @@ fn bool ElasticArray.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@param [&inout] self : "The list to remove elements from"
@param value : "The value to remove"
@return "true if the value was found"
*>
fn bool ElasticArray.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -411,8 +385,8 @@ fn bool ElasticArray.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABL
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@param [&inout] self : "The list to remove elements from"
@param value : "The value to remove"
@return "true if the value was found"
*>
fn bool ElasticArray.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -421,8 +395,8 @@ fn bool ElasticArray.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATAB
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@param [&inout] self : "The list to remove elements from"
@param value : "The value to remove"
@return "the number of deleted elements."
*>
fn usz ElasticArray.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)

View File

@@ -1,7 +1,7 @@
<*
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enummap"
*>
module std::collections::enummap(<Enum, ValueType>);
module std::collections::enummap{Enum, ValueType};
import std::io;
struct EnumMap (Printable)
{
@@ -16,7 +16,7 @@ fn void EnumMap.init(&self, ValueType init_value)
}
}
fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
fn usz? EnumMap.to_format(&self, Formatter* formatter) @dynamic
{
usz n = formatter.print("{ ")!;
foreach (i, &value : self.values)
@@ -28,21 +28,6 @@ fn usz! EnumMap.to_format(&self, Formatter* formatter) @dynamic
return n;
}
fn String EnumMap.to_string(&self, Allocator allocator) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String EnumMap.to_new_string(&self, Allocator allocator = null) @dynamic
{
return string::format("%s", *self, allocator: allocator ?: allocator::heap());
}
fn String EnumMap.to_tstring(&self) @dynamic
{
return string::tformat("%s", *self);
}
<*
@return "The total size of this map, which is the same as the number of enum values"
@pure

View File

@@ -5,13 +5,13 @@
<*
@require Enum.kindof == TypeKind.ENUM : "Only enums may be used with an enumset"
*>
module std::collections::enumset(<Enum>);
module std::collections::enumset{Enum};
import std::io;
def EnumSetType = $typefrom(private::type_for_enum_elements(Enum.elements)) @private;
alias EnumSetType @private = $typefrom(type_for_enum_elements(Enum.elements));
const IS_CHAR_ARRAY = Enum.elements > 128;
distinct EnumSet (Printable) = EnumSetType;
typedef EnumSet (Printable) = EnumSetType;
fn void EnumSet.add(&self, Enum v)
{
@@ -126,7 +126,7 @@ fn EnumSet EnumSet.xor_of(&self, EnumSet s)
$endif
}
fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
fn usz? EnumSet.to_format(&set, Formatter* formatter) @dynamic
{
usz n = formatter.print("[")!;
bool found;
@@ -141,26 +141,9 @@ fn usz! EnumSet.to_format(&set, Formatter* formatter) @dynamic
return n;
}
fn String EnumSet.to_new_string(&set, Allocator allocator = allocator::heap()) @dynamic
macro typeid type_for_enum_elements(usz $elements) @local
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_string(&set, Allocator allocator) @dynamic
{
return string::format("%s", *set, allocator: allocator);
}
fn String EnumSet.to_tstring(&set) @dynamic
{
return string::tformat("%s", *set);
}
module std::collections::enumset::private;
macro typeid type_for_enum_elements(usz $elements)
{
$switch
$switch:
$case ($elements > 128):
return char[($elements + 7) / 8].typeid;
$case ($elements > 64):

View File

@@ -2,12 +2,30 @@
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
<*
@require $defined((Key){}.hash()) `No .hash function found on the key`
@require $defined((Key){}.hash()) : `No .hash function found on the key`
*>
module std::collections::map(<Key, Value>);
module std::collections::map{Key, Value};
import std::math;
import std::io @norecurse;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
const VALUE_IS_EQUATABLE = Value.is_eq;
const bool COPY_KEYS = types::implements_copy(Key);
const Allocator MAP_HEAP_ALLOCATOR = (Allocator)&dummy;
const HashMap ONHEAP = { .allocator = MAP_HEAP_ALLOCATOR };
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}
struct HashMap (Printable)
{
Entry*[] table;
@@ -17,24 +35,13 @@ struct HashMap (Printable)
float load_factor;
}
<*
@param [&inout] allocator "The allocator to use"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.new_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = null) @deprecated("Use init(mem)")
{
return self.init(allocator ?: allocator::heap(), capacity, load_factor);
}
<*
@param [&inout] allocator "The allocator to use"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
@param [&inout] allocator : "The allocator to use"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
@@ -47,55 +54,56 @@ fn HashMap* HashMap.init(&self, Allocator allocator, uint capacity = DEFAULT_INI
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.temp_init(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), capacity, load_factor) @inline;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.tinit(&self, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.init(allocator::temp(), capacity, load_factor) @inline;
return self.init(tmem, capacity, load_factor) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
@param [&inout] allocator : "The allocator to use"
@require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.new_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_with_key_values(mem)")
macro HashMap* HashMap.init_with_key_values(&self, Allocator allocator, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.init(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
self.init(allocator, capacity, load_factor);
$for var $i = 0; $i < $vacount; $i += 2:
self.set($vaarg[$i], $vaarg[$i + 1]);
$endfor
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
@require $vacount % 2 == 0 : "There must be an even number of arguments provided for keys and values"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use init_from_keys_and_values(mem)")
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
return self.tinit_with_key_values(tmem, capacity, load_factor);
}
<*
@param [in] keys : "The keys for the HashMap entries"
@param [in] values : "The values for the HashMap entries"
@param [&inout] allocator : "The allocator to use"
@require keys.len == values.len : "Both keys and values arrays must be the same length"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.init_from_keys_and_values(&self, Allocator allocator, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
assert(keys.len == values.len);
self.init(allocator, capacity, load_factor);
@@ -106,102 +114,36 @@ fn HashMap* HashMap.new_init_from_keys_and_values(&self, Key[] keys, Value[] val
return self;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.temp_init_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR) @deprecated("Use tinit_with_key_values")
{
self.tinit(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
@param [in] keys : "The keys for the HashMap entries"
@param [in] values : "The values for the HashMap entries"
@require keys.len == values.len : "Both keys and values arrays must be the same length"
@require capacity > 0 : "The capacity must be 1 or higher"
@require load_factor > 0.0 : "The load factor must be higher than 0"
@require !self.is_initialized() : "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY : "Capacity cannot exceed maximum"
*>
macro HashMap* HashMap.tinit_with_key_values(&self, ..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
self.tinit(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
self.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.temp_init_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap()) @deprecated("Use tinit_from_keys_and_values")
{
assert(keys.len == values.len);
self.tinit(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require !self.allocator "Map was already initialized"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn HashMap* HashMap.tinit_from_keys_and_values(&self, Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
self.tinit(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
self.set(keys[i], values[i]);
}
return self;
return self.init_from_keys_and_values(tmem, keys, values, capacity, load_factor);
}
<*
Has this hash map been initialized yet?
@param [&in] map "The hash map we are testing"
@param [&in] map : "The hash map we are testing"
@return "Returns true if it has been initialized, false otherwise"
*>
fn bool HashMap.is_initialized(&map)
{
return (bool)map.allocator;
return map.allocator && map.allocator.ptr != &dummy;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.new_init_from_map(&self, HashMap* other_map) @deprecated("Use init_from_map(mem, map)")
{
return self.init_from_map(allocator::heap(), other_map) @inline;
}
<*
@param [&inout] allocator "The allocator to use"
@param [&in] other_map "The map to copy from."
@param [&inout] allocator : "The allocator to use"
@param [&in] other_map : "The map to copy from."
@require !self.is_initialized() : "Map was already initialized"
*>
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
{
@@ -211,19 +153,12 @@ fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map
}
<*
@param [&in] other_map "The map to copy from."
*>
fn HashMap* HashMap.temp_init_from_map(&map, HashMap* other_map) @deprecated("Use tinit_from_map")
{
return map.init_from_map(allocator::temp(), other_map) @inline;
}
<*
@param [&in] other_map "The map to copy from."
@param [&in] other_map : "The map to copy from."
@require !map.is_initialized() : "Map was already initialized"
*>
fn HashMap* HashMap.tinit_from_map(&map, HashMap* other_map)
{
return map.init_from_map(allocator::temp(), other_map) @inline;
return map.init_from_map(tmem, other_map) @inline;
}
fn bool HashMap.is_empty(&map) @inline
@@ -236,26 +171,26 @@ fn usz HashMap.len(&map) @inline
return map.count;
}
fn Value*! HashMap.get_ref(&map, Key key)
fn Value*? HashMap.get_ref(&map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
if (!map.count) return NOT_FOUND?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
fn Entry*! HashMap.get_entry(&map, Key key)
fn Entry*? HashMap.get_entry(&map, Key key)
{
if (!map.count) return SearchResult.MISSING?;
if (!map.count) return NOT_FOUND?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
<*
@@ -281,7 +216,7 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
return val;
}
fn Value! HashMap.get(&map, Key key) @operator([])
fn Value? HashMap.get(&map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
@@ -294,10 +229,15 @@ fn bool HashMap.has_key(&map, Key key)
fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!map.allocator)
{
map.init(allocator::heap());
}
switch (map.allocator.ptr)
{
case &dummy:
map.init(mem);
case null:
map.tinit();
default:
break;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
@@ -312,9 +252,9 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
return false;
}
fn void! HashMap.remove(&map, Key key) @maydiscard
fn void? HashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING?;
if (!map.remove_entry_for_key(key)) return NOT_FOUND?;
}
fn void HashMap.clear(&map)
@@ -339,37 +279,24 @@ fn void HashMap.clear(&map)
fn void HashMap.free(&map)
{
if (!map.allocator) return;
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
map.table = {};
}
fn Key[] HashMap.tcopy_keys(&map)
fn Key[] HashMap.tkeys(&self)
{
return map.copy_keys(allocator::temp()) @inline;
return self.keys(tmem) @inline;
}
fn Key[] HashMap.key_tlist(&map) @deprecated("Use 'tcopy_keys'")
fn Key[] HashMap.keys(&self, Allocator allocator)
{
return map.copy_keys(allocator::temp()) @inline;
}
if (!self.count) return {};
<*
@deprecated "use copy_keys"
*>
fn Key[] HashMap.key_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_keys(allocator) @inline;
}
fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
Key[] list = allocator::alloc_array(allocator, Key, self.count);
usz index = 0;
foreach (Entry* entry : map.table)
foreach (Entry* entry : self.table)
{
while (entry)
{
@@ -386,53 +313,36 @@ fn Key[] HashMap.copy_keys(&map, Allocator allocator = allocator::heap())
macro HashMap.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
map.@each_entry(; Entry* entry)
{
@body(entry.key, entry.value);
};
}
macro HashMap.@each_entry(map; @body(entry))
{
if (map.count)
if (!map.count) return;
foreach (Entry* entry : map.table)
{
foreach (Entry* entry : map.table)
while (entry)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
@body(entry);
entry = entry.next;
}
}
}
<*
@deprecated `use tcopy_values`
*>
fn Value[] HashMap.value_tlist(&map)
fn Value[] HashMap.tvalues(&map)
{
return map.copy_values(allocator::temp()) @inline;
return map.values(tmem) @inline;
}
fn Value[] HashMap.tcopy_values(&map)
fn Value[] HashMap.values(&self, Allocator allocator)
{
return map.copy_values(allocator::temp()) @inline;
}
<*
@deprecated `use copy_values`
*>
fn Value[] HashMap.value_new_list(&map, Allocator allocator = allocator::heap())
{
return map.copy_values(allocator);
}
fn Value[] HashMap.copy_values(&map, Allocator allocator = allocator::heap())
{
if (!map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
if (!self.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, self.count);
usz index = 0;
foreach (Entry* entry : map.table)
foreach (Entry* entry : self.table)
{
while (entry)
{
@@ -503,7 +413,7 @@ fn void HashMap.resize(&map, uint new_capacity) @private
map.threshold = (uint)(new_capacity * map.load_factor);
}
fn usz! HashMap.to_format(&self, Formatter* f) @dynamic
fn usz? HashMap.to_format(&self, Formatter* f) @dynamic
{
usz len;
len += f.print("{ ")!;
@@ -625,8 +535,8 @@ struct HashMapIterator
Entry* current_entry;
}
distinct HashMapValueIterator = HashMapIterator;
distinct HashMapKeyIterator = HashMapIterator;
typedef HashMapValueIterator = HashMapIterator;
typedef HashMapKeyIterator = HashMapIterator;
<*
@@ -667,3 +577,16 @@ fn Key HashMapKeyIterator.get(&self, usz idx) @operator([])
fn usz HashMapValueIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapKeyIterator.len(self) @operator(len) => self.map.count;
fn usz HashMapIterator.len(self) @operator(len) => self.map.count;
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private
{
return hash & (capacity - 1);
}
int dummy @local;

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::linkedlist(<Type>);
module std::collections::linkedlist{Type};
const ELEMENT_IS_EQUATABLE = types::is_equatable_type(Type);
@@ -21,7 +21,7 @@ struct LinkedList
}
<*
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
@param [&inout] allocator : "The allocator to use, defaults to the heap allocator"
@return "the initialized list"
*>
fn LinkedList* LinkedList.init(&self, Allocator allocator)
@@ -30,27 +30,15 @@ fn LinkedList* LinkedList.init(&self, Allocator allocator)
return self;
}
<*
@return "the initialized list"
*>
fn LinkedList* LinkedList.new_init(&self) @deprecated("Use init(mem)")
{
return self.init(allocator::heap()) @inline;
}
fn LinkedList* LinkedList.temp_init(&self) @deprecated("Use tinit()")
{
return self.init(allocator::temp()) @inline;
}
fn LinkedList* LinkedList.tinit(&self)
{
return self.init(allocator::temp()) @inline;
return self.init(tmem) @inline;
}
fn bool LinkedList.is_initialized(&self) @inline => self.allocator != null;
<*
@require self.allocator != null
@require self.is_initialized()
*>
macro void LinkedList.free_node(&self, Node* node) @private
{
@@ -59,7 +47,7 @@ macro void LinkedList.free_node(&self, Node* node) @private
macro Node* LinkedList.alloc_node(&self) @private
{
if (!self.allocator) self.allocator = allocator::heap();
if (!self.allocator) self.allocator = tmem;
return allocator::alloc(self.allocator, Node);
}
@@ -97,18 +85,18 @@ fn void LinkedList.push(&self, Type value)
self.size++;
}
fn Type! LinkedList.peek(&self) => self.first() @inline;
fn Type! LinkedList.peek_last(&self) => self.last() @inline;
fn Type? LinkedList.peek(&self) => self.first() @inline;
fn Type? LinkedList.peek_last(&self) => self.last() @inline;
fn Type! LinkedList.first(&self)
fn Type? LinkedList.first(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT?;
return self._first.value;
}
fn Type! LinkedList.last(&self)
fn Type? LinkedList.last(&self)
{
if (!self._last) return IteratorResult.NO_MORE_ELEMENT?;
if (!self._last) return NO_MORE_ELEMENT?;
return self._last.value;
}
@@ -243,9 +231,9 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
return start - self.size;
}
fn Type! LinkedList.pop(&self)
fn Type? LinkedList.pop(&self)
{
if (!self._last) return IteratorResult.NO_MORE_ELEMENT?;
if (!self._last) return NO_MORE_ELEMENT?;
defer self.unlink_last();
return self._last.value;
}
@@ -255,22 +243,22 @@ fn bool LinkedList.is_empty(&self)
return !self._first;
}
fn Type! LinkedList.pop_front(&self)
fn Type? LinkedList.pop_front(&self)
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT?;
defer self.unlink_first();
return self._first.value;
}
fn void! LinkedList.remove_last(&self) @maydiscard
fn void? LinkedList.remove_last(&self) @maydiscard
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT?;
self.unlink_last();
}
fn void! LinkedList.remove_first(&self) @maydiscard
fn void? LinkedList.remove_first(&self) @maydiscard
{
if (!self._first) return IteratorResult.NO_MORE_ELEMENT?;
if (!self._first) return NO_MORE_ELEMENT?;
self.unlink_first();
}

View File

@@ -1,14 +1,18 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Use of self source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::list(<Type>);
module std::collections::list{Type};
import std::io, std::math, std::collections::list_common;
def ElementPredicate = fn bool(Type *type);
def ElementTest = fn bool(Type *type, any context);
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;
const Allocator LIST_HEAP_ALLOCATOR = (Allocator)&dummy;
const List ONHEAP = { .allocator = LIST_HEAP_ALLOCATOR };
macro type_is_overaligned() => Type.alignof > mem::DEFAULT_MEM_ALIGNMENT;
struct List (Printable)
@@ -20,8 +24,8 @@ struct List (Printable)
}
<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
@param initial_capacity : "The initial capacity to reserve"
@param [&inout] allocator : "The allocator to use, defaults to the heap allocator"
*>
fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16)
{
@@ -33,56 +37,22 @@ fn List* List.init(&self, Allocator allocator, usz initial_capacity = 16)
return self;
}
<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
self.allocator = allocator;
self.size = 0;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
*>
fn List* List.temp_init(&self, usz initial_capacity = 16) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), initial_capacity) @inline;
}
<*
Initialize the list using the temp allocator.
@param initial_capacity "The initial capacity to reserve"
@param initial_capacity : "The initial capacity to reserve"
*>
fn List* List.tinit(&self, usz initial_capacity = 16)
{
return self.init(allocator::temp(), initial_capacity) @inline;
return self.init(tmem, initial_capacity) @inline;
}
<*
Initialize a new list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.new_init_with_array(&self, Type[] values, Allocator allocator = allocator::heap()) @deprecated("Use init_with_array(mem)")
{
return self.init_with_array(allocator, values);
}
<*
Initialize a new list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
@param [in] values : `The values to initialize the list with.`
@require self.size == 0 : "The List must be empty"
*>
fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
{
@@ -94,21 +64,8 @@ fn List* List.init_with_array(&self, Allocator allocator, Type[] values)
<*
Initialize a temporary list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
*>
fn List* List.temp_init_with_array(&self, Type[] values) @deprecated("Use tinit_with_array()")
{
self.tinit(values.len) @inline;
self.add_array(values) @inline;
return self;
}
<*
Initialize a temporary list with an array.
@param [in] values `The values to initialize the list with.`
@require self.size == 0 "The List must be empty"
@param [in] values : `The values to initialize the list with.`
@require self.size == 0 : "The List must be empty"
*>
fn List* List.tinit_with_array(&self, Type[] values)
{
@@ -118,9 +75,9 @@ fn List* List.tinit_with_array(&self, Type[] values)
}
<*
@require self.capacity == 0 "The List must not be allocated"
@require !self.is_initialized() : "The List must not be allocated"
*>
fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allocator::heap())
fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types)
{
self.allocator = allocator;
self.capacity = types.len;
@@ -128,7 +85,9 @@ fn void List.init_wrapping_array(&self, Type[] types, Allocator allocator = allo
self.set_size(types.len);
}
fn usz! List.to_format(&self, Formatter* formatter) @dynamic
fn bool List.is_initialized(&self) @inline => self.allocator != null && self.allocator != (Allocator)&dummy;
fn usz? List.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.size)
{
@@ -148,25 +107,15 @@ fn usz! List.to_format(&self, Formatter* formatter) @dynamic
}
}
fn String List.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return string::format("%s", *self, allocator: allocator);
}
fn String List.to_tstring(&self)
{
return string::tformat("%s", *self);
}
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
self.entries[self.set_size(self.size + 1)] = element;
}
fn Type! List.pop(&self)
fn Type? List.pop(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.set_size(self.size - 1);
return self.entries[self.size - 1];
}
@@ -176,15 +125,15 @@ fn void List.clear(&self)
self.set_size(0);
}
fn Type! List.pop_first(&self)
fn Type? List.pop_first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
defer self.remove_at(0);
return self.entries[0];
}
<*
@require index < self.size `Removed element out of bounds`
@require index < self.size : `Removed element out of bounds`
*>
fn void List.remove_at(&self, usz index)
{
@@ -208,25 +157,25 @@ fn void List.add_all(&self, List* other_list)
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
fn Type[] List.to_new_aligned_array(&self, Allocator allocator = allocator::heap())
fn Type[] List.to_aligned_array(&self, Allocator allocator)
{
return list_common::list_to_new_aligned_array(Type, self, allocator);
return list_common::list_to_aligned_array(Type, self, allocator);
}
<*
@require !type_is_overaligned() : "This function is not available on overaligned types"
*>
macro Type[] List.to_new_array(&self, Allocator allocator = allocator::heap())
macro Type[] List.to_array(&self, Allocator allocator)
{
return list_common::list_to_new_array(Type, self, allocator);
return list_common::list_to_array(Type, self, allocator);
}
fn Type[] List.to_tarray(&self)
{
$if type_is_overaligned():
return self.to_new_aligned_array(allocator::temp());
return self.to_aligned_array(tmem);
$else
return self.to_new_array(allocator::temp());
return self.to_array(tmem);
$endif;
}
@@ -263,7 +212,7 @@ fn void List.push_front(&self, Type type) @inline
}
<*
@require index <= self.size `Insert was out of bounds`
@require index <= self.size : `Insert was out of bounds`
*>
fn void List.insert_at(&self, usz index, Type type)
{
@@ -284,27 +233,27 @@ fn void List.set_at(&self, usz index, Type type)
self.entries[index] = type;
}
fn void! List.remove_last(&self) @maydiscard
fn void? List.remove_last(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
self.set_size(self.size - 1);
}
fn void! List.remove_first(&self) @maydiscard
fn void? List.remove_first(&self) @maydiscard
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
self.remove_at(0);
}
fn Type! List.first(&self)
fn Type? List.first(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[0];
}
fn Type! List.last(&self)
fn Type? List.last(&self)
{
if (!self.size) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.size) return NO_MORE_ELEMENT?;
return self.entries[self.size - 1];
}
@@ -324,7 +273,7 @@ fn usz List.len(&self) @operator(len) @inline
}
<*
@require index < self.size `Access out of bounds`
@require index < self.size : `Access out of bounds`
*>
fn Type List.get(&self, usz index) @inline
{
@@ -333,7 +282,7 @@ fn Type List.get(&self, usz index) @inline
fn void List.free(&self)
{
if (!self.allocator || !self.capacity) return;
if (!self.allocator || self.allocator.ptr == &dummy || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
@@ -348,7 +297,7 @@ fn void List.free(&self)
}
<*
@require i < self.size && j < self.size `Access out of bounds`
@require i < self.size && j < self.size : `Access out of bounds`
*>
fn void List.swap(&self, usz i, usz j)
{
@@ -356,7 +305,7 @@ fn void List.swap(&self, usz i, usz j)
}
<*
@param filter "The function to determine if it should be removed or not"
@param filter : "The function to determine if it should be removed or not"
@return "the number of deleted elements"
*>
fn usz List.remove_if(&self, ElementPredicate filter)
@@ -365,7 +314,7 @@ fn usz List.remove_if(&self, ElementPredicate filter)
}
<*
@param selection "The function to determine if it should be kept or not"
@param selection : "The function to determine if it should be kept or not"
@return "the number of deleted elements"
*>
fn usz List.retain_if(&self, ElementPredicate selection)
@@ -398,7 +347,17 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
if (!self.allocator) self.allocator = allocator::heap();
// Get a proper allocator
switch (self.allocator.ptr)
{
case &dummy:
self.allocator = mem;
case null:
self.allocator = tmem;
default:
break;
}
self.pre_free(); // Remove sanitizer annotation
@@ -414,7 +373,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
}
<*
@require index < self.size `Access out of bounds`
@require index < self.size : `Access out of bounds`
*>
macro Type List.@item_at(&self, usz index) @operator([])
{
@@ -422,7 +381,7 @@ macro Type List.@item_at(&self, usz index) @operator([])
}
<*
@require index < self.size `Access out of bounds`
@require index < self.size : `Access out of bounds`
*>
fn Type* List.get_ref(&self, usz index) @operator(&[]) @inline
{
@@ -430,7 +389,7 @@ fn Type* List.get_ref(&self, usz index) @operator(&[]) @inline
}
<*
@require index < self.size `Access out of bounds`
@require index < self.size : `Access out of bounds`
*>
fn void List.set(&self, usz index, Type value) @operator([]=)
{
@@ -451,10 +410,13 @@ fn void List.reserve(&self, usz added)
fn void List._update_size_change(&self,usz old_size, usz new_size)
{
if (old_size == new_size) return;
sanitizer::annotate_contiguous_container(self.entries,
$if env::ADDRESS_SANITIZER:
if (self.allocator.ptr != &allocator::LIBC_ALLOCATOR) return;
sanitizer::annotate_contiguous_container(self.entries,
&self.entries[self.capacity],
&self.entries[old_size],
&self.entries[new_size]);
$endif
}
<*
@require new_size == 0 || self.capacity != 0
@@ -484,22 +446,22 @@ macro void List.post_alloc(&self) @private
// Functions for equatable types
fn usz! List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
fn usz? List.index_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
fn usz! List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
fn usz? List.rindex_of(&self, Type type) @if(ELEMENT_IS_EQUATABLE)
{
foreach_r (i, v : self)
{
if (equals(v, type)) return i;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE)
@@ -515,8 +477,8 @@ fn bool List.equals(&self, List other_list) @if(ELEMENT_IS_EQUATABLE)
<*
Check for presence of a value in a list.
@param [&in] self "the list to find elements in"
@param value "The value to search for"
@param [&in] self : "the list to find elements in"
@param value : "The value to search for"
@return "True if the value is found, false otherwise"
*>
fn bool List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -529,8 +491,8 @@ fn bool List.contains(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@param [&inout] self : "The list to remove elements from"
@param value : "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -539,8 +501,8 @@ fn bool List.remove_last_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@param [&inout] self : "The list to remove elements from"
@param value : "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -548,8 +510,8 @@ fn bool List.remove_first_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
return @ok(self.remove_at(self.index_of(value)));
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@param [&inout] self : "The list to remove elements from"
@param value : "The value to remove"
@return "the number of deleted elements."
*>
fn usz List.remove_item(&self, Type value) @if(ELEMENT_IS_EQUATABLE)
@@ -593,35 +555,4 @@ fn usz List.compact(&self) @if(ELEMENT_IS_POINTER)
return list_common::list_compact(self);
}
// --> Deprecated
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_last_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_last_item(value) @inline;
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "true if the value was found"
*>
fn bool List.remove_first_match(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_first_item(value) @inline;
}
<*
@param [&inout] self "The list to remove elements from"
@param value "The value to remove"
@return "the number of deleted elements."
*>
fn usz List.remove_all_matches(&self, Type value) @if(ELEMENT_IS_EQUATABLE) @deprecated
{
return self.remove_item(value) @inline;
}
int dummy @local;

View File

@@ -3,7 +3,7 @@ module std::collections::list_common;
<*
IMPORTANT The returned array must be freed using free_aligned.
*>
macro list_to_new_aligned_array($Type, self, Allocator allocator)
macro list_to_aligned_array($Type, self, Allocator allocator)
{
if (!self.size) return ($Type[]){};
$Type[] result = allocator::alloc_array_aligned(allocator, $Type, self.size);
@@ -11,7 +11,7 @@ macro list_to_new_aligned_array($Type, self, Allocator allocator)
return result;
}
macro list_to_new_array($Type, self, Allocator allocator)
macro list_to_array($Type, self, Allocator allocator)
{
if (!self.size) return ($Type[]){};
$Type[] result = allocator::alloc_array(allocator, $Type, self.size);

View File

@@ -1,508 +0,0 @@
// Copyright (c) 2023 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
module std::collections::map(<Key, Value>);
import std::math;
const uint DEFAULT_INITIAL_CAPACITY = 16;
const uint MAXIMUM_CAPACITY = 1u << 31;
const float DEFAULT_LOAD_FACTOR = 0.75;
const VALUE_IS_EQUATABLE = Value.is_eq;
const bool COPY_KEYS = types::implements_copy(Key);
distinct Map = void*;
struct MapImpl
{
Entry*[] table;
Allocator allocator;
uint count; // Number of elements
uint threshold; // Resize limit
float load_factor;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map new(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
MapImpl* map = allocator::alloc(allocator, MapImpl);
_init(map, capacity, load_factor, allocator);
return (Map)map;
}
<*
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map temp(uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
MapImpl* map = mem::temp_alloc(MapImpl);
_init(map, capacity, load_factor, allocator::temp());
return (Map)map;
}
<*
@param [&inout] allocator "The allocator to use"
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro Map new_init_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
Map map = new(capacity, load_factor, allocator);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
<*
@param [in] keys "Array of keys for the Map entries"
@param [in] values "Array of values for the Map entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map new_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
Map map = new(capacity, load_factor, allocator);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
<*
@require $vacount % 2 == 0 "There must be an even number of arguments provided for keys and values"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
macro Map temp_new_with_key_values(..., uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
{
Map map = temp(capacity, load_factor);
$for (var $i = 0; $i < $vacount; $i += 2)
map.set($vaarg[$i], $vaarg[$i+1]);
$endfor
return map;
}
<*
@param [in] keys "The keys for the HashMap entries"
@param [in] values "The values for the HashMap entries"
@param [&inout] allocator "The allocator to use"
@require keys.len == values.len "Both keys and values arrays must be the same length"
@require capacity > 0 "The capacity must be 1 or higher"
@require load_factor > 0.0 "The load factor must be higher than 0"
@require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
*>
fn Map temp_init_from_keys_and_values(Key[] keys, Value[] values, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator allocator = allocator::heap())
{
assert(keys.len == values.len);
Map map = temp(capacity, load_factor);
for (usz i = 0; i < keys.len; i++)
{
map.set(keys[i], values[i]);
}
return map;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn Map new_from_map(Map other_map, Allocator allocator = null)
{
MapImpl* other_map_impl = (MapImpl*)other_map;
if (!other_map_impl)
{
if (allocator) return new(allocator: allocator);
return null;
}
MapImpl* map = (MapImpl*)new(other_map_impl.table.len, other_map_impl.load_factor, allocator ?: allocator::heap());
if (!other_map_impl.count) return (Map)map;
foreach (Entry *e : other_map_impl.table)
{
while (e)
{
map._put_for_create(e.key, e.value);
e = e.next;
}
}
return (Map)map;
}
<*
@param [&in] other_map "The map to copy from."
*>
fn Map temp_from_map(Map other_map)
{
return new_from_map(other_map, allocator::temp());
}
fn bool Map.is_empty(map) @inline
{
return !map || !((MapImpl*)map).count;
}
fn usz Map.len(map) @inline
{
return map ? ((MapImpl*)map).count : 0;
}
fn Value*! Map.get_ref(self, Key key)
{
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map.table[index_for(hash, map.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return &e.value;
}
return SearchResult.MISSING?;
}
fn Entry*! Map.get_entry(map, Key key)
{
MapImpl *map_impl = (MapImpl*)map;
if (!map_impl || !map_impl.count) return SearchResult.MISSING?;
uint hash = rehash(key.hash());
for (Entry *e = map_impl.table[index_for(hash, map_impl.table.len)]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e;
}
return SearchResult.MISSING?;
}
<*
Get the value or update and
@require $assignable(#expr, Value)
*>
macro Value Map.@get_or_set(&self, Key key, Value #expr)
{
MapImpl *map = (MapImpl*)*self;
if (!map || !map.count)
{
Value val = #expr;
map.set(key, val);
return val;
}
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
return val;
}
fn Value! Map.get(map, Key key) @operator([])
{
return *map.get_ref(key) @inline;
}
fn bool Map.has_key(map, Key key)
{
return @ok(map.get_ref(key));
}
macro Value Map.set_value_return(&map, Key key, Value value) @operator([]=)
{
map.set(key, value);
return value;
}
fn bool Map.set(&self, Key key, Value value)
{
// If the map isn't initialized, use the defaults to initialize it.
if (!*self) *self = new();
MapImpl* map = (MapImpl*)*self;
uint hash = rehash(key.hash());
uint index = index_for(hash, map.table.len);
for (Entry *e = map.table[index]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return true;
}
}
map._add_entry(hash, key, value, index);
return false;
}
fn void! Map.remove(map, Key key) @maydiscard
{
if (!map || !((MapImpl*)map)._remove_entry_for_key(key)) return SearchResult.MISSING?;
}
fn void Map.clear(self)
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry** &entry_ref : map.table)
{
Entry* entry = *entry_ref;
if (!entry) continue;
Entry *next = entry.next;
while (next)
{
Entry *to_delete = next;
next = next.next;
map._free_entry(to_delete);
}
map._free_entry(entry);
*entry_ref = null;
}
map.count = 0;
}
fn void Map.free(self)
{
if (!self) return;
MapImpl* map = (MapImpl*)self;
self.clear();
map._free_internal(map.table.ptr);
map.table = {};
allocator::free(map.allocator, map);
}
fn Key[] Map.temp_keys_list(map)
{
return map.new_keys_list(allocator::temp()) @inline;
}
fn Key[] Map.new_keys_list(self, Allocator allocator = allocator::heap())
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Key[] list = allocator::alloc_array(allocator, Key, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.key;
entry = entry.next;
}
}
return list;
}
macro Map.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro Map.@each_entry(self; @body(entry))
{
MapImpl *map = (MapImpl*)self;
if (!map || !map.count) return;
foreach (Entry* entry : map.table)
{
while (entry)
{
@body(entry);
entry = entry.next;
}
}
}
fn Value[] Map.temp_values_list(map)
{
return map.new_values_list(allocator::temp()) @inline;
}
fn Value[] Map.new_values_list(self, Allocator allocator = allocator::heap())
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return {};
Value[] list = allocator::alloc_array(allocator, Value, map.count);
usz index = 0;
foreach (Entry* entry : map.table)
{
while (entry)
{
list[index++] = entry.value;
entry = entry.next;
}
}
return list;
}
fn bool Map.has_value(self, Value v) @if(VALUE_IS_EQUATABLE)
{
MapImpl* map = (MapImpl*)self;
if (!map || !map.count) return false;
foreach (Entry* entry : map.table)
{
while (entry)
{
if (equals(v, entry.value)) return true;
entry = entry.next;
}
}
return false;
}
// --- private methods
fn void MapImpl._add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map._resize(map.table.len * 2);
}
}
fn void MapImpl._resize(&map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
if (old_capacity == MAXIMUM_CAPACITY)
{
map.threshold = uint.max;
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map._transfer(new_table);
map.table = new_table;
map._free_internal(old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
fn uint rehash(uint hash) @inline @private
{
hash ^= (hash >> 20) ^ (hash >> 12);
return hash ^ ((hash >> 7) ^ (hash >> 4));
}
macro uint index_for(uint hash, uint capacity) @private
{
return hash & (capacity - 1);
}
fn void MapImpl._transfer(&map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
foreach (uint j, Entry *e : src)
{
if (!e) continue;
do
{
Entry* next = e.next;
uint i = index_for(e.hash, new_capacity);
e.next = new_table[i];
new_table[i] = e;
e = next;
}
while (e);
}
}
fn void _init(MapImpl* impl, uint capacity, float load_factor, Allocator allocator) @private
{
capacity = math::next_power_of_2(capacity);
*impl = {
.allocator = allocator,
.load_factor = load_factor,
.threshold = (uint)(capacity * load_factor),
.table = allocator::new_array(allocator, Entry*, capacity)
};
}
fn void MapImpl._put_for_create(&map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
for (Entry *e = map.table[i]; e != null; e = e.next)
{
if (e.hash == hash && equals(key, e.key))
{
e.value = value;
return;
}
}
map._create_entry(hash, key, value, i);
}
fn void MapImpl._free_internal(&map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool MapImpl._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);
Entry* prev = map.table[i];
Entry* e = prev;
while (e)
{
Entry *next = e.next;
if (e.hash == hash && equals(key, e.key))
{
map.count--;
if (prev == e)
{
map.table[i] = next;
}
else
{
prev.next = next;
}
map._free_entry(e);
return true;
}
prev = e;
e = next;
}
return false;
}
fn void MapImpl._create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
$endif
Entry* entry = allocator::new(map.allocator, Entry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void MapImpl._free_entry(&self, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
$endif
self._free_internal(entry);
}
struct Entry
{
uint hash;
Key key;
Value value;
Entry* next;
}

View File

@@ -1,4 +1,4 @@
module std::collections::maybe(<Type>);
module std::collections::maybe{Type};
import std::io;
struct Maybe (Printable)
@@ -7,7 +7,7 @@ struct Maybe (Printable)
bool has_value;
}
fn usz! Maybe.to_format(&self, Formatter* f) @dynamic
fn usz? Maybe.to_format(&self, Formatter* f) @dynamic
{
if (self.has_value) return f.printf("[%s]", self.value);
return f.printf("[EMPTY]");
@@ -28,19 +28,9 @@ fn Maybe value(Type val)
return { .value = val, .has_value = true };
}
fn Maybe Maybe.with_value(Type val) @deprecated("Use maybe::value instead.") @operator(construct)
{
return { .value = val, .has_value = true };
}
fn Maybe Maybe.empty() @deprecated("Use maybe::EMPTY instead.") @operator(construct)
{
return { };
}
const Maybe EMPTY = { };
macro Type! Maybe.get(self)
macro Type? Maybe.get(self)
{
return self.has_value ? self.value : SearchResult.MISSING?;
return self.has_value ? self.value : NOT_FOUND?;
}

View File

@@ -25,7 +25,7 @@ struct Object (Printable)
}
fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
fn usz? Object.to_format(&self, Formatter* formatter) @dynamic
{
switch (self.type)
{
@@ -50,7 +50,7 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
usz n = formatter.printf("{")!;
@stack_mem(1024; Allocator mem)
{
foreach (i, key : self.map.copy_keys(mem))
foreach (i, key : self.map.keys(mem))
{
if (i > 0) n += formatter.printf(",")!;
n += formatter.printf(`"%s":`, key)!;
@@ -178,7 +178,7 @@ fn void Object.init_array_if_needed(&self) @private
fn void Object.set_object(&self, String key, Object* new_object) @private
{
self.init_map_if_needed();
ObjectInternalMapEntry*! entry = self.map.get_entry(key);
ObjectInternalMapEntry*? entry = self.map.get_entry(key);
defer
{
(void)entry.value.free();
@@ -189,7 +189,7 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
macro Object* Object.object_from_value(&self, value) @private
{
var $Type = $typeof(value);
$switch
$switch:
$case types::is_int($Type):
return new_int(value, self.allocator);
$case types::is_float($Type):
@@ -201,7 +201,7 @@ macro Object* Object.object_from_value(&self, value) @private
$case $Type.typeid == Object*.typeid:
return value;
$case $Type.typeid == void*.typeid:
if (value != null) return CastResult.TYPE_MISMATCH?;
if (value != null) return TYPE_MISMATCH?;
return &NULL_OBJECT;
$case $assignable(value, String):
return new_string(value, self.allocator);
@@ -242,7 +242,7 @@ macro Object* Object.push(&self, value)
<*
@require self.is_keyable()
*>
fn Object*! Object.get(&self, String key) => self.is_empty() ? SearchResult.MISSING? : self.map.get(key);
fn Object*? Object.get(&self, String key) => self.is_empty() ? NOT_FOUND? : self.map.get(key);
fn bool Object.has_key(&self, String key) => self.is_map() && self.map.has_key(key);
@@ -292,7 +292,7 @@ fn void Object.set_object_at(&self, usz index, Object* to_set)
}
<*
@require $Type.kindof.is_int() "Expected an integer type."
@require $Type.kindof.is_int() : "Expected an integer type."
*>
macro get_integer_value(Object* value, $Type)
{
@@ -308,7 +308,7 @@ macro get_integer_value(Object* value, $Type)
return ($Type)value.s.to_uint128();
$endif
}
if (!value.is_int()) return NumberConversion.MALFORMED_INTEGER?;
if (!value.is_int()) return string::MALFORMED_INTEGER?;
return ($Type)value.i;
}
@@ -331,77 +331,77 @@ macro Object.get_integer(&self, $Type, String key) @private
return get_integer_value(self.get(key), $Type);
}
fn ichar! Object.get_ichar(&self, String key) => self.get_integer(ichar, key);
fn short! Object.get_short(&self, String key) => self.get_integer(short, key);
fn int! Object.get_int(&self, String key) => self.get_integer(int, key);
fn long! Object.get_long(&self, String key) => self.get_integer(long, key);
fn int128! Object.get_int128(&self, String key) => self.get_integer(int128, key);
fn ichar? Object.get_ichar(&self, String key) => self.get_integer(ichar, key);
fn short? Object.get_short(&self, String key) => self.get_integer(short, key);
fn int? Object.get_int(&self, String key) => self.get_integer(int, key);
fn long? Object.get_long(&self, String key) => self.get_integer(long, key);
fn int128? Object.get_int128(&self, String key) => self.get_integer(int128, key);
fn ichar! Object.get_ichar_at(&self, usz index) => self.get_integer_at(ichar, index);
fn short! Object.get_short_at(&self, usz index) => self.get_integer_at(short, index);
fn int! Object.get_int_at(&self, usz index) => self.get_integer_at(int, index);
fn long! Object.get_long_at(&self, usz index) => self.get_integer_at(long, index);
fn int128! Object.get_int128_at(&self, usz index) => self.get_integer_at(int128, index);
fn ichar? Object.get_ichar_at(&self, usz index) => self.get_integer_at(ichar, index);
fn short? Object.get_short_at(&self, usz index) => self.get_integer_at(short, index);
fn int? Object.get_int_at(&self, usz index) => self.get_integer_at(int, index);
fn long? Object.get_long_at(&self, usz index) => self.get_integer_at(long, index);
fn int128? Object.get_int128_at(&self, usz index) => self.get_integer_at(int128, index);
fn char! Object.get_char(&self, String key) => self.get_integer(ichar, key);
fn short! Object.get_ushort(&self, String key) => self.get_integer(ushort, key);
fn uint! Object.get_uint(&self, String key) => self.get_integer(uint, key);
fn ulong! Object.get_ulong(&self, String key) => self.get_integer(ulong, key);
fn uint128! Object.get_uint128(&self, String key) => self.get_integer(uint128, key);
fn char? Object.get_char(&self, String key) => self.get_integer(ichar, key);
fn short? Object.get_ushort(&self, String key) => self.get_integer(ushort, key);
fn uint? Object.get_uint(&self, String key) => self.get_integer(uint, key);
fn ulong? Object.get_ulong(&self, String key) => self.get_integer(ulong, key);
fn uint128? Object.get_uint128(&self, String key) => self.get_integer(uint128, key);
fn char! Object.get_char_at(&self, usz index) => self.get_integer_at(char, index);
fn ushort! Object.get_ushort_at(&self, usz index) => self.get_integer_at(ushort, index);
fn uint! Object.get_uint_at(&self, usz index) => self.get_integer_at(uint, index);
fn ulong! Object.get_ulong_at(&self, usz index) => self.get_integer_at(ulong, index);
fn uint128! Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint128, index);
fn char? Object.get_char_at(&self, usz index) => self.get_integer_at(char, index);
fn ushort? Object.get_ushort_at(&self, usz index) => self.get_integer_at(ushort, index);
fn uint? Object.get_uint_at(&self, usz index) => self.get_integer_at(uint, index);
fn ulong? Object.get_ulong_at(&self, usz index) => self.get_integer_at(ulong, index);
fn uint128? Object.get_uint128_at(&self, usz index) => self.get_integer_at(uint128, index);
<*
@require self.is_keyable()
*>
fn String! Object.get_string(&self, String key)
fn String? Object.get_string(&self, String key)
{
Object* value = self.get(key)!;
if (!value.is_string()) return CastResult.TYPE_MISMATCH?;
if (!value.is_string()) return TYPE_MISMATCH?;
return value.s;
}
<*
@require self.is_indexable()
*>
fn String! Object.get_string_at(&self, usz index)
fn String? Object.get_string_at(&self, usz index)
{
Object* value = self.get_at(index);
if (!value.is_string()) return CastResult.TYPE_MISMATCH?;
if (!value.is_string()) return TYPE_MISMATCH?;
return value.s;
}
<*
@require self.is_keyable()
*>
macro String! Object.get_enum(&self, $EnumType, String key)
macro String? Object.get_enum(&self, $EnumType, String key)
{
Object value = self.get(key)!;
if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?;
if ($EnumType.typeid != value.type) return TYPE_MISMATCH?;
return ($EnumType)value.i;
}
<*
@require self.is_indexable()
*>
macro String! Object.get_enum_at(&self, $EnumType, usz index)
macro String? Object.get_enum_at(&self, $EnumType, usz index)
{
Object value = self.get_at(index);
if ($EnumType.typeid != value.type) return CastResult.TYPE_MISMATCH?;
if ($EnumType.typeid != value.type) return TYPE_MISMATCH?;
return ($EnumType)value.i;
}
<*
@require self.is_keyable()
*>
fn bool! Object.get_bool(&self, String key)
fn bool? Object.get_bool(&self, String key)
{
Object* value = self.get(key)!;
if (!value.is_bool()) return CastResult.TYPE_MISMATCH?;
if (!value.is_bool()) return TYPE_MISMATCH?;
return value.b;
}
@@ -409,17 +409,17 @@ fn bool! Object.get_bool(&self, String key)
<*
@require self.is_indexable()
*>
fn bool! Object.get_bool_at(&self, usz index)
fn bool? Object.get_bool_at(&self, usz index)
{
Object* value = self.get_at(index);
if (!value.is_bool()) return CastResult.TYPE_MISMATCH?;
if (!value.is_bool()) return TYPE_MISMATCH?;
return value.b;
}
<*
@require self.is_keyable()
*>
fn double! Object.get_float(&self, String key)
fn double? Object.get_float(&self, String key)
{
Object* value = self.get(key)!;
switch (value.type.kindof)
@@ -431,14 +431,14 @@ fn double! Object.get_float(&self, String key)
case FLOAT:
return value.f;
default:
return CastResult.TYPE_MISMATCH?;
return TYPE_MISMATCH?;
}
}
<*
@require self.is_indexable()
*>
fn double! Object.get_float_at(&self, usz index)
fn double? Object.get_float_at(&self, usz index)
{
Object* value = self.get_at(index);
switch (value.type.kindof)
@@ -450,7 +450,7 @@ fn double! Object.get_float_at(&self, usz index)
case FLOAT:
return value.f;
default:
return CastResult.TYPE_MISMATCH?;
return TYPE_MISMATCH?;
}
}
@@ -462,7 +462,7 @@ fn Object* Object.get_or_create_obj(&self, String key)
return container;
}
def ObjectInternalMap = HashMap(<String, Object*>) @private;
def ObjectInternalList = List(<Object*>) @private;
def ObjectInternalMapEntry = Entry(<String, Object*>) @private;
alias ObjectInternalMap @private = HashMap {String, Object*};
alias ObjectInternalList @private = List {Object*};
alias ObjectInternalMapEntry @private = Entry {String, Object*};

View File

@@ -1,7 +1,7 @@
// priorityqueue.c3
// A priority queue using a classic binary heap for C3.
//
// Copyright (c) 2022 David Kopec
// Copyright (c) 2022-2025 David Kopec
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -20,35 +20,30 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
module std::collections::priorityqueue(<Type>);
module std::collections::priorityqueue{Type};
import std::collections::priorityqueue::private;
distinct PriorityQueue = inline PrivatePriorityQueue(<Type, false>);
distinct PriorityQueueMax = inline PrivatePriorityQueue(<Type, true>);
typedef PriorityQueue = inline PrivatePriorityQueue{Type, false};
typedef PriorityQueueMax = inline PrivatePriorityQueue{Type, true};
module std::collections::priorityqueue::private(<Type, MAX>);
module std::collections::priorityqueue::private{Type, MAX};
import std::collections::list, std::io;
def Heap = List(<Type>);
struct PrivatePriorityQueue (Printable)
{
Heap heap;
List{Type} heap;
}
fn void PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline
fn PrivatePriorityQueue* PrivatePriorityQueue.init(&self, Allocator allocator, usz initial_capacity = 16, ) @inline
{
self.heap.init(allocator, initial_capacity);
return self;
}
fn void PrivatePriorityQueue.new_init(&self, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @inline
fn PrivatePriorityQueue* PrivatePriorityQueue.tinit(&self, usz initial_capacity = 16) @inline
{
self.heap.init(allocator, initial_capacity);
}
fn void PrivatePriorityQueue.temp_init(&self, usz initial_capacity = 16) @inline
{
self.heap.init(allocator::temp(), initial_capacity) @inline;
self.init(tmem, initial_capacity);
return self;
}
@@ -72,6 +67,9 @@ fn void PrivatePriorityQueue.push(&self, Type element)
}
}
<*
@require index < self.len() : "Index out of range"
*>
fn void PrivatePriorityQueue.remove_at(&self, usz index)
{
if (index == 0)
@@ -84,11 +82,11 @@ fn void PrivatePriorityQueue.remove_at(&self, usz index)
<*
@require self != null
*>
fn Type! PrivatePriorityQueue.pop(&self)
fn Type? PrivatePriorityQueue.pop(&self)
{
usz i = 0;
usz len = self.heap.len();
if (!len) return IteratorResult.NO_MORE_ELEMENT?;
if (!len) return NO_MORE_ELEMENT?;
usz new_count = len - 1;
self.heap.swap(0, new_count);
while OUTER: ((2 * i + 1) < new_count)
@@ -122,10 +120,9 @@ fn Type! PrivatePriorityQueue.pop(&self)
return self.heap.pop();
}
fn Type! PrivatePriorityQueue.first(&self)
fn Type? PrivatePriorityQueue.first(&self)
{
if (!self.len()) return IteratorResult.NO_MORE_ELEMENT?;
return self.heap.get(0);
return self.heap.first();
}
fn void PrivatePriorityQueue.free(&self)
@@ -135,12 +132,12 @@ fn void PrivatePriorityQueue.free(&self)
fn usz PrivatePriorityQueue.len(&self) @operator(len)
{
return self.heap.len();
return self.heap.len() @inline;
}
fn bool PrivatePriorityQueue.is_empty(&self)
{
return self.heap.is_empty();
return self.heap.is_empty() @inline;
}
<*
@@ -151,13 +148,8 @@ fn Type PrivatePriorityQueue.get(&self, usz index) @operator([])
return self.heap[index];
}
fn usz! PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
fn usz? PrivatePriorityQueue.to_format(&self, Formatter* formatter) @dynamic
{
return self.heap.to_format(formatter);
}
fn String PrivatePriorityQueue.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.heap.to_new_string(allocator);
}

View File

@@ -1,7 +1,7 @@
<*
@require Type.is_ordered : "The type must be ordered"
*>
module std::collections::range(<Type>);
module std::collections::range{Type};
import std::io;
struct Range (Printable)
@@ -29,22 +29,7 @@ fn Type Range.get(&self, usz index) @operator([])
return (Type)(self.start + (usz)index);
}
fn String Range.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic @deprecated
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..%s]", self.start, self.end, allocator: allocator);
}
fn String Range.to_tstring(&self)
{
return self.to_string(allocator::temp());
}
fn usz! Range.to_format(&self, Formatter* formatter) @dynamic
fn usz? Range.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.printf("[%s..%s]", self.start, self.end)!;
}
@@ -66,26 +51,11 @@ fn bool ExclusiveRange.contains(&self, Type value) @inline
return value >= self.start && value < self.end;
}
fn usz! ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
fn usz? ExclusiveRange.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.printf("[%s..<%s]", self.start, self.end)!;
}
fn String ExclusiveRange.to_new_string(&self, Allocator allocator = null) @dynamic
{
return self.to_string(allocator ?: allocator::heap());
}
fn String ExclusiveRange.to_string(&self, Allocator allocator) @dynamic
{
return string::format("[%s..<%s]", self.start, self.end, allocator: allocator);
}
fn String ExclusiveRange.to_tstring(&self)
{
return self.to_new_string(allocator::temp());
}
<*
@require index < self.len() : "Can't index into an empty range"
*>

View File

@@ -1,11 +1,14 @@
<*
@require values::@is_int(SIZE) &&& SIZE > 0 "The size must be positive integer"
@require Type.kindof == ARRAY : "Required an array type"
*>
module std::collections::ringbuffer(<Type, SIZE>);
module std::collections::ringbuffer{Type};
import std::io;
struct RingBuffer
alias Element = $typeof((Type){}[0]);
struct RingBuffer (Printable)
{
Type[SIZE] buf;
Type buf;
usz written;
usz head;
}
@@ -15,9 +18,9 @@ fn void RingBuffer.init(&self) @inline
*self = {};
}
fn void RingBuffer.push(&self, Type c)
fn void RingBuffer.push(&self, Element c)
{
if (self.written < SIZE)
if (self.written < self.buf.len)
{
self.buf[self.written] = c;
self.written++;
@@ -25,14 +28,14 @@ fn void RingBuffer.push(&self, Type c)
else
{
self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE;
self.head = (self.head + 1) % self.buf.len;
}
}
fn Type RingBuffer.get(&self, usz index) @operator([])
fn Element RingBuffer.get(&self, usz index) @operator([])
{
index %= SIZE;
usz avail = SIZE - self.head;
index %= self.buf.len;
usz avail = self.buf.len - self.head;
if (index < avail)
{
return self.buf[self.head + index];
@@ -40,25 +43,31 @@ fn Type RingBuffer.get(&self, usz index) @operator([])
return self.buf[index - avail];
}
fn Type! RingBuffer.pop(&self)
fn Element? RingBuffer.pop(&self)
{
switch
{
case self.written == 0:
return SearchResult.MISSING?;
case self.written < SIZE:
return NO_MORE_ELEMENT?;
case self.written < self.buf.len:
self.written--;
return self.buf[self.written];
default:
self.head = (self.head - 1) % SIZE;
self.head = (self.head - 1) % self.buf.len;
return self.buf[self.head];
}
}
fn usz RingBuffer.read(&self, usz index, Type[] buffer)
fn usz? RingBuffer.to_format(&self, Formatter* format) @dynamic
{
index %= SIZE;
if (self.written < SIZE)
// Improve this?
return format.printf("%s", self.buf);
}
fn usz RingBuffer.read(&self, usz index, Element[] buffer)
{
index %= self.buf.len;
if (self.written < self.buf.len)
{
if (index >= self.written) return 0;
usz end = self.written - index;
@@ -66,7 +75,7 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
buffer[:n] = self.buf[index:n];
return n;
}
usz end = SIZE - self.head;
usz end = self.buf.len - self.head;
if (index >= end)
{
index -= end;
@@ -75,13 +84,13 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
buffer[:n] = self.buf[index:n];
return n;
}
if (buffer.len <= SIZE - index)
if (buffer.len <= self.buf.len - index)
{
usz n = buffer.len;
buffer[:n] = self.buf[self.head + index:n];
return n;
}
usz n1 = SIZE - index;
usz n1 = self.buf.len - index;
buffer[:n1] = self.buf[self.head + index:n1];
buffer = buffer[n1..];
index -= n1;
@@ -90,10 +99,10 @@ fn usz RingBuffer.read(&self, usz index, Type[] buffer)
return n1 + n2;
}
fn void RingBuffer.write(&self, Type[] buffer)
fn void RingBuffer.write(&self, Element[] buffer)
{
usz i;
while (self.written < SIZE && i < buffer.len)
while (self.written < self.buf.len && i < buffer.len)
{
self.buf[self.written] = buffer[i++];
self.written++;
@@ -101,6 +110,6 @@ fn void RingBuffer.write(&self, Type[] buffer)
foreach (c : buffer[i..])
{
self.buf[self.head] = c;
self.head = (self.head + 1) % SIZE;
self.head = (self.head + 1) % self.buf.len;
}
}

View File

@@ -1,4 +1,4 @@
module std::collections::tuple(<Type1, Type2>);
module std::collections::tuple{Type1, Type2};
struct Tuple
{
@@ -6,7 +6,7 @@ struct Tuple
Type2 second;
}
module std::collections::triple(<Type1, Type2, Type3>);
module std::collections::triple{Type1, Type2, Type3};
struct Triple
{

View File

@@ -37,19 +37,11 @@ struct QOIDesc
QOIChannels channels;
QOIColorspace colorspace;
}
<*
QOI Errors.
These are all the possible bad outcomes.
*>
fault QOIError
{
INVALID_PARAMETERS,
FILE_OPEN_FAILED,
FILE_WRITE_FAILED,
INVALID_DATA,
TOO_MANY_PIXELS
}
faultdef INVALID_PARAMETERS, FILE_OPEN_FAILED, FILE_WRITE_FAILED, INVALID_DATA, TOO_MANY_PIXELS;
// Let the user decide if they want to use std::io
@@ -67,25 +59,17 @@ import std::io;
The function returns an optional, which can either be a QOIError
or the number of bytes written on success.
@param [in] filename `The file's name to write the image to`
@param [in] input `The raw RGB or RGBA pixels to encode`
@param [&in] desc `The descriptor of the image`
@param [in] filename : `The file's name to write the image to`
@param [in] input : `The raw RGB or RGBA pixels to encode`
@param [&in] desc : `The descriptor of the image`
*>
fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
fn usz? write(String filename, char[] input, QOIDesc* desc) => @pool()
{
// encode data
char[] output = new_encode(input, desc, allocator: allocator::temp())!;
char[] output = encode(tmem, input, desc)!;
// open file
File! f = file::open(filename, "wb");
if (catch f) return QOIError.FILE_OPEN_FAILED?;
// write data to file and close it
usz! written = f.write(output);
if (catch written) return QOIError.FILE_WRITE_FAILED?;
if (catch f.close()) return QOIError.FILE_WRITE_FAILED?;
return written;
file::save(filename, output)!;
return output.len;
}
@@ -106,33 +90,25 @@ fn usz! write(String filename, char[] input, QOIDesc* desc) => @pool()
The returned pixel data should be free()d after use, or the decoding
and use of the data should be wrapped in a @pool() { ... }; block.
@param [in] filename `The file's name to read the image from`
@param [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used`
@param [in] filename : `The file's name to read the image from`
@param [&out] desc : `The descriptor to fill with the image's info`
@param channels : `The channels to be used`
@return? FILE_OPEN_FAILED, INVALID_DATA, TOO_MANY_PIXELS
*>
fn char[]! new_read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) => @pool(allocator)
fn char[]? read(Allocator allocator, String filename, QOIDesc* desc, QOIChannels channels = AUTO) => @pool()
{
// read file
char[] data = file::load_temp(filename) ?? QOIError.FILE_OPEN_FAILED?!;
char[] data = file::load_temp(filename) ?? FILE_OPEN_FAILED?!;
// pass data to decode function
return new_decode(data, desc, channels, allocator);
return decode(allocator, data, desc, channels);
}
fn char[]! read(String filename, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @deprecated("Use new_read")
{
return new_read(filename, desc, channels, allocator);
}
// Back to basic non-stdio mode
module std::compression::qoi;
import std::bits;
fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @deprecated("use encode_new")
{
return new_encode(input, desc, allocator);
}
<*
Encode raw RGB or RGBA pixels into a QOI image in memory.
@@ -143,20 +119,21 @@ fn char[]! encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::
and use of the data should be wrapped in a @pool() { ... }; block.
See the write() function for an example.
@param [in] input `The raw RGB or RGBA pixels to encode`
@param [&in] desc `The descriptor of the image`
@param [in] input : `The raw RGB or RGBA pixels to encode`
@param [&in] desc : `The descriptor of the image`
@return? INVALID_PARAMETERS, TOO_MANY_PIXELS, INVALID_DATA
*>
fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocator::heap()) @nodiscard
fn char[]? encode(Allocator allocator, char[] input, QOIDesc* desc) @nodiscard
{
// check info in desc
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_PARAMETERS?;
if (desc.channels == AUTO) return QOIError.INVALID_PARAMETERS?;
if (desc.width == 0 || desc.height == 0) return INVALID_PARAMETERS?;
if (desc.channels == AUTO) return INVALID_PARAMETERS?;
uint pixels = desc.width * desc.height;
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
// check input data size
uint image_size = pixels * desc.channels.id;
if (image_size != input.len) return QOIError.INVALID_DATA?;
if (image_size != input.len) return INVALID_DATA?;
// allocate memory for encoded data (output)
// header + chunk tag and RGB(A) data for each pixel + end of stream
@@ -278,10 +255,6 @@ fn char[]! new_encode(char[] input, QOIDesc* desc, Allocator allocator = allocat
}
fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap())
{
return new_decode(data, desc, channels, allocator);
}
<*
Decode a QOI image from memory.
@@ -300,34 +273,35 @@ fn char[]! decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Alloc
The returned pixel data should be free()d after use, or the decoding
and use of the data should be wrapped in a @pool() { ... }; block.
@param [in] data `The QOI image data to decode`
@param [&out] desc `The descriptor to fill with the image's info`
@param channels `The channels to be used`
@param [in] data : `The QOI image data to decode`
@param [&out] desc : `The descriptor to fill with the image's info`
@param channels : `The channels to be used`
@return? INVALID_DATA, TOO_MANY_PIXELS
*>
fn char[]! new_decode(char[] data, QOIDesc* desc, QOIChannels channels = AUTO, Allocator allocator = allocator::heap()) @nodiscard
fn char[]? decode(Allocator allocator, char[] data, QOIDesc* desc, QOIChannels channels = AUTO) @nodiscard
{
// check input data
if (data.len < Header.sizeof + END_OF_STREAM.len) return QOIError.INVALID_DATA?;
if (data.len < Header.sizeof + END_OF_STREAM.len) return INVALID_DATA?;
// get header
Header* header = (Header*)data.ptr;
// check magic bytes (FourCC)
if (bswap(header.be_magic) != 'qoif') return QOIError.INVALID_DATA?;
if (bswap(header.be_magic) != 'qoif') return INVALID_DATA?;
// copy header data to desc
desc.width = bswap(header.be_width);
desc.height = bswap(header.be_height);
desc.channels = @enumcast(QOIChannels, header.channels)!; // Rethrow if invalid
desc.colorspace = @enumcast(QOIColorspace, header.colorspace)!; // Rethrow if invalid
if (desc.channels == AUTO) return QOIError.INVALID_DATA?; // Channels must be specified in the header
if (desc.channels == AUTO) return INVALID_DATA?; // Channels must be specified in the header
// check width and height
if (desc.width == 0 || desc.height == 0) return QOIError.INVALID_DATA?;
if (desc.width == 0 || desc.height == 0) return INVALID_DATA?;
// check pixel count
ulong pixels = (ulong)desc.width * (ulong)desc.height;
if (pixels > PIXELS_MAX) return QOIError.TOO_MANY_PIXELS?;
if (pixels > PIXELS_MAX) return TOO_MANY_PIXELS?;
uint pos = Header.sizeof; // Current position in data
uint loc; // Current position in image (top-left corner)
@@ -426,19 +400,23 @@ struct Header @packed
char colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
}
const char[?] END_OF_STREAM = {0, 0, 0, 0, 0, 0, 0, 1};
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 QOIError.INVALID_DATA?;
return INVALID_DATA?;
}
distinct Pixel = inline char[<4>];
typedef Pixel = inline char[<4>];
macro char Pixel.hash(Pixel p)
{
return (p.r * 3 + p.g * 5 + p.b * 7 + p.a * 11) % 64;

View File

@@ -28,7 +28,7 @@ fn void ArenaAllocator.clear(&self)
struct ArenaAllocatorHeader @local
{
usz size;
char[?] data;
char[*] data;
}
macro ArenaAllocator* wrap(char[] bytes)
@@ -50,24 +50,25 @@ fn void ArenaAllocator.release(&self, void* ptr, bool) @dynamic
}
}
fn usz ArenaAllocator.mark(&self) @dynamic => self.used;
fn void ArenaAllocator.reset(&self, usz mark) @dynamic => self.used = mark;
fn usz ArenaAllocator.mark(&self) => self.used;
fn void ArenaAllocator.reset(&self, usz mark) => self.used = mark;
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
void* start_mem = self.data.ptr;
void* unaligned_pointer_to_offset = start_mem + self.used + ArenaAllocatorHeader.sizeof;
void* mem = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
usz end = (usz)(mem - self.data.ptr) + size;
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY?;
if (end > total_len) return mem::OUT_OF_MEMORY?;
self.used = end;
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
header.size = size;
@@ -77,16 +78,17 @@ fn void*! ArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz a
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require old_pointer != null
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
fn void*? ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
assert(old_pointer >= self.data.ptr, "Pointer originates from a different allocator.");
usz total_len = self.data.len;
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE?;
if (size > total_len) return mem::INVALID_ALLOC_SIZE?;
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
usz old_size = header.size;
// Do last allocation and alignment match?
@@ -99,7 +101,7 @@ fn void*! ArenaAllocator.resize(&self, void *old_pointer, usz size, usz alignmen
else
{
usz new_used = self.used + size - old_size;
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY?;
if (new_used > total_len) return mem::OUT_OF_MEMORY?;
self.used = new_used;
}
header.size = size;

View File

@@ -1,43 +1,42 @@
module std::core::mem::allocator;
import std::io, std::math;
struct TempAllocatorChunk @local
struct AllocChunk @local
{
usz size;
char[?] data;
char[*] data;
}
struct TempAllocator (Allocator)
struct BackedArenaAllocator (Allocator)
{
Allocator backing_allocator;
TempAllocatorPage* last_page;
ExtraPage* last_page;
usz used;
usz capacity;
char[?] data;
char[*] data;
}
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
const usz PAGE_IS_ALIGNED @local = (usz)isz.max + 1u;
struct TempAllocatorPage
struct ExtraPage @local
{
TempAllocatorPage* prev_page;
ExtraPage* prev_page;
void* start;
usz mark;
usz size;
usz ident;
char[?] data;
char[*] data;
}
macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
macro usz ExtraPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
macro bool ExtraPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED;
<*
@require size >= 16
*>
fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
fn BackedArenaAllocator*? new_backed_allocator(usz size, Allocator allocator)
{
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
BackedArenaAllocator* temp = allocator::alloc_with_padding(allocator, BackedArenaAllocator, size)!;
temp.last_page = null;
temp.backing_allocator = allocator;
temp.used = 0;
@@ -45,16 +44,16 @@ fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
return temp;
}
fn void TempAllocator.destroy(&self)
fn void BackedArenaAllocator.destroy(&self)
{
self.reset(0);
if (self.last_page) (void)self._free_page(self.last_page);
allocator::free(self.backing_allocator, self);
}
fn usz TempAllocator.mark(&self) @dynamic => self.used;
fn usz BackedArenaAllocator.mark(&self) => self.used;
fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
fn void BackedArenaAllocator.release(&self, void* old_pointer, bool) @dynamic
{
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
if (old_pointer + old_size == &self.data[self.used])
@@ -63,13 +62,13 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
asan::poison_memory_region(&self.data[self.used], old_size);
}
}
fn void TempAllocator.reset(&self, usz mark) @dynamic
fn void BackedArenaAllocator.reset(&self, usz mark)
{
TempAllocatorPage *last_page = self.last_page;
ExtraPage *last_page = self.last_page;
while (last_page && last_page.mark > mark)
{
self.used = last_page.mark;
TempAllocatorPage *to_free = last_page;
ExtraPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
}
@@ -90,19 +89,19 @@ fn void TempAllocator.reset(&self, usz mark) @dynamic
self.used = mark;
}
fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
fn void? BackedArenaAllocator._free_page(&self, ExtraPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
fn void*? BackedArenaAllocator._realloc_page(&self, ExtraPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
// Walk backwards to find the pointer to this page.
TempAllocatorPage **pointer_to_prev = &self.last_page;
ExtraPage **pointer_to_prev = &self.last_page;
// Remove the page from the list
while (*pointer_to_prev != page)
{
@@ -117,18 +116,18 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
AllocChunk *chunk = pointer - AllocChunk.sizeof;
if (chunk.size == (usz)-1)
{
assert(self.last_page, "Realloc of non temp pointer");
assert(self.last_page, "Realloc of unrelated pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
ExtraPage *page = pointer - ExtraPage.sizeof;
return self._realloc_page(page, size, alignment);
}
TempAllocatorChunk* data = self.acquire(size, NO_ZERO, alignment)!;
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
return data;
@@ -137,16 +136,16 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
<*
@require size > 0
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
*>
fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? BackedArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
void* starting_ptr = start_mem + self.used;
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof);
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
if (alignment > TempAllocatorChunk.alignof)
void* aligned_header_start = mem::aligned_pointer(starting_ptr, AllocChunk.alignof);
void* mem = aligned_header_start + AllocChunk.sizeof;
if (alignment > AllocChunk.alignof)
{
mem = mem::aligned_pointer(mem, alignment);
}
@@ -156,7 +155,7 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
if (new_usage <= self.capacity)
{
asan::unpoison_memory_region(starting_ptr, new_usage - self.used);
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
AllocChunk* chunk_start = mem - AllocChunk.sizeof;
chunk_start.size = size;
self.used = new_usage;
if (init_type == ZERO) mem::clear(mem, size, mem::DEFAULT_MEM_ALIGNMENT);
@@ -164,13 +163,13 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
}
// Fallback to backing allocator
TempAllocatorPage* page;
ExtraPage* page;
// We have something we need to align.
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
// This is actually simpler, since it will create the offset for us.
usz total_alloc_size = mem::aligned_offset(TempAllocatorPage.sizeof + size, alignment);
usz total_alloc_size = mem::aligned_offset(ExtraPage.sizeof + size, alignment);
if (init_type == ZERO)
{
mem = allocator::calloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
@@ -180,21 +179,21 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
mem = allocator::malloc_aligned(self.backing_allocator, total_alloc_size, alignment)!;
}
void* start = mem;
mem += mem::aligned_offset(TempAllocatorPage.sizeof, alignment);
page = (TempAllocatorPage*)mem - 1;
mem += mem::aligned_offset(ExtraPage.sizeof, alignment);
page = (ExtraPage*)mem - 1;
page.start = start;
page.size = size | PAGE_IS_ALIGNED;
}
else
{
// Here we might need to pad
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz padded_header_size = mem::aligned_offset(ExtraPage.sizeof, mem::DEFAULT_MEM_ALIGNMENT);
usz total_alloc_size = padded_header_size + size;
void* alloc = self.backing_allocator.acquire(total_alloc_size, init_type, 0)!;
// Find the page.
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
assert(mem::ptr_is_aligned(page, TempAllocator.alignof));
page = alloc + padded_header_size - ExtraPage.sizeof;
assert(mem::ptr_is_aligned(page, BackedArenaAllocator.alignof));
assert(mem::ptr_is_aligned(&page.data[0], mem::DEFAULT_MEM_ALIGNMENT));
page.start = alloc;
page.size = size;
@@ -209,22 +208,3 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
self.last_page = page;
return &page.data[0];
}
fn void! TempAllocator.print_pages(&self, File* f)
{
TempAllocatorPage *last_page = self.last_page;
if (!last_page)
{
io::fprintf(f, "No pages.\n")!;
return;
}
io::fprintf(f, "---Pages----\n")!;
uint index = 0;
while (last_page)
{
bool is_not_aligned = !(last_page.size & (1u64 << 63));
io::fprintf(f, "%d. Alloc: %d %d at %p%s\n", ++index,
last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]")!;
last_page = last_page.prev_page;
}
}

View File

@@ -16,7 +16,7 @@ struct DynamicArenaAllocator (Allocator)
@param [&inout] allocator
@require page_size >= 128
*>
fn void DynamicArenaAllocator.init(&self, usz page_size, Allocator allocator)
fn void DynamicArenaAllocator.init(&self, Allocator allocator, usz page_size)
{
self.page = null;
self.unused_page = null;
@@ -62,7 +62,7 @@ struct DynamicArenaChunk @local
<*
@require ptr != null
@require self.page != null `tried to free pointer on invalid allocator`
@require self.page != null : `tried to free pointer on invalid allocator`
*>
fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
{
@@ -75,11 +75,12 @@ fn void DynamicArenaAllocator.release(&self, void* ptr, bool) @dynamic
}
<*
@require size > 0 `Resize doesn't support zeroing`
@require old_pointer != null `Resize doesn't handle null pointers`
@require self.page != null `tried to realloc pointer on invalid allocator`
@require size > 0 : `Resize doesn't support zeroing`
@require old_pointer != null : `Resize doesn't handle null pointers`
@require self.page != null : `tried to realloc pointer on invalid allocator`
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
fn void*? DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
DynamicArenaPage* current_page = self.page;
alignment = alignment_for_allocation(alignment);
@@ -109,9 +110,8 @@ fn void*! DynamicArenaAllocator.resize(&self, void* old_pointer, usz size, usz a
return new_mem;
}
fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
fn void DynamicArenaAllocator.reset(&self)
{
assert(mark == 0, "Unexpectedly reset dynamic arena allocator with mark %d", mark);
DynamicArenaPage* page = self.page;
DynamicArenaPage** unused_page_ptr = &self.unused_page;
while (page)
@@ -129,15 +129,16 @@ fn void DynamicArenaAllocator.reset(&self, usz mark = 0) @dynamic
<*
@require math::is_power_of_2(alignment)
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
assert(page_size > size + DynamicArenaChunk.sizeof);
// Grab the page without alignment (we do it ourselves)
void* mem = allocator::malloc_try(self.backing_allocator, page_size)!;
DynamicArenaPage*! page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
DynamicArenaPage*? page = allocator::new_try(self.backing_allocator, DynamicArenaPage);
if (catch err = page)
{
allocator::free(self.backing_allocator, mem);
@@ -157,10 +158,11 @@ fn void*! DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
}
<*
@require size > 0 `acquire expects size > 0`
@require size > 0 : `acquire expects size > 0`
@require !alignment || math::is_power_of_2(alignment)
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2021-2024 Christoffer Lerno. All rights reserved.
// Copyright (c) 2021-2025 Christoffer Lerno. All rights reserved.
// Use of this source code is governed by the MIT license
// a copy of which can be found in the LICENSE_STDLIB file.
@@ -12,8 +12,8 @@ struct SimpleHeapAllocator (Allocator)
}
<*
@require allocator != null "An underlying memory provider must be given"
@require !self.free_list "The allocator may not be already initialized"
@require allocator != null : "An underlying memory provider must be given"
@require !self.free_list : "The allocator may not be already initialized"
*>
fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
{
@@ -21,7 +21,7 @@ fn void SimpleHeapAllocator.init(&self, MemoryAllocFn allocator)
self.free_list = null;
}
fn void*! SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
@@ -30,7 +30,7 @@ fn void*! SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type,
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
}
fn void*! SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
fn void*? SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
@@ -52,7 +52,7 @@ fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dyn
<*
@require old_pointer && bytes > 0
*>
fn void*! SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
fn void*? SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
{
// Find the block header.
Header* block = (Header*)old_pointer - 1;
@@ -64,14 +64,14 @@ fn void*! SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @loc
return new;
}
fn void*! SimpleHeapAllocator._calloc(&self, usz bytes) @local
fn void*? SimpleHeapAllocator._calloc(&self, usz bytes) @local
{
void* data = self._alloc(bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
{
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!self.free_list)
@@ -120,7 +120,7 @@ fn void*! SimpleHeapAllocator._alloc(&self, usz bytes) @local
return self._alloc(aligned_bytes);
}
fn void! SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
fn void? SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!;

View File

@@ -7,39 +7,39 @@ import std::io;
import libc;
const LibcAllocator LIBC_ALLOCATOR = {};
distinct LibcAllocator (Allocator, Printable) = uptr;
typedef LibcAllocator (Allocator, Printable) = uptr;
fn String LibcAllocator.to_string(&self, Allocator allocator) @dynamic => "Libc allocator".copy(allocator);
fn usz! LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
fn usz? LibcAllocator.to_format(&self, Formatter *format) @dynamic => format.print("Libc allocator");
module std::core::mem::allocator @if(env::POSIX);
import std::os;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY?;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY?;
}
else
{
void* data @noinit;
if (alignment > mem::DEFAULT_MEM_ALIGNMENT)
{
if (posix::posix_memalign(&data, alignment, bytes)) return AllocationFailure.OUT_OF_MEMORY?;
if (posix::posix_memalign(&data, alignment, bytes)) return mem::OUT_OF_MEMORY?;
}
else
{
if (!(data = libc::malloc(bytes))) return AllocationFailure.OUT_OF_MEMORY?;
if (!(data = libc::malloc(bytes))) return mem::OUT_OF_MEMORY?;
}
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
@@ -48,13 +48,13 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
}
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
if (alignment <= mem::DEFAULT_MEM_ALIGNMENT) return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
void* new_ptr;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return AllocationFailure.OUT_OF_MEMORY?;
if (posix::posix_memalign(&new_ptr, alignment, new_bytes)) return mem::OUT_OF_MEMORY?;
$switch
$switch:
$case env::DARWIN:
usz old_usable_size = darwin::malloc_size(old_ptr);
$case env::LINUX:
@@ -78,31 +78,31 @@ module std::core::mem::allocator @if(env::WIN32);
import std::os::win32;
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
if (alignment > 0)
{
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
return win32::_aligned_recalloc(null, 1, bytes, alignment) ?: mem::OUT_OF_MEMORY?;
}
return libc::calloc(1, bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
return libc::calloc(1, bytes) ?: mem::OUT_OF_MEMORY?;
}
void* data = alignment > 0 ? win32::_aligned_malloc(bytes, alignment) : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
if (!data) return mem::OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
return data;
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment)
{
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: AllocationFailure.OUT_OF_MEMORY?;
return win32::_aligned_realloc(old_ptr, new_bytes, alignment) ?: mem::OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
}
fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
@@ -118,17 +118,17 @@ fn void LibcAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
module std::core::mem::allocator @if(!env::WIN32 && !env::POSIX && env::LIBC);
import libc;
fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
fn void*? LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (init_type == ZERO)
{
void* data = alignment ? @aligned_alloc(fn void*(usz bytes) => libc::calloc(bytes, 1), bytes, alignment)!! : libc::calloc(bytes, 1);
return data ?: AllocationFailure.OUT_OF_MEMORY?;
return data ?: mem::OUT_OF_MEMORYY?;
}
else
{
void* data = alignment ? @aligned_alloc(libc::malloc, bytes, alignment)!! : libc::malloc(bytes);
if (!data) return AllocationFailure.OUT_OF_MEMORY?;
if (!data) return mem::OUT_OF_MEMORY?;
$if env::TESTING:
for (usz i = 0; i < bytes; i++) ((char*)data)[i] = 0xAA;
$endif
@@ -137,14 +137,14 @@ fn void*! LibcAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz a
}
fn void*! LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
fn void*? LibcAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (alignment)
{
void* data = @aligned_realloc(fn void*(usz bytes) => libc::malloc(bytes), libc::free, old_ptr, new_bytes, alignment)!!;
return data ?: AllocationFailure.OUT_OF_MEMORY?;
return data ?: mem::OUT_OF_MEMORY?;
}
return libc::realloc(old_ptr, new_bytes) ?: AllocationFailure.OUT_OF_MEMORY?;
return libc::realloc(old_ptr, new_bytes) ?: mem::OUT_OF_MEMORY?;
}

View File

@@ -52,7 +52,7 @@ fn void OnStackAllocator.free(&self)
struct OnStackAllocatorHeader
{
usz size;
char[?] data;
char[*] data;
}
<*
@@ -102,9 +102,9 @@ fn OnStackAllocatorExtraChunk* on_stack_allocator_find_chunk(OnStackAllocator* a
<*
@require size > 0
@require old_pointer != null
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
*>
fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
fn void*? OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
if (!allocation_in_stack_mem(self, old_pointer))
{
@@ -121,10 +121,10 @@ fn void*! OnStackAllocator.resize(&self, void* old_pointer, usz size, usz alignm
}
<*
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require size > 0
*>
fn void*! OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? OnStackAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
bool aligned = alignment > 0;
alignment = alignment_for_allocation(alignment);

View File

@@ -1,19 +1,24 @@
module std::core::mem::allocator;
import std::io, std::math;
import std::core::sanitizer::asan;
struct TempAllocatorChunk @local
{
usz size;
char[?] data;
char[*] data;
}
struct TempAllocator (Allocator)
{
Allocator backing_allocator;
TempAllocatorPage* last_page;
TempAllocator* derived;
bool allocated;
usz used;
usz capacity;
char[?] data;
usz original_capacity;
char[*] data;
}
const usz PAGE_IS_ALIGNED @private = (usz)isz.max + 1u;
@@ -23,10 +28,9 @@ struct TempAllocatorPage
{
TempAllocatorPage* prev_page;
void* start;
usz mark;
usz size;
usz ident;
char[?] data;
char[*] data;
}
macro usz TempAllocatorPage.pagesize(&self) => self.size & ~PAGE_IS_ALIGNED;
@@ -34,25 +38,98 @@ macro bool TempAllocatorPage.is_aligned(&self) => self.size & PAGE_IS_ALIGNED ==
<*
@require size >= 16
@require allocator.type != TempAllocator.typeid : "You may not create a temp allocator with a TempAllocator as the backing allocator."
*>
fn TempAllocator*! new_temp_allocator(usz size, Allocator allocator)
fn TempAllocator*? new_temp_allocator(Allocator allocator, usz size)
{
TempAllocator* temp = allocator::alloc_with_padding(allocator, TempAllocator, size)!;
temp.last_page = null;
temp.backing_allocator = allocator;
temp.used = 0;
temp.capacity = size;
temp.allocated = true;
temp.derived = null;
temp.original_capacity = temp.capacity = size;
return temp;
}
fn void TempAllocator.destroy(&self)
<*
@require !self.derived
@require min_size > TempAllocator.sizeof + 64 : "Min size must meaningfully hold the data + some bytes"
@require mult > 0 : "The multiple can never be zero"
*>
fn TempAllocator*? TempAllocator.derive_allocator(&self, usz min_size, usz buffer, usz mult)
{
self.reset(0);
if (self.last_page) (void)self._free_page(self.last_page);
allocator::free(self.backing_allocator, self);
usz remaining = self.capacity - self.used;
void* mem @noinit;
usz size @noinit;
if (min_size + buffer > remaining)
{
return self.derived = new_temp_allocator(self.backing_allocator, min_size * mult)!;
}
usz start = mem::aligned_offset(self.used + buffer, mem::DEFAULT_MEM_ALIGNMENT);
void* ptr = &self.data[start];
TempAllocator* temp = (TempAllocator*)ptr;
$if env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(ptr, TempAllocator.sizeof);
$endif
temp.last_page = null;
temp.backing_allocator = self.backing_allocator;
temp.used = 0;
temp.allocated = false;
temp.derived = null;
temp.original_capacity = temp.capacity = self.capacity - start - TempAllocator.sizeof;
self.capacity = start;
self.derived = temp;
return temp;
}
fn usz TempAllocator.mark(&self) @dynamic => self.used;
fn void TempAllocator.reset(&self)
{
TempAllocator* child = self.derived;
if (!child) return;
while (child)
{
TempAllocator* old = child;
child = old.derived;
old.destroy();
}
self.capacity = self.original_capacity;
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(&self.data[self.used], self.capacity - self.used);
$endif
self.derived = null;
}
<*
@require self.allocated : "Only a top level allocator should be freed."
*>
fn void TempAllocator.free(&self)
{
self.reset();
self.destroy();
}
fn void TempAllocator.destroy(&self) @local
{
TempAllocatorPage *last_page = self.last_page;
while (last_page)
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
}
if (self.allocated)
{
allocator::free(self.backing_allocator, self);
return;
}
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
self.data[0 : self.used] = 0xAA;
$else
asan::poison_memory_region(&self.data[0], self.used);
$endif
$endif
}
fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
{
@@ -63,40 +140,15 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
asan::poison_memory_region(&self.data[self.used], old_size);
}
}
fn void TempAllocator.reset(&self, usz mark) @dynamic
{
TempAllocatorPage *last_page = self.last_page;
while (last_page && last_page.mark > mark)
{
self.used = last_page.mark;
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
}
self.last_page = last_page;
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
if (!last_page)
{
usz cleaned = self.used - mark;
if (cleaned > 0)
{
$if env::COMPILER_SAFE_MODE && !env::ADDRESS_SANITIZER:
self.data[mark : cleaned] = 0xAA;
$endif
asan::poison_memory_region(&self.data[mark], cleaned);
}
}
$endif
self.used = mark;
}
fn void! TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
fn void? TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
fn void*? TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -112,12 +164,13 @@ fn void*! TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size,
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, NO_ZERO, alignment)!;
if (page_size > size) page_size = size;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
if (chunk.size == (usz)-1)
@@ -127,19 +180,47 @@ fn void*! TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment);
}
TempAllocatorChunk* data = self.acquire(size, NO_ZERO, alignment)!;
mem::copy(data, pointer, chunk.size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
bool is_realloc_of_last = chunk.size + pointer == &self.data[self.used];
if (is_realloc_of_last)
{
isz diff = size - chunk.size;
if (diff == 0) return pointer;
if (self.capacity - self.used > diff)
{
chunk.size += diff;
self.used += diff;
$if env::ADDRESS_SANITIZER:
if (diff < 0)
{
asan::poison_memory_region(pointer + chunk.size, -diff);
}
else
{
asan::unpoison_memory_region(pointer, chunk.size);
}
$endif
return pointer;
}
}
void* data = self.acquire(size, NO_ZERO, alignment)!;
usz len_to_copy = chunk.size > size ? size : chunk.size;
mem::copy(data, pointer, len_to_copy, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
if (is_realloc_of_last)
{
self.used = (uptr)chunk - (uptr)&self.data;
$if env::ADDRESS_SANITIZER:
asan::poison_memory_region(chunk, TempAllocatorChunk.sizeof + chunk.size);
$endif
}
return data;
}
<*
@require size > 0
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
*>
fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
void* start_mem = &self.data;
@@ -202,29 +283,9 @@ fn void*! TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
// Mark it as a page
page.ident = ~(usz)0;
// Store when it was created
page.mark = ++self.used;
// Hook up the page.
page.prev_page = self.last_page;
self.last_page = page;
return &page.data[0];
}
fn void! TempAllocator.print_pages(&self, File* f)
{
TempAllocatorPage *last_page = self.last_page;
if (!last_page)
{
io::fprintf(f, "No pages.\n")!;
return;
}
io::fprintf(f, "---Pages----\n")!;
uint index = 0;
while (last_page)
{
bool is_not_aligned = !(last_page.size & (1u64 << 63));
io::fprintf(f, "%d. Alloc: %d %d at %p%s\n", ++index,
last_page.size & ~(1u64 << 63), last_page.mark, &last_page.data[0], is_not_aligned ? "" : " [aligned]")!;
last_page = last_page.prev_page;
}
}

View File

@@ -13,7 +13,7 @@ struct Allocation
void*[MAX_BACKTRACE] backtrace;
}
def AllocMap = HashMap(<uptr, Allocation>);
alias AllocMap = HashMap { uptr, Allocation };
// A simple tracking allocator.
// It tracks allocations using a hash map but
@@ -29,7 +29,7 @@ struct TrackingAllocator (Allocator)
<*
Initialize a tracking allocator to wrap (and track) another allocator.
@param [&inout] allocator "The allocator to track"
@param [&inout] allocator : "The allocator to track"
*>
fn void TrackingAllocator.init(&self, Allocator allocator)
{
@@ -52,7 +52,7 @@ fn void TrackingAllocator.free(&self)
fn usz TrackingAllocator.allocated(&self) => @pool()
{
usz allocated = 0;
foreach (&allocation : self.map.value_tlist()) allocated += allocation.size;
foreach (&allocation : self.map.tvalues()) allocated += allocation.size;
return allocated;
}
@@ -68,7 +68,7 @@ fn usz TrackingAllocator.total_allocation_count(&self) => self.allocs_total;
fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
{
return self.map.value_tlist();
return self.map.tvalues();
}
<*
@@ -76,7 +76,7 @@ fn Allocation[] TrackingAllocator.allocations_tlist(&self, Allocator allocator)
*>
fn usz TrackingAllocator.allocation_count(&self) => self.map.count;
fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
fn void*? TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
void* data = self.inner_allocator.acquire(size, init_type, alignment)!;
self.allocs_total++;
@@ -87,7 +87,7 @@ fn void*! TrackingAllocator.acquire(&self, usz size, AllocInitType init_type, us
return data;
}
fn void*! TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
fn void*? TrackingAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
void* data = self.inner_allocator.resize(old_pointer, size, alignment)!;
self.map.remove((uptr)old_pointer);
@@ -121,13 +121,13 @@ fn bool TrackingAllocator.has_leaks(&self)
fn void TrackingAllocator.print_report(&self) => self.fprint_report(io::stdout())!!;
fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
fn void? TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
{
usz total = 0;
usz entries = 0;
bool leaks = false;
Allocation[] allocs = self.map.value_tlist();
Allocation[] allocs = self.map.tvalues();
if (allocs.len)
{
if (!allocs[0].backtrace[0])
@@ -155,7 +155,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
Backtrace trace = backtrace::BACKTRACE_UNKNOWN;
if (allocation.backtrace[3])
{
trace = backtrace::symbolize_backtrace(allocation.backtrace[3:1], allocator::temp()).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
trace = backtrace::symbolize_backtrace(tmem, allocation.backtrace[3:1]).get(0) ?? backtrace::BACKTRACE_UNKNOWN;
}
if (trace.function.len) leaks = true;
io::fprintfn(out, "%13s %p %s:%d", allocation.size,
@@ -194,7 +194,7 @@ fn void! TrackingAllocator.fprint_report(&self, OutStream out) => @pool()
break;
}
}
BacktraceList list = backtrace::symbolize_backtrace(allocation.backtrace[3..(end - 1)], allocator::temp())!;
BacktraceList list = backtrace::symbolize_backtrace(tmem, allocation.backtrace[3..(end - 1)])!;
io::fprintfn(out, "Allocation %d (%d bytes): ", i + 1, allocation.size)!;
foreach (trace : list)
{

View File

@@ -5,7 +5,7 @@ import std::core::array::slice;
@param [in] array
@param [in] element
@return "the first index of the element"
@return! SearchResult.MISSING
@return? NOT_FOUND
*>
macro index_of(array, element)
{
@@ -13,7 +13,7 @@ macro index_of(array, element)
{
if (*e == element) return i;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
<*
@@ -26,7 +26,7 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
if (xlen < 1) xlen = $typeof((*array_ptr)[0]).len + xlen;
if (ylen < 1) ylen = $typeof((*array_ptr)).len + ylen;
var $ElementType = $typeof((*array_ptr)[0][0]);
return Slice2d(<$ElementType>) { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
return Slice2d{$ElementType} { ($ElementType*)array_ptr, $typeof((*array_ptr)[0]).len, y, ylen, x, xlen };
}
@@ -34,7 +34,7 @@ macro slice2d(array_ptr, x = 0, xlen = 0, y = 0, ylen = 0)
@param [in] array
@param [in] element
@return "the last index of the element"
@return! SearchResult.MISSING
@return? NOT_FOUND
*>
macro rindex_of(array, element)
{
@@ -42,7 +42,7 @@ macro rindex_of(array, element)
{
if (*e == element) return i;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
<*
@@ -50,13 +50,13 @@ macro rindex_of(array, element)
@param [in] arr1
@param [in] arr2
@param [&inout] allocator "The allocator to use, default is the heap allocator"
@param [&inout] allocator : "The allocator to use, default is the heap allocator"
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
@ensure result.len == arr1.len + arr2.len
*>
macro concat(arr1, arr2, Allocator allocator) @nodiscard
macro concat(Allocator allocator, arr1, arr2) @nodiscard
{
var $Type = $typeof(arr1[0]);
$Type[] result = allocator::alloc_array(allocator, $Type, arr1.len + arr2.len);
@@ -70,22 +70,6 @@ macro concat(arr1, arr2, Allocator allocator) @nodiscard
}
return result;
}
<*
Concatenate two arrays or slices, returning a slice containing the concatenation of them.
@param [in] arr1
@param [in] arr2
@param [&inout] allocator "The allocator to use, default is the heap allocator"
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
{
return concat(arr1, arr2, allocator);
}
<*
Concatenate two arrays or slices, returning a slice containing the concatenation of them,
allocated using the temp allocator.
@@ -94,12 +78,12 @@ macro concat_new(arr1, arr2, Allocator allocator = allocator::heap()) @nodiscard
@param [in] arr2
@require @typekind(arr1) == SLICE || @typekind(arr1) == ARRAY
@require @typekind(arr2) == SLICE || @typekind(arr2) == ARRAY
@require @typeis(arr1[0], $typeof(arr2[0])) "Arrays must have the same type"
@require @typeis(arr1[0], $typeof(arr2[0])) : "Arrays must have the same type"
@ensure return.len == arr1.len + arr2.len
*>
macro tconcat(arr1, arr2) @nodiscard => concat(arr1, arr2, allocator::temp());
macro tconcat(arr1, arr2) @nodiscard => concat(tmem, arr1, arr2);
module std::core::array::slice(<Type>);
module std::core::array::slice{Type};
struct Slice2d
{

View File

@@ -88,13 +88,13 @@ bitstruct UInt128LE : uint128 @littleendian
}
<*
@require is_array_or_slice_of_char(bytes) "argument must be an array, a pointer to an array or a slice of char"
@require is_bitorder($Type) "type must be a bitorder integer"
@require is_array_or_slice_of_char(bytes) : "argument must be an array, a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
*>
macro read(bytes, $Type)
{
char[] s;
$switch (@typekind(bytes))
$switch @typekind(bytes):
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
@@ -104,13 +104,13 @@ macro read(bytes, $Type)
}
<*
@require is_arrayptr_or_slice_of_char(bytes) "argument must be a pointer to an array or a slice of char"
@require is_bitorder($Type) "type must be a bitorder integer"
@require is_arrayptr_or_slice_of_char(bytes) : "argument must be a pointer to an array or a slice of char"
@require is_bitorder($Type) : "type must be a bitorder integer"
*>
macro write(x, bytes, $Type)
{
char[] s;
$switch (@typekind(bytes))
$switch @typekind(bytes):
$case POINTER:
s = (*bytes)[:$Type.sizeof];
$default:
@@ -121,7 +121,7 @@ macro write(x, bytes, $Type)
macro is_bitorder($Type)
{
$switch ($Type)
$switch $Type:
$case UShortLE:
$case ShortLE:
$case UIntLE:
@@ -146,7 +146,7 @@ macro is_bitorder($Type)
macro bool is_array_or_slice_of_char(bytes)
{
$switch (@typekind(bytes))
$switch @typekind(bytes):
$case POINTER:
var $Inner = $typefrom($typeof(bytes).inner);
$if $Inner.kindof == ARRAY:
@@ -164,7 +164,7 @@ macro bool is_array_or_slice_of_char(bytes)
macro bool is_arrayptr_or_slice_of_char(bytes)
{
$switch (@typekind(bytes))
$switch @typekind(bytes):
$case POINTER:
var $Inner = $typefrom($typeof(bytes).inner);
$if $Inner.kindof == ARRAY:

View File

@@ -4,29 +4,55 @@
module std::core::builtin;
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.
You can use `@is_empty_macro_slot()` and `@is_valid_macro_slot()` to figure out whether
the argument has been used or not.
An example:
```c3
macro foo(a, #b = EMPTY_MACRO_SLOT)
{
$if @is_valid_macro_slot(#b):
return invoke_foo2(a, #b);
$else
return invoke_foo1(a);
$endif
}
*>
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
typedef EmptySlot = void*;
macro @is_empty_macro_slot(#arg) @const @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @const @builtin => !@typeis(#arg, EmptySlot);
/*
Use `IteratorResult` when reading the end of an iterator, or accessing a result out of bounds.
*/
fault IteratorResult { NO_MORE_ELEMENT }
faultdef NO_MORE_ELEMENT @builtin;
/*
Use `SearchResult` when trying to return a value from some collection but the element is missing.
*/
fault SearchResult { MISSING }
faultdef NOT_FOUND @builtin;
/*
Use `CastResult` when an attempt at conversion fails.
*/
fault CastResult { TYPE_MISMATCH }
faultdef TYPE_MISMATCH @builtin;
def VoidFn = fn void();
alias VoidFn = fn void();
<*
Stores a variable on the stack, then restores it at the end of the
macro scope.
@param #variable `the variable to store and restore`
@param #variable : `the variable to store and restore`
@require values::@is_lvalue(#variable)
*>
macro void @scope(#variable; @body) @builtin
@@ -38,7 +64,7 @@ macro void @scope(#variable; @body) @builtin
<*
Swap two variables
@require $defined(#a = #b, #b = #a) `The values must be mutually assignable`
@require $defined(#a = #b, #b = #a) : `The values must be mutually assignable`
*>
macro void @swap(#a, #b) @builtin
{
@@ -50,24 +76,32 @@ macro void @swap(#a, #b) @builtin
<*
Convert an `any` type to a type, returning an failure if there is a type mismatch.
@param v `the any to convert to the given type.`
@param $Type `the type to convert to`
@param v : `the any to convert to the given type.`
@param $Type : `the type to convert to`
@return `The any.ptr converted to its type.`
@ensure @typeis(return, $Type*)
@return! CastResult.TYPE_MISMATCH
@return? TYPE_MISMATCH
*>
macro anycast(any v, $Type) @builtin
{
if (v.type != $Type.typeid) return CastResult.TYPE_MISMATCH?;
if (v.type != $Type.typeid) return TYPE_MISMATCH?;
return ($Type*)v.ptr;
}
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @pool()
fn bool print_backtrace(String message, int backtraces_to_ignore) @if(env::NATIVE_STACKTRACE) => @stack_mem(0x1100; Allocator smem)
{
Allocator t = allocator::current_temp;
TempAllocator* new_t = allocator::new_temp_allocator(smem, 0x1000)!!;
allocator::current_temp = new_t;
defer
{
allocator::current_temp = t;
new_t.free();
}
void*[256] buffer;
void*[] backtraces = backtrace::capture_current(&buffer);
backtraces_to_ignore++;
BacktraceList! backtrace = backtrace::symbolize_backtrace(backtraces, allocator::temp());
BacktraceList? backtrace = backtrace::symbolize_backtrace(tmem, backtraces);
if (catch backtrace) return false;
if (backtrace.len() <= backtraces_to_ignore) return false;
io::eprint("\nERROR: '");
@@ -130,7 +164,7 @@ fn void default_panic(String message, String file, String function, uint line) @
$$trap();
}
def PanicFn = fn void(String message, String file, String function, uint line);
alias PanicFn = fn void(String message, String file, String function, uint line);
PanicFn panic = &default_panic;
@@ -156,7 +190,7 @@ fn void panicf(String fmt, String file, String function, uint line, args...)
<*
Marks the path as unreachable. This will panic in safe mode, and in fast will simply be assumed
never happens.
@param [in] string "The panic message or format string"
@param [in] string : "The panic message or format string"
*>
macro void unreachable(String string = "Unreachable statement reached.", ...) @builtin @noreturn
{
@@ -168,7 +202,7 @@ macro void unreachable(String string = "Unreachable statement reached.", ...) @b
<*
Marks the path as unsupported, this is similar to unreachable.
@param [in] string "The error message"
@param [in] string : "The error message"
*>
macro void unsupported(String string = "Unsupported function invoked") @builtin @noreturn
{
@@ -200,10 +234,10 @@ macro any.as_inner(&self)
}
<*
@param expr "the expression to cast"
@param $Type "the type to cast to"
@param expr : "the expression to cast"
@param $Type : "the type to cast to"
@require $sizeof(expr) == $Type.sizeof "Cannot bitcast between types of different size."
@require $sizeof(expr) == $Type.sizeof : "Cannot bitcast between types of different size."
@ensure @typeis(return, $Type)
*>
macro bitcast(expr, $Type) @builtin
@@ -218,11 +252,11 @@ macro bitcast(expr, $Type) @builtin
}
<*
@param $Type `The type of the enum`
@param [in] enum_name `The name of the enum to search for`
@require $Type.kindof == ENUM `Only enums may be used`
@param $Type : `The type of the enum`
@param [in] enum_name : `The name of the enum to search for`
@require $Type.kindof == ENUM : `Only enums may be used`
@ensure @typeis(return, $Type)
@return! SearchResult.MISSING
@return? NOT_FOUND
*>
macro enum_by_name($Type, String enum_name) @builtin
{
@@ -231,16 +265,16 @@ macro enum_by_name($Type, String enum_name) @builtin
{
if (name == enum_name) return $Type.from_ordinal(i);
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
<*
@param $Type `The type of the enum`
@require $Type.kindof == ENUM `Only enums may be used`
@require $defined($Type.#value) `Expected '#value' to match an enum associated value`
@require $assignable(value, $typeof(($Type){}.#value)) `Expected the value to match the type of the associated value`
@param $Type : `The type of the enum`
@require $Type.kindof == ENUM : `Only enums may be used`
@require $defined($Type.#value) : `Expected '#value' to match an enum associated value`
@require $assignable(value, $typeof(($Type){}.#value)) : `Expected the value to match the type of the associated value`
@ensure @typeis(return, $Type)
@return! SearchResult.MISSING
@return? NOT_FOUND
*>
macro @enum_from_value($Type, #value, value) @builtin
{
@@ -249,19 +283,19 @@ macro @enum_from_value($Type, #value, value) @builtin
{
if (e.#value == value) return e;
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
<*
Mark an expression as likely to be true
@param #value "expression to be marked likely"
@param $probability "in the range 0 - 1"
@param #value : "expression to be marked likely"
@param $probability : "in the range 0 - 1"
@require $probability >= 0 && $probability <= 1.0
*>
macro bool @likely(bool #value, $probability = 1.0) @builtin
{
$switch
$switch:
$case env::BUILTIN_EXPECT_IS_DISABLED:
return #value;
$case $probability == 1.0:
@@ -274,13 +308,13 @@ macro bool @likely(bool #value, $probability = 1.0) @builtin
<*
Mark an expression as unlikely to be true
@param #value "expression to be marked unlikely"
@param $probability "in the range 0 - 1"
@param #value : "expression to be marked unlikely"
@param $probability : "in the range 0 - 1"
@require $probability >= 0 && $probability <= 1.0
*>
macro bool @unlikely(bool #value, $probability = 1.0) @builtin
{
$switch
$switch:
$case env::BUILTIN_EXPECT_IS_DISABLED:
return #value;
$case $probability == 1.0:
@@ -297,7 +331,7 @@ macro bool @unlikely(bool #value, $probability = 1.0) @builtin
*>
macro @expect(#value, expected, $probability = 1.0) @builtin
{
$switch
$switch:
$case env::BUILTIN_EXPECT_IS_DISABLED:
return #value == expected;
$case $probability == 1.0:
@@ -322,9 +356,9 @@ enum PrefetchLocality
<*
Prefetch a pointer.
@param [in] ptr `Pointer to prefetch`
@param $locality `Locality ranging from none to extremely local`
@param $write `Prefetch for write, otherwise prefetch for read.`
@param [in] ptr : `Pointer to prefetch`
@param $locality : `Locality ranging from none to extremely local`
@param $write : `Prefetch for write, otherwise prefetch for read.`
*>
macro @prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
{
@@ -348,7 +382,7 @@ macro swizzle2(v, v2, ...) @builtin
@require @typekind(#expr) == OPTIONAL : `@catch expects an Optional value`
*>
macro anyfault @catch(#expr) @builtin
macro fault @catch(#expr) @builtin
{
if (catch f = #expr) return f;
return {};
@@ -367,7 +401,7 @@ macro bool @ok(#expr) @builtin
}
<*
@require $defined(&#value, (char*)&#value) "This must be a value that can be viewed as a char array"
@require $defined(&#value, (char*)&#value) : "This must be a value that can be viewed as a char array"
*>
macro char[] @as_char_view(#value) @builtin
{
@@ -390,7 +424,7 @@ macro @generic_hash_core(h, value)
macro @generic_hash(value)
{
uint h = @generic_hash_core((uint)0x3efd4391, value);
$for (var $cnt = 4; $cnt < $sizeof(value); $cnt += 4)
$for var $cnt = 4; $cnt < $sizeof(value); $cnt += 4:
value >>= 32; // reduce value
h = @generic_hash_core(h, value);
$endfor
@@ -409,14 +443,10 @@ macro uint int128.hash(int128 i) => @generic_hash(i);
macro uint uint128.hash(uint128 i) => @generic_hash(i);
macro uint bool.hash(bool b) => @generic_hash(b);
macro uint typeid.hash(typeid t) => @generic_hash(((ulong)(uptr)t));
macro uint String.hash(String c) => (uint)fnv32a::encode(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::encode(c);
macro uint String.hash(String c) => (uint)fnv32a::hash(c);
macro uint char[].hash(char[] c) => (uint)fnv32a::hash(c);
macro uint void*.hash(void* ptr) => @generic_hash(((ulong)(uptr)ptr));
distinct EmptySlot = void*;
const EmptySlot EMPTY_MACRO_SLOT @builtin = null;
macro @is_empty_macro_slot(#arg) @builtin => @typeis(#arg, EmptySlot);
macro @is_valid_macro_slot(#arg) @builtin => !@typeis(#arg, EmptySlot);
const MAX_FRAMEADDRESS = 128;
<*

View File

@@ -8,7 +8,7 @@ module std::core::builtin;
*>
macro less(a, b) @builtin
{
$switch
$switch:
$case $defined(a.less):
return a.less(b);
$case $defined(a.compare_to):
@@ -23,7 +23,7 @@ macro less(a, b) @builtin
*>
macro less_eq(a, b) @builtin
{
$switch
$switch:
$case $defined(a.less):
return !b.less(a);
$case $defined(a.compare_to):
@@ -38,7 +38,7 @@ macro less_eq(a, b) @builtin
*>
macro greater(a, b) @builtin
{
$switch
$switch:
$case $defined(a.less):
return b.less(a);
$case $defined(a.compare_to):
@@ -53,7 +53,7 @@ macro greater(a, b) @builtin
*>
macro int compare_to(a, b) @builtin
{
$switch
$switch:
$case $defined(a.compare_to):
return a.compare_to(b);
$case $defined(a.less):
@@ -67,7 +67,7 @@ macro int compare_to(a, b) @builtin
*>
macro greater_eq(a, b) @builtin
{
$switch
$switch:
$case $defined(a.less):
return !a.less(b);
$case $defined(a.compare_to):
@@ -78,11 +78,11 @@ macro greater_eq(a, b) @builtin
}
<*
@require types::@equatable_value(a) && types::@equatable_value(b) `values must be equatable`
@require types::@equatable_value(a) && types::@equatable_value(b) : `values must be equatable`
*>
macro bool equals(a, b) @builtin
{
$switch
$switch:
$case $defined(a.equals, a.equals(b)):
return a.equals(b);
$case $defined(a.compare_to, a.compare_to(b)):
@@ -100,7 +100,7 @@ macro min(x, ...) @builtin
return less(x, $vaarg[0]) ? x : $vaarg[0];
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++)
$for var $i = 0; $i < $vacount; $i++:
if (less($vaarg[$i], result))
{
result = $vaarg[$i];
@@ -116,7 +116,7 @@ macro max(x, ...) @builtin
return greater(x, $vaarg[0]) ? x : $vaarg[0];
$else
var result = x;
$for (var $i = 0; $i < $vacount; $i++)
$for var $i = 0; $i < $vacount; $i++:
if (greater($vaarg[$i], result))
{
result = $vaarg[$i];

View File

@@ -16,18 +16,18 @@ $assert C_SHORT_SIZE <= C_INT_SIZE;
$assert C_INT_SIZE <= C_LONG_SIZE;
$assert C_LONG_SIZE <= C_LONG_LONG_SIZE;
def CShort = $typefrom(signed_int_from_bitsize($$C_SHORT_SIZE));
def CUShort = $typefrom(unsigned_int_from_bitsize($$C_SHORT_SIZE));
def CInt = $typefrom(signed_int_from_bitsize($$C_INT_SIZE));
def CUInt = $typefrom(unsigned_int_from_bitsize($$C_INT_SIZE));
def CLong = $typefrom(signed_int_from_bitsize($$C_LONG_SIZE));
def CULong = $typefrom(unsigned_int_from_bitsize($$C_LONG_SIZE));
def CLongLong = $typefrom(signed_int_from_bitsize($$C_LONG_LONG_SIZE));
def CULongLong = $typefrom(unsigned_int_from_bitsize($$C_LONG_LONG_SIZE));
def CSChar = ichar;
def CUChar = char;
alias CShort = $typefrom(signed_int_from_bitsize($$C_SHORT_SIZE));
alias CUShort = $typefrom(unsigned_int_from_bitsize($$C_SHORT_SIZE));
alias CInt = $typefrom(signed_int_from_bitsize($$C_INT_SIZE));
alias CUInt = $typefrom(unsigned_int_from_bitsize($$C_INT_SIZE));
alias CLong = $typefrom(signed_int_from_bitsize($$C_LONG_SIZE));
alias CULong = $typefrom(unsigned_int_from_bitsize($$C_LONG_SIZE));
alias CLongLong = $typefrom(signed_int_from_bitsize($$C_LONG_LONG_SIZE));
alias CULongLong = $typefrom(unsigned_int_from_bitsize($$C_LONG_LONG_SIZE));
alias CSChar = ichar;
alias CUChar = char;
def CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid);
alias CChar = $typefrom($$C_CHAR_IS_SIGNED ? ichar.typeid : char.typeid);
enum CBool : char
{
@@ -38,7 +38,7 @@ enum CBool : char
// Helper macros
macro typeid signed_int_from_bitsize(usz $bitsize) @private
{
$switch ($bitsize)
$switch $bitsize:
$case 128: return int128.typeid;
$case 64: return long.typeid;
$case 32: return int.typeid;
@@ -50,7 +50,7 @@ macro typeid signed_int_from_bitsize(usz $bitsize) @private
macro typeid unsigned_int_from_bitsize(usz $bitsize) @private
{
$switch ($bitsize)
$switch $bitsize:
$case 128: return uint128.typeid;
$case 64: return ulong.typeid;
$case 32: return uint.typeid;

View File

@@ -10,30 +10,31 @@ const uint UTF16_SURROGATE_LOW_VALUE @private = 0xDC00;
const uint UTF16_SURROGATE_HIGH_VALUE @private = 0xD800;
<*
@param c `The utf32 codepoint to convert`
@param [out] output `the resulting buffer`
@param c : `The utf32 codepoint to convert`
@param [out] output : `the resulting buffer`
@return? string::CONVERSION_FAILED
*>
fn usz! char32_to_utf8(Char32 c, char[] output)
fn usz? char32_to_utf8(Char32 c, char[] output)
{
if (!output.len) return UnicodeResult.CONVERSION_FAILED?;
if (!output.len) return string::CONVERSION_FAILED?;
switch (true)
{
case c <= 0x7f:
output[0] = (char)c;
return 1;
case c <= 0x7ff:
if (output.len < 2) return UnicodeResult.CONVERSION_FAILED?;
if (output.len < 2) return string::CONVERSION_FAILED?;
output[0] = (char)(0xC0 | c >> 6);
output[1] = (char)(0x80 | (c & 0x3F));
return 2;
case c <= 0xffff:
if (output.len < 3) return UnicodeResult.CONVERSION_FAILED?;
if (output.len < 3) return string::CONVERSION_FAILED?;
output[0] = (char)(0xE0 | c >> 12);
output[1] = (char)(0x80 | (c >> 6 & 0x3F));
output[2] = (char)(0x80 | (c & 0x3F));
return 3;
case c <= 0x10ffff:
if (output.len < 4) return UnicodeResult.CONVERSION_FAILED?;
if (output.len < 4) return string::CONVERSION_FAILED?;
output[0] = (char)(0xF0 | c >> 18);
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
@@ -41,15 +42,15 @@ fn usz! char32_to_utf8(Char32 c, char[] output)
return 4;
default:
// 0x10FFFF and above is not defined.
return UnicodeResult.CONVERSION_FAILED?;
return string::CONVERSION_FAILED?;
}
}
<*
Convert a code pointer into 1-2 UTF16 characters.
@param c `The character to convert.`
@param [inout] output `the resulting UTF16 buffer to write to.`
@param c : `The character to convert.`
@param [inout] output : `the resulting UTF16 buffer to write to.`
*>
fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
{
@@ -69,11 +70,11 @@ fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
<*
Convert 1-2 UTF16 data points into UTF8.
@param [in] ptr `The UTF16 data to convert.`
@param [inout] available `amount of UTF16 data available.`
@param [inout] output `the resulting utf8 buffer to write to.`
@param [in] ptr : `The UTF16 data to convert.`
@param [inout] available : `amount of UTF16 data available.`
@param [inout] output : `the resulting utf8 buffer to write to.`
*>
fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
fn void? char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
{
Char16 high = *ptr;
if (high & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
@@ -83,15 +84,15 @@ fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
return;
}
// Low surrogate first is an error
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16?;
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return string::INVALID_UTF16?;
// Unmatched high surrogate is an error
if (*available == 1) return UnicodeResult.INVALID_UTF16?;
if (*available == 1) return string::INVALID_UTF16?;
Char16 low = ptr[1];
// Unmatched high surrogate, invalid
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.INVALID_UTF16?;
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return string::INVALID_UTF16?;
// The high bits of the codepoint are the value bits of the high surrogate
// The low bits of the codepoint are the value bits of the low surrogate
@@ -101,8 +102,8 @@ fn void! char16_to_utf8_unsafe(Char16 *ptr, usz *available, char** output)
*available = 2;
}
<*
@param c `The utf32 codepoint to convert`
@param [inout] output `the resulting buffer`
@param c : `The utf32 codepoint to convert`
@param [inout] output : `the resulting buffer`
*>
fn usz char32_to_utf8_unsafe(Char32 c, char** output)
{
@@ -130,14 +131,14 @@ fn usz char32_to_utf8_unsafe(Char32 c, char** output)
}
<*
@param [in] ptr `pointer to the first character to parse`
@param [inout] size `Set to max characters to read, set to characters read`
@param [in] ptr : `pointer to the first character to parse`
@param [inout] size : `Set to max characters to read, set to characters read`
@return `the parsed 32 bit codepoint`
*>
fn Char32! utf8_to_char32(char* ptr, usz* size)
fn Char32? utf8_to_char32(char* ptr, usz* size)
{
usz max_size = *size;
if (max_size < 1) return UnicodeResult.INVALID_UTF8?;
if (max_size < 1) return string::INVALID_UTF8?;
char c = (ptr++)[0];
if ((c & 0x80) == 0)
@@ -147,45 +148,45 @@ fn Char32! utf8_to_char32(char* ptr, usz* size)
}
if ((c & 0xE0) == 0xC0)
{
if (max_size < 2) return UnicodeResult.INVALID_UTF8?;
if (max_size < 2) return string::INVALID_UTF8?;
*size = 2;
Char32 uc = (c & 0x1F) << 6;
c = *ptr;
// Overlong sequence or invalid second.
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8?;
return uc + c & 0x3F;
}
if ((c & 0xF0) == 0xE0)
{
if (max_size < 3) return UnicodeResult.INVALID_UTF8?;
if (max_size < 3) return string::INVALID_UTF8?;
*size = 3;
Char32 uc = (c & 0x0F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
if (c & 0xC0 != 0x80) return string::INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8?;
return uc + c & 0x3F;
}
if (max_size < 4) return UnicodeResult.INVALID_UTF8?;
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8?;
if (max_size < 4) return string::INVALID_UTF8?;
if ((c & 0xF8) != 0xF0) return string::INVALID_UTF8?;
*size = 4;
Char32 uc = (c & 0x07) << 18;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
if (c & 0xC0 != 0x80) return string::INVALID_UTF8?;
uc += (c & 0x3F) << 12;
c = ptr++[0];
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
if (c & 0xC0 != 0x80) return string::INVALID_UTF8?;
uc += (c & 0x3F) << 6;
c = ptr++[0];
// Overlong sequence or invalid last
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8?;
if (!uc || c & 0xC0 != 0x80) return string::INVALID_UTF8?;
return uc + c & 0x3F;
}
<*
@param utf8 `An UTF-8 encoded slice of bytes`
@param utf8 : `An UTF-8 encoded slice of bytes`
@return `the number of encoded code points`
*>
fn usz utf8_codepoints(String utf8)
@@ -200,7 +201,7 @@ fn usz utf8_codepoints(String utf8)
<*
Calculate the UTF8 length required to encode an UTF32 array.
@param [in] utf32 `the utf32 data to calculate from`
@param [in] utf32 : `the utf32 data to calculate from`
@return `the length of the resulting UTF8 array`
*>
fn usz utf8len_for_utf32(Char32[] utf32)
@@ -225,7 +226,7 @@ fn usz utf8len_for_utf32(Char32[] utf32)
<*
Calculate the UTF8 length required to encode an UTF16 array.
@param [in] utf16 `the utf16 data to calculate from`
@param [in] utf16 : `the utf16 data to calculate from`
@return `the length of the resulting UTF8 array`
*>
fn usz utf8len_for_utf16(Char16[] utf16)
@@ -257,7 +258,7 @@ fn usz utf8len_for_utf16(Char16[] utf16)
<*
Calculate the UTF16 length required to encode a UTF8 array.
@param utf8 `the utf8 data to calculate from`
@param utf8 : `the utf8 data to calculate from`
@return `the length of the resulting UTF16 array`
*>
fn usz utf16len_for_utf8(String utf8)
@@ -280,7 +281,7 @@ fn usz utf16len_for_utf8(String utf8)
}
<*
@param [in] utf32 `the UTF32 array to check the length for`
@param [in] utf32 : `the UTF32 array to check the length for`
@return `the required length of an UTF16 array to hold the UTF32 data.`
*>
fn usz utf16len_for_utf32(Char32[] utf32)
@@ -300,7 +301,7 @@ fn usz utf16len_for_utf32(Char32[] utf32)
@param [out] utf8_buffer
@return `the number of bytes written.`
*>
fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
fn usz? utf32to8(Char32[] utf32, char[] utf8_buffer)
{
char[] buffer = utf8_buffer;
foreach (uc : utf32)
@@ -320,7 +321,7 @@ fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
@param [out] utf32_buffer
@return `the number of Char32s written.`
*>
fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
fn usz? utf8to32(String utf8, Char32[] utf32_buffer)
{
usz len = utf8.len;
Char32* ptr = utf32_buffer.ptr;
@@ -328,7 +329,7 @@ fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
usz buf_len = utf32_buffer.len;
for (usz i = 0; i < len;)
{
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED?;
if (len32 == buf_len) return string::CONVERSION_FAILED?;
usz width = len - i;
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline!;
i += width;
@@ -344,10 +345,10 @@ fn usz! utf8to32(String utf8, Char32[] utf32_buffer)
checking. This will assume the buffer is sufficiently large to hold
the converted data.
@param [in] utf16 `The UTF16 array containing the data to convert.`
@param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF16 data.`
@param [in] utf16 : `The UTF16 array containing the data to convert.`
@param [out] utf8_buffer : `the (sufficiently large) buffer to hold the UTF16 data.`
*>
fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
fn void? utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
{
usz len16 = utf16.len;
for (usz i = 0; i < len16;)
@@ -363,10 +364,10 @@ fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
checking. This will assume the buffer is sufficiently large to hold
the converted data.
@param [in] utf8 `The UTF8 buffer containing the data to convert.`
@param [out] utf32_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
@param [in] utf8 : `The UTF8 buffer containing the data to convert.`
@param [out] utf32_buffer : `the (sufficiently large) buffer to hold the UTF8 data.`
*>
fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
fn void? utf8to32_unsafe(String utf8, Char32* utf32_buffer)
{
usz len = utf8.len;
for (usz i = 0; i < len;)
@@ -383,10 +384,10 @@ fn void! utf8to32_unsafe(String utf8, Char32* utf32_buffer)
checking. This will assume the buffer is sufficiently large to hold
the converted data.
@param [in] utf8 `The UTF8 buffer containing the data to convert.`
@param [out] utf16_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
@param [in] utf8 : `The UTF8 buffer containing the data to convert.`
@param [out] utf16_buffer : `the (sufficiently large) buffer to hold the UTF8 data.`
*>
fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
fn void? utf8to16_unsafe(String utf8, Char16* utf16_buffer)
{
usz len = utf8.len;
for (usz i = 0; i < len;)
@@ -403,8 +404,8 @@ fn void! utf8to16_unsafe(String utf8, Char16* utf16_buffer)
checking. This will assume the buffer is sufficiently large to hold
the converted data.
@param [in] utf32 `The UTF32 buffer containing the data to convert.`
@param [out] utf8_buffer `the (sufficiently large) buffer to hold the UTF8 data.`
@param [in] utf32 : `The UTF32 buffer containing the data to convert.`
@param [out] utf8_buffer : `the (sufficiently large) buffer to hold the UTF8 data.`
*>
fn void utf32to8_unsafe(Char32[] utf32, char* utf8_buffer)
{

View File

@@ -1,26 +1,13 @@
module std::core::dstring;
import std::io;
distinct DString (OutStream) = DStringOpaque*;
distinct DStringOpaque = void;
typedef DString (OutStream) = DStringOpaque*;
typedef DStringOpaque = void;
const usz MIN_CAPACITY @private = 16;
<*
@require !self.data() "String already initialized"
*>
fn DString DString.new_init(&self, usz capacity = MIN_CAPACITY, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
StringData* data = allocator::alloc_with_padding(allocator, StringData, capacity)!!;
data.allocator = allocator;
data.len = 0;
data.capacity = capacity;
return *self = (DString)data;
}
<*
@require !self.data() "String already initialized"
@require !self.data() : "String already initialized"
*>
fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
{
@@ -33,34 +20,24 @@ fn DString DString.init(&self, Allocator allocator, usz capacity = MIN_CAPACITY)
}
<*
@require !self.data() "String already initialized"
*>
fn DString DString.temp_init(&self, usz capacity = MIN_CAPACITY) @deprecated("Use tinit()")
{
self.init(allocator::temp(), capacity) @inline;
return *self;
}
<*
@require !self.data() "String already initialized"
@require !self.data() : "String already initialized"
*>
fn DString DString.tinit(&self, usz capacity = MIN_CAPACITY)
{
self.init(allocator::temp(), capacity) @inline;
return *self;
return self.init(tmem, capacity) @inline;
}
fn DString new_with_capacity(usz capacity, Allocator allocator = allocator::heap())
fn DString new_with_capacity(Allocator allocator, usz capacity)
{
return (DString){}.init(allocator, capacity);
}
fn DString temp_with_capacity(usz capacity) => new_with_capacity(capacity, allocator::temp()) @inline;
fn DString temp_with_capacity(usz capacity) => new_with_capacity(tmem, capacity) @inline;
fn DString new(String c = "", Allocator allocator = allocator::heap())
fn DString new(Allocator allocator, String c = "")
{
usz len = c.len;
StringData* data = (StringData*)new_with_capacity(len, allocator);
StringData* data = (StringData*)new_with_capacity(allocator, len);
if (len)
{
data.len = len;
@@ -69,7 +46,7 @@ fn DString new(String c = "", Allocator allocator = allocator::heap())
return (DString)data;
}
fn DString temp_new(String s = "") => new(s, allocator::temp()) @inline;
fn DString temp(String s = "") => new(tmem, s) @inline;
fn void DString.replace_char(self, char ch, char replacement)
@@ -92,7 +69,8 @@ fn void DString.replace(&self, String needle, String replacement)
self.replace_char(needle[0], replacement[0]);
return;
}
@pool(data.allocator) {
@pool()
{
String str = self.tcopy_str();
self.clear();
usz len = str.len;
@@ -121,15 +99,6 @@ fn void DString.replace(&self, String needle, String replacement)
};
}
fn DString DString.new_concat(self, DString b, Allocator allocator = allocator::heap()) @deprecated("Use concat(mem)")
{
DString string;
string.init(allocator, self.len() + b.len());
string.append(self);
string.append(b);
return string;
}
fn DString DString.concat(self, Allocator allocator, DString b)
{
DString string;
@@ -139,7 +108,7 @@ fn DString DString.concat(self, Allocator allocator, DString b)
return string;
}
fn DString DString.temp_concat(self, DString b) => self.concat(allocator::temp(), b);
fn DString DString.tconcat(self, DString b) => self.concat(tmem, b);
fn ZString DString.zstr_view(&self)
{
@@ -188,7 +157,7 @@ fn String DString.str_view(self)
<*
@require index < self.len()
@require self.data() != null "Empty string"
@require self.data() != null : "Empty string"
*>
fn char DString.char_at(self, usz index) @operator([])
{
@@ -197,7 +166,7 @@ fn char DString.char_at(self, usz index) @operator([])
<*
@require index < self.len()
@require self.data() != null "Empty string"
@require self.data() != null : "Empty string"
*>
fn char* DString.char_ref(&self, usz index) @operator(&[])
{
@@ -249,23 +218,18 @@ fn usz DString.append_char32(&self, Char32 c)
return n;
}
fn DString DString.tcopy(&self) => self.copy(allocator::temp());
fn DString DString.tcopy(&self) => self.copy(tmem);
fn DString DString.copy(self, Allocator allocator = null)
fn DString DString.copy(self, Allocator allocator)
{
if (!self)
{
if (allocator) return new_with_capacity(0, allocator);
return (DString)null;
}
if (!self) return new(allocator);
StringData* data = self.data();
if (!allocator) allocator = allocator::heap();
DString new_string = new_with_capacity(data.capacity, allocator);
DString new_string = new_with_capacity(allocator, data.capacity);
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
return new_string;
}
fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
fn ZString DString.copy_zstr(self, Allocator allocator)
{
usz str_len = self.len();
if (!str_len)
@@ -279,12 +243,12 @@ fn ZString DString.copy_zstr(self, Allocator allocator = allocator::heap())
return (ZString)zstr;
}
fn String DString.copy_str(self, Allocator allocator = allocator::heap())
fn String DString.copy_str(self, Allocator allocator)
{
return (String)self.copy_zstr(allocator)[:self.len()];
}
fn String DString.tcopy_str(self) => self.copy_str(allocator::temp()) @inline;
fn String DString.tcopy_str(self) => self.copy_str(tmem) @inline;
fn bool DString.equals(self, DString other_string)
{
@@ -334,7 +298,7 @@ fn void DString.append_chars(&self, String str)
if (!other_len) return;
if (!*self)
{
*self = new(str);
*self = temp(str);
return;
}
self.reserve(other_len);
@@ -343,7 +307,7 @@ fn void DString.append_chars(&self, String str)
data.len += other_len;
}
fn Char32[] DString.copy_utf32(&self, Allocator allocator = allocator::heap())
fn Char32[] DString.copy_utf32(&self, Allocator allocator)
{
return self.str_view().to_utf32(allocator) @inline!!;
}
@@ -361,13 +325,13 @@ fn void DString.clear(self)
self.data().len = 0;
}
fn usz! DString.write(&self, char[] buffer) @dynamic
fn usz? DString.write(&self, char[] buffer) @dynamic
{
self.append_chars((String)buffer);
return buffer.len;
}
fn void! DString.write_byte(&self, char c) @dynamic
fn void? DString.write_byte(&self, char c) @dynamic
{
self.append_char(c);
}
@@ -376,7 +340,7 @@ fn void DString.append_char(&self, char c)
{
if (!*self)
{
*self = new_with_capacity(MIN_CAPACITY);
*self = temp_with_capacity(MIN_CAPACITY);
}
self.reserve(1);
StringData* data = self.data();
@@ -386,7 +350,7 @@ fn void DString.append_char(&self, char c)
<*
@require start < self.len()
@require end < self.len()
@require end >= start "End must be same or equal to the start"
@require end >= start : "End must be same or equal to the start"
*>
fn void DString.delete_range(&self, usz start, usz end)
{
@@ -418,7 +382,7 @@ fn void DString.delete(&self, usz start, usz len = 1)
macro void DString.append(&self, value)
{
var $Type = $typeof(value);
$switch ($Type)
$switch $Type:
$case char:
$case ichar:
self.append_char(value);
@@ -429,7 +393,7 @@ macro void DString.append(&self, value)
$case Char32:
self.append_char32(value);
$default:
$switch
$switch:
$case $defined((Char32)value):
self.append_char32((Char32)value);
$case $defined((String)value):
@@ -550,7 +514,7 @@ fn usz DString.insert_utf32_at(&self, usz index, Char32[] chars)
macro void DString.insert_at(&self, usz index, value)
{
var $Type = $typeof(value);
$switch ($Type)
$switch $Type:
$case char:
$case ichar:
self.insert_char_at(index, value);
@@ -561,7 +525,7 @@ macro void DString.insert_at(&self, usz index, value)
$case Char32:
self.insert_char32_at(index, value);
$default:
$switch
$switch:
$case $defined((Char32)value):
self.insert_char32_at(index, (Char32)value);
$case $defined((String)value):
@@ -572,21 +536,19 @@ macro void DString.insert_at(&self, usz index, value)
$endswitch
}
fn usz! DString.appendf(&self, String format, args...) @maydiscard
import libc;
fn usz? DString.appendf(&self, String format, args...) @maydiscard
{
if (!self.data()) self.init(mem, format.len + 20);
@pool(self.data().allocator)
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
return formatter.vprintf(format, args);
};
if (!self.data()) self.tinit(format.len + 20);
Formatter formatter;
formatter.init(&out_string_append_fn, self);
return formatter.vprintf(format, args);
}
fn usz! DString.appendfn(&self, String format, args...) @maydiscard
fn usz? DString.appendfn(&self, String format, args...) @maydiscard
{
if (!self.data()) self.init(mem, format.len + 20);
@pool(self.data().allocator)
if (!self.data()) self.tinit(format.len + 20);
@pool()
{
Formatter formatter;
formatter.init(&out_string_append_fn, self);
@@ -596,15 +558,15 @@ fn usz! DString.appendfn(&self, String format, args...) @maydiscard
};
}
fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::heap())
fn DString join(Allocator allocator, String[] s, String joiner)
{
if (!s.len) return (DString)null;
if (!s.len) return new(allocator);
usz total_size = joiner.len * s.len;
foreach (String* &str : s)
{
total_size += str.len;
}
DString res = new_with_capacity(total_size, allocator);
DString res = new_with_capacity(allocator, total_size);
res.append(s[0]);
foreach (String* &str : s[1..])
{
@@ -614,7 +576,7 @@ fn DString new_join(String[] s, String joiner, Allocator allocator = allocator::
return res;
}
fn void! out_string_append_fn(void* data, char c) @private
fn void? out_string_append_fn(void* data, char c) @private
{
DString* s = data;
s.append_char(c);
@@ -644,7 +606,7 @@ fn void DString.reserve(&self, usz addition)
StringData* data = self.data();
if (!data)
{
*self = dstring::new_with_capacity(addition);
*self = dstring::temp_with_capacity(addition);
return;
}
usz len = data.len + addition;
@@ -656,7 +618,7 @@ fn void DString.reserve(&self, usz addition)
*self = (DString)allocator::realloc(data.allocator, data, StringData.sizeof + new_capacity);
}
fn usz! DString.read_from_stream(&self, InStream reader)
fn usz? DString.read_from_stream(&self, InStream reader)
{
if (&reader.available)
{
@@ -691,5 +653,5 @@ struct StringData @private
Allocator allocator;
usz len;
usz capacity;
char[?] chars;
char[*] chars;
}

View File

@@ -57,6 +57,7 @@ enum OsType
HURD,
WASI,
EMSCRIPTEN,
ANDROID,
}
enum ArchType
@@ -150,15 +151,17 @@ const bool FREEBSD = LIBC && OS_TYPE == FREEBSD;
const bool NETBSD = LIBC && OS_TYPE == NETBSD;
const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD;
const bool WASI = LIBC && OS_TYPE == WASI;
const bool ANDROID = LIBC && OS_TYPE == ANDROID;
const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64;
const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER;
const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER;
const bool THREAD_SANITIZER = $$THREAD_SANITIZER;
const bool ANY_SANITIZER = ADDRESS_SANITIZER || MEMORY_SANITIZER || THREAD_SANITIZER;
const int LANGUAGE_DEV_VERSION = $$LANGUAGE_DEV_VERSION;
macro bool os_is_darwin() @const
{
$switch (OS_TYPE)
$switch OS_TYPE:
$case IOS:
$case MACOS:
$case TVOS:
@@ -171,7 +174,7 @@ macro bool os_is_darwin() @const
macro bool os_is_posix() @const
{
$switch (OS_TYPE)
$switch OS_TYPE:
$case IOS:
$case MACOS:
$case NETBSD:
@@ -182,6 +185,7 @@ macro bool os_is_posix() @const
$case SOLARIS:
$case TVOS:
$case WATCHOS:
$case ANDROID:
return true;
$case WIN32:
$case WASI:

View File

@@ -8,6 +8,8 @@ import std::math;
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
faultdef OUT_OF_MEMORY, INVALID_ALLOC_SIZE;
macro bool @constant_is_power_of_2($x) @const @private
{
return $x != 0 && ($x & ($x - 1)) == 0;
@@ -16,16 +18,16 @@ macro bool @constant_is_power_of_2($x) @const @private
<*
Load a vector from memory according to a mask assuming default alignment.
@param ptr "The pointer address to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@param ptr : "The pointer address to load from."
@param mask : "The mask for the load"
@param passthru : "The value to use for non masked values"
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@require passthru.len == mask.len : "Mask and passthru must have the same length"
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro masked_load(ptr, bool[<?>] mask, passthru)
macro masked_load(ptr, bool[<*>] mask, passthru)
{
return $$masked_load(ptr, mask, passthru, 0);
}
@@ -33,10 +35,10 @@ macro masked_load(ptr, bool[<?>] mask, passthru)
<*
Load a vector from memory according to a mask.
@param ptr "The pointer address to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@param $alignment "The alignment to assume for the pointer"
@param ptr : "The pointer address to load from."
@param mask : "The mask for the load"
@param passthru : "The value to use for non masked values"
@param $alignment : "The alignment to assume for the pointer"
@require $assignable(&&passthru, $typeof(ptr)) : "Pointer and passthru must match"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@@ -45,7 +47,7 @@ macro masked_load(ptr, bool[<?>] mask, passthru)
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro @masked_load_aligned(ptr, bool[<?>] mask, passthru, usz $alignment)
macro @masked_load_aligned(ptr, bool[<*>] mask, passthru, usz $alignment)
{
return $$masked_load(ptr, mask, passthru, $alignment);
}
@@ -53,9 +55,9 @@ macro @masked_load_aligned(ptr, bool[<?>] mask, passthru, usz $alignment)
<*
Load values from a pointer vector, assuming default alignment.
@param ptrvec "The vector of pointers to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@param ptrvec : "The vector of pointers to load from."
@param mask : "The mask for the load"
@param passthru : "The value to use for non masked values"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@@ -65,7 +67,7 @@ macro @masked_load_aligned(ptr, bool[<?>] mask, passthru, usz $alignment)
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro gather(ptrvec, bool[<?>] mask, passthru)
macro gather(ptrvec, bool[<*>] mask, passthru)
{
return $$gather(ptrvec, mask, passthru, 0);
}
@@ -74,10 +76,10 @@ macro gather(ptrvec, bool[<?>] mask, passthru)
<*
Load values from a pointer vector.
@param ptrvec "The vector of pointers to load from."
@param mask "The mask for the load"
@param passthru "The value to use for non masked values"
@param $alignment "The alignment to assume for the pointers"
@param ptrvec : "The vector of pointers to load from."
@param mask : "The mask for the load"
@param passthru : "The value to use for non masked values"
@param $alignment : "The alignment to assume for the pointers"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(passthru) == VECTOR : "Expected passthru to be a vector"
@@ -88,7 +90,7 @@ macro gather(ptrvec, bool[<?>] mask, passthru)
@return "A vector with the loaded values where the mask is true, passthru where the mask is false"
*>
macro @gather_aligned(ptrvec, bool[<?>] mask, passthru, usz $alignment)
macro @gather_aligned(ptrvec, bool[<*>] mask, passthru, usz $alignment)
{
return $$gather(ptrvec, mask, passthru, $alignment);
}
@@ -97,24 +99,24 @@ macro @gather_aligned(ptrvec, bool[<?>] mask, passthru, usz $alignment)
<*
Store parts of a vector according to the mask, assuming default alignment.
@param ptr "The pointer address to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@param ptr : "The pointer address to store to."
@param value : "The value to store masked"
@param mask : "The mask for the store"
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require value.len == mask.len : "Mask and value must have the same length"
*>
macro masked_store(ptr, value, bool[<?>] mask)
macro masked_store(ptr, value, bool[<*>] mask)
{
return $$masked_store(ptr, value, mask, 0);
}
<*
@param ptr "The pointer address to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@param $alignment "The alignment of the pointer"
@param ptr : "The pointer address to store to."
@param value : "The value to store masked"
@param mask : "The mask for the store"
@param $alignment : "The alignment of the pointer"
@require $assignable(&&value, $typeof(ptr)) : "Pointer and value must match"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@@ -122,15 +124,15 @@ macro masked_store(ptr, value, bool[<?>] mask)
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*>
macro @masked_store_aligned(ptr, value, bool[<?>] mask, usz $alignment)
macro @masked_store_aligned(ptr, value, bool[<*>] mask, usz $alignment)
{
return $$masked_store(ptr, value, mask, $alignment);
}
<*
@param ptrvec "The vector pointer containing the addresses to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@param ptrvec : "The vector pointer containing the addresses to store to."
@param value : "The value to store masked"
@param mask : "The mask for the store"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@require $assignable(&&value[0], $typeof(ptrvec[0])) : "Pointer and value must match"
@@ -138,16 +140,16 @@ macro @masked_store_aligned(ptr, value, bool[<?>] mask, usz $alignment)
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
*>
macro scatter(ptrvec, value, bool[<?>] mask)
macro scatter(ptrvec, value, bool[<*>] mask)
{
return $$scatter(ptrvec, value, mask, 0);
}
<*
@param ptrvec "The vector pointer containing the addresses to store to."
@param value "The value to store masked"
@param mask "The mask for the store"
@param $alignment "The alignment of the load"
@param ptrvec : "The vector pointer containing the addresses to store to."
@param value : "The value to store masked"
@param mask : "The mask for the store"
@param $alignment : "The alignment of the load"
@require @typekind(ptrvec) == VECTOR : "Expected ptrvec to be a vector"
@require @typekind(value) == VECTOR : "Expected value to be a vector"
@@ -156,14 +158,14 @@ macro scatter(ptrvec, value, bool[<?>] mask)
@require mask.len == ptrvec.len : "Mask and ptrvec must have the same length"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
*>
macro @scatter_aligned(ptrvec, value, bool[<?>] mask, usz $alignment)
macro @scatter_aligned(ptrvec, value, bool[<*>] mask, usz $alignment)
{
return $$scatter(ptrvec, value, mask, $alignment);
}
<*
@param #x "The variable or dereferenced pointer to load."
@param $alignment "The alignment to assume for the load"
@param #x : "The variable or dereferenced pointer to load."
@param $alignment : "The alignment to assume for the load"
@return "The value of the variable"
@require @constant_is_power_of_2($alignment) : "The alignment must be a power of two"
@@ -175,9 +177,9 @@ macro @unaligned_load(#x, usz $alignment) @builtin
}
<*
@param #x "The variable or dereferenced pointer to store to."
@param value "The value to store."
@param $alignment "The alignment to assume for the store"
@param #x : "The variable or dereferenced pointer to store to."
@param value : "The value to store."
@param $alignment : "The alignment to assume for the store"
@return "The value stored"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@@ -190,7 +192,7 @@ macro @unaligned_store(#x, value, usz $alignment) @builtin
}
<*
@param #x "The variable or dereferenced pointer to load."
@param #x : "The variable or dereferenced pointer to load."
@return "The value of the variable"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@@ -201,8 +203,8 @@ macro @volatile_load(#x) @builtin
}
<*
@param #x "The variable or dereferenced pointer to store to."
@param value "The value to store."
@param #x : "The variable or dereferenced pointer to store to."
@param value : "The value to store."
@return "The value stored"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@@ -225,15 +227,15 @@ enum AtomicOrdering : int
}
<*
@param #x "the variable or dereferenced pointer to load."
@param $ordering "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param $volatile "whether the load should be volatile, defaults to 'false'"
@param #x : "the variable or dereferenced pointer to load."
@param $ordering : "atomic ordering of the load, defaults to SEQ_CONSISTENT"
@param $volatile : "whether the load should be volatile, defaults to 'false'"
@return "returns the value of x"
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@require $ordering != AtomicOrdering.RELEASE "Release ordering is not valid for load."
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for load."
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
@require $ordering != AtomicOrdering.RELEASE : "Release ordering is not valid for load."
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE : "Acquire release is not valid for load."
@require types::may_load_atomic($typeof(#x)) : "Only integer, float and pointers may be used."
*>
macro @atomic_load(#x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = false) @builtin
{
@@ -241,14 +243,14 @@ macro @atomic_load(#x, AtomicOrdering $ordering = SEQ_CONSISTENT, $volatile = fa
}
<*
@param #x "the variable or dereferenced pointer to store to."
@param value "the value to store."
@param $ordering "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
@param $volatile "whether the store should be volatile, defaults to 'false'"
@param #x : "the variable or dereferenced pointer to store to."
@param value : "the value to store."
@param $ordering : "the atomic ordering of the store, defaults to SEQ_CONSISTENT"
@param $volatile : "whether the store should be volatile, defaults to 'false'"
@require $ordering != AtomicOrdering.ACQUIRE "Acquire ordering is not valid for store."
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid for store."
@require types::may_load_atomic($typeof(#x)) "Only integer, float and pointers may be used."
@require $ordering != AtomicOrdering.ACQUIRE : "Acquire ordering is not valid for store."
@require $ordering != AtomicOrdering.ACQUIRE_RELEASE : "Acquire release is not valid for store."
@require types::may_load_atomic($typeof(#x)) : "Only integer, float and pointers may be used."
@require $defined(&#x) : "This must be a variable or dereferenced pointer"
@require $defined(#x = value) : "The value doesn't match the variable"
*>
@@ -258,8 +260,8 @@ macro void @atomic_store(#x, value, AtomicOrdering $ordering = SEQ_CONSISTENT, $
}
<*
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE : "Acquire release is not valid."
*>
macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT, bool $volatile = true, bool $weak = false, usz $alignment = 0)
{
@@ -267,8 +269,8 @@ macro compare_exchange(ptr, compare, value, AtomicOrdering $success = SEQ_CONSIS
}
<*
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED "Acquire ordering is not valid."
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE "Acquire release is not valid."
@require $success != AtomicOrdering.NOT_ATOMIC && $success != AtomicOrdering.UNORDERED : "Acquire ordering is not valid."
@require $failure != AtomicOrdering.RELEASE && $failure != AtomicOrdering.ACQUIRE_RELEASE : "Acquire release is not valid."
*>
macro compare_exchange_volatile(ptr, compare, value, AtomicOrdering $success = SEQ_CONSISTENT, AtomicOrdering $failure = SEQ_CONSISTENT)
{
@@ -314,13 +316,14 @@ macro void clear_inline(void* dst, usz $len, usz $dst_align = 0, bool $is_volati
<*
Copy memory from src to dst efficiently, assuming the memory ranges do not overlap.
@param [&out] dst "The destination to copy to"
@param [&in] src "The source to copy from"
@param len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@param [&out] dst : "The destination to copy to"
@param [in] src : "The source to copy from"
@param len : "The number of bytes to copy"
@param $dst_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@require src != null || len == 0 : "Copying a null with non-zero length is invalid"
@require len == 0 || dst + len <= src || src + len <= dst : "Ranges may not overlap"
*>
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
@@ -332,13 +335,14 @@ macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
Copy memory from src to dst efficiently, assuming the memory ranges do not overlap, it
will always be inlined and never call memcopy
@param [&out] dst "The destination to copy to"
@param [&in] src "The source to copy from"
@param $len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@param [&out] dst : "The destination to copy to"
@param [in] src : "The source to copy from"
@param $len : "The number of bytes to copy"
@param $dst_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@require src != null || len == 0 : "Copying a null with non-zero length is invalid"
@require $len == 0 || dst + $len <= src || src + $len <= dst : "Ranges may not overlap"
*>
macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
@@ -349,12 +353,14 @@ macro void copy_inline(void* dst, void* src, usz $len, usz $dst_align = 0, usz $
<*
Copy memory from src to dst but correctly handle the possibility of overlapping ranges.
@param [&out] dst "The destination to copy to"
@param [&in] src "The source to copy from"
@param len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@param [&out] dst : "The destination to copy to"
@param [in] src : "The source to copy from"
@param len : "The number of bytes to copy"
@param $dst_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $src_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@require src != null || len == 0 : "Moving a null with non-zero length is invalid"
*>
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
{
@@ -364,11 +370,11 @@ macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_alig
<*
Sets all memory in a region to that of the provided byte.
@param [&out] dst "The destination to copy to"
@param val "The value to copy into memory"
@param len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@param [&out] dst : "The destination to copy to"
@param val : "The value to copy into memory"
@param len : "The number of bytes to copy"
@param $dst_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@ensure !len || (dst[0] == val && dst[len - 1] == val)
*>
@@ -380,11 +386,11 @@ macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volati
<*
Sets all memory in a region to that of the provided byte. Never calls OS memset.
@param [&out] dst "The destination to copy to"
@param val "The value to copy into memory"
@param $len "The number of bytes to copy"
@param $dst_align "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@param [&out] dst : "The destination to copy to"
@param val : "The value to copy into memory"
@param $len : "The number of bytes to copy"
@param $dst_align : "the alignment of the destination if different from the default, 0 assumes the default"
@param $is_volatile : "True if this copy should be treated as volatile, i.e. it can't be optimized away."
@ensure !$len || (dst[0] == val && dst[$len - 1] == val)
*>
@@ -421,7 +427,7 @@ macro bool equals(a, b, isz len = -1, usz $align = 0)
if (!len) return true;
var $Type;
$switch ($align)
$switch $align:
$case 1:
$Type = char;
$case 2:
@@ -471,7 +477,7 @@ macro void @scoped(Allocator allocator; @body())
Run the tracking allocator in the scope, then
print out stats.
@param $enabled "Set to false to disable tracking"
@param $enabled : "Set to false to disable tracking"
*>
macro void @report_heap_allocs_in_scope($enabled = true; @body())
{
@@ -493,14 +499,14 @@ macro void @report_heap_allocs_in_scope($enabled = true; @body())
<*
Assert on memory leak in the scope of the macro body.
@param $report "Set to false to disable memory report"
@param $report : "Set to false to disable memory report"
*>
macro void @assert_leak($report = true; @body()) @builtin
{
$if env::DEBUG_SYMBOLS || $feature(MEMORY_ASSERTS):
TrackingAllocator tracker;
tracker.init(allocator::thread_allocator);
Allocator old_allocator = allocator::thread_allocator;
tracker.init(mem);
Allocator old_allocator = mem;
allocator::thread_allocator = &tracker;
defer
{
@@ -509,7 +515,8 @@ macro void @assert_leak($report = true; @body()) @builtin
usz allocated = tracker.allocated();
if (allocated)
{
DString report = dstring::new();
DString report;
report.init(old_allocator);
defer report.free();
$if $report:
report.append_char('\n');
@@ -530,13 +537,13 @@ macro void @assert_leak($report = true; @body()) @builtin
Release everything on scope exit.
@param $size `the size of the buffer`
@param $size : `the size of the buffer`
*>
macro void @stack_mem(usz $size; @body(Allocator mem)) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, allocator::heap());
allocator.init(&buffer, mem);
defer allocator.free();
@body(&allocator);
}
@@ -545,7 +552,7 @@ macro void @stack_pool(usz $size; @body) @builtin
{
char[$size] buffer;
OnStackAllocator allocator;
allocator.init(&buffer, allocator::heap());
allocator.init(&buffer, mem);
defer allocator.free();
mem::@scoped(&allocator)
{
@@ -553,60 +560,44 @@ macro void @stack_pool(usz $size; @body) @builtin
};
}
struct TempState
{
TempAllocator* old;
TempAllocator* current;
usz mark;
}
<*
Push the current temp allocator. A push must always be balanced with a pop using the current state.
*>
fn TempState temp_push(TempAllocator* other = null)
fn PoolState temp_push()
{
TempAllocator* current = allocator::temp();
TempAllocator* old = current;
if (other == current)
{
current = allocator::temp_allocator_next();
}
return { old, current, current.used };
return allocator::push_pool() @inline;
}
<*
Pop the current temp allocator. A pop must always be balanced with a push.
*>
fn void temp_pop(TempState old_state)
fn void temp_pop(PoolState old_state)
{
assert(allocator::thread_temp_allocator == old_state.current, "Tried to pop temp allocators out of order.");
assert(old_state.current.used >= old_state.mark, "Tried to pop temp allocators out of order.");
old_state.current.reset(old_state.mark);
allocator::thread_temp_allocator = old_state.old;
allocator::pop_pool(old_state) @inline;
}
<*
@require @is_empty_macro_slot(#other_temp) ||| $assignable(#other_temp, Allocator) "Must be an allocator"
*>
macro void @pool(#other_temp = EMPTY_MACRO_SLOT; @body) @builtin
macro void @pool_init(Allocator allocator, usz pool_size, usz buffer_size; @body) @builtin
{
TempAllocator* current = allocator::temp();
$if @is_valid_macro_slot(#other_temp):
TempAllocator* original = current;
if (current == #other_temp.ptr) current = allocator::temp_allocator_next();
$endif
usz mark = current.used;
Allocator current = allocator::current_temp;
TempAllocator* top = allocator::top_temp;
allocator::create_temp_allocator(allocator, pool_size, buffer_size);
defer
{
current.reset(mark);
$if @is_valid_macro_slot(#other_temp):
allocator::thread_temp_allocator = original;
$endif;
allocator::destroy_temp_allocators();
allocator::top_temp = top;
allocator::current_temp = current;
}
@body();
}
macro void @pool(;@body) @builtin
{
PoolState state = allocator::push_pool() @inline;
defer
{
allocator::pop_pool(state) @inline;
}
@body();
}
import libc;
module std::core::mem @if(WASM_NOLIBC);
import std::core::mem::allocator @public;
@@ -622,7 +613,6 @@ fn void initialize_wasm_mem() @init(1024) @private
wasm_allocator.init(fn (x) => allocator::wasm_memory.allocate_block(x));
allocator::thread_allocator = &wasm_allocator;
allocator::temp_base_allocator = &wasm_allocator;
allocator::init_default_temp_allocators();
}
module std::core::mem;
@@ -631,7 +621,7 @@ module std::core::mem;
macro TrackingEnv* get_tracking_env()
{
$if env::TRACK_MEMORY:
return &&TrackingEnv { $$FILE, $$FUNC, $$LINE };
return &&(TrackingEnv){ $$FILE, $$FUNC, $$LINE };
$else
return null;
$endif
@@ -639,17 +629,17 @@ macro TrackingEnv* get_tracking_env()
macro @clone(value) @builtin @nodiscard
{
return allocator::clone(allocator::heap(), value);
return allocator::clone(mem, value);
}
macro @tclone(value) @builtin @nodiscard
{
return temp_new($typeof(value), value);
return tnew($typeof(value), value);
}
fn void* malloc(usz size) @builtin @inline @nodiscard
{
return allocator::malloc(allocator::heap(), size);
return allocator::malloc(mem, size);
}
<*
@@ -658,13 +648,13 @@ fn void* malloc(usz size) @builtin @inline @nodiscard
*>
fn void* malloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::malloc_aligned(allocator::heap(), size, alignment)!!;
return allocator::malloc_aligned(mem, size, alignment)!!;
}
fn void* tmalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, NO_ZERO, alignment)!!;
return tmem.acquire(size, NO_ZERO, alignment)!!;
}
<*
@@ -745,7 +735,7 @@ macro alloc_aligned($Type) @nodiscard
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro temp_new($Type, ...) @nodiscard
macro tnew($Type, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof) @inline;
@@ -760,7 +750,7 @@ macro temp_new($Type, ...) @nodiscard
@require $vacount < 2 : "Too many arguments."
@require $vacount == 0 ||| $assignable($vaexpr[0], $Type) : "The second argument must be an initializer for the type"
*>
macro temp_new_with_padding($Type, usz padding, ...) @nodiscard
macro temp_with_padding($Type, usz padding, ...) @nodiscard
{
$if $vacount == 0:
return ($Type*)tcalloc($Type.sizeof + padding) @inline;
@@ -771,12 +761,12 @@ macro temp_new_with_padding($Type, usz padding, ...) @nodiscard
$endif
}
macro temp_alloc($Type) @nodiscard
macro talloc($Type) @nodiscard
{
return tmalloc($Type.sizeof);
}
macro temp_alloc_with_padding($Type, usz padding) @nodiscard
macro talloc_with_padding($Type, usz padding) @nodiscard
{
return tmalloc($Type.sizeof + padding);
}
@@ -786,7 +776,7 @@ macro temp_alloc_with_padding($Type, usz padding) @nodiscard
*>
macro new_array($Type, usz elements) @nodiscard
{
return allocator::new_array(allocator::heap(), $Type, elements);
return allocator::new_array(mem, $Type, elements);
}
<*
@@ -795,7 +785,7 @@ macro new_array($Type, usz elements) @nodiscard
*>
macro new_array_aligned($Type, usz elements) @nodiscard
{
return allocator::new_array_aligned(allocator::heap(), $Type, elements);
return allocator::new_array_aligned(mem, $Type, elements);
}
<*
@@ -803,7 +793,7 @@ macro new_array_aligned($Type, usz elements) @nodiscard
*>
macro alloc_array($Type, usz elements) @nodiscard
{
return allocator::alloc_array(allocator::heap(), $Type, elements);
return allocator::alloc_array(mem, $Type, elements);
}
<*
@@ -812,22 +802,22 @@ macro alloc_array($Type, usz elements) @nodiscard
*>
macro alloc_array_aligned($Type, usz elements) @nodiscard
{
return allocator::alloc_array_aligned(allocator::heap(), $Type, elements);
return allocator::alloc_array_aligned(mem, $Type, elements);
}
macro temp_alloc_array($Type, usz elements) @nodiscard
macro talloc_array($Type, usz elements) @nodiscard
{
return (($Type*)tmalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
macro temp_new_array($Type, usz elements) @nodiscard
macro temp_array($Type, usz elements) @nodiscard
{
return (($Type*)tcalloc($Type.sizeof * elements, $Type.alignof))[:elements];
}
fn void* calloc(usz size) @builtin @inline @nodiscard
{
return allocator::calloc(allocator::heap(), size);
return allocator::calloc(mem, size);
}
<*
@@ -836,40 +826,40 @@ fn void* calloc(usz size) @builtin @inline @nodiscard
*>
fn void* calloc_aligned(usz size, usz alignment) @builtin @inline @nodiscard
{
return allocator::calloc_aligned(allocator::heap(), size, alignment)!!;
return allocator::calloc_aligned(mem, size, alignment)!!;
}
fn void* tcalloc(usz size, usz alignment = 0) @builtin @inline @nodiscard
{
if (!size) return null;
return allocator::temp().acquire(size, ZERO, alignment)!!;
return tmem.acquire(size, ZERO, alignment)!!;
}
fn void* realloc(void *ptr, usz new_size) @builtin @inline @nodiscard
{
return allocator::realloc(allocator::heap(), ptr, new_size);
return allocator::realloc(mem, ptr, new_size);
}
fn void* realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline @nodiscard
{
return allocator::realloc_aligned(allocator::heap(), ptr, new_size, alignment)!!;
return allocator::realloc_aligned(mem, ptr, new_size, alignment)!!;
}
fn void free(void* ptr) @builtin @inline
{
return allocator::free(allocator::heap(), ptr);
return allocator::free(mem, ptr);
}
fn void free_aligned(void* ptr) @builtin @inline
{
return allocator::free_aligned(allocator::heap(), ptr);
return allocator::free_aligned(mem, ptr);
}
fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline @nodiscard
{
if (!size) return null;
if (!ptr) return tmalloc(size, alignment);
return allocator::temp().resize(ptr, size, alignment)!!;
return tmem.resize(ptr, size, alignment)!!;
}
module std::core::mem @if(env::NO_LIBC);

View File

@@ -1,4 +1,5 @@
module std::core::mem::allocator;
import std::math;
const DEFAULT_SIZE_PREFIX = usz.sizeof;
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
@@ -18,34 +19,30 @@ enum AllocInitType
interface Allocator
{
fn void reset(usz mark) @optional;
fn usz mark() @optional;
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! acquire(usz size, AllocInitType init_type, usz alignment = 0);
fn void*? acquire(usz size, AllocInitType init_type, usz alignment = 0);
<*
@require !alignment || math::is_power_of_2(alignment)
@require alignment <= mem::MAX_MEMORY_ALIGNMENT `alignment too big`
@require alignment <= mem::MAX_MEMORY_ALIGNMENT : `alignment too big`
@require ptr != null
@require new_size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*! resize(void* ptr, usz new_size, usz alignment = 0);
fn void*? resize(void* ptr, usz new_size, usz alignment = 0);
<*
@require ptr != null
*>
fn void release(void* ptr, bool aligned);
}
def MemoryAllocFn = fn char[]!(usz);
alias MemoryAllocFn = fn char[]?(usz);
fault AllocationFailure
{
OUT_OF_MEMORY,
CHUNK_TOO_LARGE,
}
fn usz alignment_for_allocation(usz alignment) @inline @private
{
@@ -57,7 +54,7 @@ macro void* malloc(Allocator allocator, usz size) @nodiscard
return malloc_try(allocator, size)!!;
}
macro void*! malloc_try(Allocator allocator, usz size) @nodiscard
macro void*? malloc_try(Allocator allocator, usz size) @nodiscard
{
if (!size) return null;
$if env::TESTING:
@@ -74,7 +71,7 @@ macro void* calloc(Allocator allocator, usz size) @nodiscard
return calloc_try(allocator, size)!!;
}
macro void*! calloc_try(Allocator allocator, usz size) @nodiscard
macro void*? calloc_try(Allocator allocator, usz size) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, ZERO);
@@ -85,7 +82,7 @@ macro void* realloc(Allocator allocator, void* ptr, usz new_size) @nodiscard
return realloc_try(allocator, ptr, new_size)!!;
}
macro void*! realloc_try(Allocator allocator, void* ptr, usz new_size) @nodiscard
macro void*? realloc_try(Allocator allocator, void* ptr, usz new_size) @nodiscard
{
if (!new_size)
{
@@ -105,7 +102,7 @@ macro void free(Allocator allocator, void* ptr)
allocator.release(ptr, false);
}
macro void*! malloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
macro void*? malloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
{
if (!size) return null;
$if env::TESTING:
@@ -117,13 +114,13 @@ macro void*! malloc_aligned(Allocator allocator, usz size, usz alignment) @nodis
$endif
}
macro void*! calloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
macro void*? calloc_aligned(Allocator allocator, usz size, usz alignment) @nodiscard
{
if (!size) return null;
return allocator.acquire(size, ZERO, alignment);
}
macro void*! realloc_aligned(Allocator allocator, void* ptr, usz new_size, usz alignment) @nodiscard
macro void*? realloc_aligned(Allocator allocator, void* ptr, usz new_size, usz alignment) @nodiscard
{
if (!new_size)
{
@@ -305,7 +302,7 @@ fn any clone_any(Allocator allocator, any value) @nodiscard
@require alignment > 0
@require bytes <= isz.max
*>
macro void*! @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
macro void*? @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
{
if (alignment < void*.alignof) alignment = void*.alignof;
usz header = AlignedBlock.sizeof + alignment;
@@ -328,7 +325,7 @@ struct AlignedBlock
void* start;
}
macro void! @aligned_free(#free_fn, void* old_pointer)
macro void? @aligned_free(#free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if @typekind(#free_fn(desc.start)) == OPTIONAL:
@@ -342,7 +339,7 @@ macro void! @aligned_free(#free_fn, void* old_pointer)
@require bytes > 0
@require alignment > 0
*>
macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
macro void*? @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
@@ -358,14 +355,34 @@ macro void*! @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
// All allocators
def mem = thread_allocator @builtin;
alias mem @builtin = thread_allocator ;
tlocal Allocator thread_allocator @private = base_allocator();
Allocator temp_base_allocator @private = base_allocator();
tlocal TempAllocator* thread_temp_allocator @private = null;
tlocal TempAllocator*[2] temp_allocator_pair @private;
typedef PoolState = TempAllocator*;
const LazyTempAllocator LAZY_TEMP @private = {};
tlocal Allocator current_temp = &LAZY_TEMP;
tlocal TempAllocator* top_temp;
tlocal bool auto_create_temp = false;
usz temp_allocator_min_size = temp_allocator_default_min_size();
usz temp_allocator_buffer_size = temp_allocator_default_buffer_size();
usz temp_allocator_new_mult = 4;
fn PoolState push_pool()
{
Allocator old = top_temp ? current_temp : create_temp_allocator_on_demand();
current_temp = ((TempAllocator*)old).derive_allocator(temp_allocator_min_size, temp_allocator_buffer_size, temp_allocator_new_mult)!!;
return (PoolState)old.ptr;
}
fn void pop_pool(PoolState old)
{
TempAllocator* temp = (TempAllocator*)old;
current_temp = temp;
temp.reset();
}
macro Allocator base_allocator() @private
{
@@ -376,36 +393,68 @@ macro Allocator base_allocator() @private
$endif
}
macro TempAllocator* create_default_sized_temp_allocator(Allocator allocator) @local
macro usz temp_allocator_size() @local
{
$switch (env::MEMORY_ENV)
$case NORMAL:
return new_temp_allocator(1024 * 256, allocator)!!;
$case SMALL:
return new_temp_allocator(1024 * 16, allocator)!!;
$case TINY:
return new_temp_allocator(1024 * 2, allocator)!!;
$case NONE:
unreachable("Temp allocator must explicitly created when memory-env is set to 'none'.");
$switch env::MEMORY_ENV:
$case NORMAL: return 256 * 1024;
$case SMALL: return 1024 * 32;
$case TINY: return 1024 * 4;
$case NONE: return 0;
$endswitch
}
macro usz temp_allocator_default_min_size() @local
{
$switch env::MEMORY_ENV:
$case NORMAL: return 16 * 1024;
$case SMALL: return 1024 * 2;
$case TINY: return 256;
$case NONE: return 256;
$endswitch
}
macro usz temp_allocator_default_buffer_size() @local
{
$switch env::MEMORY_ENV:
$case NORMAL: return 1024;
$case SMALL: return 128;
$case TINY: return 64;
$case NONE: return 64;
$endswitch
}
macro Allocator heap() => thread_allocator;
macro TempAllocator* temp()
<*
@require !top_temp : "This should never be called when temp already exists"
*>
fn Allocator create_temp_allocator_on_demand() @private
{
if (!thread_temp_allocator)
if (!auto_create_temp)
{
init_default_temp_allocators();
auto_create_temp = true;
abort("Use '@pool_init()' to enable the temp allocator on a new thread. A temp allocator is only implicitly created on the main thread.");
}
return thread_temp_allocator;
return create_temp_allocator(base_allocator(), temp_allocator_size());
}
<*
@require !top_temp : "This should never be called when temp already exists"
*>
fn Allocator create_temp_allocator(Allocator allocator, usz size, usz buffer = temp_allocator_default_buffer_size()) @private
{
return current_temp = top_temp = allocator::new_temp_allocator(allocator, size)!!;
}
fn void init_default_temp_allocators() @private
macro Allocator temp()
{
temp_allocator_pair[0] = create_default_sized_temp_allocator(temp_base_allocator);
temp_allocator_pair[1] = create_default_sized_temp_allocator(temp_base_allocator);
thread_temp_allocator = temp_allocator_pair[0];
return current_temp;
}
alias tmem @builtin = current_temp;
fn void allow_implicit_temp_allocator_on_load_thread() @init(1) @local @if(env::LIBC)
{
auto_create_temp = true;
}
fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::LIBC)
@@ -418,35 +467,42 @@ fn void destroy_temp_allocators_after_exit() @finalizer(65535) @local @if(env::L
*>
fn void destroy_temp_allocators()
{
if (!thread_temp_allocator) return;
temp_allocator_pair[0].destroy();
temp_allocator_pair[1].destroy();
temp_allocator_pair[..] = null;
thread_temp_allocator = null;
if (!top_temp) return;
top_temp.free();
top_temp = null;
current_temp = &LAZY_TEMP;
}
fn TempAllocator* temp_allocator_next() @private
import libc;
typedef LazyTempAllocator (Allocator) @private = uptr;
fn void*? LazyTempAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
if (!top_temp) create_temp_allocator_on_demand();
return top_temp.acquire(bytes, init_type, alignment);
}
fn void*? LazyTempAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
if (!top_temp) create_temp_allocator_on_demand();
return top_temp.resize(old_ptr, new_bytes, alignment);
}
fn void LazyTempAllocator.release(&self, void* old_ptr, bool aligned) @dynamic
{
if (!thread_temp_allocator)
{
init_default_temp_allocators();
return thread_temp_allocator;
}
usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0;
return thread_temp_allocator = temp_allocator_pair[index];
}
const NullAllocator NULL_ALLOCATOR = {};
distinct NullAllocator (Allocator) = uptr;
typedef NullAllocator (Allocator) = uptr;
fn void*! NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
fn void*? NullAllocator.acquire(&self, usz bytes, AllocInitType init_type, usz alignment) @dynamic
{
return AllocationFailure.OUT_OF_MEMORY?;
return mem::OUT_OF_MEMORY?;
}
fn void*! NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
fn void*? NullAllocator.resize(&self, void* old_ptr, usz new_bytes, usz alignment) @dynamic
{
return AllocationFailure.OUT_OF_MEMORY?;
return mem::OUT_OF_MEMORY?;
}
fn void NullAllocator.release(&self, void* old_ptr, bool aligned) @dynamic

View File

@@ -11,7 +11,7 @@ struct WasmMemory
uptr use;
}
fn char[]! WasmMemory.allocate_block(&self, usz bytes)
fn char[]? WasmMemory.allocate_block(&self, usz bytes)
{
if (!self.allocation)
{
@@ -25,7 +25,7 @@ fn char[]! WasmMemory.allocate_block(&self, usz bytes)
}
usz blocks_required = (bytes_required + WASM_BLOCK_SIZE + 1) / WASM_BLOCK_SIZE;
if ($$wasm_memory_grow(0, blocks_required) == -1) return AllocationFailure.OUT_OF_MEMORY?;
if ($$wasm_memory_grow(0, blocks_required) == -1) return mem::OUT_OF_MEMORY?;
self.allocation = $$wasm_memory_size(0) * WASM_BLOCK_SIZE;
defer self.use += bytes;
return ((char*)self.use)[:bytes];

View File

@@ -56,10 +56,7 @@ struct MachHeader64
const LC_SEGMENT_64 = 0x19;
fault MachoSearch
{
NOT_FOUND
}
fn bool name_cmp(char* a, char[16]* b)
{
for (usz i = 0; i < 16; i++)
@@ -70,7 +67,7 @@ fn bool name_cmp(char* a, char[16]* b)
return false;
}
fn SegmentCommand64*! find_segment(MachHeader* header, char* segname)
fn SegmentCommand64*? find_segment(MachHeader* header, char* segname)
{
LoadCommand* command = (void*)header + MachHeader64.sizeof;
for (uint i = 0; i < header.ncmds; i++)
@@ -82,9 +79,9 @@ fn SegmentCommand64*! find_segment(MachHeader* header, char* segname)
}
command = (void*)command + command.cmdsize;
}
return MachoSearch.NOT_FOUND?;
return NOT_FOUND?;
}
fn Section64*! find_section(SegmentCommand64* command, char* sectname)
fn Section64*? find_section(SegmentCommand64* command, char* sectname)
{
Section64* section = (void*)command + SegmentCommand64.sizeof;
for (uint i = 0; i < command.nsects; i++)
@@ -92,13 +89,13 @@ fn Section64*! find_section(SegmentCommand64* command, char* sectname)
if (name_cmp(sectname, &section.sectname)) return section;
section++;
}
return MachoSearch.NOT_FOUND?;
return NOT_FOUND?;
}
macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type)
{
Section64*! section = find_section(find_segment(header, segname), sectname);
Section64*? section = find_section(find_segment(header, segname), sectname);
if (catch section)
{
return ($Type[]){};
@@ -107,7 +104,7 @@ macro find_segment_section_body(MachHeader* header, char* segname, char* sectnam
return ptr[:section.size / $Type.sizeof];
}
def DyldCallback = fn void (MachHeader* mh, isz vmaddr_slide);
alias DyldCallback = fn void (MachHeader* mh, isz vmaddr_slide);
extern fn void _dyld_register_func_for_add_image(DyldCallback);
@@ -126,7 +123,7 @@ extern fn void* realloc(void* ptr, usz size);
extern fn void* malloc(usz size);
extern fn void free(void* ptr);
def CallbackFn = fn void();
alias CallbackFn = fn void();
struct Callback
{
uint priority;
@@ -214,7 +211,7 @@ struct TypeId
usz sizeof;
TypeId* inner;
usz len;
typeid[?] additional;
typeid[*] additional;
}
fn void dl_reg_callback(MachHeader* mh, isz vmaddr_slide)

View File

@@ -80,7 +80,7 @@ macro String[] wargs_strings(int argc, Char16** argv) @private
{
Char16* arg = argv[i];
Char16[] argstring = arg[:_strlen(arg)];
list[i] = string::new_from_utf16(argstring) ?? "?".copy();
list[i] = string::from_utf16(mem, argstring) ?? "?".copy(mem);
}
return list[:argc];
}

View File

@@ -1,8 +1,7 @@
module std::core::runtime;
import libc, std::time, std::io, std::sort;
def BenchmarkFn = fn void!() @if($$OLD_TEST);
def BenchmarkFn = fn void() @if(!$$OLD_TEST);
alias BenchmarkFn = fn void();
struct BenchmarkUnit
{
@@ -10,7 +9,7 @@ struct BenchmarkUnit
BenchmarkFn func;
}
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator = allocator::heap())
fn BenchmarkUnit[] benchmark_collection_create(Allocator allocator)
{
BenchmarkFn[] fns = $$BENCHMARK_FNS;
String[] names = $$BENCHMARK_NAMES;
@@ -39,80 +38,7 @@ fn void set_benchmark_max_iterations(uint value) @builtin
benchmark_max_iterations = value;
}
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if($$OLD_TEST)
{
int benchmarks_passed = 0;
int benchmark_count = benchmarks.len;
usz max_name;
foreach (&unit : benchmarks)
{
if (max_name < unit.name.len) max_name = unit.name.len;
}
usz len = max_name + 9;
DString name = dstring::temp_with_capacity(64);
name.append_repeat('-', len / 2);
name.append(" BENCHMARKS ");
name.append_repeat('-', len - len / 2);
io::printn(name);
name.clear();
long sys_clock_started;
long sys_clock_finished;
long sys_clocks;
Clock clock;
anyfault err;
foreach(unit : benchmarks)
{
defer name.clear();
name.appendf("Benchmarking %s ", unit.name);
name.append_repeat('.', max_name - unit.name.len + 2);
io::printf("%s ", name.str_view());
for (uint i = 0; i < benchmark_warmup_iterations; i++)
{
err = @catch(unit.func()) @inline;
@volatile_load(err);
}
clock = std::time::clock::now();
sys_clock_started = $$sysclock();
for (uint i = 0; i < benchmark_max_iterations; i++)
{
err = @catch(unit.func()) @inline;
@volatile_load(err);
}
sys_clock_finished = $$sysclock();
NanoDuration nano_seconds = clock.mark();
sys_clocks = sys_clock_finished - sys_clock_started;
if (err)
{
io::printfn("[failed] Failed due to: %s", err);
continue;
}
io::printfn("[ok] %.2f ns, %.2f CPU's clocks", (float)nano_seconds / benchmark_max_iterations, (float)sys_clocks / benchmark_max_iterations);
benchmarks_passed++;
}
io::printfn("\n%d benchmark%s run.\n", benchmark_count, benchmark_count > 1 ? "s" : "");
io::printfn("Benchmarks Result: %s. %d passed, %d failed.",
benchmarks_passed < benchmark_count ? "FAILED" : "ok",
benchmarks_passed,
benchmark_count - benchmarks_passed);
return benchmark_count == benchmarks_passed;
}
fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
{
usz max_name;
@@ -170,5 +96,5 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks) @if(!$$OLD_TEST)
fn bool default_benchmark_runner(String[] args) => @pool()
{
return run_benchmarks(benchmark_collection_create(allocator::temp()));
return run_benchmarks(benchmark_collection_create(tmem));
}

View File

@@ -7,8 +7,7 @@ import std::core::mem::allocator @public;
import libc, std::time, std::io, std::sort;
import std::os::env;
def TestFn = fn void!() @if($$OLD_TEST);
def TestFn = fn void() @if(!$$OLD_TEST);
alias TestFn = fn void();
TestContext* test_context @private;
@@ -47,7 +46,7 @@ struct TestUnit
TestFn func;
}
fn TestUnit[] test_collection_create(Allocator allocator = allocator::heap())
fn TestUnit[] test_collection_create(Allocator allocator)
{
TestFn[] fns = $$TEST_FNS;
String[] names = $$TEST_NAMES;
@@ -75,7 +74,8 @@ fn int cmp_test_unit(TestUnit a, TestUnit b)
fn bool terminal_has_ansi_codes() @local => @pool()
{
if (try v = env::get_var_temp("TERM"))
if (try v = env::tget_var("TERM"))
{
if (v.contains("xterm") || v.contains("vt100") || v.contains("screen")) return true;
}
@@ -181,7 +181,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
.breakpoint_on_assert = false,
.test_filter = "",
.has_ansi_codes = terminal_has_ansi_codes(),
.stored.allocator = allocator::heap(),
.stored.allocator = mem,
.stored.stderr = *io::stderr(),
.stored.stdout = *io::stdout(),
};
@@ -244,7 +244,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
name.append_repeat('-', len - len / 2);
if (!context.is_quiet_mode) io::printn(name);
name.clear();
TempState temp_state = mem::temp_push();
PoolState temp_state = mem::temp_push();
defer mem::temp_pop(temp_state);
foreach(unit : tests)
{
@@ -278,15 +278,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
mute_output();
mem.clear();
if (check_leaks) allocator::thread_allocator = &mem;
$if(!$$OLD_TEST):
unit.func();
$else
if (catch err = unit.func())
{
io::printf("[FAIL] Failed due to: %s", err);
continue;
}
$endif
unit.func();
// track cleanup that may take place in teardown_fn
if (context.teardown_fn)
{
@@ -297,7 +289,7 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
unmute_output(false); // all good, discard output
if (mem.has_leaks())
{
if(context.is_quiet_mode) io::printf("\n%s ", context.current_test_name);
if (context.is_quiet_mode) io::printf("\n%s ", context.current_test_name);
io::print(context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
io::printn(" LEAKS DETECTED!");
mem.print_report();
@@ -337,6 +329,6 @@ fn bool run_tests(String[] args, TestUnit[] tests) @private
fn bool default_test_runner(String[] args) => @pool()
{
assert(test_context == null, "test suite is already running");
return run_tests(args, test_collection_create(allocator::temp()));
return run_tests(args, test_collection_create(tmem));
}

View File

@@ -12,7 +12,7 @@
module std::core::sanitizer::asan;
def ErrorCallback = fn void (ZString);
alias ErrorCallback = fn void (ZString);
<*
Marks a memory region ([addr, addr+size)) as unaddressable.
@@ -26,8 +26,8 @@ def ErrorCallback = fn void (ZString);
NOTE This function is not thread-safe because no two threads can poison or
unpoison memory in the same memory region simultaneously.
@param addr "Start of memory region."
@param size "Size of memory region."
@param addr : "Start of memory region."
@param size : "Size of memory region."
*>
macro poison_memory_region(void* addr, usz size)
{
@@ -47,8 +47,8 @@ macro poison_memory_region(void* addr, usz size)
NOTE This function is not thread-safe because no two threads can
poison or unpoison memory in the same memory region simultaneously.
@param addr "Start of memory region."
@param size "Size of memory region."
@param addr : "Start of memory region."
@param size : "Size of memory region."
*>
macro unpoison_memory_region(void* addr, usz size)
{
@@ -60,7 +60,7 @@ macro unpoison_memory_region(void* addr, usz size)
<*
Checks if an address is poisoned.
@return "True if 'addr' is poisoned (that is, 1-byte read/write access to this address would result in an error report from ASan). Otherwise returns false."
@param addr "Address to check."
@param addr : "Address to check."
*>
macro bool address_is_poisoned(void* addr)
{
@@ -77,8 +77,8 @@ macro bool address_is_poisoned(void* addr)
If at least one byte in [beg, beg+size) is poisoned, returns the
address of the first such byte. Otherwise returns 0.
@param beg "Start of memory region."
@param size "Start of memory region."
@param beg : "Start of memory region."
@param size : "Start of memory region."
@return "Address of first poisoned byte."
*>
macro void* region_is_poisoned(void* beg, usz size)

View File

@@ -1,6 +1,6 @@
module std::core::sanitizer::tsan;
distinct MutexFlags = inline CUInt;
typedef MutexFlags = inline CUInt;
const MutexFlags MUTEX_LINKER_INIT = 1 << 0;
const MutexFlags MUTEX_WRITE_REENTRANT = 1 << 1;

File diff suppressed because it is too large Load Diff

View File

@@ -11,22 +11,22 @@ fn void StringIterator.reset(&self)
self.current = 0;
}
fn Char32! StringIterator.next(&self)
fn Char32? StringIterator.next(&self)
{
usz len = self.utf8.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
if (current >= len) return NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
self.current += read;
return res;
}
fn Char32! StringIterator.peek(&self)
fn Char32? StringIterator.peek(&self)
{
usz len = self.utf8.len;
usz current = self.current;
if (current >= len) return IteratorResult.NO_MORE_ELEMENT?;
if (current >= len) return NO_MORE_ELEMENT?;
usz read = (len - current < 4 ? len - current : 4);
Char32 res = conv::utf8_to_char32(&self.utf8[current], &read)!;
return res;
@@ -37,13 +37,13 @@ fn bool StringIterator.has_next(&self)
return self.current < self.utf8.len;
}
fn Char32! StringIterator.get(&self)
fn Char32? StringIterator.get(&self)
{
usz len = self.utf8.len;
usz current = self.current;
usz read = (len - current < 4 ? len - current : 4);
usz index = current > read ? current - read : 0;
if (index >= len) return IteratorResult.NO_MORE_ELEMENT?;
if (index >= len) return NO_MORE_ELEMENT?;
Char32 res = conv::utf8_to_char32(&self.utf8[index], &read)!;
return res;
}

View File

@@ -34,13 +34,13 @@ const uint[2] B1B_MAX = { 9007199, 254740991 };
<*
@require chars.len > 0
*>
macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
macro double? decfloat(char[] chars, int $bits, int $emin, int sign)
{
uint[KMAX] x;
const uint[2] TH = B1B_MAX;
int emax = - $emin - $bits + 3;
const int[?] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
const int[*] P10S = { 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
usz index;
bool got_digit = chars[0] == '0';
bool got_rad;
@@ -64,7 +64,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_digit) return MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
@@ -83,7 +83,7 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
switch
{
case c == '.':
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
if (got_rad) return MALFORMED_FLOAT?;
got_rad = true;
lrp = dc;
case k < KMAX - 3:
@@ -113,24 +113,24 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
c = chars[++index];
}
if (!got_rad) lrp = dc;
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_digit) return MALFORMED_FLOAT?;
if ((c | 32) == 'e')
{
if (last_char == index) return NumberConversion.MALFORMED_FLOAT?;
long e10 = String.to_long((String)chars[index + 1..]) ?? NumberConversion.MALFORMED_FLOAT?!;
if (last_char == index) return MALFORMED_FLOAT?;
long e10 = String.to_long((String)chars[index + 1..]) ?? MALFORMED_FLOAT?!;
lrp += e10;
}
else if (index != last_char)
{
return NumberConversion.MALFORMED_FLOAT?;
return MALFORMED_FLOAT?;
}
// Handle zero specially to avoid nasty special cases later
if (!x[0]) return sign * 0.0;
// Optimize small integers (w/no exponent) and over/under-flow
if (lrp == dc && dc < 10 && ($bits > 30 || (ulong)x[0] >> $bits == 0)) return sign * (double)x[0];
if (lrp > - $emin / 2) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (lrp > - $emin / 2) return FLOAT_OUT_OF_RANGE?;
if (lrp < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE?;
// Align incomplete final B1B digit
if (j)
@@ -320,12 +320,12 @@ macro double! decfloat(char[] chars, int $bits, int $emin, int sign)
y *= 0.5;
e2++;
}
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return NumberConversion.MALFORMED_FLOAT?;
if (e2 + math::DOUBLE_MANT_DIG > emax || (denormal && frac)) return MALFORMED_FLOAT?;
}
return math::scalbn(y, e2);
}
macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
macro double? hexfloat(char[] chars, int $bits, int $emin, int sign)
{
double scale = 1;
uint x;
@@ -351,7 +351,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
got_rad = true;
if (index == last_char)
{
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_digit) return MALFORMED_FLOAT?;
return sign * 0.0;
}
if (index != last_char && (c = chars[++index]) == '0')
@@ -369,7 +369,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
{
if (c == '.')
{
if (got_rad) return NumberConversion.MALFORMED_FLOAT?;
if (got_rad) return MALFORMED_FLOAT?;
got_rad = true;
rp = dc;
}
@@ -393,20 +393,20 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
if (index == last_char) break;
c = chars[++index];
}
if (!got_digit) return NumberConversion.MALFORMED_FLOAT?;
if (!got_digit) return MALFORMED_FLOAT?;
if (!got_rad) rp = dc;
for (; dc < 8; dc++) x *= 16;
long e2;
if ((c | 32) == 'p')
{
long e2val = String.to_long((String)chars[index + 1..]) ?? (NumberConversion.MALFORMED_FLOAT?)!;
long e2val = String.to_long((String)chars[index + 1..]) ?? (MALFORMED_FLOAT?)!;
e2 = e2val;
}
e2 += 4 * rp - 32;
if (!x) return sign * 0.0;
if (e2 > -$emin) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (e2 > -$emin) return FLOAT_OUT_OF_RANGE?;
if (e2 < $emin - 2 * math::DOUBLE_MANT_DIG) return FLOAT_OUT_OF_RANGE?;
while (x < 0x80000000)
{
@@ -441,7 +441,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
}
y = bias + sign * (double)x + sign * y;
y -= bias;
if (!y) return NumberConversion.FLOAT_OUT_OF_RANGE?;
if (!y) return FLOAT_OUT_OF_RANGE?;
return math::scalbn(y, (int)e2);
}
@@ -449,7 +449,7 @@ macro double! hexfloat(char[] chars, int $bits, int $emin, int sign)
macro String.to_real(chars, $Type) @private
{
int sign = 1;
$switch ($Type)
$switch $Type:
$case float:
const int BITS = math::FLOAT_MANT_DIG;
const int EMIN = math::FLOAT_MIN_EXP - BITS;
@@ -463,14 +463,18 @@ macro String.to_real(chars, $Type) @private
$endswitch
while (chars.len && chars[0] == ' ') chars = chars[1..];
if (!chars.len) return NumberConversion.MALFORMED_FLOAT?;
switch (chars[0])
if (!chars.len) return MALFORMED_FLOAT?;
if (chars.len != 1)
{
case '-':
sign = -1;
nextcase;
case '+':
chars = chars[1..];
switch (chars[0])
{
case '-':
sign = -1;
nextcase;
case '+':
chars = chars[1..];
}
}
if (chars == "infinity" || chars == "INFINITY") return sign * $Type.inf;
if (chars == "NAN" || chars == "nan") return $Type.nan;

View File

@@ -8,18 +8,15 @@ Example:
module sample::m;
import std::io;
fault MathError
{
DIVISION_BY_ZERO
}
faultdef DIVISION_BY_ZERO;
fn double! divide(int a, int b)
fn double? divide(int a, int b)
{
if (b == 0) return MathError.DIVISION_BY_ZERO?;
return (double)(a) / (double)(b);
}
fn void! test_div() @test
fn void? test_div() @test
{
test::eq(2, divide(6, 3)!);
test::ne(1, 2);
@@ -44,11 +41,11 @@ import std::math, std::io, libc;
<*
Initializes test case context.
@param setup_fn `initializer function for test case`
@param teardown_fn `cleanup function for test context (may be null)`
@param setup_fn : `initializer function for test case`
@param teardown_fn : `cleanup function for test context (may be null)`
@require runtime::test_context != null "Only allowed in @test functions"
@require setup_fn != null "setup_fn must always be set"
@require runtime::test_context != null : "Only allowed in @test functions"
@require setup_fn != null : "setup_fn must always be set"
*>
macro @setup(TestFn setup_fn, TestFn teardown_fn = null)
{
@@ -60,10 +57,10 @@ macro @setup(TestFn setup_fn, TestFn teardown_fn = null)
<*
Checks condition and fails assertion if not true
@param #condition `any boolean condition, will be expanded by text`
@param format `printf compatible format`
@param args `vargs for format`
@require runtime::test_context != null "Only allowed in @test functions"
@param #condition : `any boolean condition, will be expanded by text`
@param format : `printf compatible format`
@param args : `vargs for format`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro @check(#condition, String format = "", args...)
{
@@ -83,11 +80,11 @@ macro @check(#condition, String format = "", args...)
<*
Check if function returns specific error
@param #funcresult `result of function execution`
@param error_expected `expected error of function execution`
@require runtime::test_context != null "Only allowed in @test functions"
@param #funcresult : `result of function execution`
@param error_expected : `expected error of function execution`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro @error(#funcresult, anyfault error_expected)
macro @error(#funcresult, fault error_expected)
{
if (catch err = #funcresult)
{
@@ -104,9 +101,9 @@ macro @error(#funcresult, anyfault error_expected)
<*
Check if left == right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
@param left : `left argument of any comparable type`
@param right : `right argument of any comparable type`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro eq(left, right)
{
@@ -119,13 +116,13 @@ macro eq(left, right)
<*
Check left floating point value is approximately equals to right value
@param places `number of decimal places to compare (default: 7)`
@param delta `minimal allowed difference (overrides places parameter)`
@param equal_nan `allows comparing nan values, if left and right both nans result is ok`
@param places : `number of decimal places to compare (default: 7)`
@param delta : `minimal allowed difference (overrides places parameter)`
@param equal_nan : `allows comparing nan values, if left and right both nans result is ok`
@require places > 0, places <= 20 "too many decimal places"
@require delta >= 0, delta <= 1 "delta must be a small number"
@require runtime::test_context != null "Only allowed in @test functions"
@require places > 0, places <= 20 : "too many decimal places"
@require delta >= 0, delta <= 1 : "delta must be a small number"
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro void eq_approx(double left, double right, uint places = 7, double delta = 0, bool equal_nan = true)
{
@@ -143,9 +140,9 @@ macro void eq_approx(double left, double right, uint places = 7, double delta =
<*
Check if left != right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
@param left : `left argument of any comparable type`
@param right : `right argument of any comparable type`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro void ne(left, right)
{
@@ -158,9 +155,9 @@ macro void ne(left, right)
<*
Check if left > right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
@param left : `left argument of any comparable type`
@param right : `right argument of any comparable type`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro gt(left, right)
{
@@ -173,9 +170,9 @@ macro gt(left, right)
<*
Check if left >= right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
@param left : `left argument of any comparable type`
@param right : `right argument of any comparable type`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro ge(left, right)
{
@@ -188,9 +185,9 @@ macro ge(left, right)
<*
Check if left < right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
@param left : `left argument of any comparable type`
@param right : `right argument of any comparable type`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro lt(left, right)
{
@@ -203,9 +200,9 @@ macro lt(left, right)
<*
Check if left <= right
@param left `left argument of any comparable type`
@param right `right argument of any comparable type`
@require runtime::test_context != null "Only allowed in @test functions"
@param left : `left argument of any comparable type`
@param right : `right argument of any comparable type`
@require runtime::test_context != null : "Only allowed in @test functions"
*>
macro le(left, right)
{

View File

@@ -3,15 +3,11 @@ module std::core::types;
import libc;
fault ConversionResult
{
VALUE_OUT_OF_RANGE,
VALUE_OUT_OF_UNSIGNED_RANGE,
}
faultdef VALUE_OUT_OF_RANGE, VALUE_OUT_OF_UNSIGNED_RANGE;
<*
@require $Type.kindof.is_int() "Type was not an integer"
@require v.type.kindof == ENUM "Value was not an enum"
@require $Type.kindof.is_int() : "Type was not an integer"
@require v.type.kindof == ENUM : "Value was not an enum"
*>
macro any_to_enum_ordinal(any v, $Type)
{
@@ -19,8 +15,8 @@ macro any_to_enum_ordinal(any v, $Type)
}
<*
@require $Type.kindof.is_int() "Type was not an integer"
@require v.type.kindof.is_int() "Value was not an integer"
@require $Type.kindof.is_int() : "Type was not an integer"
@require v.type.kindof.is_int() : "Value was not an integer"
*>
macro any_to_int(any v, $Type)
{
@@ -33,47 +29,47 @@ macro any_to_int(any v, $Type)
{
case ichar:
ichar c = *(char*)v.ptr;
if (is_mixed_signed && c < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (is_mixed_signed && c < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
return ($Type)c;
case short:
short s = *(short*)v.ptr;
if (is_mixed_signed && s < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (is_mixed_signed && s < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
if (s > max || s < min) return VALUE_OUT_OF_RANGE?;
return ($Type)s;
case int:
int i = *(int*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (is_mixed_signed && i < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
return ($Type)i;
case long:
long l = *(long*)v.ptr;
if (is_mixed_signed && l < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (is_mixed_signed && l < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
if (l > max || l < min) return VALUE_OUT_OF_RANGE?;
return ($Type)l;
case int128:
int128 i = *(int128*)v.ptr;
if (is_mixed_signed && i < 0) return ConversionResult.VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (is_mixed_signed && i < 0) return VALUE_OUT_OF_UNSIGNED_RANGE?;
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
return ($Type)i;
case char:
char c = *(char*)v.ptr;
if (c > max) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (c > max) return VALUE_OUT_OF_RANGE?;
return ($Type)c;
case ushort:
ushort s = *(ushort*)v.ptr;
if (s > max || s < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (s > max || s < min) return VALUE_OUT_OF_RANGE?;
return ($Type)s;
case uint:
uint i = *(uint*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
return ($Type)i;
case ulong:
ulong l = *(ulong*)v.ptr;
if (l > max || l < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (l > max || l < min) return VALUE_OUT_OF_RANGE?;
return ($Type)l;
case uint128:
uint128 i = *(uint128*)v.ptr;
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE?;
if (i > max || i < min) return VALUE_OUT_OF_RANGE?;
return ($Type)i;
default:
unreachable();
@@ -93,7 +89,7 @@ fn bool typeid.is_subtype_of(self, typeid other)
macro bool is_subtype_of($Type, $OtherType)
{
var $typeid = $Type.typeid;
$switch ($Type)
$switch $Type:
$case $OtherType: return true;
$default: return false;
$endswitch
@@ -116,7 +112,7 @@ fn bool TypeKind.is_int(kind) @inline
macro bool is_slice_convertable($Type)
{
$switch ($Type.kindof)
$switch $Type.kindof:
$case SLICE:
return true;
$case POINTER:
@@ -130,11 +126,11 @@ macro bool is_bool($Type) @const => $Type.kindof == TypeKind.BOOL;
macro bool is_int($Type) @const => $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
<*
@require is_numerical($Type) "Expected a numerical type"
@require is_numerical($Type) : "Expected a numerical type"
*>
macro bool is_signed($Type) @const
{
$switch (inner_kind($Type))
$switch inner_kind($Type):
$case SIGNED_INT:
$case FLOAT:
return true;
@@ -146,11 +142,11 @@ macro bool is_signed($Type) @const
}
<*
@require is_numerical($Type) "Expected a numerical type"
@require is_numerical($Type) : "Expected a numerical type"
*>
macro bool is_unsigned($Type) @const
{
$switch (inner_kind($Type))
$switch inner_kind($Type):
$case UNSIGNED_INT:
return true;
$case VECTOR:
@@ -160,6 +156,24 @@ macro bool is_unsigned($Type) @const
$endswitch
}
macro typeid flat_type($Type) @const
{
$if $Type.kindof == DISTINCT:
return flat_type($typefrom($Type.inner));
$else
return $Type.typeid;
$endif
}
macro TypeKind flat_kind($Type) @const
{
$if $Type.kindof == DISTINCT:
return flat_type($typefrom($Type.inner));
$else
return $Type.kindof;
$endif
}
macro bool is_indexable($Type) @const
{
return $defined(($Type){}[0]);
@@ -172,7 +186,7 @@ macro bool is_ref_indexable($Type) @const
macro bool is_intlike($Type) @const
{
$switch ($Type.kindof)
$switch $Type.kindof:
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
@@ -185,7 +199,7 @@ macro bool is_intlike($Type) @const
macro bool is_underlying_int($Type) @const
{
$switch ($Type.kindof)
$switch $Type.kindof:
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
@@ -200,7 +214,7 @@ macro bool is_float($Type) @const => $Type.kindof == TypeKind.FLOAT;
macro bool is_floatlike($Type) @const
{
$switch ($Type.kindof)
$switch $Type.kindof:
$case FLOAT:
return true;
$case VECTOR:
@@ -240,7 +254,7 @@ macro bool @has_same(#a, #b, ...) @const
$if $type_a != @typeid(#b):
return false;
$endif
$for (var $i = 0; $i < $vacount; $i++)
$for var $i = 0; $i < $vacount; $i++:
$if @typeid($vaexpr[$i]) != $type_a:
return false;
$endif
@@ -250,7 +264,7 @@ macro bool @has_same(#a, #b, ...) @const
macro bool may_load_atomic($Type) @const
{
$switch ($Type.kindof)
$switch $Type.kindof:
$case BOOL:
$case SIGNED_INT:
$case UNSIGNED_INT:
@@ -266,15 +280,16 @@ macro bool may_load_atomic($Type) @const
macro lower_to_atomic_compatible_type($Type) @const
{
$switch ($Type.kindof)
$case SIGNED_INT:
$case UNSIGNED_INT:
return $Type.typeid;
$switch $Type.kindof:
$case BOOL:
$case SIGNED_INT:
$case UNSIGNED_INT:
return $Type.typeid;
$case DISTINCT:
return lower_to_atomic_compatible_type($Type.inner);
$case FLOAT:
$switch ($Type)
$case float16:
$switch $Type:
$case float16:
return ushort.typeid;
$case float:
return uint.typeid;
@@ -319,11 +334,6 @@ macro bool implements_copy($Type) @const
return $defined($Type.copy) && $defined($Type.free);
}
macro bool is_equatable_value(value) @deprecated
{
return is_equatable_type($typeof(value));
}
macro bool @equatable_value(#value) @const
{
return is_equatable_type($typeof(#value));
@@ -338,15 +348,6 @@ macro bool @comparable_value(#value) @const
$endif
}
macro bool is_comparable_value(value) @deprecated
{
$if $defined(value.less) || $defined(value.compare_to):
return true;
$else
return $typeof(value).is_ordered;
$endif
}
enum TypeKind : char
{
VOID,
@@ -355,10 +356,9 @@ enum TypeKind : char
UNSIGNED_INT,
FLOAT,
TYPEID,
ANYFAULT,
FAULT,
ANY,
ENUM,
FAULT,
STRUCT,
UNION,
BITSTRUCT,

View File

@@ -33,7 +33,7 @@ macro promote_int(x)
This acts like `$bool ? #value_1 : #value_2` but at compile time.
@param $bool `true for picking the first value, false for the other`
@param $bool : `true for picking the first value, false for the other`
@param #value_1
@param #value_2
@returns `The selected value.`
@@ -49,7 +49,7 @@ macro @select(bool $bool, #value_1, #value_2) @builtin
macro promote_int_same(x, y)
{
$if @is_int(x):
$switch
$switch:
$case @is_vector(y) &&& $typeof(y).inner == float.typeid:
return (float)x;
$case $typeof(y).typeid == float.typeid:

View File

@@ -12,8 +12,8 @@ struct Rc4
<*
Initialize the RC4 state.
@param [in] key "The key to use"
@require key.len > 0 "The key must be at least 1 byte long"
@param [in] key : "The key to use"
@require key.len > 0 : "The key must be at least 1 byte long"
*>
fn void Rc4.init(&self, char[] key)
{
@@ -43,9 +43,9 @@ fn void crypt(char[] key, char[] data)
<*
Encrypt or decrypt a sequence of bytes.
@param [in] in "The input"
@param [out] out "The output"
@require in.len <= out.len "Output would overflow"
@param [in] in : "The input"
@param [out] out : "The output"
@require in.len <= out.len : "Output would overflow"
*>
fn void Rc4.crypt(&self, char[] in, char[] out)
{
@@ -67,7 +67,7 @@ fn void Rc4.crypt(&self, char[] in, char[] out)
<*
Clear the rc4 state.
@param [&out] self "The RC4 State"
@param [&out] self : "The RC4 State"
*>
fn void Rc4.destroy(&self)
{

View File

@@ -14,13 +14,13 @@ const char DEFAULT_PAD = '=';
<*
Encode the content of src into a newly allocated string
@param [in] src "The input to be encoded."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@param [in] src : "The input to be encoded."
@param padding : "The padding character or 0 if none"
@param alphabet : "The alphabet to use"
@require padding < 0xFF : "Invalid padding character"
@return "The encoded string."
*>
fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
fn String? encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet);
@@ -28,28 +28,26 @@ fn String! encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, B
<*
Decode the content of src into a newly allocated char array.
@param [in] src "The input to be encoded."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@param [in] src : "The input to be encoded."
@param padding : "The padding character or 0 if none"
@param alphabet : "The alphabet to use"
@require padding < 0xFF : "Invalid padding character"
@return "The decoded data."
*>
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
fn char[]? decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding));
return decode_buffer(src, dst, padding, alphabet);
}
fn String! encode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
fn String! encode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
fn String? tencode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => encode(tmem, code, padding, alphabet);
fn char[]? tdecode(char[] code, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD) @inline => decode(tmem, code, padding, alphabet);
<*
Calculate the length in bytes of the decoded data.
@param n "Length in bytes of input."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@param n : "Length in bytes of input."
@param padding : "The padding character or 0 if none"
@require padding < 0xFF : "Invalid padding character"
@return "Length in bytes of the decoded data."
*>
fn usz decode_len(usz n, char padding)
@@ -62,9 +60,9 @@ fn usz decode_len(usz n, char padding)
<*
Calculate the length in bytes of the encoded data.
@param n "Length in bytes on input."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@param n : "Length in bytes on input."
@param padding : "The padding character or 0 if none"
@require padding < 0xFF : "Invalid padding character"
@return "Length in bytes of the encoded data."
*>
fn usz encode_len(usz n, char padding)
@@ -79,16 +77,16 @@ fn usz encode_len(usz n, char padding)
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@require dst.len >= decode_len(src.len, padding) "Destination buffer too small"
@param src : "The input to be decoded."
@param dst : "The decoded input."
@param padding : "The padding character or 0 if none"
@param alphabet : "The alphabet to use"
@require padding < 0xFF : "Invalid padding character"
@require dst.len >= decode_len(src.len, padding) : "Destination buffer too small"
@return "The resulting dst buffer"
@return! DecodingFailure
@return? encoding::INVALID_PADDING, encoding::INVALID_CHARACTER
*>
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
{
if (src.len == 0) return dst[:0];
char* dst_ptr = dst;
@@ -103,12 +101,12 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
{
if (src.len == 0)
{
if (padding > 0) return DecodingFailure.INVALID_PADDING?;
if (padding > 0) return encoding::INVALID_PADDING?;
break;
}
if (src[0] == padding) break;
buf[i] = alphabet.reverse[src[0]];
if (buf[i] == INVALID) return DecodingFailure.INVALID_CHARACTER?;
if (buf[i] == INVALID) return encoding::INVALID_CHARACTER?;
src = src[1..];
}
@@ -152,7 +150,7 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
dst[0] = buf[1] >> 2 | buf[0] << 3;
n++;
default:
return DecodingFailure.INVALID_CHARACTER?;
return encoding::INVALID_CHARACTER?;
}
if (dst.len < 5) break;
dst = dst[5..];
@@ -162,12 +160,12 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
<*
Encode the content of src into dst, which must be properly sized.
@param [in] src "The input to be encoded."
@param [inout] dst "The encoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@require dst.len >= encode_len(src.len, padding) "Destination buffer too small"
@param [in] src : "The input to be encoded."
@param [inout] dst : "The encoded input."
@param padding : "The padding character or 0 if none"
@param alphabet : "The alphabet to use"
@require padding < 0xFF : "Invalid padding character"
@require dst.len >= encode_len(src.len, padding) : "Destination buffer too small"
@return "The encoded size."
*>
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base32Alphabet* alphabet = &STANDARD)
@@ -244,138 +242,7 @@ const char INVALID @private = 0xff;
const int STD_PADDING = '=';
const int NO_PADDING = -1;
fault Base32Error
{
DUPLICATE_IN_ALPHABET,
PADDING_IN_ALPHABET,
INVALID_CHARACTER_IN_ALPHABET,
DESTINATION_TOO_SMALL,
INVALID_PADDING,
CORRUPT_INPUT
}
struct Base32Encoder @deprecated
{
Base32Alphabet alphabet;
char padding;
}
<*
@param encoder "The 32-character alphabet for encoding."
@param padding "Set to a negative value to disable padding."
@require padding < 256
*>
fn void! Base32Encoder.init(&self, Alphabet encoder = STD_ALPHABET, int padding = STD_PADDING)
{
encoder.validate(padding)!;
*self = { .alphabet = { .encoding = (char[32])encoder }, .padding = padding < 0 ? (char)0 : (char)padding};
}
<*
Calculate the length in bytes of the encoded data.
@param n "Length in bytes on input."
@return "Length in bytes of the encoded data."
*>
fn usz Base32Encoder.encode_len(&self, usz n)
{
return encode_len(n, self.padding);
}
<*
Encode the content of src into dst, which must be properly sized.
@param [in] src "The input to be encoded."
@param [inout] dst "The encoded input."
@return "The encoded size."
@return! Base32Error.DESTINATION_TOO_SMALL
*>
fn usz! Base32Encoder.encode(&self, char[] src, char[] dst)
{
usz dn = self.encode_len(src.len);
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
return encode_buffer(src, dst, self.padding, &self.alphabet).len;
}
struct Base32Decoder @deprecated
{
Base32Alphabet alphabet;
char padding;
}
<*
@param decoder "The alphabet used for decoding."
@param padding "Set to a negative value to disable padding."
@require padding < 256
*>
fn void! Base32Decoder.init(&self, Alphabet decoder = STD_ALPHABET, int padding = STD_PADDING)
{
decoder.validate(padding)!;
*self = { .alphabet = { .encoding = (char[32])decoder }, .padding = padding < 0 ? (char)0 : (char)padding };
self.alphabet.reverse[..] = INVALID;
foreach (char i, c : decoder)
{
self.alphabet.reverse[c] = i;
}
}
<*
Calculate the length in bytes of the decoded data.
@param n "Length in bytes of input."
@return "Length in bytes of the decoded data."
*>
fn usz Base32Decoder.decode_len(&self, usz n)
{
return decode_len(n, self.padding);
}
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@return "The decoded size."
@return! Base32Error.DESTINATION_TOO_SMALL, Base32Error.CORRUPT_INPUT
*>
fn usz! Base32Decoder.decode(&self, char[] src, char[] dst)
{
if (src.len == 0) return 0;
usz dn = self.decode_len(src.len);
if (dst.len < dn) return Base32Error.DESTINATION_TOO_SMALL?;
return decode_buffer(src, dst, self.padding, &self.alphabet).len;
}
// Validate the 32-character alphabet to make sure that no character occurs
// twice and that the padding is not present in the alphabet.
fn void! Alphabet.validate(&self, int padding)
{
bool[256] checked;
foreach (c : self)
{
if (checked[c])
{
return Base32Error.DUPLICATE_IN_ALPHABET?;
}
checked[c] = true;
if (c == '\r' || c == '\n')
{
return Base32Error.INVALID_CHARACTER_IN_ALPHABET?;
}
}
if (padding >= 0)
{
char pad = (char)padding;
if (pad == '\r' || pad == '\n')
{
return Base32Error.INVALID_PADDING?;
}
if (checked[pad])
{
return Base32Error.PADDING_IN_ALPHABET?;
}
}
}
distinct Alphabet = char[32];
typedef Alphabet = char[32];
// Standard base32 Alphabet
const Alphabet STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
// Extended Hex Alphabet

View File

@@ -43,29 +43,27 @@ const Base64Alphabet URL = {
const STD_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const URL_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
fn String encode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
fn String encode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, encode_len(src.len, padding));
return encode_buffer(src, dst, padding, alphabet);
}
fn char[]! decode(char[] src, Allocator allocator, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
fn char[]? decode(Allocator allocator, char[] src, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
char[] dst = allocator::alloc_array(allocator, char, decode_len(src.len, padding))!;
return decode_buffer(src, dst, padding, alphabet);
}
fn String encode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::heap(), padding, alphabet);
fn String encode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(code, allocator::temp(), padding, alphabet);
fn char[]! decode_new(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::heap(), padding, alphabet);
fn char[]! decode_temp(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(code, allocator::temp(), padding, alphabet);
fn String tencode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => encode(tmem, code, padding, alphabet);
fn char[]? tdecode(char[] code, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD) @inline => decode(tmem, code, padding, alphabet);
<*
Calculate the size of the encoded data.
@param n "Size of the input to be encoded."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@param n : "Size of the input to be encoded."
@param padding : "The padding character or 0 if none"
@require padding < 0xFF : "Invalid padding character"
@return "The size of the input once encoded."
*>
fn usz encode_len(usz n, char padding)
@@ -77,35 +75,34 @@ fn usz encode_len(usz n, char padding)
<*
Calculate the size of the decoded data.
@param n "Size of the input to be decoded."
@param padding "The padding character or 0 if none"
@require padding < 0xFF "Invalid padding character"
@param n : "Size of the input to be decoded."
@param padding : "The padding character or 0 if none"
@require padding < 0xFF : "Invalid padding character"
@return "The size of the input once decoded."
@return! DecodingFailure.INVALID_PADDING
@return? encoding::INVALID_PADDING
*>
fn usz! decode_len(usz n, char padding)
fn usz? decode_len(usz n, char padding)
{
usz dn = n / 4 * 3;
usz trailing = n % 4;
if (padding)
{
if (trailing != 0) return DecodingFailure.INVALID_PADDING?;
if (trailing != 0) return encoding::INVALID_PADDING?;
// source size is multiple of 4
return dn;
}
if (trailing == 1) return DecodingFailure.INVALID_PADDING?;
if (trailing == 1) return encoding::INVALID_PADDING?;
return dn + trailing * 3 / 4;
}
<*
Encode the content of src into dst, which must be properly sized.
@param src "The input to be encoded."
@param dst "The encoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require padding < 0xFF "Invalid padding character"
@param src : "The input to be encoded."
@param dst : "The encoded input."
@param padding : "The padding character or 0 if none"
@param alphabet : "The alphabet to use"
@require padding < 0xFF : "Invalid padding character"
@return "The encoded size."
@return! Base64Error.DESTINATION_TOO_SMALL
*>
fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
@@ -159,16 +156,16 @@ fn String encode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@param padding "The padding character or 0 if none"
@param alphabet "The alphabet to use"
@require (decode_len(src.len, padding) ?? 0) <= dst.len "Destination buffer too small"
@require padding < 0xFF "Invalid padding character"
@param src : "The input to be decoded."
@param dst : "The decoded input."
@param padding : "The padding character or 0 if none"
@param alphabet : "The alphabet to use"
@require (decode_len(src.len, padding) ?? 0) <= dst.len : "Destination buffer too small"
@require padding < 0xFF : "Invalid padding character"
@return "The decoded data."
@return! DecodingFailure
@return? encoding::INVALID_CHARACTER, encoding::INVALID_PADDING
*>
fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
fn char[]? decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Base64Alphabet* alphabet = &STANDARD)
{
if (src.len == 0) return dst[:0];
usz dn = decode_len(src.len, padding)!;
@@ -199,7 +196,7 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
case c1:
case c2:
case c3:
return DecodingFailure.INVALID_CHARACTER?;
return encoding::INVALID_CHARACTER?;
}
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6 | (uint)c3;
dst[0] = (char)(group >> 16);
@@ -214,7 +211,7 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
src = src[^trailing..];
char c0 = alphabet.reverse[src[0]];
char c1 = alphabet.reverse[src[1]];
if (c0 == 0xFF || c1 == 0xFF) return DecodingFailure.INVALID_PADDING?;
if (c0 == 0xFF || c1 == 0xFF) return encoding::INVALID_PADDING?;
if (!padding)
{
switch (src.len)
@@ -224,7 +221,7 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
dst[0] = (char)(group >> 16);
case 3:
char c2 = alphabet.reverse[src[2]];
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
if (c2 == 0xFF) return encoding::INVALID_CHARACTER?;
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
dst[0] = (char)(group >> 16);
dst[1] = (char)(group >> 8);
@@ -238,13 +235,13 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
switch (padding)
{
case src[2]:
if (src[3] != padding) return DecodingFailure.INVALID_PADDING?;
if (src[3] != padding) return encoding::INVALID_PADDING?;
uint group = (uint)c0 << 18 | (uint)c1 << 12;
dst[0] = (char)(group >> 16);
dn -= 2;
case src[3]:
char c2 = alphabet.reverse[src[2]];
if (c2 == 0xFF) return DecodingFailure.INVALID_CHARACTER?;
if (c2 == 0xFF) return encoding::INVALID_CHARACTER?;
uint group = (uint)c0 << 18 | (uint)c1 << 12 | (uint)c2 << 6;
dst[0] = (char)(group >> 16);
dst[1] = (char)(group >> 8);
@@ -256,146 +253,3 @@ fn char[]! decode_buffer(char[] src, char[] dst, char padding = DEFAULT_PAD, Bas
const MASK @private = 0b111111;
struct Base64Encoder @deprecated
{
char padding;
String alphabet;
}
fault Base64Error
{
DUPLICATE_IN_ALPHABET,
PADDING_IN_ALPHABET,
DESTINATION_TOO_SMALL,
INVALID_PADDING,
INVALID_CHARACTER,
}
<*
@param alphabet "The alphabet used for encoding."
@param padding "Set to a negative value to disable padding."
@require alphabet.len == 64
@require padding < 256
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
*>
fn Base64Encoder*! Base64Encoder.init(&self, String alphabet, int padding = '=')
{
check_alphabet(alphabet, padding)!;
*self = { .padding = padding < 0 ? 0 : (char)padding, .alphabet = alphabet };
return self;
}
<*
Calculate the size of the encoded data.
@param n "Size of the input to be encoded."
@return "The size of the input once encoded."
*>
fn usz Base64Encoder.encode_len(&self, usz n)
{
return encode_len(n, self.padding);
}
<*
Encode the content of src into dst, which must be properly sized.
@param src "The input to be encoded."
@param dst "The encoded input."
@return "The encoded size."
@return! Base64Error.DESTINATION_TOO_SMALL
*>
fn usz! Base64Encoder.encode(&self, char[] src, char[] dst)
{
if (src.len == 0) return 0;
usz dn = self.encode_len(src.len);
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
Base64Alphabet a = { .encoding = self.alphabet[:64] };
return encode_buffer(src, dst, self.padding, &a).len;
}
struct Base64Decoder @deprecated
{
char padding;
Base64Alphabet encoding;
bool init_done;
}
import std;
<*
@param alphabet "The alphabet used for encoding."
@param padding "Set to a negative value to disable padding."
@require alphabet.len == 64
@require padding < 256
@return! Base64Error.DUPLICATE_IN_ALPHABET, Base64Error.PADDING_IN_ALPHABET
*>
fn void! Base64Decoder.init(&self, String alphabet, int padding = '=')
{
self.init_done = true;
check_alphabet(alphabet, padding)!;
*self = { .padding = padding < 0 ? 0 : (char)padding, .encoding.encoding = alphabet[:64] };
self.encoding.reverse[..] = 0xFF;
foreach (i, c : alphabet)
{
self.encoding.reverse[c] = (char)i;
}
}
<*
Calculate the size of the decoded data.
@param n "Size of the input to be decoded."
@return "The size of the input once decoded."
@return! Base64Error.INVALID_PADDING
*>
fn usz! Base64Decoder.decode_len(&self, usz n)
{
return decode_len(n, self.padding) ?? Base64Error.INVALID_PADDING?;
}
<*
Decode the content of src into dst, which must be properly sized.
@param src "The input to be decoded."
@param dst "The decoded input."
@return "The decoded size."
@return! Base64Error.DESTINATION_TOO_SMALL, Base64Error.INVALID_PADDING, Base64Error.INVALID_CHARACTER
*>
fn usz! Base64Decoder.decode(&self, char[] src, char[] dst)
{
if (src.len == 0) return 0;
usz dn = self.decode_len(src.len)!;
if (dst.len < dn) return Base64Error.DESTINATION_TOO_SMALL?;
char[]! decoded = decode_buffer(src, dst, self.padding, &self.encoding);
if (catch err = decoded)
{
case DecodingFailure.INVALID_PADDING:
return Base64Error.INVALID_PADDING?;
case DecodingFailure.INVALID_CHARACTER:
return Base64Error.INVALID_CHARACTER?;
default:
return err?;
}
return decoded.len;
}
// Make sure that all bytes in the alphabet are unique and
// the padding is not present in the alphabet.
fn void! check_alphabet(String alphabet, int padding) @local
{
bool[256] checked;
if (padding < 0)
{
foreach (c : alphabet)
{
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
checked[c] = true;
}
return;
}
char pad = (char)padding;
foreach (c : alphabet)
{
if (c == pad) return Base64Error.PADDING_IN_ALPHABET?;
if (checked[c]) return Base64Error.DUPLICATE_IN_ALPHABET?;
checked[c] = true;
}
}

View File

@@ -15,7 +15,7 @@ struct CsvRow (Printable)
Allocator allocator;
}
fn usz! CsvRow.to_format(&self, Formatter* f) @dynamic
fn usz? CsvRow.to_format(&self, Formatter* f) @dynamic
{
return f.printf("%s", self.list);
}
@@ -38,30 +38,24 @@ fn void CsvReader.init(&self, InStream stream, String separator = ",")
self.stream = stream;
self.separator = separator;
}
fn CsvRow! CsvReader.read_new_row(self)
{
return self.read_row(allocator::heap()) @inline;
}
<*
@param [&inout] allocator
*>
fn CsvRow! CsvReader.read_row(self, Allocator allocator)
fn CsvRow? CsvReader.read_row(self, Allocator allocator)
{
String row = io::readline(self.stream, allocator: allocator)!;
String row = io::readline(allocator, self.stream)!;
defer catch allocator::free(allocator, row);
String[] list = row.split(self.separator, allocator: allocator);
String[] list = row.split(allocator, self.separator);
return { list, row, allocator };
}
fn CsvRow! CsvReader.read_temp_row(self)
fn CsvRow? CsvReader.tread_row(self)
{
return self.read_row(allocator::temp()) @inline;
return self.read_row(tmem) @inline;
}
<*
@require self.allocator != null `Row already freed`
@require self.allocator != null : `Row already freed`
*>
fn void CsvRow.free(&self)
{
@@ -70,26 +64,32 @@ fn void CsvRow.free(&self)
self.allocator = null;
}
fn void! CsvReader.skip_row(self) @maydiscard => @pool()
fn void? CsvReader.skip_row(self) @maydiscard => @pool()
{
(void)io::treadline(self.stream);
}
macro void! CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
macro void? @each_row(InStream stream, String separator = ",", int max_rows = int.max; @body(String[] row)) @maydiscard
{
InStream stream = self.stream;
String sep = self.separator;
while (rows--)
while (max_rows--)
{
@stack_mem(512; Allocator mem)
@stack_mem(512; mem)
{
String! s = io::readline(stream, mem);
String? s = io::readline(mem, stream);
if (catch err = s)
{
if (err == IoError.EOF) return;
if (err == io::EOF) return;
return err?;
}
@body(s.split(sep, allocator: mem));
@body(s.split(mem, separator));
};
}
}
macro void? CsvReader.@each_row(self, int rows = int.max; @body(String[] row)) @maydiscard
{
return @each_row(self.stream, self.separator, rows; row)
{
@body(row);
};
}

View File

@@ -1,7 +1,3 @@
module std::encoding;
fault DecodingFailure
{
INVALID_CHARACTER,
INVALID_PADDING,
}
faultdef INVALID_CHARACTER, INVALID_PADDING;

View File

@@ -8,41 +8,40 @@ fn String encode_buffer(char[] code, char[] buffer)
return (String)buffer[:encode_bytes(code, buffer)];
}
fn char[]! decode_buffer(char[] code, char[] buffer)
fn char[]? decode_buffer(char[] code, char[] buffer)
{
return buffer[:decode_bytes(code, buffer)!];
}
fn String encode(char[] code, Allocator allocator)
fn String encode(Allocator allocator, char[] code)
{
char[] data = allocator::alloc_array(allocator, char, encode_len(code.len));
return (String)data[:encode_bytes(code, data)];
}
fn char[]! decode(char[] code, Allocator allocator)
fn char[]? decode(Allocator allocator, char[] code)
{
char[] data = allocator::alloc_array(allocator, char, decode_len(code.len));
return data[:decode_bytes(code, data)!];
}
fn String encode_new(char[] code) @inline => encode(code, allocator::heap());
fn String encode_temp(char[] code) @inline => encode(code, allocator::temp());
fn char[]! decode_new(char[] code) @inline => decode(code, allocator::heap());
fn char[]! decode_temp(char[] code) @inline => decode(code, allocator::temp());
fn String tencode(char[] code) @inline => encode(tmem, code);
fn char[]? tdecode(char[] code) @inline => decode(tmem, code);
<*
Calculate the size of the encoded data.
@param n "Size of the input to be encoded."
@param n : "Size of the input to be encoded."
@return "The size of the input once encoded."
*>
fn usz encode_len(usz n) => n * 2;
<*
Encode the content of src into dst, which must be properly sized.
@param src "The input to be encoded."
@param dst "The encoded input."
@param src : "The input to be encoded."
@param dst : "The encoded input."
@return "The encoded size."
@require dst.len >= encode_len(src.len) "Destination array is not large enough"
@require dst.len >= encode_len(src.len) : "Destination array is not large enough"
*>
fn usz encode_bytes(char[] src, char[] dst)
{
@@ -58,7 +57,7 @@ fn usz encode_bytes(char[] src, char[] dst)
<*
Calculate the size of the decoded data.
@param n "Size of the input to be decoded."
@param n : "Size of the input to be decoded."
@return "The size of the input once decoded."
*>
macro usz decode_len(usz n) => n / 2;
@@ -69,28 +68,28 @@ macro usz decode_len(usz n) => n / 2;
Expects that src only contains hexadecimal characters and that src has even
length.
@param src "The input to be decoded."
@param dst "The decoded input."
@require src.len % 2 == 0 "src is not of even length"
@require dst.len >= decode_len(src.len) "Destination array is not large enough"
@return! DecodingFailure.INVALID_CHARACTER
@param src : "The input to be decoded."
@param dst : "The decoded input."
@require src.len % 2 == 0 : "src is not of even length"
@require dst.len >= decode_len(src.len) : "Destination array is not large enough"
@return? encoding::INVALID_CHARACTER
*>
fn usz! decode_bytes(char[] src, char[] dst)
fn usz? decode_bytes(char[] src, char[] dst)
{
usz i;
for (usz j = 1; j < src.len; j += 2)
{
char a = HEXREVERSE[src[j - 1]];
char b = HEXREVERSE[src[j]];
if (a > 0x0f || b > 0x0f) return DecodingFailure.INVALID_CHARACTER?;
if (a > 0x0f || b > 0x0f) return encoding::INVALID_CHARACTER?;
dst[i] = (a << 4) | b;
i++;
}
return i;
}
const char[?] HEXALPHABET @private = "0123456789abcdef";
const char[?] HEXREVERSE @private =
const char[*] HEXALPHABET @private = "0123456789abcdef";
const char[*] HEXREVERSE @private =
x`ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff
ffffffffffffffffffffffffffffffff

View File

@@ -6,40 +6,33 @@ import std::io;
import std::ascii;
import std::collections::object;
fault JsonParsingError
faultdef UNEXPECTED_CHARACTER, INVALID_ESCAPE_SEQUENCE, DUPLICATE_MEMBERS, INVALID_NUMBER;
fn Object*? parse_string(Allocator allocator, String s)
{
EOF,
UNEXPECTED_CHARACTER,
INVALID_ESCAPE_SEQUENCE,
DUPLICATE_MEMBERS,
INVALID_NUMBER,
return parse(allocator, (ByteReader){}.init(s));
}
fn Object*! parse_string(String s, Allocator allocator = allocator::heap())
fn Object*? tparse_string(String s)
{
return parse((ByteReader){}.init(s), allocator);
return parse(tmem, (ByteReader){}.init(s));
}
fn Object*! temp_parse_string(String s)
fn Object*? parse(Allocator allocator, InStream s)
{
return parse((ByteReader){}.init(s), allocator::temp());
}
fn Object*! parse(InStream s, Allocator allocator = allocator::heap())
{
@stack_mem(512; Allocator mem)
@stack_mem(512; Allocator smem)
{
JsonContext context = { .last_string = dstring::new_with_capacity(64, mem), .stream = s, .allocator = allocator };
@pool(allocator)
JsonContext context = { .last_string = dstring::new_with_capacity(smem, 64), .stream = s, .allocator = allocator };
@pool()
{
return parse_any(&context);
};
};
}
fn Object*! temp_parse(InStream s)
fn Object*? tparse(InStream s)
{
return parse(s, allocator::temp());
return parse(tmem, s);
}
// -- Implementation follows --
@@ -78,7 +71,7 @@ struct JsonContext @local
}
fn Object*! parse_from_token(JsonContext* context, JsonTokenType token) @local
fn Object*? parse_from_token(JsonContext* context, JsonTokenType token) @local
{
switch (token)
{
@@ -88,25 +81,25 @@ fn Object*! parse_from_token(JsonContext* context, JsonTokenType token) @local
case COMMA:
case RBRACE:
case RBRACKET:
case COLON: return JsonParsingError.UNEXPECTED_CHARACTER?;
case COLON: return UNEXPECTED_CHARACTER?;
case STRING: return object::new_string(context.last_string.str_view(), context.allocator);
case NUMBER: return object::new_float(context.last_number, context.allocator);
case TRUE: return object::new_bool(true);
case FALSE: return object::new_bool(false);
case NULL: return object::new_null();
case EOF: return JsonParsingError.EOF?;
case EOF: return io::EOF?;
}
}
fn Object*! parse_any(JsonContext* context) @local
fn Object*? parse_any(JsonContext* context) @local
{
return parse_from_token(context, advance(context));
}
fn JsonTokenType! lex_number(JsonContext *context, char c) @local
fn JsonTokenType? lex_number(JsonContext *context, char c) @local
{
@stack_mem(256; Allocator mem)
{
DString t = dstring::new_with_capacity(32, allocator: mem);
DString t = dstring::new_with_capacity(mem, 32);
bool negate = c == '-';
if (negate)
{
@@ -137,7 +130,7 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local
t.append(c);
c = read_next(context)!;
}
if (!c.is_digit()) return JsonParsingError.INVALID_NUMBER?;
if (!c.is_digit()) return INVALID_NUMBER?;
while (c.is_digit())
{
t.append(c);
@@ -145,13 +138,13 @@ fn JsonTokenType! lex_number(JsonContext *context, char c) @local
}
}
pushback(context, c);
double! d = t.str_view().to_double() ?? JsonParsingError.INVALID_NUMBER?;
double? d = t.str_view().to_double() ?? INVALID_NUMBER?;
context.last_number = d!;
return NUMBER;
};
}
fn Object*! parse_map(JsonContext* context) @local
fn Object*? parse_map(JsonContext* context) @local
{
Object* map = object::new_obj(context.allocator);
defer catch map.free();
@@ -159,12 +152,12 @@ fn Object*! parse_map(JsonContext* context) @local
@stack_mem(256; Allocator mem)
{
DString temp_key = dstring::new_with_capacity(32, mem);
DString temp_key = dstring::new_with_capacity(mem, 32);
while (token != JsonTokenType.RBRACE)
{
if (token != JsonTokenType.STRING) return JsonParsingError.UNEXPECTED_CHARACTER?;
if (token != JsonTokenType.STRING) return UNEXPECTED_CHARACTER?;
DString string = context.last_string;
if (map.has_key(string.str_view())) return JsonParsingError.DUPLICATE_MEMBERS?;
if (map.has_key(string.str_view())) return DUPLICATE_MEMBERS?;
// Copy the key to our temp holder, since our
// last_string may be used in parse_any
temp_key.clear();
@@ -178,13 +171,13 @@ fn Object*! parse_map(JsonContext* context) @local
token = advance(context)!;
continue;
}
if (token != JsonTokenType.RBRACE) return JsonParsingError.UNEXPECTED_CHARACTER?;
if (token != JsonTokenType.RBRACE) return UNEXPECTED_CHARACTER?;
}
return map;
};
}
fn Object*! parse_array(JsonContext* context) @local
fn Object*? parse_array(JsonContext* context) @local
{
Object* list = object::new_obj(context.allocator);
defer catch list.free();
@@ -199,7 +192,7 @@ fn Object*! parse_array(JsonContext* context) @local
token = advance(context)!;
continue;
}
if (token != JsonTokenType.RBRACKET) return JsonParsingError.UNEXPECTED_CHARACTER?;
if (token != JsonTokenType.RBRACKET) return UNEXPECTED_CHARACTER?;
}
return list;
}
@@ -214,7 +207,7 @@ fn void pushback(JsonContext* context, char c) @local
}
}
fn char! read_next(JsonContext* context) @local
fn char? read_next(JsonContext* context) @local
{
if (context.reached_end) return '\0';
if (context.pushed_back)
@@ -222,14 +215,15 @@ fn char! read_next(JsonContext* context) @local
context.pushed_back = false;
return context.current;
}
char! c = context.stream.read_byte();
char? c = context.stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
if (err == io::EOF)
{
context.reached_end = true;
return '\0';
default:
return err?;
}
return err?;
}
if (c == 0)
{
@@ -238,7 +232,7 @@ fn char! read_next(JsonContext* context) @local
return c;
}
fn JsonTokenType! advance(JsonContext* context) @local
fn JsonTokenType? advance(JsonContext* context) @local
{
char c;
// Skip whitespace
@@ -286,7 +280,7 @@ fn JsonTokenType! advance(JsonContext* context) @local
switch (c)
{
case '\0':
return IoError.EOF?;
return io::EOF?;
case '{':
return LBRACE;
case '}':
@@ -314,25 +308,25 @@ fn JsonTokenType! advance(JsonContext* context) @local
match(context, "ull")!;
return NULL;
default:
return JsonParsingError.UNEXPECTED_CHARACTER?;
return UNEXPECTED_CHARACTER?;
}
}
fn void! match(JsonContext* context, String str) @local
fn void? match(JsonContext* context, String str) @local
{
foreach (c : str)
{
char l = read_next(context)!;
if (l != c) return JsonParsingError.UNEXPECTED_CHARACTER?;
if (l != c) return UNEXPECTED_CHARACTER?;
}
}
fn void! parse_expected(JsonContext* context, JsonTokenType token) @local
fn void? parse_expected(JsonContext* context, JsonTokenType token) @local
{
if (advance(context)! != token) return JsonParsingError.UNEXPECTED_CHARACTER?;
if (advance(context)! != token) return UNEXPECTED_CHARACTER?;
}
fn JsonTokenType! lex_string(JsonContext* context)
fn JsonTokenType? lex_string(JsonContext* context)
{
context.last_string.clear();
while LOOP: (true)
@@ -341,9 +335,9 @@ fn JsonTokenType! lex_string(JsonContext* context)
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
return io::EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
return UNEXPECTED_CHARACTER?;
case '"':
break LOOP;
case '\\':
@@ -356,9 +350,9 @@ fn JsonTokenType! lex_string(JsonContext* context)
switch (c)
{
case '\0':
return JsonParsingError.EOF?;
return io::EOF?;
case 1..31:
return JsonParsingError.UNEXPECTED_CHARACTER?;
return UNEXPECTED_CHARACTER?;
case '"':
case '\\':
case '/':
@@ -378,13 +372,13 @@ fn JsonTokenType! lex_string(JsonContext* context)
for (int i = 0; i < 4; i++)
{
c = read_next(context)!;
if (!c.is_xdigit()) return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
if (!c.is_xdigit()) return INVALID_ESCAPE_SEQUENCE?;
val = val << 4 + (c > '9' ? (c | 32) - 'a' + 10 : c - '0');
}
context.last_string.append_char32(val);
continue;
default:
return JsonParsingError.INVALID_ESCAPE_SEQUENCE?;
return INVALID_ESCAPE_SEQUENCE?;
}
context.last_string.append(c);
}

View File

@@ -1,4 +1,4 @@
module std::experimental::scheduler(<Event>);
module std::experimental::scheduler{Event};
import std::collections, std::thread, std::time;
struct DelayedSchedulerEvent @local
@@ -19,9 +19,9 @@ fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @loca
struct FrameScheduler
{
PriorityQueue(<DelayedSchedulerEvent>) delayed_events;
List(<Event>) events;
List(<Event>) pending_events;
PriorityQueue{DelayedSchedulerEvent} delayed_events;
List{Event} events;
List{Event} pending_events;
bool pending;
Mutex mtx;
}
@@ -71,12 +71,12 @@ fn void FrameScheduler.queue_event(&self, Event event)
@atomic_store(self.pending, true);
};
}
fn Event! FrameScheduler.pop_event(&self)
fn Event? FrameScheduler.pop_event(&self)
{
while (true)
{
if (try event = self.events.pop()) return event;
if (!@atomic_load(self.pending)) return IteratorResult.NO_MORE_ELEMENT?;
if (!@atomic_load(self.pending)) return NO_MORE_ELEMENT?;
self.mtx.@in_lock()
{
self.events.add_all(&self.pending_events);
@@ -88,7 +88,7 @@ fn Event! FrameScheduler.pop_event(&self)
self.events.push(self.delayed_events.pop()!!);
}
@atomic_store(self.pending, self.delayed_events.len() > 0);
if (!self.events.len()) return IteratorResult.NO_MORE_ELEMENT?;
if (!self.events.len()) return NO_MORE_ELEMENT?;
};
}
}

View File

@@ -40,7 +40,7 @@ fn uint Adler32.final(&self)
return (self.b << 16) | self.a;
}
fn uint encode(char[] data)
fn uint hash(char[] data)
{
uint a = 1;
uint b = 0;

View File

@@ -33,7 +33,7 @@ fn uint Crc32.final(&self)
return ~self.result;
}
fn uint encode(char[] data)
fn uint hash(char[] data)
{
uint result = ~(uint)(0);
foreach (char x : data)

View File

@@ -33,7 +33,7 @@ fn ulong Crc64.final(&self)
return self.result;
}
fn ulong encode(char[] data)
fn ulong hash(char[] data)
{
ulong result = (ulong)(0);
foreach (char x : data)

View File

@@ -3,7 +3,7 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::fnv32a;
distinct Fnv32a = uint;
typedef Fnv32a = uint;
const FNV32A_START @private = 0x811c9dc5;
const FNV32A_MUL @private = 0x01000193;
@@ -30,7 +30,7 @@ macro void Fnv32a.update_char(&self, char c)
update(self, c);
}
fn uint encode(char[] data)
fn uint hash(char[] data)
{
uint h = FNV32A_START;
foreach (char x : data)

View File

@@ -3,7 +3,7 @@
// a copy of which can be found in the LICENSE_STDLIB file.
module std::hash::fnv64a;
distinct Fnv64a = ulong;
typedef Fnv64a = ulong;
const FNV64A_START @private = 0xcbf29ce484222325;
const FNV64A_MUL @private = 0x00000100000001b3;
@@ -30,7 +30,7 @@ macro void Fnv64a.update_char(&self, char c)
update(self, c);
}
fn ulong encode(char[] data)
fn ulong hash(char[] data)
{
ulong h = FNV64A_START;
foreach (char x : data)

View File

@@ -1,4 +1,4 @@
module std::hash::hmac(<HashAlg, HASH_BYTES, BLOCK_BYTES>);
module std::hash::hmac{HashAlg, HASH_BYTES, BLOCK_BYTES};
import std::crypto;
struct Hmac
@@ -15,8 +15,8 @@ fn char[HASH_BYTES] hash(char[] key, char[] message)
}
<*
@require output.len > 0 "Output must be greater than zero"
@require output.len < int.max / HASH_BYTES "Output is too large"
@require output.len > 0 : "Output must be greater than zero"
@require output.len < int.max / HASH_BYTES : "Output is too large"
*>
fn void pbkdf2(char[] pw, char[] salt, uint iterations, char[] output)
{

View File

@@ -13,9 +13,9 @@ struct Md5
uint[16] block;
}
def HmacMd5 = Hmac(<Md5, HASH_BYTES, BLOCK_BYTES>);
def hmac = hmac::hash(<Md5, HASH_BYTES, BLOCK_BYTES>);
def pbkdf2 = hmac::pbkdf2(<Md5, HASH_BYTES, BLOCK_BYTES>);
alias HmacMd5 = Hmac{Md5, HASH_BYTES, BLOCK_BYTES};
alias hmac = hmac::hash{Md5, HASH_BYTES, BLOCK_BYTES};
alias pbkdf2 = hmac::pbkdf2{Md5, HASH_BYTES, BLOCK_BYTES};
fn char[HASH_BYTES] hash(char[] data)
{

View File

@@ -18,9 +18,9 @@ struct Sha1
char[BLOCK_BYTES] buffer;
}
def HmacSha1 = Hmac(<Sha1, HASH_BYTES, BLOCK_BYTES>);
def hmac = hmac::hash(<Sha1, HASH_BYTES, BLOCK_BYTES>);
def pbkdf2 = hmac::pbkdf2(<Sha1, HASH_BYTES, BLOCK_BYTES>);
alias HmacSha1 = Hmac{Sha1, HASH_BYTES, BLOCK_BYTES};
alias hmac = hmac::hash{Sha1, HASH_BYTES, BLOCK_BYTES};
alias pbkdf2 = hmac::pbkdf2{Sha1, HASH_BYTES, BLOCK_BYTES};
fn char[HASH_BYTES] hash(char[] data)
{

View File

@@ -34,9 +34,9 @@ struct Sha256
char[BLOCK_SIZE] buffer;
}
def HmacSha256 = Hmac(<Sha256, HASH_SIZE, BLOCK_SIZE>);
def hmac = hmac::hash(<Sha256, HASH_SIZE, BLOCK_SIZE>);
def pbkdf2 = hmac::pbkdf2(<Sha256, HASH_SIZE, BLOCK_SIZE>);
alias HmacSha256 = Hmac{Sha256, HASH_SIZE, BLOCK_SIZE};
alias hmac = hmac::hash{Sha256, HASH_SIZE, BLOCK_SIZE};
alias pbkdf2 = hmac::pbkdf2{Sha256, HASH_SIZE, BLOCK_SIZE};
fn char[HASH_SIZE] hash(char[] data)
{

View File

@@ -21,7 +21,7 @@ fn void BitReader.clear(&self) @inline
@require nbits <= 8
@require self.len + nbits <= uint.sizeof * 8
*>
fn char! BitReader.read_bits(&self, uint nbits)
fn char? BitReader.read_bits(&self, uint nbits)
{
uint bits = self.bits;
if (self.len < nbits)
@@ -54,7 +54,7 @@ fn void BitWriter.init(&self, OutStream byte_writer)
*self = { .writer = byte_writer };
}
fn void! BitWriter.flush(&self)
fn void? BitWriter.flush(&self)
{
if (self.len == 0) return;
@@ -70,7 +70,7 @@ fn void! BitWriter.flush(&self)
<*
@require nbits <= 32
*>
fn void! BitWriter.write_bits(&self, uint bits, uint nbits)
fn void? BitWriter.write_bits(&self, uint bits, uint nbits)
{
if (nbits == 0) return;
while (self.len + nbits > WRITER_BITS)

View File

@@ -9,19 +9,19 @@ struct File (InStream, OutStream)
module std::io::file;
import libc, std::io::path, std::io::os;
fn File! open(String filename, String mode)
fn File? open(String filename, String mode)
{
return from_handle(os::native_fopen(filename, mode));
}
fn File! open_path(Path path, String mode)
fn File? open_path(Path path, String mode)
{
return from_handle(os::native_fopen(path.str_view(), mode));
}
fn bool exists(String file) => @pool()
{
return path::exists(path::temp_new(file)) ?? false;
return os::native_file_or_dir_exists(file);
}
fn File from_handle(CFile file)
@@ -34,18 +34,26 @@ fn bool is_file(String path)
return os::native_is_file(path);
}
fn usz! get_size(String path)
fn bool is_dir(String path)
{
return os::native_is_dir(path);
}
fn usz? get_size(String path)
{
return os::native_file_size(path);
}
fn void! delete(String filename) => os::native_remove(filename) @inline;
fn void? delete(String filename)
{
return os::native_remove(filename) @inline;
}
<*
@require self.file != null
*>
fn void! File.reopen(&self, String filename, String mode)
fn void? File.reopen(&self, String filename, String mode)
{
self.file = os::native_freopen(self.file, filename, mode)!;
}
@@ -53,7 +61,7 @@ fn void! File.reopen(&self, String filename, String mode)
<*
@require self.file != null
*>
fn usz! File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
fn usz? File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
{
os::native_fseek(self.file, offset, seek_mode)!;
return os::native_ftell(self.file);
@@ -65,11 +73,11 @@ Implement later
<*
@require self.file == null
*>
fn void! File.memopen(File* file, char[] data, String mode)
fn void? File.memopen(File* file, char[] data, String mode)
{
@pool()
{
file.file = libc::memopen(data.ptr, data.len, mode.zstr_tcopy(), file.file);
file.file = libc::memopen(data.ptr, data.len, mode.to_temp_zstr(), file.file);
// TODO errors
};
}
@@ -79,7 +87,7 @@ fn void! File.memopen(File* file, char[] data, String mode)
<*
@require self.file != null
*>
fn void! File.write_byte(&self, char c) @dynamic
fn void? File.write_byte(&self, char c) @dynamic
{
return os::native_fputc(c, self.file);
}
@@ -87,15 +95,15 @@ fn void! File.write_byte(&self, char c) @dynamic
<*
@param [&inout] self
*>
fn void! File.close(&self) @inline @dynamic
fn void? File.close(&self) @inline @dynamic
{
if (self.file && libc::fclose(self.file))
{
switch (libc::errno())
{
case errno::ECONNRESET:
case errno::EBADF: return IoError.FILE_NOT_VALID?;
case errno::EINTR: return IoError.INTERRUPTED?;
case errno::EBADF: return io::FILE_NOT_VALID?;
case errno::EINTR: return io::INTERRUPTED?;
case errno::EDQUOT:
case errno::EFAULT:
case errno::EAGAIN:
@@ -103,8 +111,8 @@ fn void! File.close(&self) @inline @dynamic
case errno::ENETDOWN:
case errno::ENETUNREACH:
case errno::ENOSPC:
case errno::EIO: return IoError.INCOMPLETE_WRITE?;
default: return IoError.UNKNOWN_ERROR?;
case errno::EIO: return io::INCOMPLETE_WRITE?;
default: return io::UNKNOWN_ERROR?;
}
}
self.file = null;
@@ -121,16 +129,16 @@ fn bool File.eof(&self) @inline
<*
@param [in] buffer
*>
fn usz! File.read(&self, char[] buffer) @dynamic
fn usz? File.read(&self, char[] buffer) @dynamic
{
return os::native_fread(self.file, buffer);
}
<*
@param [out] buffer
@require self.file != null `File must be initialized`
@require self.file != null : `File must be initialized`
*>
fn usz! File.write(&self, char[] buffer) @dynamic
fn usz? File.write(&self, char[] buffer) @dynamic
{
return os::native_fwrite(self.file, buffer);
}
@@ -145,26 +153,26 @@ fn bool File.isatty(self) @if(env::LIBC)
return libc::isatty(self.fd()) > 0;
}
fn char! File.read_byte(&self) @dynamic
fn char? File.read_byte(&self) @dynamic
{
int c = libc::fgetc(self.file);
if (c == -1) return IoError.EOF?;
if (c == -1) return io::EOF?;
return (char)c;
}
<*
Load up to buffer.len characters. Returns IoError.OVERFLOW if the file is longer
Load up to buffer.len characters. Returns io::OVERFLOW if the file is longer
than the buffer.
@param filename "The path to the file to read"
@param [in] buffer "The buffer to read to"
@param filename : "The path to the file to read"
@param [in] buffer : "The buffer to read to"
*>
fn char[]! load_buffer(String filename, char[] buffer)
fn char[]? load_buffer(String filename, char[] buffer)
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
if (len > buffer.len) return IoError.OVERFLOW?;
if (len > buffer.len) return io::OVERFLOW?;
file.seek(0, SET)!;
usz read = 0;
while (read < len)
@@ -175,9 +183,7 @@ fn char[]! load_buffer(String filename, char[] buffer)
}
fn char[]! load(Allocator allocator, String filename) => load_new(filename, allocator);
fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
fn char[]? load(Allocator allocator, String filename)
{
File file = open(filename, "rb")!;
defer (void)file.close();
@@ -193,16 +199,13 @@ fn char[]! load_new(String filename, Allocator allocator = allocator::heap())
return data[:len];
}
fn char[]! load_path_new(Path path, Allocator allocator = allocator::heap()) => load_new(path.str_view(), allocator);
fn char[]? load_path(Allocator allocator, Path path) => load(allocator, path.str_view());
fn char[]! load_temp(String filename)
{
return load_new(filename, allocator::temp());
}
fn char[]? load_temp(String filename) => load(tmem, filename);
fn char[]! load_path_temp(Path path) => load_temp(path.str_view());
fn char[]? load_path_temp(Path path) => load_temp(path.str_view());
fn void! save(String filename, char[] data)
fn void? save(String filename, char[] data)
{
File file = open(filename, "wb")!;
defer (void)file.close();
@@ -214,9 +217,9 @@ fn void! save(String filename, char[] data)
}
<*
@require self.file != null `File must be initialized`
@require self.file != null : `File must be initialized`
*>
fn void! File.flush(&self) @dynamic
fn void? File.flush(&self) @dynamic
{
libc::fflush(self.file);
}

View File

@@ -6,42 +6,34 @@ const int PRINTF_NTOA_BUFFER_SIZE = 256;
interface Printable
{
fn String to_string(Allocator allocator) @optional;
fn String to_new_string(Allocator allocator) @optional @deprecated("Use to_string");
fn usz! to_format(Formatter* formatter) @optional;
fn String to_constant_string() @optional;
fn usz? to_format(Formatter* formatter) @optional;
}
fault PrintFault
{
BUFFER_EXCEEDED,
INTERNAL_BUFFER_EXCEEDED,
INVALID_FORMAT,
NOT_ENOUGH_ARGUMENTS,
INVALID_ARGUMENT,
}
faultdef BUFFER_EXCEEDED, INTERNAL_BUFFER_EXCEEDED, INVALID_FORMAT,
NOT_ENOUGH_ARGUMENTS, INVALID_ARGUMENT;
def OutputFn = fn void!(void* buffer, char c);
def FloatType = double;
alias OutputFn = fn void?(void* buffer, char c);
alias FloatType = double;
macro bool is_struct_with_default_print($Type)
{
return $Type.kindof == STRUCT
&&& !$defined($Type.to_format)
&&& !$defined($Type.to_new_string)
&&& !$defined($Type.to_string);
&&& !$defined($Type.to_constant_string);
}
<*
Introspect a struct and print it to a formatter
@require @typekind(value) == STRUCT `This macro is only valid on macros`
@require @typekind(value) == STRUCT : `This macro is only valid on macros`
*>
macro usz! struct_to_format(value, Formatter* f, bool $force_dump)
macro usz? struct_to_format(value, Formatter* f, bool $force_dump)
{
var $Type = $typeof(value);
usz total = f.print("{ ")!;
$foreach ($i, $member : $Type.membersof)
$foreach $i, $member : $Type.membersof:
$if $i > 0:
total += f.print(", ")!;
$endif
@@ -58,12 +50,12 @@ macro usz! struct_to_format(value, Formatter* f, bool $force_dump)
return total + f.print(" }");
}
fn usz! ReflectedParam.to_format(&self, Formatter* f) @dynamic
fn usz? ReflectedParam.to_format(&self, Formatter* f) @dynamic
{
return f.printf("[Parameter '%s']", self.name);
}
fn usz! Formatter.printf(&self, String format, args...)
fn usz? Formatter.printf(&self, String format, args...)
{
return self.vprintf(format, args) @inline;
}
@@ -78,7 +70,7 @@ struct Formatter
uint width;
uint prec;
usz idx;
anyfault first_fault;
fault first_fault;
}
}
@@ -98,7 +90,7 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
*self = { .data = data, .out_fn = out_fn};
}
fn usz! Formatter.out(&self, char c) @private
fn usz? Formatter.out(&self, char c) @private
{
if (catch err = self.out_fn(self.data, c))
{
@@ -109,7 +101,7 @@ fn usz! Formatter.out(&self, char c) @private
return 1;
}
fn usz! Formatter.print_with_function(&self, Printable arg)
fn usz? Formatter.print_with_function(&self, Printable arg)
{
if (&arg.to_format)
{
@@ -125,7 +117,7 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
if (!arg) return self.out_substr("(null)");
return arg.to_format(self);
}
if (&arg.to_string)
if (&arg.to_constant_string)
{
PrintFlags old = self.flags;
uint old_width = self.width;
@@ -137,19 +129,16 @@ fn usz! Formatter.print_with_function(&self, Printable arg)
self.prec = old_prec;
}
if (!arg) return self.out_substr("(null)");
@stack_mem(1024; Allocator mem)
{
return self.out_substr(arg.to_string(mem));
};
return self.out_substr(arg.to_constant_string());
}
return SearchResult.MISSING?;
return NOT_FOUND?;
}
fn usz! Formatter.out_unknown(&self, String category, any arg) @private
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
{
return self.out_substr("[") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr("]");
}
fn usz! Formatter.out_str(&self, any arg) @private
fn usz? Formatter.out_str(&self, any arg) @private
{
switch (arg.type.kindof)
{
@@ -157,9 +146,8 @@ fn usz! Formatter.out_str(&self, any arg) @private
return self.out_substr("typeid");
case VOID:
return self.out_substr("void");
case ANYFAULT:
case FAULT:
return self.out_substr((*(anyfault*)arg.ptr).nameof);
return self.out_substr((*(fault*)arg.ptr).nameof);
case INTERFACE:
case ANY:
return self.out_str(*(any*)arg);
@@ -192,9 +180,9 @@ fn usz! Formatter.out_str(&self, any arg) @private
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
}
usz! n = self.print_with_function((Printable)arg);
usz? n = self.print_with_function((Printable)arg);
if (try n) return n;
if (@catch(n) != SearchResult.MISSING) n!;
if (@catch(n) != NOT_FOUND) n!;
switch (arg.type.kindof)
{
case ENUM:
@@ -231,7 +219,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
any deref = any_make(*pointer, inner);
n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != SearchResult.MISSING) n!;
if (@catch(n) != NOT_FOUND) n!;
}
PrintFlags flags = self.flags;
uint width = self.width;
@@ -243,7 +231,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
self.width = 0;
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
case ARRAY:
// this is SomeType[?] so grab the "SomeType"
// this is SomeType[*] so grab the "SomeType"
PrintFlags flags = self.flags;
uint width = self.width;
defer
@@ -277,7 +265,7 @@ fn usz! Formatter.out_str(&self, any arg) @private
}
self.flags = {};
self.width = 0;
// this is SomeType[?] so grab the "SomeType"
// this is SomeType[*] so grab the "SomeType"
typeid inner = arg.type.inner;
usz size = inner.sizeof;
usz vlen = arg.type.len;
@@ -330,33 +318,36 @@ fn usz! Formatter.out_str(&self, any arg) @private
fn void! out_null_fn(void* data @unused, char c @unused) @private
fn void? out_null_fn(void* data @unused, char c @unused) @private
{
}
macro usz! @report_fault(Formatter* f, $fault)
macro usz? @report_fault(Formatter* f, $fault)
{
(void)f.out_substr($fault);
return PrintFault.INVALID_FORMAT?;
return INVALID_FORMAT?;
}
macro usz! @wrap_bad(Formatter* f, #action)
macro usz? @wrap_bad(Formatter* f, #action)
{
usz! len = #action;
usz? len = #action;
if (catch err = len)
{
case PrintFault.BUFFER_EXCEEDED:
case PrintFault.INTERNAL_BUFFER_EXCEEDED:
return f.first_err(err)?;
default:
err = f.first_err(PrintFault.INVALID_ARGUMENT);
f.out_substr("<INVALID>")!;
return err?;
switch (err)
{
case BUFFER_EXCEEDED:
case INTERNAL_BUFFER_EXCEEDED:
return f.first_err(err)?;
default:
err = f.first_err(INVALID_ARGUMENT);
f.out_substr("<INVALID>")!;
return err?;
}
}
return len;
}
fn usz! Formatter.vprintf(&self, String format, any[] anys)
fn usz? Formatter.vprintf(&self, String format, any[] anys)
{
self.first_fault = {};
if (!self.out_fn)
@@ -402,7 +393,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
c = format[i];
}
// evaluate width field
int! w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
int? w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch w) return @report_fault(self, "%ERR");
c = format[i];
if (w < 0)
@@ -417,7 +408,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
{
self.flags.precision = true;
if (++i >= format_len) return @report_fault(self, "<BAD FORMAT>");
int! prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
int? prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i);
if (catch prec) return @report_fault(self, "<BAD FORMAT>");
self.prec = prec < 0 ? 0 : prec;
c = format[i];
@@ -427,7 +418,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
uint base = 0;
if (variant_index >= anys.len)
{
self.first_err(PrintFault.NOT_ENOUGH_ARGUMENTS);
self.first_err(NOT_ENOUGH_ARGUMENTS);
total_len += self.out_substr("<MISSING>")!;
continue;
}
@@ -484,10 +475,9 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
nextcase;
case 'h':
char[] out @noinit;
switch (current)
switch (current.type)
{
case char[]:
out = *current;
case ichar[]:
out = *(char[]*)current;
default:
@@ -535,7 +525,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
self.flags.hash = true;
base = 16;
default:
self.first_err(PrintFault.INVALID_FORMAT);
self.first_err(INVALID_FORMAT);
total_len += self.out_substr("<BAD FORMAT>")!;
continue;
}
@@ -559,7 +549,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
}
fn usz! Formatter.print(&self, String str)
fn usz? Formatter.print(&self, String str)
{
if (!self.out_fn)
{

View File

@@ -4,12 +4,9 @@ import std::math;
const char[16] XDIGITS_H = "0123456789ABCDEF";
const char[16] XDIGITS_L = "0123456789abcdef";
fault FormattingFault
{
BAD_FORMAT
}
faultdef BAD_FORMAT;
fn usz! print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
fn usz? print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
{
char past_10 = (uppercase ? 'A' : 'a') - 10;
usz len = 0;
@@ -25,20 +22,20 @@ fn usz! print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
return len;
}
macro Formatter.first_err(&self, anyfault f)
macro Formatter.first_err(&self, fault f)
{
if (self.first_fault) return self.first_fault;
self.first_fault = f;
return f;
}
fn usz! Formatter.adjust(&self, usz len) @local
fn usz? Formatter.adjust(&self, usz len) @local
{
if (!self.flags.left) return 0;
return self.pad(' ', self.width, len);
}
fn uint128! int_from_any(any arg, bool *is_neg) @private
fn uint128? int_from_any(any arg, bool *is_neg) @private
{
switch (arg.type.kindof)
{
@@ -46,53 +43,52 @@ fn uint128! int_from_any(any arg, bool *is_neg) @private
*is_neg = false;
return (uint128)(uptr)*(void**)arg.ptr;
case TypeKind.DISTINCT:
case TypeKind.ENUM:
return int_from_any(arg.as_inner(), is_neg);
default:
break;
}
*is_neg = false;
switch (arg)
switch (arg.type)
{
case bool:
return (uint128)*arg;
return (uint128)*(bool*)arg;
case ichar:
int val = *arg;
int val = *(ichar*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case short:
int val = *arg;
int val = *(short*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int:
int val = *arg;
int val = *(int*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case long:
long val = *arg;
long val = *(long*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case int128:
int128 val = *arg;
int128 val = *(int128*)arg;
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
case char:
return *arg;
return *(char*)arg;
case ushort:
return *arg;
return *(ushort*)arg;
case uint:
return *arg;
return *(uint*)arg;
case ulong:
return *arg;
return *(ulong*)arg;
case uint128:
return *arg;
return *(uint128*)arg;
case float:
float f = *arg;
float f = *(float*)arg;
return (uint128)((*is_neg = f < 0) ? -f : f);
case double:
double d = *arg;
double d = *(double*)arg;
return (uint128)((*is_neg = d < 0) ? -d : d);
default:
return FormattingFault.BAD_FORMAT?;
return BAD_FORMAT?;
}
}
fn FloatType! float_from_any(any arg) @private
fn FloatType? float_from_any(any arg) @private
{
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
@@ -101,36 +97,36 @@ fn FloatType! float_from_any(any arg) @private
{
return float_from_any(arg.as_inner());
}
switch (arg)
switch (arg.type)
{
case bool:
return (FloatType)*arg;
return (FloatType)*(bool*)arg;
case ichar:
return *arg;
return *(ichar*)arg;
case short:
return *arg;
return *(short*)arg;
case int:
return *arg;
return *(int*)arg;
case long:
return *arg;
return *(long*)arg;
case int128:
return *arg;
return *(int128*)arg;
case char:
return *arg;
return *(char*)arg;
case ushort:
return *arg;
return *(ushort*)arg;
case uint:
return *arg;
return *(uint*)arg;
case ulong:
return *arg;
return *(ulong*)arg;
case uint128:
return *arg;
return *(uint128*)arg;
case float:
return (FloatType)*arg;
return (FloatType)*(float*)arg;
case double:
return (FloatType)*arg;
return (FloatType)*(double*)arg;
default:
return FormattingFault.BAD_FORMAT?;
return BAD_FORMAT?;
}
}
@@ -138,9 +134,9 @@ fn FloatType! float_from_any(any arg) @private
<*
Read a simple integer value, typically for formatting.
@param [inout] len_ptr "the length remaining."
@param [in] buf "the buf to read from."
@param maxlen "the maximum len that can be read."
@param [inout] len_ptr : "the length remaining."
@param [in] buf : "the buf to read from."
@param maxlen : "the maximum len that can be read."
@return "The result of the atoi."
*>
fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
@@ -158,7 +154,7 @@ fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
return i;
}
fn usz! Formatter.out_substr(&self, String str) @private
fn usz? Formatter.out_substr(&self, String str) @private
{
usz l = conv::utf8_codepoints(str);
uint prec = self.prec;
@@ -177,7 +173,7 @@ fn usz! Formatter.out_substr(&self, String str) @private
return index;
}
fn usz! Formatter.pad(&self, char c, isz width, isz len) @inline
fn usz? Formatter.pad(&self, char c, isz width, isz len) @inline
{
isz delta = width - len;
for (isz i = 0; i < delta; i++) self.out(c)!;
@@ -191,7 +187,7 @@ fn char* fmt_u(uint128 x, char* s)
return s;
}
fn usz! Formatter.out_chars(&self, char[] s)
fn usz? Formatter.out_chars(&self, char[] s)
{
foreach (c : s) self.out(c)!;
return s.len;
@@ -205,12 +201,12 @@ enum FloatFormatting
HEX
}
fn usz! Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y);
fn usz! Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y);
fn usz! Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y);
fn usz! Formatter.atoa(&self, double y) => self.floatformat(HEX, y);
fn usz? Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y);
fn usz? Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y);
fn usz? Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y);
fn usz? Formatter.atoa(&self, double y) => self.floatformat(HEX, y);
fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @private
fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @private
{
// This code is heavily based on musl's printf code
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
@@ -285,7 +281,7 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
} while (y);
isz outlen = s - buf;
isz explen = ebuf - estr;
if (p > int.max - 2 - explen - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (p > int.max - 2 - explen - pl) return INTERNAL_BUFFER_EXCEEDED?;
usz len;
usz l = p && outlen - 2 < p
? p + 2 + explen
@@ -453,12 +449,12 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
}
}
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (p > int.max - 1 - (isz)(p || self.flags.hash)) return INTERNAL_BUFFER_EXCEEDED?;
int l = (int)(1 + p + (isz)(p || self.flags.hash));
char* estr @noinit;
if (formatting == FLOAT)
{
if (e > int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (e > int.max - l) return INTERNAL_BUFFER_EXCEEDED?;
if (e > 0) l += e;
}
else
@@ -467,10 +463,10 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
while (ebuf - estr < 2) (--estr)[0] = '0';
*--estr = (e < 0 ? '-' : '+');
*--estr = self.flags.uppercase ? 'E' : 'e';
if (ebuf - estr > (isz)int.max - l) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (ebuf - estr > (isz)int.max - l) return INTERNAL_BUFFER_EXCEEDED?;
l += (int)(ebuf - estr);
}
if (l > int.max - pl) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (l > int.max - pl) return INTERNAL_BUFFER_EXCEEDED?;
usz len;
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
@@ -528,7 +524,7 @@ fn usz! Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
return len;
}
fn usz! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
{
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
usz len;
@@ -542,7 +538,7 @@ fn usz! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
char past_10 = (self.flags.uppercase ? 'A' : 'a') - 10;
do
{
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED?;
char digit = (char)(value % base);
buf[len++] = digit + (digit < 10 ? '0' : past_10);
value /= base;
@@ -552,7 +548,7 @@ fn usz! Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
return self.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private
fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private
{
// pad leading zeros
if (!self.flags.left)
@@ -560,12 +556,12 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
if (self.width && self.flags.zeropad && (negative || self.flags.plus || self.flags.space)) self.width--;
while (len < self.prec)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
while (self.flags.zeropad && len < self.width)
{
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '0';
}
}
@@ -580,7 +576,7 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
}
if (base != 10)
{
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len + 1 >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
switch (base)
{
case 16:
@@ -599,13 +595,13 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
switch (true)
{
case negative:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '-';
case self.flags.plus:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = '+';
case self.flags.space:
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED?;
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED?;
buf[len++] = ' ';
}
if (len) self.out_reverse(buf[:len])!;
@@ -613,13 +609,13 @@ fn usz! Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
}
fn usz! Formatter.ntoa_any(&self, any arg, uint base) @private
fn usz? Formatter.ntoa_any(&self, any arg, uint base) @private
{
bool is_neg;
return self.ntoa(int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
}
fn usz! Formatter.out_char(&self, any arg) @private
fn usz? Formatter.out_char(&self, any arg) @private
{
if (!arg.type.kindof.is_int())
{
@@ -653,7 +649,7 @@ fn usz! Formatter.out_char(&self, any arg) @private
}
fn usz! Formatter.out_reverse(&self, char[] buf) @private
fn usz? Formatter.out_reverse(&self, char[] buf) @private
{
usz n;
usz buffer_start_idx = self.idx;
@@ -672,7 +668,7 @@ fn usz! Formatter.out_reverse(&self, char[] buf) @private
}
fn int! printf_parse_format_field(
fn int? printf_parse_format_field(
any* args_ptr, usz args_len, usz* args_index_ptr,
char* format_ptr, usz format_len, usz* index_ptr) @inline @private
{
@@ -680,10 +676,10 @@ fn int! printf_parse_format_field(
if (c.is_digit()) return simple_atoi(format_ptr, format_len, index_ptr);
if (c != '*') return 0;
usz len = ++(*index_ptr);
if (len >= format_len) return FormattingFault.BAD_FORMAT?;
if (*args_index_ptr >= args_len) return FormattingFault.BAD_FORMAT?;
if (len >= format_len) return BAD_FORMAT?;
if (*args_index_ptr >= args_len) return BAD_FORMAT?;
any val = args_ptr[(*args_index_ptr)++];
if (!val.type.kindof.is_int()) return FormattingFault.BAD_FORMAT?;
uint! intval = types::any_to_int(val, int);
return intval ?? FormattingFault.BAD_FORMAT?;
if (!val.type.kindof.is_int()) return BAD_FORMAT?;
uint? intval = types::any_to_int(val, int);
return intval ?? BAD_FORMAT?;
}

View File

@@ -11,8 +11,7 @@ enum Seek
END
}
fault IoError
{
faultdef
ALREADY_EXISTS,
BUSY,
CANNOT_READ_DIR,
@@ -41,8 +40,7 @@ fault IoError
UNEXPECTED_EOF,
UNKNOWN_ERROR,
UNSUPPORTED_OPERATION,
WOULD_BLOCK,
}
WOULD_BLOCK;
<*
@@ -50,12 +48,12 @@ fault IoError
or to the end of the stream, whatever comes first.
"\r" will be filtered from the String.
@param stream `The stream to read from.`
@require @is_instream(stream) `The stream must implement InStream.`
@param [inout] allocator `the allocator to use.`
@param stream : `The stream to read from.`
@require @is_instream(stream) : `The stream must implement InStream.`
@param [inout] allocator : `the allocator to use.`
@return `The string containing the data read.`
*>
macro String! readline(stream = io::stdin(), Allocator allocator = allocator::heap())
macro String? readline(Allocator allocator, stream = io::stdin())
{
bool $is_stream = @typeis(stream, InStream);
$if $is_stream:
@@ -66,20 +64,20 @@ macro String! readline(stream = io::stdin(), Allocator allocator = allocator::he
$endif
if (val == '\n') return "";
@pool(allocator)
@pool()
{
DString str = dstring::temp_with_capacity(256);
if (val != '\r') str.append(val);
while (1)
{
$if $is_stream:
char! c = func((void*)stream);
char? c = func((void*)stream);
$else
char! c = stream.read_byte();
char? c = stream.read_byte();
$endif
if (catch err = c)
{
if (err == IoError.EOF) break;
if (err == io::EOF) break;
return err?;
}
if (c == '\r') continue;
@@ -94,27 +92,27 @@ macro String! readline(stream = io::stdin(), Allocator allocator = allocator::he
Reads a string, see `readline`, except the it is allocated
on the temporary allocator and does not need to be freed.
@param stream `The stream to read from.`
@require @is_instream(stream) `The stream must implement InStream.`
@param stream : `The stream to read from.`
@require @is_instream(stream) : `The stream must implement InStream.`
@return `The temporary string containing the data read.`
*>
macro String! treadline(stream = io::stdin())
macro String? treadline(stream = io::stdin())
{
return readline(stream, allocator::temp()) @inline;
return readline(tmem, stream) @inline;
}
<*
Print a value to a stream.
@param out `the stream to print to`
@param x `the value to print`
@require @is_outstream(out) `The output must implement OutStream.`
@param out : `the stream to print to`
@param x : `the value to print`
@require @is_outstream(out) : `The output must implement OutStream.`
@return `the number of bytes printed.`
*>
macro usz! fprint(out, x)
macro usz? fprint(out, x)
{
var $Type = $typeof(x);
$switch ($Type)
$switch $Type:
$case String: return out.write(x);
$case ZString: return out.write(x.str_view());
$case DString: return out.write(x.str_view());
@@ -137,11 +135,11 @@ macro usz! fprint(out, x)
Prints using a 'printf'-style formatting string.
See `printf` for details on formatting.
@param [inout] out `The OutStream to print to`
@param [in] format `The printf-style format string`
@param [inout] out : `The OutStream to print to`
@param [in] format : `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! fprintf(OutStream out, String format, args...)
fn usz? fprintf(OutStream out, String format, args...) @format(1)
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
@@ -152,11 +150,11 @@ fn usz! fprintf(OutStream out, String format, args...)
Prints using a 'printf'-style formatting string,
appending '\n' at the end. See `printf`.
@param [inout] out `The OutStream to print to`
@param [in] format `The printf-style format string`
@param [inout] out : `The OutStream to print to`
@param [in] format : `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
fn usz? fprintfn(OutStream out, String format, args...) @format(1) @maydiscard
{
Formatter formatter;
formatter.init(&out_putstream_fn, &out);
@@ -167,13 +165,13 @@ fn usz! fprintfn(OutStream out, String format, args...) @maydiscard
}
<*
@require @is_outstream(out) "The output must implement OutStream"
@require @is_outstream(out) : "The output must implement OutStream"
*>
macro usz! fprintn(out, x = "")
macro usz? fprintn(out, x = "")
{
usz len = fprint(out, x)!;
out.write_byte('\n')!;
$switch
$switch:
$case @typeid(out) == OutStream.typeid:
if (&out.flush) out.flush()!;
$case $defined(out.flush):
@@ -193,7 +191,7 @@ macro void print(x)
<*
Print any value to stdout, appending an '\n after.
@param x "The value to print"
@param x : "The value to print"
*>
macro void printn(x = "")
{
@@ -211,7 +209,7 @@ macro void eprint(x)
<*
Print any value to stderr, appending an '\n after.
@param x "The value to print"
@param x : "The value to print"
*>
macro void eprintn(x)
{
@@ -219,13 +217,13 @@ macro void eprintn(x)
}
fn void! out_putstream_fn(void* data, char c) @private
fn void? out_putstream_fn(void* data, char c) @private
{
OutStream* stream = data;
return (*stream).write_byte(c);
}
fn void! out_putchar_fn(void* data @unused, char c) @private
fn void? out_putchar_fn(void* data @unused, char c) @private
{
$if env::TESTING:
// HACK: this is used for the purpose of unit test output hijacking
@@ -248,10 +246,10 @@ fn void! out_putchar_fn(void* data @unused, char c) @private
To create a custom output for a type, implement
the Printable interface.
@param [in] format `The printf-style format string`
@param [in] format : `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! printf(String format, args...) @maydiscard
fn usz? printf(String format, args...) @format(0) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
@@ -262,14 +260,14 @@ fn usz! printf(String format, args...) @maydiscard
Prints using a 'printf'-style formatting string,
appending '\n' at the end. See `printf`.
@param [in] format `The printf-style format string`
@param [in] format : `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! printfn(String format, args...) @maydiscard
fn usz? printfn(String format, args...) @format(0) @maydiscard
{
Formatter formatter;
formatter.init(&out_putchar_fn);
usz! len = formatter.vprintf(format, args);
usz? len = formatter.vprintf(format, args);
out_putchar_fn(null, '\n')!;
io::stdout().flush()!;
return len + 1;
@@ -279,10 +277,10 @@ fn usz! printfn(String format, args...) @maydiscard
Prints using a 'printf'-style formatting string
to stderr.
@param [in] format `The printf-style format string`
@param [in] format : `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! eprintf(String format, args...) @maydiscard
fn usz? eprintf(String format, args...) @maydiscard
{
Formatter formatter;
OutStream stream = stderr();
@@ -295,15 +293,15 @@ fn usz! eprintf(String format, args...) @maydiscard
Prints using a 'printf'-style formatting string,
to stderr appending '\n' at the end. See `printf`.
@param [in] format `The printf-style format string`
@param [in] format : `The printf-style format string`
@return `the number of characters printed`
*>
fn usz! eprintfn(String format, args...) @maydiscard
fn usz? eprintfn(String format, args...) @maydiscard
{
Formatter formatter;
OutStream stream = stderr();
formatter.init(&out_putstream_fn, &stream);
usz! len = formatter.vprintf(format, args);
usz? len = formatter.vprintf(format, args);
stderr().write_byte('\n')!;
stderr().flush()!;
return len + 1;
@@ -313,11 +311,11 @@ fn usz! eprintfn(String format, args...) @maydiscard
Prints using a 'printf'-style formatting string,
to a string buffer. See `printf`.
@param [inout] buffer `The buffer to print to`
@param [in] format `The printf-style format string`
@param [inout] buffer : `The buffer to print to`
@param [in] format : `The printf-style format string`
@return `a slice formed from the "buffer" with the resulting length.`
*>
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
fn char[]? bprintf(char[] buffer, String format, args...) @maydiscard
{
Formatter formatter;
BufferData data = { .buffer = buffer };
@@ -327,10 +325,10 @@ fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
}
// Used to print to a buffer.
fn void! out_buffer_fn(void *data, char c) @private
fn void? out_buffer_fn(void *data, char c) @private
{
BufferData *buffer_data = data;
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
if (buffer_data.written >= buffer_data.buffer.len) return BUFFER_EXCEEDED?;
buffer_data.buffer[buffer_data.written++] = c;
}

View File

@@ -1,20 +1,20 @@
module std::io::os;
import std::io::path, libc, std::os;
macro void! native_chdir(Path path)
macro void? native_chdir(Path path)
{
$switch
$switch:
$case env::POSIX:
if (posix::chdir(path.as_zstr()))
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR?;
case errno::ENOENT: return IoError.FILE_NOT_FOUND?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
case errno::EACCES: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::ENOTDIR: return io::FILE_NOT_DIR?;
case errno::ENOENT: return io::FILE_NOT_FOUND?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
default: return io::GENERAL_ERROR?;
}
}
$case env::WIN32:
@@ -23,8 +23,8 @@ macro void! native_chdir(Path path)
// TODO improve with better error handling.
if (win32::setCurrentDirectoryW(path.str_view().to_temp_utf16()!!)) return;
};
return IoError.GENERAL_ERROR?;
return io::GENERAL_ERROR?;
$default:
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
$endswitch
}

View File

@@ -5,7 +5,7 @@ import libc;
@require mode.len > 0
@require filename.len > 0
*>
fn void*! native_fopen(String filename, String mode) @inline => @pool()
fn void*? native_fopen(String filename, String mode) @inline => @pool()
{
$if env::WIN32:
void* file = libc::_wfopen(filename.to_temp_wstring(), mode.to_temp_wstring())!;
@@ -15,7 +15,7 @@ fn void*! native_fopen(String filename, String mode) @inline => @pool()
return file ?: file_open_errno()?;
}
fn void! native_remove(String filename) => @pool()
fn void? native_remove(String filename) => @pool()
{
$if env::WIN32:
CInt result = libc::_wremove(filename.to_temp_wstring())!;
@@ -27,10 +27,10 @@ fn void! native_remove(String filename) => @pool()
switch (libc::errno())
{
case errno::ENOENT:
return IoError.FILE_NOT_FOUND?;
return io::FILE_NOT_FOUND?;
case errno::EACCES:
default:
return IoError.FILE_CANNOT_DELETE?;
return io::FILE_CANNOT_DELETE?;
}
}
}
@@ -39,7 +39,7 @@ fn void! native_remove(String filename) => @pool()
@require mode.len > 0
@require filename.len > 0
*>
fn void*! native_freopen(void* file, String filename, String mode) @inline => @pool()
fn void*? native_freopen(void* file, String filename, String mode) @inline => @pool()
{
$if env::WIN32:
file = libc::_wfreopen(filename.to_temp_wstring(), mode.to_temp_wstring(), file)!;
@@ -49,77 +49,77 @@ fn void*! native_freopen(void* file, String filename, String mode) @inline => @p
return file ?: file_open_errno()?;
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()?;
}
fn usz! native_ftell(CFile file) @inline
fn usz? native_ftell(CFile file) @inline
{
long index = libc::ftell(file);
return index >= 0 ? (usz)index : file_seek_errno()?;
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
fn usz? native_fwrite(CFile file, char[] buffer) @inline
{
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
}
fn void! native_fputc(CInt c, CFile stream) @inline
fn void? native_fputc(CInt c, CFile stream) @inline
{
if (libc::fputc(c, stream) == libc::EOF) return IoError.EOF?;
if (libc::fputc(c, stream) == libc::EOF) return io::EOF?;
}
fn usz! native_fread(CFile file, char[] buffer) @inline
fn usz? native_fread(CFile file, char[] buffer) @inline
{
return libc::fread(buffer.ptr, 1, buffer.len, file);
}
macro anyfault file_open_errno() @local
macro fault file_open_errno() @local
{
switch (libc::errno())
{
case errno::EACCES: return IoError.NO_PERMISSION;
case errno::EDQUOT: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EEXIST: return IoError.ALREADY_EXISTS;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFAULT: return IoError.GENERAL_ERROR;
case errno::EISDIR: return IoError.FILE_IS_DIR;
case errno::ELOOP: return IoError.SYMLINK_FAILED;
case errno::EMFILE: return IoError.TOO_MANY_DESCRIPTORS;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG;
case errno::ENFILE: return IoError.OUT_OF_SPACE;
case errno::ENOTDIR: return IoError.FILE_NOT_DIR;
case errno::ENOENT: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::EROFS: return IoError.READ_ONLY;
case errno::EOPNOTSUPP: return IoError.UNSUPPORTED_OPERATION;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EWOULDBLOCK: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
case errno::EACCES: return io::NO_PERMISSION;
case errno::EDQUOT: return io::OUT_OF_SPACE;
case errno::EBADF: return io::FILE_NOT_VALID;
case errno::EEXIST: return io::ALREADY_EXISTS;
case errno::EINTR: return io::INTERRUPTED;
case errno::EFAULT: return io::GENERAL_ERROR;
case errno::EISDIR: return io::FILE_IS_DIR;
case errno::ELOOP: return io::SYMLINK_FAILED;
case errno::EMFILE: return io::TOO_MANY_DESCRIPTORS;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG;
case errno::ENFILE: return io::OUT_OF_SPACE;
case errno::ENOTDIR: return io::FILE_NOT_DIR;
case errno::ENOENT: return io::FILE_NOT_FOUND;
case errno::ENOSPC: return io::OUT_OF_SPACE;
case errno::ENXIO: return io::FILE_NOT_FOUND;
case errno::EOVERFLOW: return io::OVERFLOW;
case errno::EROFS: return io::READ_ONLY;
case errno::EOPNOTSUPP: return io::UNSUPPORTED_OPERATION;
case errno::EIO: return io::INCOMPLETE_WRITE;
case errno::EWOULDBLOCK: return io::WOULD_BLOCK;
default: return io::UNKNOWN_ERROR;
}
}
macro anyfault file_seek_errno() @local
macro fault file_seek_errno() @local
{
switch (libc::errno())
{
case errno::ESPIPE: return IoError.FILE_IS_PIPE;
case errno::EPIPE: return IoError.FILE_IS_PIPE;
case errno::EOVERFLOW: return IoError.OVERFLOW;
case errno::ENXIO: return IoError.FILE_NOT_FOUND;
case errno::ENOSPC: return IoError.OUT_OF_SPACE;
case errno::EIO: return IoError.INCOMPLETE_WRITE;
case errno::EINVAL: return IoError.INVALID_POSITION;
case errno::EINTR: return IoError.INTERRUPTED;
case errno::EFBIG: return IoError.OUT_OF_SPACE;
case errno::EBADF: return IoError.FILE_NOT_VALID;
case errno::EAGAIN: return IoError.WOULD_BLOCK;
default: return IoError.UNKNOWN_ERROR;
case errno::ESPIPE: return io::FILE_IS_PIPE;
case errno::EPIPE: return io::FILE_IS_PIPE;
case errno::EOVERFLOW: return io::OVERFLOW;
case errno::ENXIO: return io::FILE_NOT_FOUND;
case errno::ENOSPC: return io::OUT_OF_SPACE;
case errno::EIO: return io::INCOMPLETE_WRITE;
case errno::EINVAL: return io::INVALID_POSITION;
case errno::EINTR: return io::INTERRUPTED;
case errno::EFBIG: return io::OUT_OF_SPACE;
case errno::EBADF: return io::FILE_NOT_VALID;
case errno::EAGAIN: return io::WOULD_BLOCK;
default: return io::UNKNOWN_ERROR;
}
}

View File

@@ -1,15 +1,15 @@
module std::io::os @if(env::NO_LIBC);
import libc;
def FopenFn = fn void*!(String, String);
def FreopenFn = fn void*!(void*, String, String);
def FcloseFn = fn void!(void*);
def FseekFn = fn void!(void*, isz, Seek);
def FtellFn = fn usz!(void*);
def FwriteFn = fn usz!(void*, char[] buffer);
def FreadFn = fn usz!(void*, char[] buffer);
def RemoveFn = fn void!(String);
def FputcFn = fn void!(int, void*);
alias FopenFn = fn void*?(String, String);
alias FreopenFn = fn void*?(void*, String, String);
alias FcloseFn = fn void?(void*);
alias FseekFn = fn void?(void*, isz, Seek);
alias FtellFn = fn usz?(void*);
alias FwriteFn = fn usz?(void*, char[] buffer);
alias FreadFn = fn usz?(void*, char[] buffer);
alias RemoveFn = fn void?(String);
alias FputcFn = fn void?(int, void*);
FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn));
FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn));
@@ -25,10 +25,10 @@ FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn));
@require mode.len > 0
@require filename.len > 0
*>
fn void*! native_fopen(String filename, String mode) @inline
fn void*? native_fopen(String filename, String mode) @inline
{
if (native_fopen_fn) return native_fopen_fn(filename, mode);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
<*
@@ -36,48 +36,48 @@ fn void*! native_fopen(String filename, String mode) @inline
@require filename.len > 0
*>
fn void! native_remove(String filename) @inline
fn void? native_remove(String filename) @inline
{
if (native_remove_fn) return native_remove_fn(filename);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
<*
@require mode.len > 0
@require filename.len > 0
*>
fn void*! native_freopen(void* file, String filename, String mode) @inline
fn void*? native_freopen(void* file, String filename, String mode) @inline
{
if (native_freopen_fn) return native_freopen_fn(file, filename, mode);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
{
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
fn usz! native_ftell(CFile file) @inline
fn usz? native_ftell(CFile file) @inline
{
if (native_ftell_fn) return native_ftell_fn(file);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
fn usz! native_fwrite(CFile file, char[] buffer) @inline
fn usz? native_fwrite(CFile file, char[] buffer) @inline
{
if (native_fwrite_fn) return native_fwrite_fn(file, buffer);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
fn usz! native_fread(CFile file, char[] buffer) @inline
fn usz? native_fread(CFile file, char[] buffer) @inline
{
if (native_fread_fn) return native_fread_fn(file, buffer);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}
fn void! native_fputc(CInt c, CFile stream) @inline
fn void? native_fputc(CInt c, CFile stream) @inline
{
if (native_fputc_fn) return native_fputc_fn(c, stream);
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}

View File

@@ -1,7 +1,7 @@
module std::io::os;
import libc, std::os, std::io;
fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool()
fn void? native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || env::BSD_FAMILY) => @pool()
{
$if env::DARWIN || env::LINUX || env::BSD_FAMILY:
int res = libc::stat(path.zstr_tcopy(), stat);
@@ -14,30 +14,30 @@ fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX || e
switch (libc::errno())
{
case errno::EBADF:
return IoError.FILE_NOT_VALID?;
return io::FILE_NOT_VALID?;
case errno::EFAULT:
unreachable("Invalid stat");
case errno::EIO:
return IoError.GENERAL_ERROR?;
return io::GENERAL_ERROR?;
case errno::EACCES:
return IoError.NO_PERMISSION?;
return io::NO_PERMISSION?;
case errno::ELOOP:
return IoError.NO_PERMISSION?;
return io::NO_PERMISSION?;
case errno::ENAMETOOLONG:
return IoError.NAME_TOO_LONG?;
return io::NAME_TOO_LONG?;
case errno::ENOENT:
return IoError.FILE_NOT_FOUND?;
return io::FILE_NOT_FOUND?;
case errno::ENOTDIR:
return IoError.FILE_NOT_DIR?;
return io::FILE_NOT_DIR?;
case errno::EOVERFLOW:
return IoError.GENERAL_ERROR?;
return io::GENERAL_ERROR?;
default:
return IoError.UNKNOWN_ERROR?;
return io::UNKNOWN_ERROR?;
}
}
}
fn usz! native_file_size(String path) @if(env::WIN32) => @pool()
fn usz? native_file_size(String path) @if(env::WIN32) => @pool()
{
Win32_FILE_ATTRIBUTE_DATA data;
win32::getFileAttributesExW(path.to_temp_wstring()!, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data);
@@ -47,14 +47,14 @@ fn usz! native_file_size(String path) @if(env::WIN32) => @pool()
return (usz)size.quadPart;
}
fn usz! native_file_size(String path) @if(!env::WIN32 && !env::DARWIN)
fn usz? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN)
{
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)!;
}
fn usz! native_file_size(String path) @if(env::DARWIN)
fn usz? native_file_size(String path) @if(env::DARWIN)
{
Stat stat;
native_stat(&stat, path)!;
@@ -63,7 +63,7 @@ fn usz! native_file_size(String path) @if(env::DARWIN)
fn bool native_file_or_dir_exists(String path)
{
$switch
$switch:
$case env::DARWIN:
$case env::FREEBSD:
$case env::NETBSD:
@@ -88,7 +88,7 @@ fn bool native_file_or_dir_exists(String path)
fn bool native_is_file(String path)
{
$switch
$switch:
$case env::DARWIN:
$case env::FREEBSD:
$case env::NETBSD:
@@ -97,7 +97,7 @@ fn bool native_is_file(String path)
Stat stat;
return @ok(native_stat(&stat, path)) && libc_S_ISTYPE(stat.st_mode, libc::S_IFREG);
$default:
File! f = file::open(path, "r");
File? f = file::open(path, "r");
defer (void)f.close();
return @ok(f);
$endswitch

View File

@@ -1,9 +1,9 @@
module std::io::os;
import libc, std::os;
macro String! getcwd(Allocator allocator = allocator::heap())
macro String? getcwd(Allocator allocator)
{
$switch
$switch:
$case env::WIN32:
const DEFAULT_BUFFER = 256;
Char16[DEFAULT_BUFFER] buffer;
@@ -12,12 +12,12 @@ macro String! getcwd(Allocator allocator = allocator::heap())
defer if (free) libc::free(res);
if (!res)
{
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
if (libc::errno() != errno::ERANGE) return io::GENERAL_ERROR?;
res = win32::_wgetcwd(null, 0);
free = true;
}
Char16[] str16 = res[:win32::wcslen(res)];
return string::new_from_utf16(str16, allocator);
return string::from_utf16(allocator, str16);
$case env::POSIX:
const usz DEFAULT_BUFFER = 256;
@@ -27,7 +27,7 @@ macro String! getcwd(Allocator allocator = allocator::heap())
if (!res)
{
// Improve error
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR?;
if (libc::errno() != errno::ERANGE) return io::GENERAL_ERROR?;
res = posix::getcwd(null, 0);
free = true;
}
@@ -35,7 +35,7 @@ macro String! getcwd(Allocator allocator = allocator::heap())
return res.copy(allocator);
$default:
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
$endswitch
}

View File

@@ -1,13 +1,13 @@
module std::io::os @if(env::POSIX);
import std::io, std::os;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
{
PathList list;
list.init(allocator);
DIRPtr directory = posix::opendir(dir.str_view() ? dir.as_zstr() : (ZString)".");
defer if (directory) posix::closedir(directory);
if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?;
if (!directory) return (path::is_dir(dir) ? io::CANNOT_READ_DIR : io::FILE_NOT_DIR)?;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
@@ -15,7 +15,7 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
if (!name || name == "." || name == "..") continue;
if (entry.d_type == posix::DT_LNK && no_symlinks) continue;
if (entry.d_type == posix::DT_DIR && no_dirs) continue;
Path path = path::new(name, allocator)!!;
Path path = path::new(allocator, name)!!;
list.push(path);
}
return list;
@@ -24,26 +24,26 @@ fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Al
module std::io::os @if(env::WIN32);
import std::time, std::os, std::io;
fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
fn PathList? native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator allocator)
{
PathList list;
list.init(allocator);
@pool(allocator)
@pool()
{
WString result = dir.str_view().tconcat(`\*`).to_temp_wstring()!!;
Win32_WIN32_FIND_DATAW find_data;
Win32_HANDLE find = win32::findFirstFileW(result, &find_data);
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
if (find == win32::INVALID_HANDLE_VALUE) return io::CANNOT_READ_DIR?;
defer win32::findClose(find);
do
{
if (no_dirs && (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)) continue;
@pool(allocator)
@pool()
{
String filename = string::temp_from_wstring((WString)&find_data.cFileName)!;
String filename = string::tfrom_wstring((WString)&find_data.cFileName)!;
if (filename == ".." || filename == ".") continue;
list.push(path::new(filename, allocator)!);
list.push(path::new(allocator, filename)!);
};
} while (win32::findNextFileW(find, &find_data));
return list;

View File

@@ -5,9 +5,9 @@ import std::os::win32;
import std::os::posix;
macro bool! native_mkdir(Path path, MkdirPermissions permissions)
macro bool? native_mkdir(Path path, MkdirPermissions permissions)
{
$switch
$switch:
$case env::POSIX:
if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true;
switch (libc::errno())
@@ -15,15 +15,15 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions)
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::EFAULT: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::EDQUOT:
case errno::ENOSPC: return IoError.OUT_OF_SPACE?;
case errno::ENOSPC: return io::OUT_OF_SPACE?;
case errno::EISDIR:
case errno::EEXIST: return false;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?;
default: return IoError.GENERAL_ERROR?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
case errno::ENOTDIR: return io::FILE_NOT_FOUND?;
default: return io::GENERAL_ERROR?;
}
$case env::WIN32:
@pool()
@@ -33,18 +33,18 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions)
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
return io::NO_PERMISSION?;
case win32::ERROR_DISK_FULL:
return IoError.OUT_OF_SPACE?;
return io::OUT_OF_SPACE?;
case win32::ERROR_ALREADY_EXISTS:
return false;
case win32::ERROR_PATH_NOT_FOUND:
return IoError.FILE_NOT_FOUND?;
return io::FILE_NOT_FOUND?;
default:
return IoError.GENERAL_ERROR?;
return io::GENERAL_ERROR?;
}
};
$default:
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
$endswitch
}

View File

@@ -4,24 +4,24 @@ import std::io::path;
import std::os::win32;
import std::os::posix;
macro bool! native_rmdir(Path path)
macro bool? native_rmdir(Path path)
{
$switch
$switch:
$case env::POSIX:
if (!posix::rmdir(path.as_zstr())) return true;
switch (libc::errno())
{
case errno::EBUSY: return IoError.BUSY?;
case errno::EBUSY: return io::BUSY?;
case errno::EACCES:
case errno::EPERM:
case errno::EROFS:
case errno::EFAULT: return IoError.NO_PERMISSION?;
case errno::ENAMETOOLONG: return IoError.NAME_TOO_LONG?;
case errno::EFAULT: return io::NO_PERMISSION?;
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG?;
case errno::ENOTDIR:
case errno::ENOENT: return false;
case errno::ENOTEMPTY: return IoError.DIR_NOT_EMPTY?;
case errno::ELOOP: return IoError.SYMLINK_FAILED?;
default: return IoError.GENERAL_ERROR?;
case errno::ENOTEMPTY: return io::DIR_NOT_EMPTY?;
case errno::ELOOP: return io::SYMLINK_FAILED?;
default: return io::GENERAL_ERROR?;
}
$case env::WIN32:
@pool()
@@ -30,19 +30,19 @@ macro bool! native_rmdir(Path path)
switch (win32::getLastError())
{
case win32::ERROR_ACCESS_DENIED:
return IoError.NO_PERMISSION?;
return io::NO_PERMISSION?;
case win32::ERROR_CURRENT_DIRECTORY:
return IoError.BUSY?;
return io::BUSY?;
case win32::ERROR_DIR_NOT_EMPTY:
return IoError.DIR_NOT_EMPTY?;
return io::DIR_NOT_EMPTY?;
case win32::ERROR_DIRECTORY:
case win32::ERROR_PATH_NOT_FOUND:
return false;
default:
return IoError.GENERAL_ERROR?;
return io::GENERAL_ERROR?;
}
};
$default:
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
$endswitch
}

View File

@@ -4,11 +4,11 @@ import std::io, std::os, libc;
<*
@require dir.str_view().len > 0
*>
fn void! native_rmtree(Path dir)
fn void? native_rmtree(Path dir)
{
DIRPtr directory = posix::opendir(dir.as_zstr());
defer if (directory) posix::closedir(directory);
if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?;
if (!directory) return path::is_dir(dir) ? io::CANNOT_READ_DIR? : io::FILE_NOT_DIR?;
Posix_dirent* entry;
while ((entry = posix::readdir(directory)))
{
@@ -16,7 +16,7 @@ fn void! native_rmtree(Path dir)
{
String name = ((ZString)&entry.name).str_view();
if (!name || name == "." || name == "..") continue;
Path new_path = dir.temp_append(name)!;
Path new_path = dir.tappend(name)!;
if (entry.d_type == posix::DT_DIR)
{
native_rmtree(new_path)!;
@@ -25,7 +25,7 @@ fn void! native_rmtree(Path dir)
if (libc::remove(new_path.as_zstr()))
{
// TODO improve
return IoError.GENERAL_ERROR?;
return io::GENERAL_ERROR?;
}
};
}
@@ -35,21 +35,21 @@ fn void! native_rmtree(Path dir)
module std::io::os @if(env::WIN32);
import std::io, std::time, std::os;
fn void! native_rmtree(Path path)
fn void? native_rmtree(Path path)
{
Win32_WIN32_FIND_DATAW find_data;
String s = path.str_view().tconcat("\\*");
Win32_HANDLE find = win32::findFirstFileW(s.to_temp_utf16(), &find_data)!;
if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?;
if (find == win32::INVALID_HANDLE_VALUE) return io::CANNOT_READ_DIR?;
defer win32::findClose(find);
do
{
@pool()
{
String filename = string::new_from_wstring((WString)&find_data.cFileName, allocator::temp())!;
String filename = string::tfrom_wstring((WString)&find_data.cFileName)!;
if (filename == "." || filename == "..") continue;
Path file_path = path.temp_append(filename)!;
Path file_path = path.tappend(filename)!;
if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)
{
native_rmtree(file_path)!;

View File

@@ -1,29 +1,29 @@
module std::io::os @if(env::LIBC);
import std::io::path, std::os;
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(!env::WIN32)
fn Path? native_temp_directory(Allocator allocator) @if(!env::WIN32)
{
foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" })
{
String tmpdir = env::get_var(env) ?? "";
if (tmpdir) return path::new(tmpdir, allocator);
String tmpdir = env::tget_var(env) ?? "";
if (tmpdir) return path::new(allocator, tmpdir);
}
return path::new("/tmp", allocator);
return path::new(allocator, "/tmp");
}
fn Path! native_temp_directory(Allocator allocator = allocator::heap()) @if(env::WIN32) => @pool(allocator)
fn Path? native_temp_directory(Allocator allocator) @if(env::WIN32) => @pool()
{
Win32_DWORD len = win32::getTempPathW(0, null);
if (!len) return IoError.GENERAL_ERROR?;
Char16[] buff = mem::temp_alloc_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?;
return path::new(string::temp_from_utf16(buff[:len]), allocator);
if (!len) return io::GENERAL_ERROR?;
Char16[] buff = mem::talloc_array(Char16, len + (usz)1);
if (!win32::getTempPathW(len, buff)) return io::GENERAL_ERROR?;
return path::new(allocator, string::tfrom_utf16(buff[:len]));
}
module std::io::os @if(env::NO_LIBC);
import std::io::path;
macro Path! native_temp_directory(Allocator allocator = allocator::heap())
macro Path? native_temp_directory(Allocator allocator)
{
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
}

View File

@@ -2,25 +2,22 @@ module std::io::path;
import std::collections::list, std::io::os;
import std::os::win32;
const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const PathEnv DEFAULT_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX;
const char PREFERRED_SEPARATOR_WIN32 = '\\';
const char PREFERRED_SEPARATOR_POSIX = '/';
const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX;
def PathList = List(<Path>);
alias PathList = List { Path };
fault PathResult
{
INVALID_PATH,
NO_PARENT,
}
faultdef INVALID_PATH, NO_PARENT;
def Path = PathImp;
alias Path = PathImp;
struct PathImp (Printable)
{
String path_string;
PathEnv env;
Allocator allocator;
}
enum PathEnv
@@ -29,60 +26,55 @@ enum PathEnv
POSIX
}
fn Path! new_cwd(Allocator allocator = allocator::heap()) => @pool(allocator)
fn Path? cwd(Allocator allocator)
{
return new(os::getcwd(allocator::temp()), allocator);
}
fn Path! getcwd(Allocator allocator = allocator::heap()) @deprecated("Use new_cwd()")
{
@pool(allocator)
@pool()
{
return new(os::getcwd(allocator::temp()), allocator);
return new(allocator, os::getcwd(tmem));
};
}
fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
fn bool is_file(Path path) => os::native_is_file(path.str_view());
fn usz! file_size(Path path) => os::native_file_size(path.str_view());
fn usz? file_size(Path path) => os::native_file_size(path.str_view());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path! temp_cwd() => new_cwd(allocator::temp()) @inline;
fn Path! tgetcwd() @deprecated("Use temp_cwd()") => new_cwd(allocator::temp()) @inline;
fn void! chdir(Path path) => os::native_chdir(path) @inline;
fn Path! temp_directory(Allocator allocator = allocator::heap()) => os::native_temp_directory(allocator);
fn void! delete(Path path) => os::native_remove(path.str_view()) @inline;
fn Path? tcwd() => cwd(tmem) @inline;
macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV)
<*
@require @is_pathlike(path) : "Expected a Path or String to chdir"
*>
macro void? chdir(path)
{
$if @typeis(path, String):
@pool()
{
return os::native_chdir(temp(path));
};
$else
return os::native_chdir(path) @inline;
$endif
}
fn Path? temp_directory(Allocator allocator) => os::native_temp_directory(allocator);
fn void? delete(Path path) => os::native_remove(path.str_view()) @inline;
macro bool @is_pathlike(#path) => @typeis(#path, String) || @typeis(#path, Path);
macro bool is_separator(char c, PathEnv path_env = DEFAULT_ENV)
{
return c == '/' || (c == '\\' && path_env == PathEnv.WIN32);
}
macro bool is_posix_separator(char c)
{
return c == '/' || c == '\\';
}
macro bool is_posix_separator(char c) => c == '/';
macro bool is_win32_separator(char c) => c == '/' || c == '\\';
macro bool is_win32_separator(char c)
{
return c == '/' || c == '\\';
}
fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap()) @deprecated("use new_ls")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator);
}
fn PathList! temp_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
return new_ls(dir, no_dirs, no_symlinks, mask, allocator::temp()) @inline;
}
fn PathList! new_ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator allocator = allocator::heap())
fn PathList? ls(Allocator allocator, Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "")
{
$if $defined(os::native_ls):
return os::native_ls(dir, no_dirs, no_symlinks, mask, allocator);
$else
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
$endif
}
@@ -96,84 +88,84 @@ enum MkdirPermissions
<*
Create a directory on a given path, optionally recursive.
@param path `The path to create`
@param recursive `If directories in between should be created if they're missing, defaults to false`
@param permissions `The permissions to set on the directory`
@param path : `The path to create`
@require @is_pathlike(path) : "Expected a Path or String to chdir"
@param recursive : `If directories in between should be created if they're missing, defaults to false`
@param permissions : `The permissions to set on the directory`
*>
fn bool! mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
macro bool? mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return IoError.FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return IoError.CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
$if @typeis(path, String):
@pool() { return _mkdir(temp(path), recursive, permissions); };
$else
return _mkdir(path, recursive, permissions);
$endif
}
<*
Tries to delete directory, which must be empty.
@param path `The path to delete`
@param path : `The path to delete`
@require @is_pathlike(path) : "Expected a Path or String to chdir"
@return `true if there was a directory to delete, false otherwise`
@return! PathResult.INVALID_PATH `if the path was invalid`
@return? INVALID_PATH : `if the path was invalid`
*>
fn bool! rmdir(Path path)
macro bool? rmdir(path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
return os::native_rmdir(path);
$if @typeis(path, String):
@pool() { return _rmdir(temp(path)); };
$else
return _mkdir(path);
$endif
}
<*
Like [rmdir] but deletes a directory even if it contains items.
*>
fn void! rmtree(Path path)
fn void? rmtree(Path path)
{
if (!path.path_string.len) return PathResult.INVALID_PATH?;
if (!path.path_string.len) return INVALID_PATH?;
$if $defined(os::native_rmtree):
return os::native_rmtree(path);
$else
return IoError.UNSUPPORTED_OPERATION?;
return io::UNSUPPORTED_OPERATION?;
$endif
}
<*
Creates a new path.
@return! PathResult.INVALID_PATH `if the path was invalid`
@return? INVALID_PATH : `if the path was invalid`
*>
fn Path! new(String path, Allocator allocator = allocator::heap(), PathEnv path_env = DEFAULT_PATH_ENV)
fn Path? new(Allocator allocator, String path, PathEnv path_env = DEFAULT_ENV)
{
return { normalize(path.copy(allocator), path_env), path_env };
return { normalize(path.copy(allocator), path_env), path_env, allocator };
}
<*
Creates a new path using the temp allocator.
@return! PathResult.INVALID_PATH `if the path was invalid`
@return? INVALID_PATH : `if the path was invalid`
*>
fn Path! temp_new(String path, PathEnv path_env = DEFAULT_PATH_ENV)
fn Path? temp(String path, PathEnv path_env = DEFAULT_ENV)
{
return new(path, allocator::temp(), path_env);
return new(tmem, path, path_env);
}
fn Path! new_win32_wstring(WString path, Allocator allocator = allocator::heap()) => @pool(allocator)
fn Path? from_win32_wstring(Allocator allocator, WString path) => @pool()
{
return path::new(string::temp_from_wstring(path)!, allocator: allocator);
return path::new(allocator, string::tfrom_wstring(path)!);
}
fn Path! new_windows(String path, Allocator allocator = allocator::heap())
fn Path? for_windows(Allocator allocator, String path)
{
return new(path, allocator, WIN32);
return new(allocator, path, WIN32);
}
fn Path! new_posix(String path, Allocator allocator = allocator::heap())
fn Path? for_posix(Allocator allocator, String path)
{
return new(path, allocator, POSIX);
return new(allocator, path, POSIX);
}
fn bool Path.equals(self, Path p2)
@@ -181,59 +173,56 @@ fn bool Path.equals(self, Path p2)
return self.env == p2.env && self.path_string == p2.path_string;
}
fn Path! Path.append(self, String filename, Allocator allocator = allocator::heap()) @deprecated("Use path.new_append(...)")
{
return self.new_append(filename, allocator) @inline;
}
<*
Append the string to the current path.
@param [in] filename
*>
fn Path! Path.new_append(self, String filename, Allocator allocator = allocator::heap())
fn Path? Path.append(self, Allocator allocator, String filename)
{
if (!self.path_string.len) return new(filename, allocator, self.env)!;
if (!self.path_string.len) return new(allocator, filename, self.env)!;
assert(!is_separator(self.path_string[^1], self.env));
@pool(allocator)
@pool()
{
DString dstr = dstring::temp_with_capacity(self.path_string.len + 1 + filename.len);
dstr.append(self.path_string);
dstr.append(PREFERRED_SEPARATOR);
dstr.append(filename);
return { normalize(dstr.copy_str(allocator), self.env), self.env };
return new(allocator, dstr.str_view(), self.env);
};
}
fn Path! Path.temp_append(self, String filename) => self.new_append(filename, allocator::temp());
fn Path? Path.tappend(self, String filename) => self.append(tmem, filename);
fn Path! Path.tappend(self, String filename) @deprecated("Use path.temp_append(...)") => self.new_append(filename, allocator::temp());
fn usz Path.start_of_base_name(self) @local
fn usz? start_of_base_name(String str, PathEnv path_env) @local
{
String path_str = self.path_string;
if (!path_str.len) return 0;
if (self.env == PathEnv.WIN32)
if (!str.len) return 0;
usz? start_slash = str.rindex_of_char('/');
if (path_env != PathEnv.WIN32) return start_slash + 1 ?? 0;
if (try index = str.rindex_of_char('\\'))
{
if (try index = path_str.rindex_of_char('\\'))
{
// c:\ style path, we're done!
if (path_str[0] != '\\') return index + 1;
// Handle \\server\foo
// Find the \ before "foo"
usz last_index = 2 + path_str[2..].index_of_char('\\')!!;
// If they don't match, we're done
assert(last_index <= index, "Invalid normalized, path %d vs %s in %s", last_index, index, path_str);
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
return volume_name_len(path_str, self.env)!!;
if (try start_slash && start_slash > index) return start_slash + 1;
// c:\ style path, we're done!
if (str[0] != '\\') return index + 1;
// Handle \\server\foo
// Find the \ before "foo"
usz last_index = 2 + str[2..].index_of_char('\\')!;
// If they don't match, we're done
if (last_index > index) return INVALID_PATH?;
if (last_index != index) return index + 1;
// Otherwise just default to the volume length.
}
return path_str.rindex_of_char('/') + 1 ?? 0;
return volume_name_len(str, path_env)!!;
}
fn bool! Path.is_absolute(self)
fn bool? String.is_absolute_path(self) => @pool()
{
return temp(self).is_absolute();
}
fn bool? Path.is_absolute(self)
{
String path_str = self.str_view();
if (!path_str.len) return false;
@@ -242,55 +231,69 @@ fn bool! Path.is_absolute(self)
return path_start < path_str.len && is_separator(path_str[path_start], self.env);
}
fn Path! Path.absolute(self, Allocator allocator = allocator::heap()) @deprecated("Use path.new_absolute()")
fn Path? String.to_absolute_path(self, Allocator allocator) => @pool()
{
return self.new_absolute(allocator) @inline;
return temp(self).absolute(allocator);
}
<*
@require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
@require self.env == DEFAULT_ENV : "This method is only available on native paths"
*>
fn Path! Path.new_absolute(self, Allocator allocator = allocator::heap())
fn Path? Path.absolute(self, Allocator allocator)
{
String path_str = self.str_view();
if (!path_str.len) return PathResult.INVALID_PATH?;
if (self.is_absolute()!) return new(path_str, allocator, self.env);
if (!path_str.len) return INVALID_PATH?;
if (self.is_absolute()!) return new(allocator, path_str, self.env);
if (path_str == ".")
{
@pool(allocator)
@pool()
{
String cwd = os::getcwd(allocator::temp())!;
return new(cwd, allocator, self.env);
String cwd = os::getcwd(tmem)!;
return new(allocator, cwd, self.env);
};
}
$if DEFAULT_PATH_ENV == WIN32:
@pool(allocator)
$if DEFAULT_ENV == WIN32:
@pool()
{
const usz BUFFER_LEN = 4096;
WString buffer = (WString)mem::temp_alloc_array(Char16, BUFFER_LEN);
WString buffer = (WString)mem::talloc_array(Char16, BUFFER_LEN);
buffer = win32::_wfullpath(buffer, path_str.to_temp_wstring()!, BUFFER_LEN);
if (!buffer) return PathResult.INVALID_PATH?;
return { string::new_from_wstring(buffer, allocator), WIN32 };
if (!buffer) return INVALID_PATH?;
return { string::from_wstring(allocator, buffer), WIN32, allocator };
};
$else
String cwd = os::getcwd(allocator::temp())!;
return (Path){ cwd, self.env }.new_append(path_str, allocator)!;
String cwd = os::getcwd(tmem)!;
return (Path){ cwd, self.env, tmem }.append(allocator, path_str)!;
$endif
}
fn String? String.file_basename(self, Allocator allocator) => @pool()
{
return temp(self).basename().copy(allocator);
}
fn String? String.file_tbasename(self) => self.file_basename(tmem);
fn String Path.basename(self)
{
usz basename_start = self.start_of_base_name();
usz basename_start = start_of_base_name(self.path_string, self.env)!!;
String path_str = self.path_string;
if (basename_start == path_str.len) return "";
return path_str[basename_start..];
}
fn String? String.path_tdirname(self) => self.path_dirname(tmem);
fn String? String.path_dirname(self, Allocator allocator) => @pool()
{
return temp(self).dirname().copy(allocator);
}
fn String Path.dirname(self)
{
usz basename_start = self.start_of_base_name();
String path_str = self.path_string;
usz basename_start = start_of_base_name(path_str, self.env)!!;
if (basename_start == 0) return ".";
usz start = volume_name_len(path_str, self.env)!!;
if (basename_start <= start + 1)
@@ -304,11 +307,12 @@ fn String Path.dirname(self)
return path_str[:basename_start - 1];
}
<*
Test if the path has the given extension, so given the path /foo/bar.c3
this would be true matching the extension "c3"
@param [in] extension `The extension name (not including the leading '.')`
@param [in] extension : `The extension name (not including the leading '.')`
@require extension.len > 0 : `The extension cannot be empty`
@return `true if the extension matches`
*>
@@ -320,13 +324,12 @@ fn bool Path.has_extension(self, String extension)
return basename[^extension.len..] == extension;
}
fn String! Path.extension(self)
fn String? Path.extension(self)
{
String basename = self.basename();
usz index = basename.rindex_of(".")!;
// Plain ".foo" does not have an
if (index == 0) return SearchResult.MISSING?;
if (index == basename.len) return "";
// Plain ".foo" does not have an extension
if (index == 0 || index == basename.len) return "";
return basename[index + 1..];
}
@@ -337,7 +340,17 @@ fn String Path.volume_name(self)
return self.path_string[:len];
}
fn usz! volume_name_len(String path, PathEnv path_env) @local
fn Path? String.to_path(self, Allocator allocator)
{
return new(allocator, self);
}
fn Path? String.to_tpath(self)
{
return new(tmem, self);
}
fn usz? volume_name_len(String path, PathEnv path_env) @local
{
usz len = path.len;
if (len < 2 || path_env != PathEnv.WIN32) return 0;
@@ -361,10 +374,10 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
base_found = i;
continue;
}
if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH?;
if (is_reserved_win32_path_char(c)) return INVALID_PATH?;
}
if (base_found > 0 && base_found + 1 < len) return len;
return PathResult.INVALID_PATH?;
return INVALID_PATH?;
case 'A'..'Z':
case 'a'..'z':
return path[1] == ':' ? 2 : 0;
@@ -378,22 +391,22 @@ fn usz! volume_name_len(String path, PathEnv path_env) @local
of the path itself.
@return `The parent of the path as a non-allocated path`
@return! PathResult.NO_PARENT `if this path does not have a parent`
@return? NO_PARENT : `if this path does not have a parent`
*>
fn Path! Path.parent(self)
fn Path? Path.parent(self)
{
if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return PathResult.NO_PARENT?;
if (self.path_string.len == 1 && is_separator(self.path_string[0], self.env)) return NO_PARENT?;
foreach_r(i, c : self.path_string)
{
if (is_separator(c, self.env))
{
return { self.path_string[:i], self.env };
return { self.path_string[:i], self.env, null };
}
}
return PathResult.NO_PARENT?;
return NO_PARENT?;
}
fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
fn String? normalize(String path_str, PathEnv path_env = DEFAULT_ENV)
{
if (!path_str.len) return path_str;
usz path_start = volume_name_len(path_str, path_env)!;
@@ -432,7 +445,7 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
// The rest are names of the path elements, so check that the
// characters are valid.
if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH?;
if (is_reserved_path_char(c, path_env)) return INVALID_PATH?;
// If we have '.' after a separator
if (c == '.' && previous_was_separator)
@@ -465,7 +478,7 @@ fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV)
continue;
case 2:
// This is an error: /a/../..
if (len == path_start && has_root) return PathResult.INVALID_PATH?;
if (len == path_start && has_root) return INVALID_PATH?;
// If this .. at the start, or after ../? If so, we just copy ..
if (len == path_start ||
@@ -539,24 +552,24 @@ fn String Path.root_directory(self)
return path_str;
}
def PathWalker = fn bool! (Path, bool is_dir, void*);
alias PathWalker = fn bool? (Path, bool is_dir, void*);
<*
Walk the path recursively. PathWalker is run on every file and
directory found. Return true to abort the walk.
@require self.env == DEFAULT_PATH_ENV : "This method is only available on native paths"
@require self.env == DEFAULT_ENV : "This method is only available on native paths"
*>
fn bool! Path.walk(self, PathWalker w, void* data)
fn bool? Path.walk(self, PathWalker w, void* data)
{
const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = self.new_absolute(allocator)!;
PathList files = new_ls(abs, allocator: allocator)!;
Path abs = self.absolute(allocator)!;
PathList files = ls(allocator, abs)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;
f = abs.new_append(f.str_view(), allocator)!;
f = abs.append(allocator, f.str_view())!;
bool is_directory = is_dir(f);
if (w(f, is_directory, data)!) return true;
if (is_directory && f.walk(w, data)!) return true;
@@ -565,6 +578,35 @@ fn bool! Path.walk(self, PathWalker w, void* data)
return false;
}
alias TraverseCallback = fn bool? (Path, bool is_dir, any data);
<*
Walk the path recursively. TraverseCallback is run for every file and
directory found. Return true to abort the walk.
@require path.env == DEFAULT_ENV : "This method is only available on native paths"
*>
fn bool? traverse(Path path, TraverseCallback callback, any data)
{
const PATH_MAX = 512;
@stack_mem(PATH_MAX; Allocator allocator)
{
Path abs = path.absolute(allocator)!;
PathList files = ls(allocator, abs)!;
foreach (f : files)
{
if (f.str_view() == "." || f.str_view() == "..") continue;
@stack_mem(128; Allocator smem)
{
f = abs.append(smem, f.str_view())!;
bool is_directory = is_dir(f);
if (callback(f, is_directory, data)!) return true;
if (is_directory && traverse(f, callback, data)!) return true;
};
}
};
return false;
}
fn String Path.str_view(self) @inline
{
return self.path_string;
@@ -576,26 +618,19 @@ fn bool Path.has_suffix(self, String str)
return self.str_view().ends_with(str);
}
fn void Path.free_with_allocator(self, Allocator allocator)
{
allocator::free(allocator, self.path_string.ptr);
}
<*
@require self.allocator != null : "This Path should never be freed"
*>
fn void Path.free(self)
{
free(self.path_string.ptr);
allocator::free(self.allocator, self.path_string.ptr);
}
fn usz! Path.to_format(&self, Formatter* formatter) @dynamic
fn usz? Path.to_format(&self, Formatter* formatter) @dynamic
{
return formatter.print(self.str_view());
}
fn String Path.to_new_string(&self, Allocator allocator = allocator::heap()) @dynamic
{
return self.str_view().copy(allocator);
}
const bool[256] RESERVED_PATH_CHAR_POSIX = {
[0] = true,
@@ -619,9 +654,29 @@ macro bool is_reserved_win32_path_char(char c)
return RESERVED_PATH_CHAR_WIN32[c];
}
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV)
macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_ENV)
{
return path_env == PathEnv.WIN32
? RESERVED_PATH_CHAR_WIN32[c]
: RESERVED_PATH_CHAR_POSIX[c];
}
}
fn bool? _mkdir(Path path, bool recursive = false, MkdirPermissions permissions = NORMAL) @private
{
if (!path.path_string.len) return INVALID_PATH?;
if (is_dir(path)) return false;
if (exists(path)) return io::FILE_NOT_DIR?;
if (recursive)
{
if (try parent = path.parent()) mkdir(parent, true, permissions)!;
}
if (!is_dir(path.parent()) ?? false) return io::CANNOT_READ_DIR?;
return os::native_mkdir(path, permissions);
}
fn bool? _rmdir(Path path) @private
{
if (!path.path_string.len) return INVALID_PATH?;
return os::native_rmdir(path);
}

View File

@@ -3,28 +3,28 @@ import std::math;
interface InStream
{
fn void! close() @optional;
fn usz! seek(isz offset, Seek seek) @optional;
fn void? close() @optional;
fn usz? seek(isz offset, Seek seek) @optional;
fn usz len() @optional;
fn usz! available() @optional;
fn usz! read(char[] buffer);
fn char! read_byte();
fn usz! write_to(OutStream out) @optional;
fn void! pushback_byte() @optional;
fn usz? available() @optional;
fn usz? read(char[] buffer);
fn char? read_byte();
fn usz? write_to(OutStream out) @optional;
fn void? pushback_byte() @optional;
}
interface OutStream
{
fn void! destroy() @optional;
fn void! close() @optional;
fn void! flush() @optional;
fn usz! write(char[] bytes);
fn void! write_byte(char c);
fn usz! read_to(InStream in) @optional;
fn void? destroy() @optional;
fn void? close() @optional;
fn void? flush() @optional;
fn usz? write(char[] bytes);
fn void? write_byte(char c);
fn usz? read_to(InStream in) @optional;
}
fn usz! available(InStream s)
fn usz? available(InStream s)
{
if (&s.available) return s.available();
if (&s.seek)
@@ -51,17 +51,17 @@ macro bool @is_outstream(#expr)
@param [&out] ref
@require @is_instream(stream)
*>
macro usz! read_any(stream, any ref)
macro usz? read_any(stream, any ref)
{
return read_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
<*
@param [&in] ref "the object to write."
@param [&in] ref : "the object to write."
@require @is_outstream(stream)
@ensure return == ref.type.sizeof
*>
macro usz! write_any(stream, any ref)
macro usz? write_any(stream, any ref)
{
return write_all(stream, ((char*)ref)[:ref.type.sizeof]);
}
@@ -69,34 +69,18 @@ macro usz! write_any(stream, any ref)
<*
@require @is_instream(stream)
*>
macro usz! read_all(stream, char[] buffer)
macro usz? read_all(stream, char[] buffer)
{
if (buffer.len == 0) return 0;
usz n = stream.read(buffer)!;
if (n != buffer.len) return IoError.UNEXPECTED_EOF?;
if (n != buffer.len) return UNEXPECTED_EOF?;
return n;
}
<*
@require @is_instream(stream)
*>
macro char[]! read_new_fully(stream, Allocator allocator = allocator::heap()) @deprecated("Use read_fully(mem)")
{
usz len = available(stream)!;
char* data = allocator::malloc_try(allocator, len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
{
read += stream.read(data[read:len - read])!;
}
return data[:len];
}
<*
@require @is_instream(stream)
*>
macro char[]! read_fully(Allocator allocator, stream)
macro char[]? read_fully(Allocator allocator, stream)
{
usz len = available(stream)!;
char* data = allocator::malloc_try(allocator, len)!;
@@ -112,29 +96,24 @@ macro char[]! read_fully(Allocator allocator, stream)
<*
@require @is_outstream(stream)
*>
macro usz! write_all(stream, char[] buffer)
macro usz? write_all(stream, char[] buffer)
{
if (buffer.len == 0) return 0;
usz n = stream.write(buffer)!;
if (n != buffer.len) return IoError.INCOMPLETE_WRITE?;
if (n != buffer.len) return INCOMPLETE_WRITE?;
return n;
}
macro usz! @read_using_read_byte(&s, char[] buffer) @deprecated
{
return read_using_read_byte(*s, buffer);
}
macro usz! read_using_read_byte(s, char[] buffer)
macro usz? read_using_read_byte(s, char[] buffer)
{
usz len = 0;
foreach (&cptr : buffer)
{
char! c = s.read_byte();
char? c = s.read_byte();
if (catch err = c)
{
case IoError.EOF: return len;
default: return err?;
if (err == io::EOF) return len;
return err?;
}
*cptr = c;
len++;
@@ -142,60 +121,40 @@ macro usz! read_using_read_byte(s, char[] buffer)
return len;
}
macro void! write_byte_using_write(s, char c)
macro void? write_byte_using_write(s, char c)
{
char[1] buff = { c };
s.write(&buff)!;
}
macro void! @write_byte_using_write(&s, char c) @deprecated
{
return write_byte_using_write(*s, c);
}
macro char! @read_byte_using_read(&s) @deprecated
{
return read_byte_using_read(*s);
}
macro char! read_byte_using_read(s)
macro char? read_byte_using_read(s)
{
char[1] buffer;
usz read = s.read(&buffer)!;
if (read != 1) return IoError.EOF?;
if (read != 1) return io::EOF?;
return buffer[0];
}
def ReadByteFn = fn char!();
alias ReadByteFn = fn char?();
macro usz! write_using_write_byte(s, char[] bytes)
macro usz? write_using_write_byte(s, char[] bytes)
{
foreach (c : bytes) s.write_byte(self, c)!;
return bytes.len;
}
macro usz! @write_using_write_byte(&s, char[] bytes) @deprecated
{
return write_using_write_byte(*s, bytes);
}
macro void! pushback_using_seek(s)
macro void? pushback_using_seek(s)
{
s.seek(-1, CURSOR)!;
}
macro void! @pushback_using_seek(&s) @deprecated
{
s.seek(-1, CURSOR)!;
}
fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {})
fn usz? copy_to(InStream in, OutStream dst, char[] buffer = {})
{
if (buffer.len) return copy_through_buffer(in, dst, buffer);
if (&in.write_to) return in.write_to(dst);
if (&dst.read_to) return dst.read_to(in);
$switch (env::MEMORY_ENV)
$switch env::MEMORY_ENV:
$case NORMAL:
return copy_through_buffer(in, dst, &&(char[4096]){});
$case SMALL:
@@ -206,31 +165,31 @@ fn usz! copy_to(InStream in, OutStream dst, char[] buffer = {})
$endswitch
}
macro usz! copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
macro usz? copy_through_buffer(InStream in, OutStream dst, char[] buffer) @local
{
usz total_copied;
while (true)
{
usz! len = in.read(buffer);
usz? len = in.read(buffer);
if (catch err = len)
{
case IoError.EOF: return total_copied;
default: return err?;
if (err == io::EOF) return total_copied;
return err?;
}
if (!len) return total_copied;
usz written = dst.write(buffer[:len])!;
total_copied += len;
if (written != len) return IoError.INCOMPLETE_WRITE?;
if (written != len) return INCOMPLETE_WRITE?;
}
}
const char[?] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
const char[*] MAX_VARS @private = { [2] = 3, [4] = 5, [8] = 10 };
<*
@require @is_instream(stream)
@require @typekind(x_ptr) == POINTER && $typeof(x_ptr).inner.kindof.is_int()
*>
macro usz! read_varint(stream, x_ptr)
macro usz? read_varint(stream, x_ptr)
{
var $Type = $typefrom($typeof(x_ptr).inner);
const MAX = MAX_VARS[$Type.sizeof];
@@ -239,13 +198,11 @@ macro usz! read_varint(stream, x_ptr)
usz n;
for (usz i = 0; i < MAX; i++)
{
char! c = stream.read_byte();
char? c = stream.read_byte();
if (catch err = c)
{
case IoError.EOF:
return IoError.UNEXPECTED_EOF?;
default:
return err?;
if (err == io::EOF) return io::UNEXPECTED_EOF?;
return err?;
}
n++;
if (c & 0x80 == 0)
@@ -261,13 +218,13 @@ macro usz! read_varint(stream, x_ptr)
x |= (c & 0x7F) << shift;
shift += 7;
}
return MathError.OVERFLOW?;
return math::OVERFLOW?;
}
<*
@require @is_outstream(stream)
@require @typekind(x).is_int()
*>
macro usz! write_varint(stream, x)
macro usz? write_varint(stream, x)
{
var $Type = $typeof(x);
const MAX = MAX_VARS[$Type.sizeof];
@@ -286,7 +243,7 @@ macro usz! write_varint(stream, x)
<*
@require @is_instream(stream)
*>
macro ushort! read_be_ushort(stream)
macro ushort? read_be_ushort(stream)
{
char hi_byte = stream.read_byte()!;
char lo_byte = stream.read_byte()!;
@@ -296,7 +253,7 @@ macro ushort! read_be_ushort(stream)
<*
@require @is_instream(stream)
*>
macro short! read_be_short(stream)
macro short? read_be_short(stream)
{
return read_be_ushort(stream);
}
@@ -304,7 +261,7 @@ macro short! read_be_short(stream)
<*
@require @is_outstream(stream)
*>
macro void! write_be_short(stream, ushort s)
macro void? write_be_short(stream, ushort s)
{
stream.write_byte((char)(s >> 8))!;
stream.write_byte((char)s)!;
@@ -313,7 +270,7 @@ macro void! write_be_short(stream, ushort s)
<*
@require @is_instream(stream)
*>
macro uint! read_be_uint(stream)
macro uint? read_be_uint(stream)
{
uint val = stream.read_byte()! << 24;
val += stream.read_byte()! << 16;
@@ -324,7 +281,7 @@ macro uint! read_be_uint(stream)
<*
@require @is_instream(stream)
*>
macro int! read_be_int(stream)
macro int? read_be_int(stream)
{
return read_be_uint(stream);
}
@@ -332,7 +289,7 @@ macro int! read_be_int(stream)
<*
@require @is_outstream(stream)
*>
macro void! write_be_int(stream, uint s)
macro void? write_be_int(stream, uint s)
{
stream.write_byte((char)(s >> 24))!;
stream.write_byte((char)(s >> 16))!;
@@ -343,7 +300,7 @@ macro void! write_be_int(stream, uint s)
<*
@require @is_instream(stream)
*>
macro ulong! read_be_ulong(stream)
macro ulong? read_be_ulong(stream)
{
ulong val = (ulong)stream.read_byte()! << 56;
val += (ulong)stream.read_byte()! << 48;
@@ -358,7 +315,7 @@ macro ulong! read_be_ulong(stream)
<*
@require @is_instream(stream)
*>
macro long! read_be_long(stream)
macro long? read_be_long(stream)
{
return read_be_ulong(stream);
}
@@ -366,7 +323,7 @@ macro long! read_be_long(stream)
<*
@require @is_outstream(stream)
*>
macro void! write_be_long(stream, ulong s)
macro void? write_be_long(stream, ulong s)
{
stream.write_byte((char)(s >> 56))!;
stream.write_byte((char)(s >> 48))!;
@@ -381,7 +338,7 @@ macro void! write_be_long(stream, ulong s)
<*
@require @is_instream(stream)
*>
macro uint128! read_be_uint128(stream)
macro uint128? read_be_uint128(stream)
{
uint128 val = (uint128)stream.read_byte()! << 120;
val += (uint128)stream.read_byte()! << 112;
@@ -404,7 +361,7 @@ macro uint128! read_be_uint128(stream)
<*
@require @is_instream(stream)
*>
macro int128! read_be_int128(stream)
macro int128? read_be_int128(stream)
{
return read_be_uint128(stream);
}
@@ -412,7 +369,7 @@ macro int128! read_be_int128(stream)
<*
@require @is_outstream(stream)
*>
macro void! write_be_int128(stream, uint128 s)
macro void? write_be_int128(stream, uint128 s)
{
stream.write_byte((char)(s >> 120))!;
stream.write_byte((char)(s >> 112))!;
@@ -434,9 +391,9 @@ macro void! write_be_int128(stream, uint128 s)
<*
@require @is_outstream(stream)
@require data.len < 256 "Data exceeded 255"
@require data.len < 256 : "Data exceeded 255"
*>
macro usz! write_tiny_bytearray(stream, char[] data)
macro usz? write_tiny_bytearray(stream, char[] data)
{
stream.write_byte((char)data.len)!;
return stream.write(data) + 1;
@@ -445,7 +402,7 @@ macro usz! write_tiny_bytearray(stream, char[] data)
<*
@require @is_instream(stream)
*>
macro char[]! read_tiny_bytearray(stream, Allocator allocator)
macro char[]? read_tiny_bytearray(stream, Allocator allocator)
{
int len = stream.read_byte()!;
if (!len) return {};
@@ -456,9 +413,9 @@ macro char[]! read_tiny_bytearray(stream, Allocator allocator)
<*
@require @is_outstream(stream)
@require data.len < 0x1000 "Data exceeded 65535"
@require data.len < 0x1000 : "Data exceeded 65535"
*>
macro usz! write_short_bytearray(stream, char[] data)
macro usz? write_short_bytearray(stream, char[] data)
{
io::write_be_short(stream, (ushort)data.len)!;
return stream.write(data) + 2;
@@ -467,7 +424,7 @@ macro usz! write_short_bytearray(stream, char[] data)
<*
@require @is_instream(stream)
*>
macro char[]! read_short_bytearray(stream, Allocator allocator)
macro char[]? read_short_bytearray(stream, Allocator allocator)
{
int len = io::read_be_ushort(stream)!;
if (!len) return {};

View File

@@ -12,7 +12,7 @@ struct ReadBuffer (InStream)
Buffer reads from a stream.
@param [inout] self
@require bytes.len > 0
@require self.bytes.len == 0 "Init may not run on already initialized data"
@require self.bytes.len == 0 : "Init may not run on already initialized data"
*>
fn ReadBuffer* ReadBuffer.init(&self, InStream wrapped_stream, char[] bytes)
{
@@ -24,12 +24,12 @@ fn String ReadBuffer.str_view(&self) @inline
return (String)self.bytes[self.read_idx:self.write_idx - self.read_idx];
}
fn void! ReadBuffer.close(&self) @dynamic
fn void? ReadBuffer.close(&self) @dynamic
{
if (&self.wrapped_stream.close) self.wrapped_stream.close()!;
}
fn usz! ReadBuffer.read(&self, char[] bytes) @dynamic
fn usz? ReadBuffer.read(&self, char[] bytes) @dynamic
{
if (self.read_idx == self.write_idx)
{
@@ -46,16 +46,16 @@ fn usz! ReadBuffer.read(&self, char[] bytes) @dynamic
return n;
}
fn char! ReadBuffer.read_byte(&self) @dynamic
fn char? ReadBuffer.read_byte(&self) @dynamic
{
if (self.read_idx == self.write_idx) self.refill()!;
if (self.read_idx == self.write_idx) return IoError.EOF?;
if (self.read_idx == self.write_idx) return io::EOF?;
char c = self.bytes[self.read_idx];
self.read_idx++;
return c;
}
fn void! ReadBuffer.refill(&self) @local @inline
fn void? ReadBuffer.refill(&self) @local @inline
{
self.read_idx = 0;
self.write_idx = self.wrapped_stream.read(self.bytes)!;
@@ -71,8 +71,8 @@ struct WriteBuffer (OutStream)
<*
Buffer writes to a stream. Call `flush` when done writing to the buffer.
@param [inout] self
@require bytes.len > 0 "Non-empty buffer required"
@require self.bytes.len == 0 "Init may not run on already initialized data"
@require bytes.len > 0 : "Non-empty buffer required"
@require self.bytes.len == 0 : "Init may not run on already initialized data"
*>
fn WriteBuffer* WriteBuffer.init(&self, OutStream wrapped_stream, char[] bytes)
{
@@ -85,18 +85,18 @@ fn String WriteBuffer.str_view(&self) @inline
return (String)self.bytes[:self.index];
}
fn void! WriteBuffer.close(&self) @dynamic
fn void? WriteBuffer.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
fn void! WriteBuffer.flush(&self) @dynamic
fn void? WriteBuffer.flush(&self) @dynamic
{
self.write_pending()!;
if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!;
}
fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
fn usz? WriteBuffer.write(&self, char[] bytes) @dynamic
{
usz n = self.bytes.len - self.index;
if (bytes.len < n)
@@ -118,7 +118,7 @@ fn usz! WriteBuffer.write(&self, char[] bytes) @dynamic
return bytes.len;
}
fn void! WriteBuffer.write_byte(&self, char c) @dynamic
fn void? WriteBuffer.write_byte(&self, char c) @dynamic
{
usz n = self.bytes.len - self.index;
if (n == 0)
@@ -129,8 +129,8 @@ fn void! WriteBuffer.write_byte(&self, char c) @dynamic
self.index += 1;
}
fn void! WriteBuffer.write_pending(&self) @local
fn void? WriteBuffer.write_pending(&self) @local
{
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
if (self.index != 0) return IoError.INCOMPLETE_WRITE?;
if (self.index != 0) return INCOMPLETE_WRITE?;
}

View File

@@ -14,7 +14,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.
@require self.bytes.len == 0 "Buffer already initialized."
@require self.bytes.len == 0 : "Buffer already initialized."
*>
fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz initial_capacity = 16)
{
@@ -24,32 +24,14 @@ fn ByteBuffer* ByteBuffer.init(&self, Allocator allocator, usz max_read, usz ini
return self;
}
<*
ByteBuffer provides a streamable read/write buffer.
max_read defines how many bytes might be kept before its internal buffer is shrinked.
@require self.bytes.len == 0 "Buffer already initialized."
*>
fn ByteBuffer* ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
*self = { .allocator = allocator, .max_read = max_read };
initial_capacity = max(initial_capacity, 16);
self.grow(initial_capacity);
return self;
}
fn ByteBuffer* ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16)
{
return self.init(allocator::temp(), max_read, initial_capacity);
}
fn ByteBuffer* ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), max_read, initial_capacity);
return self.init(tmem, max_read, initial_capacity);
}
<*
@require buf.len > 0
@require self.bytes.len == 0 "Buffer already initialized."
@require self.bytes.len == 0 : "Buffer already initialized."
*>
fn ByteBuffer* ByteBuffer.init_with_buffer(&self, char[] buf)
{
@@ -63,7 +45,7 @@ fn void ByteBuffer.free(&self)
*self = {};
}
fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic
fn usz? ByteBuffer.write(&self, char[] bytes) @dynamic
{
usz cap = self.bytes.len - self.write_idx;
if (cap < bytes.len) self.grow(bytes.len);
@@ -72,7 +54,7 @@ fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic
return bytes.len;
}
fn void! ByteBuffer.write_byte(&self, char c) @dynamic
fn void? ByteBuffer.write_byte(&self, char c) @dynamic
{
usz cap = self.bytes.len - self.write_idx;
if (cap == 0) self.grow(1);
@@ -80,13 +62,13 @@ fn void! ByteBuffer.write_byte(&self, char c) @dynamic
self.write_idx++;
}
fn usz! ByteBuffer.read(&self, char[] bytes) @dynamic
fn usz? ByteBuffer.read(&self, char[] bytes) @dynamic
{
usz readable = self.write_idx - self.read_idx;
if (readable == 0)
{
self.has_last = false;
return IoError.EOF?;
return io::EOF?;
}
usz n = min(readable, bytes.len);
bytes[:n] = self.bytes[self.read_idx:n];
@@ -96,13 +78,13 @@ fn usz! ByteBuffer.read(&self, char[] bytes) @dynamic
return n;
}
fn char! ByteBuffer.read_byte(&self) @dynamic
fn char? ByteBuffer.read_byte(&self) @dynamic
{
usz readable = self.write_idx - self.read_idx;
if (readable == 0)
{
self.has_last = false;
return IoError.EOF?;
return io::EOF?;
}
char c = self.bytes[self.read_idx];
self.read_idx++;
@@ -114,34 +96,34 @@ fn char! ByteBuffer.read_byte(&self) @dynamic
<*
Only the last byte of a successful read can be pushed back.
*>
fn void! ByteBuffer.pushback_byte(&self) @dynamic
fn void? ByteBuffer.pushback_byte(&self) @dynamic
{
if (!self.has_last) return IoError.EOF?;
if (!self.has_last) return io::EOF?;
assert(self.read_idx > 0);
self.read_idx--;
self.has_last = false;
}
fn usz! ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
fn usz? ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
{
switch (seek)
{
case SET:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION?;
self.read_idx = offset;
return offset;
case CURSOR:
if ((offset < 0 && self.read_idx < -offset) ||
(offset > 0 && self.read_idx + offset > self.write_idx)) return IoError.INVALID_POSITION?;
(offset > 0 && self.read_idx + offset > self.write_idx)) return INVALID_POSITION?;
self.read_idx += offset;
case END:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION?;
self.read_idx = self.write_idx - offset;
}
return self.read_idx;
}
fn usz! ByteBuffer.available(&self) @inline @dynamic
fn usz? ByteBuffer.available(&self) @inline @dynamic
{
return self.write_idx - self.read_idx;
}

View File

@@ -17,9 +17,9 @@ fn ByteReader* ByteReader.init(&self, char[] bytes)
return self;
}
fn usz! ByteReader.read(&self, char[] bytes) @dynamic
fn usz? ByteReader.read(&self, char[] bytes) @dynamic
{
if (self.index >= self.bytes.len) return IoError.EOF?;
if (self.index >= self.bytes.len) return io::EOF?;
usz len = min(self.bytes.len - self.index, bytes.len);
if (len == 0) return 0;
mem::copy(bytes.ptr, &self.bytes[self.index], len);
@@ -27,19 +27,19 @@ fn usz! ByteReader.read(&self, char[] bytes) @dynamic
return len;
}
fn char! ByteReader.read_byte(&self) @dynamic
fn char? ByteReader.read_byte(&self) @dynamic
{
if (self.index >= self.bytes.len) return IoError.EOF?;
if (self.index >= self.bytes.len) return io::EOF?;
return self.bytes[self.index++];
}
fn void! ByteReader.pushback_byte(&self) @dynamic
fn void? ByteReader.pushback_byte(&self) @dynamic
{
if (!self.index) return IoError.INVALID_PUSHBACK?;
if (!self.index) return INVALID_PUSHBACK?;
self.index--;
}
fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic
fn usz? ByteReader.seek(&self, isz offset, Seek seek) @dynamic
{
isz new_index;
switch (seek)
@@ -48,12 +48,12 @@ fn usz! ByteReader.seek(&self, isz offset, Seek seek) @dynamic
case CURSOR: new_index = self.index + offset;
case END: new_index = self.bytes.len + offset;
}
if (new_index < 0) return IoError.INVALID_POSITION?;
if (new_index < 0) return INVALID_POSITION?;
self.index = new_index;
return new_index;
}
fn usz! ByteReader.write_to(&self, OutStream writer) @dynamic
fn usz? ByteReader.write_to(&self, OutStream writer) @dynamic
{
if (self.index >= self.bytes.len) return 0;
usz written = writer.write(self.bytes[self.index..])!;
@@ -62,7 +62,7 @@ fn usz! ByteReader.write_to(&self, OutStream writer) @dynamic
return written;
}
fn usz! ByteReader.available(&self) @inline @dynamic
fn usz? ByteReader.available(&self) @inline @dynamic
{
return max(0, self.bytes.len - self.index);
}

View File

@@ -11,19 +11,7 @@ struct ByteWriter (OutStream)
<*
@param [&inout] self
@param [&inout] allocator
@require self.bytes.len == 0 "Init may not run on already initialized data"
@ensure (bool)allocator, self.index == 0
*>
fn ByteWriter* ByteWriter.new_init(&self, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
*self = { .bytes = {}, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@param [&inout] allocator
@require self.bytes.len == 0 "Init may not run on already initialized data"
@require self.bytes.len == 0 : "Init may not run on already initialized data"
@ensure (bool)allocator, self.index == 0
*>
fn ByteWriter* ByteWriter.init(&self, Allocator allocator)
@@ -34,22 +22,12 @@ fn ByteWriter* ByteWriter.init(&self, Allocator allocator)
<*
@param [&inout] self
@require self.bytes.len == 0 "Init may not run on already initialized data"
@require self.bytes.len == 0 : "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn ByteWriter* ByteWriter.tinit(&self)
{
return self.init(allocator::temp()) @inline;
}
<*
@param [&inout] self
@require self.bytes.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn ByteWriter* ByteWriter.temp_init(&self) @deprecated("Use tinit")
{
return self.init(allocator::temp()) @inline;
return self.init(tmem) @inline;
}
fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
@@ -58,7 +36,7 @@ fn ByteWriter* ByteWriter.init_with_buffer(&self, char[] data)
return self;
}
fn void! ByteWriter.destroy(&self) @dynamic
fn void? ByteWriter.destroy(&self) @dynamic
{
if (!self.allocator) return;
if (void* ptr = self.bytes.ptr) allocator::free(self.allocator, ptr);
@@ -70,17 +48,17 @@ fn String ByteWriter.str_view(&self) @inline
return (String)self.bytes[:self.index];
}
fn void! ByteWriter.ensure_capacity(&self, usz len) @inline
fn void? ByteWriter.ensure_capacity(&self, usz len) @inline
{
if (self.bytes.len > len) return;
if (!self.allocator) return IoError.OUT_OF_SPACE?;
if (!self.allocator) return OUT_OF_SPACE?;
if (len < 16) len = 16;
usz new_capacity = math::next_power_of_2(len);
char* new_ptr = allocator::realloc_try(self.allocator, self.bytes.ptr, new_capacity)!;
self.bytes = new_ptr[:new_capacity];
}
fn usz! ByteWriter.write(&self, char[] bytes) @dynamic
fn usz? ByteWriter.write(&self, char[] bytes) @dynamic
{
self.ensure_capacity(self.index + bytes.len)!;
mem::copy(&self.bytes[self.index], bytes.ptr, bytes.len);
@@ -88,7 +66,7 @@ fn usz! ByteWriter.write(&self, char[] bytes) @dynamic
return bytes.len;
}
fn void! ByteWriter.write_byte(&self, char c) @dynamic
fn void? ByteWriter.write_byte(&self, char c) @dynamic
{
self.ensure_capacity(self.index + 1)!;
self.bytes[self.index++] = c;
@@ -98,7 +76,7 @@ fn void! ByteWriter.write_byte(&self, char c) @dynamic
@param [&inout] self
@param reader
*>
fn usz! ByteWriter.read_from(&self, InStream reader) @dynamic
fn usz? ByteWriter.read_from(&self, InStream reader) @dynamic
{
usz start_index = self.index;
if (&reader.available)

View File

@@ -7,8 +7,8 @@ struct LimitReader (InStream)
}
<*
@param [&inout] wrapped_stream "The stream to read from"
@param limit "The max limit to read"
@param [&inout] wrapped_stream : "The stream to read from"
@param limit : "The max limit to read"
*>
fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit)
{
@@ -16,29 +16,29 @@ fn LimitReader* LimitReader.init(&self, InStream wrapped_stream, usz limit)
return self;
}
fn void! LimitReader.close(&self) @dynamic
fn void? LimitReader.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
fn usz! LimitReader.read(&self, char[] bytes) @dynamic
fn usz? LimitReader.read(&self, char[] bytes) @dynamic
{
if (self.limit == 0) return IoError.EOF?;
if (self.limit == 0) return io::EOF?;
usz m = min(bytes.len, self.limit);
usz n = self.wrapped_stream.read(bytes[:m])!;
self.limit -= n;
return n;
}
fn char! LimitReader.read_byte(&self) @dynamic
fn char? LimitReader.read_byte(&self) @dynamic
{
if (self.limit == 0) return IoError.EOF?;
if (self.limit == 0) return io::EOF?;
defer try self.limit--;
return self.wrapped_stream.read_byte();
}
fn usz! LimitReader.available(&self) @inline @dynamic
fn usz? LimitReader.available(&self) @inline @dynamic
{
return self.limit;
}

View File

@@ -2,7 +2,7 @@ module std::io;
/* MultiReader implements the InStream interface and provides a logical
* concatenation of the provided readers. They are read sequentially. If all the
* data has been read, IoError.EOF is returned.
* data has been read, io::EOF is returned.
*/
struct MultiReader (InStream)
{
@@ -15,21 +15,7 @@ struct MultiReader (InStream)
<*
@param [&inout] self
@param [&inout] allocator
@require self.readers.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.new_init(&self, InStream... readers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
InStream []copy = allocator::new_array(allocator, InStream, readers.len);
copy[..] = readers[..];
*self = { .readers = copy, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@param [&inout] allocator
@require self.readers.len == 0 "Init may not run on already initialized data"
@require self.readers.len == 0 : "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.init(&self, Allocator allocator, InStream... readers)
@@ -42,22 +28,12 @@ fn MultiReader* MultiReader.init(&self, Allocator allocator, InStream... readers
<*
@param [&inout] self
@require self.readers.len == 0 "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.temp_init(&self, InStream... readers) @deprecated("Use tinit()")
{
return self.init(allocator::temp(), ...readers);
}
<*
@param [&inout] self
@require self.readers.len == 0 "Init may not run on already initialized data"
@require self.readers.len == 0 : "Init may not run on already initialized data"
@ensure self.index == 0
*>
fn MultiReader* MultiReader.tinit(&self, InStream... readers)
{
return self.init(allocator::temp(), ...readers);
return self.init(tmem, ...readers);
}
fn void MultiReader.free(&self)
@@ -67,26 +43,24 @@ fn void MultiReader.free(&self)
*self = {};
}
fn usz! MultiReader.read(&self, char[] bytes) @dynamic
fn usz? MultiReader.read(&self, char[] bytes) @dynamic
{
InStream r = self.readers[self.index];
usz! n = r.read(bytes);
usz? n = r.read(bytes);
if (catch err = n)
{
case IoError.EOF:
self.index++;
if (self.index >= self.readers.len)
{
return IoError.EOF?;
}
return self.read(bytes);
default:
return err?;
if (err != io::EOF) return err?;
self.index++;
if (self.index >= self.readers.len)
{
return io::EOF?;
}
return self.read(bytes);
}
return n;
}
fn char! MultiReader.read_byte(&self) @dynamic
fn char? MultiReader.read_byte(&self) @dynamic
{
char[1] data;
self.read(data[..])!;

View File

@@ -13,7 +13,7 @@ struct MultiWriter (OutStream)
@param [&inout] self
@param [&inout] allocator
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
@require self.writers.len == 0 : "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writers)
{
@@ -25,36 +25,12 @@ fn MultiWriter* MultiWriter.init(&self, Allocator allocator, OutStream... writer
<*
@param [&inout] self
@param [&inout] allocator
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.new_init(&self, OutStream... writers, Allocator allocator = allocator::heap()) @deprecated("Use init(mem)")
{
OutStream[] copy = allocator::new_array(allocator, OutStream, writers.len);
copy[..] = writers[..];
*self = { .writers = copy, .allocator = allocator };
return self;
}
<*
@param [&inout] self
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.temp_init(&self, OutStream... writers) @deprecated("Use tinit")
{
return self.init(allocator::temp(), ...writers);
}
<*
@param [&inout] self
@require writers.len > 0
@require self.writers.len == 0 "Init may not run on already initialized data"
@require self.writers.len == 0 : "Init may not run on already initialized data"
*>
fn MultiWriter* MultiWriter.tinit(&self, OutStream... writers)
{
return self.init(allocator::temp(), ...writers);
return self.init(tmem, ...writers);
}
fn void MultiWriter.free(&self)
@@ -64,18 +40,18 @@ fn void MultiWriter.free(&self)
*self = {};
}
fn usz! MultiWriter.write(&self, char[] bytes) @dynamic
fn usz? MultiWriter.write(&self, char[] bytes) @dynamic
{
usz n;
foreach (w : self.writers)
{
n = w.write(bytes)!;
if (n != bytes.len) return IoError.INCOMPLETE_WRITE?;
if (n != bytes.len) return INCOMPLETE_WRITE?;
}
return bytes.len;
}
fn void! MultiWriter.write_byte(&self, char c) @dynamic
fn void? MultiWriter.write_byte(&self, char c) @dynamic
{
char[1] data;
data[0] = c;

View File

@@ -13,8 +13,8 @@ struct Scanner (InStream)
The supplied buffer must be at least as large as the expected data length
including its pattern.
@param [&in] stream "The stream to read data from."
@require buffer.len > 0 "Non-empty buffer required."
@param [&in] stream : "The stream to read data from."
@require buffer.len > 0 : "Non-empty buffer required."
*>
fn void Scanner.init(&self, InStream stream, char[] buffer)
{
@@ -35,17 +35,17 @@ fn char[] Scanner.flush(&self) @dynamic
return buf;
}
fn void! Scanner.close(&self) @dynamic
fn void? Scanner.close(&self) @dynamic
{
if (&self.wrapped_stream.close) return self.wrapped_stream.close();
}
<*
Scan the stream for the next split character and return data up to the match.
@require pattern.len > 0 "Non-empty pattern required."
@require self.buf.len > pattern.len "Pattern too large."
@require pattern.len > 0 : "Non-empty pattern required."
@require self.buf.len > pattern.len : "Pattern too large."
*>
fn char[]! Scanner.scan(&self, String pattern = "\n")
fn char[]? Scanner.scan(&self, String pattern = "\n")
{
if (self.read_idx == 0)
{
@@ -65,7 +65,7 @@ fn char[]! Scanner.scan(&self, String pattern = "\n")
{
// Split pattern not found with maximized search, abort.
// Split pattern not found and already read as much as possible.
return SearchResult.MISSING?;
return NOT_FOUND?;
}
// Split pattern not found: maximize the search and try one more time.
self.buf[:n] = buf[..];
@@ -82,25 +82,23 @@ fn char[]! Scanner.scan(&self, String pattern = "\n")
return self.buf[:n + i];
}
macro usz! Scanner.find(&self, buf, pattern) @private
macro usz? Scanner.find(&self, buf, pattern) @private
{
return ((String)buf).index_of(pattern);
}
macro usz! Scanner.refill(&self, buf) @private
macro usz? Scanner.refill(&self, buf) @private
{
usz! n = self.wrapped_stream.read(buf);
usz? n = self.wrapped_stream.read(buf);
if (catch err = n)
{
case IoError.EOF:
return SearchResult.MISSING?;
default:
return err?;
if (err == io::EOF) return NOT_FOUND?;
return err?;
}
return n;
}
fn usz! Scanner.read(&self, char[] bytes) @dynamic
fn usz? Scanner.read(&self, char[] bytes) @dynamic
{
usz n;
if (self.pattern_idx < self.read_idx)
@@ -114,7 +112,7 @@ fn usz! Scanner.read(&self, char[] bytes) @dynamic
return n;
}
fn char! Scanner.read_byte(&self) @dynamic
fn char? Scanner.read_byte(&self) @dynamic
{
if (self.pattern_idx < self.read_idx)
{

View File

@@ -9,15 +9,15 @@ struct TeeReader (InStream)
<* Returns a reader that implements InStream and that will write any data read
from the wrapped reader r to the writer w. There is no internal buffering.
@param [&inout] r "Stream r to read from."
@param [&inout] w "Stream w to write to what it reads from r."
@param [&inout] r : "Stream r to read from."
@param [&inout] w : "Stream w to write to what it reads from r."
*>
macro TeeReader tee_reader(InStream r, OutStream w) => { r, w };
<*
@param [&inout] self
@param [&inout] r "Stream r to read from."
@param [&inout] w "Stream w to write to what it reads from r."
@param [&inout] r : "Stream r to read from."
@param [&inout] w : "Stream w to write to what it reads from r."
*>
fn TeeReader* TeeReader.init(&self, InStream r, OutStream w)
{
@@ -25,16 +25,16 @@ fn TeeReader* TeeReader.init(&self, InStream r, OutStream w)
return self;
}
fn usz! TeeReader.read(&self, char[] bytes) @dynamic
fn usz? TeeReader.read(&self, char[] bytes) @dynamic
{
usz nr, nw;
nr = self.r.read(bytes)!;
nw = self.w.write(bytes[:nr])!;
if (nr != nw) return IoError.GENERAL_ERROR?;
if (nr != nw) return GENERAL_ERROR?;
return nr;
}
fn char! TeeReader.read_byte(&self) @dynamic
fn char? TeeReader.read_byte(&self) @dynamic
{
char[1] data;
self.read(data[..])!;

View File

@@ -20,6 +20,22 @@ struct LongDivResult
CLong rem;
}
struct Fpos_t @if(!env::WIN32)
{
long __pos;
Mbstate_t __state;
}
struct Mbstate_t @if(!env::WIN32)
{
int __count;
union __value
{
uint __wch;
char[4] __wcb;
}
}
fn Errno errno()
{
return (Errno)os::errno();
@@ -30,13 +46,13 @@ fn void errno_set(Errno e)
os::errno_set((int)e);
}
distinct Errno = inline CInt;
def TerminateFunction = fn void();
def CompareFunction = fn int(void*, void*);
def JmpBuf = uptr[$$JMP_BUF_SIZE];
def Fd = CInt;
def Fpos_t = long; // TODO make sure fpos is correct on all targets.
def SignalFunction = fn void(CInt);
typedef Errno = inline CInt;
alias TerminateFunction = fn void();
alias CompareFunction = fn int(void*, void*);
alias JmpBuf = uptr[$$JMP_BUF_SIZE];
alias Fd = CInt;
alias Fpos_t @if(env::WIN32) = long;
alias SignalFunction = fn void(CInt);
const CInt SIGHUP = 1;
const CInt SIGINT = 2;
@@ -44,7 +60,6 @@ const CInt SIGQUIT = 3;
const CInt SIGILL = 4;
const CInt SIGTRAP = 5;
const CInt SIGABRT = 6;
const CInt SIGABTR @deprecated("use SIGABRT") = SIGABRT;
const CInt SIGBUS = BSD_FLAVOR_SIG ? 10 : 7; // Or Mips
const CInt SIGFPE = 8;
const CInt SIGKILL = 9;
@@ -61,20 +76,20 @@ const CInt SIGCHLD = BSD_FLAVOR_SIG ? 20 : 17;
const bool BSD_FLAVOR_SIG @local = env::DARWIN || env::BSD_FAMILY;
def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid);
def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid);
alias Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid);
alias Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid);
module libc @if(env::LIBC);
extern fn void abort();
extern fn CInt abs(CInt n);
extern fn ZString asctime(Tm* timeptr);
extern fn ZString asctime_r(Tm* timeptr, char* buf);
extern fn ZString asctime_r(Tm* timeptr, char* buf) @if(!env::WIN32);
extern fn CInt atexit(TerminateFunction func);
extern fn double atof(char* str);
extern fn int atoi(char* str);
extern fn CLongLong atoll(char* str);
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
extern fn void* bsearch(void* key, void* base, usz items, usz size, CompareFunction compare);
extern fn void* calloc(usz count, usz size);
extern fn void clearerr(CFile stream);
extern fn Clock_t clock();
@@ -82,6 +97,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 CInt fclose(CFile stream);
extern fn CFile fdopen(CInt fd, ZString mode) @if(!env::WIN32);
extern fn CInt feof(CFile stream);
@@ -108,7 +124,7 @@ extern fn CInt getchar();
extern fn ZString getenv(ZString name);
extern fn ZString gets(char* buffer);
extern fn Tm* gmtime(Time_t* timer);
extern fn Tm* gmtime_r(Time_t *timer, Tm* buf) @if(!env::WIN32);
extern fn Tm* gmtime_r(Time_t* timer, Tm* buf) @if(!env::WIN32);
extern fn CInt isatty(Fd fd) @if(!env::WIN32);
extern fn CLong labs(CLong x);
extern fn LongDivResult ldiv(CLong number, CLong denom);
@@ -170,7 +186,7 @@ extern fn CLong strtol(char* str, char** endptr, CInt base);
extern fn CULong strtoul(char* str, char** endptr, CInt base);
extern fn usz strxfrm(char* dest, ZString src, usz n);
extern fn CInt system(ZString str);
extern fn Time_t timegm(Tm *timeptr) @if(!env::WIN32);
extern fn Time_t timegm(Tm* timeptr) @if(!env::WIN32);
extern fn ZString tmpnam(ZString str);
extern fn CFile tmpfile();
extern fn CInt ungetc(CInt c, CFile stream);
@@ -181,14 +197,14 @@ extern fn CFile fmemopen(void* ptr, usz size, ZString mode);
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
extern fn CInt timespec_get(TimeSpec* ts, CInt base);
extern fn CInt nanosleep(TimeSpec* req, TimeSpec* remaining);
extern fn ZString ctime(Time_t *timer);
extern fn Time_t time(Time_t *timer);
extern fn ZString ctime(Time_t* timer);
extern fn Time_t time(Time_t* timer);
const CInt STDIN_FD = 0;
const CInt STDOUT_FD = 1;
const CInt STDERR_FD = 2;
module libc @if(env::LINUX);
module libc @if(env::LINUX || env::ANDROID);
extern CFile __stdin @extern("stdin");
extern CFile __stdout @extern("stdout");
extern CFile __stderr @extern("stderr");
@@ -231,7 +247,7 @@ macro CFile stdin() => __acrt_iob_func(STDIN_FD);
macro CFile stdout() => __acrt_iob_func(STDOUT_FD);
macro CFile stderr() => __acrt_iob_func(STDERR_FD);
module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN && !env::BSD_FAMILY);
module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::ANDROID && !env::DARWIN && !env::BSD_FAMILY);
macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; }
macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; }
macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; }
@@ -354,7 +370,7 @@ module libc;
// stdio
def CFile = void*;
alias CFile = void*;
const HAS_MALLOC_SIZE = env::LINUX || env::WIN32 || env::DARWIN;
@@ -388,7 +404,7 @@ const S_IRUSR = 0o000400; // Read permission, owner
const S_IWUSR = 0o000200; // Write permission, owner
const S_IXUSR = 0o000100; // Execute/search permission, owner
def SeekIndex = CLong;
alias SeekIndex = CLong;
// vsprintf vprintf not supported
@@ -406,7 +422,7 @@ struct Tm
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 */
char* tm_zone @if(!env::WIN32); /* timezone abbreviation */
CInt tm_nsec @if(env::WASI);
}
@@ -418,17 +434,19 @@ struct TimeSpec
}
def Clock_t = int @if(env::WIN32);
def Clock_t = CULong @if(!env::WIN32);
alias Clock_t @if(env::WIN32) = int;
alias Clock_t @if(!env::WIN32) = CLong;
def TimeOffset = int @if(env::WASI) ;
def TimeOffset = CLong @if(!env::WASI) ;
alias TimeOffset @if(env::WASI) = int;
alias TimeOffset @if(!env::WASI) = CLong ;
const int TIME_UTC = 1;
// Likely wrong, must be per platform.
const CLOCKS_PER_SEC = 1000000;
// This is a best-effort aproximation, 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;
module libc::errno;

View File

@@ -1,15 +1,15 @@
module libc @if(env::FREEBSD);
module libc @if(env::ANDROID);
// Checked for x86_64, this is notoriously incorrect when comparing with Rust code etc
def Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid);
def Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid);
def Blkcnt_t = long;
def Ino_t = ulong;
def Dev_t = ulong;
def Mode_t = uint;
def Ino64_t = ulong;
def Blkcnt64_t = long;
alias Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid);
alias Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid);
alias Blkcnt_t = long;
alias Ino_t = ulong;
alias Dev_t = ulong;
alias Mode_t = uint;
alias Ino64_t = ulong;
alias Blkcnt64_t = long;
struct Stat @if(env::X86_64)
{

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