mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Compare commits
372 Commits
lld-llvm14
...
v0.3.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a6f2eb22a9 | ||
|
|
ef7365224f | ||
|
|
f122d290f1 | ||
|
|
90f5c24d4c | ||
|
|
bcbcc3100f | ||
|
|
f6e798c283 | ||
|
|
f622b3bd4c | ||
|
|
fe54b84aed | ||
|
|
f46697bc54 | ||
|
|
e2b4a19c81 | ||
|
|
9e77fe7787 | ||
|
|
7223435df6 | ||
|
|
fe9ace713b | ||
|
|
03cdc8e3b1 | ||
|
|
352e09970c | ||
|
|
e8a8ac8bc1 | ||
|
|
334c004f1e | ||
|
|
aa534e2b3f | ||
|
|
2ee2bc3129 | ||
|
|
b88e5a8079 | ||
|
|
c339261d1e | ||
|
|
8008fb2c18 | ||
|
|
10219eccb2 | ||
|
|
1a510207e8 | ||
|
|
a21236d661 | ||
|
|
a749a4d265 | ||
|
|
b5afa98507 | ||
|
|
abf0f64ac0 | ||
|
|
dcf0b4c580 | ||
|
|
a9ed514fe5 | ||
|
|
8b0d409695 | ||
|
|
50d2a04c48 | ||
|
|
1864c69f31 | ||
|
|
af0174f360 | ||
|
|
2a4d43d7c7 | ||
|
|
13cb637cb4 | ||
|
|
de4bfe470e | ||
|
|
eaaa5362a5 | ||
|
|
1ea5625183 | ||
|
|
f7659776fc | ||
|
|
ed99e09c4e | ||
|
|
e9181a75e6 | ||
|
|
963b8f28ef | ||
|
|
5721fcc224 | ||
|
|
287de8f499 | ||
|
|
44dfeb621d | ||
|
|
eb87eb1987 | ||
|
|
c15fb7460c | ||
|
|
927ad2001f | ||
|
|
7647378e7c | ||
|
|
10b0b5f9c7 | ||
|
|
299ec1814b | ||
|
|
07700ed2c5 | ||
|
|
0ae586585a | ||
|
|
285299dcd5 | ||
|
|
2fefed5bda | ||
|
|
da1a45f718 | ||
|
|
bbef94b0fd | ||
|
|
c093f16fd0 | ||
|
|
5ff726d8d1 | ||
|
|
49eacb8824 | ||
|
|
450113d161 | ||
|
|
998c56533b | ||
|
|
73619817ba | ||
|
|
70f6ad1b27 | ||
|
|
e070bf22ee | ||
|
|
b086c85d9f | ||
|
|
66d87b25a3 | ||
|
|
d9e81c6035 | ||
|
|
98ae13c03d | ||
|
|
d9ea953b5c | ||
|
|
4cdfac092b | ||
|
|
1ef43cbc13 | ||
|
|
093bc2089a | ||
|
|
9b5e2b8578 | ||
|
|
2d377ada45 | ||
|
|
02374c6aab | ||
|
|
155c8862c9 | ||
|
|
f73b507ccb | ||
|
|
26e4662c3b | ||
|
|
ede224662c | ||
|
|
bd0e8f1ef1 | ||
|
|
e6a5f98606 | ||
|
|
e15dbd4907 | ||
|
|
ae7aa65f35 | ||
|
|
d13b7ac96a | ||
|
|
03fe2b575d | ||
|
|
f86ef8a743 | ||
|
|
7d58ce0dcb | ||
|
|
f8f249ee2c | ||
|
|
0adb15139f | ||
|
|
76ee384a4c | ||
|
|
b1ed066e55 | ||
|
|
160659c4e3 | ||
|
|
dfe3128b16 | ||
|
|
8f269a4f13 | ||
|
|
1ae251478b | ||
|
|
effec3a1f6 | ||
|
|
4d08fee30e | ||
|
|
5d9a7ab0a6 | ||
|
|
5e184f04e7 | ||
|
|
b2b1a3489a | ||
|
|
fc41179636 | ||
|
|
959c418e8b | ||
|
|
9424bba49f | ||
|
|
314369d069 | ||
|
|
ec3d77f4bd | ||
|
|
ab78663f3c | ||
|
|
348495b4c8 | ||
|
|
6523982f14 | ||
|
|
df8595cd64 | ||
|
|
febd11fa95 | ||
|
|
feba7b8ed2 | ||
|
|
3624c2a72c | ||
|
|
d5f965e137 | ||
|
|
70a429f832 | ||
|
|
7fa129932d | ||
|
|
73ac0b8ea0 | ||
|
|
407ed5a63d | ||
|
|
fa064276bc | ||
|
|
c84f82559c | ||
|
|
bb20a38cdb | ||
|
|
f010f6a926 | ||
|
|
ec1a5d97c9 | ||
|
|
870e716f59 | ||
|
|
d33ff212a7 | ||
|
|
ee533c5500 | ||
|
|
a281dbe812 | ||
|
|
1d39fc475f | ||
|
|
f5a1894876 | ||
|
|
db06f99445 | ||
|
|
05d4ec55f6 | ||
|
|
dcfcf460a5 | ||
|
|
eb86b83bd7 | ||
|
|
3d844b8722 | ||
|
|
6bd72c2ec4 | ||
|
|
4783946476 | ||
|
|
9f7ed00f04 | ||
|
|
f05ffc84d8 | ||
|
|
55f7046da8 | ||
|
|
c5b9b6c761 | ||
|
|
5029dc703e | ||
|
|
cbb731b42b | ||
|
|
258a6ba97a | ||
|
|
e1b5b0b60c | ||
|
|
58647043f4 | ||
|
|
379f9e60bf | ||
|
|
a4d4c27ca6 | ||
|
|
81bea9bad6 | ||
|
|
52f3948026 | ||
|
|
46c182f3d1 | ||
|
|
cc71b96c38 | ||
|
|
ad18d9ba48 | ||
|
|
4d5821408d | ||
|
|
48ee567f81 | ||
|
|
321c713687 | ||
|
|
be5c82cfa6 | ||
|
|
4fa4b2a631 | ||
|
|
9b14340a57 | ||
|
|
e7fad16d0f | ||
|
|
62e3b8063e | ||
|
|
c7f0f58e82 | ||
|
|
35549c21bc | ||
|
|
6220bda4a3 | ||
|
|
81a2474f75 | ||
|
|
28f2247602 | ||
|
|
1541354587 | ||
|
|
a66c0942f8 | ||
|
|
fd9d300b06 | ||
|
|
b60862ec7a | ||
|
|
c8166f6fdb | ||
|
|
4d27150952 | ||
|
|
d4aec525f5 | ||
|
|
ba9b203c52 | ||
|
|
7a797d0f28 | ||
|
|
755d6f93b7 | ||
|
|
7c2555bd3b | ||
|
|
469ec54282 | ||
|
|
1af41ee394 | ||
|
|
fa51402a16 | ||
|
|
0805750f52 | ||
|
|
03c627f646 | ||
|
|
3a09f71830 | ||
|
|
3d110850df | ||
|
|
b16e0b377e | ||
|
|
fa89ea7b79 | ||
|
|
a0a2e27127 | ||
|
|
e31967cc65 | ||
|
|
58f8e00376 | ||
|
|
581e2adbf0 | ||
|
|
e4e1541cd7 | ||
|
|
8eefce235f | ||
|
|
7cb03de16b | ||
|
|
4fbdb79b65 | ||
|
|
97de44b424 | ||
|
|
72c7da404e | ||
|
|
5adf09dd53 | ||
|
|
bdc7833b83 | ||
|
|
1a9a71f4c4 | ||
|
|
757ca3716e | ||
|
|
7a07b8ff13 | ||
|
|
bbc3792e7c | ||
|
|
a7110b52eb | ||
|
|
5d15ec23bb | ||
|
|
fffb8a1d0c | ||
|
|
54d6b1a4ec | ||
|
|
bb9c8fb93e | ||
|
|
b863ae2ec5 | ||
|
|
27a0e12979 | ||
|
|
dabe5769dd | ||
|
|
e82a7e7918 | ||
|
|
73fd9371eb | ||
|
|
4a296032ab | ||
|
|
5386b6fe50 | ||
|
|
c9ae0779e7 | ||
|
|
e33c81f85b | ||
|
|
a4e603442b | ||
|
|
6b1370ba76 | ||
|
|
52bcf4654a | ||
|
|
57e1084734 | ||
|
|
821b6a7ffa | ||
|
|
1858600449 | ||
|
|
656faa55bf | ||
|
|
b2a975945a | ||
|
|
f6ff3a0756 | ||
|
|
d95e0b4733 | ||
|
|
fd642e333b | ||
|
|
377f3eeb2e | ||
|
|
08c9400e02 | ||
|
|
afe466d7b3 | ||
|
|
ae51214c47 | ||
|
|
15586b3076 | ||
|
|
9a3e7fd34b | ||
|
|
9a69a13b04 | ||
|
|
0a17857737 | ||
|
|
31d151aa30 | ||
|
|
63d9853bd3 | ||
|
|
b72718ba2a | ||
|
|
213745fccd | ||
|
|
1a98fa0efe | ||
|
|
fc943a98ef | ||
|
|
7805fb8d1c | ||
|
|
d93c7090f6 | ||
|
|
44df6eb75b | ||
|
|
5cacc41925 | ||
|
|
571728a42e | ||
|
|
b1c78edc5e | ||
|
|
852c08e5fe | ||
|
|
7092a9f825 | ||
|
|
f513b6237f | ||
|
|
48fb342834 | ||
|
|
24e216e034 | ||
|
|
d4c1a08fcd | ||
|
|
ac7823cabb | ||
|
|
af9b99bd5a | ||
|
|
90dfc24491 | ||
|
|
aa337049ea | ||
|
|
92b4eeaa35 | ||
|
|
5ac35a49fd | ||
|
|
7a835418a7 | ||
|
|
1bf3bb51bf | ||
|
|
fd3591c8b7 | ||
|
|
cfcf5cbc16 | ||
|
|
9473adbc35 | ||
|
|
3eee9ddc06 | ||
|
|
ebd7611164 | ||
|
|
fba4df8f84 | ||
|
|
28c7db7414 | ||
|
|
167bb85a7e | ||
|
|
29918fd126 | ||
|
|
f881d195b0 | ||
|
|
ccda6dba40 | ||
|
|
31d0bb9684 | ||
|
|
1c46f7050c | ||
|
|
878bbed929 | ||
|
|
f93c20ca34 | ||
|
|
9866c7f9e9 | ||
|
|
00b880e35e | ||
|
|
398e19d727 | ||
|
|
046469843c | ||
|
|
3461f08a53 | ||
|
|
db5816edd6 | ||
|
|
6d2ab0c985 | ||
|
|
f966250185 | ||
|
|
cc8884d3d1 | ||
|
|
550bca79e9 | ||
|
|
272f134e78 | ||
|
|
a109efd14c | ||
|
|
b48ae91cbb | ||
|
|
14df5c2ed9 | ||
|
|
04695489b4 | ||
|
|
331f9b23f8 | ||
|
|
9886d381c0 | ||
|
|
12c17b62cf | ||
|
|
2698ba1a94 | ||
|
|
6f5f5feb97 | ||
|
|
64d883cb99 | ||
|
|
1adc8b8264 | ||
|
|
c02ce5ce2a | ||
|
|
e36e4b60e4 | ||
|
|
1d808be4b9 | ||
|
|
7065c28a08 | ||
|
|
da4df9d626 | ||
|
|
a7e4dda360 | ||
|
|
4471ccff13 | ||
|
|
cdff5c3e26 | ||
|
|
cc1bc58ed0 | ||
|
|
812bd8b3d0 | ||
|
|
7e0a29ef40 | ||
|
|
c1de3f059e | ||
|
|
62c1d2ddb5 | ||
|
|
b313bec69d | ||
|
|
036859c0c8 | ||
|
|
56a6e0b112 | ||
|
|
18f7f35e80 | ||
|
|
1d572f3e7c | ||
|
|
002ee006c1 | ||
|
|
8afbccd3fe | ||
|
|
6576725ed8 | ||
|
|
d3a053e049 | ||
|
|
4afec24434 | ||
|
|
29edd6e54e | ||
|
|
547d30eb1e | ||
|
|
6cf3c9f46b | ||
|
|
4beb7eff8f | ||
|
|
48a31cfa48 | ||
|
|
cd1138447e | ||
|
|
c29ad77cdb | ||
|
|
1c15ebe6d2 | ||
|
|
a68efec5e8 | ||
|
|
3f6b0646b3 | ||
|
|
28a8e17690 | ||
|
|
2a7d46844a | ||
|
|
92542ac1f9 | ||
|
|
59b41f8deb | ||
|
|
abfccb5576 | ||
|
|
ea5d7cd2e7 | ||
|
|
ca21b1daac | ||
|
|
9fdd66af42 | ||
|
|
d403912ec7 | ||
|
|
05f222616e | ||
|
|
253dbf3603 | ||
|
|
cfbfc29e84 | ||
|
|
bb020a1752 | ||
|
|
c8a614e43f | ||
|
|
bb28f6e61c | ||
|
|
b1d83e2ccd | ||
|
|
df41caabdd | ||
|
|
2f5d51c92c | ||
|
|
224390ce5a | ||
|
|
09d50ebf6c | ||
|
|
2d608a4d51 | ||
|
|
d511f150a7 | ||
|
|
f4dc4f64f2 | ||
|
|
6035cb4600 | ||
|
|
3d1eaad6b9 | ||
|
|
ca2bb505b6 | ||
|
|
8b4b4273cc | ||
|
|
7c91c56f3d | ||
|
|
5edafc5b2f | ||
|
|
dbb0dc302d | ||
|
|
e09e5c06d3 | ||
|
|
b0c55ff777 | ||
|
|
60832019bd | ||
|
|
42b5445225 | ||
|
|
9691d50a6f | ||
|
|
29a9769651 | ||
|
|
15e1db78a7 | ||
|
|
2f23d56a12 | ||
|
|
22ee082d00 | ||
|
|
212bc7d9af | ||
|
|
890c4bc435 |
317
.github/workflows/main.yml
vendored
317
.github/workflows/main.yml
vendored
@@ -6,7 +6,63 @@ on:
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
env:
|
||||
LLVM_RELEASE_VERSION: 15
|
||||
|
||||
jobs:
|
||||
|
||||
build-msvc:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [ Release, Debug ]
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: cmd
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build --config ${{ matrix.build_type }}
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\hello_world_many.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\fannkuch-redux.c3
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-run examples\contextfree\boolerr.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log run hello_world_win32
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
..\..\build\${{ matrix.build_type }}\c3c.exe --debug-log build hello_world_win32_lib
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -g1 --safe
|
||||
|
||||
- name: upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-windows-${{ matrix.build_type }}
|
||||
path: build\${{ matrix.build_type }}\c3c.exe
|
||||
|
||||
build-msys2-mingw:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
@@ -28,26 +84,39 @@ jobs:
|
||||
install: git binutils 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-13.0.1-2-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-13.0.1-2-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-llvm-15.0.3-1-any.pkg.tar.zst
|
||||
pacman --noconfirm -U https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-lld-15.0.3-1-any.pkg.tar.zst
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build --debug-log
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c.exe test_suite/
|
||||
python3 src/tester.py ../build/c3c.exe test_suite2/
|
||||
|
||||
|
||||
build-msys2-clang:
|
||||
runs-on: windows-latest
|
||||
#if: ${{ false }}
|
||||
if: ${{ false }}
|
||||
strategy:
|
||||
# Don't abort runners if a single one fails
|
||||
fail-fast: false
|
||||
@@ -70,10 +139,23 @@ jobs:
|
||||
run: |
|
||||
cmake -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build --debug-log
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
@@ -87,7 +169,7 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [12, 13, 14, 15]
|
||||
llvm_version: [13, 14, 15, 16]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -95,35 +177,79 @@ jobs:
|
||||
run: |
|
||||
sudo apt-get install zlib1g zlib1g-dev python3 ninja-build
|
||||
|
||||
- name: Install Clang ${{ matrix.llvm_version }}
|
||||
- name: Install Clang ${{matrix.llvm_version}}
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
|
||||
if [[ "${{matrix.llvm_version}}" < 16 ]]; then
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${{matrix.llvm_version}} main"
|
||||
else
|
||||
sudo add-apt-repository "deb http://apt.llvm.org/focal/ llvm-toolchain-focal main"
|
||||
fi
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}} llvm-${{matrix.llvm_version}}-dev lld-${{matrix.llvm_version}} liblld-${{matrix.llvm_version}}-dev
|
||||
|
||||
if [[ "${{matrix.llvm_version}}" > 12 ]]; then
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
fi
|
||||
sudo apt-get install -y libmlir-${{matrix.llvm_version}} libmlir-${{matrix.llvm_version}}-dev mlir-${{matrix.llvm_version}}-tools
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -G Ninja -DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} -DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake -B build \
|
||||
-G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
|
||||
-DCMAKE_C_COMPILER=clang-${{matrix.llvm_version}} \
|
||||
-DCMAKE_CXX_COMPILER=clang++-${{matrix.llvm_version}} \
|
||||
-DCMAKE_LINKER=lld-link-${{matrix.llvm_version}} \
|
||||
-DCMAKE_OBJCOPY=llvm-objcopy-${{matrix.llvm_version}} \
|
||||
-DCMAKE_STRIP=llvm-strip-${{matrix.llvm_version}} \
|
||||
-DCMAKE_DLLTOOL=llvm-dlltool-${{matrix.llvm_version}} \
|
||||
-DC3_LLVM_VERSION=${{matrix.llvm_version}}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build --debug-log
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --forcelinker
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
else
|
||||
python3 src/tester.py ../build/c3c test_suite2/
|
||||
fi
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
run: |
|
||||
mkdir linux
|
||||
cp -r lib linux
|
||||
cp msvc_build_libraries.py linux
|
||||
cp build/c3c linux
|
||||
tar czf c3-linux-${{matrix.build_type}}.tar.gz linux
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-linux-${{matrix.build_type}}
|
||||
path: c3-linux-${{matrix.build_type}}.tar.gz
|
||||
|
||||
|
||||
build-mac:
|
||||
runs-on: macos-latest
|
||||
@@ -132,11 +258,12 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build_type: [Release, Debug]
|
||||
llvm_version: [12, 13]
|
||||
llvm_version: [13, 14, 15]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download LLVM
|
||||
run: |
|
||||
brew update && brew install --overwrite python && brew install python-tk
|
||||
brew install llvm@${{ matrix.llvm_version }} botan ninja
|
||||
echo "/usr/local/opt/llvm@${{ matrix.llvm_version }}/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
@@ -147,12 +274,164 @@ jobs:
|
||||
cmake -B build -G Ninja -DC3_LLVM_VERSION=${{matrix.llvm_version}} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }}
|
||||
cmake --build build
|
||||
|
||||
- name: Compile and run some examples
|
||||
run: |
|
||||
cd resources
|
||||
../build/c3c compile-run examples/hello_world_many.c3
|
||||
../build/c3c compile-run examples/fannkuch-redux.c3
|
||||
../build/c3c compile-run examples/contextfree/boolerr.c3
|
||||
|
||||
- name: Compile run unit tests
|
||||
run: |
|
||||
cd test
|
||||
../build/c3c compile-test unit -g1 --safe
|
||||
|
||||
- name: Build testproject
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build --debug-log
|
||||
../../build/c3c run --debug-log
|
||||
|
||||
- name: Build testproject direct linker
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c run --debug-log --forcelinker
|
||||
|
||||
- name: Build testproject lib
|
||||
run: |
|
||||
cd resources/testproject
|
||||
../../build/c3c build hello_world_lib --debug-log
|
||||
|
||||
- name: run compiler tests
|
||||
run: |
|
||||
cd test
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
if [[ "${{matrix.llvm_version}}" < 15 ]]; then
|
||||
python3 src/tester.py ../build/c3c test_suite/
|
||||
else
|
||||
python3 src/tester.py ../build/c3c test_suite2/
|
||||
fi
|
||||
|
||||
- name: bundle_output
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
run: |
|
||||
mkdir macos
|
||||
cp -r lib macos
|
||||
cp msvc_build_libraries.py macos
|
||||
cp build/c3c macos
|
||||
zip -r c3-macos-${{matrix.build_type}}.zip macos
|
||||
|
||||
- name: upload artifacts
|
||||
if: matrix.llvm_version == env.LLVM_RELEASE_VERSION
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: c3-macos-${{matrix.build_type}}
|
||||
path: c3-macos-${{matrix.build_type}}.zip
|
||||
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-msvc, build-linux, build-mac]
|
||||
if: github.ref == 'refs/heads/master'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- 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@v3
|
||||
- run: cp -r lib c3-windows-Release
|
||||
- run: cp -r lib c3-windows-Debug
|
||||
- run: cp msvc_build_libraries.py c3-windows-Release
|
||||
- run: cp msvc_build_libraries.py c3-windows-Debug
|
||||
- run: cp install_win_reqs.bat c3-windows-Release
|
||||
- run: cp install_win_reqs.bat c3-windows-Debug
|
||||
- run: zip -r c3-windows-Release.zip c3-windows-Release
|
||||
- run: zip -r c3-windows-Debug.zip c3-windows-Debug
|
||||
|
||||
- id: create_release
|
||||
uses: actions/create-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
tag_name: latest
|
||||
release_name: latest
|
||||
draft: false
|
||||
prerelease: true
|
||||
|
||||
- name: upload windows
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-windows-Release.zip
|
||||
asset_name: c3-windows.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload windows debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-windows-Debug.zip
|
||||
asset_name: c3-windows-debug.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload linux
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-linux-Release/c3-linux-Release.tar.gz
|
||||
asset_name: c3-linux.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload linux debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-linux-Debug/c3-linux-Debug.tar.gz
|
||||
asset_name: c3-linux-debug.tar.gz
|
||||
asset_content_type: application/gzip
|
||||
|
||||
- name: upload macos
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-macos-Release/c3-macos-Release.zip
|
||||
asset_name: c3-macos.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
- name: upload macos debug
|
||||
uses: actions/upload-release-asset@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||
asset_path: c3-macos-Debug/c3-macos-Debug.zip
|
||||
asset_name: c3-macos-debug.zip
|
||||
asset_content_type: application/zip
|
||||
|
||||
180
CMakeLists.txt
180
CMakeLists.txt
@@ -1,10 +1,30 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
project(c3c)
|
||||
include(FetchContent)
|
||||
include(FeatureSummary)
|
||||
|
||||
set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL)
|
||||
set(CMAKE_FIND_PACKAGE_SORT_DIRECTION DEC)
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
if(MSVC)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} /O2")
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /Od /Zi")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Od /Zi")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
|
||||
endif()
|
||||
#set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1 -fsanitize=undefined")
|
||||
#set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O1 -fsanitize=undefined")
|
||||
#set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3 -fsanitize=undefined")
|
||||
#set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3 -fsanitize=undefined")
|
||||
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=undefined")
|
||||
|
||||
option(C3_USE_TB "Enable TB" OFF)
|
||||
set(C3_LLVM_VERSION "auto" CACHE STRING "Use LLVM version [default: auto]")
|
||||
option(C3_USE_MIMALLOC "Use built-in mimalloc" OFF)
|
||||
set(C3_MIMALLOC_TAG "v1.7.3" CACHE STRING "Used version of mimalloc")
|
||||
@@ -23,29 +43,50 @@ if(C3_USE_MIMALLOC)
|
||||
FetchContent_MakeAvailable(mimalloc)
|
||||
endif()
|
||||
|
||||
if(NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if(${C3_LLVM_VERSION} VERSION_LESS 12 OR ${C3_LLVM_VERSION} VERSION_GREATER 15)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
if (${C3_LLVM_VERSION} VERSION_LESS 13 OR ${C3_LLVM_VERSION} VERSION_GREATER 16)
|
||||
message(FATAL_ERROR "LLVM ${C3_LLVM_VERSION} is not supported!")
|
||||
endif()
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
endif()
|
||||
|
||||
if(CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
if (C3_LLVM_VERSION STREQUAL "auto")
|
||||
set(C3_LLVM_VERSION "14")
|
||||
endif()
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm1406/llvm-14.0.6-windows-amd64-msvc17-libcmt.7z
|
||||
)
|
||||
FetchContent_Declare(
|
||||
LLVM_Windows_debug
|
||||
URL https://github.com/c3lang/win-llvm/releases/download/llvm1406/llvm-14.0.6-windows-amd64-msvc17-libcmt-dbg.7z
|
||||
)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
message("Loading Windows LLVM debug libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows_debug)
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_debug_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
else()
|
||||
message("Loading Windows LLVM libraries, this may take a while...")
|
||||
FetchContent_MakeAvailable(LLVM_Windows)
|
||||
set(CMAKE_SYSTEM_PREFIX_PATH ${llvm_windows_SOURCE_DIR} ${CMAKE_SYSTEM_PREFIX_PATH})
|
||||
endif()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
find_package(LLD REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
if (NOT C3_LLVM_VERSION STREQUAL "auto")
|
||||
find_package(LLVM ${C3_LLVM_VERSION} REQUIRED CONFIG)
|
||||
else()
|
||||
find_package(LLVM REQUIRED CONFIG)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
|
||||
message(STATUS "Libraries located in: ${LLVM_LIBRARY_DIRS}")
|
||||
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
link_directories(${LLVM_LIBRARY_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
|
||||
set(CMAKE_C_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O1")
|
||||
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -gdwarf-3 -O3")
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -gdwarf-3")
|
||||
|
||||
set(LLVM_LINK_COMPONENTS
|
||||
AllTargetsAsmParsers
|
||||
AllTargetsCodeGens
|
||||
@@ -73,7 +114,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
Target
|
||||
TransformUtils
|
||||
WindowsManifest
|
||||
)
|
||||
)
|
||||
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_GREATER 14.1)
|
||||
set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} WindowsDriver)
|
||||
@@ -83,25 +124,31 @@ llvm_map_components_to_libnames(llvm_libs ${LLVM_LINK_COMPONENTS})
|
||||
|
||||
file(COPY ${CMAKE_SOURCE_DIR}/lib DESTINATION ${CMAKE_BINARY_DIR})
|
||||
|
||||
|
||||
# These don't seem to be reliable on windows.
|
||||
if(UNIX)
|
||||
message(STATUS "using find_library")
|
||||
# find_library(TB_LIB NAMES tinybackend.a PATHS ${CMAKE_SOURCE_DIR}/resources/tblib)
|
||||
find_library(LLD_COFF NAMES lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_COMMON NAMES lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_ELF NAMES lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MACHO NAMES lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_MINGW NAMES lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_WASM NAMES lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
message(STATUS "using find_library")
|
||||
if(C3_USE_TB)
|
||||
find_library(TB_LIB NAMES tildebackend.a tildebackend.lib PATHS ${CMAKE_SOURCE_DIR}/tb/)
|
||||
endif()
|
||||
find_library(LLD_COFF NAMES lldCOFF.lib lldCOFF.a liblldCOFF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_COMMON NAMES lldCommon.lib lldCommon.a liblldCommon.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_ELF NAMES lldELF.lib lldELF.a liblldELF.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
|
||||
find_library(LLD_MACHO NAMES lldMachO2.lib lldMachO2.a liblldMachO2.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
else ()
|
||||
find_library(LLD_MACHO NAMES lldMachO.lib lldMachO.a liblldMachO.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
endif ()
|
||||
find_library(LLD_MINGW NAMES lldMinGW.lib lldMinGW.a liblldMinGW.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_WASM NAMES lldWasm.lib lldWasm.a liblldWasm.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
|
||||
find_library(LLD_CORE NAMES lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_DRIVER NAMES lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_READER_WRITER NAMES lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
find_library(LLD_YAML NAMES lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS})
|
||||
endif()
|
||||
if (${LLVM_PACKAGE_VERSION} VERSION_LESS 14)
|
||||
find_library(LLD_CORE NAMES lldCore.lib lldCore.a liblldCore.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_DRIVER NAMES lldDriver.lib lldDriver.a liblldDriver.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_READER_WRITER NAMES lldReaderWriter.lib lldReaderWriter.a liblldReaderWriter.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
find_library(LLD_YAML NAMES lldYAML.lib lldYAML.a liblldYAML.a PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH)
|
||||
endif ()
|
||||
|
||||
set(lld_libs
|
||||
set(lld_libs
|
||||
${LLD_COFF}
|
||||
${LLD_COMMON}
|
||||
${LLD_WASM}
|
||||
@@ -113,10 +160,12 @@ if(UNIX)
|
||||
${LLD_YAML}
|
||||
${LLD_CORE}
|
||||
)
|
||||
message(STATUS "linking to llvm libs ${llvm_libs} ${lld_libs}")
|
||||
endif()
|
||||
if (APPLE)
|
||||
set(lld_libs ${lld_libs} xar)
|
||||
endif ()
|
||||
|
||||
message(STATUS "Found LLD ${lld_libs}")
|
||||
message(STATUS "linking to llvm libs ${lld_libs}")
|
||||
message(STATUS "Found lld libs ${lld_libs}")
|
||||
|
||||
add_library(c3c_wrappers STATIC wrapper/src/wrapper.cpp)
|
||||
|
||||
@@ -138,6 +187,7 @@ add_executable(c3c
|
||||
src/compiler/float.c
|
||||
src/compiler/headers.c
|
||||
src/compiler/lexer.c
|
||||
src/compiler/libraries.c
|
||||
src/compiler/linker.c
|
||||
src/compiler/llvm_codegen.c
|
||||
src/compiler/llvm_codegen_c_abi_aarch64.c
|
||||
@@ -167,6 +217,9 @@ add_executable(c3c
|
||||
src/compiler/sema_expr.c
|
||||
src/compiler/sema_internal.h
|
||||
src/compiler/sema_name_resolution.c
|
||||
src/compiler/sema_errors.c
|
||||
src/compiler/sema_builtins.c
|
||||
src/compiler/sema_initializers.c
|
||||
src/compiler/semantic_analyser.c
|
||||
src/compiler/sema_passes.c
|
||||
src/compiler/sema_stmts.c
|
||||
@@ -174,6 +227,7 @@ add_executable(c3c
|
||||
src/compiler/source_file.c
|
||||
src/compiler/symtab.c
|
||||
src/compiler/target.c
|
||||
src/compiler/sema_asm.c
|
||||
src/compiler/tb_codegen.c
|
||||
src/compiler/tilde_codegen.c
|
||||
src/compiler/tilde_codegen_instr.c
|
||||
@@ -195,18 +249,20 @@ add_executable(c3c
|
||||
src/utils/vmem.c
|
||||
src/utils/vmem.h
|
||||
src/utils/whereami.c
|
||||
src/utils/cpus.c
|
||||
src/compiler/decltable.c
|
||||
src/compiler/mac_support.c
|
||||
src/compiler/tilde_codegen_storeload.c
|
||||
src/compiler/llvm_codegen_storeload.c
|
||||
src/compiler/tilde_codegen_expr.c
|
||||
src/compiler/tilde_codegen_stmt.c
|
||||
src/compiler/tilde_codegen_type.c)
|
||||
src/compiler/tilde_codegen_type.c
|
||||
src/compiler/windows_support.c
|
||||
src/compiler/codegen_asm.c
|
||||
src/compiler/asm_target.c
|
||||
src/compiler/llvm_codegen_builtins.c
|
||||
src/compiler/expr.c src/utils/time.c)
|
||||
|
||||
if(NOT CMAKE_C_COMPILER_ID STREQUAL "MSVC")
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_compile_options(c3c PRIVATE -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
endif()
|
||||
|
||||
target_include_directories(c3c PRIVATE
|
||||
"${CMAKE_SOURCE_DIR}/src/")
|
||||
@@ -219,30 +275,42 @@ target_include_directories(c3c_wrappers PRIVATE
|
||||
|
||||
|
||||
|
||||
if(UNIX)
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${lld_libs})
|
||||
# target_link_libraries(c3c m ${llvm_libs} c3c_wrappers ${TB_LIB} ${lld_libs})
|
||||
if(C3_USE_MIMALLOC)
|
||||
target_link_libraries(c3c m mimalloc-static)
|
||||
endif()
|
||||
target_link_libraries(c3c_wrappers ${llvm_libs} ${lld_libs})
|
||||
target_link_libraries(c3c ${llvm_libs} c3c_wrappers ${lld_libs})
|
||||
if(C3_USE_TB)
|
||||
target_link_libraries(c3c c3c_wrappers ${TB_LIB})
|
||||
target_compile_definitions(c3c PUBLIC TB_BACKEND=1)
|
||||
else()
|
||||
# todo: maybe get this from llvm-config somehow? it should be in LLVM_DIR\..\..\..\bin I think.
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -LIBPATH:C:\\llvm\\llvm\\build\\Release\\lib") # needed for lldCommon.lib
|
||||
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -LIBPATH:${LLVM_LIBPATH}") # This doesn't seem to work for some reason
|
||||
message(STATUS "${LLVM_LIBPATH}")
|
||||
target_link_libraries(c3c debug ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML Advapi32)
|
||||
target_link_libraries(c3c optimized ${llvm_libs} c3c_wrappers lldCommon lldCore lldCOFF lldWASM lldMinGW lldELF lldDriver lldReaderWriter lldMachO lldYAML Advapi32)
|
||||
target_compile_definitions(c3c PUBLIC TB_BACKEND=0)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
if (CMAKE_C_COMPILER_ID STREQUAL "Clang" OR CMAKE_C_COMPILE_ID STREQUAL "GNU")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
if(C3_USE_MIMALLOC)
|
||||
target_link_libraries(c3c mimalloc-static)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
message("Adding MSVC options")
|
||||
target_compile_options(c3c PRIVATE /wd4068 /wd4090 /WX /Wv:18)
|
||||
target_compile_options(c3c_wrappers PUBLIC /wd4624 /wd4267 /wd4244 /WX /Wv:18)
|
||||
target_link_options(c3c_wrappers PUBLIC /ignore:4099)
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_options(c3c PUBLIC /MTd)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MTd)
|
||||
else()
|
||||
target_compile_options(c3c PUBLIC /MT)
|
||||
target_compile_options(c3c_wrappers PUBLIC /MT)
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "using gcc/clang warning switches")
|
||||
target_link_options(c3c PRIVATE -pthread)
|
||||
target_compile_options(c3c PRIVATE -pthread -Wall -Werror -Wno-unknown-pragmas -Wno-unused-result
|
||||
-Wno-unused-function -Wno-unused-variable -Wno-unused-parameter)
|
||||
if (WIN32)
|
||||
target_compile_definitions(c3c PRIVATE USE_PTHREAD=1)
|
||||
target_compile_options(c3c PRIVATE -mlong-double-64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
install(TARGETS c3c DESTINATION bin)
|
||||
|
||||
feature_summary(WHAT ALL)
|
||||
|
||||
105
README.md
105
README.md
@@ -29,12 +29,11 @@ The following code shows [generic modules](http://www.c3-lang.org/generics/) (mo
|
||||
```c++
|
||||
module stack <Type>;
|
||||
// Above: the parameterized type is applied to the entire module.
|
||||
import std::mem;
|
||||
|
||||
struct Stack
|
||||
{
|
||||
usize capacity;
|
||||
usize size;
|
||||
usz capacity;
|
||||
usz size;
|
||||
Type* elems;
|
||||
}
|
||||
|
||||
@@ -47,7 +46,7 @@ fn void Stack.push(Stack* this, Type element)
|
||||
if (this.capacity == this.size)
|
||||
{
|
||||
this.capacity *= 2;
|
||||
this.elems = mem::realloc(this.elems, $sizeof(Type) * this.capacity);
|
||||
this.elems = mem::realloc(this.elems, Type.sizeof * this.capacity);
|
||||
}
|
||||
this.elems[this.size++] = element;
|
||||
}
|
||||
@@ -129,7 +128,7 @@ fn void test()
|
||||
|
||||
### Current status
|
||||
|
||||
The current version of the compiler is alpha release 0.1.0.
|
||||
The current version of the compiler is alpha release 0.3.
|
||||
|
||||
It's possible to try out the current C3 compiler in the browser: https://ide.judge0.com/ – this is courtesy of the
|
||||
developer of Judge0.
|
||||
@@ -142,8 +141,7 @@ Follow the issues [here](https://github.com/c3lang/c3c/issues).
|
||||
If you have suggestions on how to improve the language, either [file an issue](https://github.com/c3lang/c3c/issues)
|
||||
or discuss C3 on its dedicated Discord: [https://discord.gg/qN76R87](https://discord.gg/qN76R87).
|
||||
|
||||
The compiler should compile on Linux, Windows (under MSVC, Mingw or MSYS2) and MacOS,
|
||||
but needs some install documentation for Windows.
|
||||
The compiler is currently verified to compile on Linux, Windows and MacOS.
|
||||
|
||||
|
||||
|
||||
@@ -151,8 +149,7 @@ but needs some install documentation for Windows.
|
||||
|
||||
- If you wish to contribute with ideas, please file issues or discuss on Discord.
|
||||
- Interested in contributing to the stdlib? Please get in touch on Discord.
|
||||
- Are you a Windows dev and know your way around Github CI? Please help us get MSVC CI working!
|
||||
- Install instructions for other Linux and Unix variants are appreciated.
|
||||
- Compilation instructions for other Linux and Unix variants are appreciated.
|
||||
- Would you like to contribute bindings to some library? It would be nice to have support for SDL, Raylib and more.
|
||||
- Build something with C3 and show it off and give feedback. The language is still open for significant tweaks.
|
||||
- Start work on the C -> C3 converter which takes C code and does a "best effort" to translate it to C3. The first version only needs to work on C headers.
|
||||
@@ -160,20 +157,34 @@ but needs some install documentation for Windows.
|
||||
|
||||
### Installing
|
||||
|
||||
#### Installing on Ubuntu 20.10
|
||||
#### Installing on Windows with precompiled binaries
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-windows.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-windows-debug.zip))
|
||||
3. Unzip exe and standard lib.
|
||||
4. Run `c3c.exe`.
|
||||
|
||||
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
||||
2. Install CMake: `sudo apt install cmake`
|
||||
3. Install LLVM 12 (or greater: C3C supports LLVM 12-15): `sudo apt-get install clang-12 zlib1g zlib1g-dev libllvm12 llvm-12 llvm-12-dev llvm-12-runtime liblld-12-dev liblld-12`
|
||||
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
5. Enter the C3C directory `cd c3c`.
|
||||
6. Create a build directory `mkdir build`
|
||||
7. Change directory to the build directory `cd build`
|
||||
8. Build: `cmake --build .`
|
||||
#### Installing on Debian with precompiled binaries
|
||||
1. Download tar file: [https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz](https://github.com/c3lang/c3c/releases/download/latest/c3-linux.tar.gz)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-linux-debug.tar.gz))
|
||||
2. Unpack executable and standard lib.
|
||||
3. Run `./c3c`.
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
#### Installing on Mac with precompiled binaries
|
||||
1. Make sure you have XCode with command line tools installed.
|
||||
2. Download the zip file: [https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip](https://github.com/c3lang/c3c/releases/download/latest/c3-macos.zip)
|
||||
(debug version [here](https://github.com/c3lang/c3c/releases/download/latest/c3-macos-debug.zip))
|
||||
3. Unzip executable and standard lib.
|
||||
4. Run `./c3c`.
|
||||
|
||||
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
#### Installing on Arch Linux
|
||||
There is an AUR package for the c3c compiler : [c3c-git](https://aur.archlinux.org/packages/c3c-git)
|
||||
You can use your AUR package manager or clone it manually:
|
||||
```sh
|
||||
git clone https://aur.archlinux.org/c3c-git.git
|
||||
cd c3c-git
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
#### Building via Docker
|
||||
|
||||
@@ -223,7 +234,57 @@ Then run
|
||||
c3c compile main.c3
|
||||
```
|
||||
|
||||
The generated binary will be called `a.out`.
|
||||
The generated binary will by default be named after the module that contains the main
|
||||
function. In our case that is `hello_world`, so the resulting binary will be
|
||||
called `hello_world` or `hello_world.exe`depending on platform.
|
||||
|
||||
### Compiling
|
||||
|
||||
#### Compiling on Windows
|
||||
|
||||
1. Make sure you have Visual Studio 17 2022 installed or alternatively install the "Buildtools for Visual Studio" (https://aka.ms/vs/17/release/vs_BuildTools.exe) and then select "Desktop development with C++" (there is also `c3c/resources/install_win_reqs.bat` to automate this)
|
||||
2. Install CMake
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Set up the CMake build `cmake -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release`
|
||||
6. Build: `cmake --build build --config Release`
|
||||
7. You should now have the c3c.exe
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
|
||||
You can try it out by running some sample code: `c3c.exe compile ../resources/examples/hash.c3`
|
||||
|
||||
*Note that if you run into linking issues when building, make sure that you are using the latest version of VS17.*
|
||||
|
||||
|
||||
#### Compiling on Ubuntu 20.10
|
||||
|
||||
1. Make sure you have a C compiler that handles C11 and a C++ compiler, such as GCC or Clang. Git also needs to be installed.
|
||||
2. Install CMake: `sudo apt install cmake`
|
||||
3. Install LLVM 13 (or greater: C3C supports LLVM 13-16): `sudo apt-get install clang-13 zlib1g zlib1g-dev libllvm13 llvm-13 llvm-13-dev llvm-13-runtime liblld-13-dev liblld-13`
|
||||
4. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
5. Enter the C3C directory `cd c3c`.
|
||||
6. Create a build directory `mkdir build`
|
||||
7. Change directory to the build directory `cd build`
|
||||
8. Set up CMake build: `cmake ..`
|
||||
9. Build: `cmake --build .`
|
||||
|
||||
You should now have a `c3c` executable.
|
||||
|
||||
You can try it out by running some sample code: `./c3c compile ../resources/examples/hash.c3`
|
||||
|
||||
|
||||
#### Compiling on other Linux / Unix variants
|
||||
|
||||
1. Install CMake.
|
||||
2. Install or compile LLVM and LLD *libraries* (version 13+ or higher)
|
||||
3. Clone the C3C github repository: `git clone https://github.com/c3lang/c3c.git`
|
||||
4. Enter the C3C directory `cd c3c`.
|
||||
5. Create a build directory `mkdir build`
|
||||
6. Change directory to the build directory `cd build`
|
||||
7. Set up CMake build for debug: `cmake ..`. At this point you may need to manually
|
||||
provide the link path to the LLVM CMake directories, e.g. `cmake -DLLVM_DIR=/usr/local/opt/llvm/lib/cmake/llvm/ ..`
|
||||
8. Build: `cmake --build .`
|
||||
|
||||
#### Licensing
|
||||
|
||||
@@ -232,4 +293,4 @@ MIT licensed.
|
||||
|
||||
#### Editor plugins
|
||||
|
||||
Editor plugins can be found at https://github.com/c3lang/editor-plugins.
|
||||
Editor plugins can be found at https://github.com/c3lang/editor-plugins.
|
||||
|
||||
@@ -29,17 +29,14 @@ else
|
||||
fi
|
||||
|
||||
TAG="$1"
|
||||
if [ "$1" = 20 ]; then
|
||||
UBUNTU_VERSION="20.04"
|
||||
LLVM_VERSION="12"
|
||||
elif [ "$1" = 21 ]; then
|
||||
if [ "$1" = 21 ]; then
|
||||
UBUNTU_VERSION="21.10"
|
||||
LLVM_VERSION="13"
|
||||
elif [ "$1" = 22 ]; then
|
||||
UBUNTU_VERSION="22.04"
|
||||
LLVM_VERSION="14"
|
||||
else
|
||||
echo "ERROR: expected 20, 21 or 22 as Ubuntu version argument" 1>&2
|
||||
echo "ERROR: expected 21 or 22 as Ubuntu version argument" 1>&2
|
||||
exit 2
|
||||
fi
|
||||
IMAGE="$IMAGE:$TAG"
|
||||
|
||||
17
install_win_reqs.bat
Normal file
17
install_win_reqs.bat
Normal file
@@ -0,0 +1,17 @@
|
||||
@echo off
|
||||
|
||||
set DOWNLOAD_URL=https://aka.ms/vs/17/release
|
||||
|
||||
mkdir tmp 2> NUL
|
||||
|
||||
if not exist "tmp\vs_buildtools.exe" (
|
||||
bitsadmin /transfer /download /priority foreground %DOWNLOAD_URL%/vs_buildtools.exe %CD%\tmp\vs_buildtools.exe
|
||||
)
|
||||
|
||||
echo Preparing Build Tools, please wait...
|
||||
tmp\vs_BuildTools.exe --quiet --wait --layout tmp\ --add Microsoft.VisualStudio.Component.Windows10SDK.19041
|
||||
|
||||
echo Installing Build Tools, please wait...
|
||||
tmp\vs_BuildTools.exe --quiet --wait --noweb --add Microsoft.VisualStudio.Component.Windows10SDK.19041
|
||||
|
||||
REM rmdir tmp /s /q
|
||||
@@ -1,25 +0,0 @@
|
||||
// Copyright (c) 2021 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::array;
|
||||
import std::mem;
|
||||
|
||||
/**
|
||||
* @require elements > 0
|
||||
**/
|
||||
macro alloc($Type, usize elements)
|
||||
{
|
||||
assert($Type.max / elements < $Type.sizeof);
|
||||
$Type* ptr = mem::alloc($Type.sizeof * elements, $alignof($Type));
|
||||
return ptr[0..(elements - 1)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require elements > 0
|
||||
**/
|
||||
macro calloc($Type, usize elements)
|
||||
{
|
||||
assert($Type.max / elements < $Type.sizeof);
|
||||
$Type* ptr = mem::calloc($sizeof($Type) * elements, $alignof($Type));
|
||||
return ptr[0..(elements - 1)];
|
||||
}
|
||||
73
lib/std/ascii.c3
Normal file
73
lib/std/ascii.c3
Normal file
@@ -0,0 +1,73 @@
|
||||
module std::ascii;
|
||||
|
||||
macro bool in_range_m(c, start, len) => (uint)(c - start) < len;
|
||||
macro bool is_lower_m(c) => in_range_m(c, 0x61, 26);
|
||||
macro bool is_upper_m(c) => in_range_m(c, 0x41, 26);
|
||||
macro bool is_digit_m(c) => in_range_m(c, 0x30, 10);
|
||||
macro bool is_bdigit_m(c) => in_range_m(c, 0x30, 2);
|
||||
macro bool is_odigit_m(c) => in_range_m(c, 0x30, 8);
|
||||
macro bool is_xdigit_m(c) => in_range_m(c | 32, 0x61, 6) || is_digit_m(c);
|
||||
macro bool is_alpha_m(c) => in_range_m(c | 32, 0x61, 26);
|
||||
macro bool is_print_m(c) => in_range_m(c, 0x20, 95);
|
||||
macro bool is_graph_m(c) => in_range_m(c, 0x21, 94);
|
||||
macro bool is_space_m(c) => in_range_m(c, 0x9, 5) || c == 0x20;
|
||||
macro bool is_alnum_m(c) => is_alpha_m(c) || is_digit_m(c);
|
||||
macro bool is_punct_m(c) => !is_alnum_m(c) && is_graph_m(c);
|
||||
macro bool is_blank_m(c) => c == 0x20 || c == 0x9;
|
||||
macro bool is_cntrl_m(c) => c < 0x20 || c == 0x7f;
|
||||
macro to_lower_m(c) => is_upper_m(c) ? c + 0x20 : c;
|
||||
macro to_upper_m(c) => is_lower_m(c) ? c - 0x20 : c;
|
||||
|
||||
fn bool in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool is_lower(char c) => is_lower_m(c);
|
||||
fn bool is_upper(char c) => is_upper_m(c);
|
||||
fn bool is_digit(char c) => is_digit_m(c);
|
||||
fn bool is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool is_print(char c) => is_print_m(c);
|
||||
fn bool is_graph(char c) => is_graph_m(c);
|
||||
fn bool is_space(char c) => is_space_m(c);
|
||||
fn bool is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool is_punct(char c) => is_punct_m(c);
|
||||
fn bool is_blank(char c) => is_blank_m(c);
|
||||
fn bool is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool char.in_range(char c, char start, char len) => in_range_m(c, start, len);
|
||||
fn bool char.is_lower(char c) => is_lower_m(c);
|
||||
fn bool char.is_upper(char c) => is_upper_m(c);
|
||||
fn bool char.is_digit(char c) => is_digit_m(c);
|
||||
fn bool char.is_bdigit(char c) => is_bdigit_m(c);
|
||||
fn bool char.is_odigit(char c) => is_odigit_m(c);
|
||||
fn bool char.is_xdigit(char c) => is_xdigit_m(c);
|
||||
fn bool char.is_alpha(char c) => is_alpha_m(c);
|
||||
fn bool char.is_print(char c) => is_print_m(c);
|
||||
fn bool char.is_graph(char c) => is_graph_m(c);
|
||||
fn bool char.is_space(char c) => is_space_m(c);
|
||||
fn bool char.is_alnum(char c) => is_alnum_m(c);
|
||||
fn bool char.is_punct(char c) => is_punct_m(c);
|
||||
fn bool char.is_blank(char c) => is_blank_m(c);
|
||||
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
|
||||
fn char char.to_lower(char c) => (char)to_lower_m(c);
|
||||
fn char char.to_upper(char c) => (char)to_upper_m(c);
|
||||
|
||||
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
|
||||
fn bool uint.is_lower(uint c) => is_lower_m(c);
|
||||
fn bool uint.is_upper(uint c) => is_upper_m(c);
|
||||
fn bool uint.is_digit(uint c) => is_digit_m(c);
|
||||
fn bool uint.is_bdigit(uint c) => is_bdigit_m(c);
|
||||
fn bool uint.is_odigit(uint c) => is_odigit_m(c);
|
||||
fn bool uint.is_xdigit(uint c) => is_xdigit_m(c);
|
||||
fn bool uint.is_alpha(uint c) => is_alpha_m(c);
|
||||
fn bool uint.is_print(uint c) => is_print_m(c);
|
||||
fn bool uint.is_graph(uint c) => is_graph_m(c);
|
||||
fn bool uint.is_space(uint c) => is_space_m(c);
|
||||
fn bool uint.is_alnum(uint c) => is_alnum_m(c);
|
||||
fn bool uint.is_punct(uint c) => is_punct_m(c);
|
||||
fn bool uint.is_blank(uint c) => is_blank_m(c);
|
||||
fn bool uint.is_cntrl(uint c) => is_cntrl_m(c);
|
||||
fn uint uint.to_lower(uint c) => (uint)to_lower_m(c);
|
||||
fn uint uint.to_upper(uint c) => (uint)to_upper_m(c);
|
||||
173
lib/std/bits.c3
Normal file
173
lib/std/bits.c3
Normal file
@@ -0,0 +1,173 @@
|
||||
module std::bits;
|
||||
|
||||
|
||||
/**
|
||||
* @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`
|
||||
**/
|
||||
macro bswap(i) @builtin = $$bswap(i);
|
||||
|
||||
|
||||
macro uint[<*>].popcount(uint[<*>] i) = $$popcount(i);
|
||||
macro uint[<*>].ctz(uint[<*>] i) = $$ctz(i);
|
||||
macro uint[<*>].clz(uint[<*>] i) = $$clz(i);
|
||||
macro uint[<*>] uint[<*>].fshl(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].fshr(uint[<*>] hi, uint[<*>] lo, uint[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint[<*>] uint[<*>].rotl(uint[<*>] i, uint[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro uint[<*>] uint[<*>].rotr(uint[<*>] i, uint[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int[<*>].popcount(int[<*>] i) = $$popcount(i);
|
||||
macro int[<*>].ctz(int[<*>] i) = $$ctz(i);
|
||||
macro int[<*>].clz(int[<*>] i) = $$clz(i);
|
||||
macro int[<*>] int[<*>].fshl(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].fshr(int[<*>] hi, int[<*>] lo, int[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int[<*>] int[<*>].rotl(int[<*>] i, int[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro int[<*>] int[<*>].rotr(int[<*>] i, int[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ushort[<*>].popcount(ushort[<*>] i) = $$popcount(i);
|
||||
macro ushort[<*>].ctz(ushort[<*>] i) = $$ctz(i);
|
||||
macro ushort[<*>].clz(ushort[<*>] i) = $$clz(i);
|
||||
macro ushort[<*>] ushort[<*>].fshl(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].fshr(ushort[<*>] hi, ushort[<*>] lo, ushort[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotl(ushort[<*>] i, ushort[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ushort[<*>] ushort[<*>].rotr(ushort[<*>] i, ushort[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro short[<*>].popcount(short[<*>] i) = $$popcount(i);
|
||||
macro short[<*>].ctz(short[<*>] i) = $$ctz(i);
|
||||
macro short[<*>].clz(short[<*>] i) = $$clz(i);
|
||||
macro short[<*>] short[<*>].fshl(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].fshr(short[<*>] hi, short[<*>] lo, short[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro short[<*>] short[<*>].rotl(short[<*>] i, short[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro short[<*>] short[<*>].rotr(short[<*>] i, short[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro char[<*>].popcount(char[<*>] i) = $$popcount(i);
|
||||
macro char[<*>].ctz(char[<*>] i) = $$ctz(i);
|
||||
macro char[<*>].clz(char[<*>] i) = $$clz(i);
|
||||
macro char[<*>] char[<*>].fshl(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].fshr(char[<*>] hi, char[<*>] lo, char[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro char[<*>] char[<*>].rotl(char[<*>] i, char[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro char[<*>] char[<*>].rotr(char[<*>] i, char[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ichar[<*>].popcount(ichar[<*>] i) = $$popcount(i);
|
||||
macro ichar[<*>].ctz(ichar[<*>] i) = $$ctz(i);
|
||||
macro ichar[<*>].clz(ichar[<*>] i) = $$clz(i);
|
||||
macro ichar[<*>] ichar[<*>].fshl(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].fshr(ichar[<*>] hi, ichar[<*>] lo, ichar[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotl(ichar[<*>] i, ichar[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ichar[<*>] ichar[<*>].rotr(ichar[<*>] i, ichar[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ulong[<*>].popcount(ulong[<*>] i) = $$popcount(i);
|
||||
macro ulong[<*>].ctz(ulong[<*>] i) = $$ctz(i);
|
||||
macro ulong[<*>].clz(ulong[<*>] i) = $$clz(i);
|
||||
macro ulong[<*>] ulong[<*>].fshl(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].fshr(ulong[<*>] hi, ulong[<*>] lo, ulong[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotl(ulong[<*>] i, ulong[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro ulong[<*>] ulong[<*>].rotr(ulong[<*>] i, ulong[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro long[<*>].popcount(long[<*>] i) = $$popcount(i);
|
||||
macro long[<*>].ctz(long[<*>] i) = $$ctz(i);
|
||||
macro long[<*>].clz(long[<*>] i) = $$clz(i);
|
||||
macro long[<*>] long[<*>].fshl(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].fshr(long[<*>] hi, long[<*>] lo, long[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro long[<*>] long[<*>].rotl(long[<*>] i, long[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro long[<*>] long[<*>].rotr(long[<*>] i, long[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint128[<*>].popcount(uint128[<*>] i) = $$popcount(i);
|
||||
macro uint128[<*>].ctz(uint128[<*>] i) = $$ctz(i);
|
||||
macro uint128[<*>].clz(uint128[<*>] i) = $$clz(i);
|
||||
macro uint128[<*>] uint128[<*>].fshl(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].fshr(uint128[<*>] hi, uint128[<*>] lo, uint128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotl(uint128[<*>] i, uint128[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro uint128[<*>] uint128[<*>].rotr(uint128[<*>] i, uint128[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int128[<*>].popcount(int128[<*>] i) = $$popcount(i);
|
||||
macro int128[<*>].ctz(int128[<*>] i) = $$ctz(i);
|
||||
macro int128[<*>].clz(int128[<*>] i) = $$clz(i);
|
||||
macro int128[<*>] int128[<*>].fshl(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshl(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].fshr(int128[<*>] hi, int128[<*>] lo, int128[<*>] shift) => $$fshr(hi, lo, shift);
|
||||
macro int128[<*>] int128[<*>].rotl(int128[<*>] i, int128[<*>] shift) => $$fshl(i, i, shift);
|
||||
macro int128[<*>] int128[<*>].rotr(int128[<*>] i, int128[<*>] shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint.popcount(uint i) = $$popcount(i);
|
||||
macro uint.ctz(uint i) = $$ctz(i);
|
||||
macro uint.clz(uint i) = $$clz(i);
|
||||
macro uint uint.fshl(uint hi, uint lo, uint shift) => $$fshl(hi, lo, shift);
|
||||
macro uint uint.fshr(uint hi, uint lo, uint shift) => $$fshr(hi, lo, shift);
|
||||
macro uint uint.rotl(uint i, uint shift) => $$fshl(i, i, shift);
|
||||
macro uint uint.rotr(uint i, uint shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int.popcount(int i) = $$popcount(i);
|
||||
macro int.ctz(int i) = $$ctz(i);
|
||||
macro int.clz(int i) = $$clz(i);
|
||||
macro int int.fshl(int hi, int lo, int shift) => $$fshl(hi, lo, shift);
|
||||
macro int int.fshr(int hi, int lo, int shift) => $$fshr(hi, lo, shift);
|
||||
macro int int.rotl(int i, int shift) => $$fshl(i, i, shift);
|
||||
macro int int.rotr(int i, int shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ushort.popcount(ushort i) = $$popcount(i);
|
||||
macro ushort.ctz(ushort i) = $$ctz(i);
|
||||
macro ushort.clz(ushort i) = $$clz(i);
|
||||
macro ushort ushort.fshl(ushort hi, ushort lo, ushort shift) => $$fshl(hi, lo, shift);
|
||||
macro ushort ushort.fshr(ushort hi, ushort lo, ushort shift) => $$fshr(hi, lo, shift);
|
||||
macro ushort ushort.rotl(ushort i, ushort shift) => $$fshl(i, i, shift);
|
||||
macro ushort ushort.rotr(ushort i, ushort shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro short.popcount(short i) = $$popcount(i);
|
||||
macro short.ctz(short i) = $$ctz(i);
|
||||
macro short.clz(short i) = $$clz(i);
|
||||
macro short short.fshl(short hi, short lo, short shift) => $$fshl(hi, lo, shift);
|
||||
macro short short.fshr(short hi, short lo, short shift) => $$fshr(hi, lo, shift);
|
||||
macro short short.rotl(short i, short shift) => $$fshl(i, i, shift);
|
||||
macro short short.rotr(short i, short shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro char.popcount(char i) = $$popcount(i);
|
||||
macro char.ctz(char i) = $$ctz(i);
|
||||
macro char.clz(char i) = $$clz(i);
|
||||
macro char char.fshl(char hi, char lo, char shift) => $$fshl(hi, lo, shift);
|
||||
macro char char.fshr(char hi, char lo, char shift) => $$fshr(hi, lo, shift);
|
||||
macro char char.rotl(char i, char shift) => $$fshl(i, i, shift);
|
||||
macro char char.rotr(char i, char shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ichar.popcount(ichar i) = $$popcount(i);
|
||||
macro ichar.ctz(ichar i) = $$ctz(i);
|
||||
macro ichar.clz(ichar i) = $$clz(i);
|
||||
macro ichar ichar.fshl(ichar hi, ichar lo, ichar shift) => $$fshl(hi, lo, shift);
|
||||
macro ichar ichar.fshr(ichar hi, ichar lo, ichar shift) => $$fshr(hi, lo, shift);
|
||||
macro ichar ichar.rotl(ichar i, ichar shift) => $$fshl(i, i, shift);
|
||||
macro ichar ichar.rotr(ichar i, ichar shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro ulong.popcount(ulong i) = $$popcount(i);
|
||||
macro ulong.ctz(ulong i) = $$ctz(i);
|
||||
macro ulong.clz(ulong i) = $$clz(i);
|
||||
macro ulong ulong.fshl(ulong hi, ulong lo, ulong shift) => $$fshl(hi, lo, shift);
|
||||
macro ulong ulong.fshr(ulong hi, ulong lo, ulong shift) => $$fshr(hi, lo, shift);
|
||||
macro ulong ulong.rotl(ulong i, ulong shift) => $$fshl(i, i, shift);
|
||||
macro ulong ulong.rotr(ulong i, ulong shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro long.popcount(long i) = $$popcount(i);
|
||||
macro long.ctz(long i) = $$ctz(i);
|
||||
macro long.clz(long i) = $$clz(i);
|
||||
macro long long.fshl(long hi, long lo, long shift) => $$fshl(hi, lo, shift);
|
||||
macro long long.fshr(long hi, long lo, long shift) => $$fshr(hi, lo, shift);
|
||||
macro long long.rotl(long i, long shift) => $$fshl(i, i, shift);
|
||||
macro long long.rotr(long i, long shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro uint128.popcount(uint128 i) = $$popcount(i);
|
||||
macro uint128.ctz(uint128 i) = $$ctz(i);
|
||||
macro uint128.clz(uint128 i) = $$clz(i);
|
||||
macro uint128 uint128.fshl(uint128 hi, uint128 lo, uint128 shift) => $$fshl(hi, lo, shift);
|
||||
macro uint128 uint128.fshr(uint128 hi, uint128 lo, uint128 shift) => $$fshr(hi, lo, shift);
|
||||
macro uint128 uint128.rotl(uint128 i, uint128 shift) => $$fshl(i, i, shift);
|
||||
macro uint128 uint128.rotr(uint128 i, uint128 shift) => $$fshr(i, i, shift);
|
||||
|
||||
macro int128.popcount(int128 i) = $$popcount(i);
|
||||
macro int128.ctz(int128 i) = $$ctz(i);
|
||||
macro int128.clz(int128 i) = $$clz(i);
|
||||
macro int128 int128.fshl(int128 hi, int128 lo, int128 shift) => $$fshl(hi, lo, shift);
|
||||
macro int128 int128.fshr(int128 hi, int128 lo, int128 shift) => $$fshr(hi, lo, shift);
|
||||
macro int128 int128.rotl(int128 i, int128 shift) => $$fshl(i, i, shift);
|
||||
macro int128 int128.rotr(int128 i, int128 shift) => $$fshr(i, i, shift);
|
||||
@@ -1,176 +0,0 @@
|
||||
// Copyright (c) 2021 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::builtin;
|
||||
|
||||
fault VarCastResult
|
||||
{
|
||||
TYPE_MISMATCH
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a variable on the stack, then restores it at the end of the
|
||||
* macro scope.
|
||||
*
|
||||
* @param variable `the variable to store and restore`
|
||||
**/
|
||||
macro void scope(&variable; @body) @autoimport
|
||||
{
|
||||
$typeof(variable) temp = variable;
|
||||
defer variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a variant type to a type, returning an failure if there is a type mismatch.
|
||||
*
|
||||
* @param v `the variant to convert to the given type.`
|
||||
* @param $Type `the type to convert to`
|
||||
* @return `The variant.ptr converted to its type.`
|
||||
**/
|
||||
macro varcast(variant v, $Type) @autoimport
|
||||
{
|
||||
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
extern fn void printf(char*, ...);
|
||||
|
||||
struct CallstackElement
|
||||
{
|
||||
CallstackElement* prev;
|
||||
char* function;
|
||||
char* file;
|
||||
uint line;
|
||||
}
|
||||
fn void panic(char* message, char *file, char *function, uint line) @autoimport
|
||||
{
|
||||
CallstackElement* stack = $$stacktrace();
|
||||
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
|
||||
|
||||
if (stack) stack = stack.prev;
|
||||
if (stack)
|
||||
{
|
||||
libc::fprintf(@libc::stderr(), "\nERROR: '%s'\n", message);
|
||||
}
|
||||
else
|
||||
{
|
||||
libc::fprintf(@libc::stderr(), "\nERROR: '%s', function %s (%s:%d)\n", message, function, file, line);
|
||||
}
|
||||
while (stack)
|
||||
{
|
||||
libc::fprintf(@libc::stderr(), " at function %s (%s:%u)\n", stack.function, stack.file, stack.line);
|
||||
if (stack == stack.prev) break;
|
||||
stack = stack.prev;
|
||||
}
|
||||
|
||||
$endif;
|
||||
|
||||
$$trap();
|
||||
}
|
||||
|
||||
macro unreachable($string = "Unreachable statement reached.") @autoimport @noreturn
|
||||
{
|
||||
panic($string, $$FILE, $$FUNC, $$LINE);
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
/*
|
||||
enum TypeKind
|
||||
{
|
||||
VOID,
|
||||
BOOL,
|
||||
FLOAT,
|
||||
INTEGER,
|
||||
STRUCT,
|
||||
UNION,
|
||||
ERROR,
|
||||
ENUM,
|
||||
ARRAY,
|
||||
POINTER,
|
||||
VAR_ARRAY,
|
||||
SUBARRAY,
|
||||
OPAQUE
|
||||
// ALIAS,
|
||||
}
|
||||
|
||||
struct TypeData
|
||||
{
|
||||
typeid typeId;
|
||||
TypeKind kind;
|
||||
int size;
|
||||
int alignment;
|
||||
char* name;
|
||||
char* fullName;
|
||||
}
|
||||
|
||||
struct TypeAlias
|
||||
{
|
||||
TypeData data;
|
||||
typeid aliasType;
|
||||
}
|
||||
|
||||
struct TypeError
|
||||
{
|
||||
TypeData data;
|
||||
TypeErrorValue[] errors;
|
||||
}
|
||||
|
||||
struct TypeArray
|
||||
{
|
||||
TypeData data;
|
||||
typeid elementType;
|
||||
ulong elements;
|
||||
}
|
||||
|
||||
struct TypeVarArray
|
||||
{
|
||||
TypeData data;
|
||||
typeid elementType;
|
||||
}
|
||||
|
||||
struct TypeSubarray
|
||||
{
|
||||
TypeData data;
|
||||
typeid elementType;
|
||||
}
|
||||
|
||||
struct TypePointer
|
||||
{
|
||||
TypeData data;
|
||||
typeid baseType;
|
||||
}
|
||||
|
||||
struct TypeStruct
|
||||
{
|
||||
TypeData data;
|
||||
TypeData*[] fields;
|
||||
}
|
||||
|
||||
struct TypeUnion
|
||||
{
|
||||
TypeData data;
|
||||
TypeData*[] variants;
|
||||
}
|
||||
|
||||
struct TypeEnum
|
||||
{
|
||||
TypeData data;
|
||||
typeid valueType;
|
||||
TypeData*[] associated_value_types;
|
||||
}
|
||||
|
||||
|
||||
struct TypeEnumValue
|
||||
{
|
||||
char* name;
|
||||
ulong value;
|
||||
void*[] associated_values;
|
||||
}
|
||||
|
||||
struct TypeErrorValue
|
||||
{
|
||||
char* name;
|
||||
ulong value;
|
||||
}
|
||||
*/
|
||||
119
lib/std/core/allocators/arena_allocator.c3
Normal file
119
lib/std/core/allocators/arena_allocator.c3
Normal file
@@ -0,0 +1,119 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
struct ArenaAllocatorHeader
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
private fn void*! arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
ArenaAllocator* arena = (ArenaAllocator*)data;
|
||||
bool clear = false;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
clear = true;
|
||||
nextcase;
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
void* mem = arena._alloc(size, alignment, offset)?;
|
||||
if (clear) mem::clear(mem, size, DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
case ALIGNED_REALLOC:
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
return arena._realloc(old_pointer, size, alignment, offset)?;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
assert((uptr)old_pointer >= (uptr)arena.data.ptr, "Pointer originates from a different allocator.");
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
// Reclaim memory if it's the last element.
|
||||
if (old_pointer + header.size == &arena.data[arena.used])
|
||||
{
|
||||
arena.used -= header.size + ArenaAllocatorHeader.sizeof;
|
||||
}
|
||||
return null;
|
||||
case MARK:
|
||||
return (void*)(uptr)arena.used;
|
||||
case RESET:
|
||||
arena.used = size;
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment > 0 `alignment must be non zero`
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require this != null
|
||||
**/
|
||||
private fn void*! ArenaAllocator._alloc(ArenaAllocator* this, usz size, usz alignment, usz offset)
|
||||
{
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
|
||||
void* start_mem = this.data.ptr;
|
||||
void* unaligned_pointer_to_offset = start_mem + this.used + ArenaAllocatorHeader.sizeof + offset;
|
||||
void* aligned_pointer_to_offset = mem::aligned_pointer(unaligned_pointer_to_offset, alignment);
|
||||
usz end = (usz)(aligned_pointer_to_offset - this.data.ptr) + size - offset;
|
||||
if (end > total_len) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
this.used = end;
|
||||
void *mem = aligned_pointer_to_offset - offset;
|
||||
ArenaAllocatorHeader* header = mem - ArenaAllocatorHeader.sizeof;
|
||||
header.size = size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment > 0 `alignment must be non zero`
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require offset <= MAX_MEMORY_ALIGNMENT `offset too big`
|
||||
* @require offset <= size && offset >= 0
|
||||
* @require mem::aligned_offset(offset, ArenaAllocatorHeader.alignof) == offset
|
||||
* @require this != null
|
||||
**/
|
||||
private fn void*! ArenaAllocator._realloc(ArenaAllocator* this, void *old_pointer, usz size, usz alignment, usz offset)
|
||||
{
|
||||
assert(old_pointer >= this.data.ptr, "Pointer originates from a different allocator.");
|
||||
usz total_len = this.data.len;
|
||||
if (size > total_len) return AllocationFailure.CHUNK_TOO_LARGE!;
|
||||
ArenaAllocatorHeader* header = old_pointer - ArenaAllocatorHeader.sizeof;
|
||||
usz old_size = header.size;
|
||||
// Do last allocation and alignment match?
|
||||
if (&this.data[this.used] == old_pointer + old_size && mem::ptr_is_aligned(old_pointer + offset, alignment))
|
||||
{
|
||||
if (old_size >= size)
|
||||
{
|
||||
this.used -= old_size - size;
|
||||
}
|
||||
else
|
||||
{
|
||||
usz new_used = this.used + size - old_size;
|
||||
if (new_used > total_len) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
this.used = new_used;
|
||||
}
|
||||
header.size = size;
|
||||
return old_pointer;
|
||||
}
|
||||
// Otherwise just allocate new memory.
|
||||
void* mem = this._alloc(size, alignment, offset)?;
|
||||
mem::copy(mem, old_pointer, old_size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
198
lib/std/core/allocators/dynamic_arena.c3
Normal file
198
lib/std/core/allocators/dynamic_arena.c3
Normal file
@@ -0,0 +1,198 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
private struct DynamicArenaPage
|
||||
{
|
||||
void* memory;
|
||||
void* prev_arena;
|
||||
usz total;
|
||||
usz used;
|
||||
void* last_ptr;
|
||||
}
|
||||
|
||||
private struct DynamicArenaChunk
|
||||
{
|
||||
usz size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require ptr && this
|
||||
* @require this.page `tried to free pointer on invalid allocator`
|
||||
*/
|
||||
private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr)
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
if (ptr == current_page.last_ptr)
|
||||
{
|
||||
current_page.used = (usz)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
current_page.last_ptr = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require old_pointer && size > 0
|
||||
* @require this.page `tried to realloc pointer on invalid allocator`
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator._realloc(DynamicArenaAllocator* this, void* old_pointer, usz size, usz alignment, usz offset)
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usz* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
||||
usz old_size = *old_size_ptr;
|
||||
// We have the old pointer and it's correctly aligned.
|
||||
if (old_size >= size && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
*old_size_ptr = size;
|
||||
if (current_page.last_ptr == old_pointer)
|
||||
{
|
||||
current_page.used = (usz)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
return old_pointer;
|
||||
}
|
||||
if REUSE: (current_page.last_ptr == old_pointer && mem::ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
assert(size > old_size);
|
||||
usz add_size = size - old_size;
|
||||
if (add_size + current_page.used > current_page.total) break REUSE;
|
||||
*old_size_ptr = size;
|
||||
current_page.used += add_size;
|
||||
return old_pointer;
|
||||
}
|
||||
void* new_mem = this._alloc(size, alignment, offset)?;
|
||||
mem::copy(new_mem, old_pointer, old_size, DEFAULT_MEM_ALIGNMENT);
|
||||
return new_mem;
|
||||
}
|
||||
|
||||
private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
|
||||
{
|
||||
DynamicArenaPage* page = this.page;
|
||||
DynamicArenaPage** unused_page_ptr = &this.unused_page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
page.used = 0;
|
||||
DynamicArenaPage* prev_unused = *unused_page_ptr;
|
||||
*unused_page_ptr = page;
|
||||
page.prev_arena = prev_unused;
|
||||
page = next_page;
|
||||
}
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator._alloc_new(DynamicArenaAllocator* this, usz size, usz alignment, usz offset)
|
||||
{
|
||||
// First, make sure that we can align it, extending the page size if needed.
|
||||
usz page_size = max(this.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + offset, alignment) - offset);
|
||||
|
||||
// Grab the page without alignment (we do it ourselves)
|
||||
void* mem = this.backing_allocator.alloc(page_size)?;
|
||||
DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof);
|
||||
if (catch err = page)
|
||||
{
|
||||
this.backing_allocator.free(mem)?;
|
||||
return err!;
|
||||
}
|
||||
page.memory = mem;
|
||||
void* mem_start = mem::aligned_pointer(mem + offset + DynamicArenaChunk.sizeof, alignment) - offset;
|
||||
assert(mem_start + size < mem + page_size);
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem_start - 1;
|
||||
chunk.size = size;
|
||||
page.prev_arena = this.page;
|
||||
page.total = page_size;
|
||||
page.used = mem_start + size - page.memory;
|
||||
this.page = page;
|
||||
page.last_ptr = mem_start;
|
||||
return mem_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require this
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator._alloc(DynamicArenaAllocator* this, usz size, usz alignment, usz offset)
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = this.page;
|
||||
if (!page && this.unused_page)
|
||||
{
|
||||
this.page = page = this.unused_page;
|
||||
this.unused_page = page.prev_arena;
|
||||
page.prev_arena = null;
|
||||
}
|
||||
if (!page) return this._alloc_new(size, alignment, offset);
|
||||
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
||||
usz new_used = start - page.memory + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
{
|
||||
if ((page = this.unused_page))
|
||||
{
|
||||
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof + offset, alignment) - offset;
|
||||
new_used = start + size - page.memory;
|
||||
if (page.total >= new_used)
|
||||
{
|
||||
this.unused_page = page.prev_arena;
|
||||
page.prev_arena = this.page;
|
||||
this.page = page;
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
return this._alloc_new(size, alignment, offset);
|
||||
}
|
||||
|
||||
page.used = new_used;
|
||||
assert(start + size == page.memory + page.used);
|
||||
void* mem = start;
|
||||
DynamicArenaChunk* chunk = (DynamicArenaChunk*)mem - 1;
|
||||
chunk.size = size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
private fn void*! dynamic_arena_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
DynamicArenaAllocator* allocator = (DynamicArenaAllocator*)data;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for calloc.");
|
||||
if (!size) return null;
|
||||
void* mem = allocator._alloc(size, alignment, offset)?;
|
||||
mem::clear(mem, size, DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return allocator._alloc(size, alignment, offset);
|
||||
case REALLOC:
|
||||
case ALIGNED_REALLOC:
|
||||
if (!size)
|
||||
{
|
||||
if (!old_pointer) return null;
|
||||
allocator.free(old_pointer);
|
||||
return null;
|
||||
}
|
||||
if (!old_pointer) return allocator._alloc(size, alignment, offset);
|
||||
void* mem = allocator._realloc(old_pointer, size, alignment, offset)?;
|
||||
return mem;
|
||||
case ALIGNED_FREE:
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
allocator.free(old_pointer);
|
||||
return null;
|
||||
case MARK:
|
||||
unreachable("Tried to mark a dynamic arena");
|
||||
case RESET:
|
||||
allocator.reset();
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
117
lib/std/core/allocators/mem_allocator_fn.c3
Normal file
117
lib/std/core/allocators/mem_allocator_fn.c3
Normal file
@@ -0,0 +1,117 @@
|
||||
module std::core::mem::allocator;
|
||||
import libc;
|
||||
|
||||
private const Allocator _NULL_ALLOCATOR = { &null_allocator_fn };
|
||||
private const Allocator _SYSTEM_ALLOCATOR = { &libc_allocator_fn };
|
||||
|
||||
private fn void*! null_allocator_fn(Allocator* this, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ALLOC:
|
||||
case CALLOC:
|
||||
case REALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
case ALIGNED_REALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
return AllocationFailure.OUT_OF_MEMORY!;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private struct AlignedBlock
|
||||
{
|
||||
usz len;
|
||||
void* start;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
private fn void* _libc_aligned_alloc(usz bytes, usz alignment, usz offset) @inline
|
||||
{
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
void* data = libc::malloc(header + bytes);
|
||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
||||
assert(mem > data);
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
*desc = { bytes, data };
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
private fn void* _libc_aligned_calloc(usz bytes, usz alignment, usz offset) @inline
|
||||
{
|
||||
usz header = mem::aligned_offset(AlignedBlock.sizeof + offset, alignment) - offset;
|
||||
void* data = libc::calloc(header + bytes, 1);
|
||||
void* mem = mem::aligned_pointer(data + header + offset, alignment) - offset;
|
||||
AlignedBlock* desc = (AlignedBlock*)mem - 1;
|
||||
assert(mem > data);
|
||||
*desc = { bytes, data };
|
||||
return mem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require bytes > 0
|
||||
* @require alignment > 0
|
||||
**/
|
||||
private fn void* _libc_aligned_realloc(void* old_pointer, usz bytes, usz alignment, usz offset) @inline
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
void* data_start = desc.start;
|
||||
void* new_data = _libc_aligned_calloc(bytes, alignment, offset);
|
||||
mem::copy(new_data, old_pointer, desc.len > bytes ? desc.len : bytes, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
|
||||
libc::free(data_start);
|
||||
return new_data;
|
||||
}
|
||||
|
||||
private fn void _libc_aligned_free(void* old_pointer) @inline
|
||||
{
|
||||
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
|
||||
libc::free(desc.start);
|
||||
}
|
||||
|
||||
fn void*! libc_allocator_fn(Allocator* unused, usz bytes, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @inline
|
||||
{
|
||||
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
|
||||
assert(math::is_power_of_2(alignment), "Alignment was not a power of 2");
|
||||
|
||||
void* data;
|
||||
switch (kind)
|
||||
{
|
||||
case ALIGNED_ALLOC:
|
||||
data = _libc_aligned_alloc(bytes, alignment, offset);
|
||||
case ALLOC:
|
||||
data = libc::malloc(bytes);
|
||||
case ALIGNED_CALLOC:
|
||||
data = _libc_aligned_calloc(bytes, alignment, offset);
|
||||
case CALLOC:
|
||||
data = libc::calloc(bytes, 1);
|
||||
case ALIGNED_REALLOC:
|
||||
if (!bytes) nextcase ALIGNED_FREE;
|
||||
if (!old_pointer) nextcase ALIGNED_CALLOC;
|
||||
data = _libc_aligned_realloc(old_pointer, bytes, alignment, offset);
|
||||
case REALLOC:
|
||||
if (!bytes) nextcase FREE;
|
||||
if (!old_pointer) nextcase CALLOC;
|
||||
data = libc::realloc(old_pointer, bytes);
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION!;
|
||||
case ALIGNED_FREE:
|
||||
_libc_aligned_free(old_pointer);
|
||||
return null;
|
||||
case FREE:
|
||||
libc::free(old_pointer);
|
||||
return null;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
return data;
|
||||
}
|
||||
256
lib/std/core/allocators/temp_allocator.c3
Normal file
256
lib/std/core/allocators/temp_allocator.c3
Normal file
@@ -0,0 +1,256 @@
|
||||
module std::core::mem::allocator;
|
||||
import std::io;
|
||||
|
||||
private struct TempAllocatorChunk
|
||||
{
|
||||
usz size;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
struct TempAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
Allocator* backing_allocator;
|
||||
TempAllocatorPage* last_page;
|
||||
usz used;
|
||||
usz capacity;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
|
||||
private const usz PAGE_IS_ALIGNED = (usz)isz.max + 1u;
|
||||
|
||||
|
||||
struct TempAllocatorPage
|
||||
{
|
||||
TempAllocatorPage* prev_page;
|
||||
void* start;
|
||||
usz mark;
|
||||
usz size;
|
||||
usz ident;
|
||||
char[*] data;
|
||||
}
|
||||
|
||||
macro usz TempAllocatorPage.pagesize(TempAllocatorPage* page) { return page.size & ~PAGE_IS_ALIGNED; }
|
||||
macro bool TempAllocatorPage.is_aligned(TempAllocatorPage* page) { return page.size & PAGE_IS_ALIGNED == PAGE_IS_ALIGNED; }
|
||||
|
||||
/**
|
||||
* @require size >= 16
|
||||
**/
|
||||
fn TempAllocator*! new_temp(usz size, Allocator* backing_allocator)
|
||||
{
|
||||
TempAllocator* allocator = backing_allocator.alloc(size + TempAllocator.sizeof)?;
|
||||
allocator.last_page = null;
|
||||
allocator.function = &temp_allocator_function;
|
||||
allocator.backing_allocator = backing_allocator;
|
||||
allocator.used = 0;
|
||||
allocator.capacity = size;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
private fn void*! temp_allocator_function(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
TempAllocator* arena = (TempAllocator*)data;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALIGNED_CALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return arena._alloc(size, alignment_for_allocation(alignment), offset, true);
|
||||
case ALLOC:
|
||||
case ALIGNED_ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return arena._alloc(size, alignment_for_allocation(alignment), offset, false);
|
||||
case ALIGNED_REALLOC:
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
return arena._realloc(old_pointer, size, alignment_for_allocation(alignment), offset);
|
||||
case FREE:
|
||||
case ALIGNED_FREE:
|
||||
if (!old_pointer) return null;
|
||||
arena._free(old_pointer)?;
|
||||
return null;
|
||||
case MARK:
|
||||
return (void*)(uptr)arena.used;
|
||||
case RESET:
|
||||
arena._reset(size)?;
|
||||
return null;
|
||||
}
|
||||
unreachable();
|
||||
}
|
||||
|
||||
private fn void! TempAllocator._free(TempAllocator* this, void* old_pointer)
|
||||
{
|
||||
// TODO fix free
|
||||
assert((uptr)old_pointer >= (uptr)&this.data, "Pointer originates from a different allocator.");
|
||||
usz old_size = *(usz*)(old_pointer - DEFAULT_SIZE_PREFIX);
|
||||
if (old_pointer + old_size == &this.data[this.used])
|
||||
{
|
||||
this.used -= old_size;
|
||||
}
|
||||
}
|
||||
private fn void! TempAllocator._reset(TempAllocator* this, usz mark)
|
||||
{
|
||||
TempAllocatorPage *last_page = this.last_page;
|
||||
while (last_page && last_page.mark > mark)
|
||||
{
|
||||
TempAllocatorPage *to_free = last_page;
|
||||
last_page = last_page.prev_page;
|
||||
this._free_page(to_free)?;
|
||||
}
|
||||
this.last_page = last_page;
|
||||
this.used = mark;
|
||||
}
|
||||
|
||||
private fn void! TempAllocator._free_page(TempAllocator* this, TempAllocatorPage* page) @inline
|
||||
{
|
||||
void* mem = page.start;
|
||||
if (page.is_aligned()) return this.backing_allocator.free_aligned(mem);
|
||||
return this.backing_allocator.free(mem);
|
||||
}
|
||||
|
||||
private fn void*! TempAllocator._realloc_page(TempAllocator* this, TempAllocatorPage* page, usz size, usz alignment, usz offset) @inline
|
||||
{
|
||||
// Then the actual start pointer:
|
||||
void* real_pointer = page.start;
|
||||
|
||||
// Walk backwards to find the pointer to this page.
|
||||
TempAllocatorPage **pointer_to_prev = &this.last_page;
|
||||
// Remove the page from the list
|
||||
while (*pointer_to_prev != page)
|
||||
{
|
||||
pointer_to_prev = &((*pointer_to_prev).prev_page);
|
||||
}
|
||||
*pointer_to_prev = page.prev_page;
|
||||
usz page_size = page.pagesize();
|
||||
// Clear on size > original size.
|
||||
void* data = this._alloc(size, alignment, offset, false)?;
|
||||
mem::copy(data, &page.data[0], page_size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
|
||||
if (page.is_aligned())
|
||||
{
|
||||
this.backing_allocator.free_aligned(real_pointer)?;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.backing_allocator.free(real_pointer)?;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
private fn void*! TempAllocator._realloc(TempAllocator* this, void* pointer, usz size, usz alignment, usz offset) @inline
|
||||
{
|
||||
TempAllocatorChunk *chunk = pointer - TempAllocatorChunk.sizeof;
|
||||
if (chunk.size == (usz)-1)
|
||||
{
|
||||
assert(this.last_page, "Realloc of non temp pointer");
|
||||
// First grab the page
|
||||
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
|
||||
return this._realloc_page(page, size, alignment, offset);
|
||||
}
|
||||
assert(pointer < &this.data + this.capacity && pointer >= &this.data, "This is not a temp allocated pointer.");
|
||||
assert(pointer < &this.data + this.used, "This is a stale temp pointer.");
|
||||
|
||||
// TODO optimize last allocation
|
||||
TempAllocatorChunk* data = this._alloc(size, alignment, offset, size > chunk.size)?;
|
||||
mem::copy(data, pointer, chunk.size, DEFAULT_MEM_ALIGNMENT, DEFAULT_MEM_ALIGNMENT);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require this != null
|
||||
**/
|
||||
private fn void*! TempAllocator._alloc(TempAllocator* this, usz size, usz alignment, usz offset, bool clear)
|
||||
{
|
||||
void* start_mem = &this.data;
|
||||
void* starting_ptr = start_mem + this.used;
|
||||
void* aligned_header_start = mem::aligned_pointer(starting_ptr, TempAllocatorChunk.alignof);
|
||||
void* mem = aligned_header_start + TempAllocatorChunk.sizeof;
|
||||
if (alignment > TempAllocatorChunk.alignof)
|
||||
{
|
||||
mem = mem::aligned_pointer(mem + offset, alignment) - offset;
|
||||
}
|
||||
usz new_usage = (usz)(mem - start_mem) + size;
|
||||
|
||||
// Arena alignment, simple!
|
||||
if (new_usage <= this.capacity)
|
||||
{
|
||||
TempAllocatorChunk* chunk_start = mem - TempAllocatorChunk.sizeof;
|
||||
chunk_start.size = size;
|
||||
this.used = new_usage;
|
||||
if (clear) mem::clear(mem, size, DEFAULT_MEM_ALIGNMENT);
|
||||
return mem;
|
||||
}
|
||||
|
||||
// Fallback to backing allocator
|
||||
TempAllocatorPage* page;
|
||||
|
||||
// We have something we need to align.
|
||||
if (alignment > DEFAULT_MEM_ALIGNMENT || offset)
|
||||
{
|
||||
// This is actually simpler, since it will create the offset for us.
|
||||
usz total_alloc_size = TempAllocatorPage.sizeof + size;
|
||||
if (clear)
|
||||
{
|
||||
page = this.backing_allocator.calloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
|
||||
}
|
||||
else
|
||||
{
|
||||
page = this.backing_allocator.alloc_aligned(total_alloc_size, alignment, TempAllocatorPage.sizeof + offset)?;
|
||||
}
|
||||
page.start = page;
|
||||
page.size = size | PAGE_IS_ALIGNED;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here we might need to pad
|
||||
usz padded_header_size = mem::aligned_offset(TempAllocatorPage.sizeof, DEFAULT_MEM_ALIGNMENT);
|
||||
usz total_alloc_size = padded_header_size + size;
|
||||
void* alloc = (clear ? this.backing_allocator.calloc(total_alloc_size) : this.backing_allocator.alloc(total_alloc_size))?;
|
||||
|
||||
// Find the page.
|
||||
page = alloc + padded_header_size - TempAllocatorPage.sizeof;
|
||||
assert(mem::ptr_is_aligned(page, TempAllocator.alignof));
|
||||
assert(mem::ptr_is_aligned(&page.data[0], DEFAULT_MEM_ALIGNMENT));
|
||||
page.start = alloc;
|
||||
page.size = size;
|
||||
}
|
||||
|
||||
// Mark it as a page
|
||||
page.ident = ~(usz)0;
|
||||
// Store when it was created
|
||||
page.mark = ++this.used;
|
||||
// Hook up the page.
|
||||
page.prev_page = this.last_page;
|
||||
this.last_page = page;
|
||||
return &page.data[0];
|
||||
}
|
||||
|
||||
fn void TempAllocator.print_pages(TempAllocator* this, File f)
|
||||
{
|
||||
TempAllocatorPage *last_page = this.last_page;
|
||||
if (!last_page)
|
||||
{
|
||||
f.printf("No pages.\n");
|
||||
return;
|
||||
}
|
||||
f.printf("---Pages----\n");
|
||||
uint index = 0;
|
||||
while (last_page)
|
||||
{
|
||||
bool is_not_aligned = !(last_page.size & (1u64 << 63));
|
||||
f.printf("%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;
|
||||
}
|
||||
}
|
||||
40
lib/std/core/array.c3
Normal file
40
lib/std/core/array.c3
Normal file
@@ -0,0 +1,40 @@
|
||||
module std::core::array;
|
||||
|
||||
macro tconcat(arr1, arr2)
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = array::talloc($Type, arr1.len + arr2.len);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
if (arr2.len > 0)
|
||||
{
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
macro index_of(array, element)
|
||||
{
|
||||
foreach (i, &e : array)
|
||||
{
|
||||
if (*e == element) return i;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
macro concat(arr1, arr2)
|
||||
{
|
||||
var $Type = $typeof(arr1[0]);
|
||||
$Type[] result = array::alloc($Type, arr1.len + arr2.len);
|
||||
if (arr1.len > 0)
|
||||
{
|
||||
mem::copy(result.ptr, &arr1[0], arr1.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
if (arr2.len > 0)
|
||||
{
|
||||
mem::copy(&result[arr1.len], &arr2[0], arr2.len * $Type.sizeof, $Type.alignof, $Type.alignof);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
82
lib/std/core/bitorder.c3
Normal file
82
lib/std/core/bitorder.c3
Normal file
@@ -0,0 +1,82 @@
|
||||
module std::core::bitorder;
|
||||
|
||||
bitstruct ShortBE : short @bigendian
|
||||
{
|
||||
short val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct UShortBE : ushort @bigendian
|
||||
{
|
||||
ushort val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct IntBE : int @bigendian
|
||||
{
|
||||
int val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct UIntBE : int @bigendian
|
||||
{
|
||||
uint val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct LongBE : long @bigendian
|
||||
{
|
||||
long val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct ULongBE : ulong @bigendian
|
||||
{
|
||||
ulong val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct Int128BE : int128 @bigendian
|
||||
{
|
||||
int128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct UInt128BE : uint128 @bigendian
|
||||
{
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct ShortLE : short @littleendian
|
||||
{
|
||||
short val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct UShortLE : ushort @littleendian
|
||||
{
|
||||
ushort val : 0..15;
|
||||
}
|
||||
|
||||
bitstruct IntLE : int @littleendian
|
||||
{
|
||||
int val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct UIntLE : int @littleendian
|
||||
{
|
||||
uint val : 0..31;
|
||||
}
|
||||
|
||||
bitstruct LongLE : long @littleendian
|
||||
{
|
||||
long val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct ULongLE : ulong @littleendian
|
||||
{
|
||||
ulong val : 0..63;
|
||||
}
|
||||
|
||||
bitstruct Int128LE : int128 @littleendian
|
||||
{
|
||||
int128 val : 0..127;
|
||||
}
|
||||
|
||||
bitstruct UInt128LE : uint128 @littleendian
|
||||
{
|
||||
uint128 val : 0..127;
|
||||
}
|
||||
|
||||
167
lib/std/core/builtin.c3
Normal file
167
lib/std/core/builtin.c3
Normal file
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. 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::core::builtin;
|
||||
import libc;
|
||||
import std::hash;
|
||||
|
||||
fault IteratorResult
|
||||
{
|
||||
NO_MORE_ELEMENT
|
||||
}
|
||||
|
||||
fault SearchResult
|
||||
{
|
||||
MISSING
|
||||
}
|
||||
|
||||
fault VarCastResult
|
||||
{
|
||||
TYPE_MISMATCH
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a variable on the stack, then restores it at the end of the
|
||||
* macro scope.
|
||||
*
|
||||
* @param variable `the variable to store and restore`
|
||||
**/
|
||||
macro void @scope(&variable; @body) @builtin
|
||||
{
|
||||
var temp = variable;
|
||||
defer variable = temp;
|
||||
@body();
|
||||
}
|
||||
|
||||
macro void @swap(&a, &b) @builtin
|
||||
{
|
||||
var temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a variant type to a type, returning an failure if there is a type mismatch.
|
||||
*
|
||||
* @param v `the variant to convert to the given type.`
|
||||
* @param $Type `the type to convert to`
|
||||
* @return `The variant.ptr converted to its type.`
|
||||
**/
|
||||
macro varcast(variant v, $Type) @builtin
|
||||
{
|
||||
if (v.type != $Type.typeid) return VarCastResult.TYPE_MISMATCH!;
|
||||
return ($Type*)v.ptr;
|
||||
}
|
||||
|
||||
struct CallstackElement
|
||||
{
|
||||
CallstackElement* prev;
|
||||
char[] function;
|
||||
char[] file;
|
||||
uint line;
|
||||
}
|
||||
|
||||
fn void default_panic(char[] message, char[] file, char[] function, uint line)
|
||||
{
|
||||
CallstackElement* stack = $$stacktrace();
|
||||
$if ($defined(libc::stderr) && $defined(libc::fprintf)):
|
||||
|
||||
if (stack) stack = stack.prev;
|
||||
if (stack)
|
||||
{
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%.*s'\n", (int)message.len, message.ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
libc::fprintf(libc::stderr(), "\nERROR: '%.*s', function %.*s (%.*s:%d)\n",
|
||||
(int)message.len, message.ptr, (int)function.len, function.ptr, (int)file.len, file.ptr, line);
|
||||
}
|
||||
while (stack)
|
||||
{
|
||||
libc::fprintf(libc::stderr(), " at function %.*s (%.*s:%u)\n", (int)stack.function.len, stack.function.ptr,
|
||||
(int)stack.file.len, stack.file.ptr, stack.line);
|
||||
if (stack == stack.prev) break;
|
||||
stack = stack.prev;
|
||||
}
|
||||
|
||||
$endif;
|
||||
$$trap();
|
||||
}
|
||||
|
||||
define PanicFn = fn void(char[] message, char[] file, char[] function, uint line);
|
||||
|
||||
PanicFn panic = &default_panic;
|
||||
|
||||
macro void unreachable($string = "Unreachable statement reached.") @builtin @noreturn
|
||||
{
|
||||
panic($string, $$FILE, $$FUNC, $$LINE);
|
||||
$$unreachable();
|
||||
}
|
||||
|
||||
macro bitcast(expr, $Type) @builtin
|
||||
{
|
||||
var $size = (usz)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
$Type x = void;
|
||||
mem::copy(&x, &expr, $size, $Type.alignof, $alignof(expr));
|
||||
return x;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $Type.kindof == TypeKind.ENUM `Only enums may be used`
|
||||
**/
|
||||
macro enum_by_name($Type, char[] enum_name) @builtin
|
||||
{
|
||||
typeid x = $Type.typeid;
|
||||
foreach (i, name : x.names)
|
||||
{
|
||||
if (str::compare(name, enum_name)) return ($Type)i;
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locality for prefetch, levels 0 - 3, corresponding
|
||||
* to "extremely local" to "no locality"
|
||||
**/
|
||||
enum PrefetchLocality
|
||||
{
|
||||
NO_LOCALITY,
|
||||
FAR,
|
||||
NEAR,
|
||||
VERY_NEAR,
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.`
|
||||
**/
|
||||
macro prefetch(void* ptr, PrefetchLocality $locality = VERY_NEAR, bool $write = false) @builtin
|
||||
{
|
||||
$$prefetch(ptr, $write ? 1 : 0, $locality.ordinal);
|
||||
}
|
||||
|
||||
macro bool @castable(#expr, $To) @builtin
|
||||
{
|
||||
return $checks(($To)#expr);
|
||||
}
|
||||
|
||||
macro bool @convertible(#expr, $To) @builtin
|
||||
{
|
||||
return $checks($To x = #expr);
|
||||
}
|
||||
|
||||
macro uint int.hash(int i) = i;
|
||||
macro uint uint.hash(uint i) = i;
|
||||
macro uint short.hash(short s) = s;
|
||||
macro uint ushort.hash(ushort s) = s;
|
||||
macro uint char.hash(char c) = c;
|
||||
macro uint ichar.hash(ichar c) = c;
|
||||
macro uint long.hash(long i) = (uint)((i >> 32) ^ i);
|
||||
macro uint ulong.hash(ulong i) = (uint)((i >> 32) ^ i);
|
||||
macro uint bool.hash(bool b) = (uint)b;
|
||||
macro uint typeid.hash(typeid t) = (uint)(((uptr)t >> 32) ^ (uptr)t);
|
||||
macro uint char[].hash(char[] c) = (uint)fnv32a::encode(c);
|
||||
109
lib/std/core/builtin_comparison.c3
Normal file
109
lib/std/core/builtin_comparison.c3
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright (c) 2021-2022 Christoffer Lerno and contributors. 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::core::builtin;
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro less(a, b) @builtin
|
||||
{
|
||||
$if ($defined(a.less)):
|
||||
return a.less(b);
|
||||
$elif ($defined(a.compare_to)):
|
||||
return a.compare_to(b) < 0;
|
||||
$else:
|
||||
return a < b;
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro less_eq(a, b) @builtin
|
||||
{
|
||||
$if ($defined(a.less)):
|
||||
return !b.less(a);
|
||||
$elif ($defined(a.compare_to)):
|
||||
return a.compare_to(b) <= 0;
|
||||
$else:
|
||||
return a <= b;
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro greater(a, b) @builtin
|
||||
{
|
||||
$if ($defined(a.less)):
|
||||
return b.less(a);
|
||||
$elif ($defined(a.compare_to)):
|
||||
return a.compare_to(b) > 0;
|
||||
$else:
|
||||
return a > b;
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_comparable_value(a) && types::is_comparable_value(b)
|
||||
**/
|
||||
macro greater_eq(a, b) @builtin
|
||||
{
|
||||
$if ($defined(a.less)):
|
||||
return !a.less(b);
|
||||
$elif ($defined(a.compare_to)):
|
||||
return a.compare_to(b) >= 0;
|
||||
$else:
|
||||
return a >= b;
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require types::is_equatable_value(a) && types::is_equatable_value(b) `values must be equatable`
|
||||
**/
|
||||
macro bool equals(a, b) @builtin
|
||||
{
|
||||
$if ($defined(a.equals)):
|
||||
return a.equals(b);
|
||||
$elif ($defined(a.compare_to)):
|
||||
return a.compare_to(b) == 0;
|
||||
$elif ($defined(a.less)):
|
||||
return !a.less(b) && !b.less(a);
|
||||
$else:
|
||||
return a == b;
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro min(x, ...) @builtin
|
||||
{
|
||||
$if ($vacount == 1):
|
||||
return less(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else:
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
if (less($vaarg($i), result))
|
||||
{
|
||||
result = $vaarg($i);
|
||||
}
|
||||
$endfor;
|
||||
return result;
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro max(x, ...) @builtin
|
||||
{
|
||||
$if ($vacount == 1):
|
||||
return greater(x, $vaarg(0)) ? x : $vaarg(0);
|
||||
$else:
|
||||
var result = x;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
if (greater($vaarg($i), result))
|
||||
{
|
||||
result = $vaarg($i);
|
||||
}
|
||||
$endfor;
|
||||
return result;
|
||||
$endif;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2021 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::cinterop;
|
||||
module std::core::cinterop;
|
||||
|
||||
const C_INT_SIZE = $$C_INT_SIZE;
|
||||
const C_LONG_SIZE = $$C_LONG_SIZE;
|
||||
411
lib/std/core/conv.c3
Normal file
411
lib/std/core/conv.c3
Normal file
@@ -0,0 +1,411 @@
|
||||
module std::core::string::conv;
|
||||
|
||||
private const uint UTF16_SURROGATE_OFFSET = 0x10000;
|
||||
private const uint UTF16_SURROGATE_GENERIC_MASK = 0xF800;
|
||||
private const uint UTF16_SURROGATE_GENERIC_VALUE = 0xD800;
|
||||
private const uint UTF16_SURROGATE_MASK = 0xFC00;
|
||||
private const uint UTF16_SURROGATE_CODEPOINT_MASK = 0x03FF;
|
||||
private const uint UTF16_SURROGATE_BITS = 10;
|
||||
private const uint UTF16_SURROGATE_LOW_VALUE = 0xDC00;
|
||||
private const uint UTF16_SURROGATE_HIGH_VALUE = 0xD800;
|
||||
|
||||
/**
|
||||
* @param c `The utf32 codepoint to convert`
|
||||
* @param [out] output `the resulting buffer`
|
||||
* @param available `the size available`
|
||||
**/
|
||||
fn usz! char32_to_utf8(Char32 c, char* output, usz available)
|
||||
{
|
||||
if (!available) return UnicodeResult.CONVERSION_FAILED!;
|
||||
switch (true)
|
||||
{
|
||||
case c <= 0x7f:
|
||||
output[0] = (char)c;
|
||||
return 1;
|
||||
case c <= 0x7ff:
|
||||
if (available < 2) return UnicodeResult.CONVERSION_FAILED!;
|
||||
output[0] = (char)(0xC0 | c >> 6);
|
||||
output[1] = (char)(0x80 | (c & 0x3F));
|
||||
return 2;
|
||||
case c <= 0xffff:
|
||||
if (available < 3) return UnicodeResult.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 (available < 4) return UnicodeResult.CONVERSION_FAILED!;
|
||||
output[0] = (char)(0xF0 | c >> 18);
|
||||
output[1] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
output[2] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
output[3] = (char)(0x80 | (c & 0x3F));
|
||||
return 4;
|
||||
default:
|
||||
// 0x10FFFF and above is not defined.
|
||||
return UnicodeResult.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.`
|
||||
**/
|
||||
fn void char32_to_utf16_unsafe(Char32 c, Char16** output)
|
||||
{
|
||||
if (c < UTF16_SURROGATE_OFFSET)
|
||||
{
|
||||
(*output)++[0] = (Char16)c;
|
||||
return;
|
||||
}
|
||||
c -= UTF16_SURROGATE_OFFSET;
|
||||
Char16 low = (Char16)(UTF16_SURROGATE_LOW_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK));
|
||||
c >>= UTF16_SURROGATE_BITS;
|
||||
Char16 high = (Char16)(UTF16_SURROGATE_HIGH_VALUE | (c & UTF16_SURROGATE_CODEPOINT_MASK));
|
||||
(*output)++[0] = (Char16)high;
|
||||
(*output)++[0] = (Char16)low;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.`
|
||||
**/
|
||||
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)
|
||||
{
|
||||
char32_to_utf8_unsafe(high, output);
|
||||
*available = 1;
|
||||
return;
|
||||
}
|
||||
// Low surrogate first is an error
|
||||
if (high & UTF16_SURROGATE_MASK != UTF16_SURROGATE_HIGH_VALUE) return UnicodeResult.INVALID_UTF16!;
|
||||
|
||||
// Unmatched high surrogate is an error
|
||||
if (*available == 1) return UnicodeResult.INVALID_UTF16!;
|
||||
|
||||
Char16 low = ptr[1];
|
||||
|
||||
// Unmatched high surrogate, invalid
|
||||
if (low & UTF16_SURROGATE_MASK != UTF16_SURROGATE_LOW_VALUE) return UnicodeResult.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
|
||||
Char32 uc = (high & UTF16_SURROGATE_CODEPOINT_MASK) << UTF16_SURROGATE_BITS
|
||||
| (low & UTF16_SURROGATE_CODEPOINT_MASK) + UTF16_SURROGATE_OFFSET;
|
||||
char32_to_utf8_unsafe(uc, output);
|
||||
*available = 2;
|
||||
}
|
||||
/**
|
||||
* @param c `The utf32 codepoint to convert`
|
||||
* @param [inout] output `the resulting buffer`
|
||||
**/
|
||||
fn void char32_to_utf8_unsafe(Char32 c, char** output)
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
(*output)++[0] = (char)c;
|
||||
case c < 0x7ff:
|
||||
(*output)++[0] = (char)(0xC0 | c >> 6);
|
||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||
case c < 0xffff:
|
||||
(*output)++[0] = (char)(0xE0 | c >> 12);
|
||||
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||
default:
|
||||
(*output)++[0] = (char)(0xF0 | c >> 18);
|
||||
(*output)++[0] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
(*output)++[0] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
(*output)++[0] = (char)(0x80 | (c & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
usz max_size = *size;
|
||||
if (max_size < 1) return UnicodeResult.INVALID_UTF8!;
|
||||
char c = (ptr++)[0];
|
||||
|
||||
if ((c & 0x80) == 0)
|
||||
{
|
||||
*size = 1;
|
||||
return c;
|
||||
}
|
||||
if ((c & 0xE0) == 0xC0)
|
||||
{
|
||||
if (max_size < 2) return UnicodeResult.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!;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if ((c & 0xF0) == 0xE0)
|
||||
{
|
||||
if (max_size < 3) return UnicodeResult.INVALID_UTF8!;
|
||||
*size = 3;
|
||||
Char32 uc = (c & 0x0F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
if (max_size < 4) return UnicodeResult.INVALID_UTF8!;
|
||||
if ((c & 0xF8) != 0xF0) return UnicodeResult.INVALID_UTF8!;
|
||||
*size = 4;
|
||||
Char32 uc = (c & 0x07) << 18;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
uc += (c & 0x3F) << 12;
|
||||
c = ptr++[0];
|
||||
if (c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
uc += (c & 0x3F) << 6;
|
||||
c = ptr++[0];
|
||||
// Overlong sequence or invalid last
|
||||
if (!uc || c & 0xC0 != 0x80) return UnicodeResult.INVALID_UTF8!;
|
||||
return uc + c & 0x3F;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param utf8 `An UTF-8 encoded slice of bytes`
|
||||
* @return `the number of encoded code points`
|
||||
**/
|
||||
fn usz utf8_codepoints(char[] utf8)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (char c : utf8)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the UTF8 length required to encode an UTF32 array.
|
||||
* @param [in] utf32 `the utf32 data to calculate from`
|
||||
* @return `the length of the resulting UTF8 array`
|
||||
**/
|
||||
fn usz utf8len_for_utf32(Char32[] utf32)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
switch (true)
|
||||
{
|
||||
case uc < 0x7f:
|
||||
len++;
|
||||
case uc < 0x7ff:
|
||||
len += 2;
|
||||
case uc < 0xffff:
|
||||
len += 3;
|
||||
default:
|
||||
len += 4;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the UTF8 length required to encode an UTF16 array.
|
||||
* @param [in] utf16 `the utf16 data to calculate from`
|
||||
* @return `the length of the resulting UTF8 array`
|
||||
**/
|
||||
fn usz utf8len_for_utf16(Char16[] utf16)
|
||||
{
|
||||
usz len = 0;
|
||||
usz len16 = utf16.len;
|
||||
for (usz i = 0; i < len16; i++)
|
||||
{
|
||||
Char16 c = utf16[i];
|
||||
if (c & UTF16_SURROGATE_GENERIC_MASK != UTF16_SURROGATE_GENERIC_VALUE)
|
||||
{
|
||||
if (c < 0x7f)
|
||||
{
|
||||
len++;
|
||||
continue;
|
||||
}
|
||||
if (c < 0x7ff)
|
||||
{
|
||||
len += 2;
|
||||
continue;
|
||||
}
|
||||
len += 3;
|
||||
continue;
|
||||
}
|
||||
len += 4;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the UTF16 length required to encode a UTF8 array.
|
||||
* @param utf8 `the utf8 data to calculate from`
|
||||
* @return `the length of the resulting UTF16 array`
|
||||
**/
|
||||
fn usz utf16len_for_utf8(char[] utf8)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
usz len16 = 0;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
len16++;
|
||||
char c = utf8[i];
|
||||
if (c & 0x80 == 0) continue;
|
||||
i++;
|
||||
if (c & 0xE0 == 0xC0) continue;
|
||||
i++;
|
||||
if (c & 0xF0 == 0xE0) continue;
|
||||
i++;
|
||||
len16++;
|
||||
}
|
||||
return len16;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
usz len = utf32.len;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
if (uc >= UTF16_SURROGATE_OFFSET) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an UTF32 array to an UTF8 array.
|
||||
*
|
||||
* @param [in] utf32
|
||||
* @param [out] utf8_buffer
|
||||
* @return `the number of bytes written.`
|
||||
**/
|
||||
fn usz! utf32to8(Char32[] utf32, char[] utf8_buffer)
|
||||
{
|
||||
usz len = utf8_buffer.len;
|
||||
char* ptr = utf8_buffer.ptr;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
usz used = char32_to_utf8(uc, ptr, len) @inline?;
|
||||
len -= used;
|
||||
ptr += used;
|
||||
}
|
||||
return utf8_buffer.len - len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert an UTF8 array to an UTF32 array.
|
||||
*
|
||||
* @param [in] utf8
|
||||
* @param [out] utf32_buffer
|
||||
* @return `the number of Char32s written.`
|
||||
**/
|
||||
fn usz! utf8to32(char[] utf8, Char32[] utf32_buffer)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
Char32* ptr = utf32_buffer.ptr;
|
||||
usz len32 = 0;
|
||||
usz buf_len = utf32_buffer.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
if (len32 == buf_len) return UnicodeResult.CONVERSION_FAILED!;
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
i += width;
|
||||
ptr[len32++] = uc;
|
||||
}
|
||||
return len32;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF16 data into an UTF8 buffer without bounds
|
||||
* 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.`
|
||||
**/
|
||||
fn void! utf16to8_unsafe(Char16[] utf16, char* utf8_buffer)
|
||||
{
|
||||
usz len16 = utf16.len;
|
||||
for (usz i = 0; i < len16;)
|
||||
{
|
||||
usz available = len16 - i;
|
||||
char16_to_utf8_unsafe(&utf16[i], &available, &utf8_buffer) @inline?;
|
||||
i += available;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF8 data into an UTF32 buffer without bounds
|
||||
* 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.`
|
||||
**/
|
||||
fn void! utf8to32_unsafe(char[] utf8, Char32* utf32_buffer)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
i += width;
|
||||
(utf32_buffer++)[0] = uc;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF8 data into an UTF16 buffer without bounds
|
||||
* 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.`
|
||||
**/
|
||||
fn void! utf8to16_unsafe(char[] utf8, Char16* utf16_buffer)
|
||||
{
|
||||
usz len = utf8.len;
|
||||
for (usz i = 0; i < len;)
|
||||
{
|
||||
usz width = len - i;
|
||||
Char32 uc = utf8_to_char32(&utf8[i], &width) @inline?;
|
||||
char32_to_utf16_unsafe(uc, &utf16_buffer) @inline;
|
||||
i += width;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy an array of UTF32 code points into an UTF8 buffer without bounds
|
||||
* 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.`
|
||||
**/
|
||||
fn void utf32to8_unsafe(Char32[] utf32, char* utf8_buffer)
|
||||
{
|
||||
char* start = utf8_buffer;
|
||||
foreach (Char32 uc : utf32)
|
||||
{
|
||||
char32_to_utf8_unsafe(uc, &utf8_buffer) @inline;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
// Copyright (c) 2021 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::env;
|
||||
module std::core::env;
|
||||
|
||||
enum CompilerOptLevel
|
||||
{
|
||||
@@ -52,7 +52,38 @@ enum OsType
|
||||
}
|
||||
|
||||
const OsType OS_TYPE = (OsType)($$OS_TYPE);
|
||||
const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE;
|
||||
const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)($$COMPILER_OPT_LEVEL);
|
||||
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
|
||||
const bool I128_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;
|
||||
const bool F16_SUPPORT = $$PLATFORM_F16_SUPPORTED;
|
||||
const bool F128_SUPPORT = $$PLATFORM_F128_SUPPORTED;
|
||||
const bool COMPILER_SAFE_MODE = $$COMPILER_SAFE_MODE;
|
||||
const usz LLVM_VERSION = $$LLVM_VERSION;
|
||||
const bool BENCHMARKING = $$BENCHMARKING;
|
||||
const bool TESTING = $$TESTING;
|
||||
const usz TEMP_ALLOCATOR_SIZE = 128 * 1024;
|
||||
|
||||
macro bool os_is_posix()
|
||||
{
|
||||
$switch (OS_TYPE):
|
||||
$case IOS:
|
||||
$case MACOSX:
|
||||
$case NETBSD:
|
||||
$case LINUX:
|
||||
$case KFREEBSD:
|
||||
$case FREEBSD:
|
||||
$case OPENBSD:
|
||||
$case SOLARIS:
|
||||
$case TVOS:
|
||||
$case WATCHOS:
|
||||
return true;
|
||||
$case WIN32:
|
||||
$case WASI:
|
||||
$case EMSCRIPTEN:
|
||||
return false;
|
||||
$default:
|
||||
$echo("Assuming non-Posix environment");
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
269
lib/std/core/mem.c3
Normal file
269
lib/std/core/mem.c3
Normal file
@@ -0,0 +1,269 @@
|
||||
// Copyright (c) 2021 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::core::mem;
|
||||
|
||||
macro @volatile_load(&x)
|
||||
{
|
||||
return $$volatile_load(&x);
|
||||
}
|
||||
|
||||
macro @volatile_store(&x, y)
|
||||
{
|
||||
return $$volatile_store(&x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
**/
|
||||
fn usz aligned_offset(usz offset, usz alignment)
|
||||
{
|
||||
return alignment * ((offset + alignment - 1) / alignment);
|
||||
}
|
||||
|
||||
macro void* aligned_pointer(void* ptr, usz alignment)
|
||||
{
|
||||
return (void*)(uptr)aligned_offset((uptr)ptr, alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require math::is_power_of_2(alignment)
|
||||
**/
|
||||
fn bool ptr_is_aligned(void* ptr, usz alignment) @inline
|
||||
{
|
||||
return (uptr)ptr & ((uptr)alignment - 1) == 0;
|
||||
}
|
||||
|
||||
macro void clear(void* dst, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if ($inlined):
|
||||
$$memset_inline(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$else:
|
||||
$$memset(dst, (char)0, len, $is_volatile, $dst_align);
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro void copy(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if ($inlined):
|
||||
$$memcpy_inline(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$else:
|
||||
$$memcpy(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro void move(void* dst, void* src, usz len, usz $dst_align = 0, usz $src_align = 0, bool $is_volatile = false)
|
||||
{
|
||||
$$memmove(dst, src, len, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
|
||||
macro void set(void* dst, char val, usz len, usz $dst_align = 0, bool $is_volatile = false, bool $inlined = false)
|
||||
{
|
||||
$if ($inlined):
|
||||
$$memset_inline(dst, val, len, $is_volatile, $dst_align);
|
||||
$else:
|
||||
$$memset(dst, val, len, $is_volatile, $dst_align);
|
||||
$endif;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $typeof(a).kindof == TypeKind.SUBARRAY || $typeof(a).kindof == TypeKind.POINTER
|
||||
* @require $typeof(b).kindof == TypeKind.SUBARRAY || $typeof(b).kindof == TypeKind.POINTER
|
||||
* @require $typeof(a).kindof != TypeKind.SUBARRAY || len == -1
|
||||
* @require $typeof(a).kindof != TypeKind.POINTER || len > -1
|
||||
* @checked (a = b), (b = a)
|
||||
**/
|
||||
macro bool equals(a, b, isz len = -1, usz $align = 0)
|
||||
{
|
||||
$if (!$align):
|
||||
$align = $typeof(a[0]).alignof;
|
||||
$endif;
|
||||
void* x = void;
|
||||
void* y = void;
|
||||
$if ($typeof(a).kindof == TypeKind.SUBARRAY):
|
||||
len = a.len;
|
||||
if (len != b.len) return false;
|
||||
x = a.ptr;
|
||||
y = b.ptr;
|
||||
$else:
|
||||
x = a;
|
||||
y = b;
|
||||
assert(len >= 0, "A zero or positive length must be given when comparing pointers.");
|
||||
$endif;
|
||||
|
||||
if (!len) return true;
|
||||
var $Type;
|
||||
$switch ($align):
|
||||
$case 1:
|
||||
$Type = char;
|
||||
$case 2:
|
||||
$Type = ushort;
|
||||
$case 4:
|
||||
$Type = uint;
|
||||
$case 8:
|
||||
$default:
|
||||
$Type = ulong;
|
||||
$endswitch;
|
||||
var $step = $Type.sizeof;
|
||||
usz end = len / $step;
|
||||
for (usz i = 0; i < end; i++)
|
||||
{
|
||||
if ((($Type*)x)[i] != (($Type*)y)[i]) return false;
|
||||
}
|
||||
usz last = len % $align;
|
||||
for (usz i = len - last; i < len; i++)
|
||||
{
|
||||
if (((char*)x)[i] != ((char*)y)[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
macro @clone(&value) @builtin
|
||||
{
|
||||
$typeof(value)* x = malloc($typeof(value));
|
||||
*x = value;
|
||||
return x;
|
||||
}
|
||||
|
||||
macro @tclone(&value) @builtin
|
||||
{
|
||||
$typeof(value)* x = talloc($typeof(value));
|
||||
*x = value;
|
||||
return x;
|
||||
}
|
||||
|
||||
fn void* malloc(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.alloc(size)!!;
|
||||
}
|
||||
|
||||
fn void*! malloc_checked(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.alloc(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! malloc_aligned(usz size, usz alignment) @builtin @inline
|
||||
{
|
||||
return thread_allocator.alloc_aligned(size, alignment);
|
||||
}
|
||||
|
||||
fn char[] alloc_bytes(usz bytes) @inline
|
||||
{
|
||||
return ((char*)thread_allocator.alloc(bytes))[:bytes]!!;
|
||||
}
|
||||
|
||||
macro alloc($Type)
|
||||
{
|
||||
return ($Type*)thread_allocator.alloc($Type.sizeof)!!;
|
||||
}
|
||||
|
||||
|
||||
fn void* calloc(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.calloc(size)!!;
|
||||
}
|
||||
|
||||
fn void*! calloc_checked(usz size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.calloc(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! calloc_aligned(usz size, usz alignment) @builtin @inline
|
||||
{
|
||||
return thread_allocator.calloc_aligned(size, alignment);
|
||||
}
|
||||
|
||||
fn void* realloc(void *ptr, usz new_size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.realloc(ptr, new_size)!!;
|
||||
}
|
||||
|
||||
fn void*! realloc_checked(void *ptr, usz new_size) @builtin @inline
|
||||
{
|
||||
return thread_allocator.realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! realloc_aligned(void *ptr, usz new_size, usz alignment) @builtin @inline
|
||||
{
|
||||
return thread_allocator.realloc_aligned(ptr, new_size, alignment);
|
||||
}
|
||||
|
||||
fn void free(void* ptr) @builtin @inline
|
||||
{
|
||||
return thread_allocator.free(ptr)!!;
|
||||
}
|
||||
|
||||
fn void free_aligned(void* ptr) @builtin @inline
|
||||
{
|
||||
return thread_allocator.free_aligned(ptr)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run with a specific allocator inside of the macro body.
|
||||
**/
|
||||
macro void @scoped(Allocator* allocator; @body())
|
||||
{
|
||||
Allocator* old_allocator = thread_allocator;
|
||||
thread_allocator = allocator;
|
||||
defer thread_allocator = old_allocator;
|
||||
@body();
|
||||
}
|
||||
|
||||
|
||||
macro talloc($Type) @builtin
|
||||
{
|
||||
return temp_allocator().alloc_aligned($Type.sizeof, $Type.alignof)!!;
|
||||
}
|
||||
|
||||
fn void* tmalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().alloc_aligned(size, alignment)!!;
|
||||
}
|
||||
|
||||
fn void* tcalloc(usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().calloc_aligned(size, alignment)!!;
|
||||
}
|
||||
|
||||
fn void* trealloc(void* ptr, usz size, usz alignment = allocator::DEFAULT_MEM_ALIGNMENT) @builtin @inline
|
||||
{
|
||||
return temp_allocator().realloc_aligned(ptr, size, alignment)!!;
|
||||
}
|
||||
|
||||
macro void @pool(;@body) @builtin
|
||||
{
|
||||
TempAllocator* temp = temp_allocator();
|
||||
usz mark = temp.used;
|
||||
defer temp.reset(mark);
|
||||
@body();
|
||||
}
|
||||
|
||||
private tlocal Allocator* thread_allocator = allocator::LIBC_ALLOCATOR;
|
||||
private tlocal TempAllocator* thread_temp_allocator = null;
|
||||
|
||||
macro TempAllocator* temp_allocator()
|
||||
{
|
||||
if (!thread_temp_allocator)
|
||||
{
|
||||
thread_temp_allocator = allocator::new_temp(env::TEMP_ALLOCATOR_SIZE, allocator::LIBC_ALLOCATOR)!!;
|
||||
}
|
||||
return thread_temp_allocator;
|
||||
}
|
||||
|
||||
macro Allocator* current_allocator()
|
||||
{
|
||||
return thread_allocator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
181
lib/std/core/mem_allocator.c3
Normal file
181
lib/std/core/mem_allocator.c3
Normal file
@@ -0,0 +1,181 @@
|
||||
module std::core::mem::allocator;
|
||||
|
||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||
const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2;
|
||||
const DEFAULT_SIZE_PREFIX = usz.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = usz.alignof;
|
||||
|
||||
const Allocator* NULL_ALLOCATOR = &_NULL_ALLOCATOR;
|
||||
const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR;
|
||||
|
||||
define AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind);
|
||||
|
||||
struct Allocator
|
||||
{
|
||||
AllocatorFunction function;
|
||||
}
|
||||
|
||||
enum AllocationKind
|
||||
{
|
||||
ALLOC,
|
||||
CALLOC,
|
||||
REALLOC,
|
||||
FREE,
|
||||
ALIGNED_ALLOC,
|
||||
ALIGNED_CALLOC,
|
||||
ALIGNED_REALLOC,
|
||||
ALIGNED_FREE,
|
||||
RESET,
|
||||
MARK,
|
||||
}
|
||||
|
||||
fault AllocationFailure
|
||||
{
|
||||
OUT_OF_MEMORY,
|
||||
UNSUPPORTED_OPERATION,
|
||||
CHUNK_TOO_LARGE,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void*! Allocator.alloc(Allocator* allocator, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, null, ALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.alloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_ALLOC);
|
||||
}
|
||||
|
||||
fn void*! Allocator.realloc(Allocator* allocator, void* old_pointer, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, old_pointer, REALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.realloc_aligned(Allocator* allocator, void* old_pointer, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, old_pointer, ALIGNED_REALLOC);
|
||||
}
|
||||
|
||||
fn usz! Allocator.mark(Allocator* allocator) @inline
|
||||
{
|
||||
return (usz)(uptr)allocator.function(allocator, 0, 0, 0, null, MARK);
|
||||
}
|
||||
|
||||
|
||||
fn void*! Allocator.calloc(Allocator* allocator, usz size) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, 0, 0, null, CALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment && math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.calloc_aligned(Allocator* allocator, usz size, usz alignment, usz offset = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator, size, alignment, offset, null, ALIGNED_CALLOC);
|
||||
}
|
||||
|
||||
fn void! Allocator.free(Allocator* allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, FREE)?;
|
||||
}
|
||||
|
||||
fn void! Allocator.free_aligned(Allocator* allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator, 0, 0, 0, old_pointer, ALIGNED_FREE)?;
|
||||
}
|
||||
|
||||
fn void Allocator.reset(Allocator* allocator, usz mark = 0)
|
||||
{
|
||||
allocator.function(allocator, mark, 0, 0, null, RESET)!!;
|
||||
}
|
||||
|
||||
private fn usz alignment_for_allocation(usz alignment) @inline
|
||||
{
|
||||
if (alignment < DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
alignment = DEFAULT_MEM_ALIGNMENT;
|
||||
}
|
||||
return alignment;
|
||||
}
|
||||
|
||||
struct DynamicArenaAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
Allocator* backing_allocator;
|
||||
DynamicArenaPage* page;
|
||||
DynamicArenaPage* unused_page;
|
||||
usz page_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require page_size >= 128
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usz page_size, Allocator* backing_allocator = mem::current_allocator())
|
||||
{
|
||||
this.function = &dynamic_arena_allocator_function;
|
||||
this.page = null;
|
||||
this.unused_page = null;
|
||||
this.page_size = page_size;
|
||||
this.backing_allocator = backing_allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
|
||||
{
|
||||
DynamicArenaPage* page = this.page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
this.backing_allocator.free(page)!!;
|
||||
page = next_page;
|
||||
}
|
||||
page = this.unused_page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
this.backing_allocator.free(page)!!;
|
||||
page = next_page;
|
||||
}
|
||||
this.page = null;
|
||||
this.unused_page = null;
|
||||
}
|
||||
|
||||
|
||||
struct ArenaAllocator
|
||||
{
|
||||
inline Allocator allocator;
|
||||
char[] data;
|
||||
usz used;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
*
|
||||
* @require this != null
|
||||
**/
|
||||
fn void ArenaAllocator.init(ArenaAllocator* this, char[] data)
|
||||
{
|
||||
this.function = &arena_allocator_function;
|
||||
this.data = data;
|
||||
this.used = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void ArenaAllocator.reset(ArenaAllocator* this)
|
||||
{
|
||||
this.used = 0;
|
||||
}
|
||||
40
lib/std/core/mem_array.c3
Normal file
40
lib/std/core/mem_array.c3
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2021 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::core::mem::array;
|
||||
|
||||
/**
|
||||
* @require usz.max / elements > $Type.sizeof
|
||||
**/
|
||||
macro alloc($Type, usz elements)
|
||||
{
|
||||
$Type* ptr = malloc($Type.sizeof * elements);
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require usz.max / elements > $Type.sizeof
|
||||
**/
|
||||
macro talloc($Type, usz elements)
|
||||
{
|
||||
$Type* ptr = tmalloc($Type.sizeof * elements, $Type[1].alignof);
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require (usz.max / elements > $Type.sizeof)
|
||||
**/
|
||||
macro make($Type, usz elements, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
$Type* ptr = allocator.calloc($Type.sizeof * elements)!!;
|
||||
return ptr[:elements];
|
||||
}
|
||||
|
||||
/**
|
||||
* @require (usz.max / elements > $Type.sizeof)
|
||||
**/
|
||||
macro tmake($Type, usz elements)
|
||||
{
|
||||
$Type* ptr = tcalloc($Type.sizeof * elements, $Type[1].alignof);
|
||||
return ptr[:elements];
|
||||
}
|
||||
344
lib/std/core/str.c3
Normal file
344
lib/std/core/str.c3
Normal file
@@ -0,0 +1,344 @@
|
||||
module std::core::str;
|
||||
define ZString = distinct char*;
|
||||
define Char32 = uint;
|
||||
define Char16 = ushort;
|
||||
|
||||
private const uint SURROGATE_OFFSET = 0x10000;
|
||||
private const uint SURROGATE_GENERIC_MASK = 0xF800;
|
||||
private const uint SURROGATE_MASK = 0xFC00;
|
||||
private const uint SURROGATE_CODEPOINT_MASK = 0x03FF;
|
||||
private const uint SURROGATE_BITS = 10;
|
||||
private const uint SURROGATE_LOW_VALUE = 0xDC00;
|
||||
private const uint SURROGATE_HIGH_VALUE = 0xD800;
|
||||
|
||||
fault NumberConversion
|
||||
{
|
||||
EMPTY_STRING,
|
||||
NEGATIVE_VALUE,
|
||||
MALFORMED_INTEGER,
|
||||
INTEGER_OVERFLOW,
|
||||
}
|
||||
fn String join(char[][] s, char[] joiner)
|
||||
{
|
||||
if (!s.len) return (String)null;
|
||||
usz total_size = joiner.len * s.len;
|
||||
foreach (char[]* &str : s)
|
||||
{
|
||||
total_size += str.len;
|
||||
}
|
||||
String res = string::new_with_capacity(total_size);
|
||||
res.append(s[0]);
|
||||
foreach (char[]* &str : s[1..])
|
||||
{
|
||||
res.append(joiner);
|
||||
res.append(*str);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
macro bool char_in_set(char c, char[] set)
|
||||
{
|
||||
foreach (ch : set)
|
||||
{
|
||||
if (ch == c) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private macro char_is_space_tab(char c)
|
||||
{
|
||||
return c == ' ' || c == '\t';
|
||||
}
|
||||
|
||||
private macro to_signed_integer($Type, char[] string)
|
||||
{
|
||||
usz len = string.len;
|
||||
usz index = 0;
|
||||
char* ptr = string.ptr;
|
||||
while (index < len && char_is_space_tab(ptr[index])) index++;
|
||||
if (len == index) return NumberConversion.EMPTY_STRING!;
|
||||
bool is_negative;
|
||||
switch (string[index])
|
||||
{
|
||||
case '-':
|
||||
if ($Type.min == 0) return NumberConversion.NEGATIVE_VALUE!;
|
||||
is_negative = true;
|
||||
index++;
|
||||
case '+':
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
|
||||
$Type base = 10;
|
||||
if (string[index] == '0')
|
||||
{
|
||||
index++;
|
||||
if (index == len) return ($Type)0;
|
||||
switch (string[index])
|
||||
{
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
index++;
|
||||
case 'b':
|
||||
case 'B':
|
||||
base = 2;
|
||||
index++;
|
||||
case 'o':
|
||||
case 'O':
|
||||
base = 8;
|
||||
index++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (len == index) return NumberConversion.MALFORMED_INTEGER!;
|
||||
}
|
||||
$Type value = 0;
|
||||
while (index != len)
|
||||
{
|
||||
char c = {|
|
||||
char ch = string[index++];
|
||||
if (base != 16 || ch < 'A') return (char)(ch - '0');
|
||||
if (ch <= 'F') return (char)(ch - 'A');
|
||||
if (ch < 'a') return NumberConversion.MALFORMED_INTEGER!;
|
||||
if (ch > 'f') return NumberConversion.MALFORMED_INTEGER!;
|
||||
return (char)(ch - 'a');
|
||||
|}?;
|
||||
if (c >= base) return NumberConversion.MALFORMED_INTEGER!;
|
||||
value = {|
|
||||
if (is_negative)
|
||||
{
|
||||
$Type new_value = value * base - c;
|
||||
if (new_value > value) return NumberConversion.INTEGER_OVERFLOW!;
|
||||
return new_value;
|
||||
}
|
||||
$Type new_value = value * base + c;
|
||||
if (new_value < value) return NumberConversion.INTEGER_OVERFLOW!;
|
||||
return new_value;
|
||||
|}?;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
fn int128! to_int128(char[] string) = to_signed_integer(int128, string);
|
||||
fn long! to_long(char[] string) = to_signed_integer(long, string);
|
||||
fn int! to_int(char[] string) = to_signed_integer(int, string);
|
||||
fn short! to_short(char[] string) = to_signed_integer(short, string);
|
||||
fn ichar! to_ichar(char[] string) = to_signed_integer(ichar, string);
|
||||
|
||||
fn char[] trim(char[] string, char[] to_trim = "\t\n\r ")
|
||||
{
|
||||
usz start = 0;
|
||||
usz len = string.len;
|
||||
while (start < len && char_in_set(string[start], to_trim)) start++;
|
||||
if (start == len) return string[:0];
|
||||
usz end = len - 1;
|
||||
while (end > start && char_in_set(string[end], to_trim)) end--;
|
||||
return string[start..end];
|
||||
}
|
||||
|
||||
fn bool starts_with(char[] s, char[] needle)
|
||||
{
|
||||
if (needle.len > s.len) return false;
|
||||
foreach (i, c : needle)
|
||||
{
|
||||
if (c != s[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn char[][] tsplit(char[] s, char[] needle) = split(s, needle, mem::temp_allocator()) @inline;
|
||||
|
||||
fn char[][] split(char[] s, char[] needle, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz capacity = 16;
|
||||
usz i = 0;
|
||||
char[]* holder = allocator.alloc(char[].sizeof * capacity)!!;
|
||||
while (s.len)
|
||||
{
|
||||
usz! index = index_of(s, needle);
|
||||
char[] res = void;
|
||||
if (try index)
|
||||
{
|
||||
res = s[:index];
|
||||
s = s[index + needle.len..];
|
||||
}
|
||||
else
|
||||
{
|
||||
res = s;
|
||||
s = s[:0];
|
||||
}
|
||||
if (i == capacity)
|
||||
{
|
||||
capacity *= 2;
|
||||
holder = allocator.realloc(holder, char[].sizeof * capacity)!!;
|
||||
}
|
||||
holder[i++] = res;
|
||||
}
|
||||
return holder[:i];
|
||||
}
|
||||
|
||||
fn usz! index_of(char[] s, char[] needle)
|
||||
{
|
||||
usz match = 0;
|
||||
usz needed = needle.len;
|
||||
if (!needed) return SearchResult.MISSING!;
|
||||
usz index_start = 0;
|
||||
char search = needle[0];
|
||||
foreach (usz i, char c : s)
|
||||
{
|
||||
if (c == search)
|
||||
{
|
||||
if (!match) index_start = i;
|
||||
match++;
|
||||
if (match == needed) return index_start;
|
||||
search = needle[match];
|
||||
continue;
|
||||
}
|
||||
if (match)
|
||||
{
|
||||
match = 0;
|
||||
search = needle[0];
|
||||
}
|
||||
}
|
||||
return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
fn ZString copy_zstring(char[] s, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz len = s.len;
|
||||
char* str = allocator.alloc(len + 1)!!;
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return (ZString)str;
|
||||
}
|
||||
|
||||
fn char[] copyz(char[] s, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz len = s.len;
|
||||
char* str = allocator.alloc(len + 1)!!;
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return str[:len];
|
||||
}
|
||||
|
||||
fn ZString tcopy_zstring(char[] s)
|
||||
{
|
||||
return copy_zstring(s, mem::temp_allocator());
|
||||
}
|
||||
|
||||
fn bool compare(char[] a, char[] b)
|
||||
{
|
||||
if (a.len != b.len) return false;
|
||||
foreach (i, c : a)
|
||||
{
|
||||
if (c != b[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
fault UnicodeResult
|
||||
{
|
||||
INVALID_UTF8,
|
||||
INVALID_UTF16,
|
||||
CONVERSION_FAILED,
|
||||
}
|
||||
|
||||
fn usz utf8_codepoints(char[] utf8)
|
||||
{
|
||||
usz len = 0;
|
||||
foreach (char c : utf8)
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
fn Char32[]! utf8to32(char[] utf8, Allocator* allocator = mem::current_allocator)
|
||||
{
|
||||
usz codepoints = conv::utf8_codepoints(utf8);
|
||||
Char32* data = allocator.alloc(Char32.sizeof * (codepoints + 1))?;
|
||||
conv::utf8to32_unsafe(utf8, data)?;
|
||||
data[codepoints] = 0;
|
||||
return data[:codepoints];
|
||||
}
|
||||
|
||||
fn char[] utf32to8(Char32[] utf32, Allocator* allocator = mem::current_allocator)
|
||||
{
|
||||
usz len = conv::utf8len_for_utf32(utf32);
|
||||
char* data = allocator.alloc(len + 1)!!;
|
||||
conv::utf32to8_unsafe(utf32, data);
|
||||
data[len] = 0;
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
fn Char16[]! utf8to16(char[] utf8, Allocator* allocator = mem::current_allocator)
|
||||
{
|
||||
usz len16 = conv::utf16len_for_utf8(utf8);
|
||||
Char16* data = allocator.alloc((len16 + 1) * Char16.sizeof)?;
|
||||
conv::utf8to16_unsafe(utf8, data)?;
|
||||
data[len16] = 0;
|
||||
return data[:len16];
|
||||
}
|
||||
|
||||
|
||||
fn char[]! utf16to8(Char16[] utf16, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz len = conv::utf8len_for_utf16(utf16);
|
||||
char* data = allocator.alloc(len + 1)?;
|
||||
conv::utf16to8_unsafe(utf16, data)?;
|
||||
return data[:len];
|
||||
}
|
||||
|
||||
fn char[] copy(char[] s, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz len = s.len;
|
||||
ZString str_copy = copy_zstring(s, allocator) @inline;
|
||||
return str_copy[:len];
|
||||
}
|
||||
|
||||
fn char[] tcopy(char[] s)
|
||||
{
|
||||
usz len = s.len;
|
||||
ZString str_copy = tcopy_zstring(s) @inline;
|
||||
return str_copy[:len];
|
||||
}
|
||||
|
||||
fn char[] tconcat(char[] s1, char[] s2)
|
||||
{
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = tmalloc(full_len + 1);
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return str[:full_len];
|
||||
}
|
||||
|
||||
fn char[] concat(char[] s1, char[] s2)
|
||||
{
|
||||
usz full_len = s1.len + s2.len;
|
||||
char* str = malloc(full_len + 1);
|
||||
usz s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return str[:full_len];
|
||||
}
|
||||
|
||||
fn char[] ZString.as_str(ZString str)
|
||||
{
|
||||
return ((char*)str)[:str.len()];
|
||||
}
|
||||
|
||||
fn usz ZString.len(ZString str)
|
||||
{
|
||||
usz len = 0;
|
||||
char* ptr = (char*)str;
|
||||
while (char c = ptr++[0])
|
||||
{
|
||||
if (c & 0xC0 != 0x80) len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
311
lib/std/core/string.c3
Normal file
311
lib/std/core/string.c3
Normal file
@@ -0,0 +1,311 @@
|
||||
module std::core::string;
|
||||
import libc;
|
||||
|
||||
define String = distinct void*;
|
||||
|
||||
private struct StringData
|
||||
{
|
||||
Allocator* allocator;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
}
|
||||
|
||||
const usz MIN_CAPACITY = 16;
|
||||
|
||||
fn String new_with_capacity(usz capacity, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (capacity < MIN_CAPACITY) capacity = MIN_CAPACITY;
|
||||
StringData* data = allocator.alloc(StringData.sizeof + capacity)!!;
|
||||
data.allocator = allocator;
|
||||
data.len = 0;
|
||||
data.capacity = capacity;
|
||||
return (String)data;
|
||||
}
|
||||
|
||||
fn String new(char[] c)
|
||||
{
|
||||
usz len = c.len;
|
||||
String str = new_with_capacity(len);
|
||||
StringData* data = str.data();
|
||||
if (len)
|
||||
{
|
||||
data.len = len;
|
||||
mem::copy(&data.chars, c.ptr, len);
|
||||
}
|
||||
return (String)data;
|
||||
}
|
||||
|
||||
fn ZString String.zstr(String str)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data) return (ZString)"";
|
||||
if (data.capacity == data.len)
|
||||
{
|
||||
str.reserve(1);
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
else if (data.chars[data.len] != 0)
|
||||
{
|
||||
data.chars[data.len] = 0;
|
||||
}
|
||||
return (ZString)&data.chars[0];
|
||||
}
|
||||
|
||||
fn usz String.len(String this)
|
||||
{
|
||||
if (!this) return 0;
|
||||
return this.data().len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require new_size <= this.len()
|
||||
*/
|
||||
fn void String.chop(String this, usz new_size)
|
||||
{
|
||||
if (!this) return;
|
||||
this.data().len = new_size;
|
||||
}
|
||||
|
||||
fn char[] String.str(String str)
|
||||
{
|
||||
StringData* data = (StringData*)str;
|
||||
if (!data) return char[] {};
|
||||
return data.chars[:data.len];
|
||||
}
|
||||
|
||||
fn void String.append_utf32(String* str, Char32[] chars)
|
||||
{
|
||||
str.reserve(chars.len);
|
||||
foreach (Char32 c : chars)
|
||||
{
|
||||
str.append_char32(c);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require index < str.len()
|
||||
**/
|
||||
fn void String.set(String str, usz index, char c)
|
||||
{
|
||||
str.data().chars[index] = c;
|
||||
}
|
||||
|
||||
fn void String.append_repeat(String* str, char c, usz times)
|
||||
{
|
||||
if (times == 0) return;
|
||||
str.reserve(times);
|
||||
StringData* data = str.data();
|
||||
for (usz i = 0; i < times; i++)
|
||||
{
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require c < 0x10ffff
|
||||
*/
|
||||
fn void String.append_char32(String* str, Char32 c)
|
||||
{
|
||||
if (c < 0x7f)
|
||||
{
|
||||
str.reserve(1);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)c;
|
||||
return;
|
||||
}
|
||||
if (c < 0x7ff)
|
||||
{
|
||||
str.reserve(2);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xC0 | c >> 6);
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
return;
|
||||
}
|
||||
if (c < 0xffff)
|
||||
{
|
||||
str.reserve(3);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xE0 | c >> 12);
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
return;
|
||||
}
|
||||
str.reserve(4);
|
||||
StringData* data = str.data();
|
||||
data.chars[data.len++] = (char)(0xF0 | c >> 18);
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 12 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c >> 6 & 0x3F));
|
||||
data.chars[data.len++] = (char)(0x80 | (c & 0x3F));
|
||||
}
|
||||
|
||||
fn String String.tcopy(String* str) = str.copy(mem::temp_allocator());
|
||||
|
||||
fn String String.copy(String* str, Allocator* allocator = null)
|
||||
{
|
||||
if (!str)
|
||||
{
|
||||
if (allocator) return new_with_capacity(0, allocator);
|
||||
return (String)null;
|
||||
}
|
||||
if (!allocator) allocator = mem::current_allocator();
|
||||
StringData* data = str.data();
|
||||
String new_string = new_with_capacity(data.capacity, allocator);
|
||||
mem::copy((char*)new_string.data(), (char*)data, StringData.sizeof + data.len);
|
||||
return new_string;
|
||||
}
|
||||
|
||||
fn ZString String.copy_zstr(String* str, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
usz str_len = str.len();
|
||||
if (!str_len)
|
||||
{
|
||||
return (ZString)allocator.calloc(1)!!;
|
||||
}
|
||||
char* zstr = allocator.alloc(str_len + 1)!!;
|
||||
StringData* data = str.data();
|
||||
mem::copy(zstr, &data.chars, str_len);
|
||||
zstr[str_len] = 0;
|
||||
return (ZString)zstr;
|
||||
}
|
||||
|
||||
fn char[] String.copy_str(String* str, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
return str.copy_zstr(allocator)[:str.len()];
|
||||
}
|
||||
|
||||
fn char[] String.tcopy_str(String* str) = str.copy_str(mem::temp_allocator()) @inline;
|
||||
|
||||
fn bool String.equals(String str, String other_string)
|
||||
{
|
||||
StringData *str1 = str.data();
|
||||
StringData *str2 = other_string.data();
|
||||
if (str1 == str2) return true;
|
||||
if (!str1) return str2.len == 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usz str1_len = str1.len;
|
||||
if (str1_len != str2.len) return false;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
if (str1.chars[i] != str2.chars[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void String.destroy(String* str)
|
||||
{
|
||||
if (!*str) return;
|
||||
StringData* data = str.data();
|
||||
if (!data) return;
|
||||
data.allocator.free(data)!!;
|
||||
*str = (String)null;
|
||||
}
|
||||
|
||||
fn bool String.less(String str, String other_string)
|
||||
{
|
||||
StringData* str1 = str.data();
|
||||
StringData* str2 = other_string.data();
|
||||
if (str1 == str2) return false;
|
||||
if (!str1) return str2.len != 0;
|
||||
if (!str2) return str1.len == 0;
|
||||
usz str1_len = str1.len;
|
||||
usz str2_len = str2.len;
|
||||
if (str1_len != str2_len) return str1_len < str2_len;
|
||||
for (int i = 0; i < str1_len; i++)
|
||||
{
|
||||
if (str1.chars[i] >= str2.chars[i]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void String.append_chars(String* this, char[] str)
|
||||
{
|
||||
usz other_len = str.len;
|
||||
if (!other_len) return;
|
||||
if (!*this)
|
||||
{
|
||||
*this = new(str);
|
||||
return;
|
||||
}
|
||||
this.reserve(other_len);
|
||||
StringData* data = (StringData*)*this;
|
||||
mem::copy(&data.chars[data.len], str.ptr, other_len);
|
||||
data.len += other_len;
|
||||
}
|
||||
|
||||
fn Char32[] String.copy_utf32(String* this, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
return str::utf8to32(this.str(), allocator) @inline!!;
|
||||
}
|
||||
|
||||
fn void String.append_string(String* this, String str)
|
||||
{
|
||||
StringData* other = (StringData*)str;
|
||||
if (!other) return;
|
||||
this.append(str.str());
|
||||
}
|
||||
|
||||
fn void String.append_char(String* str, char c)
|
||||
{
|
||||
if (!*str)
|
||||
{
|
||||
*str = new_with_capacity(MIN_CAPACITY);
|
||||
}
|
||||
str.reserve(1);
|
||||
StringData* data = (StringData*)*str;
|
||||
data.chars[data.len++] = c;
|
||||
}
|
||||
|
||||
|
||||
macro void String.append(String* str, value)
|
||||
{
|
||||
var $Type = $typeof(value);
|
||||
$switch ($Type):
|
||||
$case char:
|
||||
$case ichar:
|
||||
str.append_char(value);
|
||||
$case String:
|
||||
str.append_string(value);
|
||||
$case char[]:
|
||||
str.append_chars(value);
|
||||
$case Char32:
|
||||
str.append_char32(value);
|
||||
$default:
|
||||
$if (@convertible($Type, Char32)):
|
||||
str.append_char32(value);
|
||||
$elif (@convertible($Type, char[])):
|
||||
str.append_chars(value);
|
||||
$else:
|
||||
$assert("Unsupported type for appending");
|
||||
$endif;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
|
||||
private fn StringData* String.data(String str) @inline
|
||||
{
|
||||
return (StringData*)str;
|
||||
}
|
||||
|
||||
private fn void String.reserve(String* str, usz addition)
|
||||
{
|
||||
StringData* data = str.data();
|
||||
if (!data)
|
||||
{
|
||||
*str = string::new_with_capacity(addition);
|
||||
return;
|
||||
}
|
||||
usz len = data.len + addition;
|
||||
if (data.capacity >= len) return;
|
||||
usz new_capacity = data.capacity *= 2;
|
||||
if (new_capacity < MIN_CAPACITY) new_capacity = MIN_CAPACITY;
|
||||
*str = (String)data.allocator.realloc(data, StringData.sizeof + new_capacity)!!;
|
||||
}
|
||||
|
||||
fn String String.new_concat(String a, String b, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
String string = new_with_capacity(a.len() + b.len(), allocator);
|
||||
string.append(a);
|
||||
string.append(b);
|
||||
return string;
|
||||
}
|
||||
23
lib/std/core/string_iterator.c3
Normal file
23
lib/std/core/string_iterator.c3
Normal file
@@ -0,0 +1,23 @@
|
||||
module std::core::string::iterator;
|
||||
|
||||
struct StringIterator
|
||||
{
|
||||
char[] utf8;
|
||||
usz current;
|
||||
}
|
||||
|
||||
fn void StringIterator.reset(StringIterator* this)
|
||||
{
|
||||
this.current = 0;
|
||||
}
|
||||
|
||||
fn Char32! StringIterator.next(StringIterator* this)
|
||||
{
|
||||
usz len = this.utf8.len;
|
||||
usz current = this.current;
|
||||
if (current >= len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
usz read = (len - current < 4 ? len - current : 4);
|
||||
Char32 res = conv::utf8_to_char32(&this.utf8[current], &read)?;
|
||||
this.current += read;
|
||||
return res;
|
||||
}
|
||||
233
lib/std/core/types.c3
Normal file
233
lib/std/core/types.c3
Normal file
@@ -0,0 +1,233 @@
|
||||
module std::core::types;
|
||||
import libc;
|
||||
|
||||
|
||||
fault ConversionResult
|
||||
{
|
||||
VALUE_OUT_OF_RANGE,
|
||||
VALUE_OUT_OF_UNSIGNED_RANGE,
|
||||
}
|
||||
/**
|
||||
* @require $Type.kindof.is_int() || $Type.kindof == TypeKind.ENUM "Argument was not an integer"
|
||||
**/
|
||||
macro variant_to_int(variant v, $Type)
|
||||
{
|
||||
typeid variant_type = v.type;
|
||||
TypeKind kind = variant_type.kindof;
|
||||
if (kind == TypeKind.ENUM)
|
||||
{
|
||||
variant_type = variant_type.inner;
|
||||
kind = variant_type.kindof;
|
||||
}
|
||||
bool is_mixed_signed = $Type.kindof != variant_type.kindof;
|
||||
$Type max = $Type.max;
|
||||
$Type min = $Type.min;
|
||||
switch (variant_type)
|
||||
{
|
||||
case ichar:
|
||||
ichar c = *(char*)v.ptr;
|
||||
if (is_mixed_signed && c < 0) return ConversionResult.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!;
|
||||
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!;
|
||||
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!;
|
||||
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!;
|
||||
return ($Type)i;
|
||||
case char:
|
||||
char c = *(char*)v.ptr;
|
||||
if (c > max) return ConversionResult.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!;
|
||||
return ($Type)s;
|
||||
case uint:
|
||||
uint i = *(uint*)v.ptr;;
|
||||
if (i > max || i < min) return ConversionResult.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!;
|
||||
return ($Type)l;
|
||||
case uint128:
|
||||
uint128 i = *(uint128*)v.ptr;
|
||||
if (i > max || i < min) return ConversionResult.VALUE_OUT_OF_RANGE!;
|
||||
return ($Type)i;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
macro bool is_numerical($Type)
|
||||
{
|
||||
var $kind = $Type.kindof;
|
||||
$if ($kind == TypeKind.DISTINCT):
|
||||
return is_numerical($Type.inner);
|
||||
$else:
|
||||
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|
||||
|| $kind == TypeKind.VECTOR;
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn bool TypeKind.is_int(TypeKind kind) @inline
|
||||
{
|
||||
return kind == TypeKind.SIGNED_INT || kind == TypeKind.UNSIGNED_INT;
|
||||
}
|
||||
|
||||
macro bool is_indexable($Type)
|
||||
{
|
||||
return $checks($Type t, int i, t[i]);
|
||||
}
|
||||
|
||||
macro bool is_comparable($Type)
|
||||
{
|
||||
var $kind = $Type.kindof;
|
||||
$if ($kind == TypeKind.DISTINCT):
|
||||
return is_comparable($Type.inner);
|
||||
$else:
|
||||
return $kind == TypeKind.SIGNED_INT || $kind == TypeKind.UNSIGNED_INT || $kind == TypeKind.FLOAT
|
||||
|| $kind == TypeKind.VECTOR || $kind == TypeKind.BOOL || $kind == TypeKind.POINTER
|
||||
|| $kind == TypeKind.ENUM;
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro bool is_equatable($Type)
|
||||
{
|
||||
return $checks($Type a, a == a);
|
||||
}
|
||||
|
||||
macro bool is_subarray_convertable($Type)
|
||||
{
|
||||
$switch ($Type.kindof):
|
||||
$case SUBARRAY:
|
||||
return true;
|
||||
$case POINTER:
|
||||
return $Type.inner.kindof == TypeKind.ARRAY;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
macro bool is_int($Type) = $Type.kindof == TypeKind.SIGNED_INT || $Type.kindof == TypeKind.UNSIGNED_INT;
|
||||
|
||||
macro bool is_intlike($Type)
|
||||
{
|
||||
$switch ($Type.kindof):
|
||||
$case SIGNED_INT:
|
||||
$case UNSIGNED_INT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return $Type.inner.kindof == TypeKind.SIGNED_INT || $Type.inner.kindof == TypeKind.UNSIGNED_INT;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
|
||||
macro bool is_float($Type) = $Type.kindof == TypeKind.FLOAT;
|
||||
|
||||
macro bool is_floatlike($Type)
|
||||
{
|
||||
$switch ($Type.kindof):
|
||||
$case FLOAT:
|
||||
return true;
|
||||
$case VECTOR:
|
||||
return $Type.inner.kindof == TypeKind.FLOAT;
|
||||
$default:
|
||||
return false;
|
||||
$endswitch;
|
||||
}
|
||||
|
||||
macro bool is_vector($Type)
|
||||
{
|
||||
return $Type.kindof == TypeKind.VECTOR;
|
||||
}
|
||||
|
||||
macro bool @convertable(#a, $TypeB)
|
||||
{
|
||||
return $checks($TypeB x = #a);
|
||||
}
|
||||
|
||||
macro bool is_same($TypeA, $TypeB)
|
||||
{
|
||||
return $TypeA.typeid == $TypeB.typeid;
|
||||
}
|
||||
|
||||
macro bool @has_same(#a, #b, ...)
|
||||
{
|
||||
var $type_a = $typeof(#a).typeid;
|
||||
$if ($type_a != $typeof(#b).typeid):
|
||||
return false;
|
||||
$endif;
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
$if ($typeof($vaexpr($i)).typeid != $type_a):
|
||||
return false;
|
||||
$endif;
|
||||
$endfor;
|
||||
return true;
|
||||
}
|
||||
|
||||
macro bool is_equatable_value(value)
|
||||
{
|
||||
$if ($defined(value.less) || $defined(value.compare_to) || $defined(value.equals)):
|
||||
return true;
|
||||
$else:
|
||||
return is_equatable($typeof(value));
|
||||
$endif;
|
||||
}
|
||||
|
||||
macro bool is_comparable_value(value)
|
||||
{
|
||||
$if ($defined(value.less) || $defined(value.compare_to)):
|
||||
return true;
|
||||
$else:
|
||||
return is_comparable($typeof(value));
|
||||
$endif;
|
||||
}
|
||||
|
||||
enum TypeKind : char
|
||||
{
|
||||
VOID,
|
||||
BOOL,
|
||||
SIGNED_INT,
|
||||
UNSIGNED_INT,
|
||||
FLOAT,
|
||||
TYPEID,
|
||||
ANYERR,
|
||||
ANY,
|
||||
ENUM,
|
||||
FAULT,
|
||||
STRUCT,
|
||||
UNION,
|
||||
BITSTRUCT,
|
||||
FUNC,
|
||||
OPTIONAL,
|
||||
ARRAY,
|
||||
SUBARRAY,
|
||||
VECTOR,
|
||||
DISTINCT,
|
||||
POINTER,
|
||||
VARIANT
|
||||
}
|
||||
|
||||
struct TypeEnum
|
||||
{
|
||||
TypeKind type;
|
||||
usz elements;
|
||||
}
|
||||
|
||||
6
lib/std/core/values.c3
Normal file
6
lib/std/core/values.c3
Normal file
@@ -0,0 +1,6 @@
|
||||
module std::core::values;
|
||||
|
||||
macro bool @is_int(#value) = types::is_int($typeof(#value));
|
||||
macro bool @convertable_to(#a, #b) = $checks($typeof(#b) x = #a);
|
||||
macro bool @is_floatlike(#value) = types::is_floatlike($typeof(#value));
|
||||
macro bool @is_float(#value) = types::is_float($typeof(#value));
|
||||
@@ -1,23 +1,21 @@
|
||||
// TODO: ensure the type is an enum first.
|
||||
module enumset<Enum>;
|
||||
module std::enumset<Enum>;
|
||||
|
||||
$assert(Enum.min < Enum.max, "Only strictly increasing enums may be used with enum sets.");
|
||||
$assert(Enum.max < 64, "Maximum value of an enum used as enum set is 63");
|
||||
$assert(Enum.min >= 0, "Minimum value of an enum used as enum set is 0");
|
||||
$assert(Enum.elements < 64, "Maximum number of elements for an enum used as enum set is 63");
|
||||
|
||||
$switch ($$C_INT_SIZE):
|
||||
$case 64:
|
||||
private define EnumSetType = ulong;
|
||||
$case 32:
|
||||
$if (Enum.max < 32):
|
||||
$if (Enum.elements < 32):
|
||||
private define EnumSetType = uint;
|
||||
$else:
|
||||
private define EnumSetType = ulong;
|
||||
$endif;
|
||||
$default:
|
||||
$if (Enum.max < 16):
|
||||
$if (Enum.elements < 16):
|
||||
private define EnumSetType = ushort;
|
||||
$elif (Enum.max < 31):
|
||||
$elif (Enum.elements < 31):
|
||||
private define EnumSetType = uint;
|
||||
$else:
|
||||
private define EnumSetType = ulong;
|
||||
@@ -26,17 +24,17 @@ $endswitch;
|
||||
|
||||
define EnumSet = distinct EnumSetType;
|
||||
|
||||
fn void EnumSet.add(EnumSet *this, Enum v)
|
||||
fn void EnumSet.add(EnumSet* this, Enum v)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this | 1u << (EnumSetType)v);
|
||||
}
|
||||
|
||||
fn void EnumSet.clear(EnumSet *this)
|
||||
fn void EnumSet.clear(EnumSet* this)
|
||||
{
|
||||
*this = 0;
|
||||
}
|
||||
|
||||
fn bool EnumSet.remove(EnumSet *this, Enum v)
|
||||
fn bool EnumSet.remove(EnumSet* this, Enum v)
|
||||
{
|
||||
EnumSetType old = (EnumSetType)*this;
|
||||
EnumSetType new = old & ~(1u << (EnumSetType)v);
|
||||
@@ -44,37 +42,42 @@ fn bool EnumSet.remove(EnumSet *this, Enum v)
|
||||
return old != new;
|
||||
}
|
||||
|
||||
fn bool EnumSet.has(EnumSet *this, Enum v)
|
||||
fn bool EnumSet.has(EnumSet* this, Enum v)
|
||||
{
|
||||
return ((EnumSetType)*this & (1u << (EnumSetType)v)) != 0;
|
||||
}
|
||||
|
||||
fn void EnumSet.add_all(EnumSet *this, EnumSet s)
|
||||
fn void EnumSet.add_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn void EnumSet.retain_all(EnumSet *this, EnumSet s)
|
||||
fn void EnumSet.retain_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.and_of(EnumSet *this, EnumSet s)
|
||||
fn void EnumSet.remove_all(EnumSet* this, EnumSet s)
|
||||
{
|
||||
*this = (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.and_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this & (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.or_of(EnumSet *this, EnumSet s)
|
||||
fn EnumSet EnumSet.or_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this | (EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.diff_of(EnumSet *this, EnumSet s)
|
||||
fn EnumSet EnumSet.diff_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this & ~(EnumSetType)s);
|
||||
}
|
||||
|
||||
fn EnumSet EnumSet.xor_of(EnumSet *this, EnumSet s)
|
||||
fn EnumSet EnumSet.xor_of(EnumSet* this, EnumSet s)
|
||||
{
|
||||
return (EnumSet)((EnumSetType)*this ^ (EnumSetType)s);
|
||||
}
|
||||
41
lib/std/hash/fnv32a.c3
Normal file
41
lib/std/hash/fnv32a.c3
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2021 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::hash::fnv32a;
|
||||
|
||||
define Fnv32a = distinct uint;
|
||||
|
||||
private const FNV32A_START = 0x811c9dc5;
|
||||
private const FNV32A_MUL = 0x01000193;
|
||||
|
||||
private macro void @update(uint &h, char x) => h = (h * FNV32A_MUL) ^ x;
|
||||
|
||||
fn void Fnv32a.init(Fnv32a* this)
|
||||
{
|
||||
*this = FNV32A_START;
|
||||
}
|
||||
|
||||
fn void Fnv32a.update(Fnv32a* this, char[] data)
|
||||
{
|
||||
uint h = (uint)*this;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
}
|
||||
*this = (Fnv32a)h;
|
||||
}
|
||||
|
||||
macro void Fnv32a.update_char(Fnv32a* this, char c)
|
||||
{
|
||||
@update(*this, x);
|
||||
}
|
||||
|
||||
fn uint encode(char[] data)
|
||||
{
|
||||
uint h = FNV32A_START;
|
||||
foreach (char x : data)
|
||||
{
|
||||
@update(h, x);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
198
lib/std/io.c3
198
lib/std/io.c3
@@ -1,198 +0,0 @@
|
||||
// Copyright (c) 2021-2022 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::io;
|
||||
import std::mem;
|
||||
import libc;
|
||||
import std::env;
|
||||
|
||||
struct File
|
||||
{
|
||||
CFile file;
|
||||
}
|
||||
|
||||
fn int putchar(char c) @inline
|
||||
{
|
||||
return libc::putchar(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int print(char* message)
|
||||
{
|
||||
char* pointer = message;
|
||||
while (*pointer != '\0')
|
||||
{
|
||||
if (!putchar(*pointer)) return 0;
|
||||
pointer++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int println(char *message = "") @inline
|
||||
{
|
||||
return libc::puts(message);
|
||||
}
|
||||
|
||||
fn void! File.open(File* file, char[] filename, char[] mode)
|
||||
{
|
||||
char* filename_copy = mem::talloc(filename.len + 1)!!;
|
||||
|
||||
char* mode_copy = mem::talloc(mode.len + 1)!!;
|
||||
|
||||
mem::copy(filename_copy, (char*)(filename), filename.len);
|
||||
mem::copy(mode_copy, (char*)(mode), mode.len);
|
||||
filename_copy[filename.len] = 0;
|
||||
mode_copy[filename.len] = 0;
|
||||
file.file = libc::fopen(filename_copy, mode_copy);
|
||||
if (!file.file) return IoError.FILE_NOT_FOUND!;
|
||||
}
|
||||
|
||||
enum Seek
|
||||
{
|
||||
SET = 0,
|
||||
CURSOR = 1,
|
||||
END = 2
|
||||
}
|
||||
|
||||
fault IoError
|
||||
{
|
||||
FILE_NOT_FOUND,
|
||||
FILE_NOT_SEEKABLE,
|
||||
FILE_NOT_VALID,
|
||||
FILE_INVALID_POSITION,
|
||||
FILE_OVERFLOW,
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
FILE_INCOMPLETE_WRITE,
|
||||
INTERRUPTED,
|
||||
UNKNOWN_ERROR,
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file.file != null
|
||||
**/
|
||||
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
|
||||
{
|
||||
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case EBADF: return IoError.FILE_NOT_SEEKABLE!;
|
||||
case EINVAL: return IoError.FILE_INVALID_POSITION!;
|
||||
case EOVERFLOW: return IoError.FILE_OVERFLOW!;
|
||||
case ESPIPE: return IoError.FILE_IS_PIPE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file != null
|
||||
*/
|
||||
fn void! File.putc(File *file, char c)
|
||||
{
|
||||
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file != null
|
||||
*/
|
||||
fn void! File.close(File *file) @inline
|
||||
{
|
||||
if (file.file && libc::fclose(file.file))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case ECONNRESET:
|
||||
case EBADF: return IoError.FILE_NOT_VALID!;
|
||||
case EINTR: return IoError.INTERRUPTED!;
|
||||
case EDQUOT:
|
||||
case EFAULT:
|
||||
case EAGAIN:
|
||||
case EFBIG:
|
||||
case ENETDOWN:
|
||||
case ENETUNREACH:
|
||||
case ENOSPC:
|
||||
case EIO: return IoError.FILE_INCOMPLETE_WRITE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
file.file = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file
|
||||
*/
|
||||
fn bool File.eof(File* file) @inline
|
||||
{
|
||||
return libc::feof(file.file) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file
|
||||
*/
|
||||
fn usize File.read(File* file, void* buffer, usize items, usize element_size = 1)
|
||||
{
|
||||
return libc::fread(buffer, element_size, items, file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @param [&out] buffer
|
||||
* @param items
|
||||
* @param element_size
|
||||
* @require file.file `File must be initialized`
|
||||
* @require element_size > 1
|
||||
*/
|
||||
fn usize File.write(File* file, void* buffer, usize items, usize element_size = 1)
|
||||
{
|
||||
return libc::fwrite(buffer, element_size, items, file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn usize! File.println(File* file, char[] string)
|
||||
{
|
||||
usize len = string.len;
|
||||
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
error FileError
|
||||
{
|
||||
ulong errno;
|
||||
}
|
||||
|
||||
fn FileError errorFromErrno()
|
||||
{
|
||||
return FileError { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pubic fn void! File.clearerr(File *file) @inline
|
||||
{
|
||||
clearerr(file->file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void File.error(File *file) @inline
|
||||
{
|
||||
int err = ferror
|
||||
}
|
||||
*/
|
||||
183
lib/std/io/dir.c3
Normal file
183
lib/std/io/dir.c3
Normal file
@@ -0,0 +1,183 @@
|
||||
module std::io::dir;
|
||||
import std::io::os;
|
||||
// In progress.
|
||||
define Path = distinct char[];
|
||||
|
||||
const PREFERRED_SEPARATOR = USE_WIN32_FILESYSTEM ? '\\' : '/';
|
||||
private const USE_WIN32_FILESYSTEM = env::OS_TYPE != OsType.WIN32;
|
||||
|
||||
fault PathResult
|
||||
{
|
||||
INVALID_PATH
|
||||
}
|
||||
|
||||
fn char[]! getcwd(Allocator* allocator = mem::default_allocator())
|
||||
{
|
||||
return os::getcwd(allocator);
|
||||
}
|
||||
|
||||
fn char[]! tgetcwd()
|
||||
{
|
||||
return getcwd(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
macro bool is_separator(char c)
|
||||
{
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
return c == '/' || c == '\\';
|
||||
$else:
|
||||
return c == '/';
|
||||
$endif;
|
||||
}
|
||||
|
||||
const bool[256] RESERVED_PATH_CHAR_POSIX = {
|
||||
[0] = true,
|
||||
['/'] = true,
|
||||
};
|
||||
const bool[256] RESERVED_PATH_CHAR_WIN32 = {
|
||||
[0..31] = true,
|
||||
['>'] = true,
|
||||
['<'] = true,
|
||||
[':'] = true,
|
||||
['\"'] = true,
|
||||
['/'] = true,
|
||||
['\\'] = true,
|
||||
['|'] = true,
|
||||
['?'] = true,
|
||||
['*'] = true,
|
||||
};
|
||||
|
||||
macro bool is_reserved_path_char(char c)
|
||||
{
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
return RESERVED_PATH_CHAR_WIN32[c];
|
||||
$else:
|
||||
return RESERVED_PATH_CHAR_POSIX[c];
|
||||
$endif;
|
||||
}
|
||||
|
||||
private fn usz! root_name_len(char[] path)
|
||||
{
|
||||
usz len = path.len;
|
||||
if (!len) return 0;
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
switch (path[0])
|
||||
{
|
||||
case '\\':
|
||||
if (len < 2 || path[1] != '\\') break;
|
||||
if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!;
|
||||
for (usz i = 2; i < len; i++)
|
||||
{
|
||||
char c = path[i];
|
||||
if (is_separator(c)) return i;
|
||||
if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!;
|
||||
}
|
||||
return len;
|
||||
case 'A'..'Z':
|
||||
case 'a'..'z':
|
||||
if (len < 2 || path[1] != ':') break;
|
||||
if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!;
|
||||
return 2;
|
||||
}
|
||||
$endif;
|
||||
return 0;
|
||||
}
|
||||
|
||||
private fn void! normalize_path(char[]* path_ref)
|
||||
{
|
||||
char[] path = *path_ref;
|
||||
if (!path.len) return;
|
||||
usz path_start = root_name_len(path)?;
|
||||
usz len = path_start;
|
||||
bool previous_was_separator = false;
|
||||
usz path_len = path.len;
|
||||
for (usz i = path_start; i < path_len; i++)
|
||||
{
|
||||
char c = path[i];
|
||||
// Fold foo///bar into foo/bar
|
||||
if (is_separator(c))
|
||||
{
|
||||
if (previous_was_separator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
path.ptr[len++] = PREFERRED_SEPARATOR;
|
||||
previous_was_separator = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If we get . we have different things that might happen:
|
||||
if (c == '.' && i < path_len - 1)
|
||||
{
|
||||
// Is this ./ or /./ ?
|
||||
if ((previous_was_separator || i == path_start) && is_separator(path[i + 1]))
|
||||
{
|
||||
// Then we skip this
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
// Is this /../ in that case we must walk back and erase(!)
|
||||
if (i < path_len - 2 && previous_was_separator && path[i + 1] == '.' && is_separator(path[i + 2]))
|
||||
{
|
||||
assert(len > path_start);
|
||||
len--;
|
||||
while (len > path_start && !is_separator(path[len - 1]))
|
||||
{
|
||||
len--;
|
||||
}
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (i != len) path[len] = c;
|
||||
previous_was_separator = false;
|
||||
len++;
|
||||
}
|
||||
path.ptr[len] = 0;
|
||||
*path_ref = path[:len];
|
||||
}
|
||||
|
||||
fn Path new_path(char[] path)
|
||||
{
|
||||
char[] copy = str::copy(path);
|
||||
normalize_path(©)!!;
|
||||
return (Path)copy;
|
||||
}
|
||||
|
||||
fn Path Path.root_name(Path path)
|
||||
{
|
||||
char[] path_str = (char[])path;
|
||||
usz len = root_name_len(path_str)!!;
|
||||
if (!len) return (Path)"";
|
||||
return (Path)path_str[0:len];
|
||||
}
|
||||
|
||||
fn Path Path.root_directory(Path path)
|
||||
{
|
||||
char[] path_str = (char[])path;
|
||||
usz len = path_str.len;
|
||||
if (!len) return (Path)"";
|
||||
$if (USE_WIN32_FILESYSTEM):
|
||||
usz root_len = root_name_len(path_str)!!;
|
||||
if (root_len == len || !is_separator(path_str[root_len])) return (Path)"";
|
||||
return (Path)path_str[root_len..root_len];
|
||||
$else:
|
||||
if (!is_separator(path_str[0])) return (Path)"";
|
||||
for (usz i = 1; i < len; i++)
|
||||
{
|
||||
if (is_separator(path_str[i]))
|
||||
{
|
||||
return (Path)path_str[:i];
|
||||
}
|
||||
}
|
||||
return path;
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
fn void Path.destroy(Path path)
|
||||
{
|
||||
free(path.ptr);
|
||||
}
|
||||
|
||||
|
||||
111
lib/std/io/io.c3
Normal file
111
lib/std/io/io.c3
Normal file
@@ -0,0 +1,111 @@
|
||||
// Copyright (c) 2021-2022 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::io;
|
||||
import libc;
|
||||
|
||||
struct File
|
||||
{
|
||||
CFile file;
|
||||
}
|
||||
|
||||
enum Seek
|
||||
{
|
||||
SET,
|
||||
CURSOR,
|
||||
END
|
||||
}
|
||||
|
||||
fault IoError
|
||||
{
|
||||
FILE_NOT_FOUND,
|
||||
FILE_NOT_SEEKABLE,
|
||||
FILE_NOT_VALID,
|
||||
FILE_INVALID_POSITION,
|
||||
FILE_OVERFLOW,
|
||||
FILE_IS_PIPE,
|
||||
FILE_EOF,
|
||||
FILE_INCOMPLETE_WRITE,
|
||||
FILE_NOT_DIR,
|
||||
NO_PERMISSION,
|
||||
NAME_TOO_LONG,
|
||||
INTERRUPTED,
|
||||
GENERAL_ERROR,
|
||||
UNKNOWN_ERROR,
|
||||
}
|
||||
|
||||
fn int putchar(char c) @inline
|
||||
{
|
||||
return libc::putchar(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int print(char* message)
|
||||
{
|
||||
char* pointer = message;
|
||||
while (*pointer != '\0')
|
||||
{
|
||||
if (!putchar(*pointer)) return 0;
|
||||
pointer++;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] message
|
||||
* @return `number of bytes printed.`
|
||||
*/
|
||||
fn int println(char *message = "") @inline
|
||||
{
|
||||
return libc::puts(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fn File stdout()
|
||||
{
|
||||
return { libc::stdout() };
|
||||
}
|
||||
|
||||
fn File stderr()
|
||||
{
|
||||
return { libc::stderr() };
|
||||
}
|
||||
|
||||
fn File stdin()
|
||||
{
|
||||
return { libc::stdin() };
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
|
||||
error FileError
|
||||
{
|
||||
ulong errno;
|
||||
}
|
||||
|
||||
fn FileError errorFromErrno()
|
||||
{
|
||||
return FileError { };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
pubic fn void! File.clearerr(File *file) @inline
|
||||
{
|
||||
clearerr(file->file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn void File.error(File *file) @inline
|
||||
{
|
||||
int err = ferror
|
||||
}
|
||||
*/
|
||||
157
lib/std/io/io_file.c3
Normal file
157
lib/std/io/io_file.c3
Normal file
@@ -0,0 +1,157 @@
|
||||
module std::io;
|
||||
import libc;
|
||||
|
||||
fn void! File.open(File* file, char[] filename, char[] mode)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
char* filename_copy = tmalloc(filename.len + 1);
|
||||
char* mode_copy = tmalloc(mode.len + 1);
|
||||
|
||||
mem::copy(filename_copy, (char*)(filename), filename.len);
|
||||
mem::copy(mode_copy, (char*)(mode), mode.len);
|
||||
filename_copy[filename.len] = 0;
|
||||
mode_copy[filename.len] = 0;
|
||||
file.file = libc::fopen(filename_copy, mode_copy);
|
||||
if (!file.file) return IoError.FILE_NOT_FOUND!;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file.file != null
|
||||
**/
|
||||
fn void! File.seek(File *file, long offset, Seek seekMode = Seek.SET)
|
||||
{
|
||||
if (libc::fseek(file.file, (SeekIndex)(offset), (int)(seekMode)))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF: return IoError.FILE_NOT_SEEKABLE!;
|
||||
case errno::EINVAL: return IoError.FILE_INVALID_POSITION!;
|
||||
case errno::EOVERFLOW: return IoError.FILE_OVERFLOW!;
|
||||
case errno::ESPIPE: return IoError.FILE_IS_PIPE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file != null
|
||||
*/
|
||||
fn void! File.putc(File *file, char c)
|
||||
{
|
||||
if (!libc::fputc(c, file.file)) return IoError.FILE_EOF!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file != null
|
||||
*/
|
||||
fn void! File.close(File *file) @inline
|
||||
{
|
||||
if (file.file && libc::fclose(file.file))
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::ECONNRESET:
|
||||
case errno::EBADF: return IoError.FILE_NOT_VALID!;
|
||||
case errno::EINTR: return IoError.INTERRUPTED!;
|
||||
case errno::EDQUOT:
|
||||
case errno::EFAULT:
|
||||
case errno::EAGAIN:
|
||||
case errno::EFBIG:
|
||||
case errno::ENETDOWN:
|
||||
case errno::ENETUNREACH:
|
||||
case errno::ENOSPC:
|
||||
case errno::EIO: return IoError.FILE_INCOMPLETE_WRITE!;
|
||||
default: return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
file.file = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file
|
||||
*/
|
||||
fn bool File.eof(File* file) @inline
|
||||
{
|
||||
return libc::feof(file.file) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require file && file.file
|
||||
*/
|
||||
fn usz File.read(File* file, void* buffer, usz items, usz element_size = 1)
|
||||
{
|
||||
return libc::fread(buffer, element_size, items, file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @param [&out] buffer
|
||||
* @param items
|
||||
* @param element_size
|
||||
* @require file.file `File must be initialized`
|
||||
* @require element_size > 1
|
||||
*/
|
||||
fn usz File.write(File* file, void* buffer, usz items, usz element_size = 1)
|
||||
{
|
||||
return libc::fwrite(buffer, element_size, items, file.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn usz! File.println(File* file, char[] string)
|
||||
{
|
||||
usz len = string.len;
|
||||
if (len != libc::fwrite(string.ptr, 1, len, file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
if (!libc::putc('\n', file.file)) return IoError.UNKNOWN_ERROR!;
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn String File.getline(File* file, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
String s = string::new_with_capacity(120, allocator);
|
||||
while (!file.eof())
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) break;
|
||||
if (c == '\n') break;
|
||||
s.append_char((char)c);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
* @return "a zero terminated char[] (the pointer may be safely cast into a ZString)"
|
||||
*/
|
||||
fn char[] File.tgetline(File* file)
|
||||
{
|
||||
|
||||
String s = file.getline(mem::temp_allocator());
|
||||
ZString z = s.zstr();
|
||||
return z[:s.len()];
|
||||
}
|
||||
|
||||
fn char! File.getc(File* file)
|
||||
{
|
||||
int c = libc::fgetc(file.file);
|
||||
if (c == -1) return IoError.FILE_EOF!;
|
||||
return (char)c;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param [&in] file
|
||||
* @require file.file `File must be initialized`
|
||||
*/
|
||||
fn void File.flush(File* file)
|
||||
{
|
||||
libc::fflush(file.file);
|
||||
}
|
||||
81
lib/std/io/io_fileinfo.c3
Normal file
81
lib/std/io/io_fileinfo.c3
Normal file
@@ -0,0 +1,81 @@
|
||||
module std::io::file;
|
||||
import libc;
|
||||
|
||||
struct FileInfo
|
||||
{
|
||||
ulong size;
|
||||
}
|
||||
|
||||
$switch (env::OS_TYPE):
|
||||
$case MACOSX:
|
||||
$case IOS:
|
||||
$case WATCHOS:
|
||||
$case TVOS:
|
||||
private struct DarwinTimespec
|
||||
{
|
||||
long tv_sec;
|
||||
long tv_nsec;
|
||||
}
|
||||
private struct Darwin64Stat
|
||||
{
|
||||
int st_dev;
|
||||
ushort st_mode;
|
||||
ushort st_nlink;
|
||||
ulong st_ino;
|
||||
uint st_uid;
|
||||
uint st_gid;
|
||||
int st_rdev;
|
||||
DarwinTimespec st_atimespec; // time of last access
|
||||
DarwinTimespec st_mtimespec; // time of last data modification
|
||||
DarwinTimespec st_ctimespec; // time of last status change
|
||||
DarwinTimespec st_birthtimespec; // time of file creation(birth)
|
||||
long st_size;
|
||||
long st_blocks;
|
||||
int st_blocksize;
|
||||
uint st_flags;
|
||||
uint st_gen;
|
||||
int st_lspare;
|
||||
long[2] st_qspare;
|
||||
}
|
||||
extern fn int _stat(ZString str, Darwin64Stat* stat) @extname("stat64");
|
||||
fn void! FileInfo.read(FileInfo* info, Path path)
|
||||
{
|
||||
@pool()
|
||||
{
|
||||
Darwin64Stat stat;
|
||||
int res = _stat(str::tcopy_zstring((char[])path), &stat);
|
||||
if (res != 0)
|
||||
{
|
||||
switch (libc::errno())
|
||||
{
|
||||
case errno::EBADF:
|
||||
return IoError.FILE_NOT_VALID!;
|
||||
case errno::EFAULT:
|
||||
unreachable("Invalid stat");
|
||||
case errno::EIO:
|
||||
return IoError.GENERAL_ERROR!;
|
||||
case errno::EACCES:
|
||||
return IoError.NO_PERMISSION!;
|
||||
case errno::ELOOP:
|
||||
return IoError.NO_PERMISSION!;
|
||||
case errno::ENAMETOOLONG:
|
||||
return IoError.NAME_TOO_LONG!;
|
||||
case errno::ENOENT:
|
||||
return IoError.FILE_NOT_FOUND!;
|
||||
case errno::ENOTDIR:
|
||||
return IoError.FILE_NOT_DIR!;
|
||||
case errno::EOVERFLOW:
|
||||
return IoError.GENERAL_ERROR!;
|
||||
default:
|
||||
return IoError.UNKNOWN_ERROR!;
|
||||
}
|
||||
}
|
||||
info.size = stat.st_size;
|
||||
};
|
||||
}
|
||||
$default:
|
||||
macro void! FileInfo.read(FileInfo* info, Path path)
|
||||
{
|
||||
$assert("Unsupported function");
|
||||
}
|
||||
$endswitch;
|
||||
556
lib/std/io/io_formatter_private.c3
Normal file
556
lib/std/io/io_formatter_private.c3
Normal file
@@ -0,0 +1,556 @@
|
||||
module std::io;
|
||||
|
||||
private fn void! Formatter.left_adjust(Formatter* this, usz len)
|
||||
{
|
||||
if (!this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')?;
|
||||
}
|
||||
|
||||
private fn void! Formatter.right_adjust(Formatter* this, usz len)
|
||||
{
|
||||
if (this.flags.left) return;
|
||||
for (usz l = len; l < this.width; l++) this.out(' ')?;
|
||||
}
|
||||
|
||||
|
||||
private fn uint128 int_from_variant(variant arg, bool *is_neg)
|
||||
{
|
||||
*is_neg = false;
|
||||
if (arg.type.kindof == TypeKind.POINTER)
|
||||
{
|
||||
return (uint128)(uptr)*(void**)arg.ptr;
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (uint128)*arg;
|
||||
case ichar:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case short:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case int:
|
||||
int val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case long:
|
||||
long val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case int128:
|
||||
int128 val = *arg;
|
||||
return (*is_neg = val < 0) ? (~(uint128)val) + 1 : (uint128)val;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
case float:
|
||||
float f = *arg;
|
||||
return (uint128)((*is_neg = f < 0) ? -f : f);
|
||||
case double:
|
||||
double d = *arg;
|
||||
return (uint128)((*is_neg = d < 0) ? -d : d);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private fn FloatType float_from_variant(variant arg)
|
||||
{
|
||||
$if (env::F128_SUPPORT):
|
||||
if (arg.type == float128.typeid) return *((float128*)arg.ptr);
|
||||
$endif;
|
||||
$if (env::F16_SUPPORT):
|
||||
if (arg.type == float16.typeid) return *((float16*)arg.ptr);
|
||||
$endif;
|
||||
|
||||
if (arg.type.kindof == TypeKind.POINTER)
|
||||
{
|
||||
return (FloatType)(uptr)(void*)arg.ptr;
|
||||
}
|
||||
switch (arg)
|
||||
{
|
||||
case bool:
|
||||
return (FloatType)*arg;
|
||||
case ichar:
|
||||
return *arg;
|
||||
case short:
|
||||
return *arg;
|
||||
case int:
|
||||
return *arg;
|
||||
case long:
|
||||
return *arg;
|
||||
case int128:
|
||||
return *arg;
|
||||
case char:
|
||||
return *arg;
|
||||
case ushort:
|
||||
return *arg;
|
||||
case uint:
|
||||
return *arg;
|
||||
case ulong:
|
||||
return *arg;
|
||||
case uint128:
|
||||
return *arg;
|
||||
case float:
|
||||
return (FloatType)*arg;
|
||||
case double:
|
||||
return (FloatType)*arg;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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."
|
||||
* @return "The result of the atoi."
|
||||
**/
|
||||
private fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline
|
||||
{
|
||||
uint i = 0;
|
||||
usz len = *len_ptr;
|
||||
while (len < maxlen)
|
||||
{
|
||||
char c = buf[len];
|
||||
if (c < '0' || c > '9') break;
|
||||
i = i * 10 + c - '0';
|
||||
len++;
|
||||
}
|
||||
*len_ptr = len;
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
private fn void! Formatter.out_substr(Formatter *this, char[] str)
|
||||
{
|
||||
usz l = conv::utf8_codepoints(str);
|
||||
uint prec = this.prec;
|
||||
if (this.flags.precision && l < prec) l = prec;
|
||||
this.right_adjust(' ')?;
|
||||
usz index = 0;
|
||||
usz chars = str.len;
|
||||
char* ptr = str.ptr;
|
||||
while (index < chars)
|
||||
{
|
||||
char c = ptr[index];
|
||||
// Break if we have precision set and we ran out...
|
||||
if (c & 0xC0 != 0x80 && this.flags.precision && !prec--) break;
|
||||
this.out(c)?;
|
||||
index++;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
|
||||
|
||||
union ConvUnion
|
||||
{
|
||||
ulong u;
|
||||
double f;
|
||||
}
|
||||
|
||||
private fn void! Formatter.etoa(Formatter* this, FloatType value)
|
||||
{
|
||||
// check for NaN and special values
|
||||
if (value != value || value < -FloatType.max || value > FloatType.max)
|
||||
{
|
||||
return this.ftoa(value);
|
||||
}
|
||||
|
||||
// determine the sign
|
||||
bool negative = value < 0;
|
||||
if (negative) value = -value;
|
||||
|
||||
// default precision
|
||||
if (!this.flags.precision)
|
||||
{
|
||||
this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
}
|
||||
|
||||
// determine the decimal exponent
|
||||
// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
|
||||
ConvUnion conv;
|
||||
|
||||
conv.f = (double)value;
|
||||
int exp2 = (int)(conv.u >> 52 & 0x7FF) - 1023; // effectively log2
|
||||
conv.u = (conv.u & (1u64 << 52 - 1)) | (1023u64 << 52); // drop the exponent so conv.F is now in [1,2)
|
||||
// now approximate log10 from the log2 integer part and an expansion of ln around 1.5
|
||||
int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.f - 1.5) * 0.289529654602168);
|
||||
// now we want to compute 10^expval but we want to be sure it won't overflow
|
||||
exp2 = (int)(expval * 3.321928094887362 + 0.5);
|
||||
double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
|
||||
double z2 = z * z;
|
||||
conv.u = (ulong)(exp2 + 1023) << 52;
|
||||
// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
|
||||
conv.f *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
|
||||
// correct for rounding errors
|
||||
if (value < conv.f)
|
||||
{
|
||||
expval--;
|
||||
conv.f /= 10;
|
||||
}
|
||||
|
||||
// the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
|
||||
uint minwidth = ((expval < 100) && (expval > -100)) ? 4 : 5;
|
||||
|
||||
// in "%g" mode, "prec" is the number of *significant figures* not decimals
|
||||
if (this.flags.adapt_exp)
|
||||
{
|
||||
// do we want to fall-back to "%f" mode?
|
||||
if (value >= 1e-4 && value < 1e6)
|
||||
{
|
||||
this.prec = this.prec > expval ? this.prec - expval - 1 : 0;
|
||||
this.flags.precision = true; // make sure ftoa respects precision
|
||||
// no characters in exponent
|
||||
minwidth = 0;
|
||||
expval = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we use one sigfig for the whole part
|
||||
if (this.prec > 0 && this.flags.precision) this.prec--;
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust width
|
||||
uint fwidth = this.width > minwidth ? this.width - minwidth : 0;
|
||||
|
||||
// if we're padding on the right, DON'T pad the floating part
|
||||
if (this.flags.left && minwidth) fwidth = 0;
|
||||
|
||||
// rescale the float value
|
||||
if (expval) value /= conv.f;
|
||||
|
||||
// output the floating part
|
||||
usz start_idx = this.idx;
|
||||
PrintFlags old = this.flags;
|
||||
this.flags.adapt_exp = false;
|
||||
this.width = fwidth;
|
||||
this.ftoa(negative ? -value : value)?;
|
||||
this.flags = old;
|
||||
|
||||
// output the exponent part
|
||||
if (minwidth)
|
||||
{
|
||||
// output the exponential symbol
|
||||
this.out(this.flags.uppercase ? 'E' : 'e')?;
|
||||
// output the exponent value
|
||||
this.flags = { .zeropad = true, .plus = true };
|
||||
this.width = minwidth - 1;
|
||||
this.prec = 0;
|
||||
this.ntoa((uint128)(expval < 0 ? -expval : expval), expval < 0, 10)?;
|
||||
this.flags = old;
|
||||
// might need to right-pad spaces
|
||||
this.left_adjust(this.idx - start_idx)?;
|
||||
}
|
||||
}
|
||||
|
||||
// internal ftoa for fixed decimal floating point
|
||||
private fn void! Formatter.ftoa(Formatter* this, FloatType value)
|
||||
{
|
||||
char[PRINTF_FTOA_BUFFER_SIZE] buf = void;
|
||||
usz len = 0;
|
||||
const FloatType[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
|
||||
FloatType diff = 0.0;
|
||||
|
||||
// powers of 10
|
||||
|
||||
// test for special values
|
||||
if (value != value)
|
||||
{
|
||||
return this.out_reverse("nan");
|
||||
}
|
||||
if (value < -FloatType.max)
|
||||
{
|
||||
return this.out_reverse("fni-");
|
||||
}
|
||||
if (value > FloatType.max)
|
||||
{
|
||||
if (this.flags.plus)
|
||||
{
|
||||
return this.out_reverse("fni+");
|
||||
}
|
||||
return this.out_reverse("fni");
|
||||
}
|
||||
|
||||
// test for very large values
|
||||
// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
|
||||
if (value > PRINTF_MAX_FLOAT || value < -PRINTF_MAX_FLOAT)
|
||||
{
|
||||
return this.etoa(value);
|
||||
}
|
||||
|
||||
// test for negative
|
||||
bool negative = value < 0;
|
||||
if (negative) value = 0 - value;
|
||||
|
||||
// set default precision, if not set explicitly
|
||||
if (!this.flags.precision) this.prec = PRINTF_DEFAULT_FLOAT_PRECISION;
|
||||
|
||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
||||
while (this.prec > 9)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
this.prec--;
|
||||
}
|
||||
|
||||
// Safe due to 1e9 limit.
|
||||
int whole = (int)value;
|
||||
FloatType tmp = (value - whole) * POW10[this.prec];
|
||||
ulong frac = (ulong)tmp;
|
||||
diff = tmp - frac;
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case diff > 0.5:
|
||||
++frac;
|
||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
||||
if (frac >= POW10[this.prec])
|
||||
{
|
||||
frac = 0;
|
||||
++whole;
|
||||
}
|
||||
case diff < 0.5:
|
||||
break;
|
||||
case !frac && (frac & 1):
|
||||
// if halfway, round up if odd OR if last digit is 0
|
||||
++frac;
|
||||
}
|
||||
if (!this.prec)
|
||||
{
|
||||
diff = value - (FloatType)whole;
|
||||
if ((!(diff < 0.5) || diff > 0.5) && (whole & 1))
|
||||
{
|
||||
// exactly 0.5 and ODD, then round up
|
||||
// 1.5 -> 2, but 2.5 -> 2
|
||||
++whole;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint count = this.prec;
|
||||
// now do fractional part, as an unsigned number
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
--count;
|
||||
buf[len++] = (char)(48 + (frac % 10));
|
||||
}
|
||||
while (frac /= 10);
|
||||
// add extra 0s
|
||||
while (count-- > 0)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
// add decimal
|
||||
buf[len++] = '.';
|
||||
}
|
||||
|
||||
// do whole part, number is reversed
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = (char)(48 + (whole % 10));
|
||||
}
|
||||
while (whole /= 10);
|
||||
|
||||
// pad leading zeros
|
||||
if (!this.flags.left && this.flags.zeropad)
|
||||
{
|
||||
if (this.width && (negative || this.flags.plus || this.flags.space)) this.width--;
|
||||
while (len < this.width)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
char next = {|
|
||||
if (negative) return '-';
|
||||
if (this.flags.plus) return '+';
|
||||
if (this.flags.space) return ' ';
|
||||
return 0;
|
||||
|};
|
||||
if (next)
|
||||
{
|
||||
if (len >= PRINTF_FTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = next;
|
||||
}
|
||||
return this.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
private fn void! Formatter.ntoa(Formatter* this, uint128 value, bool negative, uint base)
|
||||
{
|
||||
char[PRINTF_NTOA_BUFFER_SIZE] buf = void;
|
||||
usz len = 0;
|
||||
|
||||
// no hash for 0 values
|
||||
if (!value) this.flags.hash = false;
|
||||
|
||||
// write if precision != 0 or value is != 0
|
||||
if (!this.flags.precision || value)
|
||||
{
|
||||
char past_10 = (this.flags.uppercase ? 'A' : 'a') - 10;
|
||||
do
|
||||
{
|
||||
if (len >= PRINTF_NTOA_BUFFER_SIZE) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
char digit = (char)(value % base);
|
||||
buf[len++] = digit + (digit < 10 ? '0' : past_10);
|
||||
value /= base;
|
||||
}
|
||||
while (value);
|
||||
}
|
||||
return this.ntoa_format(buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
|
||||
}
|
||||
|
||||
private fn void! Formatter.ntoa_format(Formatter* this, char[] buf, usz len, bool negative, uint base)
|
||||
{
|
||||
// pad leading zeros
|
||||
if (!this.flags.left)
|
||||
{
|
||||
if (this.width && this.flags.zeropad && (negative || this.flags.plus || this.flags.space)) this.width--;
|
||||
while (len < this.prec)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (this.flags.zeropad && len < this.width)
|
||||
{
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (this.flags.hash && base != 10)
|
||||
{
|
||||
if (!this.flags.precision && len && len == this.prec && len == this.width)
|
||||
{
|
||||
len--;
|
||||
if (len) len--;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
if (len + 1 >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
switch (base)
|
||||
{
|
||||
case 16:
|
||||
buf[len++] = this.flags.uppercase ? 'X' : 'x';
|
||||
case 8:
|
||||
buf[len++] = this.flags.uppercase ? 'O' : 'o';
|
||||
case 2:
|
||||
buf[len++] = this.flags.uppercase ? 'B' : 'b';
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch (true)
|
||||
{
|
||||
case negative:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '-';
|
||||
case this.flags.plus:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = '+';
|
||||
case this.flags.space:
|
||||
if (len >= buf.len) return PrintFault.INTERNAL_BUFFER_EXCEEDED!;
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
if (!len) return;
|
||||
return this.out_reverse(buf[:len]);
|
||||
}
|
||||
|
||||
|
||||
private fn void! Formatter.ntoa_variant(Formatter* this, variant arg, uint base)
|
||||
{
|
||||
bool is_neg;
|
||||
uint128 val = int_from_variant(arg, &is_neg);
|
||||
return this.ntoa(val, is_neg, base) @inline;
|
||||
}
|
||||
|
||||
private fn void! Formatter.out_char(Formatter* this, variant arg)
|
||||
{
|
||||
uint l = 1;
|
||||
// pre padding
|
||||
this.right_adjust(l)?;
|
||||
// char output
|
||||
Char32 c = types::variant_to_int(arg, uint) ?? 0xFFFD;
|
||||
switch (true)
|
||||
{
|
||||
case c < 0x7f:
|
||||
this.out((char)c)?;
|
||||
case c < 0x7ff:
|
||||
this.out((char)(0xC0 | c >> 6))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
case c < 0xffff:
|
||||
this.out((char)(0xE0 | c >> 12))?;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
default:
|
||||
this.out((char)(0xF0 | c >> 18))?;
|
||||
this.out((char)(0x80 | (c >> 12 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c >> 6 & 0x3F)))?;
|
||||
this.out((char)(0x80 | (c & 0x3F)))?;
|
||||
}
|
||||
return this.left_adjust(l);
|
||||
}
|
||||
|
||||
|
||||
private fn void! Formatter.out_reverse(Formatter* this, char[] buf)
|
||||
{
|
||||
usz buffer_start_idx = this.idx;
|
||||
usz len = buf.len;
|
||||
// pad spaces up to given width
|
||||
if (!this.flags.left && !this.flags.zeropad)
|
||||
{
|
||||
for (usz i = len; i < this.width; i++)
|
||||
{
|
||||
this.out(' ')?;
|
||||
}
|
||||
}
|
||||
// reverse string
|
||||
while (len) this.out(buf[--len])?;
|
||||
|
||||
// append pad spaces up to given width
|
||||
return this.left_adjust(this.idx - buffer_start_idx);
|
||||
}
|
||||
|
||||
private fn void! printf_advance_format(usz format_len, usz *index_ptr) @inline
|
||||
{
|
||||
usz val = ++(*index_ptr);
|
||||
if (val >= format_len) return FormattingFault.UNTERMINATED_FORMAT!;
|
||||
}
|
||||
|
||||
private fn variant! next_variant(variant* args_ptr, usz args_len, usz* arg_index_ptr) @inline
|
||||
{
|
||||
if (*arg_index_ptr >= args_len) return FormattingFault.MISSING_ARG!;
|
||||
return args_ptr[(*arg_index_ptr)++];
|
||||
}
|
||||
|
||||
private fn int! printf_parse_format_field(variant* args_ptr, usz args_len, usz* args_index_ptr, char* format_ptr, usz format_len, usz* index_ptr) @inline
|
||||
{
|
||||
char c = format_ptr[*index_ptr];
|
||||
if (c >= '0' && c <= '9') return simple_atoi(format_ptr, format_len, index_ptr);
|
||||
if (c != '*') return 0;
|
||||
printf_advance_format(format_len, index_ptr)?;
|
||||
variant val = next_variant(args_ptr, args_len, args_index_ptr)?;
|
||||
if (!val.type.kindof.is_int()) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
uint! intval = types::variant_to_int(val, int);
|
||||
if (catch intval) return FormattingFault.INVALID_WIDTH_ARG!;
|
||||
return intval;
|
||||
}
|
||||
497
lib/std/io/io_printf.c3
Normal file
497
lib/std/io/io_printf.c3
Normal file
@@ -0,0 +1,497 @@
|
||||
module std::io;
|
||||
import std::map;
|
||||
import libc;
|
||||
|
||||
const int PRINTF_NTOA_BUFFER_SIZE = 256;
|
||||
const int PRINTF_FTOA_BUFFER_SIZE = 256;
|
||||
const float PRINTF_MAX_FLOAT = 1e9;
|
||||
const uint PRINTF_DEFAULT_FLOAT_PRECISION = 6;
|
||||
|
||||
fault PrintFault
|
||||
{
|
||||
BUFFER_EXCEEDED,
|
||||
INTERNAL_BUFFER_EXCEEDED,
|
||||
INVALID_FORMAT_STRING,
|
||||
MISSING_ARG,
|
||||
}
|
||||
|
||||
|
||||
define OutputFn = fn void!(char c, void* buffer);
|
||||
define ToStringFunction = fn char[](void* value, Allocator *allocator);
|
||||
define ToFormatFunction = fn void!(void* value, Formatter* formatter);
|
||||
define FloatType = double;
|
||||
|
||||
private define StringFunctionMap = std::map::HashMap<typeid, ToStringFunction>;
|
||||
private define FormatterFunctionMap = std::map::HashMap<typeid, ToFormatFunction>;
|
||||
private FormatterFunctionMap toformat_functions;
|
||||
private StringFunctionMap tostring_functions;
|
||||
|
||||
struct Formatter
|
||||
{
|
||||
void *data;
|
||||
OutputFn out_fn;
|
||||
struct
|
||||
{
|
||||
PrintFlags flags;
|
||||
uint width;
|
||||
uint prec;
|
||||
usz idx;
|
||||
}
|
||||
}
|
||||
|
||||
bitstruct PrintFlags : uint
|
||||
{
|
||||
bool zeropad : 0;
|
||||
bool left : 1;
|
||||
bool plus : 2;
|
||||
bool space : 3;
|
||||
bool hash : 4;
|
||||
bool uppercase : 5;
|
||||
bool precision : 6;
|
||||
bool adapt_exp : 7;
|
||||
}
|
||||
|
||||
fn void Formatter.init(Formatter* this, OutputFn out_fn, void* data = null)
|
||||
{
|
||||
*this = { .data = data, .out_fn = out_fn};
|
||||
}
|
||||
|
||||
/**
|
||||
* @require $checks($Type a, a.to_string()) || $checks($Type a, a.to_format(&&Formatter{})) "Expected a type with 'to_string' or 'to_format' defined"
|
||||
* @require !$checks($Type a, a.to_string()) || $checks($Type a, a.to_string(&&Allocator{})) "Expected 'to_string' to take an allocator as argument."
|
||||
* @require !$checks($Type a, a.to_format(&&Formatter{})) || $checks($Type a, Formatter b, a.to_format(&b)) "Expected 'to_format' to take a Formatter as argument."
|
||||
*/
|
||||
macro void formatter_register_type($Type)
|
||||
{
|
||||
$if ($checks($Type a, a.to_format(&&Formatter {}))):
|
||||
if (!toformat_functions.table.len)
|
||||
{
|
||||
toformat_functions.init(512);
|
||||
}
|
||||
toformat_functions.set($Type.typeid, (ToFormatFunction)&$Type.to_format);
|
||||
$else:
|
||||
if (!tostring_functions.table.len)
|
||||
{
|
||||
tostring_functions.init(512);
|
||||
}
|
||||
tostring_functions.set($Type.typeid, (ToStringFunction)&$Type.to_string);
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
static initialize @priority(101)
|
||||
{
|
||||
if (!toformat_functions.table.len)
|
||||
{
|
||||
toformat_functions.init(512);
|
||||
}
|
||||
if (!tostring_functions.table.len)
|
||||
{
|
||||
tostring_functions.init(512);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void! Formatter.out(Formatter* this, char c)
|
||||
{
|
||||
this.out_fn(c, this.data)?;
|
||||
}
|
||||
|
||||
macro bool! Formatter.print_with_function(Formatter* this, variant arg)
|
||||
{
|
||||
if (try to_format = toformat_functions.get(arg.type))
|
||||
{
|
||||
PrintFlags old = this.flags;
|
||||
uint old_width = this.width;
|
||||
uint old_prec = this.prec;
|
||||
defer
|
||||
{
|
||||
this.flags = old;
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
to_format(arg.ptr, this)?;
|
||||
return true;
|
||||
}
|
||||
if (try to_string = tostring_functions.get(arg.type))
|
||||
{
|
||||
PrintFlags old = this.flags;
|
||||
uint old_width = this.width;
|
||||
uint old_prec = this.prec;
|
||||
defer
|
||||
{
|
||||
this.flags = old;
|
||||
this.width = old_width;
|
||||
this.prec = old_prec;
|
||||
}
|
||||
@pool()
|
||||
{
|
||||
this.out_substr(to_string(arg.ptr, mem::temp_allocator()))?;
|
||||
return true;
|
||||
};
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private fn void! Formatter.out_str(Formatter* this, variant arg)
|
||||
{
|
||||
switch (arg.type.kindof)
|
||||
{
|
||||
case TYPEID:
|
||||
return this.out_substr("<typeid>");
|
||||
case VOID:
|
||||
return this.out_substr("void");
|
||||
case ANYERR:
|
||||
case FAULT:
|
||||
return this.out_substr((*(anyerr*)arg.ptr).nameof);
|
||||
case VARIANT:
|
||||
return this.out_substr("<variant>");
|
||||
case ENUM:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr(arg.type.names[types::variant_to_int(arg, usz)!!]);
|
||||
case STRUCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<struct>");
|
||||
case UNION:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<union>");
|
||||
case BITSTRUCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<bitstruct>");
|
||||
case FUNC:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("<function>");
|
||||
case OPTIONAL:
|
||||
unreachable();
|
||||
case DISTINCT:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
if (arg.type == String.typeid)
|
||||
{
|
||||
return this.out_substr(((String*)arg).str());
|
||||
}
|
||||
return this.out_str(variant { arg.ptr, arg.type.inner });
|
||||
case POINTER:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner.kindof == TypeKind.ARRAY && inner.inner == char.typeid)
|
||||
{
|
||||
char *ptr = *(char**)arg.ptr;
|
||||
return this.out_substr(ptr[:inner.len]);
|
||||
}
|
||||
return this.ntoa_variant(arg, 16);
|
||||
case SIGNED_INT:
|
||||
case UNSIGNED_INT:
|
||||
return this.ntoa_variant(arg, 10);
|
||||
case FLOAT:
|
||||
return this.ftoa(float_from_variant(arg));
|
||||
case ARRAY:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a char[]
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out('[')?;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out(']');
|
||||
case VECTOR:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
// this is SomeType[*] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
usz size = inner.sizeof;
|
||||
usz len = arg.type.len;
|
||||
// Pretend this is a char[]
|
||||
void* ptr = (void*)arg.ptr;
|
||||
this.out_substr("[<")?;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
return this.out_substr(">]");
|
||||
case SUBARRAY:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
// this is SomeType[] so grab the "SomeType"
|
||||
typeid inner = arg.type.inner;
|
||||
if (inner == char.typeid)
|
||||
{
|
||||
return this.out_substr(*(char[]*)arg);
|
||||
}
|
||||
usz size = inner.sizeof;
|
||||
// Pretend this is a char[]
|
||||
char[]* temp = (void*)arg.ptr;
|
||||
void* ptr = (void*)temp.ptr;
|
||||
usz len = temp.len;
|
||||
this.out('[')?;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (i != 0) this.out_substr(", ")?;
|
||||
this.out_str(variant { ptr, inner })?;
|
||||
ptr += size;
|
||||
}
|
||||
this.out(']')?;
|
||||
case BOOL:
|
||||
if (*(bool*)arg.ptr)
|
||||
{
|
||||
return this.out_substr("true");
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.out_substr("false");
|
||||
}
|
||||
default:
|
||||
if (this.print_with_function(arg)?) return;
|
||||
return this.out_substr("Invalid type");
|
||||
}
|
||||
}
|
||||
|
||||
fault FormattingFault
|
||||
{
|
||||
UNTERMINATED_FORMAT,
|
||||
MISSING_ARG,
|
||||
INVALID_WIDTH_ARG,
|
||||
INVALID_FORMAT_TYPE,
|
||||
}
|
||||
|
||||
|
||||
private fn void! out_buffer_fn(char c, void *data)
|
||||
{
|
||||
BufferData *buffer_data = data;
|
||||
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED!;
|
||||
buffer_data.buffer[buffer_data.written++] = c;
|
||||
}
|
||||
|
||||
private fn void! out_null_fn(char c @unused, void* data @unused)
|
||||
{
|
||||
}
|
||||
|
||||
private fn void! out_putchar_fn(char c, void* data @unused)
|
||||
{
|
||||
libc::putchar(c);
|
||||
}
|
||||
|
||||
private fn void! out_fputchar_fn(char c, void* data)
|
||||
{
|
||||
File* f = data;
|
||||
f.putc(c)?;
|
||||
}
|
||||
|
||||
private fn void! out_string_append_fn(char c, void* data)
|
||||
{
|
||||
String* s = data;
|
||||
s.append_char(c);
|
||||
}
|
||||
|
||||
fn usz! printf(char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! printfln(char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
putchar('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn usz! String.printf(String* str, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
return formatter.vprintf(format, args);
|
||||
}
|
||||
|
||||
fn usz! String.printfln(String* str, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_string_append_fn, str);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
str.append('\n');
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
private struct BufferData
|
||||
{
|
||||
char[] buffer;
|
||||
usz written;
|
||||
}
|
||||
|
||||
fn char[]! bprintf(char[] buffer, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
BufferData data = { .buffer = buffer };
|
||||
formatter.init(&out_buffer_fn, &data);
|
||||
usz size = formatter.vprintf(format, args)?;
|
||||
return buffer[:size];
|
||||
}
|
||||
|
||||
fn usz! File.printf(File file, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn, &file);
|
||||
return formatter.vprintf(format, args)?;
|
||||
}
|
||||
|
||||
fn usz! File.printfln(File file, char[] format, args...) @maydiscard
|
||||
{
|
||||
Formatter formatter;
|
||||
formatter.init(&out_putchar_fn, &file);
|
||||
usz len = formatter.vprintf(format, args)?;
|
||||
file.putc('\n')?;
|
||||
file.flush();
|
||||
return len + 1;
|
||||
}
|
||||
|
||||
fn usz! Formatter.printf(Formatter* this, char[] format, args...)
|
||||
{
|
||||
return this.vprintf(format, args) @inline;
|
||||
}
|
||||
|
||||
fn usz! Formatter.vprintf(Formatter* this, char[] format, variant[] variants)
|
||||
{
|
||||
if (!this.out_fn)
|
||||
{
|
||||
// use null output function
|
||||
this.out_fn = &out_null_fn;
|
||||
}
|
||||
usz format_len = format.len;
|
||||
usz variant_index = 0;
|
||||
for (usz i = 0; i < format_len; i++)
|
||||
{
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
char c = format[i];
|
||||
if (c != '%')
|
||||
{
|
||||
// no
|
||||
this.out(c)?;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
c = format[i];
|
||||
if (c == '%')
|
||||
{
|
||||
this.out(c)?;
|
||||
continue;
|
||||
}
|
||||
// evaluate flags
|
||||
this.flags = {};
|
||||
while FLAG_EVAL: (true)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '0': this.flags.zeropad = true;
|
||||
case '-': this.flags.left = true;
|
||||
case '+': this.flags.plus = true;
|
||||
case ' ': this.flags.space = true;
|
||||
case '#': this.flags.hash = true;
|
||||
default: break FLAG_EVAL;
|
||||
}
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
c = format[i];
|
||||
}
|
||||
// evaluate width field
|
||||
int w = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
c = format[i];
|
||||
if (w < 0)
|
||||
{
|
||||
this.flags.left = true;
|
||||
w = -w;
|
||||
}
|
||||
this.width = w;
|
||||
// evaluate precision field
|
||||
this.prec = 0;
|
||||
if (c == '.')
|
||||
{
|
||||
this.flags.precision = true;
|
||||
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING!;
|
||||
int prec = printf_parse_format_field(variants.ptr, variants.len, &variant_index, format.ptr, format.len, &i)?;
|
||||
this.prec = prec < 0 ? 0 : prec;
|
||||
c = format[i];
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
uint base = 0;
|
||||
if (variant_index >= variants.len) return PrintFault.MISSING_ARG!;
|
||||
variant current = variants[variant_index++];
|
||||
switch (c)
|
||||
{
|
||||
case 'd':
|
||||
base = 10;
|
||||
this.flags.hash = false;
|
||||
case 'X' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'x' :
|
||||
base = 16;
|
||||
case 'O':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'o' :
|
||||
base = 8;
|
||||
case 'B':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'b' :
|
||||
base = 2;
|
||||
case 'F' :
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'f':
|
||||
this.ftoa(float_from_variant(current))?;
|
||||
continue;
|
||||
case 'E':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'e':
|
||||
this.etoa(float_from_variant(current))?;
|
||||
continue;
|
||||
case 'G':
|
||||
this.flags.uppercase = true;
|
||||
nextcase;
|
||||
case 'g':
|
||||
this.flags.adapt_exp = true;
|
||||
this.etoa(float_from_variant(current))?;
|
||||
continue;
|
||||
case 'c':
|
||||
this.out_char(current)?;
|
||||
continue;
|
||||
case 's':
|
||||
this.out_str(current)?;
|
||||
continue;
|
||||
case 'p':
|
||||
this.flags.zeropad = true;
|
||||
this.flags.hash = true;
|
||||
base = 16;
|
||||
default:
|
||||
return PrintFault.INVALID_FORMAT_STRING!;
|
||||
}
|
||||
if (base != 10)
|
||||
{
|
||||
this.flags.plus = false;
|
||||
this.flags.space = false;
|
||||
}
|
||||
// ignore '0' flag when precision is given
|
||||
if (this.flags.precision) this.flags.zeropad = false;
|
||||
|
||||
bool is_neg;
|
||||
uint128 v = int_from_variant(current, &is_neg);
|
||||
|
||||
this.ntoa(v, is_neg, base)?;
|
||||
}
|
||||
// termination
|
||||
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return this.idx;
|
||||
}
|
||||
|
||||
47
lib/std/io/os/getcwd.c3
Normal file
47
lib/std/io/os/getcwd.c3
Normal file
@@ -0,0 +1,47 @@
|
||||
module std::io::os;
|
||||
import libc;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
|
||||
extern fn Char16* _wgetcwd(Char16* buffer, int maxlen);
|
||||
extern fn usz wcslen(Char16* str);
|
||||
|
||||
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
|
||||
{
|
||||
const DEFAULT_BUFFER = 256;
|
||||
Char16[DEFAULT_BUFFER] buffer;
|
||||
Char16 *res = _wgetcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
defer if (free) libc::free(res);
|
||||
if (!res)
|
||||
{
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
|
||||
res = _wgetcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
Char16[] str16 = res[:wcslen(res)];
|
||||
return str::utf16to8(str16, allocator);
|
||||
}
|
||||
|
||||
$else:
|
||||
|
||||
extern fn ZString _getcwd(char* pwd, usz len) @extname("getcwd");
|
||||
macro char[]! getcwd(Allocator* allocator = mem::default_allocator())
|
||||
{
|
||||
const usz DEFAULT_BUFFER = 256;
|
||||
char[DEFAULT_BUFFER] buffer;
|
||||
ZString res = _getcwd(&buffer, DEFAULT_BUFFER);
|
||||
bool free = false;
|
||||
if (!res)
|
||||
{
|
||||
// Improve error
|
||||
if (libc::errno() != errno::ERANGE) return IoError.GENERAL_ERROR!;
|
||||
res = _getcwd(null, 0);
|
||||
free = true;
|
||||
}
|
||||
defer if (free) libc::free((void*)res);
|
||||
char[] copy = str::copyz(res.as_str(), allocator);
|
||||
return copy;
|
||||
}
|
||||
|
||||
$endif;
|
||||
355
lib/std/libc.c3
355
lib/std/libc.c3
@@ -1,355 +0,0 @@
|
||||
// Copyright (c) 2021 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 libc;
|
||||
import std::cinterop;
|
||||
import std::env;
|
||||
import std::os::linux;
|
||||
import std::os::macos;
|
||||
import std::os::windows;
|
||||
// stdlib
|
||||
|
||||
|
||||
// Constants need to be per os/arch
|
||||
const int EXIT_FAILURE = 1;
|
||||
const int EXIT_SUCCESS = 0;
|
||||
const int RAND_MAX = 0x7fffffff;
|
||||
|
||||
struct DivResult
|
||||
{
|
||||
int quot;
|
||||
int rem;
|
||||
}
|
||||
|
||||
struct LongDivResult
|
||||
{
|
||||
long quot;
|
||||
long rem;
|
||||
}
|
||||
|
||||
enum Errno : ErrnoType
|
||||
{
|
||||
EPERM = 1, /* Operation not permitted */
|
||||
ENOENT = 2, /* No such file or directory */
|
||||
ESRCH = 3, /* No such process */
|
||||
EINTR = 4, /* Interrupted system call */
|
||||
EIO = 5, /* I/O error */
|
||||
ENXIO = 6, /* No such device or address */
|
||||
E2BIG = 7, /* Argument list too long */
|
||||
ENOEXEC = 8, /* Exec format error */
|
||||
EBADF = 9, /* Bad file number */
|
||||
ECHILD = 10, /* No child processes */
|
||||
EAGAIN = 11, /* Try again */
|
||||
ENOMEM = 12, /* Out of memory */
|
||||
EACCES = 13, /* Permission denied */
|
||||
EFAULT = 14, /* Bad address */
|
||||
ENOTBLK = 15, /* Block device required */
|
||||
EBUSY = 16, /* Device or resource busy */
|
||||
EEXIST = 17, /* File exists */
|
||||
EXDEV = 18, /* Cross-device link */
|
||||
ENODEV = 19, /* No such device */
|
||||
ENOTDIR = 20, /* Not a directory */
|
||||
EISDIR = 21, /* Is a directory */
|
||||
EINVAL = 22, /* Invalid argument */
|
||||
ENFILE = 23, /* File table overflow */
|
||||
EMFILE = 24, /* Too many open files */
|
||||
ENOTTY = 25, /* Not a typewriter */
|
||||
ETXTBSY = 26, /* Text file busy */
|
||||
EFBIG = 27, /* File too large */
|
||||
ENOSPC = 28, /* No space left on device */
|
||||
ESPIPE = 29, /* Illegal seek */
|
||||
EROFS = 30, /* Read-only file system */
|
||||
EMLINK = 31, /* Too many links */
|
||||
EPIPE = 32, /* Broken pipe */
|
||||
EDOM = 33, /* Math argument out of domain of func */
|
||||
ERANGE = 34, /* Math result not representable */
|
||||
EDEADLK = 35, /* Resource deadlock would occur */
|
||||
ENAMETOOLONG = 36, /* File name too long */
|
||||
ENOLCK = 37, /* No record locks available */
|
||||
ENOSYS = 38, /* Function not implemented */
|
||||
ENOTEMPTY = 39, /* Directory not empty */
|
||||
ELOOP = 40, /* Too many symbolic links encountered */
|
||||
|
||||
ENOMSG = 42, /* No message of desired type */
|
||||
EIDRM = 43, /* Identifier removed */
|
||||
ECHRNG = 44, /* Channel number out of range */
|
||||
EL2NSYNC = 45, /* Level 2 not synchronized */
|
||||
EL3HLT = 46, /* Level 3 halted */
|
||||
EL3RST = 47, /* Level 3 reset */
|
||||
ELNRNG = 48, /* Link number out of range */
|
||||
EUNATCH = 49, /* Protocol driver not attached */
|
||||
ENOCSI = 50, /* No CSI structure available */
|
||||
EL2HLT = 51, /* Level 2 halted */
|
||||
EBADE = 52, /* Invalid exchange */
|
||||
EBADR = 53, /* Invalid request descriptor */
|
||||
EXFULL = 54, /* Exchange full */
|
||||
ENOANO = 55, /* No anode */
|
||||
EBADRQC = 56, /* Invalid request code */
|
||||
EBADSLT = 57, /* Invalid slot */
|
||||
|
||||
EBFONT = 59, /* Bad font file format */
|
||||
ENOSTR = 60, /* Device not a stream */
|
||||
ENODATA = 61, /* No data available */
|
||||
ETIME = 62, /* Timer expired */
|
||||
ENOSR = 63, /* Out of streams resources */
|
||||
ENONET = 64, /* Machine is not on the network */
|
||||
ENOPKG = 65, /* Package not installed */
|
||||
EREMOTE = 66, /* Object is remote */
|
||||
ENOLINK = 67, /* Link has been severed */
|
||||
EADV = 68, /* Advertise error */
|
||||
ESRMNT = 69, /* Srmount error */
|
||||
ECOMM = 70, /* Communication error on send */
|
||||
EPROTO = 71, /* Protocol error */
|
||||
EMULTIHOP = 72, /* Multihop attempted */
|
||||
EDOTDOT = 73, /* RFS specific error */
|
||||
EBADMSG = 74, /* Not a data message */
|
||||
EOVERFLOW = 75, /* Value too large for defined data type */
|
||||
ENOTUNIQ = 76, /* Name not unique on network */
|
||||
EBADFD = 77, /* File descriptor in bad state */
|
||||
EREMCHG = 78, /* Remote address changed */
|
||||
ELIBACC = 79, /* Can not access a needed shared library */
|
||||
ELIBBAD = 80, /* Accessing a corrupted shared library */
|
||||
ELIBSCN = 81, /* .lib section in a.out corrupted */
|
||||
ELIBMAX = 82, /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC = 83, /* Cannot exec a shared library directly */
|
||||
EILSEQ = 84, /* Illegal byte sequence */
|
||||
ERESTART = 85, /* Interrupted system call should be restarted */
|
||||
ESTRPIPE = 86, /* Streams pipe error */
|
||||
EUSERS = 87, /* Too many users */
|
||||
ENOTSOCK = 88, /* Socket operation on non-socket */
|
||||
EDESTADDRREQ = 89, /* Destination address required */
|
||||
EMSGSIZE = 90, /* Message too long */
|
||||
EPROTOTYPE = 91, /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT = 92, /* Protocol not available */
|
||||
EPROTONOSUPPORT = 93, /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT = 94, /* Socket type not supported */
|
||||
EOPNOTSUPP = 95, /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT = 96, /* Protocol family not supported */
|
||||
EAFNOSUPPORT = 97, /* Address family not supported by protocol */
|
||||
EADDRINUSE = 98, /* Address already in use */
|
||||
EADDRNOTAVAIL = 99, /* Cannot assign requested address */
|
||||
ENETDOWN = 100, /* Network is down */
|
||||
ENETUNREACH = 101, /* Network is unreachable */
|
||||
ENETRESET = 102, /* Network dropped connection because of reset */
|
||||
ECONNABORTED = 103, /* Software caused connection abort */
|
||||
ECONNRESET = 104, /* Connection reset by peer */
|
||||
ENOBUFS = 105, /* No buffer space available */
|
||||
EISCONN = 106, /* Transport endpoint is already connected */
|
||||
ENOTCONN = 107, /* Transport endpoint is not connected */
|
||||
ESHUTDOWN = 108, /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS = 109, /* Too many references: cannot splice */
|
||||
ETIMEDOUT = 110, /* Connection timed out */
|
||||
ECONNREFUSED = 111, /* Connection refused */
|
||||
EHOSTDOWN = 112, /* Host is down */
|
||||
EHOSTUNREACH = 113, /* No route to host */
|
||||
EALREADY = 114, /* Operation already in progress */
|
||||
EINPROGRESS = 115, /* Operation now in progress */
|
||||
ESTALE = 116, /* Stale NFS file handle */
|
||||
EUCLEAN = 117, /* Structure needs cleaning */
|
||||
ENOTNAM = 118, /* Not a XENIX named type file */
|
||||
ENAVAIL = 119, /* No XENIX semaphores available */
|
||||
EISNAM = 120, /* Is a named type file */
|
||||
EREMOTEIO = 121, /* Remote I/O error */
|
||||
EDQUOT = 122, /* Quota exceeded */
|
||||
|
||||
ENOMEDIUM = 123, /* No medium found */
|
||||
EMEDIUMTYPE = 124, /* Wrong medium type */
|
||||
ECANCELED = 125, /* Operation Canceled */
|
||||
ENOKEY = 126, /* Required key not available */
|
||||
EKEYEXPIRED = 127, /* Key has expired */
|
||||
EKEYREVOKED = 128, /* Key has been revoked */
|
||||
EKEYREJECTED = 129, /* Key was rejected by service */
|
||||
|
||||
EOWNERDEAD = 130, /* Owner died */
|
||||
ENOTRECOVERABLE = 131, /* State not recoverable */
|
||||
}
|
||||
|
||||
fn Errno errno()
|
||||
{
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
return (Errno)windows::errno();
|
||||
$elif (env::OS_TYPE == OsType.MACOSX):
|
||||
return (Errno)macos::errno();
|
||||
$elif (env::OS_TYPE == OsType.LINUX):
|
||||
return (Errno)linux::errno();
|
||||
$else:
|
||||
return Errno.ENOTRECOVERABLE;
|
||||
$endif;
|
||||
}
|
||||
|
||||
|
||||
define TerminateFunction = fn void();
|
||||
define CompareFunction = fn int(void*, void*);
|
||||
extern fn double atof(char* str);
|
||||
extern fn int atoi(char* str);
|
||||
extern fn CLongLong atoll(char* str);
|
||||
extern fn double strtod(char* str, char** endptr);
|
||||
extern fn CLong strtol(char* str, char** endptr, int base);
|
||||
extern fn CULong stroul(char* str, char** endptr, int base);
|
||||
extern fn void abort();
|
||||
extern fn void atexit(TerminateFunction f);
|
||||
extern fn void exit(int status);
|
||||
extern fn char* getenv(char* name);
|
||||
extern fn int system(char* str);
|
||||
extern fn void bsearch(void* key, void *base, usize items, usize size, CompareFunction compare);
|
||||
extern fn void qsort(void* base, usize items, usize size, CompareFunction compare);
|
||||
extern fn int abs(int x);
|
||||
extern fn DivResult div(int numer, int denom);
|
||||
extern fn long labs(long x);
|
||||
extern fn LongDivResult ldiv(long number, long denom);
|
||||
extern fn int rand();
|
||||
extern fn void srand(uint seed);
|
||||
|
||||
// MB functions omitted
|
||||
|
||||
// string
|
||||
extern fn void* memchr(void* str, int c, usize n);
|
||||
extern fn int memcmp(void* str1, void* str2, usize n);
|
||||
extern fn void* memcpy(void* dest, void* src, usize n);
|
||||
extern fn void* memmove(void* dest, void* src, usize n);
|
||||
extern fn void* memset(void* dest, usize n);
|
||||
extern fn char* strcat(char* dest, char* src);
|
||||
extern fn char* strncat(char* dest, char* src, usize n);
|
||||
extern fn char* strchr(char* str, int c);
|
||||
extern fn int strcmp(char* str1, char* str2);
|
||||
extern fn int strncmp(char* str1, char* str2, usize n);
|
||||
extern fn int strcoll(char* str1, char* str2);
|
||||
extern fn char* strcpy(char* dst, char* src);
|
||||
extern fn char* strncpy(char* dst, char* src, usize n);
|
||||
extern fn usize strcspn(char* str1, char* str2);
|
||||
extern fn char* strerror(int errn);
|
||||
extern fn usize strlen(char* str);
|
||||
extern fn char* strpbrk(char* str1, char* str2);
|
||||
extern fn usize strspn(char* str1, char* str2);
|
||||
extern fn char* strstr(char* haystack, char* needle);
|
||||
extern fn char* strtok(char* str, char* delim);
|
||||
extern fn usize strxfrm(char* dest, char* src, usize n);
|
||||
|
||||
// malloc
|
||||
extern fn void* malloc(usize size);
|
||||
extern fn void* calloc(usize count, usize size);
|
||||
extern fn void* free(void*);
|
||||
extern fn void* realloc(void* ptr, usize size);
|
||||
|
||||
// stdio
|
||||
|
||||
define Fpos = long;
|
||||
define CFile = void*;
|
||||
$switch (env::OS_TYPE):
|
||||
$case OsType.LINUX:
|
||||
extern CFile __stdin @extname("stdin");
|
||||
extern CFile __stdout @extname("stdout");
|
||||
extern CFile __stderr @extname("stderr");
|
||||
macro CFile stdin() { return __stdin; }
|
||||
macro CFile stdout() { return __stdout; }
|
||||
macro CFile stderr() { return __stderr; }
|
||||
$case OsType.MACOSX:
|
||||
extern CFile __stdinp;
|
||||
extern CFile __stdoutp;
|
||||
extern CFile __stderrp;
|
||||
macro CFile stdin() { return __stdinp; }
|
||||
macro CFile stdout() { return __stdoutp; }
|
||||
macro CFile stderr() { return __stderrp; }
|
||||
$case OsType.WIN32:
|
||||
extern fn CFile __acrt_iob_func(CInt c);
|
||||
macro CFile stdin() { return __acrt_iob_func(0); }
|
||||
macro CFile stdout() { return __acrt_iob_func(1); }
|
||||
macro CFile stderr() { return __acrt_iob_func(2); }
|
||||
$default:
|
||||
$endswitch;
|
||||
|
||||
// The following needs to be set per arch+os
|
||||
// For now I have simply pulled the defaults from MacOS
|
||||
const int SEEK_SET = 0;
|
||||
const int SEEK_CUR = 1;
|
||||
const int SEEK_END = 2;
|
||||
const int _IOFBF = 0; // Fully buffered
|
||||
const int _IOLBF = 1; // Line buffered
|
||||
const int _IONBF = 2; // Unbuffered
|
||||
const int BUFSIZ = 1024;
|
||||
const int EOF = -1;
|
||||
const int FOPEN_MAX = 20;
|
||||
const int FILENAME_MAX = 1024;
|
||||
|
||||
define ErrnoType = CInt;
|
||||
define SeekIndex = CLong;
|
||||
|
||||
extern fn int fclose(CFile stream);
|
||||
extern fn void clearerr(CFile stream);
|
||||
extern fn int feof(CFile stream);
|
||||
extern fn int ferror(CFile stream);
|
||||
extern fn int fflush(CFile stream);
|
||||
extern fn int fgetpos(CFile stream, Fpos* pos);
|
||||
extern fn CFile fopen(char* filename, char* mode);
|
||||
extern fn usize fread(void* ptr, usize size, usize nmemb, CFile stream);
|
||||
extern fn CFile freopen(char* filename, char* mode, CFile stream);
|
||||
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
|
||||
extern fn int fsetpos(CFile stream, Fpos* pos);
|
||||
extern fn SeekIndex ftell(CFile stream);
|
||||
extern fn usize fwrite(void* ptr, usize size, usize nmemb, CFile stream);
|
||||
extern fn int remove(char* filename);
|
||||
extern fn int rename(char* old_name, char* new_name);
|
||||
extern fn void rewind(CFile stream);
|
||||
extern fn void setbuf(CFile stream, char* buffer);
|
||||
extern fn void setvbuf(CFile stream, char* buffer, int mode, usize size);
|
||||
extern fn CFile tmpnam(char* str);
|
||||
extern fn int fprintf(CFile stream, char* format, ...);
|
||||
extern fn int printf(char* format, ...);
|
||||
extern fn int sprintf(char* str, char* format, ...);
|
||||
extern fn int snprintf(char* str, usize size, char* format, ...);
|
||||
extern fn int fscanf(CFile stream, char* format, ...);
|
||||
extern fn int scanf(char* format, ...);
|
||||
extern fn int sscanf(char* str, char* format, ...);
|
||||
extern fn int fgetc(CFile stream);
|
||||
extern fn char* fgets(char* str, int n, CFile stream);
|
||||
extern fn int fputc(int c, CFile stream);
|
||||
extern fn int getc(CFile stream);
|
||||
extern fn int getchar();
|
||||
extern fn int putc(char c, CFile stream);
|
||||
extern fn int putchar(int c);
|
||||
extern fn int puts(char* str);
|
||||
extern fn int ungetc(int c, CFile stream);
|
||||
extern fn void perror(char* str);
|
||||
|
||||
// vsprintf vprintf not supported
|
||||
|
||||
// time.h
|
||||
|
||||
define TimeOffset = CLong;
|
||||
|
||||
struct Tm
|
||||
{
|
||||
int tm_sec; /* seconds after the minute [0-60] */
|
||||
int tm_min; /* minutes after the hour [0-59] */
|
||||
int tm_hour; /* hours since midnight [0-23] */
|
||||
int tm_mday; /* day of the month [1-31] */
|
||||
int tm_mon; /* months since January [0-11] */
|
||||
int tm_year; /* years since 1900 */
|
||||
int tm_wday; /* days since Sunday [0-6] */
|
||||
int tm_yday; /* days since January 1 [0-365] */
|
||||
int tm_isdst; /* Daylight Savings Time flag */
|
||||
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
|
||||
char *tm_zone; /* timezone abbreviation */
|
||||
}
|
||||
|
||||
// Likely wrong, must be per platform.
|
||||
const CLOCKS_PER_SEC = 1000000;
|
||||
|
||||
// Time also needs to be per platform
|
||||
define Time = long;
|
||||
define Clock = ulong;
|
||||
|
||||
extern fn char* asctime(Tm *timeptr);
|
||||
extern fn Clock clock();
|
||||
extern fn char* ctime(Time *timer);
|
||||
extern fn double difftime(Time time1, Time time2);
|
||||
extern fn Tm* gmtime(Time *timer);
|
||||
extern fn Tm* localtime(Time *timer);
|
||||
extern fn Time mktime(Tm *timeptr);
|
||||
extern fn usize strftime(char* str, usize maxsize, char* format, Tm *timeptr);
|
||||
extern fn Time time(Time *timer);
|
||||
|
||||
// signal
|
||||
define SignalFunction = fn void(int);
|
||||
extern fn SignalFunction signal(int sig, SignalFunction function);
|
||||
// Incomplete
|
||||
428
lib/std/libc/libc.c3
Normal file
428
lib/std/libc/libc.c3
Normal file
@@ -0,0 +1,428 @@
|
||||
// Copyright (c) 2021 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 libc;
|
||||
|
||||
|
||||
// stdlib
|
||||
|
||||
|
||||
// Constants need to be per os/arch
|
||||
const int EXIT_FAILURE = 1;
|
||||
const int EXIT_SUCCESS = 0;
|
||||
const int RAND_MAX = 0x7fffffff;
|
||||
|
||||
struct DivResult
|
||||
{
|
||||
int quot;
|
||||
int rem;
|
||||
}
|
||||
|
||||
struct LongDivResult
|
||||
{
|
||||
long quot;
|
||||
long rem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn Errno errno()
|
||||
{
|
||||
return (Errno)os::errno();
|
||||
}
|
||||
|
||||
fn void errno_set(Errno e)
|
||||
{
|
||||
os::errno_set((int)e);
|
||||
}
|
||||
|
||||
define TerminateFunction = fn void();
|
||||
define CompareFunction = fn int(void*, void*);
|
||||
extern fn double atof(char* str);
|
||||
extern fn int atoi(char* str);
|
||||
extern fn CLongLong atoll(char* str);
|
||||
extern fn double strtod(char* str, char** endptr);
|
||||
extern fn CLong strtol(char* str, char** endptr, int base);
|
||||
extern fn CULong stroul(char* str, char** endptr, int base);
|
||||
extern fn void abort();
|
||||
extern fn void atexit(TerminateFunction f);
|
||||
extern fn void exit(int status);
|
||||
extern fn char* getenv(char* name);
|
||||
extern fn int system(char* str);
|
||||
extern fn void bsearch(void* key, void *base, usz items, usz size, CompareFunction compare);
|
||||
extern fn void qsort(void* base, usz items, usz size, CompareFunction compare);
|
||||
extern fn int abs(int x);
|
||||
extern fn DivResult div(int numer, int denom);
|
||||
extern fn long labs(long x);
|
||||
extern fn LongDivResult ldiv(long number, long denom);
|
||||
extern fn int rand();
|
||||
extern fn void srand(uint seed);
|
||||
|
||||
define JmpBuf = CInt[$$JMP_BUF_SIZE];
|
||||
|
||||
extern fn void longjmp(JmpBuf* buffer, CInt value);
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
// TODO win32 aarch64
|
||||
extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer);
|
||||
macro CInt setjmp(JmpBuf* buffer) = _setjmp($$frameaddress(), buffer);
|
||||
$else:
|
||||
extern fn CInt setjmp(JmpBuf* buffer);
|
||||
$endif;
|
||||
// MB functions omitted
|
||||
|
||||
// string
|
||||
extern fn void* memchr(void* str, int c, usz n);
|
||||
extern fn int memcmp(void* str1, void* str2, usz n);
|
||||
extern fn void* memcpy(void* dest, void* src, usz n);
|
||||
extern fn void* memmove(void* dest, void* src, usz n);
|
||||
extern fn void* memset(void* dest, usz n);
|
||||
extern fn char* strcat(char* dest, char* src);
|
||||
extern fn char* strncat(char* dest, char* src, usz n);
|
||||
extern fn char* strchr(char* str, int c);
|
||||
extern fn int strcmp(char* str1, char* str2);
|
||||
extern fn int strncmp(char* str1, char* str2, usz n);
|
||||
extern fn int strcoll(char* str1, char* str2);
|
||||
extern fn char* strcpy(char* dst, char* src);
|
||||
extern fn char* strncpy(char* dst, char* src, usz n);
|
||||
extern fn usz strcspn(char* str1, char* str2);
|
||||
extern fn char* strerror(int errn);
|
||||
extern fn usz strlen(char* str);
|
||||
extern fn char* strpbrk(char* str1, char* str2);
|
||||
extern fn usz strspn(char* str1, char* str2);
|
||||
extern fn char* strstr(char* haystack, char* needle);
|
||||
extern fn char* strtok(char* str, char* delim);
|
||||
extern fn usz strxfrm(char* dest, char* src, usz n);
|
||||
|
||||
// malloc
|
||||
extern fn void* malloc(usz size);
|
||||
extern fn void* calloc(usz count, usz size);
|
||||
extern fn void* free(void*);
|
||||
extern fn void* realloc(void* ptr, usz size);
|
||||
|
||||
// stdio
|
||||
|
||||
define Fpos = long;
|
||||
define CFile = void*;
|
||||
|
||||
$switch (env::OS_TYPE):
|
||||
$case OsType.LINUX:
|
||||
extern CFile __stdin @extname("stdin");
|
||||
extern CFile __stdout @extname("stdout");
|
||||
extern CFile __stderr @extname("stderr");
|
||||
extern fn usz malloc_usable_size(void* ptr);
|
||||
macro usz malloc_size(void* ptr) { return malloc_usable_size(ptr); }
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return __stdin; }
|
||||
macro CFile stdout() { return __stdout; }
|
||||
macro CFile stderr() { return __stderr; }
|
||||
$case OsType.MACOSX:
|
||||
extern CFile __stdinp;
|
||||
extern CFile __stdoutp;
|
||||
extern CFile __stderrp;
|
||||
extern fn usz malloc_size(void* ptr);
|
||||
extern fn void* aligned_alloc(usz align, usz size);
|
||||
macro CFile stdin() { return __stdinp; }
|
||||
macro CFile stdout() { return __stdoutp; }
|
||||
macro CFile stderr() { return __stderrp; }
|
||||
$case OsType.WIN32:
|
||||
extern fn CFile __acrt_iob_func(CInt c);
|
||||
extern fn usz _msize(void* ptr);
|
||||
macro usz malloc_size(void* ptr) { return _msize(ptr); }
|
||||
macro CFile stdin() { return __acrt_iob_func(0); }
|
||||
macro CFile stdout() { return __acrt_iob_func(1); }
|
||||
macro CFile stderr() { return __acrt_iob_func(2); }
|
||||
$default:
|
||||
macro CFile stdin() { return (CFile*)(uptr)0; }
|
||||
macro CFile stdout() { return (CFile*)(uptr)1; }
|
||||
macro CFile stderr() { return (CFile*)(uptr)2; }
|
||||
$endswitch;
|
||||
|
||||
|
||||
const HAS_MALLOC_SIZE =
|
||||
env::OS_TYPE == OsType.LINUX
|
||||
|| env::OS_TYPE == OsType.WIN32
|
||||
|| env::OS_TYPE == OsType.MACOSX;
|
||||
|
||||
// The following needs to be set per arch+os
|
||||
// For now I have simply pulled the defaults from MacOS
|
||||
const int SEEK_SET = 0;
|
||||
const int SEEK_CUR = 1;
|
||||
const int SEEK_END = 2;
|
||||
const int _IOFBF = 0; // Fully buffered
|
||||
const int _IOLBF = 1; // Line buffered
|
||||
const int _IONBF = 2; // Unbuffered
|
||||
const int BUFSIZ = 1024;
|
||||
const int EOF = -1;
|
||||
const int FOPEN_MAX = 20;
|
||||
const int FILENAME_MAX = 1024;
|
||||
|
||||
define Errno = distinct CInt;
|
||||
define SeekIndex = CLong;
|
||||
|
||||
extern fn int fclose(CFile stream);
|
||||
extern fn void clearerr(CFile stream);
|
||||
extern fn int feof(CFile stream);
|
||||
extern fn int ferror(CFile stream);
|
||||
extern fn int fflush(CFile stream);
|
||||
extern fn int fgetpos(CFile stream, Fpos* pos);
|
||||
extern fn CFile fopen(char* filename, char* mode);
|
||||
extern fn usz fread(void* ptr, usz size, usz nmemb, CFile stream);
|
||||
extern fn CFile freopen(char* filename, char* mode, CFile stream);
|
||||
extern fn int fseek(CFile stream, SeekIndex offset, int whence);
|
||||
extern fn int fsetpos(CFile stream, Fpos* pos);
|
||||
extern fn SeekIndex ftell(CFile stream);
|
||||
extern fn usz fwrite(void* ptr, usz size, usz nmemb, CFile stream);
|
||||
extern fn int remove(char* filename);
|
||||
extern fn int rename(char* old_name, char* new_name);
|
||||
extern fn void rewind(CFile stream);
|
||||
extern fn void setbuf(CFile stream, char* buffer);
|
||||
extern fn void setvbuf(CFile stream, char* buffer, int mode, usz size);
|
||||
extern fn CFile tmpnam(char* str);
|
||||
extern fn int fprintf(CFile stream, char* format, ...);
|
||||
extern fn int printf(char* format, ...);
|
||||
extern fn int sprintf(char* str, char* format, ...);
|
||||
extern fn int snprintf(char* str, usz size, char* format, ...);
|
||||
extern fn int fscanf(CFile stream, char* format, ...);
|
||||
extern fn int scanf(char* format, ...);
|
||||
extern fn int sscanf(char* str, char* format, ...);
|
||||
extern fn int fgetc(CFile stream);
|
||||
extern fn char* fgets(char* str, int n, CFile stream);
|
||||
extern fn int fputc(int c, CFile stream);
|
||||
extern fn int getc(CFile stream);
|
||||
extern fn int getchar();
|
||||
extern fn int putc(char c, CFile stream);
|
||||
extern fn int putchar(int c);
|
||||
extern fn int puts(char* str);
|
||||
extern fn int ungetc(int c, CFile stream);
|
||||
extern fn void perror(char* str);
|
||||
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
||||
|
||||
// vsprintf vprintf not supported
|
||||
|
||||
// time.h
|
||||
|
||||
define TimeOffset = CLong;
|
||||
|
||||
struct Tm
|
||||
{
|
||||
int tm_sec; /* seconds after the minute [0-60] */
|
||||
int tm_min; /* minutes after the hour [0-59] */
|
||||
int tm_hour; /* hours since midnight [0-23] */
|
||||
int tm_mday; /* day of the month [1-31] */
|
||||
int tm_mon; /* months since January [0-11] */
|
||||
int tm_year; /* years since 1900 */
|
||||
int tm_wday; /* days since Sunday [0-6] */
|
||||
int tm_yday; /* days since January 1 [0-365] */
|
||||
int tm_isdst; /* Daylight Savings Time flag */
|
||||
TimeOffset tm_gmtoff; /* offset from UTC in seconds */
|
||||
char *tm_zone; /* timezone abbreviation */
|
||||
}
|
||||
|
||||
// Likely wrong, must be per platform.
|
||||
const CLOCKS_PER_SEC = 1000000;
|
||||
|
||||
// Time also needs to be per platform
|
||||
define Time = long;
|
||||
define Clock = ulong;
|
||||
|
||||
extern fn char* asctime(Tm *timeptr);
|
||||
extern fn Clock clock();
|
||||
extern fn char* ctime(Time *timer);
|
||||
extern fn double difftime(Time time1, Time time2);
|
||||
extern fn Tm* gmtime(Time *timer);
|
||||
extern fn Tm* localtime(Time *timer);
|
||||
extern fn Time mktime(Tm *timeptr);
|
||||
extern fn usz strftime(char* str, usz maxsize, char* format, Tm *timeptr);
|
||||
extern fn Time time(Time *timer);
|
||||
|
||||
// signal
|
||||
define SignalFunction = fn void(int);
|
||||
extern fn SignalFunction signal(int sig, SignalFunction function);
|
||||
// Incomplete
|
||||
|
||||
|
||||
module libc::errno;
|
||||
|
||||
const Errno EPERM = 1; // Operation not permitted
|
||||
const Errno ENOENT = 2; // No such file or directory
|
||||
const Errno ESRCH = 3; // No such process
|
||||
const Errno EINTR = 4; // Interrupted system call
|
||||
const Errno EIO = 5; // I/O error
|
||||
const Errno ENXIO = 6; // No such device or address
|
||||
const Errno E2BIG = 7; // Argument list too long
|
||||
const Errno ENOEXEC = 8; // Exec format error
|
||||
const Errno EBADF = 9; // Bad file number
|
||||
const Errno ECHILD = 10; // No child processes
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
const Errno EAGAIN = 35; // Try again Macos
|
||||
$else:
|
||||
const Errno EAGAIN = 11; // Try again
|
||||
$endif;
|
||||
|
||||
const Errno ENOMEM = 12; // Out of memory
|
||||
const Errno EACCES = 13; // Permission denied
|
||||
const Errno EFAULT = 14; // Bad address
|
||||
const Errno ENOTBLK = 15; // Block device required, not on Win32
|
||||
const Errno EBUSY = 16; // Device or resource busy
|
||||
const Errno EEXIST = 17; // File exists
|
||||
const Errno EXDEV = 18; // Cross-device link
|
||||
const Errno ENODEV = 19; // No such device
|
||||
const Errno ENOTDIR = 20; // Not a directory
|
||||
const Errno EISDIR = 21; // Is a directory
|
||||
const Errno EINVAL = 22; // Invalid argument
|
||||
const Errno ENFILE = 23; // File table overflow
|
||||
const Errno EMFILE = 24; // Too many open files
|
||||
const Errno ENOTTY = 25; // Not a typewriter
|
||||
const Errno ETXTBSY = 26; // Text file busy, not on Win32
|
||||
const Errno EFBIG = 27; // File too large
|
||||
const Errno ENOSPC = 28; // No space left on device
|
||||
const Errno ESPIPE = 29; // Illegal seek
|
||||
const Errno EROFS = 30; // Read-only file system
|
||||
const Errno EMLINK = 31; // Too many links
|
||||
const Errno EPIPE = 32; // Broken pipe
|
||||
const Errno EDOM = 33; // Math argument out of domain of func
|
||||
const Errno ERANGE = 34; // Math result not representable
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
const Errno EDEADLK = 11; // Resource deadlock would occur MacOS
|
||||
const Errno ENAMETOOLONG = 63; // File name too long MacOS
|
||||
const Errno ELOOP = 62; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 84; // Value too large for defined data type Macos
|
||||
const Errno ECONNRESET = 54; // Connection reset by peer Macos
|
||||
const Errno ENETDOWN = 50; // Network is down MacOS
|
||||
const Errno ENETUNREACH = 51; // Network is unreachable MacOS
|
||||
const Errno ENETRESET = 52; // Network dropped connection because of reset MacOS
|
||||
|
||||
$elif (env::OS_TYPE == OsType.WIN32):
|
||||
const Errno EDEADLK = 36; // Resource deadlock would occur Win32
|
||||
const Errno ENAMETOOLONG = 38; // File name too long Win32
|
||||
const Errno ELOOP = 114; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 132; // Value too large for defined data type
|
||||
const Errno ENETDOWN = 116; // Network is down
|
||||
const Errno ECONNRESET = 108; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 118; // Network is unreachable
|
||||
const Errno ENETRESET = 117; // Network dropped connection because of reset
|
||||
|
||||
$else:
|
||||
const Errno EDEADLK = 35; // Resource deadlock would occur Linux (others?)
|
||||
const Errno ENAMETOOLONG = 36; // File name too long Linux (others?)
|
||||
const Errno ELOOP = 40; // Too many symbolic links encountered
|
||||
const Errno EOVERFLOW = 75; // Value too large for defined data type
|
||||
const Errno ENETDOWN = 100; // Network is down
|
||||
const Errno ECONNRESET = 104; // Connection reset by peer
|
||||
const Errno ENETUNREACH = 101; // Network is unreachable
|
||||
const Errno ENETRESET = 102; // Network dropped connection because of reset
|
||||
|
||||
$endif;
|
||||
|
||||
|
||||
/*
|
||||
const Errno ENOLCK = 37; /* No record locks available */
|
||||
const Errno ENOSYS = 38; /* Function not implemented */
|
||||
const Errno ENOTEMPTY = 39; /* Directory not empty */
|
||||
|
||||
const Errno ENOMSG = 42; /* No message of desired type */
|
||||
const Errno EIDRM = 43; /* Identifier removed */
|
||||
const Errno ECHRNG = 44; /* Channel number out of range */
|
||||
const Errno EL2NSYNC = 45; /* Level 2 not synchronized */
|
||||
const Errno EL3HLT = 46; /* Level 3 halted */
|
||||
const Errno EL3RST = 47; /* Level 3 reset */
|
||||
const Errno ELNRNG = 48; /* Link number out of range */
|
||||
const Errno EUNATCH = 49; /* Protocol driver not attached */
|
||||
const Errno ENOCSI = 50; /* No CSI structure available */
|
||||
const Errno EL2HLT = 51; /* Level 2 halted */
|
||||
const Errno EBADE = 52; /* Invalid exchange */
|
||||
const Errno EBADR = 53; /* Invalid request descriptor */
|
||||
const Errno EXFULL = 54; /* Exchange full */
|
||||
const Errno ENOANO = 55; /* No anode */
|
||||
const Errno EBADRQC = 56; /* Invalid request code */
|
||||
const Errno EBADSLT = 57; /* Invalid slot */
|
||||
|
||||
const Errno EBFONT = 59; /* Bad font file format */
|
||||
const Errno ENOSTR = 60; /* Device not a stream */
|
||||
const Errno ENODATA = 61; /* No data available */
|
||||
const Errno ETIME = 62; /* Timer expired */
|
||||
const Errno ENOSR = 63; /* Out of streams resources */
|
||||
const Errno ENONET = 64; /* Machine is not on the network */
|
||||
const Errno ENOPKG = 65; /* Package not installed */
|
||||
const Errno EREMOTE = 66; /* Object is remote */
|
||||
const Errno ENOLINK = 67; /* Link has been severed */
|
||||
const Errno EADV = 68; /* Advertise error */
|
||||
const Errno ESRMNT = 69; /* Srmount error */
|
||||
const Errno ECOMM = 70; /* Communication error on send */
|
||||
const Errno EPROTO = 71; /* Protocol error */
|
||||
const Errno EMULTIHOP = 72; /* Multihop attempted */
|
||||
const Errno EDOTDOT = 73; /* RFS specific error */
|
||||
const Errno EBADMSG = 74; /* Not a data message */
|
||||
const Errno ENOTUNIQ = 76; /* Name not unique on network */
|
||||
const Errno EBADFD = 77; /* File descriptor in bad state */
|
||||
const Errno EREMCHG = 78; /* Remote address changed */
|
||||
const Errno ELIBACC = 79; /* Can not access a needed shared library */
|
||||
const Errno ELIBBAD = 80; /* Accessing a corrupted shared library */
|
||||
const Errno ELIBSCN = 81; /* .lib section in a.out corrupted */
|
||||
const Errno ELIBMAX = 82; /* Attempting to link in too many shared libraries */
|
||||
const Errno ELIBEXEC = 83; /* Cannot exec a shared library directly */
|
||||
const Errno EILSEQ = 84; /* Illegal byte sequence */
|
||||
const Errno ERESTART = 85; /* Interrupted system call should be restarted */
|
||||
const Errno ESTRPIPE = 86; /* Streams pipe error */
|
||||
const Errno EUSERS = 87; /* Too many users */
|
||||
const Errno ENOTSOCK = 88; /* Socket operation on non-socket */
|
||||
const Errno EDESTADDRREQ = 89; /* Destination address required */
|
||||
const Errno EMSGSIZE = 90; /* Message too long */
|
||||
const Errno EPROTOTYPE = 91; /* Protocol wrong type for socket */
|
||||
const Errno ENOPROTOOPT = 92; /* Protocol not available */
|
||||
const Errno EPROTONOSUPPORT = 93; /* Protocol not supported */
|
||||
const Errno ESOCKTNOSUPPORT = 94; /* Socket type not supported */
|
||||
const Errno EOPNOTSUPP = 95; /* Operation not supported on transport endpoint */
|
||||
const Errno EPFNOSUPPORT = 96; /* Protocol family not supported */
|
||||
const Errno EAFNOSUPPORT = 97; /* Address family not supported by protocol */
|
||||
const Errno EADDRINUSE = 98; /* Address already in use */
|
||||
const Errno EADDRNOTAVAIL = 99; /* Cannot assign requested address */
|
||||
const Errno ECONNABORTED = 103; /* Software caused connection abort */
|
||||
const Errno ENOBUFS = 105; /* No buffer space available */
|
||||
const Errno EISCONN = 106; /* Transport endpoint is already connected */
|
||||
const Errno ENOTCONN = 107; /* Transport endpoint is not connected */
|
||||
const Errno ESHUTDOWN = 108; /* Cannot send after transport endpoint shutdown */
|
||||
const Errno ETOOMANYREFS = 109; /* Too many references: cannot splice */
|
||||
const Errno ETIMEDOUT = 110; /* Connection timed out */
|
||||
const Errno ECONNREFUSED = 111; /* Connection refused */
|
||||
const Errno EHOSTDOWN = 112; /* Host is down */
|
||||
const Errno EHOSTUNREACH = 113; /* No route to host */
|
||||
*/
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
const Errno EINPROGRESS = 36; // Operation now in progress MacOS
|
||||
const Errno EALREADY = 37; // Operation already in progress MacOS
|
||||
const Errno EDQUOT = 69; // Quota exceeded, MacOS
|
||||
|
||||
$elif (env::OS_TYPE == OsType.WIN32):
|
||||
const Errno EALREADY = 103; // Operation already in progress
|
||||
const Errno EINPROGRESS = 112; // Operation now in progress Win32
|
||||
const Errno EDQUOT = -122; // Quota exceeded, not in Win32
|
||||
|
||||
$else:
|
||||
const Errno EALREADY = 114; // Operation already in progress
|
||||
const Errno EINPROGRESS = 115; // Operation now in progress
|
||||
const Errno EDQUOT = 122; // Quota exceeded
|
||||
$endif;
|
||||
|
||||
/*
|
||||
const Errno ESTALE = 116; /* Stale NFS file handle */
|
||||
const Errno EUCLEAN = 117; /* Structure needs cleaning */
|
||||
const Errno ENOTNAM = 118; /* Not a XENIX named type file */
|
||||
const Errno ENAVAIL = 119; /* No XENIX semaphores available */
|
||||
const Errno EISNAM = 120; /* Is a named type file */
|
||||
const Errno EREMOTEIO = 121; /* Remote I/O error */
|
||||
|
||||
const Errno ENOMEDIUM = 123; /* No medium found */
|
||||
const Errno EMEDIUMTYPE = 124; /* Wrong medium type */
|
||||
const Errno ECANCELED = 125; /* Operation Canceled */
|
||||
const Errno ENOKEY = 126; /* Required key not available */
|
||||
const Errno EKEYEXPIRED = 127; /* Key has expired */
|
||||
const Errno EKEYREVOKED = 128; /* Key has been revoked */
|
||||
const Errno EKEYREJECTED = 129; /* Key was rejected by service */
|
||||
|
||||
const Errno EOWNERDEAD = 130; /* Owner died */
|
||||
const Errno ENOTRECOVERABLE = 131; /* State not recoverable */
|
||||
*/
|
||||
|
||||
37
lib/std/libc/os/errno.c3
Normal file
37
lib/std/libc/os/errno.c3
Normal file
@@ -0,0 +1,37 @@
|
||||
module libc::os;
|
||||
|
||||
$switch (env::OS_TYPE):
|
||||
|
||||
$case LINUX:
|
||||
|
||||
extern fn int* __errno_location();
|
||||
|
||||
macro int errno() = *__errno_location();
|
||||
macro void errno_set(int err) = *(__errno_location()) = err;
|
||||
|
||||
$case MACOSX:
|
||||
|
||||
extern fn int* __error();
|
||||
macro int errno() = *__error();
|
||||
macro void errno_set(int err) = *(__error()) = err;
|
||||
|
||||
$case WIN32:
|
||||
|
||||
macro int errno()
|
||||
{
|
||||
int holder;
|
||||
_get_errno(&holder);
|
||||
return holder;
|
||||
}
|
||||
|
||||
macro void errno_set(int err) = _set_errno(err);
|
||||
|
||||
extern fn void _get_errno(int* result);
|
||||
extern fn void _set_errno(int err);
|
||||
|
||||
$default:
|
||||
|
||||
macro int errno() = 1;
|
||||
fn void errno_set(int err) {}
|
||||
|
||||
$endswitch;
|
||||
@@ -2,7 +2,6 @@
|
||||
// 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::array::linkedlist<Type>;
|
||||
import std::mem;
|
||||
|
||||
private struct Node
|
||||
{
|
||||
@@ -13,7 +12,7 @@ private struct Node
|
||||
|
||||
struct LinkedList
|
||||
{
|
||||
usize size;
|
||||
usz size;
|
||||
Node *first;
|
||||
Node *last;
|
||||
}
|
||||
@@ -26,7 +25,7 @@ fn void LinkedList.push(LinkedList *list, Type value)
|
||||
private fn void LinkedList.linkFirst(LinkedList *list, Type value)
|
||||
{
|
||||
Node *first = list.first;
|
||||
Node *new_node = @mem::malloc(Node);
|
||||
Node *new_node = mem::alloc(Node);
|
||||
*new_node = { .next = first, .value = value };
|
||||
list.first = new_node;
|
||||
if (!first)
|
||||
@@ -43,7 +42,7 @@ private fn void LinkedList.linkFirst(LinkedList *list, Type value)
|
||||
private fn void LinkedList.linkLast(LinkedList *list, Type value)
|
||||
{
|
||||
Node *last = list.last;
|
||||
Node *new_node = mem::alloc(Node.sizeof);
|
||||
Node *new_node = mem::alloc(Node);
|
||||
*new_node = { .prev = last, .value = value };
|
||||
list.last = new_node;
|
||||
if (!last)
|
||||
@@ -62,7 +61,7 @@ fn void LinkedList.free(LinkedList *list)
|
||||
for (Node* node = list.first; node != null;)
|
||||
{
|
||||
Node* next = node.next;
|
||||
mem::free(node);
|
||||
free(node);
|
||||
node = next;
|
||||
}
|
||||
list.first = null;
|
||||
@@ -70,12 +69,12 @@ fn void LinkedList.free(LinkedList *list)
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
fn usize LinkedList.len(LinkedList* list) @inline
|
||||
fn usz LinkedList.len(LinkedList* list) @inline
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn Type LinkedList.get(LinkedList* list, usize index)
|
||||
fn Type LinkedList.get(LinkedList* list, usz index)
|
||||
{
|
||||
Node* node = list.first;
|
||||
while (index--)
|
||||
@@ -90,7 +89,7 @@ fn Type LinkedList.get(LinkedList* list, usize index)
|
||||
private fn void LinkedList.linkBefore(LinkedList *list, Node *succ, Type value)
|
||||
{
|
||||
Node* pred = succ.prev;
|
||||
Node* new_node = @mem::malloc(Node);
|
||||
Node* new_node = mem::alloc(Node);
|
||||
*new_node = { .prev = pred, .next = succ, .value = value };
|
||||
succ.prev = new_node;
|
||||
if (!pred)
|
||||
@@ -110,7 +109,7 @@ private fn void LinkedList.linkBefore(LinkedList *list, Node *succ, Type value)
|
||||
private fn void unlinkFirst(LinkedList* list, Node* f)
|
||||
{
|
||||
Node* next = f.next;
|
||||
mem::free(f);
|
||||
free(f);
|
||||
list.first = next;
|
||||
if (!next)
|
||||
{
|
||||
@@ -130,7 +129,7 @@ private fn void LinkedList.unlinkLast(LinkedList *list, Node* l)
|
||||
{
|
||||
Node* prev = l.prev;
|
||||
list.last = prev;
|
||||
mem::free(l);
|
||||
free(l);
|
||||
if (!prev)
|
||||
{
|
||||
list.first = null;
|
||||
@@ -165,6 +164,6 @@ private fn void LinkedList.unlink(LinkedList* list, Node* x)
|
||||
{
|
||||
next.prev = prev;
|
||||
}
|
||||
mem::free(x);
|
||||
free(x);
|
||||
list.size--;
|
||||
}
|
||||
|
||||
125
lib/std/list.c3
125
lib/std/list.c3
@@ -2,71 +2,92 @@
|
||||
// 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::array::list<Type>;
|
||||
import std::mem;
|
||||
import std::math;
|
||||
|
||||
struct List
|
||||
{
|
||||
usize size;
|
||||
usize capacity;
|
||||
usz size;
|
||||
usz capacity;
|
||||
Allocator *allocator;
|
||||
Type *entries;
|
||||
}
|
||||
|
||||
private fn void List.ensureCapacity(List *list) @inline
|
||||
/**
|
||||
* @require allocator != null "A valid allocator must be provided"
|
||||
**/
|
||||
fn void List.init(List* list, usz initial_capacity = 16, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (list.capacity == list.size)
|
||||
{
|
||||
list.capacity = list.capacity ? 2 * list.capacity : 16;
|
||||
list.entries = mem::realloc(list.entries, Type.sizeof * list.capacity);
|
||||
}
|
||||
list.allocator = allocator;
|
||||
list.size = 0;
|
||||
if (initial_capacity > 0)
|
||||
{
|
||||
initial_capacity = math::next_power_of_2(initial_capacity);
|
||||
list.entries = allocator.alloc_aligned(Type.sizeof * initial_capacity, Type[1].alignof)!!;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.entries = null;
|
||||
}
|
||||
list.capacity = initial_capacity;
|
||||
}
|
||||
|
||||
fn void List.push(List *list, Type element) @inline
|
||||
fn void List.tinit(List* list, usz initial_capacity = 16)
|
||||
{
|
||||
list.init(initial_capacity, mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn void List.push(List* list, Type element) @inline
|
||||
{
|
||||
list.append(element);
|
||||
}
|
||||
|
||||
fn void List.append(List *list, Type element)
|
||||
fn void List.append(List* list, Type element)
|
||||
{
|
||||
list.ensureCapacity();
|
||||
list.ensure_capacity();
|
||||
list.entries[list.size++] = element;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.pop(List *list)
|
||||
fn Type List.pop(List* list)
|
||||
{
|
||||
return list.entries[--list.size];
|
||||
}
|
||||
|
||||
fn void List.clear(List* list)
|
||||
{
|
||||
list.size = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require list.size > 0
|
||||
*/
|
||||
fn Type List.popFirst(List *list)
|
||||
fn Type List.pop_first(List* list)
|
||||
{
|
||||
Type value = list.entries[0];
|
||||
list.removeAt(0);
|
||||
list.remove_at(0);
|
||||
return value;
|
||||
}
|
||||
|
||||
fn void List.removeAt(List *list, usize index)
|
||||
fn void List.remove_at(List* list, usz index)
|
||||
{
|
||||
for (usize i = index + 1; i < list.size; i++)
|
||||
for (usz i = index + 1; i < list.size; i++)
|
||||
{
|
||||
list.entries[i - 1] = list.entries[i];
|
||||
}
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.pushFront(List *list, Type type) @inline
|
||||
fn void List.push_front(List* list, Type type) @inline
|
||||
{
|
||||
list.insertAt(0, type);
|
||||
list.insert_at(0, type);
|
||||
}
|
||||
|
||||
fn void List.insertAt(List *list, usize index, Type type)
|
||||
fn void List.insert_at(List* list, usz index, Type type)
|
||||
{
|
||||
list.ensureCapacity();
|
||||
for (usize i = list.size; i > index; i--)
|
||||
list.ensure_capacity();
|
||||
for (usz i = list.size; i > index; i--)
|
||||
{
|
||||
list.entries[i] = list.entries[i - 1];
|
||||
}
|
||||
@@ -74,55 +95,83 @@ fn void List.insertAt(List *list, usize index, Type type)
|
||||
list.entries[index] = type;
|
||||
}
|
||||
|
||||
fn void List.removeLast(List *list)
|
||||
fn void List.remove_last(List* list)
|
||||
{
|
||||
list.size--;
|
||||
}
|
||||
|
||||
fn void List.removeFirst(List *list)
|
||||
fn void List.remove_first(List* list)
|
||||
{
|
||||
list.removeAt(0);
|
||||
list.remove_at(0);
|
||||
}
|
||||
|
||||
fn Type* List.first(List *list)
|
||||
fn Type* List.first(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[0] : null;
|
||||
}
|
||||
|
||||
fn Type* List.last(List *list)
|
||||
fn Type* List.last(List* list)
|
||||
{
|
||||
return list.size ? &list.entries[list.size - 1] : null;
|
||||
}
|
||||
|
||||
fn bool List.isEmpty(List *list)
|
||||
fn bool List.is_empty(List* list)
|
||||
{
|
||||
return !list.size;
|
||||
}
|
||||
|
||||
fn usz List.len(List* list) @operator(len)
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn usize List.len(List *list) @operator(len)
|
||||
{
|
||||
return list.size;
|
||||
}
|
||||
|
||||
fn Type List.get(List *list, usize index)
|
||||
fn Type List.get(List* list, usz index)
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
fn void List.free(List *list)
|
||||
fn void List.free(List* list)
|
||||
{
|
||||
mem::free(list.entries);
|
||||
if (!list.allocator) return;
|
||||
list.allocator.free_aligned(list.entries)!!;
|
||||
list.capacity = 0;
|
||||
list.size = 0;
|
||||
list.entries = null;
|
||||
}
|
||||
|
||||
fn void List.swap(List* list, usz i, usz j)
|
||||
{
|
||||
@swap(list.entries[i], list.entries[j]);
|
||||
}
|
||||
|
||||
macro Type List.item_at(List &list, usize index) @operator(elementat)
|
||||
/**
|
||||
* Reserve at least min_capacity
|
||||
**/
|
||||
fn void List.reserve(List* list, usz min_capacity)
|
||||
{
|
||||
if (!min_capacity) return;
|
||||
if (list.capacity >= min_capacity) return;
|
||||
if (!list.allocator) list.allocator = mem::temp_allocator();
|
||||
min_capacity = math::next_power_of_2(min_capacity);
|
||||
list.entries = list.allocator.realloc_aligned(list.entries, Type.sizeof * min_capacity, Type[1].alignof) ?? null;
|
||||
list.capacity = min_capacity;
|
||||
}
|
||||
|
||||
macro Type List.@item_at(List &list, usz index) @operator([])
|
||||
{
|
||||
return list.entries[index];
|
||||
}
|
||||
|
||||
macro Type* List.item_ref(List &list, usize index) @operator(elementref)
|
||||
fn Type* List.get_ref(List* list, usz index) @operator(&[]) @inline
|
||||
{
|
||||
return &list.entries[index];
|
||||
}
|
||||
|
||||
|
||||
private fn void List.ensure_capacity(List* list) @inline
|
||||
{
|
||||
if (list.capacity == list.size)
|
||||
{
|
||||
list.reserve(list.capacity ? 2 * list.capacity : 16);
|
||||
}
|
||||
}
|
||||
|
||||
341
lib/std/map.c3
Normal file
341
lib/std/map.c3
Normal file
@@ -0,0 +1,341 @@
|
||||
module std::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;
|
||||
|
||||
private struct Entry
|
||||
{
|
||||
uint hash;
|
||||
Key key;
|
||||
Value value;
|
||||
Entry* next;
|
||||
}
|
||||
struct HashMap
|
||||
{
|
||||
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 !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
* @require allocator != null "The allocator must be non-null"
|
||||
**/
|
||||
fn void HashMap.init(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
map.allocator = allocator;
|
||||
map.load_factor = load_factor;
|
||||
map.threshold = (uint)(capacity * load_factor);
|
||||
map.table = array::make(Entry*, capacity, allocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require capacity > 0 "The capacity must be 1 or higher"
|
||||
* @require load_factor > 0.0 "The load factor must be higher than 0"
|
||||
* @require !map.allocator "Map was already initialized"
|
||||
* @require capacity < MAXIMUM_CAPACITY "Capacity cannot exceed maximum"
|
||||
**/
|
||||
fn void HashMap.tinit(HashMap* map, uint capacity = DEFAULT_INITIAL_CAPACITY, float load_factor = DEFAULT_LOAD_FACTOR)
|
||||
{
|
||||
map.init(capacity, load_factor, mem::temp_allocator());
|
||||
}
|
||||
|
||||
fn void HashMap.init_from_map(HashMap* map, HashMap* other_map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
map.init(other_map.table.len, other_map.load_factor, allocator);
|
||||
map.put_all_for_create(other_map);
|
||||
}
|
||||
|
||||
fn void HashMap.tinit_from_map(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
map.init_from_map(other_map, mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.is_empty(HashMap* map) @inline
|
||||
{
|
||||
return !map.count;
|
||||
}
|
||||
|
||||
fn Value*! HashMap.get_ref(HashMap* map, Key key)
|
||||
{
|
||||
if (!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!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value or update and
|
||||
**/
|
||||
macro Value HashMap.@get_or_set(HashMap* map, Key key, Value #expr)
|
||||
{
|
||||
if (!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! HashMap.get(HashMap* map, Key key) @operator([])
|
||||
{
|
||||
return *map.get_ref(key) @inline;
|
||||
}
|
||||
|
||||
fn bool HashMap.has_key(HashMap* map, Key key)
|
||||
{
|
||||
return try(map.get_ref(key));
|
||||
}
|
||||
|
||||
fn bool HashMap.set(HashMap* map, Key key, Value value) @operator([]=)
|
||||
{
|
||||
// If the map isn't initialized, use the defaults to initialize it.
|
||||
if (!map.allocator)
|
||||
{
|
||||
map.init();
|
||||
}
|
||||
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! HashMap.remove(HashMap* map, Key key) @maydiscard
|
||||
{
|
||||
if (!map.remove_entry_for_key(key)) return SearchResult.MISSING!;
|
||||
}
|
||||
|
||||
fn void HashMap.clear(HashMap* map)
|
||||
{
|
||||
if (!map.count) return;
|
||||
foreach (Entry** &entry_ref : map.table)
|
||||
{
|
||||
Entry* entry = *entry_ref;
|
||||
if (!entry) continue;
|
||||
map.free(entry);
|
||||
*entry_ref = null;
|
||||
}
|
||||
map.count = 0;
|
||||
}
|
||||
|
||||
fn void HashMap.destroy(HashMap* map)
|
||||
{
|
||||
if (!map.allocator) return;
|
||||
map.clear();
|
||||
map.free(map.table.ptr);
|
||||
map.table = Entry*[] {};
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_tlist(HashMap* map)
|
||||
{
|
||||
return map.key_list(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn Key[] HashMap.key_list(HashMap* map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (!map.count) return Key[] {};
|
||||
|
||||
Key[] list = array::make(Key, map.count, allocator);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.key;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_tlist(HashMap* map)
|
||||
{
|
||||
return map.value_list(mem::temp_allocator()) @inline;
|
||||
}
|
||||
|
||||
fn Value[] HashMap.value_list(HashMap* map, Allocator* allocator = mem::current_allocator())
|
||||
{
|
||||
if (!map.count) return Value[] {};
|
||||
Value[] list = array::make(Value, map.count, allocator);
|
||||
usz index = 0;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
list[index++] = entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
$if (types::is_equatable(Value)):
|
||||
fn bool HashMap.has_value(HashMap* map, Value v)
|
||||
{
|
||||
if (!map.count) return false;
|
||||
foreach (Entry* entry : map.table)
|
||||
{
|
||||
while (entry)
|
||||
{
|
||||
if (equals(v, entry.value)) return true;
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
$endif;
|
||||
|
||||
// --- private methods
|
||||
|
||||
private fn void HashMap.add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index)
|
||||
{
|
||||
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
|
||||
*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);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.resize(HashMap* map, uint new_capacity)
|
||||
{
|
||||
Entry*[] old_table = map.table;
|
||||
uint old_capacity = old_table.len;
|
||||
if (old_capacity == MAXIMUM_CAPACITY)
|
||||
{
|
||||
map.threshold = uint.max;
|
||||
return;
|
||||
}
|
||||
Entry*[] new_table = array::make(Entry*, new_capacity, map.allocator);
|
||||
map.transfer(new_table);
|
||||
map.table = new_table;
|
||||
map.free(old_table.ptr);
|
||||
map.threshold = (uint)(new_capacity * map.load_factor);
|
||||
}
|
||||
|
||||
private fn uint rehash(uint hash) @inline
|
||||
{
|
||||
hash ^= (hash >> 20) ^ (hash >> 12);
|
||||
return hash ^ ((hash >> 7) ^ (hash >> 4));
|
||||
}
|
||||
|
||||
private macro uint index_for(uint hash, uint capacity)
|
||||
{
|
||||
return hash & (capacity - 1);
|
||||
}
|
||||
|
||||
private fn void HashMap.transfer(HashMap* map, Entry*[] new_table)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.put_all_for_create(HashMap* map, HashMap* other_map)
|
||||
{
|
||||
if (!other_map.count) return;
|
||||
foreach (Entry *e : other_map.table)
|
||||
{
|
||||
if (!e) continue;
|
||||
map.put_for_create(e.key, e.value);
|
||||
}
|
||||
}
|
||||
|
||||
private fn void HashMap.put_for_create(HashMap* map, Key key, Value value)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
private fn void HashMap.free(HashMap* map, void* ptr)
|
||||
{
|
||||
map.allocator.free(ptr)!!;
|
||||
}
|
||||
|
||||
private fn bool HashMap.remove_entry_for_key(HashMap* map, Key key)
|
||||
{
|
||||
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(e);
|
||||
return true;
|
||||
}
|
||||
prev = e;
|
||||
e = next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private fn void HashMap.create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index)
|
||||
{
|
||||
Entry *e = map.table[bucket_index];
|
||||
Entry* entry = map.allocator.alloc(Entry.sizeof)!!;
|
||||
*entry = { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] };
|
||||
map.table[bucket_index] = entry;
|
||||
map.count++;
|
||||
}
|
||||
412
lib/std/math.c3
412
lib/std/math.c3
@@ -2,6 +2,7 @@
|
||||
// Use of this source code is governed by the MIT license
|
||||
// a copy of which can be found in the LICENSE_STDLIB file.
|
||||
module std::math;
|
||||
import std::math::complex;
|
||||
|
||||
// TODO Define these using quad precision.
|
||||
const E = 2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466;
|
||||
@@ -53,13 +54,15 @@ const DOUBLE_MIN_10_EXP = -307;
|
||||
const DOUBLE_MAX_EXP = 1024;
|
||||
const DOUBLE_MIN_EXP = -1021;
|
||||
const DOUBLE_EPSILON = 2.22044604925031308085e-16;
|
||||
|
||||
const QUAD_MANT_DIG = 113;
|
||||
|
||||
/*
|
||||
const QUAD_MAX = 1.18973149535723176508575932662800702e+4932;
|
||||
const QUAD_MIN = 3.36210314311209350626267781732175260e-4932;
|
||||
const QUAD_DENORM_MIN = 6.47517511943802511092443895822764655e-4966;
|
||||
const QUAD_DIG = 33;
|
||||
const QUAD_DEC_DIGITS = 36;
|
||||
const QUAD_MANT_DIG = 113;
|
||||
const QUAD_MAX_10_EXP = 4932;
|
||||
const QUAD_MIN_10_EXP = -4931;
|
||||
const QUAD_MAX_EXP = 16384;
|
||||
@@ -67,65 +70,396 @@ const QUAD_MIN_EXP = -16481;
|
||||
const QUAD_EPSILON = 1.92592994438723585305597794258492732e-34;
|
||||
*/
|
||||
|
||||
macro max(x, y) @autoimport
|
||||
enum RoundingMode : int
|
||||
{
|
||||
return x > y ? x : y;
|
||||
TOWARD_ZERO,
|
||||
TO_NEAREST,
|
||||
TOWARD_INFINITY,
|
||||
TOWARD_NEG_INFINITY
|
||||
}
|
||||
|
||||
macro min(x, y) @autoimport
|
||||
define Complex32 = Complex<float>;
|
||||
define Complex64 = Complex<double>;
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
**/
|
||||
macro abs(x) = $$abs(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_int(x) `The input must be an integer`
|
||||
**/
|
||||
macro sign(x)
|
||||
{
|
||||
return x < y ? x : y;
|
||||
if (!x) return ($typeof(x))0;
|
||||
return ($typeof(x))(x < 0 ? -1 : 1);
|
||||
}
|
||||
|
||||
fn double log10(double x) @inline
|
||||
$if (env::COMPILER_LIBC_AVAILABLE):
|
||||
|
||||
extern fn double _atan(double x) @extname("atan");
|
||||
extern fn float _atanf(float x) @extname("atanf");
|
||||
extern fn double _atan2(double x) @extname("atan2");
|
||||
extern fn float _atan2f(float x) @extname("atan2f");
|
||||
|
||||
macro atan(x)
|
||||
{
|
||||
return $$log10(x);
|
||||
$if ($typeof(x).typeid == float.typeid):
|
||||
return _atanf(x);
|
||||
$else:
|
||||
return _atan(x);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn double log2(double x) @inline
|
||||
macro atan2(x, y)
|
||||
{
|
||||
return $$log2(x);
|
||||
$if ($typeof(x).typeid == float.typeid && $typeof(y).typeid == float.typeid):
|
||||
return _atan2f(x);
|
||||
$else:
|
||||
return _atan2(x);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn double log(double x) @inline
|
||||
$endif;
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro ceil(x) = $$ceil(x);
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
* @require types::@has_same(x, lower, upper) `The input types must be equal`
|
||||
**/
|
||||
macro clamp(x, lower, upper) = $$max(lower, $$min(x, upper));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(mag) `The input must be a floating point value or float vector`
|
||||
* @require types::is_same($typeof(mag), $typeof(sgn)) `The input types must be equal`
|
||||
**/
|
||||
macro copysign(mag, sgn) = $$copysign(mag, sgn);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro cos(x) = $$cos(x);
|
||||
|
||||
macro cosec(x) = 1 / sin(x);
|
||||
|
||||
macro cosech(x) = 2 / (exp(x) - exp(-x));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro cosh(x) = (exp(x) + exp(-x)) / 2.0;
|
||||
|
||||
macro cotan(x) = cos(x) / sin(x);
|
||||
|
||||
macro cotanh(x) = (exp(2.0 * x) + 1.0) / (exp(2.0 * x) - 1.0);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro exp(x) = $$exp(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro exp2(x) = $$exp2(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro floor(x) = $$floor(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(a) `The input must be a floating point value or float vector`
|
||||
* @require types::@has_same(a, b, c) `The input types must be equal`
|
||||
**/
|
||||
macro fma(a, b, c) = $$fma(a, b, c);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
* @require values::@is_floatlike(y) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro hypot(x, y) = sqrt(sqr(x) + sqr(y));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro log(x) = $$log(x);
|
||||
|
||||
/**
|
||||
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro log2(x) = $$log2(x);
|
||||
|
||||
/**
|
||||
* @require values::is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro log10(x) = $$log10(x);
|
||||
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a floating point value or float vector`
|
||||
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
|
||||
**/
|
||||
macro max(x, y, ...)
|
||||
{
|
||||
return $$log(x);
|
||||
$if ($vacount == 0):
|
||||
return $$max(x, y);
|
||||
$else:
|
||||
var m = $$max(x, y);
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
m = $$max(m, $vaarg($i));
|
||||
$endfor;
|
||||
return m;
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn double cos(double x) @inline
|
||||
/**
|
||||
* @require types::is_numerical($typeof(x)) `The input must be a numerical value or numerical vector`
|
||||
* @require types::is_same($typeof(x), $typeof(y)) `The input types must be equal`
|
||||
**/
|
||||
macro min(x, y, ...)
|
||||
{
|
||||
return $$cos(x);
|
||||
$if ($vacount == 0):
|
||||
return $$min(x, y);
|
||||
$else:
|
||||
var m = $$min(x, y);
|
||||
$for (var $i = 0; $i < $vacount; $i++):
|
||||
m = $$min(m, $vaarg($i));
|
||||
$endfor;
|
||||
return m;
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn double sin(double x) @inline
|
||||
/**
|
||||
* @require types::@is_float(a) `The input must be a floating point value`
|
||||
* @require types::@has_same(a, b, c) `The input types must be equal`
|
||||
**/
|
||||
macro muladd(a, b, c) = $$fmuladd(a, b, c);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro nearbyint(x) = $$nearbyint(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
* @require values::@convertable_to(exp, x) || values::@is_int(exp) `The input must be an integer, castable to the type of x`
|
||||
**/
|
||||
macro pow(x, exp)
|
||||
{
|
||||
return $$sin(x);
|
||||
$if (types::is_floatlike($typeof(exp))):
|
||||
return $$pow(x, ($typeof(x))exp);
|
||||
$else:
|
||||
return $$pow_int(x, exp);
|
||||
$endif;
|
||||
}
|
||||
|
||||
fn double exp(double x) @inline
|
||||
{
|
||||
return $$exp(x);
|
||||
}
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro rint(x) = $$rint(x);
|
||||
|
||||
fn double pow(double x, double y) @inline
|
||||
{
|
||||
return $$pow(x, y);
|
||||
}
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro round(x) = $$round(x);
|
||||
|
||||
fn double fabs(double x) @inline
|
||||
{
|
||||
return $$fabs(x);
|
||||
}
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro roundeven(x) = $$roundeven(x);
|
||||
|
||||
fn double trunc(double x) @inline
|
||||
{
|
||||
return $$trunc(x);
|
||||
}
|
||||
macro sec(x) = 1 / cos(x);
|
||||
|
||||
fn double ceil(double x) @inline
|
||||
{
|
||||
return $$ceil(x);
|
||||
}
|
||||
macro sech(x) = 2 / (exp(x) + exp(-x));
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sin(x) = $$sin(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sinh(x) = (exp(x) - exp(-x)) / 2.0;
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sqr(x) = x * x;
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro sqrt(x) = $$sqrt(x);
|
||||
|
||||
macro tan(x) = sin(x) / cos(x);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro tanh(x) = (exp(2.0 * x) - 1.0) / (exp(2.0 * x) + 1.0);
|
||||
|
||||
/**
|
||||
* @require values::@is_floatlike(x) `The input must be a floating point value or float vector`
|
||||
**/
|
||||
macro trunc(x) = $$trunc(x);
|
||||
|
||||
macro float float.ceil(float x) = $$ceil(x);
|
||||
macro float float.clamp(float x, float lower, float upper) = $$max(lower, $$min(x, upper));
|
||||
macro float float.copysign(float mag, float sgn) = $$copysign(mag, sgn);
|
||||
macro float float.floor(float x) = $$floor(x);
|
||||
macro float float.fma(float a, float b, float c) = $$fma(a, b, c);
|
||||
macro float float.muladd(float a, float b, float c) = $$fmuladd(a, b, c);
|
||||
macro float float.nearbyint(float x) = $$nearbyint(x);
|
||||
macro float float.pow(float x, exp) = pow(x, exp);
|
||||
macro float float.rint(float x) = $$rint(x);
|
||||
macro float float.round(float x) = $$round(x);
|
||||
macro float float.roundeven(float x) = $$roundeven(x);
|
||||
macro float float.trunc(float x) = $$trunc(x);
|
||||
|
||||
macro float float[<*>].sum(float[<*>] x, float start = 0.0) = $$reduce_fadd(x, start);
|
||||
macro float float[<*>].product(float[<*>] x, float start = 1.0) = $$reduce_fmul(x, start);
|
||||
macro float float[<*>].max(float[<*>] x) = $$reduce_max(x);
|
||||
macro float float[<*>].min(float[<*>] x) = $$reduce_min(x);
|
||||
macro float[<*>] float[<*>].ceil(float[<*>] x) = $$ceil(x);
|
||||
macro float[<*>] float[<*>].clamp(float[<*>] x, float[<*>] lower, float[<*>] upper) = $$max(lower, $$min(x, upper));
|
||||
macro float[<*>] float[<*>].copysign(float[<*>] mag, float[<*>] sgn) = $$copysign(mag, sgn);
|
||||
macro float[<*>] float[<*>].fma(float[<*>] a, float[<*>] b, float[<*>] c) = $$fma(a, b, c);
|
||||
macro float[<*>] float[<*>].floor(float[<*>] x) = $$floor(x);
|
||||
macro float[<*>] float[<*>].nearbyint(float[<*>] x) = $$nearbyint(x);
|
||||
macro float[<*>] float[<*>].pow(float[<*>] x, exp) = pow(x, exp);
|
||||
macro float[<*>] float[<*>].rint(float[<*>] x) = $$rint(x);
|
||||
macro float[<*>] float[<*>].round(float[<*>] x) = $$round(x);
|
||||
macro float[<*>] float[<*>].roundeven(float[<*>] x) = $$roundeven(x);
|
||||
macro float[<*>] float[<*>].trunc(float[<*>] x) = $$trunc(x);
|
||||
|
||||
macro double double.ceil(double x) = $$ceil(x);
|
||||
macro double double.clamp(double x, double lower, double upper) = $$max(lower, $$min(x, upper));
|
||||
macro double double.copysign(double mag, double sgn) = $$copysign(mag, sgn);
|
||||
macro double double.floor(double x) = $$floor(x);
|
||||
macro double double.fma(double a, double b, double c) = $$fma(a, b, c);
|
||||
macro double double.muladd(double a, double b, double c) = $$fmuladd(a, b, c);
|
||||
macro double double.nearbyint(double x) = $$nearbyint(x);
|
||||
macro double double.pow(double x, exp) = pow(x, exp);
|
||||
macro double double.rint(double x) = $$rint(x);
|
||||
macro double double.round(double x) = $$round(x);
|
||||
macro double double.roundeven(double x) = $$roundeven(x);
|
||||
macro double double.trunc(double x) = $$trunc(x);
|
||||
|
||||
macro double double[<*>].sum(double[<*>] x, double start = 0.0) = $$reduce_add(x, start);
|
||||
macro double double[<*>].product(double[<*>] x, double start = 1.0) = $$reduce_mul(x, start);
|
||||
macro double double[<*>].max(double[<*>] x) = $$reduce_fmax(x);
|
||||
macro double double[<*>].min(double[<*>] x) = $$reduce_fmin(x);
|
||||
macro double[<*>] double[<*>].ceil(double[<*>] x) = $$ceil(x);
|
||||
macro double[<*>] double[<*>].clamp(double[<*>] x, double[<*>] lower, double[<*>] upper) = $$max(lower, $$min(x, upper));
|
||||
macro double[<*>] double[<*>].copysign(double[<*>] mag, double[<*>] sgn) = $$copysign(mag, sgn);
|
||||
macro double[<*>] double[<*>].floor(double[<*>] x) = $$floor(x);
|
||||
macro double[<*>] double[<*>].fma(double[<*>] a, double[<*>] b, double[<*>] c) = $$fma(a, b, c);
|
||||
macro double[<*>] double[<*>].nearbyint(double[<*>] x) = $$nearbyint(x);
|
||||
macro double[<*>] double[<*>].pow(double[<*>] x, exp) = pow(x, exp);
|
||||
macro double[<*>] double[<*>].rint(double[<*>] x) = $$rint(x);
|
||||
macro double[<*>] double[<*>].round(double[<*>] x) = $$round(x);
|
||||
macro double[<*>] double[<*>].roundeven(double[<*>] x) = $$roundeven(x);
|
||||
macro double[<*>] double[<*>].trunc(double[<*>] x) = $$trunc(x);
|
||||
|
||||
macro ichar ichar[<*>].sum(ichar[<*>] x) = $$reduce_add(x);
|
||||
macro ichar ichar[<*>].product(ichar[<*>] x) = $$reduce_mul(x);
|
||||
macro ichar ichar[<*>].and(ichar[<*>] x) = $$reduce_and(x);
|
||||
macro ichar ichar[<*>].or(ichar[<*>] x) = $$reduce_or(x);
|
||||
macro ichar ichar[<*>].xor(ichar[<*>] x) = $$reduce_xor(x);
|
||||
macro ichar ichar[<*>].max(ichar[<*>] x) = $$reduce_max(x);
|
||||
macro ichar ichar[<*>].min(ichar[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro short short[<*>].sum(short[<*>] x) = $$reduce_add(x);
|
||||
macro short short[<*>].product(short[<*>] x) = $$reduce_mul(x);
|
||||
macro short short[<*>].and(short[<*>] x) = $$reduce_and(x);
|
||||
macro short short[<*>].or(short[<*>] x) = $$reduce_or(x);
|
||||
macro short short[<*>].xor(short[<*>] x) = $$reduce_xor(x);
|
||||
macro short short[<*>].max(short[<*>] x) = $$reduce_max(x);
|
||||
macro short short[<*>].min(short[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro bool[<*>] int[<*>].comp_lt(int[<*>] x, int[<*>] y) = $$veccomplt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_le(int[<*>] x, int[<*>] y) = $$veccomple(x, y);
|
||||
macro bool[<*>] int[<*>].comp_eq(int[<*>] x, int[<*>] y) = $$veccompeq(x, y);
|
||||
macro bool[<*>] int[<*>].comp_gt(int[<*>] x, int[<*>] y) = $$veccompgt(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ge(int[<*>] x, int[<*>] y) = $$veccompge(x, y);
|
||||
macro bool[<*>] int[<*>].comp_ne(int[<*>] x, int[<*>] y) = $$veccompne(x, y);
|
||||
|
||||
macro int int[<*>].sum(int[<*>] x) = $$reduce_add(x);
|
||||
macro int int[<*>].product(int[<*>] x) = $$reduce_mul(x);
|
||||
macro int int[<*>].and(int[<*>] x) = $$reduce_and(x);
|
||||
macro int int[<*>].or(int[<*>] x) = $$reduce_or(x);
|
||||
macro int int[<*>].xor(int[<*>] x) = $$reduce_xor(x);
|
||||
macro int int[<*>].max(int[<*>] x) = $$reduce_max(x);
|
||||
macro int int[<*>].min(int[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro long long[<*>].sum(long[<*>] x) = $$reduce_add(x);
|
||||
macro long long[<*>].product(long[<*>] x) = $$reduce_mul(x);
|
||||
macro long long[<*>].and(long[<*>] x) = $$reduce_and(x);
|
||||
macro long long[<*>].or(long[<*>] x) = $$reduce_or(x);
|
||||
macro long long[<*>].xor(long[<*>] x) = $$reduce_xor(x);
|
||||
macro long long[<*>].max(long[<*>] x) = $$reduce_max(x);
|
||||
macro long long[<*>].min(long[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro int128 int128[<*>].sum(int128[<*>] x) = $$reduce_add(x);
|
||||
macro int128 int128[<*>].product(int128[<*>] x) = $$reduce_mul(x);
|
||||
macro int128 int128[<*>].and(int128[<*>] x) = $$reduce_and(x);
|
||||
macro int128 int128[<*>].or(int128[<*>] x) = $$reduce_or(x);
|
||||
macro int128 int128[<*>].xor(int128[<*>] x) = $$reduce_xor(x);
|
||||
macro int128 int128[<*>].max(int128[<*>] x) = $$reduce_max(x);
|
||||
macro int128 int128[<*>].min(int128[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro bool bool[<*>].sum(bool[<*>] x) = $$reduce_add(x);
|
||||
macro bool bool[<*>].product(bool[<*>] x) = $$reduce_mul(x);
|
||||
macro bool bool[<*>].and(bool[<*>] x) = $$reduce_and(x);
|
||||
macro bool bool[<*>].or(bool[<*>] x) = $$reduce_or(x);
|
||||
macro bool bool[<*>].xor(bool[<*>] x) = $$reduce_xor(x);
|
||||
macro bool bool[<*>].max(bool[<*>] x) = $$reduce_max(x);
|
||||
macro bool bool[<*>].min(bool[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro char char[<*>].sum(char[<*>] x) = $$reduce_add(x);
|
||||
macro char char[<*>].product(char[<*>] x) = $$reduce_mul(x);
|
||||
macro char char[<*>].and(char[<*>] x) = $$reduce_and(x);
|
||||
macro char char[<*>].or(char[<*>] x) = $$reduce_or(x);
|
||||
macro char char[<*>].xor(char[<*>] x) = $$reduce_xor(x);
|
||||
macro char char[<*>].max(char[<*>] x) = $$reduce_max(x);
|
||||
macro char char[<*>].min(char[<*>] x) = $$reduce_min(x);
|
||||
|
||||
|
||||
macro ushort ushort[<*>].sum(ushort[<*>] x) = $$reduce_add(x);
|
||||
macro ushort ushort[<*>].product(ushort[<*>] x) = $$reduce_mul(x);
|
||||
macro ushort ushort[<*>].and(ushort[<*>] x) = $$reduce_and(x);
|
||||
macro ushort ushort[<*>].or(ushort[<*>] x) = $$reduce_or(x);
|
||||
macro ushort ushort[<*>].xor(ushort[<*>] x) = $$reduce_xor(x);
|
||||
macro ushort ushort[<*>].max(ushort[<*>] x) = $$reduce_max(x);
|
||||
macro ushort ushort[<*>].min(ushort[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro uint uint[<*>].sum(uint[<*>] x) = $$reduce_add(x);
|
||||
macro uint uint[<*>].product(uint[<*>] x) = $$reduce_mul(x);
|
||||
macro uint uint[<*>].and(uint[<*>] x) = $$reduce_and(x);
|
||||
macro uint uint[<*>].or(uint[<*>] x) = $$reduce_or(x);
|
||||
macro uint uint[<*>].xor(uint[<*>] x) = $$reduce_xor(x);
|
||||
macro uint uint[<*>].max(uint[<*>] x) = $$reduce_max(x);
|
||||
macro uint uint[<*>].min(uint[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro ulong ulong[<*>].sum(ulong[<*>] x) = $$reduce_add(x);
|
||||
macro ulong ulong[<*>].product(ulong[<*>] x) = $$reduce_mul(x);
|
||||
macro ulong ulong[<*>].and(ulong[<*>] x) = $$reduce_and(x);
|
||||
macro ulong ulong[<*>].or(ulong[<*>] x) = $$reduce_or(x);
|
||||
macro ulong ulong[<*>].xor(ulong[<*>] x) = $$reduce_xor(x);
|
||||
macro ulong ulong[<*>].max(ulong[<*>] x) = $$reduce_max(x);
|
||||
macro ulong ulong[<*>].min(ulong[<*>] x) = $$reduce_min(x);
|
||||
|
||||
macro uint128 uint128[<*>].sum(uint128[<*>] x) = $$reduce_add(x);
|
||||
macro uint128 uint128[<*>].product(uint128[<*>] x) = $$reduce_mul(x);
|
||||
macro uint128 uint128[<*>].and(uint128[<*>] x) = $$reduce_and(x);
|
||||
macro uint128 uint128[<*>].or(uint128[<*>] x) = $$reduce_or(x);
|
||||
macro uint128 uint128[<*>].xor(uint128[<*>] x) = $$reduce_xor(x);
|
||||
macro uint128 uint128[<*>].max(uint128[<*>] x) = $$reduce_max(x);
|
||||
macro uint128 uint128[<*>].min(uint128[<*>] x) = $$reduce_min(x);
|
||||
|
||||
/**
|
||||
* @checked x & 1
|
||||
@@ -135,5 +469,9 @@ macro bool is_power_of_2(x)
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
macro next_power_of_2(x)
|
||||
{
|
||||
$typeof(x) y = 1;
|
||||
while (y < x) y += y;
|
||||
return y;
|
||||
}
|
||||
|
||||
31
lib/std/math.complex.c3
Normal file
31
lib/std/math.complex.c3
Normal file
@@ -0,0 +1,31 @@
|
||||
module std::math::complex<Type>;
|
||||
|
||||
union Complex
|
||||
{
|
||||
struct
|
||||
{
|
||||
Type r, c;
|
||||
}
|
||||
Type[<2>] v;
|
||||
}
|
||||
|
||||
macro Complex Complex.add(Complex a, Complex b)
|
||||
{
|
||||
return Complex { .v = a.v + b.v };
|
||||
}
|
||||
|
||||
macro Complex Complex.sub(Complex a, Complex b)
|
||||
{
|
||||
return Complex { .v = a.v - b.v };
|
||||
}
|
||||
|
||||
macro Complex Complex.mult(Complex a, Complex b)
|
||||
{
|
||||
return Complex { .r = a.r * b.r - a.c * b.c, .c = a.r * b.c + b.r * a.c };
|
||||
}
|
||||
|
||||
fn Complex Complex.div(Complex a, Complex b)
|
||||
{
|
||||
Type div = b.r * b.r + b.c * b.c;
|
||||
return Complex { .r = (a.r * b.r + a.c * b.c) / div, .c = (a.c * b.r - a.r * b.c) / div };
|
||||
}
|
||||
431
lib/std/math.matrix.c3
Normal file
431
lib/std/math.matrix.c3
Normal file
@@ -0,0 +1,431 @@
|
||||
module std::math::matrix;
|
||||
|
||||
fault MatrixError
|
||||
{
|
||||
MATRIX_INVERSE_DOESNT_EXIST,
|
||||
}
|
||||
|
||||
struct Matrix2x2
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
float m00, m01;
|
||||
float m10, m11;
|
||||
}
|
||||
float[4] m;
|
||||
}
|
||||
}
|
||||
|
||||
struct Matrix3x3
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
float m00; float m01; float m02;
|
||||
float m10; float m11; float m12;
|
||||
float m20; float m21; float m22;
|
||||
}
|
||||
float[9] m;
|
||||
}
|
||||
}
|
||||
|
||||
struct Matrix4x4
|
||||
{
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
float m00, m01, m02, m03;
|
||||
float m10, m11, m12, m13;
|
||||
float m20, m21, m22, m23;
|
||||
float m30, m31, m32, m33;
|
||||
}
|
||||
float[16] m;
|
||||
}
|
||||
}
|
||||
|
||||
fn float[<2>] Matrix2x2.apply(Matrix2x2* mat, float[<2>] vec)
|
||||
{
|
||||
return float[<2>] {
|
||||
mat.m00 * vec[0] + mat.m01 * vec[1],
|
||||
mat.m10 * vec[0] + mat.m11 * vec[1],
|
||||
};
|
||||
}
|
||||
|
||||
fn float[<3>] Matrix3x3.apply(Matrix3x3* mat, float[<3>] vec)
|
||||
{
|
||||
return float[<3>] {
|
||||
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2],
|
||||
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2],
|
||||
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2],
|
||||
};
|
||||
}
|
||||
|
||||
fn float[<4>] Matrix4x4.apply(Matrix4x4* mat, float[<4>] vec)
|
||||
{
|
||||
return float[<4>] {
|
||||
mat.m00 * vec[0] + mat.m01 * vec[1] + mat.m02 * vec[2] + mat.m03 * vec[3],
|
||||
mat.m10 * vec[0] + mat.m11 * vec[1] + mat.m12 * vec[2] + mat.m13 * vec[3],
|
||||
mat.m20 * vec[0] + mat.m21 * vec[1] + mat.m22 * vec[2] + mat.m23 * vec[3],
|
||||
mat.m30 * vec[0] + mat.m31 * vec[1] + mat.m32 * vec[2] + mat.m33 * vec[3],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.mul(Matrix2x2* a, Matrix2x2 b)
|
||||
{
|
||||
return Matrix2x2 {
|
||||
a.m00 * b.m00 + a.m01 * b.m10, a.m00 * b.m01 + a.m01 * b.m11,
|
||||
a.m10 * b.m01 + a.m11 * b.m11, a.m10 * b.m01 + a.m11 * b.m11,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.mul(Matrix3x3* a, Matrix3x3 b)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20,
|
||||
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21,
|
||||
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22,
|
||||
|
||||
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20,
|
||||
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21,
|
||||
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22,
|
||||
|
||||
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20,
|
||||
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21,
|
||||
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.mul(Matrix4x4* a, Matrix4x4 b)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
a.m00 * b.m00 + a.m01 * b.m10 + a.m02 * b.m20 + a.m03 * b.m30,
|
||||
a.m00 * b.m01 + a.m01 * b.m11 + a.m02 * b.m21 + a.m03 * b.m31,
|
||||
a.m00 * b.m02 + a.m01 * b.m12 + a.m02 * b.m22 + a.m03 * b.m32,
|
||||
a.m00 * b.m03 + a.m01 * b.m13 + a.m02 * b.m23 + a.m03 * b.m33,
|
||||
|
||||
a.m10 * b.m00 + a.m11 * b.m10 + a.m12 * b.m20 + a.m13 * b.m30,
|
||||
a.m10 * b.m01 + a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31,
|
||||
a.m10 * b.m02 + a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32,
|
||||
a.m10 * b.m03 + a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33,
|
||||
|
||||
a.m20 * b.m00 + a.m21 * b.m10 + a.m22 * b.m20 + a.m23 * b.m30,
|
||||
a.m20 * b.m01 + a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31,
|
||||
a.m20 * b.m02 + a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32,
|
||||
a.m20 * b.m03 + a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33,
|
||||
|
||||
a.m30 * b.m00 + a.m31 * b.m10 + a.m32 * b.m20 + a.m33 * b.m30,
|
||||
a.m30 * b.m01 + a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31,
|
||||
a.m30 * b.m02 + a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32,
|
||||
a.m30 * b.m03 + a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.component_mul(Matrix2x2* mat, float s)
|
||||
{
|
||||
return Matrix2x2 {
|
||||
mat.m00 * s, mat.m01 * s,
|
||||
mat.m10 * s, mat.m11 * s,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.component_mul(Matrix3x3* mat, float s)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
mat.m00 * s, mat.m01 * s, mat.m02 * s,
|
||||
mat.m10 * s, mat.m11 * s, mat.m12 * s,
|
||||
mat.m20 * s, mat.m21 * s, mat.m22 * s,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.component_mul(Matrix4x4* mat, float s)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
mat.m00 * s, mat.m01 * s, mat.m02 * s, mat.m03 * s,
|
||||
mat.m10 * s, mat.m11 * s, mat.m12 * s, mat.m13 * s,
|
||||
mat.m20 * s, mat.m21 * s, mat.m22 * s, mat.m23 * s,
|
||||
mat.m30 * s, mat.m31 * s, mat.m32 * s, mat.m33 * s,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.transpose(Matrix2x2* mat)
|
||||
{
|
||||
return Matrix2x2 {
|
||||
mat.m00, mat.m10,
|
||||
mat.m01, mat.m11
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.transpose(Matrix3x3* mat)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
mat.m00, mat.m10, mat.m20,
|
||||
mat.m01, mat.m11, mat.m21,
|
||||
mat.m02, mat.m12, mat.m22,
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.transpose(Matrix4x4* mat)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
mat.m00, mat.m10, mat.m20, mat.m30,
|
||||
mat.m01, mat.m11, mat.m21, mat.m31,
|
||||
mat.m02, mat.m12, mat.m22, mat.m32,
|
||||
mat.m03, mat.m13, mat.m23, mat.m33,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn float Matrix2x2.determinant(Matrix2x2* mat)
|
||||
{
|
||||
return mat.m00 * mat.m11 - mat.m01 * mat.m10;
|
||||
}
|
||||
|
||||
fn float Matrix3x3.determinant(Matrix3x3* mat)
|
||||
{
|
||||
return
|
||||
mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
|
||||
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
|
||||
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11);
|
||||
}
|
||||
|
||||
fn float Matrix4x4.determinant(Matrix4x4* mat)
|
||||
{
|
||||
return
|
||||
mat.m00 * (mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
|
||||
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) ) -
|
||||
mat.m01 * (mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) ) +
|
||||
mat.m02 * (mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
|
||||
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) ) -
|
||||
mat.m03 * (mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
|
||||
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
|
||||
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21) );
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2 Matrix2x2.adjoint(Matrix2x2* mat)
|
||||
{
|
||||
return Matrix2x2 { mat.m00, -mat.m01, -mat.m10, mat.m11 };
|
||||
}
|
||||
|
||||
fn Matrix3x3 Matrix3x3.adjoint(Matrix3x3* mat)
|
||||
{
|
||||
return Matrix3x3 {
|
||||
(mat.m11 * mat.m22 - mat.m21 * mat.m12),
|
||||
-(mat.m10 * mat.m22 - mat.m20 * mat.m12),
|
||||
(mat.m10 * mat.m21 - mat.m20 * mat.m11),
|
||||
|
||||
-(mat.m01 * mat.m22 - mat.m21 * mat.m02),
|
||||
(mat.m00 * mat.m22 - mat.m20 * mat.m02),
|
||||
-(mat.m00 * mat.m21 - mat.m20 * mat.m01),
|
||||
|
||||
(mat.m01 * mat.m12 - mat.m11 * mat.m02),
|
||||
-(mat.m00 * mat.m12 - mat.m10 * mat.m02),
|
||||
(mat.m00 * mat.m11 - mat.m10 * mat.m01),
|
||||
};
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.adjoint(Matrix4x4* mat)
|
||||
{
|
||||
return Matrix4x4 {
|
||||
(mat.m11 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
|
||||
mat.m13 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
|
||||
-(mat.m10 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m12 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
|
||||
(mat.m10 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
|
||||
mat.m11 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m13 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
-(mat.m10 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
|
||||
mat.m11 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
|
||||
mat.m12 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
|
||||
-(mat.m01 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m02 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) +
|
||||
mat.m03 * (mat.m21 * mat.m32 - mat.m31 * mat.m22)),
|
||||
(mat.m00 * (mat.m22 * mat.m33 - mat.m32 * mat.m23) -
|
||||
mat.m02 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m03 * (mat.m20 * mat.m32 - mat.m30 * mat.m22)),
|
||||
-(mat.m00 * (mat.m21 * mat.m33 - mat.m31 * mat.m23) -
|
||||
mat.m01 * (mat.m20 * mat.m33 - mat.m30 * mat.m23) +
|
||||
mat.m03 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
(mat.m00 * (mat.m21 * mat.m32 - mat.m31 * mat.m22) -
|
||||
mat.m01 * (mat.m20 * mat.m32 - mat.m30 * mat.m22) +
|
||||
mat.m02 * (mat.m20 * mat.m31 - mat.m30 * mat.m21)),
|
||||
|
||||
(mat.m01 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
|
||||
mat.m02 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) +
|
||||
mat.m03 * (mat.m11 * mat.m32 - mat.m31 * mat.m12)),
|
||||
-(mat.m00 * (mat.m12 * mat.m33 - mat.m32 * mat.m13) -
|
||||
mat.m02 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m32 - mat.m30 * mat.m12)),
|
||||
(mat.m00 * (mat.m11 * mat.m33 - mat.m31 * mat.m13) -
|
||||
mat.m01 * (mat.m10 * mat.m33 - mat.m30 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
|
||||
-(mat.m00 * (mat.m11 * mat.m32 - mat.m31 * mat.m12) -
|
||||
mat.m01 * (mat.m10 * mat.m32 - mat.m30 * mat.m12) +
|
||||
mat.m02 * (mat.m10 * mat.m31 - mat.m30 * mat.m11)),
|
||||
|
||||
-(mat.m01 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
|
||||
mat.m02 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) +
|
||||
mat.m03 * (mat.m11 * mat.m22 - mat.m21 * mat.m12)),
|
||||
(mat.m00 * (mat.m12 * mat.m23 - mat.m22 * mat.m13) -
|
||||
mat.m02 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m22 - mat.m20 * mat.m12)),
|
||||
-(mat.m00 * (mat.m11 * mat.m23 - mat.m21 * mat.m13) -
|
||||
mat.m01 * (mat.m10 * mat.m23 - mat.m20 * mat.m13) +
|
||||
mat.m03 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
|
||||
(mat.m00 * (mat.m11 * mat.m22 - mat.m21 * mat.m12) -
|
||||
mat.m01 * (mat.m10 * mat.m22 - mat.m20 * mat.m12) +
|
||||
mat.m02 * (mat.m10 * mat.m21 - mat.m20 * mat.m11)),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
fn Matrix2x2! Matrix2x2.inverse(Matrix2x2* m)
|
||||
{
|
||||
float det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
|
||||
Matrix2x2 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
fn Matrix3x3! Matrix3x3.inverse(Matrix3x3* m)
|
||||
{
|
||||
float det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
|
||||
Matrix3x3 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
fn Matrix4x4! Matrix4x4.inverse(Matrix4x4* m)
|
||||
{
|
||||
float det = m.determinant();
|
||||
if (det == 0) return MatrixError.MATRIX_INVERSE_DOESNT_EXIST!;
|
||||
Matrix4x4 adj = m.adjoint();
|
||||
return adj.component_mul(1 / det).transpose();
|
||||
}
|
||||
|
||||
|
||||
fn Matrix3x3 Matrix3x3.translate(Matrix3x3* m, float[<2>] v)
|
||||
{
|
||||
return m.mul(Matrix3x3 {
|
||||
1, 0, v[0],
|
||||
0, 1, v[1],
|
||||
0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.translate(Matrix4x4* m, float[<3>] v)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
1, 0, 0, v[0],
|
||||
0, 1, 0, v[1],
|
||||
0, 0, 1, v[2],
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix3x3 Matrix3x3.rotate(Matrix3x3* m, float r)
|
||||
{
|
||||
return m.mul(Matrix3x3 {
|
||||
math::cos(r), -math::sin(r), 0,
|
||||
math::sin(r), math::cos(r), 0,
|
||||
0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix4x4 Matrix4x4.rotate_z(Matrix4x4* m, float r)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
math::cos(r), -math::sin(r), 0, 0,
|
||||
math::sin(r), math::cos(r), 0, 0,
|
||||
0, 0, 1, 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix4x4 Matrix4x4.rotate_y(Matrix4x4* m, float r)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
math::cos(r), 0, -math::sin(r), 0,
|
||||
0, 1, 0, 0,
|
||||
math::sin(r), 0, math::cos(r), 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
// r in radians
|
||||
fn Matrix4x4 Matrix4x4.rotate_x(Matrix4x4* m, float r)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
1, 0, 0, 0,
|
||||
0, math::cos(r), -math::sin(r), 0,
|
||||
0, math::sin(r), math::cos(r), 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
fn Matrix3x3 Matrix3x3.scale(Matrix3x3* m, float[<2>] v)
|
||||
{
|
||||
return m.mul(Matrix3x3 {
|
||||
v[0], 0, 0,
|
||||
0, v[1], 0,
|
||||
0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
fn Matrix4x4 Matrix4x4.scale(Matrix4x4* m, float[<3>] v)
|
||||
{
|
||||
return m.mul(Matrix4x4 {
|
||||
v[0], 0, 0, 0,
|
||||
0, v[1], 0, 0,
|
||||
0, 0, v[2], 0,
|
||||
0, 0, 0, 1,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
fn Matrix4x4 ortho(float left, float right, float top, float bottom, float near, float far)
|
||||
{
|
||||
float width = right - left;
|
||||
float height = top - bottom;
|
||||
float depth = far - near;
|
||||
return Matrix4x4 {
|
||||
2 / width, 0, 0, 0,
|
||||
0, 2 / height, 0, 0,
|
||||
0, 0, -2 / depth, 0,
|
||||
-(right + left) / width, -(top + bottom) / height, -(far + near) / depth, 1,
|
||||
};
|
||||
}
|
||||
|
||||
// fov in radians
|
||||
fn Matrix4x4 perspective(float fov, float aspect_ratio, float near, float far)
|
||||
{
|
||||
|
||||
float top = ((float)math::sin(fov / 2) / (float)math::cos(fov / 2)) * near;
|
||||
float right = top * aspect_ratio;
|
||||
float depth = far - near;
|
||||
return Matrix4x4 {
|
||||
1 / right, 0, 0, 0,
|
||||
0, 1 / top, 0, 0,
|
||||
0, 0, -2 / depth, 0,
|
||||
0, 0, - (far + near) / depth, 1,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ struct SimpleRandom
|
||||
long seed;
|
||||
}
|
||||
|
||||
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66Di64;
|
||||
private const long SIMPLE_RANDOM_MULTIPLIER = 0x5DEECE66D;
|
||||
private const long SIMPLE_RANDOM_ADDEND = 0xB;
|
||||
private const long SIMPLE_RANDOM_MASK = (1i64 << 48) - 1;
|
||||
|
||||
|
||||
284
lib/std/math_i128.c3
Normal file
284
lib/std/math_i128.c3
Normal file
@@ -0,0 +1,284 @@
|
||||
module std::math;
|
||||
|
||||
fn int128 __divti3(int128 a, int128 b) @extname("__divti3") @weak
|
||||
{
|
||||
int128 sign_a = a >> 127; // -1 : 0
|
||||
int128 sign_b = b >> 127; // -1 : 0
|
||||
uint128 unsigned_a = (uint128)(a ^ sign_a) + (-sign_a);
|
||||
uint128 unsigned_b = (uint128)(b ^ sign_b) + (-sign_b);
|
||||
sign_a ^= sign_b; // quotient sign
|
||||
return __udivti3(unsigned_a, unsigned_b) @inline ^ sign_a + (-sign_a);
|
||||
}
|
||||
|
||||
fn uint128 __umodti3(uint128 n, uint128 d) @extname("__umodti3") @weak
|
||||
{
|
||||
// Ignore d = 0
|
||||
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
|
||||
// If n < d then sr is wrapping.
|
||||
// which means we can just return n.
|
||||
if (sr > 127) return n;
|
||||
// If d == 1 and n = MAX
|
||||
if (sr == 127) return 0;
|
||||
sr++;
|
||||
uint128 r = n >> sr;
|
||||
// Follow known algorithm:
|
||||
n <<= 128 - sr;
|
||||
for (uint128 carry = 0; sr > 0; sr--)
|
||||
{
|
||||
r = (r << 1) | (n >> 127);
|
||||
n = (n << 1) | carry;
|
||||
int128 sign = (int128)(d - r - 1) >> 127;
|
||||
carry = sign & 1;
|
||||
r -= d & sign;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
fn uint128 __udivti3(uint128 n, uint128 d) @extname("__udivti3") @weak
|
||||
{
|
||||
// Ignore d = 0
|
||||
uint128 sr = (d ? $$clz(d) : 128) - (n ? $$clz(n) : 128);
|
||||
// If n < d then sr is wrapping.
|
||||
// which means we can just return 0.
|
||||
if (sr > 127) return 0;
|
||||
// If d == 1 and n = MAX
|
||||
if (sr == 127) return n;
|
||||
sr++;
|
||||
uint128 r = n >> sr;
|
||||
// Follow known algorithm:
|
||||
n <<= 128 - sr;
|
||||
uint128 carry = 0;
|
||||
for (; sr > 0; sr--)
|
||||
{
|
||||
r = (r << 1) | (n >> 127);
|
||||
n = (n << 1) | carry;
|
||||
int128 sign = (int128)(d - r - 1) >> 127;
|
||||
carry = sign & 1;
|
||||
r -= d & sign;
|
||||
}
|
||||
n = (n << 1) | carry;
|
||||
return n;
|
||||
}
|
||||
|
||||
fn int128 __modti3(int128 a, int128 b) @extname("__modti3") @weak
|
||||
{
|
||||
int128 sign = b >> 127;
|
||||
uint128 unsigned_b = (uint128)(b ^ sign) + (-sign);
|
||||
sign = a >> 127;
|
||||
uint128 unsigned_a = (uint128)(a ^ sign) + (-sign);
|
||||
|
||||
return __umodti3(unsigned_a, unsigned_b) ^ sign + (-sign);
|
||||
}
|
||||
|
||||
fn float __floattisf(int128 a) @extname("__floattisf") @weak = float_from_i128(float, a);
|
||||
fn double __floattidf(int128 a) @extname("__floattidf") @weak = float_from_i128(double, a);
|
||||
fn float __floatuntisf(uint128 a) @extname("__floatuntisf") @weak = float_from_u128(float, a);
|
||||
fn double __floatuntidf(uint128 a) @extname("__floatuntidf") @weak = float_from_u128(double, a);
|
||||
fn uint128 __fixunsdfti(double a) @weak @extname("__fixunsdfti") = fixuint(a);
|
||||
fn uint128 __fixunssfti(float a) @weak @extname("__fixunssfti") = fixuint(a);
|
||||
fn int128 __fixdfti(double a) @weak @extname("__fixdfti") = fixint(a);
|
||||
fn int128 __fixsfti(float a) @weak @extname("__fixsfti") = fixint(a);
|
||||
|
||||
|
||||
private macro float_from_i128($Type, a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($Type):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
const SIGN_BIT = 1u64 << 63;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
const SIGN_BIT = 1u32 << 31;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
$endswitch;
|
||||
if (a == 0) return ($Type)0;
|
||||
// Grab and remove sign.
|
||||
int128 sign = a >> 127;
|
||||
a = (a ^ sign) - sign;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
int e = sd - 1; // exponent
|
||||
if (sd > MANT_DIG)
|
||||
{
|
||||
switch (sd)
|
||||
{
|
||||
case MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case MANT_DIG + 2:
|
||||
break;
|
||||
default:
|
||||
a = (a >> (sd - (MANT_DIG + 2)))
|
||||
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
|
||||
}
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a <<= (MANT_DIG - sd);
|
||||
}
|
||||
return bitcast(((($Rep)sign & SIGN_BIT) | ((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS)) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
|
||||
}
|
||||
|
||||
private macro float_from_u128($Type, a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($Type):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const MANT_DIG = DOUBLE_MANT_DIG;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
const EXP_BIAS = 1023;
|
||||
const MANTISSA_MASK = 0xFFFFF_FFFF_FFFFu64;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const MANT_DIG = FLOAT_MANT_DIG;
|
||||
const EXP_BIAS = 127;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
const MANTISSA_MASK = 0x7F_FFFFu32;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const MANT_DIG = HALF_MANT_DIG;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const MANT_DIG = QUAD_MANT_DIG;
|
||||
$endswitch;
|
||||
if (a == 0) return ($Type)0;
|
||||
int sd = 128 - (int)$$clz(a); // digits
|
||||
int e = sd - 1; // exponent
|
||||
if (sd > MANT_DIG)
|
||||
{
|
||||
switch (sd)
|
||||
{
|
||||
case MANT_DIG + 1:
|
||||
a <<= 1;
|
||||
case MANT_DIG + 2:
|
||||
break;
|
||||
default:
|
||||
a = (a >> (sd - (MANT_DIG + 2)))
|
||||
| (uint128)((a & ((uint128)(-1) >> ((128 + MANT_DIG + 2) - sd))) != 0);
|
||||
}
|
||||
a |= (uint128)((a & 4) != 0);
|
||||
a++;
|
||||
a >>= 2;
|
||||
if (a & (1i128 << MANT_DIG))
|
||||
{
|
||||
a >>= 1;
|
||||
e++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a <<= (MANT_DIG - sd);
|
||||
}
|
||||
return bitcast(((($Rep)e + ($Rep)EXP_BIAS) << SIGNIFICANT_BITS) | (($Rep)a & ($Rep)MANTISSA_MASK), $Type);
|
||||
}
|
||||
|
||||
|
||||
private macro fixuint(a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($typeof(a)):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const EXPONENT_BITS = 11;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const EXPONENT_BITS = 8;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const EXPONENT_BITS = 5;
|
||||
const SIGNIFICANT_BITS = 10;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const EXPONENT_BITS = 15;
|
||||
const SIGNIFICANT_BITS = 112;
|
||||
$endswitch;
|
||||
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
|
||||
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
|
||||
const $Rep ONE_REP =EXPONENT_BIAS << SIGNIFICANT_BITS;
|
||||
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
|
||||
const $Rep ABS_MASK = SIGN_BIT - 1u;
|
||||
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
|
||||
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
|
||||
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
|
||||
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
|
||||
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
|
||||
const $Rep INF_REP = EXPONENT_MASK;
|
||||
|
||||
$Rep rep = bitcast(a, $Rep);
|
||||
$Rep abs = rep & ABS_MASK;
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (sign == -1 || exponent < 0) return 0u128;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return ~0u128;
|
||||
if (exponent < SIGNIFICANT_BITS) return (uint128)significand >> (SIGNIFICANT_BITS - exponent);
|
||||
return (uint128)significand << (exponent - SIGNIFICANT_BITS);
|
||||
}
|
||||
|
||||
private macro fixint(a)
|
||||
{
|
||||
var $Rep;
|
||||
$switch ($typeof(a)):
|
||||
$case double:
|
||||
$Rep = ulong;
|
||||
const EXPONENT_BITS = 11;
|
||||
const SIGNIFICANT_BITS = 52;
|
||||
$case float:
|
||||
$Rep = uint;
|
||||
const EXPONENT_BITS = 8;
|
||||
const SIGNIFICANT_BITS = 23;
|
||||
$case float16:
|
||||
$Rep = ushort;
|
||||
const EXPONENT_BITS = 5;
|
||||
const SIGNIFICANT_BITS = 10;
|
||||
$case float128:
|
||||
$Rep = uint128;
|
||||
const EXPONENT_BITS = 15;
|
||||
const SIGNIFICANT_BITS = 112;
|
||||
$endswitch;
|
||||
const $Rep MAX_EXPONENT = ($Rep)1 << EXPONENT_BITS - 1u;
|
||||
const $Rep EXPONENT_BIAS = MAX_EXPONENT >> 1u;
|
||||
const $Rep ONE_REP = EXPONENT_BIAS << SIGNIFICANT_BITS;
|
||||
const $Rep SIGN_BIT = ($Rep)1 << (EXPONENT_BITS + SIGNIFICANT_BITS);
|
||||
const $Rep ABS_MASK = SIGN_BIT - 1u;
|
||||
const $Rep IMPLICIT_BIT = ($Rep)1 << SIGNIFICANT_BITS;
|
||||
const $Rep SIGNIFICANT_MASK = IMPLICIT_BIT - 1u;
|
||||
const $Rep EXPONENT_MASK = ABS_MASK ^ SIGNIFICANT_MASK;
|
||||
const $Rep QUIET_BIT = IMPLICIT_BIT >> 1;
|
||||
const $Rep QNAN_REP = EXPONENT_MASK | QUIET_BIT;
|
||||
const $Rep INF_REP = EXPONENT_MASK;
|
||||
|
||||
$Rep rep = bitcast(a, $Rep);
|
||||
$Rep abs = rep & ABS_MASK;
|
||||
int sign = rep & SIGN_BIT ? -1 : 1;
|
||||
int exponent = (int)((abs >> SIGNIFICANT_BITS) - EXPONENT_BIAS);
|
||||
$Rep significand = (abs & SIGNIFICANT_MASK) | IMPLICIT_BIT;
|
||||
if (exponent < 0) return 0;
|
||||
if ((uint)exponent >= uint128.sizeof * 8) return sign == 1 ? int128.max : int128.min;
|
||||
|
||||
if (exponent < SIGNIFICANT_BITS) return sign * ((int128)significand >> (SIGNIFICANT_BITS - exponent));
|
||||
return sign * ((int128)significand << (exponent - SIGNIFICANT_BITS));
|
||||
}
|
||||
209
lib/std/mem.c3
209
lib/std/mem.c3
@@ -1,209 +0,0 @@
|
||||
// Copyright (c) 2021 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::mem;
|
||||
|
||||
macro volatile_load(&x)
|
||||
{
|
||||
return $$volatile_load(&x);
|
||||
}
|
||||
|
||||
macro volatile_store(&x, y)
|
||||
{
|
||||
return $$volatile_store(&x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require @math::is_power_of_2(alignment)
|
||||
**/
|
||||
fn usize aligned_offset(usize offset, usize alignment)
|
||||
{
|
||||
return alignment * ((offset + alignment - 1) / alignment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require @math::is_power_of_2(alignment)
|
||||
**/
|
||||
fn bool ptr_is_aligned(void* ptr, usize alignment) @inline
|
||||
{
|
||||
return (uptr)ptr & ((uptr)alignment - 1) == 0;
|
||||
}
|
||||
|
||||
fn void copy(char* dst, char* src, usize size) @inline
|
||||
{
|
||||
@memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
macro void memcpy(void* dst, void* src, usize size, bool $is_volatile = false, usize $dst_align = 0, usize $src_align = 0)
|
||||
{
|
||||
$$memcpy(dst, src, size, $is_volatile, $dst_align, $src_align);
|
||||
}
|
||||
|
||||
fn void set(void* dst, char val, usize bytes) @inline
|
||||
{
|
||||
@memset(dst, val, bytes);
|
||||
}
|
||||
|
||||
macro void memset(void* dst, char val, usize bytes, bool $is_volatile = false, usize $dst_align = 0)
|
||||
{
|
||||
$$memset(dst, val, bytes, $is_volatile, $dst_align);
|
||||
}
|
||||
|
||||
macro bitcast(expr, $Type)
|
||||
{
|
||||
var $size = (usize)($sizeof(expr));
|
||||
$assert($size == $Type.sizeof, "Cannot bitcast between types of different size.");
|
||||
$Type x = void;
|
||||
@memcpy(&x, &expr, $size, false, $alignof($Type), $alignof(expr));
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
enum AllocationKind
|
||||
{
|
||||
ALLOC,
|
||||
CALLOC,
|
||||
REALLOC,
|
||||
FREE,
|
||||
RESET,
|
||||
}
|
||||
|
||||
fault AllocationFailure
|
||||
{
|
||||
OUT_OF_MEMORY,
|
||||
UNSUPPORTED_OPERATION,
|
||||
}
|
||||
|
||||
private tlocal Allocator thread_allocator = { null, SYSTEM_ALLOCATOR };
|
||||
|
||||
struct Allocator
|
||||
{
|
||||
void* data;
|
||||
AllocatorFunction function;
|
||||
}
|
||||
|
||||
macro malloc($Type)
|
||||
{
|
||||
return ($Type*)(mem::alloc($Type.sizeof));
|
||||
}
|
||||
|
||||
fn char[] alloc_bytes(usize bytes) @inline
|
||||
{
|
||||
return ((char*)thread_allocator.alloc(bytes, 1))[0..bytes - 1]!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void* alloc(usize size, usize alignment = 0)
|
||||
{
|
||||
return thread_allocator.alloc(size, alignment)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! alloc_checked(usize size, usize alignment = 0)
|
||||
{
|
||||
return thread_allocator.alloc(size, alignment);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void* calloc(usize size, usize alignment = 0)
|
||||
{
|
||||
return thread_allocator.calloc(size, alignment)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! calloc_checked(usize size, usize alignment = 0)
|
||||
{
|
||||
return thread_allocator.calloc(size, alignment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void* realloc(void *ptr, usize new_size, usize alignment = 0)
|
||||
{
|
||||
return thread_allocator.realloc(ptr, new_size, alignment)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! realloc_checked(void *ptr, usize new_size, usize alignment = 0)
|
||||
{
|
||||
return thread_allocator.realloc(ptr, new_size, alignment);
|
||||
}
|
||||
|
||||
fn void free(void* ptr)
|
||||
{
|
||||
return thread_allocator.free(ptr)!!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Run with a specific allocator inside of the macro body.
|
||||
**/
|
||||
macro void with_allocator(Allocator allocator; @body())
|
||||
{
|
||||
Allocator old_allocator = thread_allocator;
|
||||
thread_allocator = allocator;
|
||||
defer thread_allocator = old_allocator;
|
||||
@body();
|
||||
}
|
||||
|
||||
fn void*! talloc(usize size)
|
||||
{
|
||||
return temp_allocator.alloc(size);
|
||||
}
|
||||
|
||||
struct MemoryArena
|
||||
{
|
||||
void* memory;
|
||||
usize total;
|
||||
usize used;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require alignment > 0 `alignment must be non zero`
|
||||
* @require @math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require alignment <= MAX_MEMORY_ALIGNMENT `alignment too big`
|
||||
* @require this != null
|
||||
**/
|
||||
fn void*! MemoryArena.alloc(MemoryArena* this, usize size, usize alignment, usize prefixed_bytes = 0)
|
||||
{
|
||||
void* start_mem = this.memory;
|
||||
void* unaligned_pointer = start_mem + this.used + prefixed_bytes;
|
||||
if ((uptr)unaligned_pointer < (uptr)start_mem) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
usize offset_start = aligned_offset((usize)(uptr)unaligned_pointer, alignment) - (usize)(uptr)start_mem;
|
||||
usize end = offset_start + size;
|
||||
if (end > this.total || end < offset_start) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
this.used = end;
|
||||
return start_mem + offset_start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a memory arena for use using the provided bytes.
|
||||
*
|
||||
* @require this != null
|
||||
**/
|
||||
fn void MemoryArena.init(MemoryArena* this, char[] data)
|
||||
{
|
||||
this.memory = data.ptr;
|
||||
this.total = data.len;
|
||||
this.used = 0;
|
||||
}
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void MemoryArena.reset(MemoryArena* this)
|
||||
{
|
||||
this.used = 0;
|
||||
}
|
||||
@@ -1,355 +0,0 @@
|
||||
module std::mem;
|
||||
|
||||
define AllocatorFunction = fn void*!(void* alloc_data, usize new_size, usize alignment, void* old_pointer, AllocationKind kind);
|
||||
|
||||
const MAX_MEMORY_ALIGNMENT = 0x1000_0000;
|
||||
const DEFAULT_MEM_ALIGNMENT = $alignof(void*) * 2;
|
||||
const DEFAULT_SIZE_PREFIX = usize.sizeof;
|
||||
const DEFAULT_SIZE_PREFIX_ALIGNMENT = $alignof(usize);
|
||||
|
||||
Allocator main_allocator = { null, SYSTEM_ALLOCATOR };
|
||||
|
||||
const AllocatorFunction NULL_ALLOCATOR = &null_allocator_fn;
|
||||
const AllocatorFunction SYSTEM_ALLOCATOR = &libc_allocator_fn;
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.alloc(Allocator *allocator, usize size, usize alignment = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator.data, size, alignment, null, ALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.realloc(Allocator *allocator, void* old_pointer, usize size, usize alignment = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator.data, size, alignment, old_pointer, REALLOC);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
*/
|
||||
fn void*! Allocator.calloc(Allocator *allocator, usize size, usize alignment = 0) @inline
|
||||
{
|
||||
return allocator.function(allocator.data, size, alignment, null, CALLOC);
|
||||
}
|
||||
|
||||
fn void! Allocator.free(Allocator *allocator, void* old_pointer) @inline
|
||||
{
|
||||
allocator.function(allocator.data, 0, 0, old_pointer, FREE)?;
|
||||
}
|
||||
|
||||
fn void Allocator.reset(Allocator *allocator)
|
||||
{
|
||||
allocator.function(allocator.data, 0, 0, null, RESET)!!;
|
||||
}
|
||||
|
||||
|
||||
fn Allocator arena_allocator(MemoryArena *arena)
|
||||
{
|
||||
return { arena, &arena_allocator_function };
|
||||
}
|
||||
|
||||
fn Allocator dynamic_arena_allocator(DynamicArenaAllocator* allocator)
|
||||
{
|
||||
return { allocator, &dynamic_arena_allocator_function };
|
||||
}
|
||||
|
||||
|
||||
private fn usize alignment_for_allocation(usize alignment) @inline
|
||||
{
|
||||
if (alignment < DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
alignment = DEFAULT_SIZE_PREFIX_ALIGNMENT;
|
||||
}
|
||||
return alignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
MemoryArena* arena = data;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
case ALLOC:
|
||||
assert(!old_pointer, "Unexpected old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?;
|
||||
*(usize*)(mem - DEFAULT_SIZE_PREFIX) = size;
|
||||
if (kind == AllocationKind.CALLOC) mem::set(mem, 0, size);
|
||||
return mem;
|
||||
case REALLOC:
|
||||
if (!size) nextcase FREE;
|
||||
if (!old_pointer) nextcase ALLOC;
|
||||
assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator.");
|
||||
if (size > arena.total) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usize* old_size_ptr = (usize*)(old_pointer - DEFAULT_SIZE_PREFIX);
|
||||
usize old_size = *old_size_ptr;
|
||||
// Do last allocation and alignment match?
|
||||
if (arena.memory + arena.used == old_pointer + old_size && ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
if (old_size >= size)
|
||||
{
|
||||
*old_size_ptr = size;
|
||||
arena.used -= old_size - size;
|
||||
return old_pointer;
|
||||
}
|
||||
usize new_used = arena.used + size - old_size;
|
||||
if (new_used > arena.total) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
arena.used = new_used;
|
||||
*old_size_ptr = size;
|
||||
return old_pointer;
|
||||
}
|
||||
// Otherwise just allocate new memory.
|
||||
void* mem = arena.alloc(size, alignment, DEFAULT_SIZE_PREFIX)?;
|
||||
*(usize*)(mem - DEFAULT_SIZE_PREFIX) = size;
|
||||
copy(mem, old_pointer, old_size);
|
||||
return mem;
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
assert((uptr)old_pointer >= (uptr)arena.memory, "Pointer originates from a different allocator.");
|
||||
usize old_size = *(usize*)(old_pointer - DEFAULT_SIZE_PREFIX);
|
||||
if (old_pointer + old_size == arena.memory + arena.used)
|
||||
{
|
||||
arena.used -= old_size;
|
||||
}
|
||||
return null;
|
||||
case RESET:
|
||||
arena.used = 0;
|
||||
return null;
|
||||
}
|
||||
@unreachable();
|
||||
}
|
||||
|
||||
struct DynamicArenaAllocator
|
||||
{
|
||||
Allocator backing_allocator;
|
||||
DynamicArenaPage* page;
|
||||
DynamicArenaPage* unused_page;
|
||||
usize page_size;
|
||||
}
|
||||
|
||||
private struct DynamicArenaPage
|
||||
{
|
||||
void* memory;
|
||||
void* prev_arena;
|
||||
usize total;
|
||||
usize used;
|
||||
void* last_ptr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
* @require data `unexpectedly missing the allocator`
|
||||
*/
|
||||
fn void*! dynamic_arena_allocator_function(void* data, usize size, usize alignment, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
DynamicArenaAllocator* allocator = data;
|
||||
switch (kind)
|
||||
{
|
||||
case CALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for calloc.");
|
||||
if (!size) return null;
|
||||
void* mem = allocator.alloc(size, alignment)?;
|
||||
set(mem, 0, size);
|
||||
return mem;
|
||||
case ALLOC:
|
||||
assert(!old_pointer, "Unexpected no old pointer for alloc.");
|
||||
if (!size) return null;
|
||||
return allocator.alloc(size, alignment);
|
||||
case REALLOC:
|
||||
if (!size)
|
||||
{
|
||||
if (!old_pointer) return null;
|
||||
allocator.free(old_pointer);
|
||||
return null;
|
||||
}
|
||||
if (!old_pointer) return allocator.alloc(size, alignment);
|
||||
void* mem = allocator.realloc(old_pointer, size, alignment)?;
|
||||
return mem;
|
||||
case FREE:
|
||||
if (!old_pointer) return null;
|
||||
allocator.free(old_pointer);
|
||||
return null;
|
||||
case RESET:
|
||||
allocator.reset();
|
||||
return null;
|
||||
}
|
||||
@unreachable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require page_size >= 128
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.init(DynamicArenaAllocator* this, usize page_size, Allocator* backing_allocator = null)
|
||||
{
|
||||
this.page = null;
|
||||
this.unused_page = null;
|
||||
this.page_size = page_size;
|
||||
this.backing_allocator = backing_allocator ? *backing_allocator : main_allocator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require this != null
|
||||
**/
|
||||
fn void DynamicArenaAllocator.destroy(DynamicArenaAllocator* this)
|
||||
{
|
||||
DynamicArenaPage* page = this.page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
this.backing_allocator.free(page);
|
||||
page = next_page;
|
||||
}
|
||||
page = this.unused_page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
this.backing_allocator.free(page);
|
||||
page = next_page;
|
||||
}
|
||||
this.page = null;
|
||||
this.unused_page = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require ptr && this
|
||||
* @require this.page `tried to free pointer on invalid allocator`
|
||||
*/
|
||||
private fn void DynamicArenaAllocator.free(DynamicArenaAllocator* this, void* ptr)
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
if (ptr == current_page.last_ptr)
|
||||
{
|
||||
current_page.used = (usize)((ptr - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
current_page.last_ptr = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require old_pointer && size > 0
|
||||
* @require this.page `tried to realloc pointer on invalid allocator`
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator.realloc(DynamicArenaAllocator* this, void* old_pointer, usize size, usize alignment)
|
||||
{
|
||||
DynamicArenaPage* current_page = this.page;
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
usize* old_size_ptr = old_pointer - DEFAULT_SIZE_PREFIX;
|
||||
usize old_size = *old_size_ptr;
|
||||
// We have the old pointer and it's correctly aligned.
|
||||
if (old_size >= size && ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
*old_size_ptr = size;
|
||||
if (current_page.last_ptr == old_pointer)
|
||||
{
|
||||
current_page.used = (usize)((old_pointer - DEFAULT_SIZE_PREFIX) - current_page.memory);
|
||||
}
|
||||
return old_pointer;
|
||||
}
|
||||
if REUSE: (current_page.last_ptr == old_pointer && ptr_is_aligned(old_pointer, alignment))
|
||||
{
|
||||
assert(size > old_size);
|
||||
usize add_size = size - old_size;
|
||||
if (add_size + current_page.used > current_page.total) break REUSE;
|
||||
*old_size_ptr = size;
|
||||
current_page.used += add_size;
|
||||
return old_pointer;
|
||||
}
|
||||
void* new_mem = this.alloc(size, alignment)?;
|
||||
copy(new_mem, old_pointer, old_size);
|
||||
return new_mem;
|
||||
}
|
||||
|
||||
private fn void DynamicArenaAllocator.reset(DynamicArenaAllocator* this)
|
||||
{
|
||||
DynamicArenaPage* page = this.page;
|
||||
DynamicArenaPage** unused_page_ptr = &this.unused_page;
|
||||
while (page)
|
||||
{
|
||||
DynamicArenaPage* next_page = page.prev_arena;
|
||||
page.used = 0;
|
||||
DynamicArenaPage* prev_unused = *unused_page_ptr;
|
||||
*unused_page_ptr = page;
|
||||
page.prev_arena = prev_unused;
|
||||
page = next_page;
|
||||
}
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require @math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator.alloc_new(DynamicArenaAllocator* this, usize size, usize alignment)
|
||||
{
|
||||
usize page_size = @max(this.page_size, size + DEFAULT_SIZE_PREFIX + alignment);
|
||||
void* mem = this.backing_allocator.alloc(page_size)?;
|
||||
DynamicArenaPage*! page = this.backing_allocator.alloc(DynamicArenaPage.sizeof);
|
||||
if (catch err = page)
|
||||
{
|
||||
this.backing_allocator.free(mem);
|
||||
return err!;
|
||||
}
|
||||
page.memory = mem;
|
||||
usize offset = aligned_offset((usize)mem + DEFAULT_SIZE_PREFIX, alignment) - (usize)mem;
|
||||
usize* size_ptr = mem + offset - DEFAULT_SIZE_PREFIX;
|
||||
*size_ptr = size;
|
||||
page.prev_arena = this.page;
|
||||
page.total = page_size;
|
||||
page.used = size + offset;
|
||||
this.page = page;
|
||||
|
||||
return page.last_ptr = page.memory + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require !alignment || @math::is_power_of_2(alignment)
|
||||
* @require size > 0
|
||||
* @require this
|
||||
*/
|
||||
private fn void*! DynamicArenaAllocator.alloc(DynamicArenaAllocator* this, usize size, usize alignment)
|
||||
{
|
||||
alignment = alignment_for_allocation(alignment);
|
||||
DynamicArenaPage* page = this.page;
|
||||
if (!page && this.unused_page)
|
||||
{
|
||||
this.page = page = this.unused_page;
|
||||
this.unused_page = page.prev_arena;
|
||||
page.prev_arena = null;
|
||||
}
|
||||
if (!page) return this.alloc_new(size, alignment);
|
||||
usize start = aligned_offset((uptr)page.memory + page.used + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory;
|
||||
usize new_used = start + size;
|
||||
if ALLOCATE_NEW: (new_used > page.total)
|
||||
{
|
||||
if ((page = this.unused_page))
|
||||
{
|
||||
start = aligned_offset((uptr)page.memory + DEFAULT_SIZE_PREFIX, alignment) - (usize)page.memory;
|
||||
new_used = start + size;
|
||||
if (page.total >= new_used)
|
||||
{
|
||||
this.unused_page = page.prev_arena;
|
||||
page.prev_arena = this.page;
|
||||
this.page = page;
|
||||
break ALLOCATE_NEW;
|
||||
}
|
||||
}
|
||||
return this.alloc_new(size, alignment);
|
||||
}
|
||||
page.used = new_used;
|
||||
void* mem = page.memory + start;
|
||||
usize* size_offset = mem - DEFAULT_SIZE_PREFIX;
|
||||
*size_offset = size;
|
||||
return mem;
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
module std::mem;
|
||||
|
||||
private fn void*! null_allocator_fn(void *data, usize bytes, usize alignment, void* old_pointer, AllocationKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ALLOC:
|
||||
case CALLOC:
|
||||
case REALLOC:
|
||||
return AllocationFailure.OUT_OF_MEMORY!;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
fn void*! libc_allocator_fn(void *unused, usize bytes, usize alignment, void* old_pointer, AllocationKind kind) @inline
|
||||
{
|
||||
if (!alignment) alignment = DEFAULT_MEM_ALIGNMENT;
|
||||
assert(@math::is_power_of_2(alignment), "Alignment was not a power of 2");
|
||||
|
||||
void* data;
|
||||
switch (kind)
|
||||
{
|
||||
case ALLOC:
|
||||
if (alignment > DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
data = (void*)aligned_offset((iptr)libc::malloc(bytes + alignment), alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = libc::malloc(bytes);
|
||||
}
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
return data;
|
||||
case CALLOC:
|
||||
if (alignment > DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
data = (void*)aligned_offset((iptr)libc::calloc(bytes + alignment, 1), alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = libc::malloc(bytes);
|
||||
}
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
return data;
|
||||
case REALLOC:
|
||||
if (alignment > DEFAULT_MEM_ALIGNMENT)
|
||||
{
|
||||
data = (void*)aligned_offset((iptr)libc::realloc(old_pointer, bytes + alignment), alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = libc::realloc(old_pointer, bytes);
|
||||
}
|
||||
if (!data) return AllocationFailure.OUT_OF_MEMORY!;
|
||||
return data;
|
||||
case RESET:
|
||||
return AllocationFailure.UNSUPPORTED_OPERATION!;
|
||||
case FREE:
|
||||
libc::free(old_pointer);
|
||||
return null;
|
||||
}
|
||||
@unreachable();
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
module std::mem;
|
||||
|
||||
const TEMP_BLOCK_SIZE = 1024;
|
||||
const TEMP_PAGES = 64;
|
||||
|
||||
private char[TEMP_BLOCK_SIZE * TEMP_PAGES] allocator_static_storage;
|
||||
private void*[TEMP_PAGES] allocator_static_page_storage;
|
||||
|
||||
SlotAllocator temp_allocator = {
|
||||
.pages = &allocator_static_storage,
|
||||
.page_size = TEMP_BLOCK_SIZE,
|
||||
.page_count = TEMP_PAGES,
|
||||
.bitmask = TEMP_PAGES - 1,
|
||||
.current_page = 0,
|
||||
};
|
||||
|
||||
struct SlotAllocator
|
||||
{
|
||||
void* pages;
|
||||
usize page_size;
|
||||
usize page_count;
|
||||
usize bitmask;
|
||||
usize current_page;
|
||||
}
|
||||
|
||||
fn void*! SlotAllocator.alloc(SlotAllocator *allocator, usize size)
|
||||
{
|
||||
void* active_page = (char*)(allocator.pages) + allocator.current_page * allocator.page_size;
|
||||
void** page_pointer = (void**)(active_page);
|
||||
if (*page_pointer)
|
||||
{
|
||||
// TODO fix
|
||||
main_allocator.free(*page_pointer)?;
|
||||
*page_pointer = null;
|
||||
}
|
||||
if (size > allocator.page_size - $sizeof(page_pointer))
|
||||
{
|
||||
void* mem = main_allocator.alloc(size)?;
|
||||
*page_pointer = mem;
|
||||
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
|
||||
return mem;
|
||||
}
|
||||
allocator.current_page = (allocator.current_page + 1) & (allocator.bitmask);
|
||||
return &page_pointer[1];
|
||||
}
|
||||
|
||||
|
||||
struct RingAllocator
|
||||
{
|
||||
char *data;
|
||||
usize size;
|
||||
usize offset;
|
||||
}
|
||||
|
||||
|
||||
fn void* RingAllocator.alloc(RingAllocator *allocator, usize size)
|
||||
{
|
||||
if (size > allocator.size) return null;
|
||||
// Wraparound? If so, start at the beginning.
|
||||
if (allocator.offset + size > allocator.size)
|
||||
{
|
||||
allocator.offset = size;
|
||||
return allocator.data;
|
||||
}
|
||||
void* data = allocator.offset + allocator.data;
|
||||
allocator.offset = (allocator.offset + size) & allocator.size;
|
||||
return data;
|
||||
}
|
||||
|
||||
fn void* RingAllocator.realloc(RingAllocator *allocator, void* ptr, usize size)
|
||||
{
|
||||
if (size > allocator.size) return null;
|
||||
assert(allocator.data >= ptr && ptr < allocator.data + size, "Realloc on other allocator.");
|
||||
// 1. The pointer is before the allocator
|
||||
if (allocator.data + allocator.offset > ptr)
|
||||
{
|
||||
if (allocator.data + allocator.size < ptr + size)
|
||||
{
|
||||
// 1a. There is not enough space, we need to copy to the start.
|
||||
usize pointer_offset = ptr - allocator.data;
|
||||
usize copy_len = pointer_offset + size > allocator.offset ? allocator.offset - pointer_offset : size;
|
||||
//memcpy(allocator.data, ptr, copy_len);
|
||||
allocator.offset = size;
|
||||
return allocator.data;
|
||||
}
|
||||
// 1b. There is enough space, so we just change the offset:
|
||||
allocator.offset = ptr - allocator.data + size;
|
||||
return ptr;
|
||||
}
|
||||
// 2. The pointer is after the allocator
|
||||
// 2a. Is there sufficient space?
|
||||
if (ptr + size <= allocator.data + allocator.size)
|
||||
{
|
||||
// Good, if so we simply change the offset and return the pointer.
|
||||
allocator.offset = ptr - allocator.data + size;
|
||||
return ptr;
|
||||
}
|
||||
// 2b. Not sufficient space, we copy to the beginning.
|
||||
usize pointer_offset = ptr - allocator.data;
|
||||
usize copy_len = allocator.size - (ptr - allocator.data);
|
||||
if (copy_len > size) copy_len = size;
|
||||
//memcpy(allocator.data, ptr, copy_len);
|
||||
allocator.offset = size;
|
||||
return allocator.data;
|
||||
}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
module std::os::linux;
|
||||
import std::env;
|
||||
|
||||
$if (env::OS_TYPE == OsType.LINUX):
|
||||
|
||||
extern fn int* __errno_location();
|
||||
|
||||
fn int errno() @inline
|
||||
{
|
||||
return *__errno_location();
|
||||
}
|
||||
|
||||
fn void errno_set(int err)
|
||||
{
|
||||
*(__errno_location()) = err;
|
||||
}
|
||||
|
||||
$endif;
|
||||
@@ -1,16 +0,0 @@
|
||||
module std::os::macos;
|
||||
import std::env;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
extern fn int* __error();
|
||||
|
||||
fn int errno() @inline
|
||||
{
|
||||
return *__error();
|
||||
}
|
||||
fn void errno_set(int err)
|
||||
{
|
||||
*(__error()) = err;
|
||||
}
|
||||
$endif;
|
||||
21
lib/std/os/macos/cf_allocator.c3
Normal file
21
lib/std/os/macos/cf_allocator.c3
Normal file
@@ -0,0 +1,21 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define CFAllocatorRef = distinct void*;
|
||||
define CFAllocatorContextRef = distinct void*;
|
||||
define CFOptionFlags = usz;
|
||||
|
||||
macro CFAllocatorRef default_allocator() = _macos_CFAllocatorGetDefault();
|
||||
macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) = _macos_CFAllocatorDeallocate(allocator, ptr);
|
||||
macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) = _macos_CFAllocatorAllocate(allocator, size, 0);
|
||||
macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) = _macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0);
|
||||
macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) = _macos_CFAllocatorSetDefault(allocator);
|
||||
|
||||
extern fn CFAllocatorRef _macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extname("CFAllocatorCreate");
|
||||
extern fn void _macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extname("CFAllocatorDeallocate");
|
||||
extern fn CFAllocatorRef _macos_CFAllocatorGetDefault() @extname("CFAllocatorGetDefault");
|
||||
extern fn void _macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extname("CFAllocatorSetDefault");
|
||||
extern fn void* _macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extname("CFAllocatorAllocate");
|
||||
extern fn CFIndex _macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extname("CFAllocatorGetPreferredSizeForSize");
|
||||
$endif;
|
||||
14
lib/std/os/macos/cf_array.c3
Normal file
14
lib/std/os/macos/cf_array.c3
Normal file
@@ -0,0 +1,14 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define CFArrayRef = distinct void*;
|
||||
define CFArrayCallBacksRef = distinct void*;
|
||||
define CFMutableArrayRef = distinct void*;
|
||||
extern fn CFArrayRef _macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extname("CFArrayCreate");
|
||||
extern fn CFArrayRef _macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extname("CFArrayCopy");
|
||||
extern fn CFIndex _macos_CFArrayGetCount(CFArrayRef array) @extname("CFArrayGetCount");
|
||||
extern fn void _macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extname("CFArrayAppendArray");
|
||||
extern fn CFMutableArrayRef _macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extname("CFArrayCreateMutable");
|
||||
extern fn void _macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extname("CFArrayAppendValue");
|
||||
$endif;
|
||||
16
lib/std/os/macos/core_foundation.c3
Normal file
16
lib/std/os/macos/core_foundation.c3
Normal file
@@ -0,0 +1,16 @@
|
||||
module std::os::macos::cf;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define CFTypeRef = distinct void*;
|
||||
define CFIndex = isz;
|
||||
struct CFRange
|
||||
{
|
||||
CFIndex location;
|
||||
CFIndex length;
|
||||
}
|
||||
|
||||
extern fn CFTypeRef _macos_CFRetain(CFTypeRef cf) @extname("CFRetain");
|
||||
extern fn void _macos_CFRelease(CFTypeRef cf) @extname("CFRelease");
|
||||
|
||||
$endif;
|
||||
49
lib/std/os/macos/objc.c3
Normal file
49
lib/std/os/macos/objc.c3
Normal file
@@ -0,0 +1,49 @@
|
||||
module std::os::macos::objc;
|
||||
|
||||
$if (env::OS_TYPE == OsType.MACOSX):
|
||||
|
||||
define Class = distinct void*;
|
||||
define Method = distinct void*;
|
||||
define Ivar = distinct void*;
|
||||
define Selector = distinct void*;
|
||||
|
||||
fault ObjcFailure
|
||||
{
|
||||
CLASS_NOT_FOUND
|
||||
}
|
||||
|
||||
macro char* Class.name(Class cls) = _macos_class_getName(cls);
|
||||
macro Class Class.superclass(Class cls) = _macos_class_getSuperclass(cls);
|
||||
macro bool Class.responds_to(Class cls, Selector sel) = _macos_class_respondsToSelector(cls, sel);
|
||||
macro Method Class.method(Class cls, Selector name) = _macos_class_getClassMethod(cls, name);
|
||||
|
||||
macro bool Selector.equals(Selector a, Selector b) = a == b;
|
||||
macro bool Class.equals(Class a, Class b) = a == b;
|
||||
|
||||
macro Selector selector_register(char* c) = _macos_sel_registerName(c);
|
||||
macro Class! class_by_name(char* c)
|
||||
{
|
||||
Class cls = _macos_objc_lookUpClass(c);
|
||||
if (!cls) return ObjcFailure.CLASS_NOT_FOUND!;
|
||||
return cls;
|
||||
}
|
||||
|
||||
macro Class[] class_get_list(Allocator *allocator = mem::current_allocator())
|
||||
{
|
||||
int num_classes = _macos_objc_getClassList(null, 0);
|
||||
if (!num_classes) return {};
|
||||
Class[] entries = array::make(Class, num_classes, allocator);
|
||||
_macos_objc_getClassList(entries.ptr, entries.len);
|
||||
return entries;
|
||||
}
|
||||
|
||||
extern fn Class _macos_objc_getClass(char* name) @extname("objc_getClass");
|
||||
extern fn int _macos_objc_getClassList(Class* buffer, int buffer_count) @extname("objc_getClassList");
|
||||
extern fn char* _macos_class_getName(Class cls) @extname("class_getName");
|
||||
extern fn Class _macos_class_getSuperclass(Class cls) @extname("class_getSuperclass");
|
||||
extern fn Method _macos_class_getClassMethod(Class cls, Selector name) @extname("class_getClassMethod");
|
||||
extern fn bool _macos_class_respondsToSelector(Class cls, Selector name) @extname("class_respondsToSelector");
|
||||
extern fn Selector _macos_sel_registerName(char* str) @extname("sel_registerName");
|
||||
extern fn Class _macos_objc_lookUpClass(char* name) @extname("objc_lookUpClass");
|
||||
$endif;
|
||||
|
||||
12
lib/std/os/win32/files.c3
Normal file
12
lib/std/os/win32/files.c3
Normal file
@@ -0,0 +1,12 @@
|
||||
module std::os::win32::files;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
/*
|
||||
private extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extname("GetCurrentDirectoryW");
|
||||
private extern bool _win32_CreateSymbolicLinkW(Char16* symlink_file, Char16* target_file, ulong flags) @extname("CreateSymbolicLinkW");
|
||||
private extern bool _win32_CreateDirectoryW(Char16* path_name, void* security_attributes) @extname("CreateDirectoryW");
|
||||
private extern bool _win32_DeleteFileW(Char16* file) @extname("DeleteFileW");
|
||||
private extern bool _win32_CopyFileW(Char16* from_file, Char16* to_file, bool no_overwrite) @extname("CopyFileW");
|
||||
private extern ulong _win32_GetFullPathNameW(Char16* file_name, ulong buffer_len, Char16* buffer, Char16** file_part) @extname("GetFullPathNameW");
|
||||
*/
|
||||
$endif;
|
||||
@@ -1,11 +0,0 @@
|
||||
module std::os::windows;
|
||||
import std::env;
|
||||
|
||||
$if (env::OS_TYPE == OsType.WIN32):
|
||||
|
||||
extern fn int getLastError() @stdcall @extname("GetLastError");
|
||||
fn int errno() @inline
|
||||
{
|
||||
return getLastError();
|
||||
}
|
||||
$endif;
|
||||
113
lib/std/priorityqueue.c3
Normal file
113
lib/std/priorityqueue.c3
Normal file
@@ -0,0 +1,113 @@
|
||||
// priorityqueue.c3
|
||||
// A priority queue using a classic binary heap for C3.
|
||||
//
|
||||
// Copyright (c) 2022 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
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// 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::priorityqueue<Type>;
|
||||
import std::array::list;
|
||||
|
||||
define Heap = List<Type>;
|
||||
|
||||
struct PriorityQueue
|
||||
{
|
||||
Heap heap;
|
||||
bool max; // true if max-heap, false if min-heap
|
||||
}
|
||||
|
||||
fn void PriorityQueue.push(PriorityQueue* pq, Type element)
|
||||
{
|
||||
pq.heap.push(element);
|
||||
usz i = pq.heap.len() - 1;
|
||||
while (i > 0)
|
||||
{
|
||||
usz parent = (i - 1) / 2;
|
||||
if ((pq.max && greater(pq.heap.get(i), pq.heap.get(parent))) || (!pq.max && less(pq.heap.get(i), pq.heap.get(parent))))
|
||||
{
|
||||
pq.heap.swap(i, parent);
|
||||
i = parent;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn Type! PriorityQueue.pop(PriorityQueue* pq)
|
||||
{
|
||||
usz i = 0;
|
||||
usz len = pq.heap.len() @inline;
|
||||
if (!len) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
usz newCount = len - 1;
|
||||
pq.heap.swap(0, newCount);
|
||||
while ((2 * i + 1) < newCount)
|
||||
{
|
||||
usz j = 2 * i + 1;
|
||||
if (((j + 1) < newCount) &&
|
||||
((pq.max && greater(pq.heap.get(j + 1), pq.heap[j]))
|
||||
|| (!pq.max && less(pq.heap.get(j + 1), pq.heap.get(j)))))
|
||||
{
|
||||
j++;
|
||||
}
|
||||
if ((pq.max && less(pq.heap.get(i), pq.heap.get(j))) || (!pq.max && greater(pq.heap.get(i), pq.heap.get(j))))
|
||||
{
|
||||
pq.heap.swap(i, j);
|
||||
i = j;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return pq.heap.pop();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn Type! PriorityQueue.peek(PriorityQueue* pq)
|
||||
{
|
||||
if (!pq.len()) return IteratorResult.NO_MORE_ELEMENT!;
|
||||
return pq.heap.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn void PriorityQueue.free(PriorityQueue* pq)
|
||||
{
|
||||
pq.heap.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null
|
||||
*/
|
||||
fn usz PriorityQueue.len(PriorityQueue* pq) @operator(len)
|
||||
{
|
||||
return pq.heap.len();
|
||||
}
|
||||
|
||||
/**
|
||||
* @require pq != null, index < pq.len()
|
||||
*/
|
||||
fn Type PriorityQueue.peek_at(PriorityQueue* pq, usz index) @operator([])
|
||||
{
|
||||
return pq.heap[index];
|
||||
}
|
||||
@@ -18,13 +18,82 @@ struct VirtualContainer
|
||||
struct SubArrayContainer
|
||||
{
|
||||
void* ptr;
|
||||
usize len;
|
||||
usz len;
|
||||
}
|
||||
|
||||
struct VarArrayHeader
|
||||
{
|
||||
usize size;
|
||||
usize capacity;
|
||||
usz size;
|
||||
usz capacity;
|
||||
void *allocator;
|
||||
}
|
||||
|
||||
define TestFn = fn void!();
|
||||
|
||||
struct TestRunner
|
||||
{
|
||||
char[][] test_names;
|
||||
TestFn[] test_fns;
|
||||
JmpBuf buf;
|
||||
}
|
||||
|
||||
fn TestRunner test_runner_create()
|
||||
{
|
||||
return TestRunner {
|
||||
.test_fns = $$TEST_FNS,
|
||||
.test_names = $$TEST_NAMES,
|
||||
};
|
||||
}
|
||||
|
||||
import libc;
|
||||
|
||||
private TestRunner* current_runner;
|
||||
fn void test_panic(char[] message, char[] file, char[] function, uint line)
|
||||
{
|
||||
io::println("[error]");
|
||||
io::printfln("\n Error: %s", message);
|
||||
io::printfln(" - in %s %s:%s.\n", function, file, line);
|
||||
libc::longjmp(¤t_runner.buf, 1);
|
||||
}
|
||||
|
||||
fn bool TestRunner.run(TestRunner* runner)
|
||||
{
|
||||
current_runner = runner;
|
||||
PanicFn old_panic = builtin::panic;
|
||||
defer builtin::panic = old_panic;
|
||||
builtin::panic = &test_panic;
|
||||
int tests_passed = 0;
|
||||
int tests = runner.test_names.len;
|
||||
io::println("----- TESTS -----");
|
||||
foreach(i, char[] name : runner.test_names)
|
||||
{
|
||||
io::printf("Testing %s ... ", name);
|
||||
if (libc::setjmp(&runner.buf) == 0)
|
||||
{
|
||||
if (catch err = runner.test_fns[i]())
|
||||
{
|
||||
io::println("[failed]");
|
||||
continue;
|
||||
}
|
||||
io::println("[ok]");
|
||||
tests_passed++;
|
||||
}
|
||||
}
|
||||
io::printfln("\n%d test(s) run.\n", tests);
|
||||
io::print("Test Result: ");
|
||||
if (tests_passed < tests)
|
||||
{
|
||||
io::print("FAILED");
|
||||
}
|
||||
else
|
||||
{
|
||||
io::print("ok");
|
||||
}
|
||||
io::printfln(". %d passed, %d failed.", tests_passed, tests - tests_passed);
|
||||
return tests == tests_passed;
|
||||
}
|
||||
|
||||
fn bool __run_default_test_runner()
|
||||
{
|
||||
return test_runner_create().run();
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
module std::str;
|
||||
import std::mem;
|
||||
|
||||
define ZString = char*;
|
||||
define String = char[];
|
||||
|
||||
fn ZString copy_zstring(String s)
|
||||
{
|
||||
usize len = s.len;
|
||||
char* str = mem::alloc(len + 1);
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
fn ZString tcopy_zstring(String s)
|
||||
{
|
||||
usize len = s.len;
|
||||
char* str = mem::talloc(len + 1)!!;
|
||||
mem::copy(str, s.ptr, len);
|
||||
str[len] = 0;
|
||||
return str;
|
||||
}
|
||||
|
||||
fn String copy(String s)
|
||||
{
|
||||
usize len = s.len;
|
||||
ZString str_copy = copy_zstring(s) @inline;
|
||||
return str_copy[..len];
|
||||
}
|
||||
|
||||
fn String tcopy(String s)
|
||||
{
|
||||
usize len = s.len;
|
||||
ZString str_copy = tcopy_zstring(s) @inline;
|
||||
return str_copy[..len];
|
||||
}
|
||||
|
||||
fn String tconcat(String s1, String s2)
|
||||
{
|
||||
usize full_len = s1.len + s2.len;
|
||||
char* str = mem::talloc(full_len + 1)!!;
|
||||
usize s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return str[..full_len];
|
||||
}
|
||||
|
||||
fn String concat(String s1, String s2)
|
||||
{
|
||||
usize full_len = s1.len + s2.len;
|
||||
char* str = mem::alloc(full_len + 1);
|
||||
usize s1_len = s1.len;
|
||||
mem::copy(str, s1.ptr, s1_len);
|
||||
mem::copy(str + s1_len, s2.ptr, s2.len);
|
||||
str[full_len] = 0;
|
||||
return str[..full_len];
|
||||
}
|
||||
243
msvc_build_libraries.py
Executable file
243
msvc_build_libraries.py
Executable file
@@ -0,0 +1,243 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Script based on https://gist.github.com/mmozeiko/7f3162ec2988e81e56d5c4e22cde9977
|
||||
# by Mārtiņš Možeiko.
|
||||
# Changes and additions to the gist are licensed under the CC0 License.
|
||||
|
||||
import platform
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import hashlib
|
||||
import zipfile
|
||||
import tempfile
|
||||
import argparse
|
||||
import subprocess
|
||||
import urllib.request
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
OUTPUT = Path("msvc_temp") # output folder
|
||||
SDK_OUTPUT = Path("msvc_sdk")
|
||||
|
||||
MANIFEST_URL = "https://aka.ms/vs/17/release/channel"
|
||||
|
||||
def download(url):
|
||||
with urllib.request.urlopen(url) as res:
|
||||
return res.read()
|
||||
|
||||
def download_progress(url, check, name, f):
|
||||
data = io.BytesIO()
|
||||
with urllib.request.urlopen(url) as res:
|
||||
total = int(res.headers["Content-Length"])
|
||||
size = 0
|
||||
while True:
|
||||
block = res.read(1<<20)
|
||||
if not block:
|
||||
break
|
||||
f.write(block)
|
||||
data.write(block)
|
||||
size += len(block)
|
||||
perc = size * 100 // total
|
||||
print(f"\r{name} ... {perc}%", end="")
|
||||
print()
|
||||
data = data.getvalue()
|
||||
digest = hashlib.sha256(data).hexdigest()
|
||||
if check.lower() != digest:
|
||||
exit(f"Hash mismatch for f{pkg}")
|
||||
return data
|
||||
|
||||
# super crappy msi format parser just to find required .cab files
|
||||
def get_msi_cabs(msi):
|
||||
index = 0
|
||||
while True:
|
||||
index = msi.find(b".cab", index+4)
|
||||
if index < 0:
|
||||
return
|
||||
yield msi[index-32:index+4].decode("ascii")
|
||||
|
||||
def first(items, cond):
|
||||
return next(item for item in items if cond(item))
|
||||
|
||||
|
||||
### parse command-line arguments
|
||||
|
||||
ap = argparse.ArgumentParser()
|
||||
ap.add_argument("--show-versions", const=True, action="store_const", help="Show available MSVC and Windows SDK versions")
|
||||
ap.add_argument("--accept-license", const=True, action="store_const", help="Automatically accept license")
|
||||
ap.add_argument("--msvc-version", help="Get specific MSVC version")
|
||||
ap.add_argument("--sdk-version", help="Get specific Windows SDK version")
|
||||
args = ap.parse_args()
|
||||
|
||||
|
||||
### get main manifest
|
||||
|
||||
manifest = json.loads(download(MANIFEST_URL))
|
||||
|
||||
|
||||
### download VS manifest
|
||||
|
||||
vs = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Manifests.VisualStudio")
|
||||
payload = vs["payloads"][0]["url"]
|
||||
|
||||
vsmanifest = json.loads(download(payload))
|
||||
|
||||
|
||||
### find MSVC & WinSDK versions
|
||||
|
||||
packages = {}
|
||||
for p in vsmanifest["packages"]:
|
||||
packages.setdefault(p["id"].lower(), []).append(p)
|
||||
|
||||
msvc = {}
|
||||
sdk = {}
|
||||
|
||||
for pid,p in packages.items():
|
||||
if pid.startswith("Microsoft.VisualStudio.Component.VC.".lower()) and pid.endswith(".x86.x64".lower()):
|
||||
pver = ".".join(pid.split(".")[4:6])
|
||||
if pver[0].isnumeric():
|
||||
msvc[pver] = pid
|
||||
elif pid.startswith("Microsoft.VisualStudio.Component.Windows10SDK.".lower()) or \
|
||||
pid.startswith("Microsoft.VisualStudio.Component.Windows11SDK.".lower()):
|
||||
pver = pid.split(".")[-1]
|
||||
if pver.isnumeric():
|
||||
sdk[pver] = pid
|
||||
|
||||
if args.show_versions:
|
||||
print("MSVC versions:", " ".join(sorted(msvc.keys())))
|
||||
print("Windows SDK versions:", " ".join(sorted(sdk.keys())))
|
||||
exit(0)
|
||||
|
||||
msvc_ver = args.msvc_version or max(sorted(msvc.keys()))
|
||||
sdk_ver = args.sdk_version or max(sorted(sdk.keys()))
|
||||
|
||||
if msvc_ver in msvc:
|
||||
msvc_pid = msvc[msvc_ver]
|
||||
msvc_ver = ".".join(msvc_pid.split(".")[4:-2])
|
||||
else:
|
||||
exit(f"Unknown MSVC version: f{args.msvc_version}")
|
||||
|
||||
if sdk_ver in sdk:
|
||||
sdk_pid = sdk[sdk_ver]
|
||||
else:
|
||||
exit(f"Unknown Windows SDK version: f{args.sdk_version}")
|
||||
|
||||
print(f"Downloading MSVC v{msvc_ver} and Windows SDK v{sdk_ver}")
|
||||
|
||||
|
||||
### agree to license
|
||||
|
||||
tools = first(manifest["channelItems"], lambda x: x["id"] == "Microsoft.VisualStudio.Product.BuildTools")
|
||||
resource = first(tools["localizedResources"], lambda x: x["language"] == "en-us")
|
||||
license = resource["license"]
|
||||
|
||||
if not args.accept_license:
|
||||
accept = input(f"Do you accept Visual Studio license at {license}, and also confirm that you have a valid license Visual Studio license allowing you to download the VS Build Tools [Y/N] ?")
|
||||
if not accept or accept[0].lower() != "y":
|
||||
exit(0)
|
||||
|
||||
shutil.rmtree(OUTPUT, ignore_errors = True)
|
||||
shutil.rmtree(SDK_OUTPUT, ignore_errors = True)
|
||||
OUTPUT.mkdir()
|
||||
total_download = 0
|
||||
|
||||
### download Windows SDK
|
||||
|
||||
archs = [
|
||||
#"arm",
|
||||
#"arm64",
|
||||
"x64",
|
||||
#"x86"
|
||||
]
|
||||
|
||||
msvc_packages = [
|
||||
f"microsoft.vc.{msvc_ver}.asan.headers.base",
|
||||
]
|
||||
|
||||
for arch in archs:
|
||||
msvc_packages.append(f"microsoft.vc.{msvc_ver}.crt.{arch}.desktop.base")
|
||||
msvc_packages.append(f"microsoft.vc.{msvc_ver}.crt.{arch}.store.base")
|
||||
msvc_packages.append(f"microsoft.vc.{msvc_ver}.asan.{arch}.base")
|
||||
|
||||
for pkg in msvc_packages:
|
||||
p = first(packages[pkg], lambda p: p.get("language") in (None, "en-US"))
|
||||
for payload in p["payloads"]:
|
||||
with tempfile.TemporaryFile() as f:
|
||||
data = download_progress(payload["url"], payload["sha256"], pkg, f)
|
||||
total_download += len(data)
|
||||
with zipfile.ZipFile(f) as z:
|
||||
for name in z.namelist():
|
||||
if name.startswith("Contents/"):
|
||||
out = OUTPUT / Path(name).relative_to("Contents")
|
||||
out.parent.mkdir(parents=True, exist_ok=True)
|
||||
out.write_bytes(z.read(name))
|
||||
|
||||
sdk_packages = [
|
||||
# Windows SDK libs
|
||||
f"Windows SDK for Windows Store Apps Libs-x86_en-us.msi",
|
||||
f"Windows SDK Desktop Libs x64-x86_en-us.msi",
|
||||
# CRT headers & libs
|
||||
f"Universal CRT Headers Libraries and Sources-x86_en-us.msi",
|
||||
]
|
||||
|
||||
with tempfile.TemporaryDirectory() as d:
|
||||
dst = Path(d)
|
||||
|
||||
sdk_pkg = packages[sdk_pid][0]
|
||||
sdk_pkg = packages[first(sdk_pkg["dependencies"], lambda x: True).lower()][0]
|
||||
|
||||
msi = []
|
||||
cabs = []
|
||||
|
||||
# download msi files
|
||||
for pkg in sdk_packages:
|
||||
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
|
||||
msi.append(dst / pkg)
|
||||
with open(dst / pkg, "wb") as f:
|
||||
data = download_progress(payload["url"], payload["sha256"], pkg, f)
|
||||
total_download += len(data)
|
||||
cabs += list(get_msi_cabs(data))
|
||||
|
||||
# download .cab files
|
||||
for pkg in cabs:
|
||||
payload = first(sdk_pkg["payloads"], lambda p: p["fileName"] == f"Installers\\{pkg}")
|
||||
with open(dst / pkg, "wb") as f:
|
||||
download_progress(payload["url"], payload["sha256"], pkg, f)
|
||||
|
||||
print("Unpacking msi files...")
|
||||
|
||||
# run msi installers
|
||||
for m in msi:
|
||||
if (platform.system() == "Windows"):
|
||||
subprocess.check_call(["msiexec.exe", "/a", m, "/quiet", "/qn", f"TARGETDIR={OUTPUT.resolve()}"])
|
||||
else:
|
||||
subprocess.check_call(["msiextract", m, '-C', OUTPUT.resolve()])
|
||||
|
||||
|
||||
### versions
|
||||
|
||||
if (platform.system() == "Windows"):
|
||||
window_kit = OUTPUT / "Windows Kits/"
|
||||
else:
|
||||
window_kit = OUTPUT / "Program Files/Windows Kits/"
|
||||
|
||||
ucrt = list(window_kit.glob("*/Lib/*/ucrt"))[0]
|
||||
um = list(window_kit.glob("*/Lib/*/um"))[0]
|
||||
lib = list((OUTPUT / "VC/Tools/MSVC/").glob("*/lib"))[0]
|
||||
|
||||
SDK_OUTPUT.mkdir(exist_ok=True)
|
||||
|
||||
|
||||
|
||||
for arch in archs:
|
||||
out_dir = SDK_OUTPUT / arch
|
||||
shutil.copytree(ucrt / arch, out_dir, dirs_exist_ok=True)
|
||||
shutil.copytree(um / arch, out_dir, dirs_exist_ok=True)
|
||||
shutil.copytree(lib / arch, out_dir, dirs_exist_ok=True)
|
||||
|
||||
### cleanup
|
||||
|
||||
shutil.rmtree(OUTPUT, ignore_errors=True)
|
||||
|
||||
42
releasenotes.md
Normal file
42
releasenotes.md
Normal file
@@ -0,0 +1,42 @@
|
||||
Release Notes
|
||||
|
||||
0.4.0 Change List
|
||||
|
||||
- Compatibility with LLVM 16.
|
||||
- Dropped LLVM 12 support.
|
||||
- Updated memory allocators. Added @scoped and @pool macros.
|
||||
- Various bug fixes.
|
||||
- Constant pointers may be compile time evaluated.
|
||||
- Added many new builtins.
|
||||
- Emit asm using --emit-asm.
|
||||
- Added --nostdlib and --nolibc.
|
||||
- Fixes to adding libraries at link time.
|
||||
- Various improved error messages.
|
||||
- Windows debug info fixes.
|
||||
- Add of `foreach_r`.
|
||||
- Script downloading the MSVC SDK to cross compile to windows.
|
||||
- Many standard library additions.
|
||||
- Extension methods may be added for built-in types.
|
||||
- Macros may take vector and array arguments generic over length.
|
||||
- Macro varargs with $vaarg, $vacount etc.
|
||||
- Many vector builtins added as dot methods.
|
||||
- in / out / inout doc parameters checked.
|
||||
- Initial inline asm support for aarch64 and x64.
|
||||
- Single line short function declaration.
|
||||
- Added `$checks` builtin.
|
||||
- Optional single module compilation.
|
||||
- Static initialization / finalization to have code running at start/end.
|
||||
- C3 custom printf function in the stdlib.
|
||||
- `[]=` overload now works correctly.
|
||||
- More compile time reflection added and general cleanup done.
|
||||
- usize/isize/iptrdiff/uptrdiff replaced by usz/isz.
|
||||
- Add `var` to allow type inference on regular variables.
|
||||
- LLVM codegen optimizations.
|
||||
- `??` now allows chaining another optional.
|
||||
- int128 support on all platforms.
|
||||
- `import` is now allowed anywhere at the top level.
|
||||
- `project.c3p` renamed `project.json`
|
||||
- Update to project properties, e.g. "libs" -> "dependencies" etc.
|
||||
- $$TIME, $$DATE and $$FUNCTION builtin defines added.
|
||||
- Improvements to untyped lists.
|
||||
- Various builtins added: $$prefetch, $$reverse, $$shufflevector etc.
|
||||
@@ -61,7 +61,7 @@ fn void encode(char[] in, char *out)
|
||||
}
|
||||
|
||||
// move back
|
||||
usize last = in.len - 1;
|
||||
usz last = in.len - 1;
|
||||
// check the last and add padding
|
||||
switch (last % 3)
|
||||
{
|
||||
|
||||
25
resources/examples/binarydigits.c3
Normal file
25
resources/examples/binarydigits.c3
Normal file
@@ -0,0 +1,25 @@
|
||||
module binarydigits;
|
||||
import std::math;
|
||||
import std::io;
|
||||
fn void main()
|
||||
{
|
||||
for (int i = 0; i < 20; i++)
|
||||
{
|
||||
String s = bin(i);
|
||||
defer s.destroy();
|
||||
io::printf("%s\n", s);
|
||||
}
|
||||
}
|
||||
|
||||
fn String bin(int x)
|
||||
{
|
||||
int bits = 1 + (int)(x == 0 ? 0 : math::log10((double)(x)) / math::log10(2));
|
||||
String str;
|
||||
str.append_repeat('0', bits);
|
||||
for (int i = 0; i < bits; i++)
|
||||
{
|
||||
str.set((usz)(bits - i - 1), x & 1 ? '1' : '0');
|
||||
x >>= 1;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
86
resources/examples/brainfk.c3
Normal file
86
resources/examples/brainfk.c3
Normal file
@@ -0,0 +1,86 @@
|
||||
// #target: macos-x64
|
||||
module brainfk;
|
||||
import std::io;
|
||||
|
||||
const BF_MEM = 30000;
|
||||
char[BF_MEM] memory;
|
||||
|
||||
fault InterpretError
|
||||
{
|
||||
INTEPRET_FAILED
|
||||
}
|
||||
|
||||
fn void! print_error(usz pos, char[] err)
|
||||
{
|
||||
io::printfln("Error at %s: %s", pos, err);
|
||||
return InterpretError.INTEPRET_FAILED!;
|
||||
}
|
||||
|
||||
fn void! brainf(char[] program)
|
||||
{
|
||||
usz sp = 0;
|
||||
usz mem = 0;
|
||||
while (sp < program.len)
|
||||
{
|
||||
char c = program[sp++];
|
||||
switch (c)
|
||||
{
|
||||
case '<':
|
||||
if (!mem) return print_error(sp, "Memory underflow");
|
||||
mem--;
|
||||
case '>':
|
||||
if (mem == BF_MEM - 1) return print_error(sp, "Memory overflow");
|
||||
mem++;
|
||||
case '+':
|
||||
memory[mem]++;
|
||||
case '-':
|
||||
memory[mem]--;
|
||||
case '.':
|
||||
io::putchar(memory[mem]);
|
||||
io::stdout().flush();
|
||||
case ',':
|
||||
memory[mem] = io::stdin().getc()!!;
|
||||
case '[':
|
||||
usz indent = 1;
|
||||
if (memory[mem]) continue;
|
||||
usz start = sp - 1;
|
||||
while (indent)
|
||||
{
|
||||
if (sp == program.len) return print_error(start, "No matching ']'");
|
||||
switch (program[sp++])
|
||||
{
|
||||
case ']': indent--;
|
||||
case '[': indent++;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
case ']':
|
||||
if (!memory[mem]) continue;
|
||||
usz start = sp--;
|
||||
usz indent = 1;
|
||||
while (indent)
|
||||
{
|
||||
if (!sp) return print_error(start, "No matching '['");
|
||||
switch (program[--sp])
|
||||
{
|
||||
case ']': indent++;
|
||||
case '[': indent--;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
sp++;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fn void! main()
|
||||
{
|
||||
char[] program = `
|
||||
++++[>+++++<-]>[<+++++>-]+<+[
|
||||
>[>+>+<<-]++>>[<<+>>-]>>>[-]++>[-]+
|
||||
>>>+[[-]++++++>>>]<<<[[<++++++++<++>>-]+<.<[>----<-]<]
|
||||
<<[>>>>>[>>>[-]+++++++++<[>-<-]+++++++++>[-[<->-]+[<<<]]<[>+<-]>]<<-]<<-
|
||||
]`;
|
||||
brainf(program)?;
|
||||
}
|
||||
141
resources/examples/contextfree/boolerr.c3
Normal file
141
resources/examples/contextfree/boolerr.c3
Normal file
@@ -0,0 +1,141 @@
|
||||
module test;
|
||||
import libc;
|
||||
import std::io;
|
||||
|
||||
struct Doc { Head *head; }
|
||||
struct Head { String* title; }
|
||||
|
||||
struct Summary
|
||||
{
|
||||
String* title;
|
||||
bool ok;
|
||||
}
|
||||
|
||||
private struct StringData
|
||||
{
|
||||
Allocator allocator;
|
||||
usz len;
|
||||
usz capacity;
|
||||
char[*] chars;
|
||||
}
|
||||
|
||||
fn void Summary.print(Summary *s, File out)
|
||||
{
|
||||
char[] title = s.title ? s.title.str() : "missing";
|
||||
out.printf("Summary({ .title = %s, .ok = %s})", title, s.ok);
|
||||
}
|
||||
|
||||
fn bool contains(char[] haystack, char[] needle)
|
||||
{
|
||||
usz len = haystack.len;
|
||||
usz needle_len = needle.len;
|
||||
if (len < needle_len) return false;
|
||||
if (!needle_len) return true;
|
||||
len -= needle_len - 1;
|
||||
for (usz i = 0; i < len; i++)
|
||||
{
|
||||
if (mem::equals(haystack[i..], needle))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
macro @dupe(value)
|
||||
{
|
||||
$typeof(&value) temp = malloc_checked($sizeof(value))?;
|
||||
*temp = value;
|
||||
return temp;
|
||||
}
|
||||
|
||||
fault ReadError
|
||||
{
|
||||
BAD_READ,
|
||||
}
|
||||
|
||||
fn Doc! readDoc(char[] url)
|
||||
{
|
||||
if (contains(url, "fail")) return ReadError.BAD_READ!;
|
||||
if (contains(url, "head-missing")) return { .head = null };
|
||||
if (contains(url, "title-missing")) return { @dupe(Head { .title = null }) };
|
||||
if (contains(url, "title-empty")) return { @dupe(Head { .title = @dupe((String)null) }) };
|
||||
String str;
|
||||
str.printf("Title of %s", url);
|
||||
return { @dupe(Head { .title = @dupe(str) }) };
|
||||
}
|
||||
|
||||
fn Summary buildSummary(Doc doc)
|
||||
{
|
||||
return Summary {
|
||||
.title = doc.head ? doc.head.title : null,
|
||||
.ok = true,
|
||||
};
|
||||
}
|
||||
|
||||
fn Summary readAndBuildSummary(char[] url)
|
||||
{
|
||||
return buildSummary(readDoc(url)) ?? Summary { .title = null, .ok = false };
|
||||
/*
|
||||
// or
|
||||
Summary summary = buildSummary(readDoc(url));
|
||||
if (catch summary) return Summary { .title = null, .ok = false };
|
||||
return summary;
|
||||
// or
|
||||
Summary summary = buildSummary(readDoc(url));
|
||||
if (try summary) return summary;
|
||||
return Summary { .title = null, .ok = false };
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
fault TitleResult
|
||||
{
|
||||
TITLE_MISSING
|
||||
}
|
||||
|
||||
fn bool! isTitleNonEmpty(Doc doc)
|
||||
{
|
||||
if (!doc.head) return TitleResult.TITLE_MISSING!;
|
||||
String* head = doc.head.title;
|
||||
if (!head) return TitleResult.TITLE_MISSING!;
|
||||
return head.len() > 0;
|
||||
}
|
||||
|
||||
|
||||
fn bool! readWhetherTitleNonEmpty(char[] url)
|
||||
{
|
||||
return isTitleNonEmpty(readDoc(url));
|
||||
}
|
||||
|
||||
fn char[] bool_to_string(bool b)
|
||||
{
|
||||
return b ? "true" : "false";
|
||||
}
|
||||
|
||||
|
||||
fn void main()
|
||||
{
|
||||
const char[][] URLS = { "good", "title-empty", "title-missing", "head-missing", "fail" };
|
||||
DynamicArenaAllocator dynamic_arena;
|
||||
dynamic_arena.init(1024);
|
||||
foreach (char[] url : URLS)
|
||||
{
|
||||
mem::@scoped(&dynamic_arena)
|
||||
{
|
||||
io::printf(`Checking "https://%s/":` "\n", url);
|
||||
Summary summary = readAndBuildSummary(url);
|
||||
io::printf(" Summary: ");
|
||||
summary.print(io::stdout());
|
||||
io::println("");
|
||||
char[] title_sure = summary.title ? summary.title.str() : "";
|
||||
io::printf(" Title: %s\n", title_sure);
|
||||
bool! has_title = readWhetherTitleNonEmpty(url);
|
||||
// This looks a bit less than elegant, but as you see it's mostly due to having to
|
||||
// use printf here.
|
||||
io::printf(" Has title: %s vs %s\n", bool_to_string(has_title) ?? catch(has_title).nameof, has_title ?? false);
|
||||
};
|
||||
dynamic_arena.reset();
|
||||
}
|
||||
dynamic_arena.destroy();
|
||||
}
|
||||
32
resources/examples/contextfree/dynscope.c3
Normal file
32
resources/examples/contextfree/dynscope.c3
Normal file
@@ -0,0 +1,32 @@
|
||||
module foo;
|
||||
import std::io;
|
||||
|
||||
tlocal char[] context_user = "safe";
|
||||
|
||||
macro long reallyPerform(task)
|
||||
{
|
||||
io::printfln("%s: %s", context_user, task);
|
||||
return task.len;
|
||||
}
|
||||
|
||||
macro long perform(task)
|
||||
{
|
||||
return reallyPerform(task);
|
||||
}
|
||||
|
||||
macro @with_mode(char[] user, #action, ...)
|
||||
{
|
||||
@scope(context_user)
|
||||
{
|
||||
context_user = user;
|
||||
return #action($vasplat());
|
||||
};
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
long val1 = perform("something");
|
||||
long val2 = @with_mode("faster", perform, "reliable");
|
||||
long val3 = perform(char[][] {"something"});
|
||||
io::printfln("%d %d %d", val1, val2, val3);
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
module guess_number;
|
||||
import std::mem;
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
extern fn void printf(char*, ...);
|
||||
extern fn isize getline(char** linep, usize* linecapp, CFile stream);
|
||||
extern fn isz getline(char** linep, usz* linecapp, CFile stream);
|
||||
|
||||
struct Game
|
||||
{
|
||||
@@ -24,7 +22,7 @@ int err_count = 0;
|
||||
|
||||
fn int! askGuess(int high)
|
||||
{
|
||||
printf("Guess a number between 1 and %d: ", high);
|
||||
libc::printf("Guess a number between 1 and %d: ", high);
|
||||
char[] text = readLine()?;
|
||||
char* end = null;
|
||||
int value = (int)libc::strtol(text.ptr, &end, 10);
|
||||
@@ -34,8 +32,8 @@ fn int! askGuess(int high)
|
||||
|
||||
fn char[]! readLine()
|
||||
{
|
||||
char* chars = mem::talloc(1024)?;
|
||||
isize loaded = getline(&chars, &&(usize)1023, @libc::stdin());
|
||||
char* chars = tmalloc(1024)?;
|
||||
isz loaded = getline(&chars, &&(usz)1023, libc::stdin());
|
||||
if (loaded < 0) return InputResult.FAILED_TO_READ!;
|
||||
chars[loaded] = 0;
|
||||
return chars[0..(loaded - 1)];
|
||||
@@ -48,13 +46,13 @@ fn int! askGuessMulti(int high)
|
||||
int! result = askGuess(high);
|
||||
if (catch(result) == InputResult.NOT_AN_INT)
|
||||
{
|
||||
printf("I didn't understand that.\n");
|
||||
libc::printf("I didn't understand that.\n");
|
||||
err_count++;
|
||||
continue;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@unreachable();
|
||||
unreachable();
|
||||
}
|
||||
|
||||
fn void! Game.play(Game *game)
|
||||
@@ -74,7 +72,7 @@ fn void Game.report(Game *game, int guess)
|
||||
if (guess > game.answer) return "too high";
|
||||
return "the answer";
|
||||
|};
|
||||
printf("%d is %.*s.\n", guess, (int)desc.len, desc.ptr);
|
||||
libc::printf("%d is %.*s.\n", guess, (int)desc.len, desc.ptr);
|
||||
}
|
||||
|
||||
fn void Game.update(Game *game, int guess)
|
||||
@@ -90,6 +88,6 @@ fn void! main()
|
||||
int answer = libc::rand() % high + 1;
|
||||
Game game = { .answer = answer, .high = high };
|
||||
game.play();
|
||||
printf("Finished in %d guesses.\n", game.guesses);
|
||||
printf("Total input errors: %d.\n", err_count);
|
||||
libc::printf("Finished in %d guesses.\n", game.guesses);
|
||||
libc::printf("Total input errors: %d.\n", err_count);
|
||||
}
|
||||
27
resources/examples/contextfree/multi.c3
Normal file
27
resources/examples/contextfree/multi.c3
Normal file
@@ -0,0 +1,27 @@
|
||||
module test;
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
/*
|
||||
Here's a comment.
|
||||
/*
|
||||
And a nested comment.
|
||||
*/
|
||||
*/
|
||||
char[] text = `
|
||||
function hello() {
|
||||
console.log("name`"\t"`age");
|
||||
}
|
||||
|
||||
hello();
|
||||
`;
|
||||
io::println(text);
|
||||
|
||||
// Binary
|
||||
const DATA = x"4749463839610100010080"
|
||||
x"0100ffffff00000021f904"
|
||||
x"010a0001002c0000000001"
|
||||
x"0001000002024c01003b";
|
||||
io::printf("%d\n", DATA.len);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ macro int factorial($n)
|
||||
$if ($n == 0):
|
||||
return 1;
|
||||
$else:
|
||||
return $n * @factorial($n - 1);
|
||||
return $n * factorial($n - 1);
|
||||
$endif;
|
||||
}
|
||||
|
||||
@@ -11,6 +11,6 @@ extern fn void printf(char *fmt, ...);
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int x = @factorial(12);
|
||||
int x = factorial(12);
|
||||
printf("12! = %d\n", x);
|
||||
}
|
||||
@@ -1,18 +1,14 @@
|
||||
module fannkuch;
|
||||
import std::array;
|
||||
import std::io;
|
||||
import std::math;
|
||||
import libc;
|
||||
import std::mem;
|
||||
|
||||
macro int max(int a, int b)
|
||||
{
|
||||
return a > b ? a : b;
|
||||
}
|
||||
|
||||
fn int fannkuchredux(int n)
|
||||
{
|
||||
int* perm = @array::make(int, n);
|
||||
int* perm1 = @array::make(int, n);
|
||||
int* count = @array::make(int, n);
|
||||
int* perm = array::alloc(int, n);
|
||||
int* perm1 = array::alloc(int, n);
|
||||
int* count = array::alloc(int, n);
|
||||
int max_flips_count;
|
||||
int perm_count;
|
||||
int checksum;
|
||||
@@ -42,7 +38,7 @@ fn int fannkuchredux(int n)
|
||||
flips_count++;
|
||||
}
|
||||
|
||||
max_flips_count = @max(max_flips_count, flips_count);
|
||||
max_flips_count = max(max_flips_count, flips_count);
|
||||
checksum += perm_count % 2 == 0 ? flips_count : -flips_count;
|
||||
|
||||
/* Use incremental change to generate another permutation */
|
||||
@@ -50,7 +46,7 @@ fn int fannkuchredux(int n)
|
||||
{
|
||||
if (r == n)
|
||||
{
|
||||
libc::printf("%d\n", checksum);
|
||||
io::printf("%d\n", checksum);
|
||||
return max_flips_count;
|
||||
}
|
||||
|
||||
@@ -75,6 +71,6 @@ fn int fannkuchredux(int n)
|
||||
fn int main(int argc, char** argv)
|
||||
{
|
||||
int n = argc > 1 ? libc::atoi(argv[1]) : 7;
|
||||
libc::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n));
|
||||
io::printf("Pfannkuchen(%d) = %d\n", n, fannkuchredux(n));
|
||||
return 0;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
module fasta;
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
const IM = 139968;
|
||||
@@ -55,14 +56,14 @@ const LINELEN = 60;
|
||||
// slowest character-at-a-time output
|
||||
fn void repeat_fasta(char[] seq, int n)
|
||||
{
|
||||
usize len = seq.len;
|
||||
usz len = seq.len;
|
||||
int i = void;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
libc::putchar(seq[i % len]);
|
||||
if (i % LINELEN == LINELEN - 1) libc::putchar('\n');
|
||||
io::putchar(seq[i % len]);
|
||||
if (i % LINELEN == LINELEN - 1) io::putchar('\n');
|
||||
}
|
||||
if (i % LINELEN != 0) libc::putchar('\n');
|
||||
if (i % LINELEN != 0) io::putchar('\n');
|
||||
}
|
||||
|
||||
fn void random_fasta(char[] symb, double[] probability, int n)
|
||||
@@ -80,10 +81,10 @@ fn void random_fasta(char[] symb, double[] probability, int n)
|
||||
v -= probability[j];
|
||||
if (v < 0) break;
|
||||
}
|
||||
libc::putchar(symb[j]);
|
||||
if (i % LINELEN == LINELEN - 1) libc::putchar('\n');
|
||||
io::putchar(symb[j]);
|
||||
if (i % LINELEN == LINELEN - 1) io::putchar('\n');
|
||||
}
|
||||
if (i % LINELEN != 0) libc::putchar('\n');
|
||||
if (i % LINELEN != 0) io::putchar('\n');
|
||||
}
|
||||
|
||||
fn void main(int argc, char **argv)
|
||||
@@ -91,13 +92,13 @@ fn void main(int argc, char **argv)
|
||||
int n = 1000;
|
||||
if (argc > 1) n = libc::atoi(argv[1]);
|
||||
|
||||
libc::printf(">ONE Homo sapiens alu\n");
|
||||
io::printf(">ONE Homo sapiens alu\n");
|
||||
repeat_fasta(alu, n * 2);
|
||||
|
||||
libc::printf(">TWO IUB ambiguity codes\n");
|
||||
io::printf(">TWO IUB ambiguity codes\n");
|
||||
random_fasta(iub, iub_p, n * 3);
|
||||
|
||||
libc::printf(">THREE Homo sapiens frequency\n");
|
||||
io::printf(">THREE Homo sapiens frequency\n");
|
||||
random_fasta(homosapiens, homosapiens_p, n * 5);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
module game_of_life;
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
extern fn void printf(char *fmt, ...);
|
||||
extern fn int atoi(char *val);
|
||||
extern void *__stdoutp;
|
||||
extern fn void fflush(void *std);
|
||||
extern fn int rand();
|
||||
extern fn void* malloc(usize size);
|
||||
extern fn void usleep(int time);
|
||||
|
||||
|
||||
@@ -20,18 +16,18 @@ struct GameBoard
|
||||
fn void GameBoard.show(GameBoard *board)
|
||||
{
|
||||
|
||||
printf("\e[H");
|
||||
io::printf("\e[H");
|
||||
char* current = board.world;
|
||||
for (int y = 0; y < board.h; y++)
|
||||
{
|
||||
for (int x = 0; x < board.w; x++)
|
||||
{
|
||||
printf(*current ? "\e[07m \e[m" : " ");
|
||||
io::printf(*current ? "\e[07m \e[m" : " ");
|
||||
current++;
|
||||
}
|
||||
printf("\e[E");
|
||||
io::printf("\e[E");
|
||||
}
|
||||
fflush(__stdoutp);
|
||||
libc::fflush(libc::stdout());
|
||||
}
|
||||
|
||||
fn void GameBoard.evolve(GameBoard *board)
|
||||
@@ -65,8 +61,8 @@ fn int main(int c, char** v)
|
||||
{
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
if (c > 1) w = atoi(v[1]);
|
||||
if (c > 2) h = atoi(v[2]);
|
||||
if (c > 1) w = libc::atoi(v[1]);
|
||||
if (c > 2) h = libc::atoi(v[2]);
|
||||
if (w <= 0) w = 30;
|
||||
if (h <= 0) h = 30;
|
||||
|
||||
@@ -78,7 +74,7 @@ fn int main(int c, char** v)
|
||||
|
||||
for (int i = h * w - 1; i >= 0; i--)
|
||||
{
|
||||
board.world[i] = rand() % 10 == 0 ? 1 : 0;
|
||||
board.world[i] = libc::rand() % 10 == 0 ? 1 : 0;
|
||||
}
|
||||
for (int j = 0; j < 1000; j++)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module hash;
|
||||
module std::hash;
|
||||
import libc;
|
||||
|
||||
// Code adapted from Odin's hash.odin
|
||||
|
||||
19
resources/examples/hello_world_many.c3
Normal file
19
resources/examples/hello_world_many.c3
Normal file
@@ -0,0 +1,19 @@
|
||||
import std::io;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
char[][] greetings = {
|
||||
"Hello, world!",
|
||||
"¡Hola Mundo!",
|
||||
"Γειά σου Κόσμε!",
|
||||
"Привет, мир!",
|
||||
"こんにちは世界!",
|
||||
"你好世界!",
|
||||
"नमस्ते दुनिया!",
|
||||
"👋🌎!"
|
||||
};
|
||||
foreach (greeting : greetings)
|
||||
{
|
||||
io::println(greeting);
|
||||
}
|
||||
}
|
||||
@@ -22,5 +22,5 @@ fn int levenshtein(char[] s, char[] t)
|
||||
int b = levenshtein(s, t[0..^2]);
|
||||
int c = levenshtein(s[0..^2], t);
|
||||
|
||||
return @min(@min(a, b), c) + 1;
|
||||
return min(min(a, b), c) + 1;
|
||||
}
|
||||
97
resources/examples/map.c3
Normal file
97
resources/examples/map.c3
Normal file
@@ -0,0 +1,97 @@
|
||||
module std::container::map <Key, Type>;
|
||||
import std::core::builtin;
|
||||
import std::io;
|
||||
fault MapResult
|
||||
{
|
||||
KEY_NOT_FOUND
|
||||
}
|
||||
|
||||
struct Entry
|
||||
{
|
||||
Key key;
|
||||
Type value;
|
||||
usz hash;
|
||||
Entry* next;
|
||||
bool used;
|
||||
}
|
||||
|
||||
struct Map
|
||||
{
|
||||
usz size;
|
||||
Entry* map;
|
||||
uint mod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require map != null
|
||||
**/
|
||||
fn void Map.init(Map *map, uint capacity = 128)
|
||||
{
|
||||
if (capacity < 16) capacity = 4;
|
||||
capacity = math::next_power_of_2(capacity);
|
||||
map.map = calloc(Entry.sizeof * capacity);
|
||||
map.mod = capacity - 1;
|
||||
}
|
||||
|
||||
fn Type! Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return MapResult.KEY_NOT_FOUND!;
|
||||
uint hash = key.hash();
|
||||
usz pos = hash & map.mod;
|
||||
Entry* entry = &map.map[pos];
|
||||
if (!entry) return MapResult.KEY_NOT_FOUND!;
|
||||
while (entry)
|
||||
{
|
||||
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
|
||||
fn Type! Map.set(Map *map, Key key, Type value) @maydiscard
|
||||
{
|
||||
if (!map.map)
|
||||
{
|
||||
map.map = calloc(Entry.sizeof * 16);
|
||||
map.mod = 0x0F;
|
||||
}
|
||||
|
||||
uint hash = key.hash();
|
||||
uint pos = hash & map.mod;
|
||||
Entry *entry = &map.map[pos];
|
||||
while (1)
|
||||
{
|
||||
if (!entry.used)
|
||||
{
|
||||
entry.used = true;
|
||||
entry.value = value;
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
if (entry.hash == hash && entry.key == key)
|
||||
{
|
||||
Type old = entry.value;
|
||||
entry.value = value;
|
||||
return old;
|
||||
}
|
||||
if (entry.next)
|
||||
{
|
||||
entry = entry.next;
|
||||
continue;
|
||||
}
|
||||
Entry* new = mem::calloc(Entry.sizeof);
|
||||
new.hash = hash;
|
||||
new.key = key;
|
||||
new.value = value;
|
||||
new.next = null;
|
||||
new.used = true;
|
||||
entry.next = new;
|
||||
return MapResult.KEY_NOT_FOUND!;
|
||||
}
|
||||
}
|
||||
|
||||
fn usz Map.size(Map* map)
|
||||
{
|
||||
return map.size;
|
||||
}
|
||||
@@ -15,10 +15,10 @@ struct Planet
|
||||
|
||||
fn void advance(Planet[] bodies) @noinline
|
||||
{
|
||||
usize nbodies = bodies.len;
|
||||
usz nbodies = bodies.len;
|
||||
foreach (i, Planet* &b : bodies)
|
||||
{
|
||||
for (usize j = i + 1; j < nbodies; j++)
|
||||
for (usz j = i + 1; j < nbodies; j++)
|
||||
{
|
||||
Planet* b2 = &bodies[j];
|
||||
double dx = b.x - b2.x;
|
||||
@@ -45,12 +45,12 @@ fn void advance(Planet[] bodies) @noinline
|
||||
fn double energy(Planet[] bodies)
|
||||
{
|
||||
double e;
|
||||
usize nbodies = bodies.len;
|
||||
usz nbodies = bodies.len;
|
||||
|
||||
foreach (i, Planet* &b : bodies)
|
||||
{
|
||||
e += 0.5 * b.mass * (b.vx * b.vx + b.vy * b.vy + b.vz * b.vz);
|
||||
for (usize j = i + 1; j < nbodies; j++)
|
||||
for (usz j = i + 1; j < nbodies; j++)
|
||||
{
|
||||
Planet* b2 = &bodies[j];
|
||||
double dx = b.x - b2.x;
|
||||
|
||||
@@ -16,14 +16,14 @@ macro assert_exp($c, $e)
|
||||
*/
|
||||
|
||||
/** A signed integer, whose size matches Value */
|
||||
typedef isize Aint;
|
||||
typedef isz Aint;
|
||||
/** An unsigned integer, whose size matches Value */
|
||||
typedef usize Auint;
|
||||
typedef usz Auint;
|
||||
|
||||
|
||||
/** A float, whose size matches Value (see avm_env.h) */
|
||||
$assert(usize.size == 8 || usize.size == 4)
|
||||
$if (usize.size == 8)
|
||||
$assert(usz.size == 8 || usz.size == 4)
|
||||
$if (usz.size == 8)
|
||||
{
|
||||
typedef double as Afloat;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
module binarydigits;
|
||||
|
||||
fn int main()
|
||||
{
|
||||
fot (int i = 0; i < 20; i++)
|
||||
{
|
||||
printf("%s\n", bin(i));
|
||||
}
|
||||
}
|
||||
|
||||
fn string bin(int x)
|
||||
{
|
||||
int bits = (x == 0) ? 1 : log10((double)(x)) / log10(2);
|
||||
string ret = str.make_repeat('0' as bits);
|
||||
for (int i = 0; i < bits; i++)
|
||||
{
|
||||
ret[bits - i - 1] = x & 1 ? '1' : '0';
|
||||
x >>= 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -14,7 +14,7 @@ fn void main()
|
||||
|
||||
Regex r;
|
||||
|
||||
r.initWithOptions("<.+?>", RegexOpt.GLOBAL) else @unreachable;
|
||||
r.initWithOptions("<.+?>", RegexOpt.GLOBAL) else unreachable;
|
||||
defer r.destroy();
|
||||
|
||||
foreach (RegexMatch* match : r.match(story))
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
module map(Key, Type);
|
||||
|
||||
public struct Entry
|
||||
{
|
||||
Key key;
|
||||
Type* value;
|
||||
usize hash;
|
||||
Entry* next;
|
||||
}
|
||||
|
||||
public struct Map
|
||||
{
|
||||
usize size;
|
||||
void* map;
|
||||
uint mod;
|
||||
}
|
||||
|
||||
public fn Map* Map.init(Map *map)
|
||||
{
|
||||
*map = { };
|
||||
return map;
|
||||
}
|
||||
|
||||
public fn Type* Map.valueForKey(Map *map, Key key)
|
||||
{
|
||||
if (!map.map) return nil;
|
||||
usize hash = key.hash();
|
||||
usize pos = hash & map.mod;
|
||||
Entry* entry = &map.map[pop];
|
||||
if () return nil;
|
||||
while (entry)
|
||||
{
|
||||
if (entry.hash == hash && entry.key == key) return entry.value;
|
||||
entry = entry.next;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
public fn Type *Map.setValueForKey(Map *map, Key key, Type *value)
|
||||
{
|
||||
if (!map.map)
|
||||
{
|
||||
map.map = @calloc(Entry, 16);
|
||||
map.mod = 0x0F;
|
||||
}
|
||||
|
||||
usize hash = key.hash();
|
||||
usize pos = hash & map.mod;
|
||||
Entry *entry = &map.map[pop];
|
||||
while (1)
|
||||
{
|
||||
if (!entry.value)
|
||||
{
|
||||
entry.value = value;
|
||||
entry.hash = hash;
|
||||
entry.key = key;
|
||||
return nil;
|
||||
}
|
||||
if (entry.hash == hash && entry.key == key)
|
||||
{
|
||||
Type *old = entry.value;
|
||||
entry.value = value;
|
||||
return old;
|
||||
}
|
||||
if (entry.next)
|
||||
{
|
||||
entry = entry.next;
|
||||
}
|
||||
entry.next = @malloc(Entry);
|
||||
entry = entry.next;
|
||||
}
|
||||
}
|
||||
|
||||
public fn usize Map.size(Vector *vector)
|
||||
{
|
||||
return vector.array.size;
|
||||
}
|
||||
|
||||
public fn void Map.removeLast(Vector *vector)
|
||||
{
|
||||
vector.array.pop();
|
||||
}
|
||||
|
||||
public macro Vector.foreach(Vector *vector, macro void(Type value) body)
|
||||
{
|
||||
for (usize i = 0, i < vector.array.size; i++)
|
||||
{
|
||||
@body(vector.array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test
|
||||
{
|
||||
define IntVector = Vector(int);
|
||||
IntVector vector = vector.init();
|
||||
vector.add(1);
|
||||
vector.add(2);
|
||||
for (int i : vector)
|
||||
{
|
||||
printDigit(i);
|
||||
}
|
||||
@vector.foreach(int i)
|
||||
{
|
||||
printDigit(i);
|
||||
}
|
||||
vector.destroy();
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
module test;
|
||||
|
||||
|
||||
public macro retry(#function, int retries = 3)
|
||||
{
|
||||
error e;
|
||||
while (1)
|
||||
{
|
||||
auto! result = #function;
|
||||
try (result) return result;
|
||||
catch (e = result);
|
||||
} while (retries-- > 0)
|
||||
return e!;
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int! result = @retry(eventually_succeed());
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
public test;
|
||||
|
||||
/**
|
||||
* @require parse(a = b), parse(b = a)
|
||||
*/
|
||||
public macro void swap(&a, &b)
|
||||
{
|
||||
typeof(a) temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require parse(a = b), parse(b = a)
|
||||
*/
|
||||
public macro void swap2(auto &a, auto &b)
|
||||
{
|
||||
auto temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
@@ -118,8 +118,8 @@ fn void! Parser.parse(Parser* p, char* input, char* diagMsg, Blocks* blocks)
|
||||
p.errorMsg[0] = 0;
|
||||
|
||||
p.blocks = blocks;
|
||||
memset(p.parents, 0, sizeof(Node*)*MaxDepth);
|
||||
memset(p.lastChild, 0, sizeof(Node*)*MaxDepth);
|
||||
mem::set(p.parents, 0, sizeof(Node*)*MaxDepth);
|
||||
mem::set(p.lastChild, 0, sizeof(Node*)*MaxDepth);
|
||||
p.numParents = 0;
|
||||
p.topParent = nil;
|
||||
|
||||
@@ -438,13 +438,13 @@ fn const Node* Reader.findNode(const Reader* r, const char* key)
|
||||
switch (*cp) {
|
||||
case 0:
|
||||
len = cast<u32>(cp - start);
|
||||
memcpy(name, start, len);
|
||||
mem::copy(name, start, len);
|
||||
name[len] = 0;
|
||||
node = r.blocks.findNode(name, node);
|
||||
return node;
|
||||
case '.':
|
||||
len = cast<u32>(cp - start);
|
||||
memcpy(name, start, len);
|
||||
mem::copy(name, start, len);
|
||||
name[len] = 0;
|
||||
start = cp + 1;
|
||||
node = r.blocks.findNode(name, node);
|
||||
@@ -644,7 +644,7 @@ public type Blocks struct {
|
||||
} @(opaque)
|
||||
|
||||
fn void Blocks.init(Blocks* b) {
|
||||
memset(b, 0, sizeof(Blocks));
|
||||
mem::set(b, 0, sizeof(Blocks));
|
||||
b.nodes = calloc(MaxNodes, sizeof(Node));
|
||||
|
||||
b.namesSize = MaxNames;
|
||||
@@ -659,7 +659,7 @@ fn void Blocks.init(Blocks* b) {
|
||||
|
||||
b.lastCache = 0;
|
||||
//memset(b.namesCache, 0, sizeof(b.namesCache)); // sizeof(struct member) not supported yet
|
||||
memset(b.namesCache, 0, sizeof(u32)*NamesCacheSize);
|
||||
mem::set(b.namesCache, 0, sizeof(u32)*NamesCacheSize);
|
||||
}
|
||||
|
||||
fn void Blocks.destroy(Blocks* b) {
|
||||
@@ -702,7 +702,7 @@ fn u32 Blocks.addNode(Blocks* b, const char* name, NodeKind k) {
|
||||
nameOffset = b.namesOffset;
|
||||
node.nameOffset = nameOffset;
|
||||
char* newname = &b.names[nameOffset];
|
||||
memcpy(newname, name, len);
|
||||
mem::copy(newname, name, len);
|
||||
b.namesCache[b.lastCache] = nameOffset;
|
||||
b.lastCache = (b.lastCache + 1) % NamesCacheSize;
|
||||
b.namesOffset += len;
|
||||
@@ -716,7 +716,7 @@ fn u32 Blocks.addValue(Blocks* b, const char* value) {
|
||||
if (value[0] == 0) return 0;
|
||||
u32 off = b.valuesOffset;
|
||||
u32 len = cast<u32>(strlen(value)) + 1;
|
||||
memcpy(&b.values[off], value, len);
|
||||
mem::copy(&b.values[off], value, len);
|
||||
b.valuesOffset += len;
|
||||
return off;
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ fn void Tokenizer.parseText(Tokenizer* t, Token* result)
|
||||
|
||||
uint len = (uint)(t.current - start);
|
||||
// assert(len < MaxText);
|
||||
memcpy(t.text as start, len);
|
||||
mem::copy(t.text as start, len);
|
||||
t.text[len] = 0;
|
||||
result.kind = TokenKind.Text;
|
||||
result.text = t.text;
|
||||
@@ -310,7 +310,7 @@ fn void! Tokenizer.parseMultiText(Tokenizer* t, Token* result)
|
||||
|
||||
uint len = uint(t.current - start);
|
||||
// assert(len < MaxText);
|
||||
memcpy(t.text, start, len);
|
||||
mem::copy(t.text, start, len);
|
||||
t.text[len] = 0;
|
||||
result.kind = TokenKind.Text;
|
||||
result.text = t.text;
|
||||
@@ -349,7 +349,7 @@ fn void Tokenizer.parseKey(Tokenizer* t, Token* result)
|
||||
|
||||
uint len = (uint)(t.current - start);
|
||||
// assert(len < MaxText);
|
||||
memcpy(t.text, start, len);
|
||||
mem::copy(t.text, start, len);
|
||||
t.text[len] = 0;
|
||||
result.kind = TokenKind.Word;
|
||||
result.text = t.text;
|
||||
|
||||
84
resources/examples/plus_minus.c3
Normal file
84
resources/examples/plus_minus.c3
Normal file
@@ -0,0 +1,84 @@
|
||||
module test;
|
||||
import std::io;
|
||||
import libc;
|
||||
|
||||
fault TokenResult
|
||||
{
|
||||
NO_MORE_TOKENS
|
||||
}
|
||||
|
||||
|
||||
// While we could have written this with libc
|
||||
// the C way, let's showcase some features added to C3.
|
||||
fn void main(char[][] args)
|
||||
{
|
||||
// Grab a string from stdin
|
||||
String s = io::stdin().getline();
|
||||
// Delete it at scope end [defer]
|
||||
defer s.destroy();
|
||||
|
||||
// Grab the string as a slice.
|
||||
char[] numbers = s.str();
|
||||
|
||||
// Track our current value
|
||||
int val = 0;
|
||||
|
||||
// Is the current state an add?
|
||||
bool add = true;
|
||||
|
||||
while (try char[] token = read_next(&numbers))
|
||||
{
|
||||
// We're assuming well formed input here
|
||||
// so just use atoi.
|
||||
int i = libc::atoi(token.ptr);
|
||||
|
||||
// This value is either added or subtracted from the sum.
|
||||
val = add ? val + i : val - i;
|
||||
|
||||
// Read an optional token.
|
||||
char[]! op = read_next(&numbers);
|
||||
|
||||
// If it's an error, then we're done.
|
||||
if (catch op) break;
|
||||
|
||||
// Let's process the operator
|
||||
switch (op)
|
||||
{
|
||||
case "+":
|
||||
add = true;
|
||||
case "-":
|
||||
add = false;
|
||||
default:
|
||||
io::println("Failed to parse expression.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
io::printfln("%d", val);
|
||||
}
|
||||
|
||||
fn char[]! read_next(char[]* remaining)
|
||||
{
|
||||
while (remaining.len > 0 && (*remaining)[0] == ' ')
|
||||
{
|
||||
// Make the slice smaller
|
||||
*remaining = (*remaining)[1..];
|
||||
}
|
||||
|
||||
// Store the beginning of the parse
|
||||
char* ptr_start = remaining.ptr;
|
||||
usz len = 0;
|
||||
while (remaining.len > 0 && (*remaining)[0] != ' ')
|
||||
{
|
||||
// Increase length
|
||||
len++;
|
||||
|
||||
// And make the slice smaller
|
||||
*remaining = (*remaining)[1..];
|
||||
}
|
||||
|
||||
// If it's a zero length token, return an optional result.
|
||||
if (!len) return TokenResult.NO_MORE_TOKENS!;
|
||||
|
||||
// Otherwise create a slice from the pointer start and length.
|
||||
return ptr_start[:len];
|
||||
}
|
||||
313
resources/examples/raylib/raylib_arkanoid.c3
Normal file
313
resources/examples/raylib/raylib_arkanoid.c3
Normal file
@@ -0,0 +1,313 @@
|
||||
module arkanoid;
|
||||
|
||||
/**
|
||||
*
|
||||
* raylib - classic game: arkanoid
|
||||
*
|
||||
* Sample game developed by Marc Palau and Ramon Santamaria
|
||||
* converted to C3 by Christoffer Lerno
|
||||
*
|
||||
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
|
||||
*/
|
||||
|
||||
const int SCREEN_WIDTH = 800;
|
||||
const int SCREEN_HEIGHT = 450;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Some Defines
|
||||
//----------------------------------------------------------------------------------
|
||||
const PLAYER_MAX_LIFE = 5;
|
||||
const LINES_OF_BRICKS = 5;
|
||||
const BRICKS_PER_LINE = 20;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
enum GameScreen
|
||||
{
|
||||
LOGO,
|
||||
TITLE,
|
||||
GAMEPLAY,
|
||||
ENDING
|
||||
}
|
||||
|
||||
struct Player
|
||||
{
|
||||
Vector2 position;
|
||||
Vector2 size;
|
||||
int life;
|
||||
}
|
||||
|
||||
struct Ball
|
||||
{
|
||||
Vector2 position;
|
||||
Vector2 speed;
|
||||
int radius;
|
||||
bool active;
|
||||
}
|
||||
|
||||
struct Brick
|
||||
{
|
||||
Vector2 position;
|
||||
bool active;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Global Variables Declaration
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
bool game_over = false;
|
||||
bool pause = false;
|
||||
|
||||
Player player;
|
||||
Ball ball;
|
||||
Brick[BRICKS_PER_LINE][LINES_OF_BRICKS] brick;
|
||||
Vector2 brick_size;
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
fn void main()
|
||||
{
|
||||
// Initialization (Note windowTitle is unused on Android)
|
||||
//---------------------------------------------------------
|
||||
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: arkanoid");
|
||||
|
||||
init_game();
|
||||
|
||||
raylib::set_target_fps(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!raylib::window_should_close()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update and Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
update_draw_frame();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
unload_game(); // Unload loaded data (textures, sounds, models...)
|
||||
|
||||
raylib::close_window(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Module Functions Definitions (local)
|
||||
//------------------------------------------------------------------------------------
|
||||
|
||||
// Initialize game variables
|
||||
fn void init_game()
|
||||
{
|
||||
brick_size = { raylib::get_screen_width() / BRICKS_PER_LINE, 40 };
|
||||
|
||||
// Initialize player
|
||||
player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT * 7 / 8 };
|
||||
player.size = { SCREEN_WIDTH / 10, 20 };
|
||||
player.life = PLAYER_MAX_LIFE;
|
||||
|
||||
// Initialize ball
|
||||
ball.position = { SCREEN_WIDTH / 2, SCREEN_HEIGHT * 7 / 8 - 30 };
|
||||
ball.speed = { 0, 0 };
|
||||
ball.radius = 7;
|
||||
ball.active = false;
|
||||
|
||||
// Initialize bricks
|
||||
int initial_down_position = 50;
|
||||
|
||||
for (int i = 0; i < LINES_OF_BRICKS; i++)
|
||||
{
|
||||
for (int j = 0; j < BRICKS_PER_LINE; j++)
|
||||
{
|
||||
brick[i][j].position = { j * brick_size.x + brick_size.x / 2, i * brick_size.y + initial_down_position };
|
||||
brick[i][j].active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update game (one frame)
|
||||
fn void update_game()
|
||||
{
|
||||
if (game_over)
|
||||
{
|
||||
if (raylib::is_key_pressed(keyboard::ENTER))
|
||||
{
|
||||
init_game();
|
||||
game_over = false;
|
||||
}
|
||||
}
|
||||
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
|
||||
|
||||
if (pause) return;
|
||||
// Player movement logic
|
||||
if (raylib::is_key_down(keyboard::LEFT)) player.position.x -= 5;
|
||||
if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2;
|
||||
if (raylib::is_key_down(keyboard::RIGHT)) player.position.x += 5;
|
||||
if ((player.position.x + player.size.x/2) >= SCREEN_WIDTH) player.position.x = SCREEN_WIDTH - player.size.x/2;
|
||||
|
||||
// Ball launching logic
|
||||
if (!ball.active)
|
||||
{
|
||||
if (raylib::is_key_pressed(keyboard::SPACE))
|
||||
{
|
||||
ball.active = true;
|
||||
ball.speed = { 0, -5 };
|
||||
}
|
||||
}
|
||||
|
||||
// Ball movement logic
|
||||
if (ball.active)
|
||||
{
|
||||
ball.position.x += ball.speed.x;
|
||||
ball.position.y += ball.speed.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
ball.position = { player.position.x, SCREEN_HEIGHT * 7 / 8 - 30 };
|
||||
}
|
||||
|
||||
// Collision logic: ball vs walls
|
||||
if (((ball.position.x + ball.radius) >= SCREEN_WIDTH) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1;
|
||||
if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1;
|
||||
if ((ball.position.y + ball.radius) >= SCREEN_HEIGHT)
|
||||
{
|
||||
ball.speed = { 0, 0 };
|
||||
ball.active = false;
|
||||
|
||||
player.life--;
|
||||
}
|
||||
|
||||
// Collision logic: ball vs player
|
||||
if (raylib::check_collision_circle_rec(ball.position, ball.radius,
|
||||
Rectangle{ player.position.x - player.size.x / 2, player.position.y - player.size.y / 2, player.size.x, player.size.y}))
|
||||
{
|
||||
if (ball.speed.y > 0)
|
||||
{
|
||||
ball.speed.y *= -1;
|
||||
ball.speed.x = (ball.position.x - player.position.x) / (player.size.x / 2) * 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Collision logic: ball vs bricks
|
||||
for (int i = 0; i < LINES_OF_BRICKS; i++)
|
||||
{
|
||||
for (int j = 0; j < BRICKS_PER_LINE; j++)
|
||||
{
|
||||
if (brick[i][j].active)
|
||||
{
|
||||
// Hit below
|
||||
if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brick_size.y / 2)) &&
|
||||
((ball.position.y - ball.radius) > (brick[i][j].position.y + brick_size.y / 2 + ball.speed.y)) &&
|
||||
((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x / 2 + ball.radius * 2 / 3)) && (ball.speed.y < 0))
|
||||
{
|
||||
brick[i][j].active = false;
|
||||
ball.speed.y *= -1;
|
||||
}
|
||||
// Hit above
|
||||
else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brick_size.y/2)) &&
|
||||
((ball.position.y + ball.radius) < (brick[i][j].position.y - brick_size.y/2 + ball.speed.y)) &&
|
||||
((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y > 0))
|
||||
{
|
||||
brick[i][j].active = false;
|
||||
ball.speed.y *= -1;
|
||||
}
|
||||
// Hit left
|
||||
else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brick_size.x/2)) &&
|
||||
((ball.position.x + ball.radius) < (brick[i][j].position.x - brick_size.x/2 + ball.speed.x)) &&
|
||||
((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x > 0))
|
||||
{
|
||||
brick[i][j].active = false;
|
||||
ball.speed.x *= -1;
|
||||
}
|
||||
// Hit right
|
||||
else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brick_size.x/2)) &&
|
||||
((ball.position.x - ball.radius) > (brick[i][j].position.x + brick_size.x/2 + ball.speed.x)) &&
|
||||
((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x < 0))
|
||||
{
|
||||
brick[i][j].active = false;
|
||||
ball.speed.x *= -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Game over logic
|
||||
if (player.life <= 0)
|
||||
{
|
||||
game_over = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
game_over = true;
|
||||
for (int i = 0; i < LINES_OF_BRICKS; i++)
|
||||
{
|
||||
for (int j = 0; j < BRICKS_PER_LINE; j++)
|
||||
{
|
||||
if (brick[i][j].active) game_over = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw game (one frame)
|
||||
fn void draw_game()
|
||||
{
|
||||
raylib::begin_drawing();
|
||||
|
||||
raylib::clear_background(raylib::RAYWHITE);
|
||||
|
||||
if (!game_over)
|
||||
{
|
||||
// Draw player bar
|
||||
raylib::draw_rectangle((int)(player.position.x - player.size.x/2), (int)(player.position.y - player.size.y/2), (int)player.size.x, (int)player.size.y, raylib::BLACK);
|
||||
|
||||
// Draw player lives
|
||||
for (int i = 0; i < player.life; i++) raylib::draw_rectangle(20 + 40*i, SCREEN_HEIGHT - 30, 35, 10, raylib::LIGHTGRAY);
|
||||
|
||||
// Draw ball
|
||||
raylib::draw_circle_v(ball.position, ball.radius, raylib::MAROON);
|
||||
|
||||
// Draw bricks
|
||||
for (int i = 0; i < LINES_OF_BRICKS; i++)
|
||||
{
|
||||
for (int j = 0; j < BRICKS_PER_LINE; j++)
|
||||
{
|
||||
if (brick[i][j].active)
|
||||
{
|
||||
if ((i + j) % 2 == 0)
|
||||
{
|
||||
raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::DARKGRAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY);
|
||||
}
|
||||
|
||||
raylib::end_drawing();
|
||||
}
|
||||
|
||||
// Unload game variables
|
||||
fn void unload_game()
|
||||
{
|
||||
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
|
||||
}
|
||||
|
||||
// Update and Draw (one frame)
|
||||
fn void update_draw_frame()
|
||||
{
|
||||
update_game();
|
||||
draw_game();
|
||||
}
|
||||
251
resources/examples/raylib/raylib_snake.c3
Normal file
251
resources/examples/raylib/raylib_snake.c3
Normal file
@@ -0,0 +1,251 @@
|
||||
module snake;
|
||||
/**
|
||||
*
|
||||
* raylib - classic game: snake
|
||||
*
|
||||
* Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria,
|
||||
* converted to C3 and modified by Christoffer Lerno
|
||||
*
|
||||
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
|
||||
*
|
||||
*/
|
||||
|
||||
const SNAKE_LENGTH = 256;
|
||||
const SQUARE_SIZE = 32;
|
||||
const int SCREEN_WIDTH = 800;
|
||||
const int SCREEN_HEIGHT = 450;
|
||||
|
||||
enum SnakeDirection
|
||||
{
|
||||
RIGHT,
|
||||
DOWN,
|
||||
LEFT,
|
||||
UP
|
||||
}
|
||||
struct Snake
|
||||
{
|
||||
Vector2 position;
|
||||
Vector2 size;
|
||||
Color color;
|
||||
}
|
||||
|
||||
struct Food
|
||||
{
|
||||
Vector2 position;
|
||||
Vector2 size;
|
||||
bool active;
|
||||
Color color;
|
||||
}
|
||||
|
||||
|
||||
int frames_counter = 0;
|
||||
bool game_over = false;
|
||||
bool pause = false;
|
||||
|
||||
Food fruit;
|
||||
SnakeDirection snake_direction;
|
||||
Snake[SNAKE_LENGTH] snake;
|
||||
Vector2[SNAKE_LENGTH] snake_position;
|
||||
bool allow_move = false;
|
||||
Vector2 offset;
|
||||
int counter_tail = 0;
|
||||
|
||||
fn void main()
|
||||
{
|
||||
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: snake");
|
||||
init_game();
|
||||
raylib::set_target_fps(60);
|
||||
|
||||
while (!raylib::window_should_close()) // Detect window close button or ESC key
|
||||
{
|
||||
update_draw_frame();
|
||||
}
|
||||
|
||||
unload_game();
|
||||
|
||||
raylib::close_window();
|
||||
}
|
||||
|
||||
// Initialize game variables
|
||||
fn void init_game()
|
||||
{
|
||||
frames_counter = 0;
|
||||
game_over = false;
|
||||
pause = false;
|
||||
|
||||
counter_tail = 1;
|
||||
allow_move = false;
|
||||
snake_direction = SnakeDirection.RIGHT;
|
||||
offset.x = SCREEN_WIDTH % SQUARE_SIZE;
|
||||
offset.y = SCREEN_HEIGHT % SQUARE_SIZE;
|
||||
|
||||
for (int i = 0; i < SNAKE_LENGTH; i++)
|
||||
{
|
||||
snake[i].position = { offset.x / 2, offset.y / 2 };
|
||||
snake[i].size = { SQUARE_SIZE, SQUARE_SIZE };
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
snake[i].color = raylib::DARKBLUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
snake[i].color = raylib::BLUE;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < SNAKE_LENGTH; i++)
|
||||
{
|
||||
snake_position[i] = { 0.0f, 0.0f };
|
||||
}
|
||||
|
||||
fruit.size = { SQUARE_SIZE, SQUARE_SIZE };
|
||||
fruit.color = raylib::SKYBLUE;
|
||||
fruit.active = false;
|
||||
}
|
||||
|
||||
|
||||
fn void update_game()
|
||||
{
|
||||
if (game_over)
|
||||
{
|
||||
if (raylib::is_key_pressed(keyboard::ENTER))
|
||||
{
|
||||
init_game();
|
||||
game_over = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
|
||||
|
||||
if (pause) return;
|
||||
|
||||
if (raylib::is_key_pressed(keyboard::RIGHT) && allow_move)
|
||||
{
|
||||
snake_direction = (SnakeDirection)((snake_direction + 1) % 4);
|
||||
allow_move = false;
|
||||
}
|
||||
if (raylib::is_key_pressed(keyboard::LEFT) && allow_move)
|
||||
{
|
||||
snake_direction = (SnakeDirection)((snake_direction + 3) % 4);
|
||||
allow_move = false;
|
||||
}
|
||||
|
||||
// Snake movement
|
||||
for (int i = 0; i < counter_tail; i++) snake_position[i] = snake[i].position;
|
||||
|
||||
if (frames_counter++ % 5 != 0) return;
|
||||
|
||||
allow_move = true;
|
||||
switch (snake_direction)
|
||||
{
|
||||
case RIGHT:
|
||||
snake[0].position.x += SQUARE_SIZE;
|
||||
snake[0].position.y += 0;
|
||||
case UP:
|
||||
snake[0].position.x += 0;
|
||||
snake[0].position.y += -SQUARE_SIZE;
|
||||
case DOWN:
|
||||
snake[0].position.x += 0;
|
||||
snake[0].position.y += SQUARE_SIZE;
|
||||
case LEFT:
|
||||
snake[0].position.x += -SQUARE_SIZE;
|
||||
snake[0].position.y += 0;
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
for (int i = 1; i < counter_tail; i++)
|
||||
{
|
||||
snake[i].position = snake_position[i - 1];
|
||||
}
|
||||
|
||||
// Wall behaviour
|
||||
if (((snake[0].position.x) > (SCREEN_WIDTH - offset.x)) ||
|
||||
((snake[0].position.y) > (SCREEN_HEIGHT - offset.y)) ||
|
||||
(snake[0].position.x < 0) || (snake[0].position.y < 0))
|
||||
{
|
||||
game_over = true;
|
||||
}
|
||||
|
||||
// Collision with yourself
|
||||
for (int i = 1; i < counter_tail; i++)
|
||||
{
|
||||
if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) game_over = true;
|
||||
}
|
||||
|
||||
// Fruit position calculation
|
||||
if (!fruit.active)
|
||||
{
|
||||
fruit.active = true;
|
||||
fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
|
||||
|
||||
for (int i = 0; i < counter_tail; i++)
|
||||
{
|
||||
while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y))
|
||||
{
|
||||
fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 };
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Collision
|
||||
if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) &&
|
||||
(snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y))
|
||||
{
|
||||
snake[counter_tail].position = snake_position[counter_tail - 1];
|
||||
counter_tail += 1;
|
||||
fruit.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Draw game (one frame)
|
||||
fn void draw_game()
|
||||
{
|
||||
raylib::begin_drawing();
|
||||
|
||||
raylib::clear_background(raylib::RAYWHITE);
|
||||
|
||||
if (!game_over)
|
||||
{
|
||||
// Draw grid lines
|
||||
for (int i = 0; i < SCREEN_WIDTH / SQUARE_SIZE + 1; i++)
|
||||
{
|
||||
raylib::draw_line_v({SQUARE_SIZE * i + offset.x/2, offset.y/2}, {SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY);
|
||||
}
|
||||
|
||||
for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++)
|
||||
{
|
||||
raylib::draw_line_v({offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY);
|
||||
}
|
||||
|
||||
// Draw snake
|
||||
for (int i = 0; i < counter_tail; i++) raylib::draw_rectangle_v(snake[i].position, snake[i].size, snake[i].color);
|
||||
|
||||
// Draw fruit to pick
|
||||
raylib::draw_rectangle_v(fruit.position, fruit.size, fruit.color);
|
||||
|
||||
if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT / 2 - 40, 40, raylib::GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY);
|
||||
}
|
||||
|
||||
raylib::end_drawing();
|
||||
}
|
||||
|
||||
// Unload game variables
|
||||
fn void unload_game()
|
||||
{
|
||||
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
|
||||
}
|
||||
|
||||
// Update and Draw (one frame)
|
||||
fn void update_draw_frame()
|
||||
{
|
||||
update_game();
|
||||
draw_game();
|
||||
}
|
||||
|
||||
793
resources/examples/raylib/raylib_tetris.c3
Normal file
793
resources/examples/raylib/raylib_tetris.c3
Normal file
@@ -0,0 +1,793 @@
|
||||
module tetris;
|
||||
/**
|
||||
* raylib - classic game: tetris
|
||||
*
|
||||
* Sample game developed by Marc Palau and Ramon Santamaria,
|
||||
* converted to C3 by Christoffer Lerno.
|
||||
*
|
||||
* This game has been created using raylib v1.3 (www.raylib.com)
|
||||
*
|
||||
* Copyright (c) 2015 Ramon Santamaria (@raysan5)
|
||||
*/
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Some Defines
|
||||
//----------------------------------------------------------------------------------
|
||||
const SQUARE_SIZE = 20;
|
||||
const GRID_HORIZONTAL_SIZE = 12;
|
||||
const GRID_VERTICAL_SIZE = 20;
|
||||
|
||||
const LATERAL_SPEED = 10;
|
||||
const TURNING_SPEED = 12;
|
||||
const FAST_FALL_AWAIT_COUNTER = 30;
|
||||
const FADING_TIME = 33;
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
// Types and Structures Definition
|
||||
//----------------------------------------------------------------------------------
|
||||
enum GridSquare { EMPTY, MOVING, FULL, BLOCK, FADING }
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Global Variables Declaration
|
||||
//------------------------------------------------------------------------------------
|
||||
const int SCREEN_WIDTH = 800;
|
||||
const int SCREEN_HEIGHT = 450;
|
||||
|
||||
bool game_over = false;
|
||||
bool pause = false;
|
||||
|
||||
// Matrices
|
||||
GridSquare[GRID_VERTICAL_SIZE][GRID_HORIZONTAL_SIZE] grid;
|
||||
GridSquare[4][4] piece;
|
||||
GridSquare[4][4] incoming_piece;
|
||||
|
||||
struct IntVec
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
}
|
||||
// These variables keep track of the active piece position
|
||||
int piece_position_x = 0;
|
||||
int piece_position_y = 0;
|
||||
|
||||
// Game parameters
|
||||
Color fading_color;
|
||||
//int fallingSpeed; // In frames
|
||||
|
||||
bool begin_play = true; // This var is only true at the begining of the game, used for the first matrix creations
|
||||
bool piece_active = false;
|
||||
bool detection = false;
|
||||
bool line_to_delete = false;
|
||||
|
||||
// Statistics
|
||||
int level = 1;
|
||||
int lines = 0;
|
||||
|
||||
// Counters
|
||||
int gravity_movement_counter = 0;
|
||||
int lateral_movement_counter = 0;
|
||||
int turn_movement_counter = 0;
|
||||
int fast_fall_movement_counter = 0;
|
||||
|
||||
int fade_line_counter = 0;
|
||||
|
||||
// Based on level
|
||||
int gravity_speed = 30;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------
|
||||
// Program main entry point
|
||||
//------------------------------------------------------------------------------------
|
||||
fn void main()
|
||||
{
|
||||
// Initialization (Note windowTitle is unused on Android)
|
||||
//---------------------------------------------------------
|
||||
raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: tetris");
|
||||
|
||||
init_game();
|
||||
|
||||
raylib::set_target_fps(60);
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Main game loop
|
||||
while (!raylib::window_should_close()) // Detect window close button or ESC key
|
||||
{
|
||||
// Update and Draw
|
||||
//----------------------------------------------------------------------------------
|
||||
update_draw_frame();
|
||||
//----------------------------------------------------------------------------------
|
||||
}
|
||||
// De-Initialization
|
||||
//--------------------------------------------------------------------------------------
|
||||
unload_game(); // Unload loaded data (textures, sounds, models...)
|
||||
|
||||
raylib::close_window(); // Close window and OpenGL context
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Game Module Functions Definition
|
||||
//--------------------------------------------------------------------------------------
|
||||
|
||||
// Initialize game variables
|
||||
fn void init_game()
|
||||
{
|
||||
// Initialize game statistics
|
||||
level = 1;
|
||||
lines = 0;
|
||||
|
||||
fading_color = raylib::GRAY;
|
||||
|
||||
piece_position_x = 0;
|
||||
piece_position_y = 0;
|
||||
|
||||
pause = false;
|
||||
|
||||
begin_play = true;
|
||||
piece_active = false;
|
||||
detection = false;
|
||||
line_to_delete = false;
|
||||
|
||||
// Counters
|
||||
gravity_movement_counter = 0;
|
||||
lateral_movement_counter = 0;
|
||||
turn_movement_counter = 0;
|
||||
fast_fall_movement_counter = 0;
|
||||
|
||||
fade_line_counter = 0;
|
||||
gravity_speed = 30;
|
||||
|
||||
// Initialize grid matrices
|
||||
for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++)
|
||||
{
|
||||
for (int j = 0; j < GRID_VERTICAL_SIZE; j++)
|
||||
{
|
||||
if ((j == GRID_VERTICAL_SIZE - 1) || (i == 0) || (i == GRID_HORIZONTAL_SIZE - 1))
|
||||
{
|
||||
grid[i][j] = BLOCK;
|
||||
}
|
||||
else
|
||||
{
|
||||
grid[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize incoming piece matrices
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j< 4; j++)
|
||||
{
|
||||
incoming_piece[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update game (one frame)
|
||||
fn void update_game()
|
||||
{
|
||||
if (game_over)
|
||||
{
|
||||
if (raylib::is_key_pressed(keyboard::ENTER))
|
||||
{
|
||||
init_game();
|
||||
game_over = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause;
|
||||
|
||||
if (pause) return;
|
||||
if (line_to_delete)
|
||||
{
|
||||
// Animation when deleting lines
|
||||
fade_line_counter++;
|
||||
|
||||
fading_color = fade_line_counter % 8 < 4 ? raylib::MAROON : raylib::GRAY;
|
||||
|
||||
if (fade_line_counter >= FADING_TIME)
|
||||
{
|
||||
lines += delete_complete_lines();
|
||||
fade_line_counter = 0;
|
||||
line_to_delete = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!piece_active)
|
||||
{
|
||||
// Get another piece
|
||||
piece_active = create_piece();
|
||||
|
||||
// We leave a little time before starting the fast falling down
|
||||
fast_fall_movement_counter = 0;
|
||||
}
|
||||
else // Piece falling
|
||||
{
|
||||
// Counters update
|
||||
fast_fall_movement_counter++;
|
||||
gravity_movement_counter++;
|
||||
lateral_movement_counter++;
|
||||
turn_movement_counter++;
|
||||
|
||||
// We make sure to move if we've pressed the key this frame
|
||||
if (raylib::is_key_pressed(keyboard::LEFT) || raylib::is_key_pressed(keyboard::RIGHT)) lateral_movement_counter = LATERAL_SPEED;
|
||||
if (raylib::is_key_pressed(keyboard::UP)) turn_movement_counter = TURNING_SPEED;
|
||||
|
||||
// Fall down
|
||||
if (raylib::is_key_down(keyboard::DOWN) && (fast_fall_movement_counter >= FAST_FALL_AWAIT_COUNTER))
|
||||
{
|
||||
// We make sure the piece is going to fall this frame
|
||||
gravity_movement_counter += gravity_speed;
|
||||
}
|
||||
|
||||
if (gravity_movement_counter >= gravity_speed)
|
||||
{
|
||||
// Basic falling movement
|
||||
if (check_detection()) detection = true;
|
||||
|
||||
// Check if the piece has collided with another piece or with the boundings
|
||||
resolve_falling_movement(&detection, &piece_active);
|
||||
|
||||
// Check if we fullfilled a line and if so, erase the line and pull down the the lines above
|
||||
check_completion(&line_to_delete);
|
||||
|
||||
gravity_movement_counter = 0;
|
||||
}
|
||||
|
||||
// Move laterally at player's will
|
||||
if (lateral_movement_counter >= LATERAL_SPEED)
|
||||
{
|
||||
// Update the lateral movement and if success, reset the lateral counter
|
||||
if (!resolve_lateral_movement()) lateral_movement_counter = 0;
|
||||
}
|
||||
|
||||
// Turn the piece at player's will
|
||||
if (turn_movement_counter >= TURNING_SPEED)
|
||||
{
|
||||
// Update the turning movement and reset the turning counter
|
||||
if (resolve_turn_movement()) turn_movement_counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Game over logic
|
||||
for (int j = 0; j < 2; j++)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if (grid[i][j] == GridSquare.FULL)
|
||||
{
|
||||
game_over = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw game (one frame)
|
||||
fn void draw_game()
|
||||
{
|
||||
raylib::begin_drawing();
|
||||
|
||||
raylib::clear_background(raylib::RAYWHITE);
|
||||
|
||||
if (game_over)
|
||||
{
|
||||
raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width() / 2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20) / 2, raylib::get_screen_height() / 2 - 50, 20, raylib::GRAY);
|
||||
raylib::end_drawing();
|
||||
return;
|
||||
}
|
||||
|
||||
// Draw gameplay area
|
||||
IntVec offset = {
|
||||
SCREEN_WIDTH / 2 - (GRID_HORIZONTAL_SIZE * SQUARE_SIZE / 2) - 50,
|
||||
SCREEN_HEIGHT / 2 - ((GRID_VERTICAL_SIZE - 1) * SQUARE_SIZE / 2) + SQUARE_SIZE * 2
|
||||
};
|
||||
offset.y -= 50; // NOTE: Harcoded position!
|
||||
|
||||
int controller = offset.x;
|
||||
|
||||
for (int j = 0; j < GRID_VERTICAL_SIZE; j++)
|
||||
{
|
||||
for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++)
|
||||
{
|
||||
// Draw each square of the grid
|
||||
switch (grid[i][j])
|
||||
{
|
||||
case EMPTY:
|
||||
raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY );
|
||||
raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
|
||||
raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
|
||||
raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY );
|
||||
offset.x += SQUARE_SIZE;
|
||||
case FULL:
|
||||
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY);
|
||||
offset.x += SQUARE_SIZE;
|
||||
case MOVING:
|
||||
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::DARKGRAY);
|
||||
offset.x += SQUARE_SIZE;
|
||||
case BLOCK:
|
||||
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::LIGHTGRAY);
|
||||
offset.x += SQUARE_SIZE;
|
||||
case FADING:
|
||||
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fading_color);
|
||||
offset.x += SQUARE_SIZE;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
offset.x = controller;
|
||||
offset.y += SQUARE_SIZE;
|
||||
}
|
||||
|
||||
// Draw incoming piece (hardcoded)
|
||||
offset.x = 500;
|
||||
offset.y = 45;
|
||||
|
||||
controller = offset.x;
|
||||
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
switch (incoming_piece[i][j])
|
||||
{
|
||||
case EMPTY:
|
||||
raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY);
|
||||
raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
|
||||
raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
|
||||
raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY);
|
||||
offset.x += SQUARE_SIZE;
|
||||
case MOVING:
|
||||
raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY);
|
||||
offset.x += SQUARE_SIZE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offset.x = controller;
|
||||
offset.y += SQUARE_SIZE;
|
||||
}
|
||||
|
||||
raylib::draw_text("INCOMING:", offset.x, offset.y - 100, 10, raylib::GRAY);
|
||||
raylib::draw_text(raylib::text_format("LINES: %04i", lines), offset.x, offset.y + 20, 10, raylib::GRAY);
|
||||
|
||||
if (pause)
|
||||
{
|
||||
raylib::draw_text("GAME PAUSED", SCREEN_WIDTH / 2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY);
|
||||
}
|
||||
raylib::end_drawing();
|
||||
}
|
||||
|
||||
// Unload game variables
|
||||
fn void unload_game()
|
||||
{
|
||||
// TODO: Unload all dynamic loaded data (textures, sounds, models...)
|
||||
}
|
||||
|
||||
// Update and Draw (one frame)
|
||||
fn void update_draw_frame()
|
||||
{
|
||||
update_game();
|
||||
draw_game();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Additional module functions
|
||||
//--------------------------------------------------------------------------------------
|
||||
fn bool create_piece()
|
||||
{
|
||||
piece_position_x = (int)((GRID_HORIZONTAL_SIZE - 4)/2);
|
||||
piece_position_y = 0;
|
||||
|
||||
// If the game is starting and you are going to create the first piece, we create an extra one
|
||||
if (begin_play)
|
||||
{
|
||||
get_random_piece();
|
||||
begin_play = false;
|
||||
}
|
||||
|
||||
// We assign the incoming piece to the actual piece
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j< 4; j++)
|
||||
{
|
||||
piece[i][j] = incoming_piece[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
// We assign a random piece to the incoming one
|
||||
get_random_piece();
|
||||
|
||||
// Assign the piece to the grid
|
||||
for (int i = piece_position_x; i < piece_position_x + 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
if (piece[i - (int)piece_position_x][j] == GridSquare.MOVING) grid[i][j] = MOVING;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn void get_random_piece()
|
||||
{
|
||||
int random = raylib::get_random_value(0, 6);
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
for (int j = 0; j < 4; j++)
|
||||
{
|
||||
incoming_piece[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
switch (random)
|
||||
{
|
||||
case 0:
|
||||
incoming_piece[1][1] = MOVING;
|
||||
incoming_piece[2][1] = MOVING;
|
||||
incoming_piece[1][2] = MOVING;
|
||||
incoming_piece[2][2] = MOVING; //Cube
|
||||
case 1:
|
||||
incoming_piece[1][0] = MOVING;
|
||||
incoming_piece[1][1] = MOVING;
|
||||
incoming_piece[1][2] = MOVING;
|
||||
incoming_piece[2][2] = MOVING; //L
|
||||
case 2:
|
||||
incoming_piece[1][2] = MOVING;
|
||||
incoming_piece[2][0] = MOVING;
|
||||
incoming_piece[2][1] = MOVING;
|
||||
incoming_piece[2][2] = MOVING; //L inversa
|
||||
case 3:
|
||||
incoming_piece[0][1] = MOVING;
|
||||
incoming_piece[1][1] = MOVING;
|
||||
incoming_piece[2][1] = MOVING;
|
||||
incoming_piece[3][1] = MOVING; //Recta
|
||||
case 4:
|
||||
incoming_piece[1][0] = MOVING;
|
||||
incoming_piece[1][1] = MOVING;
|
||||
incoming_piece[1][2] = MOVING;
|
||||
incoming_piece[2][1] = MOVING; //Creu tallada
|
||||
case 5:
|
||||
incoming_piece[1][1] = MOVING;
|
||||
incoming_piece[2][1] = MOVING;
|
||||
incoming_piece[2][2] = MOVING;
|
||||
incoming_piece[3][2] = MOVING; //S
|
||||
case 6:
|
||||
incoming_piece[1][2] = MOVING;
|
||||
incoming_piece[2][2] = MOVING;
|
||||
incoming_piece[2][1] = MOVING;
|
||||
incoming_piece[3][1] = MOVING; //S inversa
|
||||
default:
|
||||
unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
fn void resolve_falling_movement(bool* detection_ref, bool* piece_active_ref)
|
||||
{
|
||||
// If we finished moving this piece, we stop it
|
||||
if (*detection_ref)
|
||||
{
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
grid[i][j] = FULL;
|
||||
*detection_ref = false;
|
||||
*piece_active_ref = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else // We move down the piece
|
||||
{
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
grid[i][j+1] = MOVING;
|
||||
grid[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
piece_position_y++;
|
||||
}
|
||||
}
|
||||
|
||||
fn bool resolve_lateral_movement()
|
||||
{
|
||||
bool collision = false;
|
||||
|
||||
// Piece movement
|
||||
if (raylib::is_key_down(keyboard::LEFT)) // Move left
|
||||
{
|
||||
// Check if is possible to move to left
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
// Check if we are touching the left wall or we have a full square at the left
|
||||
if ((i-1 == 0) || (grid[i-1][j] == GridSquare.FULL)) collision = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If able, move left
|
||||
if (!collision)
|
||||
{
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) // We check the matrix from left to right
|
||||
{
|
||||
// Move everything to the left
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
grid[i-1][j] = MOVING;
|
||||
grid[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
piece_position_x--;
|
||||
}
|
||||
}
|
||||
else if (raylib::is_key_down(keyboard::RIGHT)) // Move right
|
||||
{
|
||||
// Check if is possible to move to right
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
// Check if we are touching the right wall or we have a full square at the right
|
||||
if ((i+1 == GRID_HORIZONTAL_SIZE - 1) || (grid[i+1][j] == GridSquare.FULL))
|
||||
{
|
||||
collision = true;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If able move right
|
||||
if (!collision)
|
||||
{
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = GRID_HORIZONTAL_SIZE - 1; i >= 1; i--) // We check the matrix from right to left
|
||||
{
|
||||
// Move everything to the right
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
grid[i+1][j] = MOVING;
|
||||
grid[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
piece_position_x++;
|
||||
}
|
||||
}
|
||||
|
||||
return collision;
|
||||
}
|
||||
|
||||
fn bool resolve_turn_movement()
|
||||
{
|
||||
// Input for turning the piece
|
||||
if (raylib::is_key_down(keyboard::UP))
|
||||
{
|
||||
GridSquare aux;
|
||||
bool checker = false;
|
||||
|
||||
// Check all turning possibilities
|
||||
if ((grid[piece_position_x + 3][piece_position_y] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x][piece_position_y] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x][piece_position_y] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 3][piece_position_y + 3] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 3][piece_position_y] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 3][piece_position_y] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x][piece_position_y + 3] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x][piece_position_y] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x][piece_position_y + 3] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 1][piece_position_y] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x][piece_position_y + 2] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 3][piece_position_y + 1] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 1][piece_position_y] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 1][piece_position_y] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 2][piece_position_y + 3] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x][piece_position_y + 2] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 2][piece_position_y] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x][piece_position_y + 1] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 3][piece_position_y + 2] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 2][piece_position_y] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 2][piece_position_y] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 1][piece_position_y + 3] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x][piece_position_y + 1] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 1][piece_position_y + 1] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 2][piece_position_y + 1] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 2][piece_position_y + 2] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if ((grid[piece_position_x + 1][piece_position_y + 2] == GridSquare.MOVING) &&
|
||||
(grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.EMPTY) &&
|
||||
(grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.MOVING)) checker = true;
|
||||
|
||||
if (!checker)
|
||||
{
|
||||
aux = piece[0][0];
|
||||
piece[0][0] = piece[3][0];
|
||||
piece[3][0] = piece[3][3];
|
||||
piece[3][3] = piece[0][3];
|
||||
piece[0][3] = aux;
|
||||
|
||||
aux = piece[1][0];
|
||||
piece[1][0] = piece[3][1];
|
||||
piece[3][1] = piece[2][3];
|
||||
piece[2][3] = piece[0][2];
|
||||
piece[0][2] = aux;
|
||||
|
||||
aux = piece[2][0];
|
||||
piece[2][0] = piece[3][2];
|
||||
piece[3][2] = piece[1][3];
|
||||
piece[1][3] = piece[0][1];
|
||||
piece[0][1] = aux;
|
||||
|
||||
aux = piece[1][1];
|
||||
piece[1][1] = piece[2][1];
|
||||
piece[2][1] = piece[2][2];
|
||||
piece[2][2] = piece[1][2];
|
||||
piece[1][2] = aux;
|
||||
}
|
||||
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if (grid[i][j] == GridSquare.MOVING)
|
||||
{
|
||||
grid[i][j] = EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = piece_position_x; i < piece_position_x + 4; i++)
|
||||
{
|
||||
for (int j = piece_position_y; j < piece_position_y + 4; j++)
|
||||
{
|
||||
if (piece[i - piece_position_x][j - piece_position_y] == GridSquare.MOVING)
|
||||
{
|
||||
grid[i][j] = MOVING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn bool check_detection()
|
||||
{
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
if ((grid[i][j] == GridSquare.MOVING) && ((grid[i][j+1] == GridSquare.FULL)
|
||||
|| (grid[i][j+1] == GridSquare.BLOCK))) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
fn void check_completion(bool *line_to_delete_ref)
|
||||
{
|
||||
int calculator = 0;
|
||||
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
calculator = 0;
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
// Count each square of the line
|
||||
if (grid[i][j] == GridSquare.FULL)
|
||||
{
|
||||
calculator++;
|
||||
}
|
||||
|
||||
// Check if we completed the whole line
|
||||
if (calculator == GRID_HORIZONTAL_SIZE - 2)
|
||||
{
|
||||
*line_to_delete_ref = true;
|
||||
calculator = 0;
|
||||
// points++;
|
||||
|
||||
// Mark the completed line
|
||||
for (int z = 1; z < GRID_HORIZONTAL_SIZE - 1; z++)
|
||||
{
|
||||
grid[z][j] = FADING;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn int delete_complete_lines()
|
||||
{
|
||||
int lines_to_erase = 0;
|
||||
// Erase the completed line
|
||||
for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--)
|
||||
{
|
||||
while (grid[1][j] == GridSquare.FADING)
|
||||
{
|
||||
lines_to_erase++;
|
||||
for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++)
|
||||
{
|
||||
grid[i][j] = GridSquare.EMPTY;
|
||||
}
|
||||
|
||||
for (int j2 = j-1; j2 >= 0; j2--)
|
||||
{
|
||||
for (int i2 = 1; i2 < GRID_HORIZONTAL_SIZE - 1; i2++)
|
||||
{
|
||||
switch (grid[i2][j2])
|
||||
{
|
||||
case FULL:
|
||||
grid[i2][j2+1] = GridSquare.FULL;
|
||||
grid[i2][j2] = GridSquare.EMPTY;
|
||||
case FADING:
|
||||
grid[i2][j2+1] = GridSquare.FADING;
|
||||
grid[i2][j2] = GridSquare.EMPTY;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lines_to_erase;
|
||||
}
|
||||
|
||||
44
resources/examples/retry.c3
Normal file
44
resources/examples/retry.c3
Normal file
@@ -0,0 +1,44 @@
|
||||
module test;
|
||||
import libc;
|
||||
|
||||
fault TestErr
|
||||
{
|
||||
NOPE
|
||||
}
|
||||
|
||||
fn int! eventually_succeed()
|
||||
{
|
||||
static int i = 0;
|
||||
if (i++ < 3) return TestErr.NOPE!;
|
||||
return i * 3;
|
||||
}
|
||||
|
||||
macro @retry(#function, int retries = 3)
|
||||
{
|
||||
var $Type = $typeof(#function);
|
||||
anyerr e;
|
||||
do
|
||||
{
|
||||
$Type! result = #function;
|
||||
if (catch err = result)
|
||||
{
|
||||
e = err;
|
||||
continue;
|
||||
}
|
||||
return result;
|
||||
} while (retries-- > 0);
|
||||
return e!;
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int! result = @retry(eventually_succeed());
|
||||
if (try result)
|
||||
{
|
||||
libc::printf("Got result: %d\n", result);
|
||||
}
|
||||
else
|
||||
{
|
||||
libc::printf("Failed :(\n");
|
||||
}
|
||||
}
|
||||
@@ -46,9 +46,9 @@ fn void eval_AtA_times_u(double[] u, double[] atau) @noinline
|
||||
fn int main(int argc, char **argv)
|
||||
{
|
||||
int n = (argc == 2) ? atoi(argv[1]) : 2000;
|
||||
temparr = @array::make(double, n);
|
||||
double[] u = @array::make(double, n);
|
||||
double[] v = @array::make(double, n);
|
||||
temparr = array::alloc(double, n);
|
||||
double[] u = array::alloc(double, n);
|
||||
double[] v = array::alloc(double, n);
|
||||
foreach(&uval : u) *uval = 1;
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
|
||||
20
resources/examples/swap.c3
Normal file
20
resources/examples/swap.c3
Normal file
@@ -0,0 +1,20 @@
|
||||
module test;
|
||||
import libc;
|
||||
|
||||
/**
|
||||
* @checked a = b, b = a
|
||||
*/
|
||||
macro void @swap(&a, &b)
|
||||
{
|
||||
$typeof(a) temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
fn void main()
|
||||
{
|
||||
int x = 123;
|
||||
int y = 456;
|
||||
@swap(x, y);
|
||||
libc::printf("x: %d y: %d\n", x, y);
|
||||
}
|
||||
@@ -238,8 +238,8 @@ macro_argument_list
|
||||
;
|
||||
|
||||
declaration
|
||||
: failable_type IDENT '=' initializer
|
||||
| failable_type IDENT
|
||||
: optional_type IDENT '=' initializer
|
||||
| optional_type IDENT
|
||||
;
|
||||
|
||||
param_declaration
|
||||
@@ -291,7 +291,7 @@ type
|
||||
| type '[' '+' ']'
|
||||
;
|
||||
|
||||
failable_type
|
||||
optional_type
|
||||
: type
|
||||
| type '!'
|
||||
;
|
||||
@@ -419,8 +419,8 @@ expression_statement
|
||||
|
||||
|
||||
if_expr
|
||||
: failable_type IDENT '=' initializer
|
||||
| failable_type IDENT NOFAIL_ASSIGN expression
|
||||
: optional_type IDENT '=' initializer
|
||||
| optional_type IDENT NOFAIL_ASSIGN expression
|
||||
| expression
|
||||
;
|
||||
|
||||
@@ -513,7 +513,7 @@ func_name
|
||||
;
|
||||
|
||||
func_declaration
|
||||
: FUNC failable_type func_name opt_parameter_type_list opt_attributes
|
||||
: FUNC optional_type func_name opt_parameter_type_list opt_attributes
|
||||
;
|
||||
|
||||
func_definition
|
||||
@@ -594,7 +594,7 @@ const_declaration
|
||||
;
|
||||
|
||||
func_typedef
|
||||
: FUNC failable_type opt_parameter_type_list
|
||||
: FUNC optional_type opt_parameter_type_list
|
||||
;
|
||||
|
||||
typedef_declaration
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
module antiprime;
|
||||
import std::io;
|
||||
|
||||
extern fn int printf(char* message, ...);
|
||||
|
||||
fn int countDivisors(int n)
|
||||
{
|
||||
if (n < 2) return 1;
|
||||
@@ -25,7 +23,7 @@ fn int main()
|
||||
int d = countDivisors(n);
|
||||
if (d > maxDiv)
|
||||
{
|
||||
printf("%d ", n);
|
||||
io::printf("%d ", n);
|
||||
maxDiv = d;
|
||||
count++;
|
||||
}
|
||||
|
||||
87
resources/testfragments/allocators_testing.c3
Normal file
87
resources/testfragments/allocators_testing.c3
Normal file
@@ -0,0 +1,87 @@
|
||||
// #target: macos-x64
|
||||
module test;
|
||||
import std::io;
|
||||
import libc;
|
||||
enum Foo
|
||||
{
|
||||
ABC
|
||||
}
|
||||
|
||||
fn void print_pages()
|
||||
{
|
||||
mem::temp_allocator().print_pages(io::stdout());
|
||||
}
|
||||
|
||||
fn void setstring(char* dst, char[] str)
|
||||
{
|
||||
foreach (char c : str)
|
||||
{
|
||||
dst++[0] = c;
|
||||
}
|
||||
dst[0] = 0;
|
||||
}
|
||||
|
||||
fn void testAllocator(Allocator* a, int val)
|
||||
{
|
||||
io::println("Test");
|
||||
void* data = a.alloc_aligned(val, 128, 16)!!;
|
||||
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
|
||||
data = a.calloc_aligned(val, 128, 16)!!;
|
||||
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
|
||||
data = a.realloc_aligned(data, val + 1, 128, 16)!!;
|
||||
io::printf("Aligned with offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
|
||||
data = a.realloc_aligned(data, val + 1, 128, 0)!!;
|
||||
io::printf("No offset %p, align 16: %s offset align 128: %s\n", data, mem::ptr_is_aligned(data, 16), mem::ptr_is_aligned(data + 16, 128));
|
||||
io::printfln("Freeing %p", data);
|
||||
a.free_aligned(data)!!;
|
||||
}
|
||||
fn void main()
|
||||
{
|
||||
char* small = tmalloc(128);
|
||||
setstring(small, "small");
|
||||
libc::printf("Small1: %p %s\n", small, small);
|
||||
print_pages();
|
||||
small = trealloc(small, 129, 1024 * 16);
|
||||
libc::printf("Small2: %p %s\n", small, small);
|
||||
print_pages();
|
||||
small = trealloc(small, 12933);
|
||||
libc::printf("Small3: %p %s\n", small, small);
|
||||
print_pages();
|
||||
char* first_big = tmalloc(9512);
|
||||
void *big = tmalloc(4095);
|
||||
io::printf("Big: %p\n", big);
|
||||
io::printf("Small: %p\n", tmalloc(13));
|
||||
print_pages();
|
||||
@pool() {
|
||||
big = trealloc(big, 5067);
|
||||
print_pages();
|
||||
void* hidden = tmalloc(4096);
|
||||
io::printf("Hidden: %p\n", hidden);
|
||||
io::printf("Big: %p\n", big);
|
||||
big = trealloc(big, 4096, 256);
|
||||
io::printf("Big: %p\n", big);
|
||||
io::printf("First big: %p\n", first_big);
|
||||
print_pages();
|
||||
};
|
||||
mem::@tscoped()
|
||||
{
|
||||
io::printf("Malloc: %p\n", malloc(23));
|
||||
io::printf("Malloc: %p\n", malloc(23));
|
||||
};
|
||||
io::printf("Malloc: %p\n", malloc(23));
|
||||
@pool()
|
||||
{
|
||||
io::printf("Talloc: %p\n", tmalloc(22));
|
||||
};
|
||||
testAllocator(mem::temp_allocator(), 126);
|
||||
testAllocator(mem::temp_allocator(), 12600);
|
||||
ArenaAllocator aa;
|
||||
aa.init(&&char[1024] {});
|
||||
testAllocator(&aa, 126);
|
||||
io::println("Test dynamic arena");
|
||||
DynamicArenaAllocator dynamic_arena;
|
||||
dynamic_arena.init(1024);
|
||||
testAllocator(&dynamic_arena, 112);
|
||||
testAllocator(&dynamic_arena, 712);
|
||||
first_big[3] = 123;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user