mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 03:31:38 -07:00
Compare commits
399 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 14c4fed94c | |||
| c3205316ed | |||
| b542ef273d | |||
| 404132de17 | |||
| cd43f4c94c | |||
| 23ff98dea0 | |||
| c3a8e232a5 | |||
| 7f89f6b582 | |||
| 159150c6d9 | |||
| 527b39ce2b | |||
| 5af3c7b0e8 | |||
| 5db4bd9944 | |||
| 20b410f149 | |||
| ae7cbd5171 | |||
| 04f7225ea5 | |||
| f0c6f29f82 | |||
| 7d9a9a2283 | |||
| ba85e432e7 | |||
| cfba29002a | |||
| 11c7b6a2e4 | |||
| 47f9876b36 | |||
| ff31f9a900 | |||
| ebc4867514 | |||
| e1ccba3de5 | |||
| 28570f8fa4 | |||
| 24bd370e1b | |||
| 3e67ae7339 | |||
| 0e52c37865 | |||
| d520b9a1ba | |||
| 5c7d6fcfd0 | |||
| 5ae924f988 | |||
| cae1e02593 | |||
| b09297da81 | |||
| 9abdfaaf6c | |||
| b32ef9e47b | |||
| b8324b0776 | |||
| d0ca045586 | |||
| 673879d1d2 | |||
| 5d1c9583cb | |||
| d017b5de9d | |||
| 93ead4bcb3 | |||
| bbe9b4dee0 | |||
| 3bd00fd6b7 | |||
| 83fec387d4 | |||
| f6f10d10e8 | |||
| 16a7c55334 | |||
| e9e2ab240d | |||
| 842281ddd3 | |||
| 978d7fcb99 | |||
| b267a5964d | |||
| b288613307 | |||
| 4591353724 | |||
| 13107628f8 | |||
| c407687a4c | |||
| 5a50ab7a99 | |||
| 4578544007 | |||
| bdfef08214 | |||
| 42678848b2 | |||
| ab52f8d795 | |||
| d79ee7d530 | |||
| 7e271310ff | |||
| f24de51c65 | |||
| 2252d992d7 | |||
| 2d70a784d1 | |||
| a8a4dc1eb1 | |||
| 9e9e905431 | |||
| 8ee41c20af | |||
| 11c705508d | |||
| 267ae0b4a2 | |||
| 1bc6e6a7cc | |||
| 33a458c520 | |||
| 6a7ccd8c0a | |||
| 9ba2926e7e | |||
| a50b2d5d04 | |||
| 7f9626e5c7 | |||
| 7140c95c55 | |||
| ceef5db547 | |||
| 5ec8dd166a | |||
| 80a32a8182 | |||
| c2b3056094 | |||
| f99f351e01 | |||
| 880c7f01a8 | |||
| 10f0961184 | |||
| eea403d0ab | |||
| 2cc5c4eed3 | |||
| f308d8d73e | |||
| ff0bc3ccad | |||
| 072979c6d2 | |||
| a3d2c40da0 | |||
| 5b1312342e | |||
| 85e31e1b69 | |||
| fb0fb4767b | |||
| 38a9a2b7fc | |||
| 005c6af302 | |||
| 1d14b3059e | |||
| cc2fa8f756 | |||
| d1c9fd4e01 | |||
| 4593730632 | |||
| f62a0891bd | |||
| 4f2d4716ad | |||
| 022b793a7d | |||
| 7267004a55 | |||
| 786c9dfe07 | |||
| 81b24594ce | |||
| 995ba0df9a | |||
| 08392d885e | |||
| d462dbb5be | |||
| 19c32ecb81 | |||
| 494b1e7eaa | |||
| c43d17bfec | |||
| c9723e2dc0 | |||
| 4ba579bc25 | |||
| 58d4d424c6 | |||
| 89ccb5b99f | |||
| 7f5021c8e9 | |||
| 8bec324779 | |||
| e6f26b9931 | |||
| b8c534eba9 | |||
| 95d3f43e15 | |||
| 2d97e1dee3 | |||
| be2dfd42fd | |||
| 851118faf4 | |||
| 53cd7a3d0c | |||
| f170648629 | |||
| 42def957d5 | |||
| 6433a0d31e | |||
| 359e5d9e15 | |||
| e229885b2b | |||
| ee78374281 | |||
| ebe152a155 | |||
| 46582a45bd | |||
| 6b5ea011e7 | |||
| 9503440eb0 | |||
| 3fa4c5043a | |||
| 9db81498d8 | |||
| 7fbe0a6f23 | |||
| 3fd5c3cd85 | |||
| 99121d6ff2 | |||
| 0c0c83ee29 | |||
| b75d59d6e0 | |||
| 71c8a3456e | |||
| 37e3e081c6 | |||
| 5ea9fc3fb0 | |||
| 53f65224f6 | |||
| 902d313c6a | |||
| 45d844f9d2 | |||
| 9b58781122 | |||
| b74f8f2047 | |||
| 88c90cf99a | |||
| 321dcc60e3 | |||
| 400f12f31f | |||
| 2c5a84bb78 | |||
| e01d8a04a9 | |||
| 69afa33fa5 | |||
| 44e0e96612 | |||
| 0839dccfdc | |||
| 2484f4d04b | |||
| d22e5b697d | |||
| 301ee664e9 | |||
| 9b4d4a2c61 | |||
| d9647174a3 | |||
| 4d29b64196 | |||
| 8be1b2e1b1 | |||
| b85258a9fc | |||
| 07897ed78e | |||
| 8115386217 | |||
| f8dd4816ff | |||
| 928a445a14 | |||
| 42cd78497a | |||
| 813f5d211f | |||
| 71d129a709 | |||
| c35762528c | |||
| aa796a1032 | |||
| 5bf71ba75c | |||
| 967981aacd | |||
| 33d05a07de | |||
| 8da8301b09 | |||
| 536cceeef9 | |||
| 098684a6fe | |||
| 694ee02247 | |||
| 0451c88ab6 | |||
| 2ef7bfc06e | |||
| 7bfdb4f9f4 | |||
| 0a35b13411 | |||
| 803f6a6651 | |||
| 672cfd51c3 | |||
| 80cdf8b6a8 | |||
| dc2d5239c5 | |||
| dacfc9de15 | |||
| 689aa4d734 | |||
| 8a46b493fd | |||
| 86abdc0603 | |||
| a634444f99 | |||
| 04a25b11ad | |||
| 40546fbde2 | |||
| c1176c2bcb | |||
| 57853fe1b1 | |||
| 013b3b9fb3 | |||
| dc0f04e53b | |||
| 40606d9272 | |||
| ebf7926fa4 | |||
| dfb3101ecf | |||
| e3d3a81617 | |||
| dbdbbcd60f | |||
| f9aaff99c6 | |||
| 4b718aae75 | |||
| 20db0e7f09 | |||
| 614ea5c168 | |||
| 57565b78a6 | |||
| b5b085914a | |||
| 044e64beb0 | |||
| adca5b57cd | |||
| 3b898e5224 | |||
| 153e7525b5 | |||
| 44a303e577 | |||
| d63878d0dd | |||
| a20c31d6b5 | |||
| 560bdc339b | |||
| 01dfb1dac8 | |||
| ee8d3e03f8 | |||
| 4aad45e3e7 | |||
| fe5c642d9f | |||
| b5fdb3f855 | |||
| 416ff149bd | |||
| 233c6e2d3a | |||
| a29a6d9285 | |||
| a7a31e4c23 | |||
| 72cad591bd | |||
| 684bf7aa61 | |||
| d760b95e4f | |||
| 5e81fc72b9 | |||
| 0977ac111a | |||
| 2db16d6a32 | |||
| 5aa46d31b2 | |||
| 14e8b299b7 | |||
| c7cb754514 | |||
| a5e42a0465 | |||
| d808f9eccf | |||
| 1734eb949f | |||
| 7fae890ef9 | |||
| 94879ed149 | |||
| 2c75fe2314 | |||
| 1da0668653 | |||
| c60fb10a6a | |||
| 7140f42915 | |||
| ad92fbfd4e | |||
| 1416946757 | |||
| 6a8c4ee04c | |||
| 516df9123d | |||
| 818d6dbbea | |||
| 286c5b7b24 | |||
| c0e8113f6f | |||
| e15dfa8eb6 | |||
| f12ded54f2 | |||
| b4951c9b39 | |||
| 9f0a28017d | |||
| d7c8a3a9dd | |||
| 10b109e25f | |||
| 2afe4bea67 | |||
| 12ae5ed09e | |||
| 0bdc3b4f21 | |||
| eaabb888d4 | |||
| b53fe14c22 | |||
| 45683703ea | |||
| 03053a18ce | |||
| 2a6d9e8927 | |||
| fa81061db0 | |||
| 39b3c8c80f | |||
| 3139151935 | |||
| 672a8f5dbd | |||
| 8672ff1c55 | |||
| abfa894566 | |||
| 5b52fed268 | |||
| 94a638b436 | |||
| 1b8c3ca22a | |||
| 71b32ae117 | |||
| 939459b635 | |||
| 562b518394 | |||
| d62503d031 | |||
| 4e8a801b35 | |||
| e1b711b3b3 | |||
| 6c69e8c043 | |||
| 7fa2d25eea | |||
| dae514a2c9 | |||
| 068993a819 | |||
| 66ae4e5afc | |||
| 218d1131e8 | |||
| f4f6e9ad49 | |||
| 48ab7f876c | |||
| 1e53a6fa96 | |||
| 4cef160c87 | |||
| 68582c5ad1 | |||
| a9a2dafca5 | |||
| da3467c25f | |||
| 5fc42bf9c9 | |||
| 42bbd31df1 | |||
| c7cc38b7d8 | |||
| 4afc78efc6 | |||
| bc34083c9c | |||
| 08dd8414c1 | |||
| d54255505a | |||
| d4914c3546 | |||
| 772c8779fa | |||
| c92b2e9612 | |||
| 1af143b749 | |||
| 1370c10d1b | |||
| 1348d8a8cd | |||
| 495aaacb81 | |||
| 22e982c8fb | |||
| 6d614ef07c | |||
| 723f351a6d | |||
| c93872cc13 | |||
| 97dece15d7 | |||
| 657103c4cf | |||
| b9d3129fb3 | |||
| b311540b16 | |||
| 07ced1cf0e | |||
| a1d4ea7718 | |||
| f921a91fc8 | |||
| 4dade34603 | |||
| d76249d90b | |||
| d118fc569a | |||
| 2dc39a5cbd | |||
| c89fc35e94 | |||
| 614d209824 | |||
| f1a7b31209 | |||
| 6a8b3fee38 | |||
| 4551521b2c | |||
| 97dfcffa76 | |||
| 6d3feb4531 | |||
| c44d25d14f | |||
| 25dd00cd0b | |||
| 01c10aa944 | |||
| 4908d1ebdd | |||
| 7bc146e6fd | |||
| 59ab51acec | |||
| cf23954297 | |||
| d1cc6534cd | |||
| 1b454c7ded | |||
| 150d4e343d | |||
| 28ada801a0 | |||
| a7676bff6e | |||
| 4369298e96 | |||
| a58c29582e | |||
| 3ad20a2d2d | |||
| b86dfa7af7 | |||
| 980890ee8a | |||
| 0a63690b39 | |||
| 0076a4df62 | |||
| 4c065a7e99 | |||
| 04036aba9c | |||
| b08aa857b3 | |||
| 2d26278a65 | |||
| 27a3c5449a | |||
| 9c63212824 | |||
| 65d41d4248 | |||
| b04231dd95 | |||
| 37633c1d2a | |||
| 5877017d30 | |||
| 132fdf14b8 | |||
| e7d3001dd1 | |||
| f163181204 | |||
| 2c5c8192f8 | |||
| 162c87b1b8 | |||
| 77734ea967 | |||
| 912fc2890b | |||
| 14059583cd | |||
| f3bffb9810 | |||
| 540730c0be | |||
| 40f0e74b8c | |||
| 5ca0cd60d0 | |||
| 96f0a08725 | |||
| d85893954d | |||
| d26033eb23 | |||
| c7a70be824 | |||
| 08c490d9ac | |||
| 8ee7ee7120 | |||
| d471a59041 | |||
| f25818e923 | |||
| 3d531be711 | |||
| 56d365a4e7 | |||
| 308300c1fc | |||
| 927d6814f2 | |||
| 7c99f52187 | |||
| 4ab9edeb53 | |||
| c5b3d7a736 | |||
| d7172e168e | |||
| d99ffe604f | |||
| b77c79294c | |||
| 8e722274f0 | |||
| ebe7fc23a5 | |||
| 4d40f564ef | |||
| fd62959bf4 | |||
| 8b8cada33e | |||
| aaa24894b6 | |||
| 2af19c496e | |||
| fea34b32ea | |||
| b891c0feab | |||
| 9f039c323e |
@@ -1,4 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [gingerBill]
|
||||
patreon: gingerbill
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build_unix:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (macOS) Download LLVM and setup PATH
|
||||
if: startsWith(matrix.os, 'macOS')
|
||||
run: |
|
||||
brew install llvm
|
||||
echo ::add-path::/usr/local/opt/llvm/bin
|
||||
echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include
|
||||
- name: (Linux) Download LLVM
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
run: |
|
||||
sudo apt-get install llvm
|
||||
- name: build odin
|
||||
run: make release
|
||||
- name: Odin run
|
||||
run: ./odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
run: ./odin check examples/demo/demo.odin -vet
|
||||
build_windows:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Install cURL
|
||||
run: choco install curl
|
||||
- name: Download and unpack LLVM bins
|
||||
shell: cmd
|
||||
run: |
|
||||
cd bin
|
||||
curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip
|
||||
7z x llvm-binaries.zip > nul
|
||||
- name: build Odin
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
./build_ci.bat
|
||||
- name: Odin run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin run examples/demo/demo.odin
|
||||
- name: Odin check
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
odin check examples/demo/demo.odin -vet
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ bld/
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
demo
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
@@ -271,3 +272,5 @@ shared/
|
||||
* .ll
|
||||
*.bc
|
||||
*.ll
|
||||
|
||||
*.sublime-workspace
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
language: cpp
|
||||
git:
|
||||
depth: false
|
||||
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
compiler:
|
||||
- clang
|
||||
|
||||
addons:
|
||||
homebrew:
|
||||
packages:
|
||||
- llvm
|
||||
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="/usr/local/opt/llvm/bin:$PATH" ; fi
|
||||
- make release
|
||||
- ./odin run examples/demo/demo.odin
|
||||
- ./odin check examples/demo/demo.odin -vet
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
@@ -1,4 +1,4 @@
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings
|
||||
DISABLED_WARNINGS=-Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined
|
||||
LDFLAGS=-pthread -ldl -lm -lstdc++
|
||||
CFLAGS=-std=c++11
|
||||
CC=clang
|
||||
|
||||
@@ -10,15 +10,12 @@
|
||||
<a href="https://github.com/odin-lang/odin/releases/latest">
|
||||
<img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg">
|
||||
</a>
|
||||
<a href="https://github.com/odin-lang/odin/blob/master/LICENSE">
|
||||
<img src="https://img.shields.io/github/license/odin-lang/odin.svg">
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://ci.appveyor.com/project/ThisDrunkDane/odin-vf0ap">
|
||||
<img src="https://ci.appveyor.com/api/projects/status/qss6l921c0eu85u6/branch/master?svg=true">
|
||||
<a href="https://discord.gg/hnwN2Rj">
|
||||
<img src="https://img.shields.io/discord/568138951836172421?logo=discord">
|
||||
</a>
|
||||
<a href="https://travis-ci.org/odin-lang/Odin">
|
||||
<img src="https://travis-ci.org/odin-lang/Odin.svg?branch=master">
|
||||
<a href="https://github.com/odin-lang/odin/actions">
|
||||
<img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg?branch=master&event=push">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
@@ -71,9 +68,9 @@ Instructions for downloading and install the Odin compiler and libraries.
|
||||
|
||||
An overview of the Odin programming language.
|
||||
|
||||
#### [Frequently Asked Questsions (FAQ)](https://odin-lang.org/docs/faq)
|
||||
#### [Frequently Asked Questions (FAQ)](https://odin-lang.org/docs/faq)
|
||||
|
||||
Answers to common questsions about Odin.
|
||||
Answers to common questions about Odin.
|
||||
|
||||
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
|
||||
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
image:
|
||||
- Visual Studio 2017
|
||||
shallow_clone: true
|
||||
|
||||
platform: x64
|
||||
|
||||
install:
|
||||
- cd bin
|
||||
- appveyor DownloadFile https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip
|
||||
- 7z x llvm-binaries.zip > nul
|
||||
- cd ..
|
||||
|
||||
build_script:
|
||||
- call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat"
|
||||
- ./build_ci.bat
|
||||
|
||||
test_script:
|
||||
- odin run examples/demo/demo.odin
|
||||
- odin check examples/demo/demo.odin -vet
|
||||
@@ -4,8 +4,15 @@
|
||||
set exe_name=odin.exe
|
||||
|
||||
:: Debug = 0, Release = 1
|
||||
set release_mode=0
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
if "%1" == "1" (
|
||||
set release_mode=1
|
||||
) else if "%1" == "release" (
|
||||
set release_mode=1
|
||||
) else (
|
||||
set release_mode=0
|
||||
)
|
||||
|
||||
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
@@ -39,7 +46,6 @@ set linker_settings=%libs% %linker_flags%
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
|
||||
cl %compiler_settings% "src\main.cpp" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run examples/demo/demo.odin
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
release_mode=$1
|
||||
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings"
|
||||
warnings_to_disable="-std=c++11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// This is purely for documentation
|
||||
package builtin
|
||||
|
||||
nil :: nil;
|
||||
false :: 0!==0;
|
||||
true :: 0==0;
|
||||
|
||||
ODIN_OS :: ODIN_OS;
|
||||
ODIN_ARCH :: ODIN_ARCH;
|
||||
ODIN_ENDIAN :: ODIN_ENDIAN;
|
||||
ODIN_VENDOR :: ODIN_VENDOR;
|
||||
ODIN_VERSION :: ODIN_VERSION;
|
||||
ODIN_ROOT :: ODIN_ROOT;
|
||||
ODIN_DEBUG :: ODIN_DEBUG;
|
||||
|
||||
byte :: u8; // alias
|
||||
|
||||
bool :: bool;
|
||||
b8 :: b8;
|
||||
b16 :: b16;
|
||||
b32 :: b32;
|
||||
b64 :: b64;
|
||||
|
||||
i8 :: i8;
|
||||
u8 :: u8;
|
||||
i16 :: i16;
|
||||
u16 :: u16;
|
||||
i32 :: i32;
|
||||
u32 :: u32;
|
||||
i64 :: i64;
|
||||
u64 :: u64;
|
||||
|
||||
i128 :: i128;
|
||||
u128 :: u128;
|
||||
|
||||
rune :: rune;
|
||||
|
||||
f16 :: f16;
|
||||
f32 :: f32;
|
||||
f64 :: f64;
|
||||
|
||||
complex32 :: complex32;
|
||||
complex64 :: complex64;
|
||||
complex128 :: complex128;
|
||||
|
||||
quaternion128 :: quaternion128;
|
||||
quaternion256 :: quaternion256;
|
||||
|
||||
int :: int;
|
||||
uint :: uint;
|
||||
uintptr :: uintptr;
|
||||
|
||||
rawptr :: rawptr;
|
||||
string :: string;
|
||||
cstring :: cstring;
|
||||
any :: any;
|
||||
|
||||
typeid :: typeid;
|
||||
|
||||
// Endian Specific Types
|
||||
i16le :: i16le;
|
||||
u16le :: u16le;
|
||||
i32le :: i32le;
|
||||
u32le :: u32le;
|
||||
i64le :: i64le;
|
||||
u64le :: u64le;
|
||||
i128le :: i128le;
|
||||
u128le :: u128le;
|
||||
|
||||
i16be :: i16be;
|
||||
u16be :: u16be;
|
||||
i32be :: i32be;
|
||||
u32be :: u32be;
|
||||
i64be :: i64be;
|
||||
u64be :: u64be;
|
||||
i128be :: i128be;
|
||||
u128be :: u128be;
|
||||
|
||||
// Procedures
|
||||
len :: proc(array: Array_Type) -> int ---
|
||||
cap :: proc(array: Array_Type) -> int ---
|
||||
|
||||
size_of :: proc($T: typeid) -> int ---
|
||||
align_of :: proc($T: typeid) -> int ---
|
||||
offset_of :: proc($T: typeid) -> uintptr ---
|
||||
type_of :: proc(x: expr) -> type ---
|
||||
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
|
||||
typeid_of :: proc($T: typeid) -> typeid ---
|
||||
|
||||
swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T ---
|
||||
|
||||
complex :: proc(real, imag: Float) -> Complex_Type ---
|
||||
quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type ---
|
||||
real :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
imag :: proc(value: Complex_Or_Quaternion) -> Float ---
|
||||
jmag :: proc(value: Quaternion) -> Float ---
|
||||
kmag :: proc(value: Quaternion) -> Float ---
|
||||
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
|
||||
|
||||
expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
|
||||
|
||||
min :: proc(values: ..T) -> T ---
|
||||
max :: proc(values: ..T) -> T ---
|
||||
abs :: proc(value: T) -> T ---
|
||||
clamp :: proc(value, minimum, maximum: T) -> T ---
|
||||
@@ -31,3 +31,5 @@ ssize_t :: b.int;
|
||||
ptrdiff_t :: b.int;
|
||||
uintptr_t :: b.uintptr;
|
||||
intptr_t :: b.int;
|
||||
|
||||
wchar_t :: (ODIN_OS == "windows") ? b.u16 : b.u32;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// +build linux, darwin
|
||||
package dynlib
|
||||
|
||||
import "core:os"
|
||||
|
||||
load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
|
||||
flags := os.RTLD_NOW;
|
||||
if global_symbols do flags |= os.RTLD_GLOBAL;
|
||||
lib := os.dlopen(path, flags);
|
||||
return Library(lib), lib != nil;
|
||||
}
|
||||
|
||||
unload_library :: proc(library: Library) {
|
||||
os.dlclose(rawptr(library));
|
||||
}
|
||||
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
ptr = os.dlsym(rawptr(library), symbol);
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// +build windows
|
||||
package dynlib
|
||||
|
||||
import "core:sys/win32"
|
||||
@@ -19,6 +20,6 @@ unload_library :: proc(library: Library) -> bool {
|
||||
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
|
||||
c_str := strings.clone_to_cstring(symbol, context.temp_allocator);
|
||||
ptr = win32.get_proc_address(cast(win32.Hmodule)library, c_str);
|
||||
found == ptr != nil;
|
||||
found = ptr != nil;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
package base32
|
||||
|
||||
// @note(zh): Encoding utility for Base32
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base32 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [32]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', '2', '3', '4', '5', '6', '7'
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [?]u8 {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 26, 27, 28, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string {
|
||||
out_length := (len(data) + 4) / 5 * 8;
|
||||
out := make([]byte, out_length);
|
||||
_encode(out, data);
|
||||
return string(out);
|
||||
}
|
||||
|
||||
@private
|
||||
_encode :: inline proc "contextless"(out, data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) {
|
||||
out := out;
|
||||
data := data;
|
||||
|
||||
for len(data) > 0 {
|
||||
carry: byte;
|
||||
switch len(data) {
|
||||
case:
|
||||
out[7] = ENC_TABLE[data[4] & 0x1f];
|
||||
carry = data[4] >> 5;
|
||||
fallthrough;
|
||||
case 4:
|
||||
out[6] = ENC_TABLE[carry | (data[3] << 3) & 0x1f];
|
||||
out[5] = ENC_TABLE[(data[3] >> 2) & 0x1f];
|
||||
carry = data[3] >> 7;
|
||||
fallthrough;
|
||||
case 3:
|
||||
out[4] = ENC_TABLE[carry | (data[2] << 1) & 0x1f];
|
||||
carry = (data[2] >> 4) & 0x1f;
|
||||
fallthrough;
|
||||
case 2:
|
||||
out[3] = ENC_TABLE[carry | (data[1] << 4) & 0x1f];
|
||||
out[2] = ENC_TABLE[(data[1] >> 1) & 0x1f];
|
||||
carry = (data[1] >> 6) & 0x1f;
|
||||
fallthrough;
|
||||
case 1:
|
||||
out[1] = ENC_TABLE[carry | (data[0] << 2) & 0x1f];
|
||||
out[0] = ENC_TABLE[data[0] >> 3];
|
||||
}
|
||||
|
||||
if len(data) < 5 {
|
||||
out[7] = byte(PADDING);
|
||||
if len(data) < 4 {
|
||||
out[6] = byte(PADDING);
|
||||
out[5] = byte(PADDING);
|
||||
if len(data) < 3 {
|
||||
out[4] = byte(PADDING);
|
||||
if len(data) < 2 {
|
||||
out[3] = byte(PADDING);
|
||||
out[2] = byte(PADDING);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
data = data[5:];
|
||||
out = out[8:];
|
||||
}
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
if len(data) == 0 do return []byte{};
|
||||
|
||||
outi := 0;
|
||||
olen := len(data);
|
||||
data := data;
|
||||
|
||||
out := make([]byte, len(data) / 8 * 5, allocator);
|
||||
end := false;
|
||||
for len(data) > 0 && !end {
|
||||
dbuf : [8]byte;
|
||||
dlen := 8;
|
||||
|
||||
for j := 0; j < 8; {
|
||||
if len(data) == 0 {
|
||||
dlen, end = j, true;
|
||||
break;
|
||||
}
|
||||
input := data[0];
|
||||
data = data[1:];
|
||||
if input == byte(PADDING) && j >= 2 && len(data) < 8 {
|
||||
assert(!(len(data) + j < 8 - 1), "Corrupted input");
|
||||
for k := 0; k < 8-1-j; k +=1 do assert(len(data) < k || data[k] == byte(PADDING), "Corrupted input");
|
||||
dlen, end = j, true;
|
||||
assert(dlen != 1 && dlen != 3 && dlen != 6, "Corrupted input");
|
||||
break;
|
||||
}
|
||||
dbuf[j] = DEC_TABLE[input];
|
||||
assert(dbuf[j] != 0xff, "Corrupted input");
|
||||
j += 1;
|
||||
}
|
||||
|
||||
switch dlen {
|
||||
case 8:
|
||||
out[outi + 4] = dbuf[6] << 5 | dbuf[7];
|
||||
fallthrough;
|
||||
case 7:
|
||||
out[outi + 3] = dbuf[4] << 7 | dbuf[5] << 2 | dbuf[6] >> 3;
|
||||
fallthrough;
|
||||
case 5:
|
||||
out[outi + 2] = dbuf[3] << 4 | dbuf[4] >> 1;
|
||||
fallthrough;
|
||||
case 4:
|
||||
out[outi + 1] = dbuf[1] << 6 | dbuf[2] << 1 | dbuf[3] >> 4;
|
||||
fallthrough;
|
||||
case 2:
|
||||
out[outi + 0] = dbuf[0] << 3 | dbuf[1] >> 2;
|
||||
}
|
||||
outi += 5;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package base64
|
||||
|
||||
// @note(zh): Encoding utility for Base64
|
||||
// A secondary param can be used to supply a custom alphabet to
|
||||
// @link(encode) and a matching decoding table to @link(decode).
|
||||
// If none is supplied it just uses the standard Base64 alphabet.
|
||||
// Incase your specific version does not use padding, you may
|
||||
// truncate it from the encoded output.
|
||||
|
||||
ENC_TABLE := [64]byte {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
|
||||
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z', '0', '1', '2', '3',
|
||||
'4', '5', '6', '7', '8', '9', '+', '/'
|
||||
};
|
||||
|
||||
PADDING :: '=';
|
||||
|
||||
DEC_TABLE := [128]int {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, 62, -1, -1, -1, 63,
|
||||
52, 53, 54, 55, 56, 57, 58, 59,
|
||||
60, 61, -1, -1, -1, -1, -1, -1,
|
||||
-1, 0, 1, 2, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
15, 16, 17, 18, 19, 20, 21, 22,
|
||||
23, 24, 25, -1, -1, -1, -1, -1,
|
||||
-1, 26, 27, 28, 29, 30, 31, 32,
|
||||
33, 34, 35, 36, 37, 38, 39, 40,
|
||||
41, 42, 43, 44, 45, 46, 47, 48,
|
||||
49, 50, 51, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check {
|
||||
length := len(data);
|
||||
if length == 0 do return "";
|
||||
|
||||
out_length := ((4 * length / 3) + 3) &~ 3;
|
||||
out := make([]byte, out_length, allocator);
|
||||
|
||||
c0, c1, c2, block: int;
|
||||
|
||||
for i, d := 0, 0; i < length; i, d = i + 3, d + 4 {
|
||||
c0, c1, c2 = int(data[i]), 0, 0;
|
||||
|
||||
if i + 1 < length do c1 = int(data[i + 1]);
|
||||
if i + 2 < length do c2 = int(data[i + 2]);
|
||||
|
||||
block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0);
|
||||
|
||||
out[d] = ENC_TBL[block >> 18 & 63];
|
||||
out[d + 1] = ENC_TBL[block >> 12 & 63];
|
||||
out[d + 2] = c1 == 0 ? PADDING : ENC_TBL[block >> 6 & 63];
|
||||
out[d + 3] = c2 == 0 ? PADDING : ENC_TBL[block & 63];
|
||||
}
|
||||
return string(out);
|
||||
}
|
||||
|
||||
decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{
|
||||
length := len(data);
|
||||
if length == 0 do return []byte{};
|
||||
|
||||
pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0;
|
||||
out_length := ((length * 6) >> 3) - pad_count;
|
||||
out := make([]byte, out_length, allocator);
|
||||
|
||||
c0, c1, c2, c3: int;
|
||||
b0, b1, b2: int;
|
||||
|
||||
for i, j := 0, 0; i < length; i, j = i + 4, j + 3 {
|
||||
c0 = DEC_TBL[data[i]];
|
||||
c1 = DEC_TBL[data[i + 1]];
|
||||
c2 = DEC_TBL[data[i + 2]];
|
||||
c3 = DEC_TBL[data[i + 3]];
|
||||
|
||||
b0 = (c0 << 2) | (c1 >> 4);
|
||||
b1 = (c1 << 4) | (c2 >> 2);
|
||||
b2 = (c2 << 6) | c3;
|
||||
|
||||
out[j] = byte(b0);
|
||||
out[j + 1] = byte(b1);
|
||||
out[j + 2] = byte(b2);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
+123
-121
@@ -91,7 +91,7 @@ print :: proc(p: ^Parser, pretty := false) {
|
||||
}
|
||||
|
||||
create_from_string :: proc(src: string) -> (^Parser, bool) {
|
||||
return init(cast([]byte)src);
|
||||
return init(transmute([]byte)src);
|
||||
}
|
||||
|
||||
|
||||
@@ -106,11 +106,11 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
|
||||
p := new(Parser);
|
||||
for {
|
||||
tok := scan(t);
|
||||
if tok.kind == Kind.Illegal {
|
||||
if tok.kind == .Illegal {
|
||||
return p, false;
|
||||
}
|
||||
append(&p.tokens, tok);
|
||||
if tok.kind == Kind.EOF {
|
||||
if tok.kind == .EOF {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
|
||||
}
|
||||
|
||||
if len(p.tokens) == 0 {
|
||||
tok := Token{kind = Kind.EOF};
|
||||
tok := Token{kind = .EOF};
|
||||
tok.line, tok.column = 1, 1;
|
||||
append(&p.tokens, tok);
|
||||
return p, true;
|
||||
@@ -134,8 +134,8 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
|
||||
p.dict_stack = make([dynamic]^Dict, 0, 4);
|
||||
append(&p.dict_stack, &p.root);
|
||||
|
||||
for p.curr_token.kind != Kind.EOF &&
|
||||
p.curr_token.kind != Kind.Illegal &&
|
||||
for p.curr_token.kind != .EOF &&
|
||||
p.curr_token.kind != .Illegal &&
|
||||
p.curr_token_index < len(p.tokens) {
|
||||
if !parse_assignment(p) {
|
||||
break;
|
||||
@@ -147,7 +147,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
|
||||
|
||||
destroy :: proc(p: ^Parser) {
|
||||
destroy_value :: proc(value: Value) {
|
||||
switch v in value {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
for elem in v do destroy_value(elem);
|
||||
delete(v);
|
||||
@@ -168,9 +168,9 @@ destroy :: proc(p: ^Parser) {
|
||||
}
|
||||
|
||||
error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) {
|
||||
fmt.printf_err("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
|
||||
fmt.printf_err(msg, ..args);
|
||||
fmt.println_err();
|
||||
fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintln();
|
||||
|
||||
p.error_count += 1;
|
||||
}
|
||||
@@ -190,7 +190,7 @@ next_token :: proc(p: ^Parser) -> Token {
|
||||
return prev;
|
||||
}
|
||||
|
||||
unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
|
||||
hex_to_int :: proc(c: byte) -> int {
|
||||
switch c {
|
||||
case '0'..'9': return int(c-'0');
|
||||
@@ -201,18 +201,19 @@ unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool,
|
||||
}
|
||||
w: int;
|
||||
|
||||
if s[0] == quote && quote == '"' {
|
||||
if str[0] == quote && quote == '"' {
|
||||
return;
|
||||
} else if s[0] >= 0x80 {
|
||||
r, w = utf8.decode_rune_in_string(s);
|
||||
return r, true, s[w:], true;
|
||||
} else if s[0] != '\\' {
|
||||
return rune(s[0]), false, s[1:], true;
|
||||
} else if str[0] >= 0x80 {
|
||||
r, w = utf8.decode_rune_in_string(str);
|
||||
return r, true, str[w:], true;
|
||||
} else if str[0] != '\\' {
|
||||
return rune(str[0]), false, str[1:], true;
|
||||
}
|
||||
|
||||
if len(s) <= 1 {
|
||||
if len(str) <= 1 {
|
||||
return;
|
||||
}
|
||||
s := str;
|
||||
c := s[1];
|
||||
s = s[2:];
|
||||
|
||||
@@ -286,7 +287,7 @@ unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool,
|
||||
|
||||
|
||||
unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
|
||||
if t.kind != Kind.String {
|
||||
if t.kind != .String {
|
||||
return t.lit, true;
|
||||
}
|
||||
s := t.lit;
|
||||
@@ -367,8 +368,8 @@ expect_operator :: proc(p: ^Parser) -> Token {
|
||||
|
||||
fix_advance :: proc(p: ^Parser) {
|
||||
for {
|
||||
switch t := p.curr_token; t.kind {
|
||||
case Kind.EOF, Kind.Semicolon:
|
||||
#partial switch t := p.curr_token; t.kind {
|
||||
case .EOF, .Semicolon:
|
||||
return;
|
||||
}
|
||||
next_token(p);
|
||||
@@ -376,7 +377,7 @@ fix_advance :: proc(p: ^Parser) {
|
||||
}
|
||||
|
||||
copy_value :: proc(value: Value) -> Value {
|
||||
switch v in value {
|
||||
#partial switch v in value {
|
||||
case Array:
|
||||
a := make(Array, len(v));
|
||||
for elem, idx in v {
|
||||
@@ -406,79 +407,79 @@ lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
|
||||
|
||||
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
tok := p.curr_token;
|
||||
switch p.curr_token.kind {
|
||||
case Kind.Ident:
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Ident:
|
||||
next_token(p);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
return v, tok.pos;
|
||||
|
||||
case Kind.True:
|
||||
case .True:
|
||||
next_token(p);
|
||||
return true, tok.pos;
|
||||
case Kind.False:
|
||||
case .False:
|
||||
next_token(p);
|
||||
return false, tok.pos;
|
||||
|
||||
case Kind.Nil:
|
||||
case .Nil:
|
||||
next_token(p);
|
||||
return Nil_Value{}, tok.pos;
|
||||
|
||||
case Kind.Integer:
|
||||
case .Integer:
|
||||
next_token(p);
|
||||
return strconv.parse_i64(tok.lit), tok.pos;
|
||||
|
||||
case Kind.Float:
|
||||
case .Float:
|
||||
next_token(p);
|
||||
return strconv.parse_f64(tok.lit), tok.pos;
|
||||
|
||||
case Kind.String:
|
||||
case .String:
|
||||
next_token(p);
|
||||
str, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
return string(str), tok.pos;
|
||||
|
||||
case Kind.Open_Paren:
|
||||
expect_token(p, Kind.Open_Paren);
|
||||
case .Open_Paren:
|
||||
expect_token(p, .Open_Paren);
|
||||
expr, _ := parse_expr(p);
|
||||
expect_token(p, Kind.Close_Paren);
|
||||
expect_token(p, .Close_Paren);
|
||||
return expr, tok.pos;
|
||||
|
||||
case Kind.Open_Bracket:
|
||||
expect_token(p, Kind.Open_Bracket);
|
||||
case .Open_Bracket:
|
||||
expect_token(p, .Open_Bracket);
|
||||
elems := make([dynamic]Value, 0, 4);
|
||||
for p.curr_token.kind != Kind.Close_Bracket &&
|
||||
p.curr_token.kind != Kind.EOF {
|
||||
for p.curr_token.kind != .Close_Bracket &&
|
||||
p.curr_token.kind != .EOF {
|
||||
elem, _ := parse_expr(p);
|
||||
append(&elems, elem);
|
||||
|
||||
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
|
||||
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
|
||||
next_token(p);
|
||||
} else if !allow_token(p, Kind.Comma) {
|
||||
} else if !allow_token(p, .Comma) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
expect_token(p, Kind.Close_Bracket);
|
||||
expect_token(p, .Close_Bracket);
|
||||
return Array(elems[:]), tok.pos;
|
||||
|
||||
case Kind.Open_Brace:
|
||||
expect_token(p, Kind.Open_Brace);
|
||||
case .Open_Brace:
|
||||
expect_token(p, .Open_Brace);
|
||||
|
||||
dict := Dict{};
|
||||
append(&p.dict_stack, &dict);
|
||||
defer pop(&p.dict_stack);
|
||||
|
||||
for p.curr_token.kind != Kind.Close_Brace &&
|
||||
p.curr_token.kind != Kind.EOF {
|
||||
for p.curr_token.kind != .Close_Brace &&
|
||||
p.curr_token.kind != .EOF {
|
||||
name_tok := p.curr_token;
|
||||
if !allow_token(p, Kind.Ident) && !allow_token(p, Kind.String) {
|
||||
name_tok = expect_token(p, Kind.Ident);
|
||||
if !allow_token(p, .Ident) && !allow_token(p, .String) {
|
||||
name_tok = expect_token(p, .Ident);
|
||||
}
|
||||
|
||||
name, ok := unquote_string(p, name_tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
expect_token(p, Kind.Assign);
|
||||
expect_token(p, .Assign);
|
||||
elem, _ := parse_expr(p);
|
||||
|
||||
if _, ok2 := dict[name]; ok2 {
|
||||
@@ -487,13 +488,13 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
dict[name] = elem;
|
||||
}
|
||||
|
||||
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
|
||||
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
|
||||
next_token(p);
|
||||
} else if !allow_token(p, Kind.Comma) {
|
||||
} else if !allow_token(p, .Comma) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
expect_token(p, Kind.Close_Brace);
|
||||
expect_token(p, .Close_Brace);
|
||||
return dict, tok.pos;
|
||||
|
||||
}
|
||||
@@ -502,14 +503,14 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
|
||||
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
loop := true;
|
||||
for loop {
|
||||
switch p.curr_token.kind {
|
||||
case Kind.Period:
|
||||
for operand := operand; loop; {
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Period:
|
||||
next_token(p);
|
||||
tok := next_token(p);
|
||||
|
||||
switch tok.kind {
|
||||
case Kind.Ident:
|
||||
#partial switch tok.kind {
|
||||
case .Ident:
|
||||
d, ok := operand.(Dict);
|
||||
if !ok || d == nil {
|
||||
error(p, tok.pos, "Expected a dictionary");
|
||||
@@ -530,13 +531,13 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
operand = nil;
|
||||
}
|
||||
|
||||
case Kind.Open_Bracket:
|
||||
expect_token(p, Kind.Open_Bracket);
|
||||
case .Open_Bracket:
|
||||
expect_token(p, .Open_Bracket);
|
||||
index, index_pos := parse_expr(p);
|
||||
expect_token(p, Kind.Close_Bracket);
|
||||
expect_token(p, .Close_Bracket);
|
||||
|
||||
|
||||
switch a in operand {
|
||||
#partial switch a in operand {
|
||||
case Array:
|
||||
i, ok := index.(i64);
|
||||
if !ok {
|
||||
@@ -586,22 +587,22 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
|
||||
|
||||
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
op := p.curr_token;
|
||||
switch p.curr_token.kind {
|
||||
case Kind.At:
|
||||
#partial switch p.curr_token.kind {
|
||||
case .At:
|
||||
next_token(p);
|
||||
tok := expect_token(p, Kind.String);
|
||||
tok := expect_token(p, .String);
|
||||
v, ok := lookup_value(p, tok.lit);
|
||||
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
|
||||
return parse_atom_expr(p, v, tok.pos);
|
||||
|
||||
case Kind.Add, Kind.Sub:
|
||||
case .Add, .Sub:
|
||||
next_token(p);
|
||||
// TODO(bill): Calcuate values as you go!
|
||||
expr, pos := parse_unary_expr(p);
|
||||
|
||||
switch e in expr {
|
||||
case i64: if op.kind == Kind.Sub do return -e, pos;
|
||||
case f64: if op.kind == Kind.Sub do return -e, pos;
|
||||
#partial switch e in expr {
|
||||
case i64: if op.kind == .Sub do return -e, pos;
|
||||
case f64: if op.kind == .Sub do return -e, pos;
|
||||
case:
|
||||
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
|
||||
return nil, op.pos;
|
||||
@@ -609,7 +610,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
|
||||
return expr, op.pos;
|
||||
|
||||
case Kind.Not:
|
||||
case .Not:
|
||||
next_token(p);
|
||||
expr, _ := parse_unary_expr(p);
|
||||
if v, ok := expr.(bool); ok {
|
||||
@@ -624,7 +625,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
|
||||
|
||||
value_order :: proc(v: Value) -> int {
|
||||
switch _ in v {
|
||||
#partial switch _ in v {
|
||||
case bool, string:
|
||||
return 1;
|
||||
case i64:
|
||||
@@ -640,13 +641,13 @@ match_values :: proc(left, right: ^Value) -> bool {
|
||||
return match_values(right, left);
|
||||
}
|
||||
|
||||
switch x in left^ {
|
||||
#partial switch x in left^ {
|
||||
case:
|
||||
right^ = left^;
|
||||
case bool, string:
|
||||
return true;
|
||||
case i64:
|
||||
switch y in right^ {
|
||||
#partial switch y in right^ {
|
||||
case i64:
|
||||
return true;
|
||||
case f64:
|
||||
@@ -655,7 +656,7 @@ match_values :: proc(left, right: ^Value) -> bool {
|
||||
}
|
||||
|
||||
case f64:
|
||||
switch y in right {
|
||||
#partial switch y in right {
|
||||
case f64:
|
||||
return true;
|
||||
}
|
||||
@@ -664,78 +665,79 @@ match_values :: proc(left, right: ^Value) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, x, y: Value) -> (Value, bool) {
|
||||
calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, bool) {
|
||||
// TODO(bill): Calculate value as you go!
|
||||
x, y := a, b;
|
||||
match_values(&x, &y);
|
||||
|
||||
|
||||
switch a in x {
|
||||
#partial switch a in x {
|
||||
case: return x, true;
|
||||
|
||||
case bool:
|
||||
b, ok := y.(bool);
|
||||
if !ok do return nil, false;
|
||||
switch op {
|
||||
case Kind.Eq: return a == b, true;
|
||||
case Kind.NotEq: return a != b, true;
|
||||
case Kind.And: return a && b, true;
|
||||
case Kind.Or: return a || b, true;
|
||||
#partial switch op {
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .And: return a && b, true;
|
||||
case .Or: return a || b, true;
|
||||
}
|
||||
|
||||
case i64:
|
||||
b, ok := y.(i64);
|
||||
if !ok do return nil, false;
|
||||
switch op {
|
||||
case Kind.Add: return a + b, true;
|
||||
case Kind.Sub: return a - b, true;
|
||||
case Kind.Mul: return a * b, true;
|
||||
case Kind.Quo: return a / b, true;
|
||||
case Kind.Rem: return a % b, true;
|
||||
case Kind.Eq: return a == b, true;
|
||||
case Kind.NotEq: return a != b, true;
|
||||
case Kind.Lt: return a < b, true;
|
||||
case Kind.Gt: return a > b, true;
|
||||
case Kind.LtEq: return a <= b, true;
|
||||
case Kind.GtEq: return a >= b, true;
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
case .Mul: return a * b, true;
|
||||
case .Quo: return a / b, true;
|
||||
case .Rem: return a % b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
|
||||
case f64:
|
||||
b, ok := y.(f64);
|
||||
if !ok do return nil, false;
|
||||
|
||||
switch op {
|
||||
case Kind.Add: return a + b, true;
|
||||
case Kind.Sub: return a - b, true;
|
||||
case Kind.Mul: return a * b, true;
|
||||
case Kind.Quo: return a / b, true;
|
||||
case Kind.Eq: return a == b, true;
|
||||
case Kind.NotEq: return a != b, true;
|
||||
case Kind.Lt: return a < b, true;
|
||||
case Kind.Gt: return a > b, true;
|
||||
case Kind.LtEq: return a <= b, true;
|
||||
case Kind.GtEq: return a >= b, true;
|
||||
#partial switch op {
|
||||
case .Add: return a + b, true;
|
||||
case .Sub: return a - b, true;
|
||||
case .Mul: return a * b, true;
|
||||
case .Quo: return a / b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
|
||||
case string:
|
||||
b, ok := y.(string);
|
||||
if !ok do return nil, false;
|
||||
|
||||
switch op {
|
||||
case Kind.Add:
|
||||
#partial switch op {
|
||||
case .Add:
|
||||
n := len(a) + len(b);
|
||||
data := make([]byte, n);
|
||||
copy(data[:], cast([]byte)a);
|
||||
copy(data[len(a):], cast([]byte)b);
|
||||
copy(data[:], a);
|
||||
copy(data[len(a):], b);
|
||||
s := string(data);
|
||||
append(&p.allocated_strings, s);
|
||||
return s, true;
|
||||
|
||||
case Kind.Eq: return a == b, true;
|
||||
case Kind.NotEq: return a != b, true;
|
||||
case Kind.Lt: return a < b, true;
|
||||
case Kind.Gt: return a > b, true;
|
||||
case Kind.LtEq: return a <= b, true;
|
||||
case Kind.GtEq: return a >= b, true;
|
||||
case .Eq: return a == b, true;
|
||||
case .NotEq: return a != b, true;
|
||||
case .Lt: return a < b, true;
|
||||
case .Gt: return a > b, true;
|
||||
case .LtEq: return a <= b, true;
|
||||
case .GtEq: return a >= b, true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -753,10 +755,10 @@ parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
|
||||
}
|
||||
expect_operator(p);
|
||||
|
||||
if op.kind == Kind.Question {
|
||||
if op.kind == .Question {
|
||||
cond := expr;
|
||||
x, _ := parse_expr(p);
|
||||
expect_token(p, Kind.Colon);
|
||||
expect_token(p, .Colon);
|
||||
y, _ := parse_expr(p);
|
||||
|
||||
if t, ok := cond.(bool); ok {
|
||||
@@ -789,13 +791,13 @@ parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
|
||||
expect_semicolon :: proc(p: ^Parser) {
|
||||
kind := p.curr_token.kind;
|
||||
|
||||
switch kind {
|
||||
case Kind.Comma:
|
||||
#partial switch kind {
|
||||
case .Comma:
|
||||
error(p, p.curr_token.pos, "Expected ';', got ','");
|
||||
next_token(p);
|
||||
case Kind.Semicolon:
|
||||
case .Semicolon:
|
||||
next_token(p);
|
||||
case Kind.EOF:
|
||||
case .EOF:
|
||||
// okay
|
||||
case:
|
||||
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
|
||||
@@ -809,17 +811,17 @@ parse_assignment :: proc(p: ^Parser) -> bool {
|
||||
return p.dict_stack[len(p.dict_stack)-1];
|
||||
}
|
||||
|
||||
if p.curr_token.kind == Kind.Semicolon {
|
||||
if p.curr_token.kind == .Semicolon {
|
||||
next_token(p);
|
||||
return true;
|
||||
}
|
||||
if p.curr_token.kind == Kind.EOF {
|
||||
if p.curr_token.kind == .EOF {
|
||||
return false;
|
||||
}
|
||||
|
||||
tok := p.curr_token;
|
||||
if allow_token(p, Kind.Ident) || allow_token(p, Kind.String) {
|
||||
expect_token(p, Kind.Assign);
|
||||
if allow_token(p, .Ident) || allow_token(p, .String) {
|
||||
expect_token(p, .Assign);
|
||||
name, ok := unquote_string(p, tok);
|
||||
if !ok do error(p, tok.pos, "Unable to unquote string");
|
||||
expr, _ := parse_expr(p);
|
||||
|
||||
@@ -137,7 +137,7 @@ kind_to_string := [len(Kind)]string{
|
||||
};
|
||||
|
||||
precedence :: proc(op: Kind) -> int {
|
||||
switch op {
|
||||
#partial switch op {
|
||||
case Question:
|
||||
return 1;
|
||||
case Or:
|
||||
@@ -183,9 +183,9 @@ tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") {
|
||||
}
|
||||
|
||||
token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) {
|
||||
fmt.printf_err("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
|
||||
fmt.printf_err(msg, ..args);
|
||||
fmt.println_err();
|
||||
fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintln();
|
||||
t.error_count += 1;
|
||||
}
|
||||
|
||||
@@ -286,9 +286,10 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (Kind, string) {
|
||||
scan_exponent :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
|
||||
kind = tok;
|
||||
if t.curr_rune == 'e' || t.curr_rune == 'E' {
|
||||
tok = Float;
|
||||
kind = Float;
|
||||
advance_to_next_rune(t);
|
||||
if t.curr_rune == '-' || t.curr_rune == '+' {
|
||||
advance_to_next_rune(t);
|
||||
@@ -299,16 +300,18 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
|
||||
token_error(t, "Illegal floating point exponent");
|
||||
}
|
||||
}
|
||||
return tok, string(t.src[offset : t.offset]);
|
||||
text = string(t.src[offset : t.offset]);
|
||||
return;
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (Kind, string) {
|
||||
scan_fraction :: proc(t: ^Tokenizer, tok: Kind, offset: int) -> (kind: Kind, text: string) {
|
||||
kind = tok;
|
||||
if t.curr_rune == '.' {
|
||||
tok = Float;
|
||||
kind = Float;
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
return scan_exponent(t, tok, offset);
|
||||
return scan_exponent(t, kind, offset);
|
||||
}
|
||||
|
||||
offset := t.offset;
|
||||
|
||||
@@ -5,7 +5,7 @@ import "core:math/bits"
|
||||
import "core:runtime"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:types"
|
||||
import "core:reflect"
|
||||
|
||||
Marshal_Error :: enum {
|
||||
None,
|
||||
@@ -40,7 +40,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
ti := type_info_base(type_info_of(v.id));
|
||||
a := any{v.data, ti.id};
|
||||
|
||||
switch info in ti.variant {
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Named:
|
||||
panic("Unreachable");
|
||||
|
||||
@@ -95,17 +95,17 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
buf: [386]byte;
|
||||
|
||||
str := strconv.append_float(buf[1:], val, 'f', 2*ti.size, 8*ti.size);
|
||||
str = string(buf[:len(str)+1]);
|
||||
if str[1] == '+' || str[1] == '-' {
|
||||
str = str[1:];
|
||||
s := buf[:len(str)+1];
|
||||
if s[1] == '+' || s[1] == '-' {
|
||||
s = s[1:];
|
||||
} else {
|
||||
str[0] = '+';
|
||||
s[0] = '+';
|
||||
}
|
||||
if str[0] == '+' {
|
||||
str = str[1:];
|
||||
if s[0] == '+' {
|
||||
s = s[1:];
|
||||
}
|
||||
|
||||
write_string(b, str);
|
||||
write_string(b, string(s));
|
||||
|
||||
case Type_Info_Complex:
|
||||
return Marshal_Error.Unsupported_Type;
|
||||
@@ -194,7 +194,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||
header := cast(^Map_Entry_Header)data;
|
||||
|
||||
if types.is_string(info.key) {
|
||||
if reflect.is_string(info.key) {
|
||||
marshal_arg(b, header.key.str);
|
||||
} else {
|
||||
marshal_arg(b, any{rawptr(&header.key.hash), info.key.id});
|
||||
@@ -281,14 +281,13 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
if ti == nil {
|
||||
return false;
|
||||
}
|
||||
ti = runtime.type_info_base(ti);
|
||||
switch info in ti.variant {
|
||||
t := runtime.type_info_base(ti);
|
||||
#partial switch info in t.variant {
|
||||
case runtime.Type_Info_Integer:
|
||||
using runtime.Type_Info_Endianness;
|
||||
switch info.endianness {
|
||||
case Platform: return false;
|
||||
case Little: return ODIN_ENDIAN != "little";
|
||||
case Big: return ODIN_ENDIAN != "big";
|
||||
case .Platform: return false;
|
||||
case .Little: return ODIN_ENDIAN != "little";
|
||||
case .Big: return ODIN_ENDIAN != "big";
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -70,7 +70,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
defer value.end = token_end_pos(p.prev_token);
|
||||
|
||||
token := p.curr_token;
|
||||
switch token.kind {
|
||||
#partial switch token.kind {
|
||||
case Kind.Null:
|
||||
value.value = Null{};
|
||||
advance_token(p);
|
||||
@@ -105,7 +105,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
switch token.kind {
|
||||
#partial switch token.kind {
|
||||
case Kind.Infinity:
|
||||
inf: u64 = 0x7ff0000000000000;
|
||||
if token.text[0] == '-' {
|
||||
@@ -174,7 +174,7 @@ parse_array :: proc(p: ^Parser) -> (value: Value, err: Error) {
|
||||
clone_string :: proc(s: string, allocator: mem.Allocator) -> string {
|
||||
n := len(s);
|
||||
b := make([]byte, n+1, allocator);
|
||||
copy(b, cast([]byte)s);
|
||||
copy(b, s);
|
||||
b[n] = 0;
|
||||
return string(b[:n]);
|
||||
}
|
||||
@@ -349,7 +349,7 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
|
||||
}
|
||||
|
||||
b := make([]byte, len(s) + 2*utf8.UTF_MAX, allocator);
|
||||
w := copy(b, cast([]byte)s[0:i]);
|
||||
w := copy(b, s[0:i]);
|
||||
loop: for i < len(s) {
|
||||
c := s[i];
|
||||
switch {
|
||||
|
||||
@@ -333,7 +333,8 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
|
||||
|
||||
|
||||
|
||||
is_valid_number :: proc(s: string, spec: Specification) -> bool {
|
||||
is_valid_number :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str;
|
||||
if s == "" {
|
||||
return false;
|
||||
}
|
||||
@@ -395,7 +396,8 @@ is_valid_number :: proc(s: string, spec: Specification) -> bool {
|
||||
return s == "";
|
||||
}
|
||||
|
||||
is_valid_string_literal :: proc(s: string, spec: Specification) -> bool {
|
||||
is_valid_string_literal :: proc(str: string, spec: Specification) -> bool {
|
||||
s := str;
|
||||
if len(s) < 2 {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ Error :: enum {
|
||||
|
||||
|
||||
destroy_value :: proc(value: Value) {
|
||||
switch v in value.value {
|
||||
#partial switch v in value.value {
|
||||
case Object:
|
||||
for key, elem in v {
|
||||
delete(key);
|
||||
|
||||
@@ -91,28 +91,27 @@ validate_array :: proc(p: ^Parser) -> bool {
|
||||
validate_value :: proc(p: ^Parser) -> bool {
|
||||
token := p.curr_token;
|
||||
|
||||
using Kind;
|
||||
switch token.kind {
|
||||
case Null, False, True:
|
||||
#partial switch token.kind {
|
||||
case .Null, .False, .True:
|
||||
advance_token(p);
|
||||
return true;
|
||||
case Integer, Float:
|
||||
case .Integer, .Float:
|
||||
advance_token(p);
|
||||
return true;
|
||||
case String:
|
||||
case .String:
|
||||
advance_token(p);
|
||||
return is_valid_string_literal(token.text, p.spec);
|
||||
|
||||
case Open_Brace:
|
||||
case .Open_Brace:
|
||||
return validate_object(p);
|
||||
|
||||
case Open_Bracket:
|
||||
case .Open_Bracket:
|
||||
return validate_array(p);
|
||||
|
||||
case:
|
||||
if p.spec == Specification.JSON5 {
|
||||
switch token.kind {
|
||||
case Infinity, NaN:
|
||||
#partial switch token.kind {
|
||||
case .Infinity, .NaN:
|
||||
advance_token(p);
|
||||
return true;
|
||||
}
|
||||
|
||||
+354
-310
@@ -5,9 +5,9 @@ import "core:os"
|
||||
import "core:mem"
|
||||
import "core:math/bits"
|
||||
import "core:unicode/utf8"
|
||||
import "core:types"
|
||||
import "core:strconv"
|
||||
import "core:strings"
|
||||
import "core:reflect"
|
||||
|
||||
|
||||
@private
|
||||
@@ -59,12 +59,18 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
|
||||
|
||||
// print* procedures return the number of bytes written
|
||||
print :: proc(args: ..any) -> int { return fprint(context.stdout, ..args); }
|
||||
print_err :: proc(args: ..any) -> int { return fprint(context.stderr, ..args); }
|
||||
println :: proc(args: ..any) -> int { return fprintln(context.stdout, ..args); }
|
||||
println_err :: proc(args: ..any) -> int { return fprintln(context.stderr, ..args); }
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); }
|
||||
printf_err :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); }
|
||||
print :: proc(args: ..any) -> int { return fprint(context.stdout, ..args); }
|
||||
println :: proc(args: ..any) -> int { return fprintln(context.stdout, ..args); }
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); }
|
||||
|
||||
eprint :: proc(args: ..any) -> int { return fprint(context.stderr, ..args); }
|
||||
eprintln :: proc(args: ..any) -> int { return fprintln(context.stderr, ..args); }
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); }
|
||||
|
||||
|
||||
@(deprecated="prefer eprint") print_err :: proc(args: ..any) -> int { return eprint(..args); }
|
||||
@(deprecated="prefer eprintf") printf_err :: proc(fmt: string, args: ..any) -> int { return eprintf(fmt, ..args); }
|
||||
@(deprecated="prefer eprintln") println_err :: proc(args: ..any) -> int { return eprintln(..args); }
|
||||
|
||||
|
||||
// aprint* procedures return a string that was allocated with the current context
|
||||
@@ -143,7 +149,7 @@ panicf :: proc "contextless" (fmt: string, args: ..any, loc := #caller_location)
|
||||
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) {
|
||||
data: [DEFAULT_BUFFER_SIZE]byte;
|
||||
buf := strings.builder_from_slice(data[:]);
|
||||
write_type(&buf, info);
|
||||
reflect.write_type(&buf, info);
|
||||
os.write_string(fd, strings.to_string(buf));
|
||||
}
|
||||
|
||||
@@ -156,7 +162,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any) -> string {
|
||||
fi.buf = buf;
|
||||
|
||||
for arg, i in args {
|
||||
is_string := arg != nil && types.is_string(type_info_of(arg.id));
|
||||
is_string := arg != nil && reflect.is_string(type_info_of(arg.id));
|
||||
if i > 0 && !is_string && !prev_string {
|
||||
strings.write_byte(buf, ' ');
|
||||
}
|
||||
@@ -399,7 +405,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
|
||||
strings.write_rune(buf, verb);
|
||||
strings.write_byte(buf, '(');
|
||||
if arg.id != nil {
|
||||
write_typeid(buf, arg.id);
|
||||
reflect.write_typeid(buf, arg.id);
|
||||
strings.write_byte(buf, '=');
|
||||
fmt_value(fi, arg, 'v');
|
||||
} else {
|
||||
@@ -648,32 +654,32 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
||||
buf: [386]byte;
|
||||
|
||||
str := strconv.append_float(buf[1:], v, 'f', prec, bit_size);
|
||||
str = string(buf[:len(str)+1]);
|
||||
if str[1] == '+' || str[1] == '-' {
|
||||
str = str[1:];
|
||||
b := buf[:len(str)+1];
|
||||
if b[1] == '+' || b[1] == '-' {
|
||||
b = b[1:];
|
||||
} else {
|
||||
str[0] = '+';
|
||||
b[0] = '+';
|
||||
}
|
||||
|
||||
if fi.space && !fi.plus && str[0] == '+' {
|
||||
str[0] = ' ';
|
||||
if fi.space && !fi.plus && b[0] == '+' {
|
||||
b[0] = ' ';
|
||||
}
|
||||
|
||||
if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
|
||||
strings.write_string(fi.buf, str);
|
||||
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
|
||||
strings.write_string(fi.buf, string(b));
|
||||
return;
|
||||
}
|
||||
|
||||
if fi.plus || str[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(str) {
|
||||
strings.write_byte(fi.buf, str[0]);
|
||||
fmt_write_padding(fi, fi.width - len(str));
|
||||
strings.write_string(fi.buf, str[1:]);
|
||||
if fi.plus || b[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(b) {
|
||||
strings.write_byte(fi.buf, b[0]);
|
||||
fmt_write_padding(fi, fi.width - len(b));
|
||||
strings.write_string(fi.buf, string(b[1:]));
|
||||
} else {
|
||||
_pad(fi, str);
|
||||
_pad(fi, string(b));
|
||||
}
|
||||
} else {
|
||||
_pad(fi, str[1:]);
|
||||
_pad(fi, string(b[1:]));
|
||||
}
|
||||
|
||||
case 'e', 'E':
|
||||
@@ -682,32 +688,32 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
||||
buf: [386]byte;
|
||||
|
||||
str := strconv.append_float(buf[1:], v, 'e', prec, bit_size);
|
||||
str = string(buf[:len(str)+1]);
|
||||
if str[1] == '+' || str[1] == '-' {
|
||||
str = str[1:];
|
||||
b := buf[:len(str)+1];
|
||||
if b[1] == '+' || b[1] == '-' {
|
||||
b = b[1:];
|
||||
} else {
|
||||
str[0] = '+';
|
||||
b[0] = '+';
|
||||
}
|
||||
|
||||
if fi.space && !fi.plus && str[0] == '+' {
|
||||
str[0] = ' ';
|
||||
if fi.space && !fi.plus && b[0] == '+' {
|
||||
b[0] = ' ';
|
||||
}
|
||||
|
||||
if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
|
||||
strings.write_string(fi.buf, str);
|
||||
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
|
||||
strings.write_string(fi.buf, string(b));
|
||||
return;
|
||||
}
|
||||
|
||||
if fi.plus || str[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(str) {
|
||||
strings.write_byte(fi.buf, str[0]);
|
||||
fmt_write_padding(fi, fi.width - len(str));
|
||||
strings.write_string(fi.buf, str[1:]);
|
||||
if fi.zero && fi.width_set && fi.width > len(b) {
|
||||
strings.write_byte(fi.buf, b[0]);
|
||||
fmt_write_padding(fi, fi.width - len(b));
|
||||
strings.write_string(fi.buf, string(b[1:]));
|
||||
} else {
|
||||
_pad(fi, str);
|
||||
_pad(fi, string(b));
|
||||
}
|
||||
} else {
|
||||
_pad(fi, str[1:]);
|
||||
_pad(fi, string(b[1:]));
|
||||
}
|
||||
|
||||
case 'h', 'H':
|
||||
@@ -783,15 +789,16 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
|
||||
}
|
||||
}
|
||||
|
||||
enum_value_to_string :: proc(v: any) -> (string, bool) {
|
||||
enum_value_to_string :: proc(val: any) -> (string, bool) {
|
||||
v := val;
|
||||
v.id = runtime.typeid_base(v.id);
|
||||
type_info := type_info_of(v.id);
|
||||
|
||||
switch e in type_info.variant {
|
||||
#partial switch e in type_info.variant {
|
||||
case: return "", false;
|
||||
case runtime.Type_Info_Enum:
|
||||
get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
|
||||
if types.is_string(e.base) {
|
||||
if reflect.is_string(e.base) {
|
||||
for val, idx in e.values {
|
||||
if v, ok := val.(T); ok && v == i {
|
||||
return e.names[idx], true;
|
||||
@@ -830,8 +837,8 @@ enum_value_to_string :: proc(v: any) -> (string, bool) {
|
||||
}
|
||||
|
||||
string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
|
||||
ti := type_info_base(type_info_of(T));
|
||||
if e, ok := ti.variant.(Type_Info_Enum); ok {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
||||
for str, idx in e.names {
|
||||
if s == str {
|
||||
// NOTE(bill): Unsafe cast
|
||||
@@ -850,7 +857,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
}
|
||||
|
||||
type_info := type_info_of(v.id);
|
||||
switch e in type_info.variant {
|
||||
#partial switch e in type_info.variant {
|
||||
case: fmt_bad_verb(fi, verb);
|
||||
case runtime.Type_Info_Enum:
|
||||
switch verb {
|
||||
@@ -866,6 +873,49 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
}
|
||||
|
||||
|
||||
stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) {
|
||||
et := runtime.type_info_base(enum_type);
|
||||
#partial switch e in et.variant {
|
||||
case: return "", false;
|
||||
case runtime.Type_Info_Enum:
|
||||
get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
|
||||
if reflect.is_string(e.base) {
|
||||
for val, idx in e.values {
|
||||
if v, ok := val.(T); ok && v == i {
|
||||
return e.names[idx], true;
|
||||
}
|
||||
}
|
||||
} else if len(e.values) == 0 {
|
||||
return "", true;
|
||||
} else {
|
||||
for val, idx in e.values {
|
||||
if v, ok := val.(T); ok && v == i {
|
||||
return e.names[idx], true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "", false;
|
||||
}
|
||||
|
||||
switch v in ev {
|
||||
case rune: return get_str(v + auto_cast offset, e);
|
||||
case i8: return get_str(v + auto_cast offset, e);
|
||||
case i16: return get_str(v + auto_cast offset, e);
|
||||
case i32: return get_str(v + auto_cast offset, e);
|
||||
case i64: return get_str(v + auto_cast offset, e);
|
||||
case int: return get_str(v + auto_cast offset, e);
|
||||
case u8: return get_str(v + auto_cast offset, e);
|
||||
case u16: return get_str(v + auto_cast offset, e);
|
||||
case u32: return get_str(v + auto_cast offset, e);
|
||||
case u64: return get_str(v + auto_cast offset, e);
|
||||
case uint: return get_str(v + auto_cast offset, e);
|
||||
case uintptr: return get_str(v + auto_cast offset, e);
|
||||
}
|
||||
}
|
||||
|
||||
return "", false;
|
||||
}
|
||||
|
||||
|
||||
enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 {
|
||||
switch i in ev {
|
||||
@@ -885,13 +935,31 @@ enum_value_to_u64 :: proc(ev: runtime.Type_Info_Enum_Value) -> u64 {
|
||||
return 0;
|
||||
}
|
||||
|
||||
enum_value_to_i64 :: proc(ev: runtime.Type_Info_Enum_Value) -> i64 {
|
||||
switch i in ev {
|
||||
case rune: return i64(i);
|
||||
case i8: return i64(i);
|
||||
case i16: return i64(i);
|
||||
case i32: return i64(i);
|
||||
case i64: return i64(i);
|
||||
case int: return i64(i);
|
||||
case u8: return i64(i);
|
||||
case u16: return i64(i);
|
||||
case u32: return i64(i);
|
||||
case u64: return i64(i);
|
||||
case uint: return i64(i);
|
||||
case uintptr: return i64(i);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
if ti == nil {
|
||||
return false;
|
||||
}
|
||||
ti = runtime.type_info_base(ti);
|
||||
switch info in ti.variant {
|
||||
t := runtime.type_info_base(ti);
|
||||
#partial switch info in t.variant {
|
||||
case runtime.Type_Info_Integer:
|
||||
switch info.endianness {
|
||||
case .Platform: return false;
|
||||
@@ -905,7 +973,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
|
||||
byte_swap :: bits.byte_swap;
|
||||
|
||||
type_info := type_info_of(v.id);
|
||||
switch info in type_info.variant {
|
||||
#partial switch info in type_info.variant {
|
||||
case runtime.Type_Info_Named:
|
||||
val := v;
|
||||
val.id = info.base.id;
|
||||
@@ -946,7 +1014,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
|
||||
if name != "" {
|
||||
strings.write_string(fi.buf, name);
|
||||
} else {
|
||||
write_type(fi.buf, type_info);
|
||||
reflect.write_type(fi.buf, type_info);
|
||||
}
|
||||
strings.write_byte(fi.buf, '{');
|
||||
defer strings.write_byte(fi.buf, '}');
|
||||
@@ -968,14 +1036,15 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
|
||||
continue loop;
|
||||
}
|
||||
}
|
||||
strings.write_i64(fi.buf, i64(i), 10);
|
||||
v := i64(i) + info.lower;
|
||||
strings.write_i64(fi.buf, v, 10);
|
||||
commas += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
|
||||
type_info := type_info_of(v.id);
|
||||
switch info in type_info.variant {
|
||||
#partial switch info in type_info.variant {
|
||||
case runtime.Type_Info_Named:
|
||||
val := v;
|
||||
val.id = info.base.id;
|
||||
@@ -1041,18 +1110,18 @@ fmt_opaque :: proc(fi: ^Info, v: any) {
|
||||
if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok {
|
||||
elem := rt.type_info_base(ot.elem);
|
||||
if elem == nil do return;
|
||||
write_type(fi.buf, type_info);
|
||||
reflect.write_type(fi.buf, type_info);
|
||||
strings.write_byte(fi.buf, '{');
|
||||
defer strings.write_byte(fi.buf, '}');
|
||||
|
||||
switch in elem.variant {
|
||||
#partial switch in elem.variant {
|
||||
case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float:
|
||||
fmt_value(fi, any{v.data, elem.id}, 'v');
|
||||
case:
|
||||
// Okay
|
||||
}
|
||||
} else {
|
||||
write_type(fi.buf, type_info);
|
||||
reflect.write_type(fi.buf, type_info);
|
||||
strings.write_byte(fi.buf, '{');
|
||||
strings.write_byte(fi.buf, '}');
|
||||
}
|
||||
@@ -1066,8 +1135,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
type_info := type_info_of(v.id);
|
||||
switch info in type_info.variant {
|
||||
case runtime.Type_Info_Any: // Ignore
|
||||
case runtime.Type_Info_Tuple: // Ignore
|
||||
|
||||
case runtime.Type_Info_Named:
|
||||
switch b in info.base.variant {
|
||||
#partial switch b in info.base.variant {
|
||||
case runtime.Type_Info_Struct:
|
||||
if verb != 'v' {
|
||||
fmt_bad_verb(fi, verb);
|
||||
@@ -1078,8 +1150,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
strings.write_string(fi.buf, "{}");
|
||||
return;
|
||||
};
|
||||
|
||||
is_soa := b.soa_kind != .None;
|
||||
|
||||
strings.write_string(fi.buf, info.name);
|
||||
strings.write_byte(fi.buf, '{');
|
||||
strings.write_byte(fi.buf, is_soa ? '[' : '{');
|
||||
|
||||
hash := fi.hash; defer fi.hash = hash;
|
||||
indent := fi.indent; defer fi.indent -= 1;
|
||||
@@ -1088,30 +1163,73 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
fi.indent += 1;
|
||||
|
||||
if hash do strings.write_byte(fi.buf, '\n');
|
||||
|
||||
field_count := -1;
|
||||
for name, i in b.names {
|
||||
// if len(name) > 0 && name[0] == '_' do continue;
|
||||
field_count += 1;
|
||||
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
|
||||
|
||||
strings.write_string(fi.buf, name);
|
||||
strings.write_string(fi.buf, " = ");
|
||||
|
||||
if t := b.types[i]; types.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + b.offsets[i]);
|
||||
fmt_arg(fi, any{data, t.id}, 'v');
|
||||
}
|
||||
|
||||
if hash do strings.write_string(fi.buf, ",\n");
|
||||
defer {
|
||||
if hash do for in 0..<indent do strings.write_byte(fi.buf, '\t');
|
||||
strings.write_byte(fi.buf, is_soa ? ']' : '}');
|
||||
}
|
||||
|
||||
if hash do for in 0..<indent do strings.write_byte(fi.buf, '\t');
|
||||
strings.write_byte(fi.buf, '}');
|
||||
if is_soa {
|
||||
fi.indent += 1;
|
||||
defer fi.indent -= 1;
|
||||
|
||||
base_type_name: string;
|
||||
if v, ok := b.soa_base_type.variant.(runtime.Type_Info_Named); ok {
|
||||
base_type_name = v.name;
|
||||
}
|
||||
|
||||
for index in 0..<uintptr(b.soa_len) {
|
||||
if !hash && index > 0 do strings.write_string(fi.buf, ", ");
|
||||
|
||||
field_count := -1;
|
||||
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
|
||||
strings.write_string(fi.buf, base_type_name);
|
||||
strings.write_byte(fi.buf, '{');
|
||||
defer strings.write_byte(fi.buf, '}');
|
||||
|
||||
for name, i in b.names {
|
||||
field_count += 1;
|
||||
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
|
||||
|
||||
strings.write_string(fi.buf, name);
|
||||
strings.write_string(fi.buf, " = ");
|
||||
|
||||
t := b.types[i].variant.(runtime.Type_Info_Array).elem;
|
||||
t_size := uintptr(t.size);
|
||||
if reflect.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size);
|
||||
fmt_arg(fi, any{data, t.id}, 'v');
|
||||
}
|
||||
|
||||
if hash do strings.write_string(fi.buf, ",\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field_count := -1;
|
||||
for name, i in b.names {
|
||||
field_count += 1;
|
||||
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
|
||||
|
||||
strings.write_string(fi.buf, name);
|
||||
strings.write_string(fi.buf, " = ");
|
||||
|
||||
if t := b.types[i]; reflect.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + b.offsets[i]);
|
||||
fmt_arg(fi, any{data, t.id}, 'v');
|
||||
}
|
||||
|
||||
if hash do strings.write_string(fi.buf, ",\n");
|
||||
}
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
fmt_bit_set(fi, v);
|
||||
@@ -1128,18 +1246,19 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
case runtime.Type_Info_Rune: fmt_arg(fi, v, verb);
|
||||
case runtime.Type_Info_Float: fmt_arg(fi, v, verb);
|
||||
case runtime.Type_Info_Complex: fmt_arg(fi, v, verb);
|
||||
case runtime.Type_Info_Quaternion: fmt_arg(fi, v, verb);
|
||||
case runtime.Type_Info_String: fmt_arg(fi, v, verb);
|
||||
|
||||
case runtime.Type_Info_Pointer:
|
||||
if v.id == typeid_of(^runtime.Type_Info) {
|
||||
write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
|
||||
reflect.write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
|
||||
} else {
|
||||
ptr := (^rawptr)(v.data)^;
|
||||
if verb != 'p' && info.elem != nil {
|
||||
a := any{ptr, info.elem.id};
|
||||
|
||||
elem := runtime.type_info_base(info.elem);
|
||||
if elem != nil do switch e in elem.variant {
|
||||
if elem != nil do #partial switch e in elem.variant {
|
||||
case runtime.Type_Info_Array,
|
||||
runtime.Type_Info_Slice,
|
||||
runtime.Type_Info_Dynamic_Array,
|
||||
@@ -1184,6 +1303,25 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
strings.write_byte(fi.buf, '[');
|
||||
defer strings.write_byte(fi.buf, ']');
|
||||
for i in 0..<info.count {
|
||||
if i > 0 do strings.write_string(fi.buf, ", ");
|
||||
|
||||
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i);
|
||||
if ok {
|
||||
strings.write_byte(fi.buf, '.');
|
||||
strings.write_string(fi.buf, idx);
|
||||
} else {
|
||||
strings.write_i64(fi.buf, enum_value_to_i64(info.min_value)+i64(i));
|
||||
}
|
||||
strings.write_string(fi.buf, " = ");
|
||||
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size);
|
||||
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
if verb == 'p' {
|
||||
slice := cast(^mem.Raw_Dynamic_Array)v.data;
|
||||
@@ -1255,7 +1393,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size);
|
||||
header := cast(^runtime.Map_Entry_Header)data;
|
||||
|
||||
if types.is_string(info.key) {
|
||||
if reflect.is_string(info.key) {
|
||||
strings.write_string(fi.buf, header.key.str);
|
||||
} else {
|
||||
fi := Info{buf = fi.buf};
|
||||
@@ -1275,8 +1413,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
return;
|
||||
}
|
||||
|
||||
strings.write_byte(fi.buf, '{');
|
||||
defer strings.write_byte(fi.buf, '}');
|
||||
is_soa := info.soa_kind != .None;
|
||||
|
||||
strings.write_byte(fi.buf, is_soa ? '[' : '{');
|
||||
defer strings.write_byte(fi.buf, is_soa ? ']' : '}');
|
||||
|
||||
fi.indent += 1; defer fi.indent -= 1;
|
||||
hash := fi.hash; defer fi.hash = hash;
|
||||
@@ -1285,27 +1425,106 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
if hash do strings.write_byte(fi.buf, '\n');
|
||||
|
||||
for _, i in info.names {
|
||||
if !hash && i > 0 do strings.write_string(fi.buf, ", ");
|
||||
if hash {
|
||||
for in 0..<fi.indent {
|
||||
strings.write_byte(fi.buf, '\t');
|
||||
if is_soa {
|
||||
fi.indent += 1;
|
||||
defer fi.indent -= 1;
|
||||
|
||||
base_type_name: string;
|
||||
if v, ok := info.soa_base_type.variant.(runtime.Type_Info_Named); ok {
|
||||
base_type_name = v.name;
|
||||
}
|
||||
|
||||
actual_field_count := len(info.names);
|
||||
|
||||
n := uintptr(info.soa_len);
|
||||
|
||||
if info.soa_kind == .Slice {
|
||||
actual_field_count = len(info.names)-1; // len
|
||||
|
||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^);
|
||||
|
||||
} else if info.soa_kind == .Dynamic {
|
||||
actual_field_count = len(info.names)-3; // len, cap, allocator
|
||||
|
||||
n = uintptr((^int)(uintptr(v.data) + info.offsets[actual_field_count])^);
|
||||
}
|
||||
|
||||
|
||||
|
||||
for index in 0..<n {
|
||||
if !hash && index > 0 do strings.write_string(fi.buf, ", ");
|
||||
|
||||
field_count := -1;
|
||||
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
|
||||
strings.write_string(fi.buf, base_type_name);
|
||||
strings.write_byte(fi.buf, '{');
|
||||
defer strings.write_byte(fi.buf, '}');
|
||||
|
||||
for i in 0..<actual_field_count {
|
||||
name := info.names[i];
|
||||
field_count += 1;
|
||||
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
|
||||
|
||||
strings.write_string(fi.buf, name);
|
||||
strings.write_string(fi.buf, " = ");
|
||||
|
||||
if info.soa_kind == .Fixed {
|
||||
t := info.types[i].variant.(runtime.Type_Info_Array).elem;
|
||||
t_size := uintptr(t.size);
|
||||
if reflect.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size);
|
||||
fmt_arg(fi, any{data, t.id}, 'v');
|
||||
}
|
||||
} else {
|
||||
t := info.types[i].variant.(runtime.Type_Info_Pointer).elem;
|
||||
t_size := uintptr(t.size);
|
||||
if reflect.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^;
|
||||
data := rawptr(uintptr(field_ptr) + index*t_size);
|
||||
fmt_arg(fi, any{data, t.id}, 'v');
|
||||
}
|
||||
}
|
||||
|
||||
if hash do strings.write_string(fi.buf, ",\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field_count := -1;
|
||||
for name, i in info.names {
|
||||
field_count += 1;
|
||||
|
||||
strings.write_string(fi.buf, info.names[i]);
|
||||
strings.write_string(fi.buf, " = ");
|
||||
if !hash && field_count > 0 do strings.write_string(fi.buf, ", ");
|
||||
if hash do for in 0..<fi.indent do strings.write_byte(fi.buf, '\t');
|
||||
|
||||
if t := info.types[i]; types.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
data := uintptr(v.data) + info.offsets[i];
|
||||
fmt_arg(fi, any{rawptr(data), t.id}, 'v');
|
||||
strings.write_string(fi.buf, name);
|
||||
strings.write_string(fi.buf, " = ");
|
||||
|
||||
if t := info.types[i]; reflect.is_any(t) {
|
||||
strings.write_string(fi.buf, "any{}");
|
||||
} else {
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i]);
|
||||
fmt_arg(fi, any{data, t.id}, 'v');
|
||||
}
|
||||
|
||||
if hash do strings.write_string(fi.buf, ",\n");
|
||||
}
|
||||
if hash do strings.write_string(fi.buf, ",\n");
|
||||
}
|
||||
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
if type_info.size == 0 {
|
||||
strings.write_string(fi.buf, "nil");
|
||||
return;
|
||||
}
|
||||
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset;
|
||||
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
||||
|
||||
@@ -1321,8 +1540,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
case i64: tag = i64(i);
|
||||
case: panic("Invalid union tag type");
|
||||
}
|
||||
assert(tag >= 0);
|
||||
|
||||
if v.data == nil || tag == 0 {
|
||||
if v.data == nil {
|
||||
strings.write_string(fi.buf, "nil");
|
||||
} else if info.no_nil {
|
||||
id := info.variants[tag].id;
|
||||
fmt_arg(fi, any{v.data, id}, verb);
|
||||
} else if tag == 0 {
|
||||
strings.write_string(fi.buf, "nil");
|
||||
} else {
|
||||
id := info.variants[tag-1].id;
|
||||
@@ -1337,14 +1562,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
if ptr == nil {
|
||||
strings.write_string(fi.buf, "nil");
|
||||
} else {
|
||||
write_typeid(fi.buf, v.id);
|
||||
reflect.write_typeid(fi.buf, v.id);
|
||||
strings.write_string(fi.buf, " @ ");
|
||||
fmt_pointer(fi, ptr, 'p');
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Type_Id:
|
||||
id := (^typeid)(v.data)^;
|
||||
write_typeid(fi.buf, id);
|
||||
reflect.write_typeid(fi.buf, id);
|
||||
|
||||
case runtime.Type_Info_Bit_Field:
|
||||
fmt_bit_field(fi, v);
|
||||
@@ -1374,6 +1599,31 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'f', 'F', 'v', 'h', 'H':
|
||||
r, i, j, k := real(q), imag(q), jmag(q), kmag(q);
|
||||
|
||||
fmt_float(fi, r, bits/4, verb);
|
||||
|
||||
if !fi.plus && i >= 0 do strings.write_rune(fi.buf, '+');
|
||||
fmt_float(fi, i, bits/4, verb);
|
||||
strings.write_rune(fi.buf, 'i');
|
||||
|
||||
if !fi.plus && j >= 0 do strings.write_rune(fi.buf, '+');
|
||||
fmt_float(fi, j, bits/4, verb);
|
||||
strings.write_rune(fi.buf, 'j');
|
||||
|
||||
if !fi.plus && k >= 0 do strings.write_rune(fi.buf, '+');
|
||||
fmt_float(fi, k, bits/4, verb);
|
||||
strings.write_rune(fi.buf, 'k');
|
||||
|
||||
case:
|
||||
fmt_bad_verb(fi, verb);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
|
||||
if arg == nil {
|
||||
strings.write_string(fi.buf, "<nil>");
|
||||
@@ -1386,7 +1636,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
|
||||
switch a in arg {
|
||||
case ^runtime.Type_Info: ti = a;
|
||||
}
|
||||
write_type(fi.buf, ti);
|
||||
reflect.write_type(fi.buf, ti);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1422,6 +1672,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
|
||||
case complex64: fmt_complex(fi, complex128(a), 64, verb);
|
||||
case complex128: fmt_complex(fi, a, 128, verb);
|
||||
|
||||
case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb);
|
||||
case quaternion256: fmt_quaternion(fi, a, 256, verb);
|
||||
|
||||
case i8: fmt_int(fi, u64(a), true, 8, verb);
|
||||
case u8: fmt_int(fi, u64(a), false, 8, verb);
|
||||
case i16: fmt_int(fi, u64(a), true, 16, verb);
|
||||
@@ -1437,7 +1690,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
|
||||
case string: fmt_string(fi, a, verb);
|
||||
case cstring: fmt_cstring(fi, a, verb);
|
||||
|
||||
case typeid: write_typeid(fi.buf, a);
|
||||
case typeid: reflect.write_typeid(fi.buf, a);
|
||||
|
||||
case i16le: fmt_int(fi, u64(a), true, 16, verb);
|
||||
case u16le: fmt_int(fi, u64(a), false, 16, verb);
|
||||
@@ -1470,212 +1723,3 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
|
||||
|
||||
|
||||
|
||||
|
||||
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
|
||||
write_type(buf, type_info_of(id));
|
||||
}
|
||||
|
||||
write_type :: proc(buf: ^strings.Builder, ti: ^runtime.Type_Info) {
|
||||
using strings;
|
||||
if ti == nil {
|
||||
write_string(buf, "nil");
|
||||
return;
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case runtime.Type_Info_Named:
|
||||
write_string(buf, info.name);
|
||||
case runtime.Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: write_string(buf, "int");
|
||||
case uint: write_string(buf, "uint");
|
||||
case uintptr: write_string(buf, "uintptr");
|
||||
case:
|
||||
write_byte(buf, info.signed ? 'i' : 'u');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
switch info.endianness {
|
||||
case runtime.Type_Info_Endianness.Little:
|
||||
write_string(buf, "le");
|
||||
case runtime.Type_Info_Endianness.Big:
|
||||
write_string(buf, "be");
|
||||
}
|
||||
}
|
||||
case runtime.Type_Info_Rune:
|
||||
write_string(buf, "rune");
|
||||
case runtime.Type_Info_Float:
|
||||
write_byte(buf, 'f');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case runtime.Type_Info_Complex:
|
||||
write_string(buf, "complex");
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case runtime.Type_Info_String:
|
||||
if info.is_cstring {
|
||||
write_string(buf, "cstring");
|
||||
} else {
|
||||
write_string(buf, "string");
|
||||
}
|
||||
case runtime.Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: write_string(buf, "bool");
|
||||
case:
|
||||
write_byte(buf, 'b');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
}
|
||||
case runtime.Type_Info_Any:
|
||||
write_string(buf, "any");
|
||||
|
||||
case runtime.Type_Info_Type_Id:
|
||||
write_string(buf, "typeid");
|
||||
|
||||
case runtime.Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
write_string(buf, "rawptr");
|
||||
} else {
|
||||
write_string(buf, "^");
|
||||
write_type(buf, info.elem);
|
||||
}
|
||||
case runtime.Type_Info_Procedure:
|
||||
write_string(buf, "proc");
|
||||
if info.params == nil {
|
||||
write_string(buf, "()");
|
||||
} else {
|
||||
t := info.params.variant.(runtime.Type_Info_Tuple);
|
||||
write_string(buf, "(");
|
||||
for t, i in t.types {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_type(buf, t);
|
||||
}
|
||||
write_string(buf, ")");
|
||||
}
|
||||
if info.results != nil {
|
||||
write_string(buf, " -> ");
|
||||
write_type(buf, info.results);
|
||||
}
|
||||
case runtime.Type_Info_Tuple:
|
||||
count := len(info.names);
|
||||
if count != 1 do write_string(buf, "(");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
|
||||
t := info.types[i];
|
||||
|
||||
if len(name) > 0 {
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
}
|
||||
write_type(buf, t);
|
||||
}
|
||||
if count != 1 do write_string(buf, ")");
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
write_string(buf, "[");
|
||||
write_i64(buf, i64(info.count), 10);
|
||||
write_string(buf, "]");
|
||||
write_type(buf, info.elem);
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
write_string(buf, "[dynamic]");
|
||||
write_type(buf, info.elem);
|
||||
case runtime.Type_Info_Slice:
|
||||
write_string(buf, "[]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
write_string(buf, "map[");
|
||||
write_type(buf, info.key);
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.value);
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
write_string(buf, "struct ");
|
||||
if info.is_packed do write_string(buf, "#packed ");
|
||||
if info.is_raw_union do write_string(buf, "#raw_union ");
|
||||
if info.custom_align {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_byte(buf, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
write_type(buf, info.types[i]);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
write_string(buf, "union ");
|
||||
if info.custom_align {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_byte(buf, '{');
|
||||
for variant, i in info.variants {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_type(buf, variant);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
write_string(buf, "enum ");
|
||||
write_type(buf, info.base);
|
||||
write_string(buf, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case runtime.Type_Info_Bit_Field:
|
||||
write_string(buf, "bit_field ");
|
||||
if ti.align != 1 {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_string(buf, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
write_i64(buf, i64(info.bits[i]), 10);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
write_string(buf, "bit_set[");
|
||||
switch {
|
||||
case types.is_enum(info.elem):
|
||||
write_type(buf, info.elem);
|
||||
case types.is_rune(info.elem):
|
||||
write_encoded_rune(buf, rune(info.lower));
|
||||
write_string(buf, "..");
|
||||
write_encoded_rune(buf, rune(info.upper));
|
||||
case:
|
||||
write_i64(buf, info.lower, 10);
|
||||
write_string(buf, "..");
|
||||
write_i64(buf, info.upper, 10);
|
||||
}
|
||||
if info.underlying != nil {
|
||||
write_string(buf, "; ");
|
||||
write_type(buf, info.underlying);
|
||||
}
|
||||
write_byte(buf, ']');
|
||||
|
||||
case runtime.Type_Info_Opaque:
|
||||
write_string(buf, "opaque ");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case runtime.Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
write_string(buf, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
write_string(buf, "intrinsics.vector(");
|
||||
write_i64(buf, i64(info.count));
|
||||
write_string(buf, ", ");
|
||||
write_type(buf, info.elem);
|
||||
write_byte(buf, ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
// This is purely for documentation
|
||||
package intrinsics
|
||||
|
||||
|
||||
x86_mmx :: x86_mmx; // Specialized SIMD Vector type
|
||||
|
||||
simd_vector :: proc($N: int, $T: typeid) -> type/#simd[N]T
|
||||
|
||||
atomic_fence :: proc() ---
|
||||
atomic_fence_acq :: proc() ---
|
||||
atomic_fence_rel :: proc() ---
|
||||
atomic_fence_acqrel :: proc() ---
|
||||
|
||||
atomic_store :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_rel :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_relaxed :: proc(dst: ^$T, val: $T) ---
|
||||
atomic_store_unordered :: proc(dst: ^$T, val: $T) ---
|
||||
|
||||
atomic_load :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_acq :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_relaxed :: proc(dst: ^$T) -> T ---
|
||||
atomic_load_unordered :: proc(dst: ^$T) -> T ---
|
||||
|
||||
atomic_add :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
|
||||
atomic_xchg :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T ---
|
||||
atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T ---
|
||||
|
||||
atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) ---
|
||||
|
||||
|
||||
// Constant type tests
|
||||
|
||||
type_base_type :: proc($T: typeid) -> type ---
|
||||
type_core_type :: proc($T: typeid) -> type ---
|
||||
type_elem_type :: proc($T: typeid) -> type ---
|
||||
|
||||
type_is_boolean :: proc($T: typeid) -> bool ---
|
||||
type_is_integer :: proc($T: typeid) -> bool ---
|
||||
type_is_rune :: proc($T: typeid) -> bool ---
|
||||
type_is_float :: proc($T: typeid) -> bool ---
|
||||
type_is_complex :: proc($T: typeid) -> bool ---
|
||||
type_is_quaternion :: proc($T: typeid) -> bool ---
|
||||
type_is_string :: proc($T: typeid) -> bool ---
|
||||
type_is_typeid :: proc($T: typeid) -> bool ---
|
||||
type_is_any :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_endian_little :: proc($T: typeid) -> bool ---
|
||||
type_is_endian_big :: proc($T: typeid) -> bool ---
|
||||
type_is_unsigned :: proc($T: typeid) -> bool ---
|
||||
type_is_numeric :: proc($T: typeid) -> bool ---
|
||||
type_is_ordered :: proc($T: typeid) -> bool ---
|
||||
type_is_ordered_numeric :: proc($T: typeid) -> bool ---
|
||||
type_is_indexable :: proc($T: typeid) -> bool ---
|
||||
type_is_sliceable :: proc($T: typeid) -> bool ---
|
||||
type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp
|
||||
type_is_dereferenceable :: proc($T: typeid) -> bool ---
|
||||
type_is_valid_map_key :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_named :: proc($T: typeid) -> bool ---
|
||||
type_is_pointer :: proc($T: typeid) -> bool ---
|
||||
type_is_opaque :: proc($T: typeid) -> bool ---
|
||||
type_is_array :: proc($T: typeid) -> bool ---
|
||||
type_is_enumerated_array :: proc($T: typeid) -> bool ---
|
||||
type_is_slice :: proc($T: typeid) -> bool ---
|
||||
type_is_dynamic_array :: proc($T: typeid) -> bool ---
|
||||
type_is_map :: proc($T: typeid) -> bool ---
|
||||
type_is_struct :: proc($T: typeid) -> bool ---
|
||||
type_is_union :: proc($T: typeid) -> bool ---
|
||||
type_is_enum :: proc($T: typeid) -> bool ---
|
||||
type_is_proc :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_field_value :: proc($T: typeid) -> bool ---
|
||||
type_is_bit_set :: proc($T: typeid) -> bool ---
|
||||
type_is_simd_vector :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
|
||||
|
||||
type_has_nil :: proc($T: typeid) -> bool ---
|
||||
|
||||
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
|
||||
@@ -14,18 +14,18 @@ Level_Headers := []string{
|
||||
};
|
||||
|
||||
Default_Console_Logger_Opts :: Options{
|
||||
Option.Level,
|
||||
Option.Terminal_Color,
|
||||
Option.Short_File_Path,
|
||||
Option.Line,
|
||||
Option.Procedure,
|
||||
.Level,
|
||||
.Terminal_Color,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
Default_File_Logger_Opts :: Options{
|
||||
Option.Level,
|
||||
Option.Short_File_Path,
|
||||
Option.Line,
|
||||
Option.Procedure,
|
||||
.Level,
|
||||
.Short_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
} | Full_Timestamp_Opts;
|
||||
|
||||
|
||||
@@ -109,10 +109,10 @@ do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) {
|
||||
case Level.Error, Level.Fatal : col = RED;
|
||||
}
|
||||
|
||||
if Option.Level in opts {
|
||||
if Option.Terminal_Color in opts do fmt.sbprint(str, col);
|
||||
if .Level in opts {
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, col);
|
||||
fmt.sbprint(str, Level_Headers[level]);
|
||||
if Option.Terminal_Color in opts do fmt.sbprint(str, RESET);
|
||||
if .Terminal_Color in opts do fmt.sbprint(str, RESET);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,22 +120,21 @@ do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := #
|
||||
if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return;
|
||||
|
||||
file := location.file_path;
|
||||
if Option.Short_File_Path in opts {
|
||||
when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/';
|
||||
if .Short_File_Path in opts {
|
||||
last := 0;
|
||||
for r, i in location.file_path do if r == delimiter do last = i+1;
|
||||
for r, i in location.file_path do if r == '/' do last = i+1;
|
||||
file = location.file_path[last:];
|
||||
}
|
||||
|
||||
if Location_File_Opts & opts != nil do fmt.sbprint(buf, file);
|
||||
|
||||
if Option.Procedure in opts {
|
||||
if .Procedure in opts {
|
||||
if Location_File_Opts & opts != nil do fmt.sbprint(buf, ".");
|
||||
fmt.sbprintf(buf, "%s()", location.procedure);
|
||||
}
|
||||
|
||||
if Option.Line in opts {
|
||||
if Location_File_Opts & opts != nil || Option.Procedure in opts do fmt.sbprint(buf, ":");
|
||||
if .Line in opts {
|
||||
if Location_File_Opts & opts != nil || .Procedure in opts do fmt.sbprint(buf, ":");
|
||||
fmt.sbprint(buf, location.line);
|
||||
}
|
||||
|
||||
|
||||
+58
-32
@@ -1,7 +1,13 @@
|
||||
package log
|
||||
|
||||
import "core:runtime"
|
||||
import "core:fmt"
|
||||
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
|
||||
Level :: runtime.Logger_Level;
|
||||
/*
|
||||
Level :: enum {
|
||||
Debug,
|
||||
Info,
|
||||
@@ -9,63 +15,82 @@ Level :: enum {
|
||||
Error,
|
||||
Fatal,
|
||||
}
|
||||
*/
|
||||
|
||||
Option :: runtime.Logger_Option;
|
||||
/*
|
||||
Option :: enum {
|
||||
Level,
|
||||
Date,
|
||||
Time,
|
||||
Short_File_Path,
|
||||
Long_File_Path,
|
||||
Line,
|
||||
Procedure,
|
||||
Terminal_Color
|
||||
Level,
|
||||
Date,
|
||||
Time,
|
||||
Short_File_Path,
|
||||
Long_File_Path,
|
||||
Line,
|
||||
Procedure,
|
||||
Terminal_Color
|
||||
}
|
||||
*/
|
||||
|
||||
Options :: runtime.Logger_Options;
|
||||
/*
|
||||
Options :: bit_set[Option];
|
||||
*/
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
Option.Date,
|
||||
Option.Time
|
||||
.Date,
|
||||
.Time
|
||||
};
|
||||
Location_Header_Opts :: Options{
|
||||
Option.Short_File_Path,
|
||||
Option.Long_File_Path,
|
||||
Option.Line,
|
||||
Option.Procedure,
|
||||
.Short_File_Path,
|
||||
.Long_File_Path,
|
||||
.Line,
|
||||
.Procedure,
|
||||
};
|
||||
Location_File_Opts :: Options{
|
||||
Option.Short_File_Path,
|
||||
Option.Long_File_Path
|
||||
.Short_File_Path,
|
||||
.Long_File_Path
|
||||
};
|
||||
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
|
||||
Logger_Proc :: runtime.Logger_Proc;
|
||||
/*
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
*/
|
||||
|
||||
Logger :: runtime.Logger;
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
data: rawptr,
|
||||
options: Options,
|
||||
options: Options,
|
||||
}
|
||||
*/
|
||||
|
||||
Multi_Logger_Data :: struct {
|
||||
loggers : []Logger,
|
||||
loggers : []Logger,
|
||||
}
|
||||
|
||||
create_multi_logger :: proc(logs: ..Logger) -> Logger {
|
||||
data := new(Multi_Logger_Data);
|
||||
data.loggers = make([]Logger, len(logs));
|
||||
copy(data.loggers, logs);
|
||||
return Logger{multi_logger_proc, data, nil};
|
||||
data := new(Multi_Logger_Data);
|
||||
data.loggers = make([]Logger, len(logs));
|
||||
copy(data.loggers, logs);
|
||||
return Logger{multi_logger_proc, data, nil};
|
||||
}
|
||||
|
||||
destroy_multi_logger ::proc(log : ^Logger) {
|
||||
free(log.data);
|
||||
log^ = nil_logger();
|
||||
destroy_multi_logger :: proc(log : ^Logger) {
|
||||
free(log.data);
|
||||
log^ = nil_logger();
|
||||
}
|
||||
|
||||
multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string,
|
||||
options: Options, location := #caller_location) {
|
||||
data := cast(^Multi_Logger_Data)logger_data;
|
||||
if data.loggers == nil || len(data.loggers) == 0 do return;
|
||||
for log in data.loggers do log.procedure(log.data, level, text, log.options, location);
|
||||
data := cast(^Multi_Logger_Data)logger_data;
|
||||
if data.loggers == nil || len(data.loggers) == 0 {
|
||||
return;
|
||||
}
|
||||
for log in data.loggers {
|
||||
log.procedure(log.data, level, text, log.options, location);
|
||||
}
|
||||
}
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
@@ -76,6 +101,7 @@ nil_logger :: proc() -> Logger {
|
||||
return Logger{nil_logger_proc, nil, nil};
|
||||
}
|
||||
|
||||
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
|
||||
debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location);
|
||||
info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location);
|
||||
warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location);
|
||||
@@ -83,7 +109,7 @@ error :: proc(fmt_str : string, args : ..any, location := #caller_location) do l
|
||||
fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location);
|
||||
|
||||
logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) {
|
||||
logger := context.logger;
|
||||
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
|
||||
logger.procedure(logger.data, level, str, logger.options, location);
|
||||
logger := context.logger;
|
||||
str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
|
||||
logger.procedure(logger.data, level, str, logger.options, location);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package bits
|
||||
package math_bits
|
||||
|
||||
import "core:os"
|
||||
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
package linalg
|
||||
|
||||
import "core:math"
|
||||
import "intrinsics"
|
||||
|
||||
// Generic
|
||||
|
||||
@private IS_NUMERIC :: intrinsics.type_is_numeric;
|
||||
@private IS_QUATERNION :: intrinsics.type_is_quaternion;
|
||||
@private IS_ARRAY :: intrinsics.type_is_array;
|
||||
|
||||
|
||||
vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
c += a[i] * b[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
|
||||
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z;
|
||||
}
|
||||
|
||||
dot :: proc{vector_dot, quaternion128_dot, quaternion256_dot};
|
||||
|
||||
quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
|
||||
return a[0]*b[1] - b[0]*a[1];
|
||||
}
|
||||
|
||||
vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
|
||||
c[0] = a[1]*b[2] - b[1]*a[2];
|
||||
c[1] = a[2]*b[0] - b[2]*a[0];
|
||||
c[2] = a[0]*b[1] - b[0]*a[1];
|
||||
return;
|
||||
}
|
||||
|
||||
vector_cross :: proc{vector_cross2, vector_cross3};
|
||||
cross :: vector_cross;
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
return v / length(v);
|
||||
}
|
||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return q/abs(q);
|
||||
}
|
||||
normalize :: proc{vector_normalize, quaternion_normalize};
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
m := length(v);
|
||||
return m == 0 ? 0 : v/m;
|
||||
}
|
||||
quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
m := abs(q);
|
||||
return m == 0 ? 0 : q/m;
|
||||
}
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0};
|
||||
|
||||
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return math.sqrt(dot(v, v));
|
||||
}
|
||||
|
||||
vector_length2 :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
return dot(v, v);
|
||||
}
|
||||
|
||||
quaternion_length :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return abs(q);
|
||||
}
|
||||
|
||||
quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
return dot(q, q);
|
||||
}
|
||||
|
||||
length :: proc{vector_length, quaternion_length};
|
||||
length2 :: proc{vector_length2, quaternion_length2};
|
||||
|
||||
|
||||
vector_lerp :: proc(x, y, t: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ti := t[i];
|
||||
s[i] = x[i]*(1-ti) + y[i]*ti;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_unlerp :: proc(a, b, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
ai := a[i];
|
||||
s[i] = (x[i]-ai)/(b[i]-ai);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sin :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sin(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_cos :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.cos(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_tan :: proc(angle: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.tan(angle[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_asin :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.asin(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_acos :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.acos(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_atan2 :: proc(y, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.atan(y[i], x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_pow :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.pow(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_expr :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.expr(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sqrt :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sqrt(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_abs :: proc(x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = abs(x[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_sign :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.sign(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_floor :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.floor(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_ceil :: proc(v: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.ceil(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
vector_mod :: proc(x, y: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = math.mod(x[i], y[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_min :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = min(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_max :: proc(a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = max(a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_clamp :: proc(x, a, b: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = clamp(x[i], a[i], b[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_mix :: proc(x, y, a: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = x[i]*(1-a[i]) + y[i]*a[i];
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_step :: proc(edge, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
s[i] = x[i] < edge[i] ? 0 : 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smoothstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * (3 - 2*t);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_smootherstep :: proc(edge0, edge1, x: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
s: V;
|
||||
for i in 0..<N {
|
||||
e0, e1 := edge0[i], edge1[i];
|
||||
t := clamp((x[i] - e0) / (e1 - e0), 0, 1);
|
||||
s[i] = t * t * t * (t * (6*t - 15) + 10);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
vector_distance :: proc(p0, p1: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
return length(p1 - p0);
|
||||
}
|
||||
|
||||
vector_reflect :: proc(i, n: $V/[$N]$E) -> V where IS_NUMERIC(E) {
|
||||
b := n * (2 * dot(n, i));
|
||||
return i - b;
|
||||
}
|
||||
|
||||
vector_refract :: proc(i, n: $V/[$N]$E, eta: E) -> V where IS_NUMERIC(E) {
|
||||
dv := dot(n, i);
|
||||
k := 1 - eta*eta - (1 - dv*dv);
|
||||
a := i * eta;
|
||||
b := n * eta*dv*math.sqrt(k);
|
||||
return (a - b) * E(int(k >= 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
identity :: proc($T: typeid/[$N][N]$E) -> (m: T) {
|
||||
for i in 0..<N do m[i][i] = E(1);
|
||||
return m;
|
||||
}
|
||||
|
||||
trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
|
||||
for i in 0..<N {
|
||||
tr += m[i][i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
transpose :: proc(a: $T/[$N][$M]$E) -> (m: T) {
|
||||
for j in 0..<M {
|
||||
for i in 0..<N {
|
||||
m[j][i] = a[i][j];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (c: M)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<N {
|
||||
for k in 0..<N {
|
||||
for j in 0..<N {
|
||||
c[k][i] += a[j][i] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (c: [K][I]E)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E),
|
||||
I != K {
|
||||
for k in 0..<K {
|
||||
for j in 0..<J {
|
||||
for i in 0..<I {
|
||||
c[k][i] += a[j][i] * b[k][j];
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B)
|
||||
where !IS_ARRAY(E),
|
||||
IS_NUMERIC(E) {
|
||||
for i in 0..<I {
|
||||
for j in 0..<J {
|
||||
c[i] += a[i][j] * b[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f32, r: f32};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f32)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
|
||||
quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
|
||||
Raw_Quaternion :: struct {xyz: [3]f64, r: f64};
|
||||
|
||||
q := transmute(Raw_Quaternion)q;
|
||||
v := transmute([3]f64)v;
|
||||
|
||||
t := cross(2*q.xyz, v);
|
||||
return V(v + q.r*t + cross(q.xyz, t));
|
||||
}
|
||||
quaternion_mul_vector3 :: proc{quaternion128_mul_vector3, quaternion256_mul_vector3};
|
||||
|
||||
mul :: proc{
|
||||
matrix_mul,
|
||||
matrix_mul_differ,
|
||||
matrix_mul_vector,
|
||||
quaternion128_mul_vector3,
|
||||
quaternion256_mul_vector3,
|
||||
};
|
||||
|
||||
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E) {
|
||||
return &v[0];
|
||||
}
|
||||
matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E) {
|
||||
return &m[0][0];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,786 @@
|
||||
package linalg
|
||||
|
||||
import "core:math"
|
||||
import "intrinsics"
|
||||
|
||||
|
||||
// Specific
|
||||
|
||||
Float :: f32;
|
||||
|
||||
FLOAT_EPSILON :: size_of(Float) == 4 ? 1e-7 : 1e-15;
|
||||
|
||||
Vector2 :: distinct [2]Float;
|
||||
Vector3 :: distinct [3]Float;
|
||||
Vector4 :: distinct [4]Float;
|
||||
|
||||
Matrix1x1 :: distinct [1][1]Float;
|
||||
Matrix1x2 :: distinct [1][2]Float;
|
||||
Matrix1x3 :: distinct [1][3]Float;
|
||||
Matrix1x4 :: distinct [1][4]Float;
|
||||
|
||||
Matrix2x1 :: distinct [2][1]Float;
|
||||
Matrix2x2 :: distinct [2][2]Float;
|
||||
Matrix2x3 :: distinct [2][3]Float;
|
||||
Matrix2x4 :: distinct [2][4]Float;
|
||||
|
||||
Matrix3x1 :: distinct [3][1]Float;
|
||||
Matrix3x2 :: distinct [3][2]Float;
|
||||
Matrix3x3 :: distinct [3][3]Float;
|
||||
Matrix3x4 :: distinct [3][4]Float;
|
||||
|
||||
Matrix4x1 :: distinct [4][1]Float;
|
||||
Matrix4x2 :: distinct [4][2]Float;
|
||||
Matrix4x3 :: distinct [4][3]Float;
|
||||
Matrix4x4 :: distinct [4][4]Float;
|
||||
|
||||
Matrix1 :: Matrix1x1;
|
||||
Matrix2 :: Matrix2x2;
|
||||
Matrix3 :: Matrix3x3;
|
||||
Matrix4 :: Matrix4x4;
|
||||
|
||||
Quaternion :: distinct (size_of(Float) == size_of(f32) ? quaternion128 : quaternion256);
|
||||
|
||||
MATRIX1_IDENTITY :: Matrix1{{1}};
|
||||
MATRIX2_IDENTITY :: Matrix2{{1, 0}, {0, 1}};
|
||||
MATRIX3_IDENTITY :: Matrix3{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}};
|
||||
MATRIX4_IDENTITY :: Matrix4{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}};
|
||||
|
||||
QUATERNION_IDENTITY :: Quaternion(1);
|
||||
|
||||
VECTOR3_X_AXIS :: Vector3{1, 0, 0};
|
||||
VECTOR3_Y_AXIS :: Vector3{0, 1, 0};
|
||||
VECTOR3_Z_AXIS :: Vector3{0, 0, 1};
|
||||
|
||||
|
||||
radians :: proc(degrees: Float) -> Float {
|
||||
return math.TAU * degrees / 360.0;
|
||||
}
|
||||
|
||||
degrees :: proc(radians: Float) -> Float {
|
||||
return 360.0 * radians / math.TAU;
|
||||
}
|
||||
|
||||
|
||||
vector2_orthogonal :: proc(v: Vector2) -> Vector2 {
|
||||
return {-v.y, v.x};
|
||||
}
|
||||
|
||||
vector3_orthogonal :: proc(v: Vector3) -> Vector3 {
|
||||
x := abs(v.x);
|
||||
y := abs(v.y);
|
||||
z := abs(v.z);
|
||||
|
||||
other: Vector3 = x < y ? (x < z ? {1, 0, 0} : {0, 0, 1}) : (y < z ? {0, 1, 0} : {0, 0, 1});
|
||||
|
||||
return normalize(cross(v, other));
|
||||
}
|
||||
|
||||
|
||||
vector4_srgb_to_linear :: proc(col: Vector4) -> Vector4 {
|
||||
r := math.pow(col.x, 2.2);
|
||||
g := math.pow(col.y, 2.2);
|
||||
b := math.pow(col.z, 2.2);
|
||||
a := col.w;
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
vector4_linear_to_srgb :: proc(col: Vector4) -> Vector4 {
|
||||
a :: 2.51;
|
||||
b :: 0.03;
|
||||
c :: 2.43;
|
||||
d :: 0.59;
|
||||
e :: 0.14;
|
||||
|
||||
x := col.x;
|
||||
y := col.y;
|
||||
z := col.z;
|
||||
|
||||
x = (x * (a * x + b)) / (x * (c * x + d) + e);
|
||||
y = (y * (a * y + b)) / (y * (c * y + d) + e);
|
||||
z = (z * (a * z + b)) / (z * (c * z + d) + e);
|
||||
|
||||
x = math.pow(clamp(x, 0, 1), 1.0 / 2.2);
|
||||
y = math.pow(clamp(y, 0, 1), 1.0 / 2.2);
|
||||
z = math.pow(clamp(z, 0, 1), 1.0 / 2.2);
|
||||
|
||||
return {x, y, z, col.w};
|
||||
}
|
||||
|
||||
vector4_hsl_to_rgb :: proc(h, s, l: Float, a: Float = 1) -> Vector4 {
|
||||
hue_to_rgb :: proc(p, q, t0: Float) -> Float {
|
||||
t := math.mod(t0, 1.0);
|
||||
switch {
|
||||
case t < 1.0/6.0: return p + (q - p) * 6.0 * t;
|
||||
case t < 1.0/2.0: return q;
|
||||
case t < 2.0/3.0: return p + (q - p) * 6.0 * (2.0/3.0 - t);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
r, g, b: Float;
|
||||
if s == 0 {
|
||||
r = l;
|
||||
g = l;
|
||||
b = l;
|
||||
} else {
|
||||
q := l < 0.5 ? l * (1+s) : l+s - l*s;
|
||||
p := 2*l - q;
|
||||
r = hue_to_rgb(p, q, h + 1.0/3.0);
|
||||
g = hue_to_rgb(p, q, h);
|
||||
b = hue_to_rgb(p, q, h - 1.0/3.0);
|
||||
}
|
||||
return {r, g, b, a};
|
||||
}
|
||||
|
||||
vector4_rgb_to_hsl :: proc(col: Vector4) -> Vector4 {
|
||||
r := col.x;
|
||||
g := col.y;
|
||||
b := col.z;
|
||||
a := col.w;
|
||||
v_min := min(r, g, b);
|
||||
v_max := max(r, g, b);
|
||||
h, s, l: Float;
|
||||
h = 0.0;
|
||||
s = 0.0;
|
||||
l = (v_min + v_max) * 0.5;
|
||||
|
||||
if v_max != v_min {
|
||||
d: = v_max - v_min;
|
||||
s = l > 0.5 ? d / (2.0 - v_max - v_min) : d / (v_max + v_min);
|
||||
switch {
|
||||
case v_max == r:
|
||||
h = (g - b) / d + (g < b ? 6.0 : 0.0);
|
||||
case v_max == g:
|
||||
h = (b - r) / d + 2.0;
|
||||
case v_max == b:
|
||||
h = (r - g) / d + 4.0;
|
||||
}
|
||||
|
||||
h *= 1.0/6.0;
|
||||
}
|
||||
|
||||
return {h, s, l, a};
|
||||
}
|
||||
|
||||
|
||||
|
||||
quaternion_angle_axis :: proc(angle_radians: Float, axis: Vector3) -> Quaternion {
|
||||
t := angle_radians*0.5;
|
||||
w := math.cos(t);
|
||||
v := normalize(axis) * math.sin(t);
|
||||
return quaternion(w, v.x, v.y, v.z);
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_euler_angles :: proc(roll, pitch, yaw: Float) -> Quaternion {
|
||||
x, y, z := roll, pitch, yaw;
|
||||
a, b, c := x, y, z;
|
||||
|
||||
ca, sa := math.cos(a*0.5), math.sin(a*0.5);
|
||||
cb, sb := math.cos(b*0.5), math.sin(b*0.5);
|
||||
cc, sc := math.cos(c*0.5), math.sin(c*0.5);
|
||||
|
||||
q: Quaternion;
|
||||
q.x = sa*cb*cc - ca*sb*sc;
|
||||
q.y = ca*sb*cc + sa*cb*sc;
|
||||
q.z = ca*cb*sc - sa*sb*cc;
|
||||
q.w = ca*cb*cc + sa*sb*sc;
|
||||
return q;
|
||||
}
|
||||
|
||||
euler_angles_from_quaternion :: proc(q: Quaternion) -> (roll, pitch, yaw: Float) {
|
||||
// roll, x-axis rotation
|
||||
sinr_cosp: Float = 2 * (q.w * q.x + q.y * q.z);
|
||||
cosr_cosp: Float = 1 - 2 * (q.x * q.x + q.y * q.y);
|
||||
roll = math.atan2(sinr_cosp, cosr_cosp);
|
||||
|
||||
// pitch, y-axis rotation
|
||||
sinp: Float = 2 * (q.w * q.y - q.z * q.x);
|
||||
if abs(sinp) >= 1 {
|
||||
pitch = math.copy_sign(math.TAU * 0.25, sinp);
|
||||
} else {
|
||||
pitch = math.asin(sinp);
|
||||
}
|
||||
|
||||
// yaw, z-axis rotation
|
||||
siny_cosp: Float = 2 * (q.w * q.z + q.x * q.y);
|
||||
cosy_cosp: Float = 1 - 2 * (q.y * q.y + q.z * q.z);
|
||||
yaw = math.atan2(siny_cosp, cosy_cosp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
quaternion_from_forward_and_up :: proc(forward, up: Vector3) -> Quaternion {
|
||||
f := normalize(forward);
|
||||
s := normalize(cross(f, up));
|
||||
u := cross(s, f);
|
||||
m := Matrix3{
|
||||
{+s.x, +u.x, -f.x},
|
||||
{+s.y, +u.y, -f.y},
|
||||
{+s.z, +u.z, -f.z},
|
||||
};
|
||||
|
||||
tr := trace(m);
|
||||
|
||||
q: Quaternion;
|
||||
|
||||
switch {
|
||||
case tr > 0:
|
||||
S := 2 * math.sqrt(1 + tr);
|
||||
q.w = 0.25 * S;
|
||||
q.x = (m[2][1] - m[1][2]) / S;
|
||||
q.y = (m[0][2] - m[2][0]) / S;
|
||||
q.z = (m[1][0] - m[0][1]) / S;
|
||||
case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
|
||||
S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2]);
|
||||
q.w = (m[2][1] - m[1][2]) / S;
|
||||
q.x = 0.25 * S;
|
||||
q.y = (m[0][1] + m[1][0]) / S;
|
||||
q.z = (m[0][2] + m[2][0]) / S;
|
||||
case m[1][1] > m[2][2]:
|
||||
S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2]);
|
||||
q.w = (m[0][2] - m[2][0]) / S;
|
||||
q.x = (m[0][1] + m[1][0]) / S;
|
||||
q.y = 0.25 * S;
|
||||
q.z = (m[1][2] + m[2][1]) / S;
|
||||
case:
|
||||
S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1]);
|
||||
q.w = (m[1][0] - m[0][1]) / S;
|
||||
q.x = (m[0][2] - m[2][0]) / S;
|
||||
q.y = (m[1][2] + m[2][1]) / S;
|
||||
q.z = 0.25 * S;
|
||||
}
|
||||
|
||||
return normalize(q);
|
||||
}
|
||||
|
||||
quaternion_look_at :: proc(eye, centre: Vector3, up: Vector3) -> Quaternion {
|
||||
return quaternion_from_forward_and_up(centre-eye, up);
|
||||
}
|
||||
|
||||
|
||||
quaternion_nlerp :: proc(a, b: Quaternion, t: Float) -> Quaternion {
|
||||
c := a + (b-a)*quaternion(t, 0, 0, 0);
|
||||
return normalize(c);
|
||||
}
|
||||
|
||||
|
||||
quaternion_slerp :: proc(x, y: Quaternion, t: Float) -> Quaternion {
|
||||
|
||||
a, b := x, y;
|
||||
cos_angle := dot(a, b);
|
||||
if cos_angle < 0 {
|
||||
b = -b;
|
||||
cos_angle = -cos_angle;
|
||||
}
|
||||
if cos_angle > 1 - FLOAT_EPSILON {
|
||||
return a + (b-a)*quaternion(t, 0, 0, 0);
|
||||
}
|
||||
|
||||
angle := math.acos(cos_angle);
|
||||
sin_angle := math.sin(angle);
|
||||
factor_a, factor_b: Quaternion;
|
||||
factor_a = quaternion(math.sin((1-t) * angle) / sin_angle, 0, 0, 0);
|
||||
factor_b = quaternion(math.sin(t * angle) / sin_angle, 0, 0, 0);
|
||||
|
||||
return factor_a * a + factor_b * b;
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_matrix4 :: proc(m: Matrix4) -> Quaternion {
|
||||
four_x_squared_minus_1, four_y_squared_minus_1,
|
||||
four_z_squared_minus_1, four_w_squared_minus_1,
|
||||
four_biggest_squared_minus_1: Float;
|
||||
|
||||
/* xyzw */
|
||||
/* 0123 */
|
||||
biggest_index := 3;
|
||||
biggest_value, mult: Float;
|
||||
|
||||
four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2];
|
||||
four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2];
|
||||
four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1];
|
||||
four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2];
|
||||
|
||||
four_biggest_squared_minus_1 = four_w_squared_minus_1;
|
||||
if four_x_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_x_squared_minus_1;
|
||||
biggest_index = 0;
|
||||
}
|
||||
if four_y_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_y_squared_minus_1;
|
||||
biggest_index = 1;
|
||||
}
|
||||
if four_z_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_z_squared_minus_1;
|
||||
biggest_index = 2;
|
||||
}
|
||||
|
||||
biggest_value = math.sqrt(four_biggest_squared_minus_1 + 1) * 0.5;
|
||||
mult = 0.25 / biggest_value;
|
||||
|
||||
|
||||
switch biggest_index {
|
||||
case 0:
|
||||
return quaternion(
|
||||
biggest_value,
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
);
|
||||
case 1:
|
||||
return quaternion(
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
biggest_value,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
);
|
||||
case 2:
|
||||
return quaternion(
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
biggest_value,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
);
|
||||
case 3:
|
||||
return quaternion(
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
biggest_value,
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
quaternion_from_matrix3 :: proc(m: Matrix3) -> Quaternion {
|
||||
four_x_squared_minus_1, four_y_squared_minus_1,
|
||||
four_z_squared_minus_1, four_w_squared_minus_1,
|
||||
four_biggest_squared_minus_1: Float;
|
||||
|
||||
/* xyzw */
|
||||
/* 0123 */
|
||||
biggest_index := 3;
|
||||
biggest_value, mult: Float;
|
||||
|
||||
four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2];
|
||||
four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2];
|
||||
four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1];
|
||||
four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2];
|
||||
|
||||
four_biggest_squared_minus_1 = four_w_squared_minus_1;
|
||||
if four_x_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_x_squared_minus_1;
|
||||
biggest_index = 0;
|
||||
}
|
||||
if four_y_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_y_squared_minus_1;
|
||||
biggest_index = 1;
|
||||
}
|
||||
if four_z_squared_minus_1 > four_biggest_squared_minus_1 {
|
||||
four_biggest_squared_minus_1 = four_z_squared_minus_1;
|
||||
biggest_index = 2;
|
||||
}
|
||||
|
||||
biggest_value = math.sqrt(four_biggest_squared_minus_1 + 1) * 0.5;
|
||||
mult = 0.25 / biggest_value;
|
||||
|
||||
|
||||
switch biggest_index {
|
||||
case 0:
|
||||
return quaternion(
|
||||
biggest_value,
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
);
|
||||
case 1:
|
||||
return quaternion(
|
||||
(m[0][1] + m[1][0]) * mult,
|
||||
biggest_value,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
);
|
||||
case 2:
|
||||
return quaternion(
|
||||
(m[2][0] + m[0][2]) * mult,
|
||||
(m[1][2] + m[2][1]) * mult,
|
||||
biggest_value,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
);
|
||||
case 3:
|
||||
return quaternion(
|
||||
(m[1][2] - m[2][1]) * mult,
|
||||
(m[2][0] - m[0][2]) * mult,
|
||||
(m[0][1] - m[1][0]) * mult,
|
||||
biggest_value,
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
quaternion_between_two_vector3 :: proc(from, to: Vector3) -> Quaternion {
|
||||
x := normalize(from);
|
||||
y := normalize(to);
|
||||
|
||||
cos_theta := dot(x, y);
|
||||
if abs(cos_theta + 1) < 2*FLOAT_EPSILON {
|
||||
v := vector3_orthogonal(x);
|
||||
return quaternion(0, v.x, v.y, v.z);
|
||||
}
|
||||
v := cross(x, y);
|
||||
w := cos_theta + 1;
|
||||
return Quaternion(normalize(quaternion(w, v.x, v.y, v.z)));
|
||||
}
|
||||
|
||||
|
||||
matrix2_inverse_transpose :: proc(m: Matrix2) -> Matrix2 {
|
||||
c: Matrix2;
|
||||
d := m[0][0]*m[1][1] - m[1][0]*m[0][1];
|
||||
id := 1.0/d;
|
||||
c[0][0] = +m[1][1] * id;
|
||||
c[0][1] = -m[0][1] * id;
|
||||
c[1][0] = -m[1][0] * id;
|
||||
c[1][1] = +m[0][0] * id;
|
||||
return c;
|
||||
}
|
||||
matrix2_determinant :: proc(m: Matrix2) -> Float {
|
||||
return m[0][0]*m[1][1] - m[1][0]*m[0][1];
|
||||
}
|
||||
matrix2_inverse :: proc(m: Matrix2) -> Matrix2 {
|
||||
c: Matrix2;
|
||||
d := m[0][0]*m[1][1] - m[1][0]*m[0][1];
|
||||
id := 1.0/d;
|
||||
c[0][0] = +m[1][1] * id;
|
||||
c[1][0] = -m[0][1] * id;
|
||||
c[0][1] = -m[1][0] * id;
|
||||
c[1][1] = +m[0][0] * id;
|
||||
return c;
|
||||
}
|
||||
|
||||
matrix2_adjoint :: proc(m: Matrix2) -> Matrix2 {
|
||||
c: Matrix2;
|
||||
c[0][0] = +m[1][1];
|
||||
c[0][1] = -m[1][0];
|
||||
c[1][0] = -m[0][1];
|
||||
c[1][1] = +m[0][0];
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
matrix3_from_quaternion :: proc(q: Quaternion) -> Matrix3 {
|
||||
xx := q.x * q.x;
|
||||
xy := q.x * q.y;
|
||||
xz := q.x * q.z;
|
||||
xw := q.x * q.w;
|
||||
yy := q.y * q.y;
|
||||
yz := q.y * q.z;
|
||||
yw := q.y * q.w;
|
||||
zz := q.z * q.z;
|
||||
zw := q.z * q.w;
|
||||
|
||||
m: Matrix3;
|
||||
m[0][0] = 1 - 2 * (yy + zz);
|
||||
m[1][0] = 2 * (xy - zw);
|
||||
m[2][0] = 2 * (xz + yw);
|
||||
|
||||
m[0][1] = 2 * (xy + zw);
|
||||
m[1][1] = 1 - 2 * (xx + zz);
|
||||
m[2][1] = 2 * (yz - xw);
|
||||
|
||||
m[0][2] = 2 * (xz - yw);
|
||||
m[1][2] = 2 * (yz + xw);
|
||||
m[2][2] = 1 - 2 * (xx + yy);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix3_inverse :: proc(m: Matrix3) -> Matrix3 {
|
||||
return transpose(matrix3_inverse_transpose(m));
|
||||
}
|
||||
|
||||
|
||||
matrix3_determinant :: proc(m: Matrix3) -> Float {
|
||||
a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2]);
|
||||
b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2]);
|
||||
c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2]);
|
||||
return a + b + c;
|
||||
}
|
||||
|
||||
matrix3_adjoint :: proc(m: Matrix3) -> Matrix3 {
|
||||
adjoint: Matrix3;
|
||||
adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1]);
|
||||
adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1]);
|
||||
adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1]);
|
||||
adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]);
|
||||
adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0]);
|
||||
adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]);
|
||||
adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0]);
|
||||
adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]);
|
||||
adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0]);
|
||||
return adjoint;
|
||||
}
|
||||
|
||||
matrix3_inverse_transpose :: proc(m: Matrix3) -> Matrix3 {
|
||||
inverse_transpose: Matrix3;
|
||||
|
||||
adjoint := matrix3_adjoint(m);
|
||||
determinant := matrix3_determinant(m);
|
||||
inv_determinant := 1.0 / determinant;
|
||||
for i in 0..<3 {
|
||||
for j in 0..<3 {
|
||||
inverse_transpose[i][j] = adjoint[i][j] * inv_determinant;
|
||||
}
|
||||
}
|
||||
return inverse_transpose;
|
||||
}
|
||||
|
||||
|
||||
matrix3_scale :: proc(s: Vector3) -> Matrix3 {
|
||||
m: Matrix3;
|
||||
m[0][0] = s[0];
|
||||
m[1][1] = s[1];
|
||||
m[2][2] = s[2];
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix3_rotate :: proc(angle_radians: Float, v: Vector3) -> Matrix3 {
|
||||
c := math.cos(angle_radians);
|
||||
s := math.sin(angle_radians);
|
||||
|
||||
a := normalize(v);
|
||||
t := a * (1-c);
|
||||
|
||||
rot: Matrix3 = ---;
|
||||
|
||||
rot[0][0] = c + t[0]*a[0];
|
||||
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
|
||||
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
|
||||
|
||||
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
|
||||
rot[1][1] = c + t[1]*a[1];
|
||||
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
|
||||
|
||||
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
|
||||
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
|
||||
rot[2][2] = c + t[2]*a[2];
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
matrix3_look_at :: proc(eye, centre, up: Vector3) -> Matrix3 {
|
||||
f := normalize(centre - eye);
|
||||
s := normalize(cross(f, up));
|
||||
u := cross(s, f);
|
||||
return Matrix3{
|
||||
{+s.x, +u.x, -f.x},
|
||||
{+s.y, +u.y, -f.y},
|
||||
{+s.z, +u.z, -f.z},
|
||||
};
|
||||
}
|
||||
|
||||
matrix4_from_quaternion :: proc(q: Quaternion) -> Matrix4 {
|
||||
m := identity(Matrix4);
|
||||
|
||||
xx := q.x * q.x;
|
||||
xy := q.x * q.y;
|
||||
xz := q.x * q.z;
|
||||
xw := q.x * q.w;
|
||||
yy := q.y * q.y;
|
||||
yz := q.y * q.z;
|
||||
yw := q.y * q.w;
|
||||
zz := q.z * q.z;
|
||||
zw := q.z * q.w;
|
||||
|
||||
m[0][0] = 1 - 2 * (yy + zz);
|
||||
m[1][0] = 2 * (xy - zw);
|
||||
m[2][0] = 2 * (xz + yw);
|
||||
|
||||
m[0][1] = 2 * (xy + zw);
|
||||
m[1][1] = 1 - 2 * (xx + zz);
|
||||
m[2][1] = 2 * (yz - xw);
|
||||
|
||||
m[0][2] = 2 * (xz - yw);
|
||||
m[1][2] = 2 * (yz + xw);
|
||||
m[2][2] = 1 - 2 * (xx + yy);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix4_from_trs :: proc(t: Vector3, r: Quaternion, s: Vector3) -> Matrix4 {
|
||||
translation := matrix4_translate(t);
|
||||
rotation := matrix4_from_quaternion(r);
|
||||
scale := matrix4_scale(s);
|
||||
return mul(translation, mul(rotation, scale));
|
||||
}
|
||||
|
||||
|
||||
matrix4_inverse :: proc(m: Matrix4) -> Matrix4 {
|
||||
return transpose(matrix4_inverse_transpose(m));
|
||||
}
|
||||
|
||||
|
||||
matrix4_minor :: proc(m: Matrix4, c, r: int) -> Float {
|
||||
cut_down: Matrix3;
|
||||
for i in 0..<3 {
|
||||
col := i < c ? i : i+1;
|
||||
for j in 0..<3 {
|
||||
row := j < r ? j : j+1;
|
||||
cut_down[i][j] = m[col][row];
|
||||
}
|
||||
}
|
||||
return matrix3_determinant(cut_down);
|
||||
}
|
||||
|
||||
matrix4_cofactor :: proc(m: Matrix4, c, r: int) -> Float {
|
||||
sign, minor: Float;
|
||||
sign = (c + r) % 2 == 0 ? 1 : -1;
|
||||
minor = matrix4_minor(m, c, r);
|
||||
return sign * minor;
|
||||
}
|
||||
|
||||
matrix4_adjoint :: proc(m: Matrix4) -> Matrix4 {
|
||||
adjoint: Matrix4;
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
adjoint[i][j] = matrix4_cofactor(m, i, j);
|
||||
}
|
||||
}
|
||||
return adjoint;
|
||||
}
|
||||
|
||||
matrix4_determinant :: proc(m: Matrix4) -> Float {
|
||||
adjoint := matrix4_adjoint(m);
|
||||
determinant: Float = 0;
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0];
|
||||
}
|
||||
return determinant;
|
||||
|
||||
}
|
||||
|
||||
matrix4_inverse_transpose :: proc(m: Matrix4) -> Matrix4 {
|
||||
adjoint := matrix4_adjoint(m);
|
||||
determinant: Float = 0;
|
||||
for i in 0..<4 {
|
||||
determinant += m[i][0] * adjoint[i][0];
|
||||
}
|
||||
inv_determinant := 1.0 / determinant;
|
||||
inverse_transpose: Matrix4;
|
||||
for i in 0..<4 {
|
||||
for j in 0..<4 {
|
||||
inverse_transpose[i][j] = adjoint[i][j] * inv_determinant;
|
||||
}
|
||||
}
|
||||
return inverse_transpose;
|
||||
}
|
||||
|
||||
matrix4_translate :: proc(v: Vector3) -> Matrix4 {
|
||||
m := identity(Matrix4);
|
||||
m[3][0] = v[0];
|
||||
m[3][1] = v[1];
|
||||
m[3][2] = v[2];
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
matrix4_rotate :: proc(angle_radians: Float, v: Vector3) -> Matrix4 {
|
||||
c := math.cos(angle_radians);
|
||||
s := math.sin(angle_radians);
|
||||
|
||||
a := normalize(v);
|
||||
t := a * (1-c);
|
||||
|
||||
rot := identity(Matrix4);
|
||||
|
||||
rot[0][0] = c + t[0]*a[0];
|
||||
rot[0][1] = 0 + t[0]*a[1] + s*a[2];
|
||||
rot[0][2] = 0 + t[0]*a[2] - s*a[1];
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t[1]*a[0] - s*a[2];
|
||||
rot[1][1] = c + t[1]*a[1];
|
||||
rot[1][2] = 0 + t[1]*a[2] + s*a[0];
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t[2]*a[0] + s*a[1];
|
||||
rot[2][1] = 0 + t[2]*a[1] - s*a[0];
|
||||
rot[2][2] = c + t[2]*a[2];
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot;
|
||||
}
|
||||
|
||||
matrix4_scale :: proc(v: Vector3) -> Matrix4 {
|
||||
m: Matrix4;
|
||||
m[0][0] = v[0];
|
||||
m[1][1] = v[1];
|
||||
m[2][2] = v[2];
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
matrix4_look_at :: proc(eye, centre, up: Vector3) -> Matrix4 {
|
||||
f := normalize(centre - eye);
|
||||
s := normalize(cross(f, up));
|
||||
u := cross(s, f);
|
||||
return Matrix4{
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), +dot(f, eye), 1},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
matrix4_perspective :: proc(fovy, aspect, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
|
||||
tan_half_fovy := math.tan(0.5 * fovy);
|
||||
m[0][0] = 1 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1 / (tan_half_fovy);
|
||||
m[2][2] = +(far + near) / (far - near);
|
||||
m[2][3] = +1;
|
||||
m[3][2] = -2*far*near / (far - near);
|
||||
|
||||
if flip_z_axis {
|
||||
m[2] = -m[2];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix_ortho3d :: proc(left, right, bottom, top, near, far: Float, flip_z_axis := true) -> (m: Matrix4) {
|
||||
m[0][0] = +2 / (right - left);
|
||||
m[1][1] = +2 / (top - bottom);
|
||||
m[2][2] = +2 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far- near);
|
||||
m[3][3] = 1;
|
||||
|
||||
if flip_z_axis {
|
||||
m[2] = -m[2];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
matrix4_infinite_perspective :: proc(fovy, aspect, near: Float, flip_z_axis := true) -> (m: Matrix4) {
|
||||
tan_half_fovy := math.tan(0.5 * fovy);
|
||||
m[0][0] = 1 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1 / (tan_half_fovy);
|
||||
m[2][2] = +1;
|
||||
m[2][3] = +1;
|
||||
m[3][2] = -2*near;
|
||||
|
||||
if flip_z_axis {
|
||||
m[2] = -m[2];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
+579
-359
File diff suppressed because it is too large
Load Diff
@@ -128,8 +128,8 @@ norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 {
|
||||
|
||||
if i == 0 {
|
||||
for {
|
||||
x = -math.log(float64(r)) * (1.0/ rn);
|
||||
y := -math.log(float64(r));
|
||||
x = -math.ln(float64(r)) * (1.0/ rn);
|
||||
y := -math.ln(float64(r));
|
||||
if y+y >= x*x {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -47,8 +47,17 @@ uint64 :: proc(r: ^Rand = global_rand_ptr) -> u64 {
|
||||
return (a<<32) | b;
|
||||
}
|
||||
|
||||
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
uint128 :: proc(r: ^Rand = global_rand_ptr) -> u128 {
|
||||
a := u128(_random(r));
|
||||
b := u128(_random(r));
|
||||
c := u128(_random(r));
|
||||
d := u128(_random(r));
|
||||
return (a<<96) | (b<<64) | (c<<32) | d;
|
||||
}
|
||||
|
||||
int31 :: proc(r: ^Rand = global_rand_ptr) -> i32 { return i32(uint32(r) << 1 >> 1); }
|
||||
int63 :: proc(r: ^Rand = global_rand_ptr) -> i64 { return i64(uint64(r) << 1 >> 1); }
|
||||
int127 :: proc(r: ^Rand = global_rand_ptr) -> i128 { return i128(uint128(r) << 1 >> 1); }
|
||||
|
||||
int31_max :: proc(n: i32, r: ^Rand = global_rand_ptr) -> i32 {
|
||||
if n <= 0 do panic("Invalid argument to int31_max");
|
||||
@@ -76,6 +85,19 @@ int63_max :: proc(n: i64, r: ^Rand = global_rand_ptr) -> i64 {
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int127_max :: proc(n: i128, r: ^Rand = global_rand_ptr) -> i128 {
|
||||
if n <= 0 do panic("Invalid argument to int63_max");
|
||||
if n&(n-1) == 0 {
|
||||
return int127(r) & (n-1);
|
||||
}
|
||||
max := i128((1<<63) - 1 - (1<<63)&u128(n));
|
||||
v := int127(r);
|
||||
for v > max {
|
||||
v = int127(r);
|
||||
}
|
||||
return v % n;
|
||||
}
|
||||
|
||||
int_max :: proc(n: int, r: ^Rand = global_rand_ptr) -> int {
|
||||
if n <= 0 do panic("Invalid argument to int_max");
|
||||
when size_of(int) == 4 {
|
||||
|
||||
+26
-9
@@ -2,26 +2,33 @@ package mem
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
Allocator_Mode :: runtime.Allocator_Mode;
|
||||
/*
|
||||
Allocator_Mode :: enum byte {
|
||||
Alloc,
|
||||
Free,
|
||||
Free_All,
|
||||
Resize,
|
||||
}
|
||||
*/
|
||||
|
||||
Allocator_Proc :: runtime.Allocator_Proc;
|
||||
/*
|
||||
Allocator_Proc :: #type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, location := #caller_location) -> rawptr;
|
||||
*/
|
||||
|
||||
Allocator :: runtime.Allocator;
|
||||
/*
|
||||
Allocator :: struct {
|
||||
procedure: Allocator_Proc,
|
||||
data: rawptr,
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
@@ -84,7 +91,10 @@ delete :: proc{
|
||||
|
||||
|
||||
new :: inline proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), align_of(T), allocator, loc));
|
||||
return new_aligned(T, align_of(T), allocator, loc);
|
||||
}
|
||||
new_aligned :: inline proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> ^T {
|
||||
ptr := (^T)(alloc(size_of(T), alignment, allocator, loc));
|
||||
if ptr != nil do ptr^ = T{};
|
||||
return ptr;
|
||||
}
|
||||
@@ -95,9 +105,13 @@ new_clone :: inline proc(data: $T, allocator := context.allocator, loc := #calle
|
||||
}
|
||||
|
||||
|
||||
make_slice :: proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_slice :: inline proc($T: typeid/[]$E, auto_cast len: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
return make_aligned(T, len, align_of(E), allocator, loc);
|
||||
}
|
||||
make_aligned :: proc($T: typeid/[]$E, auto_cast len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_slice_error_loc(loc, len);
|
||||
data := alloc(size_of(E)*len, align_of(E), allocator, loc);
|
||||
data := alloc(size_of(E)*len, alignment, allocator, loc);
|
||||
if data == nil do return nil;
|
||||
s := Raw_Slice{data, len};
|
||||
return transmute(T)s;
|
||||
}
|
||||
@@ -111,6 +125,9 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, auto_cast len: int, a
|
||||
runtime.make_dynamic_array_error_loc(loc, len, cap);
|
||||
data := alloc(size_of(E)*cap, align_of(E), allocator, loc);
|
||||
s := Raw_Dynamic_Array{data, len, cap, allocator};
|
||||
if data == nil {
|
||||
s.len, s.cap = 0, 0;
|
||||
}
|
||||
return transmute(T)s;
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, auto_cast cap: int = 16, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
|
||||
+37
-21
@@ -1,7 +1,5 @@
|
||||
package mem
|
||||
|
||||
|
||||
|
||||
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
@@ -57,7 +55,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[len(arena.data)];
|
||||
#no_bounds_check end := &arena.data[arena.offset];
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment));
|
||||
arena.offset += total_size;
|
||||
@@ -101,6 +99,7 @@ Scratch_Allocator :: struct {
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
default_to_default_allocator: bool,
|
||||
}
|
||||
|
||||
scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
@@ -110,6 +109,18 @@ scratch_allocator_init :: proc(scratch: ^Scratch_Allocator, data: []byte, backup
|
||||
scratch.backup_allocator = backup_allocator;
|
||||
}
|
||||
|
||||
scratch_allocator_destroy :: proc(using scratch: ^Scratch_Allocator) {
|
||||
if scratch == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in leaked_allocations {
|
||||
free(ptr, backup_allocator);
|
||||
}
|
||||
delete(leaked_allocations);
|
||||
delete(data, backup_allocator);
|
||||
scratch^ = {};
|
||||
}
|
||||
|
||||
scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
@@ -118,11 +129,15 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
if scratch.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
panic("cyclic initialization of the scratch allocator with itself");
|
||||
}
|
||||
scratch_allocator_init(scratch, make([]byte, 1<<22));
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case Allocator_Mode.Alloc:
|
||||
case .Alloc:
|
||||
switch {
|
||||
case scratch.curr_offset+size <= len(scratch.data):
|
||||
offset := align_forward_uintptr(uintptr(scratch.curr_offset), uintptr(alignment));
|
||||
@@ -154,7 +169,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
return ptr;
|
||||
|
||||
case Allocator_Mode.Free:
|
||||
case .Free:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
full_size := scratch.curr_offset - scratch.prev_offset;
|
||||
@@ -164,7 +179,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
// NOTE(bill): It's scratch memory, don't worry about freeing
|
||||
|
||||
case Allocator_Mode.Free_All:
|
||||
case .Free_All:
|
||||
scratch.curr_offset = 0;
|
||||
scratch.prev_offset = 0;
|
||||
for ptr in scratch.leaked_allocations {
|
||||
@@ -172,7 +187,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
clear(&scratch.leaked_allocations);
|
||||
|
||||
case Allocator_Mode.Resize:
|
||||
case .Resize:
|
||||
last_ptr := rawptr(&scratch.data[scratch.prev_offset]);
|
||||
if old_memory == last_ptr && len(scratch.data)-scratch.prev_offset >= size {
|
||||
scratch.curr_offset = scratch.prev_offset+size;
|
||||
@@ -377,7 +392,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil;
|
||||
}
|
||||
|
||||
alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2);
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> rawptr {
|
||||
curr_addr := uintptr(&s.data[0]) + uintptr(s.offset);
|
||||
@@ -400,7 +415,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, alignment);
|
||||
return raw_alloc(s, size, align);
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil;
|
||||
@@ -429,7 +444,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, alignment);
|
||||
return raw_alloc(s, size, align);
|
||||
}
|
||||
if size == 0 {
|
||||
return nil;
|
||||
@@ -452,7 +467,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
ptr := raw_alloc(s, size, alignment);
|
||||
ptr := raw_alloc(s, size, align);
|
||||
copy(ptr, old_memory, min(old_size, size));
|
||||
return ptr;
|
||||
}
|
||||
@@ -493,13 +508,13 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
pool := (^Dynamic_Pool)(allocator_data);
|
||||
|
||||
switch mode {
|
||||
case Allocator_Mode.Alloc:
|
||||
case .Alloc:
|
||||
return dynamic_pool_alloc(pool, size);
|
||||
case Allocator_Mode.Free:
|
||||
case .Free:
|
||||
panic("Allocator_Mode.Free is not supported for a pool");
|
||||
case Allocator_Mode.Free_All:
|
||||
case .Free_All:
|
||||
dynamic_pool_free_all(pool);
|
||||
case Allocator_Mode.Resize:
|
||||
case .Resize:
|
||||
panic("Allocator_Mode.Resize is not supported for a pool");
|
||||
if old_size >= size {
|
||||
return old_memory;
|
||||
@@ -568,9 +583,10 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
}
|
||||
|
||||
|
||||
extra := alignment - (bytes % alignment);
|
||||
bytes += extra;
|
||||
if bytes >= out_band_size {
|
||||
n := bytes;
|
||||
extra := alignment - (n % alignment);
|
||||
n += extra;
|
||||
if n >= out_band_size {
|
||||
assert(block_allocator.procedure != nil);
|
||||
memory := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
@@ -581,7 +597,7 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
return memory;
|
||||
}
|
||||
|
||||
if bytes_left < bytes {
|
||||
if bytes_left < n {
|
||||
cycle_new_block(pool);
|
||||
if current_block == nil {
|
||||
return nil;
|
||||
@@ -589,8 +605,8 @@ dynamic_pool_alloc :: proc(using pool: ^Dynamic_Pool, bytes: int) -> rawptr {
|
||||
}
|
||||
|
||||
memory := current_pos;
|
||||
current_pos = ptr_offset((^byte)(current_pos), bytes);
|
||||
bytes_left -= bytes;
|
||||
current_pos = ptr_offset((^byte)(current_pos), n);
|
||||
bytes_left -= n;
|
||||
return memory;
|
||||
}
|
||||
|
||||
|
||||
+40
-45
@@ -1,5 +1,7 @@
|
||||
package mem
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
foreign _ {
|
||||
@(link_name = "llvm.bswap.i16") swap16 :: proc(b: u16) -> u16 ---;
|
||||
@(link_name = "llvm.bswap.i32") swap32 :: proc(b: u32) -> u32 ---;
|
||||
@@ -10,8 +12,6 @@ swap :: proc{swap16, swap32, swap64};
|
||||
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
if data == nil do return nil;
|
||||
if len < 0 do return data;
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memset.p0i8.i64")
|
||||
@@ -21,7 +21,8 @@ set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
llvm_memset :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memset(data, byte(value), len, 1, false);
|
||||
|
||||
llvm_memset(data, value, len, 1, false);
|
||||
return data;
|
||||
}
|
||||
zero :: inline proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
@@ -38,34 +39,10 @@ zero_slice :: proc "contextless" (data: $T/[]$E) {
|
||||
|
||||
|
||||
copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memmove(dst, src, len, 1, false);
|
||||
return dst;
|
||||
return runtime.mem_copy(dst, src, len);
|
||||
}
|
||||
copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memcpy(dst, src, len, 1, false);
|
||||
return dst;
|
||||
return runtime.mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: inline proc "contextless" (a, b: []byte) -> int {
|
||||
return compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
@@ -129,6 +106,16 @@ slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte {
|
||||
return transmute([]byte)s;
|
||||
}
|
||||
|
||||
slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
|
||||
when size_of(A) == 0 || size_of(B) == 0 {
|
||||
return nil;
|
||||
} else {
|
||||
s := transmute(Raw_Slice)slice;
|
||||
s.len = (len(slice) * size_of(B)) / size_of(A);
|
||||
return transmute(T)s;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
|
||||
s := transmute(Raw_Slice)backing;
|
||||
@@ -163,27 +150,19 @@ is_power_of_two :: inline proc(x: uintptr) -> bool {
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uintptr(align);
|
||||
p := uintptr(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 do p += a - modulo;
|
||||
return rawptr(p);
|
||||
align_forward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_forward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := uintptr(align);
|
||||
p := uintptr(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 do p += a - modulo;
|
||||
return uintptr(p);
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
align_forward_int :: inline proc(ptr, align: int) -> int {
|
||||
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
@@ -191,6 +170,24 @@ align_forward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
align_backward :: inline proc(ptr: rawptr, align: uintptr) -> rawptr {
|
||||
return rawptr(align_backward_uintptr(uintptr(ptr), align));
|
||||
}
|
||||
|
||||
align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
ptr := rawptr(ptr - align);
|
||||
return uintptr(align_forward(ptr, align));
|
||||
}
|
||||
|
||||
align_backward_int :: inline proc(ptr, align: int) -> int {
|
||||
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
align_backward_uint :: inline proc(ptr, align: uint) -> uint {
|
||||
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)));
|
||||
}
|
||||
|
||||
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
|
||||
context.allocator = a;
|
||||
return context;
|
||||
@@ -238,5 +235,3 @@ calc_padding_with_header :: proc(ptr: uintptr, align: uintptr, header_size: int)
|
||||
|
||||
return int(padding);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+9
-3
@@ -31,19 +31,25 @@ Raw_Map :: struct {
|
||||
entries: Raw_Dynamic_Array,
|
||||
}
|
||||
|
||||
Raw_Complex64 :: struct {real, imag: f32};
|
||||
Raw_Complex128 :: struct {real, imag: f64};
|
||||
Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32};
|
||||
Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64};
|
||||
Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32};
|
||||
Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64};
|
||||
|
||||
make_any :: inline proc(data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id};
|
||||
}
|
||||
|
||||
raw_string_data :: inline proc(s: $T/string) -> ^byte {
|
||||
return (^Raw_String)(&s).data;
|
||||
return (transmute(Raw_String)s).data;
|
||||
}
|
||||
raw_slice_data :: inline proc(a: $T/[]$E) -> ^E {
|
||||
return cast(^E)(^Raw_Slice)(&a).data;
|
||||
return cast(^E)(transmute(Raw_Slice)a).data;
|
||||
}
|
||||
raw_dynamic_array_data :: inline proc(a: $T/[dynamic]$E) -> ^E {
|
||||
return cast(^E)(^Raw_Dynamic_Array)(&a).data;
|
||||
return cast(^E)(transmute(Raw_Dynamic_Array)a).data;
|
||||
}
|
||||
|
||||
raw_data :: proc{raw_string_data, raw_slice_data, raw_dynamic_array_data};
|
||||
|
||||
+125
-112
@@ -1,11 +1,10 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:odin/token"
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
Proc_Tag :: enum {
|
||||
Bounds_Check,
|
||||
No_Bounds_Check,
|
||||
Require_Results,
|
||||
}
|
||||
Proc_Tags :: distinct bit_set[Proc_Tag; u32];
|
||||
|
||||
@@ -34,12 +33,12 @@ Node_State_Flags :: distinct bit_set[Node_State_Flag];
|
||||
|
||||
|
||||
Comment_Group :: struct {
|
||||
list: []token.Token,
|
||||
list: []tokenizer.Token,
|
||||
}
|
||||
|
||||
Node :: struct {
|
||||
pos: token.Pos,
|
||||
end: token.Pos,
|
||||
pos: tokenizer.Pos,
|
||||
end: tokenizer.Pos,
|
||||
derived: any,
|
||||
state_flags: Node_State_Flags,
|
||||
}
|
||||
@@ -68,29 +67,29 @@ Ident :: struct {
|
||||
|
||||
Implicit :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Token,
|
||||
tok: tokenizer.Token,
|
||||
}
|
||||
|
||||
|
||||
Undef :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Kind,
|
||||
tok: tokenizer.Token_Kind,
|
||||
}
|
||||
|
||||
Basic_Lit :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Token,
|
||||
tok: tokenizer.Token,
|
||||
}
|
||||
|
||||
Basic_Directive :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Token,
|
||||
tok: tokenizer.Token,
|
||||
name: string,
|
||||
}
|
||||
|
||||
Ellipsis :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Kind,
|
||||
tok: tokenizer.Token_Kind,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
@@ -100,42 +99,44 @@ Proc_Lit :: struct {
|
||||
body: ^Stmt,
|
||||
tags: Proc_Tags,
|
||||
inlining: Proc_Inlining,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
}
|
||||
|
||||
Comp_Lit :: struct {
|
||||
using node: Expr,
|
||||
type: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
elems: []^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
|
||||
Tag_Expr :: struct {
|
||||
using node: Expr,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
name: string,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Unary_Expr :: struct {
|
||||
using node: Expr,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Binary_Expr :: struct {
|
||||
using node: Expr,
|
||||
left: ^Expr,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
right: ^Expr,
|
||||
}
|
||||
|
||||
Paren_Expr :: struct {
|
||||
using node: Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Selector_Expr :: struct {
|
||||
@@ -152,74 +153,74 @@ Implicit_Selector_Expr :: struct {
|
||||
Index_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
index: ^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Deref_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
}
|
||||
|
||||
Slice_Expr :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
low: ^Expr,
|
||||
interval: token.Token,
|
||||
interval: tokenizer.Token,
|
||||
high: ^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Call_Expr :: struct {
|
||||
using node: Expr,
|
||||
inlining: Proc_Inlining,
|
||||
expr: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
args: []^Expr,
|
||||
ellipsis: token.Token,
|
||||
close: token.Pos,
|
||||
ellipsis: tokenizer.Token,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Field_Value :: struct {
|
||||
using node: Expr,
|
||||
field: ^Expr,
|
||||
sep: token.Pos,
|
||||
sep: tokenizer.Pos,
|
||||
value: ^Expr,
|
||||
}
|
||||
|
||||
Ternary_Expr :: struct {
|
||||
using node: Expr,
|
||||
cond: ^Expr,
|
||||
op1: token.Token,
|
||||
op1: tokenizer.Token,
|
||||
x: ^Expr,
|
||||
op2: token.Token,
|
||||
op2: tokenizer.Token,
|
||||
y: ^Expr,
|
||||
}
|
||||
|
||||
Type_Assertion :: struct {
|
||||
using node: Expr,
|
||||
expr: ^Expr,
|
||||
dot: token.Pos,
|
||||
open: token.Pos,
|
||||
dot: tokenizer.Pos,
|
||||
open: tokenizer.Pos,
|
||||
type: ^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Type_Cast :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Token,
|
||||
open: token.Pos,
|
||||
tok: tokenizer.Token,
|
||||
open: tokenizer.Pos,
|
||||
type: ^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
Auto_Cast :: struct {
|
||||
using node: Expr,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
expr: ^Expr,
|
||||
}
|
||||
|
||||
@@ -234,7 +235,7 @@ Bad_Stmt :: struct {
|
||||
|
||||
Empty_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
semicolon: token.Pos, // Position of the following ';'
|
||||
semicolon: tokenizer.Pos, // Position of the following ';'
|
||||
}
|
||||
|
||||
Expr_Stmt :: struct {
|
||||
@@ -244,7 +245,7 @@ Expr_Stmt :: struct {
|
||||
|
||||
Tag_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
name: string,
|
||||
stmt: ^Stmt,
|
||||
}
|
||||
@@ -252,7 +253,7 @@ Tag_Stmt :: struct {
|
||||
Assign_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
lhs: []^Expr,
|
||||
op: token.Token,
|
||||
op: tokenizer.Token,
|
||||
rhs: []^Expr,
|
||||
}
|
||||
|
||||
@@ -260,15 +261,15 @@ Assign_Stmt :: struct {
|
||||
Block_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
stmts: []^Stmt,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
If_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
if_pos: token.Pos,
|
||||
if_pos: tokenizer.Pos,
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
@@ -277,7 +278,7 @@ If_Stmt :: struct {
|
||||
|
||||
When_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
when_pos: token.Pos,
|
||||
when_pos: tokenizer.Pos,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
else_stmt: ^Stmt,
|
||||
@@ -296,7 +297,7 @@ Defer_Stmt :: struct {
|
||||
For_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
for_pos: token.Pos,
|
||||
for_pos: tokenizer.Pos,
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
post: ^Stmt,
|
||||
@@ -306,10 +307,10 @@ For_Stmt :: struct {
|
||||
Range_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
for_pos: token.Pos,
|
||||
for_pos: tokenizer.Pos,
|
||||
val0: ^Expr,
|
||||
val1: ^Expr,
|
||||
in_pos: token.Pos,
|
||||
in_pos: tokenizer.Pos,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
@@ -317,35 +318,35 @@ Range_Stmt :: struct {
|
||||
|
||||
Case_Clause :: struct {
|
||||
using node: Stmt,
|
||||
case_pos: token.Pos,
|
||||
case_pos: tokenizer.Pos,
|
||||
list: []^Expr,
|
||||
terminator: token.Token,
|
||||
terminator: tokenizer.Token,
|
||||
body: []^Stmt,
|
||||
}
|
||||
|
||||
Switch_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
switch_pos: token.Pos,
|
||||
switch_pos: tokenizer.Pos,
|
||||
init: ^Stmt,
|
||||
cond: ^Expr,
|
||||
body: ^Stmt,
|
||||
complete: bool,
|
||||
partial: bool,
|
||||
}
|
||||
|
||||
Type_Switch_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
label: ^Expr,
|
||||
switch_pos: token.Pos,
|
||||
switch_pos: tokenizer.Pos,
|
||||
tag: ^Stmt,
|
||||
expr: ^Expr,
|
||||
body: ^Stmt,
|
||||
complete: bool,
|
||||
partial: bool,
|
||||
}
|
||||
|
||||
Branch_Stmt :: struct {
|
||||
using node: Stmt,
|
||||
tok: token.Token,
|
||||
tok: tokenizer.Token,
|
||||
label: ^Ident,
|
||||
}
|
||||
|
||||
@@ -376,7 +377,7 @@ Value_Decl :: struct {
|
||||
Package_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
token: token.Token,
|
||||
token: tokenizer.Token,
|
||||
name: string,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
@@ -385,9 +386,9 @@ Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
is_using: bool,
|
||||
import_tok: token.Token,
|
||||
name: token.Token,
|
||||
relpath: token.Token,
|
||||
import_tok: tokenizer.Token,
|
||||
name: tokenizer.Token,
|
||||
relpath: tokenizer.Token,
|
||||
fullpath: string,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
@@ -396,7 +397,7 @@ Foreign_Block_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
tok: token.Token,
|
||||
tok: tokenizer.Token,
|
||||
foreign_library: ^Expr,
|
||||
body: ^Stmt,
|
||||
}
|
||||
@@ -404,27 +405,29 @@ Foreign_Block_Decl :: struct {
|
||||
Foreign_Import_Decl :: struct {
|
||||
using node: Decl,
|
||||
docs: ^Comment_Group,
|
||||
foreign_tok: token.Token,
|
||||
import_tok: token.Token,
|
||||
foreign_tok: tokenizer.Token,
|
||||
import_tok: tokenizer.Token,
|
||||
name: ^Ident,
|
||||
collection_name: string,
|
||||
fullpaths: []string,
|
||||
attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Other things
|
||||
unparen_expr :: proc(expr: ^Expr) -> ^Expr {
|
||||
unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
||||
val = expr;
|
||||
if expr == nil {
|
||||
return nil;
|
||||
return;
|
||||
}
|
||||
for {
|
||||
e, ok := expr.derived.(Paren_Expr);
|
||||
e, ok := val.derived.(Paren_Expr);
|
||||
if !ok do break;
|
||||
expr = e.expr;
|
||||
val = e.expr;
|
||||
}
|
||||
return expr;
|
||||
return;
|
||||
}
|
||||
|
||||
Field_Flag :: enum {
|
||||
@@ -434,7 +437,9 @@ Field_Flag :: enum {
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
In,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
Default_Parameters,
|
||||
Typeid_Token,
|
||||
}
|
||||
@@ -442,18 +447,19 @@ Field_Flag :: enum {
|
||||
Field_Flags :: distinct bit_set[Field_Flag];
|
||||
|
||||
Field_Flags_Struct :: Field_Flags{
|
||||
Field_Flag.Using,
|
||||
.Using,
|
||||
.Tags,
|
||||
};
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
Field_Flag.Typeid_Token,
|
||||
.Typeid_Token,
|
||||
};
|
||||
Field_Flags_Signature :: Field_Flags{
|
||||
Field_Flag.Ellipsis,
|
||||
Field_Flag.Using,
|
||||
Field_Flag.No_Alias,
|
||||
Field_Flag.C_Vararg,
|
||||
Field_Flag.Auto_Cast,
|
||||
Field_Flag.Default_Parameters,
|
||||
.Ellipsis,
|
||||
.Using,
|
||||
.No_Alias,
|
||||
.C_Vararg,
|
||||
.Auto_Cast,
|
||||
.Default_Parameters,
|
||||
};
|
||||
|
||||
Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token};
|
||||
@@ -462,18 +468,18 @@ Field_Flags_Signature_Results :: Field_Flags_Signature;
|
||||
|
||||
Proc_Group :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Token,
|
||||
open: token.Pos,
|
||||
tok: tokenizer.Token,
|
||||
open: tokenizer.Pos,
|
||||
args: []^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Attribute :: struct {
|
||||
using node: Node,
|
||||
tok: token.Kind,
|
||||
open: token.Pos,
|
||||
tok: tokenizer.Token_Kind,
|
||||
open: tokenizer.Pos,
|
||||
elems: []^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Field :: struct {
|
||||
@@ -482,56 +488,57 @@ Field :: struct {
|
||||
names: []^Expr, // Could be polymorphic
|
||||
type: ^Expr,
|
||||
default_value: ^Expr,
|
||||
tag: tokenizer.Token,
|
||||
flags: Field_Flags,
|
||||
comment: ^Comment_Group,
|
||||
}
|
||||
|
||||
Field_List :: struct {
|
||||
using node: Node,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
list: []^Field,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
|
||||
// Types
|
||||
Typeid_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Kind,
|
||||
tok: tokenizer.Token_Kind,
|
||||
specialization: ^Expr,
|
||||
}
|
||||
|
||||
Helper_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Kind,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Distinct_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Kind,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Opaque_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Kind,
|
||||
tok: tokenizer.Token_Kind,
|
||||
type: ^Expr,
|
||||
}
|
||||
|
||||
Poly_Type :: struct {
|
||||
using node: Expr,
|
||||
dollar: token.Pos,
|
||||
dollar: tokenizer.Pos,
|
||||
type: ^Ident,
|
||||
specialization: ^Expr,
|
||||
}
|
||||
|
||||
Proc_Type :: struct {
|
||||
using node: Expr,
|
||||
tok: token.Token,
|
||||
tok: tokenizer.Token,
|
||||
calling_convention: Proc_Calling_Convention,
|
||||
params: ^Field_List,
|
||||
arrow: token.Pos,
|
||||
arrow: tokenizer.Pos,
|
||||
results: ^Field_List,
|
||||
tags: Proc_Tags,
|
||||
generic: bool,
|
||||
@@ -540,77 +547,83 @@ Proc_Type :: struct {
|
||||
|
||||
Pointer_Type :: struct {
|
||||
using node: Expr,
|
||||
pointer: token.Pos,
|
||||
pointer: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
Array_Type :: struct {
|
||||
using node: Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
tag: ^Expr,
|
||||
len: ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
Dynamic_Array_Type :: struct {
|
||||
using node: Expr,
|
||||
open: token.Pos,
|
||||
dynamic_pos: token.Pos,
|
||||
close: token.Pos,
|
||||
tag: ^Expr,
|
||||
open: tokenizer.Pos,
|
||||
dynamic_pos: tokenizer.Pos,
|
||||
close: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
Struct_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: token.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
fields: ^Field_List,
|
||||
name_count: int,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
is_packed: bool,
|
||||
is_raw_union: bool,
|
||||
}
|
||||
|
||||
Union_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: token.Pos,
|
||||
tok_pos: tokenizer.Pos,
|
||||
poly_params: ^Field_List,
|
||||
align: ^Expr,
|
||||
variants: []^Expr,
|
||||
where_token: tokenizer.Token,
|
||||
where_clauses: []^Expr,
|
||||
}
|
||||
|
||||
Enum_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: token.Pos,
|
||||
tok_pos: tokenizer.Pos,
|
||||
base_type: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
fields: []^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
|
||||
is_using: bool,
|
||||
}
|
||||
|
||||
Bit_Field_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: token.Pos,
|
||||
tok_pos: tokenizer.Pos,
|
||||
align: ^Expr,
|
||||
open: token.Pos,
|
||||
open: tokenizer.Pos,
|
||||
fields: []^Field_Value, // Field_Value with ':' rather than '='
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Bit_Set_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: token.Pos,
|
||||
open: token.Pos,
|
||||
tok_pos: tokenizer.Pos,
|
||||
open: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
underlying: ^Expr,
|
||||
close: token.Pos,
|
||||
close: tokenizer.Pos,
|
||||
}
|
||||
|
||||
Map_Type :: struct {
|
||||
using node: Expr,
|
||||
tok_pos: token.Pos,
|
||||
tok_pos: tokenizer.Pos,
|
||||
key: ^Expr,
|
||||
value: ^Expr,
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package odin_ast
|
||||
|
||||
import "core:mem"
|
||||
import "core:fmt"
|
||||
import "core:odin/token"
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
new :: proc($T: typeid, pos, end: token.Pos) -> ^T {
|
||||
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
|
||||
n := mem.new(T);
|
||||
n.pos = pos;
|
||||
n.end = end;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package odin_ast
|
||||
|
||||
import "core:odin/token"
|
||||
import "core:odin/tokenizer"
|
||||
|
||||
Package_Kind :: enum {
|
||||
Normal,
|
||||
@@ -26,7 +26,7 @@ File :: struct {
|
||||
src: []byte,
|
||||
|
||||
pkg_decl: ^Package_Decl,
|
||||
pkg_token: token.Token,
|
||||
pkg_token: tokenizer.Token,
|
||||
pkg_name: string,
|
||||
|
||||
decls: [dynamic]^Stmt,
|
||||
|
||||
+525
-436
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
package odin_token
|
||||
package odin_tokenizer
|
||||
|
||||
import "core:strings"
|
||||
|
||||
Token :: struct {
|
||||
kind: Kind,
|
||||
kind: Token_Kind,
|
||||
text: string,
|
||||
pos: Pos,
|
||||
}
|
||||
@@ -28,7 +28,7 @@ pos_compare :: proc(lhs, rhs: Pos) -> int {
|
||||
return strings.compare(lhs.file, rhs.file);
|
||||
}
|
||||
|
||||
using Kind :: enum u32 {
|
||||
Token_Kind :: enum u32 {
|
||||
Invalid,
|
||||
EOF,
|
||||
Comment,
|
||||
@@ -118,12 +118,13 @@ using Kind :: enum u32 {
|
||||
Package,
|
||||
Typeid,
|
||||
When,
|
||||
Where,
|
||||
If,
|
||||
Else,
|
||||
For,
|
||||
Switch,
|
||||
In,
|
||||
Notin,
|
||||
Not_In,
|
||||
Do,
|
||||
Case,
|
||||
Break,
|
||||
@@ -154,9 +155,6 @@ using Kind :: enum u32 {
|
||||
Offset_Of,
|
||||
Type_Of,
|
||||
Const,
|
||||
Asm,
|
||||
Yield,
|
||||
Await,
|
||||
B_Keyword_End,
|
||||
|
||||
COUNT,
|
||||
@@ -165,7 +163,7 @@ using Kind :: enum u32 {
|
||||
// ... Custom keywords
|
||||
};
|
||||
|
||||
tokens := [Kind.COUNT]string {
|
||||
tokens := [Token_Kind.COUNT]string {
|
||||
"Invalid",
|
||||
"EOF",
|
||||
"Comment",
|
||||
@@ -255,12 +253,13 @@ tokens := [Kind.COUNT]string {
|
||||
"package",
|
||||
"typeid",
|
||||
"when",
|
||||
"where",
|
||||
"if",
|
||||
"else",
|
||||
"for",
|
||||
"switch",
|
||||
"in",
|
||||
"notin",
|
||||
"not_in",
|
||||
"do",
|
||||
"case",
|
||||
"break",
|
||||
@@ -291,20 +290,17 @@ tokens := [Kind.COUNT]string {
|
||||
"offset_of",
|
||||
"type_of",
|
||||
"const",
|
||||
"asm",
|
||||
"yield",
|
||||
"await",
|
||||
"",
|
||||
};
|
||||
|
||||
custom_keyword_tokens: []string;
|
||||
|
||||
to_string :: proc(kind: Kind) -> string {
|
||||
if Invalid <= kind && kind < COUNT {
|
||||
to_string :: proc(kind: Token_Kind) -> string {
|
||||
if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT {
|
||||
return tokens[kind];
|
||||
}
|
||||
if B_Custom_Keyword_Begin < kind {
|
||||
n := int(u16(kind)-u16(B_Custom_Keyword_Begin));
|
||||
if Token_Kind.B_Custom_Keyword_Begin < kind {
|
||||
n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin));
|
||||
if n < len(custom_keyword_tokens) {
|
||||
return custom_keyword_tokens[n];
|
||||
}
|
||||
@@ -313,24 +309,26 @@ to_string :: proc(kind: Kind) -> string {
|
||||
return "Invalid";
|
||||
}
|
||||
|
||||
is_literal :: proc(kind: Kind) -> bool { return B_Literal_Begin < kind && kind < B_Literal_End; }
|
||||
is_operator :: proc(kind: Kind) -> bool {
|
||||
switch kind {
|
||||
case B_Operator_Begin..B_Operator_End:
|
||||
is_literal :: proc(kind: Token_Kind) -> bool {
|
||||
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End;
|
||||
}
|
||||
is_operator :: proc(kind: Token_Kind) -> bool {
|
||||
#partial switch kind {
|
||||
case .B_Operator_Begin .. .B_Operator_End:
|
||||
return true;
|
||||
case In, Notin:
|
||||
case .In, .Not_In:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_assignment_operator :: proc(kind: Kind) -> bool {
|
||||
return B_Assign_Op_Begin < kind && kind < B_Assign_Op_End || kind == Eq;
|
||||
is_assignment_operator :: proc(kind: Token_Kind) -> bool {
|
||||
return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq;
|
||||
}
|
||||
is_keyword :: proc(kind: Kind) -> bool {
|
||||
is_keyword :: proc(kind: Token_Kind) -> bool {
|
||||
switch {
|
||||
case B_Keyword_Begin < kind && kind < B_Keyword_End:
|
||||
case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End:
|
||||
return true;
|
||||
case B_Custom_Keyword_Begin < kind:
|
||||
case Token_Kind.B_Custom_Keyword_Begin < kind:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1,10 +1,9 @@
|
||||
package odin_tokenizer
|
||||
|
||||
import "core:fmt"
|
||||
import "core:odin/token"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
Error_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any);
|
||||
Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any);
|
||||
|
||||
Tokenizer :: struct {
|
||||
// Immutable data
|
||||
@@ -41,11 +40,11 @@ init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = defa
|
||||
}
|
||||
|
||||
@(private)
|
||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> token.Pos {
|
||||
offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos {
|
||||
line := t.line_count;
|
||||
column := offset - t.line_offset + 1;
|
||||
|
||||
return token.Pos {
|
||||
return Pos {
|
||||
file = t.path,
|
||||
offset = offset,
|
||||
line = line,
|
||||
@@ -53,10 +52,10 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> token.Pos {
|
||||
};
|
||||
}
|
||||
|
||||
default_error_handler :: proc(pos: token.Pos, msg: string, args: ..any) {
|
||||
fmt.printf_err("%s(%d:%d) ", pos.file, pos.line, pos.column);
|
||||
fmt.printf_err(msg, ..args);
|
||||
fmt.printf_err("\n");
|
||||
default_error_handler :: proc(pos: Pos, msg: string, args: ..any) {
|
||||
fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column);
|
||||
fmt.eprintf(msg, ..args);
|
||||
fmt.eprintf("\n");
|
||||
}
|
||||
|
||||
error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
@@ -98,9 +97,9 @@ advance_rune :: proc(using t: ^Tokenizer) {
|
||||
}
|
||||
}
|
||||
|
||||
peek_byte :: proc(using t: ^Tokenizer, offset := 0) -> byte {
|
||||
if read_offset+offset < len(src) {
|
||||
return src[read_offset+offset];
|
||||
peek_byte :: proc(t: ^Tokenizer, offset := 0) -> byte {
|
||||
if t.read_offset+offset < len(t.src) {
|
||||
return t.src[t.read_offset+offset];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -322,15 +321,15 @@ scan_rune :: proc(t: ^Tokenizer) -> string {
|
||||
return string(t.src[offset : t.offset]);
|
||||
}
|
||||
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, string) {
|
||||
scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) {
|
||||
scan_mantissa :: proc(t: ^Tokenizer, base: int) {
|
||||
for digit_val(t.ch) < base || t.ch == '_' {
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
scan_exponent :: proc(t: ^Tokenizer, kind: ^token.Kind) {
|
||||
scan_exponent :: proc(t: ^Tokenizer, kind: ^Token_Kind) {
|
||||
if t.ch == 'e' || t.ch == 'E' {
|
||||
kind^ = token.Float;
|
||||
kind^ = .Float;
|
||||
advance_rune(t);
|
||||
if t.ch == '-' || t.ch == '+' {
|
||||
advance_rune(t);
|
||||
@@ -343,17 +342,18 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
|
||||
}
|
||||
|
||||
// NOTE(bill): This needs to be here for sanity's sake
|
||||
if t.ch == 'i' {
|
||||
kind^ = token.Imag;
|
||||
switch t.ch {
|
||||
case 'i', 'j', 'k':
|
||||
kind^ = .Imag;
|
||||
advance_rune(t);
|
||||
}
|
||||
}
|
||||
scan_fraction :: proc(t: ^Tokenizer, kind: ^token.Kind) -> (early_exit: bool) {
|
||||
scan_fraction :: proc(t: ^Tokenizer, kind: ^Token_Kind) -> (early_exit: bool) {
|
||||
if t.ch == '.' && peek_byte(t) == '.' {
|
||||
return true;
|
||||
}
|
||||
if t.ch == '.' {
|
||||
kind^ = token.Float;
|
||||
kind^ = .Float;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
@@ -362,21 +362,22 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
|
||||
|
||||
|
||||
offset := t.offset;
|
||||
kind := token.Integer;
|
||||
kind := Token_Kind.Integer;
|
||||
seen_point := seen_decimal_point;
|
||||
|
||||
if seen_decimal_point {
|
||||
if seen_point {
|
||||
offset -= 1;
|
||||
kind = token.Float;
|
||||
kind = .Float;
|
||||
scan_mantissa(t, 10);
|
||||
scan_exponent(t, &kind);
|
||||
} else {
|
||||
if t.ch == '0' {
|
||||
int_base :: inline proc(t: ^Tokenizer, kind: ^token.Kind, base: int, msg: string) {
|
||||
int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) {
|
||||
prev := t.offset;
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, base);
|
||||
if t.offset - prev <= 1 {
|
||||
kind^ = token.Invalid;
|
||||
kind^ = .Invalid;
|
||||
error(t, t.offset, msg);
|
||||
}
|
||||
}
|
||||
@@ -393,7 +394,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
|
||||
advance_rune(t);
|
||||
scan_mantissa(t, 16);
|
||||
if t.offset - prev <= 1 {
|
||||
kind = token.Invalid;
|
||||
kind = .Invalid;
|
||||
error(t, t.offset, "illegal hexadecimal floating-point number");
|
||||
} else {
|
||||
sub := t.src[prev+1 : t.offset];
|
||||
@@ -412,10 +413,10 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
|
||||
}
|
||||
|
||||
case:
|
||||
seen_decimal_point = false;
|
||||
seen_point = false;
|
||||
scan_mantissa(t, 10);
|
||||
if t.ch == '.' {
|
||||
seen_decimal_point = true;
|
||||
seen_point = true;
|
||||
if scan_fraction(t, &kind) {
|
||||
return kind, string(t.src[offset : t.offset]);
|
||||
}
|
||||
@@ -438,15 +439,15 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str
|
||||
}
|
||||
|
||||
|
||||
scan :: proc(t: ^Tokenizer) -> token.Token {
|
||||
switch2 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind) -> token.Kind {
|
||||
scan :: proc(t: ^Tokenizer) -> Token {
|
||||
switch2 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind) -> Token_Kind {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok1;
|
||||
}
|
||||
return tok0;
|
||||
}
|
||||
switch3 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind, ch2: rune, tok2: token.Kind) -> token.Kind {
|
||||
switch3 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2: Token_Kind) -> Token_Kind {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok1;
|
||||
@@ -457,7 +458,7 @@ scan :: proc(t: ^Tokenizer) -> token.Token {
|
||||
}
|
||||
return tok0;
|
||||
}
|
||||
switch4 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind, ch2: rune, tok2, tok3: token.Kind) -> token.Kind {
|
||||
switch4 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2, tok3: Token_Kind) -> Token_Kind {
|
||||
if t.ch == '=' {
|
||||
advance_rune(t);
|
||||
return tok1;
|
||||
@@ -478,28 +479,31 @@ scan :: proc(t: ^Tokenizer) -> token.Token {
|
||||
|
||||
offset := t.offset;
|
||||
|
||||
kind: token.Kind;
|
||||
kind: Token_Kind;
|
||||
lit: string;
|
||||
pos := offset_to_pos(t, offset);
|
||||
|
||||
switch ch := t.ch; true {
|
||||
case is_letter(ch):
|
||||
lit = scan_identifier(t);
|
||||
kind = token.Ident;
|
||||
kind = .Ident;
|
||||
check_keyword: if len(lit) > 1 {
|
||||
// TODO(bill): Maybe have a hash table lookup rather than this linear search
|
||||
for i in token.B_Keyword_Begin .. token.B_Keyword_End {
|
||||
if lit == token.tokens[i] {
|
||||
kind = token.Kind(i);
|
||||
for i in Token_Kind.B_Keyword_Begin .. Token_Kind.B_Keyword_End {
|
||||
if lit == tokens[i] {
|
||||
kind = Token_Kind(i);
|
||||
break check_keyword;
|
||||
}
|
||||
}
|
||||
for keyword, i in token.custom_keyword_tokens {
|
||||
for keyword, i in custom_keyword_tokens {
|
||||
if lit == keyword {
|
||||
kind = token.Kind(i+1)+token.B_Custom_Keyword_Begin;
|
||||
kind = Token_Kind(i+1) + .B_Custom_Keyword_Begin;
|
||||
break check_keyword;
|
||||
}
|
||||
}
|
||||
if kind == .Ident && lit == "notin" {
|
||||
kind = .Not_In;
|
||||
}
|
||||
}
|
||||
case '0' <= ch && ch <= '9':
|
||||
kind, lit = scan_number(t, false);
|
||||
@@ -507,115 +511,109 @@ scan :: proc(t: ^Tokenizer) -> token.Token {
|
||||
advance_rune(t);
|
||||
switch ch {
|
||||
case -1:
|
||||
kind = token.EOF;
|
||||
kind = .EOF;
|
||||
case '"':
|
||||
kind = token.String;
|
||||
kind = .String;
|
||||
lit = scan_string(t);
|
||||
case '\'':
|
||||
kind = token.Rune;
|
||||
kind = .Rune;
|
||||
lit = scan_rune(t);
|
||||
case '`':
|
||||
kind = token.String;
|
||||
kind = .String;
|
||||
lit = scan_raw_string(t);
|
||||
case '=':
|
||||
if t.ch == '>' {
|
||||
advance_rune(t);
|
||||
kind = token.Double_Arrow_Right;
|
||||
kind = .Double_Arrow_Right;
|
||||
} else {
|
||||
kind = switch2(t, token.Eq, token.Cmp_Eq);
|
||||
kind = switch2(t, .Eq, .Cmp_Eq);
|
||||
}
|
||||
case '!': kind = switch2(t, token.Not, token.Not_Eq);
|
||||
case '!': kind = switch2(t, .Not, .Not_Eq);
|
||||
case '#':
|
||||
kind = token.Hash;
|
||||
kind = .Hash;
|
||||
if t.ch == '!' {
|
||||
kind = token.Comment;
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
}
|
||||
case '?': kind = token.Question;
|
||||
case '@': kind = token.At;
|
||||
case '$': kind = token.Dollar;
|
||||
case '^': kind = token.Pointer;
|
||||
case '+': kind = switch2(t, token.Add, token.Add_Eq);
|
||||
case '?': kind = .Question;
|
||||
case '@': kind = .At;
|
||||
case '$': kind = .Dollar;
|
||||
case '^': kind = .Pointer;
|
||||
case '+': kind = switch2(t, .Add, .Add_Eq);
|
||||
case '-':
|
||||
if t.ch == '>' {
|
||||
advance_rune(t);
|
||||
kind = token.Arrow_Right;
|
||||
kind = .Arrow_Right;
|
||||
} else if t.ch == '-' && peek_byte(t) == '-' {
|
||||
advance_rune(t);
|
||||
advance_rune(t);
|
||||
kind = token.Undef;
|
||||
kind = .Undef;
|
||||
} else {
|
||||
kind = switch2(t, token.Sub, token.Sub_Eq);
|
||||
kind = switch2(t, .Sub, .Sub_Eq);
|
||||
}
|
||||
case '*': kind = switch2(t, token.Mul, token.Mul_Eq);
|
||||
case '*': kind = switch2(t, .Mul, .Mul_Eq);
|
||||
case '/':
|
||||
if t.ch == '/' || t.ch == '*' {
|
||||
kind = token.Comment;
|
||||
kind = .Comment;
|
||||
lit = scan_comment(t);
|
||||
} else {
|
||||
kind = switch2(t, token.Quo, token.Quo_Eq);
|
||||
kind = switch2(t, .Quo, .Quo_Eq);
|
||||
}
|
||||
case '%': kind = switch4(t, token.Mod, token.Mod_Eq, '%', token.Mod_Mod, token.Mod_Mod_Eq);
|
||||
case '%': kind = switch4(t, .Mod, .Mod_Eq, '%', .Mod_Mod, .Mod_Mod_Eq);
|
||||
case '&':
|
||||
if t.ch == '~' {
|
||||
advance_rune(t);
|
||||
kind = switch2(t, token.And_Not, token.And_Not_Eq);
|
||||
kind = switch2(t, .And_Not, .And_Not_Eq);
|
||||
} else {
|
||||
kind = switch3(t, token.And, token.And_Eq, '&', token.Cmp_And);
|
||||
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
|
||||
}
|
||||
case '|': kind = switch3(t, token.Or, token.Or_Eq, '|', token.Cmp_Or);
|
||||
case '~': kind = token.Xor;
|
||||
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
|
||||
case '~': kind = .Xor;
|
||||
case '<':
|
||||
if t.ch == '-' {
|
||||
advance_rune(t);
|
||||
kind = token.Arrow_Left;
|
||||
kind = .Arrow_Left;
|
||||
} else {
|
||||
kind = switch4(t, token.Lt, token.Lt_Eq, '<', token.Shl, token.Shl_Eq);
|
||||
kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
|
||||
}
|
||||
case '>': kind = switch4(t, token.Gt, token.Gt_Eq, '>', token.Shr,token.Shr_Eq);
|
||||
|
||||
case '≠': kind = token.Not_Eq;
|
||||
case '≤': kind = token.Lt_Eq;
|
||||
case '≥': kind = token.Gt_Eq;
|
||||
case '∈': kind = token.In;
|
||||
case '∉': kind = token.Notin;
|
||||
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
|
||||
|
||||
case '.':
|
||||
if '0' <= t.ch && t.ch <= '9' {
|
||||
kind, lit = scan_number(t, true);
|
||||
} else {
|
||||
kind = token.Period;
|
||||
kind = .Period;
|
||||
if t.ch == '.' {
|
||||
advance_rune(t);
|
||||
kind = token.Ellipsis;
|
||||
kind = .Ellipsis;
|
||||
if t.ch == '<' {
|
||||
advance_rune(t);
|
||||
kind = token.Range_Half;
|
||||
kind = .Range_Half;
|
||||
}
|
||||
}
|
||||
}
|
||||
case ':': kind = token.Colon;
|
||||
case ',': kind = token.Comma;
|
||||
case ';': kind = token.Semicolon;
|
||||
case '(': kind = token.Open_Paren;
|
||||
case ')': kind = token.Close_Paren;
|
||||
case '[': kind = token.Open_Bracket;
|
||||
case ']': kind = token.Close_Bracket;
|
||||
case '{': kind = token.Open_Brace;
|
||||
case '}': kind = token.Close_Brace;
|
||||
case ':': kind = .Colon;
|
||||
case ',': kind = .Comma;
|
||||
case ';': kind = .Semicolon;
|
||||
case '(': kind = .Open_Paren;
|
||||
case ')': kind = .Close_Paren;
|
||||
case '[': kind = .Open_Bracket;
|
||||
case ']': kind = .Close_Bracket;
|
||||
case '{': kind = .Open_Brace;
|
||||
case '}': kind = .Close_Brace;
|
||||
|
||||
case '\\': kind = token.Back_Slash;
|
||||
case '\\': kind = .Back_Slash;
|
||||
|
||||
case:
|
||||
if ch != utf8.RUNE_BOM {
|
||||
error(t, t.offset, "illegal character '%r': %d", ch, ch);
|
||||
}
|
||||
kind = token.Invalid;
|
||||
kind = .Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
if lit == "" {
|
||||
lit = string(t.src[offset : t.offset]);
|
||||
}
|
||||
return token.Token{kind, lit, pos};
|
||||
return Token{kind, lit, pos};
|
||||
}
|
||||
|
||||
+75
-8
@@ -5,7 +5,7 @@ import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, cast([]byte)str);
|
||||
return write(fd, transmute([]byte)str);
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
|
||||
@@ -99,7 +99,14 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
|
||||
if truncate {
|
||||
flags |= O_TRUNC;
|
||||
}
|
||||
fd, err := open(name, flags, 0);
|
||||
|
||||
mode: int = 0;
|
||||
when OS == "linux" {
|
||||
// NOTE(justasd): 644 (owner read, write; group read; others read)
|
||||
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
}
|
||||
|
||||
fd, err := open(name, flags, mode);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
@@ -119,16 +126,56 @@ read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) {
|
||||
return read(fd, s);
|
||||
}
|
||||
|
||||
|
||||
heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
/*
|
||||
//
|
||||
// NOTE(tetra, 2019-11-10): The heap doesn't respect alignment.
|
||||
// HACK: Overallocate, align forwards, and then use the two bytes immediately before
|
||||
// the address we return, to store the padding we inserted.
|
||||
// This allows us to pass the original pointer we got back from the heap to `free` later.
|
||||
//
|
||||
|
||||
align_and_store_padding :: proc(ptr: rawptr, alignment: int) -> rawptr {
|
||||
ptr := mem.ptr_offset(cast(^u8) ptr, 2);
|
||||
new_ptr := cast(^u8) mem.align_forward(ptr, uintptr(alignment));
|
||||
offset := mem.ptr_sub(new_ptr, cast(^u8) ptr) + 2;
|
||||
assert(offset < int(max(u16)));
|
||||
(^[2]u8)(mem.ptr_offset(new_ptr, -2))^ = transmute([2]u8) u16(offset);
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
recover_original_pointer :: proc(ptr: rawptr) -> rawptr {
|
||||
ptr := cast(^u8) ptr;
|
||||
offset := transmute(u16) (^[2]u8)(mem.ptr_offset(ptr, -2))^;
|
||||
ptr = mem.ptr_offset(ptr, -int(offset));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
aligned_heap_alloc :: proc(size: int, alignment: int) -> rawptr {
|
||||
// NOTE(tetra): Alignment 1 will mean we only have one extra byte.
|
||||
// This is not enough for a u16 - so we ensure there is at least two bytes extra.
|
||||
// This also means that the pointer is always aligned to at least 2.
|
||||
extra := alignment;
|
||||
if extra <= 1 do extra = 2;
|
||||
|
||||
orig := cast(^u8) heap_alloc(size + extra);
|
||||
if orig == nil do return nil;
|
||||
ptr := align_and_store_padding(orig, alignment);
|
||||
assert(recover_original_pointer(ptr) == orig);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return heap_alloc(size);
|
||||
return aligned_heap_alloc(size, alignment);
|
||||
|
||||
case .Free:
|
||||
heap_free(old_memory);
|
||||
if old_memory != nil {
|
||||
ptr := recover_original_pointer(old_memory);
|
||||
heap_free(ptr);
|
||||
}
|
||||
return nil;
|
||||
|
||||
case .Free_All:
|
||||
@@ -136,11 +183,31 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return heap_alloc(size);
|
||||
return aligned_heap_alloc(size, alignment);
|
||||
}
|
||||
ptr := heap_resize(old_memory, size);
|
||||
ptr := recover_original_pointer(old_memory);
|
||||
ptr = heap_resize(ptr, size);
|
||||
assert(ptr != nil);
|
||||
return ptr;
|
||||
return align_and_store_padding(ptr, alignment);
|
||||
}
|
||||
|
||||
return nil;
|
||||
*/
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return heap_alloc(size);
|
||||
|
||||
case .Free:
|
||||
if old_memory != nil {
|
||||
heap_free(old_memory);
|
||||
}
|
||||
return nil;
|
||||
|
||||
case .Free_All:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case .Resize:
|
||||
return heap_resize(old_memory, size);
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
||||
@@ -6,7 +6,7 @@ foreign import libc "system:c"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
OS :: "osx";
|
||||
OS :: "darwin";
|
||||
|
||||
Handle :: distinct i32;
|
||||
File_Time :: distinct u64;
|
||||
@@ -14,6 +14,142 @@ Errno :: distinct int;
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
EPERM: Errno : 1; /* Operation not permitted */
|
||||
ENOENT: Errno : 2; /* No such file or directory */
|
||||
ESRCH: Errno : 3; /* No such process */
|
||||
EINTR: Errno : 4; /* Interrupted system call */
|
||||
EIO: Errno : 5; /* Input/output error */
|
||||
ENXIO: Errno : 6; /* Device not configured */
|
||||
E2BIG: Errno : 7; /* Argument list too long */
|
||||
ENOEXEC: Errno : 8; /* Exec format error */
|
||||
EBADF: Errno : 9; /* Bad file descriptor */
|
||||
ECHILD: Errno : 10; /* No child processes */
|
||||
EDEADLK: Errno : 11; /* Resource deadlock avoided */
|
||||
ENOMEM: Errno : 12; /* Cannot allocate memory */
|
||||
EACCES: Errno : 13; /* Permission denied */
|
||||
EFAULT: Errno : 14; /* Bad address */
|
||||
ENOTBLK: Errno : 15; /* Block device required */
|
||||
EBUSY: Errno : 16; /* Device / Resource busy */
|
||||
EEXIST: Errno : 17; /* File exists */
|
||||
EXDEV: Errno : 18; /* Cross-device link */
|
||||
ENODEV: Errno : 19; /* Operation not supported by device */
|
||||
ENOTDIR: Errno : 20; /* Not a directory */
|
||||
EISDIR: Errno : 21; /* Is a directory */
|
||||
EINVAL: Errno : 22; /* Invalid argument */
|
||||
ENFILE: Errno : 23; /* Too many open files in system */
|
||||
EMFILE: Errno : 24; /* Too many open files */
|
||||
ENOTTY: Errno : 25; /* Inappropriate ioctl for device */
|
||||
ETXTBSY: Errno : 26; /* Text file busy */
|
||||
EFBIG: Errno : 27; /* File too large */
|
||||
ENOSPC: Errno : 28; /* No space left on device */
|
||||
ESPIPE: Errno : 29; /* Illegal seek */
|
||||
EROFS: Errno : 30; /* Read-only file system */
|
||||
EMLINK: Errno : 31; /* Too many links */
|
||||
EPIPE: Errno : 32; /* Broken pipe */
|
||||
|
||||
/* math software */
|
||||
EDOM: Errno : 33; /* Numerical argument out of domain */
|
||||
ERANGE: Errno : 34; /* Result too large */
|
||||
|
||||
/* non-blocking and interrupt i/o */
|
||||
EAGAIN: Errno : 35; /* Resource temporarily unavailable */
|
||||
EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
|
||||
EINPROGRESS: Errno : 36; /* Operation now in progress */
|
||||
EALREADY: Errno : 37; /* Operation already in progress */
|
||||
|
||||
/* ipc/network software -- argument errors */
|
||||
ENOTSOCK: Errno : 38; /* Socket operation on non-socket */
|
||||
EDESTADDRREQ: Errno : 39; /* Destination address required */
|
||||
EMSGSIZE: Errno : 40; /* Message too long */
|
||||
EPROTOTYPE: Errno : 41; /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT: Errno : 42; /* Protocol not available */
|
||||
EPROTONOSUPPORT: Errno : 43; /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT: Errno : 44; /* Socket type not supported */
|
||||
ENOTSUP: Errno : 45; /* Operation not supported */
|
||||
EPFNOSUPPORT: Errno : 46; /* Protocol family not supported */
|
||||
EAFNOSUPPORT: Errno : 47; /* Address family not supported by protocol family */
|
||||
EADDRINUSE: Errno : 48; /* Address already in use */
|
||||
EADDRNOTAVAIL: Errno : 49; /* Can't assign requested address */
|
||||
|
||||
/* ipc/network software -- operational errors */
|
||||
ENETDOWN: Errno : 50; /* Network is down */
|
||||
ENETUNREACH: Errno : 51; /* Network is unreachable */
|
||||
ENETRESET: Errno : 52; /* Network dropped connection on reset */
|
||||
ECONNABORTED: Errno : 53; /* Software caused connection abort */
|
||||
ECONNRESET: Errno : 54; /* Connection reset by peer */
|
||||
ENOBUFS: Errno : 55; /* No buffer space available */
|
||||
EISCONN: Errno : 56; /* Socket is already connected */
|
||||
ENOTCONN: Errno : 57; /* Socket is not connected */
|
||||
ESHUTDOWN: Errno : 58; /* Can't send after socket shutdown */
|
||||
ETOOMANYREFS: Errno : 59; /* Too many references: can't splice */
|
||||
ETIMEDOUT: Errno : 60; /* Operation timed out */
|
||||
ECONNREFUSED: Errno : 61; /* Connection refused */
|
||||
|
||||
ELOOP: Errno : 62; /* Too many levels of symbolic links */
|
||||
ENAMETOOLONG: Errno : 63; /* File name too long */
|
||||
|
||||
/* should be rearranged */
|
||||
EHOSTDOWN: Errno : 64; /* Host is down */
|
||||
EHOSTUNREACH: Errno : 65; /* No route to host */
|
||||
ENOTEMPTY: Errno : 66; /* Directory not empty */
|
||||
|
||||
/* quotas & mush */
|
||||
EPROCLIM: Errno : 67; /* Too many processes */
|
||||
EUSERS: Errno : 68; /* Too many users */
|
||||
EDQUOT: Errno : 69; /* Disc quota exceeded */
|
||||
|
||||
/* Network File System */
|
||||
ESTALE: Errno : 70; /* Stale NFS file handle */
|
||||
EREMOTE: Errno : 71; /* Too many levels of remote in path */
|
||||
EBADRPC: Errno : 72; /* RPC struct is bad */
|
||||
ERPCMISMATCH: Errno : 73; /* RPC version wrong */
|
||||
EPROGUNAVAIL: Errno : 74; /* RPC prog. not avail */
|
||||
EPROGMISMATCH: Errno : 75; /* Program version wrong */
|
||||
EPROCUNAVAIL: Errno : 76; /* Bad procedure for program */
|
||||
|
||||
ENOLCK: Errno : 77; /* No locks available */
|
||||
ENOSYS: Errno : 78; /* Function not implemented */
|
||||
|
||||
EFTYPE: Errno : 79; /* Inappropriate file type or format */
|
||||
EAUTH: Errno : 80; /* Authentication error */
|
||||
ENEEDAUTH: Errno : 81; /* Need authenticator */
|
||||
|
||||
/* Intelligent device errors */
|
||||
EPWROFF: Errno : 82; /* Device power is off */
|
||||
EDEVERR: Errno : 83; /* Device error, e.g. paper out */
|
||||
EOVERFLOW: Errno : 84; /* Value too large to be stored in data type */
|
||||
|
||||
/* Program loading errors */
|
||||
EBADEXEC: Errno : 85; /* Bad executable */
|
||||
EBADARCH: Errno : 86; /* Bad CPU type in executable */
|
||||
ESHLIBVERS: Errno : 87; /* Shared library version mismatch */
|
||||
EBADMACHO: Errno : 88; /* Malformed Macho file */
|
||||
|
||||
ECANCELED: Errno : 89; /* Operation canceled */
|
||||
|
||||
EIDRM: Errno : 90; /* Identifier removed */
|
||||
ENOMSG: Errno : 91; /* No message of desired type */
|
||||
EILSEQ: Errno : 92; /* Illegal byte sequence */
|
||||
ENOATTR: Errno : 93; /* Attribute not found */
|
||||
|
||||
EBADMSG: Errno : 94; /* Bad message */
|
||||
EMULTIHOP: Errno : 95; /* Reserved */
|
||||
ENODATA: Errno : 96; /* No message available on STREAM */
|
||||
ENOLINK: Errno : 97; /* Reserved */
|
||||
ENOSR: Errno : 98; /* No STREAM resources */
|
||||
ENOSTR: Errno : 99; /* Not a STREAM */
|
||||
EPROTO: Errno : 100; /* Protocol error */
|
||||
ETIME: Errno : 101; /* STREAM ioctl timeout */
|
||||
|
||||
ENOPOLICY: Errno : 103; /* No such policy registered */
|
||||
|
||||
ENOTRECOVERABLE: Errno : 104; /* State not recoverable */
|
||||
EOWNERDEAD: Errno : 105; /* Previous owner died */
|
||||
|
||||
EQFULL: Errno : 106; /* Interface output queue is full */
|
||||
ELAST: Errno : 106; /* Must be equal largest errno */
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
@@ -133,6 +269,7 @@ foreign libc {
|
||||
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
|
||||
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
|
||||
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
|
||||
|
||||
@@ -169,6 +306,9 @@ close :: proc(fd: Handle) {
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
if len(data) == 0 {
|
||||
return 0, 0;
|
||||
}
|
||||
bytes_written := _unix_write(fd, &data[0], len(data));
|
||||
if(bytes_written == -1) {
|
||||
return 0, 1;
|
||||
@@ -258,7 +398,6 @@ exit :: inline proc(code: int) -> ! {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
// return int(_unix_gettid());
|
||||
return 0;
|
||||
@@ -285,11 +424,21 @@ dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror());
|
||||
}
|
||||
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1;
|
||||
if page_size != -1 do return page_size;
|
||||
|
||||
page_size = int(_unix_getpagesize());
|
||||
return page_size;
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
args := make([]string, len(runtime.args__));
|
||||
res := make([]string, len(runtime.args__));
|
||||
for arg, i in runtime.args__ {
|
||||
args[i] = string(arg);
|
||||
res[i] = string(arg);
|
||||
}
|
||||
return args;
|
||||
return res;
|
||||
}
|
||||
+2422
-162
File diff suppressed because it is too large
Load Diff
+150
-74
@@ -15,37 +15,138 @@ Syscall :: distinct int;
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0);
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
EPERM: Errno : 1;
|
||||
ENOENT: Errno : 2;
|
||||
EINTR: Errno : 4;
|
||||
EIO: Errno : 5;
|
||||
ENXIO: Errno : 6;
|
||||
EBADF: Errno : 9;
|
||||
EAGAIN: Errno : 11;
|
||||
EWOULDBLOCK: Errno : EAGAIN;
|
||||
ENOMEM: Errno : 12;
|
||||
EACCES: Errno : 13;
|
||||
EFAULT: Errno : 14;
|
||||
EEXIST: Errno : 17;
|
||||
ENODEV: Errno : 19;
|
||||
ENOTDIR: Errno : 20;
|
||||
EISDIR: Errno : 21;
|
||||
EINVAL: Errno : 22;
|
||||
ENFILE: Errno : 23;
|
||||
EMFILE: Errno : 24;
|
||||
ETXTBSY: Errno : 26;
|
||||
EFBIG: Errno : 27;
|
||||
ENOSPC: Errno : 28;
|
||||
ESPIPE: Errno : 29;
|
||||
EROFS: Errno : 30;
|
||||
EPIPE: Errno : 32;
|
||||
ENAMETOOLONG: Errno : 36;
|
||||
ELOOP: Errno : 40;
|
||||
EOVERFLOW: Errno : 75;
|
||||
EDESTADDRREQ: Errno : 89;
|
||||
EOPNOTSUPP: Errno : 95;
|
||||
EDQUOT: Errno : 122;
|
||||
ERROR_NONE: Errno : 0;
|
||||
EPERM: Errno : 1;
|
||||
ENOENT: Errno : 2;
|
||||
ESRCH: Errno : 3;
|
||||
EINTR: Errno : 4;
|
||||
EIO: Errno : 5;
|
||||
ENXIO: Errno : 6;
|
||||
EBADF: Errno : 9;
|
||||
EAGAIN: Errno : 11;
|
||||
ENOMEM: Errno : 12;
|
||||
EACCES: Errno : 13;
|
||||
EFAULT: Errno : 14;
|
||||
EEXIST: Errno : 17;
|
||||
ENODEV: Errno : 19;
|
||||
ENOTDIR: Errno : 20;
|
||||
EISDIR: Errno : 21;
|
||||
EINVAL: Errno : 22;
|
||||
ENFILE: Errno : 23;
|
||||
EMFILE: Errno : 24;
|
||||
ETXTBSY: Errno : 26;
|
||||
EFBIG: Errno : 27;
|
||||
ENOSPC: Errno : 28;
|
||||
ESPIPE: Errno : 29;
|
||||
EROFS: Errno : 30;
|
||||
EPIPE: Errno : 32;
|
||||
|
||||
EDEADLK: Errno : 35; /* Resource deadlock would occur */
|
||||
ENAMETOOLONG: Errno : 36; /* File name too long */
|
||||
ENOLCK: Errno : 37; /* No record locks available */
|
||||
|
||||
ENOSYS: Errno : 38; /* Invalid system call number */
|
||||
|
||||
ENOTEMPTY: Errno : 39; /* Directory not empty */
|
||||
ELOOP: Errno : 40; /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK: Errno : EAGAIN; /* Operation would block */
|
||||
ENOMSG: Errno : 42; /* No message of desired type */
|
||||
EIDRM: Errno : 43; /* Identifier removed */
|
||||
ECHRNG: Errno : 44; /* Channel number out of range */
|
||||
EL2NSYNC: Errno : 45; /* Level 2 not synchronized */
|
||||
EL3HLT: Errno : 46; /* Level 3 halted */
|
||||
EL3RST: Errno : 47; /* Level 3 reset */
|
||||
ELNRNG: Errno : 48; /* Link number out of range */
|
||||
EUNATCH: Errno : 49; /* Protocol driver not attached */
|
||||
ENOCSI: Errno : 50; /* No CSI structure available */
|
||||
EL2HLT: Errno : 51; /* Level 2 halted */
|
||||
EBADE: Errno : 52; /* Invalid exchange */
|
||||
EBADR: Errno : 53; /* Invalid request descriptor */
|
||||
EXFULL: Errno : 54; /* Exchange full */
|
||||
ENOANO: Errno : 55; /* No anode */
|
||||
EBADRQC: Errno : 56; /* Invalid request code */
|
||||
EBADSLT: Errno : 57; /* Invalid slot */
|
||||
EDEADLOCK: Errno : EDEADLK;
|
||||
EBFONT: Errno : 59; /* Bad font file format */
|
||||
ENOSTR: Errno : 60; /* Device not a stream */
|
||||
ENODATA: Errno : 61; /* No data available */
|
||||
ETIME: Errno : 62; /* Timer expired */
|
||||
ENOSR: Errno : 63; /* Out of streams resources */
|
||||
ENONET: Errno : 64; /* Machine is not on the network */
|
||||
ENOPKG: Errno : 65; /* Package not installed */
|
||||
EREMOTE: Errno : 66; /* Object is remote */
|
||||
ENOLINK: Errno : 67; /* Link has been severed */
|
||||
EADV: Errno : 68; /* Advertise error */
|
||||
ESRMNT: Errno : 69; /* Srmount error */
|
||||
ECOMM: Errno : 70; /* Communication error on send */
|
||||
EPROTO: Errno : 71; /* Protocol error */
|
||||
EMULTIHOP: Errno : 72; /* Multihop attempted */
|
||||
EDOTDOT: Errno : 73; /* RFS specific error */
|
||||
EBADMSG: Errno : 74; /* Not a data message */
|
||||
EOVERFLOW: Errno : 75; /* Value too large for defined data type */
|
||||
ENOTUNIQ: Errno : 76; /* Name not unique on network */
|
||||
EBADFD: Errno : 77; /* File descriptor in bad state */
|
||||
EREMCHG: Errno : 78; /* Remote address changed */
|
||||
ELIBACC: Errno : 79; /* Can not access a needed shared library */
|
||||
ELIBBAD: Errno : 80; /* Accessing a corrupted shared library */
|
||||
ELIBSCN: Errno : 81; /* .lib section in a.out corrupted */
|
||||
ELIBMAX: Errno : 82; /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC: Errno : 83; /* Cannot exec a shared library directly */
|
||||
EILSEQ: Errno : 84; /* Illegal byte sequence */
|
||||
ERESTART: Errno : 85; /* Interrupted system call should be restarted */
|
||||
ESTRPIPE: Errno : 86; /* Streams pipe error */
|
||||
EUSERS: Errno : 87; /* Too many users */
|
||||
ENOTSOCK: Errno : 88; /* Socket operation on non-socket */
|
||||
EDESTADDRREQ: Errno : 89; /* Destination address required */
|
||||
EMSGSIZE: Errno : 90; /* Message too long */
|
||||
EPROTOTYPE: Errno : 91; /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT: Errno : 92; /* Protocol not available */
|
||||
EPROTONOSUPPORT: Errno : 93; /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT: Errno : 94; /* Socket type not supported */
|
||||
EOPNOTSUPP: Errno : 95; /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT: Errno : 96; /* Protocol family not supported */
|
||||
EAFNOSUPPORT: Errno : 97; /* Address family not supported by protocol */
|
||||
EADDRINUSE: Errno : 98; /* Address already in use */
|
||||
EADDRNOTAVAIL: Errno : 99; /* Cannot assign requested address */
|
||||
ENETDOWN: Errno : 100; /* Network is down */
|
||||
ENETUNREACH: Errno : 101; /* Network is unreachable */
|
||||
ENETRESET: Errno : 102; /* Network dropped connection because of reset */
|
||||
ECONNABORTED: Errno : 103; /* Software caused connection abort */
|
||||
ECONNRESET: Errno : 104; /* Connection reset by peer */
|
||||
ENOBUFS: Errno : 105; /* No buffer space available */
|
||||
EISCONN: Errno : 106; /* Transport endpoint is already connected */
|
||||
ENOTCONN: Errno : 107; /* Transport endpoint is not connected */
|
||||
ESHUTDOWN: Errno : 108; /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS: Errno : 109; /* Too many references: cannot splice */
|
||||
ETIMEDOUT: Errno : 110; /* Connection timed out */
|
||||
ECONNREFUSED: Errno : 111; /* Connection refused */
|
||||
EHOSTDOWN: Errno : 112; /* Host is down */
|
||||
EHOSTUNREACH: Errno : 113; /* No route to host */
|
||||
EALREADY: Errno : 114; /* Operation already in progress */
|
||||
EINPROGRESS: Errno : 115; /* Operation now in progress */
|
||||
ESTALE: Errno : 116; /* Stale file handle */
|
||||
EUCLEAN: Errno : 117; /* Structure needs cleaning */
|
||||
ENOTNAM: Errno : 118; /* Not a XENIX named type file */
|
||||
ENAVAIL: Errno : 119; /* No XENIX semaphores available */
|
||||
EISNAM: Errno : 120; /* Is a named type file */
|
||||
EREMOTEIO: Errno : 121; /* Remote I/O error */
|
||||
EDQUOT: Errno : 122; /* Quota exceeded */
|
||||
|
||||
ENOMEDIUM: Errno : 123; /* No medium found */
|
||||
EMEDIUMTYPE: Errno : 124; /* Wrong medium type */
|
||||
ECANCELED: Errno : 125; /* Operation Canceled */
|
||||
ENOKEY: Errno : 126; /* Required key not available */
|
||||
EKEYEXPIRED: Errno : 127; /* Key has expired */
|
||||
EKEYREVOKED: Errno : 128; /* Key has been revoked */
|
||||
EKEYREJECTED: Errno : 129; /* Key was rejected by service */
|
||||
|
||||
/* for robust mutexes */
|
||||
EOWNERDEAD: Errno : 130; /* Owner died */
|
||||
ENOTRECOVERABLE: Errno : 131; /* State not recoverable */
|
||||
|
||||
ERFKILL: Errno : 132; /* Operation not possible due to RF-kill */
|
||||
|
||||
EHWPOISON: Errno : 133; /* Memory page has hardware error */
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
@@ -152,34 +253,19 @@ X_OK :: 1; // Test for execute permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
R_OK :: 4; // Test for read permission
|
||||
|
||||
TimeSpec :: struct {
|
||||
tv_sec : i64, /* seconds */
|
||||
tv_nsec : i64, /* nanoseconds */
|
||||
};
|
||||
|
||||
CLOCK_REALTIME :: 0;
|
||||
CLOCK_MONOTONIC :: 1;
|
||||
CLOCK_PROCESS_CPUTIME_ID :: 2;
|
||||
CLOCK_THREAD_CPUTIME_ID :: 3;
|
||||
CLOCK_MONOTONIC_RAW :: 4;
|
||||
CLOCK_REALTIME_COARSE :: 5;
|
||||
CLOCK_MONOTONIC_COARSE :: 6;
|
||||
CLOCK_BOOTTIME :: 7;
|
||||
CLOCK_REALTIME_ALARM :: 8;
|
||||
CLOCK_BOOTTIME_ALARM :: 9;
|
||||
|
||||
SYS_GETTID: Syscall : 186;
|
||||
|
||||
foreign libc {
|
||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
|
||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, mode: int) -> Handle ---;
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> int ---;
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
|
||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
|
||||
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
|
||||
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> int ---;
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
|
||||
@@ -190,10 +276,6 @@ foreign libc {
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||
|
||||
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) ---;
|
||||
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> int ---;
|
||||
@(link_name="sleep") _unix_sleep :: proc(seconds: u64) -> int ---;
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
|
||||
}
|
||||
foreign dl {
|
||||
@@ -238,6 +320,9 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_written := _unix_write(fd, &data[0], len(data));
|
||||
if bytes_written == -1 {
|
||||
return -1, Errno(get_last_error());
|
||||
@@ -346,25 +431,6 @@ exit :: proc(code: int) -> ! {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
clock_gettime :: proc(clock_id: u64) -> TimeSpec {
|
||||
ts : TimeSpec;
|
||||
_unix_clock_gettime(clock_id, &ts);
|
||||
return ts;
|
||||
}
|
||||
|
||||
sleep :: proc(seconds: u64) -> int {
|
||||
|
||||
return _unix_sleep(seconds);
|
||||
}
|
||||
|
||||
nanosleep :: proc(nanoseconds: i64) -> int {
|
||||
assert(nanoseconds <= 999999999);
|
||||
requested, remaining : TimeSpec;
|
||||
requested = TimeSpec{tv_nsec = nanoseconds};
|
||||
|
||||
return _unix_nanosleep(&requested, &remaining);
|
||||
}
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return syscall(SYS_GETTID);
|
||||
}
|
||||
@@ -390,11 +456,21 @@ dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror());
|
||||
}
|
||||
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1;
|
||||
if page_size != -1 do return page_size;
|
||||
|
||||
page_size = int(_unix_getpagesize());
|
||||
return page_size;
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
args := make([]string, len(runtime.args__));
|
||||
res := make([]string, len(runtime.args__));
|
||||
for arg, i in runtime.args__ {
|
||||
args[i] = string(arg);
|
||||
res[i] = string(arg);
|
||||
}
|
||||
return args;
|
||||
return res;
|
||||
}
|
||||
|
||||
+14
-3
@@ -65,7 +65,7 @@ is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/' || r == '\\';
|
||||
}
|
||||
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
|
||||
access: u32;
|
||||
@@ -210,7 +210,6 @@ get_std_handle :: proc(h: int) -> Handle {
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
file_info: win32.By_Handle_File_Information;
|
||||
if !win32.get_file_information_by_handle(win32.Handle(fd), &file_info) {
|
||||
@@ -253,6 +252,18 @@ heap_free :: proc(ptr: rawptr) {
|
||||
win32.heap_free(win32.get_process_heap(), 0, ptr);
|
||||
}
|
||||
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@static page_size := -1;
|
||||
if page_size != -1 do return page_size;
|
||||
|
||||
info: win32.System_Info;
|
||||
win32.get_system_info(&info);
|
||||
page_size = int(info.page_size);
|
||||
return page_size;
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) -> ! {
|
||||
win32.exit_process(u32(code));
|
||||
@@ -322,4 +333,4 @@ is_windows_8_1 :: proc() -> bool {
|
||||
is_windows_10 :: proc() -> bool {
|
||||
osvi := get_windows_version_ansi();
|
||||
return (osvi.major_version == 10 && osvi.minor_version == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
|
||||
|
||||
Type_Kind :: enum {
|
||||
Invalid,
|
||||
|
||||
Named,
|
||||
Integer,
|
||||
Rune,
|
||||
Float,
|
||||
Complex,
|
||||
Quaternion,
|
||||
String,
|
||||
Boolean,
|
||||
Any,
|
||||
Type_Id,
|
||||
Pointer,
|
||||
Procedure,
|
||||
Array,
|
||||
Enumerated_Array,
|
||||
Dynamic_Array,
|
||||
Slice,
|
||||
Tuple,
|
||||
Struct,
|
||||
Union,
|
||||
Enum,
|
||||
Map,
|
||||
Bit_Field,
|
||||
Bit_Set,
|
||||
Opaque,
|
||||
Simd_Vector,
|
||||
}
|
||||
|
||||
|
||||
type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
ti := type_info_of(T);
|
||||
if ti != nil {
|
||||
switch _ in ti.variant {
|
||||
case runtime.Type_Info_Named: return .Named;
|
||||
case runtime.Type_Info_Integer: return .Integer;
|
||||
case runtime.Type_Info_Rune: return .Rune;
|
||||
case runtime.Type_Info_Float: return .Float;
|
||||
case runtime.Type_Info_Complex: return .Complex;
|
||||
case runtime.Type_Info_Quaternion: return .Quaternion;
|
||||
case runtime.Type_Info_String: return .String;
|
||||
case runtime.Type_Info_Boolean: return .Boolean;
|
||||
case runtime.Type_Info_Any: return .Any;
|
||||
case runtime.Type_Info_Type_Id: return .Type_Id;
|
||||
case runtime.Type_Info_Pointer: return .Pointer;
|
||||
case runtime.Type_Info_Procedure: return .Procedure;
|
||||
case runtime.Type_Info_Array: return .Array;
|
||||
case runtime.Type_Info_Enumerated_Array: return .Enumerated_Array;
|
||||
case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array;
|
||||
case runtime.Type_Info_Slice: return .Slice;
|
||||
case runtime.Type_Info_Tuple: return .Tuple;
|
||||
case runtime.Type_Info_Struct: return .Struct;
|
||||
case runtime.Type_Info_Union: return .Union;
|
||||
case runtime.Type_Info_Enum: return .Enum;
|
||||
case runtime.Type_Info_Map: return .Map;
|
||||
case runtime.Type_Info_Bit_Field: return .Bit_Field;
|
||||
case runtime.Type_Info_Bit_Set: return .Bit_Set;
|
||||
case runtime.Type_Info_Opaque: return .Opaque;
|
||||
case runtime.Type_Info_Simd_Vector: return .Simd_Vector;
|
||||
}
|
||||
|
||||
}
|
||||
return .Invalid;
|
||||
}
|
||||
|
||||
// TODO(bill): Better name
|
||||
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return type_kind(runtime.typeid_base(T));
|
||||
}
|
||||
|
||||
// TODO(bill): Better name
|
||||
backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
return type_kind(runtime.typeid_core(T));
|
||||
}
|
||||
|
||||
|
||||
|
||||
size_of_typeid :: proc(T: typeid) -> int {
|
||||
if ti := type_info_of(T); ti != nil {
|
||||
return ti.size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
align_of_typeid :: proc(T: typeid) -> int {
|
||||
if ti := type_info_of(T); ti != nil {
|
||||
return ti.align;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
to_bytes :: proc(v: any) -> []byte {
|
||||
if v != nil {
|
||||
sz := size_of_typeid(v.id);
|
||||
return mem.slice_ptr((^byte)(v.data), sz);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) {
|
||||
return v.data, v.id;
|
||||
}
|
||||
|
||||
is_nil :: proc(v: any) -> bool {
|
||||
data := to_bytes(v);
|
||||
if data != nil {
|
||||
return true;
|
||||
}
|
||||
for v in data do if v != 0 {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
length :: proc(val: any) -> int {
|
||||
if val == nil do return 0;
|
||||
|
||||
v := val;
|
||||
v.id = runtime.typeid_base(v.id);
|
||||
switch a in v {
|
||||
case runtime.Type_Info_Array:
|
||||
return a.count;
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
return (^mem.Raw_Slice)(v.data).len;
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(v.data).len;
|
||||
|
||||
case runtime.Type_Info_String:
|
||||
if a.is_cstring {
|
||||
return len((^cstring)(v.data)^);
|
||||
} else {
|
||||
return (^mem.Raw_String)(v.data).len;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
if val == nil do return nil;
|
||||
|
||||
v := val;
|
||||
v.id = runtime.typeid_base(v.id);
|
||||
switch a in v {
|
||||
case runtime.Type_Info_Array:
|
||||
runtime.bounds_check_error_loc(loc, i, a.count);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(v.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
raw := (^mem.Raw_Slice)(v.data);
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(raw.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
raw := (^mem.Raw_Dynamic_Array)(v.data);
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||
offset := uintptr(a.elem.size * i);
|
||||
data := rawptr(uintptr(raw.data) + offset);
|
||||
return any{data, a.elem.id};
|
||||
|
||||
case runtime.Type_Info_String:
|
||||
if a.is_cstring do return nil;
|
||||
|
||||
raw := (^mem.Raw_String)(v.data);
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len);
|
||||
offset := uintptr(size_of(u8) * i);
|
||||
data := rawptr(uintptr(raw.data) + offset);
|
||||
return any{data, typeid_of(u8)};
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Struct_Tag :: distinct string;
|
||||
|
||||
Struct_Field :: struct {
|
||||
name: string,
|
||||
type: typeid,
|
||||
tag: Struct_Tag,
|
||||
offset: uintptr,
|
||||
}
|
||||
|
||||
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
if 0 <= i && i < len(s.names) {
|
||||
field.name = s.names[i];
|
||||
field.type = s.types[i].id;
|
||||
field.tag = Struct_Tag(s.tags[i]);
|
||||
field.offset = s.offsets[i];
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
for fname, i in s.names {
|
||||
if fname == name {
|
||||
field.name = s.names[i];
|
||||
field.type = s.types[i].id;
|
||||
field.tag = Struct_Tag(s.tags[i]);
|
||||
field.offset = s.offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> any {
|
||||
if a == nil do return nil;
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
for name, i in s.names {
|
||||
if name == field {
|
||||
return any{
|
||||
rawptr(uintptr(a.data) + s.offsets[i]),
|
||||
s.types[i].id,
|
||||
};
|
||||
}
|
||||
|
||||
if recurse && s.usings[i] {
|
||||
f := any{
|
||||
rawptr(uintptr(a.data) + s.offsets[i]),
|
||||
s.types[i].id,
|
||||
};
|
||||
|
||||
if res := struct_field_value_by_name(f, field, recurse); res != nil {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct_field_names :: proc(T: typeid) -> []string {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.names;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.types;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return transmute([]Struct_Tag)s.tags;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
struct_field_offsets :: proc(T: typeid) -> []uintptr {
|
||||
ti := runtime.type_info_base(type_info_of(T));
|
||||
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
|
||||
return s.offsets;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) {
|
||||
value, _ = struct_tag_lookup(tag, key);
|
||||
return;
|
||||
}
|
||||
|
||||
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) {
|
||||
for t := tag; t != ""; /**/ {
|
||||
i := 0;
|
||||
for i < len(t) && t[i] == ' ' { // Skip whitespace
|
||||
i += 1;
|
||||
}
|
||||
t = t[i:];
|
||||
if len(t) == 0 do break;
|
||||
|
||||
i = 0;
|
||||
loop: for i < len(t) {
|
||||
switch t[i] {
|
||||
case ':', '"':
|
||||
break loop;
|
||||
case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found
|
||||
break loop;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i == 0 do break;
|
||||
if i+1 >= len(t) do break;
|
||||
|
||||
if t[i] != ':' || t[i+1] != '"' {
|
||||
break;
|
||||
}
|
||||
name := string(t[:i]);
|
||||
t = t[i+1:];
|
||||
|
||||
i = 1;
|
||||
for i < len(t) && t[i] != '"' { // find closing quote
|
||||
if t[i] == '\\' do i += 1; // Skip escaped characters
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if i >= len(t) do break;
|
||||
|
||||
val := string(t[:i+1]);
|
||||
t = t[i+1:];
|
||||
|
||||
if key == name {
|
||||
return val[1:i], true;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
enum_string :: proc(a: any) -> string {
|
||||
if a == nil do return "";
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
|
||||
for _, i in e.values {
|
||||
value := &e.values[i];
|
||||
n := mem.compare_byte_ptrs((^byte)(a.data), (^byte)(value), ti.size);
|
||||
if n == 0 {
|
||||
return e.names[i];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic("expected an enum to reflect.enum_string");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info {
|
||||
id := union_variant_typeid(a);
|
||||
return type_info_of(id);
|
||||
}
|
||||
|
||||
union_variant_typeid :: proc(a: any) -> typeid {
|
||||
if a == nil do return nil;
|
||||
|
||||
ti := runtime.type_info_base(type_info_of(a.id));
|
||||
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
||||
tag_ptr := uintptr(a.data) + info.tag_offset;
|
||||
tag_any := any{rawptr(tag_ptr), info.tag_type.id};
|
||||
|
||||
tag: i64 = ---;
|
||||
switch i in tag_any {
|
||||
case u8: tag = i64(i);
|
||||
case i8: tag = i64(i);
|
||||
case u16: tag = i64(i);
|
||||
case i16: tag = i64(i);
|
||||
case u32: tag = i64(i);
|
||||
case i32: tag = i64(i);
|
||||
case u64: tag = i64(i);
|
||||
case i64: tag = i64(i);
|
||||
case: unimplemented();
|
||||
}
|
||||
|
||||
if a.data != nil && tag != 0 {
|
||||
return info.variants[tag-1].id;
|
||||
}
|
||||
} else {
|
||||
panic("expected a union to reflect.union_variant_typeid");
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package types
|
||||
package reflect
|
||||
|
||||
import rt "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
if a == b do return true;
|
||||
@@ -39,6 +40,14 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
_, ok := b.variant.(rt.Type_Info_Complex);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Quaternion:
|
||||
_, ok := b.variant.(rt.Type_Info_Quaternion);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_Type_Id:
|
||||
_, ok := b.variant.(rt.Type_Info_Type_Id);
|
||||
return ok;
|
||||
|
||||
case rt.Type_Info_String:
|
||||
_, ok := b.variant.(rt.Type_Info_String);
|
||||
return ok;
|
||||
@@ -73,6 +82,13 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
if x.count != y.count do return false;
|
||||
return are_types_identical(x.elem, y.elem);
|
||||
|
||||
case rt.Type_Info_Enumerated_Array:
|
||||
y, ok := b.variant.(rt.Type_Info_Enumerated_Array);
|
||||
if !ok do return false;
|
||||
if x.count != y.count do return false;
|
||||
return are_types_identical(x.index, y.index) &&
|
||||
are_types_identical(x.elem, y.elem);
|
||||
|
||||
case rt.Type_Info_Dynamic_Array:
|
||||
y, ok := b.variant.(rt.Type_Info_Dynamic_Array);
|
||||
if !ok do return false;
|
||||
@@ -108,9 +124,11 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
for _, i in x.types {
|
||||
xn, yn := x.names[i], y.names[i];
|
||||
xt, yt := x.types[i], y.types[i];
|
||||
xl, yl := x.tags[i], y.tags[i];
|
||||
|
||||
if xn != yn do return false;
|
||||
if !are_types_identical(xt, yt) do return false;
|
||||
if xl != yl do return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -159,6 +177,11 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
y, ok := b.variant.(rt.Type_Info_Opaque);
|
||||
if !ok do return false;
|
||||
return x.elem == y.elem;
|
||||
|
||||
case rt.Type_Info_Simd_Vector:
|
||||
y, ok := b.variant.(rt.Type_Info_Simd_Vector);
|
||||
if !ok do return false;
|
||||
return x.count == y.count && x.elem == y.elem;
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -166,7 +189,7 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
|
||||
|
||||
is_signed :: proc(info: ^rt.Type_Info) -> bool {
|
||||
if info == nil do return false;
|
||||
switch i in rt.type_info_base(info).variant {
|
||||
#partial switch i in rt.type_info_base(info).variant {
|
||||
case rt.Type_Info_Integer: return i.signed;
|
||||
case rt.Type_Info_Float: return true;
|
||||
}
|
||||
@@ -272,3 +295,244 @@ is_simd_vector :: proc(info: ^rt.Type_Info) -> bool {
|
||||
_, ok := rt.type_info_base(info).variant.(rt.Type_Info_Simd_Vector);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
write_typeid :: proc(buf: ^strings.Builder, id: typeid) {
|
||||
write_type(buf, type_info_of(id));
|
||||
}
|
||||
|
||||
write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
|
||||
using strings;
|
||||
if ti == nil {
|
||||
write_string(buf, "nil");
|
||||
return;
|
||||
}
|
||||
|
||||
switch info in ti.variant {
|
||||
case rt.Type_Info_Named:
|
||||
write_string(buf, info.name);
|
||||
case rt.Type_Info_Integer:
|
||||
switch ti.id {
|
||||
case int: write_string(buf, "int");
|
||||
case uint: write_string(buf, "uint");
|
||||
case uintptr: write_string(buf, "uintptr");
|
||||
case:
|
||||
write_byte(buf, info.signed ? 'i' : 'u');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
switch info.endianness {
|
||||
case .Platform: // Okay
|
||||
case .Little: write_string(buf, "le");
|
||||
case .Big: write_string(buf, "be");
|
||||
}
|
||||
}
|
||||
case rt.Type_Info_Rune:
|
||||
write_string(buf, "rune");
|
||||
case rt.Type_Info_Float:
|
||||
write_byte(buf, 'f');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case rt.Type_Info_Complex:
|
||||
write_string(buf, "complex");
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case rt.Type_Info_Quaternion:
|
||||
write_string(buf, "quaternion");
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
case rt.Type_Info_String:
|
||||
if info.is_cstring {
|
||||
write_string(buf, "cstring");
|
||||
} else {
|
||||
write_string(buf, "string");
|
||||
}
|
||||
case rt.Type_Info_Boolean:
|
||||
switch ti.id {
|
||||
case bool: write_string(buf, "bool");
|
||||
case:
|
||||
write_byte(buf, 'b');
|
||||
write_i64(buf, i64(8*ti.size), 10);
|
||||
}
|
||||
case rt.Type_Info_Any:
|
||||
write_string(buf, "any");
|
||||
|
||||
case rt.Type_Info_Type_Id:
|
||||
write_string(buf, "typeid");
|
||||
|
||||
case rt.Type_Info_Pointer:
|
||||
if info.elem == nil {
|
||||
write_string(buf, "rawptr");
|
||||
} else {
|
||||
write_string(buf, "^");
|
||||
write_type(buf, info.elem);
|
||||
}
|
||||
case rt.Type_Info_Procedure:
|
||||
write_string(buf, "proc");
|
||||
if info.params == nil {
|
||||
write_string(buf, "()");
|
||||
} else {
|
||||
t := info.params.variant.(rt.Type_Info_Tuple);
|
||||
write_string(buf, "(");
|
||||
for t, i in t.types {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_type(buf, t);
|
||||
}
|
||||
write_string(buf, ")");
|
||||
}
|
||||
if info.results != nil {
|
||||
write_string(buf, " -> ");
|
||||
write_type(buf, info.results);
|
||||
}
|
||||
case rt.Type_Info_Tuple:
|
||||
count := len(info.names);
|
||||
if count != 1 do write_string(buf, "(");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
|
||||
t := info.types[i];
|
||||
|
||||
if len(name) > 0 {
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
}
|
||||
write_type(buf, t);
|
||||
}
|
||||
if count != 1 do write_string(buf, ")");
|
||||
|
||||
case rt.Type_Info_Array:
|
||||
write_string(buf, "[");
|
||||
write_i64(buf, i64(info.count), 10);
|
||||
write_string(buf, "]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case rt.Type_Info_Enumerated_Array:
|
||||
write_string(buf, "[");
|
||||
write_type(buf, info.index);
|
||||
write_string(buf, "]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case rt.Type_Info_Dynamic_Array:
|
||||
write_string(buf, "[dynamic]");
|
||||
write_type(buf, info.elem);
|
||||
case rt.Type_Info_Slice:
|
||||
write_string(buf, "[]");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case rt.Type_Info_Map:
|
||||
write_string(buf, "map[");
|
||||
write_type(buf, info.key);
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.value);
|
||||
|
||||
case rt.Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
write_string(buf, "#soa[");
|
||||
write_i64(buf, i64(info.soa_len));
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.soa_base_type);
|
||||
return;
|
||||
case .Slice:
|
||||
write_string(buf, "#soa[]");
|
||||
write_type(buf, info.soa_base_type);
|
||||
return;
|
||||
case .Dynamic:
|
||||
write_string(buf, "#soa[dynamic]");
|
||||
write_type(buf, info.soa_base_type);
|
||||
return;
|
||||
}
|
||||
|
||||
write_string(buf, "struct ");
|
||||
if info.is_packed do write_string(buf, "#packed ");
|
||||
if info.is_raw_union do write_string(buf, "#raw_union ");
|
||||
if info.custom_align {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_byte(buf, '{');
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
write_type(buf, info.types[i]);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case rt.Type_Info_Union:
|
||||
write_string(buf, "union ");
|
||||
if info.custom_align {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_byte(buf, '{');
|
||||
for variant, i in info.variants {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_type(buf, variant);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case rt.Type_Info_Enum:
|
||||
write_string(buf, "enum ");
|
||||
write_type(buf, info.base);
|
||||
write_string(buf, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case rt.Type_Info_Bit_Field:
|
||||
write_string(buf, "bit_field ");
|
||||
if ti.align != 1 {
|
||||
write_string(buf, "#align ");
|
||||
write_i64(buf, i64(ti.align), 10);
|
||||
write_byte(buf, ' ');
|
||||
}
|
||||
write_string(buf, " {");
|
||||
for name, i in info.names {
|
||||
if i > 0 do write_string(buf, ", ");
|
||||
write_string(buf, name);
|
||||
write_string(buf, ": ");
|
||||
write_i64(buf, i64(info.bits[i]), 10);
|
||||
}
|
||||
write_byte(buf, '}');
|
||||
|
||||
case rt.Type_Info_Bit_Set:
|
||||
write_string(buf, "bit_set[");
|
||||
switch {
|
||||
case is_enum(info.elem):
|
||||
write_type(buf, info.elem);
|
||||
case is_rune(info.elem):
|
||||
write_encoded_rune(buf, rune(info.lower));
|
||||
write_string(buf, "..");
|
||||
write_encoded_rune(buf, rune(info.upper));
|
||||
case:
|
||||
write_i64(buf, info.lower, 10);
|
||||
write_string(buf, "..");
|
||||
write_i64(buf, info.upper, 10);
|
||||
}
|
||||
if info.underlying != nil {
|
||||
write_string(buf, "; ");
|
||||
write_type(buf, info.underlying);
|
||||
}
|
||||
write_byte(buf, ']');
|
||||
|
||||
case rt.Type_Info_Opaque:
|
||||
write_string(buf, "opaque ");
|
||||
write_type(buf, info.elem);
|
||||
|
||||
case rt.Type_Info_Simd_Vector:
|
||||
if info.is_x86_mmx {
|
||||
write_string(buf, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
write_string(buf, "#simd[");
|
||||
write_i64(buf, i64(info.count));
|
||||
write_byte(buf, ']');
|
||||
write_type(buf, info.elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+525
-121
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,143 @@
|
||||
package runtime
|
||||
|
||||
import "core:os"
|
||||
|
||||
default_allocator_proc :: os.heap_allocator_proc;
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return os.heap_allocator();
|
||||
}
|
||||
|
||||
|
||||
Default_Temp_Allocator :: struct {
|
||||
data: []byte,
|
||||
curr_offset: int,
|
||||
prev_offset: int,
|
||||
backup_allocator: Allocator,
|
||||
leaked_allocations: [dynamic]rawptr,
|
||||
}
|
||||
|
||||
default_temp_allocator_init :: proc(allocator: ^Default_Temp_Allocator, data: []byte, backup_allocator := context.allocator) {
|
||||
allocator.data = data;
|
||||
allocator.curr_offset = 0;
|
||||
allocator.prev_offset = 0;
|
||||
allocator.backup_allocator = backup_allocator;
|
||||
allocator.leaked_allocations.allocator = backup_allocator;
|
||||
}
|
||||
|
||||
default_temp_allocator_destroy :: proc(using allocator: ^Default_Temp_Allocator) {
|
||||
if allocator == nil {
|
||||
return;
|
||||
}
|
||||
for ptr in leaked_allocations {
|
||||
free(ptr, backup_allocator);
|
||||
}
|
||||
delete(leaked_allocations);
|
||||
delete(data, backup_allocator);
|
||||
allocator^ = {};
|
||||
}
|
||||
|
||||
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr {
|
||||
|
||||
allocator := (^Default_Temp_Allocator)(allocator_data);
|
||||
|
||||
if allocator.data == nil {
|
||||
DEFAULT_SCRATCH_BACKING_SIZE :: 1<<22;
|
||||
a := context.allocator;
|
||||
if !(context.allocator.procedure != default_temp_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
a = default_allocator();
|
||||
}
|
||||
default_temp_allocator_init(allocator, make([]byte, 1<<22, a), a);
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
switch {
|
||||
case allocator.curr_offset+size <= len(allocator.data):
|
||||
offset := align_forward_uintptr(uintptr(allocator.curr_offset), uintptr(alignment));
|
||||
ptr := &allocator.data[offset];
|
||||
mem_zero(ptr, size);
|
||||
allocator.prev_offset = int(offset);
|
||||
allocator.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
case size <= len(allocator.data):
|
||||
offset := align_forward_uintptr(uintptr(0), uintptr(alignment));
|
||||
ptr := &allocator.data[offset];
|
||||
mem_zero(ptr, size);
|
||||
allocator.prev_offset = int(offset);
|
||||
allocator.curr_offset = int(offset) + size;
|
||||
return ptr;
|
||||
}
|
||||
// TODO(bill): Should leaks be notified about? Should probably use a logging system that is built into the context system
|
||||
a := allocator.backup_allocator;
|
||||
if a.procedure == nil {
|
||||
a = context.allocator;
|
||||
allocator.backup_allocator = a;
|
||||
}
|
||||
|
||||
ptr := mem_alloc(size, alignment, a, loc);
|
||||
if allocator.leaked_allocations == nil {
|
||||
allocator.leaked_allocations = make([dynamic]rawptr, a);
|
||||
}
|
||||
append(&allocator.leaked_allocations, ptr);
|
||||
|
||||
return ptr;
|
||||
|
||||
case .Free:
|
||||
if len(allocator.data) == 0 {
|
||||
return nil;
|
||||
}
|
||||
last_ptr := rawptr(&allocator.data[allocator.prev_offset]);
|
||||
if old_memory == last_ptr {
|
||||
full_size := allocator.curr_offset - allocator.prev_offset;
|
||||
allocator.curr_offset = allocator.prev_offset;
|
||||
mem_zero(last_ptr, full_size);
|
||||
return nil;
|
||||
} else {
|
||||
#no_bounds_check start, end := &allocator.data[0], &allocator.data[allocator.curr_offset];
|
||||
if start <= old_memory && old_memory < end {
|
||||
// NOTE(bill): Cannot free this pointer
|
||||
return nil;
|
||||
}
|
||||
|
||||
if len(allocator.leaked_allocations) != 0 {
|
||||
for ptr, i in allocator.leaked_allocations {
|
||||
if ptr == old_memory {
|
||||
free(ptr, allocator.backup_allocator);
|
||||
ordered_remove(&allocator.leaked_allocations, i);
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// NOTE(bill): It's a temporary memory, don't worry about freeing
|
||||
|
||||
case .Free_All:
|
||||
allocator.curr_offset = 0;
|
||||
allocator.prev_offset = 0;
|
||||
for ptr in allocator.leaked_allocations {
|
||||
free(ptr, allocator.backup_allocator);
|
||||
}
|
||||
clear(&allocator.leaked_allocations);
|
||||
|
||||
case .Resize:
|
||||
last_ptr := rawptr(&allocator.data[allocator.prev_offset]);
|
||||
if old_memory == last_ptr && len(allocator.data)-allocator.prev_offset >= size {
|
||||
allocator.curr_offset = allocator.prev_offset+size;
|
||||
return old_memory;
|
||||
}
|
||||
return default_temp_allocator_proc(allocator_data, Allocator_Mode.Alloc, size, alignment, old_memory, old_size, flags, loc);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_temp_allocator_proc,
|
||||
data = allocator,
|
||||
};
|
||||
}
|
||||
+402
-26
@@ -1,16 +1,138 @@
|
||||
package runtime
|
||||
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
ptr_offset :: inline proc "contextless" (ptr: $P/^$T, n: int) -> P {
|
||||
new := int(uintptr(ptr)) + size_of(T)*n;
|
||||
return P(uintptr(new));
|
||||
}
|
||||
|
||||
is_power_of_two_int :: inline proc(x: int) -> bool {
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward_int :: inline proc(ptr, align: int) -> int {
|
||||
assert(is_power_of_two_int(align));
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
return p;
|
||||
}
|
||||
|
||||
is_power_of_two_uintptr :: inline proc(x: uintptr) -> bool {
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward_uintptr :: inline proc(ptr, align: uintptr) -> uintptr {
|
||||
assert(is_power_of_two_uintptr(align));
|
||||
|
||||
p := ptr;
|
||||
modulo := p & (align-1);
|
||||
if modulo != 0 do p += align - modulo;
|
||||
return p;
|
||||
}
|
||||
|
||||
mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
if data == nil do return nil;
|
||||
if len < 0 do return data;
|
||||
when !#defined(memset) {
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memset.p0i8.i64")
|
||||
memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memset.p0i8.i32")
|
||||
memset :: proc(dst: rawptr, val: byte, len: int, align: i32 = 1, is_volatile: bool = false) ---;
|
||||
}
|
||||
}
|
||||
}
|
||||
memset(data, 0, len);
|
||||
return data;
|
||||
}
|
||||
|
||||
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i64")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memmove.p0i8.p0i8.i32")
|
||||
llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memmove(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil do return dst;
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
foreign _ {
|
||||
when size_of(rawptr) == 8 {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
} else {
|
||||
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
|
||||
llvm_memcpy :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---;
|
||||
}
|
||||
}
|
||||
llvm_memcpy(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
DEFAULT_ALIGNMENT :: 2*align_of(rawptr);
|
||||
|
||||
mem_alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 do return nil;
|
||||
if allocator.procedure == nil do return nil;
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
|
||||
mem_free :: inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) {
|
||||
if ptr == nil do return;
|
||||
if allocator.procedure == nil do return;
|
||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
||||
}
|
||||
|
||||
mem_free_all :: inline proc(allocator := context.allocator, loc := #caller_location) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, 0, loc);
|
||||
}
|
||||
}
|
||||
|
||||
mem_resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
switch {
|
||||
case allocator.procedure == nil:
|
||||
return nil;
|
||||
case new_size == 0:
|
||||
allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, 0, loc);
|
||||
return nil;
|
||||
case ptr == nil:
|
||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, 0, loc);
|
||||
}
|
||||
return allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, 0, loc);
|
||||
}
|
||||
|
||||
|
||||
print_u64 :: proc(fd: os.Handle, u: u64) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
print_u64 :: proc(fd: os.Handle, x: u64) {
|
||||
digits := "0123456789";
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
b := u64(10);
|
||||
u := x;
|
||||
for u >= b {
|
||||
i -= 1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
@@ -20,10 +142,11 @@ print_u64 :: proc(fd: os.Handle, u: u64) {
|
||||
os.write(fd, a[i:]);
|
||||
}
|
||||
|
||||
print_i64 :: proc(fd: os.Handle, u: i64) {
|
||||
print_i64 :: proc(fd: os.Handle, x: i64) {
|
||||
digits := "0123456789";
|
||||
b :: i64(10);
|
||||
|
||||
u := x;
|
||||
neg := u < 0;
|
||||
u = abs(u);
|
||||
|
||||
@@ -79,6 +202,9 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
case Type_Info_Complex:
|
||||
os.write_string(fd, "complex");
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_Quaternion:
|
||||
os.write_string(fd, "quaternion");
|
||||
print_u64(fd, u64(8*ti.size));
|
||||
case Type_Info_String:
|
||||
os.write_string(fd, "string");
|
||||
case Type_Info_Boolean:
|
||||
@@ -106,7 +232,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
os.write_string(fd, "()");
|
||||
} else {
|
||||
t := info.params.variant.(Type_Info_Tuple);
|
||||
os.write_string(fd, "(");
|
||||
os.write_byte(fd, '(');
|
||||
for t, i in t.types {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
print_type(fd, t);
|
||||
@@ -119,7 +245,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
}
|
||||
case Type_Info_Tuple:
|
||||
count := len(info.names);
|
||||
if count != 1 do os.write_string(fd, "(");
|
||||
if count != 1 do os.write_byte(fd, '(');
|
||||
for name, i in info.names {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
|
||||
@@ -134,10 +260,18 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
if count != 1 do os.write_string(fd, ")");
|
||||
|
||||
case Type_Info_Array:
|
||||
os.write_string(fd, "[");
|
||||
os.write_byte(fd, '[');
|
||||
print_u64(fd, u64(info.count));
|
||||
os.write_string(fd, "]");
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.elem);
|
||||
|
||||
case Type_Info_Enumerated_Array:
|
||||
os.write_byte(fd, '[');
|
||||
print_type(fd, info.index);
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.elem);
|
||||
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
os.write_string(fd, "[dynamic]");
|
||||
print_type(fd, info.elem);
|
||||
@@ -152,6 +286,24 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
print_type(fd, info.value);
|
||||
|
||||
case Type_Info_Struct:
|
||||
switch info.soa_kind {
|
||||
case .None: // Ignore
|
||||
case .Fixed:
|
||||
os.write_string(fd, "#soa[");
|
||||
print_u64(fd, u64(info.soa_len));
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.soa_base_type);
|
||||
return;
|
||||
case .Slice:
|
||||
os.write_string(fd, "#soa[]");
|
||||
print_type(fd, info.soa_base_type);
|
||||
return;
|
||||
case .Dynamic:
|
||||
os.write_string(fd, "#soa[dynamic]");
|
||||
print_type(fd, info.soa_base_type);
|
||||
return;
|
||||
}
|
||||
|
||||
os.write_string(fd, "struct ");
|
||||
if info.is_packed do os.write_string(fd, "#packed ");
|
||||
if info.is_raw_union do os.write_string(fd, "#raw_union ");
|
||||
@@ -170,7 +322,15 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
os.write_byte(fd, '}');
|
||||
|
||||
case Type_Info_Union:
|
||||
os.write_string(fd, "union {");
|
||||
os.write_string(fd, "union ");
|
||||
if info.custom_align {
|
||||
os.write_string(fd, "#align ");
|
||||
print_u64(fd, u64(ti.align));
|
||||
}
|
||||
if info.no_nil {
|
||||
os.write_string(fd, "#no_nil ");
|
||||
}
|
||||
os.write_byte(fd, '{');
|
||||
for variant, i in info.variants {
|
||||
if i > 0 do os.write_string(fd, ", ");
|
||||
print_type(fd, variant);
|
||||
@@ -206,7 +366,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
case Type_Info_Bit_Set:
|
||||
os.write_string(fd, "bit_set[");
|
||||
|
||||
switch elem in type_info_base(info.elem).variant {
|
||||
#partial switch elem in type_info_base(info.elem).variant {
|
||||
case Type_Info_Enum:
|
||||
print_type(fd, info.elem);
|
||||
case Type_Info_Rune:
|
||||
@@ -232,26 +392,101 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
|
||||
if info.is_x86_mmx {
|
||||
os.write_string(fd, "intrinsics.x86_mmx");
|
||||
} else {
|
||||
os.write_string(fd, "intrinsics.vector(");
|
||||
os.write_string(fd, "#simd[");
|
||||
print_u64(fd, u64(info.count));
|
||||
os.write_string(fd, ", ");
|
||||
os.write_byte(fd, ']');
|
||||
print_type(fd, info.elem);
|
||||
os.write_byte(fd, ')');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
|
||||
x := uintptr(a);
|
||||
y := uintptr(b);
|
||||
n := uintptr(n);
|
||||
|
||||
SU :: size_of(uintptr);
|
||||
fast := uintptr(n/SU + 1);
|
||||
offset := (fast-1)*SU;
|
||||
curr_block := uintptr(0);
|
||||
if n < SU {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
|
||||
vb := (^uintptr)(y + curr_block * size_of(uintptr))^;
|
||||
if va ~ vb != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
a := (^byte)(x+pos)^;
|
||||
b := (^byte)(y+pos)^;
|
||||
if a ~ b != 0 {
|
||||
return (int(a) - int(b)) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
a := (^byte)(x+offset)^;
|
||||
b := (^byte)(y+offset)^;
|
||||
if a ~ b != 0 {
|
||||
return (int(a) - int(b)) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_check {
|
||||
x := uintptr(a);
|
||||
n := uintptr(n);
|
||||
|
||||
SU :: size_of(uintptr);
|
||||
fast := uintptr(n/SU + 1);
|
||||
offset := (fast-1)*SU;
|
||||
curr_block := uintptr(0);
|
||||
if n < SU {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
va := (^uintptr)(x + curr_block * size_of(uintptr))^;
|
||||
if va ~ 0 != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
a := (^byte)(x+pos)^;
|
||||
if a ~ 0 != 0 {
|
||||
return int(a) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
a := (^byte)(x+offset)^;
|
||||
if a ~ 0 != 0 {
|
||||
return int(a) < 0 ? -1 : +1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
string_eq :: proc "contextless" (a, b: string) -> bool {
|
||||
x := transmute(Raw_String)a;
|
||||
y := transmute(Raw_String)b;
|
||||
switch {
|
||||
case len(a) != len(b): return false;
|
||||
case len(a) == 0: return true;
|
||||
case &a[0] == &b[0]: return true;
|
||||
case x.len != y.len: return false;
|
||||
case x.len == 0: return true;
|
||||
case x.data == y.data: return true;
|
||||
}
|
||||
return string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
string_cmp :: proc "contextless" (a, b: string) -> int {
|
||||
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
x := transmute(Raw_String)a;
|
||||
y := transmute(Raw_String)b;
|
||||
return memory_compare(x.data, y.data, min(x.len, y.len));
|
||||
}
|
||||
|
||||
string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); }
|
||||
@@ -261,18 +496,19 @@ string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cm
|
||||
string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; }
|
||||
|
||||
cstring_len :: proc "contextless" (s: cstring) -> int {
|
||||
n := 0;
|
||||
for p := (^byte)(s); p != nil && p^ != 0; p = mem.ptr_offset(p, 1) {
|
||||
n += 1;
|
||||
p0 := uintptr((^byte)(s));
|
||||
p := p0;
|
||||
for p != 0 && (^byte)(p)^ != 0 {
|
||||
p += 1;
|
||||
}
|
||||
return n;
|
||||
return int(p - p0);
|
||||
}
|
||||
|
||||
cstring_to_string :: proc "contextless" (s: cstring) -> string {
|
||||
if s == nil do return "";
|
||||
ptr := (^byte)(s);
|
||||
n := cstring_len(s);
|
||||
return transmute(string)mem.Raw_String{ptr, n};
|
||||
return transmute(string)Raw_String{ptr, n};
|
||||
}
|
||||
|
||||
|
||||
@@ -283,6 +519,11 @@ complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return r
|
||||
complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
|
||||
quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
|
||||
quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
|
||||
|
||||
quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); }
|
||||
quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); }
|
||||
|
||||
|
||||
bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) {
|
||||
@@ -356,8 +597,84 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column
|
||||
handle_error(file, line, column, from, to);
|
||||
}
|
||||
|
||||
|
||||
string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) {
|
||||
return utf8.decode_rune_in_string(s);
|
||||
// NOTE(bill): Duplicated here to remove dependency on package unicode/utf8
|
||||
|
||||
@static accept_sizes := [256]u8{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
|
||||
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
|
||||
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
|
||||
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
|
||||
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
||||
};
|
||||
Accept_Range :: struct {lo, hi: u8};
|
||||
|
||||
@static accept_ranges := [5]Accept_Range{
|
||||
{0x80, 0xbf},
|
||||
{0xa0, 0xbf},
|
||||
{0x80, 0x9f},
|
||||
{0x90, 0xbf},
|
||||
{0x80, 0x8f},
|
||||
};
|
||||
|
||||
MASKX :: 0b0011_1111;
|
||||
MASK2 :: 0b0001_1111;
|
||||
MASK3 :: 0b0000_1111;
|
||||
MASK4 :: 0b0000_0111;
|
||||
|
||||
LOCB :: 0b1000_0000;
|
||||
HICB :: 0b1011_1111;
|
||||
|
||||
|
||||
RUNE_ERROR :: '\ufffd';
|
||||
|
||||
n := len(s);
|
||||
if n < 1 {
|
||||
return RUNE_ERROR, 0;
|
||||
}
|
||||
s0 := s[0];
|
||||
x := accept_sizes[s0];
|
||||
if x >= 0xF0 {
|
||||
mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
|
||||
return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
|
||||
}
|
||||
sz := x & 7;
|
||||
accept := accept_ranges[x>>4];
|
||||
if n < int(sz) {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
b1 := s[1];
|
||||
if b1 < accept.lo || accept.hi < b1 {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
if sz == 2 {
|
||||
return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
|
||||
}
|
||||
b2 := s[2];
|
||||
if b2 < LOCB || HICB < b2 {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
if sz == 3 {
|
||||
return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
|
||||
}
|
||||
b3 := s[3];
|
||||
if b3 < LOCB || HICB < b3 {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
||||
}
|
||||
|
||||
bounds_check_error_loc :: inline proc "contextless" (using loc := #caller_location, index, count: int) {
|
||||
@@ -472,9 +789,16 @@ abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 {
|
||||
r, i := real(x), imag(x);
|
||||
return _sqrt_f64(r*r + i*i);
|
||||
}
|
||||
abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return _sqrt_f32(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return _sqrt_f64(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
|
||||
|
||||
quo_complex64 :: proc(n, m: complex64) -> complex64 {
|
||||
quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 {
|
||||
e, f: f32;
|
||||
|
||||
if abs(real(m)) >= abs(imag(m)) {
|
||||
@@ -492,7 +816,7 @@ quo_complex64 :: proc(n, m: complex64) -> complex64 {
|
||||
return complex(e, f);
|
||||
}
|
||||
|
||||
quo_complex128 :: proc(n, m: complex128) -> complex128 {
|
||||
quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 {
|
||||
e, f: f64;
|
||||
|
||||
if abs(real(m)) >= abs(imag(m)) {
|
||||
@@ -509,3 +833,55 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 {
|
||||
|
||||
return complex(e, f);
|
||||
}
|
||||
|
||||
mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
|
||||
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3;
|
||||
t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2;
|
||||
t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1;
|
||||
t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
|
||||
|
||||
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
|
||||
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
|
||||
q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q);
|
||||
r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r);
|
||||
|
||||
invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3);
|
||||
|
||||
t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2;
|
||||
t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2;
|
||||
t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2;
|
||||
t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2;
|
||||
|
||||
return quaternion(t0, t1, t2, t3);
|
||||
}
|
||||
|
||||
@@ -1,183 +1,5 @@
|
||||
package runtime
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
}
|
||||
_ctz :: proc{
|
||||
_ctz_u8,
|
||||
_ctz_u16,
|
||||
_ctz_u32,
|
||||
_ctz_u64,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
}
|
||||
_clz :: proc{
|
||||
_clz_u8,
|
||||
_clz_u16,
|
||||
_clz_u32,
|
||||
_clz_u64,
|
||||
};
|
||||
|
||||
|
||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
n := transmute([2]u64)a;
|
||||
d := transmute([2]u64)b;
|
||||
q, r: [2]u64 = ---, ---;
|
||||
sr: u32 = 0;
|
||||
|
||||
low :: ODIN_ENDIAN == "big" ? 1 : 0;
|
||||
high :: 1 - low;
|
||||
U64_BITS :: 8*size_of(u64);
|
||||
U128_BITS :: 8*size_of(u128);
|
||||
|
||||
// Special Cases
|
||||
|
||||
if n[high] == 0 {
|
||||
if d[high] == 0 {
|
||||
if rem != nil {
|
||||
rem^ = u128(n[low] % d[low]);
|
||||
}
|
||||
return u128(n[low] / d[low]);
|
||||
}
|
||||
|
||||
if rem != nil {
|
||||
rem^ = u128(n[low]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if d[low] == 0 {
|
||||
if d[high] == 0 {
|
||||
if rem != nil {
|
||||
rem^ = u128(n[high] % d[low]);
|
||||
}
|
||||
return u128(n[high] / d[low]);
|
||||
}
|
||||
if n[low] == 0 {
|
||||
if rem != nil {
|
||||
r[high] = n[high] % d[high];
|
||||
r[low] = 0;
|
||||
rem^ = transmute(u128)r;
|
||||
}
|
||||
return u128(n[high] / d[high]);
|
||||
}
|
||||
|
||||
if d[high] & (d[high]-1) == 0 {
|
||||
if rem != nil {
|
||||
r[low] = n[low];
|
||||
r[high] = n[high] & (d[high] - 1);
|
||||
rem^ = transmute(u128)r;
|
||||
}
|
||||
return u128(n[high] >> _ctz(d[high]));
|
||||
}
|
||||
|
||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
|
||||
if sr > U64_BITS - 2 {
|
||||
if rem != nil {
|
||||
rem^ = a;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sr += 1;
|
||||
|
||||
q[low] = 0;
|
||||
q[high] = n[low] << u64(U64_BITS - sr);
|
||||
r[high] = n[high] >> sr;
|
||||
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
|
||||
} else {
|
||||
if d[high] == 0 {
|
||||
if d[low] & (d[low] - 1) == 0 {
|
||||
if rem != nil {
|
||||
rem^ = u128(n[low] & (d[low] - 1));
|
||||
}
|
||||
if d[low] == 1 {
|
||||
return a;
|
||||
}
|
||||
sr = u32(_ctz(d[low]));
|
||||
q[high] = n[high] >> sr;
|
||||
q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr);
|
||||
return transmute(u128)q;
|
||||
}
|
||||
|
||||
sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high]));
|
||||
|
||||
switch {
|
||||
case sr == U64_BITS:
|
||||
q[low] = 0;
|
||||
q[high] = n[low];
|
||||
r[high] = 0;
|
||||
r[low] = n[high];
|
||||
case sr < U64_BITS:
|
||||
q[low] = 0;
|
||||
q[high] = n[low] << (U64_BITS - sr);
|
||||
r[high] = n[high] >> sr;
|
||||
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
|
||||
case:
|
||||
q[low] = n[low] << (U128_BITS - sr);
|
||||
q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS));
|
||||
r[high] = 0;
|
||||
r[low] = n[high] >> (sr - U64_BITS);
|
||||
}
|
||||
} else {
|
||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
|
||||
|
||||
if sr > U64_BITS - 1 {
|
||||
if rem != nil {
|
||||
rem^ = a;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sr += 1;
|
||||
|
||||
q[low] = 0;
|
||||
if sr == U64_BITS {
|
||||
q[high] = n[low];
|
||||
r[high] = 0;
|
||||
r[low] = n[high];
|
||||
} else {
|
||||
r[high] = n[high] >> sr;
|
||||
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
|
||||
q[high] = n[low] << (U64_BITS - sr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
carry: u32 = 0;
|
||||
r_all: u128 = ---;
|
||||
|
||||
for ; sr > 0; sr -= 1 {
|
||||
r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1));
|
||||
r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1));
|
||||
q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1));
|
||||
q[low] = (q[low] << 1) | u64(carry);
|
||||
|
||||
r_all = transmute(u128)r;
|
||||
s := i128(b - r_all - 1) >> (U128_BITS - 1);
|
||||
carry = u32(s & 1);
|
||||
r_all -= b & transmute(u128)s;
|
||||
r = transmute([2]u64)r_all;
|
||||
}
|
||||
|
||||
q_all := ((transmute(u128)q) << 1) | u128(carry);
|
||||
if rem != nil {
|
||||
rem^ = r_all;
|
||||
}
|
||||
|
||||
return q_all;
|
||||
}
|
||||
|
||||
@(link_name="__umodti3")
|
||||
umodti3 :: proc "c" (a, b: i128) -> i128 {
|
||||
s_a := a >> (128 - 1);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
//+build linux, darwin
|
||||
package runtime
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
b := byte(val);
|
||||
|
||||
p_start := uintptr(ptr);
|
||||
p_end := p_start + uintptr(max(len, 0));
|
||||
for p := p_start; p < p_end; p += 1 {
|
||||
(^byte)(p)^ = b;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package runtime
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@private
|
||||
@(link_name="_tls_index")
|
||||
_tls_index: u32;
|
||||
|
||||
@private
|
||||
@(link_name="_fltused")
|
||||
_fltused: i32 = 0x9875;
|
||||
|
||||
@(link_name="memcpy")
|
||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
foreign kernel32 {
|
||||
RtlCopyMemory :: proc "c" (dst, src: rawptr, len: int) ---
|
||||
}
|
||||
RtlCopyMemory(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@(link_name="memmove")
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
foreign kernel32 {
|
||||
RtlMoveMemory :: proc "c" (dst, src: rawptr, len: int) ---
|
||||
}
|
||||
RtlMoveMemory(dst, src, len);
|
||||
return dst;
|
||||
}
|
||||
|
||||
@(link_name="memset")
|
||||
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
|
||||
d := uintptr(ptr);
|
||||
b := byte(val);
|
||||
for i in 0..<uintptr(len) {
|
||||
(^byte)(d+i)^ = b;
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
// @(link_name="memcmp")
|
||||
// memcmp :: proc "c" (dst, src: rawptr, len: int) -> i32 {
|
||||
// if dst == nil || src == nil {
|
||||
// return 0;
|
||||
// }
|
||||
// if dst == src {
|
||||
// return 0;
|
||||
// }
|
||||
// d, s := uintptr(dst), uintptr(src);
|
||||
// n := uintptr(len);
|
||||
|
||||
// for i := uintptr(0); i < n; i += 1 {
|
||||
// x, y := (^byte)(d+i)^, (^byte)(s+i)^;
|
||||
// if x != y {
|
||||
// return x < y ? -1 : +1;
|
||||
// }
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
@@ -2,6 +2,14 @@ package runtime
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@private
|
||||
@(link_name="_tls_index")
|
||||
_tls_index: u32;
|
||||
|
||||
@private
|
||||
@(link_name="_fltused")
|
||||
_fltused: i32 = 0x9875;
|
||||
|
||||
@(link_name="memcpy")
|
||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
foreign kernel32 {
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
package runtime
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.cttz.i8") _ctz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.cttz.i16") _ctz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.cttz.i32") _ctz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.cttz.i64") _ctz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
}
|
||||
_ctz :: proc{
|
||||
_ctz_u8,
|
||||
_ctz_u16,
|
||||
_ctz_u32,
|
||||
_ctz_u64,
|
||||
};
|
||||
|
||||
@(default_calling_convention="none")
|
||||
foreign {
|
||||
@(link_name="llvm.ctlz.i8") _clz_u8 :: proc(i: u8, is_zero_undef := false) -> u8 ---
|
||||
@(link_name="llvm.ctlz.i16") _clz_u16 :: proc(i: u16, is_zero_undef := false) -> u16 ---
|
||||
@(link_name="llvm.ctlz.i32") _clz_u32 :: proc(i: u32, is_zero_undef := false) -> u32 ---
|
||||
@(link_name="llvm.ctlz.i64") _clz_u64 :: proc(i: u64, is_zero_undef := false) -> u64 ---
|
||||
}
|
||||
_clz :: proc{
|
||||
_clz_u8,
|
||||
_clz_u16,
|
||||
_clz_u32,
|
||||
_clz_u64,
|
||||
};
|
||||
|
||||
|
||||
udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
n := transmute([2]u64)a;
|
||||
d := transmute([2]u64)b;
|
||||
q, r: [2]u64 = ---, ---;
|
||||
sr: u32 = 0;
|
||||
|
||||
low :: ODIN_ENDIAN == "big" ? 1 : 0;
|
||||
high :: 1 - low;
|
||||
U64_BITS :: 8*size_of(u64);
|
||||
U128_BITS :: 8*size_of(u128);
|
||||
|
||||
// Special Cases
|
||||
|
||||
if n[high] == 0 {
|
||||
if d[high] == 0 {
|
||||
if rem != nil {
|
||||
res := n[low] % d[low];
|
||||
rem^ = u128(res);
|
||||
}
|
||||
return u128(n[low] / d[low]);
|
||||
}
|
||||
|
||||
if rem != nil {
|
||||
rem^ = u128(n[low]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if d[low] == 0 {
|
||||
if d[high] == 0 {
|
||||
if rem != nil {
|
||||
rem^ = u128(n[high] % d[low]);
|
||||
}
|
||||
return u128(n[high] / d[low]);
|
||||
}
|
||||
if n[low] == 0 {
|
||||
if rem != nil {
|
||||
r[high] = n[high] % d[high];
|
||||
r[low] = 0;
|
||||
rem^ = transmute(u128)r;
|
||||
}
|
||||
return u128(n[high] / d[high]);
|
||||
}
|
||||
|
||||
if d[high] & (d[high]-1) == 0 {
|
||||
if rem != nil {
|
||||
r[low] = n[low];
|
||||
r[high] = n[high] & (d[high] - 1);
|
||||
rem^ = transmute(u128)r;
|
||||
}
|
||||
return u128(n[high] >> _ctz(d[high]));
|
||||
}
|
||||
|
||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
|
||||
if sr > U64_BITS - 2 {
|
||||
if rem != nil {
|
||||
rem^ = a;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sr += 1;
|
||||
|
||||
q[low] = 0;
|
||||
q[high] = n[low] << u64(U64_BITS - sr);
|
||||
r[high] = n[high] >> sr;
|
||||
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
|
||||
} else {
|
||||
if d[high] == 0 {
|
||||
if d[low] & (d[low] - 1) == 0 {
|
||||
if rem != nil {
|
||||
rem^ = u128(n[low] & (d[low] - 1));
|
||||
}
|
||||
if d[low] == 1 {
|
||||
return a;
|
||||
}
|
||||
sr = u32(_ctz(d[low]));
|
||||
q[high] = n[high] >> sr;
|
||||
q[low] = (n[high] << (U64_BITS-sr)) | (n[low] >> sr);
|
||||
return transmute(u128)q;
|
||||
}
|
||||
|
||||
sr = 1 + U64_BITS + u32(_clz(d[low])) - u32(_clz(n[high]));
|
||||
|
||||
switch {
|
||||
case sr == U64_BITS:
|
||||
q[low] = 0;
|
||||
q[high] = n[low];
|
||||
r[high] = 0;
|
||||
r[low] = n[high];
|
||||
case sr < U64_BITS:
|
||||
q[low] = 0;
|
||||
q[high] = n[low] << (U64_BITS - sr);
|
||||
r[high] = n[high] >> sr;
|
||||
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
|
||||
case:
|
||||
q[low] = n[low] << (U128_BITS - sr);
|
||||
q[high] = (n[high] << (U128_BITS - sr)) | (n[low] >> (sr - U64_BITS));
|
||||
r[high] = 0;
|
||||
r[low] = n[high] >> (sr - U64_BITS);
|
||||
}
|
||||
} else {
|
||||
sr = transmute(u32)(i32(_clz(d[high])) - i32(_clz(n[high])));
|
||||
|
||||
if sr > U64_BITS - 1 {
|
||||
if rem != nil {
|
||||
rem^ = a;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
sr += 1;
|
||||
|
||||
q[low] = 0;
|
||||
if sr == U64_BITS {
|
||||
q[high] = n[low];
|
||||
r[high] = 0;
|
||||
r[low] = n[high];
|
||||
} else {
|
||||
r[high] = n[high] >> sr;
|
||||
r[low] = (n[high] << (U64_BITS - sr)) | (n[low] >> sr);
|
||||
q[high] = n[low] << (U64_BITS - sr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
carry: u32 = 0;
|
||||
r_all: u128 = ---;
|
||||
|
||||
for ; sr > 0; sr -= 1 {
|
||||
r[high] = (r[high] << 1) | (r[low] >> (U64_BITS - 1));
|
||||
r[low] = (r[low] << 1) | (q[high] >> (U64_BITS - 1));
|
||||
q[high] = (q[high] << 1) | (q[low] >> (U64_BITS - 1));
|
||||
q[low] = (q[low] << 1) | u64(carry);
|
||||
|
||||
r_all = transmute(u128)r;
|
||||
s := i128(b - r_all - 1) >> (U128_BITS - 1);
|
||||
carry = u32(s & 1);
|
||||
r_all -= b & transmute(u128)s;
|
||||
r = transmute([2]u64)r_all;
|
||||
}
|
||||
|
||||
q_all := ((transmute(u128)q) << 1) | u128(carry);
|
||||
if rem != nil {
|
||||
rem^ = r_all;
|
||||
}
|
||||
|
||||
return q_all;
|
||||
}
|
||||
+134
-71
@@ -1,6 +1,7 @@
|
||||
package sort
|
||||
|
||||
import "core:mem"
|
||||
import "intrinsics"
|
||||
|
||||
bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil);
|
||||
@@ -26,7 +27,7 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
}
|
||||
}
|
||||
|
||||
bubble_sort :: proc(array: $A/[]$T) {
|
||||
bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
count := len(array);
|
||||
|
||||
init_j, last_j := 0, count-1;
|
||||
@@ -73,7 +74,7 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
quick_sort_proc(a[i:n], f);
|
||||
}
|
||||
|
||||
quick_sort :: proc(array: $A/[]$T) {
|
||||
quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
a := array;
|
||||
n := len(a);
|
||||
if n < 2 do return;
|
||||
@@ -96,99 +97,159 @@ quick_sort :: proc(array: $A/[]$T) {
|
||||
quick_sort(a[i:n]);
|
||||
}
|
||||
|
||||
_log2 :: proc(n: int) -> int {
|
||||
_log2 :: proc(x: int) -> int {
|
||||
res := 0;
|
||||
for ; n != 0; n >>= 1 do res += 1;
|
||||
for n := x; n != 0; n >>= 1 do res += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
merge_slices :: proc(arr1, arr2, out: A, f: proc(T, T) -> int) {
|
||||
N1, N2 := len(arr1), len(arr2);
|
||||
i, j := 0, 0;
|
||||
for k in 0..<N1+N2 {
|
||||
if j == N2 || i < N1 && j < N2 && f(arr1[i], arr2[j]) < 0 {
|
||||
out[k] = arr1[i];
|
||||
i += 1;
|
||||
merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) where intrinsics.type_is_ordered(T) {
|
||||
merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) {
|
||||
s, m := start, mid;
|
||||
|
||||
s2 := m + 1;
|
||||
if f(a[m], a[s2]) <= 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
for s <= m && s2 <= end {
|
||||
if f(a[s], a[s2]) <= 0 {
|
||||
s += 1;
|
||||
} else {
|
||||
out[k] = arr2[j];
|
||||
j += 1;
|
||||
v := a[s2];
|
||||
i := s2;
|
||||
|
||||
for i != s {
|
||||
a[i] = a[i-1];
|
||||
i -= 1;
|
||||
}
|
||||
a[s] = v;
|
||||
|
||||
s += 1;
|
||||
m += 1;
|
||||
s2 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal_sort :: proc(a: A, l, r: int, f: proc(T, T) -> int) {
|
||||
if l < r {
|
||||
m := l + (r - l) / 2;
|
||||
|
||||
assert(f != nil);
|
||||
|
||||
arr1 := array;
|
||||
N := len(arr1);
|
||||
arr2 := make([]T, N);
|
||||
defer free(arr2);
|
||||
|
||||
a, b, m, M := N/2, N, 1, _log2(N);
|
||||
|
||||
for i in 0..M {
|
||||
for j in 0..<a {
|
||||
k := 2*j*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:], f);
|
||||
internal_sort(a, l, m, f);
|
||||
internal_sort(a, m+1, r, f);
|
||||
merge(a, l, m, r, f);
|
||||
}
|
||||
if N-b > m {
|
||||
k := 2*a*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:], f);
|
||||
} else {
|
||||
copy(arr2[b:N], arr1[b:N]);
|
||||
}
|
||||
arr1, arr2 = arr2, arr1;
|
||||
m <<= 1;
|
||||
a >>= 1;
|
||||
b = a << uint(i) << 2;
|
||||
}
|
||||
|
||||
if M & 1 == 0 do copy(arr2, arr1);
|
||||
internal_sort(array, 0, len(array)-1, f);
|
||||
}
|
||||
|
||||
merge_sort :: proc(array: $A/[]$T) {
|
||||
merge_slices :: proc(arr1, arr2, out: A) {
|
||||
N1, N2 := len(arr1), len(arr2);
|
||||
i, j := 0, 0;
|
||||
for k in 0..<N1+N2 {
|
||||
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
|
||||
out[k] = arr1[i];
|
||||
i += 1;
|
||||
merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
merge :: proc(a: A, start, mid, end: int) {
|
||||
s, m := start, mid;
|
||||
|
||||
s2 := m + 1;
|
||||
if a[m] <= a[s2] {
|
||||
return;
|
||||
}
|
||||
|
||||
for s <= m && s2 <= end {
|
||||
if a[s] <= a[s2] {
|
||||
s += 1;
|
||||
} else {
|
||||
out[k] = arr2[j];
|
||||
j += 1;
|
||||
v := a[s2];
|
||||
i := s2;
|
||||
|
||||
for i != s {
|
||||
a[i] = a[i-1];
|
||||
i -= 1;
|
||||
}
|
||||
a[s] = v;
|
||||
|
||||
s += 1;
|
||||
m += 1;
|
||||
s2 += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
internal_sort :: proc(a: A, l, r: int) {
|
||||
if l < r {
|
||||
m := l + (r - l) / 2;
|
||||
|
||||
arr1 := array;
|
||||
N := len(arr1);
|
||||
arr2 := make([]T, N);
|
||||
defer free(arr2);
|
||||
|
||||
a, b, m, M := N/2, N, 1, _log2(N);
|
||||
|
||||
for i in 0..M {
|
||||
for j in 0..<a {
|
||||
k := 2*j*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m:k+m+m], arr2[k:]);
|
||||
internal_sort(a, l, m);
|
||||
internal_sort(a, m+1, r);
|
||||
merge(a, l, m, r);
|
||||
}
|
||||
if N-b > m {
|
||||
k := 2*a*m;
|
||||
merge_slices(arr1[k:k+m], arr1[k+m : k+m+(N-b)&(m-1)], arr2[k:]);
|
||||
} else {
|
||||
copy(arr2[b:N], arr1[b:N]);
|
||||
}
|
||||
arr1, arr2 = arr2, arr1;
|
||||
m <<= 1;
|
||||
a >>= 1;
|
||||
b = a << uint(i) << 2;
|
||||
}
|
||||
|
||||
if M & 1 == 0 do copy(arr2, arr1);
|
||||
internal_sort(array, 0, len(array)-1);
|
||||
}
|
||||
|
||||
|
||||
heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check {
|
||||
p := pi;
|
||||
v := a[p];
|
||||
m := p*2 + 1;
|
||||
for m <= n {
|
||||
if (m < n) && f(a[m+1], a[m]) > 0 {
|
||||
m += 1;
|
||||
}
|
||||
if f(v, a[m]) >= 0 {
|
||||
break;
|
||||
}
|
||||
a[p] = a[m];
|
||||
p = m;
|
||||
m += m+1;
|
||||
a[p] = v;
|
||||
}
|
||||
}
|
||||
|
||||
n := len(array);
|
||||
if n == 0 do return;
|
||||
|
||||
for i := n/2; i >= 0; i -= 1 {
|
||||
sift_proc(array, i, n-1, f);
|
||||
}
|
||||
|
||||
for i := n-1; i >= 1; i -= 1 {
|
||||
array[0], array[i] = array[i], array[0];
|
||||
sift_proc(array, 0, i-1, f);
|
||||
}
|
||||
}
|
||||
|
||||
heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) {
|
||||
sift :: proc(a: A, pi: int, n: int) #no_bounds_check {
|
||||
p := pi;
|
||||
v := a[p];
|
||||
m := p*2 + 1;
|
||||
for m <= n {
|
||||
if (m < n) && (a[m+1] > a[m]) {
|
||||
m += 1;
|
||||
}
|
||||
if v >= a[m] {
|
||||
break;
|
||||
}
|
||||
a[p] = a[m];
|
||||
p = m;
|
||||
m += m+1;
|
||||
a[p] = v;
|
||||
}
|
||||
}
|
||||
|
||||
n := len(array);
|
||||
if n == 0 do return;
|
||||
|
||||
for i := n/2; i >= 0; i -= 1 {
|
||||
sift(array, i, n-1);
|
||||
}
|
||||
|
||||
for i := n-1; i >= 1; i -= 1 {
|
||||
array[0], array[i] = array[i], array[0];
|
||||
sift(array, 0, i-1);
|
||||
}
|
||||
}
|
||||
|
||||
compare_bools :: proc(a, b: bool) -> int {
|
||||
switch {
|
||||
case !a && b: return -1;
|
||||
@@ -221,5 +282,7 @@ compare_f64s :: proc(a, b: f64) -> int {
|
||||
return 0;
|
||||
}
|
||||
compare_strings :: proc(a, b: string) -> int {
|
||||
return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b)));
|
||||
x := transmute(mem.Raw_String)a;
|
||||
y := transmute(mem.Raw_String)b;
|
||||
return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// Multiple precision decimal numbers
|
||||
// NOTE: This is only for floating point printing and nothing else
|
||||
package decimal
|
||||
package strconv_decimal
|
||||
|
||||
Decimal :: struct {
|
||||
digits: [384]byte, // big-endian digits
|
||||
@@ -20,29 +20,29 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
|
||||
// TODO(bill): make this work with a buffer that's not big enough
|
||||
assert(len(buf) >= n);
|
||||
buf = buf[0:n];
|
||||
b := buf[0:n];
|
||||
|
||||
if a.count == 0 {
|
||||
buf[0] = '0';
|
||||
return string(buf[0:1]);
|
||||
b[0] = '0';
|
||||
return string(b[0:1]);
|
||||
}
|
||||
|
||||
w := 0;
|
||||
if a.decimal_point <= 0 {
|
||||
buf[w] = '0'; w += 1;
|
||||
buf[w] = '.'; w += 1;
|
||||
w += digit_zero(buf[w : w-a.decimal_point]);
|
||||
w += copy(buf[w:], a.digits[0:a.count]);
|
||||
b[w] = '0'; w += 1;
|
||||
b[w] = '.'; w += 1;
|
||||
w += digit_zero(b[w : w-a.decimal_point]);
|
||||
w += copy(b[w:], a.digits[0:a.count]);
|
||||
} else if a.decimal_point < a.count {
|
||||
w += copy(buf[w:], a.digits[0:a.decimal_point]);
|
||||
buf[w] = '.'; w += 1;
|
||||
w += copy(buf[w:], a.digits[a.decimal_point : a.count]);
|
||||
w += copy(b[w:], a.digits[0:a.decimal_point]);
|
||||
b[w] = '.'; w += 1;
|
||||
w += copy(b[w:], a.digits[a.decimal_point : a.count]);
|
||||
} else {
|
||||
w += copy(buf[w:], a.digits[0:a.count]);
|
||||
w += digit_zero(buf[w : w+a.decimal_point-a.count]);
|
||||
w += copy(b[w:], a.digits[0:a.count]);
|
||||
w += digit_zero(b[w : w+a.decimal_point-a.count]);
|
||||
}
|
||||
|
||||
return string(buf[0:w]);
|
||||
return string(b[0:w]);
|
||||
}
|
||||
|
||||
// trim trailing zeros
|
||||
@@ -56,10 +56,10 @@ trim :: proc(a: ^Decimal) {
|
||||
}
|
||||
|
||||
|
||||
assign :: proc(a: ^Decimal, i: u64) {
|
||||
assign :: proc(a: ^Decimal, idx: u64) {
|
||||
buf: [64]byte;
|
||||
n := 0;
|
||||
for i > 0 {
|
||||
for i := idx; i > 0; {
|
||||
j := i/10;
|
||||
i -= 10*j;
|
||||
buf[n] = byte('0'+i);
|
||||
@@ -175,11 +175,11 @@ shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
trim(a);
|
||||
}
|
||||
|
||||
shift :: proc(a: ^Decimal, k: int) {
|
||||
shift :: proc(a: ^Decimal, i: int) {
|
||||
uint_size :: 8*size_of(uint);
|
||||
max_shift :: uint_size-4;
|
||||
|
||||
switch {
|
||||
switch k := i; {
|
||||
case a.count == 0:
|
||||
// no need to update
|
||||
case k > 0:
|
||||
+23
-179
@@ -1,13 +1,6 @@
|
||||
package strconv
|
||||
|
||||
using import "core:decimal"
|
||||
|
||||
Int_Flag :: enum {
|
||||
Prefix,
|
||||
Plus,
|
||||
Space,
|
||||
}
|
||||
Int_Flags :: bit_set[Int_Flag];
|
||||
import "decimal"
|
||||
|
||||
Decimal_Slice :: struct {
|
||||
digits: []byte,
|
||||
@@ -28,7 +21,7 @@ _f32_info := Float_Info{23, 8, -127};
|
||||
_f64_info := Float_Info{52, 11, -1023};
|
||||
|
||||
|
||||
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte {
|
||||
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
|
||||
bits: u64;
|
||||
flt: ^Float_Info;
|
||||
switch bit_size {
|
||||
@@ -56,7 +49,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
} else {
|
||||
s = "+Inf";
|
||||
}
|
||||
n := copy(buf, cast([]byte)s);
|
||||
n := copy(buf, s);
|
||||
return buf[:n];
|
||||
|
||||
case 0: // denormalized
|
||||
@@ -68,11 +61,12 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
|
||||
exp += flt.bias;
|
||||
|
||||
d_: Decimal;
|
||||
d_: decimal.Decimal;
|
||||
d := &d_;
|
||||
assign(d, mant);
|
||||
shift(d, exp - int(flt.mantbits));
|
||||
decimal.assign(d, mant);
|
||||
decimal.shift(d, exp - int(flt.mantbits));
|
||||
digs: Decimal_Slice;
|
||||
prec := precision;
|
||||
shortest := prec < 0;
|
||||
if shortest {
|
||||
round_shortest(d, mant, exp, flt);
|
||||
@@ -84,13 +78,13 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
}
|
||||
} else {
|
||||
switch fmt {
|
||||
case 'e', 'E': round(d, prec+1);
|
||||
case 'f', 'F': round(d, d.decimal_point+prec);
|
||||
case 'e', 'E': decimal.round(d, prec+1);
|
||||
case 'f', 'F': decimal.round(d, d.decimal_point+prec);
|
||||
case 'g', 'G':
|
||||
if prec == 0 {
|
||||
prec = 1;
|
||||
}
|
||||
round(d, prec);
|
||||
decimal.round(d, prec);
|
||||
}
|
||||
|
||||
digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
|
||||
@@ -100,11 +94,11 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
|
||||
|
||||
|
||||
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
|
||||
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
|
||||
Buffer :: struct {
|
||||
b: []byte,
|
||||
n: int,
|
||||
}
|
||||
};
|
||||
|
||||
to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n];
|
||||
add_bytes :: proc(buf: ^Buffer, bytes: ..byte) {
|
||||
@@ -112,6 +106,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
|
||||
}
|
||||
|
||||
b := Buffer{b = buf};
|
||||
prec := precision;
|
||||
|
||||
switch fmt {
|
||||
case 'f', 'F':
|
||||
@@ -218,7 +213,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
|
||||
|
||||
}
|
||||
|
||||
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
if mant == 0 { // If mantissa is zero, the number is zero
|
||||
d.count = 0;
|
||||
return;
|
||||
@@ -236,9 +231,9 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
return;
|
||||
}
|
||||
|
||||
upper_: Decimal; upper := &upper_;
|
||||
assign(upper, 2*mant - 1);
|
||||
shift(upper, exp - int(flt.mantbits) - 1);
|
||||
upper_: decimal.Decimal; upper := &upper_;
|
||||
decimal.assign(upper, 2*mant - 1);
|
||||
decimal.shift(upper, exp - int(flt.mantbits) - 1);
|
||||
|
||||
mantlo: u64;
|
||||
explo: int;
|
||||
@@ -249,9 +244,9 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
mantlo = 2*mant - 1;
|
||||
explo = exp-1;
|
||||
}
|
||||
lower_: Decimal; lower := &lower_;
|
||||
assign(lower, 2*mantlo + 1);
|
||||
shift(lower, explo - int(flt.mantbits) - 1);
|
||||
lower_: decimal.Decimal; lower := &lower_;
|
||||
decimal.assign(lower, 2*mantlo + 1);
|
||||
decimal.shift(lower, explo - int(flt.mantbits) - 1);
|
||||
|
||||
inclusive := mant%2 == 0;
|
||||
|
||||
@@ -270,168 +265,17 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
|
||||
|
||||
if ok_round_down && ok_round_up {
|
||||
round(d, i+1);
|
||||
decimal.round(d, i+1);
|
||||
return;
|
||||
}
|
||||
if ok_round_down {
|
||||
round_down(d, i+1);
|
||||
decimal.round_down(d, i+1);
|
||||
return;
|
||||
}
|
||||
if ok_round_up {
|
||||
round_up(d, i+1);
|
||||
decimal.round_up(d, i+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MAX_BASE :: 32;
|
||||
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
|
||||
if is_signed {
|
||||
switch bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return u, neg;
|
||||
}
|
||||
|
||||
append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
neg: bool;
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
u, neg = is_integer_negative(u, is_signed, bit_size);
|
||||
b := u64(base);
|
||||
for u >= b {
|
||||
i-=1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i-=1; a[i] = digits[u % b];
|
||||
|
||||
if .Prefix in flags {
|
||||
ok := true;
|
||||
switch base {
|
||||
case 2: i-=1; a[i] = 'b';
|
||||
case 8: i-=1; a[i] = 'o';
|
||||
case 10: i-=1; a[i] = 'd';
|
||||
case 12: i-=1; a[i] = 'z';
|
||||
case 16: i-=1; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i-=1; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case neg:
|
||||
i-=1; a[i] = '-';
|
||||
case .Plus in flags:
|
||||
i-=1; a[i] = '+';
|
||||
case .Space in flags:
|
||||
i-=1; a[i] = ' ';
|
||||
}
|
||||
|
||||
out := a[i:];
|
||||
copy(buf, out);
|
||||
return string(buf[0:len(out)]);
|
||||
}
|
||||
|
||||
is_integer_negative_128 :: proc(u: u128, is_signed: bool, bit_size: int) -> (unsigned: u128, neg: bool) {
|
||||
if is_signed {
|
||||
switch bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 128:
|
||||
i := i128(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return u, neg;
|
||||
}
|
||||
|
||||
|
||||
append_bits_128 :: proc(buf: []byte, u: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
neg: bool;
|
||||
a: [140]byte;
|
||||
i := len(a);
|
||||
u, neg = is_integer_negative_128(u, is_signed, bit_size);
|
||||
b := u128(base);
|
||||
for u >= b {
|
||||
i-=1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i-=1; a[i] = digits[u % b];
|
||||
|
||||
if .Prefix in flags {
|
||||
ok := true;
|
||||
switch base {
|
||||
case 2: i-=1; a[i] = 'b';
|
||||
case 8: i-=1; a[i] = 'o';
|
||||
case 10: i-=1; a[i] = 'd';
|
||||
case 12: i-=1; a[i] = 'z';
|
||||
case 16: i-=1; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i-=1; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case neg:
|
||||
i-=1; a[i] = '-';
|
||||
case .Plus in flags:
|
||||
i-=1; a[i] = '+';
|
||||
case .Space in flags:
|
||||
i-=1; a[i] = ' ';
|
||||
}
|
||||
|
||||
out := a[i:];
|
||||
copy(buf, out);
|
||||
return string(buf[0:len(out)]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package strconv
|
||||
|
||||
Int_Flag :: enum {
|
||||
Prefix,
|
||||
Plus,
|
||||
Space,
|
||||
}
|
||||
Int_Flags :: bit_set[Int_Flag];
|
||||
|
||||
MAX_BASE :: 32;
|
||||
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) {
|
||||
u = x;
|
||||
if is_signed {
|
||||
switch bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
u = u64(abs(i64(i)));
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
a: [129]byte;
|
||||
i := len(a);
|
||||
u, neg := is_integer_negative(x, is_signed, bit_size);
|
||||
b := u64(base);
|
||||
for u >= b {
|
||||
i-=1; a[i] = digits[u % b];
|
||||
u /= b;
|
||||
}
|
||||
i-=1; a[i] = digits[u % b];
|
||||
|
||||
if .Prefix in flags {
|
||||
ok := true;
|
||||
switch base {
|
||||
case 2: i-=1; a[i] = 'b';
|
||||
case 8: i-=1; a[i] = 'o';
|
||||
case 10: i-=1; a[i] = 'd';
|
||||
case 12: i-=1; a[i] = 'z';
|
||||
case 16: i-=1; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i-=1; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case neg:
|
||||
i-=1; a[i] = '-';
|
||||
case .Plus in flags:
|
||||
i-=1; a[i] = '+';
|
||||
case .Space in flags:
|
||||
i-=1; a[i] = ' ';
|
||||
}
|
||||
|
||||
out := a[i:];
|
||||
copy(buf, out);
|
||||
return string(buf[0:len(out)]);
|
||||
}
|
||||
|
||||
is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) {
|
||||
u = x;
|
||||
if is_signed {
|
||||
switch bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case 128:
|
||||
i := i128(u);
|
||||
neg = i < 0;
|
||||
u = u128(abs(i128(i)));
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// import "core:runtime"
|
||||
|
||||
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
a: [140]byte;
|
||||
i := len(a);
|
||||
u, neg := is_integer_negative_128(x, is_signed, bit_size);
|
||||
b := u128(base);
|
||||
for u >= b && i >= 0 {
|
||||
i-=1;
|
||||
// rem: u128;
|
||||
// u = runtime.udivmod128(u, b, &rem);
|
||||
// u /= b;
|
||||
rem := u % b;
|
||||
u /= b;
|
||||
|
||||
idx := u32(rem);
|
||||
a[i] = digits[idx];
|
||||
}
|
||||
i-=1; a[i] = digits[u64(u % b)];
|
||||
|
||||
if .Prefix in flags {
|
||||
ok := true;
|
||||
switch base {
|
||||
case 2: i-=1; a[i] = 'b';
|
||||
case 8: i-=1; a[i] = 'o';
|
||||
case 10: i-=1; a[i] = 'd';
|
||||
case 12: i-=1; a[i] = 'z';
|
||||
case 16: i-=1; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i-=1; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case neg:
|
||||
i-=1; a[i] = '-';
|
||||
case .Plus in flags:
|
||||
i-=1; a[i] = '+';
|
||||
case .Space in flags:
|
||||
i-=1; a[i] = ' ';
|
||||
}
|
||||
|
||||
out := a[i:];
|
||||
copy(buf, out);
|
||||
return string(buf[0:len(out)]);
|
||||
}
|
||||
@@ -23,7 +23,8 @@ _digit_value :: proc(r: rune) -> int {
|
||||
return v;
|
||||
}
|
||||
|
||||
parse_i64 :: proc(s: string) -> i64 {
|
||||
parse_i64 :: proc(str: string) -> i64 {
|
||||
s := str;
|
||||
neg := false;
|
||||
if len(s) > 1 {
|
||||
switch s[0] {
|
||||
@@ -66,7 +67,8 @@ parse_i64 :: proc(s: string) -> i64 {
|
||||
return value;
|
||||
}
|
||||
|
||||
parse_u64 :: proc(s: string) -> u64 {
|
||||
parse_u64 :: proc(str: string) -> u64 {
|
||||
s := str;
|
||||
neg := false;
|
||||
if len(s) > 1 && s[0] == '+' {
|
||||
s = s[1:];
|
||||
@@ -102,7 +104,7 @@ parse_u64 :: proc(s: string) -> u64 {
|
||||
parse_int :: proc(s: string) -> int {
|
||||
return int(parse_i64(s));
|
||||
}
|
||||
parse_uint :: proc(s: string, base: int) -> uint {
|
||||
parse_uint :: proc(s: string) -> uint {
|
||||
return uint(parse_u64(s));
|
||||
}
|
||||
|
||||
@@ -185,8 +187,8 @@ parse_f64 :: proc(s: string) -> f64 {
|
||||
|
||||
append_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
n := 0;
|
||||
if b do n = copy(buf, cast([]byte)"true");
|
||||
else do n = copy(buf, cast([]byte)"false");
|
||||
if b do n = copy(buf, "true");
|
||||
else do n = copy(buf, "false");
|
||||
return string(buf[:n]);
|
||||
}
|
||||
|
||||
@@ -203,13 +205,17 @@ itoa :: proc(buf: []byte, i: int) -> string {
|
||||
atoi :: proc(s: string) -> int {
|
||||
return parse_int(s);
|
||||
}
|
||||
atof :: proc(s: string) -> f64 {
|
||||
return parse_f64(s);
|
||||
}
|
||||
|
||||
ftoa :: append_float;
|
||||
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
|
||||
}
|
||||
|
||||
|
||||
quote :: proc(buf: []byte, s: string) -> string {
|
||||
quote :: proc(buf: []byte, str: string) -> string {
|
||||
write_byte :: inline proc(buf: []byte, i: ^int, bytes: ..byte) {
|
||||
if i^ >= len(buf) do return;
|
||||
n := copy(buf[i^:], bytes[:]);
|
||||
@@ -222,6 +228,7 @@ quote :: proc(buf: []byte, s: string) -> string {
|
||||
|
||||
c :: '"';
|
||||
i := 0;
|
||||
s := str;
|
||||
|
||||
write_byte(buf, &i, c);
|
||||
for width := 0; len(s) > 0; s = s[width:] {
|
||||
@@ -253,7 +260,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
|
||||
}
|
||||
write_string :: inline proc(buf: []byte, i: ^int, s: string) {
|
||||
if i^ < len(buf) {
|
||||
n := copy(buf[i^:], cast([]byte)s);
|
||||
n := copy(buf[i^:], s);
|
||||
i^ += n;
|
||||
}
|
||||
}
|
||||
|
||||
+16
-12
@@ -21,6 +21,10 @@ grow_builder :: proc(b: ^Builder, cap: int) {
|
||||
reserve(&b.buf, cap);
|
||||
}
|
||||
|
||||
reset_builder :: proc(b: ^Builder) {
|
||||
clear(&b.buf);
|
||||
}
|
||||
|
||||
builder_from_slice :: proc(backing: []byte) -> Builder {
|
||||
s := transmute(mem.Raw_Slice)backing;
|
||||
d := mem.Raw_Dynamic_Array{
|
||||
@@ -58,7 +62,7 @@ write_rune :: proc(b: ^Builder, r: rune) -> int {
|
||||
}
|
||||
|
||||
write_string :: proc(b: ^Builder, s: string) {
|
||||
write_bytes(b, cast([]byte)s);
|
||||
write_bytes(b, transmute([]byte)s);
|
||||
}
|
||||
|
||||
write_bytes :: proc(b: ^Builder, x: []byte) {
|
||||
@@ -68,9 +72,9 @@ write_bytes :: proc(b: ^Builder, x: []byte) {
|
||||
@(private, static)
|
||||
DIGITS_LOWER := "0123456789abcdefx";
|
||||
|
||||
write_quoted_string :: proc(b: ^Builder, s: string, quote: byte = '"') {
|
||||
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') {
|
||||
write_byte(b, quote);
|
||||
for width := 0; len(s) > 0; s = s[width:] {
|
||||
for width, s := 0, str; len(s) > 0; s = s[width:] {
|
||||
r := rune(s[0]);
|
||||
width = 1;
|
||||
if r >= utf8.RUNE_SELF {
|
||||
@@ -166,27 +170,27 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
|
||||
case '\t': write_string(b, `\t`);
|
||||
case '\v': write_string(b, `\v`);
|
||||
case:
|
||||
switch {
|
||||
case r < ' ':
|
||||
switch c := r; {
|
||||
case c < ' ':
|
||||
write_byte(b, '\\');
|
||||
write_byte(b, 'x');
|
||||
write_byte(b, DIGITS_LOWER[byte(r)>>4]);
|
||||
write_byte(b, DIGITS_LOWER[byte(r)&0xf]);
|
||||
write_byte(b, DIGITS_LOWER[byte(c)>>4]);
|
||||
write_byte(b, DIGITS_LOWER[byte(c)&0xf]);
|
||||
|
||||
case r > utf8.MAX_RUNE:
|
||||
r = 0xfffd;
|
||||
case c > utf8.MAX_RUNE:
|
||||
c = 0xfffd;
|
||||
fallthrough;
|
||||
case r < 0x10000:
|
||||
case c < 0x10000:
|
||||
write_byte(b, '\\');
|
||||
write_byte(b, 'u');
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
|
||||
write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
|
||||
}
|
||||
case:
|
||||
write_byte(b, '\\');
|
||||
write_byte(b, 'U');
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
write_byte(b, DIGITS_LOWER[r>>uint(s) & 0xf]);
|
||||
write_byte(b, DIGITS_LOWER[c>>uint(s) & 0xf]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+199
-36
@@ -5,14 +5,14 @@ import "core:unicode/utf8"
|
||||
|
||||
clone :: proc(s: string, allocator := context.allocator) -> string {
|
||||
c := make([]byte, len(s)+1, allocator);
|
||||
copy(c, cast([]byte)s);
|
||||
copy(c, s);
|
||||
c[len(s)] = 0;
|
||||
return string(c[:len(s)]);
|
||||
}
|
||||
|
||||
clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
|
||||
c := make([]byte, len(s)+1, allocator);
|
||||
copy(c, cast([]byte)s);
|
||||
copy(c, s);
|
||||
c[len(s)] = 0;
|
||||
return cstring(&c[0]);
|
||||
}
|
||||
@@ -20,7 +20,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
|
||||
@(deprecated="Please use 'strings.clone'")
|
||||
new_string :: proc(s: string, allocator := context.allocator) -> string {
|
||||
c := make([]byte, len(s)+1, allocator);
|
||||
copy(c, cast([]byte)s);
|
||||
copy(c, s);
|
||||
c[len(s)] = 0;
|
||||
return string(c[:len(s)]);
|
||||
}
|
||||
@@ -28,7 +28,7 @@ new_string :: proc(s: string, allocator := context.allocator) -> string {
|
||||
@(deprecated="Please use 'strings.clone_to_cstring'")
|
||||
new_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
|
||||
c := make([]byte, len(s)+1, allocator);
|
||||
copy(c, cast([]byte)s);
|
||||
copy(c, s);
|
||||
c[len(s)] = 0;
|
||||
return cstring(&c[0]);
|
||||
}
|
||||
@@ -42,8 +42,18 @@ string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
|
||||
return transmute(string)mem.Raw_String{ptr, len};
|
||||
}
|
||||
|
||||
ptr_from_string :: proc(str: string) -> ^byte {
|
||||
d := transmute(mem.Raw_String)str;
|
||||
return d.data;
|
||||
}
|
||||
|
||||
unsafe_string_to_cstring :: proc(str: string) -> cstring {
|
||||
d := transmute(mem.Raw_String)str;
|
||||
return cstring(d.data);
|
||||
}
|
||||
|
||||
compare :: proc(lhs, rhs: string) -> int {
|
||||
return mem.compare(cast([]byte)lhs, cast([]byte)rhs);
|
||||
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs);
|
||||
}
|
||||
|
||||
contains_rune :: proc(s: string, r: rune) -> int {
|
||||
@@ -67,7 +77,8 @@ rune_count :: proc(s: string) -> int {
|
||||
}
|
||||
|
||||
|
||||
equal_fold :: proc(s, t: string) -> bool {
|
||||
equal_fold :: proc(u, v: string) -> bool {
|
||||
s, t := u, v;
|
||||
loop: for s != "" && t != "" {
|
||||
sr, tr: rune;
|
||||
if s[0] < utf8.RUNE_SELF {
|
||||
@@ -129,10 +140,10 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string
|
||||
}
|
||||
|
||||
b := make([]byte, n, allocator);
|
||||
i := copy(b, cast([]byte)a[0]);
|
||||
i := copy(b, a[0]);
|
||||
for s in a[1:] {
|
||||
i += copy(b[i:], cast([]byte)sep);
|
||||
i += copy(b[i:], cast([]byte)s);
|
||||
i += copy(b[i:], sep);
|
||||
i += copy(b[i:], s);
|
||||
}
|
||||
return string(b);
|
||||
}
|
||||
@@ -149,11 +160,78 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
|
||||
b := make([]byte, n, allocator);
|
||||
i := 0;
|
||||
for s in a {
|
||||
i += copy(b[i:], cast([]byte)s);
|
||||
i += copy(b[i:], s);
|
||||
}
|
||||
return string(b);
|
||||
}
|
||||
|
||||
@private
|
||||
_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
|
||||
s, n := s_, n_;
|
||||
|
||||
if n == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if sep == "" {
|
||||
l := utf8.rune_count_in_string(s);
|
||||
if n < 0 || n > l {
|
||||
n = l;
|
||||
}
|
||||
|
||||
res := make([dynamic]string, n, allocator);
|
||||
for i := 0; i < n-1; i += 1 {
|
||||
_, w := utf8.decode_rune_in_string(s);
|
||||
res[i] = s[:w];
|
||||
s = s[w:];
|
||||
}
|
||||
if n > 0 {
|
||||
res[n-1] = s;
|
||||
}
|
||||
return res[:];
|
||||
}
|
||||
|
||||
if n < 0 {
|
||||
n = count(s, sep) + 1;
|
||||
}
|
||||
|
||||
res := make([dynamic]string, n, allocator);
|
||||
|
||||
n -= 1;
|
||||
|
||||
i := 0;
|
||||
for ; i < n; i += 1 {
|
||||
m := index(s, sep);
|
||||
if m < 0 {
|
||||
break;
|
||||
}
|
||||
res[i] = s[:m+sep_save];
|
||||
s = s[m+len(sep):];
|
||||
}
|
||||
res[i] = s;
|
||||
|
||||
return res[:i+1];
|
||||
}
|
||||
|
||||
split :: inline proc(s, sep: string, allocator := context.allocator) -> []string {
|
||||
return _split(s, sep, 0, -1, allocator);
|
||||
}
|
||||
|
||||
split_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
|
||||
return _split(s, sep, 0, n, allocator);
|
||||
}
|
||||
|
||||
split_after :: inline proc(s, sep: string, allocator := context.allocator) -> []string {
|
||||
return _split(s, sep, len(sep), -1, allocator);
|
||||
}
|
||||
|
||||
split_after_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
|
||||
return _split(s, sep, len(sep), n, allocator);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
index_byte :: proc(s: string, c: byte) -> int {
|
||||
for i := 0; i < len(s); i += 1 {
|
||||
if s[i] == c do return i;
|
||||
@@ -169,7 +247,25 @@ last_index_byte :: proc(s: string, c: byte) -> int {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@private PRIME_RABIN_KARP :: 16777619;
|
||||
|
||||
index :: proc(s, substr: string) -> int {
|
||||
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
|
||||
for i := 0; i < len(s); i += 1 {
|
||||
hash = hash*PRIME_RABIN_KARP + u32(s[i]);
|
||||
}
|
||||
sq := u32(PRIME_RABIN_KARP);
|
||||
for i := len(s); i > 0; i >>= 1 {
|
||||
if (i & 1) != 0 {
|
||||
pow *= sq;
|
||||
}
|
||||
sq *= sq;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
n := len(substr);
|
||||
switch {
|
||||
case n == 0:
|
||||
@@ -185,9 +281,68 @@ index :: proc(s, substr: string) -> int {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for i := 0; i < len(s)-n+1; i += 1 {
|
||||
x := s[i:i+n];
|
||||
if x == substr {
|
||||
hash, pow := hash_str_rabin_karp(substr);
|
||||
h: u32;
|
||||
for i := 0; i < n; i += 1 {
|
||||
h = h*PRIME_RABIN_KARP + u32(s[i]);
|
||||
}
|
||||
if h == hash && s[:n] == substr {
|
||||
return 0;
|
||||
}
|
||||
for i := n; i < len(s); /**/ {
|
||||
h *= PRIME_RABIN_KARP;
|
||||
h += u32(s[i]);
|
||||
h -= pow * u32(s[i-n]);
|
||||
i += 1;
|
||||
if h == hash && s[i-n:i] == substr {
|
||||
return i - n;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
last_index :: proc(s, substr: string) -> int {
|
||||
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
|
||||
for i := len(s) - 1; i >= 0; i -= 1 {
|
||||
hash = hash*PRIME_RABIN_KARP + u32(s[i]);
|
||||
}
|
||||
sq := u32(PRIME_RABIN_KARP);
|
||||
for i := len(s); i > 0; i >>= 1 {
|
||||
if (i & 1) != 0 {
|
||||
pow *= sq;
|
||||
}
|
||||
sq *= sq;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
n := len(substr);
|
||||
switch {
|
||||
case n == 0:
|
||||
return len(s);
|
||||
case n == 1:
|
||||
return last_index_byte(s, substr[0]);
|
||||
case n == len(s):
|
||||
return substr == s ? 0 : -1;
|
||||
case n > len(s):
|
||||
return -1;
|
||||
}
|
||||
|
||||
hash, pow := hash_str_rabin_karp_reverse(substr);
|
||||
last := len(s) - n;
|
||||
h: u32;
|
||||
for i := len(s)-1; i >= last; i -= 1 {
|
||||
h = h*PRIME_RABIN_KARP + u32(s[i]);
|
||||
}
|
||||
if h == hash && s[last:] == substr {
|
||||
return last;
|
||||
}
|
||||
|
||||
for i := last-1; i >= 0; i -= 1 {
|
||||
h *= PRIME_RABIN_KARP;
|
||||
h += u32(s[i]);
|
||||
h -= pow * u32(s[i+n]);
|
||||
if h == hash && s[i:i+n] == substr {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -250,13 +405,14 @@ count :: proc(s, substr: string) -> int {
|
||||
|
||||
// TODO(bill): Use a non-brute for approach
|
||||
n := 0;
|
||||
str := s;
|
||||
for {
|
||||
i := index(s, substr);
|
||||
i := index(str, substr);
|
||||
if i == -1 {
|
||||
return n;
|
||||
}
|
||||
n += 1;
|
||||
s = s[i+len(substr):];
|
||||
str = str[i+len(substr):];
|
||||
}
|
||||
return n;
|
||||
}
|
||||
@@ -270,7 +426,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string
|
||||
}
|
||||
|
||||
b := make([]byte, len(s)*count, allocator);
|
||||
i := copy(b, cast([]byte)s);
|
||||
i := copy(b, s);
|
||||
for i < len(b) { // 2^N trick to reduce the need to copy
|
||||
copy(b[i:], b[:i]);
|
||||
i *= 2;
|
||||
@@ -289,22 +445,22 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
|
||||
output = s;
|
||||
return;
|
||||
}
|
||||
|
||||
byte_count := n;
|
||||
if m := count(s, old); m == 0 {
|
||||
was_allocation = false;
|
||||
output = s;
|
||||
return;
|
||||
} else if n < 0 || m < n {
|
||||
n = m;
|
||||
byte_count = m;
|
||||
}
|
||||
|
||||
|
||||
t := make([]byte, len(s) + n*(len(new) - len(old)), allocator);
|
||||
t := make([]byte, len(s) + byte_count*(len(new) - len(old)), allocator);
|
||||
was_allocation = true;
|
||||
|
||||
w := 0;
|
||||
start := 0;
|
||||
for i := 0; i < n; i += 1 {
|
||||
for i := 0; i < byte_count; i += 1 {
|
||||
j := start;
|
||||
if len(old) == 0 {
|
||||
if i > 0 {
|
||||
@@ -314,11 +470,11 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
|
||||
} else {
|
||||
j += index(s[start:], old);
|
||||
}
|
||||
w += copy(t[w:], cast([]byte)s[start:j]);
|
||||
w += copy(t[w:], cast([]byte)new);
|
||||
w += copy(t[w:], s[start:j]);
|
||||
w += copy(t[w:], new);
|
||||
start = j + len(old);
|
||||
}
|
||||
w += copy(t[w:], cast([]byte)s[start:]);
|
||||
w += copy(t[w:], s[start:]);
|
||||
output = string(t[0:w]);
|
||||
return;
|
||||
}
|
||||
@@ -475,14 +631,16 @@ trim_left :: proc(s: string, cutset: string) -> string {
|
||||
if s == "" || cutset == "" {
|
||||
return s;
|
||||
}
|
||||
return trim_left_proc_with_state(s, is_in_cutset, &cutset);
|
||||
state := cutset;
|
||||
return trim_left_proc_with_state(s, is_in_cutset, &state);
|
||||
}
|
||||
|
||||
trim_right :: proc(s: string, cutset: string) -> string {
|
||||
if s == "" || cutset == "" {
|
||||
return s;
|
||||
}
|
||||
return trim_right_proc_with_state(s, is_in_cutset, &cutset);
|
||||
state := cutset;
|
||||
return trim_right_proc_with_state(s, is_in_cutset, &state);
|
||||
}
|
||||
|
||||
trim :: proc(s: string, cutset: string) -> string {
|
||||
@@ -515,7 +673,8 @@ trim_null :: proc(s: string) -> string {
|
||||
|
||||
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
|
||||
// Adjacent invalid bytes are only replaced once
|
||||
scrub :: proc(str: string, replacement: string, allocator := context.allocator) -> string {
|
||||
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
|
||||
str := s;
|
||||
b := make_builder(allocator);;
|
||||
grow_builder(&b, len(str));
|
||||
|
||||
@@ -547,30 +706,33 @@ scrub :: proc(str: string, replacement: string, allocator := context.allocator)
|
||||
}
|
||||
|
||||
|
||||
reverse :: proc(str: string, allocator := context.allocator) -> string {
|
||||
reverse :: proc(s: string, allocator := context.allocator) -> string {
|
||||
str := s;
|
||||
n := len(str);
|
||||
buf := make([]byte, n);
|
||||
i := 0;
|
||||
i := n;
|
||||
|
||||
for len(str) > 0 {
|
||||
_, w := utf8.decode_rune_in_string(str);
|
||||
copy(buf[i:], cast([]byte)str[:w]);
|
||||
i -= w;
|
||||
copy(buf[i:], str[:w]);
|
||||
str = str[w:];
|
||||
}
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
expand_tabs :: proc(str: string, tab_size: int, allocator := context.allocator) -> string {
|
||||
expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
|
||||
if tab_size <= 0 {
|
||||
panic("tab size must be positive");
|
||||
}
|
||||
|
||||
if str == "" {
|
||||
|
||||
if s == "" {
|
||||
return "";
|
||||
}
|
||||
|
||||
b := make_builder(allocator);
|
||||
|
||||
str := s;
|
||||
column: int;
|
||||
|
||||
for len(str) > 0 {
|
||||
@@ -683,11 +845,12 @@ write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
|
||||
write_string(b, pad);
|
||||
}
|
||||
|
||||
remains = remains % pad_len;
|
||||
n := remains % pad_len;
|
||||
p := pad;
|
||||
|
||||
if remains != 0 do for i := 0; i < remains; i += 1 {
|
||||
r, w := utf8.decode_rune_in_string(pad);
|
||||
for i := 0; i < n; i += 1 {
|
||||
r, w := utf8.decode_rune_in_string(p);
|
||||
write_rune(b, r);
|
||||
pad = pad[w:];
|
||||
p = p[w:];
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -11,7 +11,7 @@ Ordering :: enum {
|
||||
}
|
||||
|
||||
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return .Relaxed;
|
||||
case .Release: return .Relaxed;
|
||||
case .Acquire: return .Acquire;
|
||||
@@ -22,7 +22,7 @@ strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ord
|
||||
}
|
||||
|
||||
fence :: inline proc "contextless" ($order: Ordering) {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: panic("there is no such thing as a relaxed fence");
|
||||
case .Release: intrinsics.atomic_fence_rel();
|
||||
case .Acquire: intrinsics.atomic_fence_acq();
|
||||
@@ -34,7 +34,7 @@ fence :: inline proc "contextless" ($order: Ordering) {
|
||||
|
||||
|
||||
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: intrinsics.atomic_store_relaxed(dst, val);
|
||||
case .Release: intrinsics.atomic_store_rel(dst, val);
|
||||
case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
|
||||
@@ -45,7 +45,7 @@ atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
|
||||
}
|
||||
|
||||
atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_load_relaxed(dst);
|
||||
case .Acquire: return intrinsics.atomic_load_acq(dst);
|
||||
case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
|
||||
@@ -57,7 +57,7 @@ atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
|
||||
}
|
||||
|
||||
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_xchg_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_xchg_acq(dst, val);
|
||||
@@ -138,7 +138,7 @@ atomic_compare_exchange_weak :: inline proc "contextless" (dst: ^$T, old, new: T
|
||||
|
||||
|
||||
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_add_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_add_acq(dst, val);
|
||||
@@ -150,7 +150,7 @@ atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
|
||||
}
|
||||
|
||||
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_sub_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_sub_acq(dst, val);
|
||||
@@ -162,7 +162,7 @@ atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
|
||||
}
|
||||
|
||||
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_and_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_and_acq(dst, val);
|
||||
@@ -174,7 +174,7 @@ atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
|
||||
}
|
||||
|
||||
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_nand_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_nand_acq(dst, val);
|
||||
@@ -186,7 +186,7 @@ atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
|
||||
}
|
||||
|
||||
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_or_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_or_acq(dst, val);
|
||||
@@ -198,7 +198,7 @@ atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T
|
||||
}
|
||||
|
||||
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
|
||||
#complete switch order {
|
||||
switch order {
|
||||
case .Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
|
||||
case .Release: return intrinsics.atomic_xor_rel(dst, val);
|
||||
case .Acquire: return intrinsics.atomic_xor_acq(dst, val);
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package sync
|
||||
|
||||
foreign {
|
||||
@(link_name="llvm.x86.sse2.pause")
|
||||
yield_processor :: proc() ---
|
||||
}
|
||||
|
||||
Ticket_Mutex :: struct {
|
||||
ticket: u64,
|
||||
serving: u64,
|
||||
}
|
||||
|
||||
ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
|
||||
atomic_store(&m.ticket, 0, .Relaxed);
|
||||
atomic_store(&m.serving, 0, .Relaxed);
|
||||
}
|
||||
|
||||
ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) {
|
||||
ticket := atomic_add(&m.ticket, 1, .Relaxed);
|
||||
for ticket != m.serving {
|
||||
yield_processor();
|
||||
}
|
||||
}
|
||||
|
||||
ticket_mutex_unlock :: inline proc(m: ^Ticket_Mutex) {
|
||||
atomic_add(&m.serving, 1, .Relaxed);
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package sync
|
||||
|
||||
import "core:sys/darwin"
|
||||
|
||||
import "core:c"
|
||||
|
||||
// The Darwin docs say it best:
|
||||
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
|
||||
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
|
||||
// but when there are none left, a thread must wait until another thread returns one.
|
||||
Semaphore :: struct #align 16 {
|
||||
handle: darwin.semaphore_t,
|
||||
}
|
||||
// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments.
|
||||
// See core/sys/unix/pthread_linux.odin/pthread_t.
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
|
||||
ct := darwin.mach_task_self();
|
||||
res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count));
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
ct := darwin.mach_task_self();
|
||||
res := darwin.semaphore_destroy(ct, s.handle);
|
||||
assert(res == 0);
|
||||
s.handle = {};
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count := 1) {
|
||||
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
|
||||
for in 0..count-1 {
|
||||
res := darwin.semaphore_signal(s.handle);
|
||||
assert(res == 0);
|
||||
}
|
||||
}
|
||||
|
||||
semaphore_wait_for :: proc(s: ^Semaphore) {
|
||||
res := darwin.semaphore_wait(s.handle);
|
||||
assert(res == 0);
|
||||
}
|
||||
+18
-85
@@ -1,98 +1,31 @@
|
||||
package sync
|
||||
|
||||
/*
|
||||
import "core:sys/unix"
|
||||
|
||||
import "core:atomics"
|
||||
import "core:os"
|
||||
|
||||
Semaphore :: struct {
|
||||
// _handle: win32.Handle,
|
||||
// The Darwin docs say it best:
|
||||
// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
|
||||
// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
|
||||
// but when there are none left, a thread must wait until another thread returns one.
|
||||
Semaphore :: struct #align 16 {
|
||||
handle: unix.sem_t,
|
||||
}
|
||||
|
||||
Mutex :: struct {
|
||||
_semaphore: Semaphore,
|
||||
_counter: i32,
|
||||
_owner: i32,
|
||||
_recursion: i32,
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> i32 {
|
||||
return i32(os.current_thread_id());
|
||||
}
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore) {
|
||||
// s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
|
||||
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
|
||||
assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
// win32.CloseHandle(s._handle);
|
||||
assert(unix.sem_destroy(&s.handle) == 0);
|
||||
s.handle = {};
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
// win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
|
||||
semaphore_post :: proc(s: ^Semaphore, count := 1) {
|
||||
// NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
|
||||
for in 0..count-1 {
|
||||
assert(unix.sem_post(&s.handle) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
semaphore_release :: inline proc(s: ^Semaphore) {
|
||||
semaphore_post(s, 1);
|
||||
semaphore_wait_for :: proc(s: ^Semaphore) {
|
||||
assert(unix.sem_wait(&s.handle) == 0);
|
||||
}
|
||||
|
||||
semaphore_wait :: proc(s: ^Semaphore) {
|
||||
// win32.WaitForSingleObject(s._handle, win32.INFINITE);
|
||||
}
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex) {
|
||||
atomics.store(&m._counter, 0);
|
||||
atomics.store(&m._owner, current_thread_id());
|
||||
semaphore_init(&m._semaphore);
|
||||
m._recursion = 0;
|
||||
}
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
semaphore_destroy(&m._semaphore);
|
||||
}
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
thread_id := current_thread_id();
|
||||
if atomics.fetch_add(&m._counter, 1) > 0 {
|
||||
if thread_id != atomics.load(&m._owner) {
|
||||
semaphore_wait(&m._semaphore);
|
||||
}
|
||||
}
|
||||
atomics.store(&m._owner, thread_id);
|
||||
m._recursion += 1;
|
||||
}
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
thread_id := current_thread_id();
|
||||
if atomics.load(&m._owner) == thread_id {
|
||||
atomics.fetch_add(&m._counter, 1);
|
||||
} else {
|
||||
expected: i32 = 0;
|
||||
if atomics.load(&m._counter) != 0 {
|
||||
return false;
|
||||
}
|
||||
if atomics.compare_exchange(&m._counter, expected, 1) == 0 {
|
||||
return false;
|
||||
}
|
||||
atomics.store(&m._owner, thread_id);
|
||||
}
|
||||
m._recursion += 1;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
recursion: i32;
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomics.load(&m._owner));
|
||||
|
||||
m._recursion -= 1;
|
||||
recursion = m._recursion;
|
||||
if recursion == 0 {
|
||||
atomics.store(&m._owner, thread_id);
|
||||
}
|
||||
|
||||
if atomics.fetch_add(&m._counter, -1) > 1 {
|
||||
if recursion == 0 {
|
||||
semaphore_release(&m._semaphore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
// +build linux, darwin
|
||||
package sync
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
// A lock that can only be held by one thread at once.
|
||||
Mutex :: struct {
|
||||
handle: unix.pthread_mutex_t,
|
||||
}
|
||||
|
||||
// Blocks until signalled, and then lets past exactly
|
||||
// one thread.
|
||||
Condition :: struct {
|
||||
handle: unix.pthread_cond_t,
|
||||
|
||||
// NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent.
|
||||
// This means that you may signal the condition before anyone is waiting to cause the
|
||||
// next thread that tries to wait to just pass by uninterrupted, without sleeping.
|
||||
// Without this, signalling a condition will only wake up a thread which is already waiting,
|
||||
// but not one that is about to wait, which can cause your program to become out of sync in
|
||||
// ways that are hard to debug or fix.
|
||||
flag: bool, // atomically mutated
|
||||
|
||||
mutex: Mutex,
|
||||
}
|
||||
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex) {
|
||||
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
|
||||
attrs: unix.pthread_mutexattr_t;
|
||||
assert(unix.pthread_mutexattr_init(&attrs) == 0);
|
||||
defer unix.pthread_mutexattr_destroy(&attrs); // ignores destruction error
|
||||
|
||||
assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0);
|
||||
}
|
||||
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
assert(unix.pthread_mutex_destroy(&m.handle) == 0);
|
||||
m.handle = {};
|
||||
}
|
||||
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
assert(unix.pthread_mutex_lock(&m.handle) == 0);
|
||||
}
|
||||
|
||||
// Returns false if someone else holds the lock.
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
return unix.pthread_mutex_trylock(&m.handle) == 0;
|
||||
}
|
||||
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
assert(unix.pthread_mutex_unlock(&m.handle) == 0);
|
||||
}
|
||||
|
||||
|
||||
condition_init :: proc(c: ^Condition) {
|
||||
// NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition.
|
||||
attrs: unix.pthread_condattr_t;
|
||||
assert(unix.pthread_condattr_init(&attrs) == 0);
|
||||
defer unix.pthread_condattr_destroy(&attrs); // ignores destruction error
|
||||
|
||||
assert(unix.pthread_cond_init(&c.handle, &attrs) == 0);
|
||||
|
||||
mutex_init(&c.mutex);
|
||||
c.flag = false;
|
||||
}
|
||||
|
||||
condition_destroy :: proc(c: ^Condition) {
|
||||
assert(unix.pthread_cond_destroy(&c.handle) == 0);
|
||||
mutex_destroy(&c.mutex);
|
||||
c.handle = {};
|
||||
}
|
||||
|
||||
// Awaken exactly one thread who is waiting on the condition.
|
||||
condition_signal :: proc(c: ^Condition) {
|
||||
mutex_lock(&c.mutex);
|
||||
defer mutex_unlock(&c.mutex);
|
||||
atomic_swap(&c.flag, true, .Sequentially_Consistent);
|
||||
assert(unix.pthread_cond_signal(&c.handle) == 0);
|
||||
}
|
||||
|
||||
// Wait for the condition to be signalled.
|
||||
// Does not block if the condition has been signalled and no one
|
||||
// has waited on it yet.
|
||||
condition_wait_for :: proc(c: ^Condition) {
|
||||
mutex_lock(&c.mutex);
|
||||
defer mutex_unlock(&c.mutex);
|
||||
// NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
|
||||
// the thread that gets signalled and wakes up, discovers that the flag was taken and goes
|
||||
// back to sleep.
|
||||
// Though this overall behavior is the most sane, there may be a better way to do this that means that
|
||||
// the first thread to wait, gets the flag first.
|
||||
if atomic_swap(&c.flag, false, .Sequentially_Consistent) do return;
|
||||
for {
|
||||
assert(unix.pthread_cond_wait(&c.handle, &c.mutex.handle) == 0);
|
||||
if atomic_swap(&c.flag, false, .Sequentially_Consistent) do break;
|
||||
}
|
||||
}
|
||||
+22
-47
@@ -1,51 +1,40 @@
|
||||
// +build windows
|
||||
package sync
|
||||
|
||||
import "core:sys/win32"
|
||||
|
||||
foreign {
|
||||
@(link_name="llvm.x86.sse2.pause")
|
||||
yield_processor :: proc() ---
|
||||
}
|
||||
|
||||
Semaphore :: struct {
|
||||
_handle: win32.Handle,
|
||||
}
|
||||
|
||||
// A lock that can only be held by one thread at once.
|
||||
Mutex :: struct {
|
||||
_critical_section: win32.Critical_Section,
|
||||
}
|
||||
|
||||
// Blocks until signalled.
|
||||
// When signalled, awakens exactly one waiting thread.
|
||||
Condition :: struct {
|
||||
event: win32.Handle,
|
||||
}
|
||||
|
||||
Ticket_Mutex :: struct {
|
||||
ticket: u64,
|
||||
serving: u64,
|
||||
// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
|
||||
// Posting to the semaphore increases the count by one, or the provided amount.
|
||||
Semaphore :: struct {
|
||||
_handle: win32.Handle,
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc() -> i32 {
|
||||
return i32(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore) {
|
||||
s._handle = win32.create_semaphore_w(nil, 0, 1<<31-1, nil);
|
||||
semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
|
||||
s._handle = win32.create_semaphore_w(nil, i32(initial_count), 1<<31-1, nil);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
win32.close_handle(s._handle);
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
semaphore_post :: proc(s: ^Semaphore, count := 1) {
|
||||
win32.release_semaphore(s._handle, i32(count), nil);
|
||||
}
|
||||
|
||||
semaphore_release :: inline proc(s: ^Semaphore) {
|
||||
semaphore_post(s, 1);
|
||||
}
|
||||
|
||||
semaphore_wait :: proc(s: ^Semaphore) {
|
||||
semaphore_wait_for :: proc(s: ^Semaphore) {
|
||||
// NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
|
||||
result := win32.wait_for_single_object(s._handle, win32.INFINITE);
|
||||
assert(result != win32.WAIT_FAILED);
|
||||
}
|
||||
@@ -73,10 +62,19 @@ mutex_unlock :: proc(m: ^Mutex) {
|
||||
|
||||
|
||||
condition_init :: proc(using c: ^Condition) {
|
||||
// create an auto-reset event.
|
||||
// NOTE(tetra, 2019-10-30): this will, when signalled, signal exactly one waiting thread
|
||||
// and then reset itself automatically.
|
||||
event = win32.create_event_w(nil, false, false, nil);
|
||||
assert(event != nil);
|
||||
}
|
||||
|
||||
condition_destroy :: proc(using c: ^Condition) {
|
||||
if event != nil {
|
||||
win32.close_handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
condition_signal :: proc(using c: ^Condition) {
|
||||
ok := win32.set_event(event);
|
||||
assert(bool(ok));
|
||||
@@ -86,26 +84,3 @@ condition_wait_for :: proc(using c: ^Condition) {
|
||||
result := win32.wait_for_single_object(event, win32.INFINITE);
|
||||
assert(result != win32.WAIT_FAILED);
|
||||
}
|
||||
|
||||
condition_destroy :: proc(using c: ^Condition) {
|
||||
if event != nil {
|
||||
win32.close_handle(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
|
||||
atomic_store(&m.ticket, 0, Ordering.Relaxed);
|
||||
atomic_store(&m.serving, 0, Ordering.Relaxed);
|
||||
}
|
||||
|
||||
ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) {
|
||||
ticket := atomic_add(&m.ticket, 1, Ordering.Relaxed);
|
||||
for ticket != m.serving {
|
||||
yield_processor();
|
||||
}
|
||||
}
|
||||
|
||||
ticket_mutex_unlock :: inline proc(m: ^Ticket_Mutex) {
|
||||
atomic_add(&m.serving, 1, Ordering.Relaxed);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package darwin;
|
||||
|
||||
foreign import "system:pthread"
|
||||
|
||||
import "core:c"
|
||||
|
||||
// NOTE(tetra): Unclear whether these should be aligned 16 or not.
|
||||
// However all other sync primitives are aligned for robustness.
|
||||
// I cannot currently align these though.
|
||||
// See core/sys/unix/pthread_linux.odin/pthread_t.
|
||||
task_t :: distinct u64;
|
||||
semaphore_t :: distinct u64;
|
||||
|
||||
kern_return_t :: distinct u64;
|
||||
thread_act_t :: distinct u64;
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign pthread {
|
||||
mach_task_self :: proc() -> task_t ---;
|
||||
|
||||
semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t ---;
|
||||
semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t ---;
|
||||
|
||||
semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t ---;
|
||||
semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t ---;
|
||||
semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t ---;
|
||||
|
||||
semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t ---;
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x100000;
|
||||
.text BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.text)
|
||||
}
|
||||
.rodata BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.rodata)
|
||||
}
|
||||
.data BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(.data)
|
||||
}
|
||||
|
||||
.bss BLOCK(4K) : ALIGN(4K)
|
||||
{
|
||||
*(COMMON)
|
||||
*(.bss)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package unix;
|
||||
|
||||
import "core:c"
|
||||
|
||||
// NOTE(tetra): No 32-bit Macs.
|
||||
// Source: _pthread_types.h on my Mac.
|
||||
PTHREAD_SIZE :: 8176;
|
||||
PTHREAD_ATTR_SIZE :: 56;
|
||||
PTHREAD_MUTEXATTR_SIZE :: 8;
|
||||
PTHREAD_MUTEX_SIZE :: 56;
|
||||
PTHREAD_CONDATTR_SIZE :: 8;
|
||||
PTHREAD_COND_SIZE :: 40;
|
||||
PTHREAD_ONCE_SIZE :: 8;
|
||||
PTHREAD_RWLOCK_SIZE :: 192;
|
||||
PTHREAD_RWLOCKATTR_SIZE :: 16;
|
||||
|
||||
pthread_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
cleanup_stack: rawptr,
|
||||
_: [PTHREAD_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_attr_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_ATTR_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_cond_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_COND_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_condattr_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_CONDATTR_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_mutex_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_MUTEX_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_mutexattr_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_MUTEXATTR_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_once_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_ONCE_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_rwlock_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_RWLOCK_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_rwlockattr_t :: opaque struct #align 16 {
|
||||
sig: c.long,
|
||||
_: [PTHREAD_RWLOCKATTR_SIZE] c.char,
|
||||
};
|
||||
|
||||
SCHED_OTHER :: 1; // Avoid if you are writing portable software.
|
||||
SCHED_FIFO :: 4;
|
||||
SCHED_RR :: 2; // Round robin.
|
||||
|
||||
SCHED_PARAM_SIZE :: 4;
|
||||
|
||||
sched_param :: struct {
|
||||
sched_priority: c.int,
|
||||
_: [SCHED_PARAM_SIZE] c.char,
|
||||
};
|
||||
|
||||
// Source: https://github.com/apple/darwin-libpthread/blob/03c4628c8940cca6fd6a82957f683af804f62e7f/pthread/pthread.h#L138
|
||||
PTHREAD_CREATE_JOINABLE :: 1;
|
||||
PTHREAD_CREATE_DETACHED :: 2;
|
||||
PTHREAD_INHERIT_SCHED :: 1;
|
||||
PTHREAD_EXPLICIT_SCHED :: 2;
|
||||
PTHREAD_PROCESS_SHARED :: 1;
|
||||
PTHREAD_PROCESS_PRIVATE :: 2;
|
||||
@@ -0,0 +1,110 @@
|
||||
package unix;
|
||||
|
||||
import "core:c"
|
||||
|
||||
// TODO(tetra): For robustness, I'd like to mark this with align 16.
|
||||
// I cannot currently do this.
|
||||
// And at the time of writing there is a bug with putting it
|
||||
// as the only field in a struct.
|
||||
pthread_t :: distinct u64;
|
||||
// pthread_t :: struct #align 16 { x: u64 };
|
||||
|
||||
// NOTE(tetra): Got all the size constants from pthreadtypes-arch.h on my
|
||||
// Linux machine.
|
||||
|
||||
PTHREAD_COND_T_SIZE :: 48;
|
||||
|
||||
PTHREAD_MUTEXATTR_T_SIZE :: 4;
|
||||
PTHREAD_CONDATTR_T_SIZE :: 4;
|
||||
PTHREAD_RWLOCKATTR_T_SIZE :: 8;
|
||||
PTHREAD_BARRIERATTR_T_SIZE :: 4;
|
||||
|
||||
// WARNING: The sizes of these things are different yet again
|
||||
// on non-X86!
|
||||
when size_of(int) == 8 {
|
||||
PTHREAD_ATTR_T_SIZE :: 56;
|
||||
PTHREAD_MUTEX_T_SIZE :: 40;
|
||||
PTHREAD_RWLOCK_T_SIZE :: 56;
|
||||
PTHREAD_BARRIER_T_SIZE :: 32;
|
||||
} else when size_of(int) == 4 {
|
||||
PTHREAD_ATTR_T_SIZE :: 32;
|
||||
PTHREAD_MUTEX_T_SIZE :: 32;
|
||||
PTHREAD_RWLOCK_T_SIZE :: 44;
|
||||
PTHREAD_BARRIER_T_SIZE :: 20;
|
||||
}
|
||||
|
||||
pthread_cond_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_COND_T_SIZE] c.char,
|
||||
};
|
||||
pthread_mutex_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_MUTEX_T_SIZE] c.char,
|
||||
};
|
||||
pthread_rwlock_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
|
||||
};
|
||||
pthread_barrier_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_BARRIER_T_SIZE] c.char,
|
||||
};
|
||||
|
||||
pthread_attr_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_ATTR_T_SIZE] c.char,
|
||||
};
|
||||
pthread_condattr_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
|
||||
};
|
||||
pthread_mutexattr_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
|
||||
};
|
||||
pthread_rwlockattr_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
|
||||
};
|
||||
pthread_barrierattr_t :: opaque struct #align 16 {
|
||||
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
|
||||
};
|
||||
|
||||
|
||||
// TODO(tetra, 2019-11-01): Maybe make `enum c.int`s for these?
|
||||
PTHREAD_CREATE_JOINABLE :: 0;
|
||||
PTHREAD_CREATE_DETACHED :: 1;
|
||||
PTHREAD_INHERIT_SCHED :: 0;
|
||||
PTHREAD_EXPLICIT_SCHED :: 1;
|
||||
PTHREAD_PROCESS_PRIVATE :: 0;
|
||||
PTHREAD_PROCESS_SHARED :: 1;
|
||||
|
||||
SCHED_OTHER :: 0;
|
||||
SCHED_FIFO :: 1;
|
||||
SCHED_RR :: 2; // Round robin.
|
||||
|
||||
sched_param :: struct {
|
||||
sched_priority: c.int,
|
||||
}
|
||||
|
||||
sem_t :: struct #align 16 {
|
||||
_: [SEM_T_SIZE] c.char,
|
||||
}
|
||||
|
||||
when size_of(int) == 8 {
|
||||
SEM_T_SIZE :: 32;
|
||||
} else when size_of(int) == 4 {
|
||||
SEM_T_SIZE :: 16;
|
||||
}
|
||||
|
||||
foreign import "system:pthread"
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign pthread {
|
||||
// create named semaphore.
|
||||
// used in process-shared semaphores.
|
||||
sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---;
|
||||
|
||||
sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---;
|
||||
sem_destroy :: proc(sem: ^sem_t) -> c.int ---;
|
||||
sem_post :: proc(sem: ^sem_t) -> c.int ---;
|
||||
sem_wait :: proc(sem: ^sem_t) -> c.int ---;
|
||||
sem_trywait :: proc(sem: ^sem_t) -> c.int ---;
|
||||
// sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---;
|
||||
|
||||
// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
|
||||
// see https://linux.die.net/man/3/pthread_yield
|
||||
pthread_yield :: proc() -> c.int ---;
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package unix;
|
||||
|
||||
foreign import "system:pthread"
|
||||
|
||||
import "core:c"
|
||||
import "core:time"
|
||||
|
||||
//
|
||||
// On success, these functions return 0.
|
||||
//
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign pthread {
|
||||
pthread_create :: proc(t: ^pthread_t, attrs: ^pthread_attr_t, routine: proc(data: rawptr) -> rawptr, arg: rawptr) -> c.int ---;
|
||||
|
||||
// retval is a pointer to a location to put the return value of the thread proc.
|
||||
pthread_join :: proc(t: pthread_t, retval: rawptr) -> c.int ---;
|
||||
|
||||
pthread_self :: proc() -> pthread_t ---;
|
||||
|
||||
pthread_equal :: proc(a, b: pthread_t) -> b32 ---;
|
||||
|
||||
sched_get_priority_min :: proc(policy: c.int) -> c.int ---;
|
||||
sched_get_priority_max :: proc(policy: c.int) -> c.int ---;
|
||||
|
||||
// NOTE: POSIX says this can fail with OOM.
|
||||
pthread_attr_init :: proc(attrs: ^pthread_attr_t) -> c.int ---;
|
||||
|
||||
pthread_attr_destroy :: proc(attrs: ^pthread_attr_t) -> c.int ---;
|
||||
|
||||
pthread_attr_getschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---;
|
||||
pthread_attr_setschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---;
|
||||
|
||||
pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---;
|
||||
pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---;
|
||||
|
||||
// states: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE
|
||||
pthread_attr_setdetachstate :: proc(attrs: ^pthread_attr_t, detach_state: c.int) -> c.int ---;
|
||||
|
||||
// scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
|
||||
pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---;
|
||||
|
||||
// NOTE(tetra, 2019-11-06): WARNING: Different systems have different alignment requirements.
|
||||
// For maximum usefulness, use the OS's page size.
|
||||
// ALSO VERY MAJOR WARNING: `stack_ptr` must be the LAST byte of the stack on systems
|
||||
// where the stack grows downwards, which is the common case, so far as I know.
|
||||
// On systems where it grows upwards, give the FIRST byte instead.
|
||||
// ALSO SLIGHTLY LESS MAJOR WARNING: Using this procedure DISABLES automatically-provided
|
||||
// guard pages. If you are using this procedure, YOU must set them up manually.
|
||||
// If you forget to do this, you WILL get stack corruption bugs if you do not EXTREMELY
|
||||
// know what you are doing!
|
||||
pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int ---;
|
||||
pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int ---;
|
||||
|
||||
sched_yield :: proc() -> c.int ---;
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign pthread {
|
||||
// NOTE: POSIX says this can fail with OOM.
|
||||
pthread_cond_init :: proc(cond: ^pthread_cond_t, attrs: ^pthread_condattr_t) -> c.int ---;
|
||||
|
||||
pthread_cond_destroy :: proc(cond: ^pthread_cond_t) -> c.int ---;
|
||||
|
||||
pthread_cond_signal :: proc(cond: ^pthread_cond_t) -> c.int ---;
|
||||
|
||||
// same as signal, but wakes up _all_ threads that are waiting
|
||||
pthread_cond_broadcast :: proc(cond: ^pthread_cond_t) -> c.int ---;
|
||||
|
||||
|
||||
// assumes the mutex is pre-locked
|
||||
pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int ---;
|
||||
pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---;
|
||||
|
||||
pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int ---;
|
||||
pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int ---;
|
||||
|
||||
// p-shared = "process-shared" - i.e: is this condition shared among multiple processes?
|
||||
// values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED
|
||||
pthread_condattr_setpshared :: proc(attrs: ^pthread_condattr_t, value: c.int) -> c.int ---;
|
||||
pthread_condattr_getpshared :: proc(attrs: ^pthread_condattr_t, result: ^c.int) -> c.int ---;
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign pthread {
|
||||
// NOTE: POSIX says this can fail with OOM.
|
||||
pthread_mutex_init :: proc(mutex: ^pthread_mutex_t, attrs: ^pthread_mutexattr_t) -> c.int ---;
|
||||
|
||||
pthread_mutex_destroy :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
|
||||
|
||||
pthread_mutex_trylock :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
|
||||
|
||||
pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
|
||||
|
||||
pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^time.TimeSpec) -> c.int ---;
|
||||
|
||||
pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int ---;
|
||||
|
||||
|
||||
pthread_mutexattr_init :: proc(attrs: ^pthread_mutexattr_t) -> c.int ---;
|
||||
pthread_mutexattr_destroy :: proc(attrs: ^pthread_mutexattr_t) -> c.int ---;
|
||||
|
||||
// p-shared = "process-shared" - i.e: is this mutex shared among multiple processes?
|
||||
// values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED
|
||||
pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int ---;
|
||||
pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int ---;
|
||||
|
||||
}
|
||||
@@ -89,7 +89,6 @@ _open_file_dialog :: proc(title: string, dir: string,
|
||||
// Filters need to be passed as a pair of strings (title, filter)
|
||||
filter_len := u32(len(filters));
|
||||
if filter_len % 2 != 0 do return "", false;
|
||||
default_filter = clamp(default_filter, 1, filter_len / 2);
|
||||
|
||||
filter: string;
|
||||
filter = strings.join(filters, "\u0000", context.temp_allocator);
|
||||
@@ -102,7 +101,7 @@ _open_file_dialog :: proc(title: string, dir: string,
|
||||
title = utf8_to_wstring(title, context.temp_allocator),
|
||||
filter = utf8_to_wstring(filter, context.temp_allocator),
|
||||
initial_dir = utf8_to_wstring(dir, context.temp_allocator),
|
||||
filter_index = u32(default_filter),
|
||||
filter_index = u32(clamp(default_filter, 1, filter_len / 2)),
|
||||
def_ext = utf8_to_wstring(default_ext, context.temp_allocator),
|
||||
flags = u32(flags),
|
||||
};
|
||||
@@ -121,7 +120,7 @@ _open_file_dialog :: proc(title: string, dir: string,
|
||||
return "", false;
|
||||
}
|
||||
|
||||
file_name := ucs2_to_utf8(file_buf[:], allocator);
|
||||
file_name := utf16_to_utf8(file_buf[:], allocator);
|
||||
path = strings.trim_right_null(file_name);
|
||||
return;
|
||||
}
|
||||
@@ -143,7 +142,7 @@ select_file_to_save :: proc(title := SAVE_TITLE, dir := ".",
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
|
||||
// TODO: Implement convenience function for select_file_to_open with ALLOW_MULTI_SELECT that takes
|
||||
// it output of the form "path\u0000\file1u\0000file2" and turns it into []string with the path + file pre-concatenated for you.
|
||||
|
||||
OFN_ALLOWMULTISELECT :: 0x00000200; // NOTE(Jeroen): Without OFN_EXPLORER it uses the Win3 dialog.
|
||||
@@ -186,4 +185,4 @@ CDERR_LOCKRESFAILURE :: 0x00000008;
|
||||
CDERR_MEMALLOCFAILURE :: 0x00000009;
|
||||
CDERR_MEMLOCKFAILURE :: 0x0000000A;
|
||||
CDERR_NOHOOK :: 0x0000000B;
|
||||
CDERR_REGISTERMSGFAIL :: 0x0000000C;
|
||||
CDERR_REGISTERMSGFAIL :: 0x0000000C;
|
||||
|
||||
@@ -9,6 +9,6 @@ foreign {
|
||||
get_cwd :: proc(allocator := context.temp_allocator) -> string {
|
||||
buffer := make([]u16, MAX_PATH_WIDE, allocator);
|
||||
_get_cwd_wide(Wstring(&buffer[0]), MAX_PATH_WIDE);
|
||||
file := ucs2_to_utf8(buffer[:], allocator);
|
||||
file := utf16_to_utf8(buffer[:], allocator);
|
||||
return strings.trim_right_null(file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,6 +300,25 @@ File_Notify_Information :: struct {
|
||||
file_name: [1]u16,
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
|
||||
System_Info :: struct {
|
||||
using _: struct #raw_union {
|
||||
oem_id: u32,
|
||||
using _: struct #raw_union {
|
||||
processor_architecture: u16,
|
||||
_: u16, // reserved
|
||||
},
|
||||
},
|
||||
page_size: u32,
|
||||
minimum_application_address: rawptr,
|
||||
maximum_application_address: rawptr,
|
||||
active_processor_mask: u32,
|
||||
number_of_processors: u32,
|
||||
processor_type: u32,
|
||||
allocation_granularity: u32,
|
||||
processor_level: u16,
|
||||
processor_revision: u16,
|
||||
}
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoexa
|
||||
OS_Version_Info_Ex_A :: struct {
|
||||
@@ -728,19 +747,21 @@ CP_UTF8 :: 65001; // UTF-8 translation
|
||||
MB_ERR_INVALID_CHARS :: 8;
|
||||
WC_ERR_INVALID_CHARS :: 128;
|
||||
|
||||
utf8_to_ucs2 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
|
||||
utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
|
||||
if len(s) < 1 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstring(&s[0]), i32(len(s)), nil, 0);
|
||||
b := transmute([]byte)s;
|
||||
cstr := cstring(&b[0]);
|
||||
n := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), nil, 0);
|
||||
if n == 0 {
|
||||
return nil;
|
||||
}
|
||||
|
||||
text := make([]u16, n+1, allocator);
|
||||
|
||||
n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstring(&s[0]), i32(len(s)), Wstring(&text[0]), i32(n));
|
||||
n1 := multi_byte_to_wide_char(CP_UTF8, MB_ERR_INVALID_CHARS, cstr, i32(len(s)), Wstring(&text[0]), i32(n));
|
||||
if n1 == 0 {
|
||||
delete(text, allocator);
|
||||
return nil;
|
||||
@@ -751,13 +772,13 @@ utf8_to_ucs2 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
|
||||
return text[:len(text)-1];
|
||||
}
|
||||
utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> Wstring {
|
||||
if res := utf8_to_ucs2(s, allocator); res != nil {
|
||||
if res := utf8_to_utf16(s, allocator); res != nil {
|
||||
return Wstring(&res[0]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
ucs2_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
|
||||
utf16_to_utf8 :: proc(s: []u16, allocator := context.temp_allocator) -> string {
|
||||
if len(s) < 1 {
|
||||
return "";
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ foreign kernel32 {
|
||||
@(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---;
|
||||
@(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---;
|
||||
|
||||
@(link_name="Sleep") sleep :: proc(ms: i32) -> i32 ---;
|
||||
@(link_name="Sleep") sleep :: proc(ms: u32) ---;
|
||||
@(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 ---;
|
||||
@(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 ---;
|
||||
@(link_name="OutputDebugStringA") output_debug_string_a :: proc(c_str: cstring) ---;
|
||||
@@ -31,6 +31,7 @@ foreign kernel32 {
|
||||
@(link_name="GetCommandLineA") get_command_line_a :: proc() -> cstring ---;
|
||||
@(link_name="GetCommandLineW") get_command_line_w :: proc() -> Wstring ---;
|
||||
@(link_name="GetSystemMetrics") get_system_metrics :: proc(index: i32) -> i32 ---;
|
||||
@(link_name="GetSystemInfo") get_system_info :: proc(info: ^System_Info) ---;
|
||||
@(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---;
|
||||
@(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---;
|
||||
|
||||
|
||||
@@ -182,7 +182,7 @@ foreign user32 {
|
||||
@(link_name="DestroyIcon") destroy_icon :: proc(icon: Hicon) -> Bool ---;
|
||||
|
||||
@(link_name="LoadCursorA") load_cursor_a :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---;
|
||||
@(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---;
|
||||
@(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: Wstring) -> Hcursor ---;
|
||||
@(link_name="GetCursor") get_cursor :: proc() -> Hcursor ---;
|
||||
@(link_name="SetCursor") set_cursor :: proc(cursor: Hcursor) -> Hcursor ---;
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package thread
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
Thread_Proc :: #type proc(^Thread);
|
||||
|
||||
Thread :: struct {
|
||||
using specific: Thread_Os_Specific,
|
||||
procedure: Thread_Proc,
|
||||
data: rawptr,
|
||||
user_index: int,
|
||||
|
||||
init_context: runtime.Context,
|
||||
use_init_context: bool,
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package thread
|
||||
|
||||
import "intrinsics"
|
||||
import "core:sync"
|
||||
import "core:mem"
|
||||
|
||||
Task_Status :: enum i32 {
|
||||
Ready,
|
||||
Busy,
|
||||
Waiting,
|
||||
Term,
|
||||
}
|
||||
|
||||
Task_Proc :: #type proc(task: ^Task);
|
||||
|
||||
Task :: struct {
|
||||
procedure: Task_Proc,
|
||||
data: rawptr,
|
||||
user_index: int,
|
||||
}
|
||||
|
||||
Task_Id :: distinct i32;
|
||||
INVALID_TASK_ID :: Task_Id(-1);
|
||||
|
||||
|
||||
Pool :: struct {
|
||||
allocator: mem.Allocator,
|
||||
mutex: sync.Mutex,
|
||||
sem_available: sync.Semaphore,
|
||||
processing_task_count: int, // atomic
|
||||
is_running: bool,
|
||||
|
||||
threads: []^Thread,
|
||||
|
||||
tasks: [dynamic]Task,
|
||||
}
|
||||
|
||||
pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
|
||||
worker_thread_internal :: proc(t: ^Thread) {
|
||||
pool := (^Pool)(t.data);
|
||||
|
||||
for pool.is_running {
|
||||
sync.semaphore_wait_for(&pool.sem_available);
|
||||
|
||||
if task, ok := pool_try_and_pop_task(pool); ok {
|
||||
pool_do_work(pool, &task);
|
||||
}
|
||||
}
|
||||
|
||||
sync.semaphore_post(&pool.sem_available, 1);
|
||||
}
|
||||
|
||||
|
||||
context.allocator = allocator;
|
||||
pool.allocator = allocator;
|
||||
pool.tasks = make([dynamic]Task);
|
||||
pool.threads = make([]^Thread, thread_count);
|
||||
|
||||
sync.mutex_init(&pool.mutex);
|
||||
sync.semaphore_init(&pool.sem_available);
|
||||
pool.is_running = true;
|
||||
|
||||
for _, i in pool.threads {
|
||||
t := create(worker_thread_internal);
|
||||
t.user_index = i;
|
||||
t.data = pool;
|
||||
pool.threads[i] = t;
|
||||
}
|
||||
}
|
||||
|
||||
pool_destroy :: proc(pool: ^Pool) {
|
||||
delete(pool.tasks);
|
||||
delete(pool.threads, pool.allocator);
|
||||
|
||||
sync.mutex_destroy(&pool.mutex);
|
||||
sync.semaphore_destroy(&pool.sem_available);
|
||||
}
|
||||
|
||||
pool_start :: proc(pool: ^Pool) {
|
||||
for t in pool.threads {
|
||||
start(t);
|
||||
}
|
||||
}
|
||||
|
||||
pool_join :: proc(pool: ^Pool) {
|
||||
pool.is_running = false;
|
||||
|
||||
sync.semaphore_post(&pool.sem_available, len(pool.threads));
|
||||
|
||||
yield();
|
||||
|
||||
for t in pool.threads {
|
||||
join(t);
|
||||
}
|
||||
}
|
||||
|
||||
pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
|
||||
sync.mutex_lock(&pool.mutex);
|
||||
defer sync.mutex_unlock(&pool.mutex);
|
||||
|
||||
task: Task;
|
||||
task.procedure = procedure;
|
||||
task.data = data;
|
||||
task.user_index = user_index;
|
||||
|
||||
append(&pool.tasks, task);
|
||||
sync.semaphore_post(&pool.sem_available, 1);
|
||||
}
|
||||
|
||||
pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
|
||||
if sync.mutex_try_lock(&pool.mutex) {
|
||||
if len(pool.tasks) != 0 {
|
||||
intrinsics.atomic_add(&pool.processing_task_count, 1);
|
||||
task = pool.tasks[0];
|
||||
got_task = true;
|
||||
ordered_remove(&pool.tasks, 0);
|
||||
}
|
||||
sync.mutex_unlock(&pool.mutex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
pool_do_work :: proc(pool: ^Pool, task: ^Task) {
|
||||
task.procedure(task);
|
||||
intrinsics.atomic_sub(&pool.processing_task_count, 1);
|
||||
}
|
||||
|
||||
|
||||
pool_wait_and_process :: proc(pool: ^Pool) {
|
||||
for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
|
||||
if task, ok := pool_try_and_pop_task(pool); ok {
|
||||
pool_do_work(pool, &task);
|
||||
}
|
||||
|
||||
// Safety kick
|
||||
if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
|
||||
sync.mutex_lock(&pool.mutex);
|
||||
sync.semaphore_post(&pool.sem_available, len(pool.tasks));
|
||||
sync.mutex_unlock(&pool.mutex);
|
||||
}
|
||||
|
||||
yield();
|
||||
}
|
||||
|
||||
pool_join(pool);
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
// +build linux, darwin
|
||||
package thread;
|
||||
|
||||
import "core:runtime"
|
||||
import "core:sync"
|
||||
import "core:sys/unix"
|
||||
|
||||
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
|
||||
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
|
||||
Thread_Os_Specific :: struct #align 16 {
|
||||
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
|
||||
|
||||
// NOTE: pthread has a proc to query this, but it is marked
|
||||
// as non-portable ("np") so we do this instead.
|
||||
done: bool,
|
||||
|
||||
// since libpthread doesn't seem to have a way to create a thread
|
||||
// in a suspended state, we have it wait on this gate, which we
|
||||
// signal to start it.
|
||||
// destroyed after thread is started.
|
||||
start_gate: sync.Condition,
|
||||
|
||||
// if true, the thread has been started and the start_gate has been destroyed.
|
||||
started: bool,
|
||||
|
||||
// NOTE: with pthreads, it is undefined behavior for multiple threads
|
||||
// to call join on the same thread at the same time.
|
||||
// this value is atomically updated to detect this.
|
||||
// See the comment in `join`.
|
||||
already_joined: bool,
|
||||
}
|
||||
|
||||
Thread_Priority :: enum {
|
||||
Normal,
|
||||
Low,
|
||||
High,
|
||||
}
|
||||
|
||||
//
|
||||
// Creates a thread which will run the given procedure.
|
||||
// It then waits for `start` to be called.
|
||||
//
|
||||
// You may provide a slice of bytes to use as the stack for the new thread,
|
||||
// but if you do, you are expected to set up the guard pages yourself.
|
||||
//
|
||||
// The stack must also be aligned appropriately for the platform.
|
||||
// We require it's at least 16 bytes aligned to help robustness; other
|
||||
// platforms may require page-size alignment.
|
||||
// Note also that pthreads requires the stack is at least 6 OS pages in size:
|
||||
// 4 are required by pthreads, and two extra for guards pages that will be applied.
|
||||
//
|
||||
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
|
||||
t := (^Thread)(t);
|
||||
sync.condition_wait_for(&t.start_gate);
|
||||
sync.condition_destroy(&t.start_gate);
|
||||
t.start_gate = {};
|
||||
|
||||
c := context;
|
||||
if t.use_init_context {
|
||||
c = t.init_context;
|
||||
}
|
||||
context = c;
|
||||
|
||||
t.procedure(t);
|
||||
|
||||
if !t.use_init_context {
|
||||
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
|
||||
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data);
|
||||
}
|
||||
}
|
||||
|
||||
sync.atomic_store(&t.done, true, .Sequentially_Consistent);
|
||||
return nil;
|
||||
}
|
||||
|
||||
attrs: unix.pthread_attr_t;
|
||||
if unix.pthread_attr_init(&attrs) != 0 do return nil; // NOTE(tetra, 2019-11-01): POSIX OOM.
|
||||
defer unix.pthread_attr_destroy(&attrs);
|
||||
|
||||
// NOTE(tetra, 2019-11-01): These only fail if their argument is invalid.
|
||||
assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0);
|
||||
assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0);
|
||||
|
||||
thread := new(Thread);
|
||||
if thread == nil do return nil;
|
||||
|
||||
// Set thread priority.
|
||||
policy: i32;
|
||||
res := unix.pthread_attr_getschedpolicy(&attrs, &policy);
|
||||
assert(res == 0);
|
||||
params: unix.sched_param;
|
||||
res = unix.pthread_attr_getschedparam(&attrs, ¶ms);
|
||||
assert(res == 0);
|
||||
low := unix.sched_get_priority_min(policy);
|
||||
high := unix.sched_get_priority_max(policy);
|
||||
switch priority {
|
||||
case .Normal: // Okay
|
||||
case .Low: params.sched_priority = low + 1;
|
||||
case .High: params.sched_priority = high;
|
||||
}
|
||||
res = unix.pthread_attr_setschedparam(&attrs, ¶ms);
|
||||
assert(res == 0);
|
||||
|
||||
sync.condition_init(&thread.start_gate);
|
||||
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
|
||||
free(thread);
|
||||
return nil;
|
||||
}
|
||||
thread.procedure = procedure;
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
start :: proc(t: ^Thread) {
|
||||
if sync.atomic_swap(&t.started, true, .Sequentially_Consistent) do return;
|
||||
sync.condition_signal(&t.start_gate);
|
||||
}
|
||||
|
||||
is_done :: proc(t: ^Thread) -> bool {
|
||||
return sync.atomic_load(&t.done, .Sequentially_Consistent);
|
||||
}
|
||||
|
||||
join :: proc(t: ^Thread) {
|
||||
if unix.pthread_equal(unix.pthread_self(), t.unix_thread) do return;
|
||||
// if unix.pthread_self().x == t.unix_thread.x do return;
|
||||
|
||||
// NOTE(tetra): It's apparently UB for multiple threads to join the same thread
|
||||
// at the same time.
|
||||
// If someone else already did, spin until the thread dies.
|
||||
// See note on `already_joined` field.
|
||||
// TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
|
||||
// sure it makes sense to need to join from multiple threads?
|
||||
if sync.atomic_swap(&t.already_joined, true, .Sequentially_Consistent) {
|
||||
for {
|
||||
if sync.atomic_load(&t.done, .Sequentially_Consistent) do return;
|
||||
sync.yield_processor();
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
|
||||
// will just return 3 (ESRCH).
|
||||
// We do this instead because I don't know if there is a danger
|
||||
// that you may join a different thread from the one you called join on,
|
||||
// if the thread handle is reused.
|
||||
if sync.atomic_load(&t.done, .Sequentially_Consistent) do return;
|
||||
|
||||
ret := unix.pthread_join(t.unix_thread, nil);
|
||||
assert(ret == 0, "cannot join thread");
|
||||
assert(sync.atomic_load(&t.done, .Sequentially_Consistent), "thread not done after join");
|
||||
}
|
||||
|
||||
destroy :: proc(t: ^Thread) {
|
||||
join(t);
|
||||
t.unix_thread = {};
|
||||
free(t);
|
||||
}
|
||||
|
||||
|
||||
yield :: proc() {
|
||||
unix.sched_yield();
|
||||
}
|
||||
@@ -1,27 +1,30 @@
|
||||
package thread
|
||||
|
||||
import "core:runtime"
|
||||
import "core:sync"
|
||||
import "core:sys/win32"
|
||||
|
||||
Thread_Proc :: #type proc(^Thread) -> int;
|
||||
|
||||
Thread_Os_Specific :: struct {
|
||||
win32_thread: win32.Handle,
|
||||
win32_thread_id: u32,
|
||||
done: bool, // see note in `is_done`
|
||||
}
|
||||
|
||||
Thread :: struct {
|
||||
using specific: Thread_Os_Specific,
|
||||
procedure: Thread_Proc,
|
||||
data: rawptr,
|
||||
user_index: int,
|
||||
THREAD_PRIORITY_IDLE :: -15;
|
||||
THREAD_PRIORITY_LOWEST :: -2;
|
||||
THREAD_PRIORITY_BELOW_NORMAL :: -1;
|
||||
THREAD_PRIORITY_NORMAL :: 0;
|
||||
THREAD_PRIORITY_ABOVE_NORMAL :: 1;
|
||||
THREAD_PRIORITY_HIGHEST :: 2;
|
||||
THREAD_PRIORITY_TIME_CRITICAL :: 15;
|
||||
|
||||
init_context: runtime.Context,
|
||||
use_init_context: bool,
|
||||
Thread_Priority :: enum i32 {
|
||||
Normal = THREAD_PRIORITY_NORMAL,
|
||||
Low = THREAD_PRIORITY_LOWEST,
|
||||
High = THREAD_PRIORITY_HIGHEST,
|
||||
}
|
||||
|
||||
|
||||
create :: proc(procedure: Thread_Proc) -> ^Thread {
|
||||
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
|
||||
win32_thread_id: u32;
|
||||
|
||||
__windows_thread_entry_proc :: proc "c" (t: ^Thread) -> i32 {
|
||||
@@ -31,7 +34,16 @@ create :: proc(procedure: Thread_Proc) -> ^Thread {
|
||||
}
|
||||
context = c;
|
||||
|
||||
return i32(t.procedure(t));
|
||||
t.procedure(t);
|
||||
|
||||
if !t.use_init_context {
|
||||
if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
|
||||
runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data);
|
||||
}
|
||||
}
|
||||
|
||||
sync.atomic_store(&t.done, true, .Sequentially_Consistent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,6 +59,9 @@ create :: proc(procedure: Thread_Proc) -> ^Thread {
|
||||
thread.win32_thread = win32_thread;
|
||||
thread.win32_thread_id = win32_thread_id;
|
||||
|
||||
ok := win32.set_thread_priority(win32_thread, i32(priority));
|
||||
assert(ok == true);
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
@@ -55,14 +70,18 @@ start :: proc(using thread: ^Thread) {
|
||||
}
|
||||
|
||||
is_done :: proc(using thread: ^Thread) -> bool {
|
||||
res := win32.wait_for_single_object(win32_thread, 0);
|
||||
return res != win32.WAIT_TIMEOUT;
|
||||
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
|
||||
// checking if it didn't time out immediately, is not good enough,
|
||||
// so we do it this way instead.
|
||||
return sync.atomic_load(&done, .Sequentially_Consistent);
|
||||
}
|
||||
|
||||
join :: proc(using thread: ^Thread) {
|
||||
win32.wait_for_single_object(win32_thread, win32.INFINITE);
|
||||
win32.close_handle(win32_thread);
|
||||
win32_thread = win32.INVALID_HANDLE;
|
||||
if win32_thread != win32.INVALID_HANDLE {
|
||||
win32.wait_for_single_object(win32_thread, win32.INFINITE);
|
||||
win32.close_handle(win32_thread);
|
||||
win32_thread = win32.INVALID_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
destroy :: proc(thread: ^Thread) {
|
||||
@@ -73,3 +92,7 @@ destroy :: proc(thread: ^Thread) {
|
||||
terminate :: proc(using thread : ^Thread, exit_code : u32) {
|
||||
win32.terminate_thread(win32_thread, exit_code);
|
||||
}
|
||||
|
||||
yield :: proc() {
|
||||
win32.sleep(0);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
package time
|
||||
IS_SUPPORTED :: false;
|
||||
@@ -1,44 +0,0 @@
|
||||
package time
|
||||
|
||||
import "core:os";
|
||||
|
||||
// NOTE(Jeroen): The times returned are in UTC
|
||||
IS_SUPPORTED :: true;
|
||||
|
||||
now :: proc() -> Time {
|
||||
|
||||
time_spec_now := os.clock_gettime(os.CLOCK_REALTIME);
|
||||
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
|
||||
return Time{_nsec=ns};
|
||||
}
|
||||
|
||||
boot_time :: proc() -> Time {
|
||||
|
||||
ts_now := os.clock_gettime(os.CLOCK_REALTIME);
|
||||
ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
|
||||
|
||||
ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec;
|
||||
return Time{_nsec=ns};
|
||||
}
|
||||
|
||||
seconds_since_boot :: proc() -> f64 {
|
||||
|
||||
ts_boottime := os.clock_gettime(os.CLOCK_BOOTTIME);
|
||||
return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
sleep :: proc(d: Duration) {
|
||||
|
||||
ds := duration_seconds(d);
|
||||
seconds := u64(ds);
|
||||
nanoseconds := i64((ds - f64(seconds)) * 1e9);
|
||||
|
||||
if seconds > 0 do os.sleep(seconds);
|
||||
if nanoseconds > 0 do os.nanosleep(nanoseconds);
|
||||
}
|
||||
|
||||
nanosleep :: proc(d: Duration) {
|
||||
// NOTE(Jeroen): os.nanosleep returns -1 on failure, 0 on success
|
||||
// duration needs to be [0, 999999999] nanoseconds.
|
||||
os.nanosleep(i64(d));
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
package time
|
||||
|
||||
IS_SUPPORTED :: false;
|
||||
@@ -0,0 +1,80 @@
|
||||
//+build linux, darwin
|
||||
package time
|
||||
|
||||
IS_SUPPORTED :: true; // NOTE: Times on Darwin are UTC.
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^TimeSpec) -> i32 ---;
|
||||
@(link_name="sleep") _unix_sleep :: proc(seconds: u32) -> i32 ---;
|
||||
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 ---;
|
||||
}
|
||||
|
||||
TimeSpec :: struct {
|
||||
tv_sec : i64, /* seconds */
|
||||
tv_nsec : i64, /* nanoseconds */
|
||||
};
|
||||
|
||||
CLOCK_REALTIME :: 0; // NOTE(tetra): May jump in time, when user changes the system time.
|
||||
CLOCK_MONOTONIC :: 1; // NOTE(tetra): May stand still while system is asleep.
|
||||
CLOCK_PROCESS_CPUTIME_ID :: 2;
|
||||
CLOCK_THREAD_CPUTIME_ID :: 3;
|
||||
CLOCK_MONOTONIC_RAW :: 4; // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
||||
CLOCK_REALTIME_COARSE :: 5; // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
|
||||
CLOCK_MONOTONIC_COARSE :: 6;
|
||||
CLOCK_BOOTTIME :: 7; // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
|
||||
CLOCK_REALTIME_ALARM :: 8;
|
||||
CLOCK_BOOTTIME_ALARM :: 9;
|
||||
|
||||
// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
|
||||
// I do not know if Darwin programmers are used to the existance of these constants or not, so
|
||||
// I'm leaving aliases to them for now.
|
||||
CLOCK_SYSTEM :: CLOCK_REALTIME;
|
||||
CLOCK_CALENDAR :: CLOCK_MONOTONIC;
|
||||
|
||||
|
||||
clock_gettime :: proc(clock_id: u64) -> TimeSpec {
|
||||
ts : TimeSpec; // NOTE(tetra): Do we need to initialize this?
|
||||
_unix_clock_gettime(clock_id, &ts);
|
||||
return ts;
|
||||
}
|
||||
|
||||
now :: proc() -> Time {
|
||||
time_spec_now := clock_gettime(CLOCK_REALTIME);
|
||||
ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec;
|
||||
return Time{_nsec=ns};
|
||||
}
|
||||
|
||||
boot_time :: proc() -> Time {
|
||||
ts_now := clock_gettime(CLOCK_REALTIME);
|
||||
ts_boottime := clock_gettime(CLOCK_BOOTTIME);
|
||||
|
||||
ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec;
|
||||
return Time{_nsec=ns};
|
||||
}
|
||||
|
||||
seconds_since_boot :: proc() -> f64 {
|
||||
ts_boottime := clock_gettime(CLOCK_BOOTTIME);
|
||||
return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9;
|
||||
}
|
||||
|
||||
|
||||
sleep :: proc(d: Duration) {
|
||||
ds := duration_seconds(d);
|
||||
seconds := u32(ds);
|
||||
nanoseconds := i64((ds - f64(seconds)) * 1e9);
|
||||
|
||||
if seconds > 0 do _unix_sleep(seconds);
|
||||
if nanoseconds > 0 do nanosleep(nanoseconds);
|
||||
}
|
||||
|
||||
nanosleep :: proc(nanoseconds: i64) -> int {
|
||||
// NOTE(tetra): Should we remove this assert? We are measuring nanoseconds after all...
|
||||
assert(nanoseconds <= 999999999);
|
||||
|
||||
requested := TimeSpec{tv_nsec = nanoseconds};
|
||||
remaining: TimeSpec; // NOTE(tetra): Do we need to initialize this?
|
||||
return int(_unix_nanosleep(&requested, &remaining));
|
||||
}
|
||||
@@ -17,8 +17,6 @@ now :: proc() -> Time {
|
||||
return Time{_nsec=ns};
|
||||
}
|
||||
|
||||
|
||||
|
||||
sleep :: proc(d: Duration) {
|
||||
win32.sleep(i32(d/Millisecond));
|
||||
win32.sleep(u32(d/Millisecond));
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
|
||||
}
|
||||
|
||||
|
||||
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
|
||||
encode_surrogate_pair :: proc(c: rune) -> (r1, r2: rune) {
|
||||
r := c;
|
||||
if r < _surr_self || r > MAX_RUNE {
|
||||
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
+66
-21
@@ -41,26 +41,22 @@ accept_ranges := [5]Accept_Range{
|
||||
};
|
||||
|
||||
accept_sizes := [256]u8{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f
|
||||
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf
|
||||
0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf
|
||||
0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
|
||||
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
|
||||
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
||||
0x00..0x7f = 0xf0,
|
||||
0x80..0xc1 = 0xf1,
|
||||
0xc2..0xdf = 0x02,
|
||||
0xe0 = 0x13,
|
||||
0xe1..0xec = 0x03,
|
||||
0xed = 0x23,
|
||||
0xee..0xef = 0x03,
|
||||
0xf0 = 0x34,
|
||||
0xf1..0xf3 = 0x04,
|
||||
0xf4 = 0x44,
|
||||
0xf5..0xff = 0xf1,
|
||||
};
|
||||
|
||||
encode_rune :: proc(r: rune) -> ([4]u8, int) {
|
||||
encode_rune :: proc(c: rune) -> ([4]u8, int) {
|
||||
r := c;
|
||||
|
||||
buf: [4]u8;
|
||||
i := u32(r);
|
||||
mask :: u8(0x3f);
|
||||
@@ -94,7 +90,7 @@ encode_rune :: proc(r: rune) -> ([4]u8, int) {
|
||||
return buf, 4;
|
||||
}
|
||||
|
||||
decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(cast([]u8)s);
|
||||
decode_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_rune(transmute([]u8)s);
|
||||
decode_rune :: proc(s: []u8) -> (rune, int) {
|
||||
n := len(s);
|
||||
if n < 1 {
|
||||
@@ -134,7 +130,7 @@ decode_rune :: proc(s: []u8) -> (rune, int) {
|
||||
|
||||
|
||||
|
||||
decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(cast([]u8)s);
|
||||
decode_last_rune_in_string :: inline proc(s: string) -> (rune, int) do return decode_last_rune(transmute([]u8)s);
|
||||
decode_last_rune :: proc(s: []u8) -> (rune, int) {
|
||||
r: rune;
|
||||
size: int;
|
||||
@@ -165,9 +161,58 @@ decode_last_rune :: proc(s: []u8) -> (rune, int) {
|
||||
return r, size;
|
||||
}
|
||||
|
||||
rune_at_pos :: proc(s: string, pos: int) -> rune {
|
||||
if pos < 0 {
|
||||
return RUNE_ERROR;
|
||||
}
|
||||
|
||||
i := 0;
|
||||
for r in s {
|
||||
if i == pos {
|
||||
return r;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return RUNE_ERROR;
|
||||
}
|
||||
|
||||
rune_string_at_pos :: proc(s: string, pos: int) -> string {
|
||||
if pos < 0 {
|
||||
return "";
|
||||
}
|
||||
|
||||
i := 0;
|
||||
for c, offset in s {
|
||||
if i == pos {
|
||||
w := rune_size(c);
|
||||
return s[offset:][:w];
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
rune_at :: proc(s: string, byte_index: int) -> rune {
|
||||
r, _ := decode_rune_in_string(s[byte_index:]);
|
||||
return r;
|
||||
}
|
||||
|
||||
// Returns the byte position of rune at position pos in s with an optional start byte position.
|
||||
// Returns -1 if it runs out of the string.
|
||||
rune_offset :: proc(s: string, pos: int, start: int = 0) -> int {
|
||||
if pos < 0 {
|
||||
return -1;
|
||||
}
|
||||
|
||||
i := 0;
|
||||
for _, offset in s[start:] {
|
||||
if i == pos {
|
||||
return offset+start;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
valid_rune :: proc(r: rune) -> bool {
|
||||
if r < 0 {
|
||||
@@ -215,7 +260,7 @@ valid_string :: proc(s: string) -> bool {
|
||||
|
||||
rune_start :: inline proc(b: u8) -> bool do return b&0xc0 != 0x80;
|
||||
|
||||
rune_count_in_string :: inline proc(s: string) -> int do return rune_count(cast([]u8)s);
|
||||
rune_count_in_string :: inline proc(s: string) -> int do return rune_count(transmute([]u8)s);
|
||||
rune_count :: proc(s: []u8) -> int {
|
||||
count := 0;
|
||||
n := len(s);
|
||||
|
||||
+1278
-304
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
package basic
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("Hellope!");
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package eca
|
||||
|
||||
import "core:fmt"
|
||||
import "core:math/rand"
|
||||
import "core:time"
|
||||
import "intrinsics"
|
||||
|
||||
elementary_cellular_automata :: proc(state: $T, rule: u8, generations: int, pause: time.Duration = 0)
|
||||
where intrinsics.type_is_integer(T),
|
||||
intrinsics.type_is_unsigned(T) {
|
||||
N :: 8*size_of(state);
|
||||
|
||||
output :: proc(state: T) {
|
||||
buf: [N]byte;
|
||||
for i in 0..<T(N) {
|
||||
c := byte('#');
|
||||
// c := byte(rand.int_max(26) + 'A' + ('a'-'A')*rand.int_max(2));
|
||||
buf[N-1-i] = state & (1<<i) != 0 ? c : ' ';
|
||||
}
|
||||
fmt.println(string(buf[:]));
|
||||
}
|
||||
|
||||
bit :: proc(x, i: T) -> T {
|
||||
return (x >> i) & 0x1;
|
||||
}
|
||||
set :: proc(x: ^T, cell, k: T, rule: u8) {
|
||||
x^ &~= 1<<cell;
|
||||
if rule>>k&1 != 0 {
|
||||
x^ |= 1<<cell;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
a := state;
|
||||
a1 := T(0);
|
||||
|
||||
output(a);
|
||||
|
||||
last := T(N-1);
|
||||
for r in 0..<generations {
|
||||
if pause > 0 do time.sleep(pause);
|
||||
|
||||
|
||||
k := bit(a, last) | bit(a, 0)<<1 | bit(a, 1)<<2;
|
||||
set(&a1, 0, k, rule);
|
||||
a1 |= (1<<0) * T(rule>>k&1);
|
||||
for c in 1..<last {
|
||||
k = k>>1 | bit(a, c+1)<<2;
|
||||
set(&a1, c, k, rule);
|
||||
}
|
||||
set(&a1, last, k>>1|bit(a, 0)<<2, rule);
|
||||
a, a1 = a1, a;
|
||||
output(a);
|
||||
if a == a1 {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
elementary_cellular_automata(
|
||||
state=rand.uint128(),
|
||||
rule=30,
|
||||
generations=5000,
|
||||
pause=100*time.Millisecond,
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 75 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user