mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
452 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 86511d44e4 | |||
| fd4633eb25 | |||
| b0756f9e29 | |||
| c3ff1e9591 | |||
| dd3fac7523 | |||
| 13029d06b2 | |||
| 68173f4bc7 | |||
| c979c2fafa | |||
| 658435f1b9 | |||
| 3935957979 | |||
| a36640bcfc | |||
| 171d5b4012 | |||
| 1cc893f21c | |||
| 6ff2db47b4 | |||
| a11b6a9e5f | |||
| 978568684c | |||
| e8e7d3ea31 | |||
| c03cc21908 | |||
| 50a2493fd3 | |||
| b455ccd261 | |||
| a58650728e | |||
| b22ddb1453 | |||
| cb7dd12222 | |||
| 0484bdbb7e | |||
| 8f39c45e9b | |||
| 944396128b | |||
| 782f1b4718 | |||
| 85f0a1067c | |||
| c08ff891ad | |||
| 168cec1e9d | |||
| 28fb35f2f7 | |||
| c1384afe2f | |||
| 547c7bce1b | |||
| 0bb93d40d3 | |||
| 27ba1d596c | |||
| 98e5523f2f | |||
| 223b66f422 | |||
| 04a4dbcdaf | |||
| ef9e31cb31 | |||
| e019673a18 | |||
| 5f27f2dd7f | |||
| cfccf73cdd | |||
| 465d003b1e | |||
| 1d6f7680a1 | |||
| 5d0f9c428a | |||
| d904ae5191 | |||
| 8a822bdd9a | |||
| d1a3842e39 | |||
| 00823ca88c | |||
| ffa14c3aad | |||
| 41b32f0da4 | |||
| c53b2198a8 | |||
| 9b278db993 | |||
| e98f1a28e6 | |||
| c8f05b7c0c | |||
| b00c4a6a8f | |||
| 84e1fb2cee | |||
| b983ac548c | |||
| fb562ea708 | |||
| cdeeeafc3f | |||
| b9a2426e57 | |||
| 81037b3091 | |||
| fc3c76f946 | |||
| 63a0395a79 | |||
| 6f0bad816e | |||
| 94af3c2887 | |||
| e5d0417a6c | |||
| c02bda2427 | |||
| 67c1b364c4 | |||
| f029b4beb1 | |||
| 3040361fac | |||
| 44caa96d50 | |||
| 1bea0f3772 | |||
| eb0775ad53 | |||
| 8fc9566a83 | |||
| 134c7db4d2 | |||
| a0e3a99dd1 | |||
| 0edda2bea7 | |||
| ff7f139fd7 | |||
| 86a606e716 | |||
| 1e97588e7b | |||
| 3ccc0b5aa6 | |||
| 5464a605b1 | |||
| 5519749aa4 | |||
| 4a70265bfb | |||
| de0d860880 | |||
| a13e2f4578 | |||
| 01b508f182 | |||
| 2a8fa8612d | |||
| e27046098b | |||
| ca8b148fdc | |||
| c1f5be24e2 | |||
| 6cdec65ca1 | |||
| 967afd8bbb | |||
| 0ae1812f90 | |||
| eb5523d5d3 | |||
| 3f4bbbec29 | |||
| 70bd220f34 | |||
| bd3596f012 | |||
| 66ce990e0b | |||
| 690666537c | |||
| 056ba1ed13 | |||
| 93a1f2bf61 | |||
| ac5f5a33e9 | |||
| 0829ac30f7 | |||
| 9d50a04905 | |||
| 1ca7da6914 | |||
| 56e050fbc9 | |||
| 70e48e39a4 | |||
| 2b0c04f27e | |||
| 1ddbe16d28 | |||
| 09c1128d9e | |||
| 588c52a854 | |||
| 86ec3bcb44 | |||
| 9fc606de48 | |||
| 7fbee88061 | |||
| b3be2cdf9d | |||
| ff6b76986a | |||
| 5c3624eb86 | |||
| 144e357fd2 | |||
| be22f0d1e1 | |||
| 34a048f7da | |||
| ffe953b43d | |||
| b8eacfc7b6 | |||
| f8452bf1fc | |||
| 20943a81c1 | |||
| 1c4e75e83f | |||
| 9cb9964c2d | |||
| 1f8f94276e | |||
| 0d7c89e84a | |||
| a5bdb4a8e8 | |||
| 521ed28632 | |||
| d6300314c0 | |||
| 27130259cc | |||
| b4fb295bb3 | |||
| f7e608628b | |||
| 605d66845a | |||
| 37ec3d7006 | |||
| 89eb351d2b | |||
| abaacfe78d | |||
| f9f4551e8d | |||
| daf005d1ab | |||
| ce1ee962f5 | |||
| d0e4edfb43 | |||
| 749e5067fb | |||
| 75dcaf6d8d | |||
| 00a0a1e95d | |||
| 7a4106077a | |||
| 9c8eaeb988 | |||
| 7ed28e8a84 | |||
| a3d53a6288 | |||
| 2127dc56b1 | |||
| e59e34d334 | |||
| 4fd97c3ba6 | |||
| 107c7a36d0 | |||
| dcf2c43863 | |||
| 0c25f7cdc5 | |||
| e5c243ee93 | |||
| e9b6a8fc9a | |||
| a27c00862c | |||
| a06f75b6fb | |||
| d88b052d2d | |||
| 615eccb6d1 | |||
| d3c65b6ba5 | |||
| 90415e4a6e | |||
| 7352c312e0 | |||
| 0befadde1d | |||
| aef8b25a8e | |||
| ae81117f70 | |||
| d6cb105d5f | |||
| b7b9a016d3 | |||
| 5ac36b5f25 | |||
| 22bcf1ba70 | |||
| 51c705edf1 | |||
| 708a1b0cd3 | |||
| 7ab591667a | |||
| e45401bfb4 | |||
| 6b652afb8e | |||
| 0a0db23b17 | |||
| 76b85c0622 | |||
| 6fa0679be9 | |||
| afea221d64 | |||
| b9ec2de4db | |||
| 3dfd53aee0 | |||
| b54fc8ff95 | |||
| 8745942255 | |||
| c7be30e0ea | |||
| 1baa47c78e | |||
| 0b33df4e9d | |||
| 4c40495742 | |||
| 824b97d250 | |||
| 5bbab05161 | |||
| 83558a1352 | |||
| cb183e968a | |||
| 27d56d0da4 | |||
| c663566cd5 | |||
| 13d052027f | |||
| 7076cf69e4 | |||
| 6ae8adaa45 | |||
| 15bbdb2030 | |||
| 48c9c1682c | |||
| d3c5143292 | |||
| 3949e2220f | |||
| 9ed4f95c1a | |||
| 8daecf7532 | |||
| 11d665c25a | |||
| 98a086b91b | |||
| f323a179d9 | |||
| c6f282d20b | |||
| ba49a9100d | |||
| 6fe77155b5 | |||
| 677e7ff642 | |||
| 682b5fa0d3 | |||
| ab00db2ebd | |||
| 0a0e8f36eb | |||
| bbe44b49bc | |||
| 25bec19b1f | |||
| 81f83d5780 | |||
| d2019e3e4d | |||
| 489e8dc592 | |||
| 3edb3d8d8c | |||
| a705a2e38b | |||
| 7dfbda58d9 | |||
| 9b88a38e54 | |||
| 16a494347c | |||
| ad0f11668b | |||
| 699cabeb1c | |||
| 7207f4b0c5 | |||
| 9c1b464c94 | |||
| 04a1e7d638 | |||
| 7cfbd87f57 | |||
| e9e05a3783 | |||
| 2b83f27f06 | |||
| 3d0e194298 | |||
| fcd8860990 | |||
| 22840ddf97 | |||
| f9576c2f5b | |||
| 16fc961010 | |||
| d2701d8b13 | |||
| a0bd31646b | |||
| 0d37da54b4 | |||
| 5d47e2a166 | |||
| 035c75d6a9 | |||
| b475481788 | |||
| 033525fe13 | |||
| 8852d090b6 | |||
| ac259ac790 | |||
| 7b4a87d37c | |||
| f6fc3ebe37 | |||
| 5c106abe3f | |||
| db748b7a05 | |||
| f2f2d532f5 | |||
| 1bcec3f769 | |||
| b035ee2bcd | |||
| 0424fb486b | |||
| 3858422f1d | |||
| d4f343751e | |||
| 79baddc157 | |||
| bcf437dc11 | |||
| 503eb470a7 | |||
| 667af1be58 | |||
| 366779f8c7 | |||
| dae299b781 | |||
| 2f29894b45 | |||
| 0819d05a0b | |||
| 6a4e44607c | |||
| a71daee545 | |||
| 046dd55032 | |||
| 2fc3da3fde | |||
| a74093784c | |||
| ed58374964 | |||
| 6dd4d1a924 | |||
| d77269dee2 | |||
| ea263b8cc5 | |||
| 45f0c812af | |||
| 810a1eee41 | |||
| e3e225d21b | |||
| 50e10ceb3b | |||
| da774e3fd2 | |||
| 2c3febd620 | |||
| bce62b98d4 | |||
| e914a8710d | |||
| c96e0afbf1 | |||
| f1c24f434b | |||
| e8517e1d02 | |||
| 92e406cef0 | |||
| 269913ede0 | |||
| 2ed16240a7 | |||
| ff36b754cb | |||
| 503b897677 | |||
| d69c74665a | |||
| fcf081283c | |||
| 7a6e8543a6 | |||
| f30755a871 | |||
| 503220e4c1 | |||
| 051814a69c | |||
| 21843da9e3 | |||
| 30f49f81c1 | |||
| 439f4776e4 | |||
| b743f56fb9 | |||
| 1fc3f6cb2e | |||
| df19c48da8 | |||
| f7211408fc | |||
| 30db316e16 | |||
| 8c01e952f3 | |||
| 3e66b88031 | |||
| f76316f889 | |||
| 32477a88ef | |||
| e8bc576b23 | |||
| 2eea6f2490 | |||
| 1d9d79542c | |||
| 1a6d4c955a | |||
| 717522efe4 | |||
| 8d06d9c23d | |||
| 765c1546c5 | |||
| 7ec6fd30f0 | |||
| 0ca773114a | |||
| 9e1576418f | |||
| b7ea169c81 | |||
| 3b583cbac7 | |||
| 382bd87667 | |||
| 6cc07dc24e | |||
| 01cdd22a01 | |||
| 35331e6973 | |||
| c18e98e8c5 | |||
| 3cd553565f | |||
| 9eec9f5788 | |||
| 2b7ca2bdd6 | |||
| 411c0add3b | |||
| 18d7ecc1a5 | |||
| 4812601e78 | |||
| 2d5779b660 | |||
| fd53e8b955 | |||
| 53a030c65b | |||
| 91ad6b42c5 | |||
| dad10ef800 | |||
| 71eb21aab7 | |||
| f8228e305a | |||
| 0e7109cab2 | |||
| c39ef1b25c | |||
| 9da37ed394 | |||
| 8fa571c283 | |||
| 83f3ae14d5 | |||
| 6a14c3edb4 | |||
| 2cd895c50b | |||
| ee89c0458f | |||
| cee847a68c | |||
| 413f96553a | |||
| 662ed4a67c | |||
| 85a263130d | |||
| d19ae37af1 | |||
| 22672a816e | |||
| dcb873c88d | |||
| 62ab2987b6 | |||
| 7bcde35651 | |||
| 4b8721a0bb | |||
| 7743e34596 | |||
| 4003b76fd3 | |||
| c27ed1896f | |||
| 7d217269b5 | |||
| a3c8882648 | |||
| 4389059834 | |||
| a55e90fefd | |||
| f58f922487 | |||
| 1a0930f841 | |||
| a5f8c3f692 | |||
| 92fb65cf2e | |||
| a51943e27f | |||
| 03c834e410 | |||
| 989107094c | |||
| fd8956b8f4 | |||
| 648e3c65ea | |||
| d5047e621d | |||
| 8fbdef01d6 | |||
| 9dee943fae | |||
| 8ceb691cec | |||
| f26516f6fa | |||
| fda8e8a30b | |||
| 2242178d96 | |||
| a459bc13dc | |||
| 53e84b7f31 | |||
| 098f51aa80 | |||
| 765969e6a3 | |||
| 8086c14dcc | |||
| 80ce1b7d85 | |||
| 9f55404845 | |||
| 075040ae05 | |||
| aa799d6a0d | |||
| 58e607e960 | |||
| ff51c5ee56 | |||
| 73c1f08776 | |||
| 412ca36230 | |||
| 06d1df4cae | |||
| 7662808bc9 | |||
| d48828dd80 | |||
| b725e01cdd | |||
| 874c1f076d | |||
| 2c14f0a109 | |||
| cf4afc2e7b | |||
| 5ed06f7eb8 | |||
| 765cd66b30 | |||
| 5a8fbc230d | |||
| 5c62211f00 | |||
| 835b8ffa22 | |||
| b84108c4b5 | |||
| 6642e1fc9d | |||
| c909e8e4b8 | |||
| 9bdbb45517 | |||
| 1b5860e574 | |||
| 047d45584e | |||
| 721486f875 | |||
| 29f2ecd228 | |||
| 970ac22647 | |||
| 419eab5059 | |||
| a1935bc1f4 | |||
| fc06c8ed9f | |||
| 7952b26e8b | |||
| fa6cfde4b0 | |||
| 4c78ba2152 | |||
| 9870e43ac0 | |||
| 63086c7eaf | |||
| ef0c6fc4b3 | |||
| 159c5311c3 | |||
| b6a65fac36 | |||
| ab7367ae47 | |||
| 457f509b5f | |||
| 7e5c063d98 | |||
| dfabd0e0ad | |||
| 141133e326 | |||
| e188a542da | |||
| 64f1e8b7a2 | |||
| 62440df051 | |||
| 5362e883f4 | |||
| 8b06fd0935 | |||
| bb9b58b8c4 | |||
| ee070c9bd3 | |||
| aebafdcd08 | |||
| 2b4fce8684 | |||
| de8f6709f7 | |||
| 683753db96 | |||
| d13dc7eca7 | |||
| e56920e445 | |||
| 1c9aad4d7b | |||
| ce09cb0bdb | |||
| b7fd91817e | |||
| a728047281 | |||
| 5cf473b31c | |||
| c767d55e9a | |||
| 7f601c9535 | |||
| e139d1cbe4 | |||
| cb04116caf | |||
| 62ff8daa78 |
+15
-10
@@ -38,6 +38,11 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
timeout-minutes: 10
|
||||
@@ -87,10 +92,10 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
@@ -151,6 +156,13 @@ jobs:
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\internal
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -158,13 +170,6 @@ jobs:
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\issues
|
||||
call run.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v4.1.0
|
||||
uses: actions/stale@v7.0.0
|
||||
with:
|
||||
# stale-issue-message: |
|
||||
# Hello!
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
|
||||
|
||||
days-before-stale: 120
|
||||
days-before-close: 30
|
||||
days-before-close: -1
|
||||
exempt-draft-pr: true
|
||||
ascending: true
|
||||
operations-per-run: 1000
|
||||
|
||||
@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
|
||||
|
||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
Get live support and talk with other Odin programmers on the Odin Discord.
|
||||
|
||||
### Articles
|
||||
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
where /Q cl.exe || (
|
||||
set __VSCMD_ARG_NO_LOGO=1
|
||||
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
|
||||
if "!VS!" equ "" (
|
||||
echo ERROR: Visual Studio installation not found
|
||||
exit /b 1
|
||||
)
|
||||
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
|
||||
)
|
||||
|
||||
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
@@ -47,12 +62,14 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4146 ^
|
||||
-wd4505 ^
|
||||
-wd4456 -wd4457
|
||||
|
||||
set compiler_includes= ^
|
||||
/Isrc\
|
||||
set libs= ^
|
||||
kernel32.lib ^
|
||||
Synchronization.lib ^
|
||||
bin\llvm\windows\LLVM-C.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
@@ -79,4 +96,4 @@ if %release_mode% EQU 0 odin run examples/demo
|
||||
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
:end_of_build
|
||||
|
||||
+21
-1
@@ -44,7 +44,13 @@ config_darwin() {
|
||||
fi
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
}
|
||||
@@ -97,6 +103,12 @@ config_linux() {
|
||||
panic "Requirement: llvm-config must be base version greater than 11"
|
||||
fi
|
||||
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
|
||||
@@ -134,6 +146,14 @@ run_demo() {
|
||||
./odin run examples/demo/demo.odin -file
|
||||
}
|
||||
|
||||
have_which() {
|
||||
if ! which which > /dev/null 2>&1; then
|
||||
panic "Could not find \`which\`"
|
||||
fi
|
||||
}
|
||||
|
||||
have_which
|
||||
|
||||
case $OS in
|
||||
Linux)
|
||||
config_linux
|
||||
|
||||
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
|
||||
if not exist "vendor\cgltf\lib\*.lib" (
|
||||
pushd vendor\cgltf\src
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
+15
-2
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
|
||||
// EDOM,
|
||||
// EILSEQ
|
||||
// ERANGE
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
when ODIN_OS == .Linux {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__libc_errno_location")
|
||||
@(link_name="__errno_location")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 84
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .FreeBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package container_small_array
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
_ :: runtime
|
||||
|
||||
Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
data: [N]T,
|
||||
@@ -8,40 +10,54 @@ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
}
|
||||
|
||||
|
||||
len :: proc(a: $A/Small_Array) -> int {
|
||||
len :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return a.len
|
||||
}
|
||||
|
||||
cap :: proc(a: $A/Small_Array) -> int {
|
||||
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data)
|
||||
}
|
||||
|
||||
space :: proc(a: $A/Small_Array) -> int {
|
||||
space :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data) - a.len
|
||||
}
|
||||
|
||||
slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len]
|
||||
}
|
||||
|
||||
|
||||
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
get_safe :: proc(a: $A/Small_Array($N, $T), index: int) -> (T, bool) #no_bounds_check {
|
||||
if index < 0 || index >= a.len {
|
||||
return {}, false
|
||||
}
|
||||
return a.data[index], true
|
||||
}
|
||||
|
||||
get_ptr_safe :: proc(a: ^$A/Small_Array($N, $T), index: int) -> (^T, bool) #no_bounds_check {
|
||||
if index < 0 || index >= a.len {
|
||||
return {}, false
|
||||
}
|
||||
return &a.data[index], true
|
||||
}
|
||||
|
||||
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
a.data[index] = item
|
||||
}
|
||||
|
||||
resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, builtin.len(a.data))
|
||||
}
|
||||
|
||||
|
||||
push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.data[a.len] = item
|
||||
a.len += 1
|
||||
@@ -50,7 +66,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.len += 1
|
||||
data := slice(a)
|
||||
@@ -61,14 +77,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[a.len-1]
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[0]
|
||||
s := slice(a)
|
||||
@@ -77,7 +93,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
return item
|
||||
}
|
||||
|
||||
pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[a.len-1]
|
||||
a.len -= 1
|
||||
@@ -86,31 +102,60 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[0]
|
||||
s := slice(a)
|
||||
copy(s[:], s[1:])
|
||||
a.len -= 1
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
ordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||
if index+1 < a.len {
|
||||
copy(a.data[index:], a.data[index+1:])
|
||||
}
|
||||
a.len -= 1
|
||||
}
|
||||
|
||||
unordered_remove :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, loc := #caller_location) #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index, a.len)
|
||||
n := a.len-1
|
||||
if index != n {
|
||||
a.data[index] = a.data[n]
|
||||
}
|
||||
a.len -= 1
|
||||
}
|
||||
|
||||
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||
resize(a, 0)
|
||||
}
|
||||
|
||||
push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:])
|
||||
a.len += n
|
||||
}
|
||||
|
||||
inject_at :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T, index: int) -> bool #no_bounds_check {
|
||||
if a.len < cap(a^) && index >= 0 && index <= len(a^) {
|
||||
a.len += 1
|
||||
for i := a.len - 1; i >= index + 1; i -= 1 {
|
||||
a.data[i] = a.data[i - 1]
|
||||
}
|
||||
a.data[index] = item
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
append_elem :: push_back
|
||||
append_elems :: push_back_elems
|
||||
push :: proc{push_back, push_back_elems}
|
||||
|
||||
@@ -257,21 +257,18 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return .Unsupported_Type
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i in 0..<entries.len {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := rawptr(data + entry_type.offsets[2])
|
||||
value := rawptr(data + entry_type.offsets[3])
|
||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||
|
||||
// check for string type
|
||||
{
|
||||
@@ -281,13 +278,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
name: string
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
|
||||
case: return .Unsupported_Type
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,12 +399,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
raw_map := (^mem.Raw_Map)(v.data)
|
||||
if raw_map.entries.allocator.procedure == nil {
|
||||
raw_map.entries.allocator = p.allocator
|
||||
if raw_map.allocator.procedure == nil {
|
||||
raw_map.allocator = p.allocator
|
||||
}
|
||||
|
||||
header := runtime.__get_map_header_table_runtime(t)
|
||||
|
||||
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
||||
defer delete(elem_backing, p.allocator)
|
||||
|
||||
@@ -421,7 +419,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
return err
|
||||
}
|
||||
|
||||
key_hash := runtime.default_hasher_string(&key, 0)
|
||||
key_ptr := rawptr(&key)
|
||||
|
||||
key_cstr: cstring
|
||||
@@ -430,7 +427,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
key_ptr = &key_cstr
|
||||
}
|
||||
|
||||
set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
|
||||
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
||||
if set_ptr == nil {
|
||||
delete(key, p.allocator)
|
||||
}
|
||||
|
||||
+57
-104
@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
p("Panic", message, loc)
|
||||
}
|
||||
|
||||
// formatted printing for cstrings
|
||||
caprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// c string with temp allocator
|
||||
ctprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// sbprint formats using the default print settings and writes to buf
|
||||
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
|
||||
@@ -240,7 +258,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
|
||||
was_prev_index := false
|
||||
|
||||
loop: for i := 0; i < end; /**/ {
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
|
||||
|
||||
prev_i := i
|
||||
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
|
||||
@@ -529,7 +547,7 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok:
|
||||
is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' }
|
||||
|
||||
new_offset = offset
|
||||
for new_offset <= len(s) {
|
||||
for new_offset < len(s) {
|
||||
c := s[new_offset]
|
||||
if !is_digit(c) {
|
||||
break
|
||||
@@ -660,7 +678,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
||||
}
|
||||
} else if fi.zero && fi.width_set {
|
||||
prec = fi.width
|
||||
if neg || fi.plus || fi.space {
|
||||
if neg || fi.plus {
|
||||
// There needs to be space for the "sign"
|
||||
prec -= 1
|
||||
}
|
||||
@@ -679,7 +697,6 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero { flags |= {.Prefix} }
|
||||
if fi.plus { flags |= {.Plus} }
|
||||
if fi.space { flags |= {.Space} }
|
||||
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
|
||||
if fi.hash && fi.zero && fi.indent == 0 {
|
||||
@@ -726,7 +743,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
}
|
||||
} else if fi.zero && fi.width_set {
|
||||
prec = fi.width
|
||||
if neg || fi.plus || fi.space {
|
||||
if neg || fi.plus {
|
||||
// There needs to be space for the "sign"
|
||||
prec -= 1
|
||||
}
|
||||
@@ -745,7 +762,6 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
|
||||
flags: strconv.Int_Flags
|
||||
if fi.hash && !fi.zero { flags |= {.Prefix} }
|
||||
if fi.plus { flags |= {.Plus} }
|
||||
if fi.space { flags |= {.Space} }
|
||||
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
|
||||
|
||||
if fi.hash && fi.zero && fi.indent == 0 {
|
||||
@@ -849,79 +865,30 @@ _pad :: proc(fi: ^Info, s: string) {
|
||||
}
|
||||
}
|
||||
|
||||
_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) {
|
||||
prec := fi.prec if fi.prec_set else 3
|
||||
buf: [386]byte
|
||||
|
||||
// Can return "NaN", "+Inf", "-Inf", "+<value>", "-<value>".
|
||||
str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
|
||||
|
||||
if !fi.plus {
|
||||
// Strip sign from "+<value>" but not "+Inf".
|
||||
if str[0] == '+' && str[1] != 'I' {
|
||||
str = str[1:]
|
||||
}
|
||||
}
|
||||
|
||||
_pad(fi, str)
|
||||
}
|
||||
|
||||
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
|
||||
switch verb {
|
||||
case 'f', 'F', 'g', 'G', 'v':
|
||||
prec: int = 3
|
||||
if fi.prec_set {
|
||||
prec = fi.prec
|
||||
}
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], v, 'f', prec, bit_size)
|
||||
b := buf[:len(str)+1]
|
||||
if b[1] == '+' || b[1] == '-' {
|
||||
b = b[1:]
|
||||
} else {
|
||||
b[0] = '+'
|
||||
}
|
||||
|
||||
if fi.space && !fi.plus && b[0] == '+' {
|
||||
b[0] = ' '
|
||||
}
|
||||
|
||||
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
|
||||
io.write_string(fi.writer, string(b), &fi.n)
|
||||
return
|
||||
}
|
||||
|
||||
if fi.plus || b[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(b) {
|
||||
io.write_byte(fi.writer, b[0], &fi.n)
|
||||
fmt_write_padding(fi, fi.width - len(b))
|
||||
io.write_string(fi.writer, string(b[1:]), &fi.n)
|
||||
} else {
|
||||
_pad(fi, string(b))
|
||||
}
|
||||
} else {
|
||||
_pad(fi, string(b[1:]))
|
||||
}
|
||||
|
||||
_fmt_float_as(fi, v, bit_size, verb, 'f')
|
||||
case 'e', 'E':
|
||||
prec: int = 3
|
||||
if fi.prec_set {
|
||||
prec = fi.prec
|
||||
}
|
||||
buf: [386]byte
|
||||
|
||||
str := strconv.append_float(buf[1:], v, 'e', prec, bit_size)
|
||||
b := buf[:len(str)+1]
|
||||
if b[1] == '+' || b[1] == '-' {
|
||||
b = b[1:]
|
||||
} else {
|
||||
b[0] = '+'
|
||||
}
|
||||
|
||||
if fi.space && !fi.plus && b[0] == '+' {
|
||||
b[0] = ' '
|
||||
}
|
||||
|
||||
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
|
||||
io.write_string(fi.writer, string(b), &fi.n)
|
||||
return
|
||||
}
|
||||
|
||||
if fi.plus || str[0] != '+' {
|
||||
if fi.zero && fi.width_set && fi.width > len(b) {
|
||||
io.write_byte(fi.writer, b[0], &fi.n)
|
||||
fmt_write_padding(fi, fi.width - len(b))
|
||||
io.write_string(fi.writer, string(b[1:]), &fi.n)
|
||||
} else {
|
||||
_pad(fi, string(b))
|
||||
}
|
||||
} else {
|
||||
_pad(fi, string(b[1:]))
|
||||
}
|
||||
// BUG(): "%.3e" returns "3.000e+00"
|
||||
_fmt_float_as(fi, v, bit_size, verb, 'e')
|
||||
|
||||
case 'h', 'H':
|
||||
prev_fi := fi^
|
||||
@@ -975,7 +942,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
io.write_string(fi.writer, s[:fi.width], &fi.n)
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2051,41 +2018,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
/*
|
||||
NOTE: The layout of a `map` is as follows:
|
||||
|
||||
map[Key]Value
|
||||
|
||||
## Internal Layout
|
||||
struct {
|
||||
hashes: []int,
|
||||
entries: [dynamic]struct{
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
},
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
*/
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
if j > 0 {
|
||||
io.write_string(fi.writer, ", ", &fi.n)
|
||||
}
|
||||
j += 1
|
||||
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
key := data + entry_type.offsets[2] // key: Key
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
|
||||
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
|
||||
value := data + entry_type.offsets[3] // value: Value
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
// fprintf formats according to the specififed format string and writes to fd
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
@@ -34,12 +34,12 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
// println formats using the default print settings and writes to os.stdout
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
// printf formats according to the specififed format string and writes to os.stdout
|
||||
// printf formats according to the specified format string and writes to os.stdout
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
// eprint formats using the default print settings and writes to os.stderr
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintln formats using the default print settings and writes to os.stderr
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintf formats according to the specififed format string and writes to os.stderr
|
||||
// eprintf formats according to the specified format string and writes to os.stderr
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
|
||||
+7
-3
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
||||
return
|
||||
}
|
||||
|
||||
// If you have a choice, prefer fnv32a
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
return h
|
||||
}
|
||||
|
||||
fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
|
||||
// If you have a choice, prefer fnv64a
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package png
|
||||
|
||||
import "core:image"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package intrinsics
|
||||
|
||||
// Package-Related
|
||||
@@ -188,6 +188,9 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
type_map_info :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
|
||||
type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
||||
|
||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
@@ -43,6 +43,13 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Alloc_Non_Zeroed:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Free:
|
||||
if old_size != 0 {
|
||||
logf(
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
TODO: Handle +/- Infinity and NaN.
|
||||
*/
|
||||
|
||||
|
||||
//+ignore
|
||||
package math_big
|
||||
|
||||
import "core:mem"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||
*/
|
||||
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package math_big
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
|
||||
package linalg
|
||||
@@ -531,7 +531,7 @@ not_equal :: proc{not_equal_single, not_equal_array}
|
||||
|
||||
any :: proc(x: $A/[$N]bool) -> (out: bool) {
|
||||
for e in x {
|
||||
if x {
|
||||
if e {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
+337
-19
@@ -114,6 +114,92 @@ exp :: proc{
|
||||
exp_f64, exp_f64le, exp_f64be,
|
||||
}
|
||||
|
||||
pow10_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(pow10_f16(f16(x))) }
|
||||
pow10_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(pow10_f16(f16(x))) }
|
||||
pow10_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(pow10_f32(f32(x))) }
|
||||
pow10_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(pow10_f32(f32(x))) }
|
||||
pow10_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(pow10_f64(f64(x))) }
|
||||
pow10_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(pow10_f64(f64(x))) }
|
||||
pow10 :: proc{
|
||||
pow10_f16, pow10_f16le, pow10_f16be,
|
||||
pow10_f32, pow10_f32le, pow10_f32be,
|
||||
pow10_f64, pow10_f64le, pow10_f64be,
|
||||
}
|
||||
|
||||
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
|
||||
@static pow10_pos_tab := [?]f16{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f16{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 4 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -7 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f16(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
|
||||
@static pow10_pos_tab := [?]f32{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f32{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
|
||||
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
|
||||
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
|
||||
1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39,
|
||||
1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 38 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -45 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f32(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
|
||||
@static pow10_tab := [?]f64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31,
|
||||
}
|
||||
@static pow10_pos_tab32 := [?]f64{
|
||||
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
|
||||
}
|
||||
@static pow10_neg_tab32 := [?]f64{
|
||||
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 308 {
|
||||
return pow10_pos_tab32[uint(n)/32] * pow10_tab[uint(n)%32]
|
||||
}
|
||||
if -323 <= n && n <= 0 {
|
||||
return pow10_neg_tab32[uint(-n)/32] / pow10_tab[uint(-n)%32]
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
return inf_f64(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
|
||||
@@ -1088,7 +1174,7 @@ is_nan :: proc{
|
||||
// If sign < 0, is_inf reports whether f is negative infinity.
|
||||
// If sign == 0, is_inf reports whether f is either infinity.
|
||||
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1105,7 +1191,7 @@ is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1122,7 +1208,7 @@ is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1344,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case x == 0:
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
case is_inf(x, 0):
|
||||
if is_inf(x, 1) {
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.25, y)
|
||||
return copy_sign(PI/4, y)
|
||||
}
|
||||
return copy_sign(0, y)
|
||||
}
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.75, y)
|
||||
return copy_sign(3*PI/4, y)
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case is_inf(y, 0):
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
}
|
||||
|
||||
q := atan(y / x)
|
||||
@@ -1379,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
|
||||
}
|
||||
|
||||
atan2 :: proc{
|
||||
atan2_f16, atan2_f16le, atan2_f16be,
|
||||
atan2_f32, atan2_f32le, atan2_f32be,
|
||||
atan2_f64, atan2_f64le, atan2_f64be,
|
||||
atan2_f64, atan2_f32, atan2_f16,
|
||||
atan2_f64le, atan2_f64be,
|
||||
atan2_f32le, atan2_f32be,
|
||||
atan2_f16le, atan2_f16be,
|
||||
}
|
||||
|
||||
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1)
|
||||
}
|
||||
|
||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, sqrt(1 - x*x))
|
||||
|
||||
|
||||
asin_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
x := x
|
||||
z, r, s: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* asin(1) = +-pi/2 with inexact */
|
||||
return x*pio2_hi + 1e-120
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
|
||||
if ix < 0x3e500000 && ix >= 0x00100000 {
|
||||
return x
|
||||
}
|
||||
return x + x*R(x*x)
|
||||
}
|
||||
/* 1 > |x| >= 0.5 */
|
||||
z = (1 - abs(x))*0.5
|
||||
s = sqrt(z)
|
||||
r = R(z)
|
||||
if ix >= 0x3fef3333 { /* if |x| > 0.975 */
|
||||
x = pio2_hi-(2*(s+s*r)-pio2_lo)
|
||||
} else {
|
||||
f, c: f64
|
||||
/* f+c = sqrt(z) */
|
||||
f = s
|
||||
(^u64)(&f)^ &= 0xffffffff_00000000
|
||||
c = (z-f*f)/(s+f)
|
||||
x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f))
|
||||
}
|
||||
return -x if hx >> 31 != 0 else x
|
||||
}
|
||||
asin_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(asin_f64(f64(x)))
|
||||
}
|
||||
asin :: proc{
|
||||
asin_f64, asin_f32, asin_f16,
|
||||
asin_f64le, asin_f64be,
|
||||
asin_f32le, asin_f32be,
|
||||
asin_f16le, asin_f16be,
|
||||
}
|
||||
|
||||
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return 2 * atan2(sqrt(1 - x), sqrt(1 + x))
|
||||
|
||||
acos_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
z, w, s, c, df: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* acos(1)=0, acos(-1)=pi */
|
||||
if hx >> 31 != 0 {
|
||||
return 2*pio2_hi + 1e-120
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
if ix <= 0x3c600000 { /* |x| < 2**-57 */
|
||||
return pio2_hi + 1e-120
|
||||
}
|
||||
return pio2_hi - (x - (pio2_lo-x*R(x*x)))
|
||||
}
|
||||
/* x < -0.5 */
|
||||
if hx >> 31 != 0 {
|
||||
z = (1.0+x)*0.5
|
||||
s = sqrt(z)
|
||||
w = R(z)*s-pio2_lo
|
||||
return 2*(pio2_hi - (s+w))
|
||||
}
|
||||
/* x > 0.5 */
|
||||
z = (1.0-x)*0.5
|
||||
s = sqrt(z)
|
||||
df = s
|
||||
(^u64)(&df)^ &= 0xffffffff_00000000
|
||||
c = (z-df*df)/(s+df)
|
||||
w = R(z)*s+c
|
||||
return 2*(df+w)
|
||||
}
|
||||
acos_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(acos_f64(f64(x)))
|
||||
}
|
||||
acos :: proc{
|
||||
acos_f64, acos_f32, acos_f16,
|
||||
acos_f64le, acos_f64be,
|
||||
acos_f32le, acos_f32be,
|
||||
acos_f16le, acos_f16be,
|
||||
}
|
||||
|
||||
sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) - exp(-x))*0.5
|
||||
return copy_sign(((exp(x) - exp(-x))*0.5), x)
|
||||
}
|
||||
|
||||
cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) + exp(-x))*0.5
|
||||
return ((exp(x) + exp(-x))*0.5)
|
||||
}
|
||||
|
||||
tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
t := exp(2*x)
|
||||
return (t - 1) / (t + 1)
|
||||
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
P0 :: -9.64399179425052238628e-1
|
||||
P1 :: -9.92877231001918586564e1
|
||||
P2 :: -1.61468768441708447952e3
|
||||
Q0 :: +1.12811678491632931402e2
|
||||
Q1 :: +2.23548839060100448583e3
|
||||
Q2 :: +4.84406305325125486048e3
|
||||
|
||||
MAXLOG :: 8.8029691931113054295988e+01 // log(2**127)
|
||||
|
||||
|
||||
x := f64(y)
|
||||
z := abs(x)
|
||||
switch {
|
||||
case z > 0.5*MAXLOG:
|
||||
if x < 0 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case z >= 0.625:
|
||||
s := exp(2 * z)
|
||||
z = 1 - 2/(s+1)
|
||||
if x < 0 {
|
||||
z = -z
|
||||
}
|
||||
case:
|
||||
if x == 0 {
|
||||
return T(x)
|
||||
}
|
||||
s := x * x
|
||||
z = x + x*s*((P0*s+P1)*s+P2)/(((s+Q0)*s+Q1)*s+Q2)
|
||||
}
|
||||
return T(z)
|
||||
}
|
||||
|
||||
asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
|
||||
@@ -182,3 +182,12 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
array[i], array[j] = array[j], array[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random element from the given slice
|
||||
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
|
||||
n := i64(len(array))
|
||||
if n < 1 {
|
||||
return E{}
|
||||
}
|
||||
return array[int63_max(n, r)]
|
||||
}
|
||||
+25
-17
@@ -69,10 +69,22 @@ alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator :=
|
||||
return runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.mem_free(ptr, allocator, loc)
|
||||
}
|
||||
|
||||
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
|
||||
return err
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.mem_free_bytes(bytes, allocator, loc)
|
||||
}
|
||||
@@ -108,22 +120,20 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
|
||||
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc)
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
free((^byte)(str), allocator, loc)
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free((^byte)(str), allocator, loc)
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
free(raw_data(array), array.allocator, loc)
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(array), allocator, loc)
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
free(raw.entries.data, raw.entries.allocator, loc)
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -154,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16
|
||||
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
|
||||
runtime.make_slice_error_loc(loc, len)
|
||||
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
|
||||
@@ -169,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
}
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
|
||||
}
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
@@ -184,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
array = transmute(T)s
|
||||
return
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_map_expr_error_loc(loc, cap)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, cap, loc)
|
||||
return m
|
||||
}
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
||||
|
||||
+61
-35
@@ -59,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena := cast(^Arena)allocator_data
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
@@ -72,7 +72,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(ptr, size)
|
||||
}
|
||||
return byte_slice(ptr, size), nil
|
||||
|
||||
case .Free:
|
||||
@@ -87,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -151,7 +153,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
s := (^Scratch_Allocator)(allocator_data)
|
||||
|
||||
if s.data == nil {
|
||||
DEFAULT_BACKING_SIZE :: 1<<22
|
||||
DEFAULT_BACKING_SIZE :: 4 * Megabyte
|
||||
if !(context.allocator.procedure != scratch_allocator_proc &&
|
||||
context.allocator.data != allocator_data) {
|
||||
panic("cyclic initialization of the scratch allocator with itself")
|
||||
@@ -162,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size := size
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
size = align_forward_int(size, alignment)
|
||||
|
||||
switch {
|
||||
@@ -170,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := start + uintptr(s.curr_offset)
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -180,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -211,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return ptr, err
|
||||
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
@@ -266,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -333,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset)
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
@@ -351,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, alignment, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -392,7 +403,7 @@ 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, alignment, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -418,7 +429,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
||||
|
||||
if old_offset != header.prev_offset {
|
||||
data, err := raw_alloc(s, size, alignment)
|
||||
data, err := raw_alloc(s, size, alignment, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -439,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
case .Query_Info:
|
||||
@@ -497,7 +508,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset)
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
@@ -513,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, align)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, align, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -548,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, align)
|
||||
return raw_alloc(s, size, align, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -571,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return byte_slice(old_memory, size), nil
|
||||
}
|
||||
|
||||
data, err := raw_alloc(s, size, align)
|
||||
data, err := raw_alloc(s, size, align, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -580,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -623,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
pool := (^Dynamic_Pool)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return dynamic_pool_alloc_bytes(pool, size)
|
||||
case .Free:
|
||||
return nil, .Mode_Not_Implemented
|
||||
@@ -643,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -794,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc called")
|
||||
}
|
||||
case .Alloc_Non_Zeroed:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
|
||||
}
|
||||
case .Resize:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Resize called")
|
||||
@@ -831,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
mode: Allocator_Mode,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
@@ -849,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
|
||||
t.backing = backing_allocator
|
||||
t.allocation_map.allocator = internals_allocator
|
||||
t.bad_free_array.allocator = internals_allocator
|
||||
|
||||
if .Free_All in query_features(t.backing) {
|
||||
t.clear_on_free_all = true
|
||||
}
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
@@ -856,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
|
||||
clear(&t.allocation_map)
|
||||
clear(&t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
data = data,
|
||||
@@ -865,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data)
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory)
|
||||
@@ -877,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
info.pointer = nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return
|
||||
}
|
||||
|
||||
result: []byte
|
||||
err: Allocator_Error
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
})
|
||||
} else {
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
|
||||
}
|
||||
result_ptr := raw_data(result)
|
||||
|
||||
@@ -900,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -921,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -929,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -937,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
unreachable()
|
||||
}
|
||||
|
||||
return result, err
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -3,11 +3,11 @@ package mem
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
|
||||
Byte :: 1
|
||||
Kilobyte :: 1024 * Byte
|
||||
Megabyte :: 1024 * Kilobyte
|
||||
Gigabyte :: 1024 * Megabyte
|
||||
Terabyte :: 1024 * Gigabyte
|
||||
Byte :: runtime.Byte
|
||||
Kilobyte :: runtime.Kilobyte
|
||||
Megabyte :: runtime.Megabyte
|
||||
Gigabyte :: runtime.Gigabyte
|
||||
Terabyte :: runtime.Terabyte
|
||||
|
||||
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
|
||||
return runtime.memset(data, i32(value), len)
|
||||
|
||||
+2
-19
@@ -1,5 +1,6 @@
|
||||
package mem
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
|
||||
Raw_Any :: runtime.Raw_Any
|
||||
@@ -21,22 +22,4 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id}
|
||||
}
|
||||
|
||||
raw_array_data :: runtime.raw_array_data
|
||||
raw_simd_data :: runtime.raw_simd_data
|
||||
raw_string_data :: runtime.raw_string_data
|
||||
raw_slice_data :: runtime.raw_slice_data
|
||||
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
|
||||
raw_data :: runtime.raw_data
|
||||
|
||||
|
||||
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
Poly_Raw_Map :: struct($Key, $Value: typeid) {
|
||||
hashes: []int,
|
||||
entries: [dynamic]Poly_Raw_Map_Entry(Key, Value),
|
||||
}
|
||||
raw_data :: builtin.raw_data
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Arena_Kind :: enum uint {
|
||||
Growing = 0, // Chained memory blocks (singly linked list).
|
||||
Static = 1, // Fixed reservation sized.
|
||||
Buffer = 2, // Uses a fixed sized buffer.
|
||||
}
|
||||
|
||||
Arena :: struct {
|
||||
kind: Arena_Kind,
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
minimum_block_size: uint,
|
||||
temp_count: uint,
|
||||
}
|
||||
|
||||
|
||||
// 1 MiB should be enough to start with
|
||||
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: mem.Megabyte
|
||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 else 128 * mem.Megabyte
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
|
||||
arena.kind = .Growing
|
||||
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.kind = .Static
|
||||
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
|
||||
if len(buffer) < size_of(Memory_Block) {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
arena.kind = .Buffer
|
||||
|
||||
mem.zero_slice(buffer)
|
||||
|
||||
block_base := raw_data(buffer)
|
||||
block := (^Memory_Block)(block_base)
|
||||
block.base = block_base[size_of(Memory_Block):]
|
||||
block.reserved = len(buffer) - size_of(Memory_Block)
|
||||
block.committed = block.reserved
|
||||
block.used = 0
|
||||
|
||||
arena.curr_block = block
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
|
||||
|
||||
size := size
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch arena.kind {
|
||||
case .Growing:
|
||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
|
||||
size = mem.align_forward_uint(size, alignment)
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||
}
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
prev_used := arena.curr_block.used
|
||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||
arena.total_used += arena.curr_block.used - prev_used
|
||||
case .Static:
|
||||
if arena.curr_block == nil {
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
|
||||
}
|
||||
arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
|
||||
}
|
||||
fallthrough
|
||||
case .Buffer:
|
||||
if arena.curr_block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||
arena.total_used = arena.curr_block.used
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
|
||||
if arena.curr_block != nil {
|
||||
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
|
||||
|
||||
prev_pos := arena.curr_block.used
|
||||
arena.curr_block.used = clamp(pos, 0, arena.curr_block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:pos-prev_pos])
|
||||
}
|
||||
arena.total_used = arena.curr_block.used
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
arena.total_used = 0
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
if free_block := arena.curr_block; free_block != nil {
|
||||
assert(arena.kind == .Growing, "expected a .Growing arena", loc)
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
}
|
||||
|
||||
arena_free_all :: proc(arena: ^Arena) {
|
||||
switch arena.kind {
|
||||
case .Growing:
|
||||
for arena.curr_block != nil {
|
||||
arena_growing_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_reserved = 0
|
||||
case .Static, .Buffer:
|
||||
arena_static_reset_to(arena, 0)
|
||||
}
|
||||
arena.total_used = 0
|
||||
}
|
||||
|
||||
arena_destroy :: proc(arena: ^Arena) {
|
||||
arena_free_all(arena)
|
||||
if arena.kind != .Buffer {
|
||||
memory_block_dealloc(arena.curr_block)
|
||||
}
|
||||
arena.curr_block = nil
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
arena.temp_count = 0
|
||||
}
|
||||
|
||||
arena_growing_bootstrap_new :: proc{
|
||||
arena_growing_bootstrap_new_by_offset,
|
||||
arena_growing_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
arena_static_bootstrap_new :: proc{
|
||||
arena_static_bootstrap_new_by_offset,
|
||||
arena_static_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Arena
|
||||
bootstrap.kind = .Growing
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Arena
|
||||
bootstrap.kind = .Static
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
|
||||
return mem.Allocator{arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Arena)(allocator_data)
|
||||
|
||||
size, alignment := uint(size), uint(alignment)
|
||||
old_size := uint(old_size)
|
||||
|
||||
switch mode {
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
case .Free_All:
|
||||
arena_free_all(arena)
|
||||
case .Resize:
|
||||
old_data := ([^]byte)(old_memory)
|
||||
|
||||
switch {
|
||||
case old_data == nil:
|
||||
return arena_alloc(arena, size, alignment)
|
||||
case size == old_size:
|
||||
// return old memory
|
||||
data = old_data[:size]
|
||||
return
|
||||
case size == 0:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
|
||||
// shrink data in-place
|
||||
data = old_data[:size]
|
||||
return
|
||||
}
|
||||
|
||||
new_memory := arena_alloc(arena, size, alignment) or_return
|
||||
if new_memory == nil {
|
||||
return
|
||||
}
|
||||
copy(new_memory, old_data[:old_size])
|
||||
return new_memory, nil
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
case .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Arena_Temp :: struct {
|
||||
arena: ^Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
|
||||
assert(arena != nil, "nil arena", loc)
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
memory_block_found := false
|
||||
for block := arena.curr_block; block != nil; block = block.prev {
|
||||
if block == temp.block {
|
||||
memory_block_found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !memory_block_found {
|
||||
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
|
||||
}
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
arena_growing_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
arena_init :: proc{
|
||||
static_arena_init,
|
||||
growing_arena_init,
|
||||
}
|
||||
|
||||
arena_temp_begin :: proc{
|
||||
static_arena_temp_begin,
|
||||
growing_arena_temp_begin,
|
||||
}
|
||||
|
||||
arena_temp_end :: proc{
|
||||
static_arena_temp_end,
|
||||
growing_arena_temp_end,
|
||||
}
|
||||
|
||||
arena_check_temp :: proc{
|
||||
static_arena_check_temp,
|
||||
growing_arena_check_temp,
|
||||
}
|
||||
|
||||
arena_allocator :: proc{
|
||||
static_arena_allocator,
|
||||
growing_arena_allocator,
|
||||
}
|
||||
|
||||
arena_alloc :: proc{
|
||||
static_arena_alloc,
|
||||
growing_arena_alloc,
|
||||
}
|
||||
|
||||
arena_free_all :: proc{
|
||||
static_arena_free_all,
|
||||
growing_arena_free_all,
|
||||
}
|
||||
|
||||
arena_destroy :: proc{
|
||||
static_arena_destroy,
|
||||
growing_arena_destroy,
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Growing_Arena :: struct {
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
|
||||
|
||||
growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
|
||||
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
||||
mask := uintptr(alignment-1)
|
||||
if ptr & mask != 0 {
|
||||
alignment_offset = uint(alignment) - uint(ptr & mask)
|
||||
}
|
||||
return alignment_offset
|
||||
}
|
||||
|
||||
assert(mem.is_power_of_two(uintptr(alignment)))
|
||||
|
||||
size := uint(0)
|
||||
if arena.curr_block != nil {
|
||||
size = uint(min_size) + align_forward_offset(arena, alignment)
|
||||
}
|
||||
|
||||
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
|
||||
size = uint(mem.align_forward_int(min_size, alignment))
|
||||
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
|
||||
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
|
||||
if err == nil {
|
||||
arena.total_used += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
|
||||
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
|
||||
for arena.curr_block != nil {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
}
|
||||
|
||||
growing_arena_destroy :: proc(arena: ^Growing_Arena) {
|
||||
growing_arena_free_all(arena)
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Growing_Arena
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
growing_arena_bootstrap_new :: proc{
|
||||
growing_arena_bootstrap_new_by_offset,
|
||||
growing_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{growing_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return growing_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
growing_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
Growing_Arena_Temp :: struct {
|
||||
arena: ^Growing_Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Static_Arena :: struct {
|
||||
block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
|
||||
|
||||
static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_destroy :: proc(arena: ^Static_Arena) {
|
||||
memory_block_dealloc(arena.block)
|
||||
arena^ = {}
|
||||
}
|
||||
|
||||
|
||||
static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
|
||||
mask := align-1
|
||||
return (ptr + mask) &~ mask
|
||||
}
|
||||
|
||||
if arena.block == nil {
|
||||
reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
|
||||
static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
|
||||
}
|
||||
|
||||
MINIMUM_ALIGN :: 2*align_of(uintptr)
|
||||
|
||||
defer arena.total_used = arena.block.used
|
||||
return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
|
||||
}
|
||||
|
||||
static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
|
||||
if arena.block != nil {
|
||||
prev_pos := arena.block.used
|
||||
arena.block.used = clamp(pos, 0, arena.block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
|
||||
}
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static_arena_free_all :: proc(arena: ^Static_Arena) {
|
||||
static_arena_reset_to(arena, 0)
|
||||
}
|
||||
|
||||
|
||||
static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Static_Arena
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
static_arena_bootstrap_new :: proc{
|
||||
static_arena_bootstrap_new_by_offset,
|
||||
static_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
|
||||
static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{static_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Static_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return static_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
static_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Static_Arena_Temp :: struct {
|
||||
arena: ^Static_Arena,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
|
||||
static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.used = arena.block.used if arena.block != nil else 0
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
used := arena.block.used if arena.block != nil else 0
|
||||
|
||||
assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
|
||||
|
||||
static_arena_reset_to(arena, temp.used)
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
|
||||
static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
|
||||
DEFAULT_PAGE_SIZE := uint(4096)
|
||||
|
||||
@@ -95,18 +96,11 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
pmblock.block.committed = committed
|
||||
pmblock.block.reserved = reserved
|
||||
|
||||
sentinel := &global_platform_memory_block_sentinel
|
||||
platform_mutex_lock()
|
||||
pmblock.next = sentinel
|
||||
pmblock.prev = sentinel.prev
|
||||
pmblock.prev.next = pmblock
|
||||
pmblock.next.prev = pmblock
|
||||
platform_mutex_unlock()
|
||||
|
||||
return &pmblock.block, nil
|
||||
}
|
||||
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(block.base[block.used:])
|
||||
@@ -134,11 +128,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
return nil
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||
size := uint(min_size) + alignment_offset
|
||||
size, size_ok := safe_add(min_size, alignment_offset)
|
||||
if !size_ok {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
|
||||
if block.used + size > block.reserved {
|
||||
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
@@ -153,12 +154,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
|
||||
platform_mutex_lock()
|
||||
block.prev.next = block.next
|
||||
block.next.prev = block.prev
|
||||
platform_mutex_unlock()
|
||||
|
||||
platform_memory_free(block)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
||||
z, did_overflow := intrinsics.overflow_add(x, y)
|
||||
return z, !did_overflow
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import "core:sync"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
committed: uint,
|
||||
reserved: uint,
|
||||
prev, next: ^Platform_Memory_Block,
|
||||
}
|
||||
|
||||
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
||||
@@ -33,28 +30,6 @@ platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
|
||||
}
|
||||
}
|
||||
|
||||
platform_mutex_lock :: proc() {
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
platform_mutex_unlock :: proc() {
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
global_memory_block_mutex: sync.Mutex
|
||||
global_platform_memory_block_sentinel: Platform_Memory_Block
|
||||
global_platform_memory_block_sentinel_set: bool
|
||||
|
||||
@(init)
|
||||
platform_memory_init :: proc() {
|
||||
if !global_platform_memory_block_sentinel_set {
|
||||
_platform_memory_init()
|
||||
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel_set = true
|
||||
}
|
||||
}
|
||||
|
||||
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
|
||||
if to_commit < block.committed {
|
||||
return nil
|
||||
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
|
||||
commit(block, to_commit) or_return
|
||||
block.committed = to_commit
|
||||
return nil
|
||||
|
||||
@@ -14,11 +14,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
|
||||
dirpath: string
|
||||
dirpath, err = absolute_path_from_handle(fd)
|
||||
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
|
||||
defer delete(dirpath)
|
||||
|
||||
n := n
|
||||
size := n
|
||||
if n <= 0 {
|
||||
|
||||
@@ -2,7 +2,6 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||
@@ -41,9 +40,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
// fi.mode |= file_type_mode(h);
|
||||
}
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
return
|
||||
|
||||
@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
total_read: int
|
||||
length := len(data)
|
||||
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
|
||||
to_read := min(i64(length), MAX_RW)
|
||||
|
||||
e: win32.BOOL
|
||||
if is_console {
|
||||
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return int(total_read), err
|
||||
}
|
||||
} else {
|
||||
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
|
||||
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
|
||||
e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
|
||||
}
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
|
||||
+9
-5
@@ -178,7 +178,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
@@ -187,7 +187,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -226,8 +226,8 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -261,3 +261,7 @@ heap_allocator :: proc() -> mem.Allocator {
|
||||
data = nil,
|
||||
}
|
||||
}
|
||||
|
||||
processor_core_count :: proc() -> int {
|
||||
return _processor_core_count()
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.fd = rawptr(handle)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment)
|
||||
|
||||
case .Free:
|
||||
|
||||
@@ -4,17 +4,17 @@ package os2
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory: bool) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int, zero_memory: bool) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr)
|
||||
return nil
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size)
|
||||
return heap_alloc(new_size, zero_memory)
|
||||
}
|
||||
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
@@ -36,16 +36,16 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr), zero_memory)
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -72,12 +72,12 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p)
|
||||
return aligned_alloc(new_size, new_alignment, true, p)
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -87,7 +87,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment)
|
||||
return aligned_alloc(size, alignment, true)
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment)
|
||||
|
||||
|
||||
+43
-9
@@ -314,6 +314,7 @@ foreign libc {
|
||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||
|
||||
@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
|
||||
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||
}
|
||||
@@ -333,7 +334,7 @@ foreign dl {
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
|
||||
}
|
||||
|
||||
get_last_error :: proc() -> int {
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return __error()^
|
||||
}
|
||||
|
||||
@@ -342,21 +343,33 @@ get_last_error_string :: proc() -> string {
|
||||
}
|
||||
|
||||
open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
|
||||
isDir := is_dir_path(path)
|
||||
flags := flags
|
||||
if isDir {
|
||||
/*
|
||||
@INFO(Platin): To make it impossible to use the wrong flag for dir's
|
||||
as you can't write to a dir only read which makes it fail to open
|
||||
*/
|
||||
flags = O_RDONLY
|
||||
}
|
||||
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
handle := _unix_open(cstr, i32(flags), u16(mode))
|
||||
if handle == -1 {
|
||||
return INVALID_HANDLE, 1
|
||||
return INVALID_HANDLE, cast(Errno)get_last_error()
|
||||
}
|
||||
|
||||
when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||
if mode != 0 {
|
||||
/*
|
||||
@INFO(Platin): this is only done because O_CREATE for some reason fails to apply mode
|
||||
should not happen if the handle is a directory
|
||||
*/
|
||||
if mode != 0 && !isDir {
|
||||
err := fchmod(handle, cast(u16)mode)
|
||||
if err != 0 {
|
||||
_unix_close(handle)
|
||||
return INVALID_HANDLE, 1
|
||||
return INVALID_HANDLE, cast(Errno)err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handle, 0
|
||||
}
|
||||
@@ -404,6 +417,9 @@ read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
if bytes_read == -1 {
|
||||
return bytes_read_total, 1
|
||||
}
|
||||
if bytes_read == 0 {
|
||||
break
|
||||
}
|
||||
bytes_read_total += bytes_read
|
||||
}
|
||||
|
||||
@@ -643,9 +659,15 @@ access :: proc(path: string, mask: int) -> bool {
|
||||
return _unix_access(cstr, mask) == 0
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0)
|
||||
return _unix_calloc(1, size)
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, size)
|
||||
} else {
|
||||
return _unix_malloc(size)
|
||||
}
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||
@@ -750,6 +772,18 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
count : int = 0
|
||||
count_size := size_of(count)
|
||||
if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
|
||||
if count > 0 {
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
|
||||
@@ -18,8 +18,8 @@ current_thread_id :: proc "contextless" () -> int {
|
||||
return (int) (es.ThreadGetID(es.CURRENT_THREAD));
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return es.HeapAllocate(size, false);
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return es.HeapAllocate(size, zero_memory);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
|
||||
+24
-4
@@ -287,6 +287,7 @@ foreign libc {
|
||||
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||
}
|
||||
@@ -303,7 +304,7 @@ is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc() -> int {
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return __errno_location()^
|
||||
}
|
||||
|
||||
@@ -603,9 +604,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -696,6 +703,19 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
count : int = 0
|
||||
count_size := size_of(count)
|
||||
if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
|
||||
if count > 0 {
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
|
||||
+15
-4
@@ -404,6 +404,7 @@ foreign libc {
|
||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
|
||||
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
|
||||
@(link_name="get_nprocs") _unix_get_nprocs :: proc() -> c.int ---
|
||||
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
|
||||
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
|
||||
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
|
||||
@@ -441,7 +442,7 @@ _get_errno :: proc(res: int) -> Errno {
|
||||
}
|
||||
|
||||
// get errno from libc
|
||||
get_last_error :: proc() -> int {
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return __errno_location()^
|
||||
}
|
||||
|
||||
@@ -755,9 +756,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -872,6 +879,10 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
return int(_unix_get_nprocs())
|
||||
}
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
|
||||
+18
-5
@@ -269,6 +269,7 @@ foreign libc {
|
||||
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
|
||||
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
|
||||
@(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
|
||||
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
|
||||
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
|
||||
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
|
||||
@@ -294,7 +295,7 @@ is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc() -> int {
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return __errno()^
|
||||
}
|
||||
|
||||
@@ -605,9 +606,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -698,6 +705,12 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
_SC_NPROCESSORS_ONLN :: 503
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
return int(_sysconf(_SC_NPROCESSORS_ONLN))
|
||||
}
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
@@ -705,4 +718,4 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
res[i] = string(arg)
|
||||
}
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
+37
-5
@@ -24,7 +24,7 @@ O_CLOEXEC :: 0x80000
|
||||
stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
current_dir: Handle = 3
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
@@ -47,7 +47,36 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
return 0, -1
|
||||
oflags: wasi.oflags_t
|
||||
if mode & O_CREATE == O_CREATE {
|
||||
oflags += {.CREATE}
|
||||
}
|
||||
if mode & O_EXCL == O_EXCL {
|
||||
oflags += {.EXCL}
|
||||
}
|
||||
if mode & O_TRUNC == O_TRUNC {
|
||||
oflags += {.TRUNC}
|
||||
}
|
||||
|
||||
rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET}
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: rights += {.FD_READ}
|
||||
case O_WRONLY: rights += {.FD_WRITE}
|
||||
case O_RDWR: rights += {.FD_READ, .FD_WRITE}
|
||||
}
|
||||
|
||||
fdflags: wasi.fdflags_t
|
||||
if mode & O_APPEND == O_APPEND {
|
||||
fdflags += {.APPEND}
|
||||
}
|
||||
if mode & O_NONBLOCK == O_NONBLOCK {
|
||||
fdflags += {.NONBLOCK}
|
||||
}
|
||||
if mode & O_SYNC == O_SYNC {
|
||||
fdflags += {.SYNC}
|
||||
}
|
||||
fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags)
|
||||
return Handle(fd), Errno(err)
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
err := wasi.fd_close(wasi.fd_t(fd))
|
||||
@@ -60,7 +89,10 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return 0
|
||||
}
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
return 1
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
|
||||
@@ -72,7 +104,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return nil
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -96,4 +128,4 @@ heap_free :: proc(ptr: rawptr) {
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
runtime._cleanup_runtime_contextless()
|
||||
wasi.proc_exit(wasi.exitcode_t(code))
|
||||
}
|
||||
}
|
||||
|
||||
+25
-3
@@ -3,6 +3,7 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
import "core:intrinsics"
|
||||
|
||||
Handle :: distinct uintptr
|
||||
File_Time :: distinct u64
|
||||
@@ -91,8 +92,8 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
@@ -126,7 +127,28 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
length : win32.DWORD = 0
|
||||
result := win32.GetLogicalProcessorInformation(nil, &length)
|
||||
|
||||
thread_count := 0
|
||||
if !result && win32.GetLastError() == 122 && length > 0 {
|
||||
processors := make([]win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION, length, context.temp_allocator)
|
||||
|
||||
result = win32.GetLogicalProcessorInformation(&processors[0], &length)
|
||||
if result {
|
||||
for processor in processors {
|
||||
if processor.Relationship == .RelationProcessorCore {
|
||||
thread := intrinsics.count_ones(processor.ProcessorMask)
|
||||
thread_count += int(thread)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return thread_count
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
runtime._cleanup_runtime_contextless()
|
||||
@@ -214,4 +236,4 @@ is_windows_10 :: proc() -> bool {
|
||||
is_windows_11 :: proc() -> bool {
|
||||
osvi := get_windows_version_w()
|
||||
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
}
|
||||
|
||||
@(private)
|
||||
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
@@ -235,9 +242,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
|
||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.name = basename(fi.fullpath)
|
||||
@@ -252,9 +257,7 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
|
||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.name = basename(fi.fullpath)
|
||||
@@ -290,10 +293,7 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
|
||||
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
|
||||
windows_set_file_info_times(&fi, &d)
|
||||
|
||||
return fi, ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import "core:slice"
|
||||
// The sole exception is if 'skip_dir' is returned as true:
|
||||
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
|
||||
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
|
||||
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool)
|
||||
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr) -> (err: os.Errno, skip_dir: bool)
|
||||
|
||||
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
|
||||
// All errors that happen visiting files and directories are filtered by walk_proc
|
||||
@@ -22,28 +22,28 @@ Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno,
|
||||
// NOTE: Walking large directories can be inefficient due to the lexical sort
|
||||
// NOTE: walk does not follow symbolic links
|
||||
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
|
||||
walk :: proc(root: string, walk_proc: Walk_Proc) -> os.Errno {
|
||||
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Errno {
|
||||
info, err := os.lstat(root, context.temp_allocator)
|
||||
defer os.file_info_delete(info, context.temp_allocator)
|
||||
|
||||
skip_dir: bool
|
||||
if err != 0 {
|
||||
err, skip_dir = walk_proc(info, err)
|
||||
err, skip_dir = walk_proc(info, err, user_data)
|
||||
} else {
|
||||
err, skip_dir = _walk(info, walk_proc)
|
||||
err, skip_dir = _walk(info, walk_proc, user_data)
|
||||
}
|
||||
return 0 if skip_dir else err
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_dir: bool) {
|
||||
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) {
|
||||
if !info.is_dir {
|
||||
if info.fullpath == "" && info.name == "" {
|
||||
// ignore empty things
|
||||
return
|
||||
}
|
||||
return walk_proc(info, 0)
|
||||
return walk_proc(info, 0, user_data)
|
||||
}
|
||||
|
||||
fis: []os.File_Info
|
||||
@@ -51,14 +51,14 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
|
||||
fis, err = read_dir(info.fullpath, context.temp_allocator)
|
||||
defer os.file_info_slice_delete(fis, context.temp_allocator)
|
||||
|
||||
err1, skip_dir = walk_proc(info, err)
|
||||
err1, skip_dir = walk_proc(info, err, user_data)
|
||||
if err != 0 || err1 != 0 || skip_dir {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
|
||||
for fi in fis {
|
||||
err, skip_dir = _walk(fi, walk_proc)
|
||||
err, skip_dir = _walk(fi, walk_proc, user_data)
|
||||
if err != 0 || skip_dir {
|
||||
if !fi.is_dir || !skip_dir {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ti := type_info_base(type_info_of(val.id))
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
if ptr := (^rawptr)(val.data)^; ptr != nil {
|
||||
return iterate_array(any{ptr, info.elem.id}, it)
|
||||
}
|
||||
case Type_Info_Array:
|
||||
if it^ < info.count {
|
||||
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Slice:
|
||||
array := (^runtime.Raw_Slice)(val.data)
|
||||
if it^ < array.len {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Dynamic_Array:
|
||||
array := (^runtime.Raw_Dynamic_Array)(val.data)
|
||||
if it^ < array.len {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
return
|
||||
}
|
||||
ti := type_info_base(type_info_of(val.id))
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
if ptr := (^rawptr)(val.data)^; ptr != nil {
|
||||
return iterate_map(any{ptr, info.elem.id}, it)
|
||||
}
|
||||
case Type_Info_Map:
|
||||
if info.map_info == nil {
|
||||
break
|
||||
}
|
||||
rm := (^runtime.Raw_Map)(val.data)
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
|
||||
for /**/ ; it^ < int(runtime.map_cap(rm^)); it^ += 1 {
|
||||
if hash := hs[it^]; runtime.map_hash_is_valid(hash) {
|
||||
key_ptr := runtime.map_cell_index_dynamic(ks, info.map_info.ks, uintptr(it^))
|
||||
value_ptr := runtime.map_cell_index_dynamic(vs, info.map_info.vs, uintptr(it^))
|
||||
|
||||
key.data = rawptr(key_ptr)
|
||||
value.data = rawptr(value_ptr)
|
||||
key.id = info.key.id
|
||||
value.id = info.value.id
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
_ :: runtime
|
||||
_ :: mem
|
||||
|
||||
Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
hash: uintptr,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
map_entry_info_slice :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
|
||||
m := m
|
||||
rm := (^mem.Raw_Map)(&m)
|
||||
|
||||
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
|
||||
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct)
|
||||
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(Type_Info_Struct)
|
||||
key_offset := entry_type.offsets[2]
|
||||
value_offset := entry_type.offsets[3]
|
||||
entry_size := uintptr(ed.elem_size)
|
||||
|
||||
entries = make(type_of(entries), rm.entries.len)
|
||||
|
||||
data := uintptr(rm.entries.data)
|
||||
for i in 0..<rm.entries.len {
|
||||
header := (^runtime.Map_Entry_Header)(data)
|
||||
|
||||
hash := header.hash
|
||||
key := (^K)(data + key_offset)^
|
||||
value := (^V)(data + value_offset)^
|
||||
|
||||
entries[i] = {hash, key, value}
|
||||
|
||||
data += entry_size
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
@@ -123,46 +123,17 @@ backing_type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
}
|
||||
|
||||
|
||||
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil { return nil }
|
||||
|
||||
base := info
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base
|
||||
case: break loop
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
|
||||
type_info_core :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil { return nil }
|
||||
|
||||
base := info
|
||||
loop: for {
|
||||
#partial switch i in base.variant {
|
||||
case Type_Info_Named: base = i.base
|
||||
case Type_Info_Enum: base = i.base
|
||||
case: break loop
|
||||
}
|
||||
}
|
||||
return base
|
||||
}
|
||||
type_info_base :: runtime.type_info_base
|
||||
type_info_core :: runtime.type_info_core
|
||||
type_info_base_without_enum :: type_info_core
|
||||
|
||||
|
||||
typeid_base :: proc(id: typeid) -> typeid {
|
||||
ti := type_info_of(id)
|
||||
ti = type_info_base(ti)
|
||||
return ti.id
|
||||
when !ODIN_DISALLOW_RTTI {
|
||||
typeid_base :: runtime.typeid_base
|
||||
typeid_core :: runtime.typeid_core
|
||||
typeid_base_without_enum :: typeid_core
|
||||
}
|
||||
typeid_core :: proc(id: typeid) -> typeid {
|
||||
ti := type_info_base_without_enum(type_info_of(id))
|
||||
return ti.id
|
||||
}
|
||||
typeid_base_without_enum :: typeid_core
|
||||
|
||||
|
||||
any_base :: proc(v: any) -> any {
|
||||
v := v
|
||||
@@ -273,7 +244,7 @@ length :: proc(val: any) -> int {
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).len
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^runtime.Raw_Map)(val.data).entries.len
|
||||
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring {
|
||||
@@ -305,7 +276,7 @@ capacity :: proc(val: any) -> int {
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).cap
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^runtime.Raw_Map)(val.data).entries.cap
|
||||
return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
+35
-7
@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
|
||||
values: []Type_Info_Enum_Value,
|
||||
}
|
||||
Type_Info_Map :: struct {
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
generated_struct: ^Type_Info,
|
||||
key_equal: Equal_Proc,
|
||||
key_hasher: Hasher_Proc,
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
map_info: ^Map_Info,
|
||||
}
|
||||
Type_Info_Bit_Set :: struct {
|
||||
elem: ^Type_Info,
|
||||
@@ -303,6 +301,7 @@ Allocator_Mode :: enum byte {
|
||||
Resize,
|
||||
Query_Features,
|
||||
Query_Info,
|
||||
Alloc_Non_Zeroed,
|
||||
}
|
||||
|
||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||
@@ -330,6 +329,12 @@ Allocator :: struct {
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
Byte :: 1
|
||||
Kilobyte :: 1024 * Byte
|
||||
Megabyte :: 1024 * Kilobyte
|
||||
Gigabyte :: 1024 * Megabyte
|
||||
Terabyte :: 1024 * Gigabyte
|
||||
|
||||
// Logging stuff
|
||||
|
||||
Logger_Level :: enum uint {
|
||||
@@ -393,9 +398,32 @@ Raw_Dynamic_Array :: struct {
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
// The raw, type-erased representation of a map.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Raw_Map :: struct {
|
||||
hashes: []Map_Index,
|
||||
entries: Raw_Dynamic_Array,
|
||||
// A single allocation spanning all keys, values, and hashes.
|
||||
// {
|
||||
// k: Map_Cell(K) * (capacity / ks_per_cell)
|
||||
// v: Map_Cell(V) * (capacity / vs_per_cell)
|
||||
// h: Map_Cell(H) * (capacity / hs_per_cell)
|
||||
// }
|
||||
//
|
||||
// The data is allocated assuming 64-byte alignment, meaning the address is
|
||||
// always a multiple of 64. This means we have 6 bits of zeros in the pointer
|
||||
// to store the capacity. We can store a value as large as 2^6-1 or 63 in
|
||||
// there. This conveniently is the maximum log2 capacity we can have for a map
|
||||
// as Odin uses signed integers to represent capacity.
|
||||
//
|
||||
// Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned
|
||||
// integer, the cell structure for hashes is unnecessary because 64/8 is 8 and
|
||||
// requires no padding, meaning it can be indexed as a regular array of
|
||||
// Map_Hash directly, though for consistency sake it's written as if it were
|
||||
// an array of Map_Cell(Map_Hash).
|
||||
data: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
|
||||
len: int, // 8-bytes on 64-bits, 4-bytes on 32-bits
|
||||
allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
|
||||
}
|
||||
|
||||
Raw_Any :: struct {
|
||||
|
||||
@@ -159,20 +159,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
|
||||
}
|
||||
@builtin
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
Entry :: struct {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: K,
|
||||
value: V,
|
||||
}
|
||||
|
||||
raw := transmute(Raw_Map)m
|
||||
err := delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -244,12 +231,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
return
|
||||
}
|
||||
@(builtin)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map_expr_error_loc(loc, cap)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map_expr_error_loc(loc, capacity)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, capacity, loc)
|
||||
return m
|
||||
}
|
||||
@(builtin)
|
||||
@@ -285,36 +272,24 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
raw_map := (^Raw_Map)(m)
|
||||
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
|
||||
entries.len = 0
|
||||
for _, i in raw_map.hashes {
|
||||
raw_map.hashes[i] = MAP_SENTINEL
|
||||
}
|
||||
map_clear_dynamic((^Raw_Map)(m), map_info(T))
|
||||
}
|
||||
|
||||
@builtin
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
|
||||
if m != nil {
|
||||
h := __get_map_header_table(T)
|
||||
__dynamic_map_reserve(m, h, uint(capacity), loc)
|
||||
__dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a map down to the current length, or the given capacity.
|
||||
|
||||
If `new_cap` is negative, then `len(m)` is used.
|
||||
|
||||
Returns false if `cap(m) < new_cap`, or the allocator report failure.
|
||||
|
||||
If `len(m) < new_cap`, then `len(m)` will be left unchanged.
|
||||
Shrinks the capacity of a map down to the current length.
|
||||
*/
|
||||
@builtin
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
|
||||
if m != nil {
|
||||
new_cap := new_cap if new_cap >= 0 else len(m)
|
||||
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
|
||||
err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
|
||||
did_shrink = err == nil
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -325,14 +300,10 @@ shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) ->
|
||||
delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
|
||||
if m != nil {
|
||||
key := key
|
||||
h := __get_map_header(m)
|
||||
fr := __map_find(h, &key)
|
||||
if fr.entry_index != MAP_SENTINEL {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index)
|
||||
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
|
||||
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
|
||||
|
||||
__dynamic_map_erase(h, fr)
|
||||
old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
|
||||
if ok {
|
||||
deleted_key = (^K)(old_k)^
|
||||
deleted_value = (^V)(old_v)^
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -573,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
new_size := capacity * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -607,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
new_size := length * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -650,15 +615,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := new_cap * size_of(E)
|
||||
|
||||
new_data, err := a.allocator.procedure(
|
||||
a.allocator.data,
|
||||
.Resize,
|
||||
new_size,
|
||||
align_of(E),
|
||||
a.data,
|
||||
old_size,
|
||||
loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -672,10 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
@builtin
|
||||
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
|
||||
key, value := key, value
|
||||
h := __get_map_header_table(T)
|
||||
|
||||
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
|
||||
return (^V)(uintptr(e) + h.value_offset)
|
||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||
}
|
||||
|
||||
|
||||
@@ -731,34 +685,6 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Slice)s).data
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Dynamic_Array)s).data
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
|
||||
return (transmute(Raw_String)s).data
|
||||
}
|
||||
|
||||
@builtin
|
||||
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
||||
|
||||
@@ -4,7 +4,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
|
||||
@@ -10,8 +10,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = _windows_default_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data, err = _windows_default_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
_windows_default_free(old_memory)
|
||||
@@ -25,7 +25,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package runtime
|
||||
|
||||
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
|
||||
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
|
||||
|
||||
|
||||
when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
@@ -167,7 +167,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data, err = default_temp_allocator_alloc(s, size, alignment, loc)
|
||||
case .Free:
|
||||
err = default_temp_allocator_free(s, old_memory, loc)
|
||||
@@ -181,7 +181,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
@@ -197,4 +197,4 @@ default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator
|
||||
procedure = default_temp_allocator_proc,
|
||||
data = allocator,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -145,6 +145,13 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 || allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
|
||||
@@ -58,9 +58,9 @@ _os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_b
|
||||
return
|
||||
}
|
||||
|
||||
heap_alloc :: proc "contextless" (size: int) -> rawptr {
|
||||
heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uint(size))
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
@@ -91,7 +91,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
|
||||
|
||||
|
||||
|
||||
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
|
||||
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
_windows_default_free(old_ptr)
|
||||
return nil, nil
|
||||
@@ -105,7 +105,7 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -122,8 +122,8 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
return byte_slice(aligned_mem, size), nil
|
||||
}
|
||||
|
||||
_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
return _windows_default_alloc_or_resize(size, alignment, nil)
|
||||
_windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||
return _windows_default_alloc_or_resize(size, alignment, nil, zero_memory)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ objc_SEL :: ^intrinsics.objc_selector
|
||||
foreign Foundation {
|
||||
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
|
||||
+22
-26
@@ -6,8 +6,8 @@ import "core:runtime"
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
|
||||
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
|
||||
keys = make(type_of(keys), len(m), allocator)
|
||||
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K, err: runtime.Allocator_Error) {
|
||||
keys = make(type_of(keys), len(m), allocator) or_return
|
||||
i := 0
|
||||
for key in m {
|
||||
keys[i] = key
|
||||
@@ -15,8 +15,8 @@ map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K)
|
||||
}
|
||||
return
|
||||
}
|
||||
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V) {
|
||||
values = make(type_of(values), len(m), allocator)
|
||||
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V, err: runtime.Allocator_Error) {
|
||||
values = make(type_of(values), len(m), allocator) or_return
|
||||
i := 0
|
||||
for _, value in m {
|
||||
values[i] = value
|
||||
@@ -37,8 +37,8 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
}
|
||||
|
||||
|
||||
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V)) {
|
||||
entries = make(type_of(entries), len(m), allocator)
|
||||
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator_Error) {
|
||||
entries = make(type_of(entries), len(m), allocator) or_return
|
||||
i := 0
|
||||
for key, value in m {
|
||||
entries[i].key = key
|
||||
@@ -52,28 +52,24 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
|
||||
m := m
|
||||
rm := (^runtime.Raw_Map)(&m)
|
||||
|
||||
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
key_offset := entry_type.offsets[2]
|
||||
value_offset := entry_type.offsets[3]
|
||||
entry_size := uintptr(ed.elem_size)
|
||||
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
|
||||
if info.map_info != nil {
|
||||
entries = make(type_of(entries), len(m), allocator) or_return
|
||||
|
||||
entries = make(type_of(entries), rm.entries.len)
|
||||
map_cap := uintptr(cap(m))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
|
||||
entry_index := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
|
||||
key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
|
||||
entries[entry_index].hash = hash
|
||||
entries[entry_index].key = (^K)(key)^
|
||||
entries[entry_index].value = (^V)(value)^
|
||||
|
||||
data := uintptr(rm.entries.data)
|
||||
for i in 0..<rm.entries.len {
|
||||
header := (^runtime.Map_Entry_Header)(data)
|
||||
|
||||
hash := header.hash
|
||||
key := (^K)(data + key_offset)^
|
||||
value := (^V)(data + value_offset)^
|
||||
|
||||
entries[i] = {hash, key, value}
|
||||
|
||||
data += entry_size
|
||||
entry_index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ _quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND
|
||||
}
|
||||
|
||||
|
||||
// merge sort
|
||||
_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
|
||||
less :: #force_inline proc(a, b: E, call: P) -> bool {
|
||||
when KIND == .Ordered {
|
||||
@@ -190,7 +189,9 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
|
||||
#panic("unhandled Sort_Kind")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// insertion sort
|
||||
// TODO(bill): use a different algorithm as insertion sort is O(n^2)
|
||||
n := len(data)
|
||||
for i in 1..<n {
|
||||
for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 {
|
||||
|
||||
@@ -9,6 +9,89 @@ Decimal :: struct {
|
||||
neg, trunc: bool,
|
||||
}
|
||||
|
||||
set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
|
||||
d^ = {}
|
||||
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; d.neg = true
|
||||
}
|
||||
|
||||
// digits
|
||||
saw_dot := false
|
||||
saw_digits := false
|
||||
for ; i < len(s); i += 1 {
|
||||
switch {
|
||||
case s[i] == '_':
|
||||
// ignore underscores
|
||||
continue
|
||||
case s[i] == '.':
|
||||
if saw_dot {
|
||||
return
|
||||
}
|
||||
saw_dot = true
|
||||
d.decimal_point = d.count
|
||||
continue
|
||||
|
||||
case '0' <= s[i] && s[i] <= '9':
|
||||
saw_digits = true
|
||||
if s[i] == '0' && d.count == 0 {
|
||||
d.decimal_point -= 1
|
||||
continue
|
||||
}
|
||||
if d.count < len(d.digits) {
|
||||
d.digits[d.count] = s[i]
|
||||
d.count += 1
|
||||
} else if s[i] != '0' {
|
||||
d.trunc = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if !saw_digits {
|
||||
return
|
||||
}
|
||||
if !saw_dot {
|
||||
d.decimal_point = d.count
|
||||
}
|
||||
|
||||
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
|
||||
|
||||
if i < len(s) && lower(s[i]) == 'e' {
|
||||
i += 1
|
||||
if i >= len(s) {
|
||||
return
|
||||
}
|
||||
exp_sign := 1
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; exp_sign = -1
|
||||
}
|
||||
if i >= len(s) || s[i] < '0' || s[i] > '9' {
|
||||
return
|
||||
}
|
||||
e := 0
|
||||
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
|
||||
if s[i] == '_' {
|
||||
// ignore underscores
|
||||
continue
|
||||
}
|
||||
if e < 1e4 {
|
||||
e = e*10 + int(s[i]) - '0'
|
||||
}
|
||||
}
|
||||
d.decimal_point += e * exp_sign
|
||||
}
|
||||
|
||||
return i == len(s)
|
||||
}
|
||||
|
||||
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
digit_zero :: proc(buf: []byte) -> int {
|
||||
for _, i in buf {
|
||||
|
||||
@@ -284,3 +284,89 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@(private)
|
||||
decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
|
||||
end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (b: u64) {
|
||||
bits := mant & (u64(1)<<info.mantbits - 1)
|
||||
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
|
||||
if d.neg {
|
||||
bits |= 1<< info.mantbits << info.expbits
|
||||
}
|
||||
return bits
|
||||
}
|
||||
set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
|
||||
mant^ = 0
|
||||
exp^ = 1<<info.expbits - 1 + info.bias
|
||||
return true
|
||||
}
|
||||
|
||||
mant: u64
|
||||
exp: int
|
||||
if d.decimal_point == 0 {
|
||||
mant = 0
|
||||
exp = info.bias
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
if d.decimal_point > 310 {
|
||||
set_overflow(&mant, &exp, info)
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
} else if d.decimal_point < -330 {
|
||||
mant = 0
|
||||
exp = info.bias
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
@static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
|
||||
|
||||
exp = 0
|
||||
for d.decimal_point > 0 {
|
||||
n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
|
||||
decimal.shift(d, n)
|
||||
exp += n
|
||||
}
|
||||
for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
|
||||
n := 27 if -d.decimal_point >= len(power_table) else power_table[-d.decimal_point]
|
||||
decimal.shift(d, n)
|
||||
exp -= n
|
||||
}
|
||||
|
||||
// go from [0.5, 1) to [1, 2)
|
||||
exp -= 1
|
||||
|
||||
if exp < info.bias + 1 {
|
||||
n := info.bias + 1 - exp
|
||||
decimal.shift(d, n)
|
||||
exp += n
|
||||
}
|
||||
|
||||
if (exp-info.bias) >= (1<<info.expbits - 1) {
|
||||
set_overflow(&mant, &exp, info)
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
decimal.shift(d, int(1 + info.mantbits))
|
||||
mant = decimal.rounded_integer(d)
|
||||
|
||||
if mant == 2<<info.mantbits {
|
||||
mant >>= 1
|
||||
exp += 1
|
||||
if (exp-info.bias) >= (1<<info.expbits - 1) {
|
||||
set_overflow(&mant, &exp, info)
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if mant & (1<<info.mantbits) == 0 {
|
||||
exp = info.bias
|
||||
}
|
||||
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package strconv
|
||||
Int_Flag :: enum {
|
||||
Prefix,
|
||||
Plus,
|
||||
Space,
|
||||
}
|
||||
Int_Flags :: bit_set[Int_Flag]
|
||||
|
||||
@@ -73,8 +72,6 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
|
||||
i-=1; a[i] = '-'
|
||||
case .Plus in flags:
|
||||
i-=1; a[i] = '+'
|
||||
case .Space in flags:
|
||||
i-=1; a[i] = ' '
|
||||
}
|
||||
|
||||
out := a[i:]
|
||||
@@ -157,8 +154,6 @@ append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_si
|
||||
i-=1; a[i] = '-'
|
||||
case .Plus in flags:
|
||||
i-=1; a[i] = '+'
|
||||
case .Space in flags:
|
||||
i-=1; a[i] = ' '
|
||||
}
|
||||
|
||||
out := a[i:]
|
||||
|
||||
+265
-104
@@ -1,6 +1,7 @@
|
||||
package strconv
|
||||
|
||||
import "core:unicode/utf8"
|
||||
import "decimal"
|
||||
|
||||
parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
|
||||
switch s {
|
||||
@@ -532,6 +533,8 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o
|
||||
parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
|
||||
|
||||
|
||||
@(private)
|
||||
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
|
||||
|
||||
|
||||
|
||||
@@ -566,126 +569,284 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
|
||||
// assert(n == 12.34 && ok);
|
||||
// ```
|
||||
parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str) - len(s) }
|
||||
if s == "" {
|
||||
common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int {
|
||||
n := len(prefix)
|
||||
if n > len(s) {
|
||||
n = len(s)
|
||||
}
|
||||
for i in 0..<n {
|
||||
c := s[i]
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
}
|
||||
if c != prefix[i] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
check_special :: proc "contextless" (s: string) -> (f: f64, n: int, ok: bool) {
|
||||
s := s
|
||||
if len(s) > 0 {
|
||||
sign := 1
|
||||
nsign := 0
|
||||
switch s[0] {
|
||||
case '+', '-':
|
||||
if s[0] == '-' {
|
||||
sign = -1
|
||||
}
|
||||
nsign = 1
|
||||
s = s[1:]
|
||||
fallthrough
|
||||
case 'i', 'I':
|
||||
n := common_prefix_len_ignore_case(s, "infinity")
|
||||
if 3 < n && n < 8 { // "inf" or "infinity"
|
||||
n = 3
|
||||
}
|
||||
if n == 3 || n == 8 {
|
||||
f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
|
||||
n = nsign + 3
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
case 'n', 'N':
|
||||
if common_prefix_len_ignore_case(s, "nan") == 3 {
|
||||
f = 0h7ff80000_00000001
|
||||
n = nsign + 3
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
parse_components :: proc "contextless" (s: string) -> (mantissa: u64, exp: int, neg, trunc, hex: bool, i: int, ok: bool) {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; neg = true
|
||||
}
|
||||
|
||||
base := u64(10)
|
||||
MAX_MANT_DIGITS := 19
|
||||
exp_char := byte('e')
|
||||
// support stupid 0x1.ABp100 hex floats even if Odin doesn't
|
||||
if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' {
|
||||
base = 16
|
||||
MAX_MANT_DIGITS = 16
|
||||
i += 2
|
||||
exp_char = 'p'
|
||||
hex = true
|
||||
}
|
||||
|
||||
underscores := false
|
||||
saw_dot, saw_digits := false, false
|
||||
nd := 0
|
||||
nd_mant := 0
|
||||
decimal_point := 0
|
||||
loop: for ; i < len(s); i += 1 {
|
||||
switch c := s[i]; true {
|
||||
case c == '_':
|
||||
underscores = true
|
||||
continue loop
|
||||
case c == '.':
|
||||
if saw_dot {
|
||||
break loop
|
||||
}
|
||||
saw_dot = true
|
||||
decimal_point = nd
|
||||
continue loop
|
||||
|
||||
case '0' <= c && c <= '9':
|
||||
saw_digits = true
|
||||
if c == '0' && nd == 0 {
|
||||
decimal_point -= 1
|
||||
continue loop
|
||||
}
|
||||
nd += 1
|
||||
if nd_mant < MAX_MANT_DIGITS {
|
||||
mantissa *= base
|
||||
mantissa += u64(c - '0')
|
||||
nd_mant += 1
|
||||
} else if c != '0' {
|
||||
trunc = true
|
||||
}
|
||||
continue loop
|
||||
case base == 16 && 'a' <= lower(c) && lower(c) <= 'f':
|
||||
saw_digits = true
|
||||
nd += 1
|
||||
if nd_mant < MAX_MANT_DIGITS {
|
||||
MAX_MANT_DIGITS *= 16
|
||||
MAX_MANT_DIGITS += int(lower(c) - 'a' + 10)
|
||||
nd_mant += 1
|
||||
} else {
|
||||
trunc = true
|
||||
}
|
||||
continue loop
|
||||
}
|
||||
break loop
|
||||
}
|
||||
|
||||
if !saw_digits {
|
||||
return
|
||||
}
|
||||
if !saw_dot {
|
||||
decimal_point = nd
|
||||
}
|
||||
if base == 16 {
|
||||
decimal_point *= 4
|
||||
nd_mant *= 4
|
||||
}
|
||||
|
||||
if i < len(s) && lower(s[i]) == exp_char {
|
||||
i += 1
|
||||
if i >= len(s) { return }
|
||||
exp_sign := 1
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; exp_sign = -1
|
||||
}
|
||||
if i >= len(s) || s[i] < '0' || s[i] > '9' {
|
||||
return
|
||||
}
|
||||
e := 0
|
||||
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
|
||||
if s[i] == '_' {
|
||||
underscores = true
|
||||
continue
|
||||
}
|
||||
if e < 1e5 {
|
||||
e = e*10 + int(s[i]) - '0'
|
||||
}
|
||||
}
|
||||
decimal_point += e * exp_sign
|
||||
} else if base == 16 {
|
||||
return
|
||||
}
|
||||
|
||||
if mantissa != 0 {
|
||||
exp = decimal_point - nd_mant
|
||||
}
|
||||
// TODO(bill): check underscore correctness
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
parse_hex :: proc(s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
|
||||
info := &_f64_info
|
||||
|
||||
sign: f64 = 1
|
||||
switch s[i] {
|
||||
case '-': i += 1; sign = -1
|
||||
case '+': i += 1
|
||||
}
|
||||
mantissa, exp := mantissa, exp
|
||||
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i])
|
||||
if r == '_' {
|
||||
continue
|
||||
MAX_EXP := 1<<info.expbits + info.bias - 2
|
||||
MIN_EXP := info.bias + 1
|
||||
exp += int(info.mantbits)
|
||||
|
||||
for mantissa != 0 && mantissa >> (info.mantbits+2) == 0 {
|
||||
mantissa <<= 1
|
||||
exp -= 1
|
||||
}
|
||||
if trunc {
|
||||
mantissa |= 1
|
||||
}
|
||||
|
||||
v := _digit_value(r)
|
||||
if v >= 10 {
|
||||
if r == '.' || r == 'e' || r == 'E' { // Skip parsing NaN and Inf if it's probably a regular float
|
||||
break
|
||||
}
|
||||
if len(s) >= 3 + i {
|
||||
buf: [4]u8
|
||||
copy(buf[:], s[i:][:3])
|
||||
|
||||
v2 := transmute(u32)buf
|
||||
v2 &= 0xDFDFDFDF // Knock out lower-case bits
|
||||
|
||||
buf = transmute([4]u8)v2
|
||||
|
||||
when ODIN_ENDIAN == .Little {
|
||||
if v2 == 0x464e49 { // "INF"
|
||||
s = s[3+i:]
|
||||
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
|
||||
return value, len(s) == 0
|
||||
} else if v2 == 0x4e414e { // "NAN"
|
||||
s = s[3+i:]
|
||||
return 0h7ff80000_00000001, len(s) == 0
|
||||
}
|
||||
} else {
|
||||
if v2 == 0x494e4600 { // "\0FNI"
|
||||
s = s[3+i:]
|
||||
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
|
||||
return value, len(s) == 0
|
||||
} else if v2 == 0x4e414e00 { // "\0NAN"
|
||||
s = s[3+i:]
|
||||
return 0h7ff80000_00000001, len(s) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
for mantissa >> (info.mantbits+2) == 0 {
|
||||
mantissa = mantissa>>1 | mantissa&1
|
||||
exp += 1
|
||||
}
|
||||
value *= 10
|
||||
value += f64(v)
|
||||
|
||||
// denormalize
|
||||
if mantissa > 1 && exp < MIN_EXP-2 {
|
||||
mantissa = mantissa>>1 | mantissa&1
|
||||
exp += 1
|
||||
}
|
||||
|
||||
round := mantissa & 3
|
||||
mantissa >>= 2
|
||||
round |= mantissa & 1 // round to even
|
||||
exp += 2
|
||||
if round == 3 {
|
||||
mantissa += 1
|
||||
if mantissa == 1 << (1 + info.mantbits) {
|
||||
mantissa >>= 1
|
||||
exp += 1
|
||||
}
|
||||
}
|
||||
if mantissa>>info.mantbits == 0 {
|
||||
// zero or denormal
|
||||
exp = info.bias
|
||||
}
|
||||
|
||||
ok := true
|
||||
if exp > MAX_EXP {
|
||||
// infinity or invalid
|
||||
mantissa = 1<<info.mantbits
|
||||
exp = MAX_EXP + 1
|
||||
ok = false
|
||||
}
|
||||
|
||||
bits := mantissa & (1<<info.mantbits - 1)
|
||||
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
|
||||
if neg {
|
||||
bits |= 1 << info.mantbits << info.expbits
|
||||
}
|
||||
return transmute(f64)bits, ok
|
||||
}
|
||||
|
||||
if i < len(s) && s[i] == '.' {
|
||||
pow10: f64 = 10
|
||||
i += 1
|
||||
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i])
|
||||
if r == '_' {
|
||||
continue
|
||||
}
|
||||
nr: int
|
||||
defer if n != nil { n^ = nr }
|
||||
|
||||
v := _digit_value(r)
|
||||
if v >= 10 {
|
||||
break
|
||||
if value, nr, ok = check_special(str); ok {
|
||||
return
|
||||
}
|
||||
|
||||
mantissa: u64
|
||||
exp: int
|
||||
neg, trunc, hex: bool
|
||||
mantissa, exp, neg, trunc, hex, nr = parse_components(str) or_return
|
||||
|
||||
if hex {
|
||||
return parse_hex(str, mantissa, exp, neg, trunc)
|
||||
}
|
||||
|
||||
trunc_block: if !trunc {
|
||||
@static pow10 := [?]f64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22,
|
||||
}
|
||||
|
||||
if mantissa>>_f64_info.mantbits != 0 {
|
||||
return
|
||||
}
|
||||
f := f64(mantissa)
|
||||
if neg {
|
||||
f = -f
|
||||
}
|
||||
switch {
|
||||
case exp == 0:
|
||||
return f, true
|
||||
case exp > 0 && exp <= 15+22:
|
||||
if exp > 22 {
|
||||
f *= pow10[exp-22]
|
||||
exp = 22
|
||||
}
|
||||
value += f64(v)/pow10
|
||||
pow10 *= 10
|
||||
if f > 1e15 || f < 1e-15 {
|
||||
break trunc_block
|
||||
}
|
||||
return f * pow10[exp], true
|
||||
case -22 <= exp && exp < 0:
|
||||
return f / pow10[-exp], true
|
||||
}
|
||||
}
|
||||
|
||||
frac := false
|
||||
scale: f64 = 1
|
||||
|
||||
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
|
||||
i += 1
|
||||
|
||||
if i < len(s) {
|
||||
switch s[i] {
|
||||
case '-': i += 1; frac = true
|
||||
case '+': i += 1
|
||||
}
|
||||
|
||||
exp: u32 = 0
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i])
|
||||
if r == '_' {
|
||||
continue
|
||||
}
|
||||
|
||||
d := u32(_digit_value(r))
|
||||
if d >= 10 {
|
||||
break
|
||||
}
|
||||
exp = exp * 10 + d
|
||||
}
|
||||
if exp > 308 { exp = 308 }
|
||||
|
||||
for exp >= 50 { scale *= 1e50; exp -= 50 }
|
||||
for exp >= 8 { scale *= 1e8; exp -= 8 }
|
||||
for exp > 0 { scale *= 10; exp -= 1 }
|
||||
}
|
||||
}
|
||||
s = s[i:]
|
||||
|
||||
if frac {
|
||||
value = sign * (value/scale)
|
||||
} else {
|
||||
value = sign * (value*scale)
|
||||
}
|
||||
|
||||
ok = len(s) == 0
|
||||
d: decimal.Decimal
|
||||
decimal.set(&d, str[:nr])
|
||||
b, overflow := decimal_to_float_bits(&d, &_f64_info)
|
||||
value = transmute(f64)b
|
||||
ok = !overflow
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -299,6 +299,50 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
|
||||
return
|
||||
}
|
||||
|
||||
// writes a f64 value into the builder, returns the written amount of characters
|
||||
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
|
||||
// If the result starts with a `+` then unless we always want signed results,
|
||||
// we skip it unless it's followed by an `I` (because of +Inf).
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
// writes a f16 value into the builder, returns the written amount of characters
|
||||
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
// writes a f32 value into the builder, returns the written amount of characters
|
||||
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
// writes a f64 value into the builder, returns the written amount of characters
|
||||
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
|
||||
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
|
||||
buf: [32]byte
|
||||
|
||||
+87
-30
@@ -11,7 +11,7 @@ Wait_Group :: struct {
|
||||
cond: Cond,
|
||||
}
|
||||
|
||||
wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
|
||||
wait_group_add :: proc "contextless" (wg: ^Wait_Group, delta: int) {
|
||||
if delta == 0 {
|
||||
return
|
||||
}
|
||||
@@ -20,32 +20,32 @@ wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
|
||||
|
||||
atomic_add(&wg.counter, delta)
|
||||
if wg.counter < 0 {
|
||||
panic("sync.Wait_Group negative counter")
|
||||
_panic("sync.Wait_Group negative counter")
|
||||
}
|
||||
if wg.counter == 0 {
|
||||
cond_broadcast(&wg.cond)
|
||||
if wg.counter != 0 {
|
||||
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait_group_done :: proc(wg: ^Wait_Group) {
|
||||
wait_group_done :: proc "contextless" (wg: ^Wait_Group) {
|
||||
wait_group_add(wg, -1)
|
||||
}
|
||||
|
||||
wait_group_wait :: proc(wg: ^Wait_Group) {
|
||||
wait_group_wait :: proc "contextless" (wg: ^Wait_Group) {
|
||||
guard(&wg.mutex)
|
||||
|
||||
if wg.counter != 0 {
|
||||
cond_wait(&wg.cond, &wg.mutex)
|
||||
if wg.counter != 0 {
|
||||
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -> bool {
|
||||
wait_group_wait_with_timeout :: proc "contextless" (wg: ^Wait_Group, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -56,7 +56,7 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -
|
||||
return false
|
||||
}
|
||||
if wg.counter != 0 {
|
||||
panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
_panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
|
||||
}
|
||||
}
|
||||
return true
|
||||
@@ -76,7 +76,7 @@ Example:
|
||||
|
||||
barrier := &sync.Barrier{}
|
||||
|
||||
main :: proc() {
|
||||
main :: proc "contextless" () {
|
||||
fmt.println("Start")
|
||||
|
||||
THREAD_COUNT :: 4
|
||||
@@ -107,7 +107,7 @@ Barrier :: struct {
|
||||
thread_count: int,
|
||||
}
|
||||
|
||||
barrier_init :: proc(b: ^Barrier, thread_count: int) {
|
||||
barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
|
||||
b.index = 0
|
||||
b.generation_id = 0
|
||||
b.thread_count = thread_count
|
||||
@@ -115,7 +115,7 @@ barrier_init :: proc(b: ^Barrier, thread_count: int) {
|
||||
|
||||
// Block the current thread until all threads have rendezvoused
|
||||
// Barrier can be reused after all threads rendezvoused once, and can be used continuously
|
||||
barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
|
||||
barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
|
||||
guard(&b.mutex)
|
||||
local_gen := b.generation_id
|
||||
b.index += 1
|
||||
@@ -141,7 +141,7 @@ Auto_Reset_Event :: struct {
|
||||
sema: Sema,
|
||||
}
|
||||
|
||||
auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
|
||||
auto_reset_event_signal :: proc "contextless" (e: ^Auto_Reset_Event) {
|
||||
old_status := atomic_load_explicit(&e.status, .Relaxed)
|
||||
for {
|
||||
new_status := old_status + 1 if old_status < 1 else 1
|
||||
@@ -155,7 +155,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
|
||||
}
|
||||
}
|
||||
|
||||
auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
|
||||
auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {
|
||||
old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
|
||||
if old_status < 1 {
|
||||
sema_wait(&e.sema)
|
||||
@@ -169,18 +169,18 @@ Ticket_Mutex :: struct {
|
||||
serving: uint,
|
||||
}
|
||||
|
||||
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
|
||||
ticket_mutex_lock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
|
||||
ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
|
||||
for ticket != atomic_load_explicit(&m.serving, .Acquire) {
|
||||
cpu_relax()
|
||||
}
|
||||
}
|
||||
|
||||
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
|
||||
ticket_mutex_unlock :: #force_inline proc "contextless" (m: ^Ticket_Mutex) {
|
||||
atomic_add_explicit(&m.serving, 1, .Relaxed)
|
||||
}
|
||||
@(deferred_in=ticket_mutex_unlock)
|
||||
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
|
||||
ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {
|
||||
ticket_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -191,25 +191,25 @@ Benaphore :: struct {
|
||||
sema: Sema,
|
||||
}
|
||||
|
||||
benaphore_lock :: proc(b: ^Benaphore) {
|
||||
benaphore_lock :: proc "contextless" (b: ^Benaphore) {
|
||||
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
|
||||
sema_wait(&b.sema)
|
||||
}
|
||||
}
|
||||
|
||||
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
|
||||
benaphore_try_lock :: proc "contextless" (b: ^Benaphore) -> bool {
|
||||
v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
|
||||
return v == 0
|
||||
}
|
||||
|
||||
benaphore_unlock :: proc(b: ^Benaphore) {
|
||||
benaphore_unlock :: proc "contextless" (b: ^Benaphore) {
|
||||
if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
|
||||
sema_post(&b.sema)
|
||||
}
|
||||
}
|
||||
|
||||
@(deferred_in=benaphore_unlock)
|
||||
benaphore_guard :: proc(m: ^Benaphore) -> bool {
|
||||
benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool {
|
||||
benaphore_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -221,7 +221,7 @@ Recursive_Benaphore :: struct {
|
||||
sema: Sema,
|
||||
}
|
||||
|
||||
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
|
||||
recursive_benaphore_lock :: proc "contextless" (b: ^Recursive_Benaphore) {
|
||||
tid := current_thread_id()
|
||||
if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
|
||||
if tid != b.owner {
|
||||
@@ -233,7 +233,7 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
|
||||
b.recursion += 1
|
||||
}
|
||||
|
||||
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
|
||||
recursive_benaphore_try_lock :: proc "contextless" (b: ^Recursive_Benaphore) -> bool {
|
||||
tid := current_thread_id()
|
||||
if b.owner == tid {
|
||||
atomic_add_explicit(&b.counter, 1, .Acquire)
|
||||
@@ -248,9 +248,9 @@ recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
|
||||
recursive_benaphore_unlock :: proc "contextless" (b: ^Recursive_Benaphore) {
|
||||
tid := current_thread_id()
|
||||
assert(tid == b.owner)
|
||||
_assert(tid == b.owner, "tid != b.owner")
|
||||
b.recursion -= 1
|
||||
recursion := b.recursion
|
||||
if recursion == 0 {
|
||||
@@ -265,7 +265,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
|
||||
}
|
||||
|
||||
@(deferred_in=recursive_benaphore_unlock)
|
||||
recursive_benaphore_guard :: proc(m: ^Recursive_Benaphore) -> bool {
|
||||
recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> bool {
|
||||
recursive_benaphore_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -282,7 +282,15 @@ Once :: struct {
|
||||
}
|
||||
|
||||
// once_do calls the procedure fn if and only if once_do is being called for the first for this instance of Once.
|
||||
once_do :: proc(o: ^Once, fn: proc()) {
|
||||
once_do :: proc{
|
||||
once_do_without_data,
|
||||
once_do_without_data_contextless,
|
||||
once_do_with_data,
|
||||
once_do_with_data_contextless,
|
||||
}
|
||||
|
||||
// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once.
|
||||
once_do_without_data :: proc(o: ^Once, fn: proc()) {
|
||||
@(cold)
|
||||
do_slow :: proc(o: ^Once, fn: proc()) {
|
||||
guard(&o.m)
|
||||
@@ -292,12 +300,61 @@ once_do :: proc(o: ^Once, fn: proc()) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if atomic_load_explicit(&o.done, .Acquire) == false {
|
||||
do_slow(o, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// once_do_without_data calls the procedure fn if and only if once_do_without_data is being called for the first for this instance of Once.
|
||||
once_do_without_data_contextless :: proc(o: ^Once, fn: proc "contextless" ()) {
|
||||
@(cold)
|
||||
do_slow :: proc(o: ^Once, fn: proc "contextless" ()) {
|
||||
guard(&o.m)
|
||||
if !o.done {
|
||||
fn()
|
||||
atomic_store_explicit(&o.done, true, .Release)
|
||||
}
|
||||
}
|
||||
|
||||
if atomic_load_explicit(&o.done, .Acquire) == false {
|
||||
do_slow(o, fn)
|
||||
}
|
||||
}
|
||||
|
||||
// once_do_with_data calls the procedure fn if and only if once_do_with_data is being called for the first for this instance of Once.
|
||||
once_do_with_data :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
|
||||
@(cold)
|
||||
do_slow :: proc(o: ^Once, fn: proc(data: rawptr), data: rawptr) {
|
||||
guard(&o.m)
|
||||
if !o.done {
|
||||
fn(data)
|
||||
atomic_store_explicit(&o.done, true, .Release)
|
||||
}
|
||||
}
|
||||
|
||||
if atomic_load_explicit(&o.done, .Acquire) == false {
|
||||
do_slow(o, fn, data)
|
||||
}
|
||||
}
|
||||
|
||||
// once_do_with_data_contextless calls the procedure fn if and only if once_do_with_data_contextless is being called for the first for this instance of Once.
|
||||
once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
|
||||
@(cold)
|
||||
do_slow :: proc "contextless" (o: ^Once, fn: proc "contextless" (data: rawptr), data: rawptr) {
|
||||
guard(&o.m)
|
||||
if !o.done {
|
||||
fn(data)
|
||||
atomic_store_explicit(&o.done, true, .Release)
|
||||
}
|
||||
}
|
||||
|
||||
if atomic_load_explicit(&o.done, .Acquire) == false {
|
||||
do_slow(o, fn, data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// A Parker is an associated token which is initially not present:
|
||||
@@ -314,7 +371,7 @@ Parker :: struct {
|
||||
// Blocks the current thread until the token is made available.
|
||||
//
|
||||
// Assumes this is only called by the thread that owns the Parker.
|
||||
park :: proc(p: ^Parker) {
|
||||
park :: proc "contextless" (p: ^Parker) {
|
||||
EMPTY :: 0
|
||||
NOTIFIED :: 1
|
||||
PARKED :: max(u32)
|
||||
@@ -333,7 +390,7 @@ park :: proc(p: ^Parker) {
|
||||
// for a limited duration.
|
||||
//
|
||||
// Assumes this is only called by the thread that owns the Parker
|
||||
park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
|
||||
park_with_timeout :: proc "contextless" (p: ^Parker, duration: time.Duration) {
|
||||
EMPTY :: 0
|
||||
NOTIFIED :: 1
|
||||
PARKED :: max(u32)
|
||||
@@ -345,7 +402,7 @@ park_with_timeout :: proc(p: ^Parker, duration: time.Duration) {
|
||||
}
|
||||
|
||||
// Automatically makes thee token available if it was not already.
|
||||
unpark :: proc(p: ^Parker) {
|
||||
unpark :: proc "contextless" (p: ^Parker) {
|
||||
EMPTY :: 0
|
||||
NOTIFIED :: 1
|
||||
PARKED :: max(Futex)
|
||||
|
||||
@@ -24,11 +24,11 @@ EINTR :: -4
|
||||
EFAULT :: -14
|
||||
ETIMEDOUT :: -60
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
return _futex_wait_with_timeout(f, expected, 0)
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
timeout_ns := u32(duration) * 1000
|
||||
|
||||
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
|
||||
@@ -41,13 +41,13 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
|
||||
case ETIMEDOUT:
|
||||
return false
|
||||
case:
|
||||
panic("futex_wait failure")
|
||||
_panic("futex_wait failure")
|
||||
}
|
||||
return true
|
||||
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
loop: for {
|
||||
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, 0)
|
||||
if s >= 0 {
|
||||
@@ -59,12 +59,12 @@ _futex_signal :: proc(f: ^Futex) {
|
||||
case ENOENT:
|
||||
return
|
||||
case:
|
||||
panic("futex_wake_single failure")
|
||||
_panic("futex_wake_single failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
loop: for {
|
||||
s := __ulock_wake(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO | ULF_WAKE_ALL, f, 0)
|
||||
if s >= 0 {
|
||||
@@ -76,7 +76,7 @@ _futex_broadcast :: proc(f: ^Futex) {
|
||||
case ENOENT:
|
||||
return
|
||||
case:
|
||||
panic("futex_wake_all failure")
|
||||
_panic("futex_wake_all failure")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ foreign libc {
|
||||
__error :: proc "c" () -> ^c.int ---
|
||||
}
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
timeout := [2]i64{14400, 0} // 4 hours
|
||||
for {
|
||||
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
|
||||
@@ -30,12 +30,12 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
continue
|
||||
}
|
||||
|
||||
panic("_futex_wait failure")
|
||||
_panic("_futex_wait failure")
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -51,21 +51,21 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
|
||||
return false
|
||||
}
|
||||
|
||||
panic("_futex_wait_with_timeout failure")
|
||||
_panic("_futex_wait_with_timeout failure")
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
|
||||
|
||||
if res == -1 {
|
||||
panic("_futex_signal failure")
|
||||
_panic("_futex_signal failure")
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
|
||||
|
||||
if res == -1 {
|
||||
panic("_futex_broadcast failure")
|
||||
_panic("_futex_broadcast failure")
|
||||
}
|
||||
}
|
||||
|
||||
+10
-10
@@ -21,20 +21,20 @@ EFAULT :: -14
|
||||
EINVAL :: -22
|
||||
ETIMEDOUT :: -110
|
||||
|
||||
get_errno :: proc(r: int) -> int {
|
||||
get_errno :: proc "contextless" (r: int) -> int {
|
||||
if -4096 < r && r < 0 {
|
||||
return r
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
internal_futex :: proc(f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
|
||||
internal_futex :: proc "contextless" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> int {
|
||||
code := int(intrinsics.syscall(unix.SYS_futex, uintptr(f), uintptr(op), uintptr(val), uintptr(timeout), 0, 0))
|
||||
return get_errno(code)
|
||||
}
|
||||
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
err := internal_futex(f, FUTEX_WAIT_PRIVATE | FUTEX_WAIT, expected, nil)
|
||||
switch err {
|
||||
case ESUCCESS, EINTR, EAGAIN, EINVAL:
|
||||
@@ -44,12 +44,12 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
case EFAULT:
|
||||
fallthrough
|
||||
case:
|
||||
panic("futex_wait failure")
|
||||
_panic("futex_wait failure")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -71,27 +71,27 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
|
||||
case EFAULT:
|
||||
fallthrough
|
||||
case:
|
||||
panic("futex_wait_with_timeout failure")
|
||||
_panic("futex_wait_with_timeout failure")
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, 1, nil)
|
||||
switch err {
|
||||
case ESUCCESS, EINVAL, EFAULT:
|
||||
// okay
|
||||
case:
|
||||
panic("futex_wake_single failure")
|
||||
_panic("futex_wake_single failure")
|
||||
}
|
||||
}
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
err := internal_futex(f, FUTEX_WAKE_PRIVATE | FUTEX_WAKE, u32(max(i32)), nil)
|
||||
switch err {
|
||||
case ESUCCESS, EINVAL, EFAULT:
|
||||
// okay
|
||||
case:
|
||||
panic("_futex_wake_all failure")
|
||||
_panic("_futex_wake_all failure")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ foreign libc {
|
||||
_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
|
||||
}
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
|
||||
|
||||
if res != -1 {
|
||||
@@ -32,10 +32,10 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
panic("futex_wait failure")
|
||||
_panic("futex_wait failure")
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
@@ -58,21 +58,21 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
|
||||
return false
|
||||
}
|
||||
|
||||
panic("futex_wait_with_timeout failure")
|
||||
_panic("futex_wait_with_timeout failure")
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
|
||||
|
||||
if res == -1 {
|
||||
panic("futex_wake_single failure")
|
||||
_panic("futex_wake_single failure")
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
|
||||
|
||||
if res == -1 {
|
||||
panic("_futex_wake_all failure")
|
||||
_panic("_futex_wake_all failure")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,18 +5,18 @@ package sync
|
||||
import "core:intrinsics"
|
||||
import "core:time"
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
|
||||
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, -1)
|
||||
return s != 0
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
s := intrinsics.wasm_memory_atomic_wait32((^u32)(f), expected, i64(duration))
|
||||
return s != 0
|
||||
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
loop: for {
|
||||
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), 1)
|
||||
if s >= 1 {
|
||||
@@ -25,7 +25,7 @@ _futex_signal :: proc(f: ^Futex) {
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
loop: for {
|
||||
s := intrinsics.wasm_memory_atomic_notify32((^u32)(f), ~u32(0))
|
||||
if s >= 0 {
|
||||
|
||||
@@ -39,22 +39,22 @@ CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr,
|
||||
}
|
||||
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> bool {
|
||||
expect := expect
|
||||
return CustomWaitOnAddress(f, &expect, size_of(expect), nil)
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(f: ^Futex, expect: u32, duration: time.Duration) -> bool {
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> bool {
|
||||
expect := expect
|
||||
// NOTE(bill): for some bizarre reason, this has be a negative number
|
||||
timeout := -i64(duration / 100)
|
||||
return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
WakeByAddressSingle(f)
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc(f: ^Futex) {
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
WakeByAddressAll(f)
|
||||
}
|
||||
+44
-28
@@ -1,5 +1,6 @@
|
||||
package sync
|
||||
|
||||
import "core:runtime"
|
||||
import "core:time"
|
||||
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
@@ -15,17 +16,17 @@ Mutex :: struct {
|
||||
}
|
||||
|
||||
// mutex_lock locks m
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
mutex_lock :: proc "contextless" (m: ^Mutex) {
|
||||
_mutex_lock(m)
|
||||
}
|
||||
|
||||
// mutex_unlock unlocks m
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
mutex_unlock :: proc "contextless" (m: ^Mutex) {
|
||||
_mutex_unlock(m)
|
||||
}
|
||||
|
||||
// mutex_try_lock tries to lock m, will return true on success, and false on failure
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
|
||||
return _mutex_try_lock(m)
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=mutex_unlock)
|
||||
mutex_guard :: proc(m: ^Mutex) -> bool {
|
||||
mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
|
||||
mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -52,32 +53,32 @@ RW_Mutex :: struct {
|
||||
|
||||
// rw_mutex_lock locks rw for writing (with a single writer)
|
||||
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
|
||||
rw_mutex_lock :: proc(rw: ^RW_Mutex) {
|
||||
rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
_rw_mutex_lock(rw)
|
||||
}
|
||||
|
||||
// rw_mutex_unlock unlocks rw for writing (with a single writer)
|
||||
rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
|
||||
rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
_rw_mutex_unlock(rw)
|
||||
}
|
||||
|
||||
// rw_mutex_try_lock tries to lock rw for writing (with a single writer)
|
||||
rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
|
||||
rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
|
||||
return _rw_mutex_try_lock(rw)
|
||||
}
|
||||
|
||||
// rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
|
||||
rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
|
||||
rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
_rw_mutex_shared_lock(rw)
|
||||
}
|
||||
|
||||
// rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
|
||||
rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
|
||||
rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
_rw_mutex_shared_unlock(rw)
|
||||
}
|
||||
|
||||
// rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
|
||||
rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
|
||||
rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
|
||||
return _rw_mutex_try_shared_lock(rw)
|
||||
}
|
||||
/*
|
||||
@@ -87,7 +88,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=rw_mutex_unlock)
|
||||
rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool {
|
||||
rw_mutex_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
|
||||
rw_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -99,7 +100,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=rw_mutex_shared_unlock)
|
||||
rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
|
||||
rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
|
||||
rw_mutex_shared_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -114,15 +115,15 @@ Recursive_Mutex :: struct {
|
||||
impl: _Recursive_Mutex,
|
||||
}
|
||||
|
||||
recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
|
||||
recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
|
||||
_recursive_mutex_lock(m)
|
||||
}
|
||||
|
||||
recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
|
||||
recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
|
||||
_recursive_mutex_unlock(m)
|
||||
}
|
||||
|
||||
recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
|
||||
recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
|
||||
return _recursive_mutex_try_lock(m)
|
||||
}
|
||||
|
||||
@@ -133,7 +134,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=recursive_mutex_unlock)
|
||||
recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool {
|
||||
recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
|
||||
recursive_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -147,22 +148,22 @@ Cond :: struct {
|
||||
impl: _Cond,
|
||||
}
|
||||
|
||||
cond_wait :: proc(c: ^Cond, m: ^Mutex) {
|
||||
cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
|
||||
_cond_wait(c, m)
|
||||
}
|
||||
|
||||
cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
return _cond_wait_with_timeout(c, m, duration)
|
||||
}
|
||||
|
||||
cond_signal :: proc(c: ^Cond) {
|
||||
cond_signal :: proc "contextless" (c: ^Cond) {
|
||||
_cond_signal(c)
|
||||
}
|
||||
|
||||
cond_broadcast :: proc(c: ^Cond) {
|
||||
cond_broadcast :: proc "contextless" (c: ^Cond) {
|
||||
_cond_broadcast(c)
|
||||
}
|
||||
|
||||
@@ -175,15 +176,15 @@ Sema :: struct {
|
||||
impl: _Sema,
|
||||
}
|
||||
|
||||
sema_post :: proc(s: ^Sema, count := 1) {
|
||||
sema_post :: proc "contextless" (s: ^Sema, count := 1) {
|
||||
_sema_post(s, count)
|
||||
}
|
||||
|
||||
sema_wait :: proc(s: ^Sema) {
|
||||
sema_wait :: proc "contextless" (s: ^Sema) {
|
||||
_sema_wait(s)
|
||||
}
|
||||
|
||||
sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
|
||||
sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
|
||||
return _sema_wait_with_timeout(s, duration)
|
||||
}
|
||||
|
||||
@@ -194,16 +195,16 @@ sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
|
||||
// An Futex must not be copied after first use
|
||||
Futex :: distinct u32
|
||||
|
||||
futex_wait :: proc(f: ^Futex, expected: u32) {
|
||||
futex_wait :: proc "contextless" (f: ^Futex, expected: u32) {
|
||||
if u32(atomic_load_explicit(f, .Acquire)) != expected {
|
||||
return
|
||||
}
|
||||
|
||||
assert(_futex_wait(f, expected), "futex_wait failure")
|
||||
_assert(_futex_wait(f, expected), "futex_wait failure")
|
||||
}
|
||||
|
||||
// returns true if the wait happened within the duration, false if it exceeded the time duration
|
||||
futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, duration: time.Duration) -> bool {
|
||||
if u32(atomic_load_explicit(f, .Acquire)) != expected {
|
||||
return true
|
||||
}
|
||||
@@ -214,10 +215,25 @@ futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duratio
|
||||
return _futex_wait_with_timeout(f, expected, duration)
|
||||
}
|
||||
|
||||
futex_signal :: proc(f: ^Futex) {
|
||||
futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
_futex_signal(f)
|
||||
}
|
||||
|
||||
futex_broadcast :: proc(f: ^Futex) {
|
||||
futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
_futex_broadcast(f)
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_assert :: proc "contextless" (cond: bool, msg: string) {
|
||||
if !cond {
|
||||
_panic(msg)
|
||||
}
|
||||
}
|
||||
|
||||
@(private)
|
||||
_panic :: proc "contextless" (msg: string) -> ! {
|
||||
runtime.print_string(msg)
|
||||
runtime.print_byte('\n')
|
||||
runtime.trap()
|
||||
}
|
||||
@@ -18,9 +18,9 @@ Atomic_Mutex :: struct {
|
||||
}
|
||||
|
||||
// atomic_mutex_lock locks m
|
||||
atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
|
||||
atomic_mutex_lock :: proc "contextless" (m: ^Atomic_Mutex) {
|
||||
@(cold)
|
||||
lock_slow :: proc(m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
|
||||
lock_slow :: proc "contextless" (m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
|
||||
new_state := curr_state // Make a copy of it
|
||||
|
||||
spin_lock: for spin in 0..<i32(100) {
|
||||
@@ -58,9 +58,9 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
|
||||
}
|
||||
|
||||
// atomic_mutex_unlock unlocks m
|
||||
atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
|
||||
atomic_mutex_unlock :: proc "contextless" (m: ^Atomic_Mutex) {
|
||||
@(cold)
|
||||
unlock_slow :: proc(m: ^Atomic_Mutex) {
|
||||
unlock_slow :: proc "contextless" (m: ^Atomic_Mutex) {
|
||||
futex_signal((^Futex)(&m.state))
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
|
||||
}
|
||||
|
||||
// atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
|
||||
atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
|
||||
atomic_mutex_try_lock :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
|
||||
_, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
|
||||
return ok
|
||||
}
|
||||
@@ -88,7 +88,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=atomic_mutex_unlock)
|
||||
atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
|
||||
atomic_mutex_guard :: proc "contextless" (m: ^Atomic_Mutex) -> bool {
|
||||
atomic_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -117,7 +117,7 @@ Atomic_RW_Mutex :: struct {
|
||||
|
||||
// atomic_rw_mutex_lock locks rw for writing (with a single writer)
|
||||
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
|
||||
atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
atomic_rw_mutex_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Writer)
|
||||
atomic_mutex_lock(&rw.mutex)
|
||||
|
||||
@@ -128,13 +128,13 @@ atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
}
|
||||
|
||||
// atomic_rw_mutex_unlock unlocks rw for writing (with a single writer)
|
||||
atomic_rw_mutex_unlock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
atomic_rw_mutex_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
_ = atomic_and(&rw.state, ~Atomic_RW_Mutex_State_Is_Writing)
|
||||
atomic_mutex_unlock(&rw.mutex)
|
||||
}
|
||||
|
||||
// atomic_rw_mutex_try_lock tries to lock rw for writing (with a single writer)
|
||||
atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
|
||||
atomic_rw_mutex_try_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
|
||||
if atomic_mutex_try_lock(&rw.mutex) {
|
||||
state := atomic_load(&rw.state)
|
||||
if state & Atomic_RW_Mutex_State_Reader_Mask == 0 {
|
||||
@@ -148,7 +148,7 @@ atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
|
||||
}
|
||||
|
||||
// atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
|
||||
atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
atomic_rw_mutex_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
state := atomic_load(&rw.state)
|
||||
for state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
|
||||
ok: bool
|
||||
@@ -164,7 +164,7 @@ atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
}
|
||||
|
||||
// atomic_rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
|
||||
atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
atomic_rw_mutex_shared_unlock :: proc "contextless" (rw: ^Atomic_RW_Mutex) {
|
||||
state := atomic_sub(&rw.state, Atomic_RW_Mutex_State_Reader)
|
||||
|
||||
if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
|
||||
@@ -174,7 +174,7 @@ atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
|
||||
}
|
||||
|
||||
// atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
|
||||
atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
|
||||
atomic_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^Atomic_RW_Mutex) -> bool {
|
||||
state := atomic_load(&rw.state)
|
||||
if state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
|
||||
_, ok := atomic_compare_exchange_strong(&rw.state, state, state + Atomic_RW_Mutex_State_Reader)
|
||||
@@ -198,7 +198,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=atomic_rw_mutex_unlock)
|
||||
atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
|
||||
atomic_rw_mutex_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {
|
||||
atomic_rw_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -210,7 +210,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=atomic_rw_mutex_shared_unlock)
|
||||
atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
|
||||
atomic_rw_mutex_shared_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool {
|
||||
atomic_rw_mutex_shared_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -228,7 +228,7 @@ Atomic_Recursive_Mutex :: struct {
|
||||
mutex: Mutex,
|
||||
}
|
||||
|
||||
atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
|
||||
atomic_recursive_mutex_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {
|
||||
tid := current_thread_id()
|
||||
if tid != m.owner {
|
||||
mutex_lock(&m.mutex)
|
||||
@@ -238,9 +238,9 @@ atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
|
||||
m.recursion += 1
|
||||
}
|
||||
|
||||
atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
|
||||
atomic_recursive_mutex_unlock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) {
|
||||
tid := current_thread_id()
|
||||
assert(tid == m.owner)
|
||||
_assert(tid == m.owner, "tid != m.owner")
|
||||
m.recursion -= 1
|
||||
recursion := m.recursion
|
||||
if recursion == 0 {
|
||||
@@ -253,7 +253,7 @@ atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
|
||||
|
||||
}
|
||||
|
||||
atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
|
||||
atomic_recursive_mutex_try_lock :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {
|
||||
tid := current_thread_id()
|
||||
if m.owner == tid {
|
||||
return mutex_try_lock(&m.mutex)
|
||||
@@ -274,7 +274,7 @@ Example:
|
||||
}
|
||||
*/
|
||||
@(deferred_in=atomic_recursive_mutex_unlock)
|
||||
atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
|
||||
atomic_recursive_mutex_guard :: proc "contextless" (m: ^Atomic_Recursive_Mutex) -> bool {
|
||||
atomic_recursive_mutex_lock(m)
|
||||
return true
|
||||
}
|
||||
@@ -289,7 +289,7 @@ Atomic_Cond :: struct {
|
||||
state: Futex,
|
||||
}
|
||||
|
||||
atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
|
||||
atomic_cond_wait :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex) {
|
||||
state := u32(atomic_load_explicit(&c.state, .Relaxed))
|
||||
unlock(m)
|
||||
futex_wait(&c.state, state)
|
||||
@@ -297,7 +297,7 @@ atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
|
||||
|
||||
}
|
||||
|
||||
atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
|
||||
atomic_cond_wait_with_timeout :: proc "contextless" (c: ^Atomic_Cond, m: ^Atomic_Mutex, duration: time.Duration) -> (ok: bool) {
|
||||
state := u32(atomic_load_explicit(&c.state, .Relaxed))
|
||||
unlock(m)
|
||||
ok = futex_wait_with_timeout(&c.state, state, duration)
|
||||
@@ -306,12 +306,12 @@ atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, duratio
|
||||
}
|
||||
|
||||
|
||||
atomic_cond_signal :: proc(c: ^Atomic_Cond) {
|
||||
atomic_cond_signal :: proc "contextless" (c: ^Atomic_Cond) {
|
||||
atomic_add_explicit(&c.state, 1, .Release)
|
||||
futex_signal(&c.state)
|
||||
}
|
||||
|
||||
atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
|
||||
atomic_cond_broadcast :: proc "contextless" (c: ^Atomic_Cond) {
|
||||
atomic_add_explicit(&c.state, 1, .Release)
|
||||
futex_broadcast(&c.state)
|
||||
}
|
||||
@@ -324,7 +324,7 @@ Atomic_Sema :: struct {
|
||||
count: Futex,
|
||||
}
|
||||
|
||||
atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
|
||||
atomic_sema_post :: proc "contextless" (s: ^Atomic_Sema, count := 1) {
|
||||
atomic_add_explicit(&s.count, Futex(count), .Release)
|
||||
if count == 1 {
|
||||
futex_signal(&s.count)
|
||||
@@ -333,7 +333,7 @@ atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
|
||||
}
|
||||
}
|
||||
|
||||
atomic_sema_wait :: proc(s: ^Atomic_Sema) {
|
||||
atomic_sema_wait :: proc "contextless" (s: ^Atomic_Sema) {
|
||||
for {
|
||||
original_count := atomic_load_explicit(&s.count, .Relaxed)
|
||||
for original_count == 0 {
|
||||
@@ -346,7 +346,7 @@ atomic_sema_wait :: proc(s: ^Atomic_Sema) {
|
||||
}
|
||||
}
|
||||
|
||||
atomic_sema_wait_with_timeout :: proc(s: ^Atomic_Sema, duration: time.Duration) -> bool {
|
||||
atomic_sema_wait_with_timeout :: proc "contextless" (s: ^Atomic_Sema, duration: time.Duration) -> bool {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -7,15 +7,15 @@ _Sema :: struct {
|
||||
atomic: Atomic_Sema,
|
||||
}
|
||||
|
||||
_sema_post :: proc(s: ^Sema, count := 1) {
|
||||
_sema_post :: proc "contextless" (s: ^Sema, count := 1) {
|
||||
atomic_sema_post(&s.impl.atomic, count)
|
||||
}
|
||||
|
||||
_sema_wait :: proc(s: ^Sema) {
|
||||
_sema_wait :: proc "contextless" (s: ^Sema) {
|
||||
atomic_sema_wait(&s.impl.atomic)
|
||||
}
|
||||
|
||||
_sema_wait_with_timeout :: proc(s: ^Sema, duration: time.Duration) -> bool {
|
||||
_sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
|
||||
return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ _Recursive_Mutex :: struct {
|
||||
recursion: i32,
|
||||
}
|
||||
|
||||
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
|
||||
_recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
|
||||
tid := Futex(current_thread_id())
|
||||
for {
|
||||
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
|
||||
@@ -40,7 +40,7 @@ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
|
||||
}
|
||||
}
|
||||
|
||||
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
|
||||
_recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
|
||||
m.impl.recursion -= 1
|
||||
if m.impl.recursion != 0 {
|
||||
return
|
||||
@@ -52,7 +52,7 @@ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
|
||||
|
||||
}
|
||||
|
||||
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
|
||||
_recursive_mutex_try_lock :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
|
||||
tid := Futex(current_thread_id())
|
||||
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
|
||||
switch prev_owner {
|
||||
@@ -70,15 +70,15 @@ when ODIN_OS != .Windows {
|
||||
mutex: Atomic_Mutex,
|
||||
}
|
||||
|
||||
_mutex_lock :: proc(m: ^Mutex) {
|
||||
_mutex_lock :: proc "contextless" (m: ^Mutex) {
|
||||
atomic_mutex_lock(&m.impl.mutex)
|
||||
}
|
||||
|
||||
_mutex_unlock :: proc(m: ^Mutex) {
|
||||
_mutex_unlock :: proc "contextless" (m: ^Mutex) {
|
||||
atomic_mutex_unlock(&m.impl.mutex)
|
||||
}
|
||||
|
||||
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
|
||||
return atomic_mutex_try_lock(&m.impl.mutex)
|
||||
}
|
||||
|
||||
@@ -86,19 +86,19 @@ when ODIN_OS != .Windows {
|
||||
cond: Atomic_Cond,
|
||||
}
|
||||
|
||||
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
|
||||
_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
|
||||
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
|
||||
}
|
||||
|
||||
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
|
||||
}
|
||||
|
||||
_cond_signal :: proc(c: ^Cond) {
|
||||
_cond_signal :: proc "contextless" (c: ^Cond) {
|
||||
atomic_cond_signal(&c.impl.cond)
|
||||
}
|
||||
|
||||
_cond_broadcast :: proc(c: ^Cond) {
|
||||
_cond_broadcast :: proc "contextless" (c: ^Cond) {
|
||||
atomic_cond_broadcast(&c.impl.cond)
|
||||
}
|
||||
|
||||
@@ -107,27 +107,27 @@ when ODIN_OS != .Windows {
|
||||
mutex: Atomic_RW_Mutex,
|
||||
}
|
||||
|
||||
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
atomic_rw_mutex_lock(&rw.impl.mutex)
|
||||
}
|
||||
|
||||
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
atomic_rw_mutex_unlock(&rw.impl.mutex)
|
||||
}
|
||||
|
||||
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
|
||||
_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
|
||||
return atomic_rw_mutex_try_lock(&rw.impl.mutex)
|
||||
}
|
||||
|
||||
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
atomic_rw_mutex_shared_lock(&rw.impl.mutex)
|
||||
}
|
||||
|
||||
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
|
||||
}
|
||||
|
||||
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
|
||||
_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
|
||||
return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
|
||||
}
|
||||
}
|
||||
@@ -13,15 +13,15 @@ _Mutex :: struct {
|
||||
srwlock: win32.SRWLOCK,
|
||||
}
|
||||
|
||||
_mutex_lock :: proc(m: ^Mutex) {
|
||||
_mutex_lock :: proc "contextless" (m: ^Mutex) {
|
||||
win32.AcquireSRWLockExclusive(&m.impl.srwlock)
|
||||
}
|
||||
|
||||
_mutex_unlock :: proc(m: ^Mutex) {
|
||||
_mutex_unlock :: proc "contextless" (m: ^Mutex) {
|
||||
win32.ReleaseSRWLockExclusive(&m.impl.srwlock)
|
||||
}
|
||||
|
||||
_mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
|
||||
return bool(win32.TryAcquireSRWLockExclusive(&m.impl.srwlock))
|
||||
}
|
||||
|
||||
@@ -29,27 +29,27 @@ _RW_Mutex :: struct {
|
||||
srwlock: win32.SRWLOCK,
|
||||
}
|
||||
|
||||
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
win32.AcquireSRWLockExclusive(&rw.impl.srwlock)
|
||||
}
|
||||
|
||||
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
win32.ReleaseSRWLockExclusive(&rw.impl.srwlock)
|
||||
}
|
||||
|
||||
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
|
||||
_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
|
||||
return bool(win32.TryAcquireSRWLockExclusive(&rw.impl.srwlock))
|
||||
}
|
||||
|
||||
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
win32.AcquireSRWLockShared(&rw.impl.srwlock)
|
||||
}
|
||||
|
||||
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
|
||||
_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
|
||||
win32.ReleaseSRWLockShared(&rw.impl.srwlock)
|
||||
}
|
||||
|
||||
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
|
||||
_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
|
||||
return bool(win32.TryAcquireSRWLockShared(&rw.impl.srwlock))
|
||||
}
|
||||
|
||||
@@ -58,22 +58,22 @@ _Cond :: struct {
|
||||
cond: win32.CONDITION_VARIABLE,
|
||||
}
|
||||
|
||||
_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
|
||||
_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
|
||||
_ = win32.SleepConditionVariableSRW(&c.impl.cond, &m.impl.srwlock, win32.INFINITE, 0)
|
||||
}
|
||||
|
||||
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
|
||||
duration := u32(duration / time.Millisecond)
|
||||
ok := win32.SleepConditionVariableSRW(&c.impl.cond, &m.impl.srwlock, duration, 0)
|
||||
return bool(ok)
|
||||
}
|
||||
|
||||
|
||||
_cond_signal :: proc(c: ^Cond) {
|
||||
_cond_signal :: proc "contextless" (c: ^Cond) {
|
||||
win32.WakeConditionVariable(&c.impl.cond)
|
||||
}
|
||||
|
||||
_cond_broadcast :: proc(c: ^Cond) {
|
||||
_cond_broadcast :: proc "contextless" (c: ^Cond) {
|
||||
win32.WakeAllConditionVariable(&c.impl.cond)
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
CPU feature flags can be tested against `cpu_features`, where applicable, e.g.
|
||||
`if .aes in si.aes { ... }`
|
||||
*/
|
||||
// +ignore
|
||||
//+build ignore
|
||||
package sysinfo
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
@@ -464,6 +464,7 @@ macos_release_map: map[string]Darwin_To_Release = {
|
||||
"21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
|
||||
"21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
|
||||
"21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
|
||||
"21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -128,4 +128,34 @@ foreign advapi32 {
|
||||
lpData: LPCVOID,
|
||||
cbData: DWORD,
|
||||
) -> LSTATUS ---
|
||||
|
||||
GetFileSecurityW :: proc(
|
||||
lpFileName: LPCWSTR,
|
||||
RequestedInformation: SECURITY_INFORMATION,
|
||||
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
|
||||
nLength: DWORD,
|
||||
lpnLengthNeeded: LPDWORD,
|
||||
) -> BOOL ---
|
||||
|
||||
DuplicateToken :: proc(
|
||||
ExistingTokenHandle: HANDLE,
|
||||
ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL,
|
||||
DuplicateTokenHandle: PHANDLE,
|
||||
) -> BOOL ---
|
||||
|
||||
MapGenericMask :: proc(
|
||||
AccessMask: PDWORD,
|
||||
GenericMapping: PGENERIC_MAPPING,
|
||||
) ---
|
||||
|
||||
AccessCheck :: proc(
|
||||
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
|
||||
ClientToken: HANDLE,
|
||||
DesiredAccess: DWORD,
|
||||
GenericMapping: PGENERIC_MAPPING,
|
||||
PrivilegeSet: PPRIVILEGE_SET,
|
||||
PrivilegeSetLength: LPDWORD,
|
||||
GrantedAccess: LPDWORD,
|
||||
AccessStatus: LPBOOL,
|
||||
) -> BOOL ---
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build windows
|
||||
package sys_windows
|
||||
|
||||
foreign import "system:Comctl32.lib"
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Comctl32 {
|
||||
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
|
||||
}
|
||||
@@ -3,7 +3,45 @@ package sys_windows
|
||||
|
||||
foreign import dwmapi "system:Dwmapi.lib"
|
||||
|
||||
DWMWINDOWATTRIBUTE :: enum {
|
||||
DWMWA_NCRENDERING_ENABLED,
|
||||
DWMWA_NCRENDERING_POLICY,
|
||||
DWMWA_TRANSITIONS_FORCEDISABLED,
|
||||
DWMWA_ALLOW_NCPAINT,
|
||||
DWMWA_CAPTION_BUTTON_BOUNDS,
|
||||
DWMWA_NONCLIENT_RTL_LAYOUT,
|
||||
DWMWA_FORCE_ICONIC_REPRESENTATION,
|
||||
DWMWA_FLIP3D_POLICY,
|
||||
DWMWA_EXTENDED_FRAME_BOUNDS,
|
||||
DWMWA_HAS_ICONIC_BITMAP,
|
||||
DWMWA_DISALLOW_PEEK,
|
||||
DWMWA_EXCLUDED_FROM_PEEK,
|
||||
DWMWA_CLOAK,
|
||||
DWMWA_CLOAKED,
|
||||
DWMWA_FREEZE_REPRESENTATION,
|
||||
DWMWA_PASSIVE_UPDATE_MODE,
|
||||
DWMWA_USE_HOSTBACKDROPBRUSH,
|
||||
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
|
||||
DWMWA_WINDOW_CORNER_PREFERENCE = 33,
|
||||
DWMWA_BORDER_COLOR,
|
||||
DWMWA_CAPTION_COLOR,
|
||||
DWMWA_TEXT_COLOR,
|
||||
DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
|
||||
DWMWA_SYSTEMBACKDROP_TYPE,
|
||||
DWMWA_LAST,
|
||||
}
|
||||
|
||||
DWMNCRENDERINGPOLICY :: enum {
|
||||
DWMNCRP_USEWINDOWSTYLE,
|
||||
DWMNCRP_DISABLED,
|
||||
DWMNCRP_ENABLED,
|
||||
DWMNCRP_LAST,
|
||||
}
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign dwmapi {
|
||||
DwmFlush :: proc() -> HRESULT ---
|
||||
DwmIsCompositionEnabled :: proc(pfEnabled: ^BOOL) -> HRESULT ---
|
||||
DwmExtendFrameIntoClientArea :: proc(hWnd: HWND, pMarInset: PMARGINS) -> HRESULT ---
|
||||
DwmSetWindowAttribute :: proc(hWnd: HWND, dwAttribute: DWORD, pvAttribute: LPCVOID, cbAttribute: DWORD) -> HRESULT ---
|
||||
}
|
||||
|
||||
@@ -62,6 +62,7 @@ foreign gdi32 {
|
||||
|
||||
SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
|
||||
ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
|
||||
DescribePixelFormat :: proc(hdc: HDC, iPixelFormat: c_int, nBytes: UINT, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
|
||||
SwapBuffers :: proc(HDC) -> BOOL ---
|
||||
|
||||
SetDCBrushColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
|
||||
@@ -77,6 +78,9 @@ foreign gdi32 {
|
||||
) -> HFONT ---
|
||||
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
|
||||
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
|
||||
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
|
||||
|
||||
CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH ---
|
||||
}
|
||||
|
||||
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
|
||||
|
||||
@@ -120,6 +120,12 @@ foreign kernel32 {
|
||||
bManualReset: BOOL,
|
||||
lpTimerName: LPCWSTR,
|
||||
) -> HANDLE ---
|
||||
CreateWaitableTimerExW :: proc(
|
||||
lpTimerAttributes: LPSECURITY_ATTRIBUTES,
|
||||
lpTimerName: LPCWSTR,
|
||||
dwFlags: DWORD,
|
||||
dwDesiredAccess: DWORD,
|
||||
) -> HANDLE ---
|
||||
SetWaitableTimerEx :: proc(
|
||||
hTimer: HANDLE,
|
||||
lpDueTime: ^LARGE_INTEGER,
|
||||
@@ -248,6 +254,17 @@ foreign kernel32 {
|
||||
GetModuleHandleW :: proc(lpModuleName: LPCWSTR) -> HMODULE ---
|
||||
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
|
||||
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
||||
GetSystemTimePreciseAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
||||
FileTimeToSystemTime :: proc(lpFileTime: ^FILETIME, lpSystemTime: ^SYSTEMTIME) -> BOOL ---
|
||||
SystemTimeToTzSpecificLocalTime :: proc(
|
||||
lpTimeZoneInformation: ^TIME_ZONE_INFORMATION,
|
||||
lpUniversalTime: ^SYSTEMTIME,
|
||||
lpLocalTime: ^SYSTEMTIME,
|
||||
) -> BOOL ---
|
||||
SystemTimeToFileTime :: proc(
|
||||
lpSystemTime: ^SYSTEMTIME,
|
||||
lpFileTime: LPFILETIME,
|
||||
) -> BOOL ---
|
||||
CreateEventW :: proc(
|
||||
lpEventAttributes: LPSECURITY_ATTRIBUTES,
|
||||
bManualReset: BOOL,
|
||||
@@ -346,6 +363,15 @@ foreign kernel32 {
|
||||
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
|
||||
FreeConsole :: proc() -> BOOL ---
|
||||
GetConsoleWindow :: proc() -> HWND ---
|
||||
|
||||
GetDiskFreeSpaceExW :: proc(
|
||||
lpDirectoryName: LPCWSTR,
|
||||
lpFreeBytesAvailableToCaller: PULARGE_INTEGER,
|
||||
lpTotalNumberOfBytes: PULARGE_INTEGER,
|
||||
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
|
||||
) -> BOOL ---
|
||||
|
||||
GetLogicalProcessorInformation :: proc(buffer: ^SYSTEM_LOGICAL_PROCESSOR_INFORMATION, returnedLength: PDWORD) -> BOOL ---
|
||||
}
|
||||
|
||||
|
||||
@@ -963,4 +989,61 @@ DCB :: struct {
|
||||
foreign kernel32 {
|
||||
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
|
||||
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LPFIBER_START_ROUTINE :: #type proc "stdcall" (lpFiberParameter: LPVOID)
|
||||
|
||||
@(default_calling_convention = "stdcall")
|
||||
foreign kernel32 {
|
||||
CreateFiber :: proc(dwStackSize: SIZE_T, lpStartAddress: LPFIBER_START_ROUTINE, lpParameter: LPVOID) -> LPVOID ---
|
||||
DeleteFiber :: proc(lpFiber: LPVOID) ---
|
||||
ConvertThreadToFiber :: proc(lpParameter: LPVOID) -> LPVOID ---
|
||||
SwitchToFiber :: proc(lpFiber: LPVOID) ---
|
||||
}
|
||||
|
||||
LOGICAL_PROCESSOR_RELATIONSHIP :: enum c_int {
|
||||
RelationProcessorCore,
|
||||
RelationNumaNode,
|
||||
RelationCache,
|
||||
RelationProcessorPackage,
|
||||
RelationGroup,
|
||||
RelationProcessorDie,
|
||||
RelationNumaNodeEx,
|
||||
RelationProcessorModule,
|
||||
RelationAll = 0xffff,
|
||||
}
|
||||
|
||||
PROCESSOR_CACHE_TYPE :: enum c_int {
|
||||
CacheUnified,
|
||||
CacheInstruction,
|
||||
CacheData,
|
||||
CacheTrace,
|
||||
}
|
||||
|
||||
CACHE_DESCRIPTOR :: struct {
|
||||
Level: BYTE,
|
||||
Associativity: BYTE,
|
||||
LineSize: WORD,
|
||||
Size: DWORD,
|
||||
Type: PROCESSOR_CACHE_TYPE,
|
||||
}
|
||||
|
||||
ProcessorCore :: struct {
|
||||
Flags: BYTE,
|
||||
}
|
||||
NumaNode :: struct {
|
||||
NodeNumber: DWORD,
|
||||
}
|
||||
DUMMYUNIONNAME_u :: struct #raw_union {
|
||||
Core: ProcessorCore,
|
||||
Node: NumaNode,
|
||||
Cache: CACHE_DESCRIPTOR,
|
||||
Reserved: [2]ULONGLONG,
|
||||
}
|
||||
|
||||
SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
|
||||
ProcessorMask: ULONG_PTR,
|
||||
Relationship: LOGICAL_PROCESSOR_RELATIONSHIP,
|
||||
DummyUnion: DUMMYUNIONNAME_u,
|
||||
}
|
||||
|
||||
@@ -14,4 +14,45 @@ foreign shell32 {
|
||||
lpDirectory: LPCWSTR,
|
||||
nShowCmd: INT,
|
||||
) -> HINSTANCE ---
|
||||
ShellExecuteExW :: proc(pExecInfo: ^SHELLEXECUTEINFOW) -> BOOL ---
|
||||
SHCreateDirectoryExW :: proc(
|
||||
hwnd: HWND,
|
||||
pszPath: LPCWSTR,
|
||||
psa: ^SECURITY_ATTRIBUTES,
|
||||
) -> c_int ---
|
||||
SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
|
||||
SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT ---
|
||||
SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR ---
|
||||
}
|
||||
|
||||
APPBARDATA :: struct {
|
||||
cbSize: DWORD,
|
||||
hWnd: HWND,
|
||||
uCallbackMessage: UINT,
|
||||
uEdge: UINT,
|
||||
rc: RECT,
|
||||
lParam: LPARAM,
|
||||
}
|
||||
PAPPBARDATA :: ^APPBARDATA
|
||||
|
||||
ABM_NEW :: 0x00000000
|
||||
ABM_REMOVE :: 0x00000001
|
||||
ABM_QUERYPOS :: 0x00000002
|
||||
ABM_SETPOS :: 0x00000003
|
||||
ABM_GETSTATE :: 0x00000004
|
||||
ABM_GETTASKBARPOS :: 0x00000005
|
||||
ABM_ACTIVATE :: 0x00000006
|
||||
ABM_GETAUTOHIDEBAR :: 0x00000007
|
||||
ABM_SETAUTOHIDEBAR :: 0x00000008
|
||||
ABM_WINDOWPOSCHANGED :: 0x0000009
|
||||
ABM_SETSTATE :: 0x0000000a
|
||||
ABN_STATECHANGE :: 0x0000000
|
||||
ABN_POSCHANGED :: 0x0000001
|
||||
ABN_FULLSCREENAPP :: 0x0000002
|
||||
ABN_WINDOWARRANGE :: 0x0000003
|
||||
ABS_AUTOHIDE :: 0x0000001
|
||||
ABS_ALWAYSONTOP :: 0x0000002
|
||||
ABE_LEFT :: 0
|
||||
ABE_TOP :: 1
|
||||
ABE_RIGHT :: 2
|
||||
ABE_BOTTOM :: 3
|
||||
|
||||
@@ -8,4 +8,5 @@ foreign shlwapi {
|
||||
PathFileExistsW :: proc(pszPath: wstring) -> BOOL ---
|
||||
PathFindExtensionW :: proc(pszPath: wstring) -> wstring ---
|
||||
PathFindFileNameW :: proc(pszPath: wstring) -> wstring ---
|
||||
SHAutoComplete :: proc(hwndEdit: HWND, dwFlags: DWORD) -> LWSTDAPI ---
|
||||
}
|
||||
|
||||
+393
-4
@@ -20,6 +20,7 @@ DWORD :: c_ulong
|
||||
DWORDLONG :: c.ulonglong
|
||||
QWORD :: c.ulonglong
|
||||
HANDLE :: distinct LPVOID
|
||||
PHANDLE :: ^HANDLE
|
||||
HINSTANCE :: HANDLE
|
||||
HMODULE :: distinct HINSTANCE
|
||||
HRESULT :: distinct LONG
|
||||
@@ -37,12 +38,14 @@ HHOOK :: distinct HANDLE
|
||||
HKEY :: distinct HANDLE
|
||||
HDESK :: distinct HANDLE
|
||||
HFONT :: distinct HANDLE
|
||||
HRGN :: distinct HANDLE
|
||||
BOOL :: distinct b32
|
||||
BYTE :: distinct u8
|
||||
BOOLEAN :: distinct b8
|
||||
GROUP :: distinct c_uint
|
||||
LARGE_INTEGER :: distinct c_longlong
|
||||
ULARGE_INTEGER :: distinct c_ulonglong
|
||||
PULARGE_INTEGER :: ^ULARGE_INTEGER
|
||||
LONG :: c_long
|
||||
UINT :: c_uint
|
||||
INT :: c_int
|
||||
@@ -133,12 +136,24 @@ LPWSAOVERLAPPED :: distinct rawptr
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
|
||||
LPCVOID :: rawptr
|
||||
|
||||
PACCESS_TOKEN :: PVOID
|
||||
PSECURITY_DESCRIPTOR :: PVOID
|
||||
PSID :: PVOID
|
||||
PCLAIMS_BLOB :: PVOID
|
||||
|
||||
PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
|
||||
PLARGE_INTEGER :: ^LARGE_INTEGER
|
||||
PSRWLOCK :: ^SRWLOCK
|
||||
|
||||
MMRESULT :: UINT
|
||||
|
||||
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
|
||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
|
||||
|
||||
TIMER_QUERY_STATE :: 0x0001
|
||||
TIMER_MODIFY_STATE :: 0x0002
|
||||
TIMER_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | TIMER_QUERY_STATE | TIMER_MODIFY_STATE
|
||||
|
||||
SOCKET :: distinct uintptr // TODO
|
||||
socklen_t :: c_int
|
||||
ADDRESS_FAMILY :: USHORT
|
||||
@@ -175,6 +190,7 @@ FILE_SHARE_DELETE: DWORD : 0x00000004
|
||||
FILE_GENERIC_ALL: DWORD : 0x10000000
|
||||
FILE_GENERIC_EXECUTE: DWORD : 0x20000000
|
||||
FILE_GENERIC_READ: DWORD : 0x80000000
|
||||
FILE_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
|
||||
|
||||
FILE_ACTION_ADDED :: 0x00000001
|
||||
FILE_ACTION_REMOVED :: 0x00000002
|
||||
@@ -230,6 +246,20 @@ SECURITY_SQOS_PRESENT: DWORD : 0x00100000
|
||||
|
||||
FIONBIO: c_ulong : 0x8004667e
|
||||
|
||||
OWNER_SECURITY_INFORMATION :: 0x00000001
|
||||
GROUP_SECURITY_INFORMATION :: 0x00000002
|
||||
DACL_SECURITY_INFORMATION :: 0x00000004
|
||||
SACL_SECURITY_INFORMATION :: 0x00000008
|
||||
LABEL_SECURITY_INFORMATION :: 0x00000010
|
||||
ATTRIBUTE_SECURITY_INFORMATION :: 0x00000020
|
||||
SCOPE_SECURITY_INFORMATION :: 0x00000040
|
||||
PROCESS_TRUST_LABEL_SECURITY_INFORMATION :: 0x00000080
|
||||
ACCESS_FILTER_SECURITY_INFORMATION :: 0x00000100
|
||||
BACKUP_SECURITY_INFORMATION :: 0x00010000
|
||||
PROTECTED_DACL_SECURITY_INFORMATION :: 0x80000000
|
||||
PROTECTED_SACL_SECURITY_INFORMATION :: 0x40000000
|
||||
UNPROTECTED_DACL_SECURITY_INFORMATION :: 0x20000000
|
||||
UNPROTECTED_SACL_SECURITY_INFORMATION :: 0x10000000
|
||||
|
||||
GET_FILEEX_INFO_LEVELS :: distinct i32
|
||||
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
|
||||
@@ -773,6 +803,61 @@ MSG :: struct {
|
||||
|
||||
LPMSG :: ^MSG
|
||||
|
||||
// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
|
||||
HTERROR :: -2
|
||||
HTTRANSPARENT :: -1
|
||||
HTNOWHERE :: 0
|
||||
HTCLIENT :: 1
|
||||
HTCAPTION :: 2
|
||||
HTSYSMENU :: 3
|
||||
HTGROWBOX :: 4
|
||||
HTSIZE :: HTGROWBOX
|
||||
HTMENU :: 5
|
||||
HTHSCROLL :: 6
|
||||
HTVSCROLL :: 7
|
||||
HTMINBUTTON :: 8
|
||||
HTMAXBUTTON :: 9
|
||||
HTLEFT :: 10
|
||||
HTRIGHT :: 11
|
||||
HTTOP :: 12
|
||||
HTTOPLEFT :: 13
|
||||
HTTOPRIGHT :: 14
|
||||
HTBOTTOM :: 15
|
||||
HTBOTTOMLEFT :: 16
|
||||
HTBOTTOMRIGHT :: 17
|
||||
HTBORDER :: 18
|
||||
HTREDUCE :: HTMINBUTTON
|
||||
HTZOOM :: HTMAXBUTTON
|
||||
HTSIZEFIRST :: HTLEFT
|
||||
HTSIZELAST :: HTBOTTOMRIGHT
|
||||
HTOBJECT :: 19
|
||||
HTCLOSE :: 20
|
||||
HTHELP :: 21
|
||||
|
||||
TEXTMETRICW :: struct {
|
||||
tmHeight: LONG,
|
||||
tmAscent: LONG,
|
||||
tmDescent: LONG,
|
||||
tmInternalLeading: LONG,
|
||||
tmExternalLeading: LONG,
|
||||
tmAveCharWidth: LONG,
|
||||
tmMaxCharWidth: LONG,
|
||||
tmWeight: LONG,
|
||||
tmOverhang: LONG,
|
||||
tmDigitizedAspectX: LONG,
|
||||
tmDigitizedAspectY: LONG,
|
||||
tmFirstChar: WCHAR,
|
||||
tmLastChar: WCHAR,
|
||||
tmDefaultChar: WCHAR,
|
||||
tmBreakChar: WCHAR,
|
||||
tmItalic: BYTE,
|
||||
tmUnderlined: BYTE,
|
||||
tmStruckOut: BYTE,
|
||||
tmPitchAndFamily: BYTE,
|
||||
tmCharSet: BYTE,
|
||||
}
|
||||
LPTEXTMETRICW :: ^TEXTMETRICW
|
||||
|
||||
PAINTSTRUCT :: struct {
|
||||
hdc: HDC,
|
||||
fErase: BOOL,
|
||||
@@ -879,6 +964,48 @@ NM_FONTCHANGED :: NM_OUTOFMEMORY-22
|
||||
NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct
|
||||
NM_TVSTATEIMAGECHANGING :: NM_OUTOFMEMORY-23 // uses NMTVSTATEIMAGECHANGING struct, defined after HTREEITEM
|
||||
|
||||
PCZZWSTR :: ^WCHAR
|
||||
|
||||
SHFILEOPSTRUCTW :: struct {
|
||||
hwnd: HWND,
|
||||
wFunc: UINT,
|
||||
pFrom: PCZZWSTR,
|
||||
pTo: PCZZWSTR,
|
||||
fFlags: FILEOP_FLAGS,
|
||||
fAnyOperationsAborted: BOOL,
|
||||
hNameMappings: LPVOID,
|
||||
lpszProgressTitle: PCWSTR, // only used if FOF_SIMPLEPROGRESS
|
||||
}
|
||||
LPSHFILEOPSTRUCTW :: ^SHFILEOPSTRUCTW
|
||||
|
||||
// Shell File Operations
|
||||
FO_MOVE :: 0x0001
|
||||
FO_COPY :: 0x0002
|
||||
FO_DELETE :: 0x0003
|
||||
FO_RENAME :: 0x0004
|
||||
|
||||
// SHFILEOPSTRUCT.fFlags and IFileOperation::SetOperationFlags() flag values
|
||||
FOF_MULTIDESTFILES :: 0x0001
|
||||
FOF_CONFIRMMOUSE :: 0x0002
|
||||
FOF_SILENT :: 0x0004 // don't display progress UI (confirm prompts may be displayed still)
|
||||
FOF_RENAMEONCOLLISION :: 0x0008 // automatically rename the source files to avoid the collisions
|
||||
FOF_NOCONFIRMATION :: 0x0010 // don't display confirmation UI, assume "yes" for cases that can be bypassed, "no" for those that can not
|
||||
FOF_WANTMAPPINGHANDLE :: 0x0020 // Fill in SHFILEOPSTRUCT.hNameMappings
|
||||
// Must be freed using SHFreeNameMappings
|
||||
FOF_ALLOWUNDO :: 0x0040 // enable undo including Recycle behavior for IFileOperation::Delete()
|
||||
FOF_FILESONLY :: 0x0080 // only operate on the files (non folders), both files and folders are assumed without this
|
||||
FOF_SIMPLEPROGRESS :: 0x0100 // means don't show names of files
|
||||
FOF_NOCONFIRMMKDIR :: 0x0200 // don't dispplay confirmatino UI before making any needed directories, assume "Yes" in these cases
|
||||
FOF_NOERRORUI :: 0x0400 // don't put up error UI, other UI may be displayed, progress, confirmations
|
||||
FOF_NOCOPYSECURITYATTRIBS :: 0x0800 // dont copy file security attributes (ACLs)
|
||||
FOF_NORECURSION :: 0x1000 // don't recurse into directories for operations that would recurse
|
||||
FOF_NO_CONNECTED_ELEMENTS :: 0x2000 // don't operate on connected elements ("xxx_files" folders that go with .htm files)
|
||||
FOF_WANTNUKEWARNING :: 0x4000 // during delete operation, warn if object is being permanently destroyed instead of recycling (partially overrides FOF_NOCONFIRMATION)
|
||||
FOF_NORECURSEREPARSE :: 0x8000 // deprecated; the operations engine always does the right thing on FolderLink objects (symlinks, reparse points, folder shortcuts)
|
||||
FOF_NO_UI :: (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR) // don't display any UI at all
|
||||
|
||||
FILEOP_FLAGS :: WORD
|
||||
|
||||
DEVMODEW :: struct {
|
||||
dmDeviceName: [32]wchar_t,
|
||||
dmSpecVersion: WORD,
|
||||
@@ -1066,8 +1193,14 @@ WS_EX_TOPMOST : UINT : 0x0000_0008
|
||||
WS_EX_TRANSPARENT : UINT : 0x0000_0020
|
||||
WS_EX_WINDOWEDGE : UINT : 0x0000_0100
|
||||
|
||||
PBS_SMOOTH :: 0x01
|
||||
PBS_VERTICAL :: 0x04
|
||||
PBS_SMOOTH :: 0x01
|
||||
PBS_VERTICAL :: 0x04
|
||||
PBS_MARQUEE :: 0x08
|
||||
PBS_SMOOTHREVERSE :: 0x10
|
||||
|
||||
PBST_NORMAL :: 0x0001
|
||||
PBST_ERROR :: 0x0002
|
||||
PBST_PAUSED :: 0x0003
|
||||
|
||||
QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
|
||||
QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
|
||||
@@ -1127,6 +1260,10 @@ SWP_NOREPOSITION :: SWP_NOOWNERZORDER
|
||||
SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
|
||||
SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
|
||||
|
||||
CSIDL_APPDATA :: 0x001a // <user name>\Application Data
|
||||
CSIDL_COMMON_APPDATA :: 0x0023 // All Users\Application Data
|
||||
CSIDL_PROFILE :: 0x0028 // <user name>\
|
||||
|
||||
HWND_TOP :: HWND( uintptr(0)) // 0
|
||||
HWND_BOTTOM :: HWND( uintptr(1)) // 1
|
||||
HWND_TOPMOST :: HWND(~uintptr(0)) // -1
|
||||
@@ -1325,6 +1462,58 @@ WMSZ_BOTTOM :: 6
|
||||
WMSZ_BOTTOMLEFT :: 7
|
||||
WMSZ_BOTTOMRIGHT :: 8
|
||||
|
||||
// Note CLASSKEY overrides CLASSNAME
|
||||
SEE_MASK_DEFAULT :: 0x00000000
|
||||
SEE_MASK_CLASSNAME :: 0x00000001 // SHELLEXECUTEINFO.lpClass is valid
|
||||
SEE_MASK_CLASSKEY :: 0x00000003 // SHELLEXECUTEINFO.hkeyClass is valid
|
||||
// Note SEE_MASK_INVOKEIDLIST(0xC) implies SEE_MASK_IDLIST(0x04)
|
||||
SEE_MASK_IDLIST :: 0x00000004 // SHELLEXECUTEINFO.lpIDList is valid
|
||||
SEE_MASK_INVOKEIDLIST :: 0x0000000c // enable IContextMenu based verbs
|
||||
SEE_MASK_ICON :: 0x00000010 // not used
|
||||
SEE_MASK_HOTKEY :: 0x00000020 // SHELLEXECUTEINFO.dwHotKey is valid
|
||||
SEE_MASK_NOCLOSEPROCESS :: 0x00000040 // SHELLEXECUTEINFO.hProcess
|
||||
SEE_MASK_CONNECTNETDRV :: 0x00000080 // enables re-connecting disconnected network drives
|
||||
SEE_MASK_NOASYNC :: 0x00000100 // block on the call until the invoke has completed, use for callers that exit after calling ShellExecuteEx()
|
||||
SEE_MASK_FLAG_DDEWAIT :: SEE_MASK_NOASYNC // Use SEE_MASK_NOASYNC instead of SEE_MASK_FLAG_DDEWAIT as it more accuratly describes the behavior
|
||||
SEE_MASK_DOENVSUBST :: 0x00000200 // indicates that SHELLEXECUTEINFO.lpFile contains env vars that should be expanded
|
||||
SEE_MASK_FLAG_NO_UI :: 0x00000400 // disable UI including error messages
|
||||
SEE_MASK_UNICODE :: 0x00004000
|
||||
SEE_MASK_NO_CONSOLE :: 0x00008000
|
||||
SEE_MASK_ASYNCOK :: 0x00100000
|
||||
SEE_MASK_HMONITOR :: 0x00200000 // SHELLEXECUTEINFO.hMonitor
|
||||
SEE_MASK_NOZONECHECKS :: 0x00800000
|
||||
SEE_MASK_NOQUERYCLASSSTORE :: 0x01000000
|
||||
SEE_MASK_WAITFORINPUTIDLE :: 0x02000000
|
||||
SEE_MASK_FLAG_LOG_USAGE :: 0x04000000
|
||||
|
||||
// When SEE_MASK_FLAG_HINST_IS_SITE is specified SHELLEXECUTEINFO.hInstApp is used as an
|
||||
// _In_ parameter and specifies a IUnknown* to be used as a site pointer. The site pointer
|
||||
// is used to provide services to shell execute, the handler binding process and the verb handlers
|
||||
// once they are invoked.
|
||||
SEE_MASK_FLAG_HINST_IS_SITE :: 0x08000000
|
||||
|
||||
SHELLEXECUTEINFOW :: struct {
|
||||
cbSize: DWORD, // in, required, sizeof of this structure
|
||||
fMask: ULONG, // in, SEE_MASK_XXX values
|
||||
hwnd: HWND, // in, optional
|
||||
lpVerb: LPCWSTR, // in, optional when unspecified the default verb is choosen
|
||||
lpFile: LPCWSTR, // in, either this value or lpIDList must be specified
|
||||
lpParameters: LPCWSTR, // in, optional
|
||||
lpDirectory: LPCWSTR, // in, optional
|
||||
nShow: c.int, // in, required
|
||||
hInstApp: HINSTANCE, // out when SEE_MASK_NOCLOSEPROCESS is specified
|
||||
lpIDList: rawptr, // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLIST
|
||||
lpClass: LPCWSTR, // in, valid when SEE_MASK_CLASSNAME is specified
|
||||
hkeyClass: HKEY, // in, valid when SEE_MASK_CLASSKEY is specified
|
||||
dwHotKey: DWORD, // in, valid when SEE_MASK_HOTKEY is specified
|
||||
DUMMYUNIONNAME: struct #raw_union {
|
||||
hIcon: HANDLE, // not used
|
||||
hMonitor: HANDLE, // in, valid when SEE_MASK_HMONITOR specified
|
||||
},
|
||||
hProcess: HANDLE, // out, valid when SEE_MASK_NOCLOSEPROCESS specified
|
||||
}
|
||||
LPSHELLEXECUTEINFOW :: ^SHELLEXECUTEINFOW
|
||||
|
||||
// Key State Masks for Mouse Messages
|
||||
MK_LBUTTON :: 0x0001
|
||||
MK_RBUTTON :: 0x0002
|
||||
@@ -1366,6 +1555,25 @@ WA_INACTIVE :: 0
|
||||
WA_ACTIVE :: 1
|
||||
WA_CLICKACTIVE :: 2
|
||||
|
||||
// Struct pointed to by WM_GETMINMAXINFO lParam
|
||||
MINMAXINFO :: struct {
|
||||
ptReserved: POINT,
|
||||
ptMaxSize: POINT,
|
||||
ptMaxPosition: POINT,
|
||||
ptMinTrackSize: POINT,
|
||||
ptMaxTrackSize: POINT,
|
||||
}
|
||||
PMINMAXINFO :: ^MINMAXINFO
|
||||
LPMINMAXINFO :: PMINMAXINFO
|
||||
|
||||
MONITORINFO :: struct {
|
||||
cbSize: DWORD,
|
||||
rcMonitor: RECT,
|
||||
rcWork: RECT,
|
||||
dwFlags: DWORD,
|
||||
}
|
||||
LPMONITORINFO :: ^MONITORINFO
|
||||
|
||||
// SetWindowsHook() codes
|
||||
WH_MIN :: -1
|
||||
WH_MSGFILTER :: -1
|
||||
@@ -1462,6 +1670,24 @@ IDI_WARNING := IDI_EXCLAMATION
|
||||
IDI_ERROR := IDI_HAND
|
||||
IDI_INFORMATION := IDI_ASTERISK
|
||||
|
||||
IMAGE_BITMAP :: 0
|
||||
IMAGE_ICON :: 1
|
||||
IMAGE_CURSOR :: 2
|
||||
IMAGE_ENHMETAFILE :: 3
|
||||
|
||||
LR_DEFAULTCOLOR :: 0x00000000
|
||||
LR_MONOCHROME :: 0x00000001
|
||||
LR_COLOR :: 0x00000002
|
||||
LR_COPYRETURNORG :: 0x00000004
|
||||
LR_COPYDELETEORG :: 0x00000008
|
||||
LR_LOADFROMFILE :: 0x00000010
|
||||
LR_LOADTRANSPARENT :: 0x00000020
|
||||
LR_DEFAULTSIZE :: 0x00000040
|
||||
LR_VGACOLOR :: 0x00000080
|
||||
LR_LOADMAP3DCOLORS :: 0x00001000
|
||||
LR_CREATEDIBSECTION :: 0x00002000
|
||||
LR_COPYFROMRESOURCE :: 0x00004000
|
||||
LR_SHARED :: 0x00008000
|
||||
|
||||
// DIB color table identifiers
|
||||
DIB_RGB_COLORS :: 0
|
||||
@@ -1774,12 +2000,15 @@ WAIT_FAILED: DWORD : 0xFFFFFFFF
|
||||
|
||||
PIPE_ACCESS_INBOUND: DWORD : 0x00000001
|
||||
PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002
|
||||
PIPE_ACCESS_DUPLEX: DWORD : 0x00000003
|
||||
FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
|
||||
FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
|
||||
PIPE_WAIT: DWORD : 0x00000000
|
||||
PIPE_TYPE_BYTE: DWORD : 0x00000000
|
||||
PIPE_TYPE_MESSAGE: DWORD : 0x00000004
|
||||
PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
|
||||
PIPE_READMODE_BYTE: DWORD : 0x00000000
|
||||
PIPE_READMODE_MESSAGE: DWORD : 0x00000002
|
||||
PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000
|
||||
|
||||
FD_SETSIZE :: 64
|
||||
@@ -1793,7 +2022,58 @@ HEAP_ZERO_MEMORY: DWORD : 0x00000008
|
||||
HANDLE_FLAG_INHERIT: DWORD : 0x00000001
|
||||
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 0x00000002
|
||||
|
||||
TOKEN_READ: DWORD : 0x20008
|
||||
GENERIC_MAPPING :: struct {
|
||||
GenericRead: ACCESS_MASK,
|
||||
GenericWrite: ACCESS_MASK,
|
||||
GenericExecute: ACCESS_MASK,
|
||||
GenericAll: ACCESS_MASK,
|
||||
}
|
||||
PGENERIC_MAPPING :: ^GENERIC_MAPPING
|
||||
|
||||
SECURITY_IMPERSONATION_LEVEL :: enum {
|
||||
SecurityAnonymous,
|
||||
SecurityIdentification,
|
||||
SecurityImpersonation,
|
||||
SecurityDelegation,
|
||||
}
|
||||
|
||||
SECURITY_INFORMATION :: DWORD
|
||||
ANYSIZE_ARRAY :: 1
|
||||
|
||||
LUID_AND_ATTRIBUTES :: struct {
|
||||
Luid: LUID,
|
||||
Attributes: DWORD,
|
||||
}
|
||||
|
||||
PRIVILEGE_SET :: struct {
|
||||
PrivilegeCount: DWORD,
|
||||
Control: DWORD,
|
||||
Privilege: [ANYSIZE_ARRAY]LUID_AND_ATTRIBUTES,
|
||||
}
|
||||
PPRIVILEGE_SET :: ^PRIVILEGE_SET
|
||||
|
||||
// Token Specific Access Rights.
|
||||
TOKEN_ASSIGN_PRIMARY :: 0x0001
|
||||
TOKEN_DUPLICATE :: 0x0002
|
||||
TOKEN_IMPERSONATE :: 0x0004
|
||||
TOKEN_QUERY :: 0x0008
|
||||
TOKEN_QUERY_SOURCE :: 0x0010
|
||||
TOKEN_ADJUST_PRIVILEGES :: 0x0020
|
||||
TOKEN_ADJUST_GROUPS :: 0x0040
|
||||
TOKEN_ADJUST_DEFAULT :: 0x0080
|
||||
TOKEN_ADJUST_SESSIONID :: 0x0100
|
||||
|
||||
TOKEN_ALL_ACCESS_P :: STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY |\
|
||||
TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
|
||||
|
||||
TOKEN_ALL_ACCESS :: TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID
|
||||
TOKEN_READ :: STANDARD_RIGHTS_READ | TOKEN_QUERY
|
||||
TOKEN_WRITE :: STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
|
||||
TOKEN_EXECUTE :: STANDARD_RIGHTS_EXECUTE
|
||||
TOKEN_TRUST_CONSTRAINT_MASK :: STANDARD_RIGHTS_READ | TOKEN_QUERY | TOKEN_QUERY_SOURCE
|
||||
TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 :: TOKEN_QUERY | TOKEN_QUERY_SOURCE
|
||||
TOKEN_ACCESS_PSEUDO_HANDLE :: TOKEN_ACCESS_PSEUDO_HANDLE_WIN8
|
||||
|
||||
|
||||
CP_ACP :: 0 // default to ANSI code page
|
||||
CP_OEMCP :: 1 // default to OEM code page
|
||||
@@ -2101,9 +2381,10 @@ FILETIME :: struct {
|
||||
|
||||
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
|
||||
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
|
||||
return (t - 0x019db1ded53e8000) * 100
|
||||
return (t - 116444736000000000) * 100
|
||||
}
|
||||
|
||||
|
||||
OVERLAPPED :: struct {
|
||||
Internal: ^c_ulong,
|
||||
InternalHigh: ^c_ulong,
|
||||
@@ -2777,6 +3058,16 @@ SYSTEMTIME :: struct {
|
||||
milliseconds: WORD,
|
||||
}
|
||||
|
||||
TIME_ZONE_INFORMATION :: struct {
|
||||
Bias: LONG,
|
||||
StandardName: [32]WCHAR,
|
||||
StandardDate: SYSTEMTIME,
|
||||
StandardBias: LONG,
|
||||
DaylightName: [32]WCHAR,
|
||||
DaylightDate: SYSTEMTIME,
|
||||
DaylightBias: LONG,
|
||||
}
|
||||
|
||||
|
||||
@(private="file")
|
||||
IMAGE_DOS_HEADER :: struct {
|
||||
@@ -3048,12 +3339,32 @@ SHCONTF_FLATLIST :: 0x4000
|
||||
SHCONTF_ENABLE_ASYNC :: 0x8000
|
||||
SHCONTF_INCLUDESUPERHIDDEN :: 0x10000
|
||||
|
||||
SHACF_DEFAULT :: 0x00000000 // Currently (SHACF_FILESYSTEM | SHACF_URLALL)
|
||||
SHACF_FILESYSTEM :: 0x00000001 // This includes the File System as well as the rest of the shell (Desktop\My Computer\Control Panel\)
|
||||
SHACF_URLALL :: (SHACF_URLHISTORY | SHACF_URLMRU)
|
||||
SHACF_URLHISTORY :: 0x00000002 // URLs in the User's History
|
||||
SHACF_URLMRU :: 0x00000004 // URLs in the User's Recently Used list.
|
||||
SHACF_USETAB :: 0x00000008 // Use the tab to move thru the autocomplete possibilities instead of to the next dialog/window control.
|
||||
SHACF_FILESYS_ONLY :: 0x00000010 // This includes the File System
|
||||
SHACF_FILESYS_DIRS :: 0x00000020 // Same as SHACF_FILESYS_ONLY except it only includes directories, UNC servers, and UNC server shares.
|
||||
SHACF_VIRTUAL_NAMESPACE :: 0x00000040 // Also include the virtual namespace
|
||||
SHACF_AUTOSUGGEST_FORCE_ON :: 0x10000000 // Ignore the registry default and force the feature on.
|
||||
SHACF_AUTOSUGGEST_FORCE_OFF :: 0x20000000 // Ignore the registry default and force the feature off.
|
||||
SHACF_AUTOAPPEND_FORCE_ON :: 0x40000000 // Ignore the registry default and force the feature on. (Also know as AutoComplete)
|
||||
SHACF_AUTOAPPEND_FORCE_OFF :: 0x80000000 // Ignore the registry default and force the feature off. (Also know as AutoComplete)
|
||||
|
||||
LWSTDAPI :: HRESULT
|
||||
|
||||
CLSID_FileOpenDialog := &GUID{0xDC1C5A9C, 0xE88A, 0x4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}}
|
||||
CLSID_FileSaveDialog := &GUID{0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}}
|
||||
CLSID_TaskbarList := &GUID{0x56FDF344, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
|
||||
|
||||
IID_IFileDialog := &GUID{0x42F85136, 0xDB7E, 0x439C, {0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8}}
|
||||
IID_IFileSaveDialog := &GUID{0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB}}
|
||||
IID_IFileOpenDialog := &GUID{0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60}}
|
||||
IID_ITaskbarList := &GUID{0x56FDF342, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
|
||||
IID_ITaskbarList2 := &GUID{0x602D4995, 0xB13A, 0x429b, {0xA6, 0x6E, 0x19, 0x35, 0xE4, 0x4F, 0x43, 0x17}}
|
||||
IID_ITaskbarList3 := &GUID{0xea1afb91, 0x9e28, 0x4b86, {0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf}}
|
||||
|
||||
IModalWindow :: struct #raw_union {
|
||||
#subtype IUnknown: IUnknown,
|
||||
@@ -3358,6 +3669,84 @@ IFileSaveDialogVtbl :: struct {
|
||||
ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
|
||||
}
|
||||
|
||||
ITaskbarList :: struct #raw_union {
|
||||
#subtype IUnknown: IUnknown,
|
||||
using Vtbl: ^ITaskbarListVtbl,
|
||||
}
|
||||
ITaskbarListVtbl :: struct {
|
||||
using IUnknownVtbl: IUnknownVtbl,
|
||||
HrInit: proc "stdcall" (this: ^ITaskbarList) -> HRESULT,
|
||||
AddTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
DeleteTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
ActivateTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
SetActiveAlt: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
}
|
||||
|
||||
ITaskbarList2 :: struct #raw_union {
|
||||
#subtype ITaskbarList: ITaskbarList,
|
||||
using Vtbl: ^ITaskbarList2Vtbl,
|
||||
}
|
||||
ITaskbarList2Vtbl :: struct {
|
||||
using ITaskbarListVtbl: ITaskbarListVtbl,
|
||||
MarkFullscreenWindow: proc "stdcall" (this: ^ITaskbarList2, hwnd: HWND, fFullscreen: BOOL) -> HRESULT,
|
||||
}
|
||||
|
||||
TBPFLAG :: enum c_int {
|
||||
NOPROGRESS = 0,
|
||||
INDETERMINATE = 0x1,
|
||||
NORMAL = 0x2,
|
||||
ERROR = 0x4,
|
||||
PAUSED = 0x8,
|
||||
}
|
||||
|
||||
THUMBBUTTONFLAGS :: enum c_int {
|
||||
ENABLED = 0,
|
||||
DISABLED = 0x1,
|
||||
DISMISSONCLICK = 0x2,
|
||||
NOBACKGROUND = 0x4,
|
||||
HIDDEN = 0x8,
|
||||
NONINTERACTIVE = 0x10,
|
||||
}
|
||||
|
||||
THUMBBUTTONMASK :: enum c_int {
|
||||
BITMAP = 0x1,
|
||||
ICON = 0x2,
|
||||
TOOLTIP = 0x4,
|
||||
FLAGS = 0x8,
|
||||
}
|
||||
|
||||
THUMBBUTTON :: struct {
|
||||
dwMask: THUMBBUTTONMASK,
|
||||
iId: UINT,
|
||||
iBitmap: UINT,
|
||||
hIcon: HICON,
|
||||
szTip: [260]WCHAR,
|
||||
dwFlags: THUMBBUTTONFLAGS,
|
||||
}
|
||||
LPTHUMBBUTTON :: ^THUMBBUTTON
|
||||
|
||||
HIMAGELIST :: ^IUnknown
|
||||
|
||||
ITaskbarList3 :: struct #raw_union {
|
||||
#subtype ITaskbarList2: ITaskbarList2,
|
||||
using Vtbl: ^ITaskbarList3Vtbl,
|
||||
}
|
||||
ITaskbarList3Vtbl :: struct {
|
||||
using ITaskbarList2Vtbl: ITaskbarList2Vtbl,
|
||||
SetProgressValue: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, ullCompleted: ULONGLONG, ullTotal: ULONGLONG) -> HRESULT,
|
||||
SetProgressState: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, tbpFlags: TBPFLAG) -> HRESULT,
|
||||
RegisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND) -> HRESULT,
|
||||
UnregisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND) -> HRESULT,
|
||||
SetTabOrder: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndInsertBefore: HWND) -> HRESULT,
|
||||
SetTabActive: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND, dwReserved: DWORD) -> HRESULT,
|
||||
ThumbBarAddButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
|
||||
ThumbBarUpdateButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
|
||||
ThumbBarSetImageList: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, himl: HIMAGELIST) -> HRESULT,
|
||||
SetOverlayIcon: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, hIcon: HICON, pszDescription: LPCWSTR) -> HRESULT,
|
||||
SetThumbnailTooltip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, pszTip: LPCWSTR) -> HRESULT,
|
||||
SetThumbnailClip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, prcClip: ^RECT) -> HRESULT,
|
||||
}
|
||||
|
||||
MEMORYSTATUSEX :: struct {
|
||||
dwLength: DWORD,
|
||||
dwMemoryLoad: DWORD,
|
||||
|
||||
@@ -78,6 +78,7 @@ foreign user32 {
|
||||
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
|
||||
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
|
||||
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
|
||||
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE ---
|
||||
|
||||
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
|
||||
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
|
||||
@@ -99,6 +100,7 @@ foreign user32 {
|
||||
AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL ---
|
||||
|
||||
SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL ---
|
||||
GetMonitorInfoW :: proc(hMonitor: HMONITOR, lpmi: LPMONITORINFO) -> BOOL ---
|
||||
|
||||
GetWindowDC :: proc(hWnd: HWND) -> HDC ---
|
||||
GetDC :: proc(hWnd: HWND) -> HDC ---
|
||||
@@ -121,6 +123,8 @@ foreign user32 {
|
||||
|
||||
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
|
||||
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
|
||||
|
||||
GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
|
||||
|
||||
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
|
||||
|
||||
@@ -201,6 +205,17 @@ foreign user32 {
|
||||
GetRawInputDeviceList :: proc(pRawInputDeviceList: PRAWINPUTDEVICELIST, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
|
||||
GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
|
||||
RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
|
||||
|
||||
SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL ---
|
||||
|
||||
FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int ---
|
||||
EqualRect :: proc(lprc1: ^RECT, lprc2: ^RECT) -> BOOL ---
|
||||
|
||||
GetWindowInfo :: proc(hwnd: HWND, pwi: PWINDOWINFO) -> BOOL ---
|
||||
GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
|
||||
SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
|
||||
CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
|
||||
GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
|
||||
}
|
||||
|
||||
CreateWindowW :: #force_inline proc "stdcall" (
|
||||
@@ -431,3 +446,27 @@ RI_MOUSE_BUTTON_5_DOWN :: 0x0100
|
||||
RI_MOUSE_BUTTON_5_UP :: 0x0200
|
||||
RI_MOUSE_WHEEL :: 0x0400
|
||||
RI_MOUSE_HWHEEL :: 0x0800
|
||||
|
||||
WINDOWPLACEMENT :: struct {
|
||||
length: UINT,
|
||||
flags: UINT,
|
||||
showCmd: UINT,
|
||||
ptMinPosition: POINT,
|
||||
ptMaxPosition: POINT,
|
||||
rcNormalPosition: RECT,
|
||||
rcDevice: RECT,
|
||||
}
|
||||
|
||||
WINDOWINFO :: struct {
|
||||
cbSize: DWORD,
|
||||
rcWindow: RECT,
|
||||
rcClient: RECT,
|
||||
dwStyle: DWORD,
|
||||
dwExStyle: DWORD,
|
||||
dwWindowStatus: DWORD,
|
||||
cxWindowBorders: UINT,
|
||||
cyWindowBorders: UINT,
|
||||
atomWindowType: ATOM,
|
||||
wCreatorVersion: WORD,
|
||||
}
|
||||
PWINDOWINFO :: ^WINDOWINFO
|
||||
@@ -62,19 +62,19 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
|
||||
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if N <= 0 {
|
||||
if N == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
|
||||
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N) if N > 0 else -1, nil, 0, nil, nil)
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
|
||||
// If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
|
||||
// and will scan it to find the first null terminated character. The resulting string will
|
||||
// also be null terminated.
|
||||
// If N != -1 it assumes the wide string is not null terminated and the resulting string
|
||||
// If N > 0 it assumes the wide string is not null terminated and the resulting string
|
||||
// will not be null terminated.
|
||||
text := make([]byte, n) or_return
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
// +build windows
|
||||
package sys_windows
|
||||
|
||||
foreign import uxtheme "system:UxTheme.lib"
|
||||
|
||||
MARGINS :: distinct [4]int
|
||||
PMARGINS :: ^MARGINS
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign uxtheme {
|
||||
IsThemeActive :: proc() -> BOOL ---
|
||||
}
|
||||
@@ -85,3 +85,15 @@ foreign Opengl32 {
|
||||
wglUseFontBitmaps :: proc(hdc: HDC, first, count, list_base: DWORD) -> BOOL ---
|
||||
wglUseFontOutlines :: proc(hdc: HDC, first, count, list_base: DWORD, deviation, extrusion: f32, format: c.int, gmf: LPGLYPHMETRICSFLOAT) -> BOOL ---
|
||||
}
|
||||
|
||||
// Used by vendor:OpenGL
|
||||
// https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions#Windows
|
||||
gl_set_proc_address :: proc(p: rawptr, name: cstring) {
|
||||
func := wglGetProcAddress(name)
|
||||
switch uintptr(func) {
|
||||
case 0, 1, 2, 3, ~uintptr(0):
|
||||
module := LoadLibraryW(L("opengl32.dll"))
|
||||
func = GetProcAddress(module, name)
|
||||
}
|
||||
(^rawptr)(p)^ = func
|
||||
}
|
||||
|
||||
@@ -454,6 +454,7 @@ TB_ISBUTTONENABLED :: 0x0409
|
||||
TBM_CLEARTICS :: 0x0409
|
||||
TTM_SETTOOLINFOA :: 0x0409
|
||||
CBEM_HASEDITCHANGED :: 0x040a
|
||||
PBM_SETMARQUEE :: 0x040a
|
||||
RB_INSERTBANDW :: 0x040a
|
||||
SB_GETRECT :: 0x040a
|
||||
TB_ISBUTTONCHECKED :: 0x040a
|
||||
@@ -488,10 +489,12 @@ TTM_ENUMTOOLSA :: 0x040e
|
||||
SB_SETICON :: 0x040f
|
||||
TBM_GETTICPOS :: 0x040f
|
||||
TTM_GETCURRENTTOOLA :: 0x040f
|
||||
PBM_SETSTATE :: 0x0410
|
||||
RB_IDTOINDEX :: 0x0410
|
||||
SB_SETTIPTEXTA :: 0x0410
|
||||
TBM_GETNUMTICS :: 0x0410
|
||||
TTM_WINDOWFROMPOINT :: 0x0410
|
||||
PBM_GETSTATE :: 0x0411
|
||||
RB_GETTOOLTIPS :: 0x0411
|
||||
SB_SETTIPTEXTW :: 0x0411
|
||||
TBM_GETSELSTART :: 0x0411
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
package text_edit
|
||||
|
||||
/*
|
||||
Based off the articles by rxi:
|
||||
* https://rxi.github.io/textbox_behaviour.html
|
||||
* https://rxi.github.io/a_simple_undo_system.html
|
||||
*/
|
||||
|
||||
import "core:runtime"
|
||||
import "core:time"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
DEFAULT_UNDO_TIMEOUT :: 300 * time.Millisecond
|
||||
|
||||
State :: struct {
|
||||
selection: [2]int,
|
||||
line_start, line_end: int,
|
||||
|
||||
// initialized each "frame" with `begin`
|
||||
builder: ^strings.Builder, // let the caller store the text buffer data
|
||||
|
||||
up_index, down_index: int, // multi-lines
|
||||
|
||||
|
||||
// undo
|
||||
undo: [dynamic]^Undo_State,
|
||||
redo: [dynamic]^Undo_State,
|
||||
undo_text_allocator: runtime.Allocator,
|
||||
|
||||
id: u64, // useful for immediate mode GUIs
|
||||
|
||||
// Timeout information
|
||||
current_time: time.Tick,
|
||||
last_edit_time: time.Tick,
|
||||
undo_timeout: time.Duration,
|
||||
|
||||
// Set these if you want cut/copy/paste functionality
|
||||
set_clipboard: proc(user_data: rawptr, text: string) -> (ok: bool),
|
||||
get_clipboard: proc(user_data: rawptr) -> (text: string, ok: bool),
|
||||
clipboard_user_data: rawptr,
|
||||
}
|
||||
|
||||
Undo_State :: struct {
|
||||
selection: [2]int,
|
||||
len: int,
|
||||
text: [0]byte, // string(us.text[:us.len]) --- requiring #no_bounds_check
|
||||
}
|
||||
|
||||
Translation :: enum u32 {
|
||||
Start,
|
||||
End,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Word_Left,
|
||||
Word_Right,
|
||||
Word_Start,
|
||||
Word_End,
|
||||
Soft_Line_Start,
|
||||
Soft_Line_End,
|
||||
}
|
||||
|
||||
|
||||
init :: proc(s: ^State, undo_text_allocator, undo_state_allocator: runtime.Allocator, undo_timeout := DEFAULT_UNDO_TIMEOUT) {
|
||||
s.undo_timeout = undo_timeout
|
||||
|
||||
// Used for allocating `Undo_State`
|
||||
s.undo_text_allocator = undo_text_allocator
|
||||
|
||||
s.undo.allocator = undo_state_allocator
|
||||
s.redo.allocator = undo_state_allocator
|
||||
}
|
||||
|
||||
destroy :: proc(s: ^State) {
|
||||
undo_clear(s, &s.undo)
|
||||
undo_clear(s, &s.redo)
|
||||
delete(s.undo)
|
||||
delete(s.redo)
|
||||
s.builder = nil
|
||||
}
|
||||
|
||||
|
||||
// Call at the beginning of each frame
|
||||
begin :: proc(s: ^State, id: u64, builder: ^strings.Builder) {
|
||||
assert(builder != nil)
|
||||
if s.id != 0 {
|
||||
end(s)
|
||||
}
|
||||
s.id = id
|
||||
s.selection = {len(builder.buf), 0}
|
||||
s.builder = builder
|
||||
s.current_time = time.tick_now()
|
||||
if s.undo_timeout <= 0 {
|
||||
s.undo_timeout = DEFAULT_UNDO_TIMEOUT
|
||||
}
|
||||
set_text(s, string(s.builder.buf[:]))
|
||||
undo_clear(s, &s.undo)
|
||||
undo_clear(s, &s.redo)
|
||||
}
|
||||
|
||||
// Call at the end of each frame
|
||||
end :: proc(s: ^State) {
|
||||
s.id = 0
|
||||
s.builder = nil
|
||||
}
|
||||
|
||||
set_text :: proc(s: ^State, text: string) {
|
||||
strings.builder_reset(s.builder)
|
||||
strings.write_string(s.builder, text)
|
||||
}
|
||||
|
||||
|
||||
undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
|
||||
text := string(s.builder.buf[:])
|
||||
item := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator))
|
||||
item.selection = s.selection
|
||||
item.len = len(text)
|
||||
#no_bounds_check {
|
||||
runtime.copy(item.text[:len(text)], text)
|
||||
}
|
||||
append(undo, item)
|
||||
}
|
||||
|
||||
undo :: proc(s: ^State, undo, redo: ^[dynamic]^Undo_State) {
|
||||
if len(undo) > 0 {
|
||||
undo_state_push(s, redo)
|
||||
item := pop(undo)
|
||||
s.selection = item.selection
|
||||
#no_bounds_check {
|
||||
set_text(s, string(item.text[:item.len]))
|
||||
}
|
||||
free(item, s.undo_text_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
undo_clear :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
|
||||
for len(undo) > 0 {
|
||||
item := pop(undo)
|
||||
free(item, s.undo_text_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
undo_check :: proc(s: ^State) {
|
||||
undo_clear(s, &s.redo)
|
||||
if time.tick_diff(s.last_edit_time, s.current_time) > s.undo_timeout {
|
||||
undo_state_push(s, &s.undo)
|
||||
}
|
||||
s.last_edit_time = s.current_time
|
||||
}
|
||||
|
||||
|
||||
|
||||
input_text :: proc(s: ^State, text: string) {
|
||||
if len(text) == 0 {
|
||||
return
|
||||
}
|
||||
if has_selection(s) {
|
||||
selection_delete(s)
|
||||
}
|
||||
insert(s, s.selection[0], text)
|
||||
offset := s.selection[0] + len(text)
|
||||
s.selection = {offset, offset}
|
||||
}
|
||||
|
||||
input_runes :: proc(s: ^State, text: []rune) {
|
||||
if len(text) == 0 {
|
||||
return
|
||||
}
|
||||
if has_selection(s) {
|
||||
selection_delete(s)
|
||||
}
|
||||
offset := s.selection[0]
|
||||
for r in text {
|
||||
b, w := utf8.encode_rune(r)
|
||||
insert(s, offset, string(b[:w]))
|
||||
offset += w
|
||||
}
|
||||
s.selection = {offset, offset}
|
||||
}
|
||||
|
||||
|
||||
insert :: proc(s: ^State, at: int, text: string) {
|
||||
undo_check(s)
|
||||
inject_at(&s.builder.buf, at, text)
|
||||
}
|
||||
|
||||
remove :: proc(s: ^State, lo, hi: int) {
|
||||
undo_check(s)
|
||||
remove_range(&s.builder.buf, lo, hi)
|
||||
}
|
||||
|
||||
|
||||
|
||||
has_selection :: proc(s: ^State) -> bool {
|
||||
return s.selection[0] != s.selection[1]
|
||||
}
|
||||
|
||||
sorted_selection :: proc(s: ^State) -> (lo, hi: int) {
|
||||
lo = min(s.selection[0], s.selection[1])
|
||||
hi = max(s.selection[0], s.selection[1])
|
||||
lo = clamp(lo, 0, len(s.builder.buf))
|
||||
hi = clamp(hi, 0, len(s.builder.buf))
|
||||
s.selection[0] = lo
|
||||
s.selection[1] = hi
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
selection_delete :: proc(s: ^State) {
|
||||
lo, hi := sorted_selection(s)
|
||||
remove(s, lo, hi)
|
||||
s.selection = {lo, lo}
|
||||
}
|
||||
|
||||
|
||||
|
||||
translate_position :: proc(s: ^State, pos: int, t: Translation) -> int {
|
||||
is_continuation_byte :: proc(b: byte) -> bool {
|
||||
return b <= 0x80 && b < 0xc0
|
||||
}
|
||||
is_space :: proc(b: byte) -> bool {
|
||||
return b == ' ' || b == '\t' || b == '\n'
|
||||
}
|
||||
|
||||
buf := s.builder.buf[:]
|
||||
|
||||
pos := pos
|
||||
pos = clamp(pos, 0, len(buf))
|
||||
|
||||
switch t {
|
||||
case .Start:
|
||||
pos = 0
|
||||
case .End:
|
||||
pos = len(buf)
|
||||
case .Left:
|
||||
pos -= 1
|
||||
for pos >= 0 && is_continuation_byte(buf[pos]) {
|
||||
pos -= 1
|
||||
}
|
||||
case .Right:
|
||||
pos += 1
|
||||
for pos < len(buf) && is_continuation_byte(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
case .Up:
|
||||
pos = s.up_index
|
||||
case .Down:
|
||||
pos = s.down_index
|
||||
case .Word_Left:
|
||||
for pos > 0 && is_space(buf[pos-1]) {
|
||||
pos -= 1
|
||||
}
|
||||
for pos > 0 && !is_space(buf[pos-1]) {
|
||||
pos -= 1
|
||||
}
|
||||
case .Word_Right:
|
||||
for pos < len(buf) && !is_space(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
for pos < len(buf) && is_space(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
case .Word_Start:
|
||||
for pos > 0 && !is_space(buf[pos-1]) {
|
||||
pos -= 1
|
||||
}
|
||||
case .Word_End:
|
||||
for pos < len(buf) && !is_space(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
case .Soft_Line_Start:
|
||||
pos = s.line_start
|
||||
case .Soft_Line_End:
|
||||
pos = s.line_end
|
||||
}
|
||||
return clamp(pos, 0, len(buf))
|
||||
}
|
||||
|
||||
move_to :: proc(s: ^State, t: Translation) {
|
||||
if t == .Left && has_selection(s) {
|
||||
lo, _ := sorted_selection(s)
|
||||
s.selection = {lo, lo}
|
||||
} else if t == .Right && has_selection(s) {
|
||||
_, hi := sorted_selection(s)
|
||||
s.selection = {hi, hi}
|
||||
} else {
|
||||
pos := translate_position(s, s.selection[0], t)
|
||||
s.selection = {pos, pos}
|
||||
}
|
||||
}
|
||||
select_to :: proc(s: ^State, t: Translation) {
|
||||
s.selection[0] = translate_position(s, s.selection[0], t)
|
||||
}
|
||||
delete_to :: proc(s: ^State, t: Translation) {
|
||||
if has_selection(s) {
|
||||
selection_delete(s)
|
||||
} else {
|
||||
lo := s.selection[0]
|
||||
hi := translate_position(s, lo, t)
|
||||
lo, hi = min(lo, hi), max(lo, hi)
|
||||
remove(s, lo, hi)
|
||||
s.selection = {lo, lo}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_selected_text :: proc(s: ^State) -> string {
|
||||
lo, hi := sorted_selection(s)
|
||||
return string(s.builder.buf[lo:hi])
|
||||
}
|
||||
|
||||
|
||||
cut :: proc(s: ^State) -> bool {
|
||||
if copy(s) {
|
||||
selection_delete(s)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
copy :: proc(s: ^State) -> bool {
|
||||
if s.set_clipboard != nil {
|
||||
return s.set_clipboard(s.clipboard_user_data, current_selected_text(s))
|
||||
}
|
||||
return s.set_clipboard != nil
|
||||
}
|
||||
|
||||
paste :: proc(s: ^State) -> bool {
|
||||
if s.get_clipboard != nil {
|
||||
input_text(s, s.get_clipboard(s.clipboard_user_data) or_return)
|
||||
}
|
||||
return s.get_clipboard != nil
|
||||
}
|
||||
|
||||
|
||||
Command_Set :: distinct bit_set[Command; u32]
|
||||
|
||||
Command :: enum u32 {
|
||||
None,
|
||||
Undo,
|
||||
Redo,
|
||||
New_Line, // multi-lines
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Select_All,
|
||||
Backspace,
|
||||
Delete,
|
||||
Delete_Word_Left,
|
||||
Delete_Word_Right,
|
||||
Left,
|
||||
Right,
|
||||
Up, // multi-lines
|
||||
Down, // multi-lines
|
||||
Word_Left,
|
||||
Word_Right,
|
||||
Start,
|
||||
End,
|
||||
Line_Start,
|
||||
Line_End,
|
||||
Select_Left,
|
||||
Select_Right,
|
||||
Select_Up, // multi-lines
|
||||
Select_Down, // multi-lines
|
||||
Select_Word_Left,
|
||||
Select_Word_Right,
|
||||
Select_Start,
|
||||
Select_End,
|
||||
Select_Line_Start,
|
||||
Select_Line_End,
|
||||
}
|
||||
|
||||
MULTILINE_COMMANDS :: Command_Set{.New_Line, .Up, .Down, .Select_Up, .Select_Down}
|
||||
|
||||
perform_command :: proc(s: ^State, cmd: Command) {
|
||||
switch cmd {
|
||||
case .None: /**/
|
||||
case .Undo: undo(s, &s.undo, &s.redo)
|
||||
case .Redo: undo(s, &s.redo, &s.undo)
|
||||
case .New_Line: input_text(s, "\n")
|
||||
case .Cut: cut(s)
|
||||
case .Copy: copy(s)
|
||||
case .Paste: paste(s)
|
||||
case .Select_All: s.selection = {len(s.builder.buf), 0}
|
||||
case .Backspace: delete_to(s, .Left)
|
||||
case .Delete: delete_to(s, .Right)
|
||||
case .Delete_Word_Left: delete_to(s, .Word_Left)
|
||||
case .Delete_Word_Right: delete_to(s, .Word_Right)
|
||||
case .Left: move_to(s, .Left)
|
||||
case .Right: move_to(s, .Right)
|
||||
case .Up: move_to(s, .Up)
|
||||
case .Down: move_to(s, .Down)
|
||||
case .Word_Left: move_to(s, .Word_Left)
|
||||
case .Word_Right: move_to(s, .Word_Right)
|
||||
case .Start: move_to(s, .Start)
|
||||
case .End: move_to(s, .End)
|
||||
case .Line_Start: move_to(s, .Soft_Line_Start)
|
||||
case .Line_End: move_to(s, .Soft_Line_End)
|
||||
case .Select_Left: select_to(s, .Left)
|
||||
case .Select_Right: select_to(s, .Right)
|
||||
case .Select_Up: select_to(s, .Up)
|
||||
case .Select_Down: select_to(s, .Down)
|
||||
case .Select_Word_Left: select_to(s, .Word_Left)
|
||||
case .Select_Word_Right: select_to(s, .Word_Right)
|
||||
case .Select_Start: select_to(s, .Start)
|
||||
case .Select_End: select_to(s, .End)
|
||||
case .Select_Line_Start: select_to(s, .Soft_Line_Start)
|
||||
case .Select_Line_End: select_to(s, .Soft_Line_End)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package i18n
|
||||
|
||||
/*
|
||||
|
||||
@@ -0,0 +1,959 @@
|
||||
package text_match
|
||||
|
||||
import "core:runtime"
|
||||
import "core:unicode"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strings"
|
||||
|
||||
MAX_CAPTURES :: 32
|
||||
|
||||
Capture :: struct {
|
||||
init: int,
|
||||
len: int,
|
||||
}
|
||||
|
||||
Match :: struct {
|
||||
byte_start, byte_end: int,
|
||||
}
|
||||
|
||||
Error :: enum {
|
||||
OK,
|
||||
OOB,
|
||||
Invalid_Capture_Index,
|
||||
Invalid_Pattern_Capture,
|
||||
Unfinished_Capture,
|
||||
Malformed_Pattern,
|
||||
Rune_Error,
|
||||
Match_Invalid,
|
||||
}
|
||||
|
||||
L_ESC :: '%'
|
||||
CAP_POSITION :: -2
|
||||
CAP_UNFINISHED :: -1
|
||||
INVALID :: -1
|
||||
|
||||
Match_State :: struct {
|
||||
src: string,
|
||||
pattern: string,
|
||||
level: int,
|
||||
capture: [MAX_CAPTURES]Capture,
|
||||
}
|
||||
|
||||
match_class :: proc(c: rune, cl: rune) -> (res: bool) {
|
||||
switch unicode.to_lower(cl) {
|
||||
case 'a': res = is_alpha(c)
|
||||
case 'c': res = is_cntrl(c)
|
||||
case 'd': res = is_digit(c)
|
||||
case 'g': res = is_graph(c)
|
||||
case 'l': res = is_lower(c)
|
||||
case 'p': res = is_punct(c)
|
||||
case 's': res = is_space(c)
|
||||
case 'u': res = is_upper(c)
|
||||
case 'w': res = is_alnum(c)
|
||||
case 'x': res = is_xdigit(c)
|
||||
case: return cl == c
|
||||
}
|
||||
|
||||
return is_lower(cl) ? res : !res
|
||||
}
|
||||
|
||||
is_alpha :: unicode.is_alpha
|
||||
is_digit :: unicode.is_digit
|
||||
is_lower :: unicode.is_lower
|
||||
is_upper :: unicode.is_upper
|
||||
is_punct :: unicode.is_punct
|
||||
is_space :: unicode.is_space
|
||||
is_cntrl :: unicode.is_control
|
||||
|
||||
is_alnum :: proc(c: rune) -> bool {
|
||||
return unicode.is_alpha(c) || unicode.is_digit(c)
|
||||
}
|
||||
|
||||
is_graph :: proc(c: rune) -> bool {
|
||||
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
||||
}
|
||||
|
||||
is_xdigit :: proc(c: rune) -> bool {
|
||||
return (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || unicode.is_digit(c)
|
||||
}
|
||||
|
||||
// find the first utf8 charater and its size, return an error if the character is an error
|
||||
utf8_peek :: proc(bytes: string) -> (c: rune, size: int, err: Error) {
|
||||
c, size = utf8.decode_rune_in_string(bytes)
|
||||
|
||||
if c == utf8.RUNE_ERROR {
|
||||
err = .Rune_Error
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// find the first utf8 charater and its size and advance the index
|
||||
// return an error if the character is an error
|
||||
utf8_advance :: proc(bytes: string, index: ^int) -> (c: rune, err: Error) {
|
||||
size: int
|
||||
c, size = utf8.decode_rune_in_string(bytes[index^:])
|
||||
|
||||
if c == utf8.RUNE_ERROR {
|
||||
err = .Rune_Error
|
||||
}
|
||||
|
||||
index^ += size
|
||||
return
|
||||
}
|
||||
|
||||
// continuation byte?
|
||||
is_cont :: proc(b: byte) -> bool {
|
||||
return b & 0xc0 == 0x80
|
||||
}
|
||||
|
||||
utf8_prev :: proc(bytes: string, a, b: int) -> int {
|
||||
b := b
|
||||
|
||||
for a < b && is_cont(bytes[b - 1]) {
|
||||
b -= 1
|
||||
}
|
||||
|
||||
return a < b ? b - 1 : a
|
||||
}
|
||||
|
||||
utf8_next :: proc(bytes: string, a: int) -> int {
|
||||
a := a
|
||||
b := len(bytes)
|
||||
|
||||
for a < b - 1 && is_cont(bytes[a + 1]) {
|
||||
a += 1
|
||||
}
|
||||
|
||||
return a < b ? a + 1 : b
|
||||
}
|
||||
|
||||
check_capture :: proc(ms: ^Match_State, l: rune) -> (int, Error) {
|
||||
l := int(l - '1')
|
||||
|
||||
if l < 0 || l >= ms.level || ms.capture[l].len == CAP_UNFINISHED {
|
||||
return 0, .Invalid_Capture_Index
|
||||
}
|
||||
|
||||
return l, .OK
|
||||
}
|
||||
|
||||
capture_to_close :: proc(ms: ^Match_State) -> (int, Error) {
|
||||
level := ms.level - 1
|
||||
|
||||
for level >= 0 {
|
||||
if ms.capture[level].len == CAP_UNFINISHED {
|
||||
return level, .OK
|
||||
}
|
||||
|
||||
level -= 1
|
||||
}
|
||||
|
||||
return 0, .Invalid_Pattern_Capture
|
||||
}
|
||||
|
||||
class_end :: proc(ms: ^Match_State, p: int) -> (step: int, err: Error) {
|
||||
step = p
|
||||
ch := utf8_advance(ms.pattern, &step) or_return
|
||||
|
||||
switch ch {
|
||||
case L_ESC:
|
||||
if step == len(ms.pattern) {
|
||||
err = .Malformed_Pattern
|
||||
return
|
||||
}
|
||||
|
||||
utf8_advance(ms.pattern, &step) or_return
|
||||
|
||||
case '[':
|
||||
// fine with step by 1
|
||||
if step + 1 < len(ms.pattern) && ms.pattern[step] == '^' {
|
||||
step += 1
|
||||
}
|
||||
|
||||
// run till end is reached
|
||||
for {
|
||||
if step == len(ms.pattern) {
|
||||
err = .Malformed_Pattern
|
||||
return
|
||||
}
|
||||
|
||||
if ms.pattern[step] == ']' {
|
||||
break
|
||||
}
|
||||
|
||||
// dont care about utf8 here
|
||||
step += 1
|
||||
|
||||
if step < len(ms.pattern) && ms.pattern[step] == L_ESC {
|
||||
// skip escapes like '%'
|
||||
step += 1
|
||||
}
|
||||
}
|
||||
|
||||
// advance last time
|
||||
step += 1
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
match_bracket_class :: proc(ms: ^Match_State, c: rune, p, ec: int) -> (sig: bool, err: Error) {
|
||||
sig = true
|
||||
p := p
|
||||
|
||||
if ms.pattern[p + 1] == '^' {
|
||||
p += 1
|
||||
sig = false
|
||||
}
|
||||
|
||||
// while inside of class range
|
||||
for p < ec {
|
||||
char := utf8_advance(ms.pattern, &p) or_return
|
||||
|
||||
// e.g. %a
|
||||
if char == L_ESC {
|
||||
next := utf8_advance(ms.pattern, &p) or_return
|
||||
|
||||
if match_class(c, next) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
next, next_size := utf8_peek(ms.pattern[p:]) or_return
|
||||
|
||||
// TODO test case for [a-???] where ??? is missing
|
||||
if next == '-' && p + next_size < len(ms.pattern) {
|
||||
// advance 2 codepoints
|
||||
p += next_size
|
||||
last := utf8_advance(ms.pattern, &p) or_return
|
||||
|
||||
if char <= c && c <= last {
|
||||
return
|
||||
}
|
||||
} else if char == c {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sig = !sig
|
||||
return
|
||||
}
|
||||
|
||||
single_match :: proc(ms: ^Match_State, s, p, ep: int) -> (matched: bool, schar_size: int, err: Error) {
|
||||
if s >= len(ms.src) {
|
||||
return
|
||||
}
|
||||
|
||||
pchar, psize := utf8_peek(ms.pattern[p:]) or_return
|
||||
schar, ssize := utf8_peek(ms.src[s:]) or_return
|
||||
schar_size = ssize
|
||||
|
||||
switch pchar {
|
||||
case '.': matched = true
|
||||
case L_ESC:
|
||||
pchar_next, _ := utf8_peek(ms.pattern[p + psize:]) or_return
|
||||
matched = match_class(schar, pchar_next)
|
||||
case '[': matched = match_bracket_class(ms, schar, p, ep - 1) or_return
|
||||
case: matched = schar == pchar
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
if p >= len(ms.pattern) - 1 {
|
||||
return INVALID, .Invalid_Pattern_Capture
|
||||
}
|
||||
|
||||
schar, ssize := utf8_peek(ms.src[s:]) or_return
|
||||
pchar, psize := utf8_peek(ms.pattern[p:]) or_return
|
||||
|
||||
// skip until the src and pattern match
|
||||
if schar != pchar {
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
s_begin := s
|
||||
cont := 1
|
||||
s := s + ssize
|
||||
begin := pchar
|
||||
end, _ := utf8_peek(ms.pattern[p + psize:]) or_return
|
||||
|
||||
for s < len(ms.src) {
|
||||
ch := utf8_advance(ms.src, &s) or_return
|
||||
|
||||
switch ch{
|
||||
case end:
|
||||
cont -= 1
|
||||
|
||||
if cont == 0 {
|
||||
return s, .OK
|
||||
}
|
||||
|
||||
case begin:
|
||||
cont += 1
|
||||
}
|
||||
}
|
||||
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
max_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
m := s
|
||||
|
||||
// count up matches
|
||||
for {
|
||||
matched, size := single_match(ms, m, p, ep) or_return
|
||||
|
||||
if !matched {
|
||||
break
|
||||
}
|
||||
|
||||
m += size
|
||||
}
|
||||
|
||||
for s <= m {
|
||||
result := match(ms, m, ep + 1) or_return
|
||||
|
||||
if result != INVALID {
|
||||
return result, .OK
|
||||
}
|
||||
|
||||
if s == m {
|
||||
break
|
||||
}
|
||||
|
||||
m = utf8_prev(ms.src, s, m)
|
||||
}
|
||||
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
min_expand :: proc(ms: ^Match_State, s, p, ep: int) -> (res: int, err: Error) {
|
||||
s := s
|
||||
|
||||
for {
|
||||
result := match(ms, s, ep + 1) or_return
|
||||
|
||||
if result != INVALID {
|
||||
return result, .OK
|
||||
} else {
|
||||
// TODO receive next step maybe?
|
||||
matched, rune_size := single_match(ms, s, p, ep) or_return
|
||||
|
||||
if matched {
|
||||
s += rune_size
|
||||
} else {
|
||||
return INVALID, .OK
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
start_capture :: proc(ms: ^Match_State, s, p, what: int) -> (res: int, err: Error) {
|
||||
level := ms.level
|
||||
|
||||
ms.capture[level].init = s
|
||||
ms.capture[level].len = what
|
||||
ms.level += 1
|
||||
|
||||
res = match(ms, s, p) or_return
|
||||
if res == INVALID {
|
||||
ms.level -= 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
end_capture :: proc(ms: ^Match_State, s, p: int) -> (res: int, err: Error) {
|
||||
l := capture_to_close(ms) or_return
|
||||
|
||||
// TODO double check, could do string as int index
|
||||
ms.capture[l].len = s - ms.capture[l].init
|
||||
|
||||
res = match(ms, s, p) or_return
|
||||
if res == INVALID {
|
||||
ms.capture[l].len = CAP_UNFINISHED
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
match_capture :: proc(ms: ^Match_State, s: int, char: rune) -> (res: int, err: Error) {
|
||||
index := check_capture(ms, char) or_return
|
||||
length := ms.capture[index].len
|
||||
|
||||
if len(ms.src) - s >= length {
|
||||
return s + length, .OK
|
||||
}
|
||||
|
||||
return INVALID, .OK
|
||||
}
|
||||
|
||||
match :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
s := s
|
||||
p := p
|
||||
|
||||
if p == len(ms.pattern) {
|
||||
return s, .OK
|
||||
}
|
||||
|
||||
// NOTE we can walk by ascii steps if we know the characters are ascii
|
||||
char, _ := utf8_peek(ms.pattern[p:]) or_return
|
||||
switch char {
|
||||
case '(':
|
||||
if p + 1 < len(ms.pattern) && ms.pattern[p + 1] == ')' {
|
||||
s = start_capture(ms, s, p + 2, CAP_POSITION) or_return
|
||||
} else {
|
||||
s = start_capture(ms, s, p + 1, CAP_UNFINISHED) or_return
|
||||
}
|
||||
|
||||
case ')':
|
||||
s = end_capture(ms, s, p + 1) or_return
|
||||
|
||||
case '$':
|
||||
if p + 1 != len(ms.pattern) {
|
||||
return match_default(ms, s, p)
|
||||
}
|
||||
|
||||
if len(ms.src) != s {
|
||||
s = INVALID
|
||||
}
|
||||
|
||||
case L_ESC:
|
||||
// stop short patterns like "%" only
|
||||
if p + 1 >= len(ms.pattern) {
|
||||
err = .OOB
|
||||
return
|
||||
}
|
||||
|
||||
switch ms.pattern[p + 1] {
|
||||
// balanced string
|
||||
case 'b':
|
||||
s = match_balance(ms, s, p + 2) or_return
|
||||
|
||||
if s != INVALID {
|
||||
// eg after %b()
|
||||
return match(ms, s, p + 4)
|
||||
}
|
||||
|
||||
// frontier
|
||||
case 'f':
|
||||
p += 2
|
||||
|
||||
if ms.pattern[p] != '[' {
|
||||
return INVALID, .Invalid_Pattern_Capture
|
||||
}
|
||||
|
||||
ep := class_end(ms, p) or_return
|
||||
previous, current: rune
|
||||
|
||||
// get previous
|
||||
if s != 0 {
|
||||
temp := utf8_prev(ms.src, 0, s)
|
||||
previous, _ = utf8_peek(ms.src[temp:]) or_return
|
||||
}
|
||||
|
||||
// get current
|
||||
if s != len(ms.src) {
|
||||
current, _ = utf8_peek(ms.src[s:]) or_return
|
||||
}
|
||||
|
||||
m1 := match_bracket_class(ms, previous, p, ep - 1) or_return
|
||||
m2 := match_bracket_class(ms, current, p, ep - 1) or_return
|
||||
|
||||
if !m1 && m2 {
|
||||
return match(ms, s, ep)
|
||||
}
|
||||
|
||||
s = INVALID
|
||||
|
||||
// capture group
|
||||
case '0'..<'9':
|
||||
s = match_capture(ms, s, rune(ms.pattern[p + 1])) or_return
|
||||
|
||||
if s != INVALID {
|
||||
return match(ms, s, p + 2)
|
||||
}
|
||||
|
||||
case: return match_default(ms, s, p)
|
||||
}
|
||||
|
||||
case:
|
||||
return match_default(ms, s, p)
|
||||
}
|
||||
|
||||
return s, .OK
|
||||
}
|
||||
|
||||
match_default :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error) {
|
||||
s := s
|
||||
ep := class_end(ms, p) or_return
|
||||
single_matched, ssize := single_match(ms, s, p, ep) or_return
|
||||
|
||||
if !single_matched {
|
||||
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
|
||||
|
||||
switch epc {
|
||||
case '*', '?', '-': return match(ms, s, ep + 1)
|
||||
case: s = INVALID
|
||||
}
|
||||
} else {
|
||||
epc := ep < len(ms.pattern) ? ms.pattern[ep] : 0
|
||||
|
||||
switch epc {
|
||||
case '?':
|
||||
result := match(ms, s + ssize, ep + 1) or_return
|
||||
|
||||
if result != INVALID {
|
||||
s = result
|
||||
} else {
|
||||
return match(ms, s, ep + 1)
|
||||
}
|
||||
|
||||
case '+': s = max_expand(ms, s + ssize, p, ep) or_return
|
||||
case '*': s = max_expand(ms, s, p, ep) or_return
|
||||
case '-': s = min_expand(ms, s, p, ep) or_return
|
||||
case: return match(ms, s + ssize, ep)
|
||||
}
|
||||
}
|
||||
|
||||
return s, .OK
|
||||
}
|
||||
|
||||
push_onecapture :: proc(ms: ^Match_State, i: int, s: int, e: int, matches: []Match) -> (err: Error) {
|
||||
if i >= ms.level {
|
||||
if i == 0 {
|
||||
matches[0] = { 0, e - s }
|
||||
} else {
|
||||
err = .Invalid_Capture_Index
|
||||
}
|
||||
} else {
|
||||
init := ms.capture[i].init
|
||||
length := ms.capture[i].len
|
||||
|
||||
switch length {
|
||||
case CAP_UNFINISHED: err = .Unfinished_Capture
|
||||
case CAP_POSITION: matches[i] = { init, init + 1 }
|
||||
case: matches[i] = { init, init + length }
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
push_captures :: proc(
|
||||
ms: ^Match_State,
|
||||
s: int,
|
||||
e: int,
|
||||
matches: []Match,
|
||||
) -> (nlevels: int, err: Error) {
|
||||
nlevels = 1 if ms.level == 0 && s != -1 else ms.level
|
||||
|
||||
for i in 0..<nlevels {
|
||||
push_onecapture(ms, i, s, e, matches) or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// SPECIALS := "^$*+?.([%-"
|
||||
// all special characters inside a small ascii array
|
||||
SPECIALS_TABLE := [256]bool {
|
||||
'^' = true,
|
||||
'$' = true,
|
||||
'*' = true,
|
||||
'+' = true,
|
||||
'?' = true,
|
||||
'.' = true,
|
||||
'(' = true,
|
||||
'[' = true,
|
||||
'%' = true,
|
||||
'-' = true,
|
||||
}
|
||||
|
||||
// helper call to quick search for special characters
|
||||
index_special :: proc(text: string) -> int {
|
||||
for i in 0..<len(text) {
|
||||
if SPECIALS_TABLE[text[i]] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
lmem_find :: proc(s1, s2: string) -> int {
|
||||
l1 := len(s1)
|
||||
l2 := len(s2)
|
||||
|
||||
if l2 == 0 {
|
||||
return 0
|
||||
} else if l2 > l1 {
|
||||
return -1
|
||||
} else {
|
||||
init := strings.index_byte(s1, s2[0])
|
||||
end := init + l2
|
||||
|
||||
for end <= l1 && init != -1 {
|
||||
init += 1
|
||||
|
||||
if s1[init - 1:end] == s2 {
|
||||
return init - 1
|
||||
} else {
|
||||
next := strings.index_byte(s1[init:], s2[0])
|
||||
|
||||
if next == -1 {
|
||||
return -1
|
||||
} else {
|
||||
init = init + next
|
||||
end = init + l2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1
|
||||
}
|
||||
|
||||
// find a pattern with in a haystack with an offset
|
||||
// allow_memfind will speed up simple searches
|
||||
find_aux :: proc(
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
offset: int,
|
||||
allow_memfind: bool,
|
||||
matches: ^[MAX_CAPTURES]Match,
|
||||
) -> (captures: int, err: Error) {
|
||||
s := offset
|
||||
p := 0
|
||||
|
||||
specials_idx := index_special(pattern)
|
||||
if allow_memfind && specials_idx == -1 {
|
||||
if index := lmem_find(haystack[s:], pattern); index != -1 {
|
||||
matches[0] = { index + s, index + s + len(pattern) }
|
||||
captures = 1
|
||||
return
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
pattern := pattern
|
||||
anchor: bool
|
||||
if len(pattern) > 0 && pattern[0] == '^' {
|
||||
anchor = true
|
||||
pattern = pattern[1:]
|
||||
}
|
||||
|
||||
ms := Match_State {
|
||||
src = haystack,
|
||||
pattern = pattern,
|
||||
}
|
||||
|
||||
for {
|
||||
res := match(&ms, s, p) or_return
|
||||
|
||||
if res != INVALID {
|
||||
// disallow non advancing match
|
||||
if s == res {
|
||||
err = .Match_Invalid
|
||||
}
|
||||
|
||||
// NOTE(Skytrias): first result is reserved for a full match
|
||||
matches[0] = { s, res }
|
||||
|
||||
// rest are the actual captures
|
||||
captures = push_captures(&ms, -1, -1, matches[1:]) or_return
|
||||
captures += 1
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
s += 1
|
||||
|
||||
if !(s < len(ms.src) && !anchor) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// iterative matching which returns the 0th/1st match
|
||||
// rest has to be used from captures
|
||||
gmatch :: proc(
|
||||
haystack: ^string,
|
||||
pattern: string,
|
||||
captures: ^[MAX_CAPTURES]Match,
|
||||
) -> (res: string, ok: bool) {
|
||||
if len(haystack) > 0 {
|
||||
length, err := find_aux(haystack^, pattern, 0, false, captures)
|
||||
|
||||
if length != 0 && err == .OK {
|
||||
ok = true
|
||||
first := length > 1 ? 1 : 0
|
||||
cap := captures[first]
|
||||
res = haystack[cap.byte_start:cap.byte_end]
|
||||
haystack^ = haystack[cap.byte_end:]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// gsub with builder, replace patterns found with the replace content
|
||||
gsub_builder :: proc(
|
||||
builder: ^strings.Builder,
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
replace: string,
|
||||
) -> string {
|
||||
// find matches
|
||||
captures: [MAX_CAPTURES]Match
|
||||
haystack := haystack
|
||||
|
||||
for {
|
||||
length, err := find_aux(haystack, pattern, 0, false, &captures)
|
||||
|
||||
// done
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if err != .OK {
|
||||
return {}
|
||||
}
|
||||
|
||||
cap := captures[0]
|
||||
|
||||
// write front till capture
|
||||
strings.write_string(builder, haystack[:cap.byte_start])
|
||||
|
||||
// write replacements
|
||||
strings.write_string(builder, replace)
|
||||
|
||||
// advance string till end
|
||||
haystack = haystack[cap.byte_end:]
|
||||
}
|
||||
|
||||
strings.write_string(builder, haystack[:])
|
||||
return strings.to_string(builder^)
|
||||
}
|
||||
|
||||
// uses temp builder to build initial string - then allocates the result
|
||||
gsub_allocator :: proc(
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
replace: string,
|
||||
allocator := context.allocator,
|
||||
) -> string {
|
||||
builder := strings.builder_make(0, 256, context.temp_allocator)
|
||||
return gsub_builder(&builder, haystack, pattern, replace)
|
||||
}
|
||||
|
||||
Gsub_Proc :: proc(
|
||||
// optional passed data
|
||||
data: rawptr,
|
||||
// word match found
|
||||
word: string,
|
||||
// current haystack for found captures
|
||||
haystack: string,
|
||||
// found captures - empty for no captures
|
||||
captures: []Match,
|
||||
)
|
||||
|
||||
// call a procedure on every match in the haystack
|
||||
gsub_with :: proc(
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
data: rawptr,
|
||||
call: Gsub_Proc,
|
||||
) {
|
||||
// find matches
|
||||
captures: [MAX_CAPTURES]Match
|
||||
haystack := haystack
|
||||
|
||||
for {
|
||||
length, err := find_aux(haystack, pattern, 0, false, &captures)
|
||||
|
||||
// done
|
||||
if length == 0 || err != .OK {
|
||||
break
|
||||
}
|
||||
|
||||
cap := captures[0]
|
||||
|
||||
word := haystack[cap.byte_start:cap.byte_end]
|
||||
call(data, word, haystack, captures[1:length])
|
||||
|
||||
// advance string till end
|
||||
haystack = haystack[cap.byte_end:]
|
||||
}
|
||||
}
|
||||
|
||||
gsub :: proc { gsub_builder, gsub_allocator }
|
||||
|
||||
// iterative find with zeroth capture only
|
||||
gfind :: proc(
|
||||
haystack: ^string,
|
||||
pattern: string,
|
||||
captures: ^[MAX_CAPTURES]Match,
|
||||
) -> (res: string, ok: bool) {
|
||||
if len(haystack) > 0 {
|
||||
length, err := find_aux(haystack^, pattern, 0, true, captures)
|
||||
|
||||
if length != 0 && err == .OK {
|
||||
ok = true
|
||||
cap := captures[0]
|
||||
res = haystack[cap.byte_start:cap.byte_end]
|
||||
haystack^ = haystack[cap.byte_end:]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// rebuilds a pattern into a case insensitive pattern
|
||||
pattern_case_insensitive_builder :: proc(
|
||||
builder: ^strings.Builder,
|
||||
pattern: string,
|
||||
) -> (res: string) {
|
||||
p := pattern
|
||||
last_percent: bool
|
||||
|
||||
for len(p) > 0 {
|
||||
char, size := utf8.decode_rune_in_string(p)
|
||||
|
||||
if unicode.is_alpha(char) && !last_percent {
|
||||
// write character class in manually
|
||||
strings.write_byte(builder, '[')
|
||||
strings.write_rune(builder, unicode.to_lower(char))
|
||||
strings.write_rune(builder, unicode.to_upper(char))
|
||||
strings.write_byte(builder, ']')
|
||||
} else {
|
||||
strings.write_rune(builder, char)
|
||||
}
|
||||
|
||||
last_percent = char == L_ESC
|
||||
p = p[size:]
|
||||
}
|
||||
|
||||
return strings.to_string(builder^)
|
||||
}
|
||||
|
||||
pattern_case_insensitive_allocator :: proc(
|
||||
pattern: string,
|
||||
cap: int = 256,
|
||||
allocator := context.allocator,
|
||||
) -> (res: string) {
|
||||
builder := strings.builder_make(0, cap, context.temp_allocator)
|
||||
return pattern_case_insensitive_builder(&builder, pattern)
|
||||
}
|
||||
|
||||
pattern_case_insensitive :: proc { pattern_case_insensitive_builder, pattern_case_insensitive_allocator }
|
||||
|
||||
// Matcher helper struct that stores optional data you might want to use or not
|
||||
// as lua is far more dynamic this helps dealing with too much data
|
||||
// this also allows use of find/match/gmatch at through one struct
|
||||
Matcher :: struct {
|
||||
haystack: string,
|
||||
pattern: string,
|
||||
captures: [MAX_CAPTURES]Match,
|
||||
captures_length: int,
|
||||
offset: int,
|
||||
err: Error,
|
||||
|
||||
// changing content for iterators
|
||||
iter: string,
|
||||
iter_index: int,
|
||||
}
|
||||
|
||||
// init using haystack & pattern and an optional byte offset
|
||||
matcher_init :: proc(haystack, pattern: string, offset: int = 0) -> (res: Matcher) {
|
||||
res.haystack = haystack
|
||||
res.pattern = pattern
|
||||
res.offset = offset
|
||||
res.iter = haystack
|
||||
return
|
||||
}
|
||||
|
||||
// find the first match and return the byte start / end position in the string, true on success
|
||||
matcher_find :: proc(matcher: ^Matcher) -> (start, end: int, ok: bool) #no_bounds_check {
|
||||
matcher.captures_length, matcher.err = find_aux(
|
||||
matcher.haystack,
|
||||
matcher.pattern,
|
||||
matcher.offset,
|
||||
true,
|
||||
&matcher.captures,
|
||||
)
|
||||
ok = matcher.captures_length > 0 && matcher.err == .OK
|
||||
match := matcher.captures[0]
|
||||
start = match.byte_start
|
||||
end = match.byte_end
|
||||
return
|
||||
}
|
||||
|
||||
// find the first match and return the matched word, true on success
|
||||
matcher_match :: proc(matcher: ^Matcher) -> (word: string, ok: bool) #no_bounds_check {
|
||||
matcher.captures_length, matcher.err = find_aux(
|
||||
matcher.haystack,
|
||||
matcher.pattern,
|
||||
matcher.offset,
|
||||
false,
|
||||
&matcher.captures,
|
||||
)
|
||||
ok = matcher.captures_length > 0 && matcher.err == .OK
|
||||
match := matcher.captures[0]
|
||||
word = matcher.haystack[match.byte_start:match.byte_end]
|
||||
return
|
||||
}
|
||||
|
||||
// get the capture at the "correct" spot, as spot 0 is reserved for the first match
|
||||
matcher_capture :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> string #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
|
||||
cap := matcher.captures[index + 1]
|
||||
return matcher.haystack[cap.byte_start:cap.byte_end]
|
||||
}
|
||||
|
||||
// get the raw match out of the captures, skipping spot 0
|
||||
matcher_capture_raw :: proc(matcher: ^Matcher, index: int, loc := #caller_location) -> Match #no_bounds_check {
|
||||
runtime.bounds_check_error_loc(loc, index + 1, MAX_CAPTURES - 1)
|
||||
return matcher.captures[index + 1]
|
||||
}
|
||||
|
||||
// alias
|
||||
matcher_gmatch :: matcher_match_iter
|
||||
|
||||
// iteratively match the haystack till it cant find any matches
|
||||
matcher_match_iter :: proc(matcher: ^Matcher) -> (res: string, index: int, ok: bool) {
|
||||
if len(matcher.iter) > 0 {
|
||||
matcher.captures_length, matcher.err = find_aux(
|
||||
matcher.iter,
|
||||
matcher.pattern,
|
||||
matcher.offset,
|
||||
false,
|
||||
&matcher.captures,
|
||||
)
|
||||
|
||||
if matcher.captures_length != 0 && matcher.err == .OK {
|
||||
ok = true
|
||||
first := matcher.captures_length > 1 ? 1 : 0
|
||||
match := matcher.captures[first]
|
||||
|
||||
// output
|
||||
res = matcher.iter[match.byte_start:match.byte_end]
|
||||
index = matcher.iter_index
|
||||
|
||||
// advance
|
||||
matcher.iter_index += 1
|
||||
matcher.iter = matcher.iter[match.byte_end:]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// get a slice of all valid captures above the first match
|
||||
matcher_captures_slice :: proc(matcher: ^Matcher) -> []Match {
|
||||
return matcher.captures[1:matcher.captures_length]
|
||||
}
|
||||
+34
-32
@@ -17,7 +17,7 @@ MAX_DURATION :: Duration(1<<63 - 1)
|
||||
IS_SUPPORTED :: _IS_SUPPORTED
|
||||
|
||||
Time :: struct {
|
||||
_nsec: i64, // zero is 1970-01-01 00:00:00
|
||||
_nsec: i64, // Measured in UNIX nanonseconds
|
||||
}
|
||||
|
||||
Month :: enum int {
|
||||
@@ -59,36 +59,36 @@ sleep :: proc "contextless" (d: Duration) {
|
||||
_sleep(d)
|
||||
}
|
||||
|
||||
stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
|
||||
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
if !running {
|
||||
_start_time = tick_now()
|
||||
running = true
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch_stop :: proc(using stopwatch: ^Stopwatch) {
|
||||
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
if running {
|
||||
_accumulation += tick_diff(_start_time, tick_now())
|
||||
running = false
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch_reset :: proc(using stopwatch: ^Stopwatch) {
|
||||
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
_accumulation = {}
|
||||
running = false
|
||||
}
|
||||
|
||||
stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration {
|
||||
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
|
||||
if !running { return _accumulation }
|
||||
return _accumulation + tick_diff(_start_time, tick_now())
|
||||
}
|
||||
|
||||
diff :: proc(start, end: Time) -> Duration {
|
||||
diff :: proc "contextless" (start, end: Time) -> Duration {
|
||||
d := end._nsec - start._nsec
|
||||
return Duration(d)
|
||||
}
|
||||
|
||||
since :: proc(start: Time) -> Duration {
|
||||
since :: proc "contextless" (start: Time) -> Duration {
|
||||
return diff(start, now())
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
|
||||
return f64(hour) + f64(nsec)/(60*60*1e9)
|
||||
}
|
||||
|
||||
duration_round :: proc(d, m: Duration) -> Duration {
|
||||
_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
|
||||
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
|
||||
return u64(x)+u64(x) < u64(y)
|
||||
}
|
||||
|
||||
@@ -146,45 +146,45 @@ duration_round :: proc(d, m: Duration) -> Duration {
|
||||
return MAX_DURATION
|
||||
}
|
||||
|
||||
duration_truncate :: proc(d, m: Duration) -> Duration {
|
||||
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
return d if m <= 0 else d - d%m
|
||||
}
|
||||
|
||||
date :: proc(t: Time) -> (year: int, month: Month, day: int) {
|
||||
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
|
||||
year, month, day, _ = _abs_date(_time_abs(t), true)
|
||||
return
|
||||
}
|
||||
|
||||
year :: proc(t: Time) -> (year: int) {
|
||||
year :: proc "contextless" (t: Time) -> (year: int) {
|
||||
year, _, _, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
month :: proc(t: Time) -> (month: Month) {
|
||||
month :: proc "contextless" (t: Time) -> (month: Month) {
|
||||
_, month, _, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
day :: proc(t: Time) -> (day: int) {
|
||||
day :: proc "contextless" (t: Time) -> (day: int) {
|
||||
_, _, day, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
|
||||
|
||||
clock_from_time :: proc(t: Time) -> (hour, min, sec: int) {
|
||||
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
|
||||
return clock_from_seconds(_time_abs(t))
|
||||
}
|
||||
|
||||
clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) {
|
||||
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
|
||||
return clock_from_seconds(u64(d/1e9))
|
||||
}
|
||||
|
||||
clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) {
|
||||
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
|
||||
return clock_from_duration(stopwatch_duration(s))
|
||||
}
|
||||
|
||||
clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
|
||||
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
|
||||
sec = int(nsec % SECONDS_PER_DAY)
|
||||
hour = sec / SECONDS_PER_HOUR
|
||||
sec -= hour * SECONDS_PER_HOUR
|
||||
@@ -193,11 +193,11 @@ clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
|
||||
return
|
||||
}
|
||||
|
||||
read_cycle_counter :: proc() -> u64 {
|
||||
read_cycle_counter :: proc "contextless" () -> u64 {
|
||||
return u64(intrinsics.read_cycle_counter())
|
||||
}
|
||||
|
||||
unix :: proc(sec: i64, nsec: i64) -> Time {
|
||||
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
|
||||
sec, nsec := sec, nsec
|
||||
if nsec < 0 || nsec >= 1e9 {
|
||||
n := nsec / 1e9
|
||||
@@ -208,20 +208,20 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
|
||||
sec -= 1
|
||||
}
|
||||
}
|
||||
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
|
||||
return Time{(sec*1e9 + nsec)}
|
||||
}
|
||||
|
||||
to_unix_seconds :: time_to_unix
|
||||
time_to_unix :: proc(t: Time) -> i64 {
|
||||
time_to_unix :: proc "contextless" (t: Time) -> i64 {
|
||||
return t._nsec/1e9
|
||||
}
|
||||
|
||||
to_unix_nanoseconds :: time_to_unix_nano
|
||||
time_to_unix_nano :: proc(t: Time) -> i64 {
|
||||
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
|
||||
return t._nsec
|
||||
}
|
||||
|
||||
time_add :: proc(t: Time, d: Duration) -> Time {
|
||||
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
|
||||
return Time{t._nsec + i64(d)}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ time_add :: proc(t: Time, d: Duration) -> Time {
|
||||
// On Windows it depends but is comparable with regular sleep in the worst case.
|
||||
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
|
||||
// tell Windows to use a more accurate timer for your process.
|
||||
accurate_sleep :: proc(d: Duration) {
|
||||
accurate_sleep :: proc "contextless" (d: Duration) {
|
||||
to_sleep, estimate, mean, m2, count: Duration
|
||||
|
||||
to_sleep = d
|
||||
@@ -279,19 +279,19 @@ ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
|
||||
|
||||
|
||||
@(private)
|
||||
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
year, month, day, yday = _abs_date(_time_abs(t), full)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_time_abs :: proc(t: Time) -> u64 {
|
||||
_time_abs :: proc "contextless" (t: Time) -> u64 {
|
||||
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_is_leap_year :: proc(year: int) -> bool {
|
||||
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
@@ -352,9 +352,11 @@ _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, y
|
||||
return
|
||||
}
|
||||
|
||||
datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
divmod :: proc(year: int, divisor: int) -> (div: int, mod: int) {
|
||||
assert(divisor > 0)
|
||||
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
|
||||
if divisor <= 0 {
|
||||
intrinsics.debug_trap()
|
||||
}
|
||||
div = int(year / divisor)
|
||||
mod = year % divisor
|
||||
return
|
||||
|
||||
@@ -22,3 +22,5 @@ _tick_now :: proc "contextless" () -> Tick {
|
||||
return {}
|
||||
}
|
||||
|
||||
_yield :: proc "contextless" () {
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user