mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-06 03:31:38 -07:00
Compare commits
309 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 521ed28632 | |||
| d6300314c0 | |||
| 27130259cc | |||
| b4fb295bb3 | |||
| f7e608628b | |||
| 605d66845a | |||
| 37ec3d7006 | |||
| 89eb351d2b | |||
| abaacfe78d | |||
| f9f4551e8d | |||
| daf005d1ab | |||
| ce1ee962f5 | |||
| d0e4edfb43 | |||
| 749e5067fb | |||
| 75dcaf6d8d | |||
| 00a0a1e95d | |||
| 7a4106077a | |||
| 9c8eaeb988 | |||
| 7ed28e8a84 | |||
| a3d53a6288 | |||
| 2127dc56b1 | |||
| e59e34d334 | |||
| 4fd97c3ba6 | |||
| 107c7a36d0 | |||
| dcf2c43863 | |||
| 0c25f7cdc5 | |||
| e5c243ee93 | |||
| e9b6a8fc9a | |||
| a27c00862c | |||
| a06f75b6fb | |||
| d6cb105d5f | |||
| 5ac36b5f25 | |||
| 22bcf1ba70 | |||
| 51c705edf1 | |||
| e45401bfb4 | |||
| 6b652afb8e | |||
| 76b85c0622 | |||
| 6fa0679be9 | |||
| afea221d64 | |||
| b9ec2de4db | |||
| 3dfd53aee0 | |||
| b54fc8ff95 | |||
| 8745942255 | |||
| c7be30e0ea | |||
| 1baa47c78e | |||
| 0b33df4e9d | |||
| 4c40495742 | |||
| 824b97d250 | |||
| 5bbab05161 | |||
| 83558a1352 | |||
| cb183e968a | |||
| 27d56d0da4 | |||
| c663566cd5 | |||
| 13d052027f | |||
| 7076cf69e4 | |||
| 6ae8adaa45 | |||
| 15bbdb2030 | |||
| 48c9c1682c | |||
| d3c5143292 | |||
| 3949e2220f | |||
| 9ed4f95c1a | |||
| 8daecf7532 | |||
| 11d665c25a | |||
| 98a086b91b | |||
| f323a179d9 | |||
| c6f282d20b | |||
| ba49a9100d | |||
| 6fe77155b5 | |||
| 677e7ff642 | |||
| 682b5fa0d3 | |||
| ab00db2ebd | |||
| 0a0e8f36eb | |||
| bbe44b49bc | |||
| 25bec19b1f | |||
| 81f83d5780 | |||
| d2019e3e4d | |||
| 489e8dc592 | |||
| 3edb3d8d8c | |||
| a705a2e38b | |||
| 7dfbda58d9 | |||
| 9b88a38e54 | |||
| 16a494347c | |||
| ad0f11668b | |||
| 699cabeb1c | |||
| 7207f4b0c5 | |||
| 9c1b464c94 | |||
| 04a1e7d638 | |||
| 7cfbd87f57 | |||
| e9e05a3783 | |||
| 2b83f27f06 | |||
| 3d0e194298 | |||
| fcd8860990 | |||
| 22840ddf97 | |||
| f9576c2f5b | |||
| 16fc961010 | |||
| d2701d8b13 | |||
| a0bd31646b | |||
| 0d37da54b4 | |||
| 5d47e2a166 | |||
| 035c75d6a9 | |||
| b475481788 | |||
| 033525fe13 | |||
| 8852d090b6 | |||
| ac259ac790 | |||
| 7b4a87d37c | |||
| f6fc3ebe37 | |||
| 5c106abe3f | |||
| db748b7a05 | |||
| f2f2d532f5 | |||
| 1bcec3f769 | |||
| b035ee2bcd | |||
| 0424fb486b | |||
| 3858422f1d | |||
| d4f343751e | |||
| 79baddc157 | |||
| bcf437dc11 | |||
| 503eb470a7 | |||
| 667af1be58 | |||
| 366779f8c7 | |||
| dae299b781 | |||
| 2f29894b45 | |||
| 0819d05a0b | |||
| 6a4e44607c | |||
| a71daee545 | |||
| 046dd55032 | |||
| 2fc3da3fde | |||
| a74093784c | |||
| ed58374964 | |||
| 6dd4d1a924 | |||
| d77269dee2 | |||
| ea263b8cc5 | |||
| 45f0c812af | |||
| 810a1eee41 | |||
| e3e225d21b | |||
| 50e10ceb3b | |||
| da774e3fd2 | |||
| 2c3febd620 | |||
| bce62b98d4 | |||
| e914a8710d | |||
| c96e0afbf1 | |||
| f1c24f434b | |||
| e8517e1d02 | |||
| 92e406cef0 | |||
| 269913ede0 | |||
| 2ed16240a7 | |||
| ff36b754cb | |||
| 503b897677 | |||
| d69c74665a | |||
| fcf081283c | |||
| 7a6e8543a6 | |||
| f30755a871 | |||
| 503220e4c1 | |||
| 051814a69c | |||
| 21843da9e3 | |||
| 30f49f81c1 | |||
| 439f4776e4 | |||
| b743f56fb9 | |||
| 1fc3f6cb2e | |||
| df19c48da8 | |||
| f7211408fc | |||
| 30db316e16 | |||
| 8c01e952f3 | |||
| 3e66b88031 | |||
| f76316f889 | |||
| 32477a88ef | |||
| e8bc576b23 | |||
| 2eea6f2490 | |||
| 1d9d79542c | |||
| 1a6d4c955a | |||
| 717522efe4 | |||
| 8d06d9c23d | |||
| 765c1546c5 | |||
| 7ec6fd30f0 | |||
| 0ca773114a | |||
| 9e1576418f | |||
| b7ea169c81 | |||
| 3b583cbac7 | |||
| 382bd87667 | |||
| 6cc07dc24e | |||
| 01cdd22a01 | |||
| 35331e6973 | |||
| c18e98e8c5 | |||
| 3cd553565f | |||
| 9eec9f5788 | |||
| 2b7ca2bdd6 | |||
| 411c0add3b | |||
| 18d7ecc1a5 | |||
| 4812601e78 | |||
| 2d5779b660 | |||
| fd53e8b955 | |||
| 53a030c65b | |||
| 91ad6b42c5 | |||
| dad10ef800 | |||
| 71eb21aab7 | |||
| f8228e305a | |||
| 0e7109cab2 | |||
| c39ef1b25c | |||
| 9da37ed394 | |||
| 8fa571c283 | |||
| 83f3ae14d5 | |||
| 6a14c3edb4 | |||
| 2cd895c50b | |||
| ee89c0458f | |||
| cee847a68c | |||
| 413f96553a | |||
| 662ed4a67c | |||
| 85a263130d | |||
| d19ae37af1 | |||
| 22672a816e | |||
| dcb873c88d | |||
| 62ab2987b6 | |||
| 7bcde35651 | |||
| 4b8721a0bb | |||
| 7743e34596 | |||
| 4003b76fd3 | |||
| c27ed1896f | |||
| 7d217269b5 | |||
| a3c8882648 | |||
| 4389059834 | |||
| a55e90fefd | |||
| f58f922487 | |||
| 1a0930f841 | |||
| a5f8c3f692 | |||
| 92fb65cf2e | |||
| a51943e27f | |||
| 03c834e410 | |||
| 989107094c | |||
| fd8956b8f4 | |||
| 648e3c65ea | |||
| d5047e621d | |||
| 8fbdef01d6 | |||
| 9dee943fae | |||
| 8ceb691cec | |||
| f26516f6fa | |||
| fda8e8a30b | |||
| 2242178d96 | |||
| a459bc13dc | |||
| 53e84b7f31 | |||
| 098f51aa80 | |||
| 765969e6a3 | |||
| 8086c14dcc | |||
| 80ce1b7d85 | |||
| 9f55404845 | |||
| 075040ae05 | |||
| aa799d6a0d | |||
| 58e607e960 | |||
| ff51c5ee56 | |||
| 73c1f08776 | |||
| 412ca36230 | |||
| 06d1df4cae | |||
| 7662808bc9 | |||
| d48828dd80 | |||
| b725e01cdd | |||
| 874c1f076d | |||
| 2c14f0a109 | |||
| cf4afc2e7b | |||
| 5ed06f7eb8 | |||
| 765cd66b30 | |||
| 5a8fbc230d | |||
| 5c62211f00 | |||
| 835b8ffa22 | |||
| b84108c4b5 | |||
| 6642e1fc9d | |||
| c909e8e4b8 | |||
| 9bdbb45517 | |||
| 1b5860e574 | |||
| 047d45584e | |||
| 721486f875 | |||
| 29f2ecd228 | |||
| 970ac22647 | |||
| 419eab5059 | |||
| a1935bc1f4 | |||
| fc06c8ed9f | |||
| 7952b26e8b | |||
| fa6cfde4b0 | |||
| 4c78ba2152 | |||
| 9870e43ac0 | |||
| 63086c7eaf | |||
| ef0c6fc4b3 | |||
| 159c5311c3 | |||
| b6a65fac36 | |||
| ab7367ae47 | |||
| 457f509b5f | |||
| 7e5c063d98 | |||
| dfabd0e0ad | |||
| 141133e326 | |||
| e188a542da | |||
| 64f1e8b7a2 | |||
| 62440df051 | |||
| 5362e883f4 | |||
| 8b06fd0935 | |||
| bb9b58b8c4 | |||
| ee070c9bd3 | |||
| aebafdcd08 | |||
| 2b4fce8684 | |||
| de8f6709f7 | |||
| 683753db96 | |||
| d13dc7eca7 | |||
| e56920e445 | |||
| 1c9aad4d7b | |||
| ce09cb0bdb | |||
| b7fd91817e | |||
| a728047281 | |||
| 5cf473b31c | |||
| c767d55e9a | |||
| 7f601c9535 | |||
| e139d1cbe4 | |||
| cb04116caf | |||
| 62ff8daa78 |
+15
-10
@@ -38,6 +38,11 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Linux i386
|
||||
run: ./odin check examples/all -vet -strict-style -target:linux_i386
|
||||
timeout-minutes: 10
|
||||
@@ -87,10 +92,10 @@ jobs:
|
||||
cd tests/vendor
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/issues
|
||||
./run.sh
|
||||
cd tests/internal
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Darwin arm64
|
||||
run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
|
||||
@@ -151,6 +156,13 @@ jobs:
|
||||
cd tests\vendor
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\internal
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: core:math/big tests
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -158,13 +170,6 @@ jobs:
|
||||
cd tests\core\math\big
|
||||
call build.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin issues tests
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
|
||||
cd tests\issues
|
||||
call run.bat
|
||||
timeout-minutes: 10
|
||||
- name: Odin check examples/all for Windows 32bits
|
||||
shell: cmd
|
||||
run: |
|
||||
|
||||
@@ -82,7 +82,7 @@ A wiki maintained by the Odin community.
|
||||
|
||||
#### [Odin Discord](https://discord.gg/sVBPHEv)
|
||||
|
||||
Get live support and talk with other odiners on the Odin Discord.
|
||||
Get live support and talk with other Odin programmers on the Odin Discord.
|
||||
|
||||
### Articles
|
||||
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
where /Q cl.exe || (
|
||||
set __VSCMD_ARG_NO_LOGO=1
|
||||
for /f "tokens=*" %%i in ('"C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" -latest -requires Microsoft.VisualStudio.Workload.NativeDesktop -property installationPath') do set VS=%%i
|
||||
if "!VS!" equ "" (
|
||||
echo ERROR: Visual Studio installation not found
|
||||
exit /b 1
|
||||
)
|
||||
call "!VS!\VC\Auxiliary\Build\vcvarsall.bat" amd64 || exit /b 1
|
||||
)
|
||||
|
||||
if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
|
||||
echo ERROR: please run this from MSVC x64 native tools command prompt, 32-bit target is not supported!
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||
set CURR_DATE_TIME=%%j
|
||||
)
|
||||
|
||||
@@ -44,6 +44,12 @@ config_darwin() {
|
||||
fi
|
||||
fi
|
||||
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -liconv -ldl"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS -lLLVM-C"
|
||||
@@ -97,6 +103,12 @@ config_linux() {
|
||||
panic "Requirement: llvm-config must be base version greater than 11"
|
||||
fi
|
||||
|
||||
MAX_LLVM_VERSION=("14.999.999")
|
||||
if [ $(version $($LLVM_CONFIG --version)) -gt $(version $MAX_LLVM_VERSION) ]; then
|
||||
echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
|
||||
panic "Requirement: llvm-config must be base version smaller than 15"
|
||||
fi
|
||||
|
||||
LDFLAGS="$LDFLAGS -ldl"
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
|
||||
@@ -134,6 +146,14 @@ run_demo() {
|
||||
./odin run examples/demo/demo.odin -file
|
||||
}
|
||||
|
||||
have_which() {
|
||||
if ! which which > /dev/null 2>&1; then
|
||||
panic "Could not find \`which\`"
|
||||
fi
|
||||
}
|
||||
|
||||
have_which
|
||||
|
||||
case $OS in
|
||||
Linux)
|
||||
config_linux
|
||||
|
||||
@@ -15,3 +15,10 @@ if not exist "vendor\miniaudio\lib\*.lib" (
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
|
||||
if not exist "vendor\cgltf\lib\*.lib" (
|
||||
pushd vendor\cgltf\src
|
||||
call build.bat
|
||||
popd
|
||||
)
|
||||
|
||||
+15
-2
@@ -14,11 +14,24 @@ when ODIN_OS == .Windows {
|
||||
// EDOM,
|
||||
// EILSEQ
|
||||
// ERANGE
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
|
||||
when ODIN_OS == .Linux {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__libc_errno_location")
|
||||
@(link_name="__errno_location")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
EDOM :: 33
|
||||
EILSEQ :: 84
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .FreeBSD {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="__error")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package gzip
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package zlib
|
||||
|
||||
/*
|
||||
|
||||
@@ -8,40 +8,40 @@ Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
|
||||
}
|
||||
|
||||
|
||||
len :: proc(a: $A/Small_Array) -> int {
|
||||
len :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return a.len
|
||||
}
|
||||
|
||||
cap :: proc(a: $A/Small_Array) -> int {
|
||||
cap :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data)
|
||||
}
|
||||
|
||||
space :: proc(a: $A/Small_Array) -> int {
|
||||
space :: proc "contextless" (a: $A/Small_Array) -> int {
|
||||
return builtin.len(a.data) - a.len
|
||||
}
|
||||
|
||||
slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
slice :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> []T {
|
||||
return a.data[:a.len]
|
||||
}
|
||||
|
||||
|
||||
get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
get :: proc "contextless" (a: $A/Small_Array($N, $T), index: int) -> T {
|
||||
return a.data[index]
|
||||
}
|
||||
get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
get_ptr :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int) -> ^T {
|
||||
return &a.data[index]
|
||||
}
|
||||
|
||||
set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
set :: proc "contextless" (a: ^$A/Small_Array($N, $T), index: int, item: T) {
|
||||
a.data[index] = item
|
||||
}
|
||||
|
||||
resize :: proc(a: ^$A/Small_Array, length: int) {
|
||||
resize :: proc "contextless" (a: ^$A/Small_Array, length: int) {
|
||||
a.len = min(length, builtin.len(a.data))
|
||||
}
|
||||
|
||||
|
||||
push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_back :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.data[a.len] = item
|
||||
a.len += 1
|
||||
@@ -50,7 +50,7 @@ push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
push_front :: proc "contextless" (a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
if a.len < cap(a^) {
|
||||
a.len += 1
|
||||
data := slice(a)
|
||||
@@ -61,14 +61,14 @@ push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_back :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[a.len-1]
|
||||
a.len -= 1
|
||||
return item
|
||||
}
|
||||
|
||||
pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
pop_front :: proc "odin" (a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
assert(condition=(N > 0 && a.len > 0), loc=loc)
|
||||
item := a.data[0]
|
||||
s := slice(a)
|
||||
@@ -77,7 +77,7 @@ pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
|
||||
return item
|
||||
}
|
||||
|
||||
pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_back_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[a.len-1]
|
||||
a.len -= 1
|
||||
@@ -86,7 +86,7 @@ pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
pop_front_safe :: proc "contextless" (a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
if N > 0 && a.len > 0 {
|
||||
item = a.data[0]
|
||||
s := slice(a)
|
||||
@@ -97,16 +97,16 @@ pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
|
||||
return
|
||||
}
|
||||
|
||||
consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
consume :: proc "odin" (a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
|
||||
assert(condition=a.len >= count, loc=loc)
|
||||
a.len -= count
|
||||
}
|
||||
|
||||
clear :: proc(a: ^$A/Small_Array($N, $T)) {
|
||||
clear :: proc "contextless" (a: ^$A/Small_Array($N, $T)) {
|
||||
resize(a, 0)
|
||||
}
|
||||
|
||||
push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
push_back_elems :: proc "contextless" (a: ^$A/Small_Array($N, $T), items: ..T) {
|
||||
n := copy(a.data[a.len:], items[:])
|
||||
a.len += n
|
||||
}
|
||||
|
||||
@@ -257,21 +257,18 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return .Unsupported_Type
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
|
||||
for i in 0..<entries.len {
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := rawptr(data + entry_type.offsets[2])
|
||||
value := rawptr(data + entry_type.offsets[3])
|
||||
key := rawptr(runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index))
|
||||
value := rawptr(runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index))
|
||||
|
||||
// check for string type
|
||||
{
|
||||
@@ -281,13 +278,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
name: string
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
|
||||
case: return .Unsupported_Type
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,12 +399,10 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
return UNSUPPORTED_TYPE
|
||||
}
|
||||
raw_map := (^mem.Raw_Map)(v.data)
|
||||
if raw_map.entries.allocator.procedure == nil {
|
||||
raw_map.entries.allocator = p.allocator
|
||||
if raw_map.allocator.procedure == nil {
|
||||
raw_map.allocator = p.allocator
|
||||
}
|
||||
|
||||
header := runtime.__get_map_header_table_runtime(t)
|
||||
|
||||
elem_backing := bytes_make(t.value.size, t.value.align, p.allocator) or_return
|
||||
defer delete(elem_backing, p.allocator)
|
||||
|
||||
@@ -421,7 +419,6 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
return err
|
||||
}
|
||||
|
||||
key_hash := runtime.default_hasher_string(&key, 0)
|
||||
key_ptr := rawptr(&key)
|
||||
|
||||
key_cstr: cstring
|
||||
@@ -430,7 +427,7 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
|
||||
key_ptr = &key_cstr
|
||||
}
|
||||
|
||||
set_ptr := runtime.__dynamic_map_set(raw_map, header, key_hash, key_ptr, map_backing_value.data)
|
||||
set_ptr := runtime.__dynamic_map_set_without_hash(raw_map, t.map_info, key_ptr, map_backing_value.data)
|
||||
if set_ptr == nil {
|
||||
delete(key, p.allocator)
|
||||
}
|
||||
|
||||
+34
-30
@@ -162,7 +162,25 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
|
||||
p("Panic", message, loc)
|
||||
}
|
||||
|
||||
// formatted printing for cstrings
|
||||
caprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// c string with temp allocator
|
||||
ctprintf :: proc(format: string, args: ..any) -> cstring {
|
||||
str: strings.Builder
|
||||
strings.builder_init(&str, context.temp_allocator)
|
||||
sbprintf(&str, format, ..args)
|
||||
strings.write_byte(&str, 0)
|
||||
s := strings.to_string(str)
|
||||
return cstring(raw_data(s))
|
||||
}
|
||||
|
||||
// sbprint formats using the default print settings and writes to buf
|
||||
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
|
||||
@@ -240,7 +258,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
|
||||
was_prev_index := false
|
||||
|
||||
loop: for i := 0; i < end; /**/ {
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
|
||||
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered, n = fi.n}
|
||||
|
||||
prev_i := i
|
||||
for i < end && !(fmt[i] == '%' || fmt[i] == '{' || fmt[i] == '}') {
|
||||
@@ -975,7 +993,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
|
||||
}
|
||||
}
|
||||
else {
|
||||
io.write_string(fi.writer, s[:fi.width], &fi.n)
|
||||
io.write_string(fi.writer, s, &fi.n)
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2051,41 +2069,27 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
|
||||
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
if info.map_info == nil {
|
||||
return
|
||||
}
|
||||
entries := &m.entries
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
entry_size := ed.elem_size
|
||||
/*
|
||||
NOTE: The layout of a `map` is as follows:
|
||||
|
||||
map[Key]Value
|
||||
|
||||
## Internal Layout
|
||||
struct {
|
||||
hashes: []int,
|
||||
entries: [dynamic]struct{
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
},
|
||||
map_cap := uintptr(runtime.map_cap(m^))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
|
||||
j := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if !runtime.map_hash_is_valid(hs[bucket_index]) {
|
||||
continue
|
||||
}
|
||||
*/
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
if j > 0 {
|
||||
io.write_string(fi.writer, ", ", &fi.n)
|
||||
}
|
||||
j += 1
|
||||
|
||||
key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
|
||||
|
||||
key := data + entry_type.offsets[2] // key: Key
|
||||
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
|
||||
|
||||
io.write_string(fi.writer, "=", &fi.n)
|
||||
|
||||
value := data + entry_type.offsets[3] // value: Value
|
||||
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fprintln :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintln(w=w, args=args, sep=sep)
|
||||
}
|
||||
// fprintf formats according to the specififed format string and writes to fd
|
||||
// fprintf formats according to the specified format string and writes to fd
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
|
||||
w := io.to_writer(os.stream_from_handle(fd))
|
||||
return wprintf(w, fmt, ..args)
|
||||
@@ -34,12 +34,12 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
|
||||
print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
|
||||
// println formats using the default print settings and writes to os.stdout
|
||||
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
|
||||
// printf formats according to the specififed format string and writes to os.stdout
|
||||
// printf formats according to the specified format string and writes to os.stdout
|
||||
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
|
||||
|
||||
// eprint formats using the default print settings and writes to os.stderr
|
||||
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintln formats using the default print settings and writes to os.stderr
|
||||
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
|
||||
// eprintf formats according to the specififed format string and writes to os.stderr
|
||||
// eprintf formats according to the specified format string and writes to os.stderr
|
||||
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
|
||||
|
||||
+7
-3
@@ -72,8 +72,9 @@ djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bound
|
||||
return
|
||||
}
|
||||
|
||||
// If you have a choice, prefer fnv32a
|
||||
@(optimization_mode="speed")
|
||||
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
fnv32_no_a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b)
|
||||
@@ -81,15 +82,18 @@ fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
return h
|
||||
}
|
||||
|
||||
fnv32 :: fnv32_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
fnv64 :: fnv64_no_a // NOTE(bill): Not a fan of these aliases but seems necessary
|
||||
|
||||
// If you have a choice, prefer fnv64a
|
||||
@(optimization_mode="speed")
|
||||
fnv64 :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
fnv64_no_a :: proc(data: []byte, seed := u64(0xcbf29ce484222325)) -> u64 {
|
||||
h: u64 = seed
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
fnv32a :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
|
||||
h: u32 = seed
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
An example of how to use `load`.
|
||||
*/
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package png
|
||||
|
||||
import "core:image"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// This is purely for documentation
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package intrinsics
|
||||
|
||||
// Package-Related
|
||||
@@ -188,6 +188,9 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
|
||||
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
|
||||
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
|
||||
|
||||
type_map_info :: proc($T: typeid/map[$K]$V) -> ^runtime.Map_Info ---
|
||||
type_map_cell_info :: proc($T: typeid) -> ^runtime.Map_Cell_Info ---
|
||||
|
||||
type_convert_variants_to_pointers :: proc($T: typeid) -> typeid where type_is_union(T) ---
|
||||
|
||||
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
|
||||
|
||||
@@ -43,6 +43,13 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Alloc_Non_Zeroed:
|
||||
logf(
|
||||
level=la.level,
|
||||
fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
|
||||
args = {la.prefix, padding, size, alignment},
|
||||
location = location,
|
||||
)
|
||||
case .Free:
|
||||
if old_size != 0 {
|
||||
logf(
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
TODO: Handle +/- Infinity and NaN.
|
||||
*/
|
||||
|
||||
|
||||
//+ignore
|
||||
package math_big
|
||||
|
||||
import "core:mem"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
|
||||
*/
|
||||
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package math_big
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// core:math/linalg implements linear algebra procedures useful for 3D spatial transformations
|
||||
package linalg
|
||||
+337
-19
@@ -114,6 +114,92 @@ exp :: proc{
|
||||
exp_f64, exp_f64le, exp_f64be,
|
||||
}
|
||||
|
||||
pow10_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(pow10_f16(f16(x))) }
|
||||
pow10_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(pow10_f16(f16(x))) }
|
||||
pow10_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(pow10_f32(f32(x))) }
|
||||
pow10_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(pow10_f32(f32(x))) }
|
||||
pow10_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(pow10_f64(f64(x))) }
|
||||
pow10_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(pow10_f64(f64(x))) }
|
||||
pow10 :: proc{
|
||||
pow10_f16, pow10_f16le, pow10_f16be,
|
||||
pow10_f32, pow10_f32le, pow10_f32be,
|
||||
pow10_f64, pow10_f64le, pow10_f64be,
|
||||
}
|
||||
|
||||
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
|
||||
@static pow10_pos_tab := [?]f16{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f16{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 4 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -7 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f16(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
|
||||
@static pow10_pos_tab := [?]f32{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
|
||||
}
|
||||
@static pow10_neg_tab := [?]f32{
|
||||
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
|
||||
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
|
||||
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
|
||||
1e-30, 1e-31, 1e-32, 1e-33, 1e-34, 1e-35, 1e-36, 1e-37, 1e-38, 1e-39,
|
||||
1e-40, 1e-41, 1e-42, 1e-43, 1e-44, 1e-45,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 38 {
|
||||
return pow10_pos_tab[uint(n)]
|
||||
}
|
||||
if -45 <= n && n <= 0 {
|
||||
return pow10_neg_tab[uint(-n)]
|
||||
}
|
||||
if n > 0 {
|
||||
return inf_f32(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
|
||||
@static pow10_tab := [?]f64{
|
||||
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
|
||||
1e30, 1e31,
|
||||
}
|
||||
@static pow10_pos_tab32 := [?]f64{
|
||||
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
|
||||
}
|
||||
@static pow10_neg_tab32 := [?]f64{
|
||||
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
|
||||
}
|
||||
|
||||
if 0 <= n && n <= 308 {
|
||||
return pow10_pos_tab32[uint(n)/32] * pow10_tab[uint(n)%32]
|
||||
}
|
||||
if -323 <= n && n <= 0 {
|
||||
return pow10_neg_tab32[uint(-n)/32] / pow10_tab[uint(-n)%32]
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
return inf_f64(1)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
|
||||
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
|
||||
@@ -1088,7 +1174,7 @@ is_nan :: proc{
|
||||
// If sign < 0, is_inf reports whether f is negative infinity.
|
||||
// If sign == 0, is_inf reports whether f is either infinity.
|
||||
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1105,7 +1191,7 @@ is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1122,7 +1208,7 @@ is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
|
||||
}
|
||||
|
||||
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
|
||||
class := classify(abs(x))
|
||||
class := classify(x)
|
||||
switch {
|
||||
case sign > 0:
|
||||
return class == .Inf
|
||||
@@ -1344,20 +1430,20 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case x == 0:
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
case is_inf(x, 0):
|
||||
if is_inf(x, 1) {
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.25, y)
|
||||
return copy_sign(PI/4, y)
|
||||
}
|
||||
return copy_sign(0, y)
|
||||
}
|
||||
if is_inf(y, 0) {
|
||||
return copy_sign(PI*0.75, y)
|
||||
return copy_sign(3*PI/4, y)
|
||||
}
|
||||
return copy_sign(PI, y)
|
||||
case is_inf(y, 0):
|
||||
return copy_sign(PI*0.5, y)
|
||||
return copy_sign(PI/2, y)
|
||||
}
|
||||
|
||||
q := atan(y / x)
|
||||
@@ -1379,34 +1465,266 @@ atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
|
||||
}
|
||||
|
||||
atan2 :: proc{
|
||||
atan2_f16, atan2_f16le, atan2_f16be,
|
||||
atan2_f32, atan2_f32le, atan2_f32be,
|
||||
atan2_f64, atan2_f64le, atan2_f64be,
|
||||
atan2_f64, atan2_f32, atan2_f16,
|
||||
atan2_f64le, atan2_f64be,
|
||||
atan2_f32le, atan2_f32be,
|
||||
atan2_f16le, atan2_f16be,
|
||||
}
|
||||
|
||||
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, 1)
|
||||
}
|
||||
|
||||
asin :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return atan2(x, sqrt(1 - x*x))
|
||||
|
||||
|
||||
asin_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
x := x
|
||||
z, r, s: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* asin(1) = +-pi/2 with inexact */
|
||||
return x*pio2_hi + 1e-120
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
/* if 0x1p-1022 <= |x| < 0x1p-26, avoid raising underflow */
|
||||
if ix < 0x3e500000 && ix >= 0x00100000 {
|
||||
return x
|
||||
}
|
||||
return x + x*R(x*x)
|
||||
}
|
||||
/* 1 > |x| >= 0.5 */
|
||||
z = (1 - abs(x))*0.5
|
||||
s = sqrt(z)
|
||||
r = R(z)
|
||||
if ix >= 0x3fef3333 { /* if |x| > 0.975 */
|
||||
x = pio2_hi-(2*(s+s*r)-pio2_lo)
|
||||
} else {
|
||||
f, c: f64
|
||||
/* f+c = sqrt(z) */
|
||||
f = s
|
||||
(^u64)(&f)^ &= 0xffffffff_00000000
|
||||
c = (z-f*f)/(s+f)
|
||||
x = 0.5*pio2_hi - (2*s*r - (pio2_lo-2*c) - (0.5*pio2_hi-2*f))
|
||||
}
|
||||
return -x if hx >> 31 != 0 else x
|
||||
}
|
||||
asin_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(asin_f64(f64(x)))
|
||||
}
|
||||
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(asin_f64(f64(x)))
|
||||
}
|
||||
asin :: proc{
|
||||
asin_f64, asin_f32, asin_f16,
|
||||
asin_f64le, asin_f64be,
|
||||
asin_f32le, asin_f32be,
|
||||
asin_f16le, asin_f16be,
|
||||
}
|
||||
|
||||
acos :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return 2 * atan2(sqrt(1 - x), sqrt(1 + x))
|
||||
|
||||
acos_f64 :: proc "contextless" (x: f64) -> f64 {
|
||||
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
|
||||
/*
|
||||
* ====================================================
|
||||
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
|
||||
*
|
||||
* Developed at SunSoft, a Sun Microsystems, Inc. business.
|
||||
* Permission to use, copy, modify, and distribute this
|
||||
* software is freely granted, provided that this notice
|
||||
* is preserved.
|
||||
* ====================================================
|
||||
*/
|
||||
|
||||
pio2_hi :: 0h3FF921FB54442D18
|
||||
pio2_lo :: 0h3C91A62633145C07
|
||||
pS0 :: 0h3FC5555555555555
|
||||
pS1 :: 0hBFD4D61203EB6F7D
|
||||
pS2 :: 0h3FC9C1550E884455
|
||||
pS3 :: 0hBFA48228B5688F3B
|
||||
pS4 :: 0h3F49EFE07501B288
|
||||
pS5 :: 0h3F023DE10DFDF709
|
||||
qS1 :: 0hC0033A271C8A2D4B
|
||||
qS2 :: 0h40002AE59C598AC8
|
||||
qS3 :: 0hBFE6066C1B8D0159
|
||||
qS4 :: 0h3FB3B8C5B12E9282
|
||||
|
||||
R :: #force_inline proc "contextless" (z: f64) -> f64 {
|
||||
p, q: f64
|
||||
p = z*(pS0+z*(pS1+z*(pS2+z*(pS3+z*(pS4+z*pS5)))))
|
||||
q = 1.0+z*(qS1+z*(qS2+z*(qS3+z*qS4)))
|
||||
return p/q
|
||||
}
|
||||
|
||||
z, w, s, c, df: f64
|
||||
dwords := transmute([2]u32)x
|
||||
hx := dwords[1]
|
||||
ix := hx & 0x7fffffff
|
||||
/* |x| >= 1 or nan */
|
||||
if ix >= 0x3ff00000 {
|
||||
lx := dwords[0]
|
||||
|
||||
if (ix-0x3ff00000 | lx) == 0 {
|
||||
/* acos(1)=0, acos(-1)=pi */
|
||||
if hx >> 31 != 0 {
|
||||
return 2*pio2_hi + 1e-120
|
||||
}
|
||||
return 0
|
||||
}
|
||||
return 0/(x-x)
|
||||
}
|
||||
/* |x| < 0.5 */
|
||||
if ix < 0x3fe00000 {
|
||||
if ix <= 0x3c600000 { /* |x| < 2**-57 */
|
||||
return pio2_hi + 1e-120
|
||||
}
|
||||
return pio2_hi - (x - (pio2_lo-x*R(x*x)))
|
||||
}
|
||||
/* x < -0.5 */
|
||||
if hx >> 31 != 0 {
|
||||
z = (1.0+x)*0.5
|
||||
s = sqrt(z)
|
||||
w = R(z)*s-pio2_lo
|
||||
return 2*(pio2_hi - (s+w))
|
||||
}
|
||||
/* x > 0.5 */
|
||||
z = (1.0-x)*0.5
|
||||
s = sqrt(z)
|
||||
df = s
|
||||
(^u64)(&df)^ &= 0xffffffff_00000000
|
||||
c = (z-df*df)/(s+df)
|
||||
w = R(z)*s+c
|
||||
return 2*(df+w)
|
||||
}
|
||||
acos_f64le :: proc "contextless" (x: f64le) -> f64le {
|
||||
return f64le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f64be :: proc "contextless" (x: f64be) -> f64be {
|
||||
return f64be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32 :: proc "contextless" (x: f32) -> f32 {
|
||||
return f32(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32le :: proc "contextless" (x: f32le) -> f32le {
|
||||
return f32le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f32be :: proc "contextless" (x: f32be) -> f32be {
|
||||
return f32be(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16 :: proc "contextless" (x: f16) -> f16 {
|
||||
return f16(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16le :: proc "contextless" (x: f16le) -> f16le {
|
||||
return f16le(acos_f64(f64(x)))
|
||||
}
|
||||
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
|
||||
return f16be(acos_f64(f64(x)))
|
||||
}
|
||||
acos :: proc{
|
||||
acos_f64, acos_f32, acos_f16,
|
||||
acos_f64le, acos_f64be,
|
||||
acos_f32le, acos_f32be,
|
||||
acos_f16le, acos_f16be,
|
||||
}
|
||||
|
||||
sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) - exp(-x))*0.5
|
||||
return copy_sign(((exp(x) - exp(-x))*0.5), x)
|
||||
}
|
||||
|
||||
cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
return (exp(x) + exp(-x))*0.5
|
||||
return ((exp(x) + exp(-x))*0.5)
|
||||
}
|
||||
|
||||
tanh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
|
||||
t := exp(2*x)
|
||||
return (t - 1) / (t + 1)
|
||||
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
P0 :: -9.64399179425052238628e-1
|
||||
P1 :: -9.92877231001918586564e1
|
||||
P2 :: -1.61468768441708447952e3
|
||||
Q0 :: +1.12811678491632931402e2
|
||||
Q1 :: +2.23548839060100448583e3
|
||||
Q2 :: +4.84406305325125486048e3
|
||||
|
||||
MAXLOG :: 8.8029691931113054295988e+01 // log(2**127)
|
||||
|
||||
|
||||
x := f64(y)
|
||||
z := abs(x)
|
||||
switch {
|
||||
case z > 0.5*MAXLOG:
|
||||
if x < 0 {
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
case z >= 0.625:
|
||||
s := exp(2 * z)
|
||||
z = 1 - 2/(s+1)
|
||||
if x < 0 {
|
||||
z = -z
|
||||
}
|
||||
case:
|
||||
if x == 0 {
|
||||
return T(x)
|
||||
}
|
||||
s := x * x
|
||||
z = x + x*s*((P0*s+P1)*s+P2)/(((s+Q0)*s+Q1)*s+Q2)
|
||||
}
|
||||
return T(z)
|
||||
}
|
||||
|
||||
asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
|
||||
|
||||
@@ -182,3 +182,12 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
|
||||
array[i], array[j] = array[j], array[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a random element from the given slice
|
||||
choice :: proc(array: $T/[]$E, r: ^Rand = nil) -> (res: E) {
|
||||
n := i64(len(array))
|
||||
if n < 1 {
|
||||
return E{}
|
||||
}
|
||||
return array[int63_max(n, r)]
|
||||
}
|
||||
+25
-17
@@ -69,10 +69,22 @@ alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator :=
|
||||
return runtime.mem_alloc(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
alloc_bytes_non_zeroed :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
return runtime.mem_alloc_non_zeroed(size, alignment, allocator, loc)
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.mem_free(ptr, allocator, loc)
|
||||
}
|
||||
|
||||
free_with_size :: proc(ptr: rawptr, byte_count: int, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
}
|
||||
_, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, byte_count, loc)
|
||||
return err
|
||||
}
|
||||
|
||||
free_bytes :: proc(bytes: []byte, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.mem_free_bytes(bytes, allocator, loc)
|
||||
}
|
||||
@@ -108,22 +120,20 @@ query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_locatio
|
||||
|
||||
|
||||
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(str), allocator, loc)
|
||||
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(str), len(str), allocator, loc)
|
||||
}
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) {
|
||||
free((^byte)(str), allocator, loc)
|
||||
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free((^byte)(str), allocator, loc)
|
||||
}
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) {
|
||||
free(raw_data(array), array.allocator, loc)
|
||||
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
|
||||
}
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) {
|
||||
free(raw_data(array), allocator, loc)
|
||||
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
return free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
|
||||
}
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) {
|
||||
raw := transmute(Raw_Map)m
|
||||
delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
free(raw.entries.data, raw.entries.allocator, loc)
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
return runtime.map_free_dynamic(transmute(Raw_Map)m, runtime.map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -154,8 +164,6 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
DEFAULT_RESERVE_CAPACITY :: 16
|
||||
|
||||
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (slice: T, err: Allocator_Error) {
|
||||
runtime.make_slice_error_loc(loc, len)
|
||||
data := alloc_bytes(size_of(E)*len, alignment, allocator, loc) or_return
|
||||
@@ -169,7 +177,7 @@ make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allo
|
||||
return make_aligned(T, len, align_of(E), allocator, loc)
|
||||
}
|
||||
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
|
||||
return make_dynamic_array_len_cap(T, 0, 16, allocator, loc)
|
||||
}
|
||||
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
|
||||
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
|
||||
@@ -184,12 +192,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
array = transmute(T)s
|
||||
return
|
||||
}
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1<<runtime.MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
runtime.make_map_expr_error_loc(loc, cap)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, cap, loc)
|
||||
return m
|
||||
}
|
||||
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
|
||||
|
||||
+60
-34
@@ -59,7 +59,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
arena := cast(^Arena)allocator_data
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
#no_bounds_check end := &arena.data[arena.offset]
|
||||
|
||||
ptr := align_forward(end, uintptr(alignment))
|
||||
@@ -72,7 +72,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
arena.offset += total_size
|
||||
arena.peak_used = max(arena.peak_used, arena.offset)
|
||||
zero(ptr, size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(ptr, size)
|
||||
}
|
||||
return byte_slice(ptr, size), nil
|
||||
|
||||
case .Free:
|
||||
@@ -87,7 +89,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -162,7 +164,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size := size
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
size = align_forward_int(size, alignment)
|
||||
|
||||
switch {
|
||||
@@ -170,7 +172,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := start + uintptr(s.curr_offset)
|
||||
ptr = align_forward_uintptr(ptr, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -180,7 +184,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case size <= len(s.data):
|
||||
start := uintptr(raw_data(s.data))
|
||||
ptr := align_forward_uintptr(start, uintptr(alignment))
|
||||
zero(rawptr(ptr), size)
|
||||
if mode != .Alloc_Non_Zeroed {
|
||||
zero(rawptr(ptr), size)
|
||||
}
|
||||
|
||||
s.prev_allocation = rawptr(ptr)
|
||||
offset := int(ptr - start)
|
||||
@@ -211,6 +217,9 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return ptr, err
|
||||
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
}
|
||||
start := uintptr(raw_data(s.data))
|
||||
end := start + uintptr(len(s.data))
|
||||
old_ptr := uintptr(old_memory)
|
||||
@@ -266,7 +275,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -333,7 +342,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return nil, .Invalid_Argument
|
||||
}
|
||||
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.curr_offset)
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Stack_Allocation_Header))
|
||||
if s.curr_offset + padding + size > len(s.data) {
|
||||
@@ -351,13 +360,15 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.curr_offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, alignment, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -392,7 +403,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, alignment)
|
||||
return raw_alloc(s, size, alignment, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -418,7 +429,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
||||
|
||||
if old_offset != header.prev_offset {
|
||||
data, err := raw_alloc(s, size, alignment)
|
||||
data, err := raw_alloc(s, size, alignment, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -439,7 +450,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
case .Query_Info:
|
||||
@@ -497,7 +508,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
align := clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
|
||||
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
raw_alloc :: proc(s: ^Small_Stack, size, alignment: int, zero_memory: bool) -> ([]byte, Allocator_Error) {
|
||||
curr_addr := uintptr(raw_data(s.data)) + uintptr(s.offset)
|
||||
padding := calc_padding_with_header(curr_addr, uintptr(alignment), size_of(Small_Stack_Allocation_Header))
|
||||
if s.offset + padding + size > len(s.data) {
|
||||
@@ -513,13 +524,15 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
s.peak_used = max(s.peak_used, s.offset)
|
||||
|
||||
zero(rawptr(next_addr), size)
|
||||
if zero_memory {
|
||||
zero(rawptr(next_addr), size)
|
||||
}
|
||||
return byte_slice(rawptr(next_addr), size), nil
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return raw_alloc(s, size, align)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return raw_alloc(s, size, align, mode == .Alloc)
|
||||
case .Free:
|
||||
if old_memory == nil {
|
||||
return nil, nil
|
||||
@@ -548,7 +561,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return raw_alloc(s, size, align)
|
||||
return raw_alloc(s, size, align, true)
|
||||
}
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
@@ -571,7 +584,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
return byte_slice(old_memory, size), nil
|
||||
}
|
||||
|
||||
data, err := raw_alloc(s, size, align)
|
||||
data, err := raw_alloc(s, size, align, true)
|
||||
if err == nil {
|
||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||
}
|
||||
@@ -580,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -623,7 +636,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
pool := (^Dynamic_Pool)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return dynamic_pool_alloc_bytes(pool, size)
|
||||
case .Free:
|
||||
return nil, .Mode_Not_Implemented
|
||||
@@ -643,7 +656,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -794,6 +807,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc called")
|
||||
}
|
||||
case .Alloc_Non_Zeroed:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Alloc_Non_Zeroed called")
|
||||
}
|
||||
case .Resize:
|
||||
if size > 0 {
|
||||
panic("mem: panic allocator, .Resize called")
|
||||
@@ -831,6 +848,7 @@ Tracking_Allocator_Entry :: struct {
|
||||
memory: rawptr,
|
||||
size: int,
|
||||
alignment: int,
|
||||
mode: Allocator_Mode,
|
||||
err: Allocator_Error,
|
||||
location: runtime.Source_Code_Location,
|
||||
}
|
||||
@@ -849,6 +867,10 @@ tracking_allocator_init :: proc(t: ^Tracking_Allocator, backing_allocator: Alloc
|
||||
t.backing = backing_allocator
|
||||
t.allocation_map.allocator = internals_allocator
|
||||
t.bad_free_array.allocator = internals_allocator
|
||||
|
||||
if .Free_All in query_features(t.backing) {
|
||||
t.clear_on_free_all = true
|
||||
}
|
||||
}
|
||||
|
||||
tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
@@ -856,6 +878,13 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
|
||||
delete(t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
|
||||
clear(&t.allocation_map)
|
||||
clear(&t.bad_free_array)
|
||||
}
|
||||
|
||||
|
||||
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
return Allocator{
|
||||
data = data,
|
||||
@@ -865,7 +894,7 @@ tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
|
||||
|
||||
tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
|
||||
data := (^Tracking_Allocator)(allocator_data)
|
||||
if mode == .Query_Info {
|
||||
info := (^Allocator_Query_Info)(old_memory)
|
||||
@@ -877,21 +906,16 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
info.pointer = nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
return
|
||||
}
|
||||
|
||||
result: []byte
|
||||
err: Allocator_Error
|
||||
if mode == .Free && old_memory != nil && old_memory not_in data.allocation_map {
|
||||
append(&data.bad_free_array, Tracking_Allocator_Bad_Free_Entry{
|
||||
memory = old_memory,
|
||||
location = loc,
|
||||
})
|
||||
} else {
|
||||
result, err = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc)
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
result = data.backing.procedure(data.backing.data, mode, size, alignment, old_memory, old_size, loc) or_return
|
||||
}
|
||||
result_ptr := raw_data(result)
|
||||
|
||||
@@ -900,10 +924,11 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -921,6 +946,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
data.allocation_map[result_ptr] = Tracking_Allocator_Entry{
|
||||
memory = result_ptr,
|
||||
size = size,
|
||||
mode = mode,
|
||||
alignment = alignment,
|
||||
err = err,
|
||||
location = loc,
|
||||
@@ -929,7 +955,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features, .Query_Info}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
@@ -937,6 +963,6 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
unreachable()
|
||||
}
|
||||
|
||||
return result, err
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+2
-6
@@ -1,5 +1,6 @@
|
||||
package mem
|
||||
|
||||
import "core:builtin"
|
||||
import "core:runtime"
|
||||
|
||||
Raw_Any :: runtime.Raw_Any
|
||||
@@ -21,12 +22,7 @@ make_any :: proc "contextless" (data: rawptr, id: typeid) -> any {
|
||||
return transmute(any)Raw_Any{data, id}
|
||||
}
|
||||
|
||||
raw_array_data :: runtime.raw_array_data
|
||||
raw_simd_data :: runtime.raw_simd_data
|
||||
raw_string_data :: runtime.raw_string_data
|
||||
raw_slice_data :: runtime.raw_slice_data
|
||||
raw_dynamic_array_data :: runtime.raw_dynamic_array_data
|
||||
raw_data :: runtime.raw_data
|
||||
raw_data :: builtin.raw_data
|
||||
|
||||
|
||||
Poly_Raw_Map_Entry :: struct($Key, $Value: typeid) {
|
||||
|
||||
@@ -0,0 +1,331 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Arena_Kind :: enum uint {
|
||||
Growing = 0, // Chained memory blocks (singly linked list).
|
||||
Static = 1, // Fixed reservation sized.
|
||||
Buffer = 2, // Uses a fixed sized buffer.
|
||||
}
|
||||
|
||||
Arena :: struct {
|
||||
kind: Arena_Kind,
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
minimum_block_size: uint,
|
||||
temp_count: uint,
|
||||
}
|
||||
|
||||
|
||||
// 1 MiB should be enough to start with
|
||||
DEFAULT_ARENA_STATIC_COMMIT_SIZE :: 1<<20
|
||||
DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: DEFAULT_ARENA_STATIC_COMMIT_SIZE
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
DEFAULT_ARENA_STATIC_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
|
||||
arena.kind = .Growing
|
||||
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.kind = .Static
|
||||
arena.curr_block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
|
||||
if len(buffer) < size_of(Memory_Block) {
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
arena.kind = .Buffer
|
||||
|
||||
mem.zero_slice(buffer)
|
||||
|
||||
block_base := raw_data(buffer)
|
||||
block := (^Memory_Block)(block_base)
|
||||
block.base = block_base[size_of(Memory_Block):]
|
||||
block.reserved = len(buffer) - size_of(Memory_Block)
|
||||
block.committed = block.reserved
|
||||
block.used = 0
|
||||
|
||||
arena.curr_block = block
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
|
||||
|
||||
size := size
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
switch arena.kind {
|
||||
case .Growing:
|
||||
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
|
||||
size = mem.align_forward_uint(size, alignment)
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE
|
||||
}
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
prev_used := arena.curr_block.used
|
||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||
arena.total_used += arena.curr_block.used - prev_used
|
||||
case .Static:
|
||||
if arena.curr_block == nil {
|
||||
if arena.minimum_block_size == 0 {
|
||||
arena.minimum_block_size = DEFAULT_ARENA_STATIC_RESERVE_SIZE
|
||||
}
|
||||
arena_init_static(arena=arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
|
||||
}
|
||||
fallthrough
|
||||
case .Buffer:
|
||||
if arena.curr_block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
data, err = alloc_from_memory_block(arena.curr_block, size, alignment)
|
||||
arena.total_used = arena.curr_block.used
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
|
||||
if arena.curr_block != nil {
|
||||
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
|
||||
|
||||
prev_pos := arena.curr_block.used
|
||||
arena.curr_block.used = clamp(pos, 0, arena.curr_block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.curr_block.base[arena.curr_block.used:][:pos-prev_pos])
|
||||
}
|
||||
arena.total_used = arena.curr_block.used
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
arena.total_used = 0
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
arena_growing_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
if free_block := arena.curr_block; free_block != nil {
|
||||
assert(arena.kind == .Growing, "expected a .Growing arena", loc)
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
}
|
||||
|
||||
arena_free_all :: proc(arena: ^Arena) {
|
||||
switch arena.kind {
|
||||
case .Growing:
|
||||
for arena.curr_block != nil {
|
||||
arena_growing_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_reserved = 0
|
||||
case .Static, .Buffer:
|
||||
arena_static_reset_to(arena, 0)
|
||||
}
|
||||
arena.total_used = 0
|
||||
}
|
||||
|
||||
arena_destroy :: proc(arena: ^Arena) {
|
||||
arena_free_all(arena)
|
||||
if arena.kind != .Buffer {
|
||||
memory_block_dealloc(arena.curr_block)
|
||||
}
|
||||
arena.curr_block = nil
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
arena.temp_count = 0
|
||||
}
|
||||
|
||||
arena_growing_bootstrap_new :: proc{
|
||||
arena_growing_bootstrap_new_by_offset,
|
||||
arena_growing_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
arena_static_bootstrap_new :: proc{
|
||||
arena_static_bootstrap_new_by_offset,
|
||||
arena_static_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Arena
|
||||
bootstrap.kind = .Growing
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_growing_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return arena_growing_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Arena
|
||||
bootstrap.kind = .Static
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_static_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return arena_static_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
|
||||
return mem.Allocator{arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Arena)(allocator_data)
|
||||
|
||||
size, alignment := uint(size), uint(alignment)
|
||||
old_size := uint(old_size)
|
||||
|
||||
switch mode {
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
case .Free_All:
|
||||
arena_free_all(arena)
|
||||
case .Resize:
|
||||
old_data := ([^]byte)(old_memory)
|
||||
|
||||
switch {
|
||||
case old_data == nil:
|
||||
return arena_alloc(arena, size, alignment)
|
||||
case size == old_size:
|
||||
// return old memory
|
||||
data = old_data[:size]
|
||||
return
|
||||
case size == 0:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case (uintptr(old_data) & uintptr(alignment-1) == 0) && size < old_size:
|
||||
// shrink data in-place
|
||||
data = old_data[:size]
|
||||
return
|
||||
}
|
||||
|
||||
new_memory := arena_alloc(arena, size, alignment) or_return
|
||||
if new_memory == nil {
|
||||
return
|
||||
}
|
||||
copy(new_memory, old_data[:old_size])
|
||||
return new_memory, nil
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
case .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Arena_Temp :: struct {
|
||||
arena: ^Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
|
||||
assert(arena != nil, "nil arena", loc)
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
memory_block_found := false
|
||||
for block := arena.curr_block; block != nil; block = block.prev {
|
||||
if block == temp.block {
|
||||
memory_block_found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !memory_block_found {
|
||||
assert(arena.curr_block == temp.block, "memory block stored within Arena_Temp not owned by Arena", loc)
|
||||
}
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
arena_growing_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
arena_init :: proc{
|
||||
static_arena_init,
|
||||
growing_arena_init,
|
||||
}
|
||||
|
||||
arena_temp_begin :: proc{
|
||||
static_arena_temp_begin,
|
||||
growing_arena_temp_begin,
|
||||
}
|
||||
|
||||
arena_temp_end :: proc{
|
||||
static_arena_temp_end,
|
||||
growing_arena_temp_end,
|
||||
}
|
||||
|
||||
arena_check_temp :: proc{
|
||||
static_arena_check_temp,
|
||||
growing_arena_check_temp,
|
||||
}
|
||||
|
||||
arena_allocator :: proc{
|
||||
static_arena_allocator,
|
||||
growing_arena_allocator,
|
||||
}
|
||||
|
||||
arena_alloc :: proc{
|
||||
static_arena_alloc,
|
||||
growing_arena_alloc,
|
||||
}
|
||||
|
||||
arena_free_all :: proc{
|
||||
static_arena_free_all,
|
||||
growing_arena_free_all,
|
||||
}
|
||||
|
||||
arena_destroy :: proc{
|
||||
static_arena_destroy,
|
||||
growing_arena_destroy,
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Growing_Arena :: struct {
|
||||
curr_block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
DEFAULT_MINIMUM_BLOCK_SIZE :: 1<<20 // 1 MiB should be enough
|
||||
|
||||
growing_arena_init :: proc(arena: ^Growing_Arena, reserved: uint = DEFAULT_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
|
||||
arena.curr_block = memory_block_alloc(0, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.curr_block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_alloc :: proc(arena: ^Growing_Arena, min_size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward_offset :: proc "contextless" (arena: ^Growing_Arena, alignment: int) -> uint #no_bounds_check {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(arena.curr_block.base[arena.curr_block.used:])
|
||||
mask := uintptr(alignment-1)
|
||||
if ptr & mask != 0 {
|
||||
alignment_offset = uint(alignment) - uint(ptr & mask)
|
||||
}
|
||||
return alignment_offset
|
||||
}
|
||||
|
||||
assert(mem.is_power_of_two(uintptr(alignment)))
|
||||
|
||||
size := uint(0)
|
||||
if arena.curr_block != nil {
|
||||
size = uint(min_size) + align_forward_offset(arena, alignment)
|
||||
}
|
||||
|
||||
if arena.curr_block == nil || arena.curr_block.used + size > arena.curr_block.reserved {
|
||||
size = uint(mem.align_forward_int(min_size, alignment))
|
||||
arena.minimum_block_size = max(DEFAULT_MINIMUM_BLOCK_SIZE, arena.minimum_block_size)
|
||||
|
||||
block_size := max(size, arena.minimum_block_size)
|
||||
|
||||
new_block := memory_block_alloc(size, block_size, {}) or_return
|
||||
new_block.prev = arena.curr_block
|
||||
arena.curr_block = new_block
|
||||
arena.total_reserved += new_block.reserved
|
||||
}
|
||||
|
||||
|
||||
data, err = alloc_from_memory_block(arena.curr_block, int(size), alignment)
|
||||
if err == nil {
|
||||
arena.total_used += size
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_free_last_memory_block :: proc(arena: ^Growing_Arena) {
|
||||
free_block := arena.curr_block
|
||||
arena.curr_block = free_block.prev
|
||||
memory_block_dealloc(free_block)
|
||||
}
|
||||
|
||||
growing_arena_free_all :: proc(arena: ^Growing_Arena) {
|
||||
for arena.curr_block != nil {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = 0
|
||||
}
|
||||
|
||||
growing_arena_destroy :: proc(arena: ^Growing_Arena) {
|
||||
growing_arena_free_all(arena)
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Growing_Arena
|
||||
bootstrap.minimum_block_size = minimum_block_size
|
||||
|
||||
data := growing_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Growing_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, minimum_block_size := DEFAULT_MINIMUM_BLOCK_SIZE) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return growing_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), minimum_block_size)
|
||||
}
|
||||
growing_arena_bootstrap_new :: proc{
|
||||
growing_arena_bootstrap_new_by_offset,
|
||||
growing_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{growing_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return growing_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
growing_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, growing_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
Growing_Arena_Temp :: struct {
|
||||
arena: ^Growing_Arena,
|
||||
block: ^Memory_Block,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
growing_arena_temp_begin :: proc(arena: ^Growing_Arena) -> (temp: Growing_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.block = arena.curr_block
|
||||
if arena.curr_block != nil {
|
||||
temp.used = arena.curr_block.used
|
||||
}
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
growing_arena_temp_end :: proc(temp: Growing_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
for arena.curr_block != temp.block {
|
||||
growing_arena_free_last_memory_block(arena)
|
||||
}
|
||||
|
||||
if block := arena.curr_block; block != nil {
|
||||
assert(block.used >= temp.used, "out of order use of growing_arena_temp_end", loc)
|
||||
amount_to_zero := min(block.used-temp.used, block.reserved-block.used)
|
||||
mem.zero_slice(block.base[temp.used:][:amount_to_zero])
|
||||
block.used = temp.used
|
||||
}
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of growing_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
growing_arena_check_temp :: proc(arena: ^Growing_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Growing_Arena_Temp not been ended", loc)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
|
||||
Static_Arena :: struct {
|
||||
block: ^Memory_Block,
|
||||
total_used: uint,
|
||||
total_reserved: uint,
|
||||
|
||||
minimum_block_size: uint,
|
||||
temp_count: int,
|
||||
}
|
||||
|
||||
STATIC_ARENA_DEFAULT_COMMIT_SIZE :: 1<<20 // 1 MiB should be enough to start with
|
||||
|
||||
// 1 GiB on 64-bit systems, 128 MiB on 32-bit systems by default
|
||||
STATIC_ARENA_DEFAULT_RESERVE_SIZE :: 1<<30 when size_of(uintptr) == 8 else 1<<27
|
||||
|
||||
static_arena_init :: proc(arena: ^Static_Arena, reserved: uint, commit_size: uint = STATIC_ARENA_DEFAULT_COMMIT_SIZE) -> (err: Allocator_Error) {
|
||||
arena.block = memory_block_alloc(commit_size, reserved, {}) or_return
|
||||
arena.total_used = 0
|
||||
arena.total_reserved = arena.block.reserved
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_destroy :: proc(arena: ^Static_Arena) {
|
||||
memory_block_dealloc(arena.block)
|
||||
arena^ = {}
|
||||
}
|
||||
|
||||
|
||||
static_arena_alloc :: proc(arena: ^Static_Arena, size: int, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
align_forward :: #force_inline proc "contextless" (ptr: uint, align: uint) -> uint {
|
||||
mask := align-1
|
||||
return (ptr + mask) &~ mask
|
||||
}
|
||||
|
||||
if arena.block == nil {
|
||||
reserve_size := max(arena.minimum_block_size, STATIC_ARENA_DEFAULT_RESERVE_SIZE)
|
||||
static_arena_init(arena, reserve_size, STATIC_ARENA_DEFAULT_COMMIT_SIZE) or_return
|
||||
}
|
||||
|
||||
MINIMUM_ALIGN :: 2*align_of(uintptr)
|
||||
|
||||
defer arena.total_used = arena.block.used
|
||||
return alloc_from_memory_block(arena.block, size, max(MINIMUM_ALIGN, alignment))
|
||||
}
|
||||
|
||||
static_arena_reset_to :: proc(arena: ^Static_Arena, pos: uint) -> bool {
|
||||
if arena.block != nil {
|
||||
prev_pos := arena.block.used
|
||||
arena.block.used = clamp(pos, 0, arena.block.reserved)
|
||||
|
||||
if prev_pos < pos {
|
||||
mem.zero_slice(arena.block.base[arena.block.used:][:pos-prev_pos])
|
||||
}
|
||||
return true
|
||||
} else if pos == 0 {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
static_arena_free_all :: proc(arena: ^Static_Arena) {
|
||||
static_arena_reset_to(arena, 0)
|
||||
}
|
||||
|
||||
|
||||
static_arena_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
bootstrap: Static_Arena
|
||||
bootstrap.minimum_block_size = reserved
|
||||
|
||||
data := static_arena_alloc(&bootstrap, size_of(T), align_of(T)) or_return
|
||||
|
||||
ptr = (^T)(raw_data(data))
|
||||
|
||||
(^Static_Arena)(uintptr(ptr) + offset_to_arena)^ = bootstrap
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_bootstrap_new_by_name :: proc($T: typeid, $field_name: string, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
|
||||
return static_arena_bootstrap_new_by_offset(T, offset_of_by_string(T, field_name), reserved)
|
||||
}
|
||||
static_arena_bootstrap_new :: proc{
|
||||
static_arena_bootstrap_new_by_offset,
|
||||
static_arena_bootstrap_new_by_name,
|
||||
}
|
||||
|
||||
|
||||
static_arena_allocator :: proc(arena: ^Static_Arena) -> mem.Allocator {
|
||||
return mem.Allocator{static_arena_allocator_proc, arena}
|
||||
}
|
||||
|
||||
static_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Static_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return static_arena_alloc(arena, size, alignment)
|
||||
case .Free:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
case .Free_All:
|
||||
static_arena_free_all(arena)
|
||||
return
|
||||
case .Resize:
|
||||
return mem.default_resize_bytes_align(mem.byte_slice(old_memory, old_size), size, alignment, static_arena_allocator(arena), location)
|
||||
|
||||
case .Query_Features, .Query_Info:
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
err = .Mode_Not_Implemented
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
Static_Arena_Temp :: struct {
|
||||
arena: ^Static_Arena,
|
||||
used: uint,
|
||||
}
|
||||
|
||||
|
||||
static_arena_temp_begin :: proc(arena: ^Static_Arena) -> (temp: Static_Arena_Temp) {
|
||||
temp.arena = arena
|
||||
temp.used = arena.block.used if arena.block != nil else 0
|
||||
arena.temp_count += 1
|
||||
return
|
||||
}
|
||||
|
||||
static_arena_temp_end :: proc(temp: Static_Arena_Temp, loc := #caller_location) {
|
||||
assert(temp.arena != nil, "nil arena", loc)
|
||||
arena := temp.arena
|
||||
|
||||
used := arena.block.used if arena.block != nil else 0
|
||||
|
||||
assert(temp.used >= used, "invalid Static_Arena_Temp", loc)
|
||||
|
||||
static_arena_reset_to(arena, temp.used)
|
||||
|
||||
assert(arena.temp_count > 0, "double-use of static_arena_temp_end", loc)
|
||||
arena.temp_count -= 1
|
||||
}
|
||||
|
||||
|
||||
static_arena_check_temp :: proc(arena: ^Static_Arena, loc := #caller_location) {
|
||||
assert(arena.temp_count == 0, "Static_Arena_Temp not been ended", loc)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package mem_virtual
|
||||
|
||||
import "core:mem"
|
||||
import "core:intrinsics"
|
||||
|
||||
DEFAULT_PAGE_SIZE := uint(4096)
|
||||
|
||||
@@ -95,18 +96,11 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
|
||||
pmblock.block.committed = committed
|
||||
pmblock.block.reserved = reserved
|
||||
|
||||
sentinel := &global_platform_memory_block_sentinel
|
||||
platform_mutex_lock()
|
||||
pmblock.next = sentinel
|
||||
pmblock.prev = sentinel.prev
|
||||
pmblock.prev.next = pmblock
|
||||
pmblock.next.prev = pmblock
|
||||
platform_mutex_unlock()
|
||||
|
||||
return &pmblock.block, nil
|
||||
}
|
||||
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
|
||||
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: uint) -> (data: []byte, err: Allocator_Error) {
|
||||
calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
|
||||
alignment_offset := uint(0)
|
||||
ptr := uintptr(block.base[block.used:])
|
||||
@@ -134,11 +128,18 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
return nil
|
||||
}
|
||||
|
||||
if block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
|
||||
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
|
||||
size := uint(min_size) + alignment_offset
|
||||
size, size_ok := safe_add(min_size, alignment_offset)
|
||||
if !size_ok {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
|
||||
if block.used + size > block.reserved {
|
||||
if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.reserved {
|
||||
err = .Out_Of_Memory
|
||||
return
|
||||
}
|
||||
@@ -153,12 +154,14 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
|
||||
|
||||
memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
|
||||
if block := (^Platform_Memory_Block)(block_to_free); block != nil {
|
||||
platform_mutex_lock()
|
||||
block.prev.next = block.next
|
||||
block.next.prev = block.prev
|
||||
platform_mutex_unlock()
|
||||
|
||||
platform_memory_free(block)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
|
||||
z, did_overflow := intrinsics.overflow_add(x, y)
|
||||
return z, !did_overflow
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
//+private
|
||||
package mem_virtual
|
||||
|
||||
import "core:sync"
|
||||
|
||||
Platform_Memory_Block :: struct {
|
||||
block: Memory_Block,
|
||||
committed: uint,
|
||||
reserved: uint,
|
||||
prev, next: ^Platform_Memory_Block,
|
||||
}
|
||||
|
||||
platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
|
||||
@@ -33,28 +30,6 @@ platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
|
||||
}
|
||||
}
|
||||
|
||||
platform_mutex_lock :: proc() {
|
||||
sync.mutex_lock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
platform_mutex_unlock :: proc() {
|
||||
sync.mutex_unlock(&global_memory_block_mutex)
|
||||
}
|
||||
|
||||
global_memory_block_mutex: sync.Mutex
|
||||
global_platform_memory_block_sentinel: Platform_Memory_Block
|
||||
global_platform_memory_block_sentinel_set: bool
|
||||
|
||||
@(init)
|
||||
platform_memory_init :: proc() {
|
||||
if !global_platform_memory_block_sentinel_set {
|
||||
_platform_memory_init()
|
||||
global_platform_memory_block_sentinel.prev = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel.next = &global_platform_memory_block_sentinel
|
||||
global_platform_memory_block_sentinel_set = true
|
||||
}
|
||||
}
|
||||
|
||||
platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
|
||||
if to_commit < block.committed {
|
||||
return nil
|
||||
@@ -63,7 +38,6 @@ platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
|
||||
|
||||
commit(block, to_commit) or_return
|
||||
block.committed = to_commit
|
||||
return nil
|
||||
|
||||
@@ -2,7 +2,6 @@ package os
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
|
||||
@@ -41,9 +40,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
// fi.mode |= file_type_mode(h);
|
||||
}
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
return
|
||||
|
||||
@@ -162,7 +162,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
total_read: int
|
||||
length := len(data)
|
||||
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
// NOTE(Jeroen): `length` can't be casted to win32.DWORD here because it'll overflow if > 4 GiB and return 0 if exactly that.
|
||||
to_read := min(i64(length), MAX_RW)
|
||||
|
||||
e: win32.BOOL
|
||||
if is_console {
|
||||
@@ -172,7 +173,8 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return int(total_read), err
|
||||
}
|
||||
} else {
|
||||
e = win32.ReadFile(handle, &data[total_read], to_read, &single_read_length, nil)
|
||||
// NOTE(Jeroen): So we cast it here *after* we've ensured that `to_read` is at most MAX_RW (1 GiB)
|
||||
e = win32.ReadFile(handle, &data[total_read], win32.DWORD(to_read), &single_read_length, nil)
|
||||
}
|
||||
if single_read_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
|
||||
+5
-5
@@ -178,7 +178,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
@@ -187,7 +187,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -226,8 +226,8 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, nil, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -244,7 +244,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
case .Query_Features:
|
||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.fd = rawptr(handle)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment)
|
||||
|
||||
case .Free:
|
||||
|
||||
@@ -4,17 +4,17 @@ package os2
|
||||
import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory: bool) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int, zero_memory: bool) -> rawptr {
|
||||
if new_size == 0 {
|
||||
heap_free(ptr)
|
||||
return nil
|
||||
}
|
||||
if ptr == nil {
|
||||
return heap_alloc(new_size)
|
||||
return heap_alloc(new_size, zero_memory)
|
||||
}
|
||||
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, uint(new_size))
|
||||
@@ -36,16 +36,16 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
// the pointer we return to the user.
|
||||
//
|
||||
|
||||
aligned_alloc :: proc(size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
aligned_alloc :: proc(size, alignment: int, zero_memory: bool, old_ptr: rawptr = nil) -> ([]byte, mem.Allocator_Error) {
|
||||
a := max(alignment, align_of(rawptr))
|
||||
space := size + a - 1
|
||||
|
||||
allocated_mem: rawptr
|
||||
if old_ptr != nil {
|
||||
original_old_ptr := mem.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr), zero_memory)
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(mem.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -72,12 +72,12 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
if p == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return aligned_alloc(new_size, new_alignment, p)
|
||||
return aligned_alloc(new_size, new_alignment, true, p)
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
return aligned_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return aligned_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
aligned_free(old_memory)
|
||||
@@ -87,7 +87,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
|
||||
case .Resize:
|
||||
if old_memory == nil {
|
||||
return aligned_alloc(size, alignment)
|
||||
return aligned_alloc(size, alignment, true)
|
||||
}
|
||||
return aligned_resize(old_memory, old_size, size, alignment)
|
||||
|
||||
|
||||
+12
-3
@@ -404,6 +404,9 @@ read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
if bytes_read == -1 {
|
||||
return bytes_read_total, 1
|
||||
}
|
||||
if bytes_read == 0 {
|
||||
break
|
||||
}
|
||||
bytes_read_total += bytes_read
|
||||
}
|
||||
|
||||
@@ -643,9 +646,15 @@ access :: proc(path: string, mask: int) -> bool {
|
||||
return _unix_access(cstr, mask) == 0
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0)
|
||||
return _unix_calloc(1, size)
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, size)
|
||||
} else {
|
||||
return _unix_malloc(size)
|
||||
}
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
// NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
|
||||
|
||||
@@ -18,8 +18,8 @@ current_thread_id :: proc "contextless" () -> int {
|
||||
return (int) (es.ThreadGetID(es.CURRENT_THREAD));
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return es.HeapAllocate(size, false);
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return es.HeapAllocate(size, zero_memory);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
|
||||
@@ -603,9 +603,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
@@ -755,9 +755,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
@@ -605,9 +605,15 @@ access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
return true, ERROR_NONE
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size >= 0)
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
if size <= 0 {
|
||||
return nil
|
||||
}
|
||||
if zero_memory {
|
||||
return _unix_calloc(1, c.size_t(size))
|
||||
} else {
|
||||
return _unix_malloc(c.size_t(size))
|
||||
}
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
+33
-4
@@ -24,7 +24,7 @@ O_CLOEXEC :: 0x80000
|
||||
stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
current_dir: Handle = 3
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
@@ -47,7 +47,36 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
return int(n), Errno(err)
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
|
||||
return 0, -1
|
||||
oflags: wasi.oflags_t
|
||||
if mode & O_CREATE == O_CREATE {
|
||||
oflags += {.CREATE}
|
||||
}
|
||||
if mode & O_EXCL == O_EXCL {
|
||||
oflags += {.EXCL}
|
||||
}
|
||||
if mode & O_TRUNC == O_TRUNC {
|
||||
oflags += {.TRUNC}
|
||||
}
|
||||
|
||||
rights: wasi.rights_t = {.FD_SEEK, .FD_FILESTAT_GET}
|
||||
switch mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: rights += {.FD_READ}
|
||||
case O_WRONLY: rights += {.FD_WRITE}
|
||||
case O_RDWR: rights += {.FD_READ, .FD_WRITE}
|
||||
}
|
||||
|
||||
fdflags: wasi.fdflags_t
|
||||
if mode & O_APPEND == O_APPEND {
|
||||
fdflags += {.APPEND}
|
||||
}
|
||||
if mode & O_NONBLOCK == O_NONBLOCK {
|
||||
fdflags += {.NONBLOCK}
|
||||
}
|
||||
if mode & O_SYNC == O_SYNC {
|
||||
fdflags += {.SYNC}
|
||||
}
|
||||
fd, err := wasi.path_open(wasi.fd_t(current_dir),{.SYMLINK_FOLLOW},path,oflags,rights,{},fdflags)
|
||||
return Handle(fd), Errno(err)
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
err := wasi.fd_close(wasi.fd_t(fd))
|
||||
@@ -72,7 +101,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return nil
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -96,4 +125,4 @@ heap_free :: proc(ptr: rawptr) {
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
runtime._cleanup_runtime_contextless()
|
||||
wasi.proc_exit(wasi.exitcode_t(code))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, uint(size))
|
||||
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
|
||||
+10
-10
@@ -228,6 +228,13 @@ file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HAN
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
}
|
||||
|
||||
@(private)
|
||||
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
@@ -235,9 +242,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
|
||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.name = basename(fi.fullpath)
|
||||
@@ -252,9 +257,7 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
|
||||
fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
windows_set_file_info_times(&fi, d)
|
||||
|
||||
fi.fullpath, e = full_path_from_name(name)
|
||||
fi.name = basename(fi.fullpath)
|
||||
@@ -290,10 +293,7 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
|
||||
fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag)
|
||||
fi.is_dir = fi.mode & File_Mode_Dir != 0
|
||||
|
||||
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
|
||||
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
|
||||
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
|
||||
|
||||
windows_set_file_info_times(&fi, &d)
|
||||
|
||||
return fi, ERROR_NONE
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import "core:slice"
|
||||
// The sole exception is if 'skip_dir' is returned as true:
|
||||
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
|
||||
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
|
||||
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool)
|
||||
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr) -> (err: os.Errno, skip_dir: bool)
|
||||
|
||||
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
|
||||
// All errors that happen visiting files and directories are filtered by walk_proc
|
||||
@@ -22,28 +22,28 @@ Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno,
|
||||
// NOTE: Walking large directories can be inefficient due to the lexical sort
|
||||
// NOTE: walk does not follow symbolic links
|
||||
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
|
||||
walk :: proc(root: string, walk_proc: Walk_Proc) -> os.Errno {
|
||||
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Errno {
|
||||
info, err := os.lstat(root, context.temp_allocator)
|
||||
defer os.file_info_delete(info, context.temp_allocator)
|
||||
|
||||
skip_dir: bool
|
||||
if err != 0 {
|
||||
err, skip_dir = walk_proc(info, err)
|
||||
err, skip_dir = walk_proc(info, err, user_data)
|
||||
} else {
|
||||
err, skip_dir = _walk(info, walk_proc)
|
||||
err, skip_dir = _walk(info, walk_proc, user_data)
|
||||
}
|
||||
return 0 if skip_dir else err
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_dir: bool) {
|
||||
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) {
|
||||
if !info.is_dir {
|
||||
if info.fullpath == "" && info.name == "" {
|
||||
// ignore empty things
|
||||
return
|
||||
}
|
||||
return walk_proc(info, 0)
|
||||
return walk_proc(info, 0, user_data)
|
||||
}
|
||||
|
||||
fis: []os.File_Info
|
||||
@@ -51,14 +51,14 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
|
||||
fis, err = read_dir(info.fullpath, context.temp_allocator)
|
||||
defer os.file_info_slice_delete(fis, context.temp_allocator)
|
||||
|
||||
err1, skip_dir = walk_proc(info, err)
|
||||
err1, skip_dir = walk_proc(info, err, user_data)
|
||||
if err != 0 || err1 != 0 || skip_dir {
|
||||
err = err1
|
||||
return
|
||||
}
|
||||
|
||||
for fi in fis {
|
||||
err, skip_dir = _walk(fi, walk_proc)
|
||||
err, skip_dir = _walk(fi, walk_proc, user_data)
|
||||
if err != 0 || skip_dir {
|
||||
if !fi.is_dir || !skip_dir {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
return
|
||||
}
|
||||
|
||||
ti := type_info_base(type_info_of(val.id))
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
if ptr := (^rawptr)(val.data)^; ptr != nil {
|
||||
return iterate_array(any{ptr, info.elem.id}, it)
|
||||
}
|
||||
case Type_Info_Array:
|
||||
if it^ < info.count {
|
||||
elem.data = rawptr(uintptr(val.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Slice:
|
||||
array := (^runtime.Raw_Slice)(val.data)
|
||||
if it^ < array.len {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
case Type_Info_Dynamic_Array:
|
||||
array := (^runtime.Raw_Dynamic_Array)(val.data)
|
||||
if it^ < array.len {
|
||||
elem.data = rawptr(uintptr(array.data) + uintptr(it^ * info.elem_size))
|
||||
elem.id = info.elem.id
|
||||
ok = true
|
||||
it^ += 1
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
|
||||
if val == nil || it == nil {
|
||||
return
|
||||
}
|
||||
ti := type_info_base(type_info_of(val.id))
|
||||
#partial switch info in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
if ptr := (^rawptr)(val.data)^; ptr != nil {
|
||||
return iterate_map(any{ptr, info.elem.id}, it)
|
||||
}
|
||||
case Type_Info_Map:
|
||||
if info.map_info == nil {
|
||||
break
|
||||
}
|
||||
rm := (^runtime.Raw_Map)(val.data)
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
|
||||
for /**/ ; it^ < int(runtime.map_cap(rm^)); it^ += 1 {
|
||||
if hash := hs[it^]; runtime.map_hash_is_valid(hash) {
|
||||
key_ptr := runtime.map_cell_index_dynamic(ks, info.map_info.ks, uintptr(it^))
|
||||
value_ptr := runtime.map_cell_index_dynamic(vs, info.map_info.vs, uintptr(it^))
|
||||
|
||||
key.data = rawptr(key_ptr)
|
||||
value.data = rawptr(value_ptr)
|
||||
key.id = info.key.id
|
||||
value.id = info.value.id
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
package reflect
|
||||
|
||||
import "core:runtime"
|
||||
import "core:mem"
|
||||
_ :: runtime
|
||||
_ :: mem
|
||||
|
||||
Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
hash: uintptr,
|
||||
key: Key,
|
||||
value: Value,
|
||||
}
|
||||
|
||||
map_entry_info_slice :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
|
||||
m := m
|
||||
rm := (^mem.Raw_Map)(&m)
|
||||
|
||||
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
|
||||
gs := type_info_base(info.generated_struct).variant.(Type_Info_Struct)
|
||||
ed := type_info_base(gs.types[1]).variant.(Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(Type_Info_Struct)
|
||||
key_offset := entry_type.offsets[2]
|
||||
value_offset := entry_type.offsets[3]
|
||||
entry_size := uintptr(ed.elem_size)
|
||||
|
||||
entries = make(type_of(entries), rm.entries.len)
|
||||
|
||||
data := uintptr(rm.entries.data)
|
||||
for i in 0..<rm.entries.len {
|
||||
header := (^runtime.Map_Entry_Header)(data)
|
||||
|
||||
hash := header.hash
|
||||
key := (^K)(data + key_offset)^
|
||||
value := (^V)(data + value_offset)^
|
||||
|
||||
entries[i] = {hash, key, value}
|
||||
|
||||
data += entry_size
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
@@ -273,7 +273,7 @@ length :: proc(val: any) -> int {
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).len
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^runtime.Raw_Map)(val.data).entries.len
|
||||
return runtime.map_len((^runtime.Raw_Map)(val.data)^)
|
||||
|
||||
case Type_Info_String:
|
||||
if a.is_cstring {
|
||||
@@ -305,7 +305,7 @@ capacity :: proc(val: any) -> int {
|
||||
return (^runtime.Raw_Dynamic_Array)(val.data).cap
|
||||
|
||||
case Type_Info_Map:
|
||||
return (^runtime.Raw_Map)(val.data).entries.cap
|
||||
return runtime.map_cap((^runtime.Raw_Map)(val.data)^)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
+29
-7
@@ -143,11 +143,9 @@ Type_Info_Enum :: struct {
|
||||
values: []Type_Info_Enum_Value,
|
||||
}
|
||||
Type_Info_Map :: struct {
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
generated_struct: ^Type_Info,
|
||||
key_equal: Equal_Proc,
|
||||
key_hasher: Hasher_Proc,
|
||||
key: ^Type_Info,
|
||||
value: ^Type_Info,
|
||||
map_info: ^Map_Info,
|
||||
}
|
||||
Type_Info_Bit_Set :: struct {
|
||||
elem: ^Type_Info,
|
||||
@@ -303,6 +301,7 @@ Allocator_Mode :: enum byte {
|
||||
Resize,
|
||||
Query_Features,
|
||||
Query_Info,
|
||||
Alloc_Non_Zeroed,
|
||||
}
|
||||
|
||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||
@@ -393,9 +392,32 @@ Raw_Dynamic_Array :: struct {
|
||||
allocator: Allocator,
|
||||
}
|
||||
|
||||
// The raw, type-erased representation of a map.
|
||||
//
|
||||
// 32-bytes on 64-bit
|
||||
// 16-bytes on 32-bit
|
||||
Raw_Map :: struct {
|
||||
hashes: []Map_Index,
|
||||
entries: Raw_Dynamic_Array,
|
||||
// A single allocation spanning all keys, values, and hashes.
|
||||
// {
|
||||
// k: Map_Cell(K) * (capacity / ks_per_cell)
|
||||
// v: Map_Cell(V) * (capacity / vs_per_cell)
|
||||
// h: Map_Cell(H) * (capacity / hs_per_cell)
|
||||
// }
|
||||
//
|
||||
// The data is allocated assuming 64-byte alignment, meaning the address is
|
||||
// always a multiple of 64. This means we have 6 bits of zeros in the pointer
|
||||
// to store the capacity. We can store a value as large as 2^6-1 or 63 in
|
||||
// there. This conveniently is the maximum log2 capacity we can have for a map
|
||||
// as Odin uses signed integers to represent capacity.
|
||||
//
|
||||
// Since the hashes are backed by Map_Hash, which is just a 64-bit unsigned
|
||||
// integer, the cell structure for hashes is unnecessary because 64/8 is 8 and
|
||||
// requires no padding, meaning it can be indexed as a regular array of
|
||||
// Map_Hash directly, though for consistency sake it's written as if it were
|
||||
// an array of Map_Cell(Map_Hash).
|
||||
data: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
|
||||
len: int, // 8-bytes on 64-bits, 4-bytes on 32-bits
|
||||
allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
|
||||
}
|
||||
|
||||
Raw_Any :: struct {
|
||||
|
||||
@@ -159,20 +159,7 @@ delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #cal
|
||||
}
|
||||
@builtin
|
||||
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
|
||||
Entry :: struct {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: K,
|
||||
value: V,
|
||||
}
|
||||
|
||||
raw := transmute(Raw_Map)m
|
||||
err := delete_slice(raw.hashes, raw.entries.allocator, loc)
|
||||
err1 := mem_free_with_size(raw.entries.data, raw.entries.cap*size_of(Entry), raw.entries.allocator, loc)
|
||||
if err == nil {
|
||||
err = err1
|
||||
}
|
||||
return err
|
||||
return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
|
||||
}
|
||||
|
||||
|
||||
@@ -244,12 +231,12 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
|
||||
return
|
||||
}
|
||||
@(builtin)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = DEFAULT_RESERVE_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map_expr_error_loc(loc, cap)
|
||||
make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> T {
|
||||
make_map_expr_error_loc(loc, capacity)
|
||||
context.allocator = allocator
|
||||
|
||||
m: T
|
||||
reserve_map(&m, cap)
|
||||
reserve_map(&m, capacity, loc)
|
||||
return m
|
||||
}
|
||||
@(builtin)
|
||||
@@ -285,36 +272,24 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
raw_map := (^Raw_Map)(m)
|
||||
entries := (^Raw_Dynamic_Array)(&raw_map.entries)
|
||||
entries.len = 0
|
||||
for _, i in raw_map.hashes {
|
||||
raw_map.hashes[i] = MAP_SENTINEL
|
||||
}
|
||||
map_clear_dynamic((^Raw_Map)(m), map_info(T))
|
||||
}
|
||||
|
||||
@builtin
|
||||
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
|
||||
if m != nil {
|
||||
h := __get_map_header_table(T)
|
||||
__dynamic_map_reserve(m, h, uint(capacity), loc)
|
||||
__dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Shrinks the capacity of a map down to the current length, or the given capacity.
|
||||
|
||||
If `new_cap` is negative, then `len(m)` is used.
|
||||
|
||||
Returns false if `cap(m) < new_cap`, or the allocator report failure.
|
||||
|
||||
If `len(m) < new_cap`, then `len(m)` will be left unchanged.
|
||||
Shrinks the capacity of a map down to the current length.
|
||||
*/
|
||||
@builtin
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
|
||||
shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
|
||||
if m != nil {
|
||||
new_cap := new_cap if new_cap >= 0 else len(m)
|
||||
return __dynamic_map_shrink(__get_map_header(m), new_cap, loc)
|
||||
err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
|
||||
did_shrink = err == nil
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -325,14 +300,10 @@ shrink_map :: proc(m: ^$T/map[$K]$V, new_cap := -1, loc := #caller_location) ->
|
||||
delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
|
||||
if m != nil {
|
||||
key := key
|
||||
h := __get_map_header(m)
|
||||
fr := __map_find(h, &key)
|
||||
if fr.entry_index != MAP_SENTINEL {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index)
|
||||
deleted_key = (^K)(uintptr(entry)+h.key_offset)^
|
||||
deleted_value = (^V)(uintptr(entry)+h.value_offset)^
|
||||
|
||||
__dynamic_map_erase(h, fr)
|
||||
old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
|
||||
if ok {
|
||||
deleted_key = (^K)(old_k)^
|
||||
deleted_value = (^V)(old_v)^
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -573,10 +544,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
||||
new_size := capacity * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -607,10 +575,7 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
||||
new_size := length * size_of(E)
|
||||
allocator := a.allocator
|
||||
|
||||
new_data, err := allocator.procedure(
|
||||
allocator.data, .Resize, new_size, align_of(E),
|
||||
a.data, old_size, loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if new_data == nil || err != nil {
|
||||
return false
|
||||
}
|
||||
@@ -650,15 +615,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
old_size := a.cap * size_of(E)
|
||||
new_size := new_cap * size_of(E)
|
||||
|
||||
new_data, err := a.allocator.procedure(
|
||||
a.allocator.data,
|
||||
.Resize,
|
||||
new_size,
|
||||
align_of(E),
|
||||
a.data,
|
||||
old_size,
|
||||
loc,
|
||||
)
|
||||
new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -672,10 +629,7 @@ shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #call
|
||||
@builtin
|
||||
map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
|
||||
key, value := key, value
|
||||
h := __get_map_header_table(T)
|
||||
|
||||
e := __dynamic_map_set(m, h, __get_map_key_hash(&key), &key, &value, loc)
|
||||
return (^V)(uintptr(e) + h.value_offset)
|
||||
return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
|
||||
}
|
||||
|
||||
|
||||
@@ -731,34 +685,6 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int {
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
raw_array_data :: proc "contextless" (a: $P/^($T/[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_simd_data :: proc "contextless" (a: $P/^($T/#simd[$N]$E)) -> [^]E {
|
||||
return ([^]E)(a)
|
||||
}
|
||||
@builtin
|
||||
raw_slice_data :: proc "contextless" (s: $S/[]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Slice)s).data
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_dynamic_array_data :: proc "contextless" (s: $S/[dynamic]$E) -> [^]E {
|
||||
ptr := (transmute(Raw_Dynamic_Array)s).data
|
||||
return ([^]E)(ptr)
|
||||
}
|
||||
@builtin
|
||||
raw_string_data :: proc "contextless" (s: $S/string) -> [^]u8 {
|
||||
return (transmute(Raw_String)s).data
|
||||
}
|
||||
|
||||
@builtin
|
||||
raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_string_data, raw_simd_data}
|
||||
|
||||
|
||||
|
||||
@builtin
|
||||
@(disabled=ODIN_DISABLE_ASSERT)
|
||||
assert :: proc(condition: bool, message := "", loc := #caller_location) {
|
||||
|
||||
@@ -4,7 +4,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
return nil, .Out_Of_Memory
|
||||
case .Free:
|
||||
return nil, .None
|
||||
|
||||
@@ -10,8 +10,8 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
data, err = _windows_default_alloc(size, alignment)
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data, err = _windows_default_alloc(size, alignment, mode == .Alloc)
|
||||
|
||||
case .Free:
|
||||
_windows_default_free(old_memory)
|
||||
@@ -25,7 +25,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
|
||||
@@ -167,7 +167,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
}
|
||||
|
||||
switch mode {
|
||||
case .Alloc:
|
||||
case .Alloc, .Alloc_Non_Zeroed:
|
||||
data, err = default_temp_allocator_alloc(s, size, alignment, loc)
|
||||
case .Free:
|
||||
err = default_temp_allocator_free(s, old_memory, loc)
|
||||
@@ -181,7 +181,7 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
|
||||
case .Query_Features:
|
||||
set := (^Allocator_Mode_Set)(old_memory)
|
||||
if set != nil {
|
||||
set^ = {.Alloc, .Free, .Free_All, .Resize, .Query_Features}
|
||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
||||
}
|
||||
|
||||
case .Query_Info:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -145,6 +145,13 @@ mem_alloc :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, a
|
||||
return allocator.procedure(allocator.data, .Alloc, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_alloc_non_zeroed :: #force_inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 || allocator.procedure == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, size, alignment, nil, 0, loc)
|
||||
}
|
||||
|
||||
mem_free :: #force_inline proc(ptr: rawptr, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
|
||||
if ptr == nil || allocator.procedure == nil {
|
||||
return nil
|
||||
|
||||
@@ -58,9 +58,9 @@ _os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) #no_b
|
||||
return
|
||||
}
|
||||
|
||||
heap_alloc :: proc "contextless" (size: int) -> rawptr {
|
||||
heap_alloc :: proc "contextless" (size: int, zero_memory := true) -> rawptr {
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, uint(size))
|
||||
return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY if zero_memory else 0, uint(size))
|
||||
}
|
||||
heap_resize :: proc "contextless" (ptr: rawptr, new_size: int) -> rawptr {
|
||||
if new_size == 0 {
|
||||
@@ -91,7 +91,7 @@ heap_free :: proc "contextless" (ptr: rawptr) {
|
||||
|
||||
|
||||
|
||||
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
|
||||
_windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||
if size == 0 {
|
||||
_windows_default_free(old_ptr)
|
||||
return nil, nil
|
||||
@@ -105,7 +105,7 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^
|
||||
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
|
||||
} else {
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr))
|
||||
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
|
||||
}
|
||||
aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr)))
|
||||
|
||||
@@ -122,8 +122,8 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
return byte_slice(aligned_mem, size), nil
|
||||
}
|
||||
|
||||
_windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
|
||||
return _windows_default_alloc_or_resize(size, alignment, nil)
|
||||
_windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
|
||||
return _windows_default_alloc_or_resize(size, alignment, nil, zero_memory)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ objc_SEL :: ^intrinsics.objc_selector
|
||||
foreign Foundation {
|
||||
objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
|
||||
sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
|
||||
objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) -> objc_Class ---
|
||||
|
||||
objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
|
||||
objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
|
||||
|
||||
+22
-26
@@ -6,8 +6,8 @@ import "core:runtime"
|
||||
_ :: intrinsics
|
||||
_ :: runtime
|
||||
|
||||
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K) {
|
||||
keys = make(type_of(keys), len(m), allocator)
|
||||
map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K, err: runtime.Allocator_Error) {
|
||||
keys = make(type_of(keys), len(m), allocator) or_return
|
||||
i := 0
|
||||
for key in m {
|
||||
keys[i] = key
|
||||
@@ -15,8 +15,8 @@ map_keys :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (keys: []K)
|
||||
}
|
||||
return
|
||||
}
|
||||
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V) {
|
||||
values = make(type_of(values), len(m), allocator)
|
||||
map_values :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (values: []V, err: runtime.Allocator_Error) {
|
||||
values = make(type_of(values), len(m), allocator) or_return
|
||||
i := 0
|
||||
for _, value in m {
|
||||
values[i] = value
|
||||
@@ -37,8 +37,8 @@ Map_Entry_Info :: struct($Key, $Value: typeid) {
|
||||
}
|
||||
|
||||
|
||||
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V)) {
|
||||
entries = make(type_of(entries), len(m), allocator)
|
||||
map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry(K, V), err: runtime.Allocator) {
|
||||
entries = make(type_of(entries), len(m), allocator) or_return
|
||||
i := 0
|
||||
for key, value in m {
|
||||
entries[i].key = key
|
||||
@@ -52,28 +52,24 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
|
||||
m := m
|
||||
rm := (^runtime.Raw_Map)(&m)
|
||||
|
||||
info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
|
||||
gs := runtime.type_info_base(info.generated_struct).variant.(runtime.Type_Info_Struct)
|
||||
ed := runtime.type_info_base(gs.types[1]).variant.(runtime.Type_Info_Dynamic_Array)
|
||||
entry_type := ed.elem.variant.(runtime.Type_Info_Struct)
|
||||
key_offset := entry_type.offsets[2]
|
||||
value_offset := entry_type.offsets[3]
|
||||
entry_size := uintptr(ed.elem_size)
|
||||
info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
|
||||
if info.map_info != nil {
|
||||
entries = make(type_of(entries), len(m), allocator) or_return
|
||||
|
||||
entries = make(type_of(entries), rm.entries.len)
|
||||
map_cap := uintptr(cap(m))
|
||||
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(rm^, info.map_info)
|
||||
entry_index := 0
|
||||
for bucket_index in 0..<map_cap {
|
||||
if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
|
||||
key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
|
||||
value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
|
||||
entries[entry_index].hash = hash
|
||||
entries[entry_index].key = (^K)(key)^
|
||||
entries[entry_index].value = (^V)(value)^
|
||||
|
||||
data := uintptr(rm.entries.data)
|
||||
for i in 0..<rm.entries.len {
|
||||
header := (^runtime.Map_Entry_Header)(data)
|
||||
|
||||
hash := header.hash
|
||||
key := (^K)(data + key_offset)^
|
||||
value := (^V)(data + value_offset)^
|
||||
|
||||
entries[i] = {hash, key, value}
|
||||
|
||||
data += entry_size
|
||||
entry_index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -177,7 +177,6 @@ _quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND
|
||||
}
|
||||
|
||||
|
||||
// merge sort
|
||||
_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
|
||||
less :: #force_inline proc(a, b: E, call: P) -> bool {
|
||||
when KIND == .Ordered {
|
||||
@@ -190,7 +189,9 @@ _stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (O
|
||||
#panic("unhandled Sort_Kind")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// insertion sort
|
||||
// TODO(bill): use a different algorithm as insertion sort is O(n^2)
|
||||
n := len(data)
|
||||
for i in 1..<n {
|
||||
for j := i; j > 0 && less(data[j], data[j-1], call); j -= 1 {
|
||||
|
||||
@@ -9,6 +9,89 @@ Decimal :: struct {
|
||||
neg, trunc: bool,
|
||||
}
|
||||
|
||||
set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
|
||||
d^ = {}
|
||||
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; d.neg = true
|
||||
}
|
||||
|
||||
// digits
|
||||
saw_dot := false
|
||||
saw_digits := false
|
||||
for ; i < len(s); i += 1 {
|
||||
switch {
|
||||
case s[i] == '_':
|
||||
// ignore underscores
|
||||
continue
|
||||
case s[i] == '.':
|
||||
if saw_dot {
|
||||
return
|
||||
}
|
||||
saw_dot = true
|
||||
d.decimal_point = d.count
|
||||
continue
|
||||
|
||||
case '0' <= s[i] && s[i] <= '9':
|
||||
saw_digits = true
|
||||
if s[i] == '0' && d.count == 0 {
|
||||
d.decimal_point -= 1
|
||||
continue
|
||||
}
|
||||
if d.count < len(d.digits) {
|
||||
d.digits[d.count] = s[i]
|
||||
d.count += 1
|
||||
} else if s[i] != '0' {
|
||||
d.trunc = true
|
||||
}
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
if !saw_digits {
|
||||
return
|
||||
}
|
||||
if !saw_dot {
|
||||
d.decimal_point = d.count
|
||||
}
|
||||
|
||||
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
|
||||
|
||||
if i < len(s) && lower(s[i]) == 'e' {
|
||||
i += 1
|
||||
if i >= len(s) {
|
||||
return
|
||||
}
|
||||
exp_sign := 1
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; exp_sign = -1
|
||||
}
|
||||
if i >= len(s) || s[i] < '0' || s[i] > '9' {
|
||||
return
|
||||
}
|
||||
e := 0
|
||||
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
|
||||
if s[i] == '_' {
|
||||
// ignore underscores
|
||||
continue
|
||||
}
|
||||
if e < 1e4 {
|
||||
e = e*10 + int(s[i]) - '0'
|
||||
}
|
||||
}
|
||||
d.decimal_point += e * exp_sign
|
||||
}
|
||||
|
||||
return i == len(s)
|
||||
}
|
||||
|
||||
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
digit_zero :: proc(buf: []byte) -> int {
|
||||
for _, i in buf {
|
||||
|
||||
@@ -284,3 +284,89 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@(private)
|
||||
decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) {
|
||||
end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (b: u64) {
|
||||
bits := mant & (u64(1)<<info.mantbits - 1)
|
||||
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
|
||||
if d.neg {
|
||||
bits |= 1<< info.mantbits << info.expbits
|
||||
}
|
||||
return bits
|
||||
}
|
||||
set_overflow :: proc "contextless" (mant: ^u64, exp: ^int, info: ^Float_Info) -> bool {
|
||||
mant^ = 0
|
||||
exp^ = 1<<info.expbits - 1 + info.bias
|
||||
return true
|
||||
}
|
||||
|
||||
mant: u64
|
||||
exp: int
|
||||
if d.decimal_point == 0 {
|
||||
mant = 0
|
||||
exp = info.bias
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
if d.decimal_point > 310 {
|
||||
set_overflow(&mant, &exp, info)
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
} else if d.decimal_point < -330 {
|
||||
mant = 0
|
||||
exp = info.bias
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
@static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
|
||||
|
||||
exp = 0
|
||||
for d.decimal_point > 0 {
|
||||
n := 27 if d.decimal_point >= len(power_table) else power_table[d.decimal_point]
|
||||
decimal.shift(d, n)
|
||||
exp += n
|
||||
}
|
||||
for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
|
||||
n := 27 if -d.decimal_point >= len(power_table) else power_table[-d.decimal_point]
|
||||
decimal.shift(d, n)
|
||||
exp -= n
|
||||
}
|
||||
|
||||
// go from [0.5, 1) to [1, 2)
|
||||
exp -= 1
|
||||
|
||||
if exp < info.bias + 1 {
|
||||
n := info.bias + 1 - exp
|
||||
decimal.shift(d, n)
|
||||
exp += n
|
||||
}
|
||||
|
||||
if (exp-info.bias) >= (1<<info.expbits - 1) {
|
||||
set_overflow(&mant, &exp, info)
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
decimal.shift(d, int(1 + info.mantbits))
|
||||
mant = decimal.rounded_integer(d)
|
||||
|
||||
if mant == 2<<info.mantbits {
|
||||
mant >>= 1
|
||||
exp += 1
|
||||
if (exp-info.bias) >= (1<<info.expbits - 1) {
|
||||
set_overflow(&mant, &exp, info)
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if mant & (1<<info.mantbits) == 0 {
|
||||
exp = info.bias
|
||||
}
|
||||
|
||||
b = end(d, mant, exp, info)
|
||||
return
|
||||
}
|
||||
|
||||
+265
-104
@@ -1,6 +1,7 @@
|
||||
package strconv
|
||||
|
||||
import "core:unicode/utf8"
|
||||
import "decimal"
|
||||
|
||||
parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
|
||||
switch s {
|
||||
@@ -532,6 +533,8 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o
|
||||
parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
|
||||
|
||||
|
||||
@(private)
|
||||
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
|
||||
|
||||
|
||||
|
||||
@@ -566,126 +569,284 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
|
||||
// assert(n == 12.34 && ok);
|
||||
// ```
|
||||
parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
|
||||
s := str
|
||||
defer if n != nil { n^ = len(str) - len(s) }
|
||||
if s == "" {
|
||||
common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int {
|
||||
n := len(prefix)
|
||||
if n > len(s) {
|
||||
n = len(s)
|
||||
}
|
||||
for i in 0..<n {
|
||||
c := s[i]
|
||||
if 'A' <= c && c <= 'Z' {
|
||||
c += 'a' - 'A'
|
||||
}
|
||||
if c != prefix[i] {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
check_special :: proc "contextless" (s: string) -> (f: f64, n: int, ok: bool) {
|
||||
s := s
|
||||
if len(s) > 0 {
|
||||
sign := 1
|
||||
nsign := 0
|
||||
switch s[0] {
|
||||
case '+', '-':
|
||||
if s[0] == '-' {
|
||||
sign = -1
|
||||
}
|
||||
nsign = 1
|
||||
s = s[1:]
|
||||
fallthrough
|
||||
case 'i', 'I':
|
||||
n := common_prefix_len_ignore_case(s, "infinity")
|
||||
if 3 < n && n < 8 { // "inf" or "infinity"
|
||||
n = 3
|
||||
}
|
||||
if n == 3 || n == 8 {
|
||||
f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
|
||||
n = nsign + 3
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
case 'n', 'N':
|
||||
if common_prefix_len_ignore_case(s, "nan") == 3 {
|
||||
f = 0h7ff80000_00000001
|
||||
n = nsign + 3
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
parse_components :: proc "contextless" (s: string) -> (mantissa: u64, exp: int, neg, trunc, hex: bool, i: int, ok: bool) {
|
||||
if len(s) == 0 {
|
||||
return
|
||||
}
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; neg = true
|
||||
}
|
||||
|
||||
base := u64(10)
|
||||
MAX_MANT_DIGITS := 19
|
||||
exp_char := byte('e')
|
||||
// support stupid 0x1.ABp100 hex floats even if Odin doesn't
|
||||
if i+2 < len(s) && s[i] == '0' && lower(s[i+1]) == 'x' {
|
||||
base = 16
|
||||
MAX_MANT_DIGITS = 16
|
||||
i += 2
|
||||
exp_char = 'p'
|
||||
hex = true
|
||||
}
|
||||
|
||||
underscores := false
|
||||
saw_dot, saw_digits := false, false
|
||||
nd := 0
|
||||
nd_mant := 0
|
||||
decimal_point := 0
|
||||
loop: for ; i < len(s); i += 1 {
|
||||
switch c := s[i]; true {
|
||||
case c == '_':
|
||||
underscores = true
|
||||
continue loop
|
||||
case c == '.':
|
||||
if saw_dot {
|
||||
break loop
|
||||
}
|
||||
saw_dot = true
|
||||
decimal_point = nd
|
||||
continue loop
|
||||
|
||||
case '0' <= c && c <= '9':
|
||||
saw_digits = true
|
||||
if c == '0' && nd == 0 {
|
||||
decimal_point -= 1
|
||||
continue loop
|
||||
}
|
||||
nd += 1
|
||||
if nd_mant < MAX_MANT_DIGITS {
|
||||
mantissa *= base
|
||||
mantissa += u64(c - '0')
|
||||
nd_mant += 1
|
||||
} else if c != '0' {
|
||||
trunc = true
|
||||
}
|
||||
continue loop
|
||||
case base == 16 && 'a' <= lower(c) && lower(c) <= 'f':
|
||||
saw_digits = true
|
||||
nd += 1
|
||||
if nd_mant < MAX_MANT_DIGITS {
|
||||
MAX_MANT_DIGITS *= 16
|
||||
MAX_MANT_DIGITS += int(lower(c) - 'a' + 10)
|
||||
nd_mant += 1
|
||||
} else {
|
||||
trunc = true
|
||||
}
|
||||
continue loop
|
||||
}
|
||||
break loop
|
||||
}
|
||||
|
||||
if !saw_digits {
|
||||
return
|
||||
}
|
||||
if !saw_dot {
|
||||
decimal_point = nd
|
||||
}
|
||||
if base == 16 {
|
||||
decimal_point *= 4
|
||||
nd_mant *= 4
|
||||
}
|
||||
|
||||
if i < len(s) && lower(s[i]) == exp_char {
|
||||
i += 1
|
||||
if i >= len(s) { return }
|
||||
exp_sign := 1
|
||||
switch s[i] {
|
||||
case '+': i += 1
|
||||
case '-': i += 1; exp_sign = -1
|
||||
}
|
||||
if i >= len(s) || s[i] < '0' || s[i] > '9' {
|
||||
return
|
||||
}
|
||||
e := 0
|
||||
for ; i < len(s) && ('0' <= s[i] && s[i] <= '9' || s[i] == '_'); i += 1 {
|
||||
if s[i] == '_' {
|
||||
underscores = true
|
||||
continue
|
||||
}
|
||||
if e < 1e5 {
|
||||
e = e*10 + int(s[i]) - '0'
|
||||
}
|
||||
}
|
||||
decimal_point += e * exp_sign
|
||||
} else if base == 16 {
|
||||
return
|
||||
}
|
||||
|
||||
if mantissa != 0 {
|
||||
exp = decimal_point - nd_mant
|
||||
}
|
||||
// TODO(bill): check underscore correctness
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
i := 0
|
||||
parse_hex :: proc(s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
|
||||
info := &_f64_info
|
||||
|
||||
sign: f64 = 1
|
||||
switch s[i] {
|
||||
case '-': i += 1; sign = -1
|
||||
case '+': i += 1
|
||||
}
|
||||
mantissa, exp := mantissa, exp
|
||||
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i])
|
||||
if r == '_' {
|
||||
continue
|
||||
MAX_EXP := 1<<info.expbits + info.bias - 2
|
||||
MIN_EXP := info.bias + 1
|
||||
exp += int(info.mantbits)
|
||||
|
||||
for mantissa != 0 && mantissa >> (info.mantbits+2) == 0 {
|
||||
mantissa <<= 1
|
||||
exp -= 1
|
||||
}
|
||||
if trunc {
|
||||
mantissa |= 1
|
||||
}
|
||||
|
||||
v := _digit_value(r)
|
||||
if v >= 10 {
|
||||
if r == '.' || r == 'e' || r == 'E' { // Skip parsing NaN and Inf if it's probably a regular float
|
||||
break
|
||||
}
|
||||
if len(s) >= 3 + i {
|
||||
buf: [4]u8
|
||||
copy(buf[:], s[i:][:3])
|
||||
|
||||
v2 := transmute(u32)buf
|
||||
v2 &= 0xDFDFDFDF // Knock out lower-case bits
|
||||
|
||||
buf = transmute([4]u8)v2
|
||||
|
||||
when ODIN_ENDIAN == .Little {
|
||||
if v2 == 0x464e49 { // "INF"
|
||||
s = s[3+i:]
|
||||
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
|
||||
return value, len(s) == 0
|
||||
} else if v2 == 0x4e414e { // "NAN"
|
||||
s = s[3+i:]
|
||||
return 0h7ff80000_00000001, len(s) == 0
|
||||
}
|
||||
} else {
|
||||
if v2 == 0x494e4600 { // "\0FNI"
|
||||
s = s[3+i:]
|
||||
value = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
|
||||
return value, len(s) == 0
|
||||
} else if v2 == 0x4e414e00 { // "\0NAN"
|
||||
s = s[3+i:]
|
||||
return 0h7ff80000_00000001, len(s) == 0
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
for mantissa >> (info.mantbits+2) == 0 {
|
||||
mantissa = mantissa>>1 | mantissa&1
|
||||
exp += 1
|
||||
}
|
||||
value *= 10
|
||||
value += f64(v)
|
||||
|
||||
// denormalize
|
||||
if mantissa > 1 && exp < MIN_EXP-2 {
|
||||
mantissa = mantissa>>1 | mantissa&1
|
||||
exp += 1
|
||||
}
|
||||
|
||||
round := mantissa & 3
|
||||
mantissa >>= 2
|
||||
round |= mantissa & 1 // round to even
|
||||
exp += 2
|
||||
if round == 3 {
|
||||
mantissa += 1
|
||||
if mantissa == 1 << (1 + info.mantbits) {
|
||||
mantissa >>= 1
|
||||
exp += 1
|
||||
}
|
||||
}
|
||||
if mantissa>>info.mantbits == 0 {
|
||||
// zero or denormal
|
||||
exp = info.bias
|
||||
}
|
||||
|
||||
ok := true
|
||||
if exp > MAX_EXP {
|
||||
// infinity or invalid
|
||||
mantissa = 1<<info.mantbits
|
||||
exp = MAX_EXP + 1
|
||||
ok = false
|
||||
}
|
||||
|
||||
bits := mantissa & (1<<info.mantbits - 1)
|
||||
bits |= u64((exp-info.bias) & (1<<info.expbits - 1)) << info.mantbits
|
||||
if neg {
|
||||
bits |= 1 << info.mantbits << info.expbits
|
||||
}
|
||||
return transmute(f64)bits, ok
|
||||
}
|
||||
|
||||
if i < len(s) && s[i] == '.' {
|
||||
pow10: f64 = 10
|
||||
i += 1
|
||||
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i])
|
||||
if r == '_' {
|
||||
continue
|
||||
}
|
||||
nr: int
|
||||
defer if n != nil { n^ = nr }
|
||||
|
||||
v := _digit_value(r)
|
||||
if v >= 10 {
|
||||
break
|
||||
if value, nr, ok = check_special(str); ok {
|
||||
return
|
||||
}
|
||||
|
||||
mantissa: u64
|
||||
exp: int
|
||||
neg, trunc, hex: bool
|
||||
mantissa, exp, neg, trunc, hex, nr = parse_components(str) or_return
|
||||
|
||||
if hex {
|
||||
return parse_hex(str, mantissa, exp, neg, trunc)
|
||||
}
|
||||
|
||||
trunc_block: if !trunc {
|
||||
@static pow10 := [?]f64{
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
|
||||
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
|
||||
1e20, 1e21, 1e22,
|
||||
}
|
||||
|
||||
if mantissa>>_f64_info.mantbits != 0 {
|
||||
return
|
||||
}
|
||||
f := f64(mantissa)
|
||||
if neg {
|
||||
f = -f
|
||||
}
|
||||
switch {
|
||||
case exp == 0:
|
||||
return f, true
|
||||
case exp > 0 && exp <= 15+22:
|
||||
if exp > 22 {
|
||||
f *= pow10[exp-22]
|
||||
exp = 22
|
||||
}
|
||||
value += f64(v)/pow10
|
||||
pow10 *= 10
|
||||
if f > 1e15 || f < 1e-15 {
|
||||
break trunc_block
|
||||
}
|
||||
return f * pow10[exp], true
|
||||
case -22 <= exp && exp < 0:
|
||||
return f / pow10[-exp], true
|
||||
}
|
||||
}
|
||||
|
||||
frac := false
|
||||
scale: f64 = 1
|
||||
|
||||
if i < len(s) && (s[i] == 'e' || s[i] == 'E') {
|
||||
i += 1
|
||||
|
||||
if i < len(s) {
|
||||
switch s[i] {
|
||||
case '-': i += 1; frac = true
|
||||
case '+': i += 1
|
||||
}
|
||||
|
||||
exp: u32 = 0
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i])
|
||||
if r == '_' {
|
||||
continue
|
||||
}
|
||||
|
||||
d := u32(_digit_value(r))
|
||||
if d >= 10 {
|
||||
break
|
||||
}
|
||||
exp = exp * 10 + d
|
||||
}
|
||||
if exp > 308 { exp = 308 }
|
||||
|
||||
for exp >= 50 { scale *= 1e50; exp -= 50 }
|
||||
for exp >= 8 { scale *= 1e8; exp -= 8 }
|
||||
for exp > 0 { scale *= 10; exp -= 1 }
|
||||
}
|
||||
}
|
||||
s = s[i:]
|
||||
|
||||
if frac {
|
||||
value = sign * (value/scale)
|
||||
} else {
|
||||
value = sign * (value*scale)
|
||||
}
|
||||
|
||||
ok = len(s) == 0
|
||||
d: decimal.Decimal
|
||||
decimal.set(&d, str[:nr])
|
||||
b, overflow := decimal_to_float_bits(&d, &_f64_info)
|
||||
value = transmute(f64)b
|
||||
ok = !overflow
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -299,6 +299,50 @@ write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false
|
||||
return
|
||||
}
|
||||
|
||||
// writes a f64 value into the builder, returns the written amount of characters
|
||||
write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f, fmt, prec, bit_size)
|
||||
// If the result starts with a `+` then unless we always want signed results,
|
||||
// we skip it unless it's followed by an `I` (because of +Inf).
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
// writes a f16 value into the builder, returns the written amount of characters
|
||||
write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
// writes a f32 value into the builder, returns the written amount of characters
|
||||
write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
// writes a f64 value into the builder, returns the written amount of characters
|
||||
write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) {
|
||||
buf: [384]byte
|
||||
s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f))
|
||||
if !always_signed && (buf[0] == '+' && buf[1] != 'I') {
|
||||
s = s[1:]
|
||||
}
|
||||
return write_string(b, s)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
|
||||
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
|
||||
buf: [32]byte
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
CPU feature flags can be tested against `cpu_features`, where applicable, e.g.
|
||||
`if .aes in si.aes { ... }`
|
||||
*/
|
||||
// +ignore
|
||||
//+build ignore
|
||||
package sysinfo
|
||||
|
||||
import "core:fmt"
|
||||
|
||||
@@ -464,6 +464,7 @@ macos_release_map: map[string]Darwin_To_Release = {
|
||||
"21F2092" = {{21, 5, 0}, "macOS", {"Monterey", {12, 4, 0}}},
|
||||
"21G72" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 0}}},
|
||||
"21G83" = {{21, 6, 0}, "macOS", {"Monterey", {12, 5, 1}}},
|
||||
"21G115" = {{21, 6, 0}, "macOS", {"Monterey", {12, 6, 0}}},
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
@@ -128,4 +128,34 @@ foreign advapi32 {
|
||||
lpData: LPCVOID,
|
||||
cbData: DWORD,
|
||||
) -> LSTATUS ---
|
||||
|
||||
GetFileSecurityW :: proc(
|
||||
lpFileName: LPCWSTR,
|
||||
RequestedInformation: SECURITY_INFORMATION,
|
||||
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
|
||||
nLength: DWORD,
|
||||
lpnLengthNeeded: LPDWORD,
|
||||
) -> BOOL ---
|
||||
|
||||
DuplicateToken :: proc(
|
||||
ExistingTokenHandle: HANDLE,
|
||||
ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL,
|
||||
DuplicateTokenHandle: PHANDLE,
|
||||
) -> BOOL ---
|
||||
|
||||
MapGenericMask :: proc(
|
||||
AccessMask: PDWORD,
|
||||
GenericMapping: PGENERIC_MAPPING,
|
||||
) ---
|
||||
|
||||
AccessCheck :: proc(
|
||||
pSecurityDescriptor: PSECURITY_DESCRIPTOR,
|
||||
ClientToken: HANDLE,
|
||||
DesiredAccess: DWORD,
|
||||
GenericMapping: PGENERIC_MAPPING,
|
||||
PrivilegeSet: PPRIVILEGE_SET,
|
||||
PrivilegeSetLength: LPDWORD,
|
||||
GrantedAccess: LPDWORD,
|
||||
AccessStatus: LPBOOL,
|
||||
) -> BOOL ---
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// +build windows
|
||||
package sys_windows
|
||||
|
||||
foreign import "system:Comctl32.lib"
|
||||
|
||||
@(default_calling_convention="stdcall")
|
||||
foreign Comctl32 {
|
||||
LoadIconWithScaleDown :: proc(hinst: HINSTANCE, pszName: PCWSTR, cx: c_int, cy: c_int, phico: ^HICON) -> HRESULT ---
|
||||
}
|
||||
@@ -77,6 +77,7 @@ foreign gdi32 {
|
||||
) -> HFONT ---
|
||||
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
|
||||
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
|
||||
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
|
||||
}
|
||||
|
||||
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
|
||||
|
||||
@@ -120,6 +120,12 @@ foreign kernel32 {
|
||||
bManualReset: BOOL,
|
||||
lpTimerName: LPCWSTR,
|
||||
) -> HANDLE ---
|
||||
CreateWaitableTimerExW :: proc(
|
||||
lpTimerAttributes: LPSECURITY_ATTRIBUTES,
|
||||
lpTimerName: LPCWSTR,
|
||||
dwFlags: DWORD,
|
||||
dwDesiredAccess: DWORD,
|
||||
) -> HANDLE ---
|
||||
SetWaitableTimerEx :: proc(
|
||||
hTimer: HANDLE,
|
||||
lpDueTime: ^LARGE_INTEGER,
|
||||
@@ -248,6 +254,17 @@ foreign kernel32 {
|
||||
GetModuleHandleW :: proc(lpModuleName: LPCWSTR) -> HMODULE ---
|
||||
GetModuleHandleA :: proc(lpModuleName: LPCSTR) -> HMODULE ---
|
||||
GetSystemTimeAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
||||
GetSystemTimePreciseAsFileTime :: proc(lpSystemTimeAsFileTime: LPFILETIME) ---
|
||||
FileTimeToSystemTime :: proc(lpFileTime: ^FILETIME, lpSystemTime: ^SYSTEMTIME) -> BOOL ---
|
||||
SystemTimeToTzSpecificLocalTime :: proc(
|
||||
lpTimeZoneInformation: ^TIME_ZONE_INFORMATION,
|
||||
lpUniversalTime: ^SYSTEMTIME,
|
||||
lpLocalTime: ^SYSTEMTIME,
|
||||
) -> BOOL ---
|
||||
SystemTimeToFileTime :: proc(
|
||||
lpSystemTime: ^SYSTEMTIME,
|
||||
lpFileTime: LPFILETIME,
|
||||
) -> BOOL ---
|
||||
CreateEventW :: proc(
|
||||
lpEventAttributes: LPSECURITY_ATTRIBUTES,
|
||||
bManualReset: BOOL,
|
||||
@@ -346,6 +363,13 @@ foreign kernel32 {
|
||||
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
|
||||
FreeConsole :: proc() -> BOOL ---
|
||||
GetConsoleWindow :: proc() -> HWND ---
|
||||
|
||||
GetDiskFreeSpaceExW :: proc(
|
||||
lpDirectoryName: LPCWSTR,
|
||||
lpFreeBytesAvailableToCaller: PULARGE_INTEGER,
|
||||
lpTotalNumberOfBytes: PULARGE_INTEGER,
|
||||
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
|
||||
) -> BOOL ---
|
||||
}
|
||||
|
||||
|
||||
@@ -963,4 +987,15 @@ DCB :: struct {
|
||||
foreign kernel32 {
|
||||
GetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
|
||||
SetCommState :: proc(handle: HANDLE, dcb: ^DCB) -> BOOL ---
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LPFIBER_START_ROUTINE :: #type proc "stdcall" (lpFiberParameter: LPVOID)
|
||||
|
||||
@(default_calling_convention = "stdcall")
|
||||
foreign kernel32 {
|
||||
CreateFiber :: proc(dwStackSize: SIZE_T, lpStartAddress: LPFIBER_START_ROUTINE, lpParameter: LPVOID) -> LPVOID ---
|
||||
DeleteFiber :: proc(lpFiber: LPVOID) ---
|
||||
ConvertThreadToFiber :: proc(lpParameter: LPVOID) -> LPVOID ---
|
||||
SwitchToFiber :: proc(lpFiber: LPVOID) ---
|
||||
}
|
||||
|
||||
@@ -14,4 +14,12 @@ foreign shell32 {
|
||||
lpDirectory: LPCWSTR,
|
||||
nShowCmd: INT,
|
||||
) -> HINSTANCE ---
|
||||
ShellExecuteExW :: proc(pExecInfo: ^SHELLEXECUTEINFOW) -> BOOL ---
|
||||
SHCreateDirectoryExW :: proc(
|
||||
hwnd: HWND,
|
||||
pszPath: LPCWSTR,
|
||||
psa: ^SECURITY_ATTRIBUTES,
|
||||
) -> c_int ---
|
||||
SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
|
||||
SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT ---
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ foreign shlwapi {
|
||||
PathFileExistsW :: proc(pszPath: wstring) -> BOOL ---
|
||||
PathFindExtensionW :: proc(pszPath: wstring) -> wstring ---
|
||||
PathFindFileNameW :: proc(pszPath: wstring) -> wstring ---
|
||||
SHAutoComplete :: proc(hwndEdit: HWND, dwFlags: DWORD) -> LWSTDAPI ---
|
||||
}
|
||||
|
||||
+373
-4
@@ -20,6 +20,7 @@ DWORD :: c_ulong
|
||||
DWORDLONG :: c.ulonglong
|
||||
QWORD :: c.ulonglong
|
||||
HANDLE :: distinct LPVOID
|
||||
PHANDLE :: ^HANDLE
|
||||
HINSTANCE :: HANDLE
|
||||
HMODULE :: distinct HINSTANCE
|
||||
HRESULT :: distinct LONG
|
||||
@@ -43,6 +44,7 @@ BOOLEAN :: distinct b8
|
||||
GROUP :: distinct c_uint
|
||||
LARGE_INTEGER :: distinct c_longlong
|
||||
ULARGE_INTEGER :: distinct c_ulonglong
|
||||
PULARGE_INTEGER :: ^ULARGE_INTEGER
|
||||
LONG :: c_long
|
||||
UINT :: c_uint
|
||||
INT :: c_int
|
||||
@@ -133,12 +135,24 @@ LPWSAOVERLAPPED :: distinct rawptr
|
||||
LPWSAOVERLAPPED_COMPLETION_ROUTINE :: distinct rawptr
|
||||
LPCVOID :: rawptr
|
||||
|
||||
PACCESS_TOKEN :: PVOID
|
||||
PSECURITY_DESCRIPTOR :: PVOID
|
||||
PSID :: PVOID
|
||||
PCLAIMS_BLOB :: PVOID
|
||||
|
||||
PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
|
||||
PLARGE_INTEGER :: ^LARGE_INTEGER
|
||||
PSRWLOCK :: ^SRWLOCK
|
||||
|
||||
MMRESULT :: UINT
|
||||
|
||||
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
|
||||
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
|
||||
|
||||
TIMER_QUERY_STATE :: 0x0001
|
||||
TIMER_MODIFY_STATE :: 0x0002
|
||||
TIMER_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | TIMER_QUERY_STATE | TIMER_MODIFY_STATE
|
||||
|
||||
SOCKET :: distinct uintptr // TODO
|
||||
socklen_t :: c_int
|
||||
ADDRESS_FAMILY :: USHORT
|
||||
@@ -175,6 +189,7 @@ FILE_SHARE_DELETE: DWORD : 0x00000004
|
||||
FILE_GENERIC_ALL: DWORD : 0x10000000
|
||||
FILE_GENERIC_EXECUTE: DWORD : 0x20000000
|
||||
FILE_GENERIC_READ: DWORD : 0x80000000
|
||||
FILE_ALL_ACCESS :: STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0x1FF
|
||||
|
||||
FILE_ACTION_ADDED :: 0x00000001
|
||||
FILE_ACTION_REMOVED :: 0x00000002
|
||||
@@ -230,6 +245,20 @@ SECURITY_SQOS_PRESENT: DWORD : 0x00100000
|
||||
|
||||
FIONBIO: c_ulong : 0x8004667e
|
||||
|
||||
OWNER_SECURITY_INFORMATION :: 0x00000001
|
||||
GROUP_SECURITY_INFORMATION :: 0x00000002
|
||||
DACL_SECURITY_INFORMATION :: 0x00000004
|
||||
SACL_SECURITY_INFORMATION :: 0x00000008
|
||||
LABEL_SECURITY_INFORMATION :: 0x00000010
|
||||
ATTRIBUTE_SECURITY_INFORMATION :: 0x00000020
|
||||
SCOPE_SECURITY_INFORMATION :: 0x00000040
|
||||
PROCESS_TRUST_LABEL_SECURITY_INFORMATION :: 0x00000080
|
||||
ACCESS_FILTER_SECURITY_INFORMATION :: 0x00000100
|
||||
BACKUP_SECURITY_INFORMATION :: 0x00010000
|
||||
PROTECTED_DACL_SECURITY_INFORMATION :: 0x80000000
|
||||
PROTECTED_SACL_SECURITY_INFORMATION :: 0x40000000
|
||||
UNPROTECTED_DACL_SECURITY_INFORMATION :: 0x20000000
|
||||
UNPROTECTED_SACL_SECURITY_INFORMATION :: 0x10000000
|
||||
|
||||
GET_FILEEX_INFO_LEVELS :: distinct i32
|
||||
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
|
||||
@@ -773,6 +802,61 @@ MSG :: struct {
|
||||
|
||||
LPMSG :: ^MSG
|
||||
|
||||
// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
|
||||
HTERROR :: -2
|
||||
HTTRANSPARENT :: -1
|
||||
HTNOWHERE :: 0
|
||||
HTCLIENT :: 1
|
||||
HTCAPTION :: 2
|
||||
HTSYSMENU :: 3
|
||||
HTGROWBOX :: 4
|
||||
HTSIZE :: HTGROWBOX
|
||||
HTMENU :: 5
|
||||
HTHSCROLL :: 6
|
||||
HTVSCROLL :: 7
|
||||
HTMINBUTTON :: 8
|
||||
HTMAXBUTTON :: 9
|
||||
HTLEFT :: 10
|
||||
HTRIGHT :: 11
|
||||
HTTOP :: 12
|
||||
HTTOPLEFT :: 13
|
||||
HTTOPRIGHT :: 14
|
||||
HTBOTTOM :: 15
|
||||
HTBOTTOMLEFT :: 16
|
||||
HTBOTTOMRIGHT :: 17
|
||||
HTBORDER :: 18
|
||||
HTREDUCE :: HTMINBUTTON
|
||||
HTZOOM :: HTMAXBUTTON
|
||||
HTSIZEFIRST :: HTLEFT
|
||||
HTSIZELAST :: HTBOTTOMRIGHT
|
||||
HTOBJECT :: 19
|
||||
HTCLOSE :: 20
|
||||
HTHELP :: 21
|
||||
|
||||
TEXTMETRICW :: struct {
|
||||
tmHeight: LONG,
|
||||
tmAscent: LONG,
|
||||
tmDescent: LONG,
|
||||
tmInternalLeading: LONG,
|
||||
tmExternalLeading: LONG,
|
||||
tmAveCharWidth: LONG,
|
||||
tmMaxCharWidth: LONG,
|
||||
tmWeight: LONG,
|
||||
tmOverhang: LONG,
|
||||
tmDigitizedAspectX: LONG,
|
||||
tmDigitizedAspectY: LONG,
|
||||
tmFirstChar: WCHAR,
|
||||
tmLastChar: WCHAR,
|
||||
tmDefaultChar: WCHAR,
|
||||
tmBreakChar: WCHAR,
|
||||
tmItalic: BYTE,
|
||||
tmUnderlined: BYTE,
|
||||
tmStruckOut: BYTE,
|
||||
tmPitchAndFamily: BYTE,
|
||||
tmCharSet: BYTE,
|
||||
}
|
||||
LPTEXTMETRICW :: ^TEXTMETRICW
|
||||
|
||||
PAINTSTRUCT :: struct {
|
||||
hdc: HDC,
|
||||
fErase: BOOL,
|
||||
@@ -879,6 +963,48 @@ NM_FONTCHANGED :: NM_OUTOFMEMORY-22
|
||||
NM_CUSTOMTEXT :: NM_OUTOFMEMORY-23 // uses NMCUSTOMTEXT struct
|
||||
NM_TVSTATEIMAGECHANGING :: NM_OUTOFMEMORY-23 // uses NMTVSTATEIMAGECHANGING struct, defined after HTREEITEM
|
||||
|
||||
PCZZWSTR :: ^WCHAR
|
||||
|
||||
SHFILEOPSTRUCTW :: struct {
|
||||
hwnd: HWND,
|
||||
wFunc: UINT,
|
||||
pFrom: PCZZWSTR,
|
||||
pTo: PCZZWSTR,
|
||||
fFlags: FILEOP_FLAGS,
|
||||
fAnyOperationsAborted: BOOL,
|
||||
hNameMappings: LPVOID,
|
||||
lpszProgressTitle: PCWSTR, // only used if FOF_SIMPLEPROGRESS
|
||||
}
|
||||
LPSHFILEOPSTRUCTW :: ^SHFILEOPSTRUCTW
|
||||
|
||||
// Shell File Operations
|
||||
FO_MOVE :: 0x0001
|
||||
FO_COPY :: 0x0002
|
||||
FO_DELETE :: 0x0003
|
||||
FO_RENAME :: 0x0004
|
||||
|
||||
// SHFILEOPSTRUCT.fFlags and IFileOperation::SetOperationFlags() flag values
|
||||
FOF_MULTIDESTFILES :: 0x0001
|
||||
FOF_CONFIRMMOUSE :: 0x0002
|
||||
FOF_SILENT :: 0x0004 // don't display progress UI (confirm prompts may be displayed still)
|
||||
FOF_RENAMEONCOLLISION :: 0x0008 // automatically rename the source files to avoid the collisions
|
||||
FOF_NOCONFIRMATION :: 0x0010 // don't display confirmation UI, assume "yes" for cases that can be bypassed, "no" for those that can not
|
||||
FOF_WANTMAPPINGHANDLE :: 0x0020 // Fill in SHFILEOPSTRUCT.hNameMappings
|
||||
// Must be freed using SHFreeNameMappings
|
||||
FOF_ALLOWUNDO :: 0x0040 // enable undo including Recycle behavior for IFileOperation::Delete()
|
||||
FOF_FILESONLY :: 0x0080 // only operate on the files (non folders), both files and folders are assumed without this
|
||||
FOF_SIMPLEPROGRESS :: 0x0100 // means don't show names of files
|
||||
FOF_NOCONFIRMMKDIR :: 0x0200 // don't dispplay confirmatino UI before making any needed directories, assume "Yes" in these cases
|
||||
FOF_NOERRORUI :: 0x0400 // don't put up error UI, other UI may be displayed, progress, confirmations
|
||||
FOF_NOCOPYSECURITYATTRIBS :: 0x0800 // dont copy file security attributes (ACLs)
|
||||
FOF_NORECURSION :: 0x1000 // don't recurse into directories for operations that would recurse
|
||||
FOF_NO_CONNECTED_ELEMENTS :: 0x2000 // don't operate on connected elements ("xxx_files" folders that go with .htm files)
|
||||
FOF_WANTNUKEWARNING :: 0x4000 // during delete operation, warn if object is being permanently destroyed instead of recycling (partially overrides FOF_NOCONFIRMATION)
|
||||
FOF_NORECURSEREPARSE :: 0x8000 // deprecated; the operations engine always does the right thing on FolderLink objects (symlinks, reparse points, folder shortcuts)
|
||||
FOF_NO_UI :: (FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR) // don't display any UI at all
|
||||
|
||||
FILEOP_FLAGS :: WORD
|
||||
|
||||
DEVMODEW :: struct {
|
||||
dmDeviceName: [32]wchar_t,
|
||||
dmSpecVersion: WORD,
|
||||
@@ -1066,8 +1192,14 @@ WS_EX_TOPMOST : UINT : 0x0000_0008
|
||||
WS_EX_TRANSPARENT : UINT : 0x0000_0020
|
||||
WS_EX_WINDOWEDGE : UINT : 0x0000_0100
|
||||
|
||||
PBS_SMOOTH :: 0x01
|
||||
PBS_VERTICAL :: 0x04
|
||||
PBS_SMOOTH :: 0x01
|
||||
PBS_VERTICAL :: 0x04
|
||||
PBS_MARQUEE :: 0x08
|
||||
PBS_SMOOTHREVERSE :: 0x10
|
||||
|
||||
PBST_NORMAL :: 0x0001
|
||||
PBST_ERROR :: 0x0002
|
||||
PBST_PAUSED :: 0x0003
|
||||
|
||||
QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
|
||||
QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
|
||||
@@ -1127,6 +1259,10 @@ SWP_NOREPOSITION :: SWP_NOOWNERZORDER
|
||||
SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
|
||||
SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
|
||||
|
||||
CSIDL_APPDATA :: 0x001a // <user name>\Application Data
|
||||
CSIDL_COMMON_APPDATA :: 0x0023 // All Users\Application Data
|
||||
CSIDL_PROFILE :: 0x0028 // <user name>\
|
||||
|
||||
HWND_TOP :: HWND( uintptr(0)) // 0
|
||||
HWND_BOTTOM :: HWND( uintptr(1)) // 1
|
||||
HWND_TOPMOST :: HWND(~uintptr(0)) // -1
|
||||
@@ -1325,6 +1461,58 @@ WMSZ_BOTTOM :: 6
|
||||
WMSZ_BOTTOMLEFT :: 7
|
||||
WMSZ_BOTTOMRIGHT :: 8
|
||||
|
||||
// Note CLASSKEY overrides CLASSNAME
|
||||
SEE_MASK_DEFAULT :: 0x00000000
|
||||
SEE_MASK_CLASSNAME :: 0x00000001 // SHELLEXECUTEINFO.lpClass is valid
|
||||
SEE_MASK_CLASSKEY :: 0x00000003 // SHELLEXECUTEINFO.hkeyClass is valid
|
||||
// Note SEE_MASK_INVOKEIDLIST(0xC) implies SEE_MASK_IDLIST(0x04)
|
||||
SEE_MASK_IDLIST :: 0x00000004 // SHELLEXECUTEINFO.lpIDList is valid
|
||||
SEE_MASK_INVOKEIDLIST :: 0x0000000c // enable IContextMenu based verbs
|
||||
SEE_MASK_ICON :: 0x00000010 // not used
|
||||
SEE_MASK_HOTKEY :: 0x00000020 // SHELLEXECUTEINFO.dwHotKey is valid
|
||||
SEE_MASK_NOCLOSEPROCESS :: 0x00000040 // SHELLEXECUTEINFO.hProcess
|
||||
SEE_MASK_CONNECTNETDRV :: 0x00000080 // enables re-connecting disconnected network drives
|
||||
SEE_MASK_NOASYNC :: 0x00000100 // block on the call until the invoke has completed, use for callers that exit after calling ShellExecuteEx()
|
||||
SEE_MASK_FLAG_DDEWAIT :: SEE_MASK_NOASYNC // Use SEE_MASK_NOASYNC instead of SEE_MASK_FLAG_DDEWAIT as it more accuratly describes the behavior
|
||||
SEE_MASK_DOENVSUBST :: 0x00000200 // indicates that SHELLEXECUTEINFO.lpFile contains env vars that should be expanded
|
||||
SEE_MASK_FLAG_NO_UI :: 0x00000400 // disable UI including error messages
|
||||
SEE_MASK_UNICODE :: 0x00004000
|
||||
SEE_MASK_NO_CONSOLE :: 0x00008000
|
||||
SEE_MASK_ASYNCOK :: 0x00100000
|
||||
SEE_MASK_HMONITOR :: 0x00200000 // SHELLEXECUTEINFO.hMonitor
|
||||
SEE_MASK_NOZONECHECKS :: 0x00800000
|
||||
SEE_MASK_NOQUERYCLASSSTORE :: 0x01000000
|
||||
SEE_MASK_WAITFORINPUTIDLE :: 0x02000000
|
||||
SEE_MASK_FLAG_LOG_USAGE :: 0x04000000
|
||||
|
||||
// When SEE_MASK_FLAG_HINST_IS_SITE is specified SHELLEXECUTEINFO.hInstApp is used as an
|
||||
// _In_ parameter and specifies a IUnknown* to be used as a site pointer. The site pointer
|
||||
// is used to provide services to shell execute, the handler binding process and the verb handlers
|
||||
// once they are invoked.
|
||||
SEE_MASK_FLAG_HINST_IS_SITE :: 0x08000000
|
||||
|
||||
SHELLEXECUTEINFOW :: struct {
|
||||
cbSize: DWORD, // in, required, sizeof of this structure
|
||||
fMask: ULONG, // in, SEE_MASK_XXX values
|
||||
hwnd: HWND, // in, optional
|
||||
lpVerb: LPCWSTR, // in, optional when unspecified the default verb is choosen
|
||||
lpFile: LPCWSTR, // in, either this value or lpIDList must be specified
|
||||
lpParameters: LPCWSTR, // in, optional
|
||||
lpDirectory: LPCWSTR, // in, optional
|
||||
nShow: c.int, // in, required
|
||||
hInstApp: HINSTANCE, // out when SEE_MASK_NOCLOSEPROCESS is specified
|
||||
lpIDList: rawptr, // in, valid when SEE_MASK_IDLIST is specified, PCIDLIST_ABSOLUTE, for use with SEE_MASK_IDLIST & SEE_MASK_INVOKEIDLIST
|
||||
lpClass: LPCWSTR, // in, valid when SEE_MASK_CLASSNAME is specified
|
||||
hkeyClass: HKEY, // in, valid when SEE_MASK_CLASSKEY is specified
|
||||
dwHotKey: DWORD, // in, valid when SEE_MASK_HOTKEY is specified
|
||||
DUMMYUNIONNAME: struct #raw_union {
|
||||
hIcon: HANDLE, // not used
|
||||
hMonitor: HANDLE, // in, valid when SEE_MASK_HMONITOR specified
|
||||
},
|
||||
hProcess: HANDLE, // out, valid when SEE_MASK_NOCLOSEPROCESS specified
|
||||
}
|
||||
LPSHELLEXECUTEINFOW :: ^SHELLEXECUTEINFOW
|
||||
|
||||
// Key State Masks for Mouse Messages
|
||||
MK_LBUTTON :: 0x0001
|
||||
MK_RBUTTON :: 0x0002
|
||||
@@ -1462,6 +1650,24 @@ IDI_WARNING := IDI_EXCLAMATION
|
||||
IDI_ERROR := IDI_HAND
|
||||
IDI_INFORMATION := IDI_ASTERISK
|
||||
|
||||
IMAGE_BITMAP :: 0
|
||||
IMAGE_ICON :: 1
|
||||
IMAGE_CURSOR :: 2
|
||||
IMAGE_ENHMETAFILE :: 3
|
||||
|
||||
LR_DEFAULTCOLOR :: 0x00000000
|
||||
LR_MONOCHROME :: 0x00000001
|
||||
LR_COLOR :: 0x00000002
|
||||
LR_COPYRETURNORG :: 0x00000004
|
||||
LR_COPYDELETEORG :: 0x00000008
|
||||
LR_LOADFROMFILE :: 0x00000010
|
||||
LR_LOADTRANSPARENT :: 0x00000020
|
||||
LR_DEFAULTSIZE :: 0x00000040
|
||||
LR_VGACOLOR :: 0x00000080
|
||||
LR_LOADMAP3DCOLORS :: 0x00001000
|
||||
LR_CREATEDIBSECTION :: 0x00002000
|
||||
LR_COPYFROMRESOURCE :: 0x00004000
|
||||
LR_SHARED :: 0x00008000
|
||||
|
||||
// DIB color table identifiers
|
||||
DIB_RGB_COLORS :: 0
|
||||
@@ -1774,12 +1980,15 @@ WAIT_FAILED: DWORD : 0xFFFFFFFF
|
||||
|
||||
PIPE_ACCESS_INBOUND: DWORD : 0x00000001
|
||||
PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002
|
||||
PIPE_ACCESS_DUPLEX: DWORD : 0x00000003
|
||||
FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
|
||||
FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
|
||||
PIPE_WAIT: DWORD : 0x00000000
|
||||
PIPE_TYPE_BYTE: DWORD : 0x00000000
|
||||
PIPE_TYPE_MESSAGE: DWORD : 0x00000004
|
||||
PIPE_REJECT_REMOTE_CLIENTS: DWORD : 0x00000008
|
||||
PIPE_READMODE_BYTE: DWORD : 0x00000000
|
||||
PIPE_READMODE_MESSAGE: DWORD : 0x00000002
|
||||
PIPE_ACCEPT_REMOTE_CLIENTS: DWORD : 0x00000000
|
||||
|
||||
FD_SETSIZE :: 64
|
||||
@@ -1793,7 +2002,58 @@ HEAP_ZERO_MEMORY: DWORD : 0x00000008
|
||||
HANDLE_FLAG_INHERIT: DWORD : 0x00000001
|
||||
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 0x00000002
|
||||
|
||||
TOKEN_READ: DWORD : 0x20008
|
||||
GENERIC_MAPPING :: struct {
|
||||
GenericRead: ACCESS_MASK,
|
||||
GenericWrite: ACCESS_MASK,
|
||||
GenericExecute: ACCESS_MASK,
|
||||
GenericAll: ACCESS_MASK,
|
||||
}
|
||||
PGENERIC_MAPPING :: ^GENERIC_MAPPING
|
||||
|
||||
SECURITY_IMPERSONATION_LEVEL :: enum {
|
||||
SecurityAnonymous,
|
||||
SecurityIdentification,
|
||||
SecurityImpersonation,
|
||||
SecurityDelegation,
|
||||
}
|
||||
|
||||
SECURITY_INFORMATION :: DWORD
|
||||
ANYSIZE_ARRAY :: 1
|
||||
|
||||
LUID_AND_ATTRIBUTES :: struct {
|
||||
Luid: LUID,
|
||||
Attributes: DWORD,
|
||||
}
|
||||
|
||||
PRIVILEGE_SET :: struct {
|
||||
PrivilegeCount: DWORD,
|
||||
Control: DWORD,
|
||||
Privilege: [ANYSIZE_ARRAY]LUID_AND_ATTRIBUTES,
|
||||
}
|
||||
PPRIVILEGE_SET :: ^PRIVILEGE_SET
|
||||
|
||||
// Token Specific Access Rights.
|
||||
TOKEN_ASSIGN_PRIMARY :: 0x0001
|
||||
TOKEN_DUPLICATE :: 0x0002
|
||||
TOKEN_IMPERSONATE :: 0x0004
|
||||
TOKEN_QUERY :: 0x0008
|
||||
TOKEN_QUERY_SOURCE :: 0x0010
|
||||
TOKEN_ADJUST_PRIVILEGES :: 0x0020
|
||||
TOKEN_ADJUST_GROUPS :: 0x0040
|
||||
TOKEN_ADJUST_DEFAULT :: 0x0080
|
||||
TOKEN_ADJUST_SESSIONID :: 0x0100
|
||||
|
||||
TOKEN_ALL_ACCESS_P :: STANDARD_RIGHTS_REQUIRED | TOKEN_ASSIGN_PRIMARY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY |\
|
||||
TOKEN_QUERY_SOURCE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
|
||||
|
||||
TOKEN_ALL_ACCESS :: TOKEN_ALL_ACCESS_P | TOKEN_ADJUST_SESSIONID
|
||||
TOKEN_READ :: STANDARD_RIGHTS_READ | TOKEN_QUERY
|
||||
TOKEN_WRITE :: STANDARD_RIGHTS_WRITE | TOKEN_ADJUST_PRIVILEGES | TOKEN_ADJUST_GROUPS | TOKEN_ADJUST_DEFAULT
|
||||
TOKEN_EXECUTE :: STANDARD_RIGHTS_EXECUTE
|
||||
TOKEN_TRUST_CONSTRAINT_MASK :: STANDARD_RIGHTS_READ | TOKEN_QUERY | TOKEN_QUERY_SOURCE
|
||||
TOKEN_ACCESS_PSEUDO_HANDLE_WIN8 :: TOKEN_QUERY | TOKEN_QUERY_SOURCE
|
||||
TOKEN_ACCESS_PSEUDO_HANDLE :: TOKEN_ACCESS_PSEUDO_HANDLE_WIN8
|
||||
|
||||
|
||||
CP_ACP :: 0 // default to ANSI code page
|
||||
CP_OEMCP :: 1 // default to OEM code page
|
||||
@@ -2101,9 +2361,10 @@ FILETIME :: struct {
|
||||
|
||||
FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
|
||||
t := i64(u64(ft.dwLowDateTime) | u64(ft.dwHighDateTime) << 32)
|
||||
return (t - 0x019db1ded53e8000) * 100
|
||||
return (t - 116444736000000000) * 100
|
||||
}
|
||||
|
||||
|
||||
OVERLAPPED :: struct {
|
||||
Internal: ^c_ulong,
|
||||
InternalHigh: ^c_ulong,
|
||||
@@ -2777,6 +3038,16 @@ SYSTEMTIME :: struct {
|
||||
milliseconds: WORD,
|
||||
}
|
||||
|
||||
TIME_ZONE_INFORMATION :: struct {
|
||||
Bias: LONG,
|
||||
StandardName: [32]WCHAR,
|
||||
StandardDate: SYSTEMTIME,
|
||||
StandardBias: LONG,
|
||||
DaylightName: [32]WCHAR,
|
||||
DaylightDate: SYSTEMTIME,
|
||||
DaylightBias: LONG,
|
||||
}
|
||||
|
||||
|
||||
@(private="file")
|
||||
IMAGE_DOS_HEADER :: struct {
|
||||
@@ -3048,12 +3319,32 @@ SHCONTF_FLATLIST :: 0x4000
|
||||
SHCONTF_ENABLE_ASYNC :: 0x8000
|
||||
SHCONTF_INCLUDESUPERHIDDEN :: 0x10000
|
||||
|
||||
SHACF_DEFAULT :: 0x00000000 // Currently (SHACF_FILESYSTEM | SHACF_URLALL)
|
||||
SHACF_FILESYSTEM :: 0x00000001 // This includes the File System as well as the rest of the shell (Desktop\My Computer\Control Panel\)
|
||||
SHACF_URLALL :: (SHACF_URLHISTORY | SHACF_URLMRU)
|
||||
SHACF_URLHISTORY :: 0x00000002 // URLs in the User's History
|
||||
SHACF_URLMRU :: 0x00000004 // URLs in the User's Recently Used list.
|
||||
SHACF_USETAB :: 0x00000008 // Use the tab to move thru the autocomplete possibilities instead of to the next dialog/window control.
|
||||
SHACF_FILESYS_ONLY :: 0x00000010 // This includes the File System
|
||||
SHACF_FILESYS_DIRS :: 0x00000020 // Same as SHACF_FILESYS_ONLY except it only includes directories, UNC servers, and UNC server shares.
|
||||
SHACF_VIRTUAL_NAMESPACE :: 0x00000040 // Also include the virtual namespace
|
||||
SHACF_AUTOSUGGEST_FORCE_ON :: 0x10000000 // Ignore the registry default and force the feature on.
|
||||
SHACF_AUTOSUGGEST_FORCE_OFF :: 0x20000000 // Ignore the registry default and force the feature off.
|
||||
SHACF_AUTOAPPEND_FORCE_ON :: 0x40000000 // Ignore the registry default and force the feature on. (Also know as AutoComplete)
|
||||
SHACF_AUTOAPPEND_FORCE_OFF :: 0x80000000 // Ignore the registry default and force the feature off. (Also know as AutoComplete)
|
||||
|
||||
LWSTDAPI :: HRESULT
|
||||
|
||||
CLSID_FileOpenDialog := &GUID{0xDC1C5A9C, 0xE88A, 0x4DDE, {0xA5, 0xA1, 0x60, 0xF8, 0x2A, 0x20, 0xAE, 0xF7}}
|
||||
CLSID_FileSaveDialog := &GUID{0xC0B4E2F3, 0xBA21, 0x4773, {0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B}}
|
||||
CLSID_TaskbarList := &GUID{0x56FDF344, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
|
||||
|
||||
IID_IFileDialog := &GUID{0x42F85136, 0xDB7E, 0x439C, {0x85, 0xF1, 0xE4, 0x07, 0x5D, 0x13, 0x5F, 0xC8}}
|
||||
IID_IFileSaveDialog := &GUID{0x84BCCD23, 0x5FDE, 0x4CDB, {0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB}}
|
||||
IID_IFileOpenDialog := &GUID{0xD57C7288, 0xD4AD, 0x4768, {0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60}}
|
||||
IID_ITaskbarList := &GUID{0x56FDF342, 0xFD6D, 0x11d0, {0x95, 0x8A, 0x00, 0x60, 0x97, 0xC9, 0xA0, 0x90}}
|
||||
IID_ITaskbarList2 := &GUID{0x602D4995, 0xB13A, 0x429b, {0xA6, 0x6E, 0x19, 0x35, 0xE4, 0x4F, 0x43, 0x17}}
|
||||
IID_ITaskbarList3 := &GUID{0xea1afb91, 0x9e28, 0x4b86, {0x90, 0xe9, 0x9e, 0x9f, 0x8a, 0x5e, 0xef, 0xaf}}
|
||||
|
||||
IModalWindow :: struct #raw_union {
|
||||
#subtype IUnknown: IUnknown,
|
||||
@@ -3358,6 +3649,84 @@ IFileSaveDialogVtbl :: struct {
|
||||
ApplyProperties: proc "stdcall" (this: ^IFileSaveDialog, psi: ^IShellItem, pStore: ^IPropertyStore, hwnd: HWND, pSink: ^IFileOperationProgressSink) -> HRESULT,
|
||||
}
|
||||
|
||||
ITaskbarList :: struct #raw_union {
|
||||
#subtype IUnknown: IUnknown,
|
||||
using Vtbl: ^ITaskbarListVtbl,
|
||||
}
|
||||
ITaskbarListVtbl :: struct {
|
||||
using IUnknownVtbl: IUnknownVtbl,
|
||||
HrInit: proc "stdcall" (this: ^ITaskbarList) -> HRESULT,
|
||||
AddTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
DeleteTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
ActivateTab: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
SetActiveAlt: proc "stdcall" (this: ^ITaskbarList, hwnd: HWND) -> HRESULT,
|
||||
}
|
||||
|
||||
ITaskbarList2 :: struct #raw_union {
|
||||
#subtype ITaskbarList: ITaskbarList,
|
||||
using Vtbl: ^ITaskbarList2Vtbl,
|
||||
}
|
||||
ITaskbarList2Vtbl :: struct {
|
||||
using ITaskbarListVtbl: ITaskbarListVtbl,
|
||||
MarkFullscreenWindow: proc "stdcall" (this: ^ITaskbarList2, hwnd: HWND, fFullscreen: BOOL) -> HRESULT,
|
||||
}
|
||||
|
||||
TBPFLAG :: enum c_int {
|
||||
NOPROGRESS = 0,
|
||||
INDETERMINATE = 0x1,
|
||||
NORMAL = 0x2,
|
||||
ERROR = 0x4,
|
||||
PAUSED = 0x8,
|
||||
}
|
||||
|
||||
THUMBBUTTONFLAGS :: enum c_int {
|
||||
ENABLED = 0,
|
||||
DISABLED = 0x1,
|
||||
DISMISSONCLICK = 0x2,
|
||||
NOBACKGROUND = 0x4,
|
||||
HIDDEN = 0x8,
|
||||
NONINTERACTIVE = 0x10,
|
||||
}
|
||||
|
||||
THUMBBUTTONMASK :: enum c_int {
|
||||
BITMAP = 0x1,
|
||||
ICON = 0x2,
|
||||
TOOLTIP = 0x4,
|
||||
FLAGS = 0x8,
|
||||
}
|
||||
|
||||
THUMBBUTTON :: struct {
|
||||
dwMask: THUMBBUTTONMASK,
|
||||
iId: UINT,
|
||||
iBitmap: UINT,
|
||||
hIcon: HICON,
|
||||
szTip: [260]WCHAR,
|
||||
dwFlags: THUMBBUTTONFLAGS,
|
||||
}
|
||||
LPTHUMBBUTTON :: ^THUMBBUTTON
|
||||
|
||||
HIMAGELIST :: ^IUnknown
|
||||
|
||||
ITaskbarList3 :: struct #raw_union {
|
||||
#subtype ITaskbarList2: ITaskbarList2,
|
||||
using Vtbl: ^ITaskbarList3Vtbl,
|
||||
}
|
||||
ITaskbarList3Vtbl :: struct {
|
||||
using ITaskbarList2Vtbl: ITaskbarList2Vtbl,
|
||||
SetProgressValue: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, ullCompleted: ULONGLONG, ullTotal: ULONGLONG) -> HRESULT,
|
||||
SetProgressState: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, tbpFlags: TBPFLAG) -> HRESULT,
|
||||
RegisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND) -> HRESULT,
|
||||
UnregisterTab: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND) -> HRESULT,
|
||||
SetTabOrder: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndInsertBefore: HWND) -> HRESULT,
|
||||
SetTabActive: proc "stdcall" (this: ^ITaskbarList3, hwndTab: HWND, hwndMDI: HWND, dwReserved: DWORD) -> HRESULT,
|
||||
ThumbBarAddButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
|
||||
ThumbBarUpdateButtons: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, cButtons: UINT, pButton: LPTHUMBBUTTON) -> HRESULT,
|
||||
ThumbBarSetImageList: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, himl: HIMAGELIST) -> HRESULT,
|
||||
SetOverlayIcon: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, hIcon: HICON, pszDescription: LPCWSTR) -> HRESULT,
|
||||
SetThumbnailTooltip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, pszTip: LPCWSTR) -> HRESULT,
|
||||
SetThumbnailClip: proc "stdcall" (this: ^ITaskbarList3, hwnd: HWND, prcClip: ^RECT) -> HRESULT,
|
||||
}
|
||||
|
||||
MEMORYSTATUSEX :: struct {
|
||||
dwLength: DWORD,
|
||||
dwMemoryLoad: DWORD,
|
||||
|
||||
@@ -78,6 +78,7 @@ foreign user32 {
|
||||
LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
|
||||
LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
|
||||
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
|
||||
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE ---
|
||||
|
||||
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
|
||||
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
|
||||
|
||||
@@ -62,19 +62,19 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
|
||||
wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string, err: runtime.Allocator_Error) {
|
||||
context.allocator = allocator
|
||||
|
||||
if N <= 0 {
|
||||
if N == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), nil, 0, nil, nil)
|
||||
n := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N) if N > 0 else -1, nil, 0, nil, nil)
|
||||
if n == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// If N == -1 the call to WideCharToMultiByte assume the wide string is null terminated
|
||||
// If N < 0 the call to WideCharToMultiByte assume the wide string is null terminated
|
||||
// and will scan it to find the first null terminated character. The resulting string will
|
||||
// also be null terminated.
|
||||
// If N != -1 it assumes the wide string is not null terminated and the resulting string
|
||||
// If N > 0 it assumes the wide string is not null terminated and the resulting string
|
||||
// will not be null terminated.
|
||||
text := make([]byte, n) or_return
|
||||
|
||||
|
||||
@@ -85,3 +85,15 @@ foreign Opengl32 {
|
||||
wglUseFontBitmaps :: proc(hdc: HDC, first, count, list_base: DWORD) -> BOOL ---
|
||||
wglUseFontOutlines :: proc(hdc: HDC, first, count, list_base: DWORD, deviation, extrusion: f32, format: c.int, gmf: LPGLYPHMETRICSFLOAT) -> BOOL ---
|
||||
}
|
||||
|
||||
// Used by vendor:OpenGL
|
||||
// https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions#Windows
|
||||
gl_set_proc_address :: proc(p: rawptr, name: cstring) {
|
||||
func := wglGetProcAddress(name)
|
||||
switch uintptr(func) {
|
||||
case 0, 1, 2, 3, ~uintptr(0):
|
||||
module := LoadLibraryW(L("opengl32.dll"))
|
||||
func = GetProcAddress(module, name)
|
||||
}
|
||||
(^rawptr)(p)^ = func
|
||||
}
|
||||
|
||||
@@ -454,6 +454,7 @@ TB_ISBUTTONENABLED :: 0x0409
|
||||
TBM_CLEARTICS :: 0x0409
|
||||
TTM_SETTOOLINFOA :: 0x0409
|
||||
CBEM_HASEDITCHANGED :: 0x040a
|
||||
PBM_SETMARQUEE :: 0x040a
|
||||
RB_INSERTBANDW :: 0x040a
|
||||
SB_GETRECT :: 0x040a
|
||||
TB_ISBUTTONCHECKED :: 0x040a
|
||||
@@ -488,10 +489,12 @@ TTM_ENUMTOOLSA :: 0x040e
|
||||
SB_SETICON :: 0x040f
|
||||
TBM_GETTICPOS :: 0x040f
|
||||
TTM_GETCURRENTTOOLA :: 0x040f
|
||||
PBM_SETSTATE :: 0x0410
|
||||
RB_IDTOINDEX :: 0x0410
|
||||
SB_SETTIPTEXTA :: 0x0410
|
||||
TBM_GETNUMTICS :: 0x0410
|
||||
TTM_WINDOWFROMPOINT :: 0x0410
|
||||
PBM_GETSTATE :: 0x0411
|
||||
RB_GETTOOLTIPS :: 0x0411
|
||||
SB_SETTIPTEXTW :: 0x0411
|
||||
TBM_GETSELSTART :: 0x0411
|
||||
|
||||
@@ -0,0 +1,413 @@
|
||||
package text_edit
|
||||
|
||||
/*
|
||||
Based off the articles by rxi:
|
||||
* https://rxi.github.io/textbox_behaviour.html
|
||||
* https://rxi.github.io/a_simple_undo_system.html
|
||||
*/
|
||||
|
||||
import "core:runtime"
|
||||
import "core:time"
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
DEFAULT_UNDO_TIMEOUT :: 300 * time.Millisecond
|
||||
|
||||
State :: struct {
|
||||
selection: [2]int,
|
||||
line_start, line_end: int,
|
||||
|
||||
// initialized each "frame" with `begin`
|
||||
builder: ^strings.Builder, // let the caller store the text buffer data
|
||||
|
||||
up_index, down_index: int, // multi-lines
|
||||
|
||||
|
||||
// undo
|
||||
undo: [dynamic]^Undo_State,
|
||||
redo: [dynamic]^Undo_State,
|
||||
undo_text_allocator: runtime.Allocator,
|
||||
|
||||
id: u64, // useful for immediate mode GUIs
|
||||
|
||||
// Timeout information
|
||||
current_time: time.Tick,
|
||||
last_edit_time: time.Tick,
|
||||
undo_timeout: time.Duration,
|
||||
|
||||
// Set these if you want cut/copy/paste functionality
|
||||
set_clipboard: proc(user_data: rawptr, text: string) -> (ok: bool),
|
||||
get_clipboard: proc(user_data: rawptr) -> (text: string, ok: bool),
|
||||
clipboard_user_data: rawptr,
|
||||
}
|
||||
|
||||
Undo_State :: struct {
|
||||
selection: [2]int,
|
||||
len: int,
|
||||
text: [0]byte, // string(us.text[:us.len]) --- requiring #no_bounds_check
|
||||
}
|
||||
|
||||
Translation :: enum u32 {
|
||||
Start,
|
||||
End,
|
||||
Left,
|
||||
Right,
|
||||
Up,
|
||||
Down,
|
||||
Word_Left,
|
||||
Word_Right,
|
||||
Word_Start,
|
||||
Word_End,
|
||||
Soft_Line_Start,
|
||||
Soft_Line_End,
|
||||
}
|
||||
|
||||
|
||||
init :: proc(s: ^State, undo_text_allocator, undo_state_allocator: runtime.Allocator, undo_timeout := DEFAULT_UNDO_TIMEOUT) {
|
||||
s.undo_timeout = undo_timeout
|
||||
|
||||
// Used for allocating `Undo_State`
|
||||
s.undo_text_allocator = undo_text_allocator
|
||||
|
||||
s.undo.allocator = undo_state_allocator
|
||||
s.redo.allocator = undo_state_allocator
|
||||
}
|
||||
|
||||
destroy :: proc(s: ^State) {
|
||||
undo_clear(s, &s.undo)
|
||||
undo_clear(s, &s.redo)
|
||||
delete(s.undo)
|
||||
delete(s.redo)
|
||||
s.builder = nil
|
||||
}
|
||||
|
||||
|
||||
// Call at the beginning of each frame
|
||||
begin :: proc(s: ^State, id: u64, builder: ^strings.Builder) {
|
||||
assert(builder != nil)
|
||||
if s.id != 0 {
|
||||
end(s)
|
||||
}
|
||||
s.id = id
|
||||
s.selection = {len(builder.buf), 0}
|
||||
s.builder = builder
|
||||
s.current_time = time.tick_now()
|
||||
if s.undo_timeout <= 0 {
|
||||
s.undo_timeout = DEFAULT_UNDO_TIMEOUT
|
||||
}
|
||||
set_text(s, string(s.builder.buf[:]))
|
||||
undo_clear(s, &s.undo)
|
||||
undo_clear(s, &s.redo)
|
||||
}
|
||||
|
||||
// Call at the end of each frame
|
||||
end :: proc(s: ^State) {
|
||||
s.id = 0
|
||||
s.builder = nil
|
||||
}
|
||||
|
||||
set_text :: proc(s: ^State, text: string) {
|
||||
strings.builder_reset(s.builder)
|
||||
strings.write_string(s.builder, text)
|
||||
}
|
||||
|
||||
|
||||
undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
|
||||
text := string(s.builder.buf[:])
|
||||
item := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator))
|
||||
item.selection = s.selection
|
||||
item.len = len(text)
|
||||
#no_bounds_check {
|
||||
runtime.copy(item.text[:len(text)], text)
|
||||
}
|
||||
append(undo, item)
|
||||
}
|
||||
|
||||
undo :: proc(s: ^State, undo, redo: ^[dynamic]^Undo_State) {
|
||||
if len(undo) > 0 {
|
||||
undo_state_push(s, redo)
|
||||
item := pop(undo)
|
||||
s.selection = item.selection
|
||||
#no_bounds_check {
|
||||
set_text(s, string(item.text[:item.len]))
|
||||
}
|
||||
free(item, s.undo_text_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
undo_clear :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
|
||||
for len(undo) > 0 {
|
||||
item := pop(undo)
|
||||
free(item, s.undo_text_allocator)
|
||||
}
|
||||
}
|
||||
|
||||
undo_check :: proc(s: ^State) {
|
||||
undo_clear(s, &s.redo)
|
||||
if time.tick_diff(s.last_edit_time, s.current_time) > s.undo_timeout {
|
||||
undo_state_push(s, &s.undo)
|
||||
}
|
||||
s.last_edit_time = s.current_time
|
||||
}
|
||||
|
||||
|
||||
|
||||
input_text :: proc(s: ^State, text: string) {
|
||||
if len(text) == 0 {
|
||||
return
|
||||
}
|
||||
if has_selection(s) {
|
||||
selection_delete(s)
|
||||
}
|
||||
insert(s, s.selection[0], text)
|
||||
offset := s.selection[0] + len(text)
|
||||
s.selection = {offset, offset}
|
||||
}
|
||||
|
||||
input_runes :: proc(s: ^State, text: []rune) {
|
||||
if len(text) == 0 {
|
||||
return
|
||||
}
|
||||
if has_selection(s) {
|
||||
selection_delete(s)
|
||||
}
|
||||
offset := s.selection[0]
|
||||
for r in text {
|
||||
b, w := utf8.encode_rune(r)
|
||||
insert(s, offset, string(b[:w]))
|
||||
offset += w
|
||||
}
|
||||
s.selection = {offset, offset}
|
||||
}
|
||||
|
||||
|
||||
insert :: proc(s: ^State, at: int, text: string) {
|
||||
undo_check(s)
|
||||
inject_at(&s.builder.buf, at, text)
|
||||
}
|
||||
|
||||
remove :: proc(s: ^State, lo, hi: int) {
|
||||
undo_check(s)
|
||||
remove_range(&s.builder.buf, lo, hi)
|
||||
}
|
||||
|
||||
|
||||
|
||||
has_selection :: proc(s: ^State) -> bool {
|
||||
return s.selection[0] != s.selection[1]
|
||||
}
|
||||
|
||||
sorted_selection :: proc(s: ^State) -> (lo, hi: int) {
|
||||
lo = min(s.selection[0], s.selection[1])
|
||||
hi = max(s.selection[0], s.selection[1])
|
||||
lo = clamp(lo, 0, len(s.builder.buf))
|
||||
hi = clamp(hi, 0, len(s.builder.buf))
|
||||
s.selection[0] = lo
|
||||
s.selection[1] = hi
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
selection_delete :: proc(s: ^State) {
|
||||
lo, hi := sorted_selection(s)
|
||||
remove(s, lo, hi)
|
||||
s.selection = {lo, lo}
|
||||
}
|
||||
|
||||
|
||||
|
||||
translate_position :: proc(s: ^State, pos: int, t: Translation) -> int {
|
||||
is_continuation_byte :: proc(b: byte) -> bool {
|
||||
return b <= 0x80 && b < 0xc0
|
||||
}
|
||||
is_space :: proc(b: byte) -> bool {
|
||||
return b == ' ' || b == '\t' || b == '\n'
|
||||
}
|
||||
|
||||
buf := s.builder.buf[:]
|
||||
|
||||
pos := pos
|
||||
pos = clamp(pos, 0, len(buf))
|
||||
|
||||
switch t {
|
||||
case .Start:
|
||||
pos = 0
|
||||
case .End:
|
||||
pos = len(buf)
|
||||
case .Left:
|
||||
pos -= 1
|
||||
for pos >= 0 && is_continuation_byte(buf[pos]) {
|
||||
pos -= 1
|
||||
}
|
||||
case .Right:
|
||||
pos += 1
|
||||
for pos < len(buf) && is_continuation_byte(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
case .Up:
|
||||
pos = s.up_index
|
||||
case .Down:
|
||||
pos = s.down_index
|
||||
case .Word_Left:
|
||||
for pos > 0 && is_space(buf[pos-1]) {
|
||||
pos -= 1
|
||||
}
|
||||
for pos > 0 && !is_space(buf[pos-1]) {
|
||||
pos -= 1
|
||||
}
|
||||
case .Word_Right:
|
||||
for pos < len(buf) && !is_space(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
for pos < len(buf) && is_space(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
case .Word_Start:
|
||||
for pos > 0 && !is_space(buf[pos-1]) {
|
||||
pos -= 1
|
||||
}
|
||||
case .Word_End:
|
||||
for pos < len(buf) && !is_space(buf[pos]) {
|
||||
pos += 1
|
||||
}
|
||||
case .Soft_Line_Start:
|
||||
pos = s.line_start
|
||||
case .Soft_Line_End:
|
||||
pos = s.line_end
|
||||
}
|
||||
return clamp(pos, 0, len(buf))
|
||||
}
|
||||
|
||||
move_to :: proc(s: ^State, t: Translation) {
|
||||
if t == .Left && has_selection(s) {
|
||||
lo, _ := sorted_selection(s)
|
||||
s.selection = {lo, lo}
|
||||
} else if t == .Right && has_selection(s) {
|
||||
_, hi := sorted_selection(s)
|
||||
s.selection = {hi, hi}
|
||||
} else {
|
||||
pos := translate_position(s, s.selection[0], t)
|
||||
s.selection = {pos, pos}
|
||||
}
|
||||
}
|
||||
select_to :: proc(s: ^State, t: Translation) {
|
||||
s.selection[0] = translate_position(s, s.selection[0], t)
|
||||
}
|
||||
delete_to :: proc(s: ^State, t: Translation) {
|
||||
if has_selection(s) {
|
||||
selection_delete(s)
|
||||
} else {
|
||||
lo := s.selection[0]
|
||||
hi := translate_position(s, lo, t)
|
||||
lo, hi = min(lo, hi), max(lo, hi)
|
||||
remove(s, lo, hi)
|
||||
s.selection = {lo, lo}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
current_selected_text :: proc(s: ^State) -> string {
|
||||
lo, hi := sorted_selection(s)
|
||||
return string(s.builder.buf[lo:hi])
|
||||
}
|
||||
|
||||
|
||||
cut :: proc(s: ^State) -> bool {
|
||||
if copy(s) {
|
||||
selection_delete(s)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
copy :: proc(s: ^State) -> bool {
|
||||
if s.set_clipboard != nil {
|
||||
return s.set_clipboard(s.clipboard_user_data, current_selected_text(s))
|
||||
}
|
||||
return s.set_clipboard != nil
|
||||
}
|
||||
|
||||
paste :: proc(s: ^State) -> bool {
|
||||
if s.get_clipboard != nil {
|
||||
input_text(s, s.get_clipboard(s.clipboard_user_data) or_return)
|
||||
}
|
||||
return s.get_clipboard != nil
|
||||
}
|
||||
|
||||
|
||||
Command_Set :: distinct bit_set[Command; u32]
|
||||
|
||||
Command :: enum u32 {
|
||||
None,
|
||||
Undo,
|
||||
Redo,
|
||||
New_Line, // multi-lines
|
||||
Cut,
|
||||
Copy,
|
||||
Paste,
|
||||
Select_All,
|
||||
Backspace,
|
||||
Delete,
|
||||
Delete_Word_Left,
|
||||
Delete_Word_Right,
|
||||
Left,
|
||||
Right,
|
||||
Up, // multi-lines
|
||||
Down, // multi-lines
|
||||
Word_Left,
|
||||
Word_Right,
|
||||
Start,
|
||||
End,
|
||||
Line_Start,
|
||||
Line_End,
|
||||
Select_Left,
|
||||
Select_Right,
|
||||
Select_Up, // multi-lines
|
||||
Select_Down, // multi-lines
|
||||
Select_Word_Left,
|
||||
Select_Word_Right,
|
||||
Select_Start,
|
||||
Select_End,
|
||||
Select_Line_Start,
|
||||
Select_Line_End,
|
||||
}
|
||||
|
||||
MULTILINE_COMMANDS :: Command_Set{.New_Line, .Up, .Down, .Select_Up, .Select_Down}
|
||||
|
||||
perform_command :: proc(s: ^State, cmd: Command) {
|
||||
switch cmd {
|
||||
case .None: /**/
|
||||
case .Undo: undo(s, &s.undo, &s.redo)
|
||||
case .Redo: undo(s, &s.redo, &s.undo)
|
||||
case .New_Line: input_text(s, "\n")
|
||||
case .Cut: cut(s)
|
||||
case .Copy: copy(s)
|
||||
case .Paste: paste(s)
|
||||
case .Select_All: s.selection = {len(s.builder.buf), 0}
|
||||
case .Backspace: delete_to(s, .Left)
|
||||
case .Delete: delete_to(s, .Right)
|
||||
case .Delete_Word_Left: delete_to(s, .Word_Left)
|
||||
case .Delete_Word_Right: delete_to(s, .Word_Right)
|
||||
case .Left: move_to(s, .Left)
|
||||
case .Right: move_to(s, .Right)
|
||||
case .Up: move_to(s, .Up)
|
||||
case .Down: move_to(s, .Down)
|
||||
case .Word_Left: move_to(s, .Word_Left)
|
||||
case .Word_Right: move_to(s, .Word_Right)
|
||||
case .Start: move_to(s, .Start)
|
||||
case .End: move_to(s, .End)
|
||||
case .Line_Start: move_to(s, .Soft_Line_Start)
|
||||
case .Line_End: move_to(s, .Soft_Line_End)
|
||||
case .Select_Left: select_to(s, .Left)
|
||||
case .Select_Right: select_to(s, .Right)
|
||||
case .Select_Up: select_to(s, .Up)
|
||||
case .Select_Down: select_to(s, .Down)
|
||||
case .Select_Word_Left: select_to(s, .Word_Left)
|
||||
case .Select_Word_Right: select_to(s, .Word_Right)
|
||||
case .Select_Start: select_to(s, .Start)
|
||||
case .Select_End: select_to(s, .End)
|
||||
case .Select_Line_Start: select_to(s, .Soft_Line_Start)
|
||||
case .Select_Line_End: select_to(s, .Soft_Line_End)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//+ignore
|
||||
//+build ignore
|
||||
package i18n
|
||||
|
||||
/*
|
||||
|
||||
+34
-32
@@ -17,7 +17,7 @@ MAX_DURATION :: Duration(1<<63 - 1)
|
||||
IS_SUPPORTED :: _IS_SUPPORTED
|
||||
|
||||
Time :: struct {
|
||||
_nsec: i64, // zero is 1970-01-01 00:00:00
|
||||
_nsec: i64, // Measured in UNIX nanonseconds
|
||||
}
|
||||
|
||||
Month :: enum int {
|
||||
@@ -59,36 +59,36 @@ sleep :: proc "contextless" (d: Duration) {
|
||||
_sleep(d)
|
||||
}
|
||||
|
||||
stopwatch_start :: proc(using stopwatch: ^Stopwatch) {
|
||||
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
if !running {
|
||||
_start_time = tick_now()
|
||||
running = true
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch_stop :: proc(using stopwatch: ^Stopwatch) {
|
||||
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
if running {
|
||||
_accumulation += tick_diff(_start_time, tick_now())
|
||||
running = false
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch_reset :: proc(using stopwatch: ^Stopwatch) {
|
||||
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
_accumulation = {}
|
||||
running = false
|
||||
}
|
||||
|
||||
stopwatch_duration :: proc(using stopwatch: Stopwatch) -> Duration {
|
||||
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
|
||||
if !running { return _accumulation }
|
||||
return _accumulation + tick_diff(_start_time, tick_now())
|
||||
}
|
||||
|
||||
diff :: proc(start, end: Time) -> Duration {
|
||||
diff :: proc "contextless" (start, end: Time) -> Duration {
|
||||
d := end._nsec - start._nsec
|
||||
return Duration(d)
|
||||
}
|
||||
|
||||
since :: proc(start: Time) -> Duration {
|
||||
since :: proc "contextless" (start: Time) -> Duration {
|
||||
return diff(start, now())
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ duration_hours :: proc "contextless" (d: Duration) -> f64 {
|
||||
return f64(hour) + f64(nsec)/(60*60*1e9)
|
||||
}
|
||||
|
||||
duration_round :: proc(d, m: Duration) -> Duration {
|
||||
_less_than_half :: #force_inline proc(x, y: Duration) -> bool {
|
||||
duration_round :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
_less_than_half :: #force_inline proc "contextless" (x, y: Duration) -> bool {
|
||||
return u64(x)+u64(x) < u64(y)
|
||||
}
|
||||
|
||||
@@ -146,45 +146,45 @@ duration_round :: proc(d, m: Duration) -> Duration {
|
||||
return MAX_DURATION
|
||||
}
|
||||
|
||||
duration_truncate :: proc(d, m: Duration) -> Duration {
|
||||
duration_truncate :: proc "contextless" (d, m: Duration) -> Duration {
|
||||
return d if m <= 0 else d - d%m
|
||||
}
|
||||
|
||||
date :: proc(t: Time) -> (year: int, month: Month, day: int) {
|
||||
date :: proc "contextless" (t: Time) -> (year: int, month: Month, day: int) {
|
||||
year, month, day, _ = _abs_date(_time_abs(t), true)
|
||||
return
|
||||
}
|
||||
|
||||
year :: proc(t: Time) -> (year: int) {
|
||||
year :: proc "contextless" (t: Time) -> (year: int) {
|
||||
year, _, _, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
month :: proc(t: Time) -> (month: Month) {
|
||||
month :: proc "contextless" (t: Time) -> (month: Month) {
|
||||
_, month, _, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
day :: proc(t: Time) -> (day: int) {
|
||||
day :: proc "contextless" (t: Time) -> (day: int) {
|
||||
_, _, day, _ = _date(t, true)
|
||||
return
|
||||
}
|
||||
|
||||
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
|
||||
|
||||
clock_from_time :: proc(t: Time) -> (hour, min, sec: int) {
|
||||
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
|
||||
return clock_from_seconds(_time_abs(t))
|
||||
}
|
||||
|
||||
clock_from_duration :: proc(d: Duration) -> (hour, min, sec: int) {
|
||||
clock_from_duration :: proc "contextless" (d: Duration) -> (hour, min, sec: int) {
|
||||
return clock_from_seconds(u64(d/1e9))
|
||||
}
|
||||
|
||||
clock_from_stopwatch :: proc(s: Stopwatch) -> (hour, min, sec: int) {
|
||||
clock_from_stopwatch :: proc "contextless" (s: Stopwatch) -> (hour, min, sec: int) {
|
||||
return clock_from_duration(stopwatch_duration(s))
|
||||
}
|
||||
|
||||
clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
|
||||
clock_from_seconds :: proc "contextless" (nsec: u64) -> (hour, min, sec: int) {
|
||||
sec = int(nsec % SECONDS_PER_DAY)
|
||||
hour = sec / SECONDS_PER_HOUR
|
||||
sec -= hour * SECONDS_PER_HOUR
|
||||
@@ -193,11 +193,11 @@ clock_from_seconds :: proc(nsec: u64) -> (hour, min, sec: int) {
|
||||
return
|
||||
}
|
||||
|
||||
read_cycle_counter :: proc() -> u64 {
|
||||
read_cycle_counter :: proc "contextless" () -> u64 {
|
||||
return u64(intrinsics.read_cycle_counter())
|
||||
}
|
||||
|
||||
unix :: proc(sec: i64, nsec: i64) -> Time {
|
||||
unix :: proc "contextless" (sec: i64, nsec: i64) -> Time {
|
||||
sec, nsec := sec, nsec
|
||||
if nsec < 0 || nsec >= 1e9 {
|
||||
n := nsec / 1e9
|
||||
@@ -208,20 +208,20 @@ unix :: proc(sec: i64, nsec: i64) -> Time {
|
||||
sec -= 1
|
||||
}
|
||||
}
|
||||
return Time{(sec*1e9 + nsec) + UNIX_TO_INTERNAL}
|
||||
return Time{(sec*1e9 + nsec)}
|
||||
}
|
||||
|
||||
to_unix_seconds :: time_to_unix
|
||||
time_to_unix :: proc(t: Time) -> i64 {
|
||||
time_to_unix :: proc "contextless" (t: Time) -> i64 {
|
||||
return t._nsec/1e9
|
||||
}
|
||||
|
||||
to_unix_nanoseconds :: time_to_unix_nano
|
||||
time_to_unix_nano :: proc(t: Time) -> i64 {
|
||||
time_to_unix_nano :: proc "contextless" (t: Time) -> i64 {
|
||||
return t._nsec
|
||||
}
|
||||
|
||||
time_add :: proc(t: Time, d: Duration) -> Time {
|
||||
time_add :: proc "contextless" (t: Time, d: Duration) -> Time {
|
||||
return Time{t._nsec + i64(d)}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ time_add :: proc(t: Time, d: Duration) -> Time {
|
||||
// On Windows it depends but is comparable with regular sleep in the worst case.
|
||||
// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
|
||||
// tell Windows to use a more accurate timer for your process.
|
||||
accurate_sleep :: proc(d: Duration) {
|
||||
accurate_sleep :: proc "contextless" (d: Duration) {
|
||||
to_sleep, estimate, mean, m2, count: Duration
|
||||
|
||||
to_sleep = d
|
||||
@@ -279,19 +279,19 @@ ABSOLUTE_TO_UNIX :: -UNIX_TO_ABSOLUTE
|
||||
|
||||
|
||||
@(private)
|
||||
_date :: proc(t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_date :: proc "contextless" (t: Time, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
year, month, day, yday = _abs_date(_time_abs(t), full)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_time_abs :: proc(t: Time) -> u64 {
|
||||
_time_abs :: proc "contextless" (t: Time) -> u64 {
|
||||
return u64(t._nsec/1e9 + UNIX_TO_ABSOLUTE)
|
||||
}
|
||||
|
||||
@(private)
|
||||
_abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_is_leap_year :: proc(year: int) -> bool {
|
||||
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
@@ -352,9 +352,11 @@ _abs_date :: proc(abs: u64, full: bool) -> (year: int, month: Month, day: int, y
|
||||
return
|
||||
}
|
||||
|
||||
datetime_to_time :: proc(year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
divmod :: proc(year: int, divisor: int) -> (div: int, mod: int) {
|
||||
assert(divisor > 0)
|
||||
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
|
||||
if divisor <= 0 {
|
||||
intrinsics.debug_trap()
|
||||
}
|
||||
div = int(year / divisor)
|
||||
mod = year % divisor
|
||||
return
|
||||
|
||||
@@ -22,3 +22,5 @@ _tick_now :: proc "contextless" () -> Tick {
|
||||
return {}
|
||||
}
|
||||
|
||||
_yield :: proc "contextless" () {
|
||||
}
|
||||
|
||||
@@ -7,9 +7,16 @@ _IS_SUPPORTED :: true
|
||||
|
||||
_now :: proc "contextless" () -> Time {
|
||||
file_time: win32.FILETIME
|
||||
win32.GetSystemTimeAsFileTime(&file_time)
|
||||
ns := win32.FILETIME_as_unix_nanoseconds(file_time)
|
||||
return Time{_nsec=ns}
|
||||
|
||||
ns: i64
|
||||
|
||||
// monotonic
|
||||
win32.GetSystemTimePreciseAsFileTime(&file_time)
|
||||
|
||||
dt := u64(transmute(u64le)file_time) // in 100ns units
|
||||
ns = i64((dt - 116444736000000000) * 100) // convert to ns
|
||||
|
||||
return unix(0, ns)
|
||||
}
|
||||
|
||||
_sleep :: proc "contextless" (d: Duration) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import miniaudio "vendor:miniaudio"
|
||||
import PM "vendor:portmidi"
|
||||
import rl "vendor:raylib"
|
||||
import exr "vendor:OpenEXRCore"
|
||||
import cgltf "vendor:cgltf"
|
||||
|
||||
import SDL "vendor:sdl2"
|
||||
import SDLNet "vendor:sdl2/net"
|
||||
@@ -33,6 +34,7 @@ _ :: miniaudio
|
||||
_ :: PM
|
||||
_ :: rl
|
||||
_ :: exr
|
||||
_ :: cgltf
|
||||
|
||||
_ :: SDL
|
||||
_ :: SDLNet
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ void big_int_and (BigInt *dst, BigInt const *x, BigInt const *y);
|
||||
void big_int_and_not(BigInt *dst, BigInt const *x, BigInt const *y);
|
||||
void big_int_xor (BigInt *dst, BigInt const *x, BigInt const *y);
|
||||
void big_int_or (BigInt *dst, BigInt const *x, BigInt const *y);
|
||||
void big_int_not (BigInt *dst, BigInt const *x, u64 bit_count, bool is_signed);
|
||||
void big_int_not (BigInt *dst, BigInt const *x, i32 bit_count, bool is_signed);
|
||||
|
||||
|
||||
void big_int_add_eq(BigInt *dst, BigInt const *x);
|
||||
|
||||
+14
-7
@@ -298,17 +298,19 @@ struct BuildContext {
|
||||
bool ignore_microsoft_magic;
|
||||
bool linker_map_file;
|
||||
|
||||
bool use_separate_modules;
|
||||
bool threaded_checker;
|
||||
bool use_separate_modules;
|
||||
bool threaded_checker;
|
||||
|
||||
bool show_debug_messages;
|
||||
bool show_debug_messages;
|
||||
|
||||
bool copy_file_contents;
|
||||
bool copy_file_contents;
|
||||
|
||||
bool disallow_rtti;
|
||||
bool disallow_rtti;
|
||||
|
||||
bool use_static_map_calls;
|
||||
|
||||
RelocMode reloc_mode;
|
||||
bool disable_red_zone;
|
||||
bool disable_red_zone;
|
||||
|
||||
|
||||
u32 cmd_doc_flags;
|
||||
@@ -326,7 +328,7 @@ struct BuildContext {
|
||||
BlockingMutex target_features_mutex;
|
||||
StringSet target_features_set;
|
||||
String target_features_string;
|
||||
|
||||
String minimum_os_version_string;
|
||||
};
|
||||
|
||||
gb_global BuildContext build_context = {0};
|
||||
@@ -1380,6 +1382,11 @@ bool init_build_paths(String init_filename) {
|
||||
produces_output_file = true;
|
||||
}
|
||||
|
||||
|
||||
if (build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR) {
|
||||
bc->no_dynamic_literals = true;
|
||||
}
|
||||
|
||||
if (!produces_output_file) {
|
||||
// Command doesn't produce output files. We're done.
|
||||
return true;
|
||||
|
||||
+99
-2
@@ -1533,10 +1533,10 @@ bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast
|
||||
}
|
||||
|
||||
bool is_defined = check_identifier_exists(c->scope, arg);
|
||||
gb_unused(is_defined);
|
||||
// gb_unused(is_defined);
|
||||
operand->type = t_untyped_bool;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_bool(false);
|
||||
operand->value = exact_value_bool(is_defined);
|
||||
|
||||
} else if (name == "config") {
|
||||
if (ce->args.count != 2) {
|
||||
@@ -3651,6 +3651,59 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
operand->mode = Addressing_NoValue;
|
||||
break;
|
||||
|
||||
case BuiltinProc_raw_data:
|
||||
{
|
||||
Operand x = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (!is_operand_value(x)) {
|
||||
gbString s = expr_to_string(x.expr);
|
||||
error(call, "'%.*s' expects a string, slice, dynamic array, or pointer to array type, got %s", LIT(builtin_name), s);
|
||||
gb_string_free(s);
|
||||
return false;
|
||||
}
|
||||
Type *t = base_type(x.type);
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = nullptr;
|
||||
switch (t->kind) {
|
||||
case Type_Slice:
|
||||
operand->type = alloc_type_multi_pointer(t->MultiPointer.elem);
|
||||
break;
|
||||
case Type_DynamicArray:
|
||||
operand->type = alloc_type_multi_pointer(t->DynamicArray.elem);
|
||||
break;
|
||||
case Type_Basic:
|
||||
if (t->Basic.kind == Basic_string) {
|
||||
operand->type = alloc_type_multi_pointer(t_u8);
|
||||
}
|
||||
break;
|
||||
case Type_Pointer:
|
||||
case Type_MultiPointer:
|
||||
{
|
||||
Type *base = base_type(type_deref(t, true));
|
||||
switch (base->kind) {
|
||||
case Type_Array:
|
||||
case Type_EnumeratedArray:
|
||||
case Type_SimdVector:
|
||||
operand->type = alloc_type_multi_pointer(base_array_type(base));
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (operand->type == nullptr) {
|
||||
gbString s = type_to_string(x.type);
|
||||
error(call, "'%.*s' expects a string, slice, dynamic array, or pointer to array type, got %s", LIT(builtin_name), s);
|
||||
gb_string_free(s);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_read_cycle_counter:
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = t_i64;
|
||||
@@ -4129,6 +4182,13 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *elem = type_deref(ptr0.type);
|
||||
if (type_size_of(elem) == 0) {
|
||||
gbString str = type_to_string(ptr0.type);
|
||||
error(ptr0.expr, "Expected a pointer to a non-zero sized element for '%.*s', got %s", LIT(builtin_name), str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -5310,6 +5370,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_type_map_info:
|
||||
{
|
||||
Operand op = {};
|
||||
Type *bt = check_type(c, ce->args[0]);
|
||||
Type *type = base_type(bt);
|
||||
if (type == nullptr || type == t_invalid) {
|
||||
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
if (!is_type_map(type)) {
|
||||
gbString t = type_to_string(type);
|
||||
error(ce->args[0], "Expected a map type for '%.*s', got %s", LIT(builtin_name), t);
|
||||
gb_string_free(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
add_map_key_type_dependencies(c, type);
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = t_map_info_ptr;
|
||||
break;
|
||||
}
|
||||
case BuiltinProc_type_map_cell_info:
|
||||
{
|
||||
Operand op = {};
|
||||
Type *bt = check_type(c, ce->args[0]);
|
||||
Type *type = base_type(bt);
|
||||
if (type == nullptr || type == t_invalid) {
|
||||
error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = t_map_cell_info_ptr;
|
||||
break;
|
||||
}
|
||||
|
||||
case BuiltinProc_constant_utf16_cstring:
|
||||
{
|
||||
String value = {};
|
||||
|
||||
@@ -1488,6 +1488,11 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
|
||||
if (!(e->flags & EntityFlag_Using)) {
|
||||
continue;
|
||||
}
|
||||
if (is_blank_ident(e->token)) {
|
||||
error(e->token, "'using' a procedure parameter requires a non blank identifier");
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
|
||||
String name = e->token.string;
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
|
||||
+152
-51
@@ -285,6 +285,37 @@ void error_operand_no_value(Operand *o) {
|
||||
}
|
||||
}
|
||||
|
||||
void add_map_get_dependencies(CheckerContext *c) {
|
||||
if (build_context.use_static_map_calls) {
|
||||
add_package_dependency(c, "runtime", "map_desired_position");
|
||||
add_package_dependency(c, "runtime", "map_probe_distance");
|
||||
} else {
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_get");
|
||||
}
|
||||
}
|
||||
|
||||
void add_map_set_dependencies(CheckerContext *c) {
|
||||
init_core_source_code_location(c->checker);
|
||||
|
||||
if (t_map_set_proc == nullptr) {
|
||||
Type *map_set_args[5] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr, /*value*/t_rawptr, /*#caller_location*/t_source_code_location};
|
||||
t_map_set_proc = alloc_type_proc_from_types(map_set_args, gb_count_of(map_set_args), t_rawptr, false, ProcCC_Odin);
|
||||
}
|
||||
|
||||
if (build_context.use_static_map_calls) {
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_check_grow");
|
||||
add_package_dependency(c, "runtime", "map_insert_hash_dynamic");
|
||||
} else {
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_set");
|
||||
}
|
||||
}
|
||||
|
||||
void add_map_reserve_dependencies(CheckerContext *c) {
|
||||
init_core_source_code_location(c->checker);
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_reserve");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_scope_decls(CheckerContext *c, Slice<Ast *> const &nodes, isize reserve_size) {
|
||||
Scope *s = c->scope;
|
||||
@@ -580,6 +611,9 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (operand->mode == Addressing_ProcGroup && !is_type_proc(type)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
Type *s = operand->type;
|
||||
|
||||
@@ -731,16 +765,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
|
||||
}
|
||||
|
||||
// TODO(bill): Determine which rule is a better on in practice
|
||||
#if 1
|
||||
if (dst->Union.variants.count == 1) {
|
||||
Type *vt = dst->Union.variants[0];
|
||||
i64 score = check_distance_between_types(c, operand, vt);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
if (dst->Union.variants.count == 1) {
|
||||
Type *vt = dst->Union.variants[0];
|
||||
i64 score = check_distance_between_types(c, operand, vt);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
#else
|
||||
// NOTE(bill): check to see you can assign to it with one of the variants?
|
||||
} else if (is_type_untyped(src)) {
|
||||
i64 prev_lowest_score = -1;
|
||||
i64 lowest_score = -1;
|
||||
for_array(i, dst->Union.variants) {
|
||||
@@ -764,7 +795,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
|
||||
return lowest_score+2;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_relative_pointer(dst)) {
|
||||
@@ -821,11 +852,12 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
|
||||
if (are_types_identical(src, dst)) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem);
|
||||
if (distance >= 0) {
|
||||
return distance + 7;
|
||||
if (dst->Matrix.row_count == dst->Matrix.column_count) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem);
|
||||
if (distance >= 0) {
|
||||
return distance + 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -971,22 +1003,24 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co
|
||||
}
|
||||
|
||||
if (operand->mode == Addressing_ProcGroup) {
|
||||
Array<Entity *> procs = proc_group_entities(c, *operand);
|
||||
bool good = false;
|
||||
// NOTE(bill): These should be done
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
if (t == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
Operand x = {};
|
||||
x.mode = Addressing_Value;
|
||||
x.type = t;
|
||||
if (check_is_assignable_to(c, &x, type)) {
|
||||
Entity *e = procs[i];
|
||||
add_entity_use(c, operand->expr, e);
|
||||
good = true;
|
||||
break;
|
||||
if (type != nullptr && is_type_proc(type)) {
|
||||
Array<Entity *> procs = proc_group_entities(c, *operand);
|
||||
// NOTE(bill): These should be done
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
if (t == t_invalid) {
|
||||
continue;
|
||||
}
|
||||
Operand x = {};
|
||||
x.mode = Addressing_Value;
|
||||
x.type = t;
|
||||
if (check_is_assignable_to(c, &x, type)) {
|
||||
Entity *e = procs[i];
|
||||
add_entity_use(c, operand->expr, e);
|
||||
good = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1363,8 +1397,6 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
|
||||
bool key = is_polymorphic_type_assignable(c, poly->Map.key, source->Map.key, true, modify_type);
|
||||
bool value = is_polymorphic_type_assignable(c, poly->Map.value, source->Map.value, true, modify_type);
|
||||
if (key || value) {
|
||||
poly->Map.entry_type = nullptr;
|
||||
poly->Map.internal_type = nullptr;
|
||||
poly->Map.lookup_result_type = nullptr;
|
||||
init_map_internal_types(poly);
|
||||
return true;
|
||||
@@ -1477,7 +1509,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
|
||||
Array<Entity *> procs = pge->entities;
|
||||
bool skip = false;
|
||||
|
||||
if (type_hint != nullptr) {
|
||||
if (type_hint != nullptr && is_type_proc(type_hint)) {
|
||||
// NOTE(bill): These should be done
|
||||
for_array(i, procs) {
|
||||
Type *t = base_type(procs[i]->type);
|
||||
@@ -1997,7 +2029,7 @@ void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type
|
||||
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
|
||||
error_line("\t This is an UNSAFE operation as string data is assumed to be immutable, \n");
|
||||
error_line("\t whereas slices in general are assumed to be mutable.\n");
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
|
||||
error_line("\tSuggestion: the expression may be casted to %s\n", b);
|
||||
}
|
||||
}
|
||||
@@ -2038,7 +2070,7 @@ void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) {
|
||||
}
|
||||
} else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) {
|
||||
error_line("\tSuggestion: a string may be transmuted to %s\n", b);
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) {
|
||||
} else if (is_type_u8_slice(src) && are_types_identical(dst, t_string) && o->mode != Addressing_Constant) {
|
||||
error_line("\tSuggestion: the expression may be casted to %s\n", b);
|
||||
}
|
||||
}
|
||||
@@ -2916,7 +2948,12 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
if (is_type_untyped(o->type)) {
|
||||
Type *src_t = o->type;
|
||||
Type *dst_t = t;
|
||||
Type *src_bt = base_type(src_t);
|
||||
Type *dst_bt = base_type(dst_t);
|
||||
|
||||
if (is_type_untyped(src_t)) {
|
||||
gbString expr_str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot transmute untyped expression: '%s'", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
@@ -2925,7 +2962,6 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *dst_bt = base_type(t);
|
||||
if (dst_bt == nullptr || dst_bt == t_invalid) {
|
||||
GB_ASSERT(global_error_collector.count != 0);
|
||||
|
||||
@@ -2934,21 +2970,21 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Type *src_bt = base_type(o->type);
|
||||
if (src_bt == nullptr || src_bt == t_invalid) {
|
||||
// NOTE(bill): this should be an error
|
||||
GB_ASSERT(global_error_collector.count != 0);
|
||||
o->mode = Addressing_Value;
|
||||
o->expr = node;
|
||||
o->type = t;
|
||||
o->type = dst_t;
|
||||
return true;
|
||||
}
|
||||
|
||||
i64 srcz = type_size_of(o->type);
|
||||
i64 dstz = type_size_of(t);
|
||||
|
||||
i64 srcz = type_size_of(src_t);
|
||||
i64 dstz = type_size_of(dst_t);
|
||||
if (srcz != dstz) {
|
||||
gbString expr_str = expr_to_string(o->expr);
|
||||
gbString type_str = type_to_string(t);
|
||||
gbString type_str = type_to_string(dst_t);
|
||||
error(o->expr, "Cannot transmute '%s' to '%s', %lld vs %lld bytes", expr_str, type_str, srcz, dstz);
|
||||
gb_string_free(type_str);
|
||||
gb_string_free(expr_str);
|
||||
@@ -2958,16 +2994,53 @@ bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type *t) {
|
||||
}
|
||||
|
||||
if (build_context.vet_extra) {
|
||||
if (are_types_identical(o->type, t)) {
|
||||
gbString str = type_to_string(t);
|
||||
if (are_types_identical(o->type, dst_t)) {
|
||||
gbString str = type_to_string(dst_t);
|
||||
warning(o->expr, "Unneeded transmute to the same type '%s'", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
}
|
||||
|
||||
o->expr = node;
|
||||
o->type = dst_t;
|
||||
if (o->mode == Addressing_Constant) {
|
||||
if (are_types_identical(src_bt, dst_bt)) {
|
||||
return true;
|
||||
}
|
||||
if (is_type_integer(src_t) && is_type_integer(dst_t)) {
|
||||
if (types_have_same_internal_endian(src_t, dst_t)) {
|
||||
ExactValue src_v = exact_value_to_integer(o->value);
|
||||
GB_ASSERT(src_v.kind == ExactValue_Integer);
|
||||
BigInt v = src_v.value_integer;
|
||||
|
||||
BigInt smax = {};
|
||||
BigInt umax = {};
|
||||
|
||||
big_int_from_u64(&smax, 0);
|
||||
big_int_not(&smax, &smax, cast(i32)(srcz*8 - 1), false);
|
||||
|
||||
big_int_from_u64(&umax, 1);
|
||||
BigInt sz_in_bits = big_int_make_i64(srcz*8);
|
||||
big_int_shl_eq(&umax, &sz_in_bits);
|
||||
|
||||
if (is_type_unsigned(src_t) && !is_type_unsigned(dst_t)) {
|
||||
if (big_int_cmp(&v, &smax) >= 0) {
|
||||
big_int_sub_eq(&v, &umax);
|
||||
}
|
||||
} else if (!is_type_unsigned(src_t) && is_type_unsigned(dst_t)) {
|
||||
if (big_int_is_neg(&v)) {
|
||||
big_int_add_eq(&v, &umax);
|
||||
}
|
||||
}
|
||||
|
||||
o->value.kind = ExactValue_Integer;
|
||||
o->value.value_integer = v;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
o->mode = Addressing_Value;
|
||||
o->type = t;
|
||||
o->value = {};
|
||||
return true;
|
||||
}
|
||||
@@ -3204,7 +3277,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
||||
check_assignment(c, x, yt->Map.key, str_lit("map 'not_in'"));
|
||||
}
|
||||
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_get");
|
||||
add_map_get_dependencies(c);
|
||||
} else if (is_type_bit_set(rhs_type)) {
|
||||
Type *yt = base_type(rhs_type);
|
||||
|
||||
@@ -3296,6 +3369,24 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint
|
||||
error(y->expr, "built-in expression in binary expression");
|
||||
return;
|
||||
}
|
||||
if (x->mode == Addressing_ProcGroup) {
|
||||
x->mode = Addressing_Invalid;
|
||||
if (x->proc_group != nullptr) {
|
||||
error(x->expr, "procedure group '%.*s' used in binary expression", LIT(x->proc_group->token.string));
|
||||
} else {
|
||||
error(x->expr, "procedure group used in binary expression");
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (y->mode == Addressing_ProcGroup) {
|
||||
x->mode = Addressing_Invalid;
|
||||
if (x->proc_group != nullptr) {
|
||||
error(y->expr, "procedure group '%.*s' used in binary expression", LIT(y->proc_group->token.string));
|
||||
} else {
|
||||
error(y->expr, "procedure group used in binary expression");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (token_is_shift(op.kind)) {
|
||||
check_shift(c, x, y, node, type_hint);
|
||||
@@ -5454,6 +5545,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
|
||||
GB_ASSERT(is_type_proc(gept));
|
||||
proc_type = gept;
|
||||
pt = &gept->Proc;
|
||||
} else {
|
||||
err = CallArgumentError_WrongTypes;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5788,6 +5881,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
Entity *proc = procs[proc_index];
|
||||
Type *pt = base_type(proc->type);
|
||||
if (!(pt != nullptr && is_type_proc(pt))) {
|
||||
proc_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -6085,6 +6179,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
|
||||
}
|
||||
result_type = t_invalid;
|
||||
} else {
|
||||
GB_ASSERT(valids.count == 1);
|
||||
Ast *ident = operand->expr;
|
||||
while (ident->kind == Ast_SelectorExpr) {
|
||||
Ast *s = ident->SelectorExpr.selector;
|
||||
@@ -6413,7 +6508,9 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
||||
if (e->kind == Entity_TypeName) {
|
||||
if (o->mode != Addressing_Type) {
|
||||
if (show_error) {
|
||||
error(o->expr, "Expected a type for the argument '%.*s'", LIT(e->token.string));
|
||||
gbString expr = expr_to_string(o->expr);
|
||||
error(o->expr, "Expected a type for the argument '%.*s', got %s", LIT(e->token.string), expr);
|
||||
gb_string_free(expr);
|
||||
}
|
||||
err = CallArgumentError_WrongTypes;
|
||||
}
|
||||
@@ -6456,6 +6553,10 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
|
||||
// add_type_info_type(c, o->type);
|
||||
}
|
||||
|
||||
if (show_error && err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
{
|
||||
bool failure = false;
|
||||
Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
|
||||
@@ -8509,8 +8610,8 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
|
||||
if (build_context.no_dynamic_literals && cl->elems.count) {
|
||||
error(node, "Compound literals of dynamic types have been disabled");
|
||||
} else {
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_reserve");
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_set");
|
||||
add_map_reserve_dependencies(c);
|
||||
add_map_set_dependencies(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -8946,8 +9047,8 @@ ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_h
|
||||
o->type = t->Map.value;
|
||||
o->expr = node;
|
||||
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_get");
|
||||
add_package_dependency(c, "runtime", "__dynamic_map_set");
|
||||
add_map_get_dependencies(c);
|
||||
add_map_set_dependencies(c);
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -584,7 +584,11 @@ void check_label(CheckerContext *ctx, Ast *label, Ast *parent) {
|
||||
// Returns 'true' for 'continue', 'false' for 'return'
|
||||
bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, bool is_selector, Entity *e) {
|
||||
if (e == nullptr) {
|
||||
error(us->token, "'using' applied to an unknown entity");
|
||||
if (is_blank_ident(expr)) {
|
||||
error(us->token, "'using' in a statement is not allowed with the blank identifier '_'");
|
||||
} else {
|
||||
error(us->token, "'using' applied to an unknown entity");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+32
-71
@@ -1629,6 +1629,8 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
|
||||
// This is just to add the error message to determine_type_from_polymorphic which
|
||||
// depends on valid position information
|
||||
op.expr = _params;
|
||||
op.mode = Addressing_Invalid;
|
||||
op.type = t_invalid;
|
||||
}
|
||||
if (is_type_polymorphic_type) {
|
||||
type = determine_type_from_polymorphic(ctx, type, op);
|
||||
@@ -2176,70 +2178,36 @@ Type *make_optional_ok_type(Type *value, bool typed) {
|
||||
return t;
|
||||
}
|
||||
|
||||
void init_map_entry_type(Type *type) {
|
||||
GB_ASSERT(type->kind == Type_Map);
|
||||
if (type->Map.entry_type != nullptr) return;
|
||||
|
||||
// NOTE(bill): The preload types may have not been set yet
|
||||
GB_ASSERT(t_map_hash != nullptr);
|
||||
// IMPORTANT NOTE(bill): This must match the definition in dynamic_map_internal.odin
|
||||
enum : i64 {
|
||||
MAP_CACHE_LINE_LOG2 = 6,
|
||||
MAP_CACHE_LINE_SIZE = 1 << MAP_CACHE_LINE_LOG2
|
||||
};
|
||||
GB_STATIC_ASSERT(MAP_CACHE_LINE_SIZE >= 64);
|
||||
void map_cell_size_and_len(Type *type, i64 *size_, i64 *len_) {
|
||||
i64 elem_sz = type_size_of(type);
|
||||
|
||||
/*
|
||||
struct {
|
||||
hash: uintptr,
|
||||
next: int,
|
||||
key: Key,
|
||||
value: Value,
|
||||
i64 len = 1;
|
||||
if (0 < elem_sz && elem_sz < MAP_CACHE_LINE_SIZE) {
|
||||
len = MAP_CACHE_LINE_SIZE / elem_sz;
|
||||
}
|
||||
*/
|
||||
Scope *s = create_scope(nullptr, builtin_pkg->scope);
|
||||
|
||||
auto fields = slice_make<Entity *>(permanent_allocator(), 4);
|
||||
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hash")), t_uintptr, false, 0, EntityState_Resolved);
|
||||
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("next")), t_int, false, 1, EntityState_Resolved);
|
||||
fields[2] = alloc_entity_field(s, make_token_ident(str_lit("key")), type->Map.key, false, 2, EntityState_Resolved);
|
||||
fields[3] = alloc_entity_field(s, make_token_ident(str_lit("value")), type->Map.value, false, 3, EntityState_Resolved);
|
||||
|
||||
Type *entry_type = alloc_type_struct();
|
||||
entry_type->Struct.fields = fields;
|
||||
entry_type->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
|
||||
|
||||
type_set_offsets(entry_type);
|
||||
type->Map.entry_type = entry_type;
|
||||
i64 size = align_formula(elem_sz * len, MAP_CACHE_LINE_SIZE);
|
||||
if (size_) *size_ = size;
|
||||
if (len_) *len_ = len;
|
||||
}
|
||||
|
||||
void init_map_internal_types(Type *type) {
|
||||
GB_ASSERT(type->kind == Type_Map);
|
||||
init_map_entry_type(type);
|
||||
if (type->Map.internal_type != nullptr) return;
|
||||
GB_ASSERT(t_allocator != nullptr);
|
||||
if (type->Map.lookup_result_type != nullptr) return;
|
||||
|
||||
Type *key = type->Map.key;
|
||||
Type *value = type->Map.value;
|
||||
GB_ASSERT(key != nullptr);
|
||||
GB_ASSERT(value != nullptr);
|
||||
|
||||
Type *generated_struct_type = alloc_type_struct();
|
||||
|
||||
/*
|
||||
struct {
|
||||
hashes: []int;
|
||||
entries: [dynamic]EntryType;
|
||||
}
|
||||
*/
|
||||
Scope *s = create_scope(nullptr, builtin_pkg->scope);
|
||||
|
||||
Type *hashes_type = alloc_type_slice(t_int);
|
||||
Type *entries_type = alloc_type_dynamic_array(type->Map.entry_type);
|
||||
|
||||
|
||||
auto fields = slice_make<Entity *>(permanent_allocator(), 2);
|
||||
fields[0] = alloc_entity_field(s, make_token_ident(str_lit("hashes")), hashes_type, false, 0, EntityState_Resolved);
|
||||
fields[1] = alloc_entity_field(s, make_token_ident(str_lit("entries")), entries_type, false, 1, EntityState_Resolved);
|
||||
|
||||
generated_struct_type->Struct.fields = fields;
|
||||
type_set_offsets(generated_struct_type);
|
||||
|
||||
type->Map.internal_type = generated_struct_type;
|
||||
type->Map.lookup_result_type = make_optional_ok_type(value);
|
||||
type->Map.lookup_result_type = make_optional_ok_type(value);
|
||||
}
|
||||
|
||||
void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
|
||||
@@ -2255,35 +2223,27 @@ void add_map_key_type_dependencies(CheckerContext *ctx, Type *key) {
|
||||
}
|
||||
|
||||
if (is_type_simple_compare(key)) {
|
||||
i64 sz = type_size_of(key);
|
||||
if (1 <= sz && sz <= 16) {
|
||||
char buf[20] = {};
|
||||
gb_snprintf(buf, 20, "default_hasher%d", cast(i32)sz);
|
||||
add_package_dependency(ctx, "runtime", buf);
|
||||
return;
|
||||
} else {
|
||||
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||
return;
|
||||
}
|
||||
add_package_dependency(ctx, "runtime", "default_hasher");
|
||||
return;
|
||||
}
|
||||
|
||||
if (key->kind == Type_Struct) {
|
||||
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||
add_package_dependency(ctx, "runtime", "default_hasher");
|
||||
for_array(i, key->Struct.fields) {
|
||||
Entity *field = key->Struct.fields[i];
|
||||
add_map_key_type_dependencies(ctx, field->type);
|
||||
}
|
||||
} else if (key->kind == Type_Union) {
|
||||
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||
add_package_dependency(ctx, "runtime", "default_hasher");
|
||||
for_array(i, key->Union.variants) {
|
||||
Type *v = key->Union.variants[i];
|
||||
add_map_key_type_dependencies(ctx, v);
|
||||
}
|
||||
} else if (key->kind == Type_EnumeratedArray) {
|
||||
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||
add_package_dependency(ctx, "runtime", "default_hasher");
|
||||
add_map_key_type_dependencies(ctx, key->EnumeratedArray.elem);
|
||||
} else if (key->kind == Type_Array) {
|
||||
add_package_dependency(ctx, "runtime", "default_hasher_n");
|
||||
add_package_dependency(ctx, "runtime", "default_hasher");
|
||||
add_map_key_type_dependencies(ctx, key->Array.elem);
|
||||
}
|
||||
}
|
||||
@@ -2330,9 +2290,7 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
|
||||
|
||||
i64 row_count = check_array_count(ctx, &row, mt->row_count);
|
||||
i64 column_count = check_array_count(ctx, &column, mt->column_count);
|
||||
|
||||
Type *elem = check_type_expr(ctx, mt->elem, nullptr);
|
||||
|
||||
|
||||
Type *generic_row = nullptr;
|
||||
Type *generic_column = nullptr;
|
||||
|
||||
@@ -2344,22 +2302,25 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
|
||||
generic_column = column.type;
|
||||
}
|
||||
|
||||
if (row_count < MATRIX_ELEMENT_COUNT_MIN && generic_row == nullptr) {
|
||||
if (generic_row == nullptr && row_count < MATRIX_ELEMENT_COUNT_MIN) {
|
||||
gbString s = expr_to_string(row.expr);
|
||||
error(row.expr, "Invalid matrix row count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
|
||||
if (column_count < MATRIX_ELEMENT_COUNT_MIN && generic_column == nullptr) {
|
||||
if (generic_column == nullptr && column_count < MATRIX_ELEMENT_COUNT_MIN) {
|
||||
gbString s = expr_to_string(column.expr);
|
||||
error(column.expr, "Invalid matrix column count, expected %d+ rows, got %s", MATRIX_ELEMENT_COUNT_MIN, s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
|
||||
if (row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
|
||||
if ((generic_row == nullptr && generic_column == nullptr) && row_count*column_count > MATRIX_ELEMENT_COUNT_MAX) {
|
||||
i64 element_count = row_count*column_count;
|
||||
error(column.expr, "Matrix types are limited to a maximum of %d elements, got %lld", MATRIX_ELEMENT_COUNT_MAX, cast(long long)element_count);
|
||||
}
|
||||
|
||||
|
||||
Type *elem = check_type_expr(ctx, mt->elem, nullptr);
|
||||
|
||||
if (!is_type_valid_for_matrix_elems(elem)) {
|
||||
if (elem == t_typeid) {
|
||||
|
||||
+36
-12
@@ -922,10 +922,13 @@ void init_universal(void) {
|
||||
|
||||
{
|
||||
Type *equal_args[2] = {t_rawptr, t_rawptr};
|
||||
t_equal_proc = alloc_type_proc_from_types(equal_args, 2, t_bool, false, ProcCC_Contextless);
|
||||
t_equal_proc = alloc_type_proc_from_types(equal_args, gb_count_of(equal_args), t_bool, false, ProcCC_Contextless);
|
||||
|
||||
Type *hasher_args[2] = {t_rawptr, t_uintptr};
|
||||
t_hasher_proc = alloc_type_proc_from_types(hasher_args, 2, t_uintptr, false, ProcCC_Contextless);
|
||||
t_hasher_proc = alloc_type_proc_from_types(hasher_args, gb_count_of(hasher_args), t_uintptr, false, ProcCC_Contextless);
|
||||
|
||||
Type *map_get_args[3] = {/*map*/t_rawptr, /*hash*/t_uintptr, /*key*/t_rawptr};
|
||||
t_map_get_proc = alloc_type_proc_from_types(map_get_args, gb_count_of(map_get_args), t_rawptr, false, ProcCC_Contextless);
|
||||
}
|
||||
|
||||
// Constants
|
||||
@@ -1673,7 +1676,18 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
|
||||
}
|
||||
|
||||
void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, DeclInfo *d, bool is_exported) {
|
||||
GB_ASSERT(identifier->kind == Ast_Ident);
|
||||
if (identifier == nullptr) {
|
||||
// NOTE(bill): Should only happen on errors
|
||||
error(e->token, "Invalid variable declaration");
|
||||
return;
|
||||
}
|
||||
if (identifier->kind != Ast_Ident) {
|
||||
// NOTE(bill): This is a safety check
|
||||
gbString s = expr_to_string(identifier);
|
||||
error(identifier, "A variable declaration must be an identifer, got %s", s);
|
||||
gb_string_free(s);
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(e != nullptr && d != nullptr);
|
||||
GB_ASSERT(identifier->Ident.token.string == e->token.string);
|
||||
|
||||
@@ -1922,7 +1936,8 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
|
||||
init_map_internal_types(bt);
|
||||
add_type_info_type_internal(c, bt->Map.key);
|
||||
add_type_info_type_internal(c, bt->Map.value);
|
||||
add_type_info_type_internal(c, bt->Map.internal_type);
|
||||
add_type_info_type_internal(c, t_uintptr); // hash value
|
||||
add_type_info_type_internal(c, t_allocator);
|
||||
break;
|
||||
|
||||
case Type_Tuple:
|
||||
@@ -2144,7 +2159,8 @@ void add_min_dep_type_info(Checker *c, Type *t) {
|
||||
init_map_internal_types(bt);
|
||||
add_min_dep_type_info(c, bt->Map.key);
|
||||
add_min_dep_type_info(c, bt->Map.value);
|
||||
add_min_dep_type_info(c, bt->Map.internal_type);
|
||||
add_min_dep_type_info(c, t_uintptr); // hash value
|
||||
add_min_dep_type_info(c, t_allocator);
|
||||
break;
|
||||
|
||||
case Type_Tuple:
|
||||
@@ -2827,16 +2843,21 @@ void init_core_source_code_location(Checker *c) {
|
||||
return;
|
||||
}
|
||||
t_source_code_location = find_core_type(c, str_lit("Source_Code_Location"));
|
||||
t_source_code_location_ptr = alloc_type_pointer(t_allocator);
|
||||
t_source_code_location_ptr = alloc_type_pointer(t_source_code_location);
|
||||
}
|
||||
|
||||
void init_core_map_type(Checker *c) {
|
||||
if (t_map_hash != nullptr) {
|
||||
if (t_map_info != nullptr) {
|
||||
return;
|
||||
}
|
||||
t_map_hash = find_core_type(c, str_lit("Map_Hash"));
|
||||
t_map_header = find_core_type(c, str_lit("Map_Header"));
|
||||
t_map_header_table = find_core_type(c, str_lit("Map_Header_Table"));
|
||||
init_mem_allocator(c);
|
||||
t_map_info = find_core_type(c, str_lit("Map_Info"));
|
||||
t_map_cell_info = find_core_type(c, str_lit("Map_Cell_Info"));
|
||||
t_raw_map = find_core_type(c, str_lit("Raw_Map"));
|
||||
|
||||
t_map_info_ptr = alloc_type_pointer(t_map_info);
|
||||
t_map_cell_info_ptr = alloc_type_pointer(t_map_cell_info);
|
||||
t_raw_map_ptr = alloc_type_pointer(t_raw_map);
|
||||
}
|
||||
|
||||
void init_preload(Checker *c) {
|
||||
@@ -3227,9 +3248,8 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
}
|
||||
|
||||
DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
|
||||
if (name == ATTRIBUTE_USER_TAG_NAME) {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind != ExactValue_String) {
|
||||
error(elem, "Expected a string value for '%.*s'", LIT(name));
|
||||
}
|
||||
@@ -3241,6 +3261,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
ac->is_static = true;
|
||||
return true;
|
||||
} else if (name == "thread_local") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ac->init_expr_list_count > 0) {
|
||||
error(elem, "A thread local variable declaration cannot have initialization values");
|
||||
} else if (c->foreign_context.curr_library) {
|
||||
@@ -3315,6 +3336,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
}
|
||||
return true;
|
||||
} else if (name == "link_name") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
ac->link_name = ev.value_string;
|
||||
if (!is_foreign_name_valid(ac->link_name)) {
|
||||
@@ -3325,6 +3347,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
}
|
||||
return true;
|
||||
} else if (name == "link_prefix") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
ac->link_prefix = ev.value_string;
|
||||
if (!is_foreign_name_valid(ac->link_prefix)) {
|
||||
@@ -3335,6 +3358,7 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
}
|
||||
return true;
|
||||
} else if (name == "link_section") {
|
||||
ExactValue ev = check_decl_attribute_value(c, value);
|
||||
if (ev.kind == ExactValue_String) {
|
||||
ac->link_section = ev.value_string;
|
||||
if (!is_foreign_name_valid(ac->link_section)) {
|
||||
|
||||
@@ -42,6 +42,8 @@ enum BuiltinProcId {
|
||||
|
||||
BuiltinProc_unreachable,
|
||||
|
||||
BuiltinProc_raw_data,
|
||||
|
||||
BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures
|
||||
|
||||
// "Intrinsics"
|
||||
@@ -275,6 +277,8 @@ BuiltinProc__type_simple_boolean_end,
|
||||
|
||||
BuiltinProc_type_equal_proc,
|
||||
BuiltinProc_type_hasher_proc,
|
||||
BuiltinProc_type_map_info,
|
||||
BuiltinProc_type_map_cell_info,
|
||||
|
||||
BuiltinProc__type_end,
|
||||
|
||||
@@ -338,6 +342,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
|
||||
{STR_LIT("unreachable"), 0, false, Expr_Expr, BuiltinProcPkg_builtin, /*diverging*/true},
|
||||
|
||||
{STR_LIT("raw_data"), 1, false, Expr_Expr, BuiltinProcPkg_builtin},
|
||||
|
||||
{STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE
|
||||
|
||||
|
||||
@@ -566,8 +572,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
|
||||
|
||||
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_map_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
{STR_LIT("type_map_cell_info"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
|
||||
|
||||
|
||||
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
|
||||
|
||||
+11
-4
@@ -499,6 +499,13 @@ i64 exact_value_to_i64(ExactValue v) {
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
u64 exact_value_to_u64(ExactValue v) {
|
||||
v = exact_value_to_integer(v);
|
||||
if (v.kind == ExactValue_Integer) {
|
||||
return big_int_to_u64(&v.value_integer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
f64 exact_value_to_f64(ExactValue v) {
|
||||
v = exact_value_to_float(v);
|
||||
if (v.kind == ExactValue_Float) {
|
||||
@@ -945,15 +952,15 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||
|
||||
case ExactValue_Typeid:
|
||||
switch (op) {
|
||||
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
|
||||
case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid);
|
||||
case Token_CmpEq: return x.value_typeid == y.value_typeid;
|
||||
case Token_NotEq: return x.value_typeid != y.value_typeid;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Procedure:
|
||||
switch (op) {
|
||||
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
|
||||
case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid);
|
||||
case Token_CmpEq: return x.value_typeid == y.value_typeid;
|
||||
case Token_NotEq: return x.value_typeid != y.value_typeid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+44
-19
@@ -537,6 +537,22 @@ namespace lbAbiAmd64SysV {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_llvm_type_slice_like(LLVMTypeRef type) {
|
||||
if (!lb_is_type_kind(type, LLVMStructTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
if (LLVMCountStructElementTypes(type) != 2) {
|
||||
return false;
|
||||
}
|
||||
LLVMTypeRef fields[2] = {};
|
||||
LLVMGetStructElementTypes(type, fields);
|
||||
if (!lb_is_type_kind(fields[0], LLVMPointerTypeKind)) {
|
||||
return false;
|
||||
}
|
||||
return lb_is_type_kind(fields[1], LLVMIntegerTypeKind) && lb_sizeof(fields[1]) == 8;
|
||||
|
||||
}
|
||||
|
||||
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
|
||||
if (is_register(type)) {
|
||||
LLVMAttributeRef attribute = nullptr;
|
||||
@@ -550,16 +566,25 @@ namespace lbAbiAmd64SysV {
|
||||
if (is_mem_cls(cls, attribute_kind)) {
|
||||
LLVMAttributeRef attribute = nullptr;
|
||||
if (attribute_kind == Amd64TypeAttribute_ByVal) {
|
||||
if (!is_calling_convention_odin(calling_convention)) {
|
||||
// if (!is_calling_convention_odin(calling_convention)) {
|
||||
return lb_arg_type_indirect_byval(c, type);
|
||||
}
|
||||
attribute = nullptr;
|
||||
// }
|
||||
// attribute = nullptr;
|
||||
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
|
||||
attribute = lb_create_enum_attribute_with_type(c, "sret", type);
|
||||
}
|
||||
return lb_arg_type_indirect(type, attribute);
|
||||
} else {
|
||||
return lb_arg_type_direct(type, llreg(c, cls), nullptr, nullptr);
|
||||
LLVMTypeRef reg_type = nullptr;
|
||||
if (is_llvm_type_slice_like(type)) {
|
||||
// NOTE(bill): This is to make the ABI look closer to what the
|
||||
// original code is just for slices/strings whilst still adhering
|
||||
// the ABI rules for SysV
|
||||
reg_type = type;
|
||||
} else {
|
||||
reg_type = llreg(c, cls);
|
||||
}
|
||||
return lb_arg_type_direct(type, reg_type, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -982,13 +1007,13 @@ namespace lbAbiArm64 {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) {
|
||||
return (member_count_ <= 4);
|
||||
}
|
||||
|
||||
unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef base_type, unsigned member_count) {
|
||||
return (member_count <= 4);
|
||||
}
|
||||
|
||||
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
|
||||
LLVMTypeRef homo_base_type = {};
|
||||
LLVMTypeRef homo_base_type = nullptr;
|
||||
unsigned homo_member_count = 0;
|
||||
|
||||
if (!return_is_defined) {
|
||||
@@ -996,16 +1021,16 @@ namespace lbAbiArm64 {
|
||||
} else if (is_register(type)) {
|
||||
return non_struct(c, type);
|
||||
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
|
||||
if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) {
|
||||
return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
|
||||
} else {
|
||||
//TODO(Platin): do i need to create stuff that can handle the diffrent return type?
|
||||
// else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
|
||||
|
||||
//LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
|
||||
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
|
||||
return lb_arg_type_indirect(type, attr);
|
||||
}
|
||||
if (is_homogenous_aggregate_small_enough(homo_base_type, homo_member_count)) {
|
||||
return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
|
||||
} else {
|
||||
//TODO(Platin): do i need to create stuff that can handle the diffrent return type?
|
||||
// else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
|
||||
|
||||
//LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
|
||||
LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
|
||||
return lb_arg_type_indirect(type, attr);
|
||||
}
|
||||
} else {
|
||||
i64 size = lb_sizeof(type);
|
||||
if (size <= 16) {
|
||||
|
||||
+434
-84
@@ -140,7 +140,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx) {
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
lbValue lb_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
type = base_type(type);
|
||||
GB_ASSERT(is_type_comparable(type));
|
||||
|
||||
@@ -157,8 +157,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
|
||||
static u32 proc_index = 0;
|
||||
|
||||
char buf[16] = {};
|
||||
isize n = gb_snprintf(buf, 16, "__$equal%u", ++proc_index);
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$equal%u", ++proc_index);
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
@@ -166,6 +166,9 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
map_set(&m->equal_procs, type, p);
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
lb_add_attribute_to_proc(m, p->value, "readonly");
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
|
||||
LLVMValueRef x = LLVMGetParam(p->value, 0);
|
||||
LLVMValueRef y = LLVMGetParam(p->value, 1);
|
||||
x = LLVMBuildPointerCast(p->builder, x, ptr_type, "");
|
||||
@@ -173,6 +176,8 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
lbValue lhs = {x, pt};
|
||||
lbValue rhs = {y, pt};
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
|
||||
lb_add_proc_attribute_at_index(p, 1+1, "nonnull");
|
||||
|
||||
lbBlock *block_same_ptr = lb_create_block(p, "same_ptr");
|
||||
lbBlock *block_diff_ptr = lb_create_block(p, "diff_ptr");
|
||||
@@ -277,28 +282,20 @@ lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type) {
|
||||
lbValue lb_simple_compare_hash(lbProcedure *p, Type *type, lbValue data, lbValue seed) {
|
||||
GB_ASSERT_MSG(is_type_simple_compare(type), "%s", type_to_string(type));
|
||||
|
||||
i64 sz = type_size_of(type);
|
||||
|
||||
if (1 <= sz && sz <= 16) {
|
||||
char name[20] = {};
|
||||
gb_snprintf(name, 20, "default_hasher%d", cast(i32)sz);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
args[0] = data;
|
||||
args[1] = seed;
|
||||
return lb_emit_runtime_call(p, name, args);
|
||||
}
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
args[0] = data;
|
||||
args[1] = seed;
|
||||
args[2] = lb_const_int(p->module, t_int, type_size_of(type));
|
||||
return lb_emit_runtime_call(p, "default_hasher_n", args);
|
||||
return lb_emit_runtime_call(p, "default_hasher", args);
|
||||
}
|
||||
|
||||
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
void lb_add_callsite_force_inline(lbProcedure *p, lbValue ret_value) {
|
||||
LLVMAddCallSiteAttribute(ret_value.value, LLVMAttributeIndex_FunctionIndex, lb_create_enum_attribute(p->module->ctx, "alwaysinline"));
|
||||
}
|
||||
|
||||
lbValue lb_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
type = core_type(type);
|
||||
GB_ASSERT(is_type_valid_for_keys(type));
|
||||
GB_ASSERT_MSG(is_type_valid_for_keys(type), "%s", type_to_string(type));
|
||||
|
||||
Type *pt = alloc_type_pointer(type);
|
||||
|
||||
@@ -310,8 +307,8 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
|
||||
static u32 proc_index = 0;
|
||||
|
||||
char buf[16] = {};
|
||||
isize n = gb_snprintf(buf, 16, "__$hasher%u", ++proc_index);
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$hasher%u", ++proc_index);
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
@@ -320,16 +317,20 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
lb_add_attribute_to_proc(m, p->value, "readonly");
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
|
||||
LLVMValueRef x = LLVMGetParam(p->value, 0);
|
||||
LLVMValueRef y = LLVMGetParam(p->value, 1);
|
||||
lbValue data = {x, t_rawptr};
|
||||
lbValue seed = {y, t_uintptr};
|
||||
|
||||
LLVMAttributeRef nonnull_attr = lb_create_enum_attribute(m->ctx, "nonnull");
|
||||
LLVMAddAttributeAtIndex(p->value, 1+0, nonnull_attr);
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "readonly");
|
||||
|
||||
if (is_type_simple_compare(type)) {
|
||||
lbValue res = lb_simple_compare_hash(p, type, data, seed);
|
||||
lb_add_callsite_force_inline(p, res);
|
||||
LLVMBuildRet(p->builder, res.value);
|
||||
return {p->value, p->type};
|
||||
}
|
||||
@@ -343,7 +344,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
GB_ASSERT(type->Struct.offsets != nullptr);
|
||||
i64 offset = type->Struct.offsets[i];
|
||||
Entity *field = type->Struct.fields[i];
|
||||
lbValue field_hasher = lb_get_hasher_proc_for_type(m, field->type);
|
||||
lbValue field_hasher = lb_hasher_proc_for_type(m, field->type);
|
||||
lbValue ptr = lb_emit_ptr_offset(p, data, lb_const_int(m, t_uintptr, offset));
|
||||
|
||||
args[0] = ptr;
|
||||
@@ -356,11 +357,12 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
|
||||
if (is_type_union_maybe_pointer(type)) {
|
||||
Type *v = type->Union.variants[0];
|
||||
lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v);
|
||||
lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
|
||||
|
||||
args[0] = data;
|
||||
args[1] = seed;
|
||||
lbValue res = lb_emit_call(p, variant_hasher, args);
|
||||
lb_add_callsite_force_inline(p, res);
|
||||
LLVMBuildRet(p->builder, res.value);
|
||||
}
|
||||
|
||||
@@ -379,7 +381,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
Type *v = type->Union.variants[i];
|
||||
lbValue case_tag = lb_const_union_tag(p->module, type, v);
|
||||
|
||||
lbValue variant_hasher = lb_get_hasher_proc_for_type(m, v);
|
||||
lbValue variant_hasher = lb_hasher_proc_for_type(m, v);
|
||||
|
||||
args[0] = data;
|
||||
args[1] = seed;
|
||||
@@ -397,7 +399,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
lb_addr_store(p, pres, seed);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->Array.elem);
|
||||
lbValue elem_hasher = lb_hasher_proc_for_type(m, type->Array.elem);
|
||||
|
||||
auto loop_data = lb_loop_start(p, cast(isize)type->Array.count, t_i32);
|
||||
|
||||
@@ -418,7 +420,7 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
lb_addr_store(p, res, seed);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
lbValue elem_hasher = lb_get_hasher_proc_for_type(m, type->EnumeratedArray.elem);
|
||||
lbValue elem_hasher = lb_hasher_proc_for_type(m, type->EnumeratedArray.elem);
|
||||
|
||||
auto loop_data = lb_loop_start(p, cast(isize)type->EnumeratedArray.count, t_i32);
|
||||
|
||||
@@ -439,12 +441,14 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
args[0] = data;
|
||||
args[1] = seed;
|
||||
lbValue res = lb_emit_runtime_call(p, "default_hasher_cstring", args);
|
||||
lb_add_callsite_force_inline(p, res);
|
||||
LLVMBuildRet(p->builder, res.value);
|
||||
} else if (is_type_string(type)) {
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
args[0] = data;
|
||||
args[1] = seed;
|
||||
lbValue res = lb_emit_runtime_call(p, "default_hasher_string", args);
|
||||
lb_add_callsite_force_inline(p, res);
|
||||
LLVMBuildRet(p->builder, res.value);
|
||||
} else {
|
||||
GB_PANIC("Unhandled type for hasher: %s", type_to_string(type));
|
||||
@@ -454,6 +458,279 @@ lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type) {
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_map_get_proc_for_type(lbModule *m, Type *type) {
|
||||
GB_ASSERT(build_context.use_static_map_calls);
|
||||
type = base_type(type);
|
||||
GB_ASSERT(type->kind == Type_Map);
|
||||
|
||||
|
||||
lbProcedure **found = map_get(&m->map_get_procs, type);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return {(*found)->value, (*found)->type};
|
||||
}
|
||||
static u32 proc_index = 0;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$map_get-%u", ++proc_index);
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_get_proc);
|
||||
map_set(&m->map_get_procs, type, p);
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
lb_add_attribute_to_proc(m, p->value, "readonly");
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
lb_add_attribute_to_proc(m, p->value, "noinline");
|
||||
}
|
||||
|
||||
LLVMValueRef x = LLVMGetParam(p->value, 0);
|
||||
LLVMValueRef y = LLVMGetParam(p->value, 1);
|
||||
LLVMValueRef z = LLVMGetParam(p->value, 2);
|
||||
lbValue map_ptr = {x, t_rawptr};
|
||||
lbValue h = {y, t_uintptr};
|
||||
lbValue key_ptr = {z, t_rawptr};
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "noalias");
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "readonly");
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
|
||||
lb_add_proc_attribute_at_index(p, 1+2, "noalias");
|
||||
lb_add_proc_attribute_at_index(p, 1+2, "readonly");
|
||||
|
||||
lbBlock *loop_block = lb_create_block(p, "loop");
|
||||
lbBlock *hash_block = lb_create_block(p, "hash");
|
||||
lbBlock *probe_block = lb_create_block(p, "probe");
|
||||
lbBlock *increment_block = lb_create_block(p, "increment");
|
||||
lbBlock *hash_compare_block = lb_create_block(p, "hash_compare");
|
||||
lbBlock *key_compare_block = lb_create_block(p, "key_compare");
|
||||
lbBlock *value_block = lb_create_block(p, "value");
|
||||
lbBlock *nil_block = lb_create_block(p, "nil");
|
||||
|
||||
map_ptr = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
|
||||
lbValue map = lb_emit_load(p, map_ptr);
|
||||
|
||||
lbValue length = lb_map_len(p, map);
|
||||
|
||||
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, length, lb_const_nil(m, t_int)), nil_block, hash_block);
|
||||
lb_start_block(p, hash_block);
|
||||
|
||||
key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
|
||||
lbValue key = lb_emit_load(p, key_ptr);
|
||||
|
||||
lbAddr pos = lb_add_local_generated(p, t_uintptr, false);
|
||||
lbAddr distance = lb_add_local_generated(p, t_uintptr, true);
|
||||
lbValue capacity = lb_map_cap(p, map);
|
||||
lbValue mask = lb_emit_conv(p, lb_emit_arith(p, Token_Sub, capacity, lb_const_int(m, t_int, 1), t_int), t_uintptr);
|
||||
|
||||
{
|
||||
auto args = array_make<lbValue>(heap_allocator(), 2);
|
||||
args[0] = map;
|
||||
args[1] = h;
|
||||
lb_addr_store(p, pos, lb_emit_runtime_call(p, "map_desired_position", args));
|
||||
}
|
||||
lbValue zero_uintptr = lb_const_int(m, t_uintptr, 0);
|
||||
lbValue one_uintptr = lb_const_int(m, t_uintptr, 1);
|
||||
|
||||
lbValue ks = lb_map_data_uintptr(p, map);
|
||||
lbValue vs = lb_map_cell_index_static(p, type->Map.key, ks, capacity);
|
||||
lbValue hs = lb_map_cell_index_static(p, type->Map.value, vs, capacity);
|
||||
|
||||
ks = lb_emit_conv(p, ks, alloc_type_pointer(type->Map.key));
|
||||
vs = lb_emit_conv(p, vs, alloc_type_pointer(type->Map.value));
|
||||
hs = lb_emit_conv(p, hs, alloc_type_pointer(t_uintptr));
|
||||
|
||||
lb_emit_jump(p, loop_block);
|
||||
lb_start_block(p, loop_block);
|
||||
|
||||
lbValue element_hash = lb_emit_load(p, lb_emit_ptr_offset(p, hs, lb_addr_load(p, pos)));
|
||||
{
|
||||
// if element_hash == 0 { return nil }
|
||||
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, zero_uintptr), nil_block, probe_block);
|
||||
}
|
||||
|
||||
lb_start_block(p, probe_block);
|
||||
{
|
||||
auto args = array_make<lbValue>(heap_allocator(), 3);
|
||||
args[0] = map;
|
||||
args[1] = element_hash;
|
||||
args[2] = lb_addr_load(p, pos);
|
||||
lbValue probe_distance = lb_emit_runtime_call(p, "map_probe_distance", args);
|
||||
lbValue cond = lb_emit_comp(p, Token_Gt, lb_addr_load(p, distance), probe_distance);
|
||||
lb_emit_if(p, cond, nil_block, hash_compare_block);
|
||||
}
|
||||
|
||||
lb_start_block(p, hash_compare_block);
|
||||
{
|
||||
lb_emit_if(p, lb_emit_comp(p, Token_CmpEq, element_hash, h), key_compare_block, increment_block);
|
||||
}
|
||||
|
||||
lb_start_block(p, key_compare_block);
|
||||
{
|
||||
lbValue element_key = lb_map_cell_index_static(p, type->Map.key, ks, lb_addr_load(p, pos));
|
||||
element_key = lb_emit_conv(p, element_key, ks.type);
|
||||
lbValue cond = lb_emit_comp(p, Token_CmpEq, lb_emit_load(p, element_key), key);
|
||||
lb_emit_if(p, cond, value_block, increment_block);
|
||||
}
|
||||
|
||||
lb_start_block(p, value_block);
|
||||
{
|
||||
lbValue element_value = lb_map_cell_index_static(p, type->Map.value, vs, lb_addr_load(p, pos));
|
||||
element_value = lb_emit_conv(p, element_value, t_rawptr);
|
||||
LLVMBuildRet(p->builder, element_value.value);
|
||||
}
|
||||
|
||||
lb_start_block(p, increment_block);
|
||||
{
|
||||
lbValue pp = lb_addr_load(p, pos);
|
||||
pp = lb_emit_arith(p, Token_Add, pp, one_uintptr, t_uintptr);
|
||||
pp = lb_emit_arith(p, Token_And, pp, mask, t_uintptr);
|
||||
lb_addr_store(p, pos, pp);
|
||||
lb_emit_increment(p, distance.addr);
|
||||
}
|
||||
lb_emit_jump(p, loop_block);
|
||||
|
||||
lb_start_block(p, nil_block);
|
||||
{
|
||||
lbValue res = lb_const_nil(m, t_rawptr);
|
||||
LLVMBuildRet(p->builder, res.value);
|
||||
}
|
||||
|
||||
|
||||
return {p->value, p->type};
|
||||
}
|
||||
|
||||
void lb_debug_print(lbProcedure *p, String const &str) {
|
||||
auto args = array_make<lbValue>(heap_allocator(), 1);
|
||||
args[0] = lb_const_string(p->module, str);
|
||||
lb_emit_runtime_call(p, "print_string", args);
|
||||
}
|
||||
|
||||
lbValue lb_map_set_proc_for_type(lbModule *m, Type *type) {
|
||||
GB_ASSERT(build_context.use_static_map_calls);
|
||||
type = base_type(type);
|
||||
GB_ASSERT(type->kind == Type_Map);
|
||||
|
||||
|
||||
lbProcedure **found = map_get(&m->map_set_procs, type);
|
||||
if (found) {
|
||||
GB_ASSERT(*found != nullptr);
|
||||
return {(*found)->value, (*found)->type};
|
||||
}
|
||||
static u32 proc_index = 0;
|
||||
|
||||
char buf[32] = {};
|
||||
isize n = gb_snprintf(buf, 32, "__$map_set-%u", ++proc_index);
|
||||
char *str = gb_alloc_str_len(permanent_allocator(), buf, n-1);
|
||||
String proc_name = make_string_c(str);
|
||||
|
||||
lbProcedure *p = lb_create_dummy_procedure(m, proc_name, t_map_set_proc);
|
||||
map_set(&m->map_set_procs, type, p);
|
||||
lb_begin_procedure_body(p);
|
||||
defer (lb_end_procedure_body(p));
|
||||
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
lb_add_attribute_to_proc(m, p->value, "noinline");
|
||||
}
|
||||
|
||||
lbValue map_ptr = {LLVMGetParam(p->value, 0), t_rawptr};
|
||||
lbValue hash = {LLVMGetParam(p->value, 1), t_uintptr};
|
||||
lbValue key_ptr = {LLVMGetParam(p->value, 2), t_rawptr};
|
||||
lbValue value_ptr = {LLVMGetParam(p->value, 3), t_rawptr};
|
||||
lbValue location_ptr = {LLVMGetParam(p->value, 4), t_source_code_location_ptr};
|
||||
|
||||
map_ptr = lb_emit_conv(p, map_ptr, alloc_type_pointer(type));
|
||||
key_ptr = lb_emit_conv(p, key_ptr, alloc_type_pointer(type->Map.key));
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "nonnull");
|
||||
lb_add_proc_attribute_at_index(p, 1+0, "noalias");
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+2, "nonnull");
|
||||
if (!are_types_identical(type->Map.key, type->Map.value)) {
|
||||
lb_add_proc_attribute_at_index(p, 1+2, "noalias");
|
||||
}
|
||||
lb_add_proc_attribute_at_index(p, 1+2, "readonly");
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+3, "nonnull");
|
||||
if (!are_types_identical(type->Map.key, type->Map.value)) {
|
||||
lb_add_proc_attribute_at_index(p, 1+3, "noalias");
|
||||
}
|
||||
lb_add_proc_attribute_at_index(p, 1+3, "readonly");
|
||||
|
||||
lb_add_proc_attribute_at_index(p, 1+4, "nonnull");
|
||||
lb_add_proc_attribute_at_index(p, 1+4, "noalias");
|
||||
lb_add_proc_attribute_at_index(p, 1+4, "readonly");
|
||||
|
||||
////
|
||||
lbValue found_ptr = {};
|
||||
{
|
||||
lbValue map_get_proc = lb_map_get_proc_for_type(m, type);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = hash;
|
||||
args[2] = key_ptr;
|
||||
|
||||
found_ptr = lb_emit_call(p, map_get_proc, args);
|
||||
}
|
||||
|
||||
|
||||
lbBlock *found_block = lb_create_block(p, "found");
|
||||
lbBlock *check_grow_block = lb_create_block(p, "check-grow");
|
||||
lbBlock *grow_fail_block = lb_create_block(p, "grow-fail");
|
||||
lbBlock *insert_block = lb_create_block(p, "insert");
|
||||
|
||||
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, found_ptr), found_block, check_grow_block);
|
||||
lb_start_block(p, found_block);
|
||||
{
|
||||
lb_mem_copy_non_overlapping(p, found_ptr, value_ptr, lb_const_int(m, t_int, type_size_of(type->Map.value)));
|
||||
LLVMBuildRet(p->builder, lb_emit_conv(p, found_ptr, t_rawptr).value);
|
||||
}
|
||||
lb_start_block(p, check_grow_block);
|
||||
|
||||
|
||||
lbValue map_info = lb_gen_map_info_ptr(p->module, type);
|
||||
|
||||
{
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = map_info;
|
||||
args[2] = lb_emit_load(p, location_ptr);
|
||||
lbValue grow_err = lb_emit_runtime_call(p, "__dynamic_map_check_grow", args);
|
||||
|
||||
lb_emit_if(p, lb_emit_comp_against_nil(p, Token_NotEq, grow_err), grow_fail_block, insert_block);
|
||||
|
||||
lb_start_block(p, grow_fail_block);
|
||||
LLVMBuildRet(p->builder, LLVMConstNull(lb_type(m, t_rawptr)));
|
||||
}
|
||||
|
||||
lb_start_block(p, insert_block);
|
||||
{
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 5);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = map_info;
|
||||
args[2] = hash;
|
||||
args[3] = lb_emit_conv(p, key_ptr, t_uintptr);
|
||||
args[4] = lb_emit_conv(p, value_ptr, t_uintptr);
|
||||
|
||||
lbValue result = lb_emit_runtime_call(p, "map_insert_hash_dynamic", args);
|
||||
|
||||
lb_emit_increment(p, lb_map_len_ptr(p, map_ptr));
|
||||
|
||||
LLVMBuildRet(p->builder, lb_emit_conv(p, result, t_rawptr).value);
|
||||
}
|
||||
|
||||
return {p->value, p->type};
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, Ast *expr, lbProcedure *parent) {
|
||||
lbProcedure **found = map_get(&m->gen->anonymous_proc_lits, expr);
|
||||
if (found) {
|
||||
@@ -500,51 +777,60 @@ lbValue lb_generate_anonymous_proc_lit(lbModule *m, String const &prefix_name, A
|
||||
return value;
|
||||
}
|
||||
|
||||
lbValue lb_gen_map_header_table_internal(lbProcedure *p, Type *map_type) {
|
||||
lbModule *m = p->module;
|
||||
|
||||
lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type) {
|
||||
lbAddr *found = map_get(&m->map_cell_info_map, type);
|
||||
if (found) {
|
||||
return found->addr;
|
||||
}
|
||||
|
||||
i64 size = 0, len = 0;
|
||||
map_cell_size_and_len(type, &size, &len);
|
||||
|
||||
LLVMValueRef const_values[4] = {};
|
||||
const_values[0] = lb_const_int(m, t_uintptr, type_size_of(type)).value;
|
||||
const_values[1] = lb_const_int(m, t_uintptr, type_align_of(type)).value;
|
||||
const_values[2] = lb_const_int(m, t_uintptr, size).value;
|
||||
const_values[3] = lb_const_int(m, t_uintptr, len).value;
|
||||
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_cell_info, const_values, gb_count_of(const_values));
|
||||
lbValue res = {llvm_res, t_map_cell_info};
|
||||
|
||||
lbAddr addr = lb_add_global_generated(m, t_map_cell_info, res, nullptr);
|
||||
lb_make_global_private_const(addr);
|
||||
|
||||
map_set(&m->map_cell_info_map, type, addr);
|
||||
|
||||
return addr.addr;
|
||||
}
|
||||
lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type) {
|
||||
map_type = base_type(map_type);
|
||||
GB_ASSERT(map_type->kind == Type_Map);
|
||||
|
||||
lbAddr *found = map_get(&m->map_header_table_map, map_type);
|
||||
lbAddr *found = map_get(&m->map_info_map, map_type);
|
||||
if (found) {
|
||||
return lb_addr_load(p, *found);
|
||||
return found->addr;
|
||||
}
|
||||
|
||||
GB_ASSERT(map_type->Map.entry_type->kind == Type_Struct);
|
||||
i64 entry_size = type_size_of (map_type->Map.entry_type);
|
||||
i64 entry_align = type_align_of (map_type->Map.entry_type);
|
||||
GB_ASSERT(t_map_info != nullptr);
|
||||
GB_ASSERT(t_map_cell_info != nullptr);
|
||||
|
||||
i64 key_offset = type_offset_of(map_type->Map.entry_type, 2);
|
||||
i64 key_size = type_size_of (map_type->Map.key);
|
||||
LLVMValueRef key_cell_info = lb_gen_map_cell_info_ptr(m, map_type->Map.key).value;
|
||||
LLVMValueRef value_cell_info = lb_gen_map_cell_info_ptr(m, map_type->Map.value).value;
|
||||
|
||||
i64 value_offset = type_offset_of(map_type->Map.entry_type, 3);
|
||||
i64 value_size = type_size_of (map_type->Map.value);
|
||||
LLVMValueRef const_values[4] = {};
|
||||
const_values[0] = key_cell_info;
|
||||
const_values[1] = value_cell_info;
|
||||
const_values[2] = lb_hasher_proc_for_type(m, map_type->Map.key).value;
|
||||
const_values[3] = lb_equal_proc_for_type(m, map_type->Map.key).value;
|
||||
|
||||
Type *key_type = map_type->Map.key;
|
||||
Type *val_type = map_type->Map.value;
|
||||
gb_unused(val_type);
|
||||
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_info, const_values, gb_count_of(const_values));
|
||||
lbValue res = {llvm_res, t_map_info};
|
||||
|
||||
Type *st = base_type(t_map_header_table);
|
||||
GB_ASSERT(st->Struct.fields.count == 7);
|
||||
|
||||
LLVMValueRef const_values[7] = {};
|
||||
const_values[0] = lb_get_equal_proc_for_type(m, key_type) .value;
|
||||
const_values[1] = lb_const_int(m, t_int, entry_size) .value;
|
||||
const_values[2] = lb_const_int(m, t_int, entry_align) .value;
|
||||
const_values[3] = lb_const_int(m, t_uintptr, key_offset) .value;
|
||||
const_values[4] = lb_const_int(m, t_int, key_size) .value;
|
||||
const_values[5] = lb_const_int(m, t_uintptr, value_offset).value;
|
||||
const_values[6] = lb_const_int(m, t_int, value_size) .value;
|
||||
|
||||
LLVMValueRef llvm_res = llvm_const_named_struct(m, t_map_header_table, const_values, gb_count_of(const_values));
|
||||
lbValue res = {llvm_res, t_map_header_table};
|
||||
|
||||
lbAddr addr = lb_add_global_generated(m, t_map_header_table, res, nullptr);
|
||||
lbAddr addr = lb_add_global_generated(m, t_map_info, res, nullptr);
|
||||
lb_make_global_private_const(addr);
|
||||
|
||||
map_set(&m->map_header_table_map, map_type, addr);
|
||||
return lb_addr_load(p, addr);
|
||||
map_set(&m->map_info_map, map_type, addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
lbValue lb_const_hash(lbModule *m, lbValue key, Type *key_type) {
|
||||
@@ -602,7 +888,7 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
|
||||
|
||||
lbValue hashed_key = lb_const_hash(p->module, key, key_type);
|
||||
if (hashed_key.value == nullptr) {
|
||||
lbValue hasher = lb_get_hasher_proc_for_type(p->module, key_type);
|
||||
lbValue hasher = lb_hasher_proc_for_type(p->module, key_type);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
args[0] = key_ptr;
|
||||
@@ -615,42 +901,68 @@ lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue
|
||||
|
||||
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key) {
|
||||
Type *map_type = base_type(type_deref(map_ptr.type));
|
||||
GB_ASSERT(map_type->kind == Type_Map);
|
||||
|
||||
lbValue ptr = {};
|
||||
lbValue key_ptr = {};
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 4);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = lb_gen_map_header_table_internal(p, map_type);
|
||||
args[2] = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
|
||||
args[3] = key_ptr;
|
||||
lbValue hash = lb_gen_map_key_hash(p, key, map_type->Map.key, &key_ptr);
|
||||
|
||||
lbValue ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
|
||||
if (build_context.use_static_map_calls) {
|
||||
lbValue map_get_proc = lb_map_get_proc_for_type(p->module, map_type);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 3);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = hash;
|
||||
args[2] = key_ptr;
|
||||
|
||||
ptr = lb_emit_call(p, map_get_proc, args);
|
||||
} else {
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 4);
|
||||
args[0] = lb_emit_transmute(p, map_ptr, t_raw_map_ptr);
|
||||
args[1] = lb_gen_map_info_ptr(p->module, map_type);
|
||||
args[2] = hash;
|
||||
args[3] = key_ptr;
|
||||
|
||||
ptr = lb_emit_runtime_call(p, "__dynamic_map_get", args);
|
||||
}
|
||||
return lb_emit_conv(p, ptr, alloc_type_pointer(map_type->Map.value));
|
||||
}
|
||||
|
||||
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
|
||||
lbValue const &map_key, lbValue const &map_value, Ast *node) {
|
||||
void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type,
|
||||
lbValue const &map_key, lbValue const &map_value, Ast *node) {
|
||||
map_type = base_type(map_type);
|
||||
GB_ASSERT(map_type->kind == Type_Map);
|
||||
|
||||
lbValue key_ptr = {};
|
||||
lbValue key_hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
|
||||
lbValue hash = lb_gen_map_key_hash(p, map_key, map_type->Map.key, &key_ptr);
|
||||
|
||||
lbValue v = lb_emit_conv(p, map_value, map_type->Map.value);
|
||||
lbValue value_ptr = lb_address_from_load_or_generate_local(p, v);
|
||||
|
||||
lbAddr value_addr = lb_add_local_generated(p, v.type, false);
|
||||
lb_addr_store(p, value_addr, v);
|
||||
if (build_context.use_static_map_calls) {
|
||||
lbValue map_set_proc = lb_map_set_proc_for_type(p->module, map_type);
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 6);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = lb_gen_map_header_table_internal(p, map_type);
|
||||
args[2] = key_hash;
|
||||
args[3] = key_ptr;
|
||||
args[4] = lb_emit_conv(p, value_addr.addr, t_rawptr);
|
||||
args[5] = lb_emit_source_code_location(p, node);
|
||||
lb_emit_runtime_call(p, "__dynamic_map_set", args);
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 5);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = hash;
|
||||
args[2] = lb_emit_conv(p, key_ptr, t_rawptr);
|
||||
args[3] = lb_emit_conv(p, value_ptr, t_rawptr);
|
||||
args[4] = lb_emit_source_code_location_as_global(p, node);
|
||||
|
||||
lb_emit_call(p, map_set_proc, args);
|
||||
} else {
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 6);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_raw_map_ptr);
|
||||
args[1] = lb_gen_map_info_ptr(p->module, map_type);
|
||||
args[2] = hash;
|
||||
args[3] = lb_emit_conv(p, key_ptr, t_rawptr);
|
||||
args[4] = lb_emit_conv(p, value_ptr, t_rawptr);
|
||||
args[5] = lb_emit_source_code_location_as_global(p, node);
|
||||
lb_emit_runtime_call(p, "__dynamic_map_set", args);
|
||||
}
|
||||
}
|
||||
|
||||
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
|
||||
lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos) {
|
||||
GB_ASSERT(!build_context.no_dynamic_literals);
|
||||
|
||||
String proc_name = {};
|
||||
@@ -660,10 +972,10 @@ void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const
|
||||
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 4);
|
||||
args[0] = lb_emit_conv(p, map_ptr, t_rawptr);
|
||||
args[1] = lb_gen_map_header_table_internal(p, type_deref(map_ptr.type));
|
||||
args[2] = lb_const_int(p->module, t_int, capacity);
|
||||
args[3] = lb_emit_source_code_location(p, proc_name, pos);
|
||||
lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
|
||||
args[1] = lb_gen_map_info_ptr(p->module, type_deref(map_ptr.type));
|
||||
args[2] = lb_const_int(p->module, t_uint, capacity);
|
||||
args[3] = lb_emit_source_code_location_as_global(p, proc_name, pos);
|
||||
return lb_emit_runtime_call(p, "__dynamic_map_reserve", args);
|
||||
}
|
||||
|
||||
|
||||
@@ -688,6 +1000,8 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
|
||||
p->is_startup = true;
|
||||
LLVMSetLinkage(p->value, LLVMInternalLinkage);
|
||||
|
||||
lb_add_attribute_to_proc(m, p->value, "nounwind");
|
||||
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
lb_setup_type_info_data(p);
|
||||
@@ -712,6 +1026,7 @@ lbProcedure *lb_create_objc_names(lbModule *main_module) {
|
||||
}
|
||||
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
|
||||
lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
|
||||
lb_add_attribute_to_proc(p->module, p->value, "nounwind");
|
||||
p->is_startup = true;
|
||||
return p;
|
||||
}
|
||||
@@ -1198,6 +1513,14 @@ WORKER_TASK_PROC(lb_llvm_function_pass_worker_proc) {
|
||||
lbProcedure *p = m->hasher_procs.entries[i].value;
|
||||
lb_run_function_pass_manager(default_function_pass_manager, p);
|
||||
}
|
||||
for_array(i, m->map_get_procs.entries) {
|
||||
lbProcedure *p = m->map_get_procs.entries[i].value;
|
||||
lb_run_function_pass_manager(default_function_pass_manager, p);
|
||||
}
|
||||
for_array(i, m->map_set_procs.entries) {
|
||||
lbProcedure *p = m->map_set_procs.entries[i].value;
|
||||
lb_run_function_pass_manager(default_function_pass_manager, p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1736,6 +2059,13 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
|
||||
gb_unused(startup_runtime);
|
||||
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
for_array(i, builtin_pkg->scope->elements.entries) {
|
||||
Entity *e = builtin_pkg->scope->elements.entries[i].value;
|
||||
add_debug_info_for_global_constant_from_entity(gen, e);
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("LLVM Global Procedures and Types");
|
||||
for_array(i, info->entities) {
|
||||
Entity *e = info->entities[i];
|
||||
@@ -1759,6 +2089,11 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
case Entity_TypeName:
|
||||
case Entity_Procedure:
|
||||
break;
|
||||
case Entity_Constant:
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
add_debug_info_for_global_constant_from_entity(gen, e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bool polymorphic_struct = false;
|
||||
@@ -1820,6 +2155,21 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
lb_finalize_objc_names(objc_names);
|
||||
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
TIME_SECTION("LLVM Debug Info for global constant value declarations");
|
||||
{
|
||||
// lbModule *m = default_module;
|
||||
|
||||
|
||||
}
|
||||
// if (gen->modules.entries.count == 1) {
|
||||
// } else {
|
||||
// for_array(j, gen->modules.entries) {
|
||||
// lbModule *m = gen->modules.entries[j].value;
|
||||
// if (m->debug_builder != nullptr) {
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
|
||||
for_array(j, gen->modules.entries) {
|
||||
lbModule *m = gen->modules.entries[j].value;
|
||||
|
||||
+12
-8
@@ -144,6 +144,8 @@ struct lbModule {
|
||||
|
||||
PtrMap<Type *, lbProcedure *> equal_procs;
|
||||
PtrMap<Type *, lbProcedure *> hasher_procs;
|
||||
PtrMap<Type *, lbProcedure *> map_get_procs;
|
||||
PtrMap<Type *, lbProcedure *> map_set_procs;
|
||||
|
||||
u32 nested_type_name_guid;
|
||||
|
||||
@@ -160,7 +162,8 @@ struct lbModule {
|
||||
StringMap<lbAddr> objc_classes;
|
||||
StringMap<lbAddr> objc_selectors;
|
||||
|
||||
PtrMap<Type *, lbAddr> map_header_table_map;
|
||||
PtrMap<Type *, lbAddr> map_cell_info_map; // address of runtime.Map_Info
|
||||
PtrMap<Type *, lbAddr> map_info_map; // address of runtime.Map_Cell_Info
|
||||
};
|
||||
|
||||
struct lbGenerator {
|
||||
@@ -298,6 +301,7 @@ struct lbProcedure {
|
||||
lbBlock * entry_block;
|
||||
lbBlock * curr_block;
|
||||
lbTargetList * target_list;
|
||||
PtrMap<Entity *, lbValue> direct_parameters;
|
||||
|
||||
Ast *curr_stmt;
|
||||
|
||||
@@ -421,8 +425,6 @@ lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da);
|
||||
lbValue lb_dynamic_array_len(lbProcedure *p, lbValue da);
|
||||
lbValue lb_dynamic_array_cap(lbProcedure *p, lbValue da);
|
||||
lbValue lb_dynamic_array_allocator(lbProcedure *p, lbValue da);
|
||||
lbValue lb_map_entries(lbProcedure *p, lbValue value);
|
||||
lbValue lb_map_entries_ptr(lbProcedure *p, lbValue value);
|
||||
lbValue lb_map_len(lbProcedure *p, lbValue value);
|
||||
lbValue lb_map_cap(lbProcedure *p, lbValue value);
|
||||
lbValue lb_soa_struct_len(lbProcedure *p, lbValue value);
|
||||
@@ -446,22 +448,24 @@ String lb_get_const_string(lbModule *m, lbValue value);
|
||||
lbValue lb_generate_local_array(lbProcedure *p, Type *elem_type, i64 count, bool zero_init=true);
|
||||
lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String prefix, i64 id);
|
||||
lbValue lb_gen_map_key_hash(lbProcedure *p, lbValue key, Type *key_type, lbValue *key_ptr_);
|
||||
lbValue lb_gen_map_cell_info_ptr(lbModule *m, Type *type);
|
||||
lbValue lb_gen_map_info_ptr(lbModule *m, Type *map_type);
|
||||
|
||||
lbValue lb_internal_dynamic_map_get_ptr(lbProcedure *p, lbValue const &map_ptr, lbValue const &key);
|
||||
void lb_insert_dynamic_map_key_and_value(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
|
||||
void lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
|
||||
void lb_internal_dynamic_map_set(lbProcedure *p, lbValue const &map_ptr, Type *map_type, lbValue const &map_key, lbValue const &map_value, Ast *node);
|
||||
lbValue lb_dynamic_map_reserve(lbProcedure *p, lbValue const &map_ptr, isize const capacity, TokenPos const &pos);
|
||||
|
||||
lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e);
|
||||
lbValue lb_find_value_from_entity(lbModule *m, Entity *e);
|
||||
|
||||
void lb_store_type_case_implicit(lbProcedure *p, Ast *clause, lbValue value);
|
||||
lbAddr lb_store_range_stmt_val(lbProcedure *p, Ast *stmt_val, lbValue value);
|
||||
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos);
|
||||
lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos);
|
||||
|
||||
lbValue lb_handle_param_value(lbProcedure *p, Type *parameter_type, ParameterValue const ¶m_value, TokenPos const &pos);
|
||||
|
||||
lbValue lb_get_equal_proc_for_type(lbModule *m, Type *type);
|
||||
lbValue lb_get_hasher_proc_for_type(lbModule *m, Type *type);
|
||||
lbValue lb_equal_proc_for_type(lbModule *m, Type *type);
|
||||
lbValue lb_hasher_proc_for_type(lbModule *m, Type *type);
|
||||
lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t);
|
||||
|
||||
LLVMMetadataRef lb_debug_type(lbModule *m, Type *type);
|
||||
|
||||
@@ -256,7 +256,7 @@ lbValue lb_expr_untyped_const_to_typed(lbModule *m, Ast *expr, Type *t) {
|
||||
return lb_const_value(m, t, tv.value);
|
||||
}
|
||||
|
||||
lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
lbValue lb_emit_source_code_location_const(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
lbModule *m = p->module;
|
||||
|
||||
LLVMValueRef fields[4] = {};
|
||||
@@ -271,7 +271,7 @@ lbValue lb_emit_source_code_location(lbProcedure *p, String const &procedure, To
|
||||
return res;
|
||||
}
|
||||
|
||||
lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
|
||||
lbValue lb_emit_source_code_location_const(lbProcedure *p, Ast *node) {
|
||||
String proc_name = {};
|
||||
if (p->entity) {
|
||||
proc_name = p->entity->token.string;
|
||||
@@ -280,9 +280,35 @@ lbValue lb_emit_source_code_location(lbProcedure *p, Ast *node) {
|
||||
if (node) {
|
||||
pos = ast_token(node).pos;
|
||||
}
|
||||
return lb_emit_source_code_location(p, proc_name, pos);
|
||||
return lb_emit_source_code_location_const(p, proc_name, pos);
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
lbValue loc = lb_emit_source_code_location_const(p, procedure, pos);
|
||||
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
|
||||
lb_make_global_private_const(addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
|
||||
lbValue lb_emit_source_code_location_as_global_ptr(lbProcedure *p, Ast *node) {
|
||||
lbValue loc = lb_emit_source_code_location_const(p, node);
|
||||
lbAddr addr = lb_add_global_generated(p->module, loc.type, loc, nullptr);
|
||||
lb_make_global_private_const(addr);
|
||||
return addr.addr;
|
||||
}
|
||||
|
||||
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, String const &procedure, TokenPos const &pos) {
|
||||
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, procedure, pos));
|
||||
}
|
||||
|
||||
lbValue lb_emit_source_code_location_as_global(lbProcedure *p, Ast *node) {
|
||||
return lb_emit_load(p, lb_emit_source_code_location_as_global_ptr(p, node));
|
||||
}
|
||||
|
||||
|
||||
|
||||
LLVMValueRef lb_build_constant_array_values(lbModule *m, Type *type, Type *elem_type, isize count, LLVMValueRef *values, bool allow_local) {
|
||||
bool is_local = allow_local && m->curr_procedure != nullptr;
|
||||
bool is_const = true;
|
||||
|
||||
+167
-10
@@ -293,6 +293,8 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
case Type_Named:
|
||||
GB_PANIC("Type_Named should be handled in lb_debug_type separately");
|
||||
|
||||
case Type_SoaPointer:
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->SoaPointer.elem), word_bits, word_bits, 0, nullptr, 0);
|
||||
case Type_Pointer:
|
||||
return LLVMDIBuilderCreatePointerType(m->debug_builder, lb_debug_type(m, type->Pointer.elem), word_bits, word_bits, 0, nullptr, 0);
|
||||
case Type_MultiPointer:
|
||||
@@ -567,14 +569,43 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) {
|
||||
case Type_Struct:
|
||||
case Type_Union:
|
||||
case Type_BitSet:
|
||||
LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
|
||||
m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
|
||||
);
|
||||
idt.metadata = temp_forward_decl;
|
||||
{
|
||||
LLVMMetadataRef temp_forward_decl = LLVMDIBuilderCreateReplaceableCompositeType(
|
||||
m->debug_builder, tag, name_text, name_len, nullptr, nullptr, 0, 0, size_in_bits, align_in_bits, flags, "", 0
|
||||
);
|
||||
idt.metadata = temp_forward_decl;
|
||||
|
||||
array_add(&m->debug_incomplete_types, idt);
|
||||
lb_set_llvm_metadata(m, type, temp_forward_decl);
|
||||
return temp_forward_decl;
|
||||
array_add(&m->debug_incomplete_types, idt);
|
||||
lb_set_llvm_metadata(m, type, temp_forward_decl);
|
||||
|
||||
LLVMMetadataRef dummy = nullptr;
|
||||
switch (bt->kind) {
|
||||
case Type_Slice:
|
||||
dummy = lb_debug_type(m, bt->Slice.elem);
|
||||
dummy = lb_debug_type(m, alloc_type_pointer(bt->Slice.elem));
|
||||
dummy = lb_debug_type(m, t_int);
|
||||
break;
|
||||
case Type_DynamicArray:
|
||||
dummy = lb_debug_type(m, bt->DynamicArray.elem);
|
||||
dummy = lb_debug_type(m, alloc_type_pointer(bt->DynamicArray.elem));
|
||||
dummy = lb_debug_type(m, t_int);
|
||||
dummy = lb_debug_type(m, t_allocator);
|
||||
break;
|
||||
case Type_Map:
|
||||
dummy = lb_debug_type(m, bt->Map.key);
|
||||
dummy = lb_debug_type(m, bt->Map.value);
|
||||
dummy = lb_debug_type(m, t_int);
|
||||
dummy = lb_debug_type(m, t_allocator);
|
||||
dummy = lb_debug_type(m, t_uintptr);
|
||||
break;
|
||||
case Type_BitSet:
|
||||
if (bt->BitSet.elem) dummy = lb_debug_type(m, bt->BitSet.elem);
|
||||
if (bt->BitSet.underlying) dummy = lb_debug_type(m, bt->BitSet.underlying);
|
||||
break;
|
||||
}
|
||||
|
||||
return temp_forward_decl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,20 +688,33 @@ void lb_debug_complete_types(lbModule *m) {
|
||||
case Type_Slice:
|
||||
element_count = 2;
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*word_bits);
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->Slice.elem), 0*word_bits);
|
||||
#else
|
||||
// FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
|
||||
// of the debug type information
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*word_bits);
|
||||
#endif
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
|
||||
break;
|
||||
case Type_DynamicArray:
|
||||
element_count = 4;
|
||||
elements = gb_alloc_array(temporary_allocator(), LLVMMetadataRef, element_count);
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*word_bits);
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), alloc_type_pointer(bt->DynamicArray.elem), 0*word_bits);
|
||||
#else
|
||||
// FIX HACK TODO(bill): For some reason this causes a crash in *nix systems due to the reference counting
|
||||
// of the debug type information
|
||||
elements[0] = lb_debug_struct_field(m, str_lit("data"), t_rawptr, 0*word_bits);
|
||||
#endif
|
||||
elements[1] = lb_debug_struct_field(m, str_lit("len"), t_int, 1*word_bits);
|
||||
elements[2] = lb_debug_struct_field(m, str_lit("cap"), t_int, 2*word_bits);
|
||||
elements[3] = lb_debug_struct_field(m, str_lit("allocator"), t_allocator, 3*word_bits);
|
||||
break;
|
||||
|
||||
case Type_Map:
|
||||
bt = bt->Map.internal_type;
|
||||
GB_ASSERT(t_raw_map != nullptr);
|
||||
bt = base_type(t_raw_map);
|
||||
/*fallthrough*/
|
||||
case Type_Struct:
|
||||
if (file == nullptr) {
|
||||
@@ -1079,3 +1123,116 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
|
||||
|
||||
lb_add_debug_local_variable(p, ptr, t_context, token);
|
||||
}
|
||||
|
||||
|
||||
String debug_info_mangle_constant_name(Entity *e, bool *did_allocate_) {
|
||||
String name = e->token.string;
|
||||
if (e->pkg && e->pkg->name.len > 0) {
|
||||
// NOTE(bill): C++ NONSENSE FOR DEBUG SHITE!
|
||||
name = concatenate3_strings(heap_allocator(), e->pkg->name, str_lit("::"), name);
|
||||
if (did_allocate_) *did_allocate_ = true;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
void add_debug_info_global_variable_expr(lbModule *m, String const &name, LLVMMetadataRef dtype, LLVMMetadataRef expr) {
|
||||
LLVMMetadataRef scope = nullptr;
|
||||
LLVMMetadataRef file = nullptr;
|
||||
unsigned line = 0;
|
||||
|
||||
LLVMMetadataRef decl = nullptr;
|
||||
|
||||
LLVMDIBuilderCreateGlobalVariableExpression(
|
||||
m->debug_builder, scope,
|
||||
cast(char const *)name.text, cast(size_t)name.len,
|
||||
"", 0, // Linkage
|
||||
file, line, dtype,
|
||||
false, // local to unit
|
||||
expr, decl, 8/*AlignInBits*/);
|
||||
}
|
||||
|
||||
void add_debug_info_for_global_constant_internal_i64(lbModule *m, Entity *e, LLVMMetadataRef dtype, i64 v) {
|
||||
LLVMMetadataRef expr = LLVMDIBuilderCreateConstantValueExpression(m->debug_builder, v);
|
||||
|
||||
bool did_allocate = false;
|
||||
String name = debug_info_mangle_constant_name(e, &did_allocate);
|
||||
defer (if (did_allocate) {
|
||||
gb_free(heap_allocator(), name.text);
|
||||
});
|
||||
|
||||
add_debug_info_global_variable_expr(m, name, dtype, expr);
|
||||
if ((e->pkg && e->pkg->kind == Package_Init) ||
|
||||
(e->scope && (e->scope->flags & ScopeFlag_Global))) {
|
||||
add_debug_info_global_variable_expr(m, e->token.string, dtype, expr);
|
||||
}
|
||||
}
|
||||
|
||||
void add_debug_info_for_global_constant_from_entity(lbGenerator *gen, Entity *e) {
|
||||
if (e == nullptr || e->kind != Entity_Constant) {
|
||||
return;
|
||||
}
|
||||
if (is_blank_ident(e->token)) {
|
||||
return;
|
||||
}
|
||||
lbModule *m = &gen->default_module;
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
m = lb_pkg_module(gen, e->pkg);
|
||||
}
|
||||
|
||||
if (is_type_integer(e->type)) {
|
||||
ExactValue const &value = e->Constant.value;
|
||||
if (value.kind == ExactValue_Integer) {
|
||||
LLVMMetadataRef dtype = nullptr;
|
||||
i64 v = 0;
|
||||
bool is_signed = false;
|
||||
if (big_int_is_neg(&value.value_integer)) {
|
||||
v = exact_value_to_i64(value);
|
||||
is_signed = true;
|
||||
} else {
|
||||
v = cast(i64)exact_value_to_u64(value);
|
||||
}
|
||||
if (is_type_untyped(e->type)) {
|
||||
dtype = lb_debug_type(m, is_signed ? t_i64 : t_u64);
|
||||
} else {
|
||||
dtype = lb_debug_type(m, e->type);
|
||||
}
|
||||
|
||||
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
|
||||
}
|
||||
} else if (is_type_rune(e->type)) {
|
||||
ExactValue const &value = e->Constant.value;
|
||||
if (value.kind == ExactValue_Integer) {
|
||||
LLVMMetadataRef dtype = lb_debug_type(m, t_rune);
|
||||
i64 v = exact_value_to_i64(value);
|
||||
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
|
||||
}
|
||||
} else if (is_type_boolean(e->type)) {
|
||||
ExactValue const &value = e->Constant.value;
|
||||
if (value.kind == ExactValue_Bool) {
|
||||
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
|
||||
i64 v = cast(i64)value.value_bool;
|
||||
|
||||
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
|
||||
}
|
||||
} else if (is_type_enum(e->type)) {
|
||||
ExactValue const &value = e->Constant.value;
|
||||
if (value.kind == ExactValue_Integer) {
|
||||
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
|
||||
i64 v = 0;
|
||||
if (big_int_is_neg(&value.value_integer)) {
|
||||
v = exact_value_to_i64(value);
|
||||
} else {
|
||||
v = cast(i64)exact_value_to_u64(value);
|
||||
}
|
||||
|
||||
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
|
||||
}
|
||||
} else if (is_type_pointer(e->type)) {
|
||||
ExactValue const &value = e->Constant.value;
|
||||
if (value.kind == ExactValue_Integer) {
|
||||
LLVMMetadataRef dtype = lb_debug_type(m, default_type(e->type));
|
||||
i64 v = cast(i64)exact_value_to_u64(value);
|
||||
add_debug_info_for_global_constant_internal_i64(m, e, dtype, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
+34
-39
@@ -1952,34 +1952,33 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
|
||||
Type *dt = t;
|
||||
|
||||
GB_ASSERT(is_type_struct(st) || is_type_raw_union(st));
|
||||
String field_name = lookup_subtype_polymorphic_field(t, src_type);
|
||||
if (field_name.len > 0) {
|
||||
// NOTE(bill): It can be casted
|
||||
Selection sel = lookup_field(st, field_name, false, true);
|
||||
if (sel.entity != nullptr) {
|
||||
if (st_is_ptr) {
|
||||
lbValue res = lb_emit_deep_field_gep(p, value, sel);
|
||||
Type *rt = res.type;
|
||||
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
|
||||
res = lb_emit_load(p, res);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
if (is_type_pointer(value.type)) {
|
||||
Type *rt = value.type;
|
||||
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
|
||||
value = lb_emit_load(p, value);
|
||||
} else {
|
||||
value = lb_emit_deep_field_gep(p, value, sel);
|
||||
return lb_emit_load(p, value);
|
||||
}
|
||||
}
|
||||
|
||||
return lb_emit_deep_field_ev(p, value, sel);
|
||||
|
||||
Selection sel = {};
|
||||
sel.index.allocator = heap_allocator();
|
||||
defer (array_free(&sel.index));
|
||||
if (lookup_subtype_polymorphic_selection(t, src_type, &sel)) {
|
||||
if (sel.entity == nullptr) {
|
||||
GB_PANIC("invalid subtype cast %s -> ", type_to_string(src_type), type_to_string(t));
|
||||
}
|
||||
if (st_is_ptr) {
|
||||
lbValue res = lb_emit_deep_field_gep(p, value, sel);
|
||||
Type *rt = res.type;
|
||||
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
|
||||
res = lb_emit_load(p, res);
|
||||
}
|
||||
return res;
|
||||
} else {
|
||||
GB_PANIC("invalid subtype cast %s.%.*s", type_to_string(src_type), LIT(field_name));
|
||||
if (is_type_pointer(value.type)) {
|
||||
Type *rt = value.type;
|
||||
if (!are_types_identical(rt, dt) && are_types_identical(type_deref(rt), dt)) {
|
||||
value = lb_emit_load(p, value);
|
||||
} else {
|
||||
value = lb_emit_deep_field_gep(p, value, sel);
|
||||
return lb_emit_load(p, value);
|
||||
}
|
||||
}
|
||||
|
||||
return lb_emit_deep_field_ev(p, value, sel);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2216,7 +2215,7 @@ lbValue lb_compare_records(lbProcedure *p, TokenKind op_kind, lbValue left, lbVa
|
||||
args[2] = lb_const_int(p->module, t_int, type_size_of(type));
|
||||
res = lb_emit_runtime_call(p, "memory_equal", args);
|
||||
} else {
|
||||
lbValue value = lb_get_equal_proc_for_type(p->module, type);
|
||||
lbValue value = lb_equal_proc_for_type(p->module, type);
|
||||
auto args = array_make<lbValue>(permanent_allocator(), 2);
|
||||
args[0] = lb_emit_conv(p, left_ptr, t_rawptr);
|
||||
args[1] = lb_emit_conv(p, right_ptr, t_rawptr);
|
||||
@@ -2725,18 +2724,13 @@ lbValue lb_emit_comp_against_nil(lbProcedure *p, TokenKind op_kind, lbValue x) {
|
||||
|
||||
case Type_Map:
|
||||
{
|
||||
lbValue map_ptr = lb_address_from_load_or_generate_local(p, x);
|
||||
|
||||
unsigned indices[2] = {0, 0};
|
||||
lbValue hashes_data = lb_emit_struct_ep(p, map_ptr, 0);
|
||||
lbValue hashes_data_ptr_ptr = lb_emit_struct_ep(p, hashes_data, 0);
|
||||
LLVMValueRef hashes_data_ptr = LLVMBuildLoad2(p->builder, llvm_addr_type(p->module, hashes_data_ptr_ptr), hashes_data_ptr_ptr.value, "");
|
||||
lbValue data_ptr = lb_emit_struct_ev(p, x, 0);
|
||||
|
||||
if (op_kind == Token_CmpEq) {
|
||||
res.value = LLVMBuildIsNull(p->builder, hashes_data_ptr, "");
|
||||
res.value = LLVMBuildIsNull(p->builder, data_ptr.value, "");
|
||||
return res;
|
||||
} else {
|
||||
res.value = LLVMBuildIsNotNull(p->builder, hashes_data_ptr, "");
|
||||
res.value = LLVMBuildIsNotNull(p->builder, data_ptr.value, "");
|
||||
return res;
|
||||
}
|
||||
}
|
||||
@@ -4132,7 +4126,8 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
GB_ASSERT(!build_context.no_dynamic_literals);
|
||||
|
||||
lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
|
||||
lbValue err = lb_dynamic_map_reserve(p, v.addr, 2*cl->elems.count, pos);
|
||||
gb_unused(err);
|
||||
|
||||
for_array(field_index, cl->elems) {
|
||||
Ast *elem = cl->elems[field_index];
|
||||
@@ -4140,7 +4135,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
|
||||
lbValue key = lb_build_expr(p, fv->field);
|
||||
lbValue value = lb_build_expr(p, fv->value);
|
||||
lb_insert_dynamic_map_key_and_value(p, v.addr, type, key, value, elem);
|
||||
lb_internal_dynamic_map_set(p, v.addr, type, key, value, elem);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4232,7 +4227,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
args[1] = size;
|
||||
args[2] = align;
|
||||
args[3] = lb_const_int(p->module, t_int, item_count);
|
||||
args[4] = lb_emit_source_code_location(p, proc_name, pos);
|
||||
args[4] = lb_emit_source_code_location_as_global(p, proc_name, pos);
|
||||
lb_emit_runtime_call(p, "__dynamic_array_reserve", args);
|
||||
}
|
||||
|
||||
@@ -4253,7 +4248,7 @@ lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
|
||||
args[2] = align;
|
||||
args[3] = lb_emit_conv(p, items, t_rawptr);
|
||||
args[4] = lb_const_int(p->module, t_int, item_count);
|
||||
args[5] = lb_emit_source_code_location(p, proc_name, pos);
|
||||
args[5] = lb_emit_source_code_location_as_global(p, proc_name, pos);
|
||||
lb_emit_runtime_call(p, "__dynamic_array_append", args);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -67,6 +67,8 @@ void lb_init_module(lbModule *m, Checker *c) {
|
||||
map_init(&m->function_type_map, a);
|
||||
map_init(&m->equal_procs, a);
|
||||
map_init(&m->hasher_procs, a);
|
||||
map_init(&m->map_get_procs, a);
|
||||
map_init(&m->map_set_procs, a);
|
||||
array_init(&m->procedures_to_generate, a, 0, 1024);
|
||||
array_init(&m->missing_procedures_to_check, a, 0, 16);
|
||||
map_init(&m->debug_values, a);
|
||||
@@ -75,7 +77,8 @@ void lb_init_module(lbModule *m, Checker *c) {
|
||||
string_map_init(&m->objc_classes, a);
|
||||
string_map_init(&m->objc_selectors, a);
|
||||
|
||||
map_init(&m->map_header_table_map, a, 0);
|
||||
map_init(&m->map_info_map, a, 0);
|
||||
map_init(&m->map_cell_info_map, a, 0);
|
||||
|
||||
}
|
||||
|
||||
@@ -383,16 +386,27 @@ Type *lb_addr_type(lbAddr const &addr) {
|
||||
if (addr.addr.value == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (addr.kind == lbAddr_Map) {
|
||||
Type *t = base_type(addr.map.type);
|
||||
GB_ASSERT(is_type_map(t));
|
||||
return t->Map.value;
|
||||
}
|
||||
if (addr.kind == lbAddr_Swizzle) {
|
||||
switch (addr.kind) {
|
||||
case lbAddr_Map:
|
||||
{
|
||||
Type *t = base_type(addr.map.type);
|
||||
GB_ASSERT(is_type_map(t));
|
||||
return t->Map.value;
|
||||
}
|
||||
case lbAddr_Swizzle:
|
||||
return addr.swizzle.type;
|
||||
}
|
||||
if (addr.kind == lbAddr_SwizzleLarge) {
|
||||
case lbAddr_SwizzleLarge:
|
||||
return addr.swizzle_large.type;
|
||||
case lbAddr_Context:
|
||||
if (addr.ctx.sel.index.count > 0) {
|
||||
Type *t = t_context;
|
||||
for_array(i, addr.ctx.sel.index) {
|
||||
GB_ASSERT(is_type_struct(t));
|
||||
t = base_type(t)->Struct.fields[addr.ctx.sel.index[i]]->type;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return type_deref(addr.addr.type);
|
||||
}
|
||||
@@ -714,7 +728,7 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
|
||||
|
||||
return;
|
||||
} else if (addr.kind == lbAddr_Map) {
|
||||
lb_insert_dynamic_map_key_and_value(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
|
||||
lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
|
||||
return;
|
||||
} else if (addr.kind == lbAddr_Context) {
|
||||
lbAddr old_addr = lb_find_or_generate_context_ptr(p);
|
||||
@@ -1510,6 +1524,9 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
|
||||
bool *params_by_ptr = gb_alloc_array(permanent_allocator(), bool, param_count);
|
||||
if (type->Proc.result_count != 0) {
|
||||
Type *single_ret = reduce_tuple_to_single_type(type->Proc.results);
|
||||
if (is_type_proc(single_ret)) {
|
||||
single_ret = t_rawptr;
|
||||
}
|
||||
ret = lb_type(m, single_ret);
|
||||
if (ret != nullptr) {
|
||||
if (is_type_boolean(single_ret) &&
|
||||
@@ -1920,38 +1937,8 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
|
||||
case Type_Map:
|
||||
init_map_internal_types(type);
|
||||
{
|
||||
Type *internal_type = type->Map.internal_type;
|
||||
GB_ASSERT(internal_type->kind == Type_Struct);
|
||||
|
||||
m->internal_type_level -= 1;
|
||||
defer (m->internal_type_level += 1);
|
||||
|
||||
unsigned field_count = cast(unsigned)(internal_type->Struct.fields.count);
|
||||
GB_ASSERT(field_count == 2);
|
||||
LLVMTypeRef *fields = gb_alloc_array(temporary_allocator(), LLVMTypeRef, field_count);
|
||||
|
||||
LLVMTypeRef entries_fields[] = {
|
||||
lb_type(m, t_rawptr), // data
|
||||
lb_type(m, t_int), // len
|
||||
lb_type(m, t_int), // cap
|
||||
lb_type(m, t_allocator), // allocator
|
||||
};
|
||||
|
||||
fields[0] = lb_type(m, internal_type->Struct.fields[0]->type);
|
||||
fields[1] = LLVMStructTypeInContext(ctx, entries_fields, gb_count_of(entries_fields), false);
|
||||
|
||||
{ // Add this to simplify things
|
||||
lbStructFieldRemapping entries_field_remapping = {};
|
||||
slice_init(&entries_field_remapping, permanent_allocator(), gb_count_of(entries_fields));
|
||||
for_array(i, entries_field_remapping) {
|
||||
entries_field_remapping[i] = cast(i32)i;
|
||||
}
|
||||
map_set(&m->struct_field_remapping, cast(void *)fields[1], entries_field_remapping);
|
||||
}
|
||||
|
||||
return LLVMStructTypeInContext(ctx, fields, field_count, false);
|
||||
}
|
||||
GB_ASSERT(t_raw_map != nullptr);
|
||||
return lb_type_internal(m, t_raw_map);
|
||||
|
||||
case Type_Struct:
|
||||
{
|
||||
@@ -2000,7 +1987,15 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
}
|
||||
|
||||
field_remapping[field_index] = cast(i32)fields.count;
|
||||
array_add(&fields, lb_type(m, field->type));
|
||||
|
||||
Type *field_type = field->type;
|
||||
if (is_type_proc(field_type)) {
|
||||
// NOTE(bill, 2022-11-23): Prevent type cycle declaration (e.g. vtable) of procedures
|
||||
// because LLVM is dumb with procedure types
|
||||
field_type = t_rawptr;
|
||||
}
|
||||
|
||||
array_add(&fields, lb_type(m, field_type));
|
||||
|
||||
if (!type->Struct.is_packed) {
|
||||
padding_offset = align_formula(padding_offset, type_align_of(field->type));
|
||||
@@ -2590,6 +2585,15 @@ lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String co
|
||||
|
||||
|
||||
lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
|
||||
if (e->flags & EntityFlag_Param) {
|
||||
// NOTE(bill): Bypass the stack copied variable for
|
||||
// direct parameters as there is no need for the direct load
|
||||
auto *found = map_get(&p->direct_parameters, e);
|
||||
if (found) {
|
||||
return *found;
|
||||
}
|
||||
}
|
||||
|
||||
auto *found = map_get(&m->values, e);
|
||||
if (found) {
|
||||
auto v = *found;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user