mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-08 20:41:38 -07:00
Compare commits
422 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 74458ab096 | |||
| c39b1a31db | |||
| 635c7fa153 | |||
| b7ac0a9e8d | |||
| 3f3ae4b2b6 | |||
| c2423dc07f | |||
| 1296630160 | |||
| 63eec25044 | |||
| 7a9b0731cf | |||
| d45661c405 | |||
| 002bec256a | |||
| 000861cba8 | |||
| 36473b2774 | |||
| 4188f50105 | |||
| 3e3b9ae2df | |||
| e89f0de232 | |||
| 4858e16a11 | |||
| 902a6db0e1 | |||
| 19ae6122c7 | |||
| b82b91ea08 | |||
| 636f0d7063 | |||
| ed73441a4c | |||
| 4c2be6cd49 | |||
| f3f51bd643 | |||
| 4c5672119a | |||
| 8482f943ea | |||
| 15aaf7dfa0 | |||
| 768abf83f6 | |||
| ca76d53452 | |||
| b0904d6598 | |||
| b0a09f7b9e | |||
| eb4891bcc8 | |||
| 803fd8f037 | |||
| 67bdb5b1a3 | |||
| acc635b535 | |||
| 4e8ce87792 | |||
| 2c8daa25dc | |||
| 054ee0a8b5 | |||
| d0cadaf1a6 | |||
| 317db2758a | |||
| 25102d4792 | |||
| d39f1c461e | |||
| 7a6fc3a93b | |||
| 83c002c197 | |||
| fc47b5dee0 | |||
| 6c2e0b09ba | |||
| 3d4698debe | |||
| 4a25cfb27c | |||
| 294bd6a446 | |||
| d0109db23b | |||
| ee3ee66aae | |||
| f74e281efa | |||
| c0cd02883f | |||
| d9adb0fd6b | |||
| 6363013dd8 | |||
| 934131abf8 | |||
| 4e5337412a | |||
| 00f2e911a7 | |||
| c82d7d3d87 | |||
| ecfea027a0 | |||
| 96be494730 | |||
| 12c8db927b | |||
| 027ea587fc | |||
| 026900c7f0 | |||
| ffa87f55c4 | |||
| c9eed04b51 | |||
| b50b6b9f33 | |||
| 8fd5bef0bd | |||
| d6b49994a2 | |||
| 776927709b | |||
| 96e033b22c | |||
| 3469178dc1 | |||
| af1b3b6368 | |||
| d56789e5a7 | |||
| aeacf3a9d8 | |||
| 4ba486baa2 | |||
| f1ffd90294 | |||
| 777aa8b118 | |||
| cb9e16f4df | |||
| 2908923db9 | |||
| 8c1dfabb6b | |||
| 7fe36de069 | |||
| 27d556735a | |||
| b70d211f21 | |||
| b3e3b6c656 | |||
| 1734286252 | |||
| c8c076f970 | |||
| e40b3ad338 | |||
| afec321db2 | |||
| 6e9f9e6f3c | |||
| f504b200a9 | |||
| 82e840a0ca | |||
| 82765ca96e | |||
| 5387ec5f29 | |||
| f2908cbc5a | |||
| 5337b0b471 | |||
| e51afc3509 | |||
| 2c004dbcc9 | |||
| e128ed7d26 | |||
| 9064ebfe97 | |||
| 4f7bbe0e4a | |||
| 208f168564 | |||
| 737bccbd5e | |||
| f5431a046d | |||
| 5a9422b6bc | |||
| d30198c99a | |||
| 0c8d59dd20 | |||
| a460d140fe | |||
| 881d18ee88 | |||
| d73a4aa34b | |||
| 5c298c1501 | |||
| a83ca2120e | |||
| 81799f7f78 | |||
| 7973f7e750 | |||
| 3dc62a67e0 | |||
| 081e36c909 | |||
| 3a1d364f59 | |||
| e50648279d | |||
| 929af320da | |||
| 8e7c7eeeba | |||
| b739044e69 | |||
| 9e0107c9fc | |||
| 107e016508 | |||
| 22d16c20f8 | |||
| 697c839c84 | |||
| de8bd88d2a | |||
| 595efba747 | |||
| c041d15569 | |||
| 57b20e634b | |||
| e285796fc1 | |||
| a19494d3a7 | |||
| d2a362fd52 | |||
| 0f3562ef02 | |||
| 03f683f9e7 | |||
| cecadce86d | |||
| a7c3906003 | |||
| 70dc0c15fd | |||
| 9eeed9d5bd | |||
| a054c2934e | |||
| 38102f14c1 | |||
| 0997df4fcf | |||
| a5a56e061c | |||
| 8b007ad55a | |||
| 57dd5ec4db | |||
| 5b621d5be1 | |||
| 7aee762f3a | |||
| 4ee50c5a35 | |||
| 28f440dd9e | |||
| 43b78e51a4 | |||
| 84f9fb706b | |||
| 812823cad8 | |||
| 0655260378 | |||
| cfc3723879 | |||
| 4c3281b3f2 | |||
| ff94c605e0 | |||
| cb0a59bb2c | |||
| 076700bd22 | |||
| bcccc8338f | |||
| 425dec8bb8 | |||
| 838554460b | |||
| 659c3c528d | |||
| 60aeab3c38 | |||
| 5e3cf45df3 | |||
| 4633591918 | |||
| 0e6a8b7c72 | |||
| 147848ca20 | |||
| cde002c579 | |||
| f23d93ba89 | |||
| c97a8418dc | |||
| 4aca9372a6 | |||
| 4912ecc3ea | |||
| c1c8ceafc2 | |||
| 9c0a3b6c60 | |||
| 7b539e3025 | |||
| b2b0043875 | |||
| 53e0d182af | |||
| a6fa41e290 | |||
| edba99d636 | |||
| 35674959f2 | |||
| dc8b7a0eb8 | |||
| 8d1f46d837 | |||
| a2117d23b2 | |||
| a58e4d0359 | |||
| 576914aee1 | |||
| 8171f8209a | |||
| 64ff05303c | |||
| 6caab6225d | |||
| 326411498a | |||
| d50fcf0020 | |||
| d354d36a3b | |||
| 483a72ac61 | |||
| dbec4b0d0e | |||
| 4cb489b9e4 | |||
| 28ec50d567 | |||
| 73beed0477 | |||
| e0ecdd4b24 | |||
| 0cb1a578d0 | |||
| 5168cf03a9 | |||
| b886ae6515 | |||
| 277a973b98 | |||
| 0ec4d97bfd | |||
| e6236e5c3e | |||
| e201280844 | |||
| 8e50a6c61b | |||
| 7e6f5f89d0 | |||
| 97acc57649 | |||
| 83c8c48ed7 | |||
| 0815b4d59f | |||
| a0135080b3 | |||
| f45e8e5d47 | |||
| 98ba4beede | |||
| a9304f2fef | |||
| a0697ab057 | |||
| 2e895c72d3 | |||
| 674ebe395f | |||
| 96eecaab54 | |||
| b1ae5bc9fe | |||
| 7258588ed5 | |||
| d913155972 | |||
| 9746e25784 | |||
| d26cfd2141 | |||
| 21f2c06f4b | |||
| 727a25f41f | |||
| 3f27cb2309 | |||
| f213622982 | |||
| 4aad835a66 | |||
| 4af8a64580 | |||
| c9c3611b1d | |||
| 220dfd7440 | |||
| bce8819ed5 | |||
| 5f2b220a85 | |||
| f174c805a9 | |||
| 25869b7504 | |||
| ecd81e8a53 | |||
| d7f9f7f170 | |||
| 08f5259d77 | |||
| a9f744cb64 | |||
| b02e42c6dc | |||
| cb0273b5d7 | |||
| 8dbf45a65a | |||
| 9f64de9568 | |||
| 0ebe9ba487 | |||
| efe00e1aa6 | |||
| 2bdbce55f9 | |||
| 9614ca92f0 | |||
| d30e59f539 | |||
| a3afe617c2 | |||
| 69daac583e | |||
| b28d4b753b | |||
| e6ab4f4856 | |||
| c8ab1b7ee1 | |||
| 9f10487678 | |||
| 2542983d70 | |||
| d492fb3501 | |||
| 1829aa1638 | |||
| 4cb4173ced | |||
| 00e704b216 | |||
| 227ee0f705 | |||
| 17f47a7ab0 | |||
| 4e8bc0786d | |||
| 3d3ccf061f | |||
| 3a8adc6721 | |||
| e1748a5dd1 | |||
| 59b4c889d3 | |||
| b6408d1b3f | |||
| 3db3047f47 | |||
| 7420fbd95b | |||
| 7c990b3833 | |||
| 9c059f1a12 | |||
| fb167d1d0a | |||
| 0992239d86 | |||
| 9eb3da0474 | |||
| e91f8feedf | |||
| 22a0c3fce1 | |||
| 6c7e5748a8 | |||
| 0b0c6da8b0 | |||
| 78826071c0 | |||
| e61b73d7ad | |||
| f886632bf1 | |||
| eafa5098aa | |||
| 0571b80d37 | |||
| 80c10644dd | |||
| 041625381c | |||
| 48f56d728b | |||
| 872d391cfb | |||
| 3e6ec65dd9 | |||
| 157c87b2a2 | |||
| d3081bd889 | |||
| 2ae5bf4395 | |||
| f28547cae1 | |||
| 5332705e31 | |||
| bfb082cda4 | |||
| 37d04198ab | |||
| ae9d540c1c | |||
| c90b7c38f1 | |||
| 9e376fbda7 | |||
| 00739bf06d | |||
| e8148055ad | |||
| dd0a20ab45 | |||
| babbc304b8 | |||
| a8b44f33bd | |||
| bd48561688 | |||
| 23842a8950 | |||
| 1676c643df | |||
| e2bfb024de | |||
| 4d06a54c0c | |||
| 0a8e6169d7 | |||
| 04ae87eaef | |||
| b313d09c2c | |||
| c0d2359a91 | |||
| 51a2f09032 | |||
| f60e8031f2 | |||
| ea42613fec | |||
| b89bb87759 | |||
| 2cbb3d5a24 | |||
| 9e288b7ce8 | |||
| 776b48c10d | |||
| 199dae6cd5 | |||
| e58f45bef7 | |||
| abe122ecb7 | |||
| f8744d87b0 | |||
| 3e7f6b8751 | |||
| 6ffe814ca7 | |||
| eec9be71f6 | |||
| 888913c739 | |||
| 33d96fd28a | |||
| f1e8738af2 | |||
| 9c52a11b1b | |||
| d89c4606bd | |||
| c6903fbcd5 | |||
| 13c8149046 | |||
| 665db0f778 | |||
| 173286de65 | |||
| f2ecda8fec | |||
| 6f1222e9bf | |||
| 84a424f21e | |||
| 9b7710488b | |||
| 9f413862e9 | |||
| b8802d7df7 | |||
| b13dad02a4 | |||
| f045f8d805 | |||
| 30b7c8ad66 | |||
| 89222a0ab2 | |||
| 21e637d2b3 | |||
| 6c196931d2 | |||
| d7195b0798 | |||
| b40998de9e | |||
| 8c0c327df9 | |||
| 50cbb8a1fc | |||
| ff9d058392 | |||
| 1acc8f438b | |||
| c53426fcb4 | |||
| 5187db525f | |||
| a8bd340267 | |||
| ca1f419dc2 | |||
| fae60a6b88 | |||
| 108558ddfc | |||
| 65b8cfae82 | |||
| 4055c31cf0 | |||
| e88af4e458 | |||
| 79eb7b52d9 | |||
| 83b2bf44c4 | |||
| 670f18ad1b | |||
| 9a81716936 | |||
| f013499eea | |||
| d04f732e68 | |||
| 35fd8e7f68 | |||
| 38ff2a3ed9 | |||
| bd502d16bc | |||
| a11e17fbc3 | |||
| 8b3b659433 | |||
| f33228fd6e | |||
| df5b693de8 | |||
| ab98108441 | |||
| 6f80174f84 | |||
| 0ebe81fce2 | |||
| c75dd14308 | |||
| c166b6a21d | |||
| 6ed5cbee12 | |||
| 5b200ccdf8 | |||
| cf0f0c4b31 | |||
| 1399ecb41c | |||
| 339d6cfd41 | |||
| 7bded4f189 | |||
| c7269b9ef0 | |||
| 85688015aa | |||
| 3c6cc575c6 | |||
| 0564cb6483 | |||
| 4be92c7eb8 | |||
| 88e9eb7d0c | |||
| d19fc54c3d | |||
| 5d4291d9fa | |||
| b70cd03e9e | |||
| e91e5e1fe9 | |||
| ae57a49915 | |||
| bfcb527b42 | |||
| a343fb171d | |||
| fa2296a124 | |||
| a996cfc536 | |||
| c1d55b9296 | |||
| 5c18cca1ca | |||
| 78e6cd0c60 | |||
| 176954a6d8 | |||
| ad6b3bd95f | |||
| 762895bc45 | |||
| 592e9afa5f | |||
| a2e0373934 | |||
| fb49841b1d | |||
| 01ea0d6f1e | |||
| bb7f291f5f | |||
| 174fa9b490 | |||
| dda2ed290a | |||
| ee9908b09e | |||
| 66de1856e3 | |||
| a315e7c962 | |||
| 42364f2fce | |||
| 8f600798ef | |||
| f28c268d97 | |||
| f1cff20249 | |||
| 865d88dd56 | |||
| 069c05669f | |||
| 516065d7c2 |
@@ -1,5 +1,5 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build_linux:
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: "Close Stale Issues & PRs"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: "0 21 * * *"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Close Stale Issues
|
||||
uses: actions/stale@v4.1.0
|
||||
with:
|
||||
# stale-issue-message: |
|
||||
# Hello!
|
||||
#
|
||||
# I am marking this issue as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
|
||||
# - open a PR referencing and resolving the issue;
|
||||
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
|
||||
# - leave a comment and describe in detail why this issue is critical for your use case;
|
||||
# - open a new issue with updated details and a plan on resolving the issue.
|
||||
#
|
||||
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
|
||||
#
|
||||
# stale-pr-message: |
|
||||
# Hello!
|
||||
#
|
||||
# I am marking this PR as stale as it has not received any engagement from the community or maintainers 120 days. That does not imply that the issue has no merit! If you feel strongly about this issue
|
||||
# - leave a comment on it and discuss ideas how you could contribute towards resolving it;
|
||||
# - leave a comment and describe in detail why this issue is critical for your use case;
|
||||
#
|
||||
# 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
|
||||
exempt-draft-pr: true
|
||||
ascending: true
|
||||
operations-per-run: 1000
|
||||
exempt-issue-labels: "ignore"
|
||||
@@ -58,7 +58,7 @@ set libs= ^
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set linker_flags=%linker_flags% -debug
|
||||
set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
|
||||
) else ( rem Release
|
||||
set linker_flags=%linker_flags% -debug
|
||||
)
|
||||
|
||||
+35
-23
@@ -1,12 +1,20 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD)
|
||||
: ${CXX=clang++}
|
||||
: ${CPPFLAGS=}
|
||||
: ${CXXFLAGS=}
|
||||
: ${LDFLAGS=}
|
||||
: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
|
||||
CXXFLAGS="$CXXFLAGS -std=c++14"
|
||||
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
|
||||
|
||||
GIT_SHA=$(git rev-parse --short HEAD || :)
|
||||
if [ "$GIT_SHA" ]; then CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""; fi
|
||||
|
||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||
LDFLAGS="-pthread -lm -lstdc++"
|
||||
CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
|
||||
CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
|
||||
CC=clang
|
||||
OS=$(uname)
|
||||
|
||||
panic() {
|
||||
@@ -18,13 +26,13 @@ version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
|
||||
|
||||
config_darwin() {
|
||||
ARCH=$(uname -m)
|
||||
LLVM_CONFIG=llvm-config
|
||||
: ${LLVM_CONFIG=llvm-config}
|
||||
|
||||
# allow for arm only llvm's with version 13
|
||||
if [ ARCH == arm64 ]; then
|
||||
MIN_LLVM_VERSION=("13.0.0")
|
||||
else
|
||||
# allow for x86 / amd64 all llvm versions begining from 11
|
||||
# allow for x86 / amd64 all llvm versions beginning from 11
|
||||
MIN_LLVM_VERSION=("11.1.0")
|
||||
fi
|
||||
|
||||
@@ -37,34 +45,38 @@ config_darwin() {
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
}
|
||||
|
||||
config_freebsd() {
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config11
|
||||
: ${LLVM_CONFIG=/usr/local/bin/llvm-config11}
|
||||
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
config_openbsd() {
|
||||
LLVM_CONFIG=/usr/local/bin/llvm-config
|
||||
: ${LLVM_CONFIG=/usr/local/bin/llvm-config}
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
config_linux() {
|
||||
if which llvm-config > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config
|
||||
elif which llvm-config-11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
: ${LLVM_CONFIG=}
|
||||
|
||||
if [ ! "$LLVM_CONFIG" ]; then
|
||||
if which llvm-config > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config
|
||||
elif which llvm-config-11 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11
|
||||
elif which llvm-config-11-64 > /dev/null 2>&1; then
|
||||
LLVM_CONFIG=llvm-config-11-64
|
||||
else
|
||||
panic "Unable to find LLVM-config"
|
||||
fi
|
||||
fi
|
||||
|
||||
MIN_LLVM_VERSION=("11.0.0")
|
||||
@@ -74,7 +86,7 @@ config_linux() {
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
}
|
||||
|
||||
@@ -97,7 +109,7 @@ build_odin() {
|
||||
esac
|
||||
|
||||
set -x
|
||||
$CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||
$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
|
||||
set +x
|
||||
}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ scanner_destroy :: proc(s: ^Scanner) {
|
||||
}
|
||||
|
||||
|
||||
// Returns the first non-EOF error that was encounted by the scanner
|
||||
// Returns the first non-EOF error that was encountered by the scanner
|
||||
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
|
||||
switch s._err {
|
||||
case .EOF, nil:
|
||||
|
||||
@@ -161,6 +161,10 @@ buffer_write :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
return copy(b.buf[m:], p), nil
|
||||
}
|
||||
|
||||
buffer_write_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||
return buffer_write(b, ([^]byte)(ptr)[:size])
|
||||
}
|
||||
|
||||
buffer_write_string :: proc(b: ^Buffer, s: string) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
m, ok := _buffer_try_grow(b, len(s))
|
||||
@@ -229,6 +233,10 @@ buffer_read :: proc(b: ^Buffer, p: []byte) -> (n: int, err: io.Error) {
|
||||
return
|
||||
}
|
||||
|
||||
buffer_read_ptr :: proc(b: ^Buffer, ptr: rawptr, size: int) -> (n: int, err: io.Error) {
|
||||
return buffer_read(b, ([^]byte)(ptr)[:size])
|
||||
}
|
||||
|
||||
buffer_read_at :: proc(b: ^Buffer, p: []byte, offset: int) -> (n: int, err: io.Error) {
|
||||
b.last_read = .Invalid
|
||||
|
||||
|
||||
@@ -519,7 +519,7 @@ join_adjacent_string_literals :: proc(cpp: ^Preprocessor, initial_tok: ^Token) {
|
||||
|
||||
|
||||
quote_string :: proc(s: string) -> []byte {
|
||||
b := strings.make_builder(0, len(s)+2)
|
||||
b := strings.builder_make(0, len(s)+2)
|
||||
io.write_quoted_string(strings.to_writer(&b), s, '"')
|
||||
return b.buf[:]
|
||||
}
|
||||
|
||||
@@ -331,7 +331,7 @@ fmin :: proc{libc_fmin, libc_fminf}
|
||||
fma :: proc{libc_fma, libc_fmaf}
|
||||
|
||||
// But retain the 'f' suffix-variant functions as well so they can be used,
|
||||
// a trick is used here where we use explicit procedrual overloading of one
|
||||
// a trick is used here where we use explicit procedural overloading of one
|
||||
// procedure. This is done because the foreign block is marked @(private) and
|
||||
// aliasing functions does not remove privateness from the entity.
|
||||
acosf :: proc{libc_acosf}
|
||||
|
||||
@@ -196,7 +196,7 @@ foreign libc {
|
||||
getc :: proc(stream: ^FILE) -> int ---
|
||||
getchar :: proc() -> int ---
|
||||
putc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
putchar :: proc() -> int ---
|
||||
putchar :: proc(c: int) -> int ---
|
||||
puts :: proc(s: cstring) -> int ---
|
||||
ungetc :: proc(c: int, stream: ^FILE) -> int ---
|
||||
fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
|
||||
|
||||
@@ -8,7 +8,7 @@ tss_dtor_t :: proc "c" (rawptr)
|
||||
when ODIN_OS == .Windows {
|
||||
foreign import libc {
|
||||
"system:libucrt.lib",
|
||||
"system:msvcprt.lib"
|
||||
"system:msvcprt.lib",
|
||||
}
|
||||
|
||||
thrd_success :: 0 // _Thrd_success
|
||||
@@ -77,7 +77,7 @@ when ODIN_OS == .Windows {
|
||||
when ODIN_OS == .Linux {
|
||||
foreign import libc {
|
||||
"system:c",
|
||||
"system:pthread"
|
||||
"system:pthread",
|
||||
}
|
||||
|
||||
thrd_success :: 0
|
||||
|
||||
@@ -294,6 +294,24 @@ peek_data_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_memory :: #force_inline proc(z: ^Context_Memory_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
#no_bounds_check {
|
||||
if len(z.input_data) >= size + offset {
|
||||
buf := z.input_data[offset:][:size]
|
||||
return (^T)(&buf[0])^, .None
|
||||
}
|
||||
}
|
||||
|
||||
if len(z.input_data) == 0 {
|
||||
return T{}, .EOF
|
||||
} else {
|
||||
return T{}, .Short_Buffer
|
||||
}
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
@@ -321,7 +339,44 @@ peek_data_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream}
|
||||
@(optimization_mode="speed")
|
||||
peek_data_at_offset_from_stream :: #force_inline proc(z: ^Context_Stream_Input, $T: typeid, #any_int offset: int) -> (res: T, err: io.Error) {
|
||||
size :: size_of(T)
|
||||
|
||||
// Get current position to return to.
|
||||
cur_pos, e1 := z.input->impl_seek(0, .Current)
|
||||
if e1 != .None {
|
||||
return T{}, e1
|
||||
}
|
||||
|
||||
// Seek to offset.
|
||||
pos, e2 := z.input->impl_seek(offset, .Start)
|
||||
if e2 != .None {
|
||||
return T{}, e2
|
||||
}
|
||||
|
||||
r, e3 := io.to_reader_at(z.input)
|
||||
if !e3 {
|
||||
return T{}, .Empty
|
||||
}
|
||||
when size <= 128 {
|
||||
b: [size]u8
|
||||
} else {
|
||||
b := make([]u8, size, context.temp_allocator)
|
||||
}
|
||||
_, e4 := io.read_at(r, b[:], pos)
|
||||
if e4 != .None {
|
||||
return T{}, .Empty
|
||||
}
|
||||
|
||||
// Return read head to original position.
|
||||
z.input->impl_seek(cur_pos, .Start)
|
||||
|
||||
res = (^T)(&b[0])^
|
||||
return res, .None
|
||||
}
|
||||
|
||||
peek_data :: proc{peek_data_from_memory, peek_data_from_stream, peek_data_at_offset_from_memory, peek_data_at_offset_from_stream}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -185,6 +185,33 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
In:
|
||||
- ba: ^Bit_Array - a pointer to the Bit Array
|
||||
- index: The bit index. Can be an enum member.
|
||||
|
||||
Out:
|
||||
- ok: Whether or not we managed to unset requested bit.
|
||||
|
||||
`unset` automatically resizes the Bit Array to accommodate the requested index if needed.
|
||||
*/
|
||||
unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
|
||||
|
||||
idx := int(index) - ba.bias
|
||||
|
||||
if ba == nil || int(index) < ba.bias { return false }
|
||||
context.allocator = allocator
|
||||
|
||||
leg_index := idx >> INDEX_SHIFT
|
||||
bit_index := idx & INDEX_MASK
|
||||
|
||||
resize_if_needed(ba, leg_index) or_return
|
||||
|
||||
ba.max_index = max(idx, ba.max_index)
|
||||
ba.bits[leg_index] &= ~(1 << uint(bit_index))
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
|
||||
*/
|
||||
|
||||
@@ -85,7 +85,6 @@ _shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
|
||||
_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
|
||||
j := j
|
||||
queue := pq.queue[:]
|
||||
n := builtin.len(queue)
|
||||
for 0 <= j {
|
||||
i := (j-1)/2
|
||||
if i == j || !pq.less(queue[j], queue[i]) {
|
||||
|
||||
@@ -69,6 +69,23 @@ get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
|
||||
idx := (uint(i)+q.offset)%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
|
||||
front :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
return q.data[q.offset]
|
||||
}
|
||||
front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
return &q.data[q.offset]
|
||||
}
|
||||
|
||||
back :: proc(q: ^$Q/Queue($T)) -> T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return q.data[idx]
|
||||
}
|
||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
||||
idx := (q.offset+uint(q.len))%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
|
||||
runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
|
||||
|
||||
@@ -82,6 +99,18 @@ get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, 0, builtin.len(q.data))
|
||||
idx := q.offset%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
|
||||
runtime.bounds_check_error_loc(loc, int(q.len - 1), builtin.len(q.data))
|
||||
idx := (uint(q.len - 1)+q.offset)%builtin.len(q.data)
|
||||
return &q.data[idx]
|
||||
}
|
||||
|
||||
// Push an element to the back of the queue
|
||||
push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
|
||||
if space(q^) == 0 {
|
||||
|
||||
@@ -81,7 +81,7 @@ The crypto package is not thread-safe at the moment. This may change in the futu
|
||||
### Disclaimer
|
||||
The algorithms were ported out of curiosity and due to interest in the field.
|
||||
We have not had any of the code verified by a third party or tested/fuzzed by any automatic means.
|
||||
Whereever we were able to find official test vectors, those were used to verify the implementation.
|
||||
Wherever we were able to find official test vectors, those were used to verify the implementation.
|
||||
We do not recommend using them in a production environment, without any additional testing and/or verification.
|
||||
|
||||
### ToDo
|
||||
|
||||
@@ -30,6 +30,6 @@ equivalence.
|
||||
|
||||
For the most part, alterations to the base fiat-crypto generated code was
|
||||
kept to a minimum, to aid auditability. This results in a somewhat
|
||||
ideosyncratic style, and in some cases minor performance penalties.
|
||||
idiosyncratic style, and in some cases minor performance penalties.
|
||||
|
||||
[1]: https://github.com/mit-plv/fiat-crypto
|
||||
|
||||
@@ -233,7 +233,7 @@ init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
|
||||
}
|
||||
|
||||
update :: proc(ctx: ^Context, data: []byte) {
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
|
||||
assert(ctx.is_initialized, "crypto/siphash: Context is not initialized")
|
||||
ctx.last_block = len(data) / 8 * 8
|
||||
ctx.buf = data
|
||||
i := 0
|
||||
|
||||
@@ -25,8 +25,8 @@ import "core:strings"
|
||||
|
||||
MAX_RUNE_CODEPOINT :: int(unicode.MAX_RUNE)
|
||||
|
||||
write_rune :: strings.write_rune_builder
|
||||
write_string :: strings.write_string_builder
|
||||
write_rune :: strings.write_rune
|
||||
write_string :: strings.write_string
|
||||
|
||||
Error :: enum u8 {
|
||||
None = 0,
|
||||
@@ -94,8 +94,8 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
l := len(input)
|
||||
if l == 0 { return "", .None }
|
||||
|
||||
builder := strings.make_builder()
|
||||
defer strings.destroy_builder(&builder)
|
||||
builder := strings.builder_make()
|
||||
defer strings.builder_destroy(&builder)
|
||||
|
||||
t := Tokenizer{src=input}
|
||||
in_data := false
|
||||
|
||||
@@ -8,7 +8,7 @@ import "core:time"
|
||||
|
||||
doc_print :: proc(doc: ^xml.Document) {
|
||||
buf: strings.Builder
|
||||
defer strings.destroy_builder(&buf)
|
||||
defer strings.builder_destroy(&buf)
|
||||
w := strings.to_writer(&buf)
|
||||
|
||||
xml.print(w, doc)
|
||||
|
||||
@@ -107,7 +107,7 @@ Node :: struct {
|
||||
/* Conventions */
|
||||
/* ------------
|
||||
Much of HxA's use is based on convention. HxA lets users store arbitrary data in its structure that can be parsed but whose semantic meaning does not need to be understood.
|
||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basicly recomendations of how to store common data.
|
||||
A few conventions are hard, and some are soft. Hard convention that a user HAS to follow in order to produce a valid file. Hard conventions simplify parsing becaus the parser can make some assumptions. Soft convenbtions are basically recomendations of how to store common data.
|
||||
If you use HxA for something not covered by the conventions but need a convention for your use case. Please let us know so that we can add it!
|
||||
*/
|
||||
|
||||
|
||||
+218
-43
@@ -17,25 +17,54 @@ Marshal_Error :: union #shared_nil {
|
||||
io.Error,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.make_builder(allocator)
|
||||
// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
|
||||
Marshal_Options :: struct {
|
||||
// output based on spec
|
||||
spec: Specification,
|
||||
|
||||
// use line breaks & tab|spaces
|
||||
pretty: bool,
|
||||
|
||||
// spacing
|
||||
use_spaces: bool,
|
||||
spaces: int,
|
||||
|
||||
// state
|
||||
indentation: int,
|
||||
|
||||
// option to output uint in JSON5 & MJSON
|
||||
write_uint_as_hex: bool,
|
||||
|
||||
// mjson output options
|
||||
mjson_keys_use_quotes: bool,
|
||||
mjson_keys_use_equal_sign: bool,
|
||||
|
||||
// mjson state
|
||||
mjson_skipped_first_braces_start: bool,
|
||||
mjson_skipped_first_braces_end: bool,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator)
|
||||
defer if err != nil {
|
||||
strings.destroy_builder(&b)
|
||||
strings.builder_destroy(&b)
|
||||
}
|
||||
|
||||
marshal_to_builder(&b, v) or_return
|
||||
opt := opt
|
||||
marshal_to_builder(&b, v, &opt) or_return
|
||||
|
||||
if len(b.buf) != 0 {
|
||||
data = b.buf[:]
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v)
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v, opt)
|
||||
}
|
||||
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||
if v == nil {
|
||||
io.write_string(w, "null") or_return
|
||||
return
|
||||
@@ -49,13 +78,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
unreachable()
|
||||
|
||||
case runtime.Type_Info_Integer:
|
||||
buf: [21]byte
|
||||
buf: [40]byte
|
||||
u: u128
|
||||
switch i in a {
|
||||
case i8: u = u128(i)
|
||||
case i16: u = u128(i)
|
||||
case i32: u = u128(i)
|
||||
case i64: u = u128(i)
|
||||
case i128: u = u128(i)
|
||||
case int: u = u128(i)
|
||||
case u8: u = u128(i)
|
||||
case u16: u = u128(i)
|
||||
@@ -82,7 +112,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case u128be: u = u128(i)
|
||||
}
|
||||
|
||||
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s: string
|
||||
|
||||
// allow uints to be printed as hex
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
} else {
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
|
||||
io.write_string(w, s) or_return
|
||||
|
||||
|
||||
@@ -147,6 +191,9 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case runtime.Type_Info_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Soa_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Procedure:
|
||||
return .Unsupported_Type
|
||||
|
||||
@@ -166,52 +213,48 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||
for i in 0..<array.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
slice := cast(^mem.Raw_Slice)v.data
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
io.write_byte(w, '{') or_return
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
return .Unsupported_Type
|
||||
@@ -223,31 +266,50 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
entry_size := ed.elem_size
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
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])
|
||||
|
||||
marshal_to_writer(w, any{key, info.key.id}) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
marshal_to_writer(w, any{value, info.value.id}) or_return
|
||||
// check for string type
|
||||
{
|
||||
v := any{key, info.key.id}
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
name: string
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
||||
}
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
io.write_byte(w, '{') or_return
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
for name, i in info.names {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
id := info.types[i].id
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
marshal_to_writer(w, any{data, id}) or_return
|
||||
marshal_to_writer(w, any{data, id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||
@@ -270,11 +332,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
io.write_string(w, "null") or_return
|
||||
} else {
|
||||
id := info.variants[tag-1].id
|
||||
return marshal_to_writer(w, any{v.data, id})
|
||||
return marshal_to_writer(w, any{v.data, id}, opt)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
return marshal_to_writer(w, any{v.data, info.base.id})
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
@@ -330,3 +392,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// write key as quoted string or with optional quotes in mjson
|
||||
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
|
||||
case .MJSON:
|
||||
if opt.mjson_keys_use_quotes {
|
||||
io.write_quoted_string(w, name) or_return
|
||||
} else {
|
||||
io.write_string(w, name) or_return
|
||||
}
|
||||
|
||||
if opt.mjson_keys_use_equal_sign {
|
||||
io.write_string(w, " = ") or_return
|
||||
} else {
|
||||
io.write_string(w, ": ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert start byte and increase indentation on pretty
|
||||
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
// skip mjson starting braces
|
||||
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
|
||||
opt.mjson_skipped_first_braces_start = true
|
||||
return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
opt.indentation += 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert comma seperation and write indentations
|
||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
if iteration > 0 {
|
||||
io.write_string(w, ", ") or_return
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
|
||||
case .MJSON:
|
||||
if iteration > 0 {
|
||||
// on pretty no commas necessary
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
} else {
|
||||
// comma seperation necessary for non pretty output!
|
||||
io.write_string(w, ", ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// decrease indent, write spacing and insert end byte
|
||||
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
|
||||
if opt.indentation == 0 {
|
||||
opt.mjson_skipped_first_braces_end = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
opt.indentation -= 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
return
|
||||
}
|
||||
|
||||
// writes current indentation level based on options
|
||||
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
|
||||
if !opt.pretty {
|
||||
return
|
||||
}
|
||||
|
||||
if opt.use_spaces {
|
||||
spaces := opt.spaces == 0 ? 4 : opt.spaces
|
||||
for _ in 0..<opt.indentation * spaces {
|
||||
io.write_byte(w, ' ') or_return
|
||||
}
|
||||
} else {
|
||||
for _ in 0..<opt.indentation {
|
||||
io.write_byte(w, '\t') or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ parse_string :: proc(data: string, spec := DEFAULT_SPECIFICATION, parse_integers
|
||||
return parse_object(&p)
|
||||
case .JSON5:
|
||||
return parse_value(&p)
|
||||
case .MJSON:
|
||||
case .SJSON:
|
||||
#partial switch p.curr_token.kind {
|
||||
case .Ident, .String:
|
||||
return parse_object_body(&p, .EOF)
|
||||
|
||||
@@ -33,8 +33,9 @@ package json
|
||||
Specification :: enum {
|
||||
JSON,
|
||||
JSON5, // https://json5.org/
|
||||
MJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||
Bitsquid = MJSON,
|
||||
SJSON, // https://bitsquid.blogspot.com/2009/10/simplified-json-notation.html
|
||||
Bitsquid = SJSON,
|
||||
MJSON = SJSON,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
|
||||
|
||||
if end_token == .Close_Brace {
|
||||
assert(expect_token(p, .Open_Brace) == nil)
|
||||
unmarshal_expect_token(p, .Open_Brace)
|
||||
}
|
||||
|
||||
v := v
|
||||
@@ -473,7 +473,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
}
|
||||
|
||||
if end_token == .Close_Brace {
|
||||
assert(expect_token(p, .Close_Brace) == nil)
|
||||
unmarshal_expect_token(p, .Close_Brace)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ example :: proc() {
|
||||
|
||||
doc_hash :: proc(doc: ^xml.Document, print := false) -> (crc32: u32) {
|
||||
buf: strings.Builder
|
||||
defer strings.destroy_builder(&buf)
|
||||
defer strings.builder_destroy(&buf)
|
||||
w := strings.to_writer(&buf)
|
||||
|
||||
xml.print(w, doc)
|
||||
|
||||
+649
-490
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1,8 +1,8 @@
|
||||
package hash
|
||||
|
||||
@(optimization_mode="speed")
|
||||
crc64_ecma_182 :: proc(data: []byte, seed := u32(0)) -> u64 #no_bounds_check {
|
||||
result := u64(seed)
|
||||
crc64_ecma_182 :: proc(data: []byte, seed := u64(0)) -> (result: u64) #no_bounds_check {
|
||||
result = seed
|
||||
#no_bounds_check for b in data {
|
||||
result = result<<8 ~ _crc64_table_ecma_182[((result>>56) ~ u64(b)) & 0xff]
|
||||
}
|
||||
|
||||
+101
-95
@@ -172,108 +172,114 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
|
||||
return h1
|
||||
}
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L96
|
||||
@(optimization_mode="speed")
|
||||
murmur64 :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
murmur64a :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
|
||||
h: u64 = seed ~ (u64(len(data)) * m)
|
||||
data64 := mem.slice_ptr(cast(^u64)raw_data(data), len(data)/size_of(u64))
|
||||
h: u64 = seed ~ (u64(len(data)) * m)
|
||||
data64 := mem.slice_data_cast([]u64, data)
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i]
|
||||
for _, i in data64 {
|
||||
k := data64[i]
|
||||
|
||||
k *= m
|
||||
k ~= k>>r
|
||||
k *= m
|
||||
k *= m
|
||||
k ~= k>>r
|
||||
k *= m
|
||||
|
||||
h ~= k
|
||||
h *= m
|
||||
}
|
||||
|
||||
switch len(data)&7 {
|
||||
case 7: h ~= u64(data[6]) << 48; fallthrough
|
||||
case 6: h ~= u64(data[5]) << 40; fallthrough
|
||||
case 5: h ~= u64(data[4]) << 32; fallthrough
|
||||
case 4: h ~= u64(data[3]) << 24; fallthrough
|
||||
case 3: h ~= u64(data[2]) << 16; fallthrough
|
||||
case 2: h ~= u64(data[1]) << 8; fallthrough
|
||||
case 1:
|
||||
h ~= u64(data[0])
|
||||
h *= m
|
||||
}
|
||||
|
||||
h ~= h>>r
|
||||
h ~= k
|
||||
h *= m
|
||||
h ~= h>>r
|
||||
|
||||
return h
|
||||
} else {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
h1 := u32(seed) ~ u32(len(data))
|
||||
h2 := u32(seed) >> 32
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||
len := len(data)
|
||||
i := 0
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
|
||||
k2 = data32[i]; i += 1
|
||||
k2 *= m
|
||||
k2 ~= k2>>r
|
||||
k2 *= m
|
||||
h2 *= m
|
||||
h2 ~= k2
|
||||
len -= 4
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
h2 ~= u32(data8[0])
|
||||
h2 *= m
|
||||
}
|
||||
|
||||
h1 ~= h2>>18
|
||||
h1 *= m
|
||||
h2 ~= h1>>22
|
||||
h2 *= m
|
||||
h1 ~= h2>>17
|
||||
h1 *= m
|
||||
h2 ~= h1>>19
|
||||
h2 *= m
|
||||
|
||||
return u64(h1)<<32 | u64(h2)
|
||||
}
|
||||
|
||||
offset := len(data64) * size_of(u64)
|
||||
|
||||
switch len(data)&7 {
|
||||
case 7: h ~= u64(data[offset + 6]) << 48; fallthrough
|
||||
case 6: h ~= u64(data[offset + 5]) << 40; fallthrough
|
||||
case 5: h ~= u64(data[offset + 4]) << 32; fallthrough
|
||||
case 4: h ~= u64(data[offset + 3]) << 24; fallthrough
|
||||
case 3: h ~= u64(data[offset + 2]) << 16; fallthrough
|
||||
case 2: h ~= u64(data[offset + 1]) << 8; fallthrough
|
||||
case 1:
|
||||
h ~= u64(data[offset + 0])
|
||||
h *= m
|
||||
}
|
||||
|
||||
h ~= h>>r
|
||||
h *= m
|
||||
h ~= h>>r
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// See https://github.com/aappleby/smhasher/blob/master/src/MurmurHash2.cpp#L140
|
||||
@(optimization_mode="speed")
|
||||
murmur64b :: proc(data: []byte, seed := u64(0x9747b28c)) -> u64 {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
|
||||
h1 := u32(seed) ~ u32(len(data))
|
||||
h2 := u32(seed) >> 32
|
||||
|
||||
data32 := mem.slice_ptr(cast(^u32)raw_data(data), len(data)/size_of(u32))
|
||||
len := len(data)
|
||||
i := 0
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
|
||||
k2 = data32[i]; i += 1
|
||||
k2 *= m
|
||||
k2 ~= k2>>r
|
||||
k2 *= m
|
||||
h2 *= m
|
||||
h2 ~= k2
|
||||
len -= 4
|
||||
}
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32
|
||||
k1 = data32[i]; i += 1
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
}
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := mem.slice_to_bytes(data32[i:])[:3]
|
||||
switch len {
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16
|
||||
fallthrough
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8
|
||||
fallthrough
|
||||
case 1:
|
||||
h2 ~= u32(data8[0])
|
||||
h2 *= m
|
||||
}
|
||||
|
||||
h1 ~= h2>>18
|
||||
h1 *= m
|
||||
h2 ~= h1>>22
|
||||
h2 *= m
|
||||
h1 ~= h2>>17
|
||||
h1 *= m
|
||||
h2 ~= h1>>19
|
||||
h2 *= m
|
||||
|
||||
return u64(h1)<<32 | u64(h2)
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
|
||||
+65
-7
@@ -46,7 +46,7 @@ Image :: struct {
|
||||
height: int,
|
||||
channels: int,
|
||||
depth: int, // Channel depth in bits, typically 8 or 16
|
||||
pixels: bytes.Buffer,
|
||||
pixels: bytes.Buffer `fmt:"-"`,
|
||||
/*
|
||||
Some image loaders/writers can return/take an optional background color.
|
||||
For convenience, we return them as u16 so we don't need to switch on the type
|
||||
@@ -61,6 +61,7 @@ Image_Metadata :: union #shared_nil {
|
||||
^Netpbm_Info,
|
||||
^PNG_Info,
|
||||
^QOI_Info,
|
||||
^TGA_Info,
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +169,7 @@ Error :: union #shared_nil {
|
||||
|
||||
General_Image_Error :: enum {
|
||||
None = 0,
|
||||
Unsupported_Option,
|
||||
// File I/O
|
||||
Unable_To_Read_File,
|
||||
Unable_To_Write_File,
|
||||
@@ -376,10 +378,20 @@ QOI_Info :: struct {
|
||||
header: QOI_Header,
|
||||
}
|
||||
|
||||
TGA_Data_Type :: enum u8 {
|
||||
No_Image_Data = 0,
|
||||
Uncompressed_Color_Mapped = 1,
|
||||
Uncompressed_RGB = 2,
|
||||
Uncompressed_Black_White = 3,
|
||||
Compressed_Color_Mapped = 9,
|
||||
Compressed_RGB = 10,
|
||||
Compressed_Black_White = 11,
|
||||
}
|
||||
|
||||
TGA_Header :: struct #packed {
|
||||
id_length: u8,
|
||||
color_map_type: u8,
|
||||
data_type_code: u8,
|
||||
data_type_code: TGA_Data_Type,
|
||||
color_map_origin: u16le,
|
||||
color_map_length: u16le,
|
||||
color_map_depth: u8,
|
||||
@@ -390,6 +402,52 @@ TGA_Header :: struct #packed {
|
||||
}
|
||||
#assert(size_of(TGA_Header) == 18)
|
||||
|
||||
New_TGA_Signature :: "TRUEVISION-XFILE.\x00"
|
||||
|
||||
TGA_Footer :: struct #packed {
|
||||
extension_area_offset: u32le,
|
||||
developer_directory_offset: u32le,
|
||||
signature: [18]u8 `fmt:"s,0"`, // Should match signature if New TGA.
|
||||
}
|
||||
#assert(size_of(TGA_Footer) == 26)
|
||||
|
||||
TGA_Extension :: struct #packed {
|
||||
extension_size: u16le, // Size of this struct. If not 495 bytes it means it's an unsupported version.
|
||||
author_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
|
||||
author_comments: [324]u8 `fmt:"s,0"`, // Author comments, formatted as 4 lines of 80 character lines, each zero terminated.
|
||||
datetime: struct {month, day, year, hour, minute, second: u16le},
|
||||
job_name: [41]u8 `fmt:"s,0"`, // Author name, ASCII. Zero-terminated
|
||||
job_time: struct {hour, minute, second: u16le},
|
||||
software_id: [41]u8 `fmt:"s,0"`, // Software ID name, ASCII. Zero-terminated
|
||||
software_version: struct #packed {
|
||||
number: u16le, // Version number * 100
|
||||
letter: u8 `fmt:"r"`, // " " if not used
|
||||
},
|
||||
key_color: [4]u8, // ARGB key color used at time of production
|
||||
aspect_ratio: [2]u16le, // Numerator / Denominator
|
||||
gamma: [2]u16le, // Numerator / Denominator, range should be 0.0..10.0
|
||||
color_correction_offset: u32le, // 0 if no color correction information
|
||||
postage_stamp_offset: u32le, // 0 if no thumbnail
|
||||
scanline_offset: u32le, // 0 if no scanline table
|
||||
attributes: TGA_Alpha_Kind,
|
||||
}
|
||||
#assert(size_of(TGA_Extension) == 495)
|
||||
|
||||
TGA_Alpha_Kind :: enum u8 {
|
||||
None,
|
||||
Undefined_Ignore,
|
||||
Undefined_Retain,
|
||||
Useful,
|
||||
Premultiplied,
|
||||
}
|
||||
|
||||
TGA_Info :: struct {
|
||||
header: TGA_Header,
|
||||
image_id: string,
|
||||
footer: Maybe(TGA_Footer),
|
||||
extension: Maybe(TGA_Extension),
|
||||
}
|
||||
|
||||
// Function to help with image buffer calculations
|
||||
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
|
||||
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
|
||||
@@ -469,7 +527,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
|
||||
}
|
||||
|
||||
// Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
@@ -489,7 +547,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// This returns 0 if any of the inputs is zero.
|
||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||
|
||||
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
// If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||
return false
|
||||
}
|
||||
@@ -498,7 +556,7 @@ is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
@@ -518,7 +576,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// This returns 0 if any of the inputs is zero.
|
||||
bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
|
||||
|
||||
// If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
// If the dimensions are invalid or the buffer size doesn't match the image characteristics, bail.
|
||||
if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
|
||||
return false
|
||||
}
|
||||
@@ -527,7 +585,7 @@ is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
}
|
||||
|
||||
// Does the image have 1..4 channels, a valid bit depth (8 or 16),
|
||||
// Is the pointer valid, are the dimenions valid?
|
||||
// Is the pointer valid, are the dimensions valid?
|
||||
is_valid_image :: proc(img: ^Image) -> (ok: bool) {
|
||||
// Were we actually given a valid image?
|
||||
if img == nil {
|
||||
|
||||
@@ -130,7 +130,7 @@ save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context
|
||||
|
||||
// we will write to a string builder
|
||||
data: strings.Builder
|
||||
strings.init_builder(&data)
|
||||
strings.builder_init(&data)
|
||||
|
||||
// all PNM headers start with the format
|
||||
fmt.sbprintf(&data, "%s\n", header.format)
|
||||
@@ -409,7 +409,7 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
|
||||
|
||||
// string buffer for the tupltype
|
||||
tupltype: strings.Builder
|
||||
strings.init_builder(&tupltype, context.temp_allocator); defer strings.destroy_builder(&tupltype)
|
||||
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
|
||||
fmt.sbprint(&tupltype, "")
|
||||
|
||||
// PAM uses actual lines, so we can iterate easily
|
||||
|
||||
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
||||
defer close(fd)
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
|
||||
)
|
||||
|
||||
if channels == 3 {
|
||||
|
||||
@@ -1002,7 +1002,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o16 = o16[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1195,7 +1195,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
o = o[out_image_channels:]
|
||||
}
|
||||
case:
|
||||
unreachable("We should never seen # channels other than 1-4 inclusive.")
|
||||
panic("We should never seen # channels other than 1-4 inclusive.")
|
||||
}
|
||||
|
||||
img.pixels = t
|
||||
@@ -1206,7 +1206,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
This may change if we ever don't expand 1, 2 and 4 bit images. But, those raw
|
||||
returns will likely bypass this processing pipeline.
|
||||
*/
|
||||
unreachable("We should never see bit depths other than 8, 16 and 'Paletted' here.")
|
||||
panic("We should never see bit depths other than 8, 16 and 'Paletted' here.")
|
||||
}
|
||||
|
||||
return img, nil
|
||||
|
||||
+341
-2
@@ -4,6 +4,7 @@
|
||||
|
||||
List of contributors:
|
||||
Jeroen van Rijn: Initial implementation.
|
||||
Benoit Jacquier: tga loader
|
||||
*/
|
||||
|
||||
|
||||
@@ -14,11 +15,16 @@ import "core:mem"
|
||||
import "core:image"
|
||||
import "core:bytes"
|
||||
import "core:os"
|
||||
import "core:compress"
|
||||
import "core:strings"
|
||||
|
||||
// TODO: alpha_premultiply support
|
||||
|
||||
Error :: image.Error
|
||||
Image :: image.Image
|
||||
Options :: image.Options
|
||||
|
||||
GA_Pixel :: image.GA_Pixel
|
||||
RGB_Pixel :: image.RGB_Pixel
|
||||
RGBA_Pixel :: image.RGBA_Pixel
|
||||
|
||||
@@ -57,7 +63,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
|
||||
}
|
||||
|
||||
header := image.TGA_Header{
|
||||
data_type_code = 0x02, // Color, uncompressed.
|
||||
data_type_code = .Uncompressed_RGB,
|
||||
dimensions = {u16le(img.width), u16le(img.height)},
|
||||
bits_per_pixel = u8(img.depth * img.channels),
|
||||
image_descriptor = 1 << 5, // Origin is top left.
|
||||
@@ -98,4 +104,337 @@ save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocato
|
||||
return nil if write_ok else .Unable_To_Write_File
|
||||
}
|
||||
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
save :: proc{save_to_memory, save_to_file}
|
||||
|
||||
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
options := options
|
||||
|
||||
if .alpha_premultiply in options {
|
||||
return nil, .Unsupported_Option
|
||||
}
|
||||
|
||||
if .info in options {
|
||||
options |= {.return_metadata, .do_not_decompress_image}
|
||||
options -= {.info}
|
||||
}
|
||||
|
||||
if .return_header in options && .return_metadata in options {
|
||||
options -= {.return_header}
|
||||
}
|
||||
|
||||
// First check for a footer.
|
||||
filesize := compress.input_size(ctx) or_return
|
||||
|
||||
footer: image.TGA_Footer
|
||||
have_valid_footer := false
|
||||
|
||||
extension: image.TGA_Extension
|
||||
have_valid_extension := false
|
||||
|
||||
if filesize >= size_of(image.TGA_Header) + size_of(image.TGA_Footer) {
|
||||
if f, f_err := compress.peek_data(ctx, image.TGA_Footer, filesize - i64(size_of(image.TGA_Footer))); f_err == .None {
|
||||
if string(f.signature[:]) == image.New_TGA_Signature {
|
||||
have_valid_footer = true
|
||||
footer = f
|
||||
|
||||
if i64(footer.extension_area_offset) + i64(size_of(image.TGA_Extension)) < filesize {
|
||||
if e, e_err := compress.peek_data(ctx, image.TGA_Extension, footer.extension_area_offset); e_err == .None {
|
||||
if e.extension_size == size_of(image.TGA_Extension) {
|
||||
have_valid_extension = true
|
||||
extension = e
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header := image.read_data(ctx, image.TGA_Header) or_return
|
||||
|
||||
// Header checks
|
||||
rle_encoding := false
|
||||
color_mapped := false
|
||||
black_white := false
|
||||
src_channels := 0
|
||||
dest_depth := header.bits_per_pixel
|
||||
dest_channels := 0
|
||||
|
||||
#partial switch header.data_type_code {
|
||||
// Supported formats: RGB(A), RGB(A) RLE
|
||||
case .Compressed_RGB:
|
||||
rle_encoding = true
|
||||
case .Uncompressed_RGB:
|
||||
// Intentionally blank
|
||||
case .Uncompressed_Black_White:
|
||||
black_white = true
|
||||
dest_depth = 24
|
||||
case .Uncompressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
case .Compressed_Color_Mapped:
|
||||
color_mapped = true
|
||||
rle_encoding = true
|
||||
case .Compressed_Black_White:
|
||||
black_white = true
|
||||
rle_encoding = true
|
||||
dest_depth = 24
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if color_mapped {
|
||||
if header.color_map_type != 1 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
dest_depth = header.color_map_depth
|
||||
|
||||
// Expect LUT entry index to be 8 bits
|
||||
if header.bits_per_pixel != 8 || header.color_map_origin != 0 || header.color_map_length > 256 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
}
|
||||
|
||||
switch dest_depth {
|
||||
case 15: // B5G5R5
|
||||
src_channels = 2
|
||||
dest_channels = 3
|
||||
if color_mapped {
|
||||
src_channels = 1
|
||||
}
|
||||
case 16: // B5G5R5A1
|
||||
src_channels = 2
|
||||
dest_channels = 3 // Alpha bit is dodgy in TGA, so we ignore it.
|
||||
if color_mapped {
|
||||
src_channels = 1
|
||||
}
|
||||
case 24: // RGB8
|
||||
src_channels = 1 if (color_mapped || black_white) else 3
|
||||
dest_channels = 3
|
||||
case 32: // RGBA8
|
||||
src_channels = 4 if !color_mapped else 1
|
||||
dest_channels = 4
|
||||
|
||||
case:
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if header.image_descriptor & IMAGE_DESCRIPTOR_INTERLEAVING_MASK != 0 {
|
||||
return nil, .Unsupported_Format
|
||||
}
|
||||
|
||||
if int(header.dimensions[0]) * int(header.dimensions[1]) > image.MAX_DIMENSIONS {
|
||||
return nil, .Image_Dimensions_Too_Large
|
||||
}
|
||||
|
||||
if img == nil {
|
||||
img = new(Image)
|
||||
}
|
||||
|
||||
defer if err != nil {
|
||||
destroy(img)
|
||||
}
|
||||
|
||||
img.which = .TGA
|
||||
img.channels = 4 if .alpha_add_if_missing in options else dest_channels
|
||||
img.channels = 3 if .alpha_drop_if_present in options else img.channels
|
||||
|
||||
img.depth = 8
|
||||
img.width = int(header.dimensions[0])
|
||||
img.height = int(header.dimensions[1])
|
||||
|
||||
// Read Image ID if present
|
||||
image_id := ""
|
||||
if _id, e := compress.read_slice(ctx, int(header.id_length)); e != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
if .return_metadata in options {
|
||||
id := strings.trim_right_null(string(_id))
|
||||
image_id = strings.clone(id)
|
||||
}
|
||||
}
|
||||
|
||||
color_map := make([]RGBA_Pixel, header.color_map_length)
|
||||
defer delete(color_map)
|
||||
|
||||
if color_mapped {
|
||||
switch header.color_map_depth {
|
||||
case 16:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, GA_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i].rg = lut
|
||||
color_map[i].ba = 255
|
||||
}
|
||||
}
|
||||
|
||||
case 24:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, RGB_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i].rgb = lut
|
||||
color_map[i].a = 255
|
||||
}
|
||||
}
|
||||
|
||||
case 32:
|
||||
for i in 0..<header.color_map_length {
|
||||
if lut, lut_err := compress.read_data(ctx, RGBA_Pixel); lut_err != .None {
|
||||
return img, .Corrupt
|
||||
} else {
|
||||
color_map[i] = lut
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if .return_metadata in options {
|
||||
info := new(image.TGA_Info)
|
||||
info.header = header
|
||||
info.image_id = image_id
|
||||
if have_valid_footer {
|
||||
info.footer = footer
|
||||
}
|
||||
if have_valid_extension {
|
||||
info.extension = extension
|
||||
}
|
||||
img.metadata = info
|
||||
}
|
||||
|
||||
if .do_not_decompress_image in options {
|
||||
return img, nil
|
||||
}
|
||||
|
||||
if !resize(&img.pixels.buf, dest_channels * img.width * img.height) {
|
||||
return img, .Unable_To_Allocate_Or_Resize
|
||||
}
|
||||
|
||||
origin_is_top := header.image_descriptor & IMAGE_DESCRIPTOR_TOP_MASK != 0
|
||||
origin_is_left := header.image_descriptor & IMAGE_DESCRIPTOR_RIGHT_MASK == 0
|
||||
rle_repetition_count := 0
|
||||
read_pixel := true
|
||||
is_packet_rle := false
|
||||
|
||||
pixel: RGBA_Pixel
|
||||
|
||||
stride := img.width * dest_channels
|
||||
line := 0 if origin_is_top else img.height - 1
|
||||
|
||||
for _ in 0..<img.height {
|
||||
offset := line * stride + (0 if origin_is_left else (stride - dest_channels))
|
||||
for _ in 0..<img.width {
|
||||
// handle RLE decoding
|
||||
if rle_encoding {
|
||||
if rle_repetition_count == 0 {
|
||||
rle_cmd, err := compress.read_u8(ctx)
|
||||
if err != .None {
|
||||
return img, .Corrupt
|
||||
}
|
||||
is_packet_rle = (rle_cmd >> 7) != 0
|
||||
rle_repetition_count = 1 + int(rle_cmd & 0x7F)
|
||||
read_pixel = true
|
||||
} else if !is_packet_rle {
|
||||
read_pixel = rle_repetition_count > 0
|
||||
} else {
|
||||
read_pixel = false
|
||||
}
|
||||
}
|
||||
// Read pixel
|
||||
if read_pixel {
|
||||
src, src_err := compress.read_slice(ctx, src_channels)
|
||||
if src_err != .None {
|
||||
return img, .Corrupt
|
||||
}
|
||||
switch src_channels {
|
||||
case 1:
|
||||
// Color-mapped or Black & White
|
||||
if black_white {
|
||||
pixel = {src[0], src[0], src[0], 255}
|
||||
} else if header.color_map_depth == 24 {
|
||||
pixel = color_map[src[0]].bgra
|
||||
} else if header.color_map_depth == 16 {
|
||||
lut := color_map[src[0]]
|
||||
v := u16(lut.r) | u16(lut.g) << 8
|
||||
b := u8( v & 31) << 3
|
||||
g := u8((v >> 5) & 31) << 3
|
||||
r := u8((v >> 10) & 31) << 3
|
||||
pixel = {r, g, b, 255}
|
||||
}
|
||||
|
||||
case 2:
|
||||
v := u16(src[0]) | u16(src[1]) << 8
|
||||
b := u8( v & 31) << 3
|
||||
g := u8((v >> 5) & 31) << 3
|
||||
r := u8((v >> 10) & 31) << 3
|
||||
pixel = {r, g, b, 255}
|
||||
|
||||
case 3:
|
||||
pixel = {src[2], src[1], src[0], 255}
|
||||
case 4:
|
||||
pixel = {src[2], src[1], src[0], src[3]}
|
||||
case:
|
||||
return img, .Corrupt
|
||||
}
|
||||
}
|
||||
|
||||
// Write pixel
|
||||
copy(img.pixels.buf[offset:], pixel[:dest_channels])
|
||||
offset += dest_channels if origin_is_left else -dest_channels
|
||||
rle_repetition_count -= 1
|
||||
}
|
||||
line += 1 if origin_is_top else -1
|
||||
}
|
||||
return img, nil
|
||||
}
|
||||
|
||||
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
ctx := &compress.Context_Memory_Input{
|
||||
input_data = data,
|
||||
}
|
||||
|
||||
img, err = load_from_context(ctx, options, allocator)
|
||||
return img, err
|
||||
}
|
||||
|
||||
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
data, ok := os.read_entire_file(filename)
|
||||
defer delete(data)
|
||||
|
||||
if ok {
|
||||
return load_from_bytes(data, options)
|
||||
} else {
|
||||
return nil, .Unable_To_Read_File
|
||||
}
|
||||
}
|
||||
|
||||
load :: proc{load_from_file, load_from_bytes, load_from_context}
|
||||
|
||||
destroy :: proc(img: ^Image) {
|
||||
if img == nil || img.width == 0 || img.height == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
if v, ok := img.metadata.(^image.TGA_Info); ok {
|
||||
delete(v.image_id)
|
||||
free(v)
|
||||
}
|
||||
|
||||
// Make destroy idempotent
|
||||
img.width = 0
|
||||
img.height = 0
|
||||
free(img)
|
||||
}
|
||||
|
||||
IMAGE_DESCRIPTOR_INTERLEAVING_MASK :: (1<<6) | (1<<7)
|
||||
IMAGE_DESCRIPTOR_RIGHT_MASK :: 1<<4
|
||||
IMAGE_DESCRIPTOR_TOP_MASK :: 1<<5
|
||||
|
||||
@(init, private)
|
||||
_register :: proc() {
|
||||
image.register(.TGA, load_from_bytes, destroy)
|
||||
}
|
||||
@@ -194,8 +194,7 @@ constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
simd_add :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_sub :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_mul :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_rem :: proc(a, b: #simd[N]T) -> #simd[N]T ---
|
||||
simd_div :: proc(a, b: #simd[N]T) -> #simd[N]T where type_is_float(T) ---
|
||||
|
||||
// Keeps Odin's Behaviour
|
||||
// (x << y) if y <= mask else 0
|
||||
@@ -296,6 +295,9 @@ objc_register_selector :: proc($name: string) -> objc_SEL ---
|
||||
objc_find_class :: proc($name: string) -> objc_Class ---
|
||||
objc_register_class :: proc($name: string) -> objc_Class ---
|
||||
|
||||
|
||||
valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2, a3, a4: uintptr) -> uintptr ---
|
||||
|
||||
// Internal compiler use only
|
||||
|
||||
__entry_point :: proc() ---
|
||||
@@ -247,6 +247,30 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
|
||||
return
|
||||
}
|
||||
|
||||
// writer append a quoted rune into the byte buffer, return the written size
|
||||
write_quoted_rune :: proc(w: Writer, r: rune) -> (n: int) {
|
||||
_write_byte :: #force_inline proc(w: Writer, c: byte) -> int {
|
||||
err := write_byte(w, c)
|
||||
return 1 if err == nil else 0
|
||||
}
|
||||
|
||||
quote := byte('\'')
|
||||
n += _write_byte(w, quote)
|
||||
buf, width := utf8.encode_rune(r)
|
||||
if width == 1 && r == utf8.RUNE_ERROR {
|
||||
n += _write_byte(w, '\\')
|
||||
n += _write_byte(w, 'x')
|
||||
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
|
||||
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
|
||||
} else {
|
||||
i, _ := write_escaped_rune(w, r, quote)
|
||||
n += i
|
||||
}
|
||||
n += _write_byte(w, quote)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Tee_Reader :: struct {
|
||||
|
||||
@@ -56,7 +56,7 @@ create_console_logger :: proc(lowest := Level.Debug, opt := Default_Console_Logg
|
||||
return Logger{file_console_logger_proc, data, lowest, opt}
|
||||
}
|
||||
|
||||
destroy_console_logger :: proc(log: ^Logger) {
|
||||
destroy_console_logger :: proc(log: Logger) {
|
||||
free(log.data)
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
fmt.sbprintf(&buf, "[%s] ", data.ident)
|
||||
}
|
||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text)
|
||||
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
|
||||
}
|
||||
|
||||
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
|
||||
+5
-6
@@ -6,7 +6,6 @@ import "core:fmt"
|
||||
|
||||
// NOTE(bill, 2019-12-31): These are defined in `package runtime` as they are used in the `context`. This is to prevent an import definition cycle.
|
||||
|
||||
Level :: runtime.Logger_Level
|
||||
/*
|
||||
Logger_Level :: enum {
|
||||
Debug = 0,
|
||||
@@ -16,8 +15,8 @@ Logger_Level :: enum {
|
||||
Fatal = 40,
|
||||
}
|
||||
*/
|
||||
Level :: runtime.Logger_Level
|
||||
|
||||
Option :: runtime.Logger_Option
|
||||
/*
|
||||
Option :: enum {
|
||||
Level,
|
||||
@@ -30,11 +29,12 @@ Option :: enum {
|
||||
Terminal_Color
|
||||
}
|
||||
*/
|
||||
Option :: runtime.Logger_Option
|
||||
|
||||
Options :: runtime.Logger_Options
|
||||
/*
|
||||
Options :: bit_set[Option];
|
||||
*/
|
||||
Options :: runtime.Logger_Options
|
||||
|
||||
Full_Timestamp_Opts :: Options{
|
||||
.Date,
|
||||
@@ -52,12 +52,11 @@ Location_File_Opts :: Options{
|
||||
}
|
||||
|
||||
|
||||
Logger_Proc :: runtime.Logger_Proc
|
||||
/*
|
||||
Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location);
|
||||
*/
|
||||
Logger_Proc :: runtime.Logger_Proc
|
||||
|
||||
Logger :: runtime.Logger
|
||||
/*
|
||||
Logger :: struct {
|
||||
procedure: Logger_Proc,
|
||||
@@ -66,6 +65,7 @@ Logger :: struct {
|
||||
options: Logger_Options,
|
||||
}
|
||||
*/
|
||||
Logger :: runtime.Logger
|
||||
|
||||
nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) {
|
||||
// Do nothing
|
||||
@@ -75,7 +75,6 @@ nil_logger :: proc() -> Logger {
|
||||
return Logger{nil_logger_proc, nil, Level.Debug, nil}
|
||||
}
|
||||
|
||||
// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`?
|
||||
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
|
||||
logf(level=.Debug, fmt_str=fmt_str, args=args, location=location)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package log
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
Log_Allocator :: struct {
|
||||
allocator: runtime.Allocator,
|
||||
level: Level,
|
||||
prefix: string,
|
||||
locked: bool,
|
||||
}
|
||||
|
||||
log_allocator_init :: proc(la: ^Log_Allocator, level: Level, allocator := context.allocator, prefix := "") {
|
||||
la.allocator = allocator
|
||||
la.level = level
|
||||
la.prefix = prefix
|
||||
la.locked = false
|
||||
}
|
||||
|
||||
|
||||
log_allocator :: proc(la: ^Log_Allocator) -> runtime.Allocator {
|
||||
return runtime.Allocator{
|
||||
procedure = log_allocator_proc,
|
||||
data = la,
|
||||
}
|
||||
}
|
||||
|
||||
log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, runtime.Allocator_Error) {
|
||||
la := (^Log_Allocator)(allocator_data)
|
||||
|
||||
padding := " " if la.prefix != "" else ""
|
||||
|
||||
if !la.locked {
|
||||
la.locked = true
|
||||
defer la.locked = false
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Free:
|
||||
if old_size != 0 {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
|
||||
args = {la.prefix, padding, old_memory, old_size},
|
||||
location = location,
|
||||
)
|
||||
} else {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
|
||||
args = {la.prefix, padding, old_memory},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
case .Free_All:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
case .Resize:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, old_memory, old_size, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Query_Features:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
case .Query_Info:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
|
||||
args = {la.prefix, padding},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
data, err := la.allocator.procedure(la.allocator.data, mode, size, alignment, old_memory, old_size, location)
|
||||
if !la.locked {
|
||||
la.locked = true
|
||||
defer la.locked = false
|
||||
if err != nil {
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%ALLOCATOR ERROR=%v",
|
||||
args = {la.prefix, padding, error},
|
||||
location = location,
|
||||
)
|
||||
}
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
@@ -449,7 +449,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
|
||||
in the loop is non-zero, although very low.
|
||||
-- NOTE(Jeroen): This is not yet true in Odin, but I have some ideas.
|
||||
|
||||
If the BPSW test and/or the addtional Frobenious test have been
|
||||
If the BPSW test and/or the additional Frobenious test have been
|
||||
performed instead of just the Miller-Rabin test with the bases 2 and 3,
|
||||
a single extra test should suffice, so such a very unlikely event will not do much harm.
|
||||
|
||||
|
||||
+23
-11
@@ -325,9 +325,9 @@ ease :: proc "contextless" (type: Ease, p: $T) -> T
|
||||
// in case type was invalid
|
||||
return 0
|
||||
}
|
||||
|
||||
Flux_Map :: struct($T: typeid) {
|
||||
values: map[^T]Flux_Tween(T),
|
||||
keys_to_be_deleted: [dynamic]^T,
|
||||
}
|
||||
|
||||
Flux_Tween :: struct($T: typeid) {
|
||||
@@ -353,15 +353,17 @@ Flux_Tween :: struct($T: typeid) {
|
||||
}
|
||||
|
||||
// init flux map to a float type and a wanted cap
|
||||
flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||
flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
|
||||
return {
|
||||
make(map[^T]Flux_Tween(T), cap),
|
||||
values = make(map[^T]Flux_Tween(T), value_capacity),
|
||||
keys_to_be_deleted = make([dynamic]^T, 0, value_capacity),
|
||||
}
|
||||
}
|
||||
|
||||
// delete map content
|
||||
flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
|
||||
delete(flux.values)
|
||||
delete(flux.keys_to_be_deleted)
|
||||
}
|
||||
|
||||
// clear map content, stops all animations
|
||||
@@ -374,8 +376,8 @@ flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
|
||||
// return value can be used to set callbacks
|
||||
flux_to :: proc(
|
||||
flux: ^Flux_Map($T),
|
||||
value: ^f32,
|
||||
goal: f32,
|
||||
value: ^T,
|
||||
goal: T,
|
||||
type: Ease = .Quadratic_Out,
|
||||
duration: time.Duration = time.Second,
|
||||
delay: f64 = 0,
|
||||
@@ -413,6 +415,8 @@ flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where i
|
||||
// calls callbacks in all stages, when they're filled
|
||||
// deletes tween from the map after completion
|
||||
flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
|
||||
clear(&flux.keys_to_be_deleted)
|
||||
|
||||
for key, tween in &flux.values {
|
||||
delay_remainder := f64(0)
|
||||
|
||||
@@ -451,7 +455,8 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
|
||||
}
|
||||
|
||||
if tween.progress >= 1 {
|
||||
delete_key(&flux.values, key)
|
||||
// append keys to array that will be deleted after the loop
|
||||
append(&flux.keys_to_be_deleted, key)
|
||||
|
||||
if tween.on_complete != nil {
|
||||
tween.on_complete(flux, tween.data)
|
||||
@@ -459,17 +464,24 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loop through keys that should be deleted from the map
|
||||
if len(flux.keys_to_be_deleted) != 0 {
|
||||
for key in flux.keys_to_be_deleted {
|
||||
delete_key(&flux.values, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// stop a specific key inside the map
|
||||
// returns true when it successfully removed the key
|
||||
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
|
||||
if key in flux.values {
|
||||
delete_key(&flux.values, key)
|
||||
return true
|
||||
}
|
||||
if key in flux.values {
|
||||
delete_key(&flux.values, key)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
return false
|
||||
}
|
||||
|
||||
// returns the amount of time left for the tween animation, if the key exists in the map
|
||||
|
||||
@@ -426,14 +426,14 @@ distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
}
|
||||
|
||||
reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
b := n * (2 * dot(n, i))
|
||||
return i - b
|
||||
b := N * (2 * dot(N, I))
|
||||
return I - b
|
||||
}
|
||||
refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
|
||||
dv := dot(n, i)
|
||||
dv := dot(N, I)
|
||||
k := 1 - eta*eta - (1 - dv*dv)
|
||||
a := i * eta
|
||||
b := n * eta*dv*math.sqrt(k)
|
||||
a := I * eta
|
||||
b := N * eta*dv*math.sqrt(k)
|
||||
return (a - b) * E(int(k >= 0))
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@ quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
|
||||
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
|
||||
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
|
||||
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
|
||||
return v / length(v)
|
||||
}
|
||||
quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
@@ -102,7 +102,7 @@ quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
}
|
||||
normalize :: proc{vector_normalize, quaternion_normalize}
|
||||
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_NUMERIC(E) {
|
||||
vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
|
||||
m := length(v)
|
||||
return 0 if m == 0 else v/m
|
||||
}
|
||||
@@ -113,7 +113,7 @@ quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
|
||||
normalize0 :: proc{vector_normalize0, quaternion_normalize0}
|
||||
|
||||
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
|
||||
vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) {
|
||||
return math.sqrt(dot(v, v))
|
||||
}
|
||||
|
||||
|
||||
@@ -476,21 +476,21 @@ quaternion_angle_axis :: proc{
|
||||
|
||||
angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||
}
|
||||
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||
}
|
||||
|
||||
return math.acos(q.w) * 2
|
||||
}
|
||||
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
|
||||
if abs(q.w) > math.SQRT_THREE*0.5 {
|
||||
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
|
||||
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
|
||||
}
|
||||
|
||||
return math.acos(q.w) * 2
|
||||
|
||||
+37
-11
@@ -185,16 +185,23 @@ log :: proc{
|
||||
log_f64, log_f64le, log_f64be,
|
||||
}
|
||||
|
||||
log2_f16 :: logb_f16
|
||||
log2_f16le :: logb_f16le
|
||||
log2_f16be :: logb_f16be
|
||||
log2_f32 :: logb_f32
|
||||
log2_f32le :: logb_f32le
|
||||
log2_f32be :: logb_f32be
|
||||
log2_f64 :: logb_f64
|
||||
log2_f64le :: logb_f64le
|
||||
log2_f64be :: logb_f64be
|
||||
log2 :: logb
|
||||
log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
|
||||
log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
|
||||
log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
|
||||
|
||||
log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
|
||||
log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
|
||||
log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
|
||||
|
||||
log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
|
||||
log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
|
||||
log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
|
||||
|
||||
log2 :: proc{
|
||||
log2_f16, log2_f16le, log2_f16be,
|
||||
log2_f32, log2_f32le, log2_f32be,
|
||||
log2_f64, log2_f64le, log2_f64be,
|
||||
}
|
||||
|
||||
log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
|
||||
log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
|
||||
@@ -607,6 +614,25 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
|
||||
return r
|
||||
}
|
||||
|
||||
divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
div = x / y
|
||||
mod = x % y
|
||||
return
|
||||
}
|
||||
|
||||
floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
div = x / y
|
||||
mod = x % y
|
||||
if (div > 0 && y < 0) || (mod < 0 && y > 0) {
|
||||
div -= 1
|
||||
mod += y
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
|
||||
shift :: F16_SHIFT
|
||||
mask :: F16_MASK
|
||||
@@ -1357,7 +1383,7 @@ atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
}
|
||||
|
||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1 + sqrt(1 - x*x))
|
||||
return atan2(x, sqrt(1 - x*x))
|
||||
}
|
||||
|
||||
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
|
||||
+9
-85
@@ -61,114 +61,38 @@ DEFAULT_PAGE_SIZE ::
|
||||
4 * 1024
|
||||
|
||||
alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
||||
_ = err
|
||||
data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
|
||||
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, size, alignment, nil, 0, loc)
|
||||
return runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, 0, loc)
|
||||
return err
|
||||
return runtime.mem_free(ptr, allocator, loc)
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil {
|
||||
return nil
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, raw_data(bytes), len(bytes), loc)
|
||||
return err
|
||||
return runtime.mem_free_bytes(bytes, allocator, loc)
|
||||
}
|
||||
|
||||
free_all :: proc(allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if allocator.procedure != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free_All, 0, 0, nil, 0, loc)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return runtime.mem_free_all(allocator, loc)
|
||||
}
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
|
||||
if allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
}
|
||||
return nil
|
||||
} else if ptr == nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
_ = err
|
||||
return nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
runtime.copy(data, byte_slice(ptr, old_size))
|
||||
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
|
||||
return raw_data(data)
|
||||
}
|
||||
|
||||
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ptr := raw_data(old_data)
|
||||
old_size := len(old_data)
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, Allocator_Mode.Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
runtime.copy(data, old_data)
|
||||
_, err = allocator.procedure(allocator.data, Allocator_Mode.Free, 0, 0, ptr, old_size, loc)
|
||||
}
|
||||
return data, err
|
||||
return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Features, 0, 0, &set, 0, loc)
|
||||
allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc)
|
||||
return set
|
||||
}
|
||||
return nil
|
||||
@@ -177,7 +101,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
|
||||
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
|
||||
props.pointer = pointer
|
||||
if allocator.procedure != nil {
|
||||
allocator.procedure(allocator.data, Allocator_Mode.Query_Info, 0, 0, &props, 0, loc)
|
||||
allocator.procedure(allocator.data, .Query_Info, 0, 0, &props, 0, loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+38
-15
@@ -31,6 +31,14 @@ Arena_Temp_Memory :: struct {
|
||||
}
|
||||
|
||||
|
||||
arena_init :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
a.peak_used = 0
|
||||
a.temp_count = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.arena_init'")
|
||||
init_arena :: proc(a: ^Arena, data: []byte) {
|
||||
a.data = data
|
||||
a.offset = 0
|
||||
@@ -52,15 +60,16 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
total_size := size + alignment
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
|
||||
total_size := size + ptr_sub((^byte)(ptr), (^byte)(end))
|
||||
|
||||
if arena.offset + total_size > len(arena.data) {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
@@ -292,6 +301,14 @@ Stack :: struct {
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
stack_init :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
s.curr_offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'mem.stack_init'")
|
||||
init_stack :: proc(s: ^Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.prev_offset = 0
|
||||
@@ -444,27 +461,34 @@ Small_Stack_Allocation_Header :: struct {
|
||||
|
||||
// Small_Stack is a stack-like allocator which uses the smallest possible header but at the cost of non-strict memory freeing order
|
||||
Small_Stack :: struct {
|
||||
data: []byte,
|
||||
offset: int,
|
||||
data: []byte,
|
||||
offset: int,
|
||||
peak_used: int,
|
||||
}
|
||||
|
||||
small_stack_init :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
@(deprecated="prefer 'small_stack_init'")
|
||||
init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.data = data
|
||||
s.offset = 0
|
||||
s.peak_used = 0
|
||||
}
|
||||
|
||||
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = small_stack_allocator_proc,
|
||||
data = stack,
|
||||
data = stack,
|
||||
}
|
||||
}
|
||||
|
||||
small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, ocation := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, location := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
s := cast(^Small_Stack)allocator_data
|
||||
|
||||
if s.data == nil {
|
||||
@@ -886,6 +910,10 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
case .Free:
|
||||
delete_key(&data.allocation_map, old_memory)
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map)
|
||||
}
|
||||
case .Resize:
|
||||
if old_memory != result_ptr {
|
||||
delete_key(&data.allocation_map, old_memory)
|
||||
@@ -898,11 +926,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
location = loc,
|
||||
}
|
||||
|
||||
case .Free_All:
|
||||
if data.clear_on_free_all {
|
||||
clear_map(&data.allocation_map)
|
||||
}
|
||||
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
|
||||
+8
-43
@@ -54,48 +54,7 @@ compare :: proc "contextless" (a, b: []byte) -> int {
|
||||
}
|
||||
|
||||
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
|
||||
switch {
|
||||
case a == b:
|
||||
return 0
|
||||
case a == nil:
|
||||
return -1
|
||||
case b == nil:
|
||||
return -1
|
||||
case n == 0:
|
||||
return 0
|
||||
}
|
||||
|
||||
x := slice_ptr(a, n)
|
||||
y := slice_ptr(b, n)
|
||||
|
||||
SU :: size_of(uintptr)
|
||||
fast := n/SU + 1
|
||||
offset := (fast-1)*SU
|
||||
curr_block := 0
|
||||
if n < SU {
|
||||
fast = 0
|
||||
}
|
||||
|
||||
la := slice_ptr((^uintptr)(a), fast)
|
||||
lb := slice_ptr((^uintptr)(b), fast)
|
||||
|
||||
for /**/; curr_block < fast; curr_block += 1 {
|
||||
if la[curr_block] ~ lb[curr_block] != 0 {
|
||||
for pos := curr_block*SU; pos < n; pos += 1 {
|
||||
if x[pos] ~ y[pos] != 0 {
|
||||
return (int(x[pos]) - int(y[pos])) < 0 ? -1 : +1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for /**/; offset < n; offset += 1 {
|
||||
if x[offset] ~ y[offset] != 0 {
|
||||
return (int(x[offset]) - int(y[offset])) < 0 ? -1 : +1
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
return runtime.memory_compare(a, b, n)
|
||||
}
|
||||
|
||||
check_zero :: proc(data: []byte) -> bool {
|
||||
@@ -109,6 +68,12 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
|
||||
case ptr == nil:
|
||||
return true
|
||||
}
|
||||
switch len {
|
||||
case 1: return (^u8)(ptr)^ == 0
|
||||
case 2: return intrinsics.unaligned_load((^u16)(ptr)) == 0
|
||||
case 4: return intrinsics.unaligned_load((^u32)(ptr)) == 0
|
||||
case 8: return intrinsics.unaligned_load((^u64)(ptr)) == 0
|
||||
}
|
||||
|
||||
start := uintptr(ptr)
|
||||
start_aligned := align_forward_uintptr(start, align_of(uintptr))
|
||||
@@ -152,7 +117,7 @@ slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
|
||||
return ([^]T)(ptr)[:len]
|
||||
}
|
||||
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, len: int) -> []byte {
|
||||
byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
|
||||
return ([^]u8)(data)[:max(len, 0)]
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ Raw_Cstring :: runtime.Raw_Cstring
|
||||
Raw_Slice :: runtime.Raw_Slice
|
||||
Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array
|
||||
Raw_Map :: runtime.Raw_Map
|
||||
Raw_Soa_Pointer :: runtime.Raw_Soa_Pointer
|
||||
|
||||
Raw_Complex64 :: struct {real, imag: f32}
|
||||
Raw_Complex128 :: struct {real, imag: f64}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package mem_virtual
|
||||
|
||||
arena_init :: proc{
|
||||
static_arena_init,
|
||||
growing_arena_init,
|
||||
}
|
||||
|
||||
arena_temp_begin :: proc{
|
||||
static_arena_temp_begin,
|
||||
growing_arena_temp_begin,
|
||||
|
||||
@@ -13,6 +13,13 @@ Growing_Arena :: struct {
|
||||
|
||||
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)
|
||||
@@ -37,7 +44,7 @@ growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(block_size, block_size, {}) or_return
|
||||
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
|
||||
@@ -95,9 +102,9 @@ growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
}
|
||||
|
||||
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) {
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
//+build darwin
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
foreign import libc "System.framework"
|
||||
import "core:c"
|
||||
|
||||
PROT_NONE :: 0x0 /* [MC2] no permissions */
|
||||
PROT_READ :: 0x1 /* [MC2] pages can be read */
|
||||
PROT_WRITE :: 0x2 /* [MC2] pages can be written */
|
||||
PROT_EXEC :: 0x4 /* [MC2] pages can be executed */
|
||||
|
||||
// Sharing options
|
||||
MAP_SHARED :: 0x1 /* [MF|SHM] share changes */
|
||||
MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */
|
||||
|
||||
// Other flags
|
||||
MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
|
||||
MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */
|
||||
MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */
|
||||
MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */
|
||||
MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */
|
||||
MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
|
||||
MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
|
||||
MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
|
||||
|
||||
// Mapping type
|
||||
MAP_FILE :: 0x0000 /* map from file (default) */
|
||||
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
|
||||
|
||||
|
||||
/*
|
||||
* The MAP_RESILIENT_* flags can be used when the caller wants to map some
|
||||
* possibly unreliable memory and be able to access it safely, possibly
|
||||
* getting the wrong contents rather than raising any exception.
|
||||
* For safety reasons, such mappings have to be read-only (PROT_READ access
|
||||
* only).
|
||||
*
|
||||
* MAP_RESILIENT_CODESIGN:
|
||||
* accessing this mapping will not generate code-signing violations,
|
||||
* even if the contents are tainted.
|
||||
* MAP_RESILIENT_MEDIA:
|
||||
* accessing this mapping will not generate an exception if the contents
|
||||
* are not available (unreachable removable or remote media, access beyond
|
||||
* end-of-file, ...). Missing contents will be replaced with zeroes.
|
||||
*/
|
||||
MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */
|
||||
MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */
|
||||
|
||||
MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
|
||||
|
||||
// Flags used to support translated processes.
|
||||
MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */
|
||||
MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */
|
||||
|
||||
// Process memory locking
|
||||
MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */
|
||||
MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */
|
||||
|
||||
MADV_NORMAL :: 0 /* [MC1] no further special treatment */
|
||||
MADV_RANDOM :: 1 /* [MC1] expect random page refs */
|
||||
MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */
|
||||
MADV_WILLNEED :: 3 /* [MC1] will need these pages */
|
||||
MADV_DONTNEED :: 4 /* [MC1] dont need these pages */
|
||||
MADV_FREE :: 5 /* pages unneeded, discard contents */
|
||||
MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */
|
||||
MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */
|
||||
MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */
|
||||
MADV_CAN_REUSE :: 9
|
||||
MADV_PAGEOUT :: 10 /* page out now (internal only) */
|
||||
|
||||
// msync() flags
|
||||
MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */
|
||||
MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */
|
||||
MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */
|
||||
MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */
|
||||
MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */
|
||||
|
||||
// Return bits from mincore
|
||||
MINCORE_INCORE :: 0x1 /* Page is incore */
|
||||
MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */
|
||||
MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */
|
||||
MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */
|
||||
MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */
|
||||
MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */
|
||||
MINCORE_COPIED :: 0x40 /* Page has been copied */
|
||||
MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */
|
||||
|
||||
// Allocation failure result
|
||||
MAP_FAILED : rawptr = rawptr(~uintptr(0))
|
||||
|
||||
foreign libc {
|
||||
@(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int ---
|
||||
@(link_name="munlockall") _munlockall :: proc() -> c.int ---
|
||||
@(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr ---
|
||||
@(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int ---
|
||||
@(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int ---
|
||||
@(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int ---
|
||||
@(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int ---
|
||||
@(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
|
||||
@(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
|
||||
@(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int ---
|
||||
@(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int ---
|
||||
}
|
||||
|
||||
|
||||
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
|
||||
if result == MAP_FAILED {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
return ([^]byte)(uintptr(result))[:size], nil
|
||||
}
|
||||
|
||||
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
|
||||
result := _mprotect(data, size, PROT_READ|PROT_WRITE)
|
||||
if result != 0 {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
return nil
|
||||
}
|
||||
_decommit :: proc "contextless" (data: rawptr, size: uint) {
|
||||
_mprotect(data, size, PROT_NONE)
|
||||
_madvise(data, size, MADV_FREE)
|
||||
}
|
||||
_release :: proc "contextless" (data: rawptr, size: uint) {
|
||||
_munmap(data, size)
|
||||
}
|
||||
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
|
||||
pflags: c.int
|
||||
pflags = PROT_NONE
|
||||
if .Read in flags { pflags |= PROT_READ }
|
||||
if .Write in flags { pflags |= PROT_WRITE }
|
||||
if .Execute in flags { pflags |= PROT_EXEC }
|
||||
err := _mprotect(data, size, pflags)
|
||||
return err != 0
|
||||
}
|
||||
|
||||
|
||||
_platform_memory_init :: proc() {
|
||||
DEFAULT_PAGE_SIZE = 4096
|
||||
|
||||
// is power of two
|
||||
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ munmap :: proc "contextless" (addr: rawptr, length: uint) -> c.int {
|
||||
}
|
||||
|
||||
mprotect :: proc "contextless" (addr: rawptr, length: uint, prot: c.int) -> c.int {
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uint(prot))
|
||||
res := intrinsics.syscall(unix.SYS_mprotect, uintptr(addr), uintptr(length), uintptr(prot))
|
||||
return c.int(res)
|
||||
}
|
||||
|
||||
|
||||
+37
-1
@@ -552,13 +552,20 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
|
||||
return
|
||||
}
|
||||
|
||||
Field_Flags :: distinct bit_set[Field_Flag]
|
||||
|
||||
Field_Flag :: enum {
|
||||
Invalid,
|
||||
Unknown,
|
||||
|
||||
Ellipsis,
|
||||
Using,
|
||||
No_Alias,
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
Any_Int,
|
||||
Subtype,
|
||||
By_Ptr,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
@@ -566,11 +573,38 @@ Field_Flag :: enum {
|
||||
Typeid_Token,
|
||||
}
|
||||
|
||||
Field_Flags :: distinct bit_set[Field_Flag]
|
||||
field_flag_strings := [Field_Flag]string{
|
||||
.Invalid = "",
|
||||
.Unknown = "",
|
||||
|
||||
.Ellipsis = "..",
|
||||
.Using = "using",
|
||||
.No_Alias = "#no_alias",
|
||||
.C_Vararg = "#c_vararg",
|
||||
.Auto_Cast = "auto_cast",
|
||||
.Any_Int = "#any_int",
|
||||
.Subtype = "#subtype",
|
||||
.By_Ptr = "#by_ptr",
|
||||
|
||||
.Results = "results",
|
||||
.Tags = "field tag",
|
||||
.Default_Parameters = "default parameters",
|
||||
.Typeid_Token = "typeid",
|
||||
}
|
||||
|
||||
field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
||||
{"no_alias", .No_Alias},
|
||||
{"c_vararg", .C_Vararg},
|
||||
{"any_int", .Any_Int},
|
||||
{"subtype", .Subtype},
|
||||
{"by_ptr", .By_Ptr},
|
||||
}
|
||||
|
||||
|
||||
Field_Flags_Struct :: Field_Flags{
|
||||
.Using,
|
||||
.Tags,
|
||||
.Subtype,
|
||||
}
|
||||
Field_Flags_Record_Poly_Params :: Field_Flags{
|
||||
.Typeid_Token,
|
||||
@@ -583,6 +617,7 @@ Field_Flags_Signature :: Field_Flags{
|
||||
.C_Vararg,
|
||||
.Auto_Cast,
|
||||
.Any_Int,
|
||||
.By_Ptr,
|
||||
.Default_Parameters,
|
||||
}
|
||||
|
||||
@@ -665,6 +700,7 @@ Proc_Type :: struct {
|
||||
|
||||
Pointer_Type :: struct {
|
||||
using node: Expr,
|
||||
tag: ^Expr,
|
||||
pointer: tokenizer.Pos,
|
||||
elem: ^Expr,
|
||||
}
|
||||
|
||||
@@ -286,6 +286,7 @@ clone_node :: proc(node: ^Node) -> ^Node {
|
||||
r.results = auto_cast clone(r.results)
|
||||
case ^Pointer_Type:
|
||||
r.elem = clone(r.elem)
|
||||
r.tag = clone(r.tag)
|
||||
case ^Multi_Pointer_Type:
|
||||
r.elem = clone(r.elem)
|
||||
case ^Array_Type:
|
||||
|
||||
@@ -186,6 +186,7 @@ Type_Kind :: enum u32le {
|
||||
Relative_Slice = 21,
|
||||
Multi_Pointer = 22,
|
||||
Matrix = 23,
|
||||
Soa_Pointer = 24,
|
||||
}
|
||||
|
||||
Type_Elems_Cap :: 4
|
||||
@@ -245,6 +246,7 @@ Type :: struct {
|
||||
// .Relative_Slice - 2 types: 0=slice type, 1=base integer
|
||||
// .Multi_Pointer - 1 type: 0=element
|
||||
// .Matrix - 1 type: 0=element
|
||||
// .Soa_Pointer - 1 type: 0=element
|
||||
types: Array(Type_Index),
|
||||
|
||||
// Used by:
|
||||
|
||||
@@ -183,6 +183,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
|
||||
pd.name = pkg_name.text
|
||||
pd.comment = p.line_comment
|
||||
p.file.pkg_decl = pd
|
||||
p.file.docs = docs
|
||||
|
||||
expect_semicolon(p, pd)
|
||||
|
||||
@@ -244,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
||||
return
|
||||
}
|
||||
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||
if .Optional_Semicolons not_in p.flags {
|
||||
return false
|
||||
}
|
||||
|
||||
prev := p.curr_tok
|
||||
if tokenizer.is_newline(prev) {
|
||||
if tokenizer.is_newline(p.curr_tok) {
|
||||
advance_token(p)
|
||||
return true
|
||||
}
|
||||
@@ -1610,20 +1606,6 @@ new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.E
|
||||
return field
|
||||
}
|
||||
|
||||
|
||||
Field_Prefix :: enum {
|
||||
Invalid,
|
||||
Unknown,
|
||||
|
||||
Using,
|
||||
No_Alias,
|
||||
C_Vararg,
|
||||
Auto_Cast,
|
||||
Any_Int,
|
||||
}
|
||||
|
||||
Field_Prefixes :: distinct bit_set[Field_Prefix]
|
||||
|
||||
Expr_And_Flags :: struct {
|
||||
expr: ^ast.Expr,
|
||||
flags: ast.Field_Flags,
|
||||
@@ -1665,7 +1647,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
|
||||
return idents[:]
|
||||
}
|
||||
|
||||
is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
||||
is_token_field_prefix :: proc(p: ^Parser) -> ast.Field_Flag {
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .EOF:
|
||||
return .Invalid
|
||||
@@ -1676,17 +1658,15 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
||||
advance_token(p)
|
||||
return .Auto_Cast
|
||||
case .Hash:
|
||||
tok: tokenizer.Token
|
||||
advance_token(p)
|
||||
defer advance_token(p)
|
||||
#partial switch p.curr_tok.kind {
|
||||
case .Ident:
|
||||
switch p.curr_tok.text {
|
||||
case "no_alias":
|
||||
return .No_Alias
|
||||
case "c_vararg":
|
||||
return .C_Vararg
|
||||
case "any_int":
|
||||
return .Any_Int
|
||||
tok = p.curr_tok
|
||||
advance_token(p)
|
||||
if tok.kind == .Ident {
|
||||
for kf in ast.field_hash_flag_strings {
|
||||
if kf.key == tok.text {
|
||||
return kf.flag
|
||||
}
|
||||
}
|
||||
}
|
||||
return .Unknown
|
||||
@@ -1694,8 +1674,8 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
|
||||
return .Invalid
|
||||
}
|
||||
|
||||
parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
|
||||
counts: [len(Field_Prefix)]int
|
||||
parse_field_prefixes :: proc(p: ^Parser) -> (flags: ast.Field_Flags) {
|
||||
counts: [len(ast.Field_Flag)]int
|
||||
|
||||
for {
|
||||
kind := is_token_field_prefix(p)
|
||||
@@ -1711,31 +1691,17 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
|
||||
counts[kind] += 1
|
||||
}
|
||||
|
||||
flags: ast.Field_Flags
|
||||
|
||||
for kind in Field_Prefix {
|
||||
for kind in ast.Field_Flag {
|
||||
count := counts[kind]
|
||||
switch kind {
|
||||
case .Invalid, .Unknown: // Ignore
|
||||
case .Using:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple 'using' in this field list") }
|
||||
if count > 0 { flags += {.Using} }
|
||||
case .No_Alias:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#no_alias' in this field list") }
|
||||
if count > 0 { flags += {.No_Alias} }
|
||||
case .C_Vararg:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#c_vararg' in this field list") }
|
||||
if count > 0 { flags += {.C_Vararg} }
|
||||
case .Auto_Cast:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple 'auto_cast' in this field list") }
|
||||
if count > 0 { flags += {.Auto_Cast} }
|
||||
case .Any_Int:
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '#any_int' in this field list") }
|
||||
if count > 0 { flags += {.Any_Int} }
|
||||
if kind == .Invalid || kind == .Unknown {
|
||||
// Ignore
|
||||
} else {
|
||||
if count > 1 { error(p, p.curr_tok.pos, "multiple '%s' in this field list", ast.field_flag_strings[kind]) }
|
||||
if count > 0 { flags += {kind} }
|
||||
}
|
||||
}
|
||||
|
||||
return flags
|
||||
return
|
||||
}
|
||||
|
||||
check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, set_flags: ast.Field_Flags) -> (flags: ast.Field_Flags) {
|
||||
@@ -1747,19 +1713,13 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
|
||||
|
||||
for flag in ast.Field_Flag {
|
||||
if flag not_in allowed_flags && flag in flags {
|
||||
switch flag {
|
||||
case .Using:
|
||||
error(p, p.curr_tok.pos, "'using' is not allowed within this field list")
|
||||
case .No_Alias:
|
||||
error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list")
|
||||
case .C_Vararg:
|
||||
error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list")
|
||||
case .Auto_Cast:
|
||||
error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list")
|
||||
case .Any_Int:
|
||||
error(p, p.curr_tok.pos, "'#any_int' is not allowed within this field list")
|
||||
#partial switch flag {
|
||||
case .Unknown, .Invalid:
|
||||
// ignore
|
||||
case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token:
|
||||
panic("Impossible prefixes")
|
||||
case:
|
||||
error(p, p.curr_tok.pos, "'%s' is not allowed within this field list", ast.field_flag_strings[flag])
|
||||
}
|
||||
flags -= {flag}
|
||||
}
|
||||
@@ -2270,7 +2230,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
return parse_call_expr(p, bd)
|
||||
|
||||
|
||||
case "soa", "simd":
|
||||
case "soa":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
@@ -2279,6 +2239,20 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
#partial switch t in type.derived_expr {
|
||||
case ^ast.Array_Type: t.tag = bd
|
||||
case ^ast.Dynamic_Array_Type: t.tag = bd
|
||||
case ^ast.Pointer_Type: t.tag = bd
|
||||
case:
|
||||
error(p, original_type.pos, "expected an array or pointer type after #%s", name.text)
|
||||
}
|
||||
return original_type
|
||||
|
||||
case "simd":
|
||||
bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
|
||||
bd.tok = tok
|
||||
bd.name = name.text
|
||||
original_type := parse_type(p)
|
||||
type := ast.unparen_expr(original_type)
|
||||
#partial switch t in type.derived_expr {
|
||||
case ^ast.Array_Type: t.tag = bd
|
||||
case:
|
||||
error(p, original_type.pos, "expected an array type after #%s", name.text)
|
||||
}
|
||||
@@ -2630,7 +2604,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
tok := expect_token(p, .Union)
|
||||
poly_params: ^ast.Field_List
|
||||
align: ^ast.Expr
|
||||
is_maybe: bool
|
||||
is_no_nil: bool
|
||||
is_shared_nil: bool
|
||||
|
||||
@@ -2655,10 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
align = parse_expr(p, true)
|
||||
case "maybe":
|
||||
if is_maybe {
|
||||
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
||||
}
|
||||
is_maybe = true
|
||||
error(p, tag.pos, "#%s functionality has now been merged with standard 'union' functionality", tag.text)
|
||||
case "no_nil":
|
||||
if is_no_nil {
|
||||
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
|
||||
@@ -2675,19 +2645,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
|
||||
}
|
||||
p.expr_level = prev_level
|
||||
|
||||
if is_no_nil && is_maybe {
|
||||
error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
|
||||
}
|
||||
if is_no_nil && is_shared_nil {
|
||||
error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
|
||||
}
|
||||
if is_shared_nil && is_maybe {
|
||||
error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
|
||||
}
|
||||
|
||||
union_kind := ast.Union_Type_Kind.Normal
|
||||
switch {
|
||||
case is_maybe: union_kind = .maybe
|
||||
case is_no_nil: union_kind = .no_nil
|
||||
case is_shared_nil: union_kind = .shared_nil
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string {
|
||||
|
||||
fix_lines(p)
|
||||
|
||||
builder := strings.make_builder(0, 5 * mem.Megabyte, p.allocator)
|
||||
builder := strings.builder_make(0, 5 * mem.Megabyte, p.allocator)
|
||||
|
||||
last_line := 0
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
|
||||
|
||||
return 0
|
||||
} else {
|
||||
builder := strings.make_builder(context.temp_allocator)
|
||||
builder := strings.builder_make(context.temp_allocator)
|
||||
|
||||
c_len := len(comment.text)
|
||||
trim_space := true
|
||||
@@ -90,12 +90,12 @@ push_comment :: proc(p: ^Printer, comment: tokenizer.Token) -> int {
|
||||
continue
|
||||
case c == '\r' && comment.text[min(c_len - 1, i + 1)] == '\n':
|
||||
append(&multilines, strings.to_string(builder))
|
||||
builder = strings.make_builder(context.temp_allocator)
|
||||
builder = strings.builder_make(context.temp_allocator)
|
||||
trim_space = true
|
||||
i += 1
|
||||
case c == '\n':
|
||||
append(&multilines, strings.to_string(builder))
|
||||
builder = strings.make_builder(context.temp_allocator)
|
||||
builder = strings.builder_make(context.temp_allocator)
|
||||
trim_space = true
|
||||
case c == '/' && comment.text[min(c_len - 1, i + 1)] == '*':
|
||||
strings.write_string(&builder, "/*")
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package os
|
||||
|
||||
import "core:strings"
|
||||
import "core:mem"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
@@ -51,10 +50,13 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
continue
|
||||
}
|
||||
|
||||
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
|
||||
fullpath := make([]byte, len(dirpath)+1+len(filename))
|
||||
copy(fullpath, dirpath)
|
||||
copy(fullpath[len(dirpath):], "/")
|
||||
copy(fullpath[len(dirpath)+1:], filename)
|
||||
defer delete(fullpath, context.temp_allocator)
|
||||
|
||||
fi_, err = stat(fullpath, allocator)
|
||||
fi_, err = stat(string(fullpath), allocator)
|
||||
if err != ERROR_NONE {
|
||||
for fi__ in dfi {
|
||||
file_info_delete(fi__, allocator)
|
||||
|
||||
+16
-16
@@ -11,24 +11,24 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
return
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
b := make([dynamic]u16, 100, context.temp_allocator)
|
||||
for {
|
||||
n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
|
||||
resize(&b, len(b)*2)
|
||||
}
|
||||
b := make([dynamic]u16, n, context.temp_allocator)
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
+31
-11
@@ -384,21 +384,33 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
|
||||
|
||||
|
||||
change_directory :: proc(path: string) -> Errno {
|
||||
change_directory :: proc(path: string) -> (err: Errno) {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.SetCurrentDirectoryW(wpath))
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> Errno {
|
||||
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.CreateDirectoryW(wpath, nil))
|
||||
|
||||
if !win32.CreateDirectoryW(wpath, nil) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> Errno {
|
||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.RemoveDirectoryW(wpath))
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -464,23 +476,31 @@ fix_long_path :: proc(path: string) -> string {
|
||||
}
|
||||
|
||||
|
||||
link :: proc(old_name, new_name: string) -> Errno {
|
||||
link :: proc(old_name, new_name: string) -> (err: Errno) {
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_name))
|
||||
o := win32.utf8_to_wstring(fix_long_path(old_name))
|
||||
return Errno(win32.CreateHardLinkW(n, o, nil))
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> Errno {
|
||||
unlink :: proc(path: string) -> (err: Errno) {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return Errno(win32.DeleteFileW(wpath))
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Errno {
|
||||
rename :: proc(old_path, new_path: string) -> (err: Errno) {
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING))
|
||||
|
||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
err = Errno(win32.GetLastError())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
return nil, nil
|
||||
|
||||
case .Query_Info:
|
||||
return nil, nil
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
|
||||
+10
-10
@@ -6,19 +6,19 @@ import "core:runtime"
|
||||
user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("LocalAppData")
|
||||
dir = get_env("LocalAppData", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME")
|
||||
dir = get_env("XDG_CACHE_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
@@ -34,19 +34,19 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
|
||||
user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("AppData")
|
||||
dir = get_env("AppData", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir != "" {
|
||||
dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME")
|
||||
dir = get_env("XDG_CACHE_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME")
|
||||
dir = get_env("HOME", allocator)
|
||||
if dir == "" {
|
||||
return
|
||||
}
|
||||
@@ -59,13 +59,13 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
|
||||
return
|
||||
}
|
||||
|
||||
user_home_dir :: proc() -> (dir: string, err: Error) {
|
||||
user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
env := "HOME"
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
env = "USERPROFILE"
|
||||
}
|
||||
if v := get_env(env); v != "" {
|
||||
if v := get_env(env, allocator); v != "" {
|
||||
return v, nil
|
||||
}
|
||||
return "", .Invalid_Path
|
||||
|
||||
@@ -241,13 +241,13 @@ S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||
|
||||
|
||||
S_ISLNK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFLNK
|
||||
S_ISREG :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFREG
|
||||
S_ISDIR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFDIR
|
||||
S_ISCHR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFCHR
|
||||
S_ISBLK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFBLK
|
||||
S_ISFIFO :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFIFO
|
||||
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFSOCK
|
||||
S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
|
||||
F_OK :: 0 // Test for file existance
|
||||
X_OK :: 1 // Test for execute permission
|
||||
@@ -257,7 +257,7 @@ R_OK :: 4 // Test for read permission
|
||||
foreign libc {
|
||||
@(link_name="__error") __errno_location :: proc() -> ^int ---
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
|
||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||
|
||||
@@ -311,7 +311,7 @@ _unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
|
||||
low := uintptr(offset & 0xFFFFFFFF)
|
||||
high := uintptr(offset >> 32)
|
||||
result: i64
|
||||
res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
|
||||
res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
|
||||
return -1 if res < 0 else result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
|
||||
}
|
||||
|
||||
|
||||
d, derr := os.open(dir)
|
||||
d, derr := os.open(dir, os.O_RDONLY)
|
||||
if derr != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
|
||||
}
|
||||
|
||||
// join joins numerous path elements into a single path
|
||||
join :: proc(elems: ..string, allocator := context.allocator) -> string {
|
||||
join :: proc(elems: []string, allocator := context.allocator) -> string {
|
||||
context.allocator = allocator
|
||||
for elem, i in elems {
|
||||
if elem != "" {
|
||||
|
||||
+39
-24
@@ -1,7 +1,6 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
_ :: intrinsics
|
||||
|
||||
@@ -34,6 +33,7 @@ Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
|
||||
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice
|
||||
Type_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
|
||||
|
||||
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
|
||||
|
||||
@@ -68,6 +68,7 @@ Type_Kind :: enum {
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
}
|
||||
|
||||
|
||||
@@ -102,6 +103,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Slice: return .Relative_Slice
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
}
|
||||
|
||||
}
|
||||
@@ -194,6 +196,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
|
||||
}
|
||||
case Type_Info_Pointer: return v.elem.id
|
||||
case Type_Info_Multi_Pointer: return v.elem.id
|
||||
case Type_Info_Soa_Pointer: return v.elem.id
|
||||
case Type_Info_Array: return v.elem.id
|
||||
case Type_Info_Enumerated_Array: return v.elem.id
|
||||
case Type_Info_Slice: return v.elem.id
|
||||
@@ -220,7 +223,7 @@ align_of_typeid :: proc(T: typeid) -> int {
|
||||
as_bytes :: proc(v: any) -> []byte {
|
||||
if v != nil {
|
||||
sz := size_of_typeid(v.id)
|
||||
return mem.slice_ptr((^byte)(v.data), sz)
|
||||
return ([^]byte)(v.data)[:sz]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -262,19 +265,19 @@ length :: proc(val: any) -> int {
|
||||
return a.count
|
||||
|
||||
case Type_Info_Slice:
|
||||
return (^mem.Raw_Slice)(val.data).len
|
||||
return (^runtime.Raw_Slice)(val.data).len
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(val.data).len
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).len
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^mem.Raw_Map)(val.data).entries.len
|
||||
return (^runtime.Raw_Map)(val.data).entries.len
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring {
|
||||
return len((^cstring)(val.data)^)
|
||||
} else {
|
||||
return (^mem.Raw_String)(val.data).len
|
||||
return (^runtime.Raw_String)(val.data).len
|
||||
}
|
||||
}
|
||||
return 0
|
||||
@@ -297,10 +300,10 @@ capacity :: proc(val: any) -> int {
|
||||
return a.count
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
return (^mem.Raw_Dynamic_Array)(val.data).cap
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).cap
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^mem.Raw_Map)(val.data).entries.cap
|
||||
return (^runtime.Raw_Map)(val.data).entries.cap
|
||||
}
|
||||
return 0
|
||||
}
|
||||
@@ -340,14 +343,14 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
return any{data, a.elem.id}
|
||||
|
||||
case Type_Info_Slice:
|
||||
raw := (^mem.Raw_Slice)(val.data)
|
||||
raw := (^runtime.Raw_Slice)(val.data)
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||
offset := uintptr(a.elem.size * i)
|
||||
data := rawptr(uintptr(raw.data) + offset)
|
||||
return any{data, a.elem.id}
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
raw := (^mem.Raw_Dynamic_Array)(val.data)
|
||||
raw := (^runtime.Raw_Dynamic_Array)(val.data)
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||
offset := uintptr(a.elem.size * i)
|
||||
data := rawptr(uintptr(raw.data) + offset)
|
||||
@@ -356,7 +359,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
|
||||
case Type_Info_String:
|
||||
if a.is_cstring { return nil }
|
||||
|
||||
raw := (^mem.Raw_String)(val.data)
|
||||
raw := (^runtime.Raw_String)(val.data)
|
||||
runtime.bounds_check_error_loc(loc, i, raw.len)
|
||||
offset := uintptr(size_of(u8) * i)
|
||||
data := rawptr(uintptr(raw.data) + offset)
|
||||
@@ -725,6 +728,17 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
|
||||
panic("expected a union to reflect.get_union_variant_raw_tag")
|
||||
}
|
||||
|
||||
get_union_variant :: proc(a: any) -> any {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
id := union_variant_typeid(a)
|
||||
if id == nil {
|
||||
return nil
|
||||
}
|
||||
return any{a.data, id}
|
||||
}
|
||||
|
||||
|
||||
set_union_variant_raw_tag :: proc(a: any, tag: i64) {
|
||||
if a == nil { return }
|
||||
@@ -822,17 +836,17 @@ set_union_value :: proc(dst: any, value: any) -> bool {
|
||||
ti := runtime.type_info_base(type_info_of(dst.id))
|
||||
if info, ok := ti.variant.(runtime.Type_Info_Union); ok {
|
||||
if value.id == nil {
|
||||
mem.zero(dst.data, ti.size)
|
||||
intrinsics.mem_zero(dst.data, ti.size)
|
||||
return true
|
||||
}
|
||||
if ti.id == runtime.typeid_base(value.id) {
|
||||
mem.copy(dst.data, value.data, ti.size)
|
||||
intrinsics.mem_copy(dst.data, value.data, ti.size)
|
||||
return true
|
||||
}
|
||||
|
||||
if type_info_union_is_pure_maybe(info) {
|
||||
if variant := info.variants[0]; variant.id == value.id {
|
||||
mem.copy(dst.data, value.data, variant.size)
|
||||
intrinsics.mem_copy(dst.data, value.data, variant.size)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -844,7 +858,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
|
||||
if !info.no_nil {
|
||||
tag += 1
|
||||
}
|
||||
mem.copy(dst.data, value.data, variant.size)
|
||||
intrinsics.mem_copy(dst.data, value.data, variant.size)
|
||||
set_union_variant_raw_tag(dst, tag)
|
||||
return true
|
||||
}
|
||||
@@ -1337,11 +1351,11 @@ as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
|
||||
|
||||
case Type_Info_Slice:
|
||||
valid = true
|
||||
value = (^mem.Raw_Slice)(a.data).data
|
||||
value = (^runtime.Raw_Slice)(a.data).data
|
||||
|
||||
case Type_Info_Dynamic_Array:
|
||||
valid = true
|
||||
value = (^mem.Raw_Dynamic_Array)(a.data).data
|
||||
value = (^runtime.Raw_Dynamic_Array)(a.data).data
|
||||
}
|
||||
|
||||
return
|
||||
@@ -1383,7 +1397,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
}
|
||||
|
||||
if .Simple_Compare in t.flags {
|
||||
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
}
|
||||
|
||||
t = runtime.type_info_core(t)
|
||||
@@ -1419,8 +1433,9 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
Type_Info_Enum,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Matrix:
|
||||
return mem.compare_byte_ptrs((^byte)(a.data), (^byte)(b.data), t.size) == 0
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
|
||||
case Type_Info_String:
|
||||
if v.is_cstring {
|
||||
@@ -1474,8 +1489,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
if !including_indirect_array_recursion {
|
||||
return false
|
||||
}
|
||||
array_a := (^mem.Raw_Slice)(a.data)
|
||||
array_b := (^mem.Raw_Slice)(b.data)
|
||||
array_a := (^runtime.Raw_Slice)(a.data)
|
||||
array_b := (^runtime.Raw_Slice)(b.data)
|
||||
if array_a.len != array_b.len {
|
||||
return false
|
||||
}
|
||||
@@ -1494,8 +1509,8 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
if !including_indirect_array_recursion {
|
||||
return false
|
||||
}
|
||||
array_a := (^mem.Raw_Dynamic_Array)(a.data)
|
||||
array_b := (^mem.Raw_Dynamic_Array)(b.data)
|
||||
array_a := (^runtime.Raw_Dynamic_Array)(a.data)
|
||||
array_b := (^runtime.Raw_Dynamic_Array)(b.data)
|
||||
if array_a.len != array_b.len {
|
||||
return false
|
||||
}
|
||||
@@ -1503,7 +1518,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
return true
|
||||
}
|
||||
if .Simple_Compare in v.elem.flags {
|
||||
return mem.compare_byte_ptrs((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
|
||||
return runtime.memory_compare((^byte)(array_a.data), (^byte)(array_b.data), array_a.len * v.elem.size) == 0
|
||||
}
|
||||
|
||||
for i in 0..<array_a.len {
|
||||
|
||||
+20
-2
@@ -68,6 +68,11 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
y := b.variant.(Type_Info_Multi_Pointer) or_return
|
||||
return are_types_identical(x.elem, y.elem)
|
||||
|
||||
case Type_Info_Soa_Pointer:
|
||||
y := b.variant.(Type_Info_Soa_Pointer) or_return
|
||||
return are_types_identical(x.elem, y.elem)
|
||||
|
||||
|
||||
case Type_Info_Procedure:
|
||||
y := b.variant.(Type_Info_Procedure) or_return
|
||||
switch {
|
||||
@@ -256,6 +261,11 @@ is_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
|
||||
return ok
|
||||
}
|
||||
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
|
||||
return ok
|
||||
}
|
||||
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
#partial switch v in info.variant {
|
||||
@@ -292,6 +302,11 @@ is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Map)
|
||||
return ok
|
||||
}
|
||||
is_bit_set :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
|
||||
return ok
|
||||
}
|
||||
is_slice :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false }
|
||||
_, ok := type_info_base(info).variant.(Type_Info_Slice)
|
||||
@@ -437,6 +452,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
|
||||
case Type_Info_Multi_Pointer:
|
||||
io.write_string(w, "[^]", &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
case Type_Info_Soa_Pointer:
|
||||
io.write_string(w, "#soa ^", &n) or_return
|
||||
write_type(w, info.elem, &n) or_return
|
||||
case Type_Info_Procedure:
|
||||
io.write_string(w, "proc", &n) or_return
|
||||
if info.params == nil {
|
||||
@@ -573,11 +591,11 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
|
||||
write_type(w, info.elem, &n) or_return
|
||||
case is_rune(info.elem):
|
||||
io.write_encoded_rune(w, rune(info.lower), true, &n) or_return
|
||||
io.write_string(w, "..", &n) or_return
|
||||
io.write_string(w, "..=", &n) or_return
|
||||
io.write_encoded_rune(w, rune(info.upper), true, &n) or_return
|
||||
case:
|
||||
io.write_i64(w, info.lower, 10, &n) or_return
|
||||
io.write_string(w, "..", &n) or_return
|
||||
io.write_string(w, "..=", &n) or_return
|
||||
io.write_i64(w, info.upper, 10, &n) or_return
|
||||
}
|
||||
if info.underlying != nil {
|
||||
|
||||
@@ -176,6 +176,9 @@ Type_Info_Matrix :: struct {
|
||||
column_count: int,
|
||||
// Total element count = column_count * elem_stride
|
||||
}
|
||||
Type_Info_Soa_Pointer :: struct {
|
||||
elem: ^Type_Info,
|
||||
}
|
||||
|
||||
Type_Info_Flag :: enum u8 {
|
||||
Comparable = 0,
|
||||
@@ -217,6 +220,7 @@ Type_Info :: struct {
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Slice,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -267,6 +271,19 @@ type_table: []Type_Info
|
||||
|
||||
args__: []cstring
|
||||
|
||||
when ODIN_OS == .Windows {
|
||||
// NOTE(Jeroen): If we're a Windows DLL, fwdReason will be populated.
|
||||
// This tells a DLL if it's first loaded, about to be unloaded, or a thread is joining/exiting.
|
||||
|
||||
DLL_Forward_Reason :: enum u32 {
|
||||
Process_Detach = 0, // About to unload DLL
|
||||
Process_Attach = 1, // Entry point
|
||||
Thread_Attach = 2,
|
||||
Thread_Detach = 3,
|
||||
}
|
||||
dll_forward_reason: DLL_Forward_Reason
|
||||
}
|
||||
|
||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||
|
||||
|
||||
@@ -390,6 +407,12 @@ Raw_Cstring :: struct {
|
||||
data: [^]byte,
|
||||
}
|
||||
|
||||
Raw_Soa_Pointer :: struct {
|
||||
data: rawptr,
|
||||
index: int,
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
// Defined internally by the compiler
|
||||
|
||||
+180
-60
@@ -129,6 +129,9 @@ reserve :: proc{reserve_dynamic_array, reserve_map}
|
||||
@builtin
|
||||
resize :: proc{resize_dynamic_array}
|
||||
|
||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||
@builtin
|
||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||
|
||||
@builtin
|
||||
free :: proc{mem_free}
|
||||
@@ -140,7 +143,7 @@ free_all :: proc{mem_free_all}
|
||||
|
||||
@builtin
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(str), allocator, loc)
|
||||
return mem_free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
@builtin
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
@@ -148,17 +151,24 @@ delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #cal
|
||||
}
|
||||
@builtin
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(array), array.allocator, loc)
|
||||
return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
@builtin
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return mem_free(raw_data(array), allocator, loc)
|
||||
return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
@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(raw.entries.data, 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
|
||||
}
|
||||
@@ -284,12 +294,30 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
}
|
||||
|
||||
@builtin
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int) {
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
|
||||
if m != nil {
|
||||
__dynamic_map_reserve(__get_map_header(m), capacity)
|
||||
__dynamic_map_reserve(__get_map_header(m), 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.
|
||||
*/
|
||||
@builtin
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, 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)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
|
||||
// If m is nil, or there is no such element, this procedure is a no-op
|
||||
@builtin
|
||||
@@ -314,68 +342,80 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
||||
|
||||
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) {
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
|
||||
if array == nil {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
if cap(array) < len(array)+1 {
|
||||
cap := 2 * cap(array) + max(8, 1)
|
||||
_ = reserve(array, cap, loc)
|
||||
}
|
||||
if cap(array)-len(array) > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
when size_of(E) != 0 {
|
||||
data := ([^]E)(a.data)
|
||||
assert(condition=data != nil, loc=loc)
|
||||
data[a.len] = arg
|
||||
when size_of(E) == 0 {
|
||||
array.len += 1
|
||||
return 1
|
||||
} else {
|
||||
if cap(array) < len(array)+1 {
|
||||
cap := 2 * cap(array) + max(8, 1)
|
||||
_ = reserve(array, cap, loc)
|
||||
}
|
||||
a.len += 1
|
||||
if cap(array)-len(array) > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
when size_of(E) != 0 {
|
||||
data := ([^]E)(a.data)
|
||||
assert(condition=data != nil, loc=loc)
|
||||
data[a.len] = arg
|
||||
}
|
||||
a.len += 1
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) {
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
|
||||
if array == nil {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
arg_len := len(args)
|
||||
if arg_len <= 0 {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
if cap(array) < len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
_ = reserve(array, cap, loc)
|
||||
}
|
||||
arg_len = min(cap(array)-len(array), arg_len)
|
||||
if arg_len > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
when size_of(E) != 0 {
|
||||
data := ([^]E)(a.data)
|
||||
assert(condition=data != nil, loc=loc)
|
||||
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
|
||||
when size_of(E) == 0 {
|
||||
array.len += arg_len
|
||||
return arg_len
|
||||
} else {
|
||||
if cap(array) < len(array)+arg_len {
|
||||
cap := 2 * cap(array) + max(8, arg_len)
|
||||
_ = reserve(array, cap, loc)
|
||||
}
|
||||
a.len += arg_len
|
||||
arg_len = min(cap(array)-len(array), arg_len)
|
||||
if arg_len > 0 {
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
when size_of(E) != 0 {
|
||||
data := ([^]E)(a.data)
|
||||
assert(condition=data != nil, loc=loc)
|
||||
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
|
||||
}
|
||||
a.len += arg_len
|
||||
}
|
||||
return arg_len
|
||||
}
|
||||
}
|
||||
|
||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||
@builtin
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) {
|
||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
|
||||
args := transmute([]E)arg
|
||||
append_elems(array=array, args=args, loc=loc)
|
||||
return append_elems(array=array, args=args, loc=loc)
|
||||
}
|
||||
|
||||
|
||||
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
|
||||
@builtin
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) {
|
||||
append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
|
||||
for arg in args {
|
||||
append(array = array, args = transmute([]E)(arg), loc = loc)
|
||||
n += append(array = array, args = transmute([]E)(arg), loc = loc)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// The append built-in procedure appends elements to the end of a dynamic array
|
||||
@@ -383,16 +423,18 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
||||
|
||||
|
||||
@builtin
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) {
|
||||
append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
|
||||
if array == nil {
|
||||
return
|
||||
return 0
|
||||
}
|
||||
prev_len := len(array)
|
||||
resize(array, len(array)+1)
|
||||
return len(array)-prev_len
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -411,7 +453,7 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
}
|
||||
|
||||
@builtin
|
||||
insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -435,7 +477,7 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
|
||||
}
|
||||
|
||||
@builtin
|
||||
insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -456,7 +498,51 @@ insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
|
||||
return
|
||||
}
|
||||
|
||||
@builtin insert_at :: proc{insert_at_elem, insert_at_elems, insert_at_elem_string}
|
||||
@builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
if index < len(array) {
|
||||
array[index] = arg
|
||||
ok = true
|
||||
} else if resize(array, index+1, loc) {
|
||||
array[index] = arg
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
if index+len(args) < len(array) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
} else if resize(array, index+1+len(args), loc) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@builtin
|
||||
assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
|
||||
if len(args) == 0 {
|
||||
ok = true
|
||||
} else if index+len(args) < len(array) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
} else if resize(array, index+1+len(args), loc) {
|
||||
copy(array[index:], args)
|
||||
ok = true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string}
|
||||
|
||||
|
||||
|
||||
@@ -536,6 +622,54 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
return true
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||
|
||||
If `new_cap` is negative, then `len(array)` is used.
|
||||
|
||||
Returns false if `cap(array) < new_cap`, or the allocator report failure.
|
||||
|
||||
If `len(array) < new_cap`, then `len(array)` will be left unchanged.
|
||||
*/
|
||||
shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
new_cap := new_cap if new_cap >= 0 else a.len
|
||||
|
||||
if new_cap > a.cap {
|
||||
return
|
||||
}
|
||||
|
||||
if a.allocator.procedure == nil {
|
||||
a.allocator = context.allocator
|
||||
}
|
||||
assert(a.allocator.procedure != nil)
|
||||
|
||||
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,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
a.data = raw_data(new_data)
|
||||
a.len = min(new_cap, a.len)
|
||||
a.cap = new_cap
|
||||
return true
|
||||
}
|
||||
|
||||
@builtin
|
||||
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
|
||||
key, value := key, value
|
||||
@@ -666,17 +800,3 @@ unimplemented :: proc(message := "", loc := #caller_location) -> ! {
|
||||
}
|
||||
p("not yet implemented", message, loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
unreachable :: proc(message := "", loc := #caller_location) -> ! {
|
||||
p := context.assertion_failure_proc
|
||||
if p == nil {
|
||||
p = default_assertion_failure_proc
|
||||
}
|
||||
if message != "" {
|
||||
p("internal error", message, loc)
|
||||
} else {
|
||||
p("internal error", "entered unreachable code", loc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
_windows_default_free(old_memory)
|
||||
|
||||
case .Free_All:
|
||||
// NOTE(tetra): Do nothing.
|
||||
return nil, .Mode_Not_Implemented
|
||||
|
||||
case .Resize:
|
||||
data, err = _windows_default_resize(old_memory, old_size, size, alignment)
|
||||
@@ -29,7 +29,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Do nothing
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -185,7 +185,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
// Nothing to give
|
||||
return nil, .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -29,11 +29,15 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
new_size := cap * elem_size
|
||||
allocator := array.allocator
|
||||
|
||||
new_data, err := allocator.procedure(allocator.data, .Resize, new_size, elem_align, array.data, old_size, loc)
|
||||
new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if new_data != nil || elem_size == 0 {
|
||||
if elem_size == 0 {
|
||||
array.data = raw_data(new_data)
|
||||
array.cap = cap
|
||||
return true
|
||||
} else if new_data != nil {
|
||||
array.data = raw_data(new_data)
|
||||
array.cap = min(cap, len(new_data)/elem_size)
|
||||
return true
|
||||
@@ -41,6 +45,35 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap:
|
||||
return false
|
||||
}
|
||||
|
||||
__dynamic_array_shrink :: proc(array_: rawptr, elem_size, elem_align: int, new_cap: int, loc := #caller_location) -> (did_shrink: bool) {
|
||||
array := (^Raw_Dynamic_Array)(array_)
|
||||
|
||||
// NOTE(tetra, 2020-01-26): We set the allocator before earlying-out below, because user code is usually written
|
||||
// assuming that appending/reserving will set the allocator, if it is not already set.
|
||||
if array.allocator.procedure == nil {
|
||||
array.allocator = context.allocator
|
||||
}
|
||||
assert(array.allocator.procedure != nil)
|
||||
|
||||
if new_cap > array.cap {
|
||||
return
|
||||
}
|
||||
|
||||
old_size := array.cap * elem_size
|
||||
new_size := new_cap * elem_size
|
||||
allocator := array.allocator
|
||||
|
||||
new_data, err := mem_resize(array.data, old_size, new_size, elem_align, allocator, loc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
array.data = raw_data(new_data)
|
||||
array.len = min(new_cap, array.len)
|
||||
array.cap = new_cap
|
||||
return true
|
||||
}
|
||||
|
||||
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool {
|
||||
array := (^Raw_Dynamic_Array)(array_)
|
||||
|
||||
@@ -65,7 +98,7 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
|
||||
|
||||
|
||||
ok := true
|
||||
if array.cap <= array.len+item_count {
|
||||
if array.cap < array.len+item_count {
|
||||
cap := 2 * array.cap + max(8, item_count)
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc)
|
||||
}
|
||||
@@ -86,7 +119,7 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in
|
||||
array := (^Raw_Dynamic_Array)(array_)
|
||||
|
||||
ok := true
|
||||
if array.cap <= array.len+1 {
|
||||
if array.cap < array.len+1 {
|
||||
cap := 2 * array.cap + max(8, 1)
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap, loc)
|
||||
}
|
||||
|
||||
@@ -194,12 +194,15 @@ __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: Allocator, l
|
||||
new_size := new_count*size_of(T)
|
||||
|
||||
new_data, err := mem_resize(array.data, old_size, new_size, align_of(T), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
array.data = new_data
|
||||
array.len = new_count
|
||||
return true
|
||||
if new_data != nil || size_of(E) == 0 {
|
||||
array.data = raw_data(new_data)
|
||||
array.len = new_count
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
__dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_location) {
|
||||
@@ -207,7 +210,7 @@ __dynamic_map_reset_entries :: proc(using header: Map_Header, loc := #caller_loc
|
||||
m.hashes[i] = -1
|
||||
}
|
||||
|
||||
for i in 0 ..< m.entries.len {
|
||||
for i in 0..<m.entries.len {
|
||||
entry_header := __dynamic_map_get_entry(header, i)
|
||||
entry_hash := __get_map_hash_from_entry(header, entry_header)
|
||||
entry_header.next = -1
|
||||
@@ -239,6 +242,16 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_map_shrink :: proc(using header: Map_Header, cap: int, loc := #caller_location) -> (did_shrink: bool) {
|
||||
c := context
|
||||
if m.entries.allocator.procedure != nil {
|
||||
c.allocator = m.entries.allocator
|
||||
}
|
||||
context = c
|
||||
|
||||
return __dynamic_array_shrink(&m.entries, entry_size, entry_align, cap, loc)
|
||||
}
|
||||
|
||||
__dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) {
|
||||
#force_inline __dynamic_map_reserve(header, new_count, loc)
|
||||
}
|
||||
|
||||
@@ -8,15 +8,19 @@ when ODIN_BUILD_MODE == .Dynamic {
|
||||
@(link_name="DllMain", linkage="strong", require)
|
||||
DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
|
||||
context = default_context()
|
||||
switch fdwReason {
|
||||
case 1: // DLL_PROCESS_ATTACH
|
||||
|
||||
// Populate Windows DLL-specific global
|
||||
dll_forward_reason = DLL_Forward_Reason(fdwReason)
|
||||
|
||||
switch dll_forward_reason {
|
||||
case .Process_Attach:
|
||||
#force_no_inline _startup_runtime()
|
||||
intrinsics.__entry_point()
|
||||
case 0: // DLL_PROCESS_DETACH
|
||||
case .Process_Detach:
|
||||
#force_no_inline _cleanup_runtime()
|
||||
case 2: // DLL_THREAD_ATTACH
|
||||
case .Thread_Attach:
|
||||
break
|
||||
case 3: // DLL_THREAD_DETACH
|
||||
case .Thread_Detach:
|
||||
break
|
||||
}
|
||||
return true
|
||||
|
||||
+61
-37
@@ -103,7 +103,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
if len < 0 {
|
||||
if len <= 0 {
|
||||
return data
|
||||
}
|
||||
intrinsics.mem_zero(data, len)
|
||||
@@ -111,22 +111,18 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
|
||||
}
|
||||
|
||||
mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil {
|
||||
return dst
|
||||
if src != nil && dst != src && len > 0 {
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
intrinsics.mem_copy(dst, src, len)
|
||||
}
|
||||
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
intrinsics.mem_copy(dst, src, len)
|
||||
return dst
|
||||
}
|
||||
|
||||
mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if src == nil {
|
||||
return dst
|
||||
if src != nil && dst != src && len > 0 {
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
intrinsics.mem_copy_non_overlapping(dst, src, len)
|
||||
}
|
||||
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
intrinsics.mem_copy_non_overlapping(dst, src, len)
|
||||
return dst
|
||||
}
|
||||
|
||||
@@ -142,28 +138,38 @@ mem_alloc_bytes :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNM
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) {
|
||||
if size == 0 {
|
||||
mem_alloc :: #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
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
data, err := allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
return raw_data(data), err
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil {
|
||||
return .None
|
||||
}
|
||||
if allocator.procedure == nil {
|
||||
return .None
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc)
|
||||
return err
|
||||
}
|
||||
|
||||
mem_free_with_size :: #force_inline 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
|
||||
}
|
||||
|
||||
mem_free_bytes :: #force_inline proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if bytes == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, raw_data(bytes), len(bytes), loc)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
|
||||
if allocator.procedure != nil {
|
||||
_, err = allocator.procedure(allocator.data, .Free_All, 0, 0, nil, 0, loc)
|
||||
@@ -171,21 +177,34 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
||||
return
|
||||
}
|
||||
|
||||
mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (new_ptr: rawptr, err: Allocator_Error) {
|
||||
new_data: []byte
|
||||
switch {
|
||||
case allocator.procedure == nil:
|
||||
return
|
||||
case new_size == 0:
|
||||
new_data, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, 0, loc)
|
||||
case ptr == nil:
|
||||
new_data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
case:
|
||||
new_data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
new_ptr = raw_data(new_data)
|
||||
return
|
||||
if new_size == 0 {
|
||||
if ptr != nil {
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
|
||||
return nil, err
|
||||
}
|
||||
return nil, nil
|
||||
} else if ptr == nil {
|
||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
||||
return ([^]byte)(ptr)[:old_size], nil
|
||||
}
|
||||
|
||||
data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||
if err == .Mode_Not_Implemented {
|
||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||
if err != nil {
|
||||
return data, err
|
||||
}
|
||||
copy(data, ([^]byte)(ptr)[:old_size])
|
||||
_, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
|
||||
}
|
||||
return data, err
|
||||
}
|
||||
|
||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
switch {
|
||||
case n == 0: return true
|
||||
@@ -341,7 +360,12 @@ string_eq :: proc "contextless" (lhs, rhs: string) -> bool {
|
||||
string_cmp :: proc "contextless" (a, b: string) -> int {
|
||||
x := transmute(Raw_String)a
|
||||
y := transmute(Raw_String)b
|
||||
return memory_compare(x.data, y.data, min(x.len, y.len))
|
||||
|
||||
ret := memory_compare(x.data, y.data, min(x.len, y.len))
|
||||
if ret == 0 && x.len != y.len {
|
||||
return -1 if x.len < y.len else +1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
string_ne :: #force_inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b) }
|
||||
|
||||
@@ -228,6 +228,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
||||
case Type_Info_Multi_Pointer:
|
||||
print_string("[^]")
|
||||
print_type(info.elem)
|
||||
case Type_Info_Soa_Pointer:
|
||||
print_string("#soa ^")
|
||||
print_type(info.elem)
|
||||
case Type_Info_Procedure:
|
||||
print_string("proc")
|
||||
if info.params == nil {
|
||||
|
||||
+19
-9
@@ -6,7 +6,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
@(private="file")
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign lib {
|
||||
RtlMoveMemory :: proc(dst, src: rawptr, length: int) ---
|
||||
RtlMoveMemory :: proc(dst, s: rawptr, length: int) ---
|
||||
RtlFillMemory :: proc(dst: rawptr, length: int, fill: i32) ---
|
||||
}
|
||||
|
||||
@@ -40,24 +40,34 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
|
||||
@(link_name="memmove", linkage="strong", require)
|
||||
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if dst != src {
|
||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||
if d == s || len == 0 {
|
||||
return dst
|
||||
}
|
||||
if d > s && uintptr(d)-uintptr(s) < uintptr(len) {
|
||||
for i := len-1; i >= 0; i -= 1 {
|
||||
d[i] = s[i]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
return dst
|
||||
|
||||
|
||||
if s > d && uintptr(s)-uintptr(d) < uintptr(len) {
|
||||
for i := 0; i < len; i += 1 {
|
||||
d[i] = s[i]
|
||||
}
|
||||
return dst
|
||||
}
|
||||
return memcpy(dst, src, len)
|
||||
}
|
||||
@(link_name="memcpy", linkage="strong", require)
|
||||
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
|
||||
if dst != src {
|
||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||
for i := len-1; i >= 0; i -= 1 {
|
||||
d, s := ([^]byte)(dst), ([^]byte)(src)
|
||||
if d != s {
|
||||
for i := 0; i < len; i += 1 {
|
||||
d[i] = s[i]
|
||||
}
|
||||
}
|
||||
return dst
|
||||
return d
|
||||
|
||||
}
|
||||
} else {
|
||||
|
||||
+1
-2
@@ -61,8 +61,7 @@ b64x8 :: #simd[8]b64
|
||||
add :: intrinsics.simd_add
|
||||
sub :: intrinsics.simd_sub
|
||||
mul :: intrinsics.simd_mul
|
||||
div :: intrinsics.simd_div
|
||||
rem :: intrinsics.simd_rem // integers only
|
||||
div :: intrinsics.simd_div // floats only
|
||||
|
||||
// Keeps Odin's Behaviour
|
||||
// (x << y) if y <= mask else 0
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
Copyright 2022 Dale Weiler <weilercdale@gmail.com>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
Dale Weiler: Initial implementation
|
||||
*/
|
||||
|
||||
// Package implements a generic max heap in-place on a slice for any type.
|
||||
package heap
|
||||
|
||||
/*
|
||||
Constructs a max heap in slice given by data with comparator. A max heap is
|
||||
a range of elements which has the following properties:
|
||||
|
||||
1. With N = len(data), for all 0 < i < N, data[(i - 1) / 2] does not compare
|
||||
less than data[i].
|
||||
|
||||
2. A new element can be added using push in O(log n) time.
|
||||
|
||||
3. The first element can be removed using pop in O(log n) time.
|
||||
|
||||
The comparator compares elements of type T and can be used to construct a
|
||||
max heap (less than) or min heap (greater than) for T.
|
||||
*/
|
||||
make :: proc(data: []$T, less: proc(a, b: T) -> bool) {
|
||||
// amoritize length lookup
|
||||
length := len(data)
|
||||
if length <= 1 do return
|
||||
|
||||
// start from data parent, no need to consider children
|
||||
for start := (length - 2) / 2; start >= 0; start -= 1 {
|
||||
sift_down(data, less, start)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Inserts the element at the position len(data)-1 into the max heap with
|
||||
comparator.
|
||||
|
||||
At most log(N) comparisons where N = len(data) will be performed.
|
||||
*/
|
||||
push :: proc(data: []$T, less: proc(a, b: T) -> bool) {
|
||||
sift_up(data, less)
|
||||
}
|
||||
|
||||
/*
|
||||
Swaps the value in position data[0] and the value in data[len(data)-1] and
|
||||
makes subrange [0, len(data)-1) into a heap. This has the effect of removing
|
||||
the first element from the heap.
|
||||
|
||||
At most 2 * log(N) comparisons where N = len(data) will be performed.
|
||||
*/
|
||||
pop :: proc(data: []$T, less: proc(a, b: T) -> bool) {
|
||||
length := len(data)
|
||||
if length <= 1 do return
|
||||
|
||||
last := length
|
||||
|
||||
// create a hole at 0
|
||||
top := data[0]
|
||||
hole := floyd_sift_down(data, less)
|
||||
last -= 1
|
||||
|
||||
if hole == last {
|
||||
data[hole] = top
|
||||
} else {
|
||||
data[hole] = data[last]
|
||||
hole += 1
|
||||
data[last] = top
|
||||
sift_up(data[:hole], less)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Converts the max heap into a sorted range in ascending order. The resulting
|
||||
slice will no longer be a heap after this.
|
||||
|
||||
At most 2 * N * log(N) comparisons where N = len(data) will be performed.
|
||||
*/
|
||||
sort :: proc(data: []$T, less: proc(a, b: T) -> bool) {
|
||||
for n := len(data); n >= 1; n -= 1 {
|
||||
pop(data[:n], less)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Examines the slice and finds the largest range which is a max-heap. Elements
|
||||
are compared with user-supplied comparison procedure.
|
||||
|
||||
This returns the upper bound of the largest range in the slice which is a
|
||||
max heap. That is, the last index for which data is a max heap.
|
||||
|
||||
At most O(n) comparisons where N = len(data) will be performed.
|
||||
*/
|
||||
is_heap_until :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
|
||||
length := len(data)
|
||||
a := 0
|
||||
b := 1
|
||||
for b < length {
|
||||
if less(data[a], data[b]) {
|
||||
return b
|
||||
}
|
||||
b += 1
|
||||
if b == length || less(data[a], data[b]) {
|
||||
return b
|
||||
}
|
||||
a += 1
|
||||
b = 2 * a + 1
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if a given slice is a max heap.
|
||||
|
||||
At most O(n) comparisons where N = len(data) will be performed.
|
||||
*/
|
||||
is_heap :: #force_inline proc(data: []$T, less: proc(a, b: T) -> bool) -> bool {
|
||||
return is_heap_until(data, less) == len(data)
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
floyd_sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool) -> int {
|
||||
length := len(data)
|
||||
assert(length >= 2)
|
||||
|
||||
hole := 0
|
||||
child := 0
|
||||
index := 0
|
||||
for {
|
||||
index += child + 1
|
||||
child = 2 * child + 1
|
||||
if child + 1 < length && less(data[index], data[index + 1]) {
|
||||
child += 1
|
||||
index += 1
|
||||
}
|
||||
|
||||
data[hole] = data[index]
|
||||
hole = index
|
||||
|
||||
if child > (length - 2) / 2 {
|
||||
return hole
|
||||
}
|
||||
}
|
||||
|
||||
unreachable()
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
sift_down :: proc(data: []$T, less: proc(a, b: T) -> bool, start: int) {
|
||||
start := start
|
||||
child := start
|
||||
|
||||
// amoritize length lookup
|
||||
length := len(data)
|
||||
|
||||
// left child of start is at 2 * start + 1
|
||||
// right child of start is at 2 * start + 2
|
||||
if length < 2 || (length - 2) / 2 < child {
|
||||
return
|
||||
}
|
||||
|
||||
child = 2 * child + 1
|
||||
|
||||
if child + 1 < length && less(data[child], data[child + 1]) {
|
||||
// right child exists and is greater than left child
|
||||
child += 1
|
||||
}
|
||||
|
||||
// check if in heap order
|
||||
if less(data[child], data[start]) {
|
||||
// start is larger than its largest child
|
||||
return
|
||||
}
|
||||
|
||||
top := data[start]
|
||||
for {
|
||||
// not in heap order, swap parent with its largest child
|
||||
data[start] = data[child]
|
||||
start = child
|
||||
|
||||
if (length - 2) / 2 < child {
|
||||
break
|
||||
}
|
||||
|
||||
// recompute child based off updated parent
|
||||
child = 2 * child + 1
|
||||
|
||||
if child + 1 < length && less(data[child], data[child + 1]) {
|
||||
// right child exists and is greater than left child
|
||||
child += 1
|
||||
}
|
||||
|
||||
// check if we are in heap order
|
||||
if less(data[child], top) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
data[start] = top
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
sift_up :: proc(data: []$T, less: proc(a, b: T) -> bool) {
|
||||
// amoritize length lookup
|
||||
length := len(data)
|
||||
|
||||
if length <= 1 do return
|
||||
|
||||
last := length
|
||||
length = (length - 2) / 2
|
||||
index := length
|
||||
last -= 1
|
||||
if less(data[index], data[last]) {
|
||||
top := data[last]
|
||||
for {
|
||||
data[last] = data[index]
|
||||
last = index
|
||||
if length == 0 {
|
||||
break
|
||||
}
|
||||
length = (length - 1) / 2
|
||||
index = length
|
||||
if !less(data[index], top) {
|
||||
break
|
||||
}
|
||||
}
|
||||
data[last] = top
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -2,11 +2,9 @@ package slice
|
||||
|
||||
import "core:intrinsics"
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
_ :: mem
|
||||
|
||||
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
|
||||
keys = make(type_of(keys), len(m), allocator)
|
||||
@@ -52,7 +50,7 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
|
||||
|
||||
map_entry_infos :: 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)
|
||||
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)
|
||||
|
||||
+17
-2
@@ -14,14 +14,14 @@ _ :: mem
|
||||
Turn a pointer and a length into a slice.
|
||||
*/
|
||||
from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
|
||||
return ([^]T)(ptr)[:count]
|
||||
return ([^]T)(ptr)[:count]
|
||||
}
|
||||
|
||||
/*
|
||||
Turn a pointer and a length into a byte slice.
|
||||
*/
|
||||
bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
|
||||
return ([^]byte)(ptr)[:byte_count]
|
||||
return ([^]byte)(ptr)[:byte_count]
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -170,6 +170,21 @@ simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_comp
|
||||
return mem.compare_ptrs(raw_data(a), raw_data(b), len(a)*size_of(E)) == 0
|
||||
}
|
||||
|
||||
/*
|
||||
return the prefix length common between slices `a` and `b`.
|
||||
|
||||
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1}) -> 1
|
||||
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1, 2, 3}) -> 3
|
||||
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{2, 3, 4}) -> 0
|
||||
*/
|
||||
prefix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_comparable(E) {
|
||||
_len := builtin.min(len(a), len(b))
|
||||
|
||||
#no_bounds_check for n < _len && a[n] == b[n] {
|
||||
n += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) {
|
||||
n := len(needle)
|
||||
|
||||
+63
-1
@@ -38,6 +38,52 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sort_by_indices :: proc{ sort_by_indices_allocate, _sort_by_indices}
|
||||
|
||||
sort_by_indices_allocate :: proc(data: $T/[]$E, indices: []int, allocator := context.allocator) -> (sorted: T) {
|
||||
assert(len(data) == len(indices))
|
||||
sorted = make([]int, len(data), allocator)
|
||||
for v, i in indices {
|
||||
sorted[i] = data[v]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_sort_by_indices :: proc(data, sorted: $T/[]$E, indices: []int) {
|
||||
assert(len(data) == len(indices))
|
||||
assert(len(data) == len(sorted))
|
||||
for v, i in indices {
|
||||
sorted[i] = data[v]
|
||||
}
|
||||
}
|
||||
|
||||
sort_by_indices_overwrite :: proc(data: $T/[]$E, indices: []int) {
|
||||
assert(len(data) == len(indices))
|
||||
temp := make([]int, len(data), context.allocator)
|
||||
defer delete(temp)
|
||||
for v, i in indices {
|
||||
temp[i] = data[v]
|
||||
}
|
||||
swap_with_slice(data, temp)
|
||||
}
|
||||
|
||||
// sort sorts a slice and returns a slice of the original indices
|
||||
// This sort is not guaranteed to be stable
|
||||
sort_with_indices :: proc(data: $T/[]$E, allocator := context.allocator) -> (indices: []int) where ORD(E) {
|
||||
indices = make([]int, len(data), allocator)
|
||||
when size_of(E) != 0 {
|
||||
if n := len(data); n > 1 {
|
||||
for _, idx in indices {
|
||||
indices[idx] = idx
|
||||
}
|
||||
_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), struct{}{}, .Ordered)
|
||||
}
|
||||
return indices
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
|
||||
// This sort is not guaranteed to be stable
|
||||
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
|
||||
@@ -48,6 +94,22 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
|
||||
// This sort is not guaranteed to be stable
|
||||
sort_by_with_indices :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool, allocator := context.allocator) -> (indices : []int) {
|
||||
indices = make([]int, len(data), allocator)
|
||||
when size_of(E) != 0 {
|
||||
if n := len(data); n > 1 {
|
||||
for _, idx in indices {
|
||||
indices[idx] = idx
|
||||
}
|
||||
_quick_sort_general_with_indices(data, indices, 0, n, _max_depth(n), less, .Less)
|
||||
return indices
|
||||
}
|
||||
}
|
||||
return indices
|
||||
}
|
||||
|
||||
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
|
||||
when size_of(E) != 0 {
|
||||
if n := len(data); n > 1 {
|
||||
@@ -103,7 +165,7 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
|
||||
is_sorted_by_cmp :: is_sorted_cmp
|
||||
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
|
||||
for i := len(array)-1; i > 0; i -= 1 {
|
||||
if cmp(array[i], array[i-1]) == .Equal {
|
||||
if cmp(array[i], array[i-1]) == .Less {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,3 +198,180 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_quick_sort_general_with_indices :: proc(data: $T/[]$E, indices: []int, a, b, max_depth: int, 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 {
|
||||
return a < b
|
||||
} else when KIND == .Less {
|
||||
return call(a, b)
|
||||
} else when KIND == .Cmp {
|
||||
return call(a, b) == .Less
|
||||
} else {
|
||||
#panic("unhandled Sort_Kind")
|
||||
}
|
||||
}
|
||||
|
||||
insertion_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check {
|
||||
for i in a+1..<b {
|
||||
for j := i; j > a && less(data[j], data[j-1], call); j -= 1 {
|
||||
swap(data, j, j-1)
|
||||
swap(indices, j, j-1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
heap_sort :: proc(data: $T/[]$E, indices: []int, a, b: int, call: P) #no_bounds_check {
|
||||
sift_down :: proc(data: T, indices: []int, lo, hi, first: int, call: P) #no_bounds_check {
|
||||
root := lo
|
||||
for {
|
||||
child := 2*root + 1
|
||||
if child >= hi {
|
||||
break
|
||||
}
|
||||
if child+1 < hi && less(data[first+child], data[first+child+1], call) {
|
||||
child += 1
|
||||
}
|
||||
if !less(data[first+root], data[first+child], call) {
|
||||
return
|
||||
}
|
||||
swap(data, first+root, first+child)
|
||||
swap(indices, first+root, first+child)
|
||||
root = child
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
first, lo, hi := a, 0, b-a
|
||||
|
||||
for i := (hi-1)/2; i >= 0; i -= 1 {
|
||||
sift_down(data, indices, i, hi, first, call)
|
||||
}
|
||||
|
||||
for i := hi-1; i >= 0; i -= 1 {
|
||||
swap(data, first, first+i)
|
||||
swap(indices, first, first+i)
|
||||
sift_down(data, indices, lo, i, first, call)
|
||||
}
|
||||
}
|
||||
|
||||
median3 :: proc(data: T, indices: []int, m1, m0, m2: int, call: P) #no_bounds_check {
|
||||
if less(data[m1], data[m0], call) {
|
||||
swap(data, m1, m0)
|
||||
swap(indices, m1, m0)
|
||||
}
|
||||
if less(data[m2], data[m1], call) {
|
||||
swap(data, m2, m1)
|
||||
swap(indices, m2, m1)
|
||||
if less(data[m1], data[m0], call) {
|
||||
swap(data, m1, m0)
|
||||
swap(indices, m1, m0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_pivot :: proc(data: T, indices: []int, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check {
|
||||
m := int(uint(lo+hi)>>1)
|
||||
if hi-lo > 40 {
|
||||
s := (hi-lo)/8
|
||||
median3(data, indices, lo, lo+s, lo+s*2, call)
|
||||
median3(data, indices, m, m-s, m+s, call)
|
||||
median3(data, indices, hi-1, hi-1-s, hi-1-s*2, call)
|
||||
}
|
||||
median3(data, indices, lo, m, hi-1, call)
|
||||
|
||||
pivot := lo
|
||||
a, c := lo+1, hi-1
|
||||
|
||||
|
||||
for ; a < c && less(data[a], data[pivot], call); a += 1 {
|
||||
}
|
||||
b := a
|
||||
|
||||
for {
|
||||
for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot
|
||||
}
|
||||
for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot
|
||||
}
|
||||
if b >= c {
|
||||
break
|
||||
}
|
||||
|
||||
swap(data, b, c-1)
|
||||
swap(indices, b, c-1)
|
||||
b += 1
|
||||
c -= 1
|
||||
}
|
||||
|
||||
protect := hi-c < 5
|
||||
if !protect && hi-c < (hi-lo)/4 {
|
||||
dups := 0
|
||||
if !less(data[pivot], data[hi-1], call) {
|
||||
swap(data, c, hi-1)
|
||||
swap(indices, c, hi-1)
|
||||
c += 1
|
||||
dups += 1
|
||||
}
|
||||
if !less(data[b-1], data[pivot], call) {
|
||||
b -= 1
|
||||
dups += 1
|
||||
}
|
||||
|
||||
if !less(data[m], data[pivot], call) {
|
||||
swap(data, m, b-1)
|
||||
swap(indices, m, b-1)
|
||||
b -= 1
|
||||
dups += 1
|
||||
}
|
||||
protect = dups > 1
|
||||
}
|
||||
if protect {
|
||||
for {
|
||||
for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 {
|
||||
}
|
||||
for ; a < b && less(data[a], data[pivot], call); a += 1 {
|
||||
}
|
||||
if a >= b {
|
||||
break
|
||||
}
|
||||
swap(data, a, b-1)
|
||||
swap(indices, a, b-1)
|
||||
a += 1
|
||||
b -= 1
|
||||
}
|
||||
}
|
||||
swap(data, pivot, b-1)
|
||||
swap(indices, pivot, b-1)
|
||||
return b-1, c
|
||||
}
|
||||
|
||||
assert(len(data) == len(indices))
|
||||
|
||||
a, b, max_depth := a, b, max_depth
|
||||
|
||||
for b-a > 12 { // only use shell sort for lengths <= 12
|
||||
if max_depth == 0 {
|
||||
heap_sort(data, indices, a, b, call)
|
||||
return
|
||||
}
|
||||
max_depth -= 1
|
||||
mlo, mhi := do_pivot(data, indices, a, b, call)
|
||||
if mlo-a < b-mhi {
|
||||
_quick_sort_general_with_indices(data, indices, a, mlo, max_depth, call, KIND)
|
||||
a = mhi
|
||||
} else {
|
||||
_quick_sort_general_with_indices(data, indices, mhi, b, max_depth, call, KIND)
|
||||
b = mlo
|
||||
}
|
||||
}
|
||||
if b-a > 1 {
|
||||
// Shell short with gap 6
|
||||
for i in a+6..<b {
|
||||
if less(data[i], data[i-6], call) {
|
||||
swap(data, i, i-6)
|
||||
swap(indices, i, i-6)
|
||||
}
|
||||
}
|
||||
insertion_sort(data, indices, a, b, call)
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -684,5 +684,10 @@ compare_f64s :: proc(a, b: f64) -> int {
|
||||
compare_strings :: proc(a, b: string) -> int {
|
||||
x := transmute(mem.Raw_String)a
|
||||
y := transmute(mem.Raw_String)b
|
||||
return mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len))
|
||||
|
||||
ret := mem.compare_byte_ptrs(x.data, y.data, min(x.len, y.len))
|
||||
if ret == 0 && x.len != y.len {
|
||||
return -1 if x.len < y.len else +1
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
+31
-18
@@ -2,11 +2,13 @@ package strconv
|
||||
|
||||
import "core:unicode/utf8"
|
||||
|
||||
parse_bool :: proc(s: string) -> (result: bool = false, ok: bool) {
|
||||
parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
|
||||
switch s {
|
||||
case "1", "t", "T", "true", "TRUE", "True":
|
||||
if n != nil { n^ = len(s) }
|
||||
return true, true
|
||||
case "0", "f", "F", "false", "FALSE", "False":
|
||||
if n != nil { n^ = len(s) }
|
||||
return false, true
|
||||
}
|
||||
return
|
||||
@@ -32,10 +34,13 @@ _digit_value :: proc(r: rune) -> int {
|
||||
// n, ok := strconv.parse_i64_of_base("-1234eeee", 10);
|
||||
// assert(n == -1234 && ok);
|
||||
// ```
|
||||
parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
|
||||
parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) {
|
||||
assert(base <= 16, "base must be 1-16")
|
||||
|
||||
s := str
|
||||
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -87,8 +92,9 @@ parse_i64_of_base :: proc(str: string, base: int) -> (value: i64, ok: bool) {
|
||||
// n, ok = strconv.parse_i64_maybe_prefixed("0xeeee");
|
||||
// assert(n == 0xeeee && ok);
|
||||
// ```
|
||||
parse_i64_maybe_prefixed :: proc(str: string) -> (value: i64, ok: bool) {
|
||||
parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) {
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -155,9 +161,10 @@ parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}
|
||||
// n, ok = strconv.parse_u64_of_base("5678eeee", 16);
|
||||
// assert(n == 0x5678eeee && ok);
|
||||
// ```
|
||||
parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
|
||||
parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) {
|
||||
assert(base <= 16, "base must be 1-16")
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -198,8 +205,9 @@ parse_u64_of_base :: proc(str: string, base: int) -> (value: u64, ok: bool) {
|
||||
// n, ok = strconv.parse_u64_maybe_prefixed("0xeeee");
|
||||
// assert(n == 0xeeee && ok);
|
||||
// ```
|
||||
parse_u64_maybe_prefixed :: proc(str: string) -> (value: u64, ok: bool) {
|
||||
parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) {
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -259,11 +267,11 @@ parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}
|
||||
// n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base
|
||||
// assert(n == 0xffff && ok);
|
||||
// ```
|
||||
parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
|
||||
parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) {
|
||||
v: i64 = ---
|
||||
switch base {
|
||||
case 0: v, ok = parse_i64_maybe_prefixed(s)
|
||||
case: v, ok = parse_i64_of_base(s, base)
|
||||
case 0: v, ok = parse_i64_maybe_prefixed(s, n)
|
||||
case: v, ok = parse_i64_of_base(s, base, n)
|
||||
}
|
||||
value = int(v)
|
||||
return
|
||||
@@ -289,11 +297,11 @@ parse_int :: proc(s: string, base := 0) -> (value: int, ok: bool) {
|
||||
// n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base
|
||||
// assert(n == 0xffff && ok);
|
||||
// ```
|
||||
parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
|
||||
parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) {
|
||||
v: u64 = ---
|
||||
switch base {
|
||||
case 0: v, ok = parse_u64_maybe_prefixed(s)
|
||||
case: v, ok = parse_u64_of_base(s, base)
|
||||
case 0: v, ok = parse_u64_maybe_prefixed(s, n)
|
||||
case: v, ok = parse_u64_of_base(s, base, n)
|
||||
}
|
||||
value = uint(v)
|
||||
return
|
||||
@@ -309,10 +317,11 @@ parse_uint :: proc(s: string, base := 0) -> (value: uint, ok: bool) {
|
||||
// n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
|
||||
// assert(n == -1234 && ok);
|
||||
// ```
|
||||
parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
|
||||
parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) {
|
||||
assert(base <= 16, "base must be 1-16")
|
||||
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -364,8 +373,9 @@ parse_i128_of_base :: proc(str: string, base: int) -> (value: i128, ok: bool) {
|
||||
// n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
|
||||
// assert(n == 0xeeee && ok);
|
||||
// ```
|
||||
parse_i128_maybe_prefixed :: proc(str: string) -> (value: i128, ok: bool) {
|
||||
parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) {
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -432,9 +442,10 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
|
||||
// n, ok = strconv.parse_u128_of_base("5678eeee", 16);
|
||||
// assert(n == 0x5678eeee && ok);
|
||||
// ```
|
||||
parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
|
||||
parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) {
|
||||
assert(base <= 16, "base must be 1-16")
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -475,8 +486,9 @@ parse_u128_of_base :: proc(str: string, base: int) -> (value: u128, ok: bool) {
|
||||
// n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
|
||||
// assert(n == 0xeeee && ok);
|
||||
// ```
|
||||
parse_u128_maybe_prefixed :: proc(str: string) -> (value: u128, ok: bool) {
|
||||
parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) {
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str)-len(s) }
|
||||
if s == "" {
|
||||
return
|
||||
}
|
||||
@@ -535,9 +547,9 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
|
||||
// n, ok = strconv.parse_f32("12.34");
|
||||
// assert(n == 12.34 && ok);
|
||||
// ```
|
||||
parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
|
||||
parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
|
||||
v: f64 = ---
|
||||
v, ok = parse_f64(s)
|
||||
v, ok = parse_f64(s, n)
|
||||
return f32(v), ok
|
||||
}
|
||||
|
||||
@@ -553,8 +565,9 @@ parse_f32 :: proc(s: string) -> (value: f32, ok: bool) {
|
||||
// n, ok = strconv.parse_f32("12.34");
|
||||
// assert(n == 12.34 && ok);
|
||||
// ```
|
||||
parse_f64 :: proc(str: string) -> (value: f64, ok: bool) {
|
||||
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 == "" {
|
||||
return
|
||||
}
|
||||
|
||||
+49
-111
@@ -1,6 +1,6 @@
|
||||
package strings
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
import "core:unicode/utf8"
|
||||
import "core:strconv"
|
||||
import "core:io"
|
||||
@@ -17,50 +17,53 @@ Builder :: struct {
|
||||
}
|
||||
|
||||
// return a builder, default length 0 / cap 16 are done through make
|
||||
make_builder_none :: proc(allocator := context.allocator) -> Builder {
|
||||
builder_make_none :: proc(allocator := context.allocator) -> Builder {
|
||||
return Builder{buf=make([dynamic]byte, allocator)}
|
||||
}
|
||||
|
||||
// return a builder, with a set length `len` and cap 16 byte buffer
|
||||
make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder {
|
||||
builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
|
||||
return Builder{buf=make([dynamic]byte, len, allocator)}
|
||||
}
|
||||
|
||||
// return a builder, with a set length `len` byte buffer and a custom `cap`
|
||||
make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
|
||||
builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
|
||||
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
|
||||
}
|
||||
|
||||
// overload simple `make_builder_*` with or without len / cap parameters
|
||||
make_builder :: proc{
|
||||
make_builder_none,
|
||||
make_builder_len,
|
||||
make_builder_len_cap,
|
||||
// overload simple `builder_make_*` with or without len / cap parameters
|
||||
builder_make :: proc{
|
||||
builder_make_none,
|
||||
builder_make_len,
|
||||
builder_make_len_cap,
|
||||
}
|
||||
|
||||
// initialize a builder, default length 0 / cap 16 are done through make
|
||||
// replaces the existing `buf`
|
||||
init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
|
||||
builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
|
||||
b.buf = make([dynamic]byte, allocator)
|
||||
return b
|
||||
}
|
||||
|
||||
// initialize a builder, with a set length `len` and cap 16 byte buffer
|
||||
// replaces the existing `buf`
|
||||
init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
|
||||
builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
|
||||
b.buf = make([dynamic]byte, len, allocator)
|
||||
return b
|
||||
}
|
||||
|
||||
// initialize a builder, with a set length `len` byte buffer and a custom `cap`
|
||||
// replaces the existing `buf`
|
||||
init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
|
||||
builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
|
||||
b.buf = make([dynamic]byte, len, cap, allocator)
|
||||
return b
|
||||
}
|
||||
|
||||
// overload simple `init_builder_*` with or without len / ap parameters
|
||||
init_builder :: proc{
|
||||
init_builder_none,
|
||||
init_builder_len,
|
||||
init_builder_len_cap,
|
||||
// overload simple `builder_init_*` with or without len / ap parameters
|
||||
builder_init :: proc{
|
||||
builder_init_none,
|
||||
builder_init_len,
|
||||
builder_init_len_cap,
|
||||
}
|
||||
|
||||
@(private)
|
||||
@@ -103,18 +106,18 @@ to_writer :: proc(b: ^Builder) -> io.Writer {
|
||||
}
|
||||
|
||||
// delete and clear the builder byte buffer content
|
||||
destroy_builder :: proc(b: ^Builder) {
|
||||
builder_destroy :: proc(b: ^Builder) {
|
||||
delete(b.buf)
|
||||
clear(&b.buf)
|
||||
}
|
||||
|
||||
// reserve the builfer byte buffer to a specific cap, when it's higher than before
|
||||
grow_builder :: proc(b: ^Builder, cap: int) {
|
||||
builder_grow :: proc(b: ^Builder, cap: int) {
|
||||
reserve(&b.buf, cap)
|
||||
}
|
||||
|
||||
// clear the builder byte buffer content
|
||||
reset_builder :: proc(b: ^Builder) {
|
||||
builder_reset :: proc(b: ^Builder) {
|
||||
clear(&b.buf)
|
||||
}
|
||||
|
||||
@@ -129,12 +132,12 @@ reset_builder :: proc(b: ^Builder) {
|
||||
strings.write_byte(&builder, 'b') -> "ab"
|
||||
*/
|
||||
builder_from_bytes :: proc(backing: []byte) -> Builder {
|
||||
s := transmute(mem.Raw_Slice)backing
|
||||
d := mem.Raw_Dynamic_Array{
|
||||
s := transmute(runtime.Raw_Slice)backing
|
||||
d := runtime.Raw_Dynamic_Array{
|
||||
data = s.data,
|
||||
len = 0,
|
||||
cap = s.len,
|
||||
allocator = mem.nil_allocator(),
|
||||
allocator = runtime.nil_allocator(),
|
||||
}
|
||||
return Builder{
|
||||
buf = transmute([dynamic]byte)d,
|
||||
@@ -165,7 +168,7 @@ builder_space :: proc(b: Builder) -> int {
|
||||
/*
|
||||
appends a byte to the builder, returns the append diff
|
||||
|
||||
builder := strings.make_builder()
|
||||
builder := strings.builder_make()
|
||||
strings.write_byte(&builder, 'a') // 1
|
||||
strings.write_byte(&builder, 'b') // 1
|
||||
strings.write_byte(&builder, 'c') // 1
|
||||
@@ -181,7 +184,7 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
|
||||
/*
|
||||
appends a slice of bytes to the builder, returns the append diff
|
||||
|
||||
builder := strings.make_builder()
|
||||
builder := strings.builder_make()
|
||||
bytes := [?]byte { 'a', 'b', 'c' }
|
||||
strings.write_bytes(&builder, bytes[:]) // 3
|
||||
fmt.println(strings.to_string(builder)) // -> abc
|
||||
@@ -196,77 +199,46 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
|
||||
/*
|
||||
appends a single rune into the builder, returns written rune size and an `io.Error`
|
||||
|
||||
builder := strings.make_builder()
|
||||
strings.write_rune_builder(&builder, 'ä') // 2 None
|
||||
strings.write_rune_builder(&builder, 'b') // 1 None
|
||||
strings.write_rune_builder(&builder, 'c') // 1 None
|
||||
builder := strings.builder_make()
|
||||
strings.write_rune(&builder, 'ä') // 2 None
|
||||
strings.write_rune(&builder, 'b') // 1 None
|
||||
strings.write_rune(&builder, 'c') // 1 None
|
||||
fmt.println(strings.to_string(builder)) // -> äbc
|
||||
*/
|
||||
write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
|
||||
write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
|
||||
return io.write_rune(to_writer(b), r)
|
||||
}
|
||||
|
||||
/*
|
||||
appends a quoted rune into the builder, returns written size
|
||||
|
||||
builder := strings.make_builder()
|
||||
builder := strings.builder_make()
|
||||
strings.write_string(&builder, "abc") // 3
|
||||
strings.write_quoted_rune_builder(&builder, 'ä') // 4
|
||||
strings.write_quoted_rune(&builder, 'ä') // 4
|
||||
strings.write_string(&builder, "abc") // 3
|
||||
fmt.println(strings.to_string(builder)) // -> abc'ä'abc
|
||||
*/
|
||||
write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
|
||||
return write_quoted_rune(to_writer(b), r)
|
||||
write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
|
||||
return io.write_quoted_rune(to_writer(b), r)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_write_byte :: proc(w: io.Writer, c: byte) -> int {
|
||||
err := io.write_byte(w, c)
|
||||
return 1 if err == nil else 0
|
||||
}
|
||||
|
||||
// writer append a quoted rune into the byte buffer, return the written size
|
||||
write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
|
||||
quote := byte('\'')
|
||||
n += _write_byte(w, quote)
|
||||
buf, width := utf8.encode_rune(r)
|
||||
if width == 1 && r == utf8.RUNE_ERROR {
|
||||
n += _write_byte(w, '\\')
|
||||
n += _write_byte(w, 'x')
|
||||
n += _write_byte(w, DIGITS_LOWER[buf[0]>>4])
|
||||
n += _write_byte(w, DIGITS_LOWER[buf[0]&0xf])
|
||||
} else {
|
||||
i, _ := io.write_escaped_rune(w, r, quote)
|
||||
n += i
|
||||
}
|
||||
n += _write_byte(w, quote)
|
||||
return
|
||||
}
|
||||
|
||||
// overload for `write_string_*` variants
|
||||
write_string :: proc{
|
||||
write_string_builder,
|
||||
write_string_writer,
|
||||
}
|
||||
|
||||
/*
|
||||
appends a string to the builder, return the written byte size
|
||||
|
||||
builder := strings.make_builder()
|
||||
builder := strings.builder_make()
|
||||
strings.write_string(&builder, "a") // 1
|
||||
strings.write_string(&builder, "bc") // 2
|
||||
strings.write_string(&builder, "xyz") // 3
|
||||
fmt.println(strings.to_string(builder)) // -> abcxyz
|
||||
*/
|
||||
write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
|
||||
return write_string_writer(to_writer(b), s)
|
||||
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
|
||||
n0 := len(b.buf)
|
||||
append(&b.buf, s)
|
||||
n1 := len(b.buf)
|
||||
return n1-n0
|
||||
}
|
||||
|
||||
// appends a string to the writer
|
||||
write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
|
||||
n, _ = io.write(w, transmute([]byte)s)
|
||||
return
|
||||
}
|
||||
|
||||
// pops and returns the last byte in the builder
|
||||
// returns 0 when the builder is empty
|
||||
@@ -276,7 +248,7 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) {
|
||||
}
|
||||
|
||||
r = b.buf[len(b.buf)-1]
|
||||
d := cast(^mem.Raw_Dynamic_Array)&b.buf
|
||||
d := cast(^runtime.Raw_Dynamic_Array)&b.buf
|
||||
d.len = max(d.len-1, 0)
|
||||
return
|
||||
}
|
||||
@@ -289,7 +261,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
||||
}
|
||||
|
||||
r, width = utf8.decode_last_rune(b.buf[:])
|
||||
d := cast(^mem.Raw_Dynamic_Array)&b.buf
|
||||
d := cast(^runtime.Raw_Dynamic_Array)&b.buf
|
||||
d.len = max(d.len-width, 0)
|
||||
return
|
||||
}
|
||||
@@ -297,70 +269,36 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
|
||||
@(private)
|
||||
DIGITS_LOWER := "0123456789abcdefx"
|
||||
|
||||
// overload for `write_quoted_string_*` variants
|
||||
write_quoted_string :: proc{
|
||||
write_quoted_string_builder,
|
||||
write_quoted_string_writer,
|
||||
}
|
||||
|
||||
/*
|
||||
append a quoted string into the builder, return the written byte size
|
||||
|
||||
builder := strings.make_builder()
|
||||
builder := strings.builder_make()
|
||||
strings.write_quoted_string(&builder, "a") // 3
|
||||
strings.write_quoted_string(&builder, "bc", '\'') // 4
|
||||
strings.write_quoted_string(&builder, "xyz") // 5
|
||||
fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
|
||||
*/
|
||||
write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
|
||||
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
|
||||
n, _ = io.write_quoted_string(to_writer(b), str, quote)
|
||||
return
|
||||
}
|
||||
|
||||
@(deprecated="prefer io.write_quoted_string")
|
||||
write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"') -> (n: int) {
|
||||
n, _ = io.write_quoted_string(w, str, quote)
|
||||
return
|
||||
}
|
||||
|
||||
// overload for `write_encoded_rune_*`
|
||||
write_encoded_rune :: proc{
|
||||
write_encoded_rune_builder,
|
||||
write_encoded_rune_writer,
|
||||
}
|
||||
|
||||
// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
|
||||
write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
|
||||
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
|
||||
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
|
||||
return
|
||||
|
||||
}
|
||||
@(deprecated="prefer io.write_encoded_rune")
|
||||
write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) -> (n: int) {
|
||||
n, _ = io.write_encoded_rune(w, r, write_quote)
|
||||
return
|
||||
}
|
||||
|
||||
// overload for `write_escaped_rune_*`
|
||||
write_escaped_rune :: proc{
|
||||
write_escaped_rune_builder,
|
||||
write_escaped_rune_writer,
|
||||
}
|
||||
|
||||
// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
|
||||
// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
|
||||
// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
|
||||
write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
||||
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
||||
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
|
||||
return
|
||||
}
|
||||
|
||||
@(deprecated="prefer io.write_escaped_rune")
|
||||
write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe := false) -> (n: int) {
|
||||
n, _ = io.write_escaped_rune(w, r, quote, html_safe)
|
||||
return
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
@@ -10,7 +10,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
b: Builder
|
||||
init_builder(&b, 0, 0, allocator)
|
||||
builder_init(&b, 0, 0, allocator)
|
||||
|
||||
s := s
|
||||
for c, i in s {
|
||||
@@ -20,7 +20,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
|
||||
|
||||
_, w := utf8.decode_rune_in_string(s[i:])
|
||||
if w == 1 {
|
||||
grow_builder(&b, len(s) + len(replacement))
|
||||
builder_grow(&b, len(s) + len(replacement))
|
||||
write_string(&b, s[:i])
|
||||
s = s[i:]
|
||||
break
|
||||
@@ -67,9 +67,9 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
|
||||
*/
|
||||
to_lower :: proc(s: string, allocator := context.allocator) -> string {
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
for r in s {
|
||||
write_rune_builder(&b, unicode.to_lower(r))
|
||||
write_rune(&b, unicode.to_lower(r))
|
||||
}
|
||||
return to_string(b)
|
||||
}
|
||||
@@ -83,9 +83,9 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string {
|
||||
*/
|
||||
to_upper :: proc(s: string, allocator := context.allocator) -> string {
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
for r in s {
|
||||
write_rune_builder(&b, unicode.to_upper(r))
|
||||
write_rune(&b, unicode.to_upper(r))
|
||||
}
|
||||
return to_string(b)
|
||||
}
|
||||
@@ -147,7 +147,7 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||
s := s
|
||||
s = trim_space(s)
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
w := to_writer(&b)
|
||||
|
||||
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
|
||||
@@ -172,7 +172,7 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||
s := s
|
||||
s = trim_space(s)
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
w := to_writer(&b)
|
||||
|
||||
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
|
||||
@@ -203,7 +203,7 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
|
||||
s := s
|
||||
s = trim_space(s)
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
w := to_writer(&b)
|
||||
|
||||
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower
|
||||
@@ -272,7 +272,7 @@ to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
|
||||
s := s
|
||||
s = trim_space(s)
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
w := to_writer(&b)
|
||||
|
||||
prev, curr: rune
|
||||
|
||||
+13
-12
@@ -1,6 +1,6 @@
|
||||
package strings
|
||||
|
||||
import "core:mem"
|
||||
import "core:runtime"
|
||||
|
||||
// custom string entry struct
|
||||
Intern_Entry :: struct {
|
||||
@@ -11,7 +11,7 @@ Intern_Entry :: struct {
|
||||
// "intern" is a more memory efficient string map
|
||||
// `allocator` is used to allocate the actual `Intern_Entry` strings
|
||||
Intern :: struct {
|
||||
allocator: mem.Allocator,
|
||||
allocator: runtime.Allocator,
|
||||
entries: map[string]^Intern_Entry,
|
||||
}
|
||||
|
||||
@@ -31,30 +31,31 @@ intern_destroy :: proc(m: ^Intern) {
|
||||
|
||||
// returns the `text` string from the intern map - gets set if it didnt exist yet
|
||||
// the returned string lives as long as the map entry lives
|
||||
intern_get :: proc(m: ^Intern, text: string) -> string {
|
||||
entry := _intern_get_entry(m, text)
|
||||
#no_bounds_check return string(entry.str[:entry.len])
|
||||
intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
|
||||
entry := _intern_get_entry(m, text) or_return
|
||||
#no_bounds_check return string(entry.str[:entry.len]), nil
|
||||
}
|
||||
|
||||
// returns the `text` cstring from the intern map - gets set if it didnt exist yet
|
||||
// the returned cstring lives as long as the map entry lives
|
||||
intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring {
|
||||
entry := _intern_get_entry(m, text)
|
||||
return cstring(&entry.str[0])
|
||||
intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
|
||||
entry := _intern_get_entry(m, text) or_return
|
||||
return cstring(&entry.str[0]), nil
|
||||
}
|
||||
|
||||
// looks up wether the `text` string exists in the map, returns the entry
|
||||
// sets & allocates the entry if it wasnt set yet
|
||||
_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check {
|
||||
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
|
||||
if prev, ok := m.entries[text]; ok {
|
||||
return prev
|
||||
return prev, nil
|
||||
}
|
||||
if m.allocator.procedure == nil {
|
||||
m.allocator = context.allocator
|
||||
}
|
||||
|
||||
entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1
|
||||
new_entry := (^Intern_Entry)(mem.alloc(entry_size, align_of(Intern_Entry), m.allocator))
|
||||
bytes := runtime.mem_alloc(entry_size, align_of(Intern_Entry), m.allocator) or_return
|
||||
new_entry = (^Intern_Entry)(raw_data(bytes))
|
||||
|
||||
new_entry.len = len(text)
|
||||
copy(new_entry.str[:new_entry.len], text)
|
||||
@@ -62,5 +63,5 @@ _intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_
|
||||
|
||||
key := string(new_entry.str[:new_entry.len])
|
||||
m.entries[key] = new_entry
|
||||
return new_entry
|
||||
return new_entry, nil
|
||||
}
|
||||
|
||||
@@ -215,6 +215,38 @@ equal_fold :: proc(u, v: string) -> bool {
|
||||
return s == t
|
||||
}
|
||||
|
||||
/*
|
||||
return the prefix length common between strings `a` and `b`.
|
||||
|
||||
strings.prefix_length("testing", "test") -> 4
|
||||
strings.prefix_length("testing", "te") -> 2
|
||||
strings.prefix_length("telephone", "te") -> 2
|
||||
strings.prefix_length("testing", "est") -> 0
|
||||
*/
|
||||
prefix_length :: proc(a, b: string) -> (n: int) {
|
||||
_len := min(len(a), len(b))
|
||||
|
||||
// Scan for matches including partial codepoints.
|
||||
#no_bounds_check for n < _len && a[n] == b[n] {
|
||||
n += 1
|
||||
}
|
||||
|
||||
// Now scan to ignore partial codepoints.
|
||||
if n > 0 {
|
||||
s := a[:n]
|
||||
n = 0
|
||||
for {
|
||||
r0, w := utf8.decode_rune(s[n:])
|
||||
if r0 != utf8.RUNE_ERROR {
|
||||
n += w
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
return true when the string `prefix` is contained at the start of the string `s`
|
||||
|
||||
@@ -1521,7 +1553,7 @@ split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #
|
||||
scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
|
||||
str := s
|
||||
b: Builder
|
||||
init_builder(&b, 0, len(s), allocator)
|
||||
builder_init(&b, 0, len(s), allocator)
|
||||
|
||||
has_error := false
|
||||
cursor := 0
|
||||
@@ -1590,7 +1622,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
|
||||
}
|
||||
|
||||
b: Builder
|
||||
init_builder(&b, allocator)
|
||||
builder_init(&b, allocator)
|
||||
writer := to_writer(&b)
|
||||
str := s
|
||||
column: int
|
||||
@@ -1658,8 +1690,8 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Builder
|
||||
init_builder(&b, allocator)
|
||||
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad))
|
||||
builder_init(&b, allocator)
|
||||
builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
|
||||
|
||||
w := to_writer(&b)
|
||||
|
||||
@@ -1681,8 +1713,8 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Builder
|
||||
init_builder(&b, allocator)
|
||||
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad))
|
||||
builder_init(&b, allocator)
|
||||
builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
|
||||
|
||||
w := to_writer(&b)
|
||||
|
||||
@@ -1703,8 +1735,8 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
|
||||
pad_len := rune_count(pad)
|
||||
|
||||
b: Builder
|
||||
init_builder(&b, allocator)
|
||||
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad))
|
||||
builder_init(&b, allocator)
|
||||
builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
|
||||
|
||||
w := to_writer(&b)
|
||||
|
||||
|
||||
@@ -3,24 +3,22 @@
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:os"
|
||||
import "core:time"
|
||||
|
||||
UMTX_OP_WAIT :: 2
|
||||
UMTX_OP_WAKE :: 3
|
||||
|
||||
ETIMEDOUT :: 60
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
_umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int ---
|
||||
__error :: proc "c" () -> ^c.int ---
|
||||
}
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
timeout := os.Unix_File_Time{
|
||||
seconds = 5,
|
||||
nanoseconds = 0,
|
||||
}
|
||||
|
||||
timeout := [2]i64{14400, 0} // 4 hours
|
||||
for {
|
||||
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
|
||||
|
||||
@@ -28,7 +26,7 @@ _futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
|
||||
if __error()^ == ETIMEDOUT {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -42,16 +40,14 @@ _futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Durati
|
||||
return false
|
||||
}
|
||||
|
||||
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &os.Unix_File_Time{
|
||||
seconds = (os.time_t)(duration/1e9),
|
||||
nanoseconds = (c.long)(duration%1e9),
|
||||
})
|
||||
timeout := [2]i64{i64(duration/1e9), i64(duration%1e9)}
|
||||
|
||||
res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
|
||||
if res != -1 {
|
||||
return true
|
||||
}
|
||||
|
||||
if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
|
||||
if __error()^ == ETIMEDOUT {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -15,18 +15,40 @@ foreign import Ntdll "system:Ntdll.lib"
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Ntdll {
|
||||
RtlWaitOnAddress :: proc(Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> i32 ---
|
||||
RtlNtStatusToDosError :: proc(status: i32) -> u32 ---
|
||||
SetLastError :: proc(err: u32) ---
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
NOTE(bill, 2022-08-17)
|
||||
WaitOnAddress is implemented on top of RtlWaitOnAddress
|
||||
BUT requires taking the return value of it and if it is non-zero
|
||||
converting that status to a DOS error and then SetLastError
|
||||
If this is not done, then things don't work as expected when
|
||||
and error occurs
|
||||
|
||||
GODDAMN MICROSOFT!
|
||||
*/
|
||||
CustomWaitOnAddress :: proc "stdcall" (Address: rawptr, CompareAddress: rawptr, AddressSize: uint, Timeout: ^i64) -> bool {
|
||||
status := RtlWaitOnAddress(Address, CompareAddress, AddressSize, Timeout)
|
||||
if status != 0 {
|
||||
SetLastError(RtlNtStatusToDosError(status))
|
||||
}
|
||||
return status == 0
|
||||
}
|
||||
|
||||
|
||||
_futex_wait :: proc(f: ^Futex, expect: u32) -> bool {
|
||||
expect := expect
|
||||
return 0 == RtlWaitOnAddress(f, &expect, size_of(expect), nil)
|
||||
return CustomWaitOnAddress(f, &expect, size_of(expect), nil)
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc(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 0 == RtlWaitOnAddress(f, &expect, size_of(expect), &timeout)
|
||||
return CustomWaitOnAddress(f, &expect, size_of(expect), &timeout)
|
||||
}
|
||||
|
||||
_futex_signal :: proc(f: ^Futex) {
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
//+private
|
||||
package sync
|
||||
|
||||
import "core:os"
|
||||
import "core:c"
|
||||
|
||||
foreign import dl "system:dl"
|
||||
|
||||
foreign dl {
|
||||
pthread_getthreadid_np :: proc "c" () -> c.int ---
|
||||
}
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return os.current_thread_id()
|
||||
return int(pthread_getthreadid_np())
|
||||
}
|
||||
|
||||
@@ -1568,7 +1568,7 @@ sys_gettid :: proc "contextless" () -> int {
|
||||
}
|
||||
|
||||
sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
|
||||
return cast(int)intrinsics.syscall(SYS_getrandom, buf, cast(uintptr)(buflen), cast(uintptr)(flags))
|
||||
return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
|
||||
}
|
||||
|
||||
sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
|
||||
@@ -1622,7 +1622,7 @@ sys_lseek :: proc "contextless" (fd: int, offset: i64, whence: int) -> i64 {
|
||||
low := uintptr(offset & 0xFFFFFFFF)
|
||||
high := uintptr(offset >> 32)
|
||||
result: i64
|
||||
res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, &result, uintptr(whence)))
|
||||
res := i64(intrinsics.syscall(SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
|
||||
return res if res < 0 else result
|
||||
}
|
||||
}
|
||||
@@ -1748,7 +1748,7 @@ sys_unlink :: proc "contextless" (path: cstring) -> int {
|
||||
}
|
||||
|
||||
sys_unlinkat :: proc "contextless" (dfd: int, path: cstring, flag: int = 0) -> int {
|
||||
return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), flag))
|
||||
return int(intrinsics.syscall(SYS_unlinkat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flag)))
|
||||
}
|
||||
|
||||
sys_rmdir :: proc "contextless" (path: cstring) -> int {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
//+build amd64
|
||||
package sys_valgrind
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
Callgrind_Client_Request :: enum uintptr {
|
||||
Dump_Stats = 'C'<<24 | 'T'<<16,
|
||||
Zero_Stats,
|
||||
Toggle_Collect,
|
||||
Dump_Stats_At,
|
||||
Start_Instrumentation,
|
||||
Stop_Instrumentation,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
callgrind_client_request_expr :: proc "c" (default: uintptr, request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
|
||||
return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
|
||||
}
|
||||
callgrind_client_request_stmt :: proc "c" (request: Callgrind_Client_Request, a0, a1, a2, a3, a4: uintptr) {
|
||||
_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
|
||||
}
|
||||
|
||||
// Dump current state of cost centres, and zero them afterwards.
|
||||
dump_stats :: proc "c" () {
|
||||
callgrind_client_request_stmt(.Dump_Stats, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Zero cost centres
|
||||
zero_stats :: proc "c" () {
|
||||
callgrind_client_request_stmt(.Zero_Stats, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Toggles collection state.
|
||||
// The collection state specifies whether the happening of events should be noted or
|
||||
// if they are to be ignored. Events are noted by increment of counters in a cost centre.
|
||||
toggle_collect :: proc "c" () {
|
||||
callgrind_client_request_stmt(.Toggle_Collect, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Dump current state of cost centres, and zero them afterwards.
|
||||
// The argument is appended to a string stating the reason which triggered
|
||||
// the dump. This string is written as a description field into the
|
||||
// profile data dump.
|
||||
dump_stats_at :: proc "c" (pos_str: rawptr) {
|
||||
callgrind_client_request_stmt(.Dump_Stats_At, uintptr(pos_str), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Start full callgrind instrumentation if not already switched on.
|
||||
// When cache simulation is done, it will flush the simulated cache;
|
||||
// this will lead to an artificial cache warmup phase afterwards with
|
||||
// cache misses which would not have happened in reality.
|
||||
start_instrumentation :: proc "c" () {
|
||||
callgrind_client_request_stmt(.Start_Instrumentation, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Stop full callgrind instrumentation if not already switched off.
|
||||
// This flushes Valgrinds translation cache, and does no additional instrumentation
|
||||
// afterwards, which effectivly will run at the same speed as the "none" tool (ie. at minimal slowdown).
|
||||
// Use this to bypass Callgrind aggregation for uninteresting code parts.
|
||||
// To start Callgrind in this mode to ignore the setup phase, use the option "--instr-atstart=no".
|
||||
stop_instrumentation :: proc "c" () {
|
||||
callgrind_client_request_stmt(.Stop_Instrumentation, 0, 0, 0, 0, 0)
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
//+build amd64
|
||||
package sys_valgrind
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
Mem_Check_Client_Request :: enum uintptr {
|
||||
Make_Mem_No_Access = 'M'<<24 | 'C'<<16,
|
||||
Make_Mem_Undefined,
|
||||
Make_Mem_Defined,
|
||||
Discard,
|
||||
Check_Mem_Is_Addressable,
|
||||
Check_Mem_Is_Defined,
|
||||
Do_Leak_Check,
|
||||
Count_Leaks,
|
||||
Get_Vbits,
|
||||
Set_Vbits,
|
||||
Create_Block,
|
||||
Make_Mem_Defined_If_Addressable,
|
||||
Count_Leak_Blocks,
|
||||
Enable_Addr_Error_Reporting_In_Range,
|
||||
Disable_Addr_Error_Reporting_In_Range,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
mem_check_client_request_expr :: proc "c" (default: uintptr, request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
|
||||
return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
|
||||
}
|
||||
mem_check_client_request_stmt :: proc "c" (request: Mem_Check_Client_Request, a0, a1, a2, a3, a4: uintptr) {
|
||||
_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
|
||||
}
|
||||
|
||||
// Mark memory at `raw_data(qzz)` as unaddressable for `len(qzz)` bytes.
|
||||
// Returns true when run on Valgrind and false otherwise.
|
||||
make_mem_no_access :: proc "c" (qzz: []byte) -> bool {
|
||||
return 0 != mem_check_client_request_expr(0, .Make_Mem_No_Access, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
// Mark memory at `raw_data(qzz)` as addressable but undefined for `len(qzz)` bytes.
|
||||
// Returns true when run on Valgrind and false otherwise.
|
||||
make_mem_undefined :: proc "c" (qzz: []byte) -> bool {
|
||||
return 0 != mem_check_client_request_expr(0, .Make_Mem_Undefined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
// Mark memory at `raw_data(qzz)` as addressable for `len(qzz)` bytes.
|
||||
// Returns true when run on Valgrind and false otherwise.
|
||||
make_mem_defined :: proc "c" (qzz: []byte) -> bool {
|
||||
return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
|
||||
// Check that memory at `raw_data(qzz)` is addressable for `len(qzz)` bytes.
|
||||
// If suitable addressibility is not established, Valgrind prints an error
|
||||
// message and returns the address of the first offending byte.
|
||||
// Otherwise it returns zero.
|
||||
check_mem_is_addressable :: proc "c" (qzz: []byte) -> uintptr {
|
||||
return mem_check_client_request_expr(0, .Check_Mem_Is_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
// Check that memory at `raw_data(qzz)` is addressable and defined for `len(qzz)` bytes.
|
||||
// If suitable addressibility and definedness are not established,
|
||||
// Valgrind prints an error message and returns the address of the first
|
||||
// offending byte. Otherwise it returns zero.
|
||||
check_mem_is_defined :: proc "c" (qzz: []byte) -> uintptr {
|
||||
return mem_check_client_request_expr(0, .Check_Mem_Is_Defined, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
|
||||
// Similar to `make_mem_defined(qzz)` except that addressability is not altered:
|
||||
// bytes which are addressable are marked as defined, but those which
|
||||
// are not addressable are left unchanged.
|
||||
// Returns true when run on Valgrind and false otherwise.
|
||||
make_mem_defined_if_addressable :: proc "c" (qzz: []byte) -> bool {
|
||||
return 0 != mem_check_client_request_expr(0, .Make_Mem_Defined_If_Addressable, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
|
||||
// Create a block-description handle.
|
||||
// The description is an ascii string which is included in any messages
|
||||
// pertaining to addresses within the specified memory range.
|
||||
// Has no other effect on the properties of the memory range.
|
||||
create_block :: proc "c" (qzz: []u8, desc: cstring) -> bool {
|
||||
return 0 != mem_check_client_request_expr(0, .Create_Block, uintptr(raw_data(qzz)), uintptr(len(qzz)), uintptr(rawptr(desc)), 0, 0)
|
||||
}
|
||||
// Discard a block-description-handle. Returns true for an invalid handle, false for a valid handle.
|
||||
discard :: proc "c" (blk_index: uintptr) -> bool {
|
||||
return 0 != mem_check_client_request_expr(0, .Discard, 0, blk_index, 0, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
// Do a full memory leak check (like `--leak-check=full`) mid-execution.
|
||||
leak_check :: proc "c" () {
|
||||
mem_check_client_request_stmt(.Do_Leak_Check, 0, 0, 0, 0, 0)
|
||||
}
|
||||
// Same as `leak_check()` but only showing the entries for which there was an increase
|
||||
// in leaked bytes or leaked nr of blocks since the previous leak search.
|
||||
added_leak_check :: proc "c" () {
|
||||
mem_check_client_request_stmt(.Do_Leak_Check, 0, 1, 0, 0, 0)
|
||||
}
|
||||
// Same as `added_leak_check()` but showing entries with increased or decreased
|
||||
// leaked bytes/blocks since previous leak search.
|
||||
changed_leak_check :: proc "c" () {
|
||||
mem_check_client_request_stmt(.Do_Leak_Check, 0, 2, 0, 0, 0)
|
||||
}
|
||||
// Do a summary memory leak check (like `--leak-check=summary`) mid-execution.
|
||||
quick_leak_check :: proc "c" () {
|
||||
mem_check_client_request_stmt(.Do_Leak_Check, 1, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
Count_Result :: struct {
|
||||
leaked: uint,
|
||||
dubious: uint,
|
||||
reachable: uint,
|
||||
suppressed: uint,
|
||||
}
|
||||
|
||||
count_leaks :: proc "c" () -> (res: Count_Result) {
|
||||
mem_check_client_request_stmt(
|
||||
.Count_Leaks,
|
||||
uintptr(&res.leaked),
|
||||
uintptr(&res.dubious),
|
||||
uintptr(&res.reachable),
|
||||
uintptr(&res.suppressed),
|
||||
0,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
count_leak_blocks :: proc "c" () -> (res: Count_Result) {
|
||||
mem_check_client_request_stmt(
|
||||
.Count_Leak_Blocks,
|
||||
uintptr(&res.leaked),
|
||||
uintptr(&res.dubious),
|
||||
uintptr(&res.reachable),
|
||||
uintptr(&res.suppressed),
|
||||
0,
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
// Get the validity data for addresses zza and copy it
|
||||
// into the provided zzvbits array. Return values:
|
||||
// 0 - if not running on valgrind
|
||||
// 1 - success
|
||||
// 2 - [previously indicated unaligned arrays; these are now allowed]
|
||||
// 3 - if any parts of zzsrc/zzvbits are not addressable.
|
||||
// The metadata is not copied in cases 0, 2 or 3 so it should be
|
||||
// impossible to segfault your system by using this call.
|
||||
get_vbits :: proc(zza, zzvbits: []byte) -> u8 {
|
||||
// assert requires a `context` thus these procedures cannot `proc "c"`
|
||||
assert(len(zzvbits) >= len(zza)/8)
|
||||
return u8(mem_check_client_request_expr(0, .Get_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
|
||||
}
|
||||
|
||||
// Set the validity data for addresses zza, copying it
|
||||
// from the provided zzvbits array. Return values:
|
||||
// 0 - if not running on valgrind
|
||||
// 1 - success
|
||||
// 2 - [previously indicated unaligned arrays; these are now allowed]
|
||||
// 3 - if any parts of zza/zzvbits are not addressable.
|
||||
// The metadata is not copied in cases 0, 2 or 3 so it should be
|
||||
// impossible to segfault your system by using this call.
|
||||
set_vbits :: proc(zzvbits, zza: []byte) -> u8 {
|
||||
// assert requires a `context` thus these procedures cannot `proc "c"`
|
||||
assert(len(zzvbits) >= len(zza)/8)
|
||||
return u8(mem_check_client_request_expr(0, .Set_Vbits, uintptr(raw_data(zza)), uintptr(raw_data(zzvbits)), uintptr(len(zza)), 0, 0))
|
||||
}
|
||||
|
||||
// (Re-)enable reporting of addressing errors in the specified address range.
|
||||
enable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
|
||||
return mem_check_client_request_expr(0, .Enable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
// Disable reporting of addressing errors in the specified address range.
|
||||
disable_addr_error_reporting_in_range :: proc "c" (qzz: []byte) -> uintptr {
|
||||
return mem_check_client_request_expr(0, .Disable_Addr_Error_Reporting_In_Range, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
//+build amd64
|
||||
package sys_valgrind
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
Client_Request :: enum uintptr {
|
||||
Running_On_Valgrind = 4097,
|
||||
Discard_Translations = 4098,
|
||||
Client_Call0 = 4353,
|
||||
Client_Call1 = 4354,
|
||||
Client_Call2 = 4355,
|
||||
Client_Call3 = 4356,
|
||||
Count_Errors = 4609,
|
||||
Gdb_Monitor_Command = 4610,
|
||||
Malloc_Like_Block = 4865,
|
||||
Resize_Inplace_Block = 4875,
|
||||
Free_Like_Block = 4866,
|
||||
Create_Mem_Pool = 4867,
|
||||
Destroy_Mem_Pool = 4868,
|
||||
Mem_Pool_Alloc = 4869,
|
||||
Mem_Pool_Free = 4870,
|
||||
Mem_Pool_Trim = 4871,
|
||||
Move_Mem_Pool = 4872,
|
||||
Mem_Pool_Change = 4873,
|
||||
Mem_Pool_Exists = 4874,
|
||||
Printf = 5121,
|
||||
Printf_Backtrace = 5122,
|
||||
Printf_Valist_By_Ref = 5123,
|
||||
Printf_Backtrace_Valist_By_Ref = 5124,
|
||||
Stack_Register = 5377,
|
||||
Stack_Deregister = 5378,
|
||||
Stack_Change = 5379,
|
||||
Load_Pdb_Debug_Info = 5633,
|
||||
Map_Ip_To_Src_Loc = 5889,
|
||||
Change_Err_Disablement = 6145,
|
||||
Vex_Init_For_Iri = 6401,
|
||||
Inner_Threads = 6402,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
client_request_expr :: proc "c" (default: uintptr, request: Client_Request, a0, a1, a2, a3, a4: uintptr) -> uintptr {
|
||||
return intrinsics.valgrind_client_request(default, uintptr(request), a0, a1, a2, a3, a4)
|
||||
}
|
||||
client_request_stmt :: proc "c" (request: Client_Request, a0, a1, a2, a3, a4: uintptr) {
|
||||
_ = intrinsics.valgrind_client_request(0, uintptr(request), a0, a1, a2, a3, a4)
|
||||
}
|
||||
|
||||
// Returns the number of Valgrinds this code is running under
|
||||
// 0 - running natively
|
||||
// 1 - running under Valgrind
|
||||
// 2 - running under Valgrind which is running under another Valgrind
|
||||
running_on_valgrind :: proc "c" () -> uintptr {
|
||||
return client_request_expr(0, .Running_On_Valgrind, 0, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Discard translation of code in the slice qzz. Useful if you are debugging a JIT-er or some such,
|
||||
// since it provides a way to make sure valgrind will retranslate the invalidated area.
|
||||
discard_translations :: proc "c" (qzz: []byte) {
|
||||
client_request_stmt(.Discard_Translations, uintptr(raw_data(qzz)), uintptr(len(qzz)), 0, 0, 0)
|
||||
}
|
||||
|
||||
non_simd_call0 :: proc "c" (p: proc "c" (uintptr) -> uintptr) -> uintptr {
|
||||
return client_request_expr(0, .Client_Call0, uintptr(rawptr(p)), 0, 0, 0, 0)
|
||||
}
|
||||
non_simd_call1 :: proc "c" (p: proc "c" (uintptr, uintptr) -> uintptr, a0: uintptr) -> uintptr {
|
||||
return client_request_expr(0, .Client_Call1, uintptr(rawptr(p)), a0, 0, 0, 0)
|
||||
}
|
||||
non_simd_call2 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr) -> uintptr, a0, a1: uintptr) -> uintptr {
|
||||
return client_request_expr(0, .Client_Call2, uintptr(rawptr(p)), a0, a1, 0, 0)
|
||||
}
|
||||
non_simd_call3 :: proc "c" (p: proc "c" (uintptr, uintptr, uintptr, uintptr) -> uintptr, a0, a1, a2: uintptr) -> uintptr {
|
||||
return client_request_expr(0, .Client_Call3, uintptr(rawptr(p)), a0, a1, a2, 0)
|
||||
}
|
||||
|
||||
// Counts the number of errors that have been recorded by a tool.
|
||||
count_errrors :: proc "c" () -> uint {
|
||||
return uint(client_request_expr(0, .Count_Errors, 0, 0, 0, 0, 0))
|
||||
}
|
||||
|
||||
monitor_command :: proc "c" (command: cstring) -> bool {
|
||||
return 0 != client_request_expr(0, .Gdb_Monitor_Command, uintptr(rawptr(command)), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
malloc_like_block :: proc "c" (mem: []byte, rz_b: uintptr, is_zeroed: bool) {
|
||||
client_request_stmt(.Malloc_Like_Block, uintptr(raw_data(mem)), uintptr(len(mem)), rz_b, uintptr(is_zeroed), 0)
|
||||
}
|
||||
resize_inplace_block :: proc "c" (old_mem: []byte, new_size: uint, rz_b: uintptr) {
|
||||
client_request_stmt(.Resize_Inplace_Block, uintptr(raw_data(old_mem)), uintptr(len(old_mem)), uintptr(new_size), rz_b, 0)
|
||||
}
|
||||
free_like_block :: proc "c" (addr: rawptr, rz_b: uintptr) {
|
||||
client_request_stmt(.Free_Like_Block, uintptr(addr), rz_b, 0, 0, 0)
|
||||
}
|
||||
|
||||
Mem_Pool_Flags :: distinct bit_set[Mem_Pool_Flag; uintptr]
|
||||
Mem_Pool_Flag :: enum uintptr {
|
||||
Auto_Free = 0,
|
||||
Meta_Pool = 1,
|
||||
}
|
||||
|
||||
// Create a memory pool.
|
||||
create_mem_pool :: proc "c" (pool: rawptr, rz_b: uintptr, is_zeroed: bool, flags: Mem_Pool_Flags) {
|
||||
client_request_stmt(.Create_Mem_Pool, uintptr(pool), rz_b, uintptr(is_zeroed), transmute(uintptr)flags, 0)
|
||||
}
|
||||
// Destroy a memory pool.
|
||||
destroy_mem_pool :: proc "c" (pool: rawptr) {
|
||||
client_request_stmt(.Destroy_Mem_Pool, uintptr(pool), 0, 0, 0, 0)
|
||||
}
|
||||
// Associate a section of memory with a memory pool.
|
||||
mem_pool_alloc :: proc "c" (pool: rawptr, mem: []byte) {
|
||||
client_request_stmt(.Mem_Pool_Alloc, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0)
|
||||
}
|
||||
// Disassociate a section of memory from a memory pool.
|
||||
mem_pool_free :: proc "c" (pool: rawptr, addr: rawptr) {
|
||||
client_request_stmt(.Mem_Pool_Free, uintptr(pool), uintptr(addr), 0, 0, 0)
|
||||
}
|
||||
// Disassociate parts of a section of memory outside a particular range.
|
||||
mem_pool_trim :: proc "c" (pool: rawptr, mem: []byte) {
|
||||
client_request_stmt(.Mem_Pool_Trim, uintptr(pool), uintptr(raw_data(mem)), uintptr(len(mem)), 0, 0)
|
||||
}
|
||||
// Resize and/or move a section of memory associated with a memory pool.
|
||||
move_mem_pool :: proc "c" (pool_a, pool_b: rawptr) {
|
||||
client_request_stmt(.Move_Mem_Pool, uintptr(pool_a), uintptr(pool_b), 0, 0, 0)
|
||||
}
|
||||
// Resize and/or move a section of memory associated with a memory pool.
|
||||
mem_pool_change :: proc "c" (pool: rawptr, addr_a: rawptr, mem: []byte) {
|
||||
client_request_stmt(.Mem_Pool_Change, uintptr(pool), uintptr(addr_a), uintptr(raw_data(mem)), uintptr(len(mem)), 0)
|
||||
}
|
||||
// Return true if a memory pool exists
|
||||
mem_pool_exists :: proc "c" (pool: rawptr) -> bool {
|
||||
return 0 != client_request_expr(0, .Mem_Pool_Exists, uintptr(pool), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
// Mark a section of memory as being a stack. Returns a stack id.
|
||||
stack_register :: proc "c" (stack: []byte) -> (stack_id: uintptr) {
|
||||
ptr := uintptr(raw_data(stack))
|
||||
return client_request_expr(0, .Stack_Register, ptr, ptr+uintptr(len(stack)), 0, 0, 0)
|
||||
}
|
||||
|
||||
// Unmark a section of memory associated with a stack id as being a stack.
|
||||
stack_deregister :: proc "c" (id: uintptr) {
|
||||
client_request_stmt(.Stack_Deregister, id, 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
// Change the start and end address of the stack id with the `new_stack` slice.
|
||||
stack_change :: proc "c" (id: uint, new_stack: []byte) {
|
||||
ptr := uintptr(raw_data(new_stack))
|
||||
client_request_stmt(.Stack_Change, uintptr(id), ptr, ptr + uintptr(len(new_stack)), 0, 0)
|
||||
}
|
||||
|
||||
|
||||
// Disable error reporting for the current thread/
|
||||
// It behaves in a stack-like way, meaning you can safely call this multiple times
|
||||
// given that `enable_error_reporting()` is called the same number of times to
|
||||
// re-enable the error reporting.
|
||||
// The first call of this macro disables reporting.
|
||||
// Subsequent calls have no effect except to increase the number of `enable_error_reporting()`
|
||||
// calls needed to re-enable reporting.
|
||||
// Child threads do not inherit this setting from their parents;
|
||||
// they are always created with reporting enabled.
|
||||
disable_error_reporting :: proc "c" () {
|
||||
client_request_stmt(.Change_Err_Disablement, 1, 0, 0, 0, 0)
|
||||
}
|
||||
// Re-enable error reporting
|
||||
enable_error_reporting :: proc "c" () {
|
||||
client_request_stmt(.Change_Err_Disablement, ~uintptr(0), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
inner_threads :: proc "c" (qzz: rawptr) {
|
||||
client_request_stmt(.Inner_Threads, uintptr(qzz), 0, 0, 0, 0)
|
||||
}
|
||||
|
||||
|
||||
// Map a code address to a source file name and line number.
|
||||
// `buf64` must point to a 64-byte buffer in the caller's address space.
|
||||
// The result will be dumped in there and is guaranteed to be zero terminated.
|
||||
// If no info is found, the first byte is set to zero.
|
||||
map_ip_to_src_loc :: proc "c" (addr: rawptr, buf64: ^[64]byte) -> uintptr {
|
||||
return client_request_expr(0, .Map_Ip_To_Src_Loc, uintptr(addr), uintptr(buf64), 0, 0, 0)
|
||||
}
|
||||
+171
-259
@@ -1148,6 +1148,156 @@ foreign wasi {
|
||||
*/
|
||||
how: sdflags_t,
|
||||
) -> errno_t ---
|
||||
|
||||
|
||||
/**
|
||||
* Return a description of the given preopened file descriptor.
|
||||
*/
|
||||
fd_prestat_dir_name :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* A buffer into which to write the preopened directory name.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t ---
|
||||
/**
|
||||
* Create a directory.
|
||||
* Note: This is similar to `mkdirat` in POSIX.
|
||||
*/
|
||||
path_create_directory :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The path at which to create the directory.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t ---
|
||||
/**
|
||||
* Adjust the timestamps of a file or directory.
|
||||
* Note: This is similar to `utimensat` in POSIX.
|
||||
*/
|
||||
path_filestat_set_times :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* Flags determining the method of how the path is resolved.
|
||||
*/
|
||||
flags: lookupflags_t,
|
||||
/**
|
||||
* The path of the file or directory to operate on.
|
||||
*/
|
||||
path: string,
|
||||
/**
|
||||
* The desired values of the data access timestamp.
|
||||
*/
|
||||
atim: timestamp_t,
|
||||
/**
|
||||
* The desired values of the data modification timestamp.
|
||||
*/
|
||||
mtim: timestamp_t,
|
||||
/**
|
||||
* A bitmask indicating which timestamps to adjust.
|
||||
*/
|
||||
fst_flags: fstflags_t,
|
||||
) -> errno_t ---
|
||||
/**
|
||||
* Remove a directory.
|
||||
* Return `errno::notempty` if the directory is not empty.
|
||||
* Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
|
||||
*/
|
||||
path_remove_directory :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The path to a directory to remove.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t ---
|
||||
/**
|
||||
* Create a hard link.
|
||||
* Note: This is similar to `linkat` in POSIX.
|
||||
*/
|
||||
path_link :: proc(
|
||||
old_fd: fd_t,
|
||||
/**
|
||||
* Flags determining the method of how the path is resolved.
|
||||
*/
|
||||
old_flags: lookupflags_t,
|
||||
/**
|
||||
* The source path from which to link.
|
||||
*/
|
||||
old_path: string,
|
||||
/**
|
||||
* The working directory at which the resolution of the new path starts.
|
||||
*/
|
||||
new_fd: fd_t,
|
||||
/**
|
||||
* The destination path at which to create the hard link.
|
||||
*/
|
||||
new_path: string,
|
||||
) -> errno_t ---
|
||||
|
||||
/**
|
||||
* Rename a file or directory.
|
||||
* Note: This is similar to `renameat` in POSIX.
|
||||
*/
|
||||
path_rename :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The source path of the file or directory to rename.
|
||||
*/
|
||||
old_path: string,
|
||||
/**
|
||||
* The working directory at which the resolution of the new path starts.
|
||||
*/
|
||||
new_fd: fd_t,
|
||||
/**
|
||||
* The destination path to which to rename the file or directory.
|
||||
*/
|
||||
new_path: string,
|
||||
) -> errno_t ---
|
||||
|
||||
/**
|
||||
* Create a symbolic link.
|
||||
* Note: This is similar to `symlinkat` in POSIX.
|
||||
*/
|
||||
path_symlink :: proc(
|
||||
/**
|
||||
* The contents of the symbolic link.
|
||||
*/
|
||||
old_path: string,
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The destination path at which to create the symbolic link.
|
||||
*/
|
||||
new_path: string,
|
||||
) -> errno_t ---
|
||||
|
||||
/**
|
||||
* Unlink a file.
|
||||
* Return `errno::isdir` if the path refers to a directory.
|
||||
* Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
|
||||
*/
|
||||
path_unlink_file :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The path to a file to unlink.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t ---
|
||||
|
||||
/**
|
||||
* Write high-quality random data into a buffer.
|
||||
* This function blocks when the implementation is unable to immediately
|
||||
* provide sufficient high-quality random data.
|
||||
* This function may execute slowly, so when large mounts of random data are
|
||||
* required, it's advisable to use this function to seed a pseudo-random
|
||||
* number generator, rather than to provide the random data directly.
|
||||
*/
|
||||
random_get :: proc(
|
||||
/**
|
||||
* The buffer to fill with random data.
|
||||
*/
|
||||
buf: []u8,
|
||||
) -> errno_t ---
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1250,7 +1400,7 @@ fd_pread :: proc "c" (
|
||||
*/
|
||||
offset: filesize_t,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_fd_pread(fd, raw_data(iovs), len(iovs), offset, &n)
|
||||
err = wasi_fd_pread(fd, iovs, offset, &n)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1281,7 +1431,7 @@ fd_pwrite :: proc "c" (
|
||||
*/
|
||||
offset: filesize_t,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_fd_pwrite(fd, raw_data(iovs), len(iovs), offset, &n)
|
||||
err = wasi_fd_pwrite(fd, iovs, offset, &n)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1297,7 +1447,7 @@ fd_read :: proc "c" (
|
||||
*/
|
||||
iovs: []iovec_t,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_fd_read(fd, raw_data(iovs), len(iovs), &n)
|
||||
err = wasi_fd_read(fd, iovs, &n)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1324,7 +1474,7 @@ fd_readdir :: proc "c" (
|
||||
*/
|
||||
cookie: dircookie_t,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_fd_readdir(fd, raw_data(buf), len(buf), cookie, &n)
|
||||
err = wasi_fd_readdir(fd, buf, cookie, &n)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1370,7 +1520,7 @@ fd_write :: proc "c" (
|
||||
*/
|
||||
iovs: []ciovec_t,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_fd_write(fd, raw_data(iovs), len(iovs), &n)
|
||||
err = wasi_fd_write(fd, iovs, &n)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1390,7 +1540,7 @@ path_filestat_get :: proc "c" (
|
||||
*/
|
||||
path: string,
|
||||
) -> (offset: filestat_t, err: errno_t) {
|
||||
err = wasi_path_filestat_get(fd, flags, raw_data(path), len(path), &offset)
|
||||
err = wasi_path_filestat_get(fd, flags, path, &offset)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1432,7 +1582,7 @@ path_open :: proc "c" (
|
||||
fs_rights_inheriting: rights_t,
|
||||
fdflags: fdflags_t,
|
||||
) -> (file: fd_t, err: errno_t) {
|
||||
err = wasi_path_open(fd, dirflags, raw_data(path), len(path), oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
|
||||
err = wasi_path_open(fd, dirflags, path, oflags, fs_rights_base, fs_rights_inheriting, fdflags, &file)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1452,7 +1602,7 @@ path_readlink :: proc "c" (
|
||||
*/
|
||||
buf: []u8,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_path_readlink(fd, raw_data(path), len(path), raw_data(buf), len(buf), &n)
|
||||
err = wasi_path_readlink(fd, path, buf, &n)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1495,7 +1645,7 @@ sock_recv :: proc "c" (
|
||||
*/
|
||||
ri_flags: riflags_t,
|
||||
) -> (n: size_t, flags: roflags_t, err: errno_t) {
|
||||
err = wasi_sock_recv(fd, raw_data(ri_data), len(ri_data), ri_flags, &n, &flags)
|
||||
err = wasi_sock_recv(fd, ri_data, ri_flags, &n, &flags)
|
||||
return
|
||||
}
|
||||
/**
|
||||
@@ -1516,172 +1666,11 @@ sock_send :: proc "c" (
|
||||
*/
|
||||
si_flags: siflags_t,
|
||||
) -> (n: size_t, err: errno_t) {
|
||||
err = wasi_sock_send(fd, raw_data(si_data), len(si_data), si_flags, &n)
|
||||
err = wasi_sock_send(fd, si_data, si_flags, &n)
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a description of the given preopened file descriptor.
|
||||
*/
|
||||
fd_prestat_dir_name :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* A buffer into which to write the preopened directory name.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t {
|
||||
return wasm_fd_prestat_dir_name(fd, raw_data(path), len(path))
|
||||
}
|
||||
/**
|
||||
* Create a directory.
|
||||
* Note: This is similar to `mkdirat` in POSIX.
|
||||
*/
|
||||
path_create_directory :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The path at which to create the directory.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t {
|
||||
return wasm_path_create_directory(fd, raw_data(path), len(path))
|
||||
}
|
||||
/**
|
||||
* Adjust the timestamps of a file or directory.
|
||||
* Note: This is similar to `utimensat` in POSIX.
|
||||
*/
|
||||
path_filestat_set_times :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* Flags determining the method of how the path is resolved.
|
||||
*/
|
||||
flags: lookupflags_t,
|
||||
/**
|
||||
* The path of the file or directory to operate on.
|
||||
*/
|
||||
path: string,
|
||||
/**
|
||||
* The desired values of the data access timestamp.
|
||||
*/
|
||||
atim: timestamp_t,
|
||||
/**
|
||||
* The desired values of the data modification timestamp.
|
||||
*/
|
||||
mtim: timestamp_t,
|
||||
/**
|
||||
* A bitmask indicating which timestamps to adjust.
|
||||
*/
|
||||
fst_flags: fstflags_t,
|
||||
) -> errno_t {
|
||||
return wasm_path_filestat_set_times(fd, flags, raw_data(path), len(path), atim, mtim, fst_flags)
|
||||
}
|
||||
/**
|
||||
* Remove a directory.
|
||||
* Return `errno::notempty` if the directory is not empty.
|
||||
* Note: This is similar to `unlinkat(fd, path, AT_REMOVEDIR)` in POSIX.
|
||||
*/
|
||||
path_remove_directory :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The path to a directory to remove.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t {
|
||||
return wasm_path_remove_directory(fd, raw_data(path), len(path))
|
||||
}
|
||||
/**
|
||||
* Create a hard link.
|
||||
* Note: This is similar to `linkat` in POSIX.
|
||||
*/
|
||||
path_link :: proc(
|
||||
old_fd: fd_t,
|
||||
/**
|
||||
* Flags determining the method of how the path is resolved.
|
||||
*/
|
||||
old_flags: lookupflags_t,
|
||||
/**
|
||||
* The source path from which to link.
|
||||
*/
|
||||
old_path: string,
|
||||
/**
|
||||
* The working directory at which the resolution of the new path starts.
|
||||
*/
|
||||
new_fd: fd_t,
|
||||
/**
|
||||
* The destination path at which to create the hard link.
|
||||
*/
|
||||
new_path: string,
|
||||
) -> errno_t {
|
||||
return wasm_path_link(old_fd, old_flags, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename a file or directory.
|
||||
* Note: This is similar to `renameat` in POSIX.
|
||||
*/
|
||||
path_rename :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The source path of the file or directory to rename.
|
||||
*/
|
||||
old_path: string,
|
||||
/**
|
||||
* The working directory at which the resolution of the new path starts.
|
||||
*/
|
||||
new_fd: fd_t,
|
||||
/**
|
||||
* The destination path to which to rename the file or directory.
|
||||
*/
|
||||
new_path: string,
|
||||
) -> errno_t {
|
||||
return wasm_path_rename(fd, raw_data(old_path), len(old_path), new_fd, raw_data(new_path), len(new_path))
|
||||
}
|
||||
/**
|
||||
* Create a symbolic link.
|
||||
* Note: This is similar to `symlinkat` in POSIX.
|
||||
*/
|
||||
path_symlink :: proc(
|
||||
/**
|
||||
* The contents of the symbolic link.
|
||||
*/
|
||||
old_path: string,
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The destination path at which to create the symbolic link.
|
||||
*/
|
||||
new_path: string,
|
||||
) -> errno_t {
|
||||
return wasm_path_symlink(raw_data(old_path), len(old_path), fd, raw_data(new_path), len(new_path))
|
||||
}
|
||||
/**
|
||||
* Unlink a file.
|
||||
* Return `errno::isdir` if the path refers to a directory.
|
||||
* Note: This is similar to `unlinkat(fd, path, 0)` in POSIX.
|
||||
*/
|
||||
path_unlink_file :: proc(
|
||||
fd: fd_t,
|
||||
/**
|
||||
* The path to a file to unlink.
|
||||
*/
|
||||
path: string,
|
||||
) -> errno_t {
|
||||
return wasm_path_unlink_file(fd, raw_data(path), len(path))
|
||||
}
|
||||
/**
|
||||
* Write high-quality random data into a buffer.
|
||||
* This function blocks when the implementation is unable to immediately
|
||||
* provide sufficient high-quality random data.
|
||||
* This function may execute slowly, so when large mounts of random data are
|
||||
* required, it's advisable to use this function to seed a pseudo-random
|
||||
* number generator, rather than to provide the random data directly.
|
||||
*/
|
||||
random_get :: proc(
|
||||
/**
|
||||
* The buffer to fill with random data.
|
||||
*/
|
||||
buf: []u8,
|
||||
) -> errno_t {
|
||||
return wasm_random_get(raw_data(buf), len(buf))
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1722,8 +1711,7 @@ foreign wasi {
|
||||
@(link_name="fd_pread")
|
||||
wasi_fd_pread :: proc(
|
||||
fd: fd_t,
|
||||
iovs: [^]iovec_t,
|
||||
iovs_len: size_t,
|
||||
iovs: []iovec_t,
|
||||
offset: filesize_t,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@@ -1735,23 +1723,20 @@ foreign wasi {
|
||||
@(link_name="fd_pwrite")
|
||||
wasi_fd_pwrite :: proc(
|
||||
fd: fd_t,
|
||||
iovs: [^]ciovec_t,
|
||||
iovs_len: size_t,
|
||||
iovs: []ciovec_t,
|
||||
offset: filesize_t,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="fd_read")
|
||||
wasi_fd_read :: proc(
|
||||
fd: fd_t,
|
||||
iovs: [^]iovec_t,
|
||||
iovs_len: size_t,
|
||||
iovs: []iovec_t,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="fd_readdir")
|
||||
wasi_fd_readdir :: proc(
|
||||
fd: fd_t,
|
||||
buf: [^]u8,
|
||||
buf_len: size_t,
|
||||
buf: []u8,
|
||||
cookie: dircookie_t,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@@ -1770,8 +1755,7 @@ foreign wasi {
|
||||
@(link_name="fd_write")
|
||||
wasi_fd_write :: proc(
|
||||
fd: fd_t,
|
||||
iovs: [^]ciovec_t,
|
||||
iovs_len: size_t,
|
||||
iovs: []ciovec_t,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_filestat_get")
|
||||
@@ -1781,16 +1765,14 @@ foreign wasi {
|
||||
/**
|
||||
* The path of the file or directory to inspect.
|
||||
*/
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
path: string,
|
||||
retptr0: ^filestat_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_open")
|
||||
wasi_path_open :: proc(
|
||||
fd: fd_t,
|
||||
dirflags: lookupflags_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
path: string,
|
||||
oflags: oflags_t,
|
||||
fs_rights_base: rights_t,
|
||||
fs_rights_inheriting: rights_t,
|
||||
@@ -1800,10 +1782,8 @@ foreign wasi {
|
||||
@(link_name="path_readlink")
|
||||
wasi_path_readlink :: proc(
|
||||
fd: fd_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
buf: [^]u8,
|
||||
buf_len: size_t,
|
||||
path: string,
|
||||
buf: []u8,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="poll_oneoff")
|
||||
@@ -1816,8 +1796,7 @@ foreign wasi {
|
||||
@(link_name="sock_recv")
|
||||
wasi_sock_recv :: proc(
|
||||
fd: fd_t,
|
||||
ri_data: [^]iovec_t,
|
||||
ri_data_len: size_t,
|
||||
ri_data: []iovec_t,
|
||||
ri_flags: riflags_t,
|
||||
retptr0: ^size_t,
|
||||
retptr1: ^roflags_t,
|
||||
@@ -1825,75 +1804,8 @@ foreign wasi {
|
||||
@(link_name="sock_send")
|
||||
wasi_sock_send :: proc(
|
||||
fd: fd_t,
|
||||
si_data: [^]ciovec_t,
|
||||
si_data_len: size_t,
|
||||
si_data: []ciovec_t,
|
||||
si_flags: siflags_t,
|
||||
retptr0: ^size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="fd_prestat_dir_name")
|
||||
wasm_fd_prestat_dir_name :: proc(
|
||||
fd: fd_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_create_directory")
|
||||
wasm_path_create_directory :: proc(
|
||||
fd: fd_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_filestat_set_times")
|
||||
wasm_path_filestat_set_times :: proc(
|
||||
fd: fd_t,
|
||||
flags: lookupflags_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
atim: timestamp_t,
|
||||
mtim: timestamp_t,
|
||||
fst_flags: fstflags_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_remove_directory")
|
||||
wasm_path_remove_directory :: proc(
|
||||
fd: fd_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_link")
|
||||
wasm_path_link :: proc(
|
||||
old_fd: fd_t,
|
||||
old_flags: lookupflags_t,
|
||||
old_path: [^]u8,
|
||||
old_path_len: size_t,
|
||||
new_fd: fd_t,
|
||||
new_path: [^]u8,
|
||||
new_path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_rename")
|
||||
wasm_path_rename :: proc(
|
||||
fd: fd_t,
|
||||
old_path: [^]u8,
|
||||
old_path_len: size_t,
|
||||
new_fd: fd_t,
|
||||
new_path: [^]u8,
|
||||
new_path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_symlink")
|
||||
wasm_path_symlink :: proc(
|
||||
old_path: [^]u8,
|
||||
old_path_len: size_t,
|
||||
fd: fd_t,
|
||||
new_path: [^]u8,
|
||||
new_path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="path_unlink_file")
|
||||
wasm_path_unlink_file :: proc(
|
||||
fd: fd_t,
|
||||
path: [^]u8,
|
||||
path_len: size_t,
|
||||
) -> errno_t ---
|
||||
@(link_name="random_get")
|
||||
wasm_random_get :: proc(
|
||||
buf: [^]u8,
|
||||
buf_len: size_t,
|
||||
) -> errno_t ---
|
||||
}
|
||||
|
||||
@@ -70,4 +70,62 @@ foreign advapi32 {
|
||||
lpStartupInfo: LPSTARTUPINFO,
|
||||
lpProcessInformation: LPPROCESS_INFORMATION,
|
||||
) -> BOOL ---
|
||||
}
|
||||
|
||||
RegCreateKeyExW :: proc(
|
||||
hKey: HKEY,
|
||||
lpSubKey: LPCWSTR,
|
||||
Reserved: DWORD,
|
||||
lpClass: LPWSTR,
|
||||
dwOptions: DWORD,
|
||||
samDesired: REGSAM,
|
||||
lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
|
||||
phkResult: PHKEY,
|
||||
lpdwDisposition: LPDWORD,
|
||||
) -> LSTATUS ---
|
||||
|
||||
RegOpenKeyW :: proc(
|
||||
hKey: HKEY,
|
||||
lpSubKey: LPCWSTR,
|
||||
phkResult: PHKEY,
|
||||
) -> LSTATUS ---
|
||||
|
||||
RegOpenKeyExW :: proc(
|
||||
hKey: HKEY,
|
||||
lpSubKey: LPCWSTR,
|
||||
ulOptions: DWORD,
|
||||
samDesired: REGSAM,
|
||||
phkResult: PHKEY,
|
||||
) -> LSTATUS ---
|
||||
|
||||
RegCloseKey :: proc(
|
||||
hKey: HKEY,
|
||||
) -> LSTATUS ---
|
||||
|
||||
RegGetValueW :: proc(
|
||||
hkey: HKEY,
|
||||
lpSubKey: LPCWSTR,
|
||||
lpValue: LPCWSTR,
|
||||
dwFlags: DWORD,
|
||||
pdwType: LPDWORD,
|
||||
pvData: PVOID,
|
||||
pcbData: LPDWORD,
|
||||
) -> LSTATUS ---
|
||||
|
||||
RegSetValueExW :: proc(
|
||||
hKey: HKEY,
|
||||
lpValueName: LPCWSTR,
|
||||
Reserved: DWORD,
|
||||
dwType: DWORD,
|
||||
lpData: ^BYTE,
|
||||
cbData: DWORD,
|
||||
) -> LSTATUS ---
|
||||
|
||||
RegSetKeyValueW :: proc(
|
||||
hKey: HKEY,
|
||||
lpSubKey: LPCWSTR,
|
||||
lpValueName: LPCWSTR,
|
||||
dwType: DWORD,
|
||||
lpData: LPCVOID,
|
||||
cbData: DWORD,
|
||||
) -> LSTATUS ---
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user