diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index dc2691d80..ce23c2415 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -104,13 +104,13 @@ jobs:
run: ./odin check examples/all -vet -strict-style -target:linux_arm64
timeout-minutes: 10
build_windows:
- runs-on: windows-2019
+ runs-on: windows-2022
steps:
- uses: actions/checkout@v1
- name: build Odin
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
./build.bat 1
- name: Odin version
run: ./odin version
@@ -121,58 +121,65 @@ jobs:
- name: Odin check
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo -debug
timeout-minutes: 10
- name: Odin check examples/all
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\core
call build.bat
timeout-minutes: 10
- name: Vendor library tests
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
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
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\internal
call build.bat
timeout-minutes: 10
+ - name: Odin documentation tests
+ shell: cmd
+ run: |
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ cd tests\documentation
+ call build.bat
+ timeout-minutes: 10
- name: core:math/big tests
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
cd tests\core\math\big
call build.bat
timeout-minutes: 10
- name: Odin check examples/all for Windows 32bits
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin check examples/all -strict-style -target:windows_i386
timeout-minutes: 10
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 7fe7513e0..67d0396c1 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -7,18 +7,18 @@ on:
jobs:
build_windows:
- runs-on: windows-2019
+ runs-on: windows-2022
steps:
- uses: actions/checkout@v1
- name: build Odin
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
./build.bat 1 1
- name: Odin run
shell: cmd
run: |
- call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
odin run examples/demo
- name: Copy artifacts
run: |
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 341a09409..10f193056 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Close Stale Issues
- uses: actions/stale@v4.1.0
+ uses: actions/stale@v7.0.0
with:
# stale-issue-message: |
# Hello!
@@ -36,7 +36,7 @@ jobs:
# The motivation for this automation is to help prioritize issues in the backlog and not ignore, reject, or belittle anyone..
days-before-stale: 120
- days-before-close: 30
+ days-before-close: -1
exempt-draft-pr: true
ascending: true
operations-per-run: 1000
diff --git a/.gitignore b/.gitignore
index 67314e23a..824e0c203 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,8 @@ bld/
[Oo]bj/
[Ll]og/
![Cc]ore/[Ll]og/
+tests/documentation/verify/
+tests/documentation/all.odin-doc
# Visual Studio 2015 cache/options directory
.vs/
# Visual Studio Code options directory
diff --git a/README.md b/README.md
index f99174c46..4df71015d 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
-
+
diff --git a/build.bat b/build.bat
index 7391bd95f..b7537fba6 100644
--- a/build.bat
+++ b/build.bat
@@ -3,18 +3,20 @@
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
+ 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
+ if "%ODIN_IGNORE_MSVC_CHECK%" == "" (
+ 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 (
@@ -48,8 +50,11 @@ set odin_version_raw="dev-%curr_year%-%curr_month%"
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
+if not exist .git\ goto skip_git_hash
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
+:skip_git_hash
+
if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
if %release_mode% EQU 0 ( rem Debug
@@ -62,12 +67,14 @@ if %release_mode% EQU 0 ( rem Debug
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4146 ^
+ -wd4505 ^
-wd4456 -wd4457
set compiler_includes= ^
/Isrc\
set libs= ^
kernel32.lib ^
+ Synchronization.lib ^
bin\llvm\windows\LLVM-C.lib
set linker_flags= -incremental:no -opt:ref -subsystem:console
@@ -94,4 +101,4 @@ if %release_mode% EQU 0 odin run examples/demo
del *.obj > NUL 2> NUL
-:end_of_build
\ No newline at end of file
+:end_of_build
diff --git a/build_odin.sh b/build_odin.sh
index 5ecb7709a..9b90a80e2 100755
--- a/build_odin.sh
+++ b/build_odin.sh
@@ -6,13 +6,18 @@ set -eu
: ${CXXFLAGS=}
: ${LDFLAGS=}
: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
+: ${GIT_SHA=}
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
CXXFLAGS="$CXXFLAGS -std=c++14"
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
-GIT_SHA=$(git rev-parse --short HEAD || :)
-if [ "$GIT_SHA" ]; then CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""; fi
+if [ -d ".git" ]; then
+ GIT_SHA=$(git rev-parse --short HEAD || :)
+ if [ "$GIT_SHA" ]; then
+ CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
+ fi
+fi
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
OS=$(uname)
@@ -25,11 +30,11 @@ panic() {
version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
config_darwin() {
- ARCH=$(uname -m)
+ local ARCH=$(uname -m)
: ${LLVM_CONFIG=llvm-config}
# allow for arm only llvm's with version 13
- if [ ARCH == arm64 ]; then
+ if [ "${ARCH}" == "arm64" ]; then
MIN_LLVM_VERSION=("13.0.0")
else
# allow for x86 / amd64 all llvm versions beginning from 11
@@ -37,7 +42,7 @@ config_darwin() {
fi
if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then
- if [ ARCH == arm64 ]; then
+ if [ "${ARCH}" == "arm64" ]; then
panic "Requirement: llvm-config must be base version 13 for arm64"
else
panic "Requirement: llvm-config must be base version greater than 11 for amd64/x86"
@@ -50,7 +55,7 @@ config_darwin() {
panic "Requirement: llvm-config must be base version smaller than 15"
fi
- LDFLAGS="$LDFLAGS -liconv -ldl"
+ LDFLAGS="$LDFLAGS -liconv -ldl -framework System"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lLLVM-C"
}
@@ -59,11 +64,11 @@ config_freebsd() {
: ${LLVM_CONFIG=}
if [ ! "$LLVM_CONFIG" ]; then
- if which llvm-config11 > /dev/null 2>&1; then
+ if [ -x "$(command -v llvm-config11)" ]; then
LLVM_CONFIG=llvm-config11
- elif which llvm-config12 > /dev/null 2>&1; then
+ elif [ -x "$(command -v llvm-config12)" ]; then
LLVM_CONFIG=llvm-config12
- elif which llvm-config13 > /dev/null 2>&1; then
+ elif [ -x "$(command -v llvm-config13)" ]; then
LLVM_CONFIG=llvm-config13
else
panic "Unable to find LLVM-config"
@@ -86,12 +91,14 @@ config_linux() {
: ${LLVM_CONFIG=}
if [ ! "$LLVM_CONFIG" ]; then
- if which llvm-config > /dev/null 2>&1; then
+ if [ -x "$(command -v llvm-config)" ]; then
LLVM_CONFIG=llvm-config
- elif which llvm-config-11 > /dev/null 2>&1; then
+ elif [ -x "$(command -v llvm-config-11)" ]; then
LLVM_CONFIG=llvm-config-11
- elif which llvm-config-11-64 > /dev/null 2>&1; then
+ elif [ -x "$(command -v llvm-config-11-64)" ]; then
LLVM_CONFIG=llvm-config-11-64
+ elif [ -x "$(command -v llvm-config-14)" ]; then
+ LLVM_CONFIG=llvm-config-14
else
panic "Unable to find LLVM-config"
fi
@@ -111,7 +118,7 @@ config_linux() {
LDFLAGS="$LDFLAGS -ldl"
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
- LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
+ LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs --libfiles) -Wl,-rpath=\$ORIGIN"
# Creates a copy of the llvm library in the build dir, this is meant to support compiler explorer.
# The annoyance is that this copy can be cluttering the development folder. TODO: split staging folders
@@ -128,13 +135,21 @@ build_odin() {
EXTRAFLAGS="-O3"
;;
release-native)
- EXTRAFLAGS="-O3 -march=native"
+ local ARCH=$(uname -m)
+ if [ "${ARCH}" == "arm64" ]; then
+ # Use preferred flag for Arm (ie arm64 / aarch64 / etc)
+ EXTRAFLAGS="-O3 -mcpu=native"
+ else
+ # Use preferred flag for x86 / amd64
+ EXTRAFLAGS="-O3 -march=native"
+ fi
;;
nightly)
EXTRAFLAGS="-DNIGHTLY -O3"
;;
*)
panic "Build mode unsupported!"
+ ;;
esac
set -x
@@ -147,7 +162,7 @@ run_demo() {
}
have_which() {
- if ! which which > /dev/null 2>&1; then
+ if ! command -v which > /dev/null 2>&1 ; then
panic "Could not find \`which\`"
fi
}
@@ -169,6 +184,7 @@ FreeBSD)
;;
*)
panic "Platform unsupported!"
+ ;;
esac
if [[ $# -eq 0 ]]; then
diff --git a/core/bufio/lookahead_reader.odin b/core/bufio/lookahead_reader.odin
index 7d6d7832c..f51c167b9 100644
--- a/core/bufio/lookahead_reader.odin
+++ b/core/bufio/lookahead_reader.odin
@@ -2,25 +2,25 @@ package bufio
import "core:io"
-// Loadahead_Reader provides io lookahead.
+// Lookahead_Reader provides io lookahead.
// This is useful for tokenizers/parsers.
-// Loadahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Loadahead_Reader's buffer size
+// Lookahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Lookahead_Reader's buffer size
// will EXACTLY match the specified size, whereas bufio.Reader's buffer size may differ from the specified size.
// This makes sure that the buffer will not be accidentally read beyond the expected size.
-Loadahead_Reader :: struct {
+Lookahead_Reader :: struct {
r: io.Reader,
buf: []byte,
n: int,
}
-lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader {
+lookahead_reader_init :: proc(lr: ^Lookahead_Reader, r: io.Reader, buf: []byte) -> ^Lookahead_Reader {
lr.r = r
lr.buf = buf
lr.n = 0
return lr
}
-lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
+lookahead_reader_buffer :: proc(lr: ^Lookahead_Reader) -> []byte {
return lr.buf[:lr.n]
}
@@ -28,7 +28,7 @@ lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte {
// lookahead_reader_peek returns a slice of the Lookahead_Reader which holds n bytes
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
// NOTE: The returned buffer is not a copy of the underlying buffer
-lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) {
+lookahead_reader_peek :: proc(lr: ^Lookahead_Reader, n: int) -> ([]byte, io.Error) {
switch {
case n < 0:
return nil, .Negative_Read
@@ -58,13 +58,13 @@ lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Erro
// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer
// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest.
// NOTE: The returned buffer is not a copy of the underlying buffer
-lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) {
+lookahead_reader_peek_all :: proc(lr: ^Lookahead_Reader) -> ([]byte, io.Error) {
return lookahead_reader_peek(lr, len(lr.buf))
}
// lookahead_reader_consume drops the first n populated bytes from the Lookahead_Reader.
-lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
+lookahead_reader_consume :: proc(lr: ^Lookahead_Reader, n: int) -> io.Error {
switch {
case n == 0:
return nil
@@ -78,6 +78,6 @@ lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error {
return nil
}
-lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error {
+lookahead_reader_consume_all :: proc(lr: ^Lookahead_Reader) -> io.Error {
return lookahead_reader_consume(lr, lr.n)
}
diff --git a/core/bufio/read_writer.odin b/core/bufio/read_writer.odin
index f9ae1ed45..3e6bd3aa0 100644
--- a/core/bufio/read_writer.odin
+++ b/core/bufio/read_writer.odin
@@ -14,51 +14,29 @@ read_writer_init :: proc(rw: ^Read_Writer, r: ^Reader, w: ^Writer) {
}
read_writer_to_stream :: proc(rw: ^Read_Writer) -> (s: io.Stream) {
- s.stream_data = rw
- s.stream_vtable = &_read_writer_vtable
+ s.procedure = _read_writer_procedure
+ s.data = rw
return
}
@(private)
-_read_writer_vtable := io.Stream_VTable{
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Read_Writer)(s.stream_data).r
- return reader_read(b, p)
- },
- impl_unread_byte = proc(s: io.Stream) -> io.Error {
- b := (^Read_Writer)(s.stream_data).r
- return reader_unread_byte(b)
- },
- impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
- b := (^Read_Writer)(s.stream_data).r
- return reader_read_rune(b)
- },
- impl_unread_rune = proc(s: io.Stream) -> io.Error {
- b := (^Read_Writer)(s.stream_data).r
- return reader_unread_rune(b)
- },
- impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- b := (^Read_Writer)(s.stream_data).r
- return reader_write_to(b, w)
- },
- impl_flush = proc(s: io.Stream) -> io.Error {
- b := (^Read_Writer)(s.stream_data).w
- return writer_flush(b)
- },
- impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Read_Writer)(s.stream_data).w
- return writer_write(b, p)
- },
- impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
- b := (^Read_Writer)(s.stream_data).w
- return writer_write_byte(b, c)
- },
- impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
- b := (^Read_Writer)(s.stream_data).w
- return writer_write_rune(b, r)
- },
- impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
- b := (^Read_Writer)(s.stream_data).w
- return writer_read_from(b, r)
- },
-}
+_read_writer_procedure := proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ rw := (^Read_Writer)(stream_data)
+ n_int: int
+ #partial switch mode {
+ case .Flush:
+ err = writer_flush(rw.w)
+ return
+ case .Read:
+ n_int, err = reader_read(rw.r, p)
+ n = i64(n_int)
+ return
+ case .Write:
+ n_int, err = writer_write(rw.w, p)
+ n = i64(n_int)
+ return
+ case .Query:
+ return io.query_utility({.Flush, .Read, .Write, .Query})
+ }
+ return 0, .Empty
+}
\ No newline at end of file
diff --git a/core/bufio/reader.odin b/core/bufio/reader.odin
index 6bfc4cd9d..dc4e02c02 100644
--- a/core/bufio/reader.odin
+++ b/core/bufio/reader.odin
@@ -311,18 +311,6 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
}
m: i64
- if nr, ok := io.to_writer_to(b.rd); ok {
- m, err = io.write_to(nr, w)
- n += m
- return n, err
- }
-
- if nw, ok := io.to_reader_from(w); ok {
- m, err = io.read_from(nw, b.rd)
- n += m
- return n, err
- }
-
if b.w-b.r < len(b.buf) {
if err = _reader_read_new_chunk(b); err != nil {
return
@@ -352,48 +340,28 @@ reader_write_to :: proc(b: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
// reader_to_stream converts a Reader into an io.Stream
reader_to_stream :: proc(b: ^Reader) -> (s: io.Stream) {
- s.stream_data = b
- s.stream_vtable = &_reader_vtable
+ s.data = b
+ s.procedure = _reader_proc
return
}
@(private)
-_reader_vtable := io.Stream_VTable{
- impl_destroy = proc(s: io.Stream) -> io.Error {
- b := (^Reader)(s.stream_data)
+_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ b := (^Reader)(stream_data)
+ #partial switch mode {
+ case .Read:
+ return io._i64_err(reader_read(b, p))
+ case .Destroy:
reader_destroy(b)
- return nil
- },
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Reader)(s.stream_data)
- return reader_read(b, p)
- },
- impl_read_byte = proc(s: io.Stream) -> (c: byte, err: io.Error) {
- b := (^Reader)(s.stream_data)
- return reader_read_byte(b)
- },
- impl_unread_byte = proc(s: io.Stream) -> io.Error {
- b := (^Reader)(s.stream_data)
- return reader_unread_byte(b)
- },
- impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
- b := (^Reader)(s.stream_data)
- return reader_read_rune(b)
- },
- impl_unread_rune = proc(s: io.Stream) -> io.Error {
- b := (^Reader)(s.stream_data)
- return reader_unread_rune(b)
- },
- impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- b := (^Reader)(s.stream_data)
- return reader_write_to(b, w)
- },
+ return
+ case .Query:
+ return io.query_utility({.Read, .Destroy, .Query})
+ }
+ return 0, .Empty
}
-
-
//
// Utility procedures
//
diff --git a/core/bufio/writer.odin b/core/bufio/writer.odin
index 9e38395ee..bfa8b804f 100644
--- a/core/bufio/writer.odin
+++ b/core/bufio/writer.odin
@@ -173,14 +173,6 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
if b.err != nil {
return 0, b.err
}
- if writer_buffered(b) == 0 {
- if w, ok := io.to_reader_from(b.wr); !ok {
- n, err = io.read_from(w, r)
- b.err = err
- return
- }
- }
-
for {
if writer_available(b) == 0 {
writer_flush(b) or_return
@@ -222,38 +214,35 @@ writer_read_from :: proc(b: ^Writer, r: io.Reader) -> (n: i64, err: io.Error) {
// writer_to_stream converts a Writer into an io.Stream
writer_to_stream :: proc(b: ^Writer) -> (s: io.Stream) {
- s.stream_data = b
- s.stream_vtable = &_writer_vtable
+ s.data = b
+ s.procedure = _writer_proc
return
}
+// writer_to_stream converts a Writer into an io.Stream
+writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
+ return writer_to_stream(b)
+}
+
@(private)
-_writer_vtable := io.Stream_VTable{
- impl_destroy = proc(s: io.Stream) -> io.Error {
- b := (^Writer)(s.stream_data)
+_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ b := (^Writer)(stream_data)
+ #partial switch mode {
+ case .Flush:
+ err = writer_flush(b)
+ return
+ case .Write:
+ n_int: int
+ n_int, err = writer_write(b, p)
+ n = i64(n_int)
+ return
+ case .Destroy:
writer_destroy(b)
- return nil
- },
- impl_flush = proc(s: io.Stream) -> io.Error {
- b := (^Writer)(s.stream_data)
- return writer_flush(b)
- },
- impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Writer)(s.stream_data)
- return writer_write(b, p)
- },
- impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
- b := (^Writer)(s.stream_data)
- return writer_write_byte(b, c)
- },
- impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
- b := (^Writer)(s.stream_data)
- return writer_write_rune(b, r)
- },
- impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
- b := (^Writer)(s.stream_data)
- return writer_read_from(b, r)
- },
+ return
+ case .Query:
+ return io.query_utility({.Flush, .Write, .Destroy, .Query})
+ }
+ return 0, .Empty
}
diff --git a/core/builtin/builtin.odin b/core/builtin/builtin.odin
index d1cdfa6e7..211db9770 100644
--- a/core/builtin/builtin.odin
+++ b/core/builtin/builtin.odin
@@ -94,7 +94,15 @@ cap :: proc(array: Array_Type) -> int ---
size_of :: proc($T: typeid) -> int ---
align_of :: proc($T: typeid) -> int ---
-offset_of :: proc($T: typeid) -> uintptr ---
+
+// e.g. offset_of(t.f), where t is an instance of the type T
+offset_of_selector :: proc(selector: $T) -> uintptr ---
+// e.g. offset_of(T, f), where T can be the type instead of a variable
+offset_of_member :: proc($T: typeid, member: $M) -> uintptr ---
+offset_of :: proc{offset_of_selector, offset_of_member}
+// e.g. offset_of(T, "f"), where T can be the type instead of a variable
+offset_of_by_string :: proc($T: typeid, member: string) -> uintptr ---
+
type_of :: proc(x: expr) -> type ---
type_info_of :: proc($T: typeid) -> ^runtime.Type_Info ---
typeid_of :: proc($T: typeid) -> typeid ---
@@ -109,7 +117,7 @@ jmag :: proc(value: Quaternion) -> Float ---
kmag :: proc(value: Quaternion) -> Float ---
conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion ---
-expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
+expand_values :: proc(value: Struct_Or_Array) -> (A, B, C, ...) ---
min :: proc(values: ..T) -> T ---
max :: proc(values: ..T) -> T ---
diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin
index bba834f7e..4375d8195 100644
--- a/core/bytes/buffer.odin
+++ b/core/bytes/buffer.odin
@@ -38,6 +38,11 @@ buffer_init_string :: proc(b: ^Buffer, s: string) {
}
buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) {
+ if b.buf == nil {
+ b.buf = make([dynamic]byte, len, cap, allocator)
+ return
+ }
+
b.buf.allocator = allocator
reserve(&b.buf, cap)
resize(&b.buf, len)
@@ -370,69 +375,31 @@ buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #n
buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) {
- s.stream_data = b
- s.stream_vtable = &_buffer_vtable
+ s.data = b
+ s.procedure = _buffer_proc
return
}
@(private)
-_buffer_vtable := io.Stream_VTable{
- impl_size = proc(s: io.Stream) -> i64 {
- b := (^Buffer)(s.stream_data)
- return i64(buffer_capacity(b))
- },
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_read(b, p)
- },
- impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_read_at(b, p, int(offset))
- },
- impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_read_byte(b)
- },
- impl_read_rune = proc(s: io.Stream) -> (r: rune, size: int, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_read_rune(b)
- },
- impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_write(b, p)
- },
- impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_write_at(b, p, int(offset))
- },
- impl_write_byte = proc(s: io.Stream, c: byte) -> io.Error {
- b := (^Buffer)(s.stream_data)
- return buffer_write_byte(b, c)
- },
- impl_write_rune = proc(s: io.Stream, r: rune) -> (int, io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_write_rune(b, r)
- },
- impl_unread_byte = proc(s: io.Stream) -> io.Error {
- b := (^Buffer)(s.stream_data)
- return buffer_unread_byte(b)
- },
- impl_unread_rune = proc(s: io.Stream) -> io.Error {
- b := (^Buffer)(s.stream_data)
- return buffer_unread_rune(b)
- },
- impl_destroy = proc(s: io.Stream) -> io.Error {
- b := (^Buffer)(s.stream_data)
+_buffer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ b := (^Buffer)(stream_data)
+ #partial switch mode {
+ case .Read:
+ return io._i64_err(buffer_read(b, p))
+ case .Read_At:
+ return io._i64_err(buffer_read_at(b, p, int(offset)))
+ case .Write:
+ return io._i64_err(buffer_write(b, p))
+ case .Write_At:
+ return io._i64_err(buffer_write_at(b, p, int(offset)))
+ case .Size:
+ n = i64(buffer_capacity(b))
+ return
+ case .Destroy:
buffer_destroy(b)
- return nil
- },
- impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_write_to(b, w)
- },
- impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
- b := (^Buffer)(s.stream_data)
- return buffer_read_from(b, r)
- },
+ return
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Size, .Destroy})
+ }
+ return 0, .Empty
}
-
diff --git a/core/bytes/reader.odin b/core/bytes/reader.odin
index 7c37f3061..4b18345ba 100644
--- a/core/bytes/reader.odin
+++ b/core/bytes/reader.odin
@@ -16,8 +16,8 @@ reader_init :: proc(r: ^Reader, s: []byte) {
}
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
- s.stream_data = r
- s.stream_vtable = &_reader_vtable
+ s.data = r
+ s.procedure = _reader_proc
return
}
@@ -137,41 +137,22 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
@(private)
-_reader_vtable := io.Stream_VTable{
- impl_size = proc(s: io.Stream) -> i64 {
- r := (^Reader)(s.stream_data)
- return reader_size(r)
- },
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read(r, p)
- },
- impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read_at(r, p, off)
- },
- impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read_byte(r)
- },
- impl_unread_byte = proc(s: io.Stream) -> io.Error {
- r := (^Reader)(s.stream_data)
- return reader_unread_byte(r)
- },
- impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read_rune(r)
- },
- impl_unread_rune = proc(s: io.Stream) -> io.Error {
- r := (^Reader)(s.stream_data)
- return reader_unread_rune(r)
- },
- impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_seek(r, offset, whence)
- },
- impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_write_to(r, w)
- },
+_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ r := (^Reader)(stream_data)
+ #partial switch mode {
+ case .Read:
+ return io._i64_err(reader_read(r, p))
+ case .Read_At:
+ return io._i64_err(reader_read_at(r, p, offset))
+ case .Seek:
+ n, err = reader_seek(r, offset, whence)
+ return
+ case .Size:
+ n = reader_size(r)
+ return
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Seek, .Size, .Query})
+ }
+ return 0, .Empty
}
+
diff --git a/core/c/frontend/preprocessor/preprocess.odin b/core/c/frontend/preprocessor/preprocess.odin
index 4cfad2c1c..b5eab0bb3 100644
--- a/core/c/frontend/preprocessor/preprocess.odin
+++ b/core/c/frontend/preprocessor/preprocess.odin
@@ -1118,7 +1118,7 @@ expand_macro :: proc(cpp: ^Preprocessor, rest: ^^Token, tok: ^Token) -> bool {
search_include_next :: proc(cpp: ^Preprocessor, filename: string) -> (path: string, ok: bool) {
for ; cpp.include_next_index < len(cpp.include_paths); cpp.include_next_index += 1 {
- tpath := filepath.join(elems={cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator)
+ tpath := filepath.join({cpp.include_paths[cpp.include_next_index], filename}, allocator=context.temp_allocator)
if os.exists(tpath) {
return strings.clone(tpath), true
}
@@ -1136,7 +1136,7 @@ search_include_paths :: proc(cpp: ^Preprocessor, filename: string) -> (path: str
}
for include_path in cpp.include_paths {
- tpath := filepath.join(elems={include_path, filename}, allocator=context.temp_allocator)
+ tpath := filepath.join({include_path, filename}, allocator=context.temp_allocator)
if os.exists(tpath) {
path, ok = strings.clone(tpath), true
cpp.filepath_cache[filename] = path
diff --git a/core/c/libc/threads.odin b/core/c/libc/threads.odin
index f6aae2e98..8e3343234 100644
--- a/core/c/libc/threads.odin
+++ b/core/c/libc/threads.odin
@@ -44,7 +44,7 @@ when ODIN_OS == .Windows {
@(link_name="_Cnd_destroy") cnd_destroy :: proc(cond: ^cnd_t) ---
@(link_name="_Cnd_init") cnd_init :: proc(cond: ^cnd_t) -> int ---
@(link_name="_Cnd_signal") cnd_signal :: proc(cond: ^cnd_t) -> int ---
- @(link_name="_Cnd_timedwait") cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---
+ @(link_name="_Cnd_timedwait") cnd_timedwait :: proc(cond: ^cnd_t, mtx: ^mtx_t, ts: ^timespec) -> int ---
@(link_name="_Cnd_wait") cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
// 7.26.4 Mutex functions
@@ -108,7 +108,7 @@ when ODIN_OS == .Linux {
cnd_destroy :: proc(cond: ^cnd_t) ---
cnd_init :: proc(cond: ^cnd_t) -> int ---
cnd_signal :: proc(cond: ^cnd_t) -> int ---
- cnd_timedwait :: proc(cond: ^cnd_t, ts: ^timespec) -> int ---
+ cnd_timedwait :: proc(cond: ^cnd_t, mtx: ^mtx_t, ts: ^timespec) -> int ---
cnd_wait :: proc(cond: ^cnd_t, mtx: ^mtx_t) -> int ---
// 7.26.4 Mutex functions
diff --git a/core/compress/common.odin b/core/compress/common.odin
index b42cbefff..bc56229c2 100644
--- a/core/compress/common.odin
+++ b/core/compress/common.odin
@@ -188,7 +188,8 @@ input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Erro
}
input_size_from_stream :: proc(z: ^Context_Stream_Input) -> (res: i64, err: Error) {
- return io.size(z.input), nil
+ res, _ = io.size(z.input)
+ return
}
input_size :: proc{input_size_from_memory, input_size_from_stream}
@@ -212,8 +213,10 @@ read_slice_from_memory :: #force_inline proc(z: ^Context_Memory_Input, size: int
@(optimization_mode="speed")
read_slice_from_stream :: #force_inline proc(z: ^Context_Stream_Input, size: int) -> (res: []u8, err: io.Error) {
+ // TODO: REMOVE ALL USE OF context.temp_allocator here
+ // the is literally no need for it
b := make([]u8, size, context.temp_allocator)
- _, e := z.input->impl_read(b[:])
+ _, e := io.read(z.input, b[:])
if e == .None {
return b, .None
}
diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin
index 4de4d1b63..b0ca4491b 100644
--- a/core/compress/gzip/gzip.odin
+++ b/core/compress/gzip/gzip.odin
@@ -335,7 +335,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
// fmt.printf("GZIP: Expected Payload Size: %v\n", expected_output_size);
- zlib_error := zlib.inflate_raw(z=z, expected_output_size=expected_output_size)
+ zlib_error := zlib.inflate_raw(z, expected_output_size=expected_output_size)
if zlib_error != nil {
return zlib_error
}
diff --git a/core/compress/shoco/shoco.odin b/core/compress/shoco/shoco.odin
index f94ce70b7..04b0bfdc2 100644
--- a/core/compress/shoco/shoco.odin
+++ b/core/compress/shoco/shoco.odin
@@ -177,12 +177,10 @@ decompress_slice_to_string :: proc(input: []u8, model := DEFAULT_MODEL, allocato
max_output_size := decompress_bound(len(input), model)
buf: [dynamic]u8
- if !resize(&buf, max_output_size) {
- return "", .Out_Of_Memory
- }
+ resize(&buf, max_output_size) or_return
length, result := decompress_slice_to_output_buffer(input, buf[:])
- resize(&buf, length)
+ resize(&buf, length) or_return
return string(buf[:]), result
}
decompress :: proc{decompress_slice_to_output_buffer, decompress_slice_to_string}
@@ -307,12 +305,10 @@ compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := cont
max_output_size := compress_bound(len(input))
buf: [dynamic]u8
- if !resize(&buf, max_output_size) {
- return {}, .Out_Of_Memory
- }
+ resize(&buf, max_output_size) or_return
length, result := compress_string_to_buffer(input, buf[:])
- resize(&buf, length)
+ resize(&buf, length) or_return
return buf[:length], result
}
compress :: proc{compress_string_to_buffer, compress_string}
\ No newline at end of file
diff --git a/core/compress/zlib/zlib.odin b/core/compress/zlib/zlib.odin
index 855eef7a8..21172e8e8 100644
--- a/core/compress/zlib/zlib.odin
+++ b/core/compress/zlib/zlib.odin
@@ -471,7 +471,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
}
// Parse ZLIB stream without header.
- inflate_raw(z=ctx, expected_output_size=expected_output_size) or_return
+ inflate_raw(ctx, expected_output_size=expected_output_size) or_return
if !raw {
compress.discard_to_next_byte_lsb(ctx)
@@ -665,7 +665,7 @@ inflate_from_byte_array :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, e
ctx.input_data = input
ctx.output = buf
- return inflate_from_context(ctx=&ctx, raw=raw, expected_output_size=expected_output_size)
+ return inflate_from_context(&ctx, raw=raw, expected_output_size=expected_output_size)
}
inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := false, expected_output_size := -1) -> (err: Error) {
@@ -674,7 +674,7 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
ctx.input_data = input
ctx.output = buf
- return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
+ return inflate_raw(&ctx, expected_output_size=expected_output_size)
}
inflate :: proc{inflate_from_context, inflate_from_byte_array}
diff --git a/core/container/bit_array/bit_array.odin b/core/container/bit_array/bit_array.odin
index 763a19f8b..d649d039f 100644
--- a/core/container/bit_array/bit_array.odin
+++ b/core/container/bit_array/bit_array.odin
@@ -27,27 +27,28 @@ Bit_Array_Iterator :: struct {
word_idx: int,
bit_idx: uint,
}
-
/*
- In:
- - ba: ^Bit_Array - the array to iterate over
+Wraps a `Bit_Array` into an Iterator
- Out:
- - it: ^Bit_Array_Iterator - the iterator that holds iteration state
+Inputs:
+- ba: Pointer to the Bit_Array
+
+Returns:
+- it: Iterator struct
*/
make_iterator :: proc (ba: ^Bit_Array) -> (it: Bit_Array_Iterator) {
return Bit_Array_Iterator { array = ba }
}
-
/*
- In:
- - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
+Returns the next bit, including its set-state. ok=false once exhausted
- Out:
- - set: bool - the state of the bit at `index`
- - index: int - the next bit of the Bit_Array referenced by `it`.
- - ok: bool - `true` if the iterator returned a valid index,
- `false` if there were no more bits
+Inputs:
+- it: The iterator that holds the state.
+
+Returns:
+- set: `true` if the bit at `index` is set.
+- index: The next bit of the Bit_Array referenced by `it`.
+- ok: `true` if the iterator can continue, `false` if the iterator is done
*/
iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok: bool) {
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
@@ -64,39 +65,51 @@ iterate_by_all :: proc (it: ^Bit_Array_Iterator) -> (set: bool, index: int, ok:
return set, index, true
}
-
/*
- In:
- - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
+Returns the next Set Bit, for example if `0b1010`, then the iterator will return index={1, 3} over two calls.
- Out:
- - index: int - the next set bit of the Bit_Array referenced by `it`.
- - ok: bool - `true` if the iterator returned a valid index,
- `false` if there were no more bits set
+Inputs:
+- it: The iterator that holds the state.
+
+Returns:
+- index: The next *set* bit of the Bit_Array referenced by `it`.
+- ok: `true` if the iterator can continue, `false` if the iterator is done
*/
iterate_by_set :: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
return iterate_internal_(it, true)
}
-
/*
- In:
- - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
+Returns the next Unset Bit, for example if `0b1010`, then the iterator will return index={0, 2} over two calls.
- Out:
- - index: int - the next unset bit of the Bit_Array referenced by `it`.
- - ok: bool - `true` if the iterator returned a valid index,
- `false` if there were no more unset bits
+Inputs:
+- it: The iterator that holds the state.
+
+Returns:
+- index: The next *unset* bit of the Bit_Array referenced by `it`.
+- ok: `true` if the iterator can continue, `false` if the iterator is done
*/
iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
return iterate_internal_(it, false)
}
+/*
+Iterates through set/unset bits
+*Private*
+
+Inputs:
+- it: The iterator that holds the state.
+- ITERATE_SET_BITS: `true` for returning only set bits, false for returning only unset bits
+
+Returns:
+- index: The next *unset* bit of the Bit_Array referenced by `it`.
+- ok: `true` if the iterator can continue, `false` if the iterator is done
+*/
@(private="file")
iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) -> (index: int, ok: bool) {
word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
when ! ITERATE_SET_BITS { word = ~word }
- // if the word is empty or we have already gone over all the bits in it,
+ // If the word is empty or we have already gone over all the bits in it,
// b.bit_idx is greater than the index of any set bit in the word,
// meaning that word >> b.bit_idx == 0.
for it.word_idx < len(it.array.bits) && word >> it.bit_idx == 0 {
@@ -106,14 +119,14 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
when ! ITERATE_SET_BITS { word = ~word }
}
- // if we are iterating the set bits, reaching the end of the array means we have no more bits to check
+ // If we are iterating the set bits, reaching the end of the array means we have no more bits to check
when ITERATE_SET_BITS {
if it.word_idx >= len(it.array.bits) {
return 0, false
}
}
- // reaching here means that the word has some set bits
+ // Reaching here means that the word has some set bits
it.bit_idx += uint(intrinsics.count_trailing_zeros(word >> it.bit_idx))
index = it.word_idx * NUM_BITS + int(it.bit_idx) + it.array.bias
@@ -124,24 +137,21 @@ iterate_internal_ :: proc (it: ^Bit_Array_Iterator, $ITERATE_SET_BITS: bool) ->
}
return index, index <= it.array.max_index
}
-
-
/*
- In:
- - ba: ^Bit_Array - a pointer to the Bit Array
- - index: The bit index. Can be an enum member.
+Gets the state of a bit in the bit-array
- Out:
- - res: The bit you're interested in.
- - ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
+Inputs:
+- ba: Pointer to the Bit_Array
+- index: Which bit in the array
- The `ok` return value may be ignored.
+Returns:
+- res: `true` if the bit at `index` is set.
+- ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
*/
-get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
+get :: proc(ba: ^Bit_Array, #any_int index: uint) -> (res: bool, ok: bool) #optional_ok {
idx := int(index) - ba.bias
if ba == nil || int(index) < ba.bias { return false, false }
- context.allocator = allocator
leg_index := idx >> INDEX_SHIFT
bit_index := idx & INDEX_MASK
@@ -157,18 +167,36 @@ get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
return res, true
}
-
/*
- In:
- - ba: ^Bit_Array - a pointer to the Bit Array
- - index: The bit index. Can be an enum member.
+Gets the state of a bit in the bit-array
- Out:
- - ok: Whether or not we managed to set requested bit.
+*Bypasses all Checks*
- `set` automatically resizes the Bit Array to accommodate the requested index if needed.
+Inputs:
+- ba: Pointer to the Bit_Array
+- index: Which bit in the array
+
+Returns:
+- `true` if bit is set
*/
-set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
+unsafe_get :: #force_inline proc(ba: ^Bit_Array, #any_int index: uint) -> bool #no_bounds_check {
+ return bool((ba.bits[index >> INDEX_SHIFT] >> uint(index & INDEX_MASK)) & 1)
+}
+/*
+Sets the state of a bit in the bit-array
+
+*Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
+
+Inputs:
+- ba: Pointer to the Bit_Array
+- index: Which bit in the array
+- set_to: `true` sets the bit on, `false` to turn it off
+- allocator: (default is context.allocator)
+
+Returns:
+- ok: Whether the set was successful, `false` on allocation failure or bad index
+*/
+set :: proc(ba: ^Bit_Array, #any_int index: uint, set_to: bool = true, allocator := context.allocator) -> (ok: bool) {
idx := int(index) - ba.bias
@@ -181,65 +209,97 @@ set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator
resize_if_needed(ba, leg_index) or_return
ba.max_index = max(idx, ba.max_index)
- ba.bits[leg_index] |= 1 << uint(bit_index)
+
+ if set_to{ ba.bits[leg_index] |= 1 << uint(bit_index) }
+ else { ba.bits[leg_index] &= ~(1 << uint(bit_index)) }
+
return true
}
-
/*
- In:
- - ba: ^Bit_Array - a pointer to the Bit Array
- - index: The bit index. Can be an enum member.
+Sets the state of a bit in the bit-array
- Out:
- - ok: Whether or not we managed to unset requested bit.
+*Bypasses all checks*
- `unset` automatically resizes the Bit Array to accommodate the requested index if needed.
+Inputs:
+- ba: Pointer to the Bit_Array
+- index: Which bit in the array
*/
-unset :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
-
- idx := int(index) - ba.bias
-
- if ba == nil || int(index) < ba.bias { return false }
- context.allocator = allocator
-
- leg_index := idx >> INDEX_SHIFT
- bit_index := idx & INDEX_MASK
-
- resize_if_needed(ba, leg_index) or_return
-
- ba.max_index = max(idx, ba.max_index)
- ba.bits[leg_index] &= ~(1 << uint(bit_index))
- return true
+unsafe_set :: proc(ba: ^Bit_Array, bit: int) #no_bounds_check {
+ ba.bits[bit >> INDEX_SHIFT] |= 1 << uint(bit & INDEX_MASK)
}
-
/*
- A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+Unsets the state of a bit in the bit-array. (Convienence wrapper for `set`)
+
+*Conditionally Allocates (Resizes backing data when `index > len(ba.bits)`)*
+
+Inputs:
+- ba: Pointer to the Bit_Array
+- index: Which bit in the array
+- allocator: (default is context.allocator)
+
+Returns:
+- ok: Whether the unset was successful, `false` on allocation failure or bad index
*/
-create :: proc(max_index: int, min_index := 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
+unset :: #force_inline proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
+ return set(ba, index, false, allocator)
+}
+/*
+Unsets the state of a bit in the bit-array
+
+*Bypasses all Checks*
+
+Inputs:
+- ba: Pointer to the Bit_Array
+- index: Which bit in the array
+*/
+unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
+ b.bits[bit >> INDEX_SHIFT] &= ~(1 << uint(bit & INDEX_MASK))
+}
+/*
+A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+
+*Allocates (`new(Bit_Array) & make(ba.bits)`)*
+
+Inputs:
+- max_index: maximum starting index
+- min_index: minimum starting index (used as a bias)
+- allocator: (default is context.allocator)
+
+Returns:
+- ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
+*/
+create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
context.allocator = allocator
size_in_bits := max_index - min_index
if size_in_bits < 1 { return {}, false }
legs := size_in_bits >> INDEX_SHIFT
-
+ if size_in_bits & INDEX_MASK > 0 {legs+=1}
+ bits, err := make([dynamic]u64, legs)
+ ok = err == mem.Allocator_Error.None
res = new(Bit_Array)
+ res.bits = bits
res.bias = min_index
res.max_index = max_index
res.free_pointer = true
- return res, resize_if_needed(res, legs)
+ return
}
-
/*
- Sets all bits to `false`.
+Sets all values in the Bit_Array to zero.
+
+Inputs:
+- ba: The target Bit_Array
*/
clear :: proc(ba: ^Bit_Array) {
if ba == nil { return }
mem.zero_slice(ba.bits[:])
}
-
/*
- Releases the memory used by the Bit Array.
+Deallocates the Bit_Array and its backing storage
+
+Inputs:
+- ba: The target Bit_Array
*/
destroy :: proc(ba: ^Bit_Array) {
if ba == nil { return }
@@ -248,9 +308,8 @@ destroy :: proc(ba: ^Bit_Array) {
free(ba)
}
}
-
/*
- Resizes the Bit Array. For internal use.
+ Resizes the Bit Array. For internal use. Provisions needed capacity+1
If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
*/
@(private="file")
diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin
index b3a8ad43f..af44ef671 100644
--- a/core/container/queue/queue.odin
+++ b/core/container/queue/queue.odin
@@ -14,7 +14,7 @@ Queue :: struct($T: typeid) {
DEFAULT_CAPACITY :: 16
// Procedure to initialize a queue
-init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> bool {
+init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> runtime.Allocator_Error {
if q.data.allocator.procedure == nil {
q.data.allocator = allocator
}
@@ -55,11 +55,11 @@ space :: proc(q: $Q/Queue($T)) -> int {
}
// Reserve enough space for at least the specified capacity
-reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> bool {
+reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> runtime.Allocator_Error {
if uint(capacity) > q.len {
return _grow(q, uint(capacity))
}
- return true
+ return nil
}
@@ -112,25 +112,25 @@ peek_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> ^T {
}
// Push an element to the back of the queue
-push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+push_back :: proc(q: ^$Q/Queue($T), elem: T) -> (ok: bool, err: runtime.Allocator_Error) {
if space(q^) == 0 {
_grow(q) or_return
}
idx := (q.offset+uint(q.len))%builtin.len(q.data)
q.data[idx] = elem
q.len += 1
- return true
+ return true, nil
}
// Push an element to the front of the queue
-push_front :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+push_front :: proc(q: ^$Q/Queue($T), elem: T) -> (ok: bool, err: runtime.Allocator_Error) {
if space(q^) == 0 {
_grow(q) or_return
}
q.offset = uint(q.offset - 1 + builtin.len(q.data)) % builtin.len(q.data)
q.len += 1
q.data[q.offset] = elem
- return true
+ return true, nil
}
@@ -173,7 +173,7 @@ pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
}
// Push multiple elements to the front of the queue
-push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
+push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> (ok: bool, err: runtime.Allocator_Error) {
n := uint(builtin.len(elems))
if space(q^) < int(n) {
_grow(q, q.len + n) or_return
@@ -188,7 +188,7 @@ push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
copy(q.data[insert_from:], elems[:insert_to])
copy(q.data[:insert_from], elems[insert_to:])
q.len += n
- return true
+ return true, nil
}
// Consume `n` elements from the front of the queue
@@ -225,7 +225,7 @@ clear :: proc(q: ^$Q/Queue($T)) {
// Internal growinh procedure
-_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
+_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> runtime.Allocator_Error {
new_capacity := max(min_capacity, uint(8), uint(builtin.len(q.data))*2)
n := uint(builtin.len(q.data))
builtin.resize(&q.data, int(new_capacity)) or_return
@@ -234,5 +234,5 @@ _grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
q.offset += new_capacity - n
}
- return true
+ return nil
}
diff --git a/core/crypto/_fiat/fiat.odin b/core/crypto/_fiat/fiat.odin
index ae9727149..f0551722f 100644
--- a/core/crypto/_fiat/fiat.odin
+++ b/core/crypto/_fiat/fiat.odin
@@ -9,14 +9,16 @@ package fiat
u1 :: distinct u8
i1 :: distinct i8
-cmovznz_u64 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
+@(optimization_mode="none")
+cmovznz_u64 :: proc "contextless" (arg1: u1, arg2, arg3: u64) -> (out1: u64) {
x1 := (u64(arg1) * 0xffffffffffffffff)
x2 := ((x1 & arg3) | ((~x1) & arg2))
out1 = x2
return
}
-cmovznz_u32 :: #force_inline proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
+@(optimization_mode="none")
+cmovznz_u32 :: proc "contextless" (arg1: u1, arg2, arg3: u32) -> (out1: u32) {
x1 := (u32(arg1) * 0xffffffff)
x2 := ((x1 & arg3) | ((~x1) & arg2))
out1 = x2
diff --git a/core/crypto/_fiat/field_curve25519/field51.odin b/core/crypto/_fiat/field_curve25519/field51.odin
index e4ca98b57..0be94eb51 100644
--- a/core/crypto/_fiat/field_curve25519/field51.odin
+++ b/core/crypto/_fiat/field_curve25519/field51.odin
@@ -305,7 +305,8 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
out1[4] = x5
}
-fe_cond_assign :: proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) {
+@(optimization_mode="none")
+fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: int) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
@@ -596,7 +597,8 @@ fe_set :: proc "contextless" (out1, arg1: ^Tight_Field_Element) {
out1[4] = x5
}
-fe_cond_swap :: proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
+@(optimization_mode="none")
+fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: int) {
mask := -u64(arg1)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
diff --git a/core/crypto/_fiat/field_poly1305/field4344.odin b/core/crypto/_fiat/field_poly1305/field4344.odin
index ba9bc2694..8e8a7cc78 100644
--- a/core/crypto/_fiat/field_poly1305/field4344.odin
+++ b/core/crypto/_fiat/field_poly1305/field4344.odin
@@ -201,7 +201,8 @@ fe_opp :: proc "contextless" (out1: ^Loose_Field_Element, arg1: ^Tight_Field_Ele
out1[2] = x3
}
-fe_cond_assign :: proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) {
+@(optimization_mode="none")
+fe_cond_assign :: #force_no_inline proc "contextless" (out1, arg1: ^Tight_Field_Element, arg2: bool) {
x1 := fiat.cmovznz_u64(fiat.u1(arg2), out1[0], arg1[0])
x2 := fiat.cmovznz_u64(fiat.u1(arg2), out1[1], arg1[1])
x3 := fiat.cmovznz_u64(fiat.u1(arg2), out1[2], arg1[2])
@@ -342,7 +343,8 @@ fe_set :: #force_inline proc "contextless" (out1, arg1: ^Tight_Field_Element) {
out1[2] = x3
}
-fe_cond_swap :: proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
+@(optimization_mode="none")
+fe_cond_swap :: #force_no_inline proc "contextless" (out1, out2: ^Tight_Field_Element, arg1: bool) {
mask := -u64(arg1)
x := (out1[0] ~ out2[0]) & mask
x1, y1 := out1[0] ~ x, out2[0] ~ x
diff --git a/core/crypto/blake/blake.odin b/core/crypto/blake/blake.odin
index 5fc0a02b9..3685109e4 100644
--- a/core/crypto/blake/blake.odin
+++ b/core/crypto/blake/blake.odin
@@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin
index e75d74197..8f0770f82 100644
--- a/core/crypto/blake2b/blake2b.odin
+++ b/core/crypto/blake2b/blake2b.odin
@@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_blake2.update(&ctx, buf[:read])
}
diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin
index 831335081..6a2d4ab9b 100644
--- a/core/crypto/blake2s/blake2s.odin
+++ b/core/crypto/blake2s/blake2s.odin
@@ -77,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_blake2.update(&ctx, buf[:read])
}
diff --git a/core/crypto/chacha20/chacha20.odin b/core/crypto/chacha20/chacha20.odin
index 229949c22..b29dc1228 100644
--- a/core/crypto/chacha20/chacha20.odin
+++ b/core/crypto/chacha20/chacha20.odin
@@ -8,15 +8,23 @@ KEY_SIZE :: 32
NONCE_SIZE :: 12
XNONCE_SIZE :: 24
+@(private)
_MAX_CTR_IETF :: 0xffffffff
+@(private)
_BLOCK_SIZE :: 64
+@(private)
_STATE_SIZE_U32 :: 16
+@(private)
_ROUNDS :: 20
+@(private)
_SIGMA_0 : u32 : 0x61707865
+@(private)
_SIGMA_1 : u32 : 0x3320646e
+@(private)
_SIGMA_2 : u32 : 0x79622d32
+@(private)
_SIGMA_3 : u32 : 0x6b206574
Context :: struct {
@@ -179,6 +187,7 @@ reset :: proc (ctx: ^Context) {
ctx._is_initialized = false
}
+@(private)
_do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
// Enforce the maximum consumed keystream per nonce.
//
@@ -441,6 +450,7 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
}
}
+@(private)
_hchacha20 :: proc (dst, key, nonce: []byte) {
x0, x1, x2, x3 := _SIGMA_0, _SIGMA_1, _SIGMA_2, _SIGMA_3
x4 := util.U32_LE(key[0:4])
diff --git a/core/crypto/chacha20poly1305/chacha20poly1305.odin b/core/crypto/chacha20poly1305/chacha20poly1305.odin
index 67d89df56..ae395f9e0 100644
--- a/core/crypto/chacha20poly1305/chacha20poly1305.odin
+++ b/core/crypto/chacha20poly1305/chacha20poly1305.odin
@@ -10,8 +10,10 @@ KEY_SIZE :: chacha20.KEY_SIZE
NONCE_SIZE :: chacha20.NONCE_SIZE
TAG_SIZE :: poly1305.TAG_SIZE
+@(private)
_P_MAX :: 64 * 0xffffffff // 64 * (2^32-1)
+@(private)
_validate_common_slice_sizes :: proc (tag, key, nonce, aad, text: []byte) {
if len(tag) != TAG_SIZE {
panic("crypto/chacha20poly1305: invalid destination tag size")
@@ -37,7 +39,10 @@ _validate_common_slice_sizes :: proc (tag, key, nonce, aad, text: []byte) {
}
}
+@(private)
_PAD: [16]byte
+
+@(private)
_update_mac_pad16 :: #force_inline proc (ctx: ^poly1305.Context, x_len: int) {
if pad_len := 16 - (x_len & (16-1)); pad_len != 16 {
poly1305.update(ctx, _PAD[:pad_len])
diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin
index 35e88c5ed..6cdcacb9c 100644
--- a/core/crypto/crypto.odin
+++ b/core/crypto/crypto.odin
@@ -26,6 +26,7 @@ compare_constant_time :: proc "contextless" (a, b: []byte) -> int {
//
// The execution time of this routine is constant regardless of the
// contents of the memory being compared.
+@(optimization_mode="none")
compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> int {
x := mem.slice_ptr(a, n)
y := mem.slice_ptr(b, n)
diff --git a/core/crypto/gost/gost.odin b/core/crypto/gost/gost.odin
index 1d0274fae..5aca8ce95 100644
--- a/core/crypto/gost/gost.odin
+++ b/core/crypto/gost/gost.odin
@@ -65,7 +65,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/groestl/groestl.odin b/core/crypto/groestl/groestl.odin
index 8e5a2440d..61460808f 100644
--- a/core/crypto/groestl/groestl.odin
+++ b/core/crypto/groestl/groestl.odin
@@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/haval/haval.odin b/core/crypto/haval/haval.odin
index 811ecf95d..b98facb33 100644
--- a/core/crypto/haval/haval.odin
+++ b/core/crypto/haval/haval.odin
@@ -79,7 +79,7 @@ hash_stream_128_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -164,7 +164,7 @@ hash_stream_128_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -249,7 +249,7 @@ hash_stream_128_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -334,7 +334,7 @@ hash_stream_160_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -419,7 +419,7 @@ hash_stream_160_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -504,7 +504,7 @@ hash_stream_160_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -589,7 +589,7 @@ hash_stream_192_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -674,7 +674,7 @@ hash_stream_192_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -759,7 +759,7 @@ hash_stream_192_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -844,7 +844,7 @@ hash_stream_224_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -929,7 +929,7 @@ hash_stream_224_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -1014,7 +1014,7 @@ hash_stream_224_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -1099,7 +1099,7 @@ hash_stream_256_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -1184,7 +1184,7 @@ hash_stream_256_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
@@ -1270,7 +1270,7 @@ hash_stream_256_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
ctx.str_len = u32(len(buf[:read]))
if read > 0 {
update(&ctx, buf[:read])
diff --git a/core/crypto/jh/jh.odin b/core/crypto/jh/jh.odin
index 42c2d1d34..5dc6c4e6b 100644
--- a/core/crypto/jh/jh.odin
+++ b/core/crypto/jh/jh.odin
@@ -70,7 +70,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -149,7 +149,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -228,7 +228,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -307,7 +307,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/keccak/keccak.odin b/core/crypto/keccak/keccak.odin
index aeb5aac52..4c74858d2 100644
--- a/core/crypto/keccak/keccak.odin
+++ b/core/crypto/keccak/keccak.odin
@@ -77,7 +77,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -159,7 +159,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -241,7 +241,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -323,7 +323,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
diff --git a/core/crypto/md2/md2.odin b/core/crypto/md2/md2.odin
index 711e6e9f6..4942183e1 100644
--- a/core/crypto/md2/md2.odin
+++ b/core/crypto/md2/md2.odin
@@ -64,7 +64,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/md4/md4.odin b/core/crypto/md4/md4.odin
index b2651225b..8efdbb5a5 100644
--- a/core/crypto/md4/md4.odin
+++ b/core/crypto/md4/md4.odin
@@ -68,7 +68,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/md5/md5.odin b/core/crypto/md5/md5.odin
index 30a556102..858480b04 100644
--- a/core/crypto/md5/md5.odin
+++ b/core/crypto/md5/md5.odin
@@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/poly1305/poly1305.odin b/core/crypto/poly1305/poly1305.odin
index 8986be879..ab320c80c 100644
--- a/core/crypto/poly1305/poly1305.odin
+++ b/core/crypto/poly1305/poly1305.odin
@@ -8,6 +8,7 @@ import "core:mem"
KEY_SIZE :: 32
TAG_SIZE :: 16
+@(private)
_BLOCK_SIZE :: 16
sum :: proc (dst, msg, key: []byte) {
@@ -141,6 +142,7 @@ reset :: proc (ctx: ^Context) {
ctx._is_initialized = false
}
+@(private)
_blocks :: proc (ctx: ^Context, msg: []byte, final := false) {
n: field.Tight_Field_Element = ---
final_byte := byte(!final)
diff --git a/core/crypto/rand_linux.odin b/core/crypto/rand_linux.odin
index 4d1183757..e5c194220 100644
--- a/core/crypto/rand_linux.odin
+++ b/core/crypto/rand_linux.odin
@@ -12,7 +12,7 @@ _rand_bytes :: proc (dst: []byte) {
for l > 0 {
to_read := min(l, _MAX_PER_CALL_BYTES)
- ret := unix.sys_getrandom(raw_data(dst), to_read, 0)
+ ret := unix.sys_getrandom(raw_data(dst), uint(to_read), 0)
if ret < 0 {
switch os.Errno(-ret) {
case os.EINTR:
diff --git a/core/crypto/ripemd/ripemd.odin b/core/crypto/ripemd/ripemd.odin
index 702d29037..f9edb121b 100644
--- a/core/crypto/ripemd/ripemd.odin
+++ b/core/crypto/ripemd/ripemd.odin
@@ -69,7 +69,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -145,7 +145,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -221,7 +221,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -297,7 +297,7 @@ hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/sha1/sha1.odin b/core/crypto/sha1/sha1.odin
index b0dbd7dc8..599d1791e 100644
--- a/core/crypto/sha1/sha1.odin
+++ b/core/crypto/sha1/sha1.odin
@@ -67,7 +67,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin
index 9792a4cb8..0f55c4be1 100644
--- a/core/crypto/sha2/sha2.odin
+++ b/core/crypto/sha2/sha2.odin
@@ -74,7 +74,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -153,7 +153,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -232,7 +232,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -311,7 +311,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/sha3/sha3.odin b/core/crypto/sha3/sha3.odin
index 1202f8b23..5d8ad2106 100644
--- a/core/crypto/sha3/sha3.odin
+++ b/core/crypto/sha3/sha3.odin
@@ -73,7 +73,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -152,7 +152,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -231,7 +231,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -310,7 +310,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
diff --git a/core/crypto/shake/shake.odin b/core/crypto/shake/shake.odin
index 525dcfbd3..020ba68f3 100644
--- a/core/crypto/shake/shake.odin
+++ b/core/crypto/shake/shake.odin
@@ -73,7 +73,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
@@ -155,7 +155,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_sha3.update(&ctx, buf[:read])
}
diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin
index 74c9f22e2..9e684ff08 100644
--- a/core/crypto/sm3/sm3.odin
+++ b/core/crypto/sm3/sm3.odin
@@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/streebog/streebog.odin b/core/crypto/streebog/streebog.odin
index f85977cba..42da1e695 100644
--- a/core/crypto/streebog/streebog.odin
+++ b/core/crypto/streebog/streebog.odin
@@ -70,7 +70,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
@@ -146,7 +146,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/tiger/tiger.odin b/core/crypto/tiger/tiger.odin
index cf6159fad..614926129 100644
--- a/core/crypto/tiger/tiger.odin
+++ b/core/crypto/tiger/tiger.odin
@@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
@@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
@@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
diff --git a/core/crypto/tiger2/tiger2.odin b/core/crypto/tiger2/tiger2.odin
index e8f2c4edb..ead874d56 100644
--- a/core/crypto/tiger2/tiger2.odin
+++ b/core/crypto/tiger2/tiger2.odin
@@ -71,7 +71,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
@@ -150,7 +150,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
@@ -229,7 +229,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
_tiger.update(&ctx, buf[:read])
}
diff --git a/core/crypto/util/util.odin b/core/crypto/util/util.odin
index 6273a232e..b9b80124a 100644
--- a/core/crypto/util/util.odin
+++ b/core/crypto/util/util.odin
@@ -11,6 +11,8 @@ package util
*/
import "core:mem"
+// Keep vet happy
+_ :: mem
// @note(bp): this can replace the other two
cast_slice :: #force_inline proc "contextless" ($D: typeid/[]$DE, src: $S/[]$SE) -> D {
diff --git a/core/crypto/whirlpool/whirlpool.odin b/core/crypto/whirlpool/whirlpool.odin
index 0cfef7c6b..cf0bf6490 100644
--- a/core/crypto/whirlpool/whirlpool.odin
+++ b/core/crypto/whirlpool/whirlpool.odin
@@ -66,7 +66,7 @@ hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
defer delete(buf)
read := 1
for read > 0 {
- read, _ = s->impl_read(buf)
+ read, _ = io.read(s, buf)
if read > 0 {
update(&ctx, buf[:read])
}
diff --git a/core/crypto/x25519/x25519.odin b/core/crypto/x25519/x25519.odin
index dfc8daa47..fc446d25c 100644
--- a/core/crypto/x25519/x25519.odin
+++ b/core/crypto/x25519/x25519.odin
@@ -6,8 +6,10 @@ import "core:mem"
SCALAR_SIZE :: 32
POINT_SIZE :: 32
+@(private)
_BASE_POINT: [32]byte = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+@(private)
_scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
if i < 0 {
return 0
@@ -15,6 +17,7 @@ _scalar_bit :: #force_inline proc "contextless" (s: ^[32]byte, i: int) -> u8 {
return (s[i>>3] >> uint(i&7)) & 1
}
+@(private)
_scalarmult :: proc (out, scalar, point: ^[32]byte) {
// Montgomery pseduo-multiplication taken from Monocypher.
diff --git a/core/debug/pe/section.odin b/core/debug/pe/section.odin
index 809da8bb4..926306dbb 100644
--- a/core/debug/pe/section.odin
+++ b/core/debug/pe/section.odin
@@ -1,8 +1,5 @@
package debug_pe
-import "core:runtime"
-import "core:io"
-
Section_Header32 :: struct {
name: [8]u8,
virtual_size: u32le,
diff --git a/core/dynlib/doc.odin b/core/dynlib/doc.odin
new file mode 100644
index 000000000..812fb02d5
--- /dev/null
+++ b/core/dynlib/doc.odin
@@ -0,0 +1,7 @@
+/*
+Package core:dynlib implements loading of shared libraries/DLLs and their symbols.
+
+The behaviour of dynamically loaded libraries is specific to the target platform of the program.
+For in depth detail on the underlying behaviour please refer to your target platform's documentation.
+*/
+package dynlib
diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin
index a6c857ee4..b5cb16e3c 100644
--- a/core/dynlib/lib.odin
+++ b/core/dynlib/lib.odin
@@ -1,15 +1,94 @@
package dynlib
+/*
+A handle to a dynamically loaded library.
+*/
Library :: distinct rawptr
-load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
+/*
+Loads a dynamic library from the filesystem. The paramater `global_symbols` makes the symbols in the loaded
+library available to resolve references in subsequently loaded libraries.
+
+The paramater `global_symbols` is only used for the platforms `linux`, `darwin`, `freebsd` and `openbsd`.
+On `windows` this paramater is ignored.
+
+The underlying behaviour is platform specific.
+On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlopen`.
+On `windows` refer to `LoadLibraryW`.
+
+**Implicit Allocators**
+`context.temp_allocator`
+
+Example:
+ import "core:dynlib"
+ import "core:fmt"
+
+ load_my_library :: proc() {
+ LIBRARY_PATH :: "my_library.dll"
+ library, ok := dynlib.load_library(LIBRARY_PATH)
+ if ! ok {
+ return
+ }
+ fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
+ }
+*/
+load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
return _load_library(path, global_symbols)
}
-unload_library :: proc(library: Library) -> bool {
+/*
+Unloads a dynamic library.
+
+The underlying behaviour is platform specific.
+On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlclose`.
+On `windows` refer to `FreeLibrary`.
+
+Example:
+ import "core:dynlib"
+ import "core:fmt"
+
+ load_then_unload_my_library :: proc() {
+ LIBRARY_PATH :: "my_library.dll"
+ library, ok := dynlib.load_library(LIBRARY_PATH)
+ if ! ok {
+ return
+ }
+ did_unload := dynlib.unload_library(library)
+ if ! did_unload {
+ return
+ }
+ fmt.println("The library %q was successfully unloaded", LIBRARY_PATH)
+ }
+*/
+unload_library :: proc(library: Library) -> (did_unload: bool) {
return _unload_library(library)
}
+/*
+Loads the address of a procedure/variable from a dynamic library.
+
+The underlying behaviour is platform specific.
+On `linux`, `darwin`, `freebsd` and `openbsd` refer to `dlsym`.
+On `windows` refer to `GetProcAddress`.
+
+**Implicit Allocators**
+`context.temp_allocator`
+
+Example:
+ import "core:dynlib"
+ import "core:fmt"
+
+ find_a_in_my_library :: proc() {
+ LIBRARY_PATH :: "my_library.dll"
+ library, ok := dynlib.load_library(LIBRARY_PATH)
+ if ! ok {
+ return
+ }
+
+ a, found_a := dynlib.symbol_address(library, "a")
+ if found_a do fmt.printf("The symbol %q was found at the address %v", "a", a)
+ }
+*/
symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
return _symbol_address(library, symbol)
}
diff --git a/core/dynlib/lib_js.odin b/core/dynlib/lib_js.odin
new file mode 100644
index 000000000..ace1b0939
--- /dev/null
+++ b/core/dynlib/lib_js.odin
@@ -0,0 +1,15 @@
+//+build js
+//+private
+package dynlib
+
+_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
+ return nil, false
+}
+
+_unload_library :: proc(library: Library) -> bool {
+ return false
+}
+
+_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
+ return nil, false
+}
diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin
index d48e43ca2..67880df4b 100644
--- a/core/dynlib/lib_windows.odin
+++ b/core/dynlib/lib_windows.odin
@@ -4,10 +4,12 @@ package dynlib
import win32 "core:sys/windows"
import "core:strings"
+import "core:runtime"
_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wide_path := win32.utf8_to_wstring(path, context.temp_allocator)
handle := cast(Library)win32.LoadLibraryW(wide_path)
return handle, handle != nil
@@ -19,6 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
}
_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
c_str := strings.clone_to_cstring(symbol, context.temp_allocator)
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
found = ptr != nil
diff --git a/core/encoding/csv/writer.odin b/core/encoding/csv/writer.odin
index 3a0038916..d519104f2 100644
--- a/core/encoding/csv/writer.odin
+++ b/core/encoding/csv/writer.odin
@@ -42,7 +42,7 @@ write :: proc(w: ^Writer, record: []string) -> io.Error {
}
}
case:
- if strings.contains_rune(field, w.comma) >= 0 {
+ if strings.contains_rune(field, w.comma) {
return true
}
if strings.contains_any(field, CHAR_SET) {
diff --git a/core/encoding/hex/hex.odin b/core/encoding/hex/hex.odin
new file mode 100644
index 000000000..ef0bab1d0
--- /dev/null
+++ b/core/encoding/hex/hex.odin
@@ -0,0 +1,73 @@
+package hex
+
+import "core:strings"
+
+encode :: proc(src: []byte, allocator := context.allocator) -> []byte #no_bounds_check {
+ dst := make([]byte, len(src) * 2, allocator)
+ for i, j := 0, 0; i < len(src); i += 1 {
+ v := src[i]
+ dst[j] = HEXTABLE[v>>4]
+ dst[j+1] = HEXTABLE[v&0x0f]
+ j += 2
+ }
+
+ return dst
+}
+
+
+decode :: proc(src: []byte, allocator := context.allocator) -> (dst: []byte, ok: bool) #no_bounds_check {
+ if len(src) % 2 == 1 {
+ return
+ }
+
+ dst = make([]byte, len(src) / 2, allocator)
+ for i, j := 0, 1; j < len(src); j += 2 {
+ p := src[j-1]
+ q := src[j]
+
+ a := hex_digit(p) or_return
+ b := hex_digit(q) or_return
+
+ dst[i] = (a << 4) | b
+ i += 1
+ }
+
+ return dst, true
+}
+
+// Decodes the given sequence into one byte.
+// Should be called with one byte worth of the source, eg: 0x23 -> '#'.
+decode_sequence :: proc(str: string) -> (res: byte, ok: bool) {
+ str := str
+ if strings.has_prefix(str, "0x") || strings.has_prefix(str, "0X") {
+ str = str[2:]
+ }
+
+ if len(str) != 2 {
+ return 0, false
+ }
+
+ upper := hex_digit(str[0]) or_return
+ lower := hex_digit(str[1]) or_return
+
+ return upper << 4 | lower, true
+}
+
+@(private)
+HEXTABLE := [16]byte {
+ '0', '1', '2', '3',
+ '4', '5', '6', '7',
+ '8', '9', 'a', 'b',
+ 'c', 'd', 'e', 'f',
+}
+
+@(private)
+hex_digit :: proc(char: byte) -> (u8, bool) {
+ switch char {
+ case '0' ..= '9': return char - '0', true
+ case 'a' ..= 'f': return char - 'a' + 10, true
+ case 'A' ..= 'F': return char - 'A' + 10, true
+ case: return 0, false
+ }
+}
+
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index 1dadc8ef0..d25015ac7 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -153,7 +153,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case complex128: r, i = f64(real(z)), f64(imag(z))
case: return .Unsupported_Type
}
-
+
io.write_byte(w, '[') or_return
io.write_f64(w, r) or_return
io.write_string(w, ", ") or_return
@@ -165,8 +165,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_String:
switch s in a {
- case string: io.write_quoted_string(w, s) or_return
- case cstring: io.write_quoted_string(w, string(s)) or_return
+ case string: io.write_quoted_string(w, s, '"', nil, true) or_return
+ case cstring: io.write_quoted_string(w, string(s), '"', nil, true) or_return
}
case runtime.Type_Info_Boolean:
@@ -198,7 +198,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Procedure:
return .Unsupported_Type
- case runtime.Type_Info_Tuple:
+ case runtime.Type_Info_Parameters:
return .Unsupported_Type
case runtime.Type_Info_Simd_Vector:
@@ -262,10 +262,14 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
}
map_cap := uintptr(runtime.map_cap(m^))
ks, vs, hs, _, _ := runtime.map_kvh_data_dynamic(m^, info.map_info)
+
+ i := 0
for bucket_index in 0.. (err: i
return
}
-// insert comma seperation and write indentations
+// insert comma separation and write indentations
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
switch opt.spec {
case .JSON, .JSON5:
@@ -457,7 +461,7 @@ opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int)
if opt.pretty {
io.write_byte(w, '\n') or_return
} else {
- // comma seperation necessary for non pretty output!
+ // comma separation necessary for non pretty output!
io.write_string(w, ", ") or_return
}
}
diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin
index ed36ae33b..d007e16d7 100644
--- a/core/encoding/json/parser.odin
+++ b/core/encoding/json/parser.odin
@@ -2,6 +2,7 @@ package json
import "core:mem"
import "core:unicode/utf8"
+import "core:unicode/utf16"
import "core:strconv"
Parser :: struct {
@@ -403,11 +404,19 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
}
i += 6
+ // If this is a surrogate pair, decode as such by taking the next rune too.
+ if r >= utf8.SURROGATE_MIN && r <= utf8.SURROGATE_HIGH_MAX && len(s) > i + 2 && s[i:i+2] == "\\u" {
+ r2 := get_u4_rune(s[i:])
+ if r2 >= utf8.SURROGATE_LOW_MIN && r2 <= utf8.SURROGATE_MAX {
+ i += 6
+ r = utf16.decode_surrogate_pair(r, r2)
+ }
+ }
+
buf, buf_width := utf8.encode_rune(r)
copy(b[w:], buf[:buf_width])
w += buf_width
-
case '0':
if spec != .JSON {
b[w] = '\x00'
diff --git a/core/encoding/json/tokenizer.odin b/core/encoding/json/tokenizer.odin
index 567600b90..a406a73a5 100644
--- a/core/encoding/json/tokenizer.odin
+++ b/core/encoding/json/tokenizer.odin
@@ -163,8 +163,9 @@ get_token :: proc(t: ^Tokenizer) -> (token: Token, err: Error) {
skip_alphanum :: proc(t: ^Tokenizer) {
for t.offset < len(t.data) {
- switch next_rune(t) {
+ switch t.r {
case 'A'..='Z', 'a'..='z', '0'..='9', '_':
+ next_rune(t)
continue
}
diff --git a/core/encoding/json/types.odin b/core/encoding/json/types.odin
index 468774aa9..089fd9c9b 100644
--- a/core/encoding/json/types.odin
+++ b/core/encoding/json/types.odin
@@ -87,7 +87,8 @@ Error :: enum {
-destroy_value :: proc(value: Value) {
+destroy_value :: proc(value: Value, allocator := context.allocator) {
+ context.allocator = allocator
#partial switch v in value {
case Object:
for key, elem in v {
@@ -103,5 +104,4 @@ destroy_value :: proc(value: Value) {
case String:
delete(v)
}
-}
-
+}
\ No newline at end of file
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index 11cae6160..e6c61d8fa 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -215,6 +215,12 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
}
}
+ switch dst in &v {
+ // Handle json.Value as an unknown type
+ case Value:
+ dst = parse_value(p) or_return
+ return
+ }
#partial switch token.kind {
case .Null:
@@ -346,6 +352,8 @@ unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unm
fields := reflect.struct_fields_zipped(ti.id)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
+
field_used := make([]bool, len(fields), context.temp_allocator)
use_field_idx := -1
diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin
index b77ae97b3..f5523c299 100644
--- a/core/encoding/xml/xml_reader.odin
+++ b/core/encoding/xml/xml_reader.odin
@@ -33,6 +33,7 @@ import "core:intrinsics"
import "core:mem"
import "core:os"
import "core:strings"
+import "core:runtime"
likely :: intrinsics.expect
@@ -408,7 +409,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
next := scan(t)
#partial switch next.kind {
case .Ident:
- if len(next.text) == 3 && strings.to_lower(next.text, context.temp_allocator) == "xml" {
+ if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
parse_prologue(doc) or_return
} else if len(doc.prologue) > 0 {
/*
@@ -614,6 +615,7 @@ parse_prologue :: proc(doc: ^Document) -> (err: Error) {
}
case "encoding":
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
switch strings.to_lower(attr.val, context.temp_allocator) {
case "utf-8", "utf8":
doc.encoding = .UTF_8
diff --git a/core/fmt/doc.odin b/core/fmt/doc.odin
index 668fc9bc6..991058661 100644
--- a/core/fmt/doc.odin
+++ b/core/fmt/doc.odin
@@ -68,7 +68,7 @@ A period with no following number specifies a precision of 0.
Examples:
%f default width, default precision
%8f width 8, default precision
- %.3f default width, precision 2
+ %.2f default width, precision 2
%8.3f width 8, precision 3
%8.f width 8, precision 0
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 79b575482..f1f94b1b3 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -44,6 +44,31 @@ Info :: struct {
// Custom formatter signature. It returns true if the formatting was successful and false when it could not be done
User_Formatter :: #type proc(fi: ^Info, arg: any, verb: rune) -> bool
+// Example User Formatter:
+// SomeType :: struct {
+// value: int,
+// }
+// // Custom Formatter for SomeType
+// User_Formatter :: proc(fi: ^fmt.Info, arg: any, verb: rune) -> bool {
+// m := cast(^SomeType)arg.data
+// switch verb {
+// case 'v', 'd':
+// fmt.fmt_int(fi, u64(m.value), true, 8 * size_of(SomeType), verb)
+// case:
+// return false
+// }
+// return true
+// }
+// main :: proc() {
+// // Ensure the fmt._user_formatters map is initialized
+// fmt.set_user_formatters(new(map[typeid]fmt.User_Formatter))
+// err := fmt.register_user_formatter(type_info_of(SomeType).id, User_Formatter)
+// assert(err == .None)
+// // Use the custom formatter
+// x := SomeType{42}
+// fmt.println("Custom type value: ", x)
+// }
+
Register_User_Formatter_Error :: enum {
None,
No_User_Formatter,
@@ -54,13 +79,27 @@ Register_User_Formatter_Error :: enum {
// it is prefixed with `_` rather than marked with a private attribute so that users can access it if necessary
_user_formatters: ^map[typeid]User_Formatter
-// set_user_formatters assigns m to a global value allowing the user have custom print formatting for specific
-// types
+// Sets user-defined formatters for custom print formatting of specific types
+//
+// Inputs:
+// - m: A pointer to a map of typeids to User_Formatter structs.
+//
+// NOTE: Must be called before using register_user_formatter.
+//
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
- _user_formatters = m
+ assert(_user_formatters == nil, "set_user_formatters must not be called more than once.")
+ _user_formatters = m
}
-// register_user_formatter assigns a formatter to a specific typeid. set_user_formatters must be called
-// before any use of this procedure.
+// Registers a user-defined formatter for a specific typeid
+//
+// Inputs:
+// - id: The typeid of the custom type.
+// - formatter: The User_Formatter function for the custom type.
+//
+// Returns: A Register_User_Formatter_Error value indicating the success or failure of the operation.
+//
+// WARNING: set_user_formatters must be called before using this procedure.
+//
register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error {
if _user_formatters == nil {
return .No_User_Formatter
@@ -71,75 +110,151 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
_user_formatters[id] = formatter
return .None
}
-
-
-// aprint procedure return a string that was allocated with the current context
-// They must be freed accordingly
+// Creates a formatted string
+//
+// *Allocates Using Context's Allocator*
+//
+// Inputs:
+// - args: A variadic list of arguments to be formatted.
+// - sep: An optional separator string (default is a single space).
+//
+// Returns: A formatted string.
+//
aprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.builder_init(&str)
- sbprint(buf=&str, args=args, sep=sep)
+ sbprint(&str, ..args, sep=sep)
return strings.to_string(str)
}
-// aprintln procedure return a string that was allocated with the current context
-// They must be freed accordingly
+// Creates a formatted string with a newline character at the end
+//
+// *Allocates Using Context's Allocator*
+//
+// Inputs:
+// - args: A variadic list of arguments to be formatted.
+// - sep: An optional separator string (default is a single space).
+//
+// Returns: A formatted string with a newline character at the end.
+//
aprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.builder_init(&str)
- sbprintln(buf=&str, args=args, sep=sep)
+ sbprintln(&str, ..args, sep=sep)
return strings.to_string(str)
}
-// aprintf procedure return a string that was allocated with the current context
-// They must be freed accordingly
+// Creates a formatted string using a format string and arguments
+//
+// *Allocates Using Context's Allocator*
+//
+// Inputs:
+// - fmt: A format string with placeholders for the provided arguments.
+// - args: A variadic list of arguments to be formatted.
+//
+// Returns: A formatted string. The returned string must be freed accordingly.
+//
aprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.builder_init(&str)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
}
-
-
-// tprint procedure return a string that was allocated with the current context's temporary allocator
+// Creates a formatted string
+//
+// *Allocates Using Context's Temporary Allocator*
+//
+// Inputs:
+// - args: A variadic list of arguments to be formatted.
+// - sep: An optional separator string (default is a single space).
+//
+// Returns: A formatted string.
+//
tprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
- sbprint(buf=&str, args=args, sep=sep)
+ sbprint(&str, ..args, sep=sep)
return strings.to_string(str)
}
-// tprintln procedure return a string that was allocated with the current context's temporary allocator
+// Creates a formatted string with a newline character at the end
+//
+// *Allocates Using Context's Temporary Allocator*
+//
+// Inputs:
+// - args: A variadic list of arguments to be formatted.
+// - sep: An optional separator string (default is a single space).
+//
+// Returns: A formatted string with a newline character at the end.
+//
tprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
- sbprintln(buf=&str, args=args, sep=sep)
+ sbprintln(&str, ..args, sep=sep)
return strings.to_string(str)
}
-// tprintf procedure return a string that was allocated with the current context's temporary allocator
+// Creates a formatted string using a format string and arguments
+//
+// *Allocates Using Context's Temporary Allocator*
+//
+// Inputs:
+// - fmt: A format string with placeholders for the provided arguments.
+// - args: A variadic list of arguments to be formatted.
+//
+// Returns: A formatted string.
+//
tprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
sbprintf(&str, fmt, ..args)
return strings.to_string(str)
}
-
-
-// bprint procedures return a string using a buffer from an array
+// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
+//
+// Inputs:
+// - buf: The backing buffer
+// - args: A variadic list of arguments to be formatted
+// - sep: An optional separator string (default is a single space)
+//
+// Returns: A formatted string
+//
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
sb := strings.builder_from_bytes(buf[0:len(buf)])
- return sbprint(buf=&sb, args=args, sep=sep)
+ return sbprint(&sb, ..args, sep=sep)
}
-// bprintln procedures return a string using a buffer from an array
+// Creates a formatted string using a supplied buffer as the backing array, appends newline. Writes into the buffer.
+//
+// Inputs:
+// - buf: The backing buffer
+// - args: A variadic list of arguments to be formatted
+// - sep: An optional separator string (default is a single space)
+//
+// Returns: A formatted string with a newline character at the end
+//
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
sb := strings.builder_from_bytes(buf[0:len(buf)])
- return sbprintln(buf=&sb, args=args, sep=sep)
+ return sbprintln(&sb, ..args, sep=sep)
}
-// bprintf procedures return a string using a buffer from an array
+// Creates a formatted string using a supplied buffer as the backing array. Writes into the buffer.
+//
+// Inputs:
+// - buf: The backing buffer
+// - fmt: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted string
+//
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
sb := strings.builder_from_bytes(buf[0:len(buf)])
return sbprintf(&sb, fmt, ..args)
}
-
-
-// formatted assert
+// Runtime assertion with a formatted message
+//
+// Inputs:
+// - condition: The boolean condition to be asserted
+// - fmt: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+// - loc: The location of the caller
+//
+// Returns: True if the condition is met, otherwise triggers a runtime assertion with a formatted message
+//
assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool {
if !condition {
p := context.assertion_failure_proc
@@ -151,8 +266,13 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
}
return condition
}
-
-// formatted panic
+// Runtime panic with a formatted message
+//
+// Inputs:
+// - fmt: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+// - loc: The location of the caller
+//
panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p := context.assertion_failure_proc
if p == nil {
@@ -161,8 +281,16 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
message := tprintf(fmt, ..args)
p("Panic", message, loc)
}
-
-// formatted printing for cstrings
+// Creates a formatted C string
+//
+// *Allocates Using Context's Allocator*
+//
+// Inputs:
+// - format: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted C string
+//
caprintf :: proc(format: string, args: ..any) -> cstring {
str: strings.Builder
strings.builder_init(&str)
@@ -171,8 +299,16 @@ caprintf :: proc(format: string, args: ..any) -> cstring {
s := strings.to_string(str)
return cstring(raw_data(s))
}
-
-// c string with temp allocator
+// Creates a formatted C string
+//
+// *Allocates Using Context's Temporary Allocator*
+//
+// Inputs:
+// - format: A format string with placeholders for the provided arguments
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: A formatted C string
+//
ctprintf :: proc(format: string, args: ..any) -> cstring {
str: strings.Builder
strings.builder_init(&str, context.temp_allocator)
@@ -181,27 +317,54 @@ ctprintf :: proc(format: string, args: ..any) -> cstring {
s := strings.to_string(str)
return cstring(raw_data(s))
}
-
-// sbprint formats using the default print settings and writes to buf
+// Formats using the default print settings and writes to the given strings.Builder
+//
+// Inputs:
+// - buf: A pointer to a strings.Builder to store the formatted string
+// - args: A variadic list of arguments to be formatted
+// - sep: An optional separator string (default is a single space)
+//
+// Returns: A formatted string
+//
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
- wprint(w=strings.to_writer(buf), args=args, sep=sep)
+ wprint(strings.to_writer(buf), ..args, sep=sep)
return strings.to_string(buf^)
}
-
-// sbprintln formats using the default print settings and writes to buf
+// Formats and writes to a strings.Builder buffer using the default print settings
+//
+// Inputs:
+// - buf: A pointer to a strings.Builder buffer
+// - args: A variadic list of arguments to be formatted
+// - sep: An optional separator string (default is a single space)
+//
+// Returns: The resulting formatted string
+//
sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
- wprintln(w=strings.to_writer(buf), args=args, sep=sep)
+ wprintln(strings.to_writer(buf), ..args, sep=sep)
return strings.to_string(buf^)
}
-
-// sbprintf formats according to the specififed format string and writes to buf
+// Formats and writes to a strings.Builder buffer according to the specified format string
+//
+// Inputs:
+// - buf: A pointer to a strings.Builder buffer
+// - fmt: The format string
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: The resulting formatted string
+//
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
- wprintf(w=strings.to_writer(buf), fmt=fmt, args=args)
+ wprintf(strings.to_writer(buf), fmt, ..args)
return strings.to_string(buf^)
}
-
-
-// wprint formats using the default print settings and writes to w
+// Formats and writes to an io.Writer using the default print settings
+//
+// Inputs:
+// - w: An io.Writer to write to
+// - args: A variadic list of arguments to be formatted
+// - sep: An optional separator string (default is a single space)
+//
+// Returns: The number of bytes written
+//
wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
@@ -232,8 +395,15 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
return fi.n
}
-
-// wprintln formats using the default print settings and writes to w
+// Formats and writes to an io.Writer using the default print settings with a newline character at the end
+//
+// Inputs:
+// - w: An io.Writer to write to
+// - args: A variadic list of arguments to be formatted
+// - sep: An optional separator string (default is a single space)
+//
+// Returns: The number of bytes written
+//
wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
@@ -249,8 +419,15 @@ wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
io.flush(auto_cast w)
return fi.n
}
-
-// wprintf formats according to the specififed format string and writes to w
+// Formats and writes to an io.Writer according to the specified format string
+//
+// Inputs:
+// - w: An io.Writer to write to
+// - fmt: The format string
+// - args: A variadic list of arguments to be formatted
+//
+// Returns: The number of bytes written
+//
wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi: Info
arg_index: int = 0
@@ -526,28 +703,48 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
return fi.n
}
-
-// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w
+// Writes a ^runtime.Type_Info value to an io.Writer
+//
+// Inputs:
+// - w: An io.Writer to write to
+// - info: A pointer to a runtime.Type_Info value
+//
+// Returns: The number of bytes written and an io.Error if encountered
+//
wprint_type :: proc(w: io.Writer, info: ^runtime.Type_Info) -> (int, io.Error) {
n, err := reflect.write_type(w, info)
io.flush(auto_cast w)
return n, err
}
-// wprint_typeid is a utility procedure to write a typeid value to w
+// Writes a typeid value to an io.Writer
+//
+// Inputs:
+// - w: An io.Writer to write to
+// - id: A typeid value
+//
+// Returns: The number of bytes written and an io.Error if encountered
+//
wprint_typeid :: proc(w: io.Writer, id: typeid) -> (int, io.Error) {
n, err := reflect.write_type(w, type_info_of(id))
io.flush(auto_cast w)
return n, err
}
-
-
-
-
+// Parses an integer from a given string starting at a specified offset
+//
+// Inputs:
+// - s: The string to parse the integer from
+// - offset: The position in the string to start parsing the integer
+//
+// Returns:
+// - result: The parsed integer
+// - new_offset: The position in the string after parsing the integer
+// - ok: A boolean indicating if the parsing was successful
+//
_parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok: bool) {
is_digit :: #force_inline proc(r: byte) -> bool { return '0' <= r && r <= '9' }
new_offset = offset
- for new_offset <= len(s) {
+ for new_offset < len(s) {
c := s[new_offset]
if !is_digit(c) {
break
@@ -560,7 +757,20 @@ _parse_int :: proc(s: string, offset: int) -> (result: int, new_offset: int, ok:
ok = new_offset > offset
return
}
-
+// Parses an argument number from a format string and determines if it's valid
+//
+// Inputs:
+// - fi: A pointer to an Info structure
+// - arg_index: The current argument index
+// - format: The format string to parse
+// - offset: The current position in the format string
+// - arg_count: The total number of arguments
+//
+// Returns:
+// - index: The parsed argument index
+// - new_offset: The new position in the format string
+// - ok: A boolean indicating if the parsed argument number is valid
+//
_arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count: int) -> (index, new_offset: int, ok: bool) {
parse_arg_number :: proc(format: string) -> (int, int, bool) {
if len(format) < 3 {
@@ -594,7 +804,17 @@ _arg_number :: proc(fi: ^Info, arg_index: int, format: string, offset, arg_count
fi.good_arg_index = false
return arg_index, offset+width, false
}
-
+// Retrieves an integer from a list of any type at the specified index
+//
+// Inputs:
+// - args: A list of values of any type
+// - arg_index: The index to retrieve the integer from
+//
+// Returns:
+// - int: The integer value at the specified index
+// - new_arg_index: The new argument index
+// - ok: A boolean indicating if the conversion to integer was successful
+//
int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
num := 0
new_arg_index := arg_index
@@ -609,8 +829,12 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
return num, new_arg_index, ok
}
-
-
+// Writes a bad verb error message
+//
+// Inputs:
+// - fi: A pointer to an Info structure
+// - verb: The invalid format verb
+//
fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
prev_in_bad := fi.in_bad
defer fi.in_bad = prev_in_bad
@@ -628,7 +852,13 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
}
io.write_byte(writer, ')', &fi.n)
}
-
+// Formats a boolean value according to the specified format verb
+//
+// Inputs:
+// - fi: A pointer to an Info structure
+// - b: The boolean value to format
+// - verb: The format verb
+//
fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
switch verb {
case 't', 'v':
@@ -637,8 +867,12 @@ fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
fmt_bad_verb(fi, verb)
}
}
-
-
+// Writes padding characters for formatting
+//
+// Inputs:
+// - fi: A pointer to an Info structure
+// - width: The number of padding characters to write
+//
fmt_write_padding :: proc(fi: ^Info, width: int) {
if width <= 0 {
return
@@ -653,7 +887,18 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
io.write_byte(fi.writer, pad_byte, &fi.n)
}
}
-
+// Formats an integer value with specified base, sign, bit size, and digits
+//
+// Inputs:
+// - fi: A pointer to an Info structure
+// - u: The integer value to format
+// - base: The base for integer formatting
+// - is_signed: A boolean indicating if the integer is signed
+// - bit_size: The bit size of the integer
+// - digits: A string containing the digits for formatting
+//
+// WARNING: May panic if the width and precision are too big, causing a buffer overrun
+//
_fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, digits: string) {
_, neg := strconv.is_integer_negative(u, is_signed, bit_size)
@@ -678,7 +923,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
}
} else if fi.zero && fi.width_set {
prec = fi.width
- if neg || fi.plus || fi.space {
+ if neg || fi.plus {
// There needs to be space for the "sign"
prec -= 1
}
@@ -697,7 +942,6 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
flags: strconv.Int_Flags
if fi.hash && !fi.zero { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
- if fi.space { flags |= {.Space} }
s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 {
@@ -719,7 +963,18 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
fi.zero = false
_pad(fi, s)
}
-
+// Formats an int128 value based on the provided formatting options.
+//
+// Inputs:
+// - fi: A pointer to the Info struct containing formatting options.
+// - u: The int128 value to be formatted.
+// - base: The base to be used for formatting the integer (e.g. 2, 8, 10, 12, 16).
+// - is_signed: Whether the value should be treated as signed or unsigned.
+// - bit_size: The number of bits of the value (e.g. 64, 128).
+// - digits: A string containing the digit characters to use for the formatted integer.
+//
+// WARNING: Panics if the formatting options result in a buffer overrun.
+//
_fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: int, digits: string) {
_, neg := strconv.is_integer_negative_128(u, is_signed, bit_size)
@@ -744,7 +999,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
}
} else if fi.zero && fi.width_set {
prec = fi.width
- if neg || fi.plus || fi.space {
+ if neg || fi.plus {
// There needs to be space for the "sign"
prec -= 1
}
@@ -763,7 +1018,6 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
flags: strconv.Int_Flags
if fi.hash && !fi.zero { flags |= {.Prefix} }
if fi.plus { flags |= {.Plus} }
- if fi.space { flags |= {.Space} }
s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags)
if fi.hash && fi.zero && fi.indent == 0 {
@@ -785,10 +1039,16 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
fi.zero = false
_pad(fi, s)
}
-
+// Hex Values:
__DIGITS_LOWER := "0123456789abcdefx"
__DIGITS_UPPER := "0123456789ABCDEFX"
-
+// Formats a rune value according to the specified formatting verb.
+//
+// Inputs:
+// - fi: A pointer to the Info struct containing formatting options.
+// - r: The rune value to be formatted.
+// - verb: The formatting verb to use (e.g. 'c', 'r', 'v', 'q').
+//
fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
switch verb {
case 'c', 'r', 'v':
@@ -799,7 +1059,15 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
fmt_int(fi, u64(r), false, 32, verb)
}
}
-
+// Formats an integer value according to the specified formatting verb.
+//
+// Inputs:
+// - fi: A pointer to the Info struct containing formatting options.
+// - u: The integer value to be formatted.
+// - is_signed: Whether the value should be treated as signed or unsigned.
+// - bit_size: The number of bits of the value (e.g. 32, 64).
+// - verb: The formatting verb to use (e.g. 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X', 'c', 'r', 'U').
+//
fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
switch verb {
case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
@@ -824,7 +1092,15 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
fmt_bad_verb(fi, verb)
}
}
-
+// Formats an int128 value according to the specified formatting verb.
+//
+// Inputs:
+// - fi: A pointer to the Info struct containing formatting options.
+// - u: The int128 value to be formatted.
+// - is_signed: Whether the value should be treated as signed or unsigned.
+// - bit_size: The number of bits of the value (e.g. 64, 128).
+// - verb: The formatting verb to use (e.g. 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X', 'c', 'r', 'U').
+//
fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: rune) {
switch verb {
case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER)
@@ -849,7 +1125,12 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
fmt_bad_verb(fi, verb)
}
}
-
+// Pads a formatted string with the appropriate padding, based on the provided formatting options.
+//
+// Inputs:
+// - fi: A pointer to the Info struct containing formatting options.
+// - s: The string to be padded.
+//
_pad :: proc(fi: ^Info, s: string) {
if !fi.width_set {
io.write_string(fi.writer, s, &fi.n)
@@ -861,85 +1142,58 @@ _pad :: proc(fi: ^Info, s: string) {
if fi.minus { // right pad
io.write_string(fi.writer, s, &fi.n)
fmt_write_padding(fi, width)
+ } else if !fi.space && s != "" && s[0] == '-' {
+ // left pad accounting for zero pad of negative number
+ io.write_byte(fi.writer, '-', &fi.n)
+ fmt_write_padding(fi, width)
+ io.write_string(fi.writer, s[1:], &fi.n)
} else { // left pad
fmt_write_padding(fi, width)
io.write_string(fi.writer, s, &fi.n)
}
}
+// Formats a floating-point number with a specific format and precision.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - v: The floating-point number to format.
+// - bit_size: The size of the floating-point number in bits (16, 32, or 64).
+// - verb: The format specifier character.
+// - float_fmt: The byte format used for formatting the float (either 'f' or 'e').
+//
+// NOTE: Can return "NaN", "+Inf", "-Inf", "+", or "-".
+//
+_fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: byte) {
+ prec := fi.prec if fi.prec_set else 3
+ buf: [386]byte
+ // Can return "NaN", "+Inf", "-Inf", "+", "-".
+ str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size)
+
+ if !fi.plus {
+ // Strip sign from "+" but not "+Inf".
+ if str[0] == '+' && str[1] != 'I' {
+ str = str[1:]
+ }
+ }
+
+ _pad(fi, str)
+}
+// Formats a floating-point number with a specific format.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - v: The floating-point number to format.
+// - bit_size: The size of the floating-point number in bits (16, 32, or 64).
+// - verb: The format specifier character.
+//
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
case 'f', 'F', 'g', 'G', 'v':
- prec: int = 3
- if fi.prec_set {
- prec = fi.prec
- }
- buf: [386]byte
-
- str := strconv.append_float(buf[1:], v, 'f', prec, bit_size)
- b := buf[:len(str)+1]
- if b[1] == '+' || b[1] == '-' {
- b = b[1:]
- } else {
- b[0] = '+'
- }
-
- if fi.space && !fi.plus && b[0] == '+' {
- b[0] = ' '
- }
-
- if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- io.write_string(fi.writer, string(b), &fi.n)
- return
- }
-
- if fi.plus || b[0] != '+' {
- if fi.zero && fi.width_set && fi.width > len(b) {
- io.write_byte(fi.writer, b[0], &fi.n)
- fmt_write_padding(fi, fi.width - len(b))
- io.write_string(fi.writer, string(b[1:]), &fi.n)
- } else {
- _pad(fi, string(b))
- }
- } else {
- _pad(fi, string(b[1:]))
- }
-
+ _fmt_float_as(fi, v, bit_size, verb, 'f')
case 'e', 'E':
- prec: int = 3
- if fi.prec_set {
- prec = fi.prec
- }
- buf: [386]byte
-
- str := strconv.append_float(buf[1:], v, 'e', prec, bit_size)
- b := buf[:len(str)+1]
- if b[1] == '+' || b[1] == '-' {
- b = b[1:]
- } else {
- b[0] = '+'
- }
-
- if fi.space && !fi.plus && b[0] == '+' {
- b[0] = ' '
- }
-
- if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- io.write_string(fi.writer, string(b), &fi.n)
- return
- }
-
- if fi.plus || str[0] != '+' {
- if fi.zero && fi.width_set && fi.width > len(b) {
- io.write_byte(fi.writer, b[0], &fi.n)
- fmt_write_padding(fi, fi.width - len(b))
- io.write_string(fi.writer, string(b[1:]), &fi.n)
- } else {
- _pad(fi, string(b))
- }
- } else {
- _pad(fi, string(b[1:]))
- }
+ // BUG(): "%.3e" returns "3.000e+00"
+ _fmt_float_as(fi, v, bit_size, verb, 'e')
case 'h', 'H':
prev_fi := fi^
@@ -965,8 +1219,13 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
fmt_bad_verb(fi, verb)
}
}
-
-
+// Formats a string with a specific format.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - s: The string to format.
+// - verb: The format specifier character (e.g. 's', 'v', 'q', 'x', 'X').
+//
fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
s, verb := s, verb
if ol, ok := fi.optional_len.?; ok {
@@ -1024,10 +1283,23 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
fmt_bad_verb(fi, verb)
}
}
+// Formats a C-style string with a specific format.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - s: The C-style string to format.
+// - verb: The format specifier character (Ref fmt_string).
+//
fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) {
fmt_string(fi, string(s), verb)
}
-
+// Formats a raw pointer with a specific format.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - p: The raw pointer to format.
+// - verb: The format specifier character (e.g. 'p', 'v', 'b', 'o', 'i', 'd', 'z', 'x', 'X').
+//
fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
u := u64(uintptr(p))
switch verb {
@@ -1048,7 +1320,13 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
fmt_bad_verb(fi, verb)
}
}
-
+// Formats a Structure of Arrays (SoA) pointer with a specific format.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - p: The SoA pointer to format.
+// - verb: The format specifier character.
+//
fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
io.write_string(fi.writer, "#soa{data=0x", &fi.n)
_fmt_int(fi, u64(uintptr(p.data)), 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
@@ -1056,8 +1334,13 @@ fmt_soa_pointer :: proc(fi: ^Info, p: runtime.Raw_Soa_Pointer, verb: rune) {
_fmt_int(fi, u64(p.index), 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
io.write_string(fi.writer, "}", &fi.n)
}
-
-
+// String representation of an enum value.
+//
+// Inputs:
+// - val: The enum value.
+//
+// Returns: The string representation of the enum value and a boolean indicating success.
+//
enum_value_to_string :: proc(val: any) -> (string, bool) {
v := val
v.id = runtime.typeid_base(v.id)
@@ -1087,7 +1370,14 @@ enum_value_to_string :: proc(val: any) -> (string, bool) {
return "", false
}
-
+// Returns the enum value of a string representation.
+//
+// $T: The typeid of the enum type.
+// Inputs:
+// - s: The string representation of the enum value.
+//
+// Returns: The enum value and a boolean indicating success.
+//
string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
ti := runtime.type_info_base(type_info_of(T))
if e, ok := ti.variant.(runtime.Type_Info_Enum); ok {
@@ -1101,7 +1391,13 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
}
return T{}, false
}
-
+// Formats an enum value with a specific format.
+//
+// Inputs:
+// - fi: Pointer to the Info struct containing format settings.
+// - v: The enum value to format.
+// - verb: The format specifier character (e.g. 'i','d','f','s','v','q').
+//
fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
if v.id == nil || v.data == nil {
io.write_string(fi.writer, "", &fi.n)
@@ -1127,8 +1423,15 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
}
}
}
-
-
+// Converts a stored enum value to a string representation
+//
+// Inputs:
+// - enum_type: A pointer to the runtime.Type_Info of the enumeration.
+// - ev: The runtime.Type_Info_Enum_Value of the stored enum value.
+// - offset: An optional integer to adjust the enumeration value (default is 0).
+//
+// Returns: A tuple containing the string representation of the enum value and a bool indicating success.
+//
stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) {
et := runtime.type_info_base(enum_type)
ev := ev
@@ -1156,7 +1459,13 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T
return "", false
}
-
+// Formats a bit set and writes it to the provided Info structure
+//
+// Inputs:
+// - fi: A pointer to the Info structure where the formatted bit set will be written.
+// - v: The bit set value to be formatted.
+// - name: An optional string for the name of the bit set (default is an empty string).
+//
fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
if ti == nil {
@@ -1250,13 +1559,26 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
}
}
}
-
+// Writes the specified number of indents to the provided Info structure
+//
+// Inputs:
+// - fi: A pointer to the Info structure where the indents will be written.
+//
fmt_write_indent :: proc(fi: ^Info) {
for in 0.. (do_continue: bool) {
handle_optional_len :: proc(data: rawptr, info: reflect.Type_Info_Struct, field_name: string, optional_len: ^int) {
@@ -1345,7 +1677,15 @@ handle_tag :: proc(data: rawptr, info: reflect.Type_Info_Struct, idx: int, verb:
}
return false
}
-
+// Formats a struct for output, handling various struct types (e.g., SOA, raw unions)
+//
+// Inputs:
+// - fi: A mutable pointer to an Info struct containing formatting state
+// - v: The value to be formatted
+// - the_verb: The formatting verb to be used (e.g. 'v')
+// - info: Type information about the struct
+// - type_name: The name of the type being formatted
+//
fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_Struct, type_name: string) {
if the_verb != 'v' {
fmt_bad_verb(fi, the_verb)
@@ -1499,7 +1839,15 @@ fmt_struct :: proc(fi: ^Info, v: any, the_verb: rune, info: runtime.Type_Info_St
}
}
}
-
+// Searches for the first NUL-terminated element in a given buffer
+//
+// Inputs:
+// - ptr: The raw pointer to the buffer.
+// - elem_size: The size of each element in the buffer.
+// - max_n: The maximum number of elements to search (use -1 for no limit).
+//
+// Returns: The number of elements before the first NUL-terminated element.
+//
@(private)
search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: int) {
for p := uintptr(ptr); max_n < 0 || n < max_n; p += uintptr(elem_size) {
@@ -1510,7 +1858,16 @@ search_nul_termination :: proc(ptr: rawptr, elem_size: int, max_n: int) -> (n: i
}
return n
}
-
+// Formats a NUL-terminated array into a string representation
+//
+// Inputs:
+// - fi: Pointer to the formatting Info struct.
+// - data: The raw pointer to the array data.
+// - max_n: The maximum number of elements to process.
+// - elem_size: The size of each element in the array.
+// - elem: Pointer to the type information of the array element.
+// - verb: The formatting verb.
+//
fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) {
if data == nil {
io.write_string(fi.writer, "", &fi.n)
@@ -1519,7 +1876,16 @@ fmt_array_nul_terminated :: proc(fi: ^Info, data: rawptr, max_n: int, elem_size:
n := search_nul_termination(data, elem_size, max_n)
fmt_array(fi, data, n, elem_size, elem, verb)
}
-
+// Formats an array into a string representation
+//
+// Inputs:
+// - fi: Pointer to the formatting Info struct.
+// - data: The raw pointer to the array data.
+// - n: The number of elements in the array.
+// - elem_size: The size of each element in the array.
+// - elem: Pointer to the type information of the array element.
+// - verb: The formatting verb (e.g. 's','q','p').
+//
fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflect.Type_Info, verb: rune) {
if data == nil && n > 0 {
io.write_string(fi.writer, "nil")
@@ -1574,7 +1940,16 @@ fmt_array :: proc(fi: ^Info, data: rawptr, n: int, elem_size: int, elem: ^reflec
fmt_write_array(fi, data, n, elem_size, elem.id, verb)
}
}
-
+// Formats a named type into a string representation
+//
+// Inputs:
+// - fi: Pointer to the formatting Info struct.
+// - v: The value to format.
+// - verb: The formatting verb.
+// - info: The named type information.
+//
+// NOTE: This procedure supports built-in custom formatters for core library types such as runtime.Source_Code_Location, time.Duration, and time.Time.
+//
fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named) {
write_padded_number :: proc(fi: ^Info, i: i64, width: int) {
n := width-1
@@ -1591,11 +1966,22 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
switch a in v {
case runtime.Source_Code_Location:
io.write_string(fi.writer, a.file_path, &fi.n)
- io.write_byte(fi.writer, '(', &fi.n)
- io.write_int(fi.writer, int(a.line), 10, &fi.n)
- io.write_byte(fi.writer, ':', &fi.n)
- io.write_int(fi.writer, int(a.column), 10, &fi.n)
- io.write_byte(fi.writer, ')', &fi.n)
+
+ when ODIN_ERROR_POS_STYLE == .Default {
+ io.write_byte(fi.writer, '(', &fi.n)
+ io.write_int(fi.writer, int(a.line), 10, &fi.n)
+ io.write_byte(fi.writer, ':', &fi.n)
+ io.write_int(fi.writer, int(a.column), 10, &fi.n)
+ io.write_byte(fi.writer, ')', &fi.n)
+ } else when ODIN_ERROR_POS_STYLE == .Unix {
+ io.write_byte(fi.writer, ':', &fi.n)
+ io.write_int(fi.writer, int(a.line), 10, &fi.n)
+ io.write_byte(fi.writer, ':', &fi.n)
+ io.write_int(fi.writer, int(a.column), 10, &fi.n)
+ io.write_byte(fi.writer, ':', &fi.n)
+ } else {
+ #panic("Unhandled ODIN_ERROR_POS_STYLE")
+ }
return
case time.Duration:
@@ -1723,7 +2109,15 @@ fmt_named :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Named)
fmt_value(fi, any{v.data, info.base.id}, verb)
}
}
-
+// Formats a union type into a string representation
+//
+// Inputs:
+// - fi: Pointer to the formatting Info struct.
+// - v: The value to format.
+// - verb: The formatting verb.
+// - info: The union type information.
+// - type_size: The size of the union type.
+//
fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union, type_size: int) {
if type_size == 0 {
io.write_string(fi.writer, "nil", &fi.n)
@@ -1769,7 +2163,14 @@ fmt_union :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Union,
fmt_arg(fi, any{v.data, id}, verb)
}
}
-
+// Formats a matrix as a string
+//
+// Inputs:
+// - fi: A pointer to an Info struct containing formatting information.
+// - v: The matrix value to be formatted.
+// - verb: The formatting verb rune.
+// - info: A runtime.Type_Info_Matrix struct containing matrix type information.
+//
fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix) {
io.write_string(fi.writer, "matrix[", &fi.n)
defer io.write_byte(fi.writer, ']', &fi.n)
@@ -1812,7 +2213,15 @@ fmt_matrix :: proc(fi: ^Info, v: any, verb: rune, info: runtime.Type_Info_Matrix
fmt_write_indent(fi)
}
}
-
+// Formats a value based on its type and formatting verb
+//
+// Inputs:
+// - fi: A pointer to an Info struct containing formatting information.
+// - v: The value to be formatted.
+// - verb: The formatting verb rune.
+//
+// NOTE: Uses user formatters if available and not ignored.
+//
fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if v.data == nil || v.id == nil {
io.write_string(fi.writer, "", &fi.n)
@@ -1834,8 +2243,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
type_info := type_info_of(v.id)
switch info in type_info.variant {
- case runtime.Type_Info_Any: // Ignore
- case runtime.Type_Info_Tuple: // Ignore
+ case runtime.Type_Info_Any: // Ignore
+ case runtime.Type_Info_Parameters: // Ignore
case runtime.Type_Info_Named:
fmt_named(fi, v, verb, info)
@@ -2157,7 +2566,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_matrix(fi, v, verb, info)
}
}
-
+// Formats a complex number based on the given formatting verb
+//
+// Inputs:
+// - fi: A pointer to an Info struct containing formatting information.
+// - c: The complex128 value to be formatted.
+// - bits: The number of bits in the complex number (32 or 64).
+// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H').
+//
fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
switch verb {
case 'f', 'F', 'v', 'h', 'H':
@@ -2174,7 +2590,14 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
return
}
}
-
+// Formats a quaternion number based on the given formatting verb
+//
+// Inputs:
+// - fi: A pointer to an Info struct containing formatting information.
+// - q: The quaternion256 value to be formatted.
+// - bits: The number of bits in the quaternion number (64, 128, or 256).
+// - verb: The formatting verb rune ('f', 'F', 'v', 'h', 'H').
+//
fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
switch verb {
case 'f', 'F', 'v', 'h', 'H':
@@ -2205,7 +2628,15 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
return
}
}
-
+// Formats an argument based on its type and the given formatting verb
+//
+// Inputs:
+// - fi: A pointer to an Info struct containing formatting information.
+// - arg: The value to be formatted.
+// - verb: The formatting verb rune (e.g. 'T').
+//
+// NOTE: Uses user formatters if available and not ignored.
+//
fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
if arg == nil {
io.write_string(fi.writer, "")
@@ -2232,18 +2663,10 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
}
}
-
- custom_types: switch a in arg {
- case runtime.Source_Code_Location:
- if fi.hash && verb == 'v' {
- io.write_string(fi.writer, a.file_path, &fi.n)
- io.write_byte(fi.writer, '(', &fi.n)
- io.write_i64(fi.writer, i64(a.line), 10, &fi.n)
- io.write_byte(fi.writer, ':', &fi.n)
- io.write_i64(fi.writer, i64(a.column), 10, &fi.n)
- io.write_byte(fi.writer, ')', &fi.n)
- return
- }
+ arg_info := type_info_of(arg.id)
+ if info, ok := arg_info.variant.(runtime.Type_Info_Named); ok {
+ fmt_named(fi, arg, verb, info)
+ return
}
base_arg := arg
@@ -2322,7 +2745,3 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
}
}
-
-
-
-
diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin
index 7f6008889..5e06041f5 100644
--- a/core/fmt/fmt_js.odin
+++ b/core/fmt/fmt_js.odin
@@ -7,7 +7,7 @@ foreign import "odin_env"
@(private="file")
foreign odin_env {
- write :: proc "c" (fd: u32, p: []byte) ---
+ write :: proc "contextless" (fd: u32, p: []byte) ---
}
@(private="file")
diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin
index 52280a3f7..840fd1545 100644
--- a/core/fmt/fmt_os.odin
+++ b/core/fmt/fmt_os.odin
@@ -4,42 +4,72 @@ package fmt
import "core:runtime"
import "core:os"
import "core:io"
+import "core:bufio"
// fprint formats using the default print settings and writes to fd
fprint :: proc(fd: os.Handle, args: ..any, sep := " ") -> int {
- w := io.to_writer(os.stream_from_handle(fd))
- return wprint(w=w, args=args, sep=sep)
+ buf: [1024]byte
+ b: bufio.Writer
+ defer bufio.writer_flush(&b)
+
+ bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
+ w := bufio.writer_to_writer(&b)
+ return wprint(w, ..args, sep=sep)
}
// fprintln formats using the default print settings and writes to fd
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)
+ buf: [1024]byte
+ b: bufio.Writer
+ defer bufio.writer_flush(&b)
+
+ bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
+
+ w := bufio.writer_to_writer(&b)
+ return wprintln(w, ..args, sep=sep)
}
// 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))
+ buf: [1024]byte
+ b: bufio.Writer
+ defer bufio.writer_flush(&b)
+
+ bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
+
+ w := bufio.writer_to_writer(&b)
return wprintf(w, fmt, ..args)
}
fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) -> (n: int, err: io.Error) {
- w := io.to_writer(os.stream_from_handle(fd))
+ buf: [1024]byte
+ b: bufio.Writer
+ defer bufio.writer_flush(&b)
+
+ bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
+
+ w := bufio.writer_to_writer(&b)
return wprint_type(w, info)
}
fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
- w := io.to_writer(os.stream_from_handle(fd))
+ buf: [1024]byte
+ b: bufio.Writer
+ defer bufio.writer_flush(&b)
+
+ bufio.writer_init_with_buf(&b, os.stream_from_handle(fd), buf[:])
+
+ w := bufio.writer_to_writer(&b)
return wprint_typeid(w, id)
}
// print formats using the default print settings and writes to os.stdout
-print :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stdout, args=args, sep=sep) }
+print :: proc(args: ..any, sep := " ") -> int { return fprint(os.stdout, ..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) }
+println :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stdout, ..args, sep=sep) }
// 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) }
+eprint :: proc(args: ..any, sep := " ") -> int { return fprint(os.stderr, ..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) }
+eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(os.stderr, ..args, sep=sep) }
// 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) }
diff --git a/core/hash/xxhash/xxhash_3.odin b/core/hash/xxhash/xxhash_3.odin
index 5bd5537b1..fa50075f9 100644
--- a/core/hash/xxhash/xxhash_3.odin
+++ b/core/hash/xxhash/xxhash_3.odin
@@ -118,7 +118,7 @@ XXH_mul_64_to_128_fold_64 :: #force_inline proc(lhs, rhs: xxh_u64) -> (res: xxh_
}
@(optimization_mode="speed")
-XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, auto_cast shift: uint) -> (res: xxh_u64) {
+XXH_xorshift_64 :: #force_inline proc(v: xxh_u64, #any_int shift: uint) -> (res: xxh_u64) {
return v ~ (v >> shift)
}
diff --git a/core/image/common.odin b/core/image/common.odin
index 58ff83dd1..ad01f7e6b 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -634,7 +634,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
buf := bytes.Buffer{}
// Can we allocate the return buffer?
- if !resize(&buf.buf, bytes_wanted) {
+ if resize(&buf.buf, bytes_wanted) != nil {
delete(buf.buf)
return false
}
@@ -826,7 +826,7 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
buf := bytes.Buffer{}
// Can we allocate the return buffer?
- if !resize(&buf.buf, bytes_wanted) {
+ if resize(&buf.buf, bytes_wanted) != nil {
delete(buf.buf)
return false
}
@@ -1075,7 +1075,7 @@ apply_palette_rgb :: proc(img: ^Image, palette: [256]RGB_Pixel, allocator := con
// Can we allocate the return buffer?
buf := bytes.Buffer{}
bytes_wanted := compute_buffer_size(img.width, img.height, 3, 8)
- if !resize(&buf.buf, bytes_wanted) {
+ if resize(&buf.buf, bytes_wanted) != nil {
delete(buf.buf)
return false
}
@@ -1112,7 +1112,7 @@ apply_palette_rgba :: proc(img: ^Image, palette: [256]RGBA_Pixel, allocator := c
// Can we allocate the return buffer?
buf := bytes.Buffer{}
bytes_wanted := compute_buffer_size(img.width, img.height, 4, 8)
- if !resize(&buf.buf, bytes_wanted) {
+ if resize(&buf.buf, bytes_wanted) != nil {
delete(buf.buf)
return false
}
@@ -1147,7 +1147,7 @@ expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bo
// Can we allocate the return buffer?
buf := bytes.Buffer{}
bytes_wanted := compute_buffer_size(img.width, img.height, img.channels + 2, img.depth)
- if !resize(&buf.buf, bytes_wanted) {
+ if resize(&buf.buf, bytes_wanted) != nil {
delete(buf.buf)
return false
}
diff --git a/core/image/which.odin b/core/image/general.odin
similarity index 75%
rename from core/image/which.odin
rename to core/image/general.odin
index ab608174f..17a8f35ea 100644
--- a/core/image/which.odin
+++ b/core/image/general.odin
@@ -1,6 +1,48 @@
package image
-import "core:os"
+import "core:mem"
+import "core:bytes"
+
+Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
+Destroy_Proc :: #type proc(img: ^Image)
+
+@(private)
+_internal_loaders: [Which_File_Type]Loader_Proc
+_internal_destroyers: [Which_File_Type]Destroy_Proc
+
+register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
+ assert(loader != nil)
+ assert(destroyer != nil)
+ assert(_internal_loaders[kind] == nil)
+ _internal_loaders[kind] = loader
+
+ assert(_internal_destroyers[kind] == nil)
+ _internal_destroyers[kind] = destroyer
+}
+
+load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ loader := _internal_loaders[which(data)]
+ if loader == nil {
+ return nil, .Unsupported_Format
+ }
+ return loader(data, options, allocator)
+}
+
+
+destroy :: proc(img: ^Image, allocator := context.allocator) {
+ if img == nil {
+ return
+ }
+ context.allocator = allocator
+ destroyer := _internal_destroyers[img.which]
+ if destroyer != nil {
+ destroyer(img)
+ } else {
+ assert(img.metadata == nil)
+ bytes.buffer_destroy(&img.pixels)
+ free(img)
+ }
+}
Which_File_Type :: enum {
Unknown,
@@ -28,11 +70,6 @@ Which_File_Type :: enum {
XBM, // X BitMap
}
-which :: proc{
- which_bytes,
- which_file,
-}
-
which_bytes :: proc(data: []byte) -> Which_File_Type {
test_tga :: proc(s: string) -> bool {
get8 :: #force_inline proc(s: ^string) -> u8 {
@@ -164,16 +201,3 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
}
return .Unknown
}
-
-
-which_file :: proc(path: string) -> Which_File_Type {
- f, err := os.open(path)
- if err != 0 {
- return .Unknown
- }
- header: [128]byte
- os.read(f, header[:])
- file_type := which_bytes(header[:])
- os.close(f)
- return file_type
-}
\ No newline at end of file
diff --git a/core/image/general_js.odin b/core/image/general_js.odin
new file mode 100644
index 000000000..841d9c200
--- /dev/null
+++ b/core/image/general_js.odin
@@ -0,0 +1,10 @@
+//+build js
+package image
+
+load :: proc{
+ load_from_bytes,
+}
+
+which :: proc{
+ which_bytes,
+}
diff --git a/core/image/general_loader.odin b/core/image/general_loader.odin
deleted file mode 100644
index 36629c39e..000000000
--- a/core/image/general_loader.odin
+++ /dev/null
@@ -1,61 +0,0 @@
-package image
-
-import "core:mem"
-import "core:os"
-import "core:bytes"
-
-Loader_Proc :: #type proc(data: []byte, options: Options, allocator: mem.Allocator) -> (img: ^Image, err: Error)
-Destroy_Proc :: #type proc(img: ^Image)
-
-@(private)
-_internal_loaders: [Which_File_Type]Loader_Proc
-_internal_destroyers: [Which_File_Type]Destroy_Proc
-
-register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_Proc) {
- assert(loader != nil)
- assert(destroyer != nil)
- assert(_internal_loaders[kind] == nil)
- _internal_loaders[kind] = loader
-
- assert(_internal_destroyers[kind] == nil)
- _internal_destroyers[kind] = destroyer
-}
-
-load :: proc{
- load_from_bytes,
- load_from_file,
-}
-
-load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
- loader := _internal_loaders[which(data)]
- if loader == nil {
- return nil, .Unsupported_Format
- }
- return loader(data, options, allocator)
-}
-
-
-load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
- data, ok := os.read_entire_file(filename, allocator)
- defer delete(data, allocator)
- if ok {
- return load_from_bytes(data, options, allocator)
- } else {
- return nil, .Unable_To_Read_File
- }
-}
-
-destroy :: proc(img: ^Image, allocator := context.allocator) {
- if img == nil {
- return
- }
- context.allocator = allocator
- destroyer := _internal_destroyers[img.which]
- if destroyer != nil {
- destroyer(img)
- } else {
- assert(img.metadata == nil)
- bytes.buffer_destroy(&img.pixels)
- free(img)
- }
-}
\ No newline at end of file
diff --git a/core/image/general_os.odin b/core/image/general_os.odin
new file mode 100644
index 000000000..144a3470f
--- /dev/null
+++ b/core/image/general_os.odin
@@ -0,0 +1,38 @@
+//+build !js
+package image
+
+import "core:os"
+
+load :: proc{
+ load_from_bytes,
+ load_from_file,
+}
+
+
+load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ data, ok := os.read_entire_file(filename, allocator)
+ defer delete(data, allocator)
+ if ok {
+ return load_from_bytes(data, options, allocator)
+ } else {
+ return nil, .Unable_To_Read_File
+ }
+}
+
+
+which :: proc{
+ which_bytes,
+ which_file,
+}
+
+which_file :: proc(path: string) -> Which_File_Type {
+ f, err := os.open(path)
+ if err != 0 {
+ return .Unknown
+ }
+ header: [128]byte
+ os.read(f, header[:])
+ file_type := which_bytes(header[:])
+ os.close(f)
+ return file_type
+}
\ No newline at end of file
diff --git a/core/image/netpbm/netpbm.odin b/core/image/netpbm/netpbm.odin
index 18545092d..cb07b1e3a 100644
--- a/core/image/netpbm/netpbm.odin
+++ b/core/image/netpbm/netpbm.odin
@@ -4,10 +4,10 @@ import "core:bytes"
import "core:fmt"
import "core:image"
import "core:mem"
-import "core:os"
import "core:strconv"
import "core:strings"
import "core:unicode"
+import "core:runtime"
Image :: image.Image
Format :: image.Netpbm_Format
@@ -26,23 +26,6 @@ PFM :: Formats{.Pf, .PF}
ASCII :: Formats{.P1, .P2, .P3}
BINARY :: Formats{.P4, .P5, .P6} + PAM + PFM
-load :: proc {
- load_from_file,
- load_from_bytes,
-}
-
-load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
- context.allocator = allocator
-
- data, ok := os.read_entire_file(filename); defer delete(data)
- if !ok {
- err = .Unable_To_Read_File
- return
- }
-
- return load_from_bytes(data)
-}
-
load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
@@ -66,24 +49,6 @@ load_from_bytes :: proc(data: []byte, allocator := context.allocator) -> (img: ^
return img, nil
}
-save :: proc {
- save_to_file,
- save_to_buffer,
-}
-
-save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- data: []byte; defer delete(data)
- data = save_to_buffer(img, custom_info) or_return
-
- if ok := os.write_entire_file(filename, data); !ok {
- return .Unable_To_Write_File
- }
-
- return Format_Error.None
-}
-
save_to_buffer :: proc(img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (buffer: []byte, err: Error) {
context.allocator = allocator
@@ -407,6 +372,8 @@ _parse_header_pam :: proc(data: []byte, allocator := context.allocator) -> (head
}
length = header_end_index + len(HEADER_END)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+
// string buffer for the tupltype
tupltype: strings.Builder
strings.builder_init(&tupltype, context.temp_allocator); defer strings.builder_destroy(&tupltype)
diff --git a/core/image/netpbm/netpbm_js.odin b/core/image/netpbm/netpbm_js.odin
new file mode 100644
index 000000000..7db17a05d
--- /dev/null
+++ b/core/image/netpbm/netpbm_js.odin
@@ -0,0 +1,10 @@
+//+build js
+package netpbm
+
+load :: proc {
+ load_from_bytes,
+}
+
+save :: proc {
+ save_to_buffer,
+}
diff --git a/core/image/netpbm/netpbm_os.odin b/core/image/netpbm/netpbm_os.odin
new file mode 100644
index 000000000..609f1ea1f
--- /dev/null
+++ b/core/image/netpbm/netpbm_os.odin
@@ -0,0 +1,41 @@
+//+build !js
+package netpbm
+
+import "core:os"
+
+load :: proc {
+ load_from_file,
+ load_from_bytes,
+}
+
+
+load_from_file :: proc(filename: string, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ context.allocator = allocator
+
+ data, ok := os.read_entire_file(filename); defer delete(data)
+ if !ok {
+ err = .Unable_To_Read_File
+ return
+ }
+
+ return load_from_bytes(data)
+}
+
+
+save :: proc {
+ save_to_file,
+ save_to_buffer,
+}
+
+save_to_file :: proc(filename: string, img: ^Image, custom_info: Info = {}, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ data: []byte; defer delete(data)
+ data = save_to_buffer(img, custom_info) or_return
+
+ if ok := os.write_entire_file(filename, data); !ok {
+ return .Unable_To_Write_File
+ }
+
+ return Format_Error.None
+}
\ No newline at end of file
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index 0ebf0b20b..025c2b866 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -16,6 +16,7 @@ import coretime "core:time"
import "core:strings"
import "core:bytes"
import "core:mem"
+import "core:runtime"
/*
Cleanup of image-specific data.
@@ -91,12 +92,14 @@ core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
}
text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
+
assert(len(c.data) == int(c.header.length))
#partial switch c.header.type {
case .tEXt:
ok = true
- fields := bytes.split(s=c.data, sep=[]u8{0}, allocator=context.temp_allocator)
+ fields := bytes.split(c.data, sep=[]u8{0}, allocator=context.temp_allocator)
if len(fields) == 2 {
res.keyword = strings.clone(string(fields[0]))
res.text = strings.clone(string(fields[1]))
@@ -107,7 +110,7 @@ text :: proc(c: image.PNG_Chunk) -> (res: Text, ok: bool) {
case .zTXt:
ok = true
- fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
+ fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
if len(fields) != 3 || len(fields[1]) != 0 {
// Compression method must be 0=Deflate, which thanks to the split above turns
// into an empty slice
@@ -194,18 +197,18 @@ text_destroy :: proc(text: Text) {
}
iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
- ok = true
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
- fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
+ fields := bytes.split_n(c.data, sep=[]u8{0}, n=3, allocator=context.temp_allocator)
if len(fields[0]) < 1 || len(fields[0]) > 79 {
// Invalid profile name
- ok = false; return
+ return
}
if len(fields[1]) != 0 {
// Compression method should be a zero, which the split turned into an empty slice.
- ok = false; return
+ return
}
// Set up ZLIB context and decompress iCCP payload
@@ -213,12 +216,12 @@ iccp :: proc(c: image.PNG_Chunk) -> (res: iCCP, ok: bool) {
zlib_error := zlib.inflate_from_byte_array(fields[2], &buf)
if zlib_error != nil {
bytes.buffer_destroy(&buf)
- ok = false; return
+ return
}
res.name = strings.clone(string(fields[0]))
res.profile = bytes.buffer_to_bytes(&buf)
-
+ ok = true
return
}
@@ -256,18 +259,18 @@ plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
if c.header.type != .sPLT {
- return {}, false
+ return
}
- ok = true
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
- fields := bytes.split_n(s=c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
+ fields := bytes.split_n(c.data, sep=[]u8{0}, n=2, allocator=context.temp_allocator)
if len(fields) != 2 {
- return {}, false
+ return
}
res.depth = fields[1][0]
if res.depth != 8 && res.depth != 16 {
- return {}, false
+ return
}
data := fields[1][1:]
@@ -275,21 +278,21 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
if res.depth == 8 {
if len(data) % 6 != 0 {
- return {}, false
+ return
}
count = len(data) / 6
if count > 256 {
- return {}, false
+ return
}
res.entries = mem.slice_data_cast([][4]u8, data)
} else { // res.depth == 16
if len(data) % 10 != 0 {
- return {}, false
+ return
}
count = len(data) / 10
if count > 256 {
- return {}, false
+ return
}
res.entries = mem.slice_data_cast([][4]u16, data)
@@ -297,7 +300,7 @@ splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
res.name = strings.clone(string(fields[0]))
res.used = u16(count)
-
+ ok = true
return
}
diff --git a/core/image/png/png.odin b/core/image/png/png.odin
index 3faa39c83..caa1e6e8a 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -17,12 +17,12 @@ import "core:compress"
import "core:compress/zlib"
import "core:image"
-import "core:os"
import "core:hash"
import "core:bytes"
import "core:io"
import "core:mem"
import "core:intrinsics"
+import "core:runtime"
// Limit chunk sizes.
// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
@@ -335,19 +335,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err
}
-load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
- context.allocator = allocator
-
- data, ok := os.read_entire_file(filename)
- defer delete(data)
-
- if ok {
- return load_from_bytes(data, options)
- } else {
- return nil, .Unable_To_Read_File
- }
-}
-
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
options := options
@@ -744,7 +731,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// We need to create a new image buffer
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
t := bytes.Buffer{}
- if !resize(&t.buf, dest_raw_size) {
+ if resize(&t.buf, dest_raw_size) != nil {
return {}, .Unable_To_Allocate_Or_Resize
}
@@ -825,7 +812,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// We need to create a new image buffer
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 16)
t := bytes.Buffer{}
- if !resize(&t.buf, dest_raw_size) {
+ if resize(&t.buf, dest_raw_size) != nil {
return {}, .Unable_To_Allocate_Or_Resize
}
@@ -1024,7 +1011,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
// We need to create a new image buffer
dest_raw_size := compute_buffer_size(int(header.width), int(header.height), out_image_channels, 8)
t := bytes.Buffer{}
- if !resize(&t.buf, dest_raw_size) {
+ if resize(&t.buf, dest_raw_size) != nil {
return {}, .Unable_To_Allocate_Or_Resize
}
@@ -1247,6 +1234,8 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
// TODO: See about doing a Duff's #unroll where practicable
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
// Apron so we don't need to special case first rows.
up := make([]u8, row_stride, context.temp_allocator)
ok = true
@@ -1299,10 +1288,9 @@ defilter_8 :: proc(params: ^Filter_Params) -> (ok: bool) {
}
// @(optimization_mode="speed")
-defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_check {
+defilter_less_than_8 :: proc(params: ^Filter_Params) -> bool #no_bounds_check {
using params
- ok = true
row_stride_in := ((channels * width * depth) + 7) >> 3
row_stride_out := channels * width
@@ -1314,6 +1302,8 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
// TODO: See about doing a Duff's #unroll where practicable
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
// Apron so we don't need to special case first rows.
up := make([]u8, row_stride_out, context.temp_allocator)
@@ -1457,18 +1447,18 @@ defilter_less_than_8 :: proc(params: ^Filter_Params) -> (ok: bool) #no_bounds_ch
}
}
- return
+ return true
}
// @(optimization_mode="speed")
-defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
-
+defilter_16 :: proc(params: ^Filter_Params) -> bool {
using params
- ok = true
stride := channels * 2
row_stride := width * stride
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
// TODO: See about doing a Duff's #unroll where practicable
// Apron so we don't need to special case first rows.
up := make([]u8, row_stride, context.temp_allocator)
@@ -1518,7 +1508,7 @@ defilter_16 :: proc(params: ^Filter_Params) -> (ok: bool) {
dest = dest[row_stride:]
}
- return
+ return true
}
defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IHDR, options: Options) -> (err: Error) {
@@ -1532,7 +1522,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
bytes_per_channel := depth == 16 ? 2 : 1
num_bytes := compute_buffer_size(width, height, channels, depth == 16 ? 16 : 8)
- if !resize(&img.pixels.buf, num_bytes) {
+ if resize(&img.pixels.buf, num_bytes) != nil {
return .Unable_To_Allocate_Or_Resize
}
@@ -1574,7 +1564,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
if x > 0 && y > 0 {
temp: bytes.Buffer
temp_len := compute_buffer_size(x, y, channels, depth == 16 ? 16 : 8)
- if !resize(&temp.buf, temp_len) {
+ if resize(&temp.buf, temp_len) != nil {
return .Unable_To_Allocate_Or_Resize
}
@@ -1637,8 +1627,6 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
return nil
}
-load :: proc{load_from_file, load_from_bytes, load_from_context}
-
@(init, private)
_register :: proc() {
diff --git a/core/image/png/png_js.odin b/core/image/png/png_js.odin
new file mode 100644
index 000000000..57c27fc64
--- /dev/null
+++ b/core/image/png/png_js.odin
@@ -0,0 +1,4 @@
+//+build js
+package png
+
+load :: proc{load_from_bytes, load_from_context}
diff --git a/core/image/png/png_os.odin b/core/image/png/png_os.odin
new file mode 100644
index 000000000..cc65e7b42
--- /dev/null
+++ b/core/image/png/png_os.odin
@@ -0,0 +1,19 @@
+//+build !js
+package png
+
+import "core:os"
+
+load :: proc{load_from_file, load_from_bytes, load_from_context}
+
+load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ context.allocator = allocator
+
+ data, ok := os.read_entire_file(filename)
+ defer delete(data)
+
+ if ok {
+ return load_from_bytes(data, options)
+ } else {
+ return nil, .Unable_To_Read_File
+ }
+}
diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin
index 29a17d4f4..c764178dc 100644
--- a/core/image/qoi/qoi.odin
+++ b/core/image/qoi/qoi.odin
@@ -15,7 +15,6 @@ package qoi
import "core:image"
import "core:compress"
import "core:bytes"
-import "core:os"
Error :: image.Error
Image :: image.Image
@@ -24,7 +23,7 @@ Options :: image.Options
RGB_Pixel :: image.RGB_Pixel
RGBA_Pixel :: image.RGBA_Pixel
-save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
+save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
if img == nil {
@@ -54,7 +53,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
// Calculate and allocate maximum size. We'll reclaim space to actually written output at the end.
max_size := pixels * (img.channels + 1) + size_of(image.QOI_Header) + size_of(u64be)
- if !resize(&output.buf, max_size) {
+ if resize(&output.buf, max_size) != nil {
return .Unable_To_Allocate_Or_Resize
}
@@ -166,20 +165,6 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
return nil
}
-save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- out := &bytes.Buffer{}
- defer bytes.buffer_destroy(out)
-
- save_to_memory(out, img, options) or_return
- write_ok := os.write_entire_file(output, out.buf[:])
-
- return nil if write_ok else .Unable_To_Write_File
-}
-
-save :: proc{save_to_memory, save_to_file}
-
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
ctx := &compress.Context_Memory_Input{
input_data = data,
@@ -189,19 +174,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err
}
-load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
- context.allocator = allocator
-
- data, ok := os.read_entire_file(filename)
- defer delete(data)
-
- if ok {
- return load_from_bytes(data, options)
- } else {
- return nil, .Unable_To_Read_File
- }
-}
-
@(optimization_mode="speed")
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
@@ -261,7 +233,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8)
- if !resize(&img.pixels.buf, bytes_needed) {
+ if resize(&img.pixels.buf, bytes_needed) != nil {
return img, .Unable_To_Allocate_Or_Resize
}
@@ -359,8 +331,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return
}
-load :: proc{load_from_file, load_from_bytes, load_from_context}
-
/*
Cleanup of image-specific data.
*/
diff --git a/core/image/qoi/qoi_js.odin b/core/image/qoi/qoi_js.odin
new file mode 100644
index 000000000..2c23cc17a
--- /dev/null
+++ b/core/image/qoi/qoi_js.odin
@@ -0,0 +1,6 @@
+//+build js
+package qoi
+
+save :: proc{save_to_buffer}
+
+load :: proc{load_from_bytes, load_from_context}
diff --git a/core/image/qoi/qoi_os.odin b/core/image/qoi/qoi_os.odin
new file mode 100644
index 000000000..efcec6c52
--- /dev/null
+++ b/core/image/qoi/qoi_os.odin
@@ -0,0 +1,37 @@
+//+build !js
+package qoi
+
+import "core:os"
+import "core:bytes"
+
+save :: proc{save_to_buffer, save_to_file}
+
+
+save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ out := &bytes.Buffer{}
+ defer bytes.buffer_destroy(out)
+
+ save_to_buffer(out, img, options) or_return
+ write_ok := os.write_entire_file(output, out.buf[:])
+
+ return nil if write_ok else .Unable_To_Write_File
+}
+
+
+load :: proc{load_from_file, load_from_bytes, load_from_context}
+
+
+load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ context.allocator = allocator
+
+ data, ok := os.read_entire_file(filename)
+ defer delete(data)
+
+ if ok {
+ return load_from_bytes(data, options)
+ } else {
+ return nil, .Unable_To_Read_File
+ }
+}
\ No newline at end of file
diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin
index 39c46c7c7..03ef1a386 100644
--- a/core/image/tga/tga.odin
+++ b/core/image/tga/tga.odin
@@ -14,7 +14,6 @@ package tga
import "core:mem"
import "core:image"
import "core:bytes"
-import "core:os"
import "core:compress"
import "core:strings"
@@ -28,7 +27,7 @@ GA_Pixel :: image.GA_Pixel
RGB_Pixel :: image.RGB_Pixel
RGBA_Pixel :: image.RGBA_Pixel
-save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
+save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
if img == nil {
@@ -58,7 +57,7 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
// Calculate and allocate necessary space.
necessary := pixels * img.channels + size_of(image.TGA_Header)
- if !resize(&output.buf, necessary) {
+ if resize(&output.buf, necessary) != nil {
return .Unable_To_Allocate_Or_Resize
}
@@ -92,20 +91,6 @@ save_to_memory :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}
return nil
}
-save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- out := &bytes.Buffer{}
- defer bytes.buffer_destroy(out)
-
- save_to_memory(out, img, options) or_return
- write_ok := os.write_entire_file(output, out.buf[:])
-
- return nil if write_ok else .Unable_To_Write_File
-}
-
-save :: proc{save_to_memory, save_to_file}
-
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
options := options
@@ -307,7 +292,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, nil
}
- if !resize(&img.pixels.buf, dest_channels * img.width * img.height) {
+ if resize(&img.pixels.buf, dest_channels * img.width * img.height) != nil {
return img, .Unable_To_Allocate_Or_Resize
}
@@ -398,20 +383,6 @@ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context
return img, err
}
-load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
- context.allocator = allocator
-
- data, ok := os.read_entire_file(filename)
- defer delete(data)
-
- if ok {
- return load_from_bytes(data, options)
- } else {
- return nil, .Unable_To_Read_File
- }
-}
-
-load :: proc{load_from_file, load_from_bytes, load_from_context}
destroy :: proc(img: ^Image) {
if img == nil || img.width == 0 || img.height == 0 {
diff --git a/core/image/tga/tga_js.odin b/core/image/tga/tga_js.odin
new file mode 100644
index 000000000..d98b241a7
--- /dev/null
+++ b/core/image/tga/tga_js.odin
@@ -0,0 +1,5 @@
+//+build js
+package tga
+
+save :: proc{save_to_buffer}
+load :: proc{load_from_bytes, load_from_context}
diff --git a/core/image/tga/tga_os.odin b/core/image/tga/tga_os.odin
new file mode 100644
index 000000000..12747a684
--- /dev/null
+++ b/core/image/tga/tga_os.odin
@@ -0,0 +1,34 @@
+//+build !js
+package tga
+
+import "core:os"
+import "core:bytes"
+
+save :: proc{save_to_buffer, save_to_file}
+
+save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ out := &bytes.Buffer{}
+ defer bytes.buffer_destroy(out)
+
+ save_to_buffer(out, img, options) or_return
+ write_ok := os.write_entire_file(output, out.buf[:])
+
+ return nil if write_ok else .Unable_To_Write_File
+}
+
+load :: proc{load_from_file, load_from_bytes, load_from_context}
+
+load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ context.allocator = allocator
+
+ data, ok := os.read_entire_file(filename)
+ defer delete(data)
+
+ if ok {
+ return load_from_bytes(data, options)
+ } else {
+ return nil, .Unable_To_Read_File
+ }
+}
\ No newline at end of file
diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index 38542d2fc..93cd0e505 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -192,6 +192,7 @@ 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) ---
+type_merge :: proc($U, $V: typeid) -> typeid where type_is_union(U), type_is_union(V) ---
constant_utf16_cstring :: proc($literal: string) -> [^]u16 ---
@@ -283,7 +284,7 @@ wasm_memory_atomic_wait32 :: proc(ptr: ^u32, expected: u32, timeout_ns: i64) -
wasm_memory_atomic_notify32 :: proc(ptr: ^u32, waiters: u32) -> (waiters_woken_up: u32) ---
// x86 Targets (i386, amd64)
-x86_cpuid :: proc(ax, cx: u32) -> (eax, ebc, ecx, edx: u32) ---
+x86_cpuid :: proc(ax, cx: u32) -> (eax, ebx, ecx, edx: u32) ---
x86_xgetbv :: proc(cx: u32) -> (eax, edx: u32) ---
@@ -305,4 +306,4 @@ valgrind_client_request :: proc(default: uintptr, request: uintptr, a0, a1, a2,
// Internal compiler use only
-__entry_point :: proc() ---
\ No newline at end of file
+__entry_point :: proc() ---
diff --git a/core/io/conv.odin b/core/io/conv.odin
index 39a72d69d..e3286baca 100644
--- a/core/io/conv.odin
+++ b/core/io/conv.odin
@@ -1,124 +1,80 @@
package io
to_reader :: proc(s: Stream) -> (r: Reader, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read == nil {
- ok = false
- }
+ r = s
+ ok = .Read in query(s)
return
}
to_writer :: proc(s: Stream) -> (w: Writer, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write == nil {
- ok = false
- }
+ w = s
+ ok = .Write in query(s)
return
}
to_closer :: proc(s: Stream) -> (c: Closer, ok: bool = true) #optional_ok {
- c.stream = s
- if s.stream_vtable == nil || s.impl_close == nil {
- ok = false
- }
+ c = s
+ ok = .Close in query(s)
return
}
to_flusher :: proc(s: Stream) -> (f: Flusher, ok: bool = true) #optional_ok {
- f.stream = s
- if s.stream_vtable == nil || s.impl_flush == nil {
- ok = false
- }
+ f = s
+ ok = .Flush in query(s)
return
}
to_seeker :: proc(s: Stream) -> (seeker: Seeker, ok: bool = true) #optional_ok {
- seeker.stream = s
- if s.stream_vtable == nil || s.impl_seek == nil {
- ok = false
- }
+ seeker = s
+ ok = .Seek in query(s)
return
}
to_read_writer :: proc(s: Stream) -> (r: Read_Writer, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil {
- ok = false
- }
+ r = s
+ ok = query(s) >= {.Read, .Write}
return
}
to_read_closer :: proc(s: Stream) -> (r: Read_Closer, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read == nil || s.impl_close == nil {
- ok = false
- }
+ r = s
+ ok = query(s) >= {.Read, .Close}
return
}
to_read_write_closer :: proc(s: Stream) -> (r: Read_Write_Closer, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_close == nil {
- ok = false
- }
+ r = s
+ ok = query(s) >= {.Read, .Write, .Close}
return
}
to_read_write_seeker :: proc(s: Stream) -> (r: Read_Write_Seeker, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read == nil || s.impl_write == nil || s.impl_seek == nil {
- ok = false
- }
+ r = s
+ ok = query(s) >= {.Read, .Write, .Seek}
return
}
to_write_flusher :: proc(s: Stream) -> (w: Write_Flusher, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil {
- ok = false
- }
+ w = s
+ ok = query(s) >= {.Write, .Flush}
return
}
to_write_flush_closer :: proc(s: Stream) -> (w: Write_Flush_Closer, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write == nil || s.impl_flush == nil || s.impl_close == nil {
- ok = false
- }
+ w = s
+ ok = query(s) >= {.Write, .Flush, .Close}
return
}
to_reader_at :: proc(s: Stream) -> (r: Reader_At, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read_at == nil {
- ok = false
- }
+ r = s
+ ok = query(s) >= {.Read_At}
return
}
to_writer_at :: proc(s: Stream) -> (w: Writer_At, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write_at == nil {
- ok = false
- }
- return
-}
-to_reader_from :: proc(s: Stream) -> (r: Reader_From, ok: bool = true) #optional_ok {
- r.stream = s
- if s.stream_vtable == nil || s.impl_read_from == nil {
- ok = false
- }
- return
-}
-to_writer_to :: proc(s: Stream) -> (w: Writer_To, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write_to == nil {
- ok = false
- }
+ w = s
+ ok = query(s) >= {.Write_At}
return
}
to_write_closer :: proc(s: Stream) -> (w: Write_Closer, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write == nil || s.impl_close == nil {
- ok = false
- }
+ w = s
+ ok = query(s) >= {.Write, .Close}
return
}
to_write_seeker :: proc(s: Stream) -> (w: Write_Seeker, ok: bool = true) #optional_ok {
- w.stream = s
- if s.stream_vtable == nil || s.impl_write == nil || s.impl_seek == nil {
- ok = false
- }
+ w = s
+ ok = query(s) >= {.Write, .Seek}
return
}
diff --git a/core/io/io.odin b/core/io/io.odin
index 7faa500b1..566e13c54 100644
--- a/core/io/io.odin
+++ b/core/io/io.odin
@@ -53,137 +53,106 @@ Error :: enum i32 {
Empty = -1,
}
-Close_Proc :: proc(using s: Stream) -> Error
-Flush_Proc :: proc(using s: Stream) -> Error
-Seek_Proc :: proc(using s: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
-Size_Proc :: proc(using s: Stream) -> i64
-Read_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error)
-Read_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error)
-Read_From_Proc :: proc(using s: Stream, r: Reader) -> (n: i64, err: Error)
-Read_Byte_Proc :: proc(using s: Stream) -> (byte, Error)
-Read_Rune_Proc :: proc(using s: Stream) -> (ch: rune, size: int, err: Error)
-Unread_Byte_Proc :: proc(using s: Stream) -> Error
-Unread_Rune_Proc :: proc(using s: Stream) -> Error
-Write_Proc :: proc(using s: Stream, p: []byte) -> (n: int, err: Error)
-Write_At_Proc :: proc(using s: Stream, p: []byte, off: i64) -> (n: int, err: Error)
-Write_To_Proc :: proc(using s: Stream, w: Writer) -> (n: i64, err: Error)
-Write_Byte_Proc :: proc(using s: Stream, c: byte) -> Error
-Write_Rune_Proc :: proc(using s: Stream, r: rune) -> (size: int, err: Error)
-Destroy_Proc :: proc(using s: Stream) -> Error
+Stream_Mode :: enum {
+ Close,
+ Flush,
+ Read,
+ Read_At,
+ Write,
+ Write_At,
+ Seek,
+ Size,
+ Destroy,
+ Query, // query what modes are available
+}
+Stream_Mode_Set :: distinct bit_set[Stream_Mode; i64]
+
+Stream_Proc :: #type proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
Stream :: struct {
- using stream_vtable: ^Stream_VTable,
- stream_data: rawptr,
-}
-Stream_VTable :: struct {
- impl_close: Close_Proc,
- impl_flush: Flush_Proc,
-
- impl_seek: Seek_Proc,
- impl_size: Size_Proc,
-
- impl_read: Read_Proc,
- impl_read_at: Read_At_Proc,
- impl_read_byte: Read_Byte_Proc,
- impl_read_rune: Read_Rune_Proc,
- impl_write_to: Write_To_Proc,
-
- impl_write: Write_Proc,
- impl_write_at: Write_At_Proc,
- impl_write_byte: Write_Byte_Proc,
- impl_write_rune: Write_Rune_Proc,
- impl_read_from: Read_From_Proc,
-
- impl_unread_byte: Unread_Byte_Proc,
- impl_unread_rune: Unread_Rune_Proc,
-
- impl_destroy: Destroy_Proc,
+ procedure: Stream_Proc,
+ data: rawptr,
}
+Reader :: Stream
+Writer :: Stream
+Closer :: Stream
+Flusher :: Stream
+Seeker :: Stream
-Reader :: struct {using stream: Stream}
-Writer :: struct {using stream: Stream}
-Closer :: struct {using stream: Stream}
-Flusher :: struct {using stream: Stream}
-Seeker :: struct {using stream: Stream}
+Read_Writer :: Stream
+Read_Closer :: Stream
+Read_Write_Closer :: Stream
+Read_Write_Seeker :: Stream
-Read_Writer :: struct {using stream: Stream}
-Read_Closer :: struct {using stream: Stream}
-Read_Write_Closer :: struct {using stream: Stream}
-Read_Write_Seeker :: struct {using stream: Stream}
+Write_Closer :: Stream
+Write_Seeker :: Stream
+Write_Flusher :: Stream
+Write_Flush_Closer :: Stream
-Write_Closer :: struct {using stream: Stream}
-Write_Seeker :: struct {using stream: Stream}
-Write_Flusher :: struct {using stream: Stream}
-Write_Flush_Closer :: struct {using stream: Stream}
-
-Reader_At :: struct {using stream: Stream}
-Writer_At :: struct {using stream: Stream}
-Reader_From :: struct {using stream: Stream}
-Writer_To :: struct {using stream: Stream}
+Reader_At :: Stream
+Writer_At :: Stream
-destroy :: proc(s: Stream) -> Error {
- close_err := close({s})
- if s.stream_vtable != nil && s.impl_destroy != nil {
- return s->impl_destroy()
+destroy :: proc(s: Stream) -> (err: Error) {
+ _ = flush(s)
+ _ = close(s)
+ if s.procedure != nil {
+ _, err = s.procedure(s.data, .Destroy, nil, 0, nil)
+ } else {
+ err = .Empty
}
- if close_err != .None {
- return close_err
- }
- return .Empty
+ return
}
+query :: proc(s: Stream) -> (set: Stream_Mode_Set) {
+ if s.procedure != nil {
+ n, _ := s.procedure(s.data, .Query, nil, 0, nil)
+ set = transmute(Stream_Mode_Set)n
+ if set != nil {
+ set += {.Query}
+ }
+ }
+ return
+}
+
+query_utility :: #force_inline proc "contextless" (set: Stream_Mode_Set) -> (n: i64, err: Error) {
+ return transmute(i64)set, nil
+}
+
+_i64_err :: #force_inline proc "contextless" (n: int, err: Error) -> (i64, Error) {
+ return i64(n), err
+}
+
+
// read reads up to len(p) bytes into s. It returns the number of bytes read and any error if occurred.
//
// When read encounters an .EOF or error after successfully reading n > 0 bytes, it returns the number of
// bytes read along with the error.
read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
- if s.stream_vtable != nil {
- if s.impl_read != nil {
- n, err = s->impl_read(p)
- if n_read != nil {
- n_read^ += n
- }
- return
- } else if s.impl_read_byte != nil {
- bytes_read := 0
- defer if n_read != nil {
- n_read^ += bytes_read
- }
- for _, i in p {
- p[i] = s->impl_read_byte() or_return
- bytes_read += 1
- }
- return
- }
+ if s.procedure != nil {
+ n64: i64
+ n64, err = s.procedure(s.data, .Read, p, 0, nil)
+ n = int(n64)
+ if n_read != nil { n_read^ += n }
+ } else {
+ err = .Empty
}
- return 0, .Empty
+ return
}
// write writes up to len(p) bytes into s. It returns the number of bytes written and any error if occurred.
write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Error) {
- if s.stream_vtable != nil {
- if s.impl_write != nil {
- n, err = s->impl_write(p)
- if n_written != nil {
- n_written^ += n
- }
- return
- } else if s.impl_write_byte != nil {
- bytes_written := 0
- defer if n_written != nil {
- n_written^ += bytes_written
- }
- for c in p {
- s->impl_write_byte(c) or_return
- bytes_written += 1
- }
- return
- }
+ if s.procedure != nil {
+ n64: i64
+ n64, err = s.procedure(s.data, .Write, p, 0, nil)
+ n = int(n64)
+ if n_written != nil { n_written^ += n }
+ } else {
+ err = .Empty
}
- return 0, .Empty
+ return
}
// seek sets the offset of the next read or write to offset.
@@ -194,57 +163,45 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro
//
// seek returns the new offset to the start of the file/stream, and any error if occurred.
seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
- if s.stream_vtable != nil && s.impl_seek != nil {
- return s->impl_seek(offset, whence)
+ if s.procedure != nil {
+ n, err = s.procedure(s.data, .Seek, nil, offset, whence)
+ } else {
+ err = .Empty
}
- return 0, .Empty
+ return
}
// The behaviour of close after the first call is stream implementation defined.
// Different streams may document their own behaviour.
-close :: proc(s: Closer) -> Error {
- if s.stream_vtable != nil && s.impl_close != nil {
- return s->impl_close()
+close :: proc(s: Closer) -> (err: Error) {
+ if s.procedure != nil {
+ _, err = s.procedure(s.data, .Close, nil, 0, nil)
}
- // Instead of .Empty, .None is fine in this case
- return .None
+ return
}
-flush :: proc(s: Flusher) -> Error {
- if s.stream_vtable != nil && s.impl_flush != nil {
- return s->impl_flush()
+flush :: proc(s: Flusher) -> (err: Error) {
+ if s.procedure != nil {
+ _, err = s.procedure(s.data, .Flush, nil, 0, nil)
}
- // Instead of .Empty, .None is fine in this case
- return .None
+ return
}
// size returns the size of the stream. If the stream does not support querying its size, 0 will be returned.
-size :: proc(s: Stream) -> i64 {
- if s.stream_vtable == nil {
- return 0
+size :: proc(s: Stream) -> (n: i64, err: Error) {
+ if s.procedure != nil {
+ n, err = s.procedure(s.data, .Size, nil, 0, nil)
+ if err == .Empty {
+ n = 0
+ curr := seek(s, 0, .Current) or_return
+ end := seek(s, 0, .End) or_return
+ seek(s, curr, .Start) or_return
+ n = end
+ }
+ } else {
+ err = .Empty
}
- if s.impl_size != nil {
- return s->impl_size()
- }
- if s.impl_seek == nil {
- return 0
- }
-
- curr, end: i64
- err: Error
- if curr, err = s->impl_seek(0, .Current); err != nil {
- return 0
- }
-
- if end, err = s->impl_seek(0, .End); err != nil {
- return 0
- }
-
- if _, err = s->impl_seek(curr, .Start); err != nil {
- return 0
- }
-
- return end
+ return
}
@@ -256,29 +213,24 @@ size :: proc(s: Stream) -> i64 {
//
// If n == len(p), err may be either nil or .EOF
read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n: int, err: Error) {
- defer if n_read != nil {
- n_read^ += n
- }
-
- if r.stream_vtable == nil {
- return 0, .Empty
- }
- if r.impl_read_at != nil {
- return r->impl_read_at(p, offset)
- }
- if r.impl_seek == nil || r.impl_read == nil {
- return 0, .Empty
- }
-
- curr_offset := r->impl_seek(offset, .Current) or_return
-
- n, err = r->impl_read(p)
- _, err1 := r->impl_seek(curr_offset, .Start)
- if err1 != nil && err == nil {
- err = err1
+ if r.procedure != nil {
+ n64: i64
+ n64, err = r.procedure(r.data, .Read_At, p, offset, nil)
+ if err != .Empty {
+ n = int(n64)
+ } else {
+ curr := seek(r, offset, .Current) or_return
+ n, err = read(r, p)
+ _, err1 := seek(r, curr, .Start)
+ if err1 != nil && err == nil {
+ err = err1
+ }
+ }
+ if n_read != nil { n_read^ += n }
+ } else {
+ err = .Empty
}
return
-
}
// write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w.
@@ -287,97 +239,39 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
// If write_at is writing to a Writer_At which has a seek offset, then write_at should not affect the underlying
// seek offset.
write_at :: proc(w: Writer_At, p: []byte, offset: i64, n_written: ^int = nil) -> (n: int, err: Error) {
- defer if n_written != nil {
- n_written^ += n
- }
-
- if w.stream_vtable == nil {
- return 0, .Empty
- }
- if w.impl_write_at != nil {
- return w->impl_write_at(p, offset)
- }
- if w.impl_seek == nil || w.impl_write == nil {
- return 0, .Empty
- }
-
- curr_offset: i64
- curr_offset, err = w->impl_seek(offset, .Current)
- if err != nil {
- return 0, err
- }
-
- n, err = w->impl_write(p)
- _, err1 := w->impl_seek(curr_offset, .Start)
- if err1 != nil && err == nil {
- err = err1
+ if w.procedure != nil {
+ n64: i64
+ n64, err = w.procedure(w.data, .Write_At, p, offset, nil)
+ if err != .Empty {
+ n = int(n64)
+ } else {
+ curr := seek(w, offset, .Current) or_return
+ n, err = write(w, p)
+ _, err1 := seek(w, curr, .Start)
+ if err1 != nil && err == nil {
+ err = err1
+ }
+ }
+ if n_written != nil { n_written^ += n }
+ } else {
+ err = .Empty
}
return
}
-write_to :: proc(r: Writer_To, w: Writer) -> (n: i64, err: Error) {
- if r.stream_vtable == nil || w.stream_vtable == nil {
- return 0, .Empty
- }
- if r.impl_write_to != nil {
- return r->impl_write_to(w)
- }
- return 0, .Empty
-}
-read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
- if r.stream_vtable == nil || w.stream_vtable == nil {
- return 0, .Empty
- }
- if r.impl_read_from != nil {
- return w->impl_read_from(r)
- }
- return 0, .Empty
-}
-
-
// read_byte reads and returns the next byte from r.
read_byte :: proc(r: Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
- defer if err == nil && n_read != nil {
- n_read^ += 1
- }
-
- if r.stream_vtable == nil {
- return 0, .Empty
- }
- if r.impl_read_byte != nil {
- return r->impl_read_byte()
- }
- if r.impl_read == nil {
- return 0, .Empty
- }
-
buf: [1]byte
- _, err = r->impl_read(buf[:])
- return buf[0], err
+ _, err = read(r, buf[:], n_read)
+ b = buf[0]
+ return
}
write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> Error {
- return _write_byte(auto_cast w, c, n_written)
-}
-
-@(private)
-_write_byte :: proc(w: Writer, c: byte, n_written: ^int = nil) -> (err: Error) {
- defer if err == nil && n_written != nil {
- n_written^ += 1
- }
- if w.stream_vtable == nil {
- return .Empty
- }
- if w.impl_write_byte != nil {
- return w->impl_write_byte(c)
- }
- if w.impl_write == nil {
- return .Empty
- }
-
- b := [1]byte{c}
- _, err = w->impl_write(b[:])
- return err
+ buf: [1]byte
+ buf[0] = c
+ write(w, buf[:], n_written) or_return
+ return nil
}
// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
@@ -385,19 +279,9 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err:
defer if err == nil && n_read != nil {
n_read^ += size
}
- if br.stream_vtable == nil {
- return 0, 0, .Empty
- }
- if br.impl_read_rune != nil {
- return br->impl_read_rune()
- }
- if br.impl_read == nil {
- return 0, 0, .Empty
- }
b: [utf8.UTF_MAX]byte
- _, err = br->impl_read(b[:1])
-
+ _, err = read(br, b[:1])
s0 := b[0]
ch = rune(s0)
@@ -415,7 +299,7 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err:
return
}
sz := int(x&7)
- size, err = br->impl_read(b[1:sz])
+ size, err = read(br, b[1:sz])
if err != nil || size+1 < sz {
ch = utf8.RUNE_ERROR
return
@@ -425,28 +309,6 @@ read_rune :: proc(br: Reader, n_read: ^int = nil) -> (ch: rune, size: int, err:
return
}
-unread_byte :: proc(s: Stream) -> Error {
- if s.stream_vtable == nil {
- return .Empty
- }
- if s.impl_unread_byte != nil {
- return s->impl_unread_byte()
- }
- if s.impl_seek != nil {
- _, err := s->impl_seek(-1, .Current)
- return err
- }
-
- return .Empty
-}
-unread_rune :: proc(s: Writer) -> Error {
- if s.stream_vtable != nil && s.impl_unread_rune != nil {
- return s->impl_unread_rune()
- }
- return .Empty
-}
-
-
// write_string writes the contents of the string s to w.
write_string :: proc(s: Writer, str: string, n_written: ^int = nil) -> (n: int, err: Error) {
return write(s, transmute([]byte)str, n_written)
@@ -457,14 +319,6 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
defer if err == nil && n_written != nil {
n_written^ += size
}
-
- if s.stream_vtable == nil {
- return 0, .Empty
- }
- if s.impl_write_rune != nil {
- return s->impl_write_rune(r)
- }
-
if r < utf8.RUNE_SELF {
err = write_byte(s, byte(r))
if err == nil {
@@ -542,21 +396,15 @@ copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
@(private)
_copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err: Error) {
- if dst.stream_vtable == nil || src.stream_vtable == nil {
+ if dst.procedure == nil || src.procedure == nil {
return 0, .Empty
}
- if src.impl_write_to != nil {
- return src->impl_write_to(dst)
- }
- if src.impl_read_from != nil {
- return dst->impl_read_from(src)
- }
buf := buf
if buf == nil {
DEFAULT_SIZE :: 4 * 1024
size := DEFAULT_SIZE
- if src.stream_vtable == _limited_reader_vtable {
- l := (^Limited_Reader)(src.stream_data)
+ if src.procedure == _limited_reader_proc {
+ l := (^Limited_Reader)(src.data)
if i64(size) > l.n {
if l.n < 1 {
size = 1
diff --git a/core/io/multi.odin b/core/io/multi.odin
index 64c533e37..e85114a7a 100644
--- a/core/io/multi.odin
+++ b/core/io/multi.odin
@@ -5,33 +5,37 @@ Multi_Reader :: struct {
}
@(private)
-_multi_reader_vtable := &Stream_VTable{
- impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
- mr := (^Multi_Reader)(s.stream_data)
- for len(mr.readers) > 0 {
- r := mr.readers[0]
- n, err = read(r, p)
- if err == .EOF {
- ordered_remove(&mr.readers, 0)
- }
- if n > 0 || err != .EOF {
- if err == .EOF && len(mr.readers) > 0 {
- // Don't return EOF yet, more readers remain
- err = nil
- }
- return
- }
+_multi_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ if mode == .Query {
+ return query_utility({.Read, .Query})
+ } else if mode != .Read {
+ return 0, .Empty
+ }
+ mr := (^Multi_Reader)(stream_data)
+ for len(mr.readers) > 0 {
+ r := mr.readers[0]
+ n, err = _i64_err(read(r, p))
+ if err == .EOF {
+ ordered_remove(&mr.readers, 0)
}
- return 0, .EOF
- },
+ if n > 0 || err != .EOF {
+ if err == .EOF && len(mr.readers) > 0 {
+ // Don't return EOF yet, more readers remain
+ err = nil
+ }
+ return
+ }
+ }
+ return 0, .EOF
}
+
multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := context.allocator) -> (r: Reader) {
all_readers := make([dynamic]Reader, 0, len(readers), allocator)
for w in readers {
- if w.stream_vtable == _multi_reader_vtable {
- other := (^Multi_Reader)(w.stream_data)
+ if w.procedure == _multi_reader_proc {
+ other := (^Multi_Reader)(w.data)
append(&all_readers, ..other.readers[:])
} else {
append(&all_readers, w)
@@ -40,8 +44,8 @@ multi_reader_init :: proc(mr: ^Multi_Reader, readers: ..Reader, allocator := con
mr.readers = all_readers
- r.stream_vtable = _multi_reader_vtable
- r.stream_data = mr
+ r.procedure = _multi_reader_proc
+ r.data = mr
return
}
@@ -55,38 +59,42 @@ Multi_Writer :: struct {
}
@(private)
-_multi_writer_vtable := &Stream_VTable{
- impl_write = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
- mw := (^Multi_Writer)(s.stream_data)
- for w in mw.writers {
- n, err = write(w, p)
- if err != nil {
- return
- }
- if n != len(p) {
- err = .Short_Write
- return
- }
+_multi_writer_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ if mode == .Query {
+ return query_utility({.Write, .Query})
+ } else if mode != .Write {
+ return 0, .Empty
+ }
+ mw := (^Multi_Writer)(stream_data)
+ for w in mw.writers {
+ n, err = _i64_err(write(w, p))
+ if err != nil {
+ return
}
+ if n != i64(len(p)) {
+ err = .Short_Write
+ return
+ }
+ }
- return len(p), nil
- },
+ return i64(len(p)), nil
}
+
multi_writer_init :: proc(mw: ^Multi_Writer, writers: ..Writer, allocator := context.allocator) -> (out: Writer) {
mw.writers = make([dynamic]Writer, 0, len(writers), allocator)
for w in writers {
- if w.stream_vtable == _multi_writer_vtable {
- other := (^Multi_Writer)(w.stream_data)
+ if w.procedure == _multi_writer_proc {
+ other := (^Multi_Writer)(w.data)
append(&mw.writers, ..other.writers[:])
} else {
append(&mw.writers, w)
}
}
- out.stream_vtable = _multi_writer_vtable
- out.stream_data = mw
+ out.procedure = _multi_writer_proc
+ out.data = mw
return
}
diff --git a/core/io/util.odin b/core/io/util.odin
index 46aa97919..c77d0be9d 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -2,6 +2,7 @@ package io
import "core:strconv"
import "core:unicode/utf8"
+import "core:unicode/utf16"
read_ptr :: proc(r: Reader, p: rawptr, byte_size: int, n_read: ^int = nil) -> (n: int, err: Error) {
return read(r, ([^]byte)(p)[:byte_size], n_read)
@@ -146,7 +147,7 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^
return
}
-write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false, n_written: ^int = nil) -> (n: int, err: Error) {
+write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false, n_written: ^int = nil, for_json := false) -> (n: int, err: Error) {
is_printable :: proc(r: rune) -> bool {
if r <= 0xff {
switch r {
@@ -163,7 +164,7 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
defer if n_written != nil {
n_written^ += n
}
-
+
if html_safe {
switch r {
case '<', '>', '&':
@@ -211,17 +212,29 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
}
case:
- write_byte(w, '\\', &n) or_return
- write_byte(w, 'U', &n) or_return
- for s := 28; s >= 0; s -= 4 {
- write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
+ if for_json {
+ buf: [2]u16
+ utf16.encode(buf[:], []rune{c})
+ for bc in buf {
+ write_byte(w, '\\', &n) or_return
+ write_byte(w, 'u', &n) or_return
+ for s := 12; s >= 0; s -= 4 {
+ write_byte(w, DIGITS_LOWER[bc>>uint(s) & 0xf], &n) or_return
+ }
+ }
+ } else {
+ write_byte(w, '\\', &n) or_return
+ write_byte(w, 'U', &n) or_return
+ for s := 24; s >= 0; s -= 4 {
+ write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
+ }
}
}
}
return
}
-write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written: ^int = nil) -> (n: int, err: Error) {
+write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written: ^int = nil, for_json := false) -> (n: int, err: Error) {
defer if n_written != nil {
n_written^ += n
}
@@ -240,7 +253,7 @@ write_quoted_string :: proc(w: Writer, str: string, quote: byte = '"', n_written
continue
}
- n_wrapper(write_escaped_rune(w, r, quote), &n) or_return
+ n_wrapper(write_escaped_rune(w, r, quote, false, nil, for_json), &n) or_return
}
write_byte(w, quote, &n) or_return
@@ -279,17 +292,21 @@ Tee_Reader :: struct {
}
@(private)
-_tee_reader_vtable := &Stream_VTable{
- impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
- t := (^Tee_Reader)(s.stream_data)
- n, err = read(t.r, p)
+_tee_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ t := (^Tee_Reader)(stream_data)
+ #partial switch mode {
+ case .Read:
+ n, err = _i64_err(read(t.r, p))
if n > 0 {
if wn, werr := write(t.w, p[:n]); werr != nil {
- return wn, werr
+ return i64(wn), werr
}
}
return
- },
+ case .Query:
+ return query_utility({.Read, .Query})
+ }
+ return 0, .Empty
}
// tee_reader_init returns a Reader that writes to 'w' what it reads from 'r'
@@ -304,8 +321,8 @@ tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := conte
}
tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) {
- r.stream_data = t
- r.stream_vtable = _tee_reader_vtable
+ r.data = t
+ r.procedure = _tee_reader_proc
return
}
@@ -319,9 +336,10 @@ Limited_Reader :: struct {
}
@(private)
-_limited_reader_vtable := &Stream_VTable{
- impl_read = proc(s: Stream, p: []byte) -> (n: int, err: Error) {
- l := (^Limited_Reader)(s.stream_data)
+_limited_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ l := (^Limited_Reader)(stream_data)
+ #partial switch mode {
+ case .Read:
if l.n <= 0 {
return 0, .EOF
}
@@ -329,10 +347,13 @@ _limited_reader_vtable := &Stream_VTable{
if i64(len(p)) > l.n {
p = p[0:l.n]
}
- n, err = read(l.r, p)
+ n, err = _i64_err(read(l.r, p))
l.n -= i64(n)
return
- },
+ case .Query:
+ return query_utility({.Read, .Query})
+ }
+ return 0, .Empty
}
limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
@@ -342,8 +363,8 @@ limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader {
}
limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) {
- r.stream_vtable = _limited_reader_vtable
- r.stream_data = l
+ r.procedure = _limited_reader_proc
+ r.data = l
return
}
@@ -362,15 +383,16 @@ section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64)
return
}
section_reader_to_stream :: proc(s: ^Section_Reader) -> (out: Stream) {
- out.stream_data = s
- out.stream_vtable = _section_reader_vtable
+ out.data = s
+ out.procedure = _section_reader_proc
return
}
@(private)
-_section_reader_vtable := &Stream_VTable{
- impl_read = proc(stream: Stream, p: []byte) -> (n: int, err: Error) {
- s := (^Section_Reader)(stream.stream_data)
+_section_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
+ s := (^Section_Reader)(stream_data)
+ #partial switch mode {
+ case .Read:
if s.off >= s.limit {
return 0, .EOF
}
@@ -378,13 +400,11 @@ _section_reader_vtable := &Stream_VTable{
if max := s.limit - s.off; i64(len(p)) > max {
p = p[0:max]
}
- n, err = read_at(s.r, p, s.off)
+ n, err = _i64_err(read_at(s.r, p, s.off))
s.off += i64(n)
return
- },
- impl_read_at = proc(stream: Stream, p: []byte, off: i64) -> (n: int, err: Error) {
- s := (^Section_Reader)(stream.stream_data)
- p, off := p, off
+ case .Read_At:
+ p, off := p, offset
if off < 0 || off >= s.limit - s.base {
return 0, .EOF
@@ -392,17 +412,15 @@ _section_reader_vtable := &Stream_VTable{
off += s.base
if max := s.limit - off; i64(len(p)) > max {
p = p[0:max]
- n, err = read_at(s.r, p, off)
+ n, err = _i64_err(read_at(s.r, p, off))
if err == nil {
err = .EOF
}
return
}
- return read_at(s.r, p, off)
- },
- impl_seek = proc(stream: Stream, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
- s := (^Section_Reader)(stream.stream_data)
+ return _i64_err(read_at(s.r, p, off))
+ case .Seek:
offset := offset
switch whence {
case:
@@ -420,10 +438,12 @@ _section_reader_vtable := &Stream_VTable{
s.off = offset
n = offset - s.base
return
- },
- impl_size = proc(stream: Stream) -> i64 {
- s := (^Section_Reader)(stream.stream_data)
- return s.limit - s.base
- },
-}
+ case .Size:
+ n = s.limit - s.base
+ return
+ case .Query:
+ return query_utility({.Read, .Read_At, .Seek, .Size, .Query})
+ }
+ return 0, nil
+}
diff --git a/core/log/log.odin b/core/log/log.odin
index a699247b8..f3554791b 100644
--- a/core/log/log.odin
+++ b/core/log/log.odin
@@ -76,43 +76,43 @@ nil_logger :: proc() -> Logger {
}
debugf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
- logf(level=.Debug, fmt_str=fmt_str, args=args, location=location)
+ logf(.Debug, fmt_str, ..args, location=location)
}
infof :: proc(fmt_str: string, args: ..any, location := #caller_location) {
- logf(level=.Info, fmt_str=fmt_str, args=args, location=location)
+ logf(.Info, fmt_str, ..args, location=location)
}
warnf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
- logf(level=.Warning, fmt_str=fmt_str, args=args, location=location)
+ logf(.Warning, fmt_str, ..args, location=location)
}
errorf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
- logf(level=.Error, fmt_str=fmt_str, args=args, location=location)
+ logf(.Error, fmt_str, ..args, location=location)
}
fatalf :: proc(fmt_str: string, args: ..any, location := #caller_location) {
- logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location)
+ logf(.Fatal, fmt_str, ..args, location=location)
}
debug :: proc(args: ..any, sep := " ", location := #caller_location) {
- log(level=.Debug, args=args, sep=sep, location=location)
+ log(.Debug, ..args, sep=sep, location=location)
}
info :: proc(args: ..any, sep := " ", location := #caller_location) {
- log(level=.Info, args=args, sep=sep, location=location)
+ log(.Info, ..args, sep=sep, location=location)
}
warn :: proc(args: ..any, sep := " ", location := #caller_location) {
- log(level=.Warning, args=args, sep=sep, location=location)
+ log(.Warning, ..args, sep=sep, location=location)
}
error :: proc(args: ..any, sep := " ", location := #caller_location) {
- log(level=.Error, args=args, sep=sep, location=location)
+ log(.Error, ..args, sep=sep, location=location)
}
fatal :: proc(args: ..any, sep := " ", location := #caller_location) {
- log(level=.Fatal, args=args, sep=sep, location=location)
+ log(.Fatal, ..args, sep=sep, location=location)
}
panic :: proc(args: ..any, location := #caller_location) -> ! {
- log(level=.Fatal, args=args, location=location)
+ log(.Fatal, ..args, location=location)
runtime.panic("log.panic", location)
}
panicf :: proc(fmt_str: string, args: ..any, location := #caller_location) -> ! {
- logf(level=.Fatal, fmt_str=fmt_str, args=args, location=location)
+ logf(.Fatal, fmt_str, ..args, location=location)
runtime.panic("log.panicf", location)
}
@@ -127,7 +127,7 @@ log :: proc(level: Level, args: ..any, sep := " ", location := #caller_location)
if level < logger.lowest_level {
return
}
- str := fmt.tprint(args=args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
+ str := fmt.tprint(..args, sep=sep) //NOTE(Hoej): While tprint isn't thread-safe, no logging is.
logger.procedure(logger.data, level, str, logger.options, location)
}
diff --git a/core/log/log_allocator.odin b/core/log/log_allocator.odin
index f4d1841db..934f0d643 100644
--- a/core/log/log_allocator.odin
+++ b/core/log/log_allocator.odin
@@ -38,60 +38,60 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
switch mode {
case .Alloc:
logf(
- level=la.level,
- fmt_str = "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
- args = {la.prefix, padding, size, alignment},
+ la.level,
+ "%s%s>>> ALLOCATOR(mode=.Alloc, size=%d, alignment=%d)",
+ 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},
+ la.level,
+ "%s%s>>> ALLOCATOR(mode=.Alloc_Non_Zeroed, size=%d, alignment=%d)",
+ la.prefix, padding, size, alignment,
location = location,
)
case .Free:
if old_size != 0 {
logf(
- level=la.level,
- fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
- args = {la.prefix, padding, old_memory, old_size},
+ la.level,
+ "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p, size=%d)",
+ la.prefix, padding, old_memory, old_size,
location = location,
)
} else {
logf(
- level=la.level,
- fmt_str = "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
- args = {la.prefix, padding, old_memory},
+ la.level,
+ "%s%s<<< ALLOCATOR(mode=.Free, ptr=%p)",
+ la.prefix, padding, old_memory,
location = location,
)
}
case .Free_All:
logf(
- level=la.level,
- fmt_str = "%s%s<<< ALLOCATOR(mode=.Free_All)",
- args = {la.prefix, padding},
+ la.level,
+ "%s%s<<< ALLOCATOR(mode=.Free_All)",
+ la.prefix, padding,
location = location,
)
case .Resize:
logf(
- level=la.level,
- fmt_str = "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
- args = {la.prefix, padding, old_memory, old_size, size, alignment},
+ la.level,
+ "%s%s>>> ALLOCATOR(mode=.Resize, ptr=%p, old_size=%d, size=%d, alignment=%d)",
+ la.prefix, padding, old_memory, old_size, size, alignment,
location = location,
)
case .Query_Features:
logf(
- level=la.level,
- fmt_str = "%s%ALLOCATOR(mode=.Query_Features)",
- args = {la.prefix, padding},
+ la.level,
+ "%s%ALLOCATOR(mode=.Query_Features)",
+ la.prefix, padding,
location = location,
)
case .Query_Info:
logf(
- level=la.level,
- fmt_str = "%s%ALLOCATOR(mode=.Query_Info)",
- args = {la.prefix, padding},
+ la.level,
+ "%s%ALLOCATOR(mode=.Query_Info)",
+ la.prefix, padding,
location = location,
)
}
@@ -103,9 +103,9 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
defer la.locked = false
if err != nil {
logf(
- level=la.level,
- fmt_str = "%s%ALLOCATOR ERROR=%v",
- args = {la.prefix, padding, error},
+ la.level,
+ "%s%ALLOCATOR ERROR=%v",
+ la.prefix, padding, error,
location = location,
)
}
diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin
index 6f972937a..2f8b81af2 100644
--- a/core/math/big/prime.odin
+++ b/core/math/big/prime.odin
@@ -353,14 +353,14 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
// Run the Miller-Rabin test with base 2 for the BPSW test.
internal_set(b, 2) or_return
- if !internal_int_prime_miller_rabin(a, b) or_return { return }
+ if !(internal_int_prime_miller_rabin(a, b) or_return) { return }
// Rumours have it that Mathematica does a second M-R test with base 3.
// Other rumours have it that their strong L-S test is slightly different.
// It does not hurt, though, beside a bit of extra runtime.
b.digit[0] += 1
- if !internal_int_prime_miller_rabin(a, b) or_return { return }
+ if !(internal_int_prime_miller_rabin(a, b) or_return) { return }
// Both, the Frobenius-Underwood test and the the Lucas-Selfridge test are quite
// slow so if speed is an issue, set `USE_MILLER_RABIN_ONLY` to use M-R tests with
@@ -369,9 +369,9 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
if !miller_rabin_only {
if miller_rabin_trials >= 0 {
when MATH_BIG_USE_FROBENIUS_TEST {
- if !internal_int_prime_frobenius_underwood(a) or_return { return }
+ if !(internal_int_prime_frobenius_underwood(a) or_return) { return }
} else {
- if !internal_int_prime_strong_lucas_selfridge(a) or_return { return }
+ if !(internal_int_prime_strong_lucas_selfridge(a) or_return) { return }
}
}
}
@@ -410,7 +410,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
// We did bases 2 and 3 already, skip them
for ix := 2; ix < p_max; ix += 1 {
internal_set(b, _private_prime_table[ix])
- if !internal_int_prime_miller_rabin(a, b) or_return { return }
+ if !(internal_int_prime_miller_rabin(a, b) or_return) { return }
}
} else if miller_rabin_trials > 0 {
// Perform `miller_rabin_trials` M-R tests with random bases between 3 and "a".
@@ -490,7 +490,7 @@ internal_int_is_prime :: proc(a: ^Int, miller_rabin_trials := int(-1), miller_ra
ix -= 1
continue
}
- if !internal_int_prime_miller_rabin(a, b) or_return { return }
+ if !(internal_int_prime_miller_rabin(a, b) or_return) { return }
}
}
diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin
index 2b758dc35..d15ce0e98 100644
--- a/core/math/big/radix.odin
+++ b/core/math/big/radix.odin
@@ -429,7 +429,7 @@ internal_int_write_to_ascii_file :: proc(a: ^Int, filename: string, radix := i8(
len = l,
}
- ok := os.write_entire_file(name=filename, data=data, truncate=true)
+ ok := os.write_entire_file(filename, data, truncate=true)
return nil if ok else .Cannot_Write_File
}
diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin
index 850e8038a..959b5536f 100644
--- a/core/math/bits/bits.odin
+++ b/core/math/bits/bits.odin
@@ -37,68 +37,96 @@ overflowing_sub :: intrinsics.overflow_sub
overflowing_mul :: intrinsics.overflow_mul
-log2 :: proc(x: $T) -> T where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T) {
+@(require_results)
+log2 :: proc "contextless" (x: $T) -> T where intrinsics.type_is_integer(T), intrinsics.type_is_unsigned(T) {
return (8*size_of(T)-1) - count_leading_zeros(x)
}
-rotate_left8 :: proc(x: u8, k: int) -> u8 {
+@(require_results)
+rotate_left8 :: proc "contextless" (x: u8, k: int) -> u8 {
n :: 8
s := uint(k) & (n-1)
return x <>(n-s)
}
-rotate_left16 :: proc(x: u16, k: int) -> u16 {
+@(require_results)
+rotate_left16 :: proc "contextless" (x: u16, k: int) -> u16 {
n :: 16
s := uint(k) & (n-1)
return x <>(n-s)
}
-rotate_left32 :: proc(x: u32, k: int) -> u32 {
+@(require_results)
+rotate_left32 :: proc "contextless" (x: u32, k: int) -> u32 {
n :: 32
s := uint(k) & (n-1)
return x <>(n-s)
}
-rotate_left64 :: proc(x: u64, k: int) -> u64 {
+@(require_results)
+rotate_left64 :: proc "contextless" (x: u64, k: int) -> u64 {
n :: 64
s := uint(k) & (n-1)
return x <>(n-s)
}
-rotate_left :: proc(x: uint, k: int) -> uint {
+@(require_results)
+rotate_left :: proc "contextless" (x: uint, k: int) -> uint {
n :: 8*size_of(uint)
s := uint(k) & (n-1)
return x <>(n-s)
}
-from_be_u8 :: proc(i: u8) -> u8 { return i }
-from_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-from_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-from_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-from_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+from_be_u8 :: proc "contextless" (i: u8) -> u8 { return i }
+@(require_results)
+from_be_u16 :: proc "contextless" (i: u16) -> u16 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+from_be_u32 :: proc "contextless" (i: u32) -> u32 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+from_be_u64 :: proc "contextless" (i: u64) -> u64 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+from_be_uint :: proc "contextless" (i: uint) -> uint { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-from_le_u8 :: proc(i: u8) -> u8 { return i }
-from_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-from_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-from_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-from_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+from_le_u8 :: proc "contextless" (i: u8) -> u8 { return i }
+@(require_results)
+from_le_u16 :: proc "contextless" (i: u16) -> u16 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+from_le_u32 :: proc "contextless" (i: u32) -> u32 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+from_le_u64 :: proc "contextless" (i: u64) -> u64 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+from_le_uint :: proc "contextless" (i: uint) -> uint { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-to_be_u8 :: proc(i: u8) -> u8 { return i }
-to_be_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-to_be_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-to_be_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-to_be_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+to_be_u8 :: proc "contextless" (i: u8) -> u8 { return i }
+@(require_results)
+to_be_u16 :: proc "contextless" (i: u16) -> u16 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+to_be_u32 :: proc "contextless" (i: u32) -> u32 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+to_be_u64 :: proc "contextless" (i: u64) -> u64 { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
+@(require_results)
+to_be_uint :: proc "contextless" (i: uint) -> uint { when ODIN_ENDIAN == .Big { return i } else { return byte_swap(i) } }
-to_le_u8 :: proc(i: u8) -> u8 { return i }
-to_le_u16 :: proc(i: u16) -> u16 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-to_le_u32 :: proc(i: u32) -> u32 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-to_le_u64 :: proc(i: u64) -> u64 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-to_le_uint :: proc(i: uint) -> uint { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+to_le_u8 :: proc "contextless" (i: u8) -> u8 { return i }
+@(require_results)
+to_le_u16 :: proc "contextless" (i: u16) -> u16 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+to_le_u32 :: proc "contextless" (i: u32) -> u32 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+to_le_u64 :: proc "contextless" (i: u64) -> u64 { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
+@(require_results)
+to_le_uint :: proc "contextless" (i: uint) -> uint { when ODIN_ENDIAN == .Little { return i } else { return byte_swap(i) } }
-len_u8 :: proc(x: u8) -> int {
+@(require_results)
+len_u8 :: proc "contextless" (x: u8) -> int {
return int(len_u8_table[x])
}
-len_u16 :: proc(x: u16) -> (n: int) {
+@(require_results)
+len_u16 :: proc "contextless" (x: u16) -> (n: int) {
x := x
if x >= 1<<8 {
x >>= 8
@@ -106,7 +134,8 @@ len_u16 :: proc(x: u16) -> (n: int) {
}
return n + int(len_u8_table[x])
}
-len_u32 :: proc(x: u32) -> (n: int) {
+@(require_results)
+len_u32 :: proc "contextless" (x: u32) -> (n: int) {
x := x
if x >= 1<<16 {
x >>= 16
@@ -118,7 +147,8 @@ len_u32 :: proc(x: u32) -> (n: int) {
}
return n + int(len_u8_table[x])
}
-len_u64 :: proc(x: u64) -> (n: int) {
+@(require_results)
+len_u64 :: proc "contextless" (x: u64) -> (n: int) {
x := x
if x >= 1<<32 {
x >>= 32
@@ -134,7 +164,8 @@ len_u64 :: proc(x: u64) -> (n: int) {
}
return n + int(len_u8_table[x])
}
-len_uint :: proc(x: uint) -> (n: int) {
+@(require_results)
+len_uint :: proc "contextless" (x: uint) -> (n: int) {
when size_of(uint) == size_of(u64) {
return len_u64(u64(x))
} else {
@@ -146,21 +177,24 @@ len_uint :: proc(x: uint) -> (n: int) {
len :: proc{len_u8, len_u16, len_u32, len_u64, len_uint}
-add_u32 :: proc(x, y, carry: u32) -> (sum, carry_out: u32) {
+@(require_results)
+add_u32 :: proc "contextless" (x, y, carry: u32) -> (sum, carry_out: u32) {
tmp_carry, tmp_carry2: bool
sum, tmp_carry = intrinsics.overflow_add(x, y)
sum, tmp_carry2 = intrinsics.overflow_add(sum, carry)
carry_out = u32(tmp_carry | tmp_carry2)
return
}
-add_u64 :: proc(x, y, carry: u64) -> (sum, carry_out: u64) {
+@(require_results)
+add_u64 :: proc "contextless" (x, y, carry: u64) -> (sum, carry_out: u64) {
tmp_carry, tmp_carry2: bool
sum, tmp_carry = intrinsics.overflow_add(x, y)
sum, tmp_carry2 = intrinsics.overflow_add(sum, carry)
carry_out = u64(tmp_carry | tmp_carry2)
return
}
-add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
+@(require_results)
+add_uint :: proc "contextless" (x, y, carry: uint) -> (sum, carry_out: uint) {
when size_of(uint) == size_of(u64) {
a, b := add_u64(u64(x), u64(y), u64(carry))
} else {
@@ -172,21 +206,24 @@ add_uint :: proc(x, y, carry: uint) -> (sum, carry_out: uint) {
add :: proc{add_u32, add_u64, add_uint}
-sub_u32 :: proc(x, y, borrow: u32) -> (diff, borrow_out: u32) {
+@(require_results)
+sub_u32 :: proc "contextless" (x, y, borrow: u32) -> (diff, borrow_out: u32) {
tmp_borrow, tmp_borrow2: bool
diff, tmp_borrow = intrinsics.overflow_sub(x, y)
diff, tmp_borrow2 = intrinsics.overflow_sub(diff, borrow)
borrow_out = u32(tmp_borrow | tmp_borrow2)
return
}
-sub_u64 :: proc(x, y, borrow: u64) -> (diff, borrow_out: u64) {
+@(require_results)
+sub_u64 :: proc "contextless" (x, y, borrow: u64) -> (diff, borrow_out: u64) {
tmp_borrow, tmp_borrow2: bool
diff, tmp_borrow = intrinsics.overflow_sub(x, y)
diff, tmp_borrow2 = intrinsics.overflow_sub(diff, borrow)
borrow_out = u64(tmp_borrow | tmp_borrow2)
return
}
-sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
+@(require_results)
+sub_uint :: proc "contextless" (x, y, borrow: uint) -> (diff, borrow_out: uint) {
when size_of(uint) == size_of(u64) {
a, b := sub_u64(u64(x), u64(y), u64(borrow))
} else {
@@ -198,18 +235,21 @@ sub_uint :: proc(x, y, borrow: uint) -> (diff, borrow_out: uint) {
sub :: proc{sub_u32, sub_u64, sub_uint}
-mul_u32 :: proc(x, y: u32) -> (hi, lo: u32) {
+@(require_results)
+mul_u32 :: proc "contextless" (x, y: u32) -> (hi, lo: u32) {
z := u64(x) * u64(y)
hi, lo = u32(z>>32), u32(z)
return
}
-mul_u64 :: proc(x, y: u64) -> (hi, lo: u64) {
+@(require_results)
+mul_u64 :: proc "contextless" (x, y: u64) -> (hi, lo: u64) {
prod_wide := u128(x) * u128(y)
hi, lo = u64(prod_wide>>64), u64(prod_wide)
return
}
-mul_uint :: proc(x, y: uint) -> (hi, lo: uint) {
+@(require_results)
+mul_uint :: proc "contextless" (x, y: uint) -> (hi, lo: uint) {
when size_of(uint) == size_of(u32) {
a, b := mul_u32(u32(x), u32(y))
} else {
@@ -222,13 +262,15 @@ mul_uint :: proc(x, y: uint) -> (hi, lo: uint) {
mul :: proc{mul_u32, mul_u64, mul_uint}
-div_u32 :: proc(hi, lo, y: u32) -> (quo, rem: u32) {
+@(require_results)
+div_u32 :: proc "odin" (hi, lo, y: u32) -> (quo, rem: u32) {
assert(y != 0 && y <= hi)
z := u64(hi)<<32 | u64(lo)
quo, rem = u32(z/u64(y)), u32(z%u64(y))
return
}
-div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) {
+@(require_results)
+div_u64 :: proc "odin" (hi, lo, y: u64) -> (quo, rem: u64) {
y := y
two32 :: 1 << 32
mask32 :: two32 - 1
@@ -273,7 +315,8 @@ div_u64 :: proc(hi, lo, y: u64) -> (quo, rem: u64) {
return q1*two32 + q0, (un21*two32 + un0 - q0*y) >> s
}
-div_uint :: proc(hi, lo, y: uint) -> (quo, rem: uint) {
+@(require_results)
+div_uint :: proc "odin" (hi, lo, y: uint) -> (quo, rem: uint) {
when size_of(uint) == size_of(u32) {
a, b := div_u32(u32(hi), u32(lo), u32(y))
} else {
@@ -286,16 +329,26 @@ div :: proc{div_u32, div_u64, div_uint}
-is_power_of_two_u8 :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_i8 :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_u16 :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_i16 :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_u32 :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_i32 :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_u64 :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_i64 :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_uint :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0 }
-is_power_of_two_int :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_u8 :: proc "contextless" (i: u8) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_i8 :: proc "contextless" (i: i8) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_u16 :: proc "contextless" (i: u16) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_i16 :: proc "contextless" (i: i16) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_u32 :: proc "contextless" (i: u32) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_i32 :: proc "contextless" (i: i32) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_u64 :: proc "contextless" (i: u64) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_i64 :: proc "contextless" (i: i64) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_uint :: proc "contextless" (i: uint) -> bool { return i > 0 && (i & (i-1)) == 0 }
+@(require_results)
+is_power_of_two_int :: proc "contextless" (i: int) -> bool { return i > 0 && (i & (i-1)) == 0 }
is_power_of_two :: proc{
is_power_of_two_u8, is_power_of_two_i8,
@@ -320,44 +373,56 @@ len_u8_table := [256]u8{
}
-bitfield_extract_u8 :: proc(value: u8, offset, bits: uint) -> u8 { return (value >> offset) & u8(1< u16 { return (value >> offset) & u16(1< u32 { return (value >> offset) & u32(1< u64 { return (value >> offset) & u64(1< u128 { return (value >> offset) & u128(1< uint { return (value >> offset) & uint(1< u8 { return (value >> offset) & u8(1< u16 { return (value >> offset) & u16(1< u32 { return (value >> offset) & u32(1< u64 { return (value >> offset) & u64(1< u128 { return (value >> offset) & u128(1< uint { return (value >> offset) & uint(1< i8 {
+@(require_results)
+bitfield_extract_i8 :: proc "contextless" (value: i8, offset, bits: uint) -> i8 {
v := (u8(value) >> offset) & u8(1< i16 {
+@(require_results)
+bitfield_extract_i16 :: proc "contextless" (value: i16, offset, bits: uint) -> i16 {
v := (u16(value) >> offset) & u16(1< i32 {
+@(require_results)
+bitfield_extract_i32 :: proc "contextless" (value: i32, offset, bits: uint) -> i32 {
v := (u32(value) >> offset) & u32(1< i64 {
+@(require_results)
+bitfield_extract_i64 :: proc "contextless" (value: i64, offset, bits: uint) -> i64 {
v := (u64(value) >> offset) & u64(1< i128 {
+@(require_results)
+bitfield_extract_i128 :: proc "contextless" (value: i128, offset, bits: uint) -> i128 {
v := (u128(value) >> offset) & u128(1< int {
+@(require_results)
+bitfield_extract_int :: proc "contextless" (value: int, offset, bits: uint) -> int {
v := (uint(value) >> offset) & uint(1< u8 {
+@(require_results)
+bitfield_insert_u8 :: proc "contextless" (base, insert: u8, offset, bits: uint) -> u8 {
mask := u8(1< u16 {
+@(require_results)
+bitfield_insert_u16 :: proc "contextless" (base, insert: u16, offset, bits: uint) -> u16 {
mask := u16(1< u32 {
+@(require_results)
+bitfield_insert_u32 :: proc "contextless" (base, insert: u32, offset, bits: uint) -> u32 {
mask := u32(1< u64 {
+@(require_results)
+bitfield_insert_u64 :: proc "contextless" (base, insert: u64, offset, bits: uint) -> u64 {
mask := u64(1< u128 {
+@(require_results)
+bitfield_insert_u128 :: proc "contextless" (base, insert: u128, offset, bits: uint) -> u128 {
mask := u128(1< uint {
+@(require_results)
+bitfield_insert_uint :: proc "contextless" (base, insert: uint, offset, bits: uint) -> uint {
mask := uint(1< i8 {
+@(require_results)
+bitfield_insert_i8 :: proc "contextless" (base, insert: i8, offset, bits: uint) -> i8 {
mask := i8(1< i16 {
+@(require_results)
+bitfield_insert_i16 :: proc "contextless" (base, insert: i16, offset, bits: uint) -> i16 {
mask := i16(1< i32 {
+@(require_results)
+bitfield_insert_i32 :: proc "contextless" (base, insert: i32, offset, bits: uint) -> i32 {
mask := i32(1< i64 {
+@(require_results)
+bitfield_insert_i64 :: proc "contextless" (base, insert: i64, offset, bits: uint) -> i64 {
mask := i64(1< i128 {
+@(require_results)
+bitfield_insert_i128 :: proc "contextless" (base, insert: i128, offset, bits: uint) -> i128 {
mask := i128(1< int {
+@(require_results)
+bitfield_insert_int :: proc "contextless" (base, insert: int, offset, bits: uint) -> int {
mask := int(1< T where intrinsics.type_is_float(T) {
return p * p
}
// Modeled after the parabola y = -x^2 + 2x
+@(require_results)
quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return -(p * (p - 2))
}
@@ -23,6 +25,7 @@ quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(
// Modeled after the piecewise quadratic
// y = (1/2)((2x)^2) ; [0, 0.5)
// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
+@(require_results)
quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 2 * p * p
@@ -32,11 +35,13 @@ quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_flo
}
// Modeled after the cubic y = x^3
+@(require_results)
cubic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p
}
// Modeled after the cubic y = (x - 1)^3 + 1
+@(require_results)
cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := p - 1
return f * f * f + 1
@@ -45,6 +50,7 @@ cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
// Modeled after the piecewise cubic
// y = (1/2)((2x)^3) ; [0, 0.5)
// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
+@(require_results)
cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 4 * p * p * p
@@ -55,11 +61,13 @@ cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T
}
// Modeled after the quartic x^4
+@(require_results)
quartic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p * p
}
// Modeled after the quartic y = 1 - (x - 1)^4
+@(require_results)
quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := p - 1
return f * f * f * (1 - p) + 1
@@ -68,6 +76,7 @@ quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T)
// Modeled after the piecewise quartic
// y = (1/2)((2x)^4) ; [0, 0.5)
// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
+@(require_results)
quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 8 * p * p * p * p
@@ -78,11 +87,13 @@ quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float
}
// Modeled after the quintic y = x^5
+@(require_results)
quintic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p * p * p
}
// Modeled after the quintic y = (x - 1)^5 + 1
+@(require_results)
quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := p - 1
return f * f * f * f * f + 1
@@ -91,6 +102,7 @@ quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T)
// Modeled after the piecewise quintic
// y = (1/2)((2x)^5) ; [0, 0.5)
// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
+@(require_results)
quintic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 16 * p * p * p * p * p
@@ -101,26 +113,31 @@ quintic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float
}
// Modeled after quarter-cycle of sine wave
+@(require_results)
sine_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin((p - 1) * PI_2) + 1
}
// Modeled after quarter-cycle of sine wave (different phase)
+@(require_results)
sine_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin(p * PI_2)
}
// Modeled after half sine wave
+@(require_results)
sine_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return 0.5 * (1 - math.cos(p * math.PI))
}
// Modeled after shifted quadrant IV of unit circle
+@(require_results)
circular_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return 1 - math.sqrt(1 - (p * p))
}
// Modeled after shifted quadrant II of unit circle
+@(require_results)
circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sqrt((2 - p) * p)
}
@@ -128,6 +145,7 @@ circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T
// Modeled after the piecewise circular function
// y = (1/2)(1 - sqrt(1 - 4x^2)) ; [0, 0.5)
// y = (1/2)(sqrt(-(2x - 3)*(2x - 1)) + 1) ; [0.5, 1]
+@(require_results)
circular_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 0.5 * (1 - math.sqrt(1 - 4 * (p * p)))
@@ -137,11 +155,13 @@ circular_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_floa
}
// Modeled after the exponential function y = 2^(10(x - 1))
+@(require_results)
exponential_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p == 0.0 ? p : math.pow(2, 10 * (p - 1))
}
// Modeled after the exponential function y = -2^(-10x) + 1
+@(require_results)
exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p == 1.0 ? p : 1 - math.pow(2, -10 * p)
}
@@ -149,6 +169,7 @@ exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_floa
// Modeled after the piecewise exponential
// y = (1/2)2^(10(2x - 1)) ; [0,0.5)
// y = -(1/2)*2^(-10(2x - 1))) + 1 ; [0.5,1]
+@(require_results)
exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p == 0.0 || p == 1.0 {
return p
@@ -162,11 +183,13 @@ exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_f
}
// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
+@(require_results)
elastic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin(13 * PI_2 * p) * math.pow(2, 10 * (p - 1))
}
// Modeled after the damped sine wave y = sin(-13pi/2*(x + 1))*pow(2, -10x) + 1
+@(require_results)
elastic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return math.sin(-13 * PI_2 * (p + 1)) * math.pow(2, -10 * p) + 1
}
@@ -174,6 +197,7 @@ elastic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T)
// Modeled after the piecewise exponentially-damped sine wave:
// y = (1/2)*sin(13pi/2*(2*x))*pow(2, 10 * ((2*x) - 1)) ; [0,0.5)
// y = (1/2)*(sin(-13pi/2*((2x-1)+1))*pow(2,-10(2*x-1)) + 2) ; [0.5, 1]
+@(require_results)
elastic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 0.5 * math.sin(13 * PI_2 * (2 * p)) * math.pow(2, 10 * ((2 * p) - 1))
@@ -183,11 +207,13 @@ elastic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float
}
// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
+@(require_results)
back_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return p * p * p - p * math.sin(p * math.PI)
}
// Modeled after overshooting cubic y = 1-((1-x)^3-(1-x)*sin((1-x)*pi))
+@(require_results)
back_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
f := 1 - p
return 1 - (f * f * f - f * math.sin(f * math.PI))
@@ -196,6 +222,7 @@ back_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
// Modeled after the piecewise overshooting cubic function:
// y = (1/2)*((2x)^3-(2x)*sin(2*x*pi)) ; [0, 0.5)
// y = (1/2)*(1-((1-x)^3-(1-x)*sin((1-x)*pi))+1) ; [0.5, 1]
+@(require_results)
back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
f := 2 * p
@@ -206,10 +233,12 @@ back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T)
}
}
+@(require_results)
bounce_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
return 1 - bounce_out(1 - p)
}
+@(require_results)
bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 4/11.0 {
return (121 * p * p)/16.0
@@ -222,6 +251,7 @@ bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T)
}
}
+@(require_results)
bounce_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
if p < 0.5 {
return 0.5 * bounce_in(p*2)
@@ -276,50 +306,51 @@ Ease :: enum {
Bounce_In_Out,
}
+@(require_results)
ease :: proc "contextless" (type: Ease, p: $T) -> T
where intrinsics.type_is_float(T) {
switch type {
- case .Linear: return p
-
- case .Quadratic_In: return quadratic_in(p)
- case .Quadratic_Out: return quadratic_out(p)
- case .Quadratic_In_Out: return quadratic_in_out(p)
+ case .Linear: return p
- case .Cubic_In: return cubic_in(p)
- case .Cubic_Out: return cubic_out(p)
- case .Cubic_In_Out: return cubic_in_out(p)
+ case .Quadratic_In: return quadratic_in(p)
+ case .Quadratic_Out: return quadratic_out(p)
+ case .Quadratic_In_Out: return quadratic_in_out(p)
- case .Quartic_In: return quartic_in(p)
- case .Quartic_Out: return quartic_out(p)
- case .Quartic_In_Out: return quartic_in_out(p)
+ case .Cubic_In: return cubic_in(p)
+ case .Cubic_Out: return cubic_out(p)
+ case .Cubic_In_Out: return cubic_in_out(p)
- case .Quintic_In: return quintic_in(p)
- case .Quintic_Out: return quintic_out(p)
- case .Quintic_In_Out: return quintic_in_out(p)
+ case .Quartic_In: return quartic_in(p)
+ case .Quartic_Out: return quartic_out(p)
+ case .Quartic_In_Out: return quartic_in_out(p)
- case .Sine_In: return sine_in(p)
- case .Sine_Out: return sine_out(p)
- case .Sine_In_Out: return sine_in_out(p)
+ case .Quintic_In: return quintic_in(p)
+ case .Quintic_Out: return quintic_out(p)
+ case .Quintic_In_Out: return quintic_in_out(p)
- case .Circular_In: return circular_in(p)
- case .Circular_Out: return circular_out(p)
- case .Circular_In_Out: return circular_in_out(p)
+ case .Sine_In: return sine_in(p)
+ case .Sine_Out: return sine_out(p)
+ case .Sine_In_Out: return sine_in_out(p)
- case .Exponential_In: return exponential_in(p)
- case .Exponential_Out: return exponential_out(p)
- case .Exponential_In_Out: return exponential_in_out(p)
+ case .Circular_In: return circular_in(p)
+ case .Circular_Out: return circular_out(p)
+ case .Circular_In_Out: return circular_in_out(p)
- case .Elastic_In: return elastic_in(p)
- case .Elastic_Out: return elastic_out(p)
- case .Elastic_In_Out: return elastic_in_out(p)
+ case .Exponential_In: return exponential_in(p)
+ case .Exponential_Out: return exponential_out(p)
+ case .Exponential_In_Out: return exponential_in_out(p)
- case .Back_In: return back_in(p)
- case .Back_Out: return back_out(p)
- case .Back_In_Out: return back_in_out(p)
+ case .Elastic_In: return elastic_in(p)
+ case .Elastic_Out: return elastic_out(p)
+ case .Elastic_In_Out: return elastic_in_out(p)
- case .Bounce_In: return bounce_in(p)
- case .Bounce_Out: return bounce_out(p)
- case .Bounce_In_Out: return bounce_in_out(p)
+ case .Back_In: return back_in(p)
+ case .Back_Out: return back_out(p)
+ case .Back_In_Out: return back_in_out(p)
+
+ case .Bounce_In: return bounce_in(p)
+ case .Bounce_Out: return bounce_out(p)
+ case .Bounce_In_Out: return bounce_in_out(p)
}
// in case type was invalid
@@ -353,6 +384,7 @@ Flux_Tween :: struct($T: typeid) {
}
// init flux map to a float type and a wanted cap
+@(require_results)
flux_init :: proc($T: typeid, value_capacity := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
return {
values = make(map[^T]Flux_Tween(T), value_capacity),
@@ -374,6 +406,7 @@ flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
// append / overwrite existing tween value to parameters
// rest is initialized in flux_tween_init, inside update
// return value can be used to set callbacks
+@(require_results)
flux_to :: proc(
flux: ^Flux_Map($T),
value: ^T,
@@ -475,6 +508,7 @@ flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float
// stop a specific key inside the map
// returns true when it successfully removed the key
+@(require_results)
flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
if key in flux.values {
delete_key(&flux.values, key)
@@ -486,6 +520,7 @@ flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is
// returns the amount of time left for the tween animation, if the key exists in the map
// returns 0 if the tween doesnt exist on the map
+@(require_results)
flux_tween_time_left :: proc(flux: Flux_Map($T), key: ^T) -> f64 {
if tween, ok := flux.values[key]; ok {
return ((1 - tween.progress) * tween.rate) + tween.delay
diff --git a/core/math/fixed/fixed.odin b/core/math/fixed/fixed.odin
index 856e7f088..e080d63b6 100644
--- a/core/math/fixed/fixed.odin
+++ b/core/math/fixed/fixed.odin
@@ -50,39 +50,48 @@ to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
}
+@(require_results)
add :: proc(x, y: $T/Fixed) -> T {
return {x.i + y.i}
}
+@(require_results)
sub :: proc(x, y: $T/Fixed) -> T {
return {x.i - y.i}
}
+@(require_results)
mul :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_mul(x.i, y.i, Fraction_Width)
return
}
+@(require_results)
mul_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_mul_sat(x.i, y.i, Fraction_Width)
return
}
+@(require_results)
div :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_div(x.i, y.i, Fraction_Width)
return
}
+@(require_results)
div_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
z.i = intrinsics.fixed_point_div_sat(x.i, y.i, Fraction_Width)
return
}
+@(require_results)
floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
return x.i >> Fraction_Width
}
+@(require_results)
ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
Integer :: 8*size_of(Backing) - Fraction_Width
return (x.i + (1 << Integer-1)) >> Fraction_Width
}
+@(require_results)
round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
Integer :: 8*size_of(Backing) - Fraction_Width
return (x.i + (1 << (Integer - 1))) >> Fraction_Width
@@ -90,6 +99,7 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
+@(require_results)
append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
x := x
buf: [48]byte
@@ -123,6 +133,7 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
}
+@(require_results)
to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string {
buf: [48]byte
s := append(buf[:], x)
diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin
index b78697cbd..b6e05a2c2 100644
--- a/core/math/linalg/extended.odin
+++ b/core/math/linalg/extended.odin
@@ -3,20 +3,23 @@ package linalg
import "core:builtin"
import "core:math"
-radians :: proc(degrees: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+to_radians :: proc "contextless" (degrees: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+
+@(require_results)
+to_degrees :: proc "contextless" (radians: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
return
}
-min_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+min_double :: proc "contextless" (a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
return
}
-min_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+min_single :: proc "contextless" (a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
N :: len(T)
@@ -55,13 +60,15 @@ min_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T))
return
}
-min_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+min_triple :: proc "contextless" (a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
return min_double(a, min_double(b, c))
}
min :: proc{min_single, min_double, min_triple}
-max_double :: proc(a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+max_double :: proc "contextless" (a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
return
}
-max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+max_single :: proc "contextless" (a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
N :: len(T)
@@ -94,13 +102,15 @@ max_single :: proc(a: $T) -> (out: ELEM_TYPE(T)) where IS_NUMERIC(ELEM_TYPE(T))
return
}
-max_triple :: proc(a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+max_triple :: proc "contextless" (a, b, c: $T) -> T where IS_NUMERIC(ELEM_TYPE(T)) {
return max_double(a, max_double(b, c))
}
max :: proc{max_single, max_double, max_triple}
-abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+abs :: proc "contextless" (a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
return
}
-sign :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+sign :: proc "contextless" (a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
return
}
-clamp :: proc(x, a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
+@(require_results)
+clamp :: proc "contextless" (x, a, b: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
}
-saturate :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+saturate :: proc "contextless" (x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
return clamp(x, 0.0, 1.0)
}
-lerp :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+lerp :: proc "contextless" (a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
}
return
}
-mix :: proc(a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+mix :: proc "contextless" (a, b, t: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-unlerp :: proc(a, b, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+unlerp :: proc "contextless" (a, b, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
return (x - a) / (b - a)
}
-step :: proc(e, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+step :: proc "contextless" (e, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-smoothstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+smoothstep :: proc "contextless" (e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
t := saturate(unlerp(e0, e1, x))
return t * t * (3.0 - 2.0 * t)
}
-smootherstep :: proc(e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+smootherstep :: proc "contextless" (e0, e1, x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
t := saturate(unlerp(e0, e1, x))
return t * t * t * (t * (6*t - 15) + 10)
}
-sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+sqrt :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-inverse_sqrt :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+inverse_sqrt :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-cos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+cos :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-sin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+sin :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-tan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+tan :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-acos :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+acos :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-asin :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+asin :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-atan :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+atan :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
}
return
}
-atan2 :: proc(y, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+atan2 :: proc "contextless" (y, x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
}
-ln :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+ln :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-log2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-log10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+log10 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-log :: proc(x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+log :: proc "contextless" (x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-exp :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+exp :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-exp2 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+exp2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-exp10 :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+exp10 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-pow :: proc(x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+pow :: proc "contextless" (x, e: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
}
-ceil :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+ceil :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-floor :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+floor :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-round :: proc(x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+round :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
return
}
-fract :: proc(x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+fract :: proc "contextless" (x: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
f := #force_inline floor(x)
return x - f
}
-mod :: proc(x, m: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+mod :: proc "contextless" (x, m: $T) -> T where IS_FLOAT(ELEM_TYPE(T)) {
f := #force_inline floor(x / m)
return x - f * m
}
-face_forward :: proc(N, I, N_ref: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+face_forward :: proc "contextless" (N, I, N_ref: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
return dot(N_ref, I) < 0 ? N : -N
}
-distance :: proc(p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
+@(require_results)
+distance :: proc "contextless" (p0, p1: $V/[$N]$E) -> E where IS_NUMERIC(E) {
return length(p1 - p0)
}
-reflect :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
+@(require_results)
+reflect :: proc "contextless" (I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
b := N * (2 * dot(N, I))
return I - b
}
-refract :: proc(I, N: $T) -> (out: T) where IS_ARRAY(T), IS_FLOAT(ELEM_TYPE(T)) {
- dv := dot(N, I)
- k := 1 - eta*eta - (1 - dv*dv)
+@(require_results)
+refract :: proc "contextless" (I, Normal: $V/[$N]$E, eta: E) -> (out: V) where IS_ARRAY(V), IS_FLOAT(ELEM_TYPE(V)) {
+ dv := dot(Normal, I)
+ k := 1 - eta*eta * (1 - dv*dv)
a := I * eta
- b := N * eta*dv*math.sqrt(k)
+ b := Normal * (eta*dv+math.sqrt(k))
return (a - b) * E(int(k >= 0))
}
-is_nan_single :: proc(x: $T) -> bool where IS_FLOAT(T) {
+@(require_results)
+is_nan_single :: proc "contextless" (x: $T) -> bool where IS_FLOAT(T) {
return #force_inline math.is_nan(x)
}
-is_nan_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
+@(require_results)
+is_nan_array :: proc "contextless" (x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
for i in 0.. bool where IS_FLOAT(T) {
+@(require_results)
+is_inf_single :: proc "contextless" (x: $T) -> bool where IS_FLOAT(T) {
return #force_inline math.is_inf(x)
}
-is_inf_array :: proc(x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
+@(require_results)
+is_inf_array :: proc "contextless" (x: $A/[$N]$T) -> (out: [N]bool) where IS_FLOAT(T) {
for i in 0.. math.Float_Class where IS_FLOAT(T) {
+@(require_results)
+classify_single :: proc "contextless" (x: $T) -> math.Float_Class where IS_FLOAT(T) {
return #force_inline math.classify(x)
}
-classify_array :: proc(x: $A/[$N]$T) -> (out: [N]math.Float_Class) where IS_FLOAT(T) {
+@(require_results)
+classify_array :: proc "contextless" (x: $A/[$N]$T) -> (out: [N]math.Float_Class) where IS_FLOAT(T) {
for i in 0.. (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x < y }
-less_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x <= y }
-greater_than_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x > y }
-greater_than_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x >= y }
-equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x == y }
-not_equal_single :: proc(x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x != y }
+@(require_results) less_than_single :: proc "contextless" (x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x < y }
+@(require_results) less_than_equal_single :: proc "contextless" (x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x <= y }
+@(require_results) greater_than_single :: proc "contextless" (x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x > y }
+@(require_results) greater_than_equal_single :: proc "contextless" (x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x >= y }
+@(require_results) equal_single :: proc "contextless" (x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x == y }
+@(require_results) not_equal_single :: proc "contextless" (x, y: $T) -> (out: bool) where !IS_ARRAY(T), IS_FLOAT(T) { return x != y }
-less_than_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
+@(require_results)
+less_than_array :: proc "contextless" (x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0.. (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
+@(require_results)
+less_than_equal_array :: proc "contextless" (x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0.. (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
+@(require_results)
+greater_than_array :: proc "contextless" (x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0.. y[i]
}
return
}
-greater_than_equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
+@(require_results)
+greater_than_equal_array :: proc "contextless" (x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0..= y[i]
}
return
}
-equal_array :: proc(x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
+@(require_results)
+equal_array :: proc "contextless" (x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0.. (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
+@(require_results)
+not_equal_array :: proc "contextless" (x, y: $A/[$N]$T) -> (out: [N]bool) where IS_ARRAY(A), IS_FLOAT(ELEM_TYPE(A)) {
for i in 0.. (out: bool) {
+@(require_results)
+any :: proc "contextless" (x: $A/[$N]bool) -> (out: bool) {
for e in x {
if e {
return true
@@ -537,7 +595,8 @@ any :: proc(x: $A/[$N]bool) -> (out: bool) {
}
return false
}
-all :: proc(x: $A/[$N]bool) -> (out: bool) {
+@(require_results)
+all :: proc "contextless" (x: $A/[$N]bool) -> (out: bool) {
for e in x {
if !e {
return false
@@ -545,7 +604,8 @@ all :: proc(x: $A/[$N]bool) -> (out: bool) {
}
return true
}
-not :: proc(x: $A/[$N]bool) -> (out: A) {
+@(require_results)
+not :: proc "contextless" (x: $A/[$N]bool) -> (out: A) {
for e, i in x {
out[i] = !e
}
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index c11151b25..b58305c63 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -38,23 +38,28 @@ DEG_PER_RAD :: 360.0/TAU
@private ELEM_TYPE :: intrinsics.type_elem_type
-scalar_dot :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
+@(require_results)
+scalar_dot :: proc "contextless" (a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b
}
-vector_dot :: proc(a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) #no_bounds_check {
+@(require_results)
+vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) #no_bounds_check {
for i in 0.. (c: f16) {
+@(require_results)
+quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
}
-quaternion128_dot :: proc(a, b: $T/quaternion128) -> (c: f32) {
+@(require_results)
+quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
}
-quaternion256_dot :: proc(a, b: $T/quaternion256) -> (c: f64) {
+@(require_results)
+quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) {
return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
}
@@ -63,27 +68,32 @@ dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quatern
inner_product :: dot
outer_product :: builtin.outer_product
-quaternion_inverse :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_inverse :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0)
}
-scalar_cross :: proc(a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
+@(require_results)
+scalar_cross :: proc "contextless" (a, b: $T) -> T where IS_FLOAT(T), !IS_ARRAY(T) {
return a * b
}
-vector_cross2 :: proc(a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
+@(require_results)
+vector_cross2 :: proc "contextless" (a, b: $T/[2]$E) -> E where IS_NUMERIC(E) {
return a[0]*b[1] - b[0]*a[1]
}
-vector_cross3 :: proc(a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
+@(require_results)
+vector_cross3 :: proc "contextless" (a, b: $T/[3]$E) -> (c: T) where IS_NUMERIC(E) {
c[0] = a[1]*b[2] - b[1]*a[2]
c[1] = a[2]*b[0] - b[2]*a[0]
c[2] = a[0]*b[1] - b[0]*a[1]
return
}
-quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_cross :: proc "contextless" (q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
q3.x = q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y
q3.y = q1.w * q2.y + q1.y * q2.w + q1.z * q2.x - q1.x * q2.z
q3.z = q1.w * q2.z + q1.z * q2.w + q1.x * q2.y - q1.y * q2.x
@@ -94,49 +104,59 @@ quaternion_cross :: proc(q1, q2: $Q) -> (q3: Q) where IS_QUATERNION(Q) {
vector_cross :: proc{scalar_cross, vector_cross2, vector_cross3}
cross :: proc{scalar_cross, vector_cross2, vector_cross3, quaternion_cross}
-vector_normalize :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
+@(require_results)
+vector_normalize :: proc "contextless" (v: $T/[$N]$E) -> T where IS_FLOAT(E) {
return v / length(v)
}
-quaternion_normalize :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_normalize :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
return q/abs(q)
}
normalize :: proc{vector_normalize, quaternion_normalize}
-vector_normalize0 :: proc(v: $T/[$N]$E) -> T where IS_FLOAT(E) {
+@(require_results)
+vector_normalize0 :: proc "contextless" (v: $T/[$N]$E) -> T where IS_FLOAT(E) {
m := length(v)
return 0 if m == 0 else v/m
}
-quaternion_normalize0 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_normalize0 :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
m := abs(q)
return 0 if m == 0 else q/m
}
normalize0 :: proc{vector_normalize0, quaternion_normalize0}
-vector_length :: proc(v: $T/[$N]$E) -> E where IS_FLOAT(E) {
+@(require_results)
+vector_length :: proc "contextless" (v: $T/[$N]$E) -> E where IS_FLOAT(E) {
return math.sqrt(dot(v, v))
}
-vector_length2 :: proc(v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
+@(require_results)
+vector_length2 :: proc "contextless" (v: $T/[$N]$E) -> E where IS_NUMERIC(E) {
return dot(v, v)
}
-quaternion_length :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_length :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
return abs(q)
}
-quaternion_length2 :: proc(q: $Q) -> Q where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_length2 :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
return dot(q, q)
}
-scalar_triple_product :: proc(a, b, c: $T/[$N]$E) -> E where IS_NUMERIC(E) {
+@(require_results)
+scalar_triple_product :: proc "contextless" (a, b, c: $T/[$N]$E) -> E where IS_NUMERIC(E) {
// a . (b x c)
// b . (c x a)
// c . (a x b)
return dot(a, cross(b, c))
}
-vector_triple_product :: proc(a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
+@(require_results)
+vector_triple_product :: proc "contextless" (a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
// a x (b x c)
// (a . c)b - (a . b)c
return cross(a, cross(b, c))
@@ -146,11 +166,13 @@ vector_triple_product :: proc(a, b, c: $T/[$N]$E) -> T where IS_NUMERIC(E) {
length :: proc{vector_length, quaternion_length}
length2 :: proc{vector_length2, quaternion_length2}
-projection :: proc(x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
+@(require_results)
+projection :: proc "contextless" (x, normal: $T/[$N]$E) -> T where IS_NUMERIC(E) {
return dot(x, normal) / dot(normal, normal) * normal
}
-identity :: proc($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
+@(require_results)
+identity :: proc "contextless" ($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
for i in 0.. (m: T) #no_bounds_check {
trace :: builtin.matrix_trace
transpose :: builtin.transpose
-matrix_mul :: proc(a, b: $M/matrix[$N, N]$E) -> (c: M)
+@(require_results)
+matrix_mul :: proc "contextless" (a, b: $M/matrix[$N, N]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
return a * b
}
-matrix_comp_mul :: proc(a, b: $M/matrix[$I, $J]$E) -> (c: M)
+@(require_results)
+matrix_comp_mul :: proc "contextless" (a, b: $M/matrix[$I, $J]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
return hadamard_product(a, b)
}
-matrix_mul_differ :: proc(a: $A/matrix[$I, $J]$E, b: $B/matrix[J, $K]E) -> (c: matrix[I, K]E)
+@(require_results)
+matrix_mul_differ :: proc "contextless" (a: $A/matrix[$I, $J]$E, b: $B/matrix[J, $K]E) -> (c: matrix[I, K]E)
where !IS_ARRAY(E), IS_NUMERIC(E), I != K #no_bounds_check {
return a * b
}
-matrix_mul_vector :: proc(a: $A/matrix[$I, $J]$E, b: $B/[J]E) -> (c: B)
+@(require_results)
+matrix_mul_vector :: proc "contextless" (a: $A/matrix[$I, $J]$E, b: $B/[J]E) -> (c: B)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
return a * b
}
-quaternion_mul_quaternion :: proc(q1, q2: $Q) -> Q where IS_QUATERNION(Q) {
+@(require_results)
+quaternion_mul_quaternion :: proc "contextless" (q1, q2: $Q) -> Q where IS_QUATERNION(Q) {
return q1 * q2
}
-quaternion64_mul_vector3 :: proc(q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
+@(require_results)
+quaternion64_mul_vector3 :: proc "contextless" (q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
Raw_Quaternion :: struct {xyz: [3]f16, r: f16}
q := transmute(Raw_Quaternion)q
@@ -194,7 +222,8 @@ quaternion64_mul_vector3 :: proc(q: $Q/quaternion64, v: $V/[3]$F/f16) -> V {
t := cross(2*q.xyz, v)
return V(v + q.r*t + cross(q.xyz, t))
}
-quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
+@(require_results)
+quaternion128_mul_vector3 :: proc "contextless" (q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
Raw_Quaternion :: struct {xyz: [3]f32, r: f32}
q := transmute(Raw_Quaternion)q
@@ -203,7 +232,8 @@ quaternion128_mul_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V {
t := cross(2*q.xyz, v)
return V(v + q.r*t + cross(q.xyz, t))
}
-quaternion256_mul_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
+@(require_results)
+quaternion256_mul_vector3 :: proc "contextless" (q: $Q/quaternion256, v: $V/[3]$F/f64) -> V {
Raw_Quaternion :: struct {xyz: [3]f64, r: f64}
q := transmute(Raw_Quaternion)q
@@ -224,10 +254,12 @@ mul :: proc{
quaternion_mul_quaternion,
}
-vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
+@(require_results)
+vector_to_ptr :: proc "contextless" (v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
return &v[0]
}
-matrix_to_ptr :: proc(m: ^$A/matrix[$I, $J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
+@(require_results)
+matrix_to_ptr :: proc "contextless" (m: ^$A/matrix[$I, $J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
return &m[0, 0]
}
@@ -239,7 +271,8 @@ to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
// Splines
-vector_slerp :: proc(x, y: $T/[$N]$E, a: E) -> T {
+@(require_results)
+vector_slerp :: proc "contextless" (x, y: $T/[$N]$E, a: E) -> T {
cos_alpha := dot(x, y)
alpha := math.acos(cos_alpha)
sin_alpha := math.sin(alpha)
@@ -250,7 +283,8 @@ vector_slerp :: proc(x, y: $T/[$N]$E, a: E) -> T {
return x * t1 + y * t2
}
-catmull_rom :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
+@(require_results)
+catmull_rom :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
s2 := s*s
s3 := s2*s
@@ -262,7 +296,8 @@ catmull_rom :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
return (f1 * v1 + f2 * v2 + f3 * v3 + f4 * v4) * 0.5
}
-hermite :: proc(v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
+@(require_results)
+hermite :: proc "contextless" (v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
s2 := s*s
s3 := s2*s
@@ -274,20 +309,23 @@ hermite :: proc(v1, t1, v2, t2: $T/[$N]$E, s: E) -> T {
return f1 * v1 + f2 * v2 + f3 * t1 + f4 * t2
}
-cubic :: proc(v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
+@(require_results)
+cubic :: proc "contextless" (v1, v2, v3, v4: $T/[$N]$E, s: E) -> T {
return ((v1 * s + v2) * s + v3) * s + v4
}
-array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) #no_bounds_check {
+@(require_results)
+array_cast :: proc "contextless" (v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) #no_bounds_check {
for i in 0.. (w: matrix[M, N]Elem_Type) #no_bounds_check {
+@(require_results)
+matrix_cast :: proc "contextless" (v: $A/matrix[$M, $N]$T, $Elem_Type: typeid) -> (w: matrix[M, N]Elem_Type) #no_bounds_check {
for j in 0.. (w: matrix[M,
return
}
-to_f32 :: #force_inline proc(v: $A/[$N]$T) -> [N]f32 { return array_cast(v, f32) }
-to_f64 :: #force_inline proc(v: $A/[$N]$T) -> [N]f64 { return array_cast(v, f64) }
+@(require_results) to_f32 :: #force_inline proc(v: $A/[$N]$T) -> [N]f32 { return array_cast(v, f32) }
+@(require_results) to_f64 :: #force_inline proc(v: $A/[$N]$T) -> [N]f64 { return array_cast(v, f64) }
-to_i8 :: #force_inline proc(v: $A/[$N]$T) -> [N]i8 { return array_cast(v, i8) }
-to_i16 :: #force_inline proc(v: $A/[$N]$T) -> [N]i16 { return array_cast(v, i16) }
-to_i32 :: #force_inline proc(v: $A/[$N]$T) -> [N]i32 { return array_cast(v, i32) }
-to_i64 :: #force_inline proc(v: $A/[$N]$T) -> [N]i64 { return array_cast(v, i64) }
-to_int :: #force_inline proc(v: $A/[$N]$T) -> [N]int { return array_cast(v, int) }
+@(require_results) to_i8 :: #force_inline proc(v: $A/[$N]$T) -> [N]i8 { return array_cast(v, i8) }
+@(require_results) to_i16 :: #force_inline proc(v: $A/[$N]$T) -> [N]i16 { return array_cast(v, i16) }
+@(require_results) to_i32 :: #force_inline proc(v: $A/[$N]$T) -> [N]i32 { return array_cast(v, i32) }
+@(require_results) to_i64 :: #force_inline proc(v: $A/[$N]$T) -> [N]i64 { return array_cast(v, i64) }
+@(require_results) to_int :: #force_inline proc(v: $A/[$N]$T) -> [N]int { return array_cast(v, int) }
-to_u8 :: #force_inline proc(v: $A/[$N]$T) -> [N]u8 { return array_cast(v, u8) }
-to_u16 :: #force_inline proc(v: $A/[$N]$T) -> [N]u16 { return array_cast(v, u16) }
-to_u32 :: #force_inline proc(v: $A/[$N]$T) -> [N]u32 { return array_cast(v, u32) }
-to_u64 :: #force_inline proc(v: $A/[$N]$T) -> [N]u64 { return array_cast(v, u64) }
-to_uint :: #force_inline proc(v: $A/[$N]$T) -> [N]uint { return array_cast(v, uint) }
+@(require_results) to_u8 :: #force_inline proc(v: $A/[$N]$T) -> [N]u8 { return array_cast(v, u8) }
+@(require_results) to_u16 :: #force_inline proc(v: $A/[$N]$T) -> [N]u16 { return array_cast(v, u16) }
+@(require_results) to_u32 :: #force_inline proc(v: $A/[$N]$T) -> [N]u32 { return array_cast(v, u32) }
+@(require_results) to_u64 :: #force_inline proc(v: $A/[$N]$T) -> [N]u64 { return array_cast(v, u64) }
+@(require_results) to_uint :: #force_inline proc(v: $A/[$N]$T) -> [N]uint { return array_cast(v, uint) }
-to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32) }
-to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64) }
-to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128) }
-to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64) }
-to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128) }
-to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256) }
+@(require_results) to_complex32 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex32 { return array_cast(v, complex32) }
+@(require_results) to_complex64 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex64 { return array_cast(v, complex64) }
+@(require_results) to_complex128 :: #force_inline proc(v: $A/[$N]$T) -> [N]complex128 { return array_cast(v, complex128) }
+@(require_results) to_quaternion64 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion64 { return array_cast(v, quaternion64) }
+@(require_results) to_quaternion128 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion128 { return array_cast(v, quaternion128) }
+@(require_results) to_quaternion256 :: #force_inline proc(v: $A/[$N]$T) -> [N]quaternion256 { return array_cast(v, quaternion256) }
diff --git a/core/math/linalg/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin
index 74753f66f..0d91ad4a3 100644
--- a/core/math/linalg/glsl/linalg_glsl.odin
+++ b/core/math/linalg/glsl/linalg_glsl.odin
@@ -89,12 +89,12 @@ cos :: proc{
cos_dvec3,
cos_dvec4,
}
-cos_vec2 :: proc "c" (x: vec2) -> vec2 { return {cos(x.x), cos(x.y)} }
-cos_vec3 :: proc "c" (x: vec3) -> vec3 { return {cos(x.x), cos(x.y), cos(x.z)} }
-cos_vec4 :: proc "c" (x: vec4) -> vec4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
-cos_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {cos(x.x), cos(x.y)} }
-cos_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {cos(x.x), cos(x.y), cos(x.z)} }
-cos_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
+@(require_results) cos_vec2 :: proc "c" (x: vec2) -> vec2 { return {cos(x.x), cos(x.y)} }
+@(require_results) cos_vec3 :: proc "c" (x: vec3) -> vec3 { return {cos(x.x), cos(x.y), cos(x.z)} }
+@(require_results) cos_vec4 :: proc "c" (x: vec4) -> vec4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
+@(require_results) cos_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {cos(x.x), cos(x.y)} }
+@(require_results) cos_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {cos(x.x), cos(x.y), cos(x.z)} }
+@(require_results) cos_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
sin :: proc{
sin_f32,
@@ -106,12 +106,12 @@ sin :: proc{
sin_dvec3,
sin_dvec4,
}
-sin_vec2 :: proc "c" (x: vec2) -> vec2 { return {sin(x.x), sin(x.y)} }
-sin_vec3 :: proc "c" (x: vec3) -> vec3 { return {sin(x.x), sin(x.y), sin(x.z)} }
-sin_vec4 :: proc "c" (x: vec4) -> vec4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
-sin_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sin(x.x), sin(x.y)} }
-sin_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sin(x.x), sin(x.y), sin(x.z)} }
-sin_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
+@(require_results) sin_vec2 :: proc "c" (x: vec2) -> vec2 { return {sin(x.x), sin(x.y)} }
+@(require_results) sin_vec3 :: proc "c" (x: vec3) -> vec3 { return {sin(x.x), sin(x.y), sin(x.z)} }
+@(require_results) sin_vec4 :: proc "c" (x: vec4) -> vec4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
+@(require_results) sin_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sin(x.x), sin(x.y)} }
+@(require_results) sin_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sin(x.x), sin(x.y), sin(x.z)} }
+@(require_results) sin_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
tan :: proc{
tan_f32,
@@ -123,12 +123,12 @@ tan :: proc{
tan_dvec3,
tan_dvec4,
}
-tan_vec2 :: proc "c" (x: vec2) -> vec2 { return {tan(x.x), tan(x.y)} }
-tan_vec3 :: proc "c" (x: vec3) -> vec3 { return {tan(x.x), tan(x.y), tan(x.z)} }
-tan_vec4 :: proc "c" (x: vec4) -> vec4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
-tan_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {tan(x.x), tan(x.y)} }
-tan_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {tan(x.x), tan(x.y), tan(x.z)} }
-tan_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
+@(require_results) tan_vec2 :: proc "c" (x: vec2) -> vec2 { return {tan(x.x), tan(x.y)} }
+@(require_results) tan_vec3 :: proc "c" (x: vec3) -> vec3 { return {tan(x.x), tan(x.y), tan(x.z)} }
+@(require_results) tan_vec4 :: proc "c" (x: vec4) -> vec4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
+@(require_results) tan_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {tan(x.x), tan(x.y)} }
+@(require_results) tan_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {tan(x.x), tan(x.y), tan(x.z)} }
+@(require_results) tan_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
acos :: proc{
acos_f32,
@@ -140,12 +140,12 @@ acos :: proc{
acos_dvec3,
acos_dvec4,
}
-acos_vec2 :: proc "c" (x: vec2) -> vec2 { return {acos(x.x), acos(x.y)} }
-acos_vec3 :: proc "c" (x: vec3) -> vec3 { return {acos(x.x), acos(x.y), acos(x.z)} }
-acos_vec4 :: proc "c" (x: vec4) -> vec4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
-acos_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {acos(x.x), acos(x.y)} }
-acos_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {acos(x.x), acos(x.y), acos(x.z)} }
-acos_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
+@(require_results) acos_vec2 :: proc "c" (x: vec2) -> vec2 { return {acos(x.x), acos(x.y)} }
+@(require_results) acos_vec3 :: proc "c" (x: vec3) -> vec3 { return {acos(x.x), acos(x.y), acos(x.z)} }
+@(require_results) acos_vec4 :: proc "c" (x: vec4) -> vec4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
+@(require_results) acos_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {acos(x.x), acos(x.y)} }
+@(require_results) acos_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {acos(x.x), acos(x.y), acos(x.z)} }
+@(require_results) acos_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
asin :: proc{
asin_f32,
@@ -157,12 +157,12 @@ asin :: proc{
asin_dvec3,
asin_dvec4,
}
-asin_vec2 :: proc "c" (x: vec2) -> vec2 { return {asin(x.x), asin(x.y)} }
-asin_vec3 :: proc "c" (x: vec3) -> vec3 { return {asin(x.x), asin(x.y), asin(x.z)} }
-asin_vec4 :: proc "c" (x: vec4) -> vec4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
-asin_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {asin(x.x), asin(x.y)} }
-asin_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {asin(x.x), asin(x.y), asin(x.z)} }
-asin_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
+@(require_results) asin_vec2 :: proc "c" (x: vec2) -> vec2 { return {asin(x.x), asin(x.y)} }
+@(require_results) asin_vec3 :: proc "c" (x: vec3) -> vec3 { return {asin(x.x), asin(x.y), asin(x.z)} }
+@(require_results) asin_vec4 :: proc "c" (x: vec4) -> vec4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
+@(require_results) asin_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {asin(x.x), asin(x.y)} }
+@(require_results) asin_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {asin(x.x), asin(x.y), asin(x.z)} }
+@(require_results) asin_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
atan :: proc{
atan_f32,
@@ -182,12 +182,12 @@ atan :: proc{
atan2_dvec3,
atan2_dvec4,
}
-atan_vec2 :: proc "c" (x: vec2) -> vec2 { return {atan(x.x), atan(x.y)} }
-atan_vec3 :: proc "c" (x: vec3) -> vec3 { return {atan(x.x), atan(x.y), atan(x.z)} }
-atan_vec4 :: proc "c" (x: vec4) -> vec4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
-atan_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {atan(x.x), atan(x.y)} }
-atan_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {atan(x.x), atan(x.y), atan(x.z)} }
-atan_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
+@(require_results) atan_vec2 :: proc "c" (x: vec2) -> vec2 { return {atan(x.x), atan(x.y)} }
+@(require_results) atan_vec3 :: proc "c" (x: vec3) -> vec3 { return {atan(x.x), atan(x.y), atan(x.z)} }
+@(require_results) atan_vec4 :: proc "c" (x: vec4) -> vec4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
+@(require_results) atan_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {atan(x.x), atan(x.y)} }
+@(require_results) atan_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {atan(x.x), atan(x.y), atan(x.z)} }
+@(require_results) atan_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
atan2 :: proc{
atan2_f32,
@@ -199,12 +199,12 @@ atan2 :: proc{
atan2_dvec3,
atan2_dvec4,
}
-atan2_vec2 :: proc "c" (y, x: vec2) -> vec2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
-atan2_vec3 :: proc "c" (y, x: vec3) -> vec3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
-atan2_vec4 :: proc "c" (y, x: vec4) -> vec4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
-atan2_dvec2 :: proc "c" (y, x: dvec2) -> dvec2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
-atan2_dvec3 :: proc "c" (y, x: dvec3) -> dvec3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
-atan2_dvec4 :: proc "c" (y, x: dvec4) -> dvec4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
+@(require_results) atan2_vec2 :: proc "c" (y, x: vec2) -> vec2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
+@(require_results) atan2_vec3 :: proc "c" (y, x: vec3) -> vec3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
+@(require_results) atan2_vec4 :: proc "c" (y, x: vec4) -> vec4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
+@(require_results) atan2_dvec2 :: proc "c" (y, x: dvec2) -> dvec2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
+@(require_results) atan2_dvec3 :: proc "c" (y, x: dvec3) -> dvec3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
+@(require_results) atan2_dvec4 :: proc "c" (y, x: dvec4) -> dvec4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
@@ -218,12 +218,12 @@ cosh :: proc{
cosh_dvec3,
cosh_dvec4,
}
-cosh_vec2 :: proc "c" (x: vec2) -> vec2 { return {cosh(x.x), cosh(x.y)} }
-cosh_vec3 :: proc "c" (x: vec3) -> vec3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
-cosh_vec4 :: proc "c" (x: vec4) -> vec4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
-cosh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {cosh(x.x), cosh(x.y)} }
-cosh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
-cosh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
+@(require_results) cosh_vec2 :: proc "c" (x: vec2) -> vec2 { return {cosh(x.x), cosh(x.y)} }
+@(require_results) cosh_vec3 :: proc "c" (x: vec3) -> vec3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
+@(require_results) cosh_vec4 :: proc "c" (x: vec4) -> vec4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
+@(require_results) cosh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {cosh(x.x), cosh(x.y)} }
+@(require_results) cosh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
+@(require_results) cosh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
sinh :: proc{
@@ -236,12 +236,12 @@ sinh :: proc{
sinh_dvec3,
sinh_dvec4,
}
-sinh_vec2 :: proc "c" (x: vec2) -> vec2 { return {sinh(x.x), sinh(x.y)} }
-sinh_vec3 :: proc "c" (x: vec3) -> vec3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
-sinh_vec4 :: proc "c" (x: vec4) -> vec4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
-sinh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sinh(x.x), sinh(x.y)} }
-sinh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
-sinh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
+@(require_results) sinh_vec2 :: proc "c" (x: vec2) -> vec2 { return {sinh(x.x), sinh(x.y)} }
+@(require_results) sinh_vec3 :: proc "c" (x: vec3) -> vec3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
+@(require_results) sinh_vec4 :: proc "c" (x: vec4) -> vec4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
+@(require_results) sinh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sinh(x.x), sinh(x.y)} }
+@(require_results) sinh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
+@(require_results) sinh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
tanh :: proc{
tanh_f32,
@@ -253,12 +253,12 @@ tanh :: proc{
tanh_dvec3,
tanh_dvec4,
}
-tanh_vec2 :: proc "c" (x: vec2) -> vec2 { return {tanh(x.x), tanh(x.y)} }
-tanh_vec3 :: proc "c" (x: vec3) -> vec3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
-tanh_vec4 :: proc "c" (x: vec4) -> vec4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
-tanh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {tanh(x.x), tanh(x.y)} }
-tanh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
-tanh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
+@(require_results) tanh_vec2 :: proc "c" (x: vec2) -> vec2 { return {tanh(x.x), tanh(x.y)} }
+@(require_results) tanh_vec3 :: proc "c" (x: vec3) -> vec3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
+@(require_results) tanh_vec4 :: proc "c" (x: vec4) -> vec4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
+@(require_results) tanh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {tanh(x.x), tanh(x.y)} }
+@(require_results) tanh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
+@(require_results) tanh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
acosh :: proc{
acosh_f32,
@@ -270,12 +270,12 @@ acosh :: proc{
acosh_dvec3,
acosh_dvec4,
}
-acosh_vec2 :: proc "c" (x: vec2) -> vec2 { return {acosh(x.x), acosh(x.y)} }
-acosh_vec3 :: proc "c" (x: vec3) -> vec3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
-acosh_vec4 :: proc "c" (x: vec4) -> vec4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
-acosh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {acosh(x.x), acosh(x.y)} }
-acosh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
-acosh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
+@(require_results) acosh_vec2 :: proc "c" (x: vec2) -> vec2 { return {acosh(x.x), acosh(x.y)} }
+@(require_results) acosh_vec3 :: proc "c" (x: vec3) -> vec3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
+@(require_results) acosh_vec4 :: proc "c" (x: vec4) -> vec4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
+@(require_results) acosh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {acosh(x.x), acosh(x.y)} }
+@(require_results) acosh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
+@(require_results) acosh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
asinh :: proc{
asinh_f32,
@@ -287,12 +287,12 @@ asinh :: proc{
asinh_dvec3,
asinh_dvec4,
}
-asinh_vec2 :: proc "c" (x: vec2) -> vec2 { return {asinh(x.x), asinh(x.y)} }
-asinh_vec3 :: proc "c" (x: vec3) -> vec3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
-asinh_vec4 :: proc "c" (x: vec4) -> vec4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
-asinh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {asinh(x.x), asinh(x.y)} }
-asinh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
-asinh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
+@(require_results) asinh_vec2 :: proc "c" (x: vec2) -> vec2 { return {asinh(x.x), asinh(x.y)} }
+@(require_results) asinh_vec3 :: proc "c" (x: vec3) -> vec3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
+@(require_results) asinh_vec4 :: proc "c" (x: vec4) -> vec4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
+@(require_results) asinh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {asinh(x.x), asinh(x.y)} }
+@(require_results) asinh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
+@(require_results) asinh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
atanh :: proc{
atanh_f32,
@@ -304,12 +304,12 @@ atanh :: proc{
atanh_dvec3,
atanh_dvec4,
}
-atanh_vec2 :: proc "c" (x: vec2) -> vec2 { return {atanh(x.x), atanh(x.y)} }
-atanh_vec3 :: proc "c" (x: vec3) -> vec3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
-atanh_vec4 :: proc "c" (x: vec4) -> vec4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
-atanh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {atanh(x.x), atanh(x.y)} }
-atanh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
-atanh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
+@(require_results) atanh_vec2 :: proc "c" (x: vec2) -> vec2 { return {atanh(x.x), atanh(x.y)} }
+@(require_results) atanh_vec3 :: proc "c" (x: vec3) -> vec3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
+@(require_results) atanh_vec4 :: proc "c" (x: vec4) -> vec4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
+@(require_results) atanh_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {atanh(x.x), atanh(x.y)} }
+@(require_results) atanh_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
+@(require_results) atanh_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
sqrt :: proc{
sqrt_f32,
@@ -321,12 +321,12 @@ sqrt :: proc{
sqrt_dvec3,
sqrt_dvec4,
}
-sqrt_vec2 :: proc "c" (x: vec2) -> vec2 { return {sqrt(x.x), sqrt(x.y)} }
-sqrt_vec3 :: proc "c" (x: vec3) -> vec3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
-sqrt_vec4 :: proc "c" (x: vec4) -> vec4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
-sqrt_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sqrt(x.x), sqrt(x.y)} }
-sqrt_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
-sqrt_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
+@(require_results) sqrt_vec2 :: proc "c" (x: vec2) -> vec2 { return {sqrt(x.x), sqrt(x.y)} }
+@(require_results) sqrt_vec3 :: proc "c" (x: vec3) -> vec3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
+@(require_results) sqrt_vec4 :: proc "c" (x: vec4) -> vec4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
+@(require_results) sqrt_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sqrt(x.x), sqrt(x.y)} }
+@(require_results) sqrt_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
+@(require_results) sqrt_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
rsqrt :: inversesqrt
inversesqrt :: proc{
@@ -339,12 +339,12 @@ inversesqrt :: proc{
inversesqrt_dvec3,
inversesqrt_dvec4,
}
-inversesqrt_vec2 :: proc "c" (x: vec2) -> vec2 { return {inversesqrt(x.x), inversesqrt(x.y)} }
-inversesqrt_vec3 :: proc "c" (x: vec3) -> vec3 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z)} }
-inversesqrt_vec4 :: proc "c" (x: vec4) -> vec4 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z), inversesqrt(x.w)} }
-inversesqrt_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {inversesqrt(x.x), inversesqrt(x.y)} }
-inversesqrt_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z)} }
-inversesqrt_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z), inversesqrt(x.w)} }
+@(require_results) inversesqrt_vec2 :: proc "c" (x: vec2) -> vec2 { return {inversesqrt(x.x), inversesqrt(x.y)} }
+@(require_results) inversesqrt_vec3 :: proc "c" (x: vec3) -> vec3 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z)} }
+@(require_results) inversesqrt_vec4 :: proc "c" (x: vec4) -> vec4 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z), inversesqrt(x.w)} }
+@(require_results) inversesqrt_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {inversesqrt(x.x), inversesqrt(x.y)} }
+@(require_results) inversesqrt_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z)} }
+@(require_results) inversesqrt_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {inversesqrt(x.x), inversesqrt(x.y), inversesqrt(x.z), inversesqrt(x.w)} }
pow :: proc{
@@ -357,12 +357,12 @@ pow :: proc{
pow_dvec3,
pow_dvec4,
}
-pow_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
-pow_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
-pow_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
-pow_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
-pow_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
-pow_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
+@(require_results) pow_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
+@(require_results) pow_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
+@(require_results) pow_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
+@(require_results) pow_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
+@(require_results) pow_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
+@(require_results) pow_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
@@ -376,12 +376,12 @@ exp :: proc{
exp_dvec3,
exp_dvec4,
}
-exp_vec2 :: proc "c" (x: vec2) -> vec2 { return {exp(x.x), exp(x.y)} }
-exp_vec3 :: proc "c" (x: vec3) -> vec3 { return {exp(x.x), exp(x.y), exp(x.z)} }
-exp_vec4 :: proc "c" (x: vec4) -> vec4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
-exp_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {exp(x.x), exp(x.y)} }
-exp_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {exp(x.x), exp(x.y), exp(x.z)} }
-exp_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
+@(require_results) exp_vec2 :: proc "c" (x: vec2) -> vec2 { return {exp(x.x), exp(x.y)} }
+@(require_results) exp_vec3 :: proc "c" (x: vec3) -> vec3 { return {exp(x.x), exp(x.y), exp(x.z)} }
+@(require_results) exp_vec4 :: proc "c" (x: vec4) -> vec4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
+@(require_results) exp_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {exp(x.x), exp(x.y)} }
+@(require_results) exp_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {exp(x.x), exp(x.y), exp(x.z)} }
+@(require_results) exp_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
@@ -395,12 +395,12 @@ log :: proc{
log_dvec3,
log_dvec4,
}
-log_vec2 :: proc "c" (x: vec2) -> vec2 { return {log(x.x), log(x.y)} }
-log_vec3 :: proc "c" (x: vec3) -> vec3 { return {log(x.x), log(x.y), log(x.z)} }
-log_vec4 :: proc "c" (x: vec4) -> vec4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
-log_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {log(x.x), log(x.y)} }
-log_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {log(x.x), log(x.y), log(x.z)} }
-log_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
+@(require_results) log_vec2 :: proc "c" (x: vec2) -> vec2 { return {log(x.x), log(x.y)} }
+@(require_results) log_vec3 :: proc "c" (x: vec3) -> vec3 { return {log(x.x), log(x.y), log(x.z)} }
+@(require_results) log_vec4 :: proc "c" (x: vec4) -> vec4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
+@(require_results) log_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {log(x.x), log(x.y)} }
+@(require_results) log_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {log(x.x), log(x.y), log(x.z)} }
+@(require_results) log_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
@@ -414,12 +414,12 @@ exp2 :: proc{
exp2_dvec3,
exp2_dvec4,
}
-exp2_vec2 :: proc "c" (x: vec2) -> vec2 { return {exp2(x.x), exp2(x.y)} }
-exp2_vec3 :: proc "c" (x: vec3) -> vec3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
-exp2_vec4 :: proc "c" (x: vec4) -> vec4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
-exp2_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {exp2(x.x), exp2(x.y)} }
-exp2_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
-exp2_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
+@(require_results) exp2_vec2 :: proc "c" (x: vec2) -> vec2 { return {exp2(x.x), exp2(x.y)} }
+@(require_results) exp2_vec3 :: proc "c" (x: vec3) -> vec3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
+@(require_results) exp2_vec4 :: proc "c" (x: vec4) -> vec4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
+@(require_results) exp2_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {exp2(x.x), exp2(x.y)} }
+@(require_results) exp2_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
+@(require_results) exp2_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
sign :: proc{
@@ -440,20 +440,20 @@ sign :: proc{
sign_uvec3,
sign_uvec4,
}
-sign_i32 :: proc "c" (x: i32) -> i32 { return -1 if x < 0 else +1 if x > 0 else 0 }
-sign_u32 :: proc "c" (x: u32) -> u32 { return +1 if x > 0 else 0 }
-sign_vec2 :: proc "c" (x: vec2) -> vec2 { return {sign(x.x), sign(x.y)} }
-sign_vec3 :: proc "c" (x: vec3) -> vec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_vec4 :: proc "c" (x: vec4) -> vec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
-sign_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sign(x.x), sign(x.y)} }
-sign_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
-sign_ivec2 :: proc "c" (x: ivec2) -> ivec2 { return {sign(x.x), sign(x.y)} }
-sign_ivec3 :: proc "c" (x: ivec3) -> ivec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_ivec4 :: proc "c" (x: ivec4) -> ivec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
-sign_uvec2 :: proc "c" (x: uvec2) -> uvec2 { return {sign(x.x), sign(x.y)} }
-sign_uvec3 :: proc "c" (x: uvec3) -> uvec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_uvec4 :: proc "c" (x: uvec4) -> uvec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_i32 :: proc "c" (x: i32) -> i32 { return -1 if x < 0 else +1 if x > 0 else 0 }
+@(require_results) sign_u32 :: proc "c" (x: u32) -> u32 { return +1 if x > 0 else 0 }
+@(require_results) sign_vec2 :: proc "c" (x: vec2) -> vec2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_vec3 :: proc "c" (x: vec3) -> vec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_vec4 :: proc "c" (x: vec4) -> vec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_ivec2 :: proc "c" (x: ivec2) -> ivec2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_ivec3 :: proc "c" (x: ivec3) -> ivec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_ivec4 :: proc "c" (x: ivec4) -> ivec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_uvec2 :: proc "c" (x: uvec2) -> uvec2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_uvec3 :: proc "c" (x: uvec3) -> uvec3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_uvec4 :: proc "c" (x: uvec4) -> uvec4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
floor :: proc{
floor_f32,
@@ -465,12 +465,12 @@ floor :: proc{
floor_dvec3,
floor_dvec4,
}
-floor_vec2 :: proc "c" (x: vec2) -> vec2 { return {floor(x.x), floor(x.y)} }
-floor_vec3 :: proc "c" (x: vec3) -> vec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
-floor_vec4 :: proc "c" (x: vec4) -> vec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
-floor_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {floor(x.x), floor(x.y)} }
-floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
-floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+@(require_results) floor_vec2 :: proc "c" (x: vec2) -> vec2 { return {floor(x.x), floor(x.y)} }
+@(require_results) floor_vec3 :: proc "c" (x: vec3) -> vec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
+@(require_results) floor_vec4 :: proc "c" (x: vec4) -> vec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+@(require_results) floor_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {floor(x.x), floor(x.y)} }
+@(require_results) floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), floor(x.z)} }
+@(require_results) floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
@@ -484,12 +484,12 @@ round :: proc{
round_dvec3,
round_dvec4,
}
-round_vec2 :: proc "c" (x: vec2) -> vec2 { return {round(x.x), round(x.y)} }
-round_vec3 :: proc "c" (x: vec3) -> vec3 { return {round(x.x), round(x.y), round(x.z)} }
-round_vec4 :: proc "c" (x: vec4) -> vec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
-round_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {round(x.x), round(x.y)} }
-round_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {round(x.x), round(x.y), round(x.z)} }
-round_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+@(require_results) round_vec2 :: proc "c" (x: vec2) -> vec2 { return {round(x.x), round(x.y)} }
+@(require_results) round_vec3 :: proc "c" (x: vec3) -> vec3 { return {round(x.x), round(x.y), round(x.z)} }
+@(require_results) round_vec4 :: proc "c" (x: vec4) -> vec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+@(require_results) round_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {round(x.x), round(x.y)} }
+@(require_results) round_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {round(x.x), round(x.y), round(x.z)} }
+@(require_results) round_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
ceil :: proc{
@@ -502,12 +502,12 @@ ceil :: proc{
ceil_dvec3,
ceil_dvec4,
}
-ceil_vec2 :: proc "c" (x: vec2) -> vec2 { return {ceil(x.x), ceil(x.y)} }
-ceil_vec3 :: proc "c" (x: vec3) -> vec3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
-ceil_vec4 :: proc "c" (x: vec4) -> vec4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
-ceil_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {ceil(x.x), ceil(x.y)} }
-ceil_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
-ceil_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+@(require_results) ceil_vec2 :: proc "c" (x: vec2) -> vec2 { return {ceil(x.x), ceil(x.y)} }
+@(require_results) ceil_vec3 :: proc "c" (x: vec3) -> vec3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
+@(require_results) ceil_vec4 :: proc "c" (x: vec4) -> vec4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+@(require_results) ceil_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {ceil(x.x), ceil(x.y)} }
+@(require_results) ceil_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
+@(require_results) ceil_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
mod :: proc{
@@ -520,12 +520,12 @@ mod :: proc{
mod_dvec3,
mod_dvec4,
}
-mod_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {mod(x.x, y.x), mod(x.y, y.y)} }
-mod_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z)} }
-mod_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z), mod(x.w, y.w)} }
-mod_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {mod(x.x, y.x), mod(x.y, y.y)} }
-mod_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z)} }
-mod_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z), mod(x.w, y.w)} }
+@(require_results) mod_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {mod(x.x, y.x), mod(x.y, y.y)} }
+@(require_results) mod_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z)} }
+@(require_results) mod_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z), mod(x.w, y.w)} }
+@(require_results) mod_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {mod(x.x, y.x), mod(x.y, y.y)} }
+@(require_results) mod_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z)} }
+@(require_results) mod_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {mod(x.x, y.x), mod(x.y, y.y), mod(x.z, y.z), mod(x.w, y.w)} }
fract :: proc{
@@ -538,12 +538,12 @@ fract :: proc{
fract_dvec3,
fract_dvec4,
}
-fract_vec2 :: proc "c" (x: vec2) -> vec2 { return {fract(x.x), fract(x.y)} }
-fract_vec3 :: proc "c" (x: vec3) -> vec3 { return {fract(x.x), fract(x.y), fract(x.z)} }
-fract_vec4 :: proc "c" (x: vec4) -> vec4 { return {fract(x.x), fract(x.y), fract(x.z), fract(x.w)} }
-fract_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {fract(x.x), fract(x.y)} }
-fract_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {fract(x.x), fract(x.y), fract(x.z)} }
-fract_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {fract(x.x), fract(x.y), fract(x.z), fract(x.w)} }
+@(require_results) fract_vec2 :: proc "c" (x: vec2) -> vec2 { return {fract(x.x), fract(x.y)} }
+@(require_results) fract_vec3 :: proc "c" (x: vec3) -> vec3 { return {fract(x.x), fract(x.y), fract(x.z)} }
+@(require_results) fract_vec4 :: proc "c" (x: vec4) -> vec4 { return {fract(x.x), fract(x.y), fract(x.z), fract(x.w)} }
+@(require_results) fract_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {fract(x.x), fract(x.y)} }
+@(require_results) fract_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {fract(x.x), fract(x.y), fract(x.z)} }
+@(require_results) fract_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {fract(x.x), fract(x.y), fract(x.z), fract(x.w)} }
@@ -557,14 +557,14 @@ radians :: proc{
radians_dvec3,
radians_dvec4,
}
-radians_f32 :: proc "c" (degrees: f32) -> f32 { return degrees * TAU / 360.0 }
-radians_f64 :: proc "c" (degrees: f64) -> f64 { return degrees * TAU / 360.0 }
-radians_vec2 :: proc "c" (degrees: vec2) -> vec2 { return degrees * TAU / 360.0 }
-radians_vec3 :: proc "c" (degrees: vec3) -> vec3 { return degrees * TAU / 360.0 }
-radians_vec4 :: proc "c" (degrees: vec4) -> vec4 { return degrees * TAU / 360.0 }
-radians_dvec2 :: proc "c" (degrees: dvec2) -> dvec2 { return degrees * TAU / 360.0 }
-radians_dvec3 :: proc "c" (degrees: dvec3) -> dvec3 { return degrees * TAU / 360.0 }
-radians_dvec4 :: proc "c" (degrees: dvec4) -> dvec4 { return degrees * TAU / 360.0 }
+@(require_results) radians_f32 :: proc "c" (degrees: f32) -> f32 { return degrees * TAU / 360.0 }
+@(require_results) radians_f64 :: proc "c" (degrees: f64) -> f64 { return degrees * TAU / 360.0 }
+@(require_results) radians_vec2 :: proc "c" (degrees: vec2) -> vec2 { return degrees * TAU / 360.0 }
+@(require_results) radians_vec3 :: proc "c" (degrees: vec3) -> vec3 { return degrees * TAU / 360.0 }
+@(require_results) radians_vec4 :: proc "c" (degrees: vec4) -> vec4 { return degrees * TAU / 360.0 }
+@(require_results) radians_dvec2 :: proc "c" (degrees: dvec2) -> dvec2 { return degrees * TAU / 360.0 }
+@(require_results) radians_dvec3 :: proc "c" (degrees: dvec3) -> dvec3 { return degrees * TAU / 360.0 }
+@(require_results) radians_dvec4 :: proc "c" (degrees: dvec4) -> dvec4 { return degrees * TAU / 360.0 }
degrees :: proc{
@@ -577,14 +577,14 @@ degrees :: proc{
degrees_dvec3,
degrees_dvec4,
}
-degrees_f32 :: proc "c" (radians: f32) -> f32 { return radians * 360.0 / TAU }
-degrees_f64 :: proc "c" (radians: f64) -> f64 { return radians * 360.0 / TAU }
-degrees_vec2 :: proc "c" (radians: vec2) -> vec2 { return radians * 360.0 / TAU }
-degrees_vec3 :: proc "c" (radians: vec3) -> vec3 { return radians * 360.0 / TAU }
-degrees_vec4 :: proc "c" (radians: vec4) -> vec4 { return radians * 360.0 / TAU }
-degrees_dvec2 :: proc "c" (radians: dvec2) -> dvec2 { return radians * 360.0 / TAU }
-degrees_dvec3 :: proc "c" (radians: dvec3) -> dvec3 { return radians * 360.0 / TAU }
-degrees_dvec4 :: proc "c" (radians: dvec4) -> dvec4 { return radians * 360.0 / TAU }
+@(require_results) degrees_f32 :: proc "c" (radians: f32) -> f32 { return radians * 360.0 / TAU }
+@(require_results) degrees_f64 :: proc "c" (radians: f64) -> f64 { return radians * 360.0 / TAU }
+@(require_results) degrees_vec2 :: proc "c" (radians: vec2) -> vec2 { return radians * 360.0 / TAU }
+@(require_results) degrees_vec3 :: proc "c" (radians: vec3) -> vec3 { return radians * 360.0 / TAU }
+@(require_results) degrees_vec4 :: proc "c" (radians: vec4) -> vec4 { return radians * 360.0 / TAU }
+@(require_results) degrees_dvec2 :: proc "c" (radians: dvec2) -> dvec2 { return radians * 360.0 / TAU }
+@(require_results) degrees_dvec3 :: proc "c" (radians: dvec3) -> dvec3 { return radians * 360.0 / TAU }
+@(require_results) degrees_dvec4 :: proc "c" (radians: dvec4) -> dvec4 { return radians * 360.0 / TAU }
min :: proc{
min_i32,
@@ -604,22 +604,22 @@ min :: proc{
min_uvec3,
min_uvec4,
}
-min_i32 :: proc "c" (x, y: i32) -> i32 { return builtin.min(x, y) }
-min_u32 :: proc "c" (x, y: u32) -> u32 { return builtin.min(x, y) }
-min_f32 :: proc "c" (x, y: f32) -> f32 { return builtin.min(x, y) }
-min_f64 :: proc "c" (x, y: f64) -> f64 { return builtin.min(x, y) }
-min_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
-min_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
-min_ivec2 :: proc "c" (x, y: ivec2) -> ivec2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_ivec3 :: proc "c" (x, y: ivec3) -> ivec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_ivec4 :: proc "c" (x, y: ivec4) -> ivec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
-min_uvec2 :: proc "c" (x, y: uvec2) -> uvec2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_uvec3 :: proc "c" (x, y: uvec3) -> uvec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_uvec4 :: proc "c" (x, y: uvec4) -> uvec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_i32 :: proc "c" (x, y: i32) -> i32 { return builtin.min(x, y) }
+@(require_results) min_u32 :: proc "c" (x, y: u32) -> u32 { return builtin.min(x, y) }
+@(require_results) min_f32 :: proc "c" (x, y: f32) -> f32 { return builtin.min(x, y) }
+@(require_results) min_f64 :: proc "c" (x, y: f64) -> f64 { return builtin.min(x, y) }
+@(require_results) min_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_ivec2 :: proc "c" (x, y: ivec2) -> ivec2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_ivec3 :: proc "c" (x, y: ivec3) -> ivec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_ivec4 :: proc "c" (x, y: ivec4) -> ivec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_uvec2 :: proc "c" (x, y: uvec2) -> uvec2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_uvec3 :: proc "c" (x, y: uvec3) -> uvec3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_uvec4 :: proc "c" (x, y: uvec4) -> uvec4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
max :: proc{
@@ -640,22 +640,22 @@ max :: proc{
max_uvec3,
max_uvec4,
}
-max_i32 :: proc "c" (x, y: i32) -> i32 { return builtin.max(x, y) }
-max_u32 :: proc "c" (x, y: u32) -> u32 { return builtin.max(x, y) }
-max_f32 :: proc "c" (x, y: f32) -> f32 { return builtin.max(x, y) }
-max_f64 :: proc "c" (x, y: f64) -> f64 { return builtin.max(x, y) }
-max_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
-max_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
-max_ivec2 :: proc "c" (x, y: ivec2) -> ivec2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_ivec3 :: proc "c" (x, y: ivec3) -> ivec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_ivec4 :: proc "c" (x, y: ivec4) -> ivec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
-max_uvec2 :: proc "c" (x, y: uvec2) -> uvec2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_uvec3 :: proc "c" (x, y: uvec3) -> uvec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_uvec4 :: proc "c" (x, y: uvec4) -> uvec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_i32 :: proc "c" (x, y: i32) -> i32 { return builtin.max(x, y) }
+@(require_results) max_u32 :: proc "c" (x, y: u32) -> u32 { return builtin.max(x, y) }
+@(require_results) max_f32 :: proc "c" (x, y: f32) -> f32 { return builtin.max(x, y) }
+@(require_results) max_f64 :: proc "c" (x, y: f64) -> f64 { return builtin.max(x, y) }
+@(require_results) max_vec2 :: proc "c" (x, y: vec2) -> vec2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_vec3 :: proc "c" (x, y: vec3) -> vec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_vec4 :: proc "c" (x, y: vec4) -> vec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_dvec2 :: proc "c" (x, y: dvec2) -> dvec2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_dvec3 :: proc "c" (x, y: dvec3) -> dvec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_dvec4 :: proc "c" (x, y: dvec4) -> dvec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_ivec2 :: proc "c" (x, y: ivec2) -> ivec2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_ivec3 :: proc "c" (x, y: ivec3) -> ivec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_ivec4 :: proc "c" (x, y: ivec4) -> ivec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_uvec2 :: proc "c" (x, y: uvec2) -> uvec2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_uvec3 :: proc "c" (x, y: uvec3) -> uvec3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_uvec4 :: proc "c" (x, y: uvec4) -> uvec4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
@@ -677,22 +677,22 @@ clamp :: proc{
clamp_uvec3,
clamp_uvec4,
}
-clamp_i32 :: proc "c" (x, y, z: i32) -> i32 { return builtin.clamp(x, y, z) }
-clamp_u32 :: proc "c" (x, y, z: u32) -> u32 { return builtin.clamp(x, y, z) }
-clamp_f32 :: proc "c" (x, y, z: f32) -> f32 { return builtin.clamp(x, y, z) }
-clamp_f64 :: proc "c" (x, y, z: f64) -> f64 { return builtin.clamp(x, y, z) }
-clamp_vec2 :: proc "c" (x, y, z: vec2) -> vec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_vec3 :: proc "c" (x, y, z: vec3) -> vec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_vec4 :: proc "c" (x, y, z: vec4) -> vec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
-clamp_dvec2 :: proc "c" (x, y, z: dvec2) -> dvec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_dvec3 :: proc "c" (x, y, z: dvec3) -> dvec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_dvec4 :: proc "c" (x, y, z: dvec4) -> dvec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
-clamp_ivec2 :: proc "c" (x, y, z: ivec2) -> ivec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_ivec3 :: proc "c" (x, y, z: ivec3) -> ivec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_ivec4 :: proc "c" (x, y, z: ivec4) -> ivec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
-clamp_uvec2 :: proc "c" (x, y, z: uvec2) -> uvec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_uvec3 :: proc "c" (x, y, z: uvec3) -> uvec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_uvec4 :: proc "c" (x, y, z: uvec4) -> uvec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_i32 :: proc "c" (x, y, z: i32) -> i32 { return builtin.clamp(x, y, z) }
+@(require_results) clamp_u32 :: proc "c" (x, y, z: u32) -> u32 { return builtin.clamp(x, y, z) }
+@(require_results) clamp_f32 :: proc "c" (x, y, z: f32) -> f32 { return builtin.clamp(x, y, z) }
+@(require_results) clamp_f64 :: proc "c" (x, y, z: f64) -> f64 { return builtin.clamp(x, y, z) }
+@(require_results) clamp_vec2 :: proc "c" (x, y, z: vec2) -> vec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_vec3 :: proc "c" (x, y, z: vec3) -> vec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_vec4 :: proc "c" (x, y, z: vec4) -> vec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_dvec2 :: proc "c" (x, y, z: dvec2) -> dvec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_dvec3 :: proc "c" (x, y, z: dvec3) -> dvec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_dvec4 :: proc "c" (x, y, z: dvec4) -> dvec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_ivec2 :: proc "c" (x, y, z: ivec2) -> ivec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_ivec3 :: proc "c" (x, y, z: ivec3) -> ivec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_ivec4 :: proc "c" (x, y, z: ivec4) -> ivec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_uvec2 :: proc "c" (x, y, z: uvec2) -> uvec2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_uvec3 :: proc "c" (x, y, z: uvec3) -> uvec3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_uvec4 :: proc "c" (x, y, z: uvec4) -> uvec4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
saturate :: proc{
saturate_i32,
@@ -712,22 +712,22 @@ saturate :: proc{
saturate_uvec3,
saturate_uvec4,
}
-saturate_i32 :: proc "c" (v: i32) -> i32 { return builtin.clamp(v, 0, 1) }
-saturate_u32 :: proc "c" (v: u32) -> u32 { return builtin.clamp(v, 0, 1) }
-saturate_f32 :: proc "c" (v: f32) -> f32 { return builtin.clamp(v, 0, 1) }
-saturate_f64 :: proc "c" (v: f64) -> f64 { return builtin.clamp(v, 0, 1) }
-saturate_vec2 :: proc "c" (v: vec2) -> vec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_vec3 :: proc "c" (v: vec3) -> vec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_vec4 :: proc "c" (v: vec4) -> vec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
-saturate_dvec2 :: proc "c" (v: dvec2) -> dvec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_dvec3 :: proc "c" (v: dvec3) -> dvec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_dvec4 :: proc "c" (v: dvec4) -> dvec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
-saturate_ivec2 :: proc "c" (v: ivec2) -> ivec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_ivec3 :: proc "c" (v: ivec3) -> ivec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_ivec4 :: proc "c" (v: ivec4) -> ivec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
-saturate_uvec2 :: proc "c" (v: uvec2) -> uvec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_uvec3 :: proc "c" (v: uvec3) -> uvec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_uvec4 :: proc "c" (v: uvec4) -> uvec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_i32 :: proc "c" (v: i32) -> i32 { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_u32 :: proc "c" (v: u32) -> u32 { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_f32 :: proc "c" (v: f32) -> f32 { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_f64 :: proc "c" (v: f64) -> f64 { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_vec2 :: proc "c" (v: vec2) -> vec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_vec3 :: proc "c" (v: vec3) -> vec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_vec4 :: proc "c" (v: vec4) -> vec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_dvec2 :: proc "c" (v: dvec2) -> dvec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_dvec3 :: proc "c" (v: dvec3) -> dvec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_dvec4 :: proc "c" (v: dvec4) -> dvec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_ivec2 :: proc "c" (v: ivec2) -> ivec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_ivec3 :: proc "c" (v: ivec3) -> ivec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_ivec4 :: proc "c" (v: ivec4) -> ivec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_uvec2 :: proc "c" (v: uvec2) -> uvec2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_uvec3 :: proc "c" (v: uvec3) -> uvec3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_uvec4 :: proc "c" (v: uvec4) -> uvec4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
mix :: proc{
mix_f32,
@@ -739,14 +739,14 @@ mix :: proc{
mix_dvec3,
mix_dvec4,
}
-mix_f32 :: proc "c" (x, y, t: f32) -> f32 { return x*(1-t) + y*t }
-mix_f64 :: proc "c" (x, y, t: f64) -> f64 { return x*(1-t) + y*t }
-mix_vec2 :: proc "c" (x, y, t: vec2) -> vec2 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y)} }
-mix_vec3 :: proc "c" (x, y, t: vec3) -> vec3 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y), mix(x.z, y.z, t.z)} }
-mix_vec4 :: proc "c" (x, y, t: vec4) -> vec4 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, y.y), mix(x.z, y.z, t.z), mix(x.w, y.w, t.w)} }
-mix_dvec2 :: proc "c" (x, y, t: dvec2) -> dvec2 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y)} }
-mix_dvec3 :: proc "c" (x, y, t: dvec3) -> dvec3 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y), mix(x.z, y.z, t.z)} }
-mix_dvec4 :: proc "c" (x, y, t: dvec4) -> dvec4 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, y.y), mix(x.z, y.z, t.z), mix(x.w, y.w, t.w)} }
+@(require_results) mix_f32 :: proc "c" (x, y, t: f32) -> f32 { return x*(1-t) + y*t }
+@(require_results) mix_f64 :: proc "c" (x, y, t: f64) -> f64 { return x*(1-t) + y*t }
+@(require_results) mix_vec2 :: proc "c" (x, y, t: vec2) -> vec2 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y)} }
+@(require_results) mix_vec3 :: proc "c" (x, y, t: vec3) -> vec3 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y), mix(x.z, y.z, t.z)} }
+@(require_results) mix_vec4 :: proc "c" (x, y, t: vec4) -> vec4 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, y.y), mix(x.z, y.z, t.z), mix(x.w, y.w, t.w)} }
+@(require_results) mix_dvec2 :: proc "c" (x, y, t: dvec2) -> dvec2 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y)} }
+@(require_results) mix_dvec3 :: proc "c" (x, y, t: dvec3) -> dvec3 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, t.y), mix(x.z, y.z, t.z)} }
+@(require_results) mix_dvec4 :: proc "c" (x, y, t: dvec4) -> dvec4 { return {mix(x.x, y.x, t.x), mix(x.y, y.y, y.y), mix(x.z, y.z, t.z), mix(x.w, y.w, t.w)} }
lerp :: proc{
lerp_f32,
@@ -758,14 +758,14 @@ lerp :: proc{
lerp_dvec3,
lerp_dvec4,
}
-lerp_f32 :: proc "c" (x, y, t: f32) -> f32 { return x*(1-t) + y*t }
-lerp_f64 :: proc "c" (x, y, t: f64) -> f64 { return x*(1-t) + y*t }
-lerp_vec2 :: proc "c" (x, y, t: vec2) -> vec2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
-lerp_vec3 :: proc "c" (x, y, t: vec3) -> vec3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
-lerp_vec4 :: proc "c" (x, y, t: vec4) -> vec4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
-lerp_dvec2 :: proc "c" (x, y, t: dvec2) -> dvec2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
-lerp_dvec3 :: proc "c" (x, y, t: dvec3) -> dvec3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
-lerp_dvec4 :: proc "c" (x, y, t: dvec4) -> dvec4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
+@(require_results) lerp_f32 :: proc "c" (x, y, t: f32) -> f32 { return x*(1-t) + y*t }
+@(require_results) lerp_f64 :: proc "c" (x, y, t: f64) -> f64 { return x*(1-t) + y*t }
+@(require_results) lerp_vec2 :: proc "c" (x, y, t: vec2) -> vec2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
+@(require_results) lerp_vec3 :: proc "c" (x, y, t: vec3) -> vec3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
+@(require_results) lerp_vec4 :: proc "c" (x, y, t: vec4) -> vec4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
+@(require_results) lerp_dvec2 :: proc "c" (x, y, t: dvec2) -> dvec2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
+@(require_results) lerp_dvec3 :: proc "c" (x, y, t: dvec3) -> dvec3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
+@(require_results) lerp_dvec4 :: proc "c" (x, y, t: dvec4) -> dvec4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
step :: proc{
@@ -778,14 +778,14 @@ step :: proc{
step_dvec3,
step_dvec4,
}
-step_f32 :: proc "c" (edge, x: f32) -> f32 { return 0 if x < edge else 1 }
-step_f64 :: proc "c" (edge, x: f64) -> f64 { return 0 if x < edge else 1 }
-step_vec2 :: proc "c" (edge, x: vec2) -> vec2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
-step_vec3 :: proc "c" (edge, x: vec3) -> vec3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
-step_vec4 :: proc "c" (edge, x: vec4) -> vec4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
-step_dvec2 :: proc "c" (edge, x: dvec2) -> dvec2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
-step_dvec3 :: proc "c" (edge, x: dvec3) -> dvec3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
-step_dvec4 :: proc "c" (edge, x: dvec4) -> dvec4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
+@(require_results) step_f32 :: proc "c" (edge, x: f32) -> f32 { return 0 if x < edge else 1 }
+@(require_results) step_f64 :: proc "c" (edge, x: f64) -> f64 { return 0 if x < edge else 1 }
+@(require_results) step_vec2 :: proc "c" (edge, x: vec2) -> vec2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
+@(require_results) step_vec3 :: proc "c" (edge, x: vec3) -> vec3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
+@(require_results) step_vec4 :: proc "c" (edge, x: vec4) -> vec4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
+@(require_results) step_dvec2 :: proc "c" (edge, x: dvec2) -> dvec2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
+@(require_results) step_dvec3 :: proc "c" (edge, x: dvec3) -> dvec3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
+@(require_results) step_dvec4 :: proc "c" (edge, x: dvec4) -> dvec4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
smoothstep :: proc{
smoothstep_f32,
@@ -797,20 +797,20 @@ smoothstep :: proc{
smoothstep_dvec3,
smoothstep_dvec4,
}
-smoothstep_f32 :: proc "c" (edge0, edge1, x: f32) -> f32 {
+@(require_results) smoothstep_f32 :: proc "c" (edge0, edge1, x: f32) -> f32 {
y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
return y * y * (3 - 2*y)
}
-smoothstep_f64 :: proc "c" (edge0, edge1, x: f64) -> f64 {
+@(require_results) smoothstep_f64 :: proc "c" (edge0, edge1, x: f64) -> f64 {
y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
return y * y * (3 - 2*y)
}
-smoothstep_vec2 :: proc "c" (edge0, edge1, x: vec2) -> vec2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
-smoothstep_vec3 :: proc "c" (edge0, edge1, x: vec3) -> vec3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
-smoothstep_vec4 :: proc "c" (edge0, edge1, x: vec4) -> vec4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
-smoothstep_dvec2 :: proc "c" (edge0, edge1, x: dvec2) -> dvec2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
-smoothstep_dvec3 :: proc "c" (edge0, edge1, x: dvec3) -> dvec3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
-smoothstep_dvec4 :: proc "c" (edge0, edge1, x: dvec4) -> dvec4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+@(require_results) smoothstep_vec2 :: proc "c" (edge0, edge1, x: vec2) -> vec2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+@(require_results) smoothstep_vec3 :: proc "c" (edge0, edge1, x: vec3) -> vec3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+@(require_results) smoothstep_vec4 :: proc "c" (edge0, edge1, x: vec4) -> vec4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+@(require_results) smoothstep_dvec2 :: proc "c" (edge0, edge1, x: dvec2) -> dvec2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+@(require_results) smoothstep_dvec3 :: proc "c" (edge0, edge1, x: dvec3) -> dvec3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+@(require_results) smoothstep_dvec4 :: proc "c" (edge0, edge1, x: dvec4) -> dvec4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
abs :: proc{
@@ -831,22 +831,22 @@ abs :: proc{
abs_uvec3,
abs_uvec4,
}
-abs_i32 :: proc "c" (x: i32) -> i32 { return builtin.abs(x) }
-abs_u32 :: proc "c" (x: u32) -> u32 { return x }
-abs_f32 :: proc "c" (x: f32) -> f32 { return builtin.abs(x) }
-abs_f64 :: proc "c" (x: f64) -> f64 { return builtin.abs(x) }
-abs_vec2 :: proc "c" (x: vec2) -> vec2 { return {abs(x.x), abs(x.y)} }
-abs_vec3 :: proc "c" (x: vec3) -> vec3 { return {abs(x.x), abs(x.y), abs(x.z)} }
-abs_vec4 :: proc "c" (x: vec4) -> vec4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
-abs_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {abs(x.x), abs(x.y)} }
-abs_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {abs(x.x), abs(x.y), abs(x.z)} }
-abs_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
-abs_ivec2 :: proc "c" (x: ivec2) -> ivec2 { return {abs(x.x), abs(x.y)} }
-abs_ivec3 :: proc "c" (x: ivec3) -> ivec3 { return {abs(x.x), abs(x.y), abs(x.z)} }
-abs_ivec4 :: proc "c" (x: ivec4) -> ivec4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
-abs_uvec2 :: proc "c" (x: uvec2) -> uvec2 { return x }
-abs_uvec3 :: proc "c" (x: uvec3) -> uvec3 { return x }
-abs_uvec4 :: proc "c" (x: uvec4) -> uvec4 { return x }
+@(require_results) abs_i32 :: proc "c" (x: i32) -> i32 { return builtin.abs(x) }
+@(require_results) abs_u32 :: proc "c" (x: u32) -> u32 { return x }
+@(require_results) abs_f32 :: proc "c" (x: f32) -> f32 { return builtin.abs(x) }
+@(require_results) abs_f64 :: proc "c" (x: f64) -> f64 { return builtin.abs(x) }
+@(require_results) abs_vec2 :: proc "c" (x: vec2) -> vec2 { return {abs(x.x), abs(x.y)} }
+@(require_results) abs_vec3 :: proc "c" (x: vec3) -> vec3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+@(require_results) abs_vec4 :: proc "c" (x: vec4) -> vec4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+@(require_results) abs_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return {abs(x.x), abs(x.y)} }
+@(require_results) abs_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+@(require_results) abs_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+@(require_results) abs_ivec2 :: proc "c" (x: ivec2) -> ivec2 { return {abs(x.x), abs(x.y)} }
+@(require_results) abs_ivec3 :: proc "c" (x: ivec3) -> ivec3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+@(require_results) abs_ivec4 :: proc "c" (x: ivec4) -> ivec4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+@(require_results) abs_uvec2 :: proc "c" (x: uvec2) -> uvec2 { return x }
+@(require_results) abs_uvec3 :: proc "c" (x: uvec3) -> uvec3 { return x }
+@(require_results) abs_uvec4 :: proc "c" (x: uvec4) -> uvec4 { return x }
dot :: proc{
dot_i32,
@@ -868,24 +868,24 @@ dot :: proc{
dot_quat,
dot_dquat,
}
-dot_i32 :: proc "c" (a, b: i32) -> i32 { return a*b }
-dot_u32 :: proc "c" (a, b: u32) -> u32 { return a*b }
-dot_f32 :: proc "c" (a, b: f32) -> f32 { return a*b }
-dot_f64 :: proc "c" (a, b: f64) -> f64 { return a*b }
-dot_vec2 :: proc "c" (a, b: vec2) -> f32 { return a.x*b.x + a.y*b.y }
-dot_vec3 :: proc "c" (a, b: vec3) -> f32 { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_vec4 :: proc "c" (a, b: vec4) -> f32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_dvec2 :: proc "c" (a, b: dvec2) -> f64 { return a.x*b.x + a.y*b.y }
-dot_dvec3 :: proc "c" (a, b: dvec3) -> f64 { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_dvec4 :: proc "c" (a, b: dvec4) -> f64 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_ivec2 :: proc "c" (a, b: ivec2) -> i32 { return a.x*b.x + a.y*b.y }
-dot_ivec3 :: proc "c" (a, b: ivec3) -> i32 { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_ivec4 :: proc "c" (a, b: ivec4) -> i32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_uvec2 :: proc "c" (a, b: uvec2) -> u32 { return a.x*b.x + a.y*b.y }
-dot_uvec3 :: proc "c" (a, b: uvec3) -> u32 { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_uvec4 :: proc "c" (a, b: uvec4) -> u32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_quat :: proc "c" (a, b: quat) -> f32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_dquat :: proc "c" (a, b: dquat) -> f64 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_i32 :: proc "c" (a, b: i32) -> i32 { return a*b }
+@(require_results) dot_u32 :: proc "c" (a, b: u32) -> u32 { return a*b }
+@(require_results) dot_f32 :: proc "c" (a, b: f32) -> f32 { return a*b }
+@(require_results) dot_f64 :: proc "c" (a, b: f64) -> f64 { return a*b }
+@(require_results) dot_vec2 :: proc "c" (a, b: vec2) -> f32 { return a.x*b.x + a.y*b.y }
+@(require_results) dot_vec3 :: proc "c" (a, b: vec3) -> f32 { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_vec4 :: proc "c" (a, b: vec4) -> f32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_dvec2 :: proc "c" (a, b: dvec2) -> f64 { return a.x*b.x + a.y*b.y }
+@(require_results) dot_dvec3 :: proc "c" (a, b: dvec3) -> f64 { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_dvec4 :: proc "c" (a, b: dvec4) -> f64 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_ivec2 :: proc "c" (a, b: ivec2) -> i32 { return a.x*b.x + a.y*b.y }
+@(require_results) dot_ivec3 :: proc "c" (a, b: ivec3) -> i32 { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_ivec4 :: proc "c" (a, b: ivec4) -> i32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_uvec2 :: proc "c" (a, b: uvec2) -> u32 { return a.x*b.x + a.y*b.y }
+@(require_results) dot_uvec3 :: proc "c" (a, b: uvec3) -> u32 { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_uvec4 :: proc "c" (a, b: uvec4) -> u32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_quat :: proc "c" (a, b: quat) -> f32 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_dquat :: proc "c" (a, b: dquat) -> f64 { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
length :: proc{
length_f32,
@@ -899,16 +899,16 @@ length :: proc{
length_quat,
length_dquat,
}
-length_f32 :: proc "c" (x: f32) -> f32 { return builtin.abs(x) }
-length_f64 :: proc "c" (x: f64) -> f64 { return builtin.abs(x) }
-length_vec2 :: proc "c" (x: vec2) -> f32 { return sqrt(x.x*x.x + x.y*x.y) }
-length_vec3 :: proc "c" (x: vec3) -> f32 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
-length_vec4 :: proc "c" (x: vec4) -> f32 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
-length_dvec2 :: proc "c" (x: dvec2) -> f64 { return sqrt(x.x*x.x + x.y*x.y) }
-length_dvec3 :: proc "c" (x: dvec3) -> f64 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
-length_dvec4 :: proc "c" (x: dvec4) -> f64 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
-length_quat :: proc "c" (x: quat) -> f32 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
-length_dquat :: proc "c" (x: dquat) -> f64 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+@(require_results) length_f32 :: proc "c" (x: f32) -> f32 { return builtin.abs(x) }
+@(require_results) length_f64 :: proc "c" (x: f64) -> f64 { return builtin.abs(x) }
+@(require_results) length_vec2 :: proc "c" (x: vec2) -> f32 { return sqrt(x.x*x.x + x.y*x.y) }
+@(require_results) length_vec3 :: proc "c" (x: vec3) -> f32 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
+@(require_results) length_vec4 :: proc "c" (x: vec4) -> f32 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+@(require_results) length_dvec2 :: proc "c" (x: dvec2) -> f64 { return sqrt(x.x*x.x + x.y*x.y) }
+@(require_results) length_dvec3 :: proc "c" (x: dvec3) -> f64 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
+@(require_results) length_dvec4 :: proc "c" (x: dvec4) -> f64 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+@(require_results) length_quat :: proc "c" (x: quat) -> f32 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+@(require_results) length_dquat :: proc "c" (x: dquat) -> f64 { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
distance :: proc{
@@ -921,14 +921,14 @@ distance :: proc{
distance_dvec3,
distance_dvec4,
}
-distance_f32 :: proc "c" (x, y: f32) -> f32 { return length(y-x) }
-distance_f64 :: proc "c" (x, y: f64) -> f64 { return length(y-x) }
-distance_vec2 :: proc "c" (x, y: vec2) -> f32 { return length(y-x) }
-distance_vec3 :: proc "c" (x, y: vec3) -> f32 { return length(y-x) }
-distance_vec4 :: proc "c" (x, y: vec4) -> f32 { return length(y-x) }
-distance_dvec2 :: proc "c" (x, y: dvec2) -> f64 { return length(y-x) }
-distance_dvec3 :: proc "c" (x, y: dvec3) -> f64 { return length(y-x) }
-distance_dvec4 :: proc "c" (x, y: dvec4) -> f64 { return length(y-x) }
+@(require_results) distance_f32 :: proc "c" (x, y: f32) -> f32 { return length(y-x) }
+@(require_results) distance_f64 :: proc "c" (x, y: f64) -> f64 { return length(y-x) }
+@(require_results) distance_vec2 :: proc "c" (x, y: vec2) -> f32 { return length(y-x) }
+@(require_results) distance_vec3 :: proc "c" (x, y: vec3) -> f32 { return length(y-x) }
+@(require_results) distance_vec4 :: proc "c" (x, y: vec4) -> f32 { return length(y-x) }
+@(require_results) distance_dvec2 :: proc "c" (x, y: dvec2) -> f64 { return length(y-x) }
+@(require_results) distance_dvec3 :: proc "c" (x, y: dvec3) -> f64 { return length(y-x) }
+@(require_results) distance_dvec4 :: proc "c" (x, y: dvec4) -> f64 { return length(y-x) }
cross :: proc{
@@ -937,19 +937,19 @@ cross :: proc{
cross_ivec3,
}
-cross_vec3 :: proc "c" (a, b: vec3) -> (c: vec3) {
+@(require_results) cross_vec3 :: proc "c" (a, b: vec3) -> (c: vec3) {
c.x = a.y*b.z - b.y*a.z
c.y = a.z*b.x - b.z*a.x
c.z = a.x*b.y - b.x*a.y
return
}
-cross_dvec3 :: proc "c" (a, b: dvec3) -> (c: dvec3) {
+@(require_results) cross_dvec3 :: proc "c" (a, b: dvec3) -> (c: dvec3) {
c.x = a.y*b.z - b.y*a.z
c.y = a.z*b.x - b.z*a.x
c.z = a.x*b.y - b.x*a.y
return
}
-cross_ivec3 :: proc "c" (a, b: ivec3) -> (c: ivec3) {
+@(require_results) cross_ivec3 :: proc "c" (a, b: ivec3) -> (c: ivec3) {
c.x = a.y*b.z - b.y*a.z
c.y = a.z*b.x - b.z*a.x
c.z = a.x*b.y - b.x*a.y
@@ -968,16 +968,16 @@ normalize :: proc{
normalize_quat,
normalize_dquat,
}
-normalize_f32 :: proc "c" (x: f32) -> f32 { return 1.0 }
-normalize_f64 :: proc "c" (x: f64) -> f64 { return 1.0 }
-normalize_vec2 :: proc "c" (x: vec2) -> vec2 { return x / length(x) }
-normalize_vec3 :: proc "c" (x: vec3) -> vec3 { return x / length(x) }
-normalize_vec4 :: proc "c" (x: vec4) -> vec4 { return x / length(x) }
-normalize_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return x / length(x) }
-normalize_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return x / length(x) }
-normalize_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return x / length(x) }
-normalize_quat :: proc "c" (x: quat) -> quat { return x / quat(length(x)) }
-normalize_dquat :: proc "c" (x: dquat) -> dquat { return x / dquat(length(x)) }
+@(require_results) normalize_f32 :: proc "c" (x: f32) -> f32 { return 1.0 }
+@(require_results) normalize_f64 :: proc "c" (x: f64) -> f64 { return 1.0 }
+@(require_results) normalize_vec2 :: proc "c" (x: vec2) -> vec2 { return x / length(x) }
+@(require_results) normalize_vec3 :: proc "c" (x: vec3) -> vec3 { return x / length(x) }
+@(require_results) normalize_vec4 :: proc "c" (x: vec4) -> vec4 { return x / length(x) }
+@(require_results) normalize_dvec2 :: proc "c" (x: dvec2) -> dvec2 { return x / length(x) }
+@(require_results) normalize_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return x / length(x) }
+@(require_results) normalize_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return x / length(x) }
+@(require_results) normalize_quat :: proc "c" (x: quat) -> quat { return x / quat(length(x)) }
+@(require_results) normalize_dquat :: proc "c" (x: dquat) -> dquat { return x / dquat(length(x)) }
faceForward :: proc{
@@ -990,14 +990,14 @@ faceForward :: proc{
faceForward_dvec3,
faceForward_dvec4,
}
-faceForward_f32 :: proc "c" (N, I, Nref: f32) -> f32 { return N if dot(I, Nref) < 0 else -N }
-faceForward_f64 :: proc "c" (N, I, Nref: f64) -> f64 { return N if dot(I, Nref) < 0 else -N }
-faceForward_vec2 :: proc "c" (N, I, Nref: vec2) -> vec2 { return N if dot(I, Nref) < 0 else -N }
-faceForward_vec3 :: proc "c" (N, I, Nref: vec3) -> vec3 { return N if dot(I, Nref) < 0 else -N }
-faceForward_vec4 :: proc "c" (N, I, Nref: vec4) -> vec4 { return N if dot(I, Nref) < 0 else -N }
-faceForward_dvec2 :: proc "c" (N, I, Nref: dvec2) -> dvec2 { return N if dot(I, Nref) < 0 else -N }
-faceForward_dvec3 :: proc "c" (N, I, Nref: dvec3) -> dvec3 { return N if dot(I, Nref) < 0 else -N }
-faceForward_dvec4 :: proc "c" (N, I, Nref: dvec4) -> dvec4 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_f32 :: proc "c" (N, I, Nref: f32) -> f32 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_f64 :: proc "c" (N, I, Nref: f64) -> f64 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_vec2 :: proc "c" (N, I, Nref: vec2) -> vec2 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_vec3 :: proc "c" (N, I, Nref: vec3) -> vec3 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_vec4 :: proc "c" (N, I, Nref: vec4) -> vec4 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_dvec2 :: proc "c" (N, I, Nref: dvec2) -> dvec2 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_dvec3 :: proc "c" (N, I, Nref: dvec3) -> dvec3 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceForward_dvec4 :: proc "c" (N, I, Nref: dvec4) -> dvec4 { return N if dot(I, Nref) < 0 else -N }
reflect :: proc{
@@ -1010,14 +1010,14 @@ reflect :: proc{
reflect_dvec3,
reflect_dvec4,
}
-reflect_f32 :: proc "c" (I, N: f32) -> f32 { return I - 2*N*dot(N, I) }
-reflect_f64 :: proc "c" (I, N: f64) -> f64 { return I - 2*N*dot(N, I) }
-reflect_vec2 :: proc "c" (I, N: vec2) -> vec2 { return I - 2*N*dot(N, I) }
-reflect_vec3 :: proc "c" (I, N: vec3) -> vec3 { return I - 2*N*dot(N, I) }
-reflect_vec4 :: proc "c" (I, N: vec4) -> vec4 { return I - 2*N*dot(N, I) }
-reflect_dvec2 :: proc "c" (I, N: dvec2) -> dvec2 { return I - 2*N*dot(N, I) }
-reflect_dvec3 :: proc "c" (I, N: dvec3) -> dvec3 { return I - 2*N*dot(N, I) }
-reflect_dvec4 :: proc "c" (I, N: dvec4) -> dvec4 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_f32 :: proc "c" (I, N: f32) -> f32 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_f64 :: proc "c" (I, N: f64) -> f64 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_vec2 :: proc "c" (I, N: vec2) -> vec2 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_vec3 :: proc "c" (I, N: vec3) -> vec3 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_vec4 :: proc "c" (I, N: vec4) -> vec4 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_dvec2 :: proc "c" (I, N: dvec2) -> dvec2 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_dvec3 :: proc "c" (I, N: dvec3) -> dvec3 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_dvec4 :: proc "c" (I, N: dvec4) -> dvec4 { return I - 2*N*dot(N, I) }
@@ -1032,49 +1032,49 @@ refract :: proc{
refract_dvec3,
refract_dvec4,
}
-refract_f32 :: proc "c" (i, n, eta: f32) -> f32 {
+@(require_results) refract_f32 :: proc "c" (i, n, eta: f32) -> f32 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * f32(i32(cost2 > 0))
}
-refract_f64 :: proc "c" (i, n, eta: f64) -> f64 {
+@(require_results) refract_f64 :: proc "c" (i, n, eta: f64) -> f64 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * f64(i32(cost2 > 0))
}
-refract_vec2 :: proc "c" (i, n, eta: vec2) -> vec2 {
+@(require_results) refract_vec2 :: proc "c" (i, n, eta: vec2) -> vec2 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * vec2{f32(i32(cost2.x > 0)), f32(i32(cost2.y > 0))}
}
-refract_vec3 :: proc "c" (i, n, eta: vec3) -> vec3 {
+@(require_results) refract_vec3 :: proc "c" (i, n, eta: vec3) -> vec3 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * vec3{f32(i32(cost2.x > 0)), f32(i32(cost2.y > 0)), f32(i32(cost2.z > 0))}
}
-refract_vec4 :: proc "c" (i, n, eta: vec4) -> vec4 {
+@(require_results) refract_vec4 :: proc "c" (i, n, eta: vec4) -> vec4 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * vec4{f32(i32(cost2.x > 0)), f32(i32(cost2.y > 0)), f32(i32(cost2.z > 0)), f32(i32(cost2.w > 0))}
}
-refract_dvec2 :: proc "c" (i, n, eta: dvec2) -> dvec2 {
+@(require_results) refract_dvec2 :: proc "c" (i, n, eta: dvec2) -> dvec2 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * dvec2{f64(i32(cost2.x > 0)), f64(i32(cost2.y > 0))}
}
-refract_dvec3 :: proc "c" (i, n, eta: dvec3) -> dvec3 {
+@(require_results) refract_dvec3 :: proc "c" (i, n, eta: dvec3) -> dvec3 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * dvec3{f64(i32(cost2.x > 0)), f64(i32(cost2.y > 0)), f64(i32(cost2.z > 0))}
}
-refract_dvec4 :: proc "c" (i, n, eta: dvec4) -> dvec4 {
+@(require_results) refract_dvec4 :: proc "c" (i, n, eta: dvec4) -> dvec4 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
@@ -1086,18 +1086,18 @@ scalarTripleProduct :: proc{
scalarTripleProduct_dvec3,
scalarTripleProduct_ivec3,
}
-scalarTripleProduct_vec3 :: proc "c" (a, b, c: vec3) -> f32 { return dot(a, cross(b, c)) }
-scalarTripleProduct_dvec3 :: proc "c" (a, b, c: dvec3) -> f64 { return dot(a, cross(b, c)) }
-scalarTripleProduct_ivec3 :: proc "c" (a, b, c: ivec3) -> i32 { return dot(a, cross(b, c)) }
+@(require_results) scalarTripleProduct_vec3 :: proc "c" (a, b, c: vec3) -> f32 { return dot(a, cross(b, c)) }
+@(require_results) scalarTripleProduct_dvec3 :: proc "c" (a, b, c: dvec3) -> f64 { return dot(a, cross(b, c)) }
+@(require_results) scalarTripleProduct_ivec3 :: proc "c" (a, b, c: ivec3) -> i32 { return dot(a, cross(b, c)) }
vectorTripleProduct :: proc {
vectorTripleProduct_vec3,
vectorTripleProduct_dvec3,
vectorTripleProduct_ivec3,
}
-vectorTripleProduct_vec3 :: proc "c" (a, b, c: vec3) -> vec3 { return cross(a, cross(b, c)) }
-vectorTripleProduct_dvec3 :: proc "c" (a, b, c: dvec3) -> dvec3 { return cross(a, cross(b, c)) }
-vectorTripleProduct_ivec3 :: proc "c" (a, b, c: ivec3) -> ivec3 { return cross(a, cross(b, c)) }
+@(require_results) vectorTripleProduct_vec3 :: proc "c" (a, b, c: vec3) -> vec3 { return cross(a, cross(b, c)) }
+@(require_results) vectorTripleProduct_dvec3 :: proc "c" (a, b, c: dvec3) -> dvec3 { return cross(a, cross(b, c)) }
+@(require_results) vectorTripleProduct_ivec3 :: proc "c" (a, b, c: ivec3) -> ivec3 { return cross(a, cross(b, c)) }
// Vector Relational Procedures
@@ -1120,22 +1120,22 @@ lessThan :: proc{
lessThan_ivec4,
lessThan_uvec4,
}
-lessThan_f32 :: proc "c" (a, b: f32) -> bool { return a < b }
-lessThan_f64 :: proc "c" (a, b: f64) -> bool { return a < b }
-lessThan_i32 :: proc "c" (a, b: i32) -> bool { return a < b }
-lessThan_u32 :: proc "c" (a, b: u32) -> bool { return a < b }
-lessThan_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
-lessThan_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
-lessThan_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
-lessThan_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
-lessThan_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
-lessThan_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
-lessThan_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
-lessThan_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_f32 :: proc "c" (a, b: f32) -> bool { return a < b }
+@(require_results) lessThan_f64 :: proc "c" (a, b: f64) -> bool { return a < b }
+@(require_results) lessThan_i32 :: proc "c" (a, b: i32) -> bool { return a < b }
+@(require_results) lessThan_u32 :: proc "c" (a, b: u32) -> bool { return a < b }
+@(require_results) lessThan_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
lessThanEqual :: proc{
@@ -1156,22 +1156,22 @@ lessThanEqual :: proc{
lessThanEqual_ivec4,
lessThanEqual_uvec4,
}
-lessThanEqual_f32 :: proc "c" (a, b: f32) -> bool { return a <= b }
-lessThanEqual_f64 :: proc "c" (a, b: f64) -> bool { return a <= b }
-lessThanEqual_i32 :: proc "c" (a, b: i32) -> bool { return a <= b }
-lessThanEqual_u32 :: proc "c" (a, b: u32) -> bool { return a <= b }
-lessThanEqual_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
-lessThanEqual_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
-lessThanEqual_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
-lessThanEqual_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_f32 :: proc "c" (a, b: f32) -> bool { return a <= b }
+@(require_results) lessThanEqual_f64 :: proc "c" (a, b: f64) -> bool { return a <= b }
+@(require_results) lessThanEqual_i32 :: proc "c" (a, b: i32) -> bool { return a <= b }
+@(require_results) lessThanEqual_u32 :: proc "c" (a, b: u32) -> bool { return a <= b }
+@(require_results) lessThanEqual_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
greaterThan :: proc{
@@ -1192,22 +1192,22 @@ greaterThan :: proc{
greaterThan_ivec4,
greaterThan_uvec4,
}
-greaterThan_f32 :: proc "c" (a, b: f32) -> bool { return a > b }
-greaterThan_f64 :: proc "c" (a, b: f64) -> bool { return a > b }
-greaterThan_i32 :: proc "c" (a, b: i32) -> bool { return a > b }
-greaterThan_u32 :: proc "c" (a, b: u32) -> bool { return a > b }
-greaterThan_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
-greaterThan_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
-greaterThan_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
-greaterThan_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_f32 :: proc "c" (a, b: f32) -> bool { return a > b }
+@(require_results) greaterThan_f64 :: proc "c" (a, b: f64) -> bool { return a > b }
+@(require_results) greaterThan_i32 :: proc "c" (a, b: i32) -> bool { return a > b }
+@(require_results) greaterThan_u32 :: proc "c" (a, b: u32) -> bool { return a > b }
+@(require_results) greaterThan_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
greaterThanEqual :: proc{
@@ -1228,22 +1228,22 @@ greaterThanEqual :: proc{
greaterThanEqual_ivec4,
greaterThanEqual_uvec4,
}
-greaterThanEqual_f32 :: proc "c" (a, b: f32) -> bool { return a >= b }
-greaterThanEqual_f64 :: proc "c" (a, b: f64) -> bool { return a >= b }
-greaterThanEqual_i32 :: proc "c" (a, b: i32) -> bool { return a >= b }
-greaterThanEqual_u32 :: proc "c" (a, b: u32) -> bool { return a >= b }
-greaterThanEqual_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
-greaterThanEqual_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
-greaterThanEqual_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
-greaterThanEqual_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_f32 :: proc "c" (a, b: f32) -> bool { return a >= b }
+@(require_results) greaterThanEqual_f64 :: proc "c" (a, b: f64) -> bool { return a >= b }
+@(require_results) greaterThanEqual_i32 :: proc "c" (a, b: i32) -> bool { return a >= b }
+@(require_results) greaterThanEqual_u32 :: proc "c" (a, b: u32) -> bool { return a >= b }
+@(require_results) greaterThanEqual_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
equal :: proc{
@@ -1264,22 +1264,22 @@ equal :: proc{
equal_ivec4,
equal_uvec4,
}
-equal_f32 :: proc "c" (a, b: f32) -> bool { return a == b }
-equal_f64 :: proc "c" (a, b: f64) -> bool { return a == b }
-equal_i32 :: proc "c" (a, b: i32) -> bool { return a == b }
-equal_u32 :: proc "c" (a, b: u32) -> bool { return a == b }
-equal_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
-equal_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
-equal_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
-equal_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
-equal_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
-equal_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
-equal_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
-equal_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_f32 :: proc "c" (a, b: f32) -> bool { return a == b }
+@(require_results) equal_f64 :: proc "c" (a, b: f64) -> bool { return a == b }
+@(require_results) equal_i32 :: proc "c" (a, b: i32) -> bool { return a == b }
+@(require_results) equal_u32 :: proc "c" (a, b: u32) -> bool { return a == b }
+@(require_results) equal_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
notEqual :: proc{
notEqual_f32,
@@ -1299,22 +1299,22 @@ notEqual :: proc{
notEqual_ivec4,
notEqual_uvec4,
}
-notEqual_f32 :: proc "c" (a, b: f32) -> bool { return a != b }
-notEqual_f64 :: proc "c" (a, b: f64) -> bool { return a != b }
-notEqual_i32 :: proc "c" (a, b: i32) -> bool { return a != b }
-notEqual_u32 :: proc "c" (a, b: u32) -> bool { return a != b }
-notEqual_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
-notEqual_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
-notEqual_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
-notEqual_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
-notEqual_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
-notEqual_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
-notEqual_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
-notEqual_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_f32 :: proc "c" (a, b: f32) -> bool { return a != b }
+@(require_results) notEqual_f64 :: proc "c" (a, b: f64) -> bool { return a != b }
+@(require_results) notEqual_i32 :: proc "c" (a, b: i32) -> bool { return a != b }
+@(require_results) notEqual_u32 :: proc "c" (a, b: u32) -> bool { return a != b }
+@(require_results) notEqual_vec2 :: proc "c" (a, b: vec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_dvec2 :: proc "c" (a, b: dvec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_ivec2 :: proc "c" (a, b: ivec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_uvec2 :: proc "c" (a, b: uvec2) -> bvec2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_vec3 :: proc "c" (a, b: vec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_dvec3 :: proc "c" (a, b: dvec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_ivec3 :: proc "c" (a, b: ivec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_uvec3 :: proc "c" (a, b: uvec3) -> bvec3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_vec4 :: proc "c" (a, b: vec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_dvec4 :: proc "c" (a, b: dvec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_ivec4 :: proc "c" (a, b: ivec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_uvec4 :: proc "c" (a, b: uvec4) -> bvec4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
any :: proc{
@@ -1323,10 +1323,10 @@ any :: proc{
any_bvec3,
any_bvec4,
}
-any_bool :: proc "c" (v: bool) -> bool { return v }
-any_bvec2 :: proc "c" (v: bvec2) -> bool { return v.x || v.y }
-any_bvec3 :: proc "c" (v: bvec3) -> bool { return v.x || v.y || v.z }
-any_bvec4 :: proc "c" (v: bvec4) -> bool { return v.x || v.y || v.z || v.w }
+@(require_results) any_bool :: proc "c" (v: bool) -> bool { return v }
+@(require_results) any_bvec2 :: proc "c" (v: bvec2) -> bool { return v.x || v.y }
+@(require_results) any_bvec3 :: proc "c" (v: bvec3) -> bool { return v.x || v.y || v.z }
+@(require_results) any_bvec4 :: proc "c" (v: bvec4) -> bool { return v.x || v.y || v.z || v.w }
all :: proc{
all_bool,
@@ -1334,10 +1334,10 @@ all :: proc{
all_bvec3,
all_bvec4,
}
-all_bool :: proc "c" (v: bool) -> bool { return v }
-all_bvec2 :: proc "c" (v: bvec2) -> bool { return v.x && v.y }
-all_bvec3 :: proc "c" (v: bvec3) -> bool { return v.x && v.y && v.z }
-all_bvec4 :: proc "c" (v: bvec4) -> bool { return v.x && v.y && v.z && v.w }
+@(require_results) all_bool :: proc "c" (v: bool) -> bool { return v }
+@(require_results) all_bvec2 :: proc "c" (v: bvec2) -> bool { return v.x && v.y }
+@(require_results) all_bvec3 :: proc "c" (v: bvec3) -> bool { return v.x && v.y && v.z }
+@(require_results) all_bvec4 :: proc "c" (v: bvec4) -> bool { return v.x && v.y && v.z && v.w }
not :: proc{
not_bool,
@@ -1345,17 +1345,18 @@ not :: proc{
not_bvec3,
not_bvec4,
}
-not_bool :: proc "c" (v: bool) -> bool { return !v }
-not_bvec2 :: proc "c" (v: bvec2) -> bvec2 { return {!v.x, !v.y} }
-not_bvec3 :: proc "c" (v: bvec3) -> bvec3 { return {!v.x, !v.y, !v.z} }
-not_bvec4 :: proc "c" (v: bvec4) -> bvec4 { return {!v.x, !v.y, !v.z, !v.w} }
+@(require_results) not_bool :: proc "c" (v: bool) -> bool { return !v }
+@(require_results) not_bvec2 :: proc "c" (v: bvec2) -> bvec2 { return {!v.x, !v.y} }
+@(require_results) not_bvec3 :: proc "c" (v: bvec3) -> bvec3 { return {!v.x, !v.y, !v.z} }
+@(require_results) not_bvec4 :: proc "c" (v: bvec4) -> bvec4 { return {!v.x, !v.y, !v.z, !v.w} }
/// Matrix Utilities
-identity :: proc "c" ($M: typeid/matrix[$N, N]$T) -> M { return 1 }
+@(require_results) identity :: proc "c" ($M: typeid/matrix[$N, N]$T) -> M { return 1 }
+@(require_results)
mat4Perspective :: proc "c" (fovy, aspect, near, far: f32) -> (m: mat4) {
tan_half_fovy := tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
@@ -1365,6 +1366,7 @@ mat4Perspective :: proc "c" (fovy, aspect, near, far: f32) -> (m: mat4) {
m[2, 3] = -2*far*near / (far - near)
return
}
+@(require_results)
mat4PerspectiveInfinite :: proc "c" (fovy, aspect, near: f32) -> (m: mat4) {
tan_half_fovy := tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
@@ -1374,6 +1376,7 @@ mat4PerspectiveInfinite :: proc "c" (fovy, aspect, near: f32) -> (m: mat4) {
m[2, 3] = -2*near
return
}
+@(require_results)
mat4Ortho3d :: proc "c" (left, right, bottom, top, near, far: f32) -> (m: mat4) {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
@@ -1384,6 +1387,7 @@ mat4Ortho3d :: proc "c" (left, right, bottom, top, near, far: f32) -> (m: mat4)
m[3, 3] = 1
return m
}
+@(require_results)
mat4LookAt :: proc "c" (eye, centre, up: vec3) -> (m: mat4) {
f := normalize(centre - eye)
s := normalize(cross(f, up))
@@ -1397,6 +1401,7 @@ mat4LookAt :: proc "c" (eye, centre, up: vec3) -> (m: mat4) {
m[3] = {-dot(s, eye), -dot(u, eye), +fe, 1}
return
}
+@(require_results)
mat4Rotate :: proc "c" (v: vec3, radians: f32) -> (rot: mat4) {
c := cos(radians)
s := sin(radians)
@@ -1423,11 +1428,13 @@ mat4Rotate :: proc "c" (v: vec3, radians: f32) -> (rot: mat4) {
return rot
}
+@(require_results)
mat4Translate :: proc "c" (v: vec3) -> (m: mat4) {
m = 1
m[3].xyz = v.xyz
return
}
+@(require_results)
mat4Scale :: proc "c" (v: vec3) -> (m: mat4) {
m[0, 0] = v[0]
m[1, 1] = v[1]
@@ -1435,6 +1442,7 @@ mat4Scale :: proc "c" (v: vec3) -> (m: mat4) {
m[3, 3] = 1
return
}
+@(require_results)
mat4Orientation :: proc "c" (normal, up: vec3) -> mat4 {
if normal == up {
return 1
@@ -1445,6 +1453,7 @@ mat4Orientation :: proc "c" (normal, up: vec3) -> mat4 {
return mat4Rotate(rotation_axis, angle)
}
+@(require_results)
mat4FromQuat :: proc "c" (q: quat) -> (m: mat4) {
qxx := q.x * q.x
qyy := q.y * q.y
@@ -1474,6 +1483,7 @@ mat4FromQuat :: proc "c" (q: quat) -> (m: mat4) {
}
+@(require_results)
dmat4Perspective :: proc "c" (fovy, aspect, near, far: f64) -> (m: dmat4) {
tan_half_fovy := tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
@@ -1483,6 +1493,7 @@ dmat4Perspective :: proc "c" (fovy, aspect, near, far: f64) -> (m: dmat4) {
m[2, 3] = -2*far*near / (far - near)
return
}
+@(require_results)
dmat4PerspectiveInfinite :: proc "c" (fovy, aspect, near: f64) -> (m: dmat4) {
tan_half_fovy := tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
@@ -1492,6 +1503,7 @@ dmat4PerspectiveInfinite :: proc "c" (fovy, aspect, near: f64) -> (m: dmat4) {
m[2, 3] = -2*near
return
}
+@(require_results)
dmat4Ortho3d :: proc "c" (left, right, bottom, top, near, far: f64) -> (m: dmat4) {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
@@ -1502,6 +1514,7 @@ dmat4Ortho3d :: proc "c" (left, right, bottom, top, near, far: f64) -> (m: dmat4
m[3, 3] = 1
return m
}
+@(require_results)
dmat4LookAt :: proc "c" (eye, centre, up: dvec3) -> (m: dmat4) {
f := normalize(centre - eye)
s := normalize(cross(f, up))
@@ -1515,6 +1528,7 @@ dmat4LookAt :: proc "c" (eye, centre, up: dvec3) -> (m: dmat4) {
m[3] = {-dot(s, eye), -dot(u, eye), +fe, 1}
return
}
+@(require_results)
dmat4Rotate :: proc "c" (v: dvec3, radians: f64) -> (rot: dmat4) {
c := cos(radians)
s := sin(radians)
@@ -1541,11 +1555,13 @@ dmat4Rotate :: proc "c" (v: dvec3, radians: f64) -> (rot: dmat4) {
return rot
}
+@(require_results)
dmat4Translate :: proc "c" (v: dvec3) -> (m: dmat4) {
m = 1
m[3].xyz = v.xyz
return
}
+@(require_results)
dmat4Scale :: proc "c" (v: dvec3) -> (m: dmat4) {
m[0, 0] = v[0]
m[1, 1] = v[1]
@@ -1553,6 +1569,7 @@ dmat4Scale :: proc "c" (v: dvec3) -> (m: dmat4) {
m[3, 3] = 1
return
}
+@(require_results)
dmat4Orientation :: proc "c" (normal, up: dvec3) -> dmat4 {
if normal == up {
return 1
@@ -1563,6 +1580,7 @@ dmat4Orientation :: proc "c" (normal, up: dvec3) -> dmat4 {
return dmat4Rotate(rotation_axis, angle)
}
+@(require_results)
dmat4FromDquat :: proc "c" (q: dquat) -> (m: dmat4) {
qxx := q.x * q.x
qyy := q.y * q.y
@@ -1601,6 +1619,7 @@ slerp :: proc{
}
+@(require_results)
quatAxisAngle :: proc "c" (axis: vec3, radians: f32) -> (q: quat) {
t := radians*0.5
v := normalize(axis) * sin(t)
@@ -1610,6 +1629,7 @@ quatAxisAngle :: proc "c" (axis: vec3, radians: f32) -> (q: quat) {
q.w = cos(t)
return
}
+@(require_results)
quatNlerp :: proc "c" (a, b: quat, t: f32) -> (c: quat) {
c.x = a.x + (b.x-a.x)*t
c.y = a.y + (b.y-a.y)*t
@@ -1618,6 +1638,7 @@ quatNlerp :: proc "c" (a, b: quat, t: f32) -> (c: quat) {
return c/quat(builtin.abs(c))
}
+@(require_results)
quatSlerp :: proc "c" (x, y: quat, t: f32) -> (q: quat) {
a, b := x, y
cos_angle := dot(a, b)
@@ -1644,6 +1665,7 @@ quatSlerp :: proc "c" (x, y: quat, t: f32) -> (q: quat) {
q.w = factor_a * a.w + factor_b * b.w
return
}
+@(require_results)
quatFromMat3 :: proc "c" (m: mat3) -> (q: quat) {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
@@ -1693,16 +1715,19 @@ quatFromMat3 :: proc "c" (m: mat3) -> (q: quat) {
}
return
}
+@(require_results)
quatFromMat4 :: proc "c" (m: mat4) -> (q: quat) {
return quatFromMat3(mat3(m))
}
+@(require_results)
quatMulVec3 :: proc "c" (q: quat, v: vec3) -> vec3 {
xyz := vec3{q.x, q.y, q.z}
t := cross(xyz, v)
return v + q.w*t + cross(xyz, t)
}
+@(require_results)
dquatAxisAngle :: proc "c" (axis: dvec3, radians: f64) -> (q: dquat) {
t := radians*0.5
v := normalize(axis) * sin(t)
@@ -1712,6 +1737,7 @@ dquatAxisAngle :: proc "c" (axis: dvec3, radians: f64) -> (q: dquat) {
q.w = cos(t)
return
}
+@(require_results)
dquatNlerp :: proc "c" (a, b: dquat, t: f64) -> (c: dquat) {
c.x = a.x + (b.x-a.x)*t
c.y = a.y + (b.y-a.y)*t
@@ -1720,6 +1746,7 @@ dquatNlerp :: proc "c" (a, b: dquat, t: f64) -> (c: dquat) {
return c/dquat(builtin.abs(c))
}
+@(require_results)
dquatSlerp :: proc "c" (x, y: dquat, t: f64) -> (q: dquat) {
a, b := x, y
cos_angle := dot(a, b)
@@ -1746,6 +1773,7 @@ dquatSlerp :: proc "c" (x, y: dquat, t: f64) -> (q: dquat) {
q.w = factor_a * a.w + factor_b * b.w
return
}
+@(require_results)
dquatFromdMat3 :: proc "c" (m: dmat3) -> (q: dquat) {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
@@ -1795,10 +1823,12 @@ dquatFromdMat3 :: proc "c" (m: dmat3) -> (q: dquat) {
}
return
}
+@(require_results)
dquatFromDmat4 :: proc "c" (m: dmat4) -> (q: dquat) {
return dquatFromdMat3(dmat3(m))
}
+@(require_results)
dquatMulDvec3 :: proc "c" (q: dquat, v: dvec3) -> dvec3 {
xyz := dvec3{q.x, q.y, q.z}
t := cross(xyz, v)
@@ -1808,14 +1838,14 @@ dquatMulDvec3 :: proc "c" (q: dquat, v: dvec3) -> dvec3 {
-inverse_mat2 :: proc "c" (m: mat2) -> mat2 { return builtin.inverse(m) }
-inverse_mat3 :: proc "c" (m: mat3) -> mat3 { return builtin.inverse(m) }
-inverse_mat4 :: proc "c" (m: mat4) -> mat4 { return builtin.inverse(m) }
-inverse_dmat2 :: proc "c" (m: dmat2) -> dmat2 { return builtin.inverse(m) }
-inverse_dmat3 :: proc "c" (m: dmat3) -> dmat3 { return builtin.inverse(m) }
-inverse_dmat4 :: proc "c" (m: dmat4) -> dmat4 { return builtin.inverse(m) }
-inverse_quat :: proc "c" (q: quat) -> quat { return 1/q }
-inverse_dquat :: proc "c" (q: dquat) -> dquat { return 1/q }
+@(require_results) inverse_mat2 :: proc "c" (m: mat2) -> mat2 { return builtin.inverse(m) }
+@(require_results) inverse_mat3 :: proc "c" (m: mat3) -> mat3 { return builtin.inverse(m) }
+@(require_results) inverse_mat4 :: proc "c" (m: mat4) -> mat4 { return builtin.inverse(m) }
+@(require_results) inverse_dmat2 :: proc "c" (m: dmat2) -> dmat2 { return builtin.inverse(m) }
+@(require_results) inverse_dmat3 :: proc "c" (m: dmat3) -> dmat3 { return builtin.inverse(m) }
+@(require_results) inverse_dmat4 :: proc "c" (m: dmat4) -> dmat4 { return builtin.inverse(m) }
+@(require_results) inverse_quat :: proc "c" (q: quat) -> quat { return 1/q }
+@(require_results) inverse_dquat :: proc "c" (q: dquat) -> dquat { return 1/q }
inverse :: proc{
inverse_mat2,
diff --git a/core/math/linalg/glsl/linalg_glsl_math.odin b/core/math/linalg/glsl/linalg_glsl_math.odin
index 968a3fa5e..82b1857ab 100644
--- a/core/math/linalg/glsl/linalg_glsl_math.odin
+++ b/core/math/linalg/glsl/linalg_glsl_math.odin
@@ -2,30 +2,31 @@ package math_linalg_glsl
import "core:math"
-cos_f32 :: proc "c" (x: f32) -> f32 { return math.cos(x) }
-sin_f32 :: proc "c" (x: f32) -> f32 { return math.sin(x) }
-tan_f32 :: proc "c" (x: f32) -> f32 { return math.tan(x) }
-acos_f32 :: proc "c" (x: f32) -> f32 { return math.acos(x) }
-asin_f32 :: proc "c" (x: f32) -> f32 { return math.asin(x) }
-atan_f32 :: proc "c" (x: f32) -> f32 { return math.atan(x) }
-atan2_f32 :: proc "c" (y, x: f32) -> f32 { return math.atan2(y, x) }
-cosh_f32 :: proc "c" (x: f32) -> f32 { return math.cosh(x) }
-sinh_f32 :: proc "c" (x: f32) -> f32 { return math.sinh(x) }
-tanh_f32 :: proc "c" (x: f32) -> f32 { return math.tanh(x) }
-acosh_f32 :: proc "c" (x: f32) -> f32 { return math.acosh(x) }
-asinh_f32 :: proc "c" (x: f32) -> f32 { return math.asinh(x) }
-atanh_f32 :: proc "c" (x: f32) -> f32 { return math.atanh(x) }
-sqrt_f32 :: proc "c" (x: f32) -> f32 { return math.sqrt(x) }
-inversesqrt_f32 :: proc "c" (x: f32) -> f32 { return 1.0/math.sqrt(x) }
-pow_f32 :: proc "c" (x, y: f32) -> f32 { return math.pow(x, y) }
-exp_f32 :: proc "c" (x: f32) -> f32 { return math.exp(x) }
-log_f32 :: proc "c" (x: f32) -> f32 { return math.ln(x) }
-exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
-sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
-floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
-round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
-ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
-mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
+@(require_results) cos_f32 :: proc "c" (x: f32) -> f32 { return math.cos(x) }
+@(require_results) sin_f32 :: proc "c" (x: f32) -> f32 { return math.sin(x) }
+@(require_results) tan_f32 :: proc "c" (x: f32) -> f32 { return math.tan(x) }
+@(require_results) acos_f32 :: proc "c" (x: f32) -> f32 { return math.acos(x) }
+@(require_results) asin_f32 :: proc "c" (x: f32) -> f32 { return math.asin(x) }
+@(require_results) atan_f32 :: proc "c" (x: f32) -> f32 { return math.atan(x) }
+@(require_results) atan2_f32 :: proc "c" (y, x: f32) -> f32 { return math.atan2(y, x) }
+@(require_results) cosh_f32 :: proc "c" (x: f32) -> f32 { return math.cosh(x) }
+@(require_results) sinh_f32 :: proc "c" (x: f32) -> f32 { return math.sinh(x) }
+@(require_results) tanh_f32 :: proc "c" (x: f32) -> f32 { return math.tanh(x) }
+@(require_results) acosh_f32 :: proc "c" (x: f32) -> f32 { return math.acosh(x) }
+@(require_results) asinh_f32 :: proc "c" (x: f32) -> f32 { return math.asinh(x) }
+@(require_results) atanh_f32 :: proc "c" (x: f32) -> f32 { return math.atanh(x) }
+@(require_results) sqrt_f32 :: proc "c" (x: f32) -> f32 { return math.sqrt(x) }
+@(require_results) inversesqrt_f32 :: proc "c" (x: f32) -> f32 { return 1.0/math.sqrt(x) }
+@(require_results) pow_f32 :: proc "c" (x, y: f32) -> f32 { return math.pow(x, y) }
+@(require_results) exp_f32 :: proc "c" (x: f32) -> f32 { return math.exp(x) }
+@(require_results) log_f32 :: proc "c" (x: f32) -> f32 { return math.ln(x) }
+@(require_results) exp2_f32 :: proc "c" (x: f32) -> f32 { return math.pow(f32(2), x) }
+@(require_results) sign_f32 :: proc "c" (x: f32) -> f32 { return math.sign(x) }
+@(require_results) floor_f32 :: proc "c" (x: f32) -> f32 { return math.floor(x) }
+@(require_results) round_f32 :: proc "c" (x: f32) -> f32 { return math.round(x) }
+@(require_results) ceil_f32 :: proc "c" (x: f32) -> f32 { return math.ceil(x) }
+@(require_results) mod_f32 :: proc "c" (x, y: f32) -> f32 { return math.mod(x, y) }
+@(require_results)
fract_f32 :: proc "c" (x: f32) -> f32 {
if x >= 0 {
return x - math.trunc(x)
@@ -33,30 +34,31 @@ fract_f32 :: proc "c" (x: f32) -> f32 {
return math.trunc(-x) + x
}
-cos_f64 :: proc "c" (x: f64) -> f64 { return math.cos(x) }
-sin_f64 :: proc "c" (x: f64) -> f64 { return math.sin(x) }
-tan_f64 :: proc "c" (x: f64) -> f64 { return math.tan(x) }
-acos_f64 :: proc "c" (x: f64) -> f64 { return math.acos(x) }
-asin_f64 :: proc "c" (x: f64) -> f64 { return math.asin(x) }
-atan_f64 :: proc "c" (x: f64) -> f64 { return math.atan(x) }
-atan2_f64 :: proc "c" (y, x: f64) -> f64 { return math.atan2(y, x) }
-cosh_f64 :: proc "c" (x: f64) -> f64 { return math.cosh(x) }
-sinh_f64 :: proc "c" (x: f64) -> f64 { return math.sinh(x) }
-tanh_f64 :: proc "c" (x: f64) -> f64 { return math.tanh(x) }
-acosh_f64 :: proc "c" (x: f64) -> f64 { return math.acosh(x) }
-asinh_f64 :: proc "c" (x: f64) -> f64 { return math.asinh(x) }
-atanh_f64 :: proc "c" (x: f64) -> f64 { return math.atanh(x) }
-sqrt_f64 :: proc "c" (x: f64) -> f64 { return math.sqrt(x) }
-inversesqrt_f64 :: proc "c" (x: f64) -> f64 { return 1.0/math.sqrt(x) }
-pow_f64 :: proc "c" (x, y: f64) -> f64 { return math.pow(x, y) }
-exp_f64 :: proc "c" (x: f64) -> f64 { return math.exp(x) }
-log_f64 :: proc "c" (x: f64) -> f64 { return math.ln(x) }
-exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
-sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
-floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
-round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
-ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
-mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
+@(require_results) cos_f64 :: proc "c" (x: f64) -> f64 { return math.cos(x) }
+@(require_results) sin_f64 :: proc "c" (x: f64) -> f64 { return math.sin(x) }
+@(require_results) tan_f64 :: proc "c" (x: f64) -> f64 { return math.tan(x) }
+@(require_results) acos_f64 :: proc "c" (x: f64) -> f64 { return math.acos(x) }
+@(require_results) asin_f64 :: proc "c" (x: f64) -> f64 { return math.asin(x) }
+@(require_results) atan_f64 :: proc "c" (x: f64) -> f64 { return math.atan(x) }
+@(require_results) atan2_f64 :: proc "c" (y, x: f64) -> f64 { return math.atan2(y, x) }
+@(require_results) cosh_f64 :: proc "c" (x: f64) -> f64 { return math.cosh(x) }
+@(require_results) sinh_f64 :: proc "c" (x: f64) -> f64 { return math.sinh(x) }
+@(require_results) tanh_f64 :: proc "c" (x: f64) -> f64 { return math.tanh(x) }
+@(require_results) acosh_f64 :: proc "c" (x: f64) -> f64 { return math.acosh(x) }
+@(require_results) asinh_f64 :: proc "c" (x: f64) -> f64 { return math.asinh(x) }
+@(require_results) atanh_f64 :: proc "c" (x: f64) -> f64 { return math.atanh(x) }
+@(require_results) sqrt_f64 :: proc "c" (x: f64) -> f64 { return math.sqrt(x) }
+@(require_results) inversesqrt_f64 :: proc "c" (x: f64) -> f64 { return 1.0/math.sqrt(x) }
+@(require_results) pow_f64 :: proc "c" (x, y: f64) -> f64 { return math.pow(x, y) }
+@(require_results) exp_f64 :: proc "c" (x: f64) -> f64 { return math.exp(x) }
+@(require_results) log_f64 :: proc "c" (x: f64) -> f64 { return math.ln(x) }
+@(require_results) exp2_f64 :: proc "c" (x: f64) -> f64 { return math.pow(f64(2), x) }
+@(require_results) sign_f64 :: proc "c" (x: f64) -> f64 { return math.sign(x) }
+@(require_results) floor_f64 :: proc "c" (x: f64) -> f64 { return math.floor(x) }
+@(require_results) round_f64 :: proc "c" (x: f64) -> f64 { return math.round(x) }
+@(require_results) ceil_f64 :: proc "c" (x: f64) -> f64 { return math.ceil(x) }
+@(require_results) mod_f64 :: proc "c" (x, y: f64) -> f64 { return math.mod(x, y) }
+@(require_results)
fract_f64 :: proc "c" (x: f64) -> f64 {
if x >= 0 {
return x - math.trunc(x)
diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin
index 3f73dcd1f..351aa7ea3 100644
--- a/core/math/linalg/hlsl/linalg_hlsl.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl.odin
@@ -114,12 +114,12 @@ cos :: proc{
cos_double3,
cos_double4,
}
-cos_float2 :: proc "c" (x: float2) -> float2 { return {cos(x.x), cos(x.y)} }
-cos_float3 :: proc "c" (x: float3) -> float3 { return {cos(x.x), cos(x.y), cos(x.z)} }
-cos_float4 :: proc "c" (x: float4) -> float4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
-cos_double2 :: proc "c" (x: double2) -> double2 { return {cos(x.x), cos(x.y)} }
-cos_double3 :: proc "c" (x: double3) -> double3 { return {cos(x.x), cos(x.y), cos(x.z)} }
-cos_double4 :: proc "c" (x: double4) -> double4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
+@(require_results) cos_float2 :: proc "c" (x: float2) -> float2 { return {cos(x.x), cos(x.y)} }
+@(require_results) cos_float3 :: proc "c" (x: float3) -> float3 { return {cos(x.x), cos(x.y), cos(x.z)} }
+@(require_results) cos_float4 :: proc "c" (x: float4) -> float4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
+@(require_results) cos_double2 :: proc "c" (x: double2) -> double2 { return {cos(x.x), cos(x.y)} }
+@(require_results) cos_double3 :: proc "c" (x: double3) -> double3 { return {cos(x.x), cos(x.y), cos(x.z)} }
+@(require_results) cos_double4 :: proc "c" (x: double4) -> double4 { return {cos(x.x), cos(x.y), cos(x.z), cos(x.w)} }
sin :: proc{
sin_float,
@@ -131,12 +131,12 @@ sin :: proc{
sin_double3,
sin_double4,
}
-sin_float2 :: proc "c" (x: float2) -> float2 { return {sin(x.x), sin(x.y)} }
-sin_float3 :: proc "c" (x: float3) -> float3 { return {sin(x.x), sin(x.y), sin(x.z)} }
-sin_float4 :: proc "c" (x: float4) -> float4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
-sin_double2 :: proc "c" (x: double2) -> double2 { return {sin(x.x), sin(x.y)} }
-sin_double3 :: proc "c" (x: double3) -> double3 { return {sin(x.x), sin(x.y), sin(x.z)} }
-sin_double4 :: proc "c" (x: double4) -> double4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
+@(require_results) sin_float2 :: proc "c" (x: float2) -> float2 { return {sin(x.x), sin(x.y)} }
+@(require_results) sin_float3 :: proc "c" (x: float3) -> float3 { return {sin(x.x), sin(x.y), sin(x.z)} }
+@(require_results) sin_float4 :: proc "c" (x: float4) -> float4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
+@(require_results) sin_double2 :: proc "c" (x: double2) -> double2 { return {sin(x.x), sin(x.y)} }
+@(require_results) sin_double3 :: proc "c" (x: double3) -> double3 { return {sin(x.x), sin(x.y), sin(x.z)} }
+@(require_results) sin_double4 :: proc "c" (x: double4) -> double4 { return {sin(x.x), sin(x.y), sin(x.z), sin(x.w)} }
tan :: proc{
tan_float,
@@ -148,12 +148,12 @@ tan :: proc{
tan_double3,
tan_double4,
}
-tan_float2 :: proc "c" (x: float2) -> float2 { return {tan(x.x), tan(x.y)} }
-tan_float3 :: proc "c" (x: float3) -> float3 { return {tan(x.x), tan(x.y), tan(x.z)} }
-tan_float4 :: proc "c" (x: float4) -> float4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
-tan_double2 :: proc "c" (x: double2) -> double2 { return {tan(x.x), tan(x.y)} }
-tan_double3 :: proc "c" (x: double3) -> double3 { return {tan(x.x), tan(x.y), tan(x.z)} }
-tan_double4 :: proc "c" (x: double4) -> double4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
+@(require_results) tan_float2 :: proc "c" (x: float2) -> float2 { return {tan(x.x), tan(x.y)} }
+@(require_results) tan_float3 :: proc "c" (x: float3) -> float3 { return {tan(x.x), tan(x.y), tan(x.z)} }
+@(require_results) tan_float4 :: proc "c" (x: float4) -> float4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
+@(require_results) tan_double2 :: proc "c" (x: double2) -> double2 { return {tan(x.x), tan(x.y)} }
+@(require_results) tan_double3 :: proc "c" (x: double3) -> double3 { return {tan(x.x), tan(x.y), tan(x.z)} }
+@(require_results) tan_double4 :: proc "c" (x: double4) -> double4 { return {tan(x.x), tan(x.y), tan(x.z), tan(x.w)} }
acos :: proc{
acos_float,
@@ -165,12 +165,12 @@ acos :: proc{
acos_double3,
acos_double4,
}
-acos_float2 :: proc "c" (x: float2) -> float2 { return {acos(x.x), acos(x.y)} }
-acos_float3 :: proc "c" (x: float3) -> float3 { return {acos(x.x), acos(x.y), acos(x.z)} }
-acos_float4 :: proc "c" (x: float4) -> float4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
-acos_double2 :: proc "c" (x: double2) -> double2 { return {acos(x.x), acos(x.y)} }
-acos_double3 :: proc "c" (x: double3) -> double3 { return {acos(x.x), acos(x.y), acos(x.z)} }
-acos_double4 :: proc "c" (x: double4) -> double4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
+@(require_results) acos_float2 :: proc "c" (x: float2) -> float2 { return {acos(x.x), acos(x.y)} }
+@(require_results) acos_float3 :: proc "c" (x: float3) -> float3 { return {acos(x.x), acos(x.y), acos(x.z)} }
+@(require_results) acos_float4 :: proc "c" (x: float4) -> float4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
+@(require_results) acos_double2 :: proc "c" (x: double2) -> double2 { return {acos(x.x), acos(x.y)} }
+@(require_results) acos_double3 :: proc "c" (x: double3) -> double3 { return {acos(x.x), acos(x.y), acos(x.z)} }
+@(require_results) acos_double4 :: proc "c" (x: double4) -> double4 { return {acos(x.x), acos(x.y), acos(x.z), acos(x.w)} }
asin :: proc{
asin_float,
@@ -182,12 +182,12 @@ asin :: proc{
asin_double3,
asin_double4,
}
-asin_float2 :: proc "c" (x: float2) -> float2 { return {asin(x.x), asin(x.y)} }
-asin_float3 :: proc "c" (x: float3) -> float3 { return {asin(x.x), asin(x.y), asin(x.z)} }
-asin_float4 :: proc "c" (x: float4) -> float4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
-asin_double2 :: proc "c" (x: double2) -> double2 { return {asin(x.x), asin(x.y)} }
-asin_double3 :: proc "c" (x: double3) -> double3 { return {asin(x.x), asin(x.y), asin(x.z)} }
-asin_double4 :: proc "c" (x: double4) -> double4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
+@(require_results) asin_float2 :: proc "c" (x: float2) -> float2 { return {asin(x.x), asin(x.y)} }
+@(require_results) asin_float3 :: proc "c" (x: float3) -> float3 { return {asin(x.x), asin(x.y), asin(x.z)} }
+@(require_results) asin_float4 :: proc "c" (x: float4) -> float4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
+@(require_results) asin_double2 :: proc "c" (x: double2) -> double2 { return {asin(x.x), asin(x.y)} }
+@(require_results) asin_double3 :: proc "c" (x: double3) -> double3 { return {asin(x.x), asin(x.y), asin(x.z)} }
+@(require_results) asin_double4 :: proc "c" (x: double4) -> double4 { return {asin(x.x), asin(x.y), asin(x.z), asin(x.w)} }
atan :: proc{
atan_float,
@@ -207,12 +207,12 @@ atan :: proc{
atan2_double3,
atan2_double4,
}
-atan_float2 :: proc "c" (x: float2) -> float2 { return {atan(x.x), atan(x.y)} }
-atan_float3 :: proc "c" (x: float3) -> float3 { return {atan(x.x), atan(x.y), atan(x.z)} }
-atan_float4 :: proc "c" (x: float4) -> float4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
-atan_double2 :: proc "c" (x: double2) -> double2 { return {atan(x.x), atan(x.y)} }
-atan_double3 :: proc "c" (x: double3) -> double3 { return {atan(x.x), atan(x.y), atan(x.z)} }
-atan_double4 :: proc "c" (x: double4) -> double4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
+@(require_results) atan_float2 :: proc "c" (x: float2) -> float2 { return {atan(x.x), atan(x.y)} }
+@(require_results) atan_float3 :: proc "c" (x: float3) -> float3 { return {atan(x.x), atan(x.y), atan(x.z)} }
+@(require_results) atan_float4 :: proc "c" (x: float4) -> float4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
+@(require_results) atan_double2 :: proc "c" (x: double2) -> double2 { return {atan(x.x), atan(x.y)} }
+@(require_results) atan_double3 :: proc "c" (x: double3) -> double3 { return {atan(x.x), atan(x.y), atan(x.z)} }
+@(require_results) atan_double4 :: proc "c" (x: double4) -> double4 { return {atan(x.x), atan(x.y), atan(x.z), atan(x.w)} }
atan2 :: proc{
atan2_float,
@@ -224,12 +224,12 @@ atan2 :: proc{
atan2_double3,
atan2_double4,
}
-atan2_float2 :: proc "c" (y, x: float2) -> float2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
-atan2_float3 :: proc "c" (y, x: float3) -> float3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
-atan2_float4 :: proc "c" (y, x: float4) -> float4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
-atan2_double2 :: proc "c" (y, x: double2) -> double2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
-atan2_double3 :: proc "c" (y, x: double3) -> double3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
-atan2_double4 :: proc "c" (y, x: double4) -> double4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
+@(require_results) atan2_float2 :: proc "c" (y, x: float2) -> float2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
+@(require_results) atan2_float3 :: proc "c" (y, x: float3) -> float3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
+@(require_results) atan2_float4 :: proc "c" (y, x: float4) -> float4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
+@(require_results) atan2_double2 :: proc "c" (y, x: double2) -> double2 { return {atan2(y.x, x.x), atan2(y.y, x.y)} }
+@(require_results) atan2_double3 :: proc "c" (y, x: double3) -> double3 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z)} }
+@(require_results) atan2_double4 :: proc "c" (y, x: double4) -> double4 { return {atan2(y.x, x.x), atan2(y.y, x.y), atan2(y.z, x.z), atan2(y.w, x.w)} }
@@ -243,12 +243,12 @@ cosh :: proc{
cosh_double3,
cosh_double4,
}
-cosh_float2 :: proc "c" (x: float2) -> float2 { return {cosh(x.x), cosh(x.y)} }
-cosh_float3 :: proc "c" (x: float3) -> float3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
-cosh_float4 :: proc "c" (x: float4) -> float4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
-cosh_double2 :: proc "c" (x: double2) -> double2 { return {cosh(x.x), cosh(x.y)} }
-cosh_double3 :: proc "c" (x: double3) -> double3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
-cosh_double4 :: proc "c" (x: double4) -> double4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
+@(require_results) cosh_float2 :: proc "c" (x: float2) -> float2 { return {cosh(x.x), cosh(x.y)} }
+@(require_results) cosh_float3 :: proc "c" (x: float3) -> float3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
+@(require_results) cosh_float4 :: proc "c" (x: float4) -> float4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
+@(require_results) cosh_double2 :: proc "c" (x: double2) -> double2 { return {cosh(x.x), cosh(x.y)} }
+@(require_results) cosh_double3 :: proc "c" (x: double3) -> double3 { return {cosh(x.x), cosh(x.y), cosh(x.z)} }
+@(require_results) cosh_double4 :: proc "c" (x: double4) -> double4 { return {cosh(x.x), cosh(x.y), cosh(x.z), cosh(x.w)} }
sinh :: proc{
@@ -261,12 +261,12 @@ sinh :: proc{
sinh_double3,
sinh_double4,
}
-sinh_float2 :: proc "c" (x: float2) -> float2 { return {sinh(x.x), sinh(x.y)} }
-sinh_float3 :: proc "c" (x: float3) -> float3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
-sinh_float4 :: proc "c" (x: float4) -> float4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
-sinh_double2 :: proc "c" (x: double2) -> double2 { return {sinh(x.x), sinh(x.y)} }
-sinh_double3 :: proc "c" (x: double3) -> double3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
-sinh_double4 :: proc "c" (x: double4) -> double4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
+@(require_results) sinh_float2 :: proc "c" (x: float2) -> float2 { return {sinh(x.x), sinh(x.y)} }
+@(require_results) sinh_float3 :: proc "c" (x: float3) -> float3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
+@(require_results) sinh_float4 :: proc "c" (x: float4) -> float4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
+@(require_results) sinh_double2 :: proc "c" (x: double2) -> double2 { return {sinh(x.x), sinh(x.y)} }
+@(require_results) sinh_double3 :: proc "c" (x: double3) -> double3 { return {sinh(x.x), sinh(x.y), sinh(x.z)} }
+@(require_results) sinh_double4 :: proc "c" (x: double4) -> double4 { return {sinh(x.x), sinh(x.y), sinh(x.z), sinh(x.w)} }
tanh :: proc{
tanh_float,
@@ -278,12 +278,12 @@ tanh :: proc{
tanh_double3,
tanh_double4,
}
-tanh_float2 :: proc "c" (x: float2) -> float2 { return {tanh(x.x), tanh(x.y)} }
-tanh_float3 :: proc "c" (x: float3) -> float3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
-tanh_float4 :: proc "c" (x: float4) -> float4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
-tanh_double2 :: proc "c" (x: double2) -> double2 { return {tanh(x.x), tanh(x.y)} }
-tanh_double3 :: proc "c" (x: double3) -> double3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
-tanh_double4 :: proc "c" (x: double4) -> double4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
+@(require_results) tanh_float2 :: proc "c" (x: float2) -> float2 { return {tanh(x.x), tanh(x.y)} }
+@(require_results) tanh_float3 :: proc "c" (x: float3) -> float3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
+@(require_results) tanh_float4 :: proc "c" (x: float4) -> float4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
+@(require_results) tanh_double2 :: proc "c" (x: double2) -> double2 { return {tanh(x.x), tanh(x.y)} }
+@(require_results) tanh_double3 :: proc "c" (x: double3) -> double3 { return {tanh(x.x), tanh(x.y), tanh(x.z)} }
+@(require_results) tanh_double4 :: proc "c" (x: double4) -> double4 { return {tanh(x.x), tanh(x.y), tanh(x.z), tanh(x.w)} }
acosh :: proc{
acosh_float,
@@ -295,12 +295,12 @@ acosh :: proc{
acosh_double3,
acosh_double4,
}
-acosh_float2 :: proc "c" (x: float2) -> float2 { return {acosh(x.x), acosh(x.y)} }
-acosh_float3 :: proc "c" (x: float3) -> float3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
-acosh_float4 :: proc "c" (x: float4) -> float4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
-acosh_double2 :: proc "c" (x: double2) -> double2 { return {acosh(x.x), acosh(x.y)} }
-acosh_double3 :: proc "c" (x: double3) -> double3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
-acosh_double4 :: proc "c" (x: double4) -> double4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
+@(require_results) acosh_float2 :: proc "c" (x: float2) -> float2 { return {acosh(x.x), acosh(x.y)} }
+@(require_results) acosh_float3 :: proc "c" (x: float3) -> float3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
+@(require_results) acosh_float4 :: proc "c" (x: float4) -> float4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
+@(require_results) acosh_double2 :: proc "c" (x: double2) -> double2 { return {acosh(x.x), acosh(x.y)} }
+@(require_results) acosh_double3 :: proc "c" (x: double3) -> double3 { return {acosh(x.x), acosh(x.y), acosh(x.z)} }
+@(require_results) acosh_double4 :: proc "c" (x: double4) -> double4 { return {acosh(x.x), acosh(x.y), acosh(x.z), acosh(x.w)} }
asinh :: proc{
asinh_float,
@@ -312,12 +312,12 @@ asinh :: proc{
asinh_double3,
asinh_double4,
}
-asinh_float2 :: proc "c" (x: float2) -> float2 { return {asinh(x.x), asinh(x.y)} }
-asinh_float3 :: proc "c" (x: float3) -> float3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
-asinh_float4 :: proc "c" (x: float4) -> float4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
-asinh_double2 :: proc "c" (x: double2) -> double2 { return {asinh(x.x), asinh(x.y)} }
-asinh_double3 :: proc "c" (x: double3) -> double3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
-asinh_double4 :: proc "c" (x: double4) -> double4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
+@(require_results) asinh_float2 :: proc "c" (x: float2) -> float2 { return {asinh(x.x), asinh(x.y)} }
+@(require_results) asinh_float3 :: proc "c" (x: float3) -> float3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
+@(require_results) asinh_float4 :: proc "c" (x: float4) -> float4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
+@(require_results) asinh_double2 :: proc "c" (x: double2) -> double2 { return {asinh(x.x), asinh(x.y)} }
+@(require_results) asinh_double3 :: proc "c" (x: double3) -> double3 { return {asinh(x.x), asinh(x.y), asinh(x.z)} }
+@(require_results) asinh_double4 :: proc "c" (x: double4) -> double4 { return {asinh(x.x), asinh(x.y), asinh(x.z), asinh(x.w)} }
atanh :: proc{
atanh_float,
@@ -329,12 +329,12 @@ atanh :: proc{
atanh_double3,
atanh_double4,
}
-atanh_float2 :: proc "c" (x: float2) -> float2 { return {atanh(x.x), atanh(x.y)} }
-atanh_float3 :: proc "c" (x: float3) -> float3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
-atanh_float4 :: proc "c" (x: float4) -> float4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
-atanh_double2 :: proc "c" (x: double2) -> double2 { return {atanh(x.x), atanh(x.y)} }
-atanh_double3 :: proc "c" (x: double3) -> double3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
-atanh_double4 :: proc "c" (x: double4) -> double4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
+@(require_results) atanh_float2 :: proc "c" (x: float2) -> float2 { return {atanh(x.x), atanh(x.y)} }
+@(require_results) atanh_float3 :: proc "c" (x: float3) -> float3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
+@(require_results) atanh_float4 :: proc "c" (x: float4) -> float4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
+@(require_results) atanh_double2 :: proc "c" (x: double2) -> double2 { return {atanh(x.x), atanh(x.y)} }
+@(require_results) atanh_double3 :: proc "c" (x: double3) -> double3 { return {atanh(x.x), atanh(x.y), atanh(x.z)} }
+@(require_results) atanh_double4 :: proc "c" (x: double4) -> double4 { return {atanh(x.x), atanh(x.y), atanh(x.z), atanh(x.w)} }
sqrt :: proc{
sqrt_float,
@@ -346,12 +346,12 @@ sqrt :: proc{
sqrt_double3,
sqrt_double4,
}
-sqrt_float2 :: proc "c" (x: float2) -> float2 { return {sqrt(x.x), sqrt(x.y)} }
-sqrt_float3 :: proc "c" (x: float3) -> float3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
-sqrt_float4 :: proc "c" (x: float4) -> float4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
-sqrt_double2 :: proc "c" (x: double2) -> double2 { return {sqrt(x.x), sqrt(x.y)} }
-sqrt_double3 :: proc "c" (x: double3) -> double3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
-sqrt_double4 :: proc "c" (x: double4) -> double4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
+@(require_results) sqrt_float2 :: proc "c" (x: float2) -> float2 { return {sqrt(x.x), sqrt(x.y)} }
+@(require_results) sqrt_float3 :: proc "c" (x: float3) -> float3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
+@(require_results) sqrt_float4 :: proc "c" (x: float4) -> float4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
+@(require_results) sqrt_double2 :: proc "c" (x: double2) -> double2 { return {sqrt(x.x), sqrt(x.y)} }
+@(require_results) sqrt_double3 :: proc "c" (x: double3) -> double3 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z)} }
+@(require_results) sqrt_double4 :: proc "c" (x: double4) -> double4 { return {sqrt(x.x), sqrt(x.y), sqrt(x.z), sqrt(x.w)} }
rsqrt :: proc{
rsqrt_float,
@@ -363,12 +363,12 @@ rsqrt :: proc{
rsqrt_double3,
rsqrt_double4,
}
-rsqrt_float2 :: proc "c" (x: float2) -> float2 { return {rsqrt(x.x), rsqrt(x.y)} }
-rsqrt_float3 :: proc "c" (x: float3) -> float3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} }
-rsqrt_float4 :: proc "c" (x: float4) -> float4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} }
-rsqrt_double2 :: proc "c" (x: double2) -> double2 { return {rsqrt(x.x), rsqrt(x.y)} }
-rsqrt_double3 :: proc "c" (x: double3) -> double3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} }
-rsqrt_double4 :: proc "c" (x: double4) -> double4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} }
+@(require_results) rsqrt_float2 :: proc "c" (x: float2) -> float2 { return {rsqrt(x.x), rsqrt(x.y)} }
+@(require_results) rsqrt_float3 :: proc "c" (x: float3) -> float3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} }
+@(require_results) rsqrt_float4 :: proc "c" (x: float4) -> float4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} }
+@(require_results) rsqrt_double2 :: proc "c" (x: double2) -> double2 { return {rsqrt(x.x), rsqrt(x.y)} }
+@(require_results) rsqrt_double3 :: proc "c" (x: double3) -> double3 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z)} }
+@(require_results) rsqrt_double4 :: proc "c" (x: double4) -> double4 { return {rsqrt(x.x), rsqrt(x.y), rsqrt(x.z), rsqrt(x.w)} }
rcp :: proc{
rcp_float,
@@ -380,12 +380,12 @@ rcp :: proc{
rcp_double3,
rcp_double4,
}
-rcp_float2 :: proc "c" (x: float2) -> float2 { return {rcp(x.x), rcp(x.y)} }
-rcp_float3 :: proc "c" (x: float3) -> float3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} }
-rcp_float4 :: proc "c" (x: float4) -> float4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} }
-rcp_double2 :: proc "c" (x: double2) -> double2 { return {rcp(x.x), rcp(x.y)} }
-rcp_double3 :: proc "c" (x: double3) -> double3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} }
-rcp_double4 :: proc "c" (x: double4) -> double4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} }
+@(require_results) rcp_float2 :: proc "c" (x: float2) -> float2 { return {rcp(x.x), rcp(x.y)} }
+@(require_results) rcp_float3 :: proc "c" (x: float3) -> float3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} }
+@(require_results) rcp_float4 :: proc "c" (x: float4) -> float4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} }
+@(require_results) rcp_double2 :: proc "c" (x: double2) -> double2 { return {rcp(x.x), rcp(x.y)} }
+@(require_results) rcp_double3 :: proc "c" (x: double3) -> double3 { return {rcp(x.x), rcp(x.y), rcp(x.z)} }
+@(require_results) rcp_double4 :: proc "c" (x: double4) -> double4 { return {rcp(x.x), rcp(x.y), rcp(x.z), rcp(x.w)} }
pow :: proc{
@@ -398,12 +398,12 @@ pow :: proc{
pow_double3,
pow_double4,
}
-pow_float2 :: proc "c" (x, y: float2) -> float2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
-pow_float3 :: proc "c" (x, y: float3) -> float3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
-pow_float4 :: proc "c" (x, y: float4) -> float4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
-pow_double2 :: proc "c" (x, y: double2) -> double2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
-pow_double3 :: proc "c" (x, y: double3) -> double3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
-pow_double4 :: proc "c" (x, y: double4) -> double4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
+@(require_results) pow_float2 :: proc "c" (x, y: float2) -> float2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
+@(require_results) pow_float3 :: proc "c" (x, y: float3) -> float3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
+@(require_results) pow_float4 :: proc "c" (x, y: float4) -> float4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
+@(require_results) pow_double2 :: proc "c" (x, y: double2) -> double2 { return {pow(x.x, y.x), pow(x.y, y.y)} }
+@(require_results) pow_double3 :: proc "c" (x, y: double3) -> double3 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)} }
+@(require_results) pow_double4 :: proc "c" (x, y: double4) -> double4 { return {pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z), pow(x.w, y.w)} }
@@ -417,12 +417,12 @@ exp :: proc{
exp_double3,
exp_double4,
}
-exp_float2 :: proc "c" (x: float2) -> float2 { return {exp(x.x), exp(x.y)} }
-exp_float3 :: proc "c" (x: float3) -> float3 { return {exp(x.x), exp(x.y), exp(x.z)} }
-exp_float4 :: proc "c" (x: float4) -> float4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
-exp_double2 :: proc "c" (x: double2) -> double2 { return {exp(x.x), exp(x.y)} }
-exp_double3 :: proc "c" (x: double3) -> double3 { return {exp(x.x), exp(x.y), exp(x.z)} }
-exp_double4 :: proc "c" (x: double4) -> double4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
+@(require_results) exp_float2 :: proc "c" (x: float2) -> float2 { return {exp(x.x), exp(x.y)} }
+@(require_results) exp_float3 :: proc "c" (x: float3) -> float3 { return {exp(x.x), exp(x.y), exp(x.z)} }
+@(require_results) exp_float4 :: proc "c" (x: float4) -> float4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
+@(require_results) exp_double2 :: proc "c" (x: double2) -> double2 { return {exp(x.x), exp(x.y)} }
+@(require_results) exp_double3 :: proc "c" (x: double3) -> double3 { return {exp(x.x), exp(x.y), exp(x.z)} }
+@(require_results) exp_double4 :: proc "c" (x: double4) -> double4 { return {exp(x.x), exp(x.y), exp(x.z), exp(x.w)} }
@@ -436,12 +436,12 @@ log :: proc{
log_double3,
log_double4,
}
-log_float2 :: proc "c" (x: float2) -> float2 { return {log(x.x), log(x.y)} }
-log_float3 :: proc "c" (x: float3) -> float3 { return {log(x.x), log(x.y), log(x.z)} }
-log_float4 :: proc "c" (x: float4) -> float4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
-log_double2 :: proc "c" (x: double2) -> double2 { return {log(x.x), log(x.y)} }
-log_double3 :: proc "c" (x: double3) -> double3 { return {log(x.x), log(x.y), log(x.z)} }
-log_double4 :: proc "c" (x: double4) -> double4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
+@(require_results) log_float2 :: proc "c" (x: float2) -> float2 { return {log(x.x), log(x.y)} }
+@(require_results) log_float3 :: proc "c" (x: float3) -> float3 { return {log(x.x), log(x.y), log(x.z)} }
+@(require_results) log_float4 :: proc "c" (x: float4) -> float4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
+@(require_results) log_double2 :: proc "c" (x: double2) -> double2 { return {log(x.x), log(x.y)} }
+@(require_results) log_double3 :: proc "c" (x: double3) -> double3 { return {log(x.x), log(x.y), log(x.z)} }
+@(require_results) log_double4 :: proc "c" (x: double4) -> double4 { return {log(x.x), log(x.y), log(x.z), log(x.w)} }
log2 :: proc{
@@ -454,12 +454,12 @@ log2 :: proc{
log2_double3,
log2_double4,
}
-log2_float2 :: proc "c" (x: float2) -> float2 { return {log2(x.x), log2(x.y)} }
-log2_float3 :: proc "c" (x: float3) -> float3 { return {log2(x.x), log2(x.y), log2(x.z)} }
-log2_float4 :: proc "c" (x: float4) -> float4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} }
-log2_double2 :: proc "c" (x: double2) -> double2 { return {log2(x.x), log2(x.y)} }
-log2_double3 :: proc "c" (x: double3) -> double3 { return {log2(x.x), log2(x.y), log2(x.z)} }
-log2_double4 :: proc "c" (x: double4) -> double4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} }
+@(require_results) log2_float2 :: proc "c" (x: float2) -> float2 { return {log2(x.x), log2(x.y)} }
+@(require_results) log2_float3 :: proc "c" (x: float3) -> float3 { return {log2(x.x), log2(x.y), log2(x.z)} }
+@(require_results) log2_float4 :: proc "c" (x: float4) -> float4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} }
+@(require_results) log2_double2 :: proc "c" (x: double2) -> double2 { return {log2(x.x), log2(x.y)} }
+@(require_results) log2_double3 :: proc "c" (x: double3) -> double3 { return {log2(x.x), log2(x.y), log2(x.z)} }
+@(require_results) log2_double4 :: proc "c" (x: double4) -> double4 { return {log2(x.x), log2(x.y), log2(x.z), log2(x.w)} }
@@ -473,12 +473,12 @@ log10 :: proc{
log10_double3,
log10_double4,
}
-log10_float2 :: proc "c" (x: float2) -> float2 { return {log10(x.x), log10(x.y)} }
-log10_float3 :: proc "c" (x: float3) -> float3 { return {log10(x.x), log10(x.y), log10(x.z)} }
-log10_float4 :: proc "c" (x: float4) -> float4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} }
-log10_double2 :: proc "c" (x: double2) -> double2 { return {log10(x.x), log10(x.y)} }
-log10_double3 :: proc "c" (x: double3) -> double3 { return {log10(x.x), log10(x.y), log10(x.z)} }
-log10_double4 :: proc "c" (x: double4) -> double4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} }
+@(require_results) log10_float2 :: proc "c" (x: float2) -> float2 { return {log10(x.x), log10(x.y)} }
+@(require_results) log10_float3 :: proc "c" (x: float3) -> float3 { return {log10(x.x), log10(x.y), log10(x.z)} }
+@(require_results) log10_float4 :: proc "c" (x: float4) -> float4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} }
+@(require_results) log10_double2 :: proc "c" (x: double2) -> double2 { return {log10(x.x), log10(x.y)} }
+@(require_results) log10_double3 :: proc "c" (x: double3) -> double3 { return {log10(x.x), log10(x.y), log10(x.z)} }
+@(require_results) log10_double4 :: proc "c" (x: double4) -> double4 { return {log10(x.x), log10(x.y), log10(x.z), log10(x.w)} }
@@ -493,12 +493,12 @@ exp2 :: proc{
exp2_double3,
exp2_double4,
}
-exp2_float2 :: proc "c" (x: float2) -> float2 { return {exp2(x.x), exp2(x.y)} }
-exp2_float3 :: proc "c" (x: float3) -> float3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
-exp2_float4 :: proc "c" (x: float4) -> float4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
-exp2_double2 :: proc "c" (x: double2) -> double2 { return {exp2(x.x), exp2(x.y)} }
-exp2_double3 :: proc "c" (x: double3) -> double3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
-exp2_double4 :: proc "c" (x: double4) -> double4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
+@(require_results) exp2_float2 :: proc "c" (x: float2) -> float2 { return {exp2(x.x), exp2(x.y)} }
+@(require_results) exp2_float3 :: proc "c" (x: float3) -> float3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
+@(require_results) exp2_float4 :: proc "c" (x: float4) -> float4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
+@(require_results) exp2_double2 :: proc "c" (x: double2) -> double2 { return {exp2(x.x), exp2(x.y)} }
+@(require_results) exp2_double3 :: proc "c" (x: double3) -> double3 { return {exp2(x.x), exp2(x.y), exp2(x.z)} }
+@(require_results) exp2_double4 :: proc "c" (x: double4) -> double4 { return {exp2(x.x), exp2(x.y), exp2(x.z), exp2(x.w)} }
sign :: proc{
@@ -519,20 +519,20 @@ sign :: proc{
sign_uint3,
sign_uint4,
}
-sign_int :: proc "c" (x: int) -> int { return -1 if x < 0 else +1 if x > 0 else 0 }
-sign_uint :: proc "c" (x: uint) -> uint { return +1 if x > 0 else 0 }
-sign_float2 :: proc "c" (x: float2) -> float2 { return {sign(x.x), sign(x.y)} }
-sign_float3 :: proc "c" (x: float3) -> float3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_float4 :: proc "c" (x: float4) -> float4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
-sign_double2 :: proc "c" (x: double2) -> double2 { return {sign(x.x), sign(x.y)} }
-sign_double3 :: proc "c" (x: double3) -> double3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_double4 :: proc "c" (x: double4) -> double4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
-sign_int2 :: proc "c" (x: int2) -> int2 { return {sign(x.x), sign(x.y)} }
-sign_int3 :: proc "c" (x: int3) -> int3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_int4 :: proc "c" (x: int4) -> int4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
-sign_uint2 :: proc "c" (x: uint2) -> uint2 { return {sign(x.x), sign(x.y)} }
-sign_uint3 :: proc "c" (x: uint3) -> uint3 { return {sign(x.x), sign(x.y), sign(x.z)} }
-sign_uint4 :: proc "c" (x: uint4) -> uint4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_int :: proc "c" (x: int) -> int { return -1 if x < 0 else +1 if x > 0 else 0 }
+@(require_results) sign_uint :: proc "c" (x: uint) -> uint { return +1 if x > 0 else 0 }
+@(require_results) sign_float2 :: proc "c" (x: float2) -> float2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_float3 :: proc "c" (x: float3) -> float3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_float4 :: proc "c" (x: float4) -> float4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_double2 :: proc "c" (x: double2) -> double2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_double3 :: proc "c" (x: double3) -> double3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_double4 :: proc "c" (x: double4) -> double4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_int2 :: proc "c" (x: int2) -> int2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_int3 :: proc "c" (x: int3) -> int3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_int4 :: proc "c" (x: int4) -> int4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
+@(require_results) sign_uint2 :: proc "c" (x: uint2) -> uint2 { return {sign(x.x), sign(x.y)} }
+@(require_results) sign_uint3 :: proc "c" (x: uint3) -> uint3 { return {sign(x.x), sign(x.y), sign(x.z)} }
+@(require_results) sign_uint4 :: proc "c" (x: uint4) -> uint4 { return {sign(x.x), sign(x.y), sign(x.z), sign(x.w)} }
floor :: proc{
floor_float,
@@ -544,12 +544,12 @@ floor :: proc{
floor_double3,
floor_double4,
}
-floor_float2 :: proc "c" (x: float2) -> float2 { return {floor(x.x), floor(x.y)} }
-floor_float3 :: proc "c" (x: float3) -> float3 { return {floor(x.x), floor(x.y), floor(x.z)} }
-floor_float4 :: proc "c" (x: float4) -> float4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
-floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x.y)} }
-floor_double3 :: proc "c" (x: double3) -> double3 { return {floor(x.x), floor(x.y), floor(x.z)} }
-floor_double4 :: proc "c" (x: double4) -> double4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+@(require_results) floor_float2 :: proc "c" (x: float2) -> float2 { return {floor(x.x), floor(x.y)} }
+@(require_results) floor_float3 :: proc "c" (x: float3) -> float3 { return {floor(x.x), floor(x.y), floor(x.z)} }
+@(require_results) floor_float4 :: proc "c" (x: float4) -> float4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+@(require_results) floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x.y)} }
+@(require_results) floor_double3 :: proc "c" (x: double3) -> double3 { return {floor(x.x), floor(x.y), floor(x.z)} }
+@(require_results) floor_double4 :: proc "c" (x: double4) -> double4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
round :: proc{
round_float,
@@ -561,12 +561,12 @@ round :: proc{
round_double3,
round_double4,
}
-round_float2 :: proc "c" (x: float2) -> float2 { return {round(x.x), round(x.y)} }
-round_float3 :: proc "c" (x: float3) -> float3 { return {round(x.x), round(x.y), round(x.z)} }
-round_float4 :: proc "c" (x: float4) -> float4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
-round_double2 :: proc "c" (x: double2) -> double2 { return {round(x.x), round(x.y)} }
-round_double3 :: proc "c" (x: double3) -> double3 { return {round(x.x), round(x.y), round(x.z)} }
-round_double4 :: proc "c" (x: double4) -> double4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+@(require_results) round_float2 :: proc "c" (x: float2) -> float2 { return {round(x.x), round(x.y)} }
+@(require_results) round_float3 :: proc "c" (x: float3) -> float3 { return {round(x.x), round(x.y), round(x.z)} }
+@(require_results) round_float4 :: proc "c" (x: float4) -> float4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
+@(require_results) round_double2 :: proc "c" (x: double2) -> double2 { return {round(x.x), round(x.y)} }
+@(require_results) round_double3 :: proc "c" (x: double3) -> double3 { return {round(x.x), round(x.y), round(x.z)} }
+@(require_results) round_double4 :: proc "c" (x: double4) -> double4 { return {round(x.x), round(x.y), round(x.z), round(x.w)} }
ceil :: proc{
@@ -579,22 +579,22 @@ ceil :: proc{
ceil_double3,
ceil_double4,
}
-ceil_float2 :: proc "c" (x: float2) -> float2 { return {ceil(x.x), ceil(x.y)} }
-ceil_float3 :: proc "c" (x: float3) -> float3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
-ceil_float4 :: proc "c" (x: float4) -> float4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
-ceil_double2 :: proc "c" (x: double2) -> double2 { return {ceil(x.x), ceil(x.y)} }
-ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
-ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+@(require_results) ceil_float2 :: proc "c" (x: float2) -> float2 { return {ceil(x.x), ceil(x.y)} }
+@(require_results) ceil_float3 :: proc "c" (x: float3) -> float3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
+@(require_results) ceil_float4 :: proc "c" (x: float4) -> float4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
+@(require_results) ceil_double2 :: proc "c" (x: double2) -> double2 { return {ceil(x.x), ceil(x.y)} }
+@(require_results) ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y), ceil(x.z)} }
+@(require_results) ceil_double4 :: proc "c" (x: double4) -> double4 { return {ceil(x.x), ceil(x.y), ceil(x.z), ceil(x.w)} }
-isfinite_float :: proc "c" (x: float) -> bool { return !isinf_float(x) }
-isfinite_float2 :: proc "c" (x: float2) -> bool2 { return {isfinite_float(x.x), isfinite_float(x.y)} }
-isfinite_float3 :: proc "c" (x: float3) -> bool3 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z)} }
-isfinite_float4 :: proc "c" (x: float4) -> bool4 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z), isfinite_float(x.w)} }
-isfinite_double :: proc "c" (x: double) -> bool { return !isinf_double(x) }
-isfinite_double2 :: proc "c" (x: double2) -> bool2 { return {isfinite_double(x.x), isfinite_double(x.y)} }
-isfinite_double3 :: proc "c" (x: double3) -> bool3 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z)} }
-isfinite_double4 :: proc "c" (x: double4) -> bool4 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z), isfinite_double(x.w)} }
+@(require_results) isfinite_float :: proc "c" (x: float) -> bool { return !isinf_float(x) }
+@(require_results) isfinite_float2 :: proc "c" (x: float2) -> bool2 { return {isfinite_float(x.x), isfinite_float(x.y)} }
+@(require_results) isfinite_float3 :: proc "c" (x: float3) -> bool3 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z)} }
+@(require_results) isfinite_float4 :: proc "c" (x: float4) -> bool4 { return {isfinite_float(x.x), isfinite_float(x.y), isfinite_float(x.z), isfinite_float(x.w)} }
+@(require_results) isfinite_double :: proc "c" (x: double) -> bool { return !isinf_double(x) }
+@(require_results) isfinite_double2 :: proc "c" (x: double2) -> bool2 { return {isfinite_double(x.x), isfinite_double(x.y)} }
+@(require_results) isfinite_double3 :: proc "c" (x: double3) -> bool3 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z)} }
+@(require_results) isfinite_double4 :: proc "c" (x: double4) -> bool4 { return {isfinite_double(x.x), isfinite_double(x.y), isfinite_double(x.z), isfinite_double(x.w)} }
// isfinite is the opposite of isinf and returns true if the number is neither positive-infinite or negative-infinite
isfinite :: proc{
@@ -609,14 +609,14 @@ isfinite :: proc{
}
-isinf_float :: proc "c" (x: float) -> bool { return x * 0.5 == x }
-isinf_float2 :: proc "c" (x: float2) -> bool2 { return {isinf_float(x.x), isinf_float(x.y)} }
-isinf_float3 :: proc "c" (x: float3) -> bool3 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z)} }
-isinf_float4 :: proc "c" (x: float4) -> bool4 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z), isinf_float(x.w)} }
-isinf_double :: proc "c" (x: double) -> bool { return x * 0.5 == x }
-isinf_double2 :: proc "c" (x: double2) -> bool2 { return {isinf_double(x.x), isinf_double(x.y)} }
-isinf_double3 :: proc "c" (x: double3) -> bool3 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z)} }
-isinf_double4 :: proc "c" (x: double4) -> bool4 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z), isinf_double(x.w)} }
+@(require_results) isinf_float :: proc "c" (x: float) -> bool { return x * 0.5 == x }
+@(require_results) isinf_float2 :: proc "c" (x: float2) -> bool2 { return {isinf_float(x.x), isinf_float(x.y)} }
+@(require_results) isinf_float3 :: proc "c" (x: float3) -> bool3 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z)} }
+@(require_results) isinf_float4 :: proc "c" (x: float4) -> bool4 { return {isinf_float(x.x), isinf_float(x.y), isinf_float(x.z), isinf_float(x.w)} }
+@(require_results) isinf_double :: proc "c" (x: double) -> bool { return x * 0.5 == x }
+@(require_results) isinf_double2 :: proc "c" (x: double2) -> bool2 { return {isinf_double(x.x), isinf_double(x.y)} }
+@(require_results) isinf_double3 :: proc "c" (x: double3) -> bool3 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z)} }
+@(require_results) isinf_double4 :: proc "c" (x: double4) -> bool4 { return {isinf_double(x.x), isinf_double(x.y), isinf_double(x.z), isinf_double(x.w)} }
// isinf is the opposite of isfinite and returns true if the number is either positive-infinite or negative-infinite
isinf :: proc{
@@ -631,12 +631,12 @@ isinf :: proc{
}
-isnan_float2 :: proc "c" (x: float2) -> bool2 { return {isnan_float(x.x), isnan_float(x.y)} }
-isnan_float3 :: proc "c" (x: float3) -> bool3 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z)} }
-isnan_float4 :: proc "c" (x: float4) -> bool4 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z), isnan_float(x.w)} }
-isnan_double2 :: proc "c" (x: double2) -> bool2 { return {isnan_double(x.x), isnan_double(x.y)} }
-isnan_double3 :: proc "c" (x: double3) -> bool3 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z)} }
-isnan_double4 :: proc "c" (x: double4) -> bool4 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z), isnan_double(x.w)} }
+@(require_results) isnan_float2 :: proc "c" (x: float2) -> bool2 { return {isnan_float(x.x), isnan_float(x.y)} }
+@(require_results) isnan_float3 :: proc "c" (x: float3) -> bool3 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z)} }
+@(require_results) isnan_float4 :: proc "c" (x: float4) -> bool4 { return {isnan_float(x.x), isnan_float(x.y), isnan_float(x.z), isnan_float(x.w)} }
+@(require_results) isnan_double2 :: proc "c" (x: double2) -> bool2 { return {isnan_double(x.x), isnan_double(x.y)} }
+@(require_results) isnan_double3 :: proc "c" (x: double3) -> bool3 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z)} }
+@(require_results) isnan_double4 :: proc "c" (x: double4) -> bool4 { return {isnan_double(x.x), isnan_double(x.y), isnan_double(x.z), isnan_double(x.w)} }
// isnan returns true if the input value is the special case of Not-A-Number
isnan :: proc{
@@ -660,12 +660,12 @@ fmod :: proc{
fmod_double3,
fmod_double4,
}
-fmod_float2 :: proc "c" (x, y: float2) -> float2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} }
-fmod_float3 :: proc "c" (x, y: float3) -> float3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} }
-fmod_float4 :: proc "c" (x, y: float4) -> float4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} }
-fmod_double2 :: proc "c" (x, y: double2) -> double2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} }
-fmod_double3 :: proc "c" (x, y: double3) -> double3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} }
-fmod_double4 :: proc "c" (x, y: double4) -> double4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} }
+@(require_results) fmod_float2 :: proc "c" (x, y: float2) -> float2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} }
+@(require_results) fmod_float3 :: proc "c" (x, y: float3) -> float3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} }
+@(require_results) fmod_float4 :: proc "c" (x, y: float4) -> float4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} }
+@(require_results) fmod_double2 :: proc "c" (x, y: double2) -> double2 { return {fmod(x.x, y.x), fmod(x.y, y.y)} }
+@(require_results) fmod_double3 :: proc "c" (x, y: double3) -> double3 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z)} }
+@(require_results) fmod_double4 :: proc "c" (x, y: double4) -> double4 { return {fmod(x.x, y.x), fmod(x.y, y.y), fmod(x.z, y.z), fmod(x.w, y.w)} }
frac :: proc{
@@ -678,12 +678,12 @@ frac :: proc{
frac_double3,
frac_double4,
}
-frac_float2 :: proc "c" (x: float2) -> float2 { return {frac(x.x), frac(x.y)} }
-frac_float3 :: proc "c" (x: float3) -> float3 { return {frac(x.x), frac(x.y), frac(x.z)} }
-frac_float4 :: proc "c" (x: float4) -> float4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} }
-frac_double2 :: proc "c" (x: double2) -> double2 { return {frac(x.x), frac(x.y)} }
-frac_double3 :: proc "c" (x: double3) -> double3 { return {frac(x.x), frac(x.y), frac(x.z)} }
-frac_double4 :: proc "c" (x: double4) -> double4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} }
+@(require_results) frac_float2 :: proc "c" (x: float2) -> float2 { return {frac(x.x), frac(x.y)} }
+@(require_results) frac_float3 :: proc "c" (x: float3) -> float3 { return {frac(x.x), frac(x.y), frac(x.z)} }
+@(require_results) frac_float4 :: proc "c" (x: float4) -> float4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} }
+@(require_results) frac_double2 :: proc "c" (x: double2) -> double2 { return {frac(x.x), frac(x.y)} }
+@(require_results) frac_double3 :: proc "c" (x: double3) -> double3 { return {frac(x.x), frac(x.y), frac(x.z)} }
+@(require_results) frac_double4 :: proc "c" (x: double4) -> double4 { return {frac(x.x), frac(x.y), frac(x.z), frac(x.w)} }
@@ -697,14 +697,14 @@ radians :: proc{
radians_double3,
radians_double4,
}
-radians_float :: proc "c" (degrees: float) -> float { return degrees * TAU / 360.0 }
-radians_double :: proc "c" (degrees: double) -> double { return degrees * TAU / 360.0 }
-radians_float2 :: proc "c" (degrees: float2) -> float2 { return degrees * TAU / 360.0 }
-radians_float3 :: proc "c" (degrees: float3) -> float3 { return degrees * TAU / 360.0 }
-radians_float4 :: proc "c" (degrees: float4) -> float4 { return degrees * TAU / 360.0 }
-radians_double2 :: proc "c" (degrees: double2) -> double2 { return degrees * TAU / 360.0 }
-radians_double3 :: proc "c" (degrees: double3) -> double3 { return degrees * TAU / 360.0 }
-radians_double4 :: proc "c" (degrees: double4) -> double4 { return degrees * TAU / 360.0 }
+@(require_results) radians_float :: proc "c" (degrees: float) -> float { return degrees * TAU / 360.0 }
+@(require_results) radians_double :: proc "c" (degrees: double) -> double { return degrees * TAU / 360.0 }
+@(require_results) radians_float2 :: proc "c" (degrees: float2) -> float2 { return degrees * TAU / 360.0 }
+@(require_results) radians_float3 :: proc "c" (degrees: float3) -> float3 { return degrees * TAU / 360.0 }
+@(require_results) radians_float4 :: proc "c" (degrees: float4) -> float4 { return degrees * TAU / 360.0 }
+@(require_results) radians_double2 :: proc "c" (degrees: double2) -> double2 { return degrees * TAU / 360.0 }
+@(require_results) radians_double3 :: proc "c" (degrees: double3) -> double3 { return degrees * TAU / 360.0 }
+@(require_results) radians_double4 :: proc "c" (degrees: double4) -> double4 { return degrees * TAU / 360.0 }
degrees :: proc{
@@ -717,14 +717,14 @@ degrees :: proc{
degrees_double3,
degrees_double4,
}
-degrees_float :: proc "c" (radians: float) -> float { return radians * 360.0 / TAU }
-degrees_double :: proc "c" (radians: double) -> double { return radians * 360.0 / TAU }
-degrees_float2 :: proc "c" (radians: float2) -> float2 { return radians * 360.0 / TAU }
-degrees_float3 :: proc "c" (radians: float3) -> float3 { return radians * 360.0 / TAU }
-degrees_float4 :: proc "c" (radians: float4) -> float4 { return radians * 360.0 / TAU }
-degrees_double2 :: proc "c" (radians: double2) -> double2 { return radians * 360.0 / TAU }
-degrees_double3 :: proc "c" (radians: double3) -> double3 { return radians * 360.0 / TAU }
-degrees_double4 :: proc "c" (radians: double4) -> double4 { return radians * 360.0 / TAU }
+@(require_results) degrees_float :: proc "c" (radians: float) -> float { return radians * 360.0 / TAU }
+@(require_results) degrees_double :: proc "c" (radians: double) -> double { return radians * 360.0 / TAU }
+@(require_results) degrees_float2 :: proc "c" (radians: float2) -> float2 { return radians * 360.0 / TAU }
+@(require_results) degrees_float3 :: proc "c" (radians: float3) -> float3 { return radians * 360.0 / TAU }
+@(require_results) degrees_float4 :: proc "c" (radians: float4) -> float4 { return radians * 360.0 / TAU }
+@(require_results) degrees_double2 :: proc "c" (radians: double2) -> double2 { return radians * 360.0 / TAU }
+@(require_results) degrees_double3 :: proc "c" (radians: double3) -> double3 { return radians * 360.0 / TAU }
+@(require_results) degrees_double4 :: proc "c" (radians: double4) -> double4 { return radians * 360.0 / TAU }
min :: proc{
min_int,
@@ -744,22 +744,22 @@ min :: proc{
min_uint3,
min_uint4,
}
-min_int :: proc "c" (x, y: int) -> int { return builtin.min(x, y) }
-min_uint :: proc "c" (x, y: uint) -> uint { return builtin.min(x, y) }
-min_float :: proc "c" (x, y: float) -> float { return builtin.min(x, y) }
-min_double :: proc "c" (x, y: double) -> double { return builtin.min(x, y) }
-min_float2 :: proc "c" (x, y: float2) -> float2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_float3 :: proc "c" (x, y: float3) -> float3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_float4 :: proc "c" (x, y: float4) -> float4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
-min_double2 :: proc "c" (x, y: double2) -> double2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_double3 :: proc "c" (x, y: double3) -> double3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_double4 :: proc "c" (x, y: double4) -> double4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
-min_int2 :: proc "c" (x, y: int2) -> int2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_int3 :: proc "c" (x, y: int3) -> int3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_int4 :: proc "c" (x, y: int4) -> int4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
-min_uint2 :: proc "c" (x, y: uint2) -> uint2 { return {min(x.x, y.x), min(x.y, y.y)} }
-min_uint3 :: proc "c" (x, y: uint3) -> uint3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
-min_uint4 :: proc "c" (x, y: uint4) -> uint4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_int :: proc "c" (x, y: int) -> int { return builtin.min(x, y) }
+@(require_results) min_uint :: proc "c" (x, y: uint) -> uint { return builtin.min(x, y) }
+@(require_results) min_float :: proc "c" (x, y: float) -> float { return builtin.min(x, y) }
+@(require_results) min_double :: proc "c" (x, y: double) -> double { return builtin.min(x, y) }
+@(require_results) min_float2 :: proc "c" (x, y: float2) -> float2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_float3 :: proc "c" (x, y: float3) -> float3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_float4 :: proc "c" (x, y: float4) -> float4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_double2 :: proc "c" (x, y: double2) -> double2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_double3 :: proc "c" (x, y: double3) -> double3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_double4 :: proc "c" (x, y: double4) -> double4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_int2 :: proc "c" (x, y: int2) -> int2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_int3 :: proc "c" (x, y: int3) -> int3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_int4 :: proc "c" (x, y: int4) -> int4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
+@(require_results) min_uint2 :: proc "c" (x, y: uint2) -> uint2 { return {min(x.x, y.x), min(x.y, y.y)} }
+@(require_results) min_uint3 :: proc "c" (x, y: uint3) -> uint3 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z)} }
+@(require_results) min_uint4 :: proc "c" (x, y: uint4) -> uint4 { return {min(x.x, y.x), min(x.y, y.y), min(x.z, y.z), min(x.w, y.w)} }
max :: proc{
@@ -780,22 +780,22 @@ max :: proc{
max_uint3,
max_uint4,
}
-max_int :: proc "c" (x, y: int) -> int { return builtin.max(x, y) }
-max_uint :: proc "c" (x, y: uint) -> uint { return builtin.max(x, y) }
-max_float :: proc "c" (x, y: float) -> float { return builtin.max(x, y) }
-max_double :: proc "c" (x, y: double) -> double { return builtin.max(x, y) }
-max_float2 :: proc "c" (x, y: float2) -> float2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_float3 :: proc "c" (x, y: float3) -> float3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_float4 :: proc "c" (x, y: float4) -> float4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
-max_double2 :: proc "c" (x, y: double2) -> double2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_double3 :: proc "c" (x, y: double3) -> double3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_double4 :: proc "c" (x, y: double4) -> double4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
-max_int2 :: proc "c" (x, y: int2) -> int2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_int3 :: proc "c" (x, y: int3) -> int3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_int4 :: proc "c" (x, y: int4) -> int4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
-max_uint2 :: proc "c" (x, y: uint2) -> uint2 { return {max(x.x, y.x), max(x.y, y.y)} }
-max_uint3 :: proc "c" (x, y: uint3) -> uint3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
-max_uint4 :: proc "c" (x, y: uint4) -> uint4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_int :: proc "c" (x, y: int) -> int { return builtin.max(x, y) }
+@(require_results) max_uint :: proc "c" (x, y: uint) -> uint { return builtin.max(x, y) }
+@(require_results) max_float :: proc "c" (x, y: float) -> float { return builtin.max(x, y) }
+@(require_results) max_double :: proc "c" (x, y: double) -> double { return builtin.max(x, y) }
+@(require_results) max_float2 :: proc "c" (x, y: float2) -> float2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_float3 :: proc "c" (x, y: float3) -> float3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_float4 :: proc "c" (x, y: float4) -> float4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_double2 :: proc "c" (x, y: double2) -> double2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_double3 :: proc "c" (x, y: double3) -> double3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_double4 :: proc "c" (x, y: double4) -> double4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_int2 :: proc "c" (x, y: int2) -> int2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_int3 :: proc "c" (x, y: int3) -> int3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_int4 :: proc "c" (x, y: int4) -> int4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
+@(require_results) max_uint2 :: proc "c" (x, y: uint2) -> uint2 { return {max(x.x, y.x), max(x.y, y.y)} }
+@(require_results) max_uint3 :: proc "c" (x, y: uint3) -> uint3 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z)} }
+@(require_results) max_uint4 :: proc "c" (x, y: uint4) -> uint4 { return {max(x.x, y.x), max(x.y, y.y), max(x.z, y.z), max(x.w, y.w)} }
@@ -817,22 +817,22 @@ clamp :: proc{
clamp_uint3,
clamp_uint4,
}
-clamp_int :: proc "c" (x, y, z: int) -> int { return builtin.clamp(x, y, z) }
-clamp_uint :: proc "c" (x, y, z: uint) -> uint { return builtin.clamp(x, y, z) }
-clamp_float :: proc "c" (x, y, z: float) -> float { return builtin.clamp(x, y, z) }
-clamp_double :: proc "c" (x, y, z: double) -> double { return builtin.clamp(x, y, z) }
-clamp_float2 :: proc "c" (x, y, z: float2) -> float2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_float3 :: proc "c" (x, y, z: float3) -> float3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_float4 :: proc "c" (x, y, z: float4) -> float4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
-clamp_double2 :: proc "c" (x, y, z: double2) -> double2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_double3 :: proc "c" (x, y, z: double3) -> double3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_double4 :: proc "c" (x, y, z: double4) -> double4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
-clamp_int2 :: proc "c" (x, y, z: int2) -> int2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_int3 :: proc "c" (x, y, z: int3) -> int3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_int4 :: proc "c" (x, y, z: int4) -> int4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
-clamp_uint2 :: proc "c" (x, y, z: uint2) -> uint2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
-clamp_uint3 :: proc "c" (x, y, z: uint3) -> uint3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
-clamp_uint4 :: proc "c" (x, y, z: uint4) -> uint4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_int :: proc "c" (x, y, z: int) -> int { return builtin.clamp(x, y, z) }
+@(require_results) clamp_uint :: proc "c" (x, y, z: uint) -> uint { return builtin.clamp(x, y, z) }
+@(require_results) clamp_float :: proc "c" (x, y, z: float) -> float { return builtin.clamp(x, y, z) }
+@(require_results) clamp_double :: proc "c" (x, y, z: double) -> double { return builtin.clamp(x, y, z) }
+@(require_results) clamp_float2 :: proc "c" (x, y, z: float2) -> float2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_float3 :: proc "c" (x, y, z: float3) -> float3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_float4 :: proc "c" (x, y, z: float4) -> float4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_double2 :: proc "c" (x, y, z: double2) -> double2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_double3 :: proc "c" (x, y, z: double3) -> double3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_double4 :: proc "c" (x, y, z: double4) -> double4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_int2 :: proc "c" (x, y, z: int2) -> int2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_int3 :: proc "c" (x, y, z: int3) -> int3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_int4 :: proc "c" (x, y, z: int4) -> int4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
+@(require_results) clamp_uint2 :: proc "c" (x, y, z: uint2) -> uint2 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y)} }
+@(require_results) clamp_uint3 :: proc "c" (x, y, z: uint3) -> uint3 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z)} }
+@(require_results) clamp_uint4 :: proc "c" (x, y, z: uint4) -> uint4 { return {clamp(x.x, y.x, z.x), clamp(x.y, y.y, z.y), clamp(x.z, y.z, z.z), clamp(x.w, y.w, z.w)} }
saturate :: proc{
saturate_int,
@@ -852,22 +852,22 @@ saturate :: proc{
saturate_uint3,
saturate_uint4,
}
-saturate_int :: proc "c" (v: int) -> int { return builtin.clamp(v, 0, 1) }
-saturate_uint :: proc "c" (v: uint) -> uint { return builtin.clamp(v, 0, 1) }
-saturate_float :: proc "c" (v: float) -> float { return builtin.clamp(v, 0, 1) }
-saturate_double :: proc "c" (v: double) -> double { return builtin.clamp(v, 0, 1) }
-saturate_float2 :: proc "c" (v: float2) -> float2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_float3 :: proc "c" (v: float3) -> float3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_float4 :: proc "c" (v: float4) -> float4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
-saturate_double2 :: proc "c" (v: double2) -> double2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_double3 :: proc "c" (v: double3) -> double3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_double4 :: proc "c" (v: double4) -> double4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
-saturate_int2 :: proc "c" (v: int2) -> int2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_int3 :: proc "c" (v: int3) -> int3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_int4 :: proc "c" (v: int4) -> int4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
-saturate_uint2 :: proc "c" (v: uint2) -> uint2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
-saturate_uint3 :: proc "c" (v: uint3) -> uint3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
-saturate_uint4 :: proc "c" (v: uint4) -> uint4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_int :: proc "c" (v: int) -> int { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_uint :: proc "c" (v: uint) -> uint { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_float :: proc "c" (v: float) -> float { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_double :: proc "c" (v: double) -> double { return builtin.clamp(v, 0, 1) }
+@(require_results) saturate_float2 :: proc "c" (v: float2) -> float2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_float3 :: proc "c" (v: float3) -> float3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_float4 :: proc "c" (v: float4) -> float4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_double2 :: proc "c" (v: double2) -> double2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_double3 :: proc "c" (v: double3) -> double3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_double4 :: proc "c" (v: double4) -> double4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_int2 :: proc "c" (v: int2) -> int2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_int3 :: proc "c" (v: int3) -> int3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_int4 :: proc "c" (v: int4) -> int4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
+@(require_results) saturate_uint2 :: proc "c" (v: uint2) -> uint2 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1)} }
+@(require_results) saturate_uint3 :: proc "c" (v: uint3) -> uint3 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1)} }
+@(require_results) saturate_uint4 :: proc "c" (v: uint4) -> uint4 { return {builtin.clamp(v.x, 0, 1), builtin.clamp(v.y, 0, 1), builtin.clamp(v.z, 0, 1), builtin.clamp(v.w, 0, 1)} }
lerp :: proc{
@@ -880,14 +880,14 @@ lerp :: proc{
lerp_double3,
lerp_double4,
}
-lerp_float :: proc "c" (x, y, t: float) -> float { return x*(1-t) + y*t }
-lerp_double :: proc "c" (x, y, t: double) -> double { return x*(1-t) + y*t }
-lerp_float2 :: proc "c" (x, y, t: float2) -> float2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
-lerp_float3 :: proc "c" (x, y, t: float3) -> float3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
-lerp_float4 :: proc "c" (x, y, t: float4) -> float4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
-lerp_double2 :: proc "c" (x, y, t: double2) -> double2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
-lerp_double3 :: proc "c" (x, y, t: double3) -> double3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
-lerp_double4 :: proc "c" (x, y, t: double4) -> double4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
+@(require_results) lerp_float :: proc "c" (x, y, t: float) -> float { return x*(1-t) + y*t }
+@(require_results) lerp_double :: proc "c" (x, y, t: double) -> double { return x*(1-t) + y*t }
+@(require_results) lerp_float2 :: proc "c" (x, y, t: float2) -> float2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
+@(require_results) lerp_float3 :: proc "c" (x, y, t: float3) -> float3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
+@(require_results) lerp_float4 :: proc "c" (x, y, t: float4) -> float4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
+@(require_results) lerp_double2 :: proc "c" (x, y, t: double2) -> double2 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y)} }
+@(require_results) lerp_double3 :: proc "c" (x, y, t: double3) -> double3 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, t.y), lerp(x.z, y.z, t.z)} }
+@(require_results) lerp_double4 :: proc "c" (x, y, t: double4) -> double4 { return {lerp(x.x, y.x, t.x), lerp(x.y, y.y, y.y), lerp(x.z, y.z, t.z), lerp(x.w, y.w, t.w)} }
step :: proc{
@@ -900,14 +900,14 @@ step :: proc{
step_double3,
step_double4,
}
-step_float :: proc "c" (edge, x: float) -> float { return 0 if x < edge else 1 }
-step_double :: proc "c" (edge, x: double) -> double { return 0 if x < edge else 1 }
-step_float2 :: proc "c" (edge, x: float2) -> float2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
-step_float3 :: proc "c" (edge, x: float3) -> float3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
-step_float4 :: proc "c" (edge, x: float4) -> float4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
-step_double2 :: proc "c" (edge, x: double2) -> double2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
-step_double3 :: proc "c" (edge, x: double3) -> double3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
-step_double4 :: proc "c" (edge, x: double4) -> double4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
+@(require_results) step_float :: proc "c" (edge, x: float) -> float { return 0 if x < edge else 1 }
+@(require_results) step_double :: proc "c" (edge, x: double) -> double { return 0 if x < edge else 1 }
+@(require_results) step_float2 :: proc "c" (edge, x: float2) -> float2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
+@(require_results) step_float3 :: proc "c" (edge, x: float3) -> float3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
+@(require_results) step_float4 :: proc "c" (edge, x: float4) -> float4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
+@(require_results) step_double2 :: proc "c" (edge, x: double2) -> double2 { return {step(edge.x, x.x), step(edge.y, x.y)} }
+@(require_results) step_double3 :: proc "c" (edge, x: double3) -> double3 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z)} }
+@(require_results) step_double4 :: proc "c" (edge, x: double4) -> double4 { return {step(edge.x, x.x), step(edge.y, x.y), step(edge.z, x.z), step(edge.w, x.w)} }
smoothstep :: proc{
smoothstep_float,
@@ -919,20 +919,20 @@ smoothstep :: proc{
smoothstep_double3,
smoothstep_double4,
}
-smoothstep_float :: proc "c" (edge0, edge1, x: float) -> float {
+@(require_results) smoothstep_float :: proc "c" (edge0, edge1, x: float) -> float {
y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
return y * y * (3 - 2*y)
}
-smoothstep_double :: proc "c" (edge0, edge1, x: double) -> double {
+@(require_results) smoothstep_double :: proc "c" (edge0, edge1, x: double) -> double {
y := clamp(((x-edge0) / (edge1 - edge0)), 0, 1)
return y * y * (3 - 2*y)
}
-smoothstep_float2 :: proc "c" (edge0, edge1, x: float2) -> float2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
-smoothstep_float3 :: proc "c" (edge0, edge1, x: float3) -> float3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
-smoothstep_float4 :: proc "c" (edge0, edge1, x: float4) -> float4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
-smoothstep_double2 :: proc "c" (edge0, edge1, x: double2) -> double2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
-smoothstep_double3 :: proc "c" (edge0, edge1, x: double3) -> double3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
-smoothstep_double4 :: proc "c" (edge0, edge1, x: double4) -> double4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+@(require_results) smoothstep_float2 :: proc "c" (edge0, edge1, x: float2) -> float2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+@(require_results) smoothstep_float3 :: proc "c" (edge0, edge1, x: float3) -> float3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+@(require_results) smoothstep_float4 :: proc "c" (edge0, edge1, x: float4) -> float4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
+@(require_results) smoothstep_double2 :: proc "c" (edge0, edge1, x: double2) -> double2 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y)} }
+@(require_results) smoothstep_double3 :: proc "c" (edge0, edge1, x: double3) -> double3 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z)} }
+@(require_results) smoothstep_double4 :: proc "c" (edge0, edge1, x: double4) -> double4 { return {smoothstep(edge0.x, edge1.x, x.x), smoothstep(edge0.y, edge1.y, x.y), smoothstep(edge0.z, edge1.z, x.z), smoothstep(edge0.w, edge1.w, x.w)} }
abs :: proc{
@@ -953,22 +953,22 @@ abs :: proc{
abs_uint3,
abs_uint4,
}
-abs_int :: proc "c" (x: int) -> int { return builtin.abs(x) }
-abs_uint :: proc "c" (x: uint) -> uint { return x }
-abs_float :: proc "c" (x: float) -> float { return builtin.abs(x) }
-abs_double :: proc "c" (x: double) -> double { return builtin.abs(x) }
-abs_float2 :: proc "c" (x: float2) -> float2 { return {abs(x.x), abs(x.y)} }
-abs_float3 :: proc "c" (x: float3) -> float3 { return {abs(x.x), abs(x.y), abs(x.z)} }
-abs_float4 :: proc "c" (x: float4) -> float4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
-abs_double2 :: proc "c" (x: double2) -> double2 { return {abs(x.x), abs(x.y)} }
-abs_double3 :: proc "c" (x: double3) -> double3 { return {abs(x.x), abs(x.y), abs(x.z)} }
-abs_double4 :: proc "c" (x: double4) -> double4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
-abs_int2 :: proc "c" (x: int2) -> int2 { return {abs(x.x), abs(x.y)} }
-abs_int3 :: proc "c" (x: int3) -> int3 { return {abs(x.x), abs(x.y), abs(x.z)} }
-abs_int4 :: proc "c" (x: int4) -> int4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
-abs_uint2 :: proc "c" (x: uint2) -> uint2 { return x }
-abs_uint3 :: proc "c" (x: uint3) -> uint3 { return x }
-abs_uint4 :: proc "c" (x: uint4) -> uint4 { return x }
+@(require_results) abs_int :: proc "c" (x: int) -> int { return builtin.abs(x) }
+@(require_results) abs_uint :: proc "c" (x: uint) -> uint { return x }
+@(require_results) abs_float :: proc "c" (x: float) -> float { return builtin.abs(x) }
+@(require_results) abs_double :: proc "c" (x: double) -> double { return builtin.abs(x) }
+@(require_results) abs_float2 :: proc "c" (x: float2) -> float2 { return {abs(x.x), abs(x.y)} }
+@(require_results) abs_float3 :: proc "c" (x: float3) -> float3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+@(require_results) abs_float4 :: proc "c" (x: float4) -> float4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+@(require_results) abs_double2 :: proc "c" (x: double2) -> double2 { return {abs(x.x), abs(x.y)} }
+@(require_results) abs_double3 :: proc "c" (x: double3) -> double3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+@(require_results) abs_double4 :: proc "c" (x: double4) -> double4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+@(require_results) abs_int2 :: proc "c" (x: int2) -> int2 { return {abs(x.x), abs(x.y)} }
+@(require_results) abs_int3 :: proc "c" (x: int3) -> int3 { return {abs(x.x), abs(x.y), abs(x.z)} }
+@(require_results) abs_int4 :: proc "c" (x: int4) -> int4 { return {abs(x.x), abs(x.y), abs(x.z), abs(x.w)} }
+@(require_results) abs_uint2 :: proc "c" (x: uint2) -> uint2 { return x }
+@(require_results) abs_uint3 :: proc "c" (x: uint3) -> uint3 { return x }
+@(require_results) abs_uint4 :: proc "c" (x: uint4) -> uint4 { return x }
dot :: proc{
dot_int,
@@ -988,22 +988,22 @@ dot :: proc{
dot_uint3,
dot_uint4,
}
-dot_int :: proc "c" (a, b: int) -> int { return a*b }
-dot_uint :: proc "c" (a, b: uint) -> uint { return a*b }
-dot_float :: proc "c" (a, b: float) -> float { return a*b }
-dot_double :: proc "c" (a, b: double) -> double { return a*b }
-dot_float2 :: proc "c" (a, b: float2) -> float { return a.x*b.x + a.y*b.y }
-dot_float3 :: proc "c" (a, b: float3) -> float { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_float4 :: proc "c" (a, b: float4) -> float { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_double2 :: proc "c" (a, b: double2) -> double { return a.x*b.x + a.y*b.y }
-dot_double3 :: proc "c" (a, b: double3) -> double { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_double4 :: proc "c" (a, b: double4) -> double { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_int2 :: proc "c" (a, b: int2) -> int { return a.x*b.x + a.y*b.y }
-dot_int3 :: proc "c" (a, b: int3) -> int { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_int4 :: proc "c" (a, b: int4) -> int { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
-dot_uint2 :: proc "c" (a, b: uint2) -> uint { return a.x*b.x + a.y*b.y }
-dot_uint3 :: proc "c" (a, b: uint3) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z }
-dot_uint4 :: proc "c" (a, b: uint4) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_int :: proc "c" (a, b: int) -> int { return a*b }
+@(require_results) dot_uint :: proc "c" (a, b: uint) -> uint { return a*b }
+@(require_results) dot_float :: proc "c" (a, b: float) -> float { return a*b }
+@(require_results) dot_double :: proc "c" (a, b: double) -> double { return a*b }
+@(require_results) dot_float2 :: proc "c" (a, b: float2) -> float { return a.x*b.x + a.y*b.y }
+@(require_results) dot_float3 :: proc "c" (a, b: float3) -> float { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_float4 :: proc "c" (a, b: float4) -> float { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_double2 :: proc "c" (a, b: double2) -> double { return a.x*b.x + a.y*b.y }
+@(require_results) dot_double3 :: proc "c" (a, b: double3) -> double { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_double4 :: proc "c" (a, b: double4) -> double { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_int2 :: proc "c" (a, b: int2) -> int { return a.x*b.x + a.y*b.y }
+@(require_results) dot_int3 :: proc "c" (a, b: int3) -> int { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_int4 :: proc "c" (a, b: int4) -> int { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
+@(require_results) dot_uint2 :: proc "c" (a, b: uint2) -> uint { return a.x*b.x + a.y*b.y }
+@(require_results) dot_uint3 :: proc "c" (a, b: uint3) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z }
+@(require_results) dot_uint4 :: proc "c" (a, b: uint4) -> uint { return a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w }
length :: proc{
length_float,
@@ -1015,14 +1015,14 @@ length :: proc{
length_double3,
length_double4,
}
-length_float :: proc "c" (x: float) -> float { return builtin.abs(x) }
-length_double :: proc "c" (x: double) -> double { return builtin.abs(x) }
-length_float2 :: proc "c" (x: float2) -> float { return sqrt(x.x*x.x + x.y*x.y) }
-length_float3 :: proc "c" (x: float3) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
-length_float4 :: proc "c" (x: float4) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
-length_double2 :: proc "c" (x: double2) -> double { return sqrt(x.x*x.x + x.y*x.y) }
-length_double3 :: proc "c" (x: double3) -> double { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
-length_double4 :: proc "c" (x: double4) -> double { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+@(require_results) length_float :: proc "c" (x: float) -> float { return builtin.abs(x) }
+@(require_results) length_double :: proc "c" (x: double) -> double { return builtin.abs(x) }
+@(require_results) length_float2 :: proc "c" (x: float2) -> float { return sqrt(x.x*x.x + x.y*x.y) }
+@(require_results) length_float3 :: proc "c" (x: float3) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
+@(require_results) length_float4 :: proc "c" (x: float4) -> float { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
+@(require_results) length_double2 :: proc "c" (x: double2) -> double { return sqrt(x.x*x.x + x.y*x.y) }
+@(require_results) length_double3 :: proc "c" (x: double3) -> double { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z) }
+@(require_results) length_double4 :: proc "c" (x: double4) -> double { return sqrt(x.x*x.x + x.y*x.y + x.z*x.z + x.w*x.w) }
distance :: proc{
@@ -1035,14 +1035,14 @@ distance :: proc{
distance_double3,
distance_double4,
}
-distance_float :: proc "c" (x, y: float) -> float { return length(y-x) }
-distance_double :: proc "c" (x, y: double) -> double { return length(y-x) }
-distance_float2 :: proc "c" (x, y: float2) -> float { return length(y-x) }
-distance_float3 :: proc "c" (x, y: float3) -> float { return length(y-x) }
-distance_float4 :: proc "c" (x, y: float4) -> float { return length(y-x) }
-distance_double2 :: proc "c" (x, y: double2) -> double { return length(y-x) }
-distance_double3 :: proc "c" (x, y: double3) -> double { return length(y-x) }
-distance_double4 :: proc "c" (x, y: double4) -> double { return length(y-x) }
+@(require_results) distance_float :: proc "c" (x, y: float) -> float { return length(y-x) }
+@(require_results) distance_double :: proc "c" (x, y: double) -> double { return length(y-x) }
+@(require_results) distance_float2 :: proc "c" (x, y: float2) -> float { return length(y-x) }
+@(require_results) distance_float3 :: proc "c" (x, y: float3) -> float { return length(y-x) }
+@(require_results) distance_float4 :: proc "c" (x, y: float4) -> float { return length(y-x) }
+@(require_results) distance_double2 :: proc "c" (x, y: double2) -> double { return length(y-x) }
+@(require_results) distance_double3 :: proc "c" (x, y: double3) -> double { return length(y-x) }
+@(require_results) distance_double4 :: proc "c" (x, y: double4) -> double { return length(y-x) }
cross :: proc{
@@ -1051,19 +1051,19 @@ cross :: proc{
cross_int3,
}
-cross_float3 :: proc "c" (a, b: float3) -> (c: float3) {
+@(require_results) cross_float3 :: proc "c" (a, b: float3) -> (c: float3) {
c.x = a.y*b.z - b.y*a.z
c.y = a.z*b.x - b.z*a.x
c.z = a.x*b.y - b.x*a.y
return
}
-cross_double3 :: proc "c" (a, b: double3) -> (c: double3) {
+@(require_results) cross_double3 :: proc "c" (a, b: double3) -> (c: double3) {
c.x = a.y*b.z - b.y*a.z
c.y = a.z*b.x - b.z*a.x
c.z = a.x*b.y - b.x*a.y
return
}
-cross_int3 :: proc "c" (a, b: int3) -> (c: int3) {
+@(require_results) cross_int3 :: proc "c" (a, b: int3) -> (c: int3) {
c.x = a.y*b.z - b.y*a.z
c.y = a.z*b.x - b.z*a.x
c.z = a.x*b.y - b.x*a.y
@@ -1080,14 +1080,14 @@ normalize :: proc{
normalize_double3,
normalize_double4,
}
-normalize_float :: proc "c" (x: float) -> float { return 1.0 }
-normalize_double :: proc "c" (x: double) -> double { return 1.0 }
-normalize_float2 :: proc "c" (x: float2) -> float2 { return x / length(x) }
-normalize_float3 :: proc "c" (x: float3) -> float3 { return x / length(x) }
-normalize_float4 :: proc "c" (x: float4) -> float4 { return x / length(x) }
-normalize_double2 :: proc "c" (x: double2) -> double2 { return x / length(x) }
-normalize_double3 :: proc "c" (x: double3) -> double3 { return x / length(x) }
-normalize_double4 :: proc "c" (x: double4) -> double4 { return x / length(x) }
+@(require_results) normalize_float :: proc "c" (x: float) -> float { return 1.0 }
+@(require_results) normalize_double :: proc "c" (x: double) -> double { return 1.0 }
+@(require_results) normalize_float2 :: proc "c" (x: float2) -> float2 { return x / length(x) }
+@(require_results) normalize_float3 :: proc "c" (x: float3) -> float3 { return x / length(x) }
+@(require_results) normalize_float4 :: proc "c" (x: float4) -> float4 { return x / length(x) }
+@(require_results) normalize_double2 :: proc "c" (x: double2) -> double2 { return x / length(x) }
+@(require_results) normalize_double3 :: proc "c" (x: double3) -> double3 { return x / length(x) }
+@(require_results) normalize_double4 :: proc "c" (x: double4) -> double4 { return x / length(x) }
faceforward :: proc{
@@ -1100,14 +1100,14 @@ faceforward :: proc{
faceforward_double3,
faceforward_double4,
}
-faceforward_float :: proc "c" (N, I, Nref: float) -> float { return N if dot(I, Nref) < 0 else -N }
-faceforward_double :: proc "c" (N, I, Nref: double) -> double { return N if dot(I, Nref) < 0 else -N }
-faceforward_float2 :: proc "c" (N, I, Nref: float2) -> float2 { return N if dot(I, Nref) < 0 else -N }
-faceforward_float3 :: proc "c" (N, I, Nref: float3) -> float3 { return N if dot(I, Nref) < 0 else -N }
-faceforward_float4 :: proc "c" (N, I, Nref: float4) -> float4 { return N if dot(I, Nref) < 0 else -N }
-faceforward_double2 :: proc "c" (N, I, Nref: double2) -> double2 { return N if dot(I, Nref) < 0 else -N }
-faceforward_double3 :: proc "c" (N, I, Nref: double3) -> double3 { return N if dot(I, Nref) < 0 else -N }
-faceforward_double4 :: proc "c" (N, I, Nref: double4) -> double4 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_float :: proc "c" (N, I, Nref: float) -> float { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_double :: proc "c" (N, I, Nref: double) -> double { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_float2 :: proc "c" (N, I, Nref: float2) -> float2 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_float3 :: proc "c" (N, I, Nref: float3) -> float3 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_float4 :: proc "c" (N, I, Nref: float4) -> float4 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_double2 :: proc "c" (N, I, Nref: double2) -> double2 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_double3 :: proc "c" (N, I, Nref: double3) -> double3 { return N if dot(I, Nref) < 0 else -N }
+@(require_results) faceforward_double4 :: proc "c" (N, I, Nref: double4) -> double4 { return N if dot(I, Nref) < 0 else -N }
reflect :: proc{
@@ -1120,14 +1120,14 @@ reflect :: proc{
reflect_double3,
reflect_double4,
}
-reflect_float :: proc "c" (I, N: float) -> float { return I - 2*N*dot(N, I) }
-reflect_double :: proc "c" (I, N: double) -> double { return I - 2*N*dot(N, I) }
-reflect_float2 :: proc "c" (I, N: float2) -> float2 { return I - 2*N*dot(N, I) }
-reflect_float3 :: proc "c" (I, N: float3) -> float3 { return I - 2*N*dot(N, I) }
-reflect_float4 :: proc "c" (I, N: float4) -> float4 { return I - 2*N*dot(N, I) }
-reflect_double2 :: proc "c" (I, N: double2) -> double2 { return I - 2*N*dot(N, I) }
-reflect_double3 :: proc "c" (I, N: double3) -> double3 { return I - 2*N*dot(N, I) }
-reflect_double4 :: proc "c" (I, N: double4) -> double4 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_float :: proc "c" (I, N: float) -> float { return I - 2*N*dot(N, I) }
+@(require_results) reflect_double :: proc "c" (I, N: double) -> double { return I - 2*N*dot(N, I) }
+@(require_results) reflect_float2 :: proc "c" (I, N: float2) -> float2 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_float3 :: proc "c" (I, N: float3) -> float3 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_float4 :: proc "c" (I, N: float4) -> float4 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_double2 :: proc "c" (I, N: double2) -> double2 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_double3 :: proc "c" (I, N: double3) -> double3 { return I - 2*N*dot(N, I) }
+@(require_results) reflect_double4 :: proc "c" (I, N: double4) -> double4 { return I - 2*N*dot(N, I) }
@@ -1142,49 +1142,57 @@ refract :: proc{
refract_double3,
refract_double4,
}
-refract_float :: proc "c" (i, n, eta: float) -> float {
+@(require_results)
+refract_float :: proc "c" (i, n, eta: float) -> float {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * float(int(cost2 > 0))
}
-refract_double :: proc "c" (i, n, eta: double) -> double {
+@(require_results)
+refract_double :: proc "c" (i, n, eta: double) -> double {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * double(int(cost2 > 0))
}
-refract_float2 :: proc "c" (i, n, eta: float2) -> float2 {
+@(require_results)
+refract_float2 :: proc "c" (i, n, eta: float2) -> float2 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * float2{float(int(cost2.x > 0)), float(int(cost2.y > 0))}
}
-refract_float3 :: proc "c" (i, n, eta: float3) -> float3 {
+@(require_results)
+refract_float3 :: proc "c" (i, n, eta: float3) -> float3 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * float3{float(int(cost2.x > 0)), float(int(cost2.y > 0)), float(int(cost2.z > 0))}
}
-refract_float4 :: proc "c" (i, n, eta: float4) -> float4 {
+@(require_results)
+refract_float4 :: proc "c" (i, n, eta: float4) -> float4 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * float4{float(int(cost2.x > 0)), float(int(cost2.y > 0)), float(int(cost2.z > 0)), float(int(cost2.w > 0))}
}
-refract_double2 :: proc "c" (i, n, eta: double2) -> double2 {
+@(require_results)
+refract_double2 :: proc "c" (i, n, eta: double2) -> double2 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * double2{double(int(cost2.x > 0)), double(int(cost2.y > 0))}
}
-refract_double3 :: proc "c" (i, n, eta: double3) -> double3 {
+@(require_results)
+refract_double3 :: proc "c" (i, n, eta: double3) -> double3 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
return t * double3{double(int(cost2.x > 0)), double(int(cost2.y > 0)), double(int(cost2.z > 0))}
}
-refract_double4 :: proc "c" (i, n, eta: double4) -> double4 {
+@(require_results)
+refract_double4 :: proc "c" (i, n, eta: double4) -> double4 {
cosi := dot(-i, n)
cost2 := 1 - eta*eta*(1 - cosi*cosi)
t := eta*i + ((eta*cosi - sqrt(abs(cost2))) * n)
@@ -1196,18 +1204,18 @@ scalarTripleProduct :: proc{
scalarTripleProduct_double3,
scalarTripleProduct_int3,
}
-scalarTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float { return dot(a, cross(b, c)) }
-scalarTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double { return dot(a, cross(b, c)) }
-scalarTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int { return dot(a, cross(b, c)) }
+@(require_results) scalarTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float { return dot(a, cross(b, c)) }
+@(require_results) scalarTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double { return dot(a, cross(b, c)) }
+@(require_results) scalarTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int { return dot(a, cross(b, c)) }
vectorTripleProduct :: proc {
vectorTripleProduct_float3,
vectorTripleProduct_double3,
vectorTripleProduct_int3,
}
-vectorTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float3 { return cross(a, cross(b, c)) }
-vectorTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double3 { return cross(a, cross(b, c)) }
-vectorTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int3 { return cross(a, cross(b, c)) }
+@(require_results) vectorTripleProduct_float3 :: proc "c" (a, b, c: float3) -> float3 { return cross(a, cross(b, c)) }
+@(require_results) vectorTripleProduct_double3 :: proc "c" (a, b, c: double3) -> double3 { return cross(a, cross(b, c)) }
+@(require_results) vectorTripleProduct_int3 :: proc "c" (a, b, c: int3) -> int3 { return cross(a, cross(b, c)) }
// Vector Relational Procedures
@@ -1230,22 +1238,22 @@ lessThan :: proc{
lessThan_int4,
lessThan_uint4,
}
-lessThan_float :: proc "c" (a, b: float) -> bool { return a < b }
-lessThan_double :: proc "c" (a, b: double) -> bool { return a < b }
-lessThan_int :: proc "c" (a, b: int) -> bool { return a < b }
-lessThan_uint :: proc "c" (a, b: uint) -> bool { return a < b }
-lessThan_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x < b.x, a.y < b.y} }
-lessThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x < b.x, a.y < b.y} }
-lessThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x < b.x, a.y < b.y} }
-lessThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x < b.x, a.y < b.y} }
-lessThan_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
-lessThan_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
-lessThan_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
-lessThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
-lessThan_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_float :: proc "c" (a, b: float) -> bool { return a < b }
+@(require_results) lessThan_double :: proc "c" (a, b: double) -> bool { return a < b }
+@(require_results) lessThan_int :: proc "c" (a, b: int) -> bool { return a < b }
+@(require_results) lessThan_uint :: proc "c" (a, b: uint) -> bool { return a < b }
+@(require_results) lessThan_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x < b.x, a.y < b.y} }
+@(require_results) lessThan_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x < b.x, a.y < b.y, a.z < b.z} }
+@(require_results) lessThan_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
+@(require_results) lessThan_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w} }
lessThanEqual :: proc{
@@ -1266,22 +1274,22 @@ lessThanEqual :: proc{
lessThanEqual_int4,
lessThanEqual_uint4,
}
-lessThanEqual_float :: proc "c" (a, b: float) -> bool { return a <= b }
-lessThanEqual_double :: proc "c" (a, b: double) -> bool { return a <= b }
-lessThanEqual_int :: proc "c" (a, b: int) -> bool { return a <= b }
-lessThanEqual_uint :: proc "c" (a, b: uint) -> bool { return a <= b }
-lessThanEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
-lessThanEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
-lessThanEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
-lessThanEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
-lessThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
-lessThanEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_float :: proc "c" (a, b: float) -> bool { return a <= b }
+@(require_results) lessThanEqual_double :: proc "c" (a, b: double) -> bool { return a <= b }
+@(require_results) lessThanEqual_int :: proc "c" (a, b: int) -> bool { return a <= b }
+@(require_results) lessThanEqual_uint :: proc "c" (a, b: uint) -> bool { return a <= b }
+@(require_results) lessThanEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x <= b.x, a.y <= b.y} }
+@(require_results) lessThanEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z} }
+@(require_results) lessThanEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
+@(require_results) lessThanEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w} }
greaterThan :: proc{
@@ -1302,22 +1310,22 @@ greaterThan :: proc{
greaterThan_int4,
greaterThan_uint4,
}
-greaterThan_float :: proc "c" (a, b: float) -> bool { return a > b }
-greaterThan_double :: proc "c" (a, b: double) -> bool { return a > b }
-greaterThan_int :: proc "c" (a, b: int) -> bool { return a > b }
-greaterThan_uint :: proc "c" (a, b: uint) -> bool { return a > b }
-greaterThan_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x > b.x, a.y > b.y} }
-greaterThan_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
-greaterThan_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
-greaterThan_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
-greaterThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
-greaterThan_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_float :: proc "c" (a, b: float) -> bool { return a > b }
+@(require_results) greaterThan_double :: proc "c" (a, b: double) -> bool { return a > b }
+@(require_results) greaterThan_int :: proc "c" (a, b: int) -> bool { return a > b }
+@(require_results) greaterThan_uint :: proc "c" (a, b: uint) -> bool { return a > b }
+@(require_results) greaterThan_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x > b.x, a.y > b.y} }
+@(require_results) greaterThan_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x > b.x, a.y > b.y, a.z > b.z} }
+@(require_results) greaterThan_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
+@(require_results) greaterThan_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x > b.x, a.y > b.y, a.z > b.z, a.w > b.w} }
greaterThanEqual :: proc{
@@ -1338,22 +1346,22 @@ greaterThanEqual :: proc{
greaterThanEqual_int4,
greaterThanEqual_uint4,
}
-greaterThanEqual_float :: proc "c" (a, b: float) -> bool { return a >= b }
-greaterThanEqual_double :: proc "c" (a, b: double) -> bool { return a >= b }
-greaterThanEqual_int :: proc "c" (a, b: int) -> bool { return a >= b }
-greaterThanEqual_uint :: proc "c" (a, b: uint) -> bool { return a >= b }
-greaterThanEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
-greaterThanEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
-greaterThanEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
-greaterThanEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
-greaterThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
-greaterThanEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_float :: proc "c" (a, b: float) -> bool { return a >= b }
+@(require_results) greaterThanEqual_double :: proc "c" (a, b: double) -> bool { return a >= b }
+@(require_results) greaterThanEqual_int :: proc "c" (a, b: int) -> bool { return a >= b }
+@(require_results) greaterThanEqual_uint :: proc "c" (a, b: uint) -> bool { return a >= b }
+@(require_results) greaterThanEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x >= b.x, a.y >= b.y} }
+@(require_results) greaterThanEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z} }
+@(require_results) greaterThanEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
+@(require_results) greaterThanEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w} }
equal :: proc{
@@ -1374,22 +1382,22 @@ equal :: proc{
equal_int4,
equal_uint4,
}
-equal_float :: proc "c" (a, b: float) -> bool { return a == b }
-equal_double :: proc "c" (a, b: double) -> bool { return a == b }
-equal_int :: proc "c" (a, b: int) -> bool { return a == b }
-equal_uint :: proc "c" (a, b: uint) -> bool { return a == b }
-equal_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x == b.x, a.y == b.y} }
-equal_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x == b.x, a.y == b.y} }
-equal_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x == b.x, a.y == b.y} }
-equal_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x == b.x, a.y == b.y} }
-equal_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
-equal_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
-equal_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
-equal_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
-equal_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_float :: proc "c" (a, b: float) -> bool { return a == b }
+@(require_results) equal_double :: proc "c" (a, b: double) -> bool { return a == b }
+@(require_results) equal_int :: proc "c" (a, b: int) -> bool { return a == b }
+@(require_results) equal_uint :: proc "c" (a, b: uint) -> bool { return a == b }
+@(require_results) equal_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x == b.x, a.y == b.y} }
+@(require_results) equal_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x == b.x, a.y == b.y, a.z == b.z} }
+@(require_results) equal_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
+@(require_results) equal_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x == b.x, a.y == b.y, a.z == b.z, a.w == b.w} }
notEqual :: proc{
notEqual_float,
@@ -1409,22 +1417,22 @@ notEqual :: proc{
notEqual_int4,
notEqual_uint4,
}
-notEqual_float :: proc "c" (a, b: float) -> bool { return a != b }
-notEqual_double :: proc "c" (a, b: double) -> bool { return a != b }
-notEqual_int :: proc "c" (a, b: int) -> bool { return a != b }
-notEqual_uint :: proc "c" (a, b: uint) -> bool { return a != b }
-notEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x != b.x, a.y != b.y} }
-notEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x != b.x, a.y != b.y} }
-notEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x != b.x, a.y != b.y} }
-notEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x != b.x, a.y != b.y} }
-notEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
-notEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
-notEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
-notEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
-notEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_float :: proc "c" (a, b: float) -> bool { return a != b }
+@(require_results) notEqual_double :: proc "c" (a, b: double) -> bool { return a != b }
+@(require_results) notEqual_int :: proc "c" (a, b: int) -> bool { return a != b }
+@(require_results) notEqual_uint :: proc "c" (a, b: uint) -> bool { return a != b }
+@(require_results) notEqual_float2 :: proc "c" (a, b: float2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_double2 :: proc "c" (a, b: double2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_int2 :: proc "c" (a, b: int2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_uint2 :: proc "c" (a, b: uint2) -> bool2 { return {a.x != b.x, a.y != b.y} }
+@(require_results) notEqual_float3 :: proc "c" (a, b: float3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_double3 :: proc "c" (a, b: double3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_int3 :: proc "c" (a, b: int3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_uint3 :: proc "c" (a, b: uint3) -> bool3 { return {a.x != b.x, a.y != b.y, a.z != b.z} }
+@(require_results) notEqual_float4 :: proc "c" (a, b: float4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_double4 :: proc "c" (a, b: double4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_int4 :: proc "c" (a, b: int4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
+@(require_results) notEqual_uint4 :: proc "c" (a, b: uint4) -> bool4 { return {a.x != b.x, a.y != b.y, a.z != b.z, a.w != b.w} }
any :: proc{
@@ -1433,10 +1441,10 @@ any :: proc{
any_bool3,
any_bool4,
}
-any_bool :: proc "c" (v: bool) -> bool { return v }
-any_bool2 :: proc "c" (v: bool2) -> bool { return v.x || v.y }
-any_bool3 :: proc "c" (v: bool3) -> bool { return v.x || v.y || v.z }
-any_bool4 :: proc "c" (v: bool4) -> bool { return v.x || v.y || v.z || v.w }
+@(require_results) any_bool :: proc "c" (v: bool) -> bool { return v }
+@(require_results) any_bool2 :: proc "c" (v: bool2) -> bool { return v.x || v.y }
+@(require_results) any_bool3 :: proc "c" (v: bool3) -> bool { return v.x || v.y || v.z }
+@(require_results) any_bool4 :: proc "c" (v: bool4) -> bool { return v.x || v.y || v.z || v.w }
all :: proc{
all_bool,
@@ -1444,10 +1452,10 @@ all :: proc{
all_bool3,
all_bool4,
}
-all_bool :: proc "c" (v: bool) -> bool { return v }
-all_bool2 :: proc "c" (v: bool2) -> bool { return v.x && v.y }
-all_bool3 :: proc "c" (v: bool3) -> bool { return v.x && v.y && v.z }
-all_bool4 :: proc "c" (v: bool4) -> bool { return v.x && v.y && v.z && v.w }
+@(require_results) all_bool :: proc "c" (v: bool) -> bool { return v }
+@(require_results) all_bool2 :: proc "c" (v: bool2) -> bool { return v.x && v.y }
+@(require_results) all_bool3 :: proc "c" (v: bool3) -> bool { return v.x && v.y && v.z }
+@(require_results) all_bool4 :: proc "c" (v: bool4) -> bool { return v.x && v.y && v.z && v.w }
not :: proc{
not_bool,
@@ -1455,22 +1463,22 @@ not :: proc{
not_bool3,
not_bool4,
}
-not_bool :: proc "c" (v: bool) -> bool { return !v }
-not_bool2 :: proc "c" (v: bool2) -> bool2 { return {!v.x, !v.y} }
-not_bool3 :: proc "c" (v: bool3) -> bool3 { return {!v.x, !v.y, !v.z} }
-not_bool4 :: proc "c" (v: bool4) -> bool4 { return {!v.x, !v.y, !v.z, !v.w} }
+@(require_results) not_bool :: proc "c" (v: bool) -> bool { return !v }
+@(require_results) not_bool2 :: proc "c" (v: bool2) -> bool2 { return {!v.x, !v.y} }
+@(require_results) not_bool3 :: proc "c" (v: bool3) -> bool3 { return {!v.x, !v.y, !v.z} }
+@(require_results) not_bool4 :: proc "c" (v: bool4) -> bool4 { return {!v.x, !v.y, !v.z, !v.w} }
-inverse_float1x1 :: proc "c" (m: float1x1) -> float1x1 { return builtin.inverse(m) }
-inverse_float2x2 :: proc "c" (m: float2x2) -> float2x2 { return builtin.inverse(m) }
-inverse_float3x3 :: proc "c" (m: float3x3) -> float3x3 { return builtin.inverse(m) }
-inverse_float4x4 :: proc "c" (m: float4x4) -> float4x4 { return builtin.inverse(m) }
-inverse_double1x1 :: proc "c" (m: double1x1) -> double1x1 { return builtin.inverse(m) }
-inverse_double2x2 :: proc "c" (m: double2x2) -> double2x2 { return builtin.inverse(m) }
-inverse_double3x3 :: proc "c" (m: double3x3) -> double3x3 { return builtin.inverse(m) }
-inverse_double4x4 :: proc "c" (m: double4x4) -> double4x4 { return builtin.inverse(m) }
+@(require_results) inverse_float1x1 :: proc "c" (m: float1x1) -> float1x1 { return builtin.inverse(m) }
+@(require_results) inverse_float2x2 :: proc "c" (m: float2x2) -> float2x2 { return builtin.inverse(m) }
+@(require_results) inverse_float3x3 :: proc "c" (m: float3x3) -> float3x3 { return builtin.inverse(m) }
+@(require_results) inverse_float4x4 :: proc "c" (m: float4x4) -> float4x4 { return builtin.inverse(m) }
+@(require_results) inverse_double1x1 :: proc "c" (m: double1x1) -> double1x1 { return builtin.inverse(m) }
+@(require_results) inverse_double2x2 :: proc "c" (m: double2x2) -> double2x2 { return builtin.inverse(m) }
+@(require_results) inverse_double3x3 :: proc "c" (m: double3x3) -> double3x3 { return builtin.inverse(m) }
+@(require_results) inverse_double4x4 :: proc "c" (m: double4x4) -> double4x4 { return builtin.inverse(m) }
inverse :: proc{
inverse_float1x1,
@@ -1544,57 +1552,57 @@ asfloat :: proc{
asfloat_double3,
asfloat_double4,
}
-asfloat_float :: proc "c" (v: float) -> float { return float(v) }
-asfloat_double :: proc "c" (v: double) -> float { return float(v) }
-asfloat_int :: proc "c" (v: int) -> float { return float(v) }
-asfloat_uint :: proc "c" (v: uint) -> float { return float(v) }
-asfloat_float1x1 :: proc "c" (v: float1x1) -> float1x1 { return float1x1(v) }
-asfloat_float2x2 :: proc "c" (v: float2x2) -> float2x2 { return float2x2(v) }
-asfloat_float3x3 :: proc "c" (v: float3x3) -> float3x3 { return float3x3(v) }
-asfloat_float4x4 :: proc "c" (v: float4x4) -> float4x4 { return float4x4(v) }
-asfloat_float1x2 :: proc "c" (v: float1x2) -> float1x2 { return float1x2(v) }
-asfloat_float1x3 :: proc "c" (v: float1x3) -> float1x3 { return float1x3(v) }
-asfloat_float1x4 :: proc "c" (v: float1x4) -> float1x4 { return float1x4(v) }
-asfloat_float2x1 :: proc "c" (v: float2x1) -> float2x1 { return float2x1(v) }
-asfloat_float2x3 :: proc "c" (v: float2x3) -> float2x3 { return float2x3(v) }
-asfloat_float2x4 :: proc "c" (v: float2x4) -> float2x4 { return float2x4(v) }
-asfloat_float3x1 :: proc "c" (v: float3x1) -> float3x1 { return float3x1(v) }
-asfloat_float3x2 :: proc "c" (v: float3x2) -> float3x2 { return float3x2(v) }
-asfloat_float3x4 :: proc "c" (v: float3x4) -> float3x4 { return float3x4(v) }
-asfloat_float4x1 :: proc "c" (v: float4x1) -> float4x1 { return float4x1(v) }
-asfloat_float4x2 :: proc "c" (v: float4x2) -> float4x2 { return float4x2(v) }
-asfloat_float4x3 :: proc "c" (v: float4x3) -> float4x3 { return float4x3(v) }
-asfloat_float2 :: proc "c" (v: float2) -> float2 { return float2(v) }
-asfloat_float3 :: proc "c" (v: float3) -> float3 { return float3(v) }
-asfloat_float4 :: proc "c" (v: float4) -> float4 { return float4(v) }
-asfloat_int2 :: proc "c" (v: int2) -> float2 { return float2{float(v.x), float(v.y)} }
-asfloat_int3 :: proc "c" (v: int3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} }
-asfloat_int4 :: proc "c" (v: int4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
-asfloat_uint2 :: proc "c" (v: uint2) -> float2 { return float2{float(v.x), float(v.y)} }
-asfloat_uint3 :: proc "c" (v: uint3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} }
-asfloat_uint4 :: proc "c" (v: uint4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
-asfloat_bool2 :: proc "c" (v: bool2) -> float2 { return float2{float(int(v.x)), float(int(v.y))} }
-asfloat_bool3 :: proc "c" (v: bool3) -> float3 { return float3{float(int(v.x)), float(int(v.y)), float(int(v.z))} }
-asfloat_bool4 :: proc "c" (v: bool4) -> float4 { return float4{float(int(v.x)), float(int(v.y)), float(int(v.z)), float(int(v.w))} }
-asfloat_double1x1 :: proc "c" (v: double1x1) -> float1x1 { return float1x1(v) }
-asfloat_double2x2 :: proc "c" (v: double2x2) -> float2x2 { return float2x2(v) }
-asfloat_double3x3 :: proc "c" (v: double3x3) -> float3x3 { return float3x3(v) }
-asfloat_double4x4 :: proc "c" (v: double4x4) -> float4x4 { return float4x4(v) }
-asfloat_double1x2 :: proc "c" (v: double1x2) -> float1x2 { return float1x2(v) }
-asfloat_double1x3 :: proc "c" (v: double1x3) -> float1x3 { return float1x3(v) }
-asfloat_double1x4 :: proc "c" (v: double1x4) -> float1x4 { return float1x4(v) }
-asfloat_double2x1 :: proc "c" (v: double2x1) -> float2x1 { return float2x1(v) }
-asfloat_double2x3 :: proc "c" (v: double2x3) -> float2x3 { return float2x3(v) }
-asfloat_double2x4 :: proc "c" (v: double2x4) -> float2x4 { return float2x4(v) }
-asfloat_double3x1 :: proc "c" (v: double3x1) -> float3x1 { return float3x1(v) }
-asfloat_double3x2 :: proc "c" (v: double3x2) -> float3x2 { return float3x2(v) }
-asfloat_double3x4 :: proc "c" (v: double3x4) -> float3x4 { return float3x4(v) }
-asfloat_double4x1 :: proc "c" (v: double4x1) -> float4x1 { return float4x1(v) }
-asfloat_double4x2 :: proc "c" (v: double4x2) -> float4x2 { return float4x2(v) }
-asfloat_double4x3 :: proc "c" (v: double4x3) -> float4x3 { return float4x3(v) }
-asfloat_double2 :: proc "c" (v: double2) -> float2 { return float2{float(v.x), float(v.y)} }
-asfloat_double3 :: proc "c" (v: double3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} }
-asfloat_double4 :: proc "c" (v: double4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
+@(require_results) asfloat_float :: proc "c" (v: float) -> float { return float(v) }
+@(require_results) asfloat_double :: proc "c" (v: double) -> float { return float(v) }
+@(require_results) asfloat_int :: proc "c" (v: int) -> float { return float(v) }
+@(require_results) asfloat_uint :: proc "c" (v: uint) -> float { return float(v) }
+@(require_results) asfloat_float1x1 :: proc "c" (v: float1x1) -> float1x1 { return float1x1(v) }
+@(require_results) asfloat_float2x2 :: proc "c" (v: float2x2) -> float2x2 { return float2x2(v) }
+@(require_results) asfloat_float3x3 :: proc "c" (v: float3x3) -> float3x3 { return float3x3(v) }
+@(require_results) asfloat_float4x4 :: proc "c" (v: float4x4) -> float4x4 { return float4x4(v) }
+@(require_results) asfloat_float1x2 :: proc "c" (v: float1x2) -> float1x2 { return float1x2(v) }
+@(require_results) asfloat_float1x3 :: proc "c" (v: float1x3) -> float1x3 { return float1x3(v) }
+@(require_results) asfloat_float1x4 :: proc "c" (v: float1x4) -> float1x4 { return float1x4(v) }
+@(require_results) asfloat_float2x1 :: proc "c" (v: float2x1) -> float2x1 { return float2x1(v) }
+@(require_results) asfloat_float2x3 :: proc "c" (v: float2x3) -> float2x3 { return float2x3(v) }
+@(require_results) asfloat_float2x4 :: proc "c" (v: float2x4) -> float2x4 { return float2x4(v) }
+@(require_results) asfloat_float3x1 :: proc "c" (v: float3x1) -> float3x1 { return float3x1(v) }
+@(require_results) asfloat_float3x2 :: proc "c" (v: float3x2) -> float3x2 { return float3x2(v) }
+@(require_results) asfloat_float3x4 :: proc "c" (v: float3x4) -> float3x4 { return float3x4(v) }
+@(require_results) asfloat_float4x1 :: proc "c" (v: float4x1) -> float4x1 { return float4x1(v) }
+@(require_results) asfloat_float4x2 :: proc "c" (v: float4x2) -> float4x2 { return float4x2(v) }
+@(require_results) asfloat_float4x3 :: proc "c" (v: float4x3) -> float4x3 { return float4x3(v) }
+@(require_results) asfloat_float2 :: proc "c" (v: float2) -> float2 { return float2(v) }
+@(require_results) asfloat_float3 :: proc "c" (v: float3) -> float3 { return float3(v) }
+@(require_results) asfloat_float4 :: proc "c" (v: float4) -> float4 { return float4(v) }
+@(require_results) asfloat_int2 :: proc "c" (v: int2) -> float2 { return float2{float(v.x), float(v.y)} }
+@(require_results) asfloat_int3 :: proc "c" (v: int3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} }
+@(require_results) asfloat_int4 :: proc "c" (v: int4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
+@(require_results) asfloat_uint2 :: proc "c" (v: uint2) -> float2 { return float2{float(v.x), float(v.y)} }
+@(require_results) asfloat_uint3 :: proc "c" (v: uint3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} }
+@(require_results) asfloat_uint4 :: proc "c" (v: uint4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
+@(require_results) asfloat_bool2 :: proc "c" (v: bool2) -> float2 { return float2{float(int(v.x)), float(int(v.y))} }
+@(require_results) asfloat_bool3 :: proc "c" (v: bool3) -> float3 { return float3{float(int(v.x)), float(int(v.y)), float(int(v.z))} }
+@(require_results) asfloat_bool4 :: proc "c" (v: bool4) -> float4 { return float4{float(int(v.x)), float(int(v.y)), float(int(v.z)), float(int(v.w))} }
+@(require_results) asfloat_double1x1 :: proc "c" (v: double1x1) -> float1x1 { return float1x1(v) }
+@(require_results) asfloat_double2x2 :: proc "c" (v: double2x2) -> float2x2 { return float2x2(v) }
+@(require_results) asfloat_double3x3 :: proc "c" (v: double3x3) -> float3x3 { return float3x3(v) }
+@(require_results) asfloat_double4x4 :: proc "c" (v: double4x4) -> float4x4 { return float4x4(v) }
+@(require_results) asfloat_double1x2 :: proc "c" (v: double1x2) -> float1x2 { return float1x2(v) }
+@(require_results) asfloat_double1x3 :: proc "c" (v: double1x3) -> float1x3 { return float1x3(v) }
+@(require_results) asfloat_double1x4 :: proc "c" (v: double1x4) -> float1x4 { return float1x4(v) }
+@(require_results) asfloat_double2x1 :: proc "c" (v: double2x1) -> float2x1 { return float2x1(v) }
+@(require_results) asfloat_double2x3 :: proc "c" (v: double2x3) -> float2x3 { return float2x3(v) }
+@(require_results) asfloat_double2x4 :: proc "c" (v: double2x4) -> float2x4 { return float2x4(v) }
+@(require_results) asfloat_double3x1 :: proc "c" (v: double3x1) -> float3x1 { return float3x1(v) }
+@(require_results) asfloat_double3x2 :: proc "c" (v: double3x2) -> float3x2 { return float3x2(v) }
+@(require_results) asfloat_double3x4 :: proc "c" (v: double3x4) -> float3x4 { return float3x4(v) }
+@(require_results) asfloat_double4x1 :: proc "c" (v: double4x1) -> float4x1 { return float4x1(v) }
+@(require_results) asfloat_double4x2 :: proc "c" (v: double4x2) -> float4x2 { return float4x2(v) }
+@(require_results) asfloat_double4x3 :: proc "c" (v: double4x3) -> float4x3 { return float4x3(v) }
+@(require_results) asfloat_double2 :: proc "c" (v: double2) -> float2 { return float2{float(v.x), float(v.y)} }
+@(require_results) asfloat_double3 :: proc "c" (v: double3) -> float3 { return float3{float(v.x), float(v.y), float(v.z)} }
+@(require_results) asfloat_double4 :: proc "c" (v: double4) -> float4 { return float4{float(v.x), float(v.y), float(v.z), float(v.w)} }
asdouble :: proc{
asdouble_float,
@@ -1649,57 +1657,57 @@ asdouble :: proc{
asdouble_double3,
asdouble_double4,
}
-asdouble_float :: proc "c" (v: float) -> double { return double(v) }
-asdouble_double :: proc "c" (v: double) -> double { return double(v) }
-asdouble_int :: proc "c" (v: int) -> double { return double(v) }
-asdouble_uint :: proc "c" (v: uint) -> double { return double(v) }
-asdouble_float1x1 :: proc "c" (v: float1x1) -> double1x1 { return double1x1(v) }
-asdouble_float2x2 :: proc "c" (v: float2x2) -> double2x2 { return double2x2(v) }
-asdouble_float3x3 :: proc "c" (v: float3x3) -> double3x3 { return double3x3(v) }
-asdouble_float4x4 :: proc "c" (v: float4x4) -> double4x4 { return double4x4(v) }
-asdouble_float1x2 :: proc "c" (v: float1x2) -> double1x2 { return double1x2(v) }
-asdouble_float1x3 :: proc "c" (v: float1x3) -> double1x3 { return double1x3(v) }
-asdouble_float1x4 :: proc "c" (v: float1x4) -> double1x4 { return double1x4(v) }
-asdouble_float2x1 :: proc "c" (v: float2x1) -> double2x1 { return double2x1(v) }
-asdouble_float2x3 :: proc "c" (v: float2x3) -> double2x3 { return double2x3(v) }
-asdouble_float2x4 :: proc "c" (v: float2x4) -> double2x4 { return double2x4(v) }
-asdouble_float3x1 :: proc "c" (v: float3x1) -> double3x1 { return double3x1(v) }
-asdouble_float3x2 :: proc "c" (v: float3x2) -> double3x2 { return double3x2(v) }
-asdouble_float3x4 :: proc "c" (v: float3x4) -> double3x4 { return double3x4(v) }
-asdouble_float4x1 :: proc "c" (v: float4x1) -> double4x1 { return double4x1(v) }
-asdouble_float4x2 :: proc "c" (v: float4x2) -> double4x2 { return double4x2(v) }
-asdouble_float4x3 :: proc "c" (v: float4x3) -> double4x3 { return double4x3(v) }
-asdouble_float2 :: proc "c" (v: float2) -> double2 { return double2{double(v.x), double(v.y)} }
-asdouble_float3 :: proc "c" (v: float3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
-asdouble_float4 :: proc "c" (v: float4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
-asdouble_int2 :: proc "c" (v: int2) -> double2 { return double2{double(v.x), double(v.y)} }
-asdouble_int3 :: proc "c" (v: int3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
-asdouble_int4 :: proc "c" (v: int4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
-asdouble_uint2 :: proc "c" (v: uint2) -> double2 { return double2{double(v.x), double(v.y)} }
-asdouble_uint3 :: proc "c" (v: uint3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
-asdouble_uint4 :: proc "c" (v: uint4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
-asdouble_bool2 :: proc "c" (v: bool2) -> double2 { return double2{double(int(v.x)), double(int(v.y))} }
-asdouble_bool3 :: proc "c" (v: bool3) -> double3 { return double3{double(int(v.x)), double(int(v.y)), double(int(v.z))} }
-asdouble_bool4 :: proc "c" (v: bool4) -> double4 { return double4{double(int(v.x)), double(int(v.y)), double(int(v.z)), double(int(v.w))} }
-asdouble_double1x1 :: proc "c" (v: double1x1) -> double1x1 { return double1x1(v) }
-asdouble_double2x2 :: proc "c" (v: double2x2) -> double2x2 { return double2x2(v) }
-asdouble_double3x3 :: proc "c" (v: double3x3) -> double3x3 { return double3x3(v) }
-asdouble_double4x4 :: proc "c" (v: double4x4) -> double4x4 { return double4x4(v) }
-asdouble_double1x2 :: proc "c" (v: double1x2) -> double1x2 { return double1x2(v) }
-asdouble_double1x3 :: proc "c" (v: double1x3) -> double1x3 { return double1x3(v) }
-asdouble_double1x4 :: proc "c" (v: double1x4) -> double1x4 { return double1x4(v) }
-asdouble_double2x1 :: proc "c" (v: double2x1) -> double2x1 { return double2x1(v) }
-asdouble_double2x3 :: proc "c" (v: double2x3) -> double2x3 { return double2x3(v) }
-asdouble_double2x4 :: proc "c" (v: double2x4) -> double2x4 { return double2x4(v) }
-asdouble_double3x1 :: proc "c" (v: double3x1) -> double3x1 { return double3x1(v) }
-asdouble_double3x2 :: proc "c" (v: double3x2) -> double3x2 { return double3x2(v) }
-asdouble_double3x4 :: proc "c" (v: double3x4) -> double3x4 { return double3x4(v) }
-asdouble_double4x1 :: proc "c" (v: double4x1) -> double4x1 { return double4x1(v) }
-asdouble_double4x2 :: proc "c" (v: double4x2) -> double4x2 { return double4x2(v) }
-asdouble_double4x3 :: proc "c" (v: double4x3) -> double4x3 { return double4x3(v) }
-asdouble_double2 :: proc "c" (v: double2) -> double2 { return double2{double(v.x), double(v.y)} }
-asdouble_double3 :: proc "c" (v: double3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
-asdouble_double4 :: proc "c" (v: double4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+@(require_results) asdouble_float :: proc "c" (v: float) -> double { return double(v) }
+@(require_results) asdouble_double :: proc "c" (v: double) -> double { return double(v) }
+@(require_results) asdouble_int :: proc "c" (v: int) -> double { return double(v) }
+@(require_results) asdouble_uint :: proc "c" (v: uint) -> double { return double(v) }
+@(require_results) asdouble_float1x1 :: proc "c" (v: float1x1) -> double1x1 { return double1x1(v) }
+@(require_results) asdouble_float2x2 :: proc "c" (v: float2x2) -> double2x2 { return double2x2(v) }
+@(require_results) asdouble_float3x3 :: proc "c" (v: float3x3) -> double3x3 { return double3x3(v) }
+@(require_results) asdouble_float4x4 :: proc "c" (v: float4x4) -> double4x4 { return double4x4(v) }
+@(require_results) asdouble_float1x2 :: proc "c" (v: float1x2) -> double1x2 { return double1x2(v) }
+@(require_results) asdouble_float1x3 :: proc "c" (v: float1x3) -> double1x3 { return double1x3(v) }
+@(require_results) asdouble_float1x4 :: proc "c" (v: float1x4) -> double1x4 { return double1x4(v) }
+@(require_results) asdouble_float2x1 :: proc "c" (v: float2x1) -> double2x1 { return double2x1(v) }
+@(require_results) asdouble_float2x3 :: proc "c" (v: float2x3) -> double2x3 { return double2x3(v) }
+@(require_results) asdouble_float2x4 :: proc "c" (v: float2x4) -> double2x4 { return double2x4(v) }
+@(require_results) asdouble_float3x1 :: proc "c" (v: float3x1) -> double3x1 { return double3x1(v) }
+@(require_results) asdouble_float3x2 :: proc "c" (v: float3x2) -> double3x2 { return double3x2(v) }
+@(require_results) asdouble_float3x4 :: proc "c" (v: float3x4) -> double3x4 { return double3x4(v) }
+@(require_results) asdouble_float4x1 :: proc "c" (v: float4x1) -> double4x1 { return double4x1(v) }
+@(require_results) asdouble_float4x2 :: proc "c" (v: float4x2) -> double4x2 { return double4x2(v) }
+@(require_results) asdouble_float4x3 :: proc "c" (v: float4x3) -> double4x3 { return double4x3(v) }
+@(require_results) asdouble_float2 :: proc "c" (v: float2) -> double2 { return double2{double(v.x), double(v.y)} }
+@(require_results) asdouble_float3 :: proc "c" (v: float3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
+@(require_results) asdouble_float4 :: proc "c" (v: float4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+@(require_results) asdouble_int2 :: proc "c" (v: int2) -> double2 { return double2{double(v.x), double(v.y)} }
+@(require_results) asdouble_int3 :: proc "c" (v: int3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
+@(require_results) asdouble_int4 :: proc "c" (v: int4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+@(require_results) asdouble_uint2 :: proc "c" (v: uint2) -> double2 { return double2{double(v.x), double(v.y)} }
+@(require_results) asdouble_uint3 :: proc "c" (v: uint3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
+@(require_results) asdouble_uint4 :: proc "c" (v: uint4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
+@(require_results) asdouble_bool2 :: proc "c" (v: bool2) -> double2 { return double2{double(int(v.x)), double(int(v.y))} }
+@(require_results) asdouble_bool3 :: proc "c" (v: bool3) -> double3 { return double3{double(int(v.x)), double(int(v.y)), double(int(v.z))} }
+@(require_results) asdouble_bool4 :: proc "c" (v: bool4) -> double4 { return double4{double(int(v.x)), double(int(v.y)), double(int(v.z)), double(int(v.w))} }
+@(require_results) asdouble_double1x1 :: proc "c" (v: double1x1) -> double1x1 { return double1x1(v) }
+@(require_results) asdouble_double2x2 :: proc "c" (v: double2x2) -> double2x2 { return double2x2(v) }
+@(require_results) asdouble_double3x3 :: proc "c" (v: double3x3) -> double3x3 { return double3x3(v) }
+@(require_results) asdouble_double4x4 :: proc "c" (v: double4x4) -> double4x4 { return double4x4(v) }
+@(require_results) asdouble_double1x2 :: proc "c" (v: double1x2) -> double1x2 { return double1x2(v) }
+@(require_results) asdouble_double1x3 :: proc "c" (v: double1x3) -> double1x3 { return double1x3(v) }
+@(require_results) asdouble_double1x4 :: proc "c" (v: double1x4) -> double1x4 { return double1x4(v) }
+@(require_results) asdouble_double2x1 :: proc "c" (v: double2x1) -> double2x1 { return double2x1(v) }
+@(require_results) asdouble_double2x3 :: proc "c" (v: double2x3) -> double2x3 { return double2x3(v) }
+@(require_results) asdouble_double2x4 :: proc "c" (v: double2x4) -> double2x4 { return double2x4(v) }
+@(require_results) asdouble_double3x1 :: proc "c" (v: double3x1) -> double3x1 { return double3x1(v) }
+@(require_results) asdouble_double3x2 :: proc "c" (v: double3x2) -> double3x2 { return double3x2(v) }
+@(require_results) asdouble_double3x4 :: proc "c" (v: double3x4) -> double3x4 { return double3x4(v) }
+@(require_results) asdouble_double4x1 :: proc "c" (v: double4x1) -> double4x1 { return double4x1(v) }
+@(require_results) asdouble_double4x2 :: proc "c" (v: double4x2) -> double4x2 { return double4x2(v) }
+@(require_results) asdouble_double4x3 :: proc "c" (v: double4x3) -> double4x3 { return double4x3(v) }
+@(require_results) asdouble_double2 :: proc "c" (v: double2) -> double2 { return double2{double(v.x), double(v.y)} }
+@(require_results) asdouble_double3 :: proc "c" (v: double3) -> double3 { return double3{double(v.x), double(v.y), double(v.z)} }
+@(require_results) asdouble_double4 :: proc "c" (v: double4) -> double4 { return double4{double(v.x), double(v.y), double(v.z), double(v.w)} }
asint :: proc{
asint_float,
@@ -1754,57 +1762,57 @@ asint :: proc{
asint_double3,
asint_double4,
}
-asint_float :: proc "c" (v: float) -> int { return int(v) }
-asint_double :: proc "c" (v: double) -> int { return int(v) }
-asint_int :: proc "c" (v: int) -> int { return int(v) }
-asint_uint :: proc "c" (v: uint) -> int { return int(v) }
-asint_float1x1 :: proc "c" (v: float1x1) -> int1x1 { return int1x1(v) }
-asint_float2x2 :: proc "c" (v: float2x2) -> int2x2 { return int2x2(v) }
-asint_float3x3 :: proc "c" (v: float3x3) -> int3x3 { return int3x3(v) }
-asint_float4x4 :: proc "c" (v: float4x4) -> int4x4 { return int4x4(v) }
-asint_float1x2 :: proc "c" (v: float1x2) -> int1x2 { return int1x2(v) }
-asint_float1x3 :: proc "c" (v: float1x3) -> int1x3 { return int1x3(v) }
-asint_float1x4 :: proc "c" (v: float1x4) -> int1x4 { return int1x4(v) }
-asint_float2x1 :: proc "c" (v: float2x1) -> int2x1 { return int2x1(v) }
-asint_float2x3 :: proc "c" (v: float2x3) -> int2x3 { return int2x3(v) }
-asint_float2x4 :: proc "c" (v: float2x4) -> int2x4 { return int2x4(v) }
-asint_float3x1 :: proc "c" (v: float3x1) -> int3x1 { return int3x1(v) }
-asint_float3x2 :: proc "c" (v: float3x2) -> int3x2 { return int3x2(v) }
-asint_float3x4 :: proc "c" (v: float3x4) -> int3x4 { return int3x4(v) }
-asint_float4x1 :: proc "c" (v: float4x1) -> int4x1 { return int4x1(v) }
-asint_float4x2 :: proc "c" (v: float4x2) -> int4x2 { return int4x2(v) }
-asint_float4x3 :: proc "c" (v: float4x3) -> int4x3 { return int4x3(v) }
-asint_float2 :: proc "c" (v: float2) -> int2 { return int2{int(v.x), int(v.y)} }
-asint_float3 :: proc "c" (v: float3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
-asint_float4 :: proc "c" (v: float4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
-asint_int2 :: proc "c" (v: int2) -> int2 { return int2{int(v.x), int(v.y)} }
-asint_int3 :: proc "c" (v: int3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
-asint_int4 :: proc "c" (v: int4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
-asint_uint2 :: proc "c" (v: uint2) -> int2 { return int2{int(v.x), int(v.y)} }
-asint_uint3 :: proc "c" (v: uint3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
-asint_uint4 :: proc "c" (v: uint4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
-asint_bool2 :: proc "c" (v: bool2) -> int2 { return int2{int(int(v.x)), int(int(v.y))} }
-asint_bool3 :: proc "c" (v: bool3) -> int3 { return int3{int(int(v.x)), int(int(v.y)), int(int(v.z))} }
-asint_bool4 :: proc "c" (v: bool4) -> int4 { return int4{int(int(v.x)), int(int(v.y)), int(int(v.z)), int(int(v.w))} }
-asint_double1x1 :: proc "c" (v: double1x1) -> int1x1 { return int1x1(v) }
-asint_double2x2 :: proc "c" (v: double2x2) -> int2x2 { return int2x2(v) }
-asint_double3x3 :: proc "c" (v: double3x3) -> int3x3 { return int3x3(v) }
-asint_double4x4 :: proc "c" (v: double4x4) -> int4x4 { return int4x4(v) }
-asint_double1x2 :: proc "c" (v: double1x2) -> int1x2 { return int1x2(v) }
-asint_double1x3 :: proc "c" (v: double1x3) -> int1x3 { return int1x3(v) }
-asint_double1x4 :: proc "c" (v: double1x4) -> int1x4 { return int1x4(v) }
-asint_double2x1 :: proc "c" (v: double2x1) -> int2x1 { return int2x1(v) }
-asint_double2x3 :: proc "c" (v: double2x3) -> int2x3 { return int2x3(v) }
-asint_double2x4 :: proc "c" (v: double2x4) -> int2x4 { return int2x4(v) }
-asint_double3x1 :: proc "c" (v: double3x1) -> int3x1 { return int3x1(v) }
-asint_double3x2 :: proc "c" (v: double3x2) -> int3x2 { return int3x2(v) }
-asint_double3x4 :: proc "c" (v: double3x4) -> int3x4 { return int3x4(v) }
-asint_double4x1 :: proc "c" (v: double4x1) -> int4x1 { return int4x1(v) }
-asint_double4x2 :: proc "c" (v: double4x2) -> int4x2 { return int4x2(v) }
-asint_double4x3 :: proc "c" (v: double4x3) -> int4x3 { return int4x3(v) }
-asint_double2 :: proc "c" (v: double2) -> int2 { return int2{int(v.x), int(v.y)} }
-asint_double3 :: proc "c" (v: double3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
-asint_double4 :: proc "c" (v: double4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+@(require_results) asint_float :: proc "c" (v: float) -> int { return int(v) }
+@(require_results) asint_double :: proc "c" (v: double) -> int { return int(v) }
+@(require_results) asint_int :: proc "c" (v: int) -> int { return int(v) }
+@(require_results) asint_uint :: proc "c" (v: uint) -> int { return int(v) }
+@(require_results) asint_float1x1 :: proc "c" (v: float1x1) -> int1x1 { return int1x1(v) }
+@(require_results) asint_float2x2 :: proc "c" (v: float2x2) -> int2x2 { return int2x2(v) }
+@(require_results) asint_float3x3 :: proc "c" (v: float3x3) -> int3x3 { return int3x3(v) }
+@(require_results) asint_float4x4 :: proc "c" (v: float4x4) -> int4x4 { return int4x4(v) }
+@(require_results) asint_float1x2 :: proc "c" (v: float1x2) -> int1x2 { return int1x2(v) }
+@(require_results) asint_float1x3 :: proc "c" (v: float1x3) -> int1x3 { return int1x3(v) }
+@(require_results) asint_float1x4 :: proc "c" (v: float1x4) -> int1x4 { return int1x4(v) }
+@(require_results) asint_float2x1 :: proc "c" (v: float2x1) -> int2x1 { return int2x1(v) }
+@(require_results) asint_float2x3 :: proc "c" (v: float2x3) -> int2x3 { return int2x3(v) }
+@(require_results) asint_float2x4 :: proc "c" (v: float2x4) -> int2x4 { return int2x4(v) }
+@(require_results) asint_float3x1 :: proc "c" (v: float3x1) -> int3x1 { return int3x1(v) }
+@(require_results) asint_float3x2 :: proc "c" (v: float3x2) -> int3x2 { return int3x2(v) }
+@(require_results) asint_float3x4 :: proc "c" (v: float3x4) -> int3x4 { return int3x4(v) }
+@(require_results) asint_float4x1 :: proc "c" (v: float4x1) -> int4x1 { return int4x1(v) }
+@(require_results) asint_float4x2 :: proc "c" (v: float4x2) -> int4x2 { return int4x2(v) }
+@(require_results) asint_float4x3 :: proc "c" (v: float4x3) -> int4x3 { return int4x3(v) }
+@(require_results) asint_float2 :: proc "c" (v: float2) -> int2 { return int2{int(v.x), int(v.y)} }
+@(require_results) asint_float3 :: proc "c" (v: float3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
+@(require_results) asint_float4 :: proc "c" (v: float4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+@(require_results) asint_int2 :: proc "c" (v: int2) -> int2 { return int2{int(v.x), int(v.y)} }
+@(require_results) asint_int3 :: proc "c" (v: int3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
+@(require_results) asint_int4 :: proc "c" (v: int4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+@(require_results) asint_uint2 :: proc "c" (v: uint2) -> int2 { return int2{int(v.x), int(v.y)} }
+@(require_results) asint_uint3 :: proc "c" (v: uint3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
+@(require_results) asint_uint4 :: proc "c" (v: uint4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
+@(require_results) asint_bool2 :: proc "c" (v: bool2) -> int2 { return int2{int(int(v.x)), int(int(v.y))} }
+@(require_results) asint_bool3 :: proc "c" (v: bool3) -> int3 { return int3{int(int(v.x)), int(int(v.y)), int(int(v.z))} }
+@(require_results) asint_bool4 :: proc "c" (v: bool4) -> int4 { return int4{int(int(v.x)), int(int(v.y)), int(int(v.z)), int(int(v.w))} }
+@(require_results) asint_double1x1 :: proc "c" (v: double1x1) -> int1x1 { return int1x1(v) }
+@(require_results) asint_double2x2 :: proc "c" (v: double2x2) -> int2x2 { return int2x2(v) }
+@(require_results) asint_double3x3 :: proc "c" (v: double3x3) -> int3x3 { return int3x3(v) }
+@(require_results) asint_double4x4 :: proc "c" (v: double4x4) -> int4x4 { return int4x4(v) }
+@(require_results) asint_double1x2 :: proc "c" (v: double1x2) -> int1x2 { return int1x2(v) }
+@(require_results) asint_double1x3 :: proc "c" (v: double1x3) -> int1x3 { return int1x3(v) }
+@(require_results) asint_double1x4 :: proc "c" (v: double1x4) -> int1x4 { return int1x4(v) }
+@(require_results) asint_double2x1 :: proc "c" (v: double2x1) -> int2x1 { return int2x1(v) }
+@(require_results) asint_double2x3 :: proc "c" (v: double2x3) -> int2x3 { return int2x3(v) }
+@(require_results) asint_double2x4 :: proc "c" (v: double2x4) -> int2x4 { return int2x4(v) }
+@(require_results) asint_double3x1 :: proc "c" (v: double3x1) -> int3x1 { return int3x1(v) }
+@(require_results) asint_double3x2 :: proc "c" (v: double3x2) -> int3x2 { return int3x2(v) }
+@(require_results) asint_double3x4 :: proc "c" (v: double3x4) -> int3x4 { return int3x4(v) }
+@(require_results) asint_double4x1 :: proc "c" (v: double4x1) -> int4x1 { return int4x1(v) }
+@(require_results) asint_double4x2 :: proc "c" (v: double4x2) -> int4x2 { return int4x2(v) }
+@(require_results) asint_double4x3 :: proc "c" (v: double4x3) -> int4x3 { return int4x3(v) }
+@(require_results) asint_double2 :: proc "c" (v: double2) -> int2 { return int2{int(v.x), int(v.y)} }
+@(require_results) asint_double3 :: proc "c" (v: double3) -> int3 { return int3{int(v.x), int(v.y), int(v.z)} }
+@(require_results) asint_double4 :: proc "c" (v: double4) -> int4 { return int4{int(v.x), int(v.y), int(v.z), int(v.w)} }
asuint :: proc{
@@ -1828,25 +1836,25 @@ asuint :: proc{
asuint_double3,
asuint_double4,
}
-asuint_float :: proc "c" (v: float) -> uint { return uint(v) }
-asuint_double :: proc "c" (v: double) -> uint { return uint(v) }
-asuint_int :: proc "c" (v: int) -> uint { return uint(v) }
-asuint_uint :: proc "c" (v: uint) -> uint { return uint(v) }
-asuint_float2 :: proc "c" (v: float2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
-asuint_float3 :: proc "c" (v: float3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
-asuint_float4 :: proc "c" (v: float4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
-asuint_int2 :: proc "c" (v: int2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
-asuint_int3 :: proc "c" (v: int3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
-asuint_int4 :: proc "c" (v: int4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
-asuint_uint2 :: proc "c" (v: uint2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
-asuint_uint3 :: proc "c" (v: uint3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
-asuint_uint4 :: proc "c" (v: uint4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
-asuint_bool2 :: proc "c" (v: bool2) -> uint2 { return uint2{uint(uint(v.x)), uint(uint(v.y))} }
-asuint_bool3 :: proc "c" (v: bool3) -> uint3 { return uint3{uint(uint(v.x)), uint(uint(v.y)), uint(uint(v.z))} }
-asuint_bool4 :: proc "c" (v: bool4) -> uint4 { return uint4{uint(uint(v.x)), uint(uint(v.y)), uint(uint(v.z)), uint(uint(v.w))} }
-asuint_double2 :: proc "c" (v: double2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
-asuint_double3 :: proc "c" (v: double3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
-asuint_double4 :: proc "c" (v: double4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+@(require_results) asuint_float :: proc "c" (v: float) -> uint { return uint(v) }
+@(require_results) asuint_double :: proc "c" (v: double) -> uint { return uint(v) }
+@(require_results) asuint_int :: proc "c" (v: int) -> uint { return uint(v) }
+@(require_results) asuint_uint :: proc "c" (v: uint) -> uint { return uint(v) }
+@(require_results) asuint_float2 :: proc "c" (v: float2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
+@(require_results) asuint_float3 :: proc "c" (v: float3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+@(require_results) asuint_float4 :: proc "c" (v: float4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+@(require_results) asuint_int2 :: proc "c" (v: int2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
+@(require_results) asuint_int3 :: proc "c" (v: int3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+@(require_results) asuint_int4 :: proc "c" (v: int4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+@(require_results) asuint_uint2 :: proc "c" (v: uint2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
+@(require_results) asuint_uint3 :: proc "c" (v: uint3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+@(require_results) asuint_uint4 :: proc "c" (v: uint4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
+@(require_results) asuint_bool2 :: proc "c" (v: bool2) -> uint2 { return uint2{uint(uint(v.x)), uint(uint(v.y))} }
+@(require_results) asuint_bool3 :: proc "c" (v: bool3) -> uint3 { return uint3{uint(uint(v.x)), uint(uint(v.y)), uint(uint(v.z))} }
+@(require_results) asuint_bool4 :: proc "c" (v: bool4) -> uint4 { return uint4{uint(uint(v.x)), uint(uint(v.y)), uint(uint(v.z)), uint(uint(v.w))} }
+@(require_results) asuint_double2 :: proc "c" (v: double2) -> uint2 { return uint2{uint(v.x), uint(v.y)} }
+@(require_results) asuint_double3 :: proc "c" (v: double3) -> uint3 { return uint3{uint(v.x), uint(v.y), uint(v.z)} }
+@(require_results) asuint_double4 :: proc "c" (v: double4) -> uint4 { return uint4{uint(v.x), uint(v.y), uint(v.z), uint(v.w)} }
// TODO(bill): All of the `mul` procedures
diff --git a/core/math/linalg/hlsl/linalg_hlsl_math.odin b/core/math/linalg/hlsl/linalg_hlsl_math.odin
index 91c542b59..5b8004342 100644
--- a/core/math/linalg/hlsl/linalg_hlsl_math.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl_math.odin
@@ -2,34 +2,35 @@ package math_linalg_hlsl
import "core:math"
-cos_float :: proc "c" (x: float) -> float { return math.cos(x) }
-sin_float :: proc "c" (x: float) -> float { return math.sin(x) }
-tan_float :: proc "c" (x: float) -> float { return math.tan(x) }
-acos_float :: proc "c" (x: float) -> float { return math.acos(x) }
-asin_float :: proc "c" (x: float) -> float { return math.asin(x) }
-atan_float :: proc "c" (x: float) -> float { return math.atan(x) }
-atan2_float :: proc "c" (y, x: float) -> float { return math.atan2(y, x) }
-cosh_float :: proc "c" (x: float) -> float { return math.cosh(x) }
-sinh_float :: proc "c" (x: float) -> float { return math.sinh(x) }
-tanh_float :: proc "c" (x: float) -> float { return math.tanh(x) }
-acosh_float :: proc "c" (x: float) -> float { return math.acosh(x) }
-asinh_float :: proc "c" (x: float) -> float { return math.asinh(x) }
-atanh_float :: proc "c" (x: float) -> float { return math.atanh(x) }
-sqrt_float :: proc "c" (x: float) -> float { return math.sqrt(x) }
-rsqrt_float :: proc "c" (x: float) -> float { return 1.0/math.sqrt(x) }
-rcp_float :: proc "c" (x: float) -> float { return 1.0/x }
-pow_float :: proc "c" (x, y: float) -> float { return math.pow(x, y) }
-exp_float :: proc "c" (x: float) -> float { return math.exp(x) }
-log_float :: proc "c" (x: float) -> float { return math.ln(x) }
-log2_float :: proc "c" (x: float) -> float { return math.log(x, 2) }
-log10_float :: proc "c" (x: float) -> float { return math.log(x, 10) }
-exp2_float :: proc "c" (x: float) -> float { return math.pow(float(2), x) }
-sign_float :: proc "c" (x: float) -> float { return math.sign(x) }
-floor_float :: proc "c" (x: float) -> float { return math.floor(x) }
-round_float :: proc "c" (x: float) -> float { return math.round(x) }
-ceil_float :: proc "c" (x: float) -> float { return math.ceil(x) }
-isnan_float :: proc "c" (x: float) -> bool { return math.classify(x) == .NaN}
-fmod_float :: proc "c" (x, y: float) -> float { return math.mod(x, y) }
+@(require_results) cos_float :: proc "c" (x: float) -> float { return math.cos(x) }
+@(require_results) sin_float :: proc "c" (x: float) -> float { return math.sin(x) }
+@(require_results) tan_float :: proc "c" (x: float) -> float { return math.tan(x) }
+@(require_results) acos_float :: proc "c" (x: float) -> float { return math.acos(x) }
+@(require_results) asin_float :: proc "c" (x: float) -> float { return math.asin(x) }
+@(require_results) atan_float :: proc "c" (x: float) -> float { return math.atan(x) }
+@(require_results) atan2_float :: proc "c" (y, x: float) -> float { return math.atan2(y, x) }
+@(require_results) cosh_float :: proc "c" (x: float) -> float { return math.cosh(x) }
+@(require_results) sinh_float :: proc "c" (x: float) -> float { return math.sinh(x) }
+@(require_results) tanh_float :: proc "c" (x: float) -> float { return math.tanh(x) }
+@(require_results) acosh_float :: proc "c" (x: float) -> float { return math.acosh(x) }
+@(require_results) asinh_float :: proc "c" (x: float) -> float { return math.asinh(x) }
+@(require_results) atanh_float :: proc "c" (x: float) -> float { return math.atanh(x) }
+@(require_results) sqrt_float :: proc "c" (x: float) -> float { return math.sqrt(x) }
+@(require_results) rsqrt_float :: proc "c" (x: float) -> float { return 1.0/math.sqrt(x) }
+@(require_results) rcp_float :: proc "c" (x: float) -> float { return 1.0/x }
+@(require_results) pow_float :: proc "c" (x, y: float) -> float { return math.pow(x, y) }
+@(require_results) exp_float :: proc "c" (x: float) -> float { return math.exp(x) }
+@(require_results) log_float :: proc "c" (x: float) -> float { return math.ln(x) }
+@(require_results) log2_float :: proc "c" (x: float) -> float { return math.log(x, 2) }
+@(require_results) log10_float :: proc "c" (x: float) -> float { return math.log(x, 10) }
+@(require_results) exp2_float :: proc "c" (x: float) -> float { return math.pow(float(2), x) }
+@(require_results) sign_float :: proc "c" (x: float) -> float { return math.sign(x) }
+@(require_results) floor_float :: proc "c" (x: float) -> float { return math.floor(x) }
+@(require_results) round_float :: proc "c" (x: float) -> float { return math.round(x) }
+@(require_results) ceil_float :: proc "c" (x: float) -> float { return math.ceil(x) }
+@(require_results) isnan_float :: proc "c" (x: float) -> bool { return math.classify(x) == .NaN}
+@(require_results) fmod_float :: proc "c" (x, y: float) -> float { return math.mod(x, y) }
+@(require_results)
frac_float :: proc "c" (x: float) -> float {
if x >= 0 {
return x - math.trunc(x)
@@ -38,34 +39,35 @@ frac_float :: proc "c" (x: float) -> float {
}
-cos_double :: proc "c" (x: double) -> double { return math.cos(x) }
-sin_double :: proc "c" (x: double) -> double { return math.sin(x) }
-tan_double :: proc "c" (x: double) -> double { return math.tan(x) }
-acos_double :: proc "c" (x: double) -> double { return math.acos(x) }
-asin_double :: proc "c" (x: double) -> double { return math.asin(x) }
-atan_double :: proc "c" (x: double) -> double { return math.atan(x) }
-atan2_double :: proc "c" (y, x: double) -> double { return math.atan2(y, x) }
-cosh_double :: proc "c" (x: double) -> double { return math.cosh(x) }
-sinh_double :: proc "c" (x: double) -> double { return math.sinh(x) }
-tanh_double :: proc "c" (x: double) -> double { return math.tanh(x) }
-acosh_double :: proc "c" (x: double) -> double { return math.acosh(x) }
-asinh_double :: proc "c" (x: double) -> double { return math.asinh(x) }
-atanh_double :: proc "c" (x: double) -> double { return math.atanh(x) }
-sqrt_double :: proc "c" (x: double) -> double { return math.sqrt(x) }
-rsqrt_double :: proc "c" (x: double) -> double { return 1.0/math.sqrt(x) }
-rcp_double :: proc "c" (x: double) -> double { return 1.0/x }
-pow_double :: proc "c" (x, y: double) -> double { return math.pow(x, y) }
-exp_double :: proc "c" (x: double) -> double { return math.exp(x) }
-log_double :: proc "c" (x: double) -> double { return math.ln(x) }
-log2_double :: proc "c" (x: double) -> double { return math.log(x, 2) }
-log10_double :: proc "c" (x: double) -> double { return math.log(x, 10) }
-exp2_double :: proc "c" (x: double) -> double { return math.pow(double(2), x) }
-sign_double :: proc "c" (x: double) -> double { return math.sign(x) }
-floor_double :: proc "c" (x: double) -> double { return math.floor(x) }
-round_double :: proc "c" (x: double) -> double { return math.round(x) }
-ceil_double :: proc "c" (x: double) -> double { return math.ceil(x) }
-isnan_double :: proc "c" (x: double) -> bool { return math.classify(x) == .NaN}
-fmod_double :: proc "c" (x, y: double) -> double { return math.mod(x, y) }
+@(require_results) cos_double :: proc "c" (x: double) -> double { return math.cos(x) }
+@(require_results) sin_double :: proc "c" (x: double) -> double { return math.sin(x) }
+@(require_results) tan_double :: proc "c" (x: double) -> double { return math.tan(x) }
+@(require_results) acos_double :: proc "c" (x: double) -> double { return math.acos(x) }
+@(require_results) asin_double :: proc "c" (x: double) -> double { return math.asin(x) }
+@(require_results) atan_double :: proc "c" (x: double) -> double { return math.atan(x) }
+@(require_results) atan2_double :: proc "c" (y, x: double) -> double { return math.atan2(y, x) }
+@(require_results) cosh_double :: proc "c" (x: double) -> double { return math.cosh(x) }
+@(require_results) sinh_double :: proc "c" (x: double) -> double { return math.sinh(x) }
+@(require_results) tanh_double :: proc "c" (x: double) -> double { return math.tanh(x) }
+@(require_results) acosh_double :: proc "c" (x: double) -> double { return math.acosh(x) }
+@(require_results) asinh_double :: proc "c" (x: double) -> double { return math.asinh(x) }
+@(require_results) atanh_double :: proc "c" (x: double) -> double { return math.atanh(x) }
+@(require_results) sqrt_double :: proc "c" (x: double) -> double { return math.sqrt(x) }
+@(require_results) rsqrt_double :: proc "c" (x: double) -> double { return 1.0/math.sqrt(x) }
+@(require_results) rcp_double :: proc "c" (x: double) -> double { return 1.0/x }
+@(require_results) pow_double :: proc "c" (x, y: double) -> double { return math.pow(x, y) }
+@(require_results) exp_double :: proc "c" (x: double) -> double { return math.exp(x) }
+@(require_results) log_double :: proc "c" (x: double) -> double { return math.ln(x) }
+@(require_results) log2_double :: proc "c" (x: double) -> double { return math.log(x, 2) }
+@(require_results) log10_double :: proc "c" (x: double) -> double { return math.log(x, 10) }
+@(require_results) exp2_double :: proc "c" (x: double) -> double { return math.pow(double(2), x) }
+@(require_results) sign_double :: proc "c" (x: double) -> double { return math.sign(x) }
+@(require_results) floor_double :: proc "c" (x: double) -> double { return math.floor(x) }
+@(require_results) round_double :: proc "c" (x: double) -> double { return math.round(x) }
+@(require_results) ceil_double :: proc "c" (x: double) -> double { return math.ceil(x) }
+@(require_results) isnan_double :: proc "c" (x: double) -> bool { return math.classify(x) == .NaN}
+@(require_results) fmod_double :: proc "c" (x, y: double) -> double { return math.mod(x, y) }
+@(require_results)
frac_double :: proc "c" (x: double) -> double {
if x >= 0 {
return x - math.trunc(x)
diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin
index c4ecb194f..6607b241f 100644
--- a/core/math/linalg/specific.odin
+++ b/core/math/linalg/specific.odin
@@ -130,11 +130,13 @@ VECTOR3F64_Y_AXIS :: Vector3f64{0, 1, 0}
VECTOR3F64_Z_AXIS :: Vector3f64{0, 0, 1}
-vector2_orthogonal :: proc(v: $V/[2]$E) -> V where !IS_ARRAY(E), IS_FLOAT(E) {
+@(require_results)
+vector2_orthogonal :: proc "contextless" (v: $V/[2]$E) -> V where !IS_ARRAY(E), IS_FLOAT(E) {
return {-v.y, v.x}
}
-vector3_orthogonal :: proc(v: $V/[3]$E) -> V where !IS_ARRAY(E), IS_FLOAT(E) {
+@(require_results)
+vector3_orthogonal :: proc "contextless" (v: $V/[3]$E) -> V where !IS_ARRAY(E), IS_FLOAT(E) {
x := abs(v.x)
y := abs(v.y)
z := abs(v.z)
@@ -160,21 +162,24 @@ orthogonal :: proc{vector2_orthogonal, vector3_orthogonal}
-vector4_srgb_to_linear_f16 :: proc(col: Vector4f16) -> Vector4f16 {
+@(require_results)
+vector4_srgb_to_linear_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
r := math.pow(col.x, 2.2)
g := math.pow(col.y, 2.2)
b := math.pow(col.z, 2.2)
a := col.w
return {r, g, b, a}
}
-vector4_srgb_to_linear_f32 :: proc(col: Vector4f32) -> Vector4f32 {
+@(require_results)
+vector4_srgb_to_linear_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
r := math.pow(col.x, 2.2)
g := math.pow(col.y, 2.2)
b := math.pow(col.z, 2.2)
a := col.w
return {r, g, b, a}
}
-vector4_srgb_to_linear_f64 :: proc(col: Vector4f64) -> Vector4f64 {
+@(require_results)
+vector4_srgb_to_linear_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
r := math.pow(col.x, 2.2)
g := math.pow(col.y, 2.2)
b := math.pow(col.z, 2.2)
@@ -188,7 +193,8 @@ vector4_srgb_to_linear :: proc{
}
-vector4_linear_to_srgb_f16 :: proc(col: Vector4f16) -> Vector4f16 {
+@(require_results)
+vector4_linear_to_srgb_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
a :: 2.51
b :: 0.03
c :: 2.43
@@ -209,7 +215,8 @@ vector4_linear_to_srgb_f16 :: proc(col: Vector4f16) -> Vector4f16 {
return {x, y, z, col.w}
}
-vector4_linear_to_srgb_f32 :: proc(col: Vector4f32) -> Vector4f32 {
+@(require_results)
+vector4_linear_to_srgb_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
a :: 2.51
b :: 0.03
c :: 2.43
@@ -230,7 +237,8 @@ vector4_linear_to_srgb_f32 :: proc(col: Vector4f32) -> Vector4f32 {
return {x, y, z, col.w}
}
-vector4_linear_to_srgb_f64 :: proc(col: Vector4f64) -> Vector4f64 {
+@(require_results)
+vector4_linear_to_srgb_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
a :: 2.51
b :: 0.03
c :: 2.43
@@ -258,8 +266,10 @@ vector4_linear_to_srgb :: proc{
}
-vector4_hsl_to_rgb_f16 :: proc(h, s, l: f16, a: f16 = 1) -> Vector4f16 {
- hue_to_rgb :: proc(p, q, t: f16) -> f16 {
+@(require_results)
+vector4_hsl_to_rgb_f16 :: proc "contextless" (h, s, l: f16, a: f16 = 1) -> Vector4f16 {
+ @(require_results)
+ hue_to_rgb :: proc "contextless" (p, q, t: f16) -> f16 {
t := t
if t < 0 { t += 1 }
if t > 1 { t -= 1 }
@@ -285,8 +295,10 @@ vector4_hsl_to_rgb_f16 :: proc(h, s, l: f16, a: f16 = 1) -> Vector4f16 {
}
return {r, g, b, a}
}
-vector4_hsl_to_rgb_f32 :: proc(h, s, l: f32, a: f32 = 1) -> Vector4f32 {
- hue_to_rgb :: proc(p, q, t: f32) -> f32 {
+@(require_results)
+vector4_hsl_to_rgb_f32 :: proc "contextless" (h, s, l: f32, a: f32 = 1) -> Vector4f32 {
+ @(require_results)
+ hue_to_rgb :: proc "contextless" (p, q, t: f32) -> f32 {
t := t
if t < 0 { t += 1 }
if t > 1 { t -= 1 }
@@ -312,8 +324,10 @@ vector4_hsl_to_rgb_f32 :: proc(h, s, l: f32, a: f32 = 1) -> Vector4f32 {
}
return {r, g, b, a}
}
-vector4_hsl_to_rgb_f64 :: proc(h, s, l: f64, a: f64 = 1) -> Vector4f64 {
- hue_to_rgb :: proc(p, q, t: f64) -> f64 {
+@(require_results)
+vector4_hsl_to_rgb_f64 :: proc "contextless" (h, s, l: f64, a: f64 = 1) -> Vector4f64 {
+ @(require_results)
+ hue_to_rgb :: proc "contextless" (p, q, t: f64) -> f64 {
t := t
if t < 0 { t += 1 }
if t > 1 { t -= 1 }
@@ -346,7 +360,8 @@ vector4_hsl_to_rgb :: proc{
}
-vector4_rgb_to_hsl_f16 :: proc(col: Vector4f16) -> Vector4f16 {
+@(require_results)
+vector4_rgb_to_hsl_f16 :: proc "contextless" (col: Vector4f16) -> Vector4f16 {
r := col.x
g := col.y
b := col.z
@@ -375,7 +390,8 @@ vector4_rgb_to_hsl_f16 :: proc(col: Vector4f16) -> Vector4f16 {
return {h, s, l, a}
}
-vector4_rgb_to_hsl_f32 :: proc(col: Vector4f32) -> Vector4f32 {
+@(require_results)
+vector4_rgb_to_hsl_f32 :: proc "contextless" (col: Vector4f32) -> Vector4f32 {
r := col.x
g := col.y
b := col.z
@@ -404,7 +420,8 @@ vector4_rgb_to_hsl_f32 :: proc(col: Vector4f32) -> Vector4f32 {
return {h, s, l, a}
}
-vector4_rgb_to_hsl_f64 :: proc(col: Vector4f64) -> Vector4f64 {
+@(require_results)
+vector4_rgb_to_hsl_f64 :: proc "contextless" (col: Vector4f64) -> Vector4f64 {
r := col.x
g := col.y
b := col.z
@@ -441,7 +458,8 @@ vector4_rgb_to_hsl :: proc{
-quaternion_angle_axis_f16 :: proc(angle_radians: f16, axis: Vector3f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_angle_axis_f16 :: proc "contextless" (angle_radians: f16, axis: Vector3f16) -> (q: Quaternionf16) {
t := angle_radians*0.5
v := normalize(axis) * math.sin(t)
q.x = v.x
@@ -450,7 +468,8 @@ quaternion_angle_axis_f16 :: proc(angle_radians: f16, axis: Vector3f16) -> (q: Q
q.w = math.cos(t)
return
}
-quaternion_angle_axis_f32 :: proc(angle_radians: f32, axis: Vector3f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_angle_axis_f32 :: proc "contextless" (angle_radians: f32, axis: Vector3f32) -> (q: Quaternionf32) {
t := angle_radians*0.5
v := normalize(axis) * math.sin(t)
q.x = v.x
@@ -459,7 +478,8 @@ quaternion_angle_axis_f32 :: proc(angle_radians: f32, axis: Vector3f32) -> (q: Q
q.w = math.cos(t)
return
}
-quaternion_angle_axis_f64 :: proc(angle_radians: f64, axis: Vector3f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_angle_axis_f64 :: proc "contextless" (angle_radians: f64, axis: Vector3f64) -> (q: Quaternionf64) {
t := angle_radians*0.5
v := normalize(axis) * math.sin(t)
q.x = v.x
@@ -474,21 +494,24 @@ quaternion_angle_axis :: proc{
quaternion_angle_axis_f64,
}
-angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
+@(require_results)
+angle_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> f16 {
if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
}
return math.acos(q.w) * 2
}
-angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
+@(require_results)
+angle_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> f32 {
if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
}
return math.acos(q.w) * 2
}
-angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
+@(require_results)
+angle_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> f64 {
if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(math.sqrt(q.x*q.x + q.y*q.y + q.z*q.z)) * 2
}
@@ -501,7 +524,8 @@ angle_from_quaternion :: proc{
angle_from_quaternion_f64,
}
-axis_from_quaternion_f16 :: proc(q: Quaternionf16) -> Vector3f16 {
+@(require_results)
+axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> Vector3f16 {
t1 := 1 - q.w*q.w
if t1 < 0 {
return {0, 0, 1}
@@ -509,7 +533,8 @@ axis_from_quaternion_f16 :: proc(q: Quaternionf16) -> Vector3f16 {
t2 := 1.0 / math.sqrt(t1)
return {q.x*t2, q.y*t2, q.z*t2}
}
-axis_from_quaternion_f32 :: proc(q: Quaternionf32) -> Vector3f32 {
+@(require_results)
+axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> Vector3f32 {
t1 := 1 - q.w*q.w
if t1 < 0 {
return {0, 0, 1}
@@ -517,7 +542,8 @@ axis_from_quaternion_f32 :: proc(q: Quaternionf32) -> Vector3f32 {
t2 := 1.0 / math.sqrt(t1)
return {q.x*t2, q.y*t2, q.z*t2}
}
-axis_from_quaternion_f64 :: proc(q: Quaternionf64) -> Vector3f64 {
+@(require_results)
+axis_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> Vector3f64 {
t1 := 1 - q.w*q.w
if t1 < 0 {
return {0, 0, 1}
@@ -532,17 +558,20 @@ axis_from_quaternion :: proc{
}
-angle_axis_from_quaternion_f16 :: proc(q: Quaternionf16) -> (angle: f16, axis: Vector3f16) {
+@(require_results)
+angle_axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (angle: f16, axis: Vector3f16) {
angle = angle_from_quaternion(q)
axis = axis_from_quaternion(q)
return
}
-angle_axis_from_quaternion_f32 :: proc(q: Quaternionf32) -> (angle: f32, axis: Vector3f32) {
+@(require_results)
+angle_axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (angle: f32, axis: Vector3f32) {
angle = angle_from_quaternion(q)
axis = axis_from_quaternion(q)
return
}
-angle_axis_from_quaternion_f64 :: proc(q: Quaternionf64) -> (angle: f64, axis: Vector3f64) {
+@(require_results)
+angle_axis_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (angle: f64, axis: Vector3f64) {
angle = angle_from_quaternion(q)
axis = axis_from_quaternion(q)
return
@@ -554,7 +583,8 @@ angle_axis_from_quaternion :: proc {
}
-quaternion_from_forward_and_up_f16 :: proc(forward, up: Vector3f16) -> Quaternionf16 {
+@(require_results)
+quaternion_from_forward_and_up_f16 :: proc "contextless" (forward, up: Vector3f16) -> Quaternionf16 {
f := normalize(forward)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -597,7 +627,8 @@ quaternion_from_forward_and_up_f16 :: proc(forward, up: Vector3f16) -> Quaternio
return normalize(q)
}
-quaternion_from_forward_and_up_f32 :: proc(forward, up: Vector3f32) -> Quaternionf32 {
+@(require_results)
+quaternion_from_forward_and_up_f32 :: proc "contextless" (forward, up: Vector3f32) -> Quaternionf32 {
f := normalize(forward)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -640,7 +671,8 @@ quaternion_from_forward_and_up_f32 :: proc(forward, up: Vector3f32) -> Quaternio
return normalize(q)
}
-quaternion_from_forward_and_up_f64 :: proc(forward, up: Vector3f64) -> Quaternionf64 {
+@(require_results)
+quaternion_from_forward_and_up_f64 :: proc "contextless" (forward, up: Vector3f64) -> Quaternionf64 {
f := normalize(forward)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -689,13 +721,16 @@ quaternion_from_forward_and_up :: proc{
quaternion_from_forward_and_up_f64,
}
-quaternion_look_at_f16 :: proc(eye, centre: Vector3f16, up: Vector3f16) -> Quaternionf16 {
+@(require_results)
+quaternion_look_at_f16 :: proc "contextless" (eye, centre: Vector3f16, up: Vector3f16) -> Quaternionf16 {
return quaternion_from_matrix3(matrix3_look_at(eye, centre, up))
}
-quaternion_look_at_f32 :: proc(eye, centre: Vector3f32, up: Vector3f32) -> Quaternionf32 {
+@(require_results)
+quaternion_look_at_f32 :: proc "contextless" (eye, centre: Vector3f32, up: Vector3f32) -> Quaternionf32 {
return quaternion_from_matrix3(matrix3_look_at(eye, centre, up))
}
-quaternion_look_at_f64 :: proc(eye, centre: Vector3f64, up: Vector3f64) -> Quaternionf64 {
+@(require_results)
+quaternion_look_at_f64 :: proc "contextless" (eye, centre: Vector3f64, up: Vector3f64) -> Quaternionf64 {
return quaternion_from_matrix3(matrix3_look_at(eye, centre, up))
}
quaternion_look_at :: proc{
@@ -706,21 +741,24 @@ quaternion_look_at :: proc{
-quaternion_nlerp_f16 :: proc(a, b: Quaternionf16, t: f16) -> (c: Quaternionf16) {
+@(require_results)
+quaternion_nlerp_f16 :: proc "contextless" (a, b: Quaternionf16, t: f16) -> (c: Quaternionf16) {
c.x = a.x + (b.x-a.x)*t
c.y = a.y + (b.y-a.y)*t
c.z = a.z + (b.z-a.z)*t
c.w = a.w + (b.w-a.w)*t
return normalize(c)
}
-quaternion_nlerp_f32 :: proc(a, b: Quaternionf32, t: f32) -> (c: Quaternionf32) {
+@(require_results)
+quaternion_nlerp_f32 :: proc "contextless" (a, b: Quaternionf32, t: f32) -> (c: Quaternionf32) {
c.x = a.x + (b.x-a.x)*t
c.y = a.y + (b.y-a.y)*t
c.z = a.z + (b.z-a.z)*t
c.w = a.w + (b.w-a.w)*t
return normalize(c)
}
-quaternion_nlerp_f64 :: proc(a, b: Quaternionf64, t: f64) -> (c: Quaternionf64) {
+@(require_results)
+quaternion_nlerp_f64 :: proc "contextless" (a, b: Quaternionf64, t: f64) -> (c: Quaternionf64) {
c.x = a.x + (b.x-a.x)*t
c.y = a.y + (b.y-a.y)*t
c.z = a.z + (b.z-a.z)*t
@@ -734,7 +772,8 @@ quaternion_nlerp :: proc{
}
-quaternion_slerp_f16 :: proc(x, y: Quaternionf16, t: f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_slerp_f16 :: proc "contextless" (x, y: Quaternionf16, t: f16) -> (q: Quaternionf16) {
a, b := x, y
cos_angle := dot(a, b)
if cos_angle < 0 {
@@ -761,7 +800,8 @@ quaternion_slerp_f16 :: proc(x, y: Quaternionf16, t: f16) -> (q: Quaternionf16)
q.w = factor_a * a.w + factor_b * b.w
return
}
-quaternion_slerp_f32 :: proc(x, y: Quaternionf32, t: f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_slerp_f32 :: proc "contextless" (x, y: Quaternionf32, t: f32) -> (q: Quaternionf32) {
a, b := x, y
cos_angle := dot(a, b)
if cos_angle < 0 {
@@ -788,7 +828,8 @@ quaternion_slerp_f32 :: proc(x, y: Quaternionf32, t: f32) -> (q: Quaternionf32)
q.w = factor_a * a.w + factor_b * b.w
return
}
-quaternion_slerp_f64 :: proc(x, y: Quaternionf64, t: f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_slerp_f64 :: proc "contextless" (x, y: Quaternionf64, t: f64) -> (q: Quaternionf64) {
a, b := x, y
cos_angle := dot(a, b)
if cos_angle < 0 {
@@ -822,15 +863,18 @@ quaternion_slerp :: proc{
}
-quaternion_squad_f16 :: proc(q1, q2, s1, s2: Quaternionf16, h: f16) -> Quaternionf16 {
+@(require_results)
+quaternion_squad_f16 :: proc "contextless" (q1, q2, s1, s2: Quaternionf16, h: f16) -> Quaternionf16 {
slerp :: quaternion_slerp
return slerp(slerp(q1, q2, h), slerp(s1, s2, h), 2 * (1 - h) * h)
}
-quaternion_squad_f32 :: proc(q1, q2, s1, s2: Quaternionf32, h: f32) -> Quaternionf32 {
+@(require_results)
+quaternion_squad_f32 :: proc "contextless" (q1, q2, s1, s2: Quaternionf32, h: f32) -> Quaternionf32 {
slerp :: quaternion_slerp
return slerp(slerp(q1, q2, h), slerp(s1, s2, h), 2 * (1 - h) * h)
}
-quaternion_squad_f64 :: proc(q1, q2, s1, s2: Quaternionf64, h: f64) -> Quaternionf64 {
+@(require_results)
+quaternion_squad_f64 :: proc "contextless" (q1, q2, s1, s2: Quaternionf64, h: f64) -> Quaternionf64 {
slerp :: quaternion_slerp
return slerp(slerp(q1, q2, h), slerp(s1, s2, h), 2 * (1 - h) * h)
}
@@ -841,21 +885,24 @@ quaternion_squad :: proc{
}
-quaternion_from_matrix4_f16 :: proc(m: Matrix4f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (q: Quaternionf16) {
m3: Matrix3f16 = ---
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
m3[0, 2], m3[1, 2], m3[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return quaternion_from_matrix3(m3)
}
-quaternion_from_matrix4_f32 :: proc(m: Matrix4f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (q: Quaternionf32) {
m3: Matrix3f32 = ---
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
m3[0, 2], m3[1, 2], m3[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return quaternion_from_matrix3(m3)
}
-quaternion_from_matrix4_f64 :: proc(m: Matrix4f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (q: Quaternionf64) {
m3: Matrix3f64 = ---
m3[0, 0], m3[1, 0], m3[2, 0] = m[0, 0], m[1, 0], m[2, 0]
m3[0, 1], m3[1, 1], m3[2, 1] = m[0, 1], m[1, 1], m[2, 1]
@@ -869,7 +916,8 @@ quaternion_from_matrix4 :: proc{
}
-quaternion_from_matrix3_f16 :: proc(m: Matrix3f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (q: Quaternionf16) {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
@@ -918,7 +966,8 @@ quaternion_from_matrix3_f16 :: proc(m: Matrix3f16) -> (q: Quaternionf16) {
}
return
}
-quaternion_from_matrix3_f32 :: proc(m: Matrix3f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (q: Quaternionf32) {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
@@ -967,7 +1016,8 @@ quaternion_from_matrix3_f32 :: proc(m: Matrix3f32) -> (q: Quaternionf32) {
}
return
}
-quaternion_from_matrix3_f64 :: proc(m: Matrix3f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (q: Quaternionf64) {
four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
@@ -1023,7 +1073,8 @@ quaternion_from_matrix3 :: proc{
}
-quaternion_between_two_vector3_f16 :: proc(from, to: Vector3f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_between_two_vector3_f16 :: proc "contextless" (from, to: Vector3f16) -> (q: Quaternionf16) {
x := normalize(from)
y := normalize(to)
@@ -1044,7 +1095,8 @@ quaternion_between_two_vector3_f16 :: proc(from, to: Vector3f16) -> (q: Quaterni
q.z = v.z
return normalize(q)
}
-quaternion_between_two_vector3_f32 :: proc(from, to: Vector3f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_between_two_vector3_f32 :: proc "contextless" (from, to: Vector3f32) -> (q: Quaternionf32) {
x := normalize(from)
y := normalize(to)
@@ -1065,7 +1117,8 @@ quaternion_between_two_vector3_f32 :: proc(from, to: Vector3f32) -> (q: Quaterni
q.z = v.z
return normalize(q)
}
-quaternion_between_two_vector3_f64 :: proc(from, to: Vector3f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_between_two_vector3_f64 :: proc "contextless" (from, to: Vector3f64) -> (q: Quaternionf64) {
x := normalize(from)
y := normalize(to)
@@ -1093,7 +1146,8 @@ quaternion_between_two_vector3 :: proc{
}
-matrix2_inverse_transpose_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
+@(require_results)
+matrix2_inverse_transpose_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1102,7 +1156,8 @@ matrix2_inverse_transpose_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
c[1, 1] = +m[0, 0] * id
return c
}
-matrix2_inverse_transpose_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
+@(require_results)
+matrix2_inverse_transpose_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1111,7 +1166,8 @@ matrix2_inverse_transpose_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
c[1, 1] = +m[0, 0] * id
return c
}
-matrix2_inverse_transpose_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
+@(require_results)
+matrix2_inverse_transpose_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1127,13 +1183,16 @@ matrix2_inverse_transpose :: proc{
}
-matrix2_determinant_f16 :: proc(m: Matrix2f16) -> f16 {
+@(require_results)
+matrix2_determinant_f16 :: proc "contextless" (m: Matrix2f16) -> f16 {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
-matrix2_determinant_f32 :: proc(m: Matrix2f32) -> f32 {
+@(require_results)
+matrix2_determinant_f32 :: proc "contextless" (m: Matrix2f32) -> f32 {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
-matrix2_determinant_f64 :: proc(m: Matrix2f64) -> f64 {
+@(require_results)
+matrix2_determinant_f64 :: proc "contextless" (m: Matrix2f64) -> f64 {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant :: proc{
@@ -1143,7 +1202,8 @@ matrix2_determinant :: proc{
}
-matrix2_inverse_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
+@(require_results)
+matrix2_inverse_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1152,7 +1212,8 @@ matrix2_inverse_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
c[1, 1] = +m[0, 0] * id
return c
}
-matrix2_inverse_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
+@(require_results)
+matrix2_inverse_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1161,7 +1222,8 @@ matrix2_inverse_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
c[1, 1] = +m[0, 0] * id
return c
}
-matrix2_inverse_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
+@(require_results)
+matrix2_inverse_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
c[0, 0] = +m[1, 1] * id
@@ -1177,21 +1239,24 @@ matrix2_inverse :: proc{
}
-matrix2_adjoint_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
+@(require_results)
+matrix2_adjoint_f16 :: proc "contextless" (m: Matrix2f16) -> (c: Matrix2f16) {
c[0, 0] = +m[1, 1]
c[1, 0] = -m[0, 1]
c[0, 1] = -m[1, 0]
c[1, 1] = +m[0, 0]
return c
}
-matrix2_adjoint_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
+@(require_results)
+matrix2_adjoint_f32 :: proc "contextless" (m: Matrix2f32) -> (c: Matrix2f32) {
c[0, 0] = +m[1, 1]
c[1, 0] = -m[0, 1]
c[0, 1] = -m[1, 0]
c[1, 1] = +m[0, 0]
return c
}
-matrix2_adjoint_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
+@(require_results)
+matrix2_adjoint_f64 :: proc "contextless" (m: Matrix2f64) -> (c: Matrix2f64) {
c[0, 0] = +m[1, 1]
c[1, 0] = -m[0, 1]
c[0, 1] = -m[1, 0]
@@ -1205,7 +1270,8 @@ matrix2_adjoint :: proc{
}
-matrix3_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix3f16) {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1229,7 +1295,8 @@ matrix3_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix3f16) {
m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
-matrix3_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix3f32) {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1253,7 +1320,8 @@ matrix3_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix3f32) {
m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
-matrix3_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix3f64) {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1284,13 +1352,16 @@ matrix3_from_quaternion :: proc{
}
-matrix3_inverse_f16 :: proc(m: Matrix3f16) -> Matrix3f16 {
+@(require_results)
+matrix3_inverse_f16 :: proc "contextless" (m: Matrix3f16) -> Matrix3f16 {
return transpose(matrix3_inverse_transpose(m))
}
-matrix3_inverse_f32 :: proc(m: Matrix3f32) -> Matrix3f32 {
+@(require_results)
+matrix3_inverse_f32 :: proc "contextless" (m: Matrix3f32) -> Matrix3f32 {
return transpose(matrix3_inverse_transpose(m))
}
-matrix3_inverse_f64 :: proc(m: Matrix3f64) -> Matrix3f64 {
+@(require_results)
+matrix3_inverse_f64 :: proc "contextless" (m: Matrix3f64) -> Matrix3f64 {
return transpose(matrix3_inverse_transpose(m))
}
matrix3_inverse :: proc{
@@ -1300,19 +1371,22 @@ matrix3_inverse :: proc{
}
-matrix3_determinant_f16 :: proc(m: Matrix3f16) -> f16 {
+@(require_results)
+matrix3_determinant_f16 :: proc "contextless" (m: Matrix3f16) -> f16 {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
-matrix3_determinant_f32 :: proc(m: Matrix3f32) -> f32 {
+@(require_results)
+matrix3_determinant_f32 :: proc "contextless" (m: Matrix3f32) -> f32 {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
-matrix3_determinant_f64 :: proc(m: Matrix3f64) -> f64 {
+@(require_results)
+matrix3_determinant_f64 :: proc "contextless" (m: Matrix3f64) -> f64 {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
@@ -1325,7 +1399,8 @@ matrix3_determinant :: proc{
}
-matrix3_adjoint_f16 :: proc(m: Matrix3f16) -> (adjoint: Matrix3f16) {
+@(require_results)
+matrix3_adjoint_f16 :: proc "contextless" (m: Matrix3f16) -> (adjoint: Matrix3f16) {
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1337,7 +1412,8 @@ matrix3_adjoint_f16 :: proc(m: Matrix3f16) -> (adjoint: Matrix3f16) {
adjoint[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return adjoint
}
-matrix3_adjoint_f32 :: proc(m: Matrix3f32) -> (adjoint: Matrix3f32) {
+@(require_results)
+matrix3_adjoint_f32 :: proc "contextless" (m: Matrix3f32) -> (adjoint: Matrix3f32) {
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1349,7 +1425,8 @@ matrix3_adjoint_f32 :: proc(m: Matrix3f32) -> (adjoint: Matrix3f32) {
adjoint[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return adjoint
}
-matrix3_adjoint_f64 :: proc(m: Matrix3f64) -> (adjoint: Matrix3f64) {
+@(require_results)
+matrix3_adjoint_f64 :: proc "contextless" (m: Matrix3f64) -> (adjoint: Matrix3f64) {
adjoint[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
adjoint[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
adjoint[0, 2] = +(m[1, 0] * m[2, 1] - m[2, 0] * m[1, 1])
@@ -1369,13 +1446,16 @@ matrix3_adjoint :: proc{
-matrix3_inverse_transpose_f16 :: proc(m: Matrix3f16) -> (inverse_transpose: Matrix3f16) {
+@(require_results)
+matrix3_inverse_transpose_f16 :: proc "contextless" (m: Matrix3f16) -> (inverse_transpose: Matrix3f16) {
return builtin.inverse_transpose(m)
}
-matrix3_inverse_transpose_f32 :: proc(m: Matrix3f32) -> (inverse_transpose: Matrix3f32) {
+@(require_results)
+matrix3_inverse_transpose_f32 :: proc "contextless" (m: Matrix3f32) -> (inverse_transpose: Matrix3f32) {
return builtin.inverse_transpose(m)
}
-matrix3_inverse_transpose_f64 :: proc(m: Matrix3f64) -> (inverse_transpose: Matrix3f64) {
+@(require_results)
+matrix3_inverse_transpose_f64 :: proc "contextless" (m: Matrix3f64) -> (inverse_transpose: Matrix3f64) {
return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose :: proc{
@@ -1385,19 +1465,22 @@ matrix3_inverse_transpose :: proc{
}
-matrix3_scale_f16 :: proc(s: Vector3f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_scale_f16 :: proc "contextless" (s: Vector3f16) -> (m: Matrix3f16) {
m[0, 0] = s[0]
m[1, 1] = s[1]
m[2, 2] = s[2]
return m
}
-matrix3_scale_f32 :: proc(s: Vector3f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_scale_f32 :: proc "contextless" (s: Vector3f32) -> (m: Matrix3f32) {
m[0, 0] = s[0]
m[1, 1] = s[1]
m[2, 2] = s[2]
return m
}
-matrix3_scale_f64 :: proc(s: Vector3f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_scale_f64 :: proc "contextless" (s: Vector3f64) -> (m: Matrix3f64) {
m[0, 0] = s[0]
m[1, 1] = s[1]
m[2, 2] = s[2]
@@ -1410,7 +1493,8 @@ matrix3_scale :: proc{
}
-matrix3_rotate_f16 :: proc(angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f16) {
+@(require_results)
+matrix3_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f16) {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1431,7 +1515,8 @@ matrix3_rotate_f16 :: proc(angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f1
return rot
}
-matrix3_rotate_f32 :: proc(angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f32) {
+@(require_results)
+matrix3_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f32) {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1452,7 +1537,8 @@ matrix3_rotate_f32 :: proc(angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f3
return rot
}
-matrix3_rotate_f64 :: proc(angle_radians: f64, v: Vector3f64) -> (rot: Matrix3f64) {
+@(require_results)
+matrix3_rotate_f64 :: proc "contextless" (angle_radians: f64, v: Vector3f64) -> (rot: Matrix3f64) {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1480,7 +1566,8 @@ matrix3_rotate :: proc{
}
-matrix3_look_at_f16 :: proc(eye, centre, up: Vector3f16) -> Matrix3f16 {
+@(require_results)
+matrix3_look_at_f16 :: proc "contextless" (eye, centre, up: Vector3f16) -> Matrix3f16 {
f := normalize(centre - eye)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -1490,7 +1577,8 @@ matrix3_look_at_f16 :: proc(eye, centre, up: Vector3f16) -> Matrix3f16 {
-f.x, -f.y, -f.z,
}
}
-matrix3_look_at_f32 :: proc(eye, centre, up: Vector3f32) -> Matrix3f32 {
+@(require_results)
+matrix3_look_at_f32 :: proc "contextless" (eye, centre, up: Vector3f32) -> Matrix3f32 {
f := normalize(centre - eye)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -1500,7 +1588,8 @@ matrix3_look_at_f32 :: proc(eye, centre, up: Vector3f32) -> Matrix3f32 {
-f.x, -f.y, -f.z,
}
}
-matrix3_look_at_f64 :: proc(eye, centre, up: Vector3f64) -> Matrix3f64 {
+@(require_results)
+matrix3_look_at_f64 :: proc "contextless" (eye, centre, up: Vector3f64) -> Matrix3f64 {
f := normalize(centre - eye)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -1517,7 +1606,8 @@ matrix3_look_at :: proc{
}
-matrix4_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (m: Matrix4f16) {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1544,7 +1634,8 @@ matrix4_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix4f16) {
return m
}
-matrix4_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (m: Matrix4f32) {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1571,7 +1662,8 @@ matrix4_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix4f32) {
return m
}
-matrix4_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (m: Matrix4f64) {
qxx := q.x * q.x
qyy := q.y * q.y
qzz := q.z * q.z
@@ -1605,19 +1697,22 @@ matrix4_from_quaternion :: proc{
}
-matrix4_from_trs_f16 :: proc(t: Vector3f16, r: Quaternionf16, s: Vector3f16) -> Matrix4f16 {
+@(require_results)
+matrix4_from_trs_f16 :: proc "contextless" (t: Vector3f16, r: Quaternionf16, s: Vector3f16) -> Matrix4f16 {
translation := matrix4_translate(t)
rotation := matrix4_from_quaternion(r)
scale := matrix4_scale(s)
return mul(translation, mul(rotation, scale))
}
-matrix4_from_trs_f32 :: proc(t: Vector3f32, r: Quaternionf32, s: Vector3f32) -> Matrix4f32 {
+@(require_results)
+matrix4_from_trs_f32 :: proc "contextless" (t: Vector3f32, r: Quaternionf32, s: Vector3f32) -> Matrix4f32 {
translation := matrix4_translate(t)
rotation := matrix4_from_quaternion(r)
scale := matrix4_scale(s)
return mul(translation, mul(rotation, scale))
}
-matrix4_from_trs_f64 :: proc(t: Vector3f64, r: Quaternionf64, s: Vector3f64) -> Matrix4f64 {
+@(require_results)
+matrix4_from_trs_f64 :: proc "contextless" (t: Vector3f64, r: Quaternionf64, s: Vector3f64) -> Matrix4f64 {
translation := matrix4_translate(t)
rotation := matrix4_from_quaternion(r)
scale := matrix4_scale(s)
@@ -1631,13 +1726,16 @@ matrix4_from_trs :: proc{
-matrix4_inverse_f16 :: proc(m: Matrix4f16) -> Matrix4f16 {
+@(require_results)
+matrix4_inverse_f16 :: proc "contextless" (m: Matrix4f16) -> Matrix4f16 {
return transpose(matrix4_inverse_transpose(m))
}
-matrix4_inverse_f32 :: proc(m: Matrix4f32) -> Matrix4f32 {
+@(require_results)
+matrix4_inverse_f32 :: proc "contextless" (m: Matrix4f32) -> Matrix4f32 {
return transpose(matrix4_inverse_transpose(m))
}
-matrix4_inverse_f64 :: proc(m: Matrix4f64) -> Matrix4f64 {
+@(require_results)
+matrix4_inverse_f64 :: proc "contextless" (m: Matrix4f64) -> Matrix4f64 {
return transpose(matrix4_inverse_transpose(m))
}
matrix4_inverse :: proc{
@@ -1647,7 +1745,8 @@ matrix4_inverse :: proc{
}
-matrix4_minor_f16 :: proc(m: Matrix4f16, c, r: int) -> f16 {
+@(require_results)
+matrix4_minor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 {
cut_down: Matrix3f16
for i in 0..<3 {
col := i if i < c else i+1
@@ -1658,7 +1757,8 @@ matrix4_minor_f16 :: proc(m: Matrix4f16, c, r: int) -> f16 {
}
return matrix3_determinant(cut_down)
}
-matrix4_minor_f32 :: proc(m: Matrix4f32, c, r: int) -> f32 {
+@(require_results)
+matrix4_minor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 {
cut_down: Matrix3f32
for i in 0..<3 {
col := i if i < c else i+1
@@ -1669,7 +1769,8 @@ matrix4_minor_f32 :: proc(m: Matrix4f32, c, r: int) -> f32 {
}
return matrix3_determinant(cut_down)
}
-matrix4_minor_f64 :: proc(m: Matrix4f64, c, r: int) -> f64 {
+@(require_results)
+matrix4_minor_f64 :: proc "contextless" (m: Matrix4f64, c, r: int) -> f64 {
cut_down: Matrix3f64
for i in 0..<3 {
col := i if i < c else i+1
@@ -1687,19 +1788,22 @@ matrix4_minor :: proc{
}
-matrix4_cofactor_f16 :: proc(m: Matrix4f16, c, r: int) -> f16 {
+@(require_results)
+matrix4_cofactor_f16 :: proc "contextless" (m: Matrix4f16, c, r: int) -> f16 {
sign, minor: f16
sign = 1 if (c + r) % 2 == 0 else -1
minor = matrix4_minor(m, c, r)
return sign * minor
}
-matrix4_cofactor_f32 :: proc(m: Matrix4f32, c, r: int) -> f32 {
+@(require_results)
+matrix4_cofactor_f32 :: proc "contextless" (m: Matrix4f32, c, r: int) -> f32 {
sign, minor: f32
sign = 1 if (c + r) % 2 == 0 else -1
minor = matrix4_minor(m, c, r)
return sign * minor
}
-matrix4_cofactor_f64 :: proc(m: Matrix4f64, c, r: int) -> f64 {
+@(require_results)
+matrix4_cofactor_f64 :: proc "contextless" (m: Matrix4f64, c, r: int) -> f64 {
sign, minor: f64
sign = 1 if (c + r) % 2 == 0 else -1
minor = matrix4_minor(m, c, r)
@@ -1712,7 +1816,8 @@ matrix4_cofactor :: proc{
}
-matrix4_adjoint_f16 :: proc(m: Matrix4f16) -> (adjoint: Matrix4f16) {
+@(require_results)
+matrix4_adjoint_f16 :: proc "contextless" (m: Matrix4f16) -> (adjoint: Matrix4f16) {
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j)
@@ -1720,7 +1825,8 @@ matrix4_adjoint_f16 :: proc(m: Matrix4f16) -> (adjoint: Matrix4f16) {
}
return
}
-matrix4_adjoint_f32 :: proc(m: Matrix4f32) -> (adjoint: Matrix4f32) {
+@(require_results)
+matrix4_adjoint_f32 :: proc "contextless" (m: Matrix4f32) -> (adjoint: Matrix4f32) {
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j)
@@ -1728,7 +1834,8 @@ matrix4_adjoint_f32 :: proc(m: Matrix4f32) -> (adjoint: Matrix4f32) {
}
return
}
-matrix4_adjoint_f64 :: proc(m: Matrix4f64) -> (adjoint: Matrix4f64) {
+@(require_results)
+matrix4_adjoint_f64 :: proc "contextless" (m: Matrix4f64) -> (adjoint: Matrix4f64) {
for i in 0..<4 {
for j in 0..<4 {
adjoint[i][j] = matrix4_cofactor(m, i, j)
@@ -1743,21 +1850,24 @@ matrix4_adjoint :: proc{
}
-matrix4_determinant_f16 :: proc(m: Matrix4f16) -> (determinant: f16) {
+@(require_results)
+matrix4_determinant_f16 :: proc "contextless" (m: Matrix4f16) -> (determinant: f16) {
adjoint := matrix4_adjoint(m)
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0]
}
return
}
-matrix4_determinant_f32 :: proc(m: Matrix4f32) -> (determinant: f32) {
+@(require_results)
+matrix4_determinant_f32 :: proc "contextless" (m: Matrix4f32) -> (determinant: f32) {
adjoint := matrix4_adjoint(m)
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0]
}
return
}
-matrix4_determinant_f64 :: proc(m: Matrix4f64) -> (determinant: f64) {
+@(require_results)
+matrix4_determinant_f64 :: proc "contextless" (m: Matrix4f64) -> (determinant: f64) {
adjoint := matrix4_adjoint(m)
for i in 0..<4 {
determinant += m[i][0] * adjoint[i][0]
@@ -1771,7 +1881,8 @@ matrix4_determinant :: proc{
}
-matrix4_inverse_transpose_f16 :: proc(m: Matrix4f16) -> (inverse_transpose: Matrix4f16) {
+@(require_results)
+matrix4_inverse_transpose_f16 :: proc "contextless" (m: Matrix4f16) -> (inverse_transpose: Matrix4f16) {
adjoint := matrix4_adjoint(m)
determinant: f16 = 0
for i in 0..<4 {
@@ -1785,7 +1896,8 @@ matrix4_inverse_transpose_f16 :: proc(m: Matrix4f16) -> (inverse_transpose: Matr
}
return
}
-matrix4_inverse_transpose_f32 :: proc(m: Matrix4f32) -> (inverse_transpose: Matrix4f32) {
+@(require_results)
+matrix4_inverse_transpose_f32 :: proc "contextless" (m: Matrix4f32) -> (inverse_transpose: Matrix4f32) {
adjoint := matrix4_adjoint(m)
determinant: f32 = 0
for i in 0..<4 {
@@ -1799,7 +1911,8 @@ matrix4_inverse_transpose_f32 :: proc(m: Matrix4f32) -> (inverse_transpose: Matr
}
return
}
-matrix4_inverse_transpose_f64 :: proc(m: Matrix4f64) -> (inverse_transpose: Matrix4f64) {
+@(require_results)
+matrix4_inverse_transpose_f64 :: proc "contextless" (m: Matrix4f64) -> (inverse_transpose: Matrix4f64) {
adjoint := matrix4_adjoint(m)
determinant: f64 = 0
for i in 0..<4 {
@@ -1820,21 +1933,24 @@ matrix4_inverse_transpose :: proc{
}
-matrix4_translate_f16 :: proc(v: Vector3f16) -> Matrix4f16 {
+@(require_results)
+matrix4_translate_f16 :: proc "contextless" (v: Vector3f16) -> Matrix4f16 {
m := MATRIX4F16_IDENTITY
m[3][0] = v[0]
m[3][1] = v[1]
m[3][2] = v[2]
return m
}
-matrix4_translate_f32 :: proc(v: Vector3f32) -> Matrix4f32 {
+@(require_results)
+matrix4_translate_f32 :: proc "contextless" (v: Vector3f32) -> Matrix4f32 {
m := MATRIX4F32_IDENTITY
m[3][0] = v[0]
m[3][1] = v[1]
m[3][2] = v[2]
return m
}
-matrix4_translate_f64 :: proc(v: Vector3f64) -> Matrix4f64 {
+@(require_results)
+matrix4_translate_f64 :: proc "contextless" (v: Vector3f64) -> Matrix4f64 {
m := MATRIX4F64_IDENTITY
m[3][0] = v[0]
m[3][1] = v[1]
@@ -1848,7 +1964,8 @@ matrix4_translate :: proc{
}
-matrix4_rotate_f16 :: proc(angle_radians: f16, v: Vector3f16) -> Matrix4f16 {
+@(require_results)
+matrix4_rotate_f16 :: proc "contextless" (angle_radians: f16, v: Vector3f16) -> Matrix4f16 {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1874,7 +1991,8 @@ matrix4_rotate_f16 :: proc(angle_radians: f16, v: Vector3f16) -> Matrix4f16 {
return rot
}
-matrix4_rotate_f32 :: proc(angle_radians: f32, v: Vector3f32) -> Matrix4f32 {
+@(require_results)
+matrix4_rotate_f32 :: proc "contextless" (angle_radians: f32, v: Vector3f32) -> Matrix4f32 {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1900,7 +2018,8 @@ matrix4_rotate_f32 :: proc(angle_radians: f32, v: Vector3f32) -> Matrix4f32 {
return rot
}
-matrix4_rotate_f64 :: proc(angle_radians: f64, v: Vector3f64) -> Matrix4f64 {
+@(require_results)
+matrix4_rotate_f64 :: proc "contextless" (angle_radians: f64, v: Vector3f64) -> Matrix4f64 {
c := math.cos(angle_radians)
s := math.sin(angle_radians)
@@ -1933,21 +2052,24 @@ matrix4_rotate :: proc{
}
-matrix4_scale_f16 :: proc(v: Vector3f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_scale_f16 :: proc "contextless" (v: Vector3f16) -> (m: Matrix4f16) {
m[0][0] = v[0]
m[1][1] = v[1]
m[2][2] = v[2]
m[3][3] = 1
return
}
-matrix4_scale_f32 :: proc(v: Vector3f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_scale_f32 :: proc "contextless" (v: Vector3f32) -> (m: Matrix4f32) {
m[0][0] = v[0]
m[1][1] = v[1]
m[2][2] = v[2]
m[3][3] = 1
return
}
-matrix4_scale_f64 :: proc(v: Vector3f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_scale_f64 :: proc "contextless" (v: Vector3f64) -> (m: Matrix4f64) {
m[0][0] = v[0]
m[1][1] = v[1]
m[2][2] = v[2]
@@ -1961,7 +2083,8 @@ matrix4_scale :: proc{
}
-matrix4_look_at_f16 :: proc(eye, centre, up: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_look_at_f16 :: proc "contextless" (eye, centre, up: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
f := normalize(centre - eye)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -1975,7 +2098,8 @@ matrix4_look_at_f16 :: proc(eye, centre, up: Vector3f16, flip_z_axis := true) ->
0, 0, 0, 1,
}
}
-matrix4_look_at_f32 :: proc(eye, centre, up: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_look_at_f32 :: proc "contextless" (eye, centre, up: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
f := normalize(centre - eye)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -1989,7 +2113,8 @@ matrix4_look_at_f32 :: proc(eye, centre, up: Vector3f32, flip_z_axis := true) ->
0, 0, 0, 1,
}
}
-matrix4_look_at_f64 :: proc(eye, centre, up: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_look_at_f64 :: proc "contextless" (eye, centre, up: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
f := normalize(centre - eye)
s := normalize(cross(f, up))
u := cross(s, f)
@@ -2010,7 +2135,8 @@ matrix4_look_at :: proc{
}
-matrix4_look_at_from_fru_f16 :: proc(eye, f, r, u: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_look_at_from_fru_f16 :: proc "contextless" (eye, f, r, u: Vector3f16, flip_z_axis := true) -> (m: Matrix4f16) {
f, s, u := f, r, u
f = normalize(f)
s = normalize(s)
@@ -2024,7 +2150,8 @@ matrix4_look_at_from_fru_f16 :: proc(eye, f, r, u: Vector3f16, flip_z_axis := tr
0, 0, 0, 1,
}
}
-matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_look_at_from_fru_f32 :: proc "contextless" (eye, f, r, u: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
f, s, u := f, r, u
f = normalize(f)
s = normalize(s)
@@ -2038,7 +2165,8 @@ matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := tr
0, 0, 0, 1,
}
}
-matrix4_look_at_from_fru_f64 :: proc(eye, f, r, u: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_look_at_from_fru_f64 :: proc "contextless" (eye, f, r, u: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
f, s, u := f, r, u
f = normalize(f)
s = normalize(s)
@@ -2059,7 +2187,8 @@ matrix4_look_at_from_fru :: proc{
}
-matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_perspective_f16 :: proc "contextless" (fovy, aspect, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2073,7 +2202,8 @@ matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := tru
return
}
-matrix4_perspective_f32 :: proc(fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_perspective_f32 :: proc "contextless" (fovy, aspect, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2087,7 +2217,8 @@ matrix4_perspective_f32 :: proc(fovy, aspect, near, far: f32, flip_z_axis := tru
return
}
-matrix4_perspective_f64 :: proc(fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_perspective_f64 :: proc "contextless" (fovy, aspect, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2109,7 +2240,8 @@ matrix4_perspective :: proc{
-matrix_ortho3d_f16 :: proc(left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
+@(require_results)
+matrix_ortho3d_f16 :: proc "contextless" (left, right, bottom, top, near, far: f16, flip_z_axis := true) -> (m: Matrix4f16) {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
m[2, 2] = +2 / (far - near)
@@ -2124,7 +2256,8 @@ matrix_ortho3d_f16 :: proc(left, right, bottom, top, near, far: f16, flip_z_axis
return
}
-matrix_ortho3d_f32 :: proc(left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
+@(require_results)
+matrix_ortho3d_f32 :: proc "contextless" (left, right, bottom, top, near, far: f32, flip_z_axis := true) -> (m: Matrix4f32) {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
m[2, 2] = +2 / (far - near)
@@ -2139,7 +2272,8 @@ matrix_ortho3d_f32 :: proc(left, right, bottom, top, near, far: f32, flip_z_axis
return
}
-matrix_ortho3d_f64 :: proc(left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
+@(require_results)
+matrix_ortho3d_f64 :: proc "contextless" (left, right, bottom, top, near, far: f64, flip_z_axis := true) -> (m: Matrix4f64) {
m[0, 0] = +2 / (right - left)
m[1, 1] = +2 / (top - bottom)
m[2, 2] = +2 / (far - near)
@@ -2162,7 +2296,8 @@ matrix_ortho3d :: proc{
-matrix4_infinite_perspective_f16 :: proc(fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_infinite_perspective_f16 :: proc "contextless" (fovy, aspect, near: f16, flip_z_axis := true) -> (m: Matrix4f16) {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2176,7 +2311,8 @@ matrix4_infinite_perspective_f16 :: proc(fovy, aspect, near: f16, flip_z_axis :=
return
}
-matrix4_infinite_perspective_f32 :: proc(fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_infinite_perspective_f32 :: proc "contextless" (fovy, aspect, near: f32, flip_z_axis := true) -> (m: Matrix4f32) {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2190,7 +2326,8 @@ matrix4_infinite_perspective_f32 :: proc(fovy, aspect, near: f32, flip_z_axis :=
return
}
-matrix4_infinite_perspective_f64 :: proc(fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_infinite_perspective_f64 :: proc "contextless" (fovy, aspect, near: f64, flip_z_axis := true) -> (m: Matrix4f64) {
tan_half_fovy := math.tan(0.5 * fovy)
m[0, 0] = 1 / (aspect*tan_half_fovy)
m[1, 1] = 1 / (tan_half_fovy)
@@ -2212,17 +2349,20 @@ matrix4_infinite_perspective :: proc{
-matrix2_from_scalar_f16 :: proc(f: f16) -> (m: Matrix2f16) {
+@(require_results)
+matrix2_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix2f16) {
m[0, 0], m[1, 0] = f, 0
m[0, 1], m[1, 1] = 0, f
return
}
-matrix2_from_scalar_f32 :: proc(f: f32) -> (m: Matrix2f32) {
+@(require_results)
+matrix2_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix2f32) {
m[0, 0], m[1, 0] = f, 0
m[0, 1], m[1, 1] = 0, f
return
}
-matrix2_from_scalar_f64 :: proc(f: f64) -> (m: Matrix2f64) {
+@(require_results)
+matrix2_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix2f64) {
m[0, 0], m[1, 0] = f, 0
m[0, 1], m[1, 1] = 0, f
return
@@ -2234,19 +2374,22 @@ matrix2_from_scalar :: proc{
}
-matrix3_from_scalar_f16 :: proc(f: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix3f16) {
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
-matrix3_from_scalar_f32 :: proc(f: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix3f32) {
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
return
}
-matrix3_from_scalar_f64 :: proc(f: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix3f64) {
m[0, 0], m[1, 0], m[2, 0] = f, 0, 0
m[0, 1], m[1, 1], m[2, 1] = 0, f, 0
m[0, 2], m[1, 2], m[2, 2] = 0, 0, f
@@ -2259,21 +2402,24 @@ matrix3_from_scalar :: proc{
}
-matrix4_from_scalar_f16 :: proc(f: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_scalar_f16 :: proc "contextless" (f: f16) -> (m: Matrix4f16) {
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
m[0, 3], m[1, 3], m[2, 3], m[3, 3] = 0, 0, 0, f
return
}
-matrix4_from_scalar_f32 :: proc(f: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_scalar_f32 :: proc "contextless" (f: f32) -> (m: Matrix4f32) {
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
m[0, 3], m[1, 3], m[2, 3], m[3, 3] = 0, 0, 0, f
return
}
-matrix4_from_scalar_f64 :: proc(f: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_scalar_f64 :: proc "contextless" (f: f64) -> (m: Matrix4f64) {
m[0, 0], m[1, 0], m[2, 0], m[3, 0] = f, 0, 0, 0
m[0, 1], m[1, 1], m[2, 1], m[3, 1] = 0, f, 0, 0
m[0, 2], m[1, 2], m[2, 2], m[3, 2] = 0, 0, f, 0
@@ -2287,17 +2433,20 @@ matrix4_from_scalar :: proc{
}
-matrix2_from_matrix3_f16 :: proc(m: Matrix3f16) -> (r: Matrix2f16) {
+@(require_results)
+matrix2_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix2f16) {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
-matrix2_from_matrix3_f32 :: proc(m: Matrix3f32) -> (r: Matrix2f32) {
+@(require_results)
+matrix2_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix2f32) {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
-matrix2_from_matrix3_f64 :: proc(m: Matrix3f64) -> (r: Matrix2f64) {
+@(require_results)
+matrix2_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix2f64) {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
@@ -2309,17 +2458,20 @@ matrix2_from_matrix3 :: proc{
}
-matrix2_from_matrix4_f16 :: proc(m: Matrix4f16) -> (r: Matrix2f16) {
+@(require_results)
+matrix2_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix2f16) {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
-matrix2_from_matrix4_f32 :: proc(m: Matrix4f32) -> (r: Matrix2f32) {
+@(require_results)
+matrix2_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix2f32) {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
}
-matrix2_from_matrix4_f64 :: proc(m: Matrix4f64) -> (r: Matrix2f64) {
+@(require_results)
+matrix2_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix2f64) {
r[0, 0], r[1, 0] = m[0, 0], m[1, 0]
r[0, 1], r[1, 1] = m[0, 1], m[1, 1]
return
@@ -2331,19 +2483,22 @@ matrix2_from_matrix4 :: proc{
}
-matrix3_from_matrix2_f16 :: proc(m: Matrix2f16) -> (r: Matrix3f16) {
+@(require_results)
+matrix3_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix3f16) {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
-matrix3_from_matrix2_f32 :: proc(m: Matrix2f32) -> (r: Matrix3f32) {
+@(require_results)
+matrix3_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix3f32) {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
return
}
-matrix3_from_matrix2_f64 :: proc(m: Matrix2f64) -> (r: Matrix3f64) {
+@(require_results)
+matrix3_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix3f64) {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], 0
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], 0
r[0, 2], r[1, 2], r[2, 2] = 0, 0, 1
@@ -2356,19 +2511,22 @@ matrix3_from_matrix2 :: proc{
}
-matrix3_from_matrix4_f16 :: proc(m: Matrix4f16) -> (r: Matrix3f16) {
+@(require_results)
+matrix3_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (r: Matrix3f16) {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
-matrix3_from_matrix4_f32 :: proc(m: Matrix4f32) -> (r: Matrix3f32) {
+@(require_results)
+matrix3_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (r: Matrix3f32) {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
return
}
-matrix3_from_matrix4_f64 :: proc(m: Matrix4f64) -> (r: Matrix3f64) {
+@(require_results)
+matrix3_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (r: Matrix3f64) {
r[0, 0], r[1, 0], r[2, 0] = m[0, 0], m[1, 0], m[2, 0]
r[0, 1], r[1, 1], r[2, 1] = m[0, 1], m[1, 1], m[2, 1]
r[0, 2], r[1, 2], r[2, 2] = m[0, 2], m[1, 2], m[2, 2]
@@ -2381,21 +2539,24 @@ matrix3_from_matrix4 :: proc{
}
-matrix4_from_matrix2_f16 :: proc(m: Matrix2f16) -> (r: Matrix4f16) {
+@(require_results)
+matrix4_from_matrix2_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix4f16) {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
-matrix4_from_matrix2_f32 :: proc(m: Matrix2f32) -> (r: Matrix4f32) {
+@(require_results)
+matrix4_from_matrix2_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix4f32) {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
-matrix4_from_matrix2_f64 :: proc(m: Matrix2f64) -> (r: Matrix4f64) {
+@(require_results)
+matrix4_from_matrix2_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix4f64) {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], 0, 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], 0, 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = 0, 0, 1, 0
@@ -2409,21 +2570,24 @@ matrix4_from_matrix2 :: proc{
}
-matrix4_from_matrix3_f16 :: proc(m: Matrix3f16) -> (r: Matrix4f16) {
+@(require_results)
+matrix4_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix4f16) {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
-matrix4_from_matrix3_f32 :: proc(m: Matrix3f32) -> (r: Matrix4f32) {
+@(require_results)
+matrix4_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix4f32) {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
r[0, 3], r[1, 3], r[2, 3], r[3, 3] = 0, 0, 0, 1
return
}
-matrix4_from_matrix3_f64 :: proc(m: Matrix3f64) -> (r: Matrix4f64) {
+@(require_results)
+matrix4_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix4f64) {
r[0, 0], r[1, 0], r[2, 0], r[3, 0] = m[0, 0], m[1, 0], m[2, 0], 0
r[0, 1], r[1, 1], r[2, 1], r[3, 1] = m[0, 1], m[1, 1], m[2, 1], 0
r[0, 2], r[1, 2], r[2, 2], r[3, 2] = m[0, 2], m[1, 2], m[2, 2], 0
@@ -2437,15 +2601,18 @@ matrix4_from_matrix3 :: proc{
}
-quaternion_from_scalar_f16 :: proc(f: f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_from_scalar_f16 :: proc "contextless" (f: f16) -> (q: Quaternionf16) {
q.w = f
return
}
-quaternion_from_scalar_f32 :: proc(f: f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_from_scalar_f32 :: proc "contextless" (f: f32) -> (q: Quaternionf32) {
q.w = f
return
}
-quaternion_from_scalar_f64 :: proc(f: f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_from_scalar_f64 :: proc "contextless" (f: f64) -> (q: Quaternionf64) {
q.w = f
return
}
@@ -2505,7 +2672,8 @@ to_quaternion :: proc{
-matrix2_orthonormalize_f16 :: proc(m: Matrix2f16) -> (r: Matrix2f16) {
+@(require_results)
+matrix2_orthonormalize_f16 :: proc "contextless" (m: Matrix2f16) -> (r: Matrix2f16) {
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2514,7 +2682,8 @@ matrix2_orthonormalize_f16 :: proc(m: Matrix2f16) -> (r: Matrix2f16) {
return
}
-matrix2_orthonormalize_f32 :: proc(m: Matrix2f32) -> (r: Matrix2f32) {
+@(require_results)
+matrix2_orthonormalize_f32 :: proc "contextless" (m: Matrix2f32) -> (r: Matrix2f32) {
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2523,7 +2692,8 @@ matrix2_orthonormalize_f32 :: proc(m: Matrix2f32) -> (r: Matrix2f32) {
return
}
-matrix2_orthonormalize_f64 :: proc(m: Matrix2f64) -> (r: Matrix2f64) {
+@(require_results)
+matrix2_orthonormalize_f64 :: proc "contextless" (m: Matrix2f64) -> (r: Matrix2f64) {
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2539,7 +2709,8 @@ matrix2_orthonormalize :: proc{
}
-matrix3_orthonormalize_f16 :: proc(m: Matrix3f16) -> (r: Matrix3f16) {
+@(require_results)
+matrix3_orthonormalize_f16 :: proc "contextless" (m: Matrix3f16) -> (r: Matrix3f16) {
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2553,7 +2724,8 @@ matrix3_orthonormalize_f16 :: proc(m: Matrix3f16) -> (r: Matrix3f16) {
return
}
-matrix3_orthonormalize_f32 :: proc(m: Matrix3f32) -> (r: Matrix3f32) {
+@(require_results)
+matrix3_orthonormalize_f32 :: proc "contextless" (m: Matrix3f32) -> (r: Matrix3f32) {
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2567,7 +2739,8 @@ matrix3_orthonormalize_f32 :: proc(m: Matrix3f32) -> (r: Matrix3f32) {
return
}
-matrix3_orthonormalize_f64 :: proc(m: Matrix3f64) -> (r: Matrix3f64) {
+@(require_results)
+matrix3_orthonormalize_f64 :: proc "contextless" (m: Matrix3f64) -> (r: Matrix3f64) {
r[0] = normalize(m[0])
d0 := dot(r[0], r[1])
@@ -2588,13 +2761,16 @@ matrix3_orthonormalize :: proc{
}
-vector3_orthonormalize_f16 :: proc(x, y: Vector3f16) -> (z: Vector3f16) {
+@(require_results)
+vector3_orthonormalize_f16 :: proc "contextless" (x, y: Vector3f16) -> (z: Vector3f16) {
return normalize(x - y * dot(y, x))
}
-vector3_orthonormalize_f32 :: proc(x, y: Vector3f32) -> (z: Vector3f32) {
+@(require_results)
+vector3_orthonormalize_f32 :: proc "contextless" (x, y: Vector3f32) -> (z: Vector3f32) {
return normalize(x - y * dot(y, x))
}
-vector3_orthonormalize_f64 :: proc(x, y: Vector3f64) -> (z: Vector3f64) {
+@(require_results)
+vector3_orthonormalize_f64 :: proc "contextless" (x, y: Vector3f64) -> (z: Vector3f64) {
return normalize(x - y * dot(y, x))
}
vector3_orthonormalize :: proc{
@@ -2611,7 +2787,8 @@ orthonormalize :: proc{
}
-matrix4_orientation_f16 :: proc(normal, up: Vector3f16) -> Matrix4f16 {
+@(require_results)
+matrix4_orientation_f16 :: proc "contextless" (normal, up: Vector3f16) -> Matrix4f16 {
if all(equal(normal, up)) {
return MATRIX4F16_IDENTITY
}
@@ -2621,7 +2798,8 @@ matrix4_orientation_f16 :: proc(normal, up: Vector3f16) -> Matrix4f16 {
return matrix4_rotate(angle, rotation_axis)
}
-matrix4_orientation_f32 :: proc(normal, up: Vector3f32) -> Matrix4f32 {
+@(require_results)
+matrix4_orientation_f32 :: proc "contextless" (normal, up: Vector3f32) -> Matrix4f32 {
if all(equal(normal, up)) {
return MATRIX4F32_IDENTITY
}
@@ -2631,7 +2809,8 @@ matrix4_orientation_f32 :: proc(normal, up: Vector3f32) -> Matrix4f32 {
return matrix4_rotate(angle, rotation_axis)
}
-matrix4_orientation_f64 :: proc(normal, up: Vector3f64) -> Matrix4f64 {
+@(require_results)
+matrix4_orientation_f64 :: proc "contextless" (normal, up: Vector3f64) -> Matrix4f64 {
if all(equal(normal, up)) {
return MATRIX4F64_IDENTITY
}
@@ -2648,7 +2827,8 @@ matrix4_orientation :: proc{
}
-euclidean_from_polar_f16 :: proc(polar: Vector2f16) -> Vector3f16 {
+@(require_results)
+euclidean_from_polar_f16 :: proc "contextless" (polar: Vector2f16) -> Vector3f16 {
latitude, longitude := polar.x, polar.y
cx, sx := math.cos(latitude), math.sin(latitude)
cy, sy := math.cos(longitude), math.sin(longitude)
@@ -2659,7 +2839,8 @@ euclidean_from_polar_f16 :: proc(polar: Vector2f16) -> Vector3f16 {
cx*cy,
}
}
-euclidean_from_polar_f32 :: proc(polar: Vector2f32) -> Vector3f32 {
+@(require_results)
+euclidean_from_polar_f32 :: proc "contextless" (polar: Vector2f32) -> Vector3f32 {
latitude, longitude := polar.x, polar.y
cx, sx := math.cos(latitude), math.sin(latitude)
cy, sy := math.cos(longitude), math.sin(longitude)
@@ -2670,7 +2851,8 @@ euclidean_from_polar_f32 :: proc(polar: Vector2f32) -> Vector3f32 {
cx*cy,
}
}
-euclidean_from_polar_f64 :: proc(polar: Vector2f64) -> Vector3f64 {
+@(require_results)
+euclidean_from_polar_f64 :: proc "contextless" (polar: Vector2f64) -> Vector3f64 {
latitude, longitude := polar.x, polar.y
cx, sx := math.cos(latitude), math.sin(latitude)
cy, sy := math.cos(longitude), math.sin(longitude)
@@ -2688,7 +2870,8 @@ euclidean_from_polar :: proc{
}
-polar_from_euclidean_f16 :: proc(euclidean: Vector3f16) -> Vector3f16 {
+@(require_results)
+polar_from_euclidean_f16 :: proc "contextless" (euclidean: Vector3f16) -> Vector3f16 {
n := length(euclidean)
tmp := euclidean / n
@@ -2700,7 +2883,8 @@ polar_from_euclidean_f16 :: proc(euclidean: Vector3f16) -> Vector3f16 {
xz_dist,
}
}
-polar_from_euclidean_f32 :: proc(euclidean: Vector3f32) -> Vector3f32 {
+@(require_results)
+polar_from_euclidean_f32 :: proc "contextless" (euclidean: Vector3f32) -> Vector3f32 {
n := length(euclidean)
tmp := euclidean / n
@@ -2712,7 +2896,8 @@ polar_from_euclidean_f32 :: proc(euclidean: Vector3f32) -> Vector3f32 {
xz_dist,
}
}
-polar_from_euclidean_f64 :: proc(euclidean: Vector3f64) -> Vector3f64 {
+@(require_results)
+polar_from_euclidean_f64 :: proc "contextless" (euclidean: Vector3f64) -> Vector3f64 {
n := length(euclidean)
tmp := euclidean / n
diff --git a/core/math/linalg/specific_euler_angles_f16.odin b/core/math/linalg/specific_euler_angles_f16.odin
index 9e21c7f97..bacda163e 100644
--- a/core/math/linalg/specific_euler_angles_f16.odin
+++ b/core/math/linalg/specific_euler_angles_f16.odin
@@ -2,7 +2,8 @@ package linalg
import "core:math"
-euler_angles_from_matrix3_f16 :: proc(m: Matrix3f16, order: Euler_Angle_Order) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16, order: Euler_Angle_Order) -> (t1, t2, t3: f16) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_matrix3(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_matrix3(m)
@@ -19,7 +20,8 @@ euler_angles_from_matrix3_f16 :: proc(m: Matrix3f16, order: Euler_Angle_Order) -
}
return
}
-euler_angles_from_matrix4_f16 :: proc(m: Matrix4f16, order: Euler_Angle_Order) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16, order: Euler_Angle_Order) -> (t1, t2, t3: f16) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_matrix4(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_matrix4(m)
@@ -36,7 +38,8 @@ euler_angles_from_matrix4_f16 :: proc(m: Matrix4f16, order: Euler_Angle_Order) -
}
return
}
-euler_angles_from_quaternion_f16 :: proc(m: Quaternionf16, order: Euler_Angle_Order) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_from_quaternion_f16 :: proc "contextless" (m: Quaternionf16, order: Euler_Angle_Order) -> (t1, t2, t3: f16) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_quaternion(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_quaternion(m)
@@ -54,7 +57,8 @@ euler_angles_from_quaternion_f16 :: proc(m: Quaternionf16, order: Euler_Angle_Or
return
}
-matrix3_from_euler_angles_f16 :: proc(t1, t2, t3: f16, order: Euler_Angle_Order) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_f16 :: proc "contextless" (t1, t2, t3: f16, order: Euler_Angle_Order) -> (m: Matrix3f16) {
switch order {
case .XYZ: return matrix3_from_euler_angles_xyz(t1, t2, t3) // m1, m2, m3 = X(t1), Y(t2), Z(t3);
case .XZY: return matrix3_from_euler_angles_xzy(t1, t2, t3) // m1, m2, m3 = X(t1), Z(t2), Y(t3);
@@ -71,7 +75,8 @@ matrix3_from_euler_angles_f16 :: proc(t1, t2, t3: f16, order: Euler_Angle_Order)
}
return
}
-matrix4_from_euler_angles_f16 :: proc(t1, t2, t3: f16, order: Euler_Angle_Order) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_f16 :: proc "contextless" (t1, t2, t3: f16, order: Euler_Angle_Order) -> (m: Matrix4f16) {
switch order {
case .XYZ: return matrix4_from_euler_angles_xyz(t1, t2, t3) // m1, m2, m3 = X(t1), Y(t2), Z(t3);
case .XZY: return matrix4_from_euler_angles_xzy(t1, t2, t3) // m1, m2, m3 = X(t1), Z(t2), Y(t3);
@@ -89,7 +94,8 @@ matrix4_from_euler_angles_f16 :: proc(t1, t2, t3: f16, order: Euler_Angle_Order)
return
}
-quaternion_from_euler_angles_f16 :: proc(t1, t2, t3: f16, order: Euler_Angle_Order) -> Quaternionf16 {
+@(require_results)
+quaternion_from_euler_angles_f16 :: proc "contextless" (t1, t2, t3: f16, order: Euler_Angle_Order) -> Quaternionf16 {
X :: quaternion_from_euler_angle_x
Y :: quaternion_from_euler_angle_y
Z :: quaternion_from_euler_angle_z
@@ -117,17 +123,21 @@ quaternion_from_euler_angles_f16 :: proc(t1, t2, t3: f16, order: Euler_Angle_Ord
// Quaternionf16s
-quaternion_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_from_euler_angle_x_f16 :: proc "contextless" (angle_x: f16) -> (q: Quaternionf16) {
return quaternion_angle_axis_f16(angle_x, {1, 0, 0})
}
-quaternion_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_from_euler_angle_y_f16 :: proc "contextless" (angle_y: f16) -> (q: Quaternionf16) {
return quaternion_angle_axis_f16(angle_y, {0, 1, 0})
}
-quaternion_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (q: Quaternionf16) {
+@(require_results)
+quaternion_from_euler_angle_z_f16 :: proc "contextless" (angle_z: f16) -> (q: Quaternionf16) {
return quaternion_angle_axis_f16(angle_z, {0, 0, 1})
}
-quaternion_from_pitch_yaw_roll_f16 :: proc(pitch, yaw, roll: f16) -> Quaternionf16 {
+@(require_results)
+quaternion_from_pitch_yaw_roll_f16 :: proc "contextless" (pitch, yaw, roll: f16) -> Quaternionf16 {
a, b, c := pitch, yaw, roll
ca, sa := math.cos(a*0.5), math.sin(a*0.5)
@@ -142,11 +152,13 @@ quaternion_from_pitch_yaw_roll_f16 :: proc(pitch, yaw, roll: f16) -> Quaternionf
return q
}
-roll_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
+@(require_results)
+roll_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> f16 {
return math.atan2(2 * q.x*q.y + q.w*q.z, q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z)
}
-pitch_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
+@(require_results)
+pitch_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> f16 {
y := 2 * (q.y*q.z + q.w*q.w)
x := q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z
@@ -157,52 +169,66 @@ pitch_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
return math.atan2(y, x)
}
-yaw_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
+@(require_results)
+yaw_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> f16 {
return math.asin(clamp(-2 * (q.x*q.z - q.w*q.y), -1, 1))
}
-pitch_yaw_roll_from_quaternion_f16 :: proc(q: Quaternionf16) -> (pitch, yaw, roll: f16) {
+@(require_results)
+pitch_yaw_roll_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (pitch, yaw, roll: f16) {
pitch = pitch_from_quaternion(q)
yaw = yaw_from_quaternion(q)
roll = roll_from_quaternion(q)
return
}
-euler_angles_xyz_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xyz_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_xyz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yxz_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yxz_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_yxz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xzx_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xzx_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_xzx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xyx_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xyx_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_xyx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yxy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yxy_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_yxy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yzy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yzy_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_yzy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zyz_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zyz_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_zyz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zxz_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zxz_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_zxz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xzy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xzy_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_xzy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yzx_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yzx_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_yzx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zyx_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zyx_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_zyx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zxy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zxy_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> (t1, t2, t3: f16) {
return euler_angles_zxy_from_matrix4(matrix4_from_quaternion(q))
}
@@ -210,7 +236,8 @@ euler_angles_zxy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f
// Matrix3
-matrix3_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angle_x_f16 :: proc "contextless" (angle_x: f16) -> (m: Matrix3f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
m[0, 0] = 1
m[1, 1] = +cos_x
@@ -219,7 +246,8 @@ matrix3_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (m: Matrix3f16) {
m[2, 2] = +cos_x
return
}
-matrix3_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angle_y_f16 :: proc "contextless" (angle_y: f16) -> (m: Matrix3f16) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = +cos_y
m[0, 2] = -sin_y
@@ -228,7 +256,8 @@ matrix3_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix3f16) {
m[2, 2] = +cos_y
return
}
-matrix3_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angle_z_f16 :: proc "contextless" (angle_z: f16) -> (m: Matrix3f16) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
m[0, 0] = +cos_z
m[0, 1] = +sin_z
@@ -239,7 +268,8 @@ matrix3_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix3f16) {
}
-matrix3_from_derived_euler_angle_x_f16 :: proc(angle_x: f16, angular_velocity_x: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_derived_euler_angle_x_f16 :: proc "contextless" (angle_x: f16, angular_velocity_x: f16) -> (m: Matrix3f16) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
m[0, 0] = 1
@@ -249,7 +279,8 @@ matrix3_from_derived_euler_angle_x_f16 :: proc(angle_x: f16, angular_velocity_x:
m[2, 2] = +cos_x
return
}
-matrix3_from_derived_euler_angle_y_f16 :: proc(angle_y: f16, angular_velocity_y: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_derived_euler_angle_y_f16 :: proc "contextless" (angle_y: f16, angular_velocity_y: f16) -> (m: Matrix3f16) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
m[0, 0] = +cos_y
@@ -259,7 +290,8 @@ matrix3_from_derived_euler_angle_y_f16 :: proc(angle_y: f16, angular_velocity_y:
m[2, 2] = +cos_y
return
}
-matrix3_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_derived_euler_angle_z_f16 :: proc "contextless" (angle_z: f16, angular_velocity_z: f16) -> (m: Matrix3f16) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
m[0, 0] = +cos_z
@@ -271,7 +303,8 @@ matrix3_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z:
}
-matrix3_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_xy_f16 :: proc "contextless" (angle_x, angle_y: f16) -> (m: Matrix3f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -286,7 +319,8 @@ matrix3_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix3f1
}
-matrix3_from_euler_angles_yx_f16 :: proc(angle_y, angle_x: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_yx_f16 :: proc "contextless" (angle_y, angle_x: f16) -> (m: Matrix3f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -300,21 +334,26 @@ matrix3_from_euler_angles_yx_f16 :: proc(angle_y, angle_x: f16) -> (m: Matrix3f1
return
}
-matrix3_from_euler_angles_xz_f16 :: proc(angle_x, angle_z: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_xz_f16 :: proc "contextless" (angle_x, angle_z: f16) -> (m: Matrix3f16) {
return mul(matrix3_from_euler_angle_x(angle_x), matrix3_from_euler_angle_z(angle_z))
}
-matrix3_from_euler_angles_zx_f16 :: proc(angle_z, angle_x: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_zx_f16 :: proc "contextless" (angle_z, angle_x: f16) -> (m: Matrix3f16) {
return mul(matrix3_from_euler_angle_z(angle_z), matrix3_from_euler_angle_x(angle_x))
}
-matrix3_from_euler_angles_yz_f16 :: proc(angle_y, angle_z: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_yz_f16 :: proc "contextless" (angle_y, angle_z: f16) -> (m: Matrix3f16) {
return mul(matrix3_from_euler_angle_y(angle_y), matrix3_from_euler_angle_z(angle_z))
}
-matrix3_from_euler_angles_zy_f16 :: proc(angle_z, angle_y: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_zy_f16 :: proc "contextless" (angle_z, angle_y: f16) -> (m: Matrix3f16) {
return mul(matrix3_from_euler_angle_z(angle_z), matrix3_from_euler_angle_y(angle_y))
}
-matrix3_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_xyz_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(-t1)
c2 := math.cos(-t2)
c3 := math.cos(-t3)
@@ -334,7 +373,8 @@ matrix3_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_yxz_f16 :: proc "contextless" (yaw, pitch, roll: f16) -> (m: Matrix3f16) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -354,7 +394,8 @@ matrix3_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f
return
}
-matrix3_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_xzx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -374,7 +415,8 @@ matrix3_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_xyx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -394,7 +436,8 @@ matrix3_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_yxy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -414,7 +457,8 @@ matrix3_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_yzy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -434,7 +478,8 @@ matrix3_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_zyz_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -454,7 +499,8 @@ matrix3_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_zxz_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -475,7 +521,8 @@ matrix3_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
}
-matrix3_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_xzy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -495,7 +542,8 @@ matrix3_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_yzx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -515,7 +563,8 @@ matrix3_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_zyx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -535,7 +584,8 @@ matrix3_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
return
}
-matrix3_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_euler_angles_zxy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix3f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -556,7 +606,8 @@ matrix3_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
}
-matrix3_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f16) {
+@(require_results)
+matrix3_from_yaw_pitch_roll_f16 :: proc "contextless" (yaw, pitch, roll: f16) -> (m: Matrix3f16) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -576,7 +627,8 @@ matrix3_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f16
return m
}
-euler_angles_xyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xyz_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 2], m[2, 2])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
T2 := math.atan2(-m[0, 2], C2)
@@ -589,7 +641,8 @@ euler_angles_xyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yxz_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[0, 2], m[2, 2])
C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
T2 := math.atan2(-m[1, 2], C2)
@@ -602,7 +655,8 @@ euler_angles_yxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_xzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xzx_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[2, 0], m[1, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -615,7 +669,8 @@ euler_angles_xzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_xyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xyx_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 0], -m[2, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -628,7 +683,8 @@ euler_angles_xyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yxy_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[0, 1], m[2, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -641,7 +697,8 @@ euler_angles_yxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yzy_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[2, 1], -m[0, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -653,7 +710,8 @@ euler_angles_yzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
t3 = T3
return
}
-euler_angles_zyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zyz_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 2], m[0, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -666,7 +724,8 @@ euler_angles_zyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_zxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zxz_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[0, 2], -m[1, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -679,7 +738,8 @@ euler_angles_zxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_xzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xzy_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[2, 1], m[1, 1])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
T2 := math.atan2(-m[0, 1], C2)
@@ -692,7 +752,8 @@ euler_angles_xzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yzx_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(-m[2, 0], m[0, 0])
C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
T2 := math.atan2(m[1, 0], C2)
@@ -705,7 +766,8 @@ euler_angles_yzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_zyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zyx_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 0], m[0, 0])
C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
T2 := math.atan2(-m[2, 0], C2)
@@ -718,7 +780,8 @@ euler_angles_zyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_zxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zxy_from_matrix3_f16 :: proc "contextless" (m: Matrix3f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(-m[0, 1], m[1, 1])
C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
T2 := math.atan2(m[2, 1], C2)
@@ -735,7 +798,8 @@ euler_angles_zxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
// Matrix4
-matrix4_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angle_x_f16 :: proc "contextless" (angle_x: f16) -> (m: Matrix4f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
m[0, 0] = 1
m[1, 1] = +cos_x
@@ -745,7 +809,8 @@ matrix4_from_euler_angle_x_f16 :: proc(angle_x: f16) -> (m: Matrix4f16) {
m[3, 3] = 1
return
}
-matrix4_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angle_y_f16 :: proc "contextless" (angle_y: f16) -> (m: Matrix4f16) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = +cos_y
m[0, 2] = -sin_y
@@ -755,7 +820,8 @@ matrix4_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix4f16) {
m[3, 3] = 1
return
}
-matrix4_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angle_z_f16 :: proc "contextless" (angle_z: f16) -> (m: Matrix4f16) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
m[0, 0] = +cos_z
m[0, 1] = +sin_z
@@ -767,7 +833,8 @@ matrix4_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix4f16) {
}
-matrix4_from_derived_euler_angle_x_f16 :: proc(angle_x: f16, angular_velocity_x: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_derived_euler_angle_x_f16 :: proc "contextless" (angle_x: f16, angular_velocity_x: f16) -> (m: Matrix4f16) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
m[0, 0] = 1
@@ -778,7 +845,8 @@ matrix4_from_derived_euler_angle_x_f16 :: proc(angle_x: f16, angular_velocity_x:
m[3, 3] = 1
return
}
-matrix4_from_derived_euler_angle_y_f16 :: proc(angle_y: f16, angular_velocity_y: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_derived_euler_angle_y_f16 :: proc "contextless" (angle_y: f16, angular_velocity_y: f16) -> (m: Matrix4f16) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
m[0, 0] = +cos_y
@@ -789,7 +857,8 @@ matrix4_from_derived_euler_angle_y_f16 :: proc(angle_y: f16, angular_velocity_y:
m[3, 3] = 1
return
}
-matrix4_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_derived_euler_angle_z_f16 :: proc "contextless" (angle_z: f16, angular_velocity_z: f16) -> (m: Matrix4f16) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
m[0, 0] = +cos_z
@@ -802,7 +871,8 @@ matrix4_from_derived_euler_angle_z_f16 :: proc(angle_z: f16, angular_velocity_z:
}
-matrix4_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_xy_f16 :: proc "contextless" (angle_x, angle_y: f16) -> (m: Matrix4f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -818,7 +888,8 @@ matrix4_from_euler_angles_xy_f16 :: proc(angle_x, angle_y: f16) -> (m: Matrix4f1
}
-matrix4_from_euler_angles_yx_f16 :: proc(angle_y, angle_x: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_yx_f16 :: proc "contextless" (angle_y, angle_x: f16) -> (m: Matrix4f16) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -833,21 +904,26 @@ matrix4_from_euler_angles_yx_f16 :: proc(angle_y, angle_x: f16) -> (m: Matrix4f1
return
}
-matrix4_from_euler_angles_xz_f16 :: proc(angle_x, angle_z: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_xz_f16 :: proc "contextless" (angle_x, angle_z: f16) -> (m: Matrix4f16) {
return mul(matrix4_from_euler_angle_x(angle_x), matrix4_from_euler_angle_z(angle_z))
}
-matrix4_from_euler_angles_zx_f16 :: proc(angle_z, angle_x: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_zx_f16 :: proc "contextless" (angle_z, angle_x: f16) -> (m: Matrix4f16) {
return mul(matrix4_from_euler_angle_z(angle_z), matrix4_from_euler_angle_x(angle_x))
}
-matrix4_from_euler_angles_yz_f16 :: proc(angle_y, angle_z: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_yz_f16 :: proc "contextless" (angle_y, angle_z: f16) -> (m: Matrix4f16) {
return mul(matrix4_from_euler_angle_y(angle_y), matrix4_from_euler_angle_z(angle_z))
}
-matrix4_from_euler_angles_zy_f16 :: proc(angle_z, angle_y: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_zy_f16 :: proc "contextless" (angle_z, angle_y: f16) -> (m: Matrix4f16) {
return mul(matrix4_from_euler_angle_z(angle_z), matrix4_from_euler_angle_y(angle_y))
}
-matrix4_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_xyz_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(-t1)
c2 := math.cos(-t2)
c3 := math.cos(-t3)
@@ -874,7 +950,8 @@ matrix4_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_yxz_f16 :: proc "contextless" (yaw, pitch, roll: f16) -> (m: Matrix4f16) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -901,7 +978,8 @@ matrix4_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f
return
}
-matrix4_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_xzx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -928,7 +1006,8 @@ matrix4_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_xyx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -955,7 +1034,8 @@ matrix4_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_yxy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -982,7 +1062,8 @@ matrix4_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_yzy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1009,7 +1090,8 @@ matrix4_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_zyz_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1036,7 +1118,8 @@ matrix4_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_zxz_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1064,7 +1147,8 @@ matrix4_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
}
-matrix4_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_xzy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1091,7 +1175,8 @@ matrix4_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_yzx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1118,7 +1203,8 @@ matrix4_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_zyx_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1145,7 +1231,8 @@ matrix4_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
return
}
-matrix4_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_euler_angles_zxy_f16 :: proc "contextless" (t1, t2, t3: f16) -> (m: Matrix4f16) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1173,7 +1260,8 @@ matrix4_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
}
-matrix4_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f16) {
+@(require_results)
+matrix4_from_yaw_pitch_roll_f16 :: proc "contextless" (yaw, pitch, roll: f16) -> (m: Matrix4f16) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -1200,7 +1288,8 @@ matrix4_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f16
return m
}
-euler_angles_xyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xyz_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 2], m[2, 2])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
T2 := math.atan2(-m[0, 2], C2)
@@ -1213,7 +1302,8 @@ euler_angles_xyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yxz_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[0, 2], m[2, 2])
C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
T2 := math.atan2(-m[1, 2], C2)
@@ -1226,7 +1316,8 @@ euler_angles_yxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_xzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xzx_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[2, 0], m[1, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -1239,7 +1330,8 @@ euler_angles_xzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_xyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xyx_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 0], -m[2, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -1252,7 +1344,8 @@ euler_angles_xyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yxy_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[0, 1], m[2, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -1265,7 +1358,8 @@ euler_angles_yxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yzy_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[2, 1], -m[0, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -1277,7 +1371,8 @@ euler_angles_yzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
t3 = T3
return
}
-euler_angles_zyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zyz_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 2], m[0, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -1290,7 +1385,8 @@ euler_angles_zyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_zxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zxz_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[0, 2], -m[1, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -1303,7 +1399,8 @@ euler_angles_zxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_xzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_xzy_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[2, 1], m[1, 1])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
T2 := math.atan2(-m[0, 1], C2)
@@ -1316,7 +1413,8 @@ euler_angles_xzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_yzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_yzx_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(-m[2, 0], m[0, 0])
C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
T2 := math.atan2(m[1, 0], C2)
@@ -1329,7 +1427,8 @@ euler_angles_yzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_zyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zyx_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(m[1, 0], m[0, 0])
C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
T2 := math.atan2(-m[2, 0], C2)
@@ -1342,7 +1441,8 @@ euler_angles_zyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
return
}
-euler_angles_zxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
+@(require_results)
+euler_angles_zxy_from_matrix4_f16 :: proc "contextless" (m: Matrix4f16) -> (t1, t2, t3: f16) {
T1 := math.atan2(-m[0, 1], m[1, 1])
C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
T2 := math.atan2(m[2, 1], C2)
diff --git a/core/math/linalg/specific_euler_angles_f32.odin b/core/math/linalg/specific_euler_angles_f32.odin
index 80e19ce85..b9957034f 100644
--- a/core/math/linalg/specific_euler_angles_f32.odin
+++ b/core/math/linalg/specific_euler_angles_f32.odin
@@ -2,7 +2,8 @@ package linalg
import "core:math"
-euler_angles_from_matrix3_f32 :: proc(m: Matrix3f32, order: Euler_Angle_Order) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32, order: Euler_Angle_Order) -> (t1, t2, t3: f32) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_matrix3(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_matrix3(m)
@@ -19,7 +20,8 @@ euler_angles_from_matrix3_f32 :: proc(m: Matrix3f32, order: Euler_Angle_Order) -
}
return
}
-euler_angles_from_matrix4_f32 :: proc(m: Matrix4f32, order: Euler_Angle_Order) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32, order: Euler_Angle_Order) -> (t1, t2, t3: f32) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_matrix4(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_matrix4(m)
@@ -36,7 +38,8 @@ euler_angles_from_matrix4_f32 :: proc(m: Matrix4f32, order: Euler_Angle_Order) -
}
return
}
-euler_angles_from_quaternion_f32 :: proc(m: Quaternionf32, order: Euler_Angle_Order) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_from_quaternion_f32 :: proc "contextless" (m: Quaternionf32, order: Euler_Angle_Order) -> (t1, t2, t3: f32) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_quaternion(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_quaternion(m)
@@ -54,7 +57,8 @@ euler_angles_from_quaternion_f32 :: proc(m: Quaternionf32, order: Euler_Angle_Or
return
}
-matrix3_from_euler_angles_f32 :: proc(t1, t2, t3: f32, order: Euler_Angle_Order) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_f32 :: proc "contextless" (t1, t2, t3: f32, order: Euler_Angle_Order) -> (m: Matrix3f32) {
switch order {
case .XYZ: return matrix3_from_euler_angles_xyz(t1, t2, t3) // m1, m2, m3 = X(t1), Y(t2), Z(t3);
case .XZY: return matrix3_from_euler_angles_xzy(t1, t2, t3) // m1, m2, m3 = X(t1), Z(t2), Y(t3);
@@ -71,7 +75,8 @@ matrix3_from_euler_angles_f32 :: proc(t1, t2, t3: f32, order: Euler_Angle_Order)
}
return
}
-matrix4_from_euler_angles_f32 :: proc(t1, t2, t3: f32, order: Euler_Angle_Order) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_f32 :: proc "contextless" (t1, t2, t3: f32, order: Euler_Angle_Order) -> (m: Matrix4f32) {
switch order {
case .XYZ: return matrix4_from_euler_angles_xyz(t1, t2, t3) // m1, m2, m3 = X(t1), Y(t2), Z(t3);
case .XZY: return matrix4_from_euler_angles_xzy(t1, t2, t3) // m1, m2, m3 = X(t1), Z(t2), Y(t3);
@@ -89,7 +94,8 @@ matrix4_from_euler_angles_f32 :: proc(t1, t2, t3: f32, order: Euler_Angle_Order)
return
}
-quaternion_from_euler_angles_f32 :: proc(t1, t2, t3: f32, order: Euler_Angle_Order) -> Quaternionf32 {
+@(require_results)
+quaternion_from_euler_angles_f32 :: proc "contextless" (t1, t2, t3: f32, order: Euler_Angle_Order) -> Quaternionf32 {
X :: quaternion_from_euler_angle_x
Y :: quaternion_from_euler_angle_y
Z :: quaternion_from_euler_angle_z
@@ -117,17 +123,21 @@ quaternion_from_euler_angles_f32 :: proc(t1, t2, t3: f32, order: Euler_Angle_Ord
// Quaternionf32s
-quaternion_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_from_euler_angle_x_f32 :: proc "contextless" (angle_x: f32) -> (q: Quaternionf32) {
return quaternion_angle_axis_f32(angle_x, {1, 0, 0})
}
-quaternion_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_from_euler_angle_y_f32 :: proc "contextless" (angle_y: f32) -> (q: Quaternionf32) {
return quaternion_angle_axis_f32(angle_y, {0, 1, 0})
}
-quaternion_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (q: Quaternionf32) {
+@(require_results)
+quaternion_from_euler_angle_z_f32 :: proc "contextless" (angle_z: f32) -> (q: Quaternionf32) {
return quaternion_angle_axis_f32(angle_z, {0, 0, 1})
}
-quaternion_from_pitch_yaw_roll_f32 :: proc(pitch, yaw, roll: f32) -> Quaternionf32 {
+@(require_results)
+quaternion_from_pitch_yaw_roll_f32 :: proc "contextless" (pitch, yaw, roll: f32) -> Quaternionf32 {
a, b, c := pitch, yaw, roll
ca, sa := math.cos(a*0.5), math.sin(a*0.5)
@@ -142,11 +152,13 @@ quaternion_from_pitch_yaw_roll_f32 :: proc(pitch, yaw, roll: f32) -> Quaternionf
return q
}
-roll_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
+@(require_results)
+roll_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> f32 {
return math.atan2(2 * q.x*q.y + q.w*q.z, q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z)
}
-pitch_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
+@(require_results)
+pitch_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> f32 {
y := 2 * (q.y*q.z + q.w*q.w)
x := q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z
@@ -157,52 +169,66 @@ pitch_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
return math.atan2(y, x)
}
-yaw_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
+@(require_results)
+yaw_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> f32 {
return math.asin(clamp(-2 * (q.x*q.z - q.w*q.y), -1, 1))
}
-pitch_yaw_roll_from_quaternion_f32 :: proc(q: Quaternionf32) -> (pitch, yaw, roll: f32) {
+@(require_results)
+pitch_yaw_roll_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (pitch, yaw, roll: f32) {
pitch = pitch_from_quaternion(q)
yaw = yaw_from_quaternion(q)
roll = roll_from_quaternion(q)
return
}
-euler_angles_xyz_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xyz_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_xyz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yxz_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yxz_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_yxz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xzx_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xzx_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_xzx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xyx_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xyx_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_xyx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yxy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yxy_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_yxy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yzy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yzy_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_yzy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zyz_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zyz_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_zyz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zxz_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zxz_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_zxz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xzy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xzy_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_xzy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yzx_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yzx_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_yzx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zyx_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zyx_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_zyx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zxy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zxy_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> (t1, t2, t3: f32) {
return euler_angles_zxy_from_matrix4(matrix4_from_quaternion(q))
}
@@ -210,7 +236,8 @@ euler_angles_zxy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f
// Matrix3
-matrix3_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angle_x_f32 :: proc "contextless" (angle_x: f32) -> (m: Matrix3f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
m[0, 0] = 1
m[1, 1] = +cos_x
@@ -219,7 +246,8 @@ matrix3_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (m: Matrix3f32) {
m[2, 2] = +cos_x
return
}
-matrix3_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angle_y_f32 :: proc "contextless" (angle_y: f32) -> (m: Matrix3f32) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = +cos_y
m[0, 2] = -sin_y
@@ -228,7 +256,8 @@ matrix3_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix3f32) {
m[2, 2] = +cos_y
return
}
-matrix3_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angle_z_f32 :: proc "contextless" (angle_z: f32) -> (m: Matrix3f32) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
m[0, 0] = +cos_z
m[0, 1] = +sin_z
@@ -239,7 +268,8 @@ matrix3_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix3f32) {
}
-matrix3_from_derived_euler_angle_x_f32 :: proc(angle_x: f32, angular_velocity_x: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_derived_euler_angle_x_f32 :: proc "contextless" (angle_x: f32, angular_velocity_x: f32) -> (m: Matrix3f32) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
m[0, 0] = 1
@@ -249,7 +279,8 @@ matrix3_from_derived_euler_angle_x_f32 :: proc(angle_x: f32, angular_velocity_x:
m[2, 2] = +cos_x
return
}
-matrix3_from_derived_euler_angle_y_f32 :: proc(angle_y: f32, angular_velocity_y: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_derived_euler_angle_y_f32 :: proc "contextless" (angle_y: f32, angular_velocity_y: f32) -> (m: Matrix3f32) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
m[0, 0] = +cos_y
@@ -259,7 +290,8 @@ matrix3_from_derived_euler_angle_y_f32 :: proc(angle_y: f32, angular_velocity_y:
m[2, 2] = +cos_y
return
}
-matrix3_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_derived_euler_angle_z_f32 :: proc "contextless" (angle_z: f32, angular_velocity_z: f32) -> (m: Matrix3f32) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
m[0, 0] = +cos_z
@@ -271,7 +303,8 @@ matrix3_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z:
}
-matrix3_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_xy_f32 :: proc "contextless" (angle_x, angle_y: f32) -> (m: Matrix3f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -286,7 +319,8 @@ matrix3_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix3f3
}
-matrix3_from_euler_angles_yx_f32 :: proc(angle_y, angle_x: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_yx_f32 :: proc "contextless" (angle_y, angle_x: f32) -> (m: Matrix3f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -300,21 +334,26 @@ matrix3_from_euler_angles_yx_f32 :: proc(angle_y, angle_x: f32) -> (m: Matrix3f3
return
}
-matrix3_from_euler_angles_xz_f32 :: proc(angle_x, angle_z: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_xz_f32 :: proc "contextless" (angle_x, angle_z: f32) -> (m: Matrix3f32) {
return mul(matrix3_from_euler_angle_x(angle_x), matrix3_from_euler_angle_z(angle_z))
}
-matrix3_from_euler_angles_zx_f32 :: proc(angle_z, angle_x: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_zx_f32 :: proc "contextless" (angle_z, angle_x: f32) -> (m: Matrix3f32) {
return mul(matrix3_from_euler_angle_z(angle_z), matrix3_from_euler_angle_x(angle_x))
}
-matrix3_from_euler_angles_yz_f32 :: proc(angle_y, angle_z: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_yz_f32 :: proc "contextless" (angle_y, angle_z: f32) -> (m: Matrix3f32) {
return mul(matrix3_from_euler_angle_y(angle_y), matrix3_from_euler_angle_z(angle_z))
}
-matrix3_from_euler_angles_zy_f32 :: proc(angle_z, angle_y: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_zy_f32 :: proc "contextless" (angle_z, angle_y: f32) -> (m: Matrix3f32) {
return mul(matrix3_from_euler_angle_z(angle_z), matrix3_from_euler_angle_y(angle_y))
}
-matrix3_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_xyz_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(-t1)
c2 := math.cos(-t2)
c3 := math.cos(-t3)
@@ -334,7 +373,8 @@ matrix3_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_yxz_f32 :: proc "contextless" (yaw, pitch, roll: f32) -> (m: Matrix3f32) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -354,7 +394,8 @@ matrix3_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f
return
}
-matrix3_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_xzx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -374,7 +415,8 @@ matrix3_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_xyx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -394,7 +436,8 @@ matrix3_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_yxy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -414,7 +457,8 @@ matrix3_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_yzy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -434,7 +478,8 @@ matrix3_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_zyz_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -454,7 +499,8 @@ matrix3_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_zxz_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -475,7 +521,8 @@ matrix3_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
}
-matrix3_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_xzy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -495,7 +542,8 @@ matrix3_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_yzx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -515,7 +563,8 @@ matrix3_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_zyx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -535,7 +584,8 @@ matrix3_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
return
}
-matrix3_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_euler_angles_zxy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix3f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -556,7 +606,8 @@ matrix3_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
}
-matrix3_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f32) {
+@(require_results)
+matrix3_from_yaw_pitch_roll_f32 :: proc "contextless" (yaw, pitch, roll: f32) -> (m: Matrix3f32) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -576,7 +627,8 @@ matrix3_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f32
return m
}
-euler_angles_xyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xyz_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 2], m[2, 2])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
T2 := math.atan2(-m[0, 2], C2)
@@ -589,7 +641,8 @@ euler_angles_xyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yxz_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[0, 2], m[2, 2])
C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
T2 := math.atan2(-m[1, 2], C2)
@@ -602,7 +655,8 @@ euler_angles_yxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_xzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xzx_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[2, 0], m[1, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -615,7 +669,8 @@ euler_angles_xzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_xyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xyx_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 0], -m[2, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -628,7 +683,8 @@ euler_angles_xyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yxy_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[0, 1], m[2, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -641,7 +697,8 @@ euler_angles_yxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yzy_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[2, 1], -m[0, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -653,7 +710,8 @@ euler_angles_yzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
t3 = T3
return
}
-euler_angles_zyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zyz_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 2], m[0, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -666,7 +724,8 @@ euler_angles_zyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_zxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zxz_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[0, 2], -m[1, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -679,7 +738,8 @@ euler_angles_zxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_xzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xzy_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[2, 1], m[1, 1])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
T2 := math.atan2(-m[0, 1], C2)
@@ -692,7 +752,8 @@ euler_angles_xzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yzx_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(-m[2, 0], m[0, 0])
C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
T2 := math.atan2(m[1, 0], C2)
@@ -705,7 +766,8 @@ euler_angles_yzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_zyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zyx_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 0], m[0, 0])
C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
T2 := math.atan2(-m[2, 0], C2)
@@ -718,7 +780,8 @@ euler_angles_zyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_zxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zxy_from_matrix3_f32 :: proc "contextless" (m: Matrix3f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(-m[0, 1], m[1, 1])
C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
T2 := math.atan2(m[2, 1], C2)
@@ -735,7 +798,8 @@ euler_angles_zxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
// Matrix4
-matrix4_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angle_x_f32 :: proc "contextless" (angle_x: f32) -> (m: Matrix4f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
m[0, 0] = 1
m[1, 1] = +cos_x
@@ -745,7 +809,8 @@ matrix4_from_euler_angle_x_f32 :: proc(angle_x: f32) -> (m: Matrix4f32) {
m[3, 3] = 1
return
}
-matrix4_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angle_y_f32 :: proc "contextless" (angle_y: f32) -> (m: Matrix4f32) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = +cos_y
m[0, 2] = -sin_y
@@ -755,7 +820,8 @@ matrix4_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix4f32) {
m[3, 3] = 1
return
}
-matrix4_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angle_z_f32 :: proc "contextless" (angle_z: f32) -> (m: Matrix4f32) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
m[0, 0] = +cos_z
m[0, 1] = +sin_z
@@ -767,7 +833,8 @@ matrix4_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix4f32) {
}
-matrix4_from_derived_euler_angle_x_f32 :: proc(angle_x: f32, angular_velocity_x: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_derived_euler_angle_x_f32 :: proc "contextless" (angle_x: f32, angular_velocity_x: f32) -> (m: Matrix4f32) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
m[0, 0] = 1
@@ -778,7 +845,8 @@ matrix4_from_derived_euler_angle_x_f32 :: proc(angle_x: f32, angular_velocity_x:
m[3, 3] = 1
return
}
-matrix4_from_derived_euler_angle_y_f32 :: proc(angle_y: f32, angular_velocity_y: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_derived_euler_angle_y_f32 :: proc "contextless" (angle_y: f32, angular_velocity_y: f32) -> (m: Matrix4f32) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
m[0, 0] = +cos_y
@@ -789,7 +857,8 @@ matrix4_from_derived_euler_angle_y_f32 :: proc(angle_y: f32, angular_velocity_y:
m[3, 3] = 1
return
}
-matrix4_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_derived_euler_angle_z_f32 :: proc "contextless" (angle_z: f32, angular_velocity_z: f32) -> (m: Matrix4f32) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
m[0, 0] = +cos_z
@@ -802,7 +871,8 @@ matrix4_from_derived_euler_angle_z_f32 :: proc(angle_z: f32, angular_velocity_z:
}
-matrix4_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_xy_f32 :: proc "contextless" (angle_x, angle_y: f32) -> (m: Matrix4f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -818,7 +888,8 @@ matrix4_from_euler_angles_xy_f32 :: proc(angle_x, angle_y: f32) -> (m: Matrix4f3
}
-matrix4_from_euler_angles_yx_f32 :: proc(angle_y, angle_x: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_yx_f32 :: proc "contextless" (angle_y, angle_x: f32) -> (m: Matrix4f32) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -833,21 +904,26 @@ matrix4_from_euler_angles_yx_f32 :: proc(angle_y, angle_x: f32) -> (m: Matrix4f3
return
}
-matrix4_from_euler_angles_xz_f32 :: proc(angle_x, angle_z: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_xz_f32 :: proc "contextless" (angle_x, angle_z: f32) -> (m: Matrix4f32) {
return mul(matrix4_from_euler_angle_x(angle_x), matrix4_from_euler_angle_z(angle_z))
}
-matrix4_from_euler_angles_zx_f32 :: proc(angle_z, angle_x: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_zx_f32 :: proc "contextless" (angle_z, angle_x: f32) -> (m: Matrix4f32) {
return mul(matrix4_from_euler_angle_z(angle_z), matrix4_from_euler_angle_x(angle_x))
}
-matrix4_from_euler_angles_yz_f32 :: proc(angle_y, angle_z: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_yz_f32 :: proc "contextless" (angle_y, angle_z: f32) -> (m: Matrix4f32) {
return mul(matrix4_from_euler_angle_y(angle_y), matrix4_from_euler_angle_z(angle_z))
}
-matrix4_from_euler_angles_zy_f32 :: proc(angle_z, angle_y: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_zy_f32 :: proc "contextless" (angle_z, angle_y: f32) -> (m: Matrix4f32) {
return mul(matrix4_from_euler_angle_z(angle_z), matrix4_from_euler_angle_y(angle_y))
}
-matrix4_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_xyz_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(-t1)
c2 := math.cos(-t2)
c3 := math.cos(-t3)
@@ -874,7 +950,8 @@ matrix4_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_yxz_f32 :: proc "contextless" (yaw, pitch, roll: f32) -> (m: Matrix4f32) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -901,7 +978,8 @@ matrix4_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f
return
}
-matrix4_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_xzx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -928,7 +1006,8 @@ matrix4_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_xyx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -955,7 +1034,8 @@ matrix4_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_yxy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -982,7 +1062,8 @@ matrix4_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_yzy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1009,7 +1090,8 @@ matrix4_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_zyz_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1036,7 +1118,8 @@ matrix4_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_zxz_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1064,7 +1147,8 @@ matrix4_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
}
-matrix4_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_xzy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1091,7 +1175,8 @@ matrix4_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_yzx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1118,7 +1203,8 @@ matrix4_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_zyx_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1145,7 +1231,8 @@ matrix4_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
return
}
-matrix4_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_euler_angles_zxy_f32 :: proc "contextless" (t1, t2, t3: f32) -> (m: Matrix4f32) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1173,7 +1260,8 @@ matrix4_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
}
-matrix4_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f32) {
+@(require_results)
+matrix4_from_yaw_pitch_roll_f32 :: proc "contextless" (yaw, pitch, roll: f32) -> (m: Matrix4f32) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -1200,7 +1288,8 @@ matrix4_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f32
return m
}
-euler_angles_xyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xyz_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 2], m[2, 2])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
T2 := math.atan2(-m[0, 2], C2)
@@ -1213,7 +1302,8 @@ euler_angles_xyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yxz_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[0, 2], m[2, 2])
C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
T2 := math.atan2(-m[1, 2], C2)
@@ -1226,7 +1316,8 @@ euler_angles_yxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_xzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xzx_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[2, 0], m[1, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -1239,7 +1330,8 @@ euler_angles_xzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_xyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xyx_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 0], -m[2, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -1252,7 +1344,8 @@ euler_angles_xyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yxy_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[0, 1], m[2, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -1265,7 +1358,8 @@ euler_angles_yxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yzy_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[2, 1], -m[0, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -1277,7 +1371,8 @@ euler_angles_yzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
t3 = T3
return
}
-euler_angles_zyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zyz_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 2], m[0, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -1290,7 +1385,8 @@ euler_angles_zyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_zxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zxz_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[0, 2], -m[1, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -1303,7 +1399,8 @@ euler_angles_zxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_xzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_xzy_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[2, 1], m[1, 1])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
T2 := math.atan2(-m[0, 1], C2)
@@ -1316,7 +1413,8 @@ euler_angles_xzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_yzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_yzx_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(-m[2, 0], m[0, 0])
C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
T2 := math.atan2(m[1, 0], C2)
@@ -1329,7 +1427,8 @@ euler_angles_yzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_zyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zyx_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(m[1, 0], m[0, 0])
C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
T2 := math.atan2(-m[2, 0], C2)
@@ -1342,7 +1441,8 @@ euler_angles_zyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
return
}
-euler_angles_zxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
+@(require_results)
+euler_angles_zxy_from_matrix4_f32 :: proc "contextless" (m: Matrix4f32) -> (t1, t2, t3: f32) {
T1 := math.atan2(-m[0, 1], m[1, 1])
C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
T2 := math.atan2(m[2, 1], C2)
diff --git a/core/math/linalg/specific_euler_angles_f64.odin b/core/math/linalg/specific_euler_angles_f64.odin
index 2f8f758b0..8001d080a 100644
--- a/core/math/linalg/specific_euler_angles_f64.odin
+++ b/core/math/linalg/specific_euler_angles_f64.odin
@@ -2,7 +2,8 @@ package linalg
import "core:math"
-euler_angles_from_matrix3_f64 :: proc(m: Matrix3f64, order: Euler_Angle_Order) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64, order: Euler_Angle_Order) -> (t1, t2, t3: f64) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_matrix3(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_matrix3(m)
@@ -19,7 +20,8 @@ euler_angles_from_matrix3_f64 :: proc(m: Matrix3f64, order: Euler_Angle_Order) -
}
return
}
-euler_angles_from_matrix4_f64 :: proc(m: Matrix4f64, order: Euler_Angle_Order) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64, order: Euler_Angle_Order) -> (t1, t2, t3: f64) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_matrix4(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_matrix4(m)
@@ -36,7 +38,8 @@ euler_angles_from_matrix4_f64 :: proc(m: Matrix4f64, order: Euler_Angle_Order) -
}
return
}
-euler_angles_from_quaternion_f64 :: proc(m: Quaternionf64, order: Euler_Angle_Order) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_from_quaternion_f64 :: proc "contextless" (m: Quaternionf64, order: Euler_Angle_Order) -> (t1, t2, t3: f64) {
switch order {
case .XYZ: t1, t2, t3 = euler_angles_xyz_from_quaternion(m)
case .XZY: t1, t2, t3 = euler_angles_xzy_from_quaternion(m)
@@ -54,7 +57,8 @@ euler_angles_from_quaternion_f64 :: proc(m: Quaternionf64, order: Euler_Angle_Or
return
}
-matrix3_from_euler_angles_f64 :: proc(t1, t2, t3: f64, order: Euler_Angle_Order) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_f64 :: proc "contextless" (t1, t2, t3: f64, order: Euler_Angle_Order) -> (m: Matrix3f64) {
switch order {
case .XYZ: return matrix3_from_euler_angles_xyz(t1, t2, t3) // m1, m2, m3 = X(t1), Y(t2), Z(t3);
case .XZY: return matrix3_from_euler_angles_xzy(t1, t2, t3) // m1, m2, m3 = X(t1), Z(t2), Y(t3);
@@ -71,7 +75,8 @@ matrix3_from_euler_angles_f64 :: proc(t1, t2, t3: f64, order: Euler_Angle_Order)
}
return
}
-matrix4_from_euler_angles_f64 :: proc(t1, t2, t3: f64, order: Euler_Angle_Order) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_f64 :: proc "contextless" (t1, t2, t3: f64, order: Euler_Angle_Order) -> (m: Matrix4f64) {
switch order {
case .XYZ: return matrix4_from_euler_angles_xyz(t1, t2, t3) // m1, m2, m3 = X(t1), Y(t2), Z(t3);
case .XZY: return matrix4_from_euler_angles_xzy(t1, t2, t3) // m1, m2, m3 = X(t1), Z(t2), Y(t3);
@@ -89,7 +94,8 @@ matrix4_from_euler_angles_f64 :: proc(t1, t2, t3: f64, order: Euler_Angle_Order)
return
}
-quaternion_from_euler_angles_f64 :: proc(t1, t2, t3: f64, order: Euler_Angle_Order) -> Quaternionf64 {
+@(require_results)
+quaternion_from_euler_angles_f64 :: proc "contextless" (t1, t2, t3: f64, order: Euler_Angle_Order) -> Quaternionf64 {
X :: quaternion_from_euler_angle_x
Y :: quaternion_from_euler_angle_y
Z :: quaternion_from_euler_angle_z
@@ -117,17 +123,21 @@ quaternion_from_euler_angles_f64 :: proc(t1, t2, t3: f64, order: Euler_Angle_Ord
// Quaternionf64s
-quaternion_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_from_euler_angle_x_f64 :: proc "contextless" (angle_x: f64) -> (q: Quaternionf64) {
return quaternion_angle_axis_f64(angle_x, {1, 0, 0})
}
-quaternion_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_from_euler_angle_y_f64 :: proc "contextless" (angle_y: f64) -> (q: Quaternionf64) {
return quaternion_angle_axis_f64(angle_y, {0, 1, 0})
}
-quaternion_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (q: Quaternionf64) {
+@(require_results)
+quaternion_from_euler_angle_z_f64 :: proc "contextless" (angle_z: f64) -> (q: Quaternionf64) {
return quaternion_angle_axis_f64(angle_z, {0, 0, 1})
}
-quaternion_from_pitch_yaw_roll_f64 :: proc(pitch, yaw, roll: f64) -> Quaternionf64 {
+@(require_results)
+quaternion_from_pitch_yaw_roll_f64 :: proc "contextless" (pitch, yaw, roll: f64) -> Quaternionf64 {
a, b, c := pitch, yaw, roll
ca, sa := math.cos(a*0.5), math.sin(a*0.5)
@@ -142,11 +152,13 @@ quaternion_from_pitch_yaw_roll_f64 :: proc(pitch, yaw, roll: f64) -> Quaternionf
return q
}
-roll_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
+@(require_results)
+roll_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> f64 {
return math.atan2(2 * q.x*q.y + q.w*q.z, q.w*q.w + q.x*q.x - q.y*q.y - q.z*q.z)
}
-pitch_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
+@(require_results)
+pitch_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> f64 {
y := 2 * (q.y*q.z + q.w*q.w)
x := q.w*q.w - q.x*q.x - q.y*q.y + q.z*q.z
@@ -157,52 +169,66 @@ pitch_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
return math.atan2(y, x)
}
-yaw_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
+@(require_results)
+yaw_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> f64 {
return math.asin(clamp(-2 * (q.x*q.z - q.w*q.y), -1, 1))
}
-pitch_yaw_roll_from_quaternion_f64 :: proc(q: Quaternionf64) -> (pitch, yaw, roll: f64) {
+@(require_results)
+pitch_yaw_roll_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (pitch, yaw, roll: f64) {
pitch = pitch_from_quaternion(q)
yaw = yaw_from_quaternion(q)
roll = roll_from_quaternion(q)
return
}
-euler_angles_xyz_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xyz_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_xyz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yxz_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yxz_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_yxz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xzx_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xzx_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_xzx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xyx_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xyx_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_xyx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yxy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yxy_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_yxy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yzy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yzy_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_yzy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zyz_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zyz_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_zyz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zxz_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zxz_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_zxz_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_xzy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xzy_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_xzy_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_yzx_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yzx_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_yzx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zyx_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zyx_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_zyx_from_matrix4(matrix4_from_quaternion(q))
}
-euler_angles_zxy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zxy_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> (t1, t2, t3: f64) {
return euler_angles_zxy_from_matrix4(matrix4_from_quaternion(q))
}
@@ -210,7 +236,8 @@ euler_angles_zxy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f
// Matrix3
-matrix3_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angle_x_f64 :: proc "contextless" (angle_x: f64) -> (m: Matrix3f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
m[0, 0] = 1
m[1, 1] = +cos_x
@@ -219,7 +246,8 @@ matrix3_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (m: Matrix3f64) {
m[2, 2] = +cos_x
return
}
-matrix3_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angle_y_f64 :: proc "contextless" (angle_y: f64) -> (m: Matrix3f64) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = +cos_y
m[0, 2] = -sin_y
@@ -228,7 +256,8 @@ matrix3_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix3f64) {
m[2, 2] = +cos_y
return
}
-matrix3_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angle_z_f64 :: proc "contextless" (angle_z: f64) -> (m: Matrix3f64) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
m[0, 0] = +cos_z
m[0, 1] = +sin_z
@@ -239,7 +268,8 @@ matrix3_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix3f64) {
}
-matrix3_from_derived_euler_angle_x_f64 :: proc(angle_x: f64, angular_velocity_x: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_derived_euler_angle_x_f64 :: proc "contextless" (angle_x: f64, angular_velocity_x: f64) -> (m: Matrix3f64) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
m[0, 0] = 1
@@ -249,7 +279,8 @@ matrix3_from_derived_euler_angle_x_f64 :: proc(angle_x: f64, angular_velocity_x:
m[2, 2] = +cos_x
return
}
-matrix3_from_derived_euler_angle_y_f64 :: proc(angle_y: f64, angular_velocity_y: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_derived_euler_angle_y_f64 :: proc "contextless" (angle_y: f64, angular_velocity_y: f64) -> (m: Matrix3f64) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
m[0, 0] = +cos_y
@@ -259,7 +290,8 @@ matrix3_from_derived_euler_angle_y_f64 :: proc(angle_y: f64, angular_velocity_y:
m[2, 2] = +cos_y
return
}
-matrix3_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_derived_euler_angle_z_f64 :: proc "contextless" (angle_z: f64, angular_velocity_z: f64) -> (m: Matrix3f64) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
m[0, 0] = +cos_z
@@ -271,7 +303,8 @@ matrix3_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z:
}
-matrix3_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_xy_f64 :: proc "contextless" (angle_x, angle_y: f64) -> (m: Matrix3f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -286,7 +319,8 @@ matrix3_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix3f6
}
-matrix3_from_euler_angles_yx_f64 :: proc(angle_y, angle_x: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_yx_f64 :: proc "contextless" (angle_y, angle_x: f64) -> (m: Matrix3f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -300,21 +334,26 @@ matrix3_from_euler_angles_yx_f64 :: proc(angle_y, angle_x: f64) -> (m: Matrix3f6
return
}
-matrix3_from_euler_angles_xz_f64 :: proc(angle_x, angle_z: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_xz_f64 :: proc "contextless" (angle_x, angle_z: f64) -> (m: Matrix3f64) {
return mul(matrix3_from_euler_angle_x(angle_x), matrix3_from_euler_angle_z(angle_z))
}
-matrix3_from_euler_angles_zx_f64 :: proc(angle_z, angle_x: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_zx_f64 :: proc "contextless" (angle_z, angle_x: f64) -> (m: Matrix3f64) {
return mul(matrix3_from_euler_angle_z(angle_z), matrix3_from_euler_angle_x(angle_x))
}
-matrix3_from_euler_angles_yz_f64 :: proc(angle_y, angle_z: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_yz_f64 :: proc "contextless" (angle_y, angle_z: f64) -> (m: Matrix3f64) {
return mul(matrix3_from_euler_angle_y(angle_y), matrix3_from_euler_angle_z(angle_z))
}
-matrix3_from_euler_angles_zy_f64 :: proc(angle_z, angle_y: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_zy_f64 :: proc "contextless" (angle_z, angle_y: f64) -> (m: Matrix3f64) {
return mul(matrix3_from_euler_angle_z(angle_z), matrix3_from_euler_angle_y(angle_y))
}
-matrix3_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_xyz_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(-t1)
c2 := math.cos(-t2)
c3 := math.cos(-t3)
@@ -334,7 +373,8 @@ matrix3_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_yxz_f64 :: proc "contextless" (yaw, pitch, roll: f64) -> (m: Matrix3f64) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -354,7 +394,8 @@ matrix3_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f
return
}
-matrix3_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_xzx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -374,7 +415,8 @@ matrix3_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_xyx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -394,7 +436,8 @@ matrix3_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_yxy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -414,7 +457,8 @@ matrix3_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_yzy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -434,7 +478,8 @@ matrix3_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_zyz_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -454,7 +499,8 @@ matrix3_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_zxz_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -475,7 +521,8 @@ matrix3_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
}
-matrix3_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_xzy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -495,7 +542,8 @@ matrix3_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_yzx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -515,7 +563,8 @@ matrix3_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_zyx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -535,7 +584,8 @@ matrix3_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
return
}
-matrix3_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_euler_angles_zxy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix3f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -556,7 +606,8 @@ matrix3_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
}
-matrix3_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f64) {
+@(require_results)
+matrix3_from_yaw_pitch_roll_f64 :: proc "contextless" (yaw, pitch, roll: f64) -> (m: Matrix3f64) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -576,7 +627,8 @@ matrix3_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f64
return m
}
-euler_angles_xyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xyz_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 2], m[2, 2])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
T2 := math.atan2(-m[0, 2], C2)
@@ -589,7 +641,8 @@ euler_angles_xyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yxz_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[0, 2], m[2, 2])
C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
T2 := math.atan2(-m[1, 2], C2)
@@ -602,7 +655,8 @@ euler_angles_yxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_xzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xzx_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[2, 0], m[1, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -615,7 +669,8 @@ euler_angles_xzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_xyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xyx_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 0], -m[2, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -628,7 +683,8 @@ euler_angles_xyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yxy_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[0, 1], m[2, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -641,7 +697,8 @@ euler_angles_yxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yzy_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[2, 1], -m[0, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -653,7 +710,8 @@ euler_angles_yzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
t3 = T3
return
}
-euler_angles_zyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zyz_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 2], m[0, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -666,7 +724,8 @@ euler_angles_zyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_zxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zxz_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[0, 2], -m[1, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -679,7 +738,8 @@ euler_angles_zxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_xzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xzy_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[2, 1], m[1, 1])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
T2 := math.atan2(-m[0, 1], C2)
@@ -692,7 +752,8 @@ euler_angles_xzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yzx_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(-m[2, 0], m[0, 0])
C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
T2 := math.atan2(m[1, 0], C2)
@@ -705,7 +766,8 @@ euler_angles_yzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_zyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zyx_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 0], m[0, 0])
C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
T2 := math.atan2(-m[2, 0], C2)
@@ -718,7 +780,8 @@ euler_angles_zyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_zxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zxy_from_matrix3_f64 :: proc "contextless" (m: Matrix3f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(-m[0, 1], m[1, 1])
C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
T2 := math.atan2(m[2, 1], C2)
@@ -735,7 +798,8 @@ euler_angles_zxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
// Matrix4
-matrix4_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angle_x_f64 :: proc "contextless" (angle_x: f64) -> (m: Matrix4f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
m[0, 0] = 1
m[1, 1] = +cos_x
@@ -745,7 +809,8 @@ matrix4_from_euler_angle_x_f64 :: proc(angle_x: f64) -> (m: Matrix4f64) {
m[3, 3] = 1
return
}
-matrix4_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angle_y_f64 :: proc "contextless" (angle_y: f64) -> (m: Matrix4f64) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = +cos_y
m[0, 2] = -sin_y
@@ -755,7 +820,8 @@ matrix4_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix4f64) {
m[3, 3] = 1
return
}
-matrix4_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angle_z_f64 :: proc "contextless" (angle_z: f64) -> (m: Matrix4f64) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
m[0, 0] = +cos_z
m[0, 1] = +sin_z
@@ -767,7 +833,8 @@ matrix4_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix4f64) {
}
-matrix4_from_derived_euler_angle_x_f64 :: proc(angle_x: f64, angular_velocity_x: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_derived_euler_angle_x_f64 :: proc "contextless" (angle_x: f64, angular_velocity_x: f64) -> (m: Matrix4f64) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
m[0, 0] = 1
@@ -778,7 +845,8 @@ matrix4_from_derived_euler_angle_x_f64 :: proc(angle_x: f64, angular_velocity_x:
m[3, 3] = 1
return
}
-matrix4_from_derived_euler_angle_y_f64 :: proc(angle_y: f64, angular_velocity_y: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_derived_euler_angle_y_f64 :: proc "contextless" (angle_y: f64, angular_velocity_y: f64) -> (m: Matrix4f64) {
cos_y := math.cos(angle_y) * angular_velocity_y
sin_y := math.sin(angle_y) * angular_velocity_y
m[0, 0] = +cos_y
@@ -789,7 +857,8 @@ matrix4_from_derived_euler_angle_y_f64 :: proc(angle_y: f64, angular_velocity_y:
m[3, 3] = 1
return
}
-matrix4_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_derived_euler_angle_z_f64 :: proc "contextless" (angle_z: f64, angular_velocity_z: f64) -> (m: Matrix4f64) {
cos_z := math.cos(angle_z) * angular_velocity_z
sin_z := math.sin(angle_z) * angular_velocity_z
m[0, 0] = +cos_z
@@ -802,7 +871,8 @@ matrix4_from_derived_euler_angle_z_f64 :: proc(angle_z: f64, angular_velocity_z:
}
-matrix4_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_xy_f64 :: proc "contextless" (angle_x, angle_y: f64) -> (m: Matrix4f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -818,7 +888,8 @@ matrix4_from_euler_angles_xy_f64 :: proc(angle_x, angle_y: f64) -> (m: Matrix4f6
}
-matrix4_from_euler_angles_yx_f64 :: proc(angle_y, angle_x: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_yx_f64 :: proc "contextless" (angle_y, angle_x: f64) -> (m: Matrix4f64) {
cos_x, sin_x := math.cos(angle_x), math.sin(angle_x)
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
m[0, 0] = cos_y
@@ -833,21 +904,26 @@ matrix4_from_euler_angles_yx_f64 :: proc(angle_y, angle_x: f64) -> (m: Matrix4f6
return
}
-matrix4_from_euler_angles_xz_f64 :: proc(angle_x, angle_z: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_xz_f64 :: proc "contextless" (angle_x, angle_z: f64) -> (m: Matrix4f64) {
return mul(matrix4_from_euler_angle_x(angle_x), matrix4_from_euler_angle_z(angle_z))
}
-matrix4_from_euler_angles_zx_f64 :: proc(angle_z, angle_x: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_zx_f64 :: proc "contextless" (angle_z, angle_x: f64) -> (m: Matrix4f64) {
return mul(matrix4_from_euler_angle_z(angle_z), matrix4_from_euler_angle_x(angle_x))
}
-matrix4_from_euler_angles_yz_f64 :: proc(angle_y, angle_z: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_yz_f64 :: proc "contextless" (angle_y, angle_z: f64) -> (m: Matrix4f64) {
return mul(matrix4_from_euler_angle_y(angle_y), matrix4_from_euler_angle_z(angle_z))
}
-matrix4_from_euler_angles_zy_f64 :: proc(angle_z, angle_y: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_zy_f64 :: proc "contextless" (angle_z, angle_y: f64) -> (m: Matrix4f64) {
return mul(matrix4_from_euler_angle_z(angle_z), matrix4_from_euler_angle_y(angle_y))
}
-matrix4_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_xyz_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(-t1)
c2 := math.cos(-t2)
c3 := math.cos(-t3)
@@ -874,7 +950,8 @@ matrix4_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_yxz_f64 :: proc "contextless" (yaw, pitch, roll: f64) -> (m: Matrix4f64) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -901,7 +978,8 @@ matrix4_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f
return
}
-matrix4_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_xzx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -928,7 +1006,8 @@ matrix4_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_xyx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -955,7 +1034,8 @@ matrix4_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_yxy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -982,7 +1062,8 @@ matrix4_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_yzy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1009,7 +1090,8 @@ matrix4_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_zyz_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1036,7 +1118,8 @@ matrix4_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_zxz_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1064,7 +1147,8 @@ matrix4_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
}
-matrix4_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_xzy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1091,7 +1175,8 @@ matrix4_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_yzx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1118,7 +1203,8 @@ matrix4_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_zyx_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1145,7 +1231,8 @@ matrix4_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
return
}
-matrix4_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_euler_angles_zxy_f64 :: proc "contextless" (t1, t2, t3: f64) -> (m: Matrix4f64) {
c1 := math.cos(t1)
s1 := math.sin(t1)
c2 := math.cos(t2)
@@ -1173,7 +1260,8 @@ matrix4_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
}
-matrix4_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f64) {
+@(require_results)
+matrix4_from_yaw_pitch_roll_f64 :: proc "contextless" (yaw, pitch, roll: f64) -> (m: Matrix4f64) {
ch := math.cos(yaw)
sh := math.sin(yaw)
cp := math.cos(pitch)
@@ -1200,7 +1288,8 @@ matrix4_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f64
return m
}
-euler_angles_xyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xyz_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 2], m[2, 2])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 1]*m[0, 1])
T2 := math.atan2(-m[0, 2], C2)
@@ -1213,7 +1302,8 @@ euler_angles_xyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yxz_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[0, 2], m[2, 2])
C2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 1]*m[1, 1])
T2 := math.atan2(-m[1, 2], C2)
@@ -1226,7 +1316,8 @@ euler_angles_yxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_xzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xzx_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[2, 0], m[1, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -1239,7 +1330,8 @@ euler_angles_xzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_xyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xyx_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 0], -m[2, 0])
S2 := math.sqrt(m[0, 1]*m[0, 1] + m[0, 2]*m[0, 2])
T2 := math.atan2(S2, m[0, 0])
@@ -1252,7 +1344,8 @@ euler_angles_xyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yxy_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[0, 1], m[2, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -1265,7 +1358,8 @@ euler_angles_yxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yzy_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[2, 1], -m[0, 1])
S2 := math.sqrt(m[1, 0]*m[1, 0] + m[1, 2]*m[1, 2])
T2 := math.atan2(S2, m[1, 1])
@@ -1277,7 +1371,8 @@ euler_angles_yzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
t3 = T3
return
}
-euler_angles_zyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zyz_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 2], m[0, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -1290,7 +1385,8 @@ euler_angles_zyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_zxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zxz_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[0, 2], -m[1, 2])
S2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 1]*m[2, 1])
T2 := math.atan2(S2, m[2, 2])
@@ -1303,7 +1399,8 @@ euler_angles_zxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_xzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_xzy_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[2, 1], m[1, 1])
C2 := math.sqrt(m[0, 0]*m[0, 0] + m[0, 2]*m[0, 2])
T2 := math.atan2(-m[0, 1], C2)
@@ -1316,7 +1413,8 @@ euler_angles_xzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_yzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_yzx_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(-m[2, 0], m[0, 0])
C2 := math.sqrt(m[1, 1]*m[1, 1] + m[1, 2]*m[1, 2])
T2 := math.atan2(m[1, 0], C2)
@@ -1329,7 +1427,8 @@ euler_angles_yzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_zyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zyx_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(m[1, 0], m[0, 0])
C2 := math.sqrt(m[2, 1]*m[2, 1] + m[2, 2]*m[2, 2])
T2 := math.atan2(-m[2, 0], C2)
@@ -1342,7 +1441,8 @@ euler_angles_zyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
return
}
-euler_angles_zxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
+@(require_results)
+euler_angles_zxy_from_matrix4_f64 :: proc "contextless" (m: Matrix4f64) -> (t1, t2, t3: f64) {
T1 := math.atan2(-m[0, 1], m[1, 1])
C2 := math.sqrt(m[2, 0]*m[2, 0] + m[2, 2]*m[2, 2])
T2 := math.atan2(m[2, 1], C2)
diff --git a/core/math/linalg/swizzle.odin b/core/math/linalg/swizzle.odin
index ada4aebcf..f1dafbeca 100644
--- a/core/math/linalg/swizzle.odin
+++ b/core/math/linalg/swizzle.odin
@@ -37,110 +37,142 @@ Vector4_Components :: enum u8 {
a = 3,
}
-scalar_f32_swizzle1 :: proc(f: f32, c0: Scalar_Components) -> f32 {
+@(require_results)
+scalar_f32_swizzle1 :: proc "contextless" (f: f32, c0: Scalar_Components) -> f32 {
return f
}
-scalar_f32_swizzle2 :: proc(f: f32, c0, c1: Scalar_Components) -> Vector2f32 {
+@(require_results)
+scalar_f32_swizzle2 :: proc "contextless" (f: f32, c0, c1: Scalar_Components) -> Vector2f32 {
return {f, f}
}
-scalar_f32_swizzle3 :: proc(f: f32, c0, c1, c2: Scalar_Components) -> Vector3f32 {
+@(require_results)
+scalar_f32_swizzle3 :: proc "contextless" (f: f32, c0, c1, c2: Scalar_Components) -> Vector3f32 {
return {f, f, f}
}
-scalar_f32_swizzle4 :: proc(f: f32, c0, c1, c2, c3: Scalar_Components) -> Vector4f32 {
+@(require_results)
+scalar_f32_swizzle4 :: proc "contextless" (f: f32, c0, c1, c2, c3: Scalar_Components) -> Vector4f32 {
return {f, f, f, f}
}
-vector2f32_swizzle1 :: proc(v: Vector2f32, c0: Vector2_Components) -> f32 {
+@(require_results)
+vector2f32_swizzle1 :: proc "contextless" (v: Vector2f32, c0: Vector2_Components) -> f32 {
return v[c0]
}
-vector2f32_swizzle2 :: proc(v: Vector2f32, c0, c1: Vector2_Components) -> Vector2f32 {
+@(require_results)
+vector2f32_swizzle2 :: proc "contextless" (v: Vector2f32, c0, c1: Vector2_Components) -> Vector2f32 {
return {v[c0], v[c1]}
}
-vector2f32_swizzle3 :: proc(v: Vector2f32, c0, c1, c2: Vector2_Components) -> Vector3f32 {
+@(require_results)
+vector2f32_swizzle3 :: proc "contextless" (v: Vector2f32, c0, c1, c2: Vector2_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]}
}
-vector2f32_swizzle4 :: proc(v: Vector2f32, c0, c1, c2, c3: Vector2_Components) -> Vector4f32 {
+@(require_results)
+vector2f32_swizzle4 :: proc "contextless" (v: Vector2f32, c0, c1, c2, c3: Vector2_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]}
}
-vector3f32_swizzle1 :: proc(v: Vector3f32, c0: Vector3_Components) -> f32 {
+@(require_results)
+vector3f32_swizzle1 :: proc "contextless" (v: Vector3f32, c0: Vector3_Components) -> f32 {
return v[c0]
}
-vector3f32_swizzle2 :: proc(v: Vector3f32, c0, c1: Vector3_Components) -> Vector2f32 {
+@(require_results)
+vector3f32_swizzle2 :: proc "contextless" (v: Vector3f32, c0, c1: Vector3_Components) -> Vector2f32 {
return {v[c0], v[c1]}
}
-vector3f32_swizzle3 :: proc(v: Vector3f32, c0, c1, c2: Vector3_Components) -> Vector3f32 {
+@(require_results)
+vector3f32_swizzle3 :: proc "contextless" (v: Vector3f32, c0, c1, c2: Vector3_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]}
}
-vector3f32_swizzle4 :: proc(v: Vector3f32, c0, c1, c2, c3: Vector3_Components) -> Vector4f32 {
+@(require_results)
+vector3f32_swizzle4 :: proc "contextless" (v: Vector3f32, c0, c1, c2, c3: Vector3_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]}
}
-vector4f32_swizzle1 :: proc(v: Vector4f32, c0: Vector4_Components) -> f32 {
+@(require_results)
+vector4f32_swizzle1 :: proc "contextless" (v: Vector4f32, c0: Vector4_Components) -> f32 {
return v[c0]
}
-vector4f32_swizzle2 :: proc(v: Vector4f32, c0, c1: Vector4_Components) -> Vector2f32 {
+@(require_results)
+vector4f32_swizzle2 :: proc "contextless" (v: Vector4f32, c0, c1: Vector4_Components) -> Vector2f32 {
return {v[c0], v[c1]}
}
-vector4f32_swizzle3 :: proc(v: Vector4f32, c0, c1, c2: Vector4_Components) -> Vector3f32 {
+@(require_results)
+vector4f32_swizzle3 :: proc "contextless" (v: Vector4f32, c0, c1, c2: Vector4_Components) -> Vector3f32 {
return {v[c0], v[c1], v[c2]}
}
-vector4f32_swizzle4 :: proc(v: Vector4f32, c0, c1, c2, c3: Vector4_Components) -> Vector4f32 {
+@(require_results)
+vector4f32_swizzle4 :: proc "contextless" (v: Vector4f32, c0, c1, c2, c3: Vector4_Components) -> Vector4f32 {
return {v[c0], v[c1], v[c2], v[c3]}
}
-scalar_f64_swizzle1 :: proc(f: f64, c0: Scalar_Components) -> f64 {
+@(require_results)
+scalar_f64_swizzle1 :: proc "contextless" (f: f64, c0: Scalar_Components) -> f64 {
return f
}
-scalar_f64_swizzle2 :: proc(f: f64, c0, c1: Scalar_Components) -> Vector2f64 {
+@(require_results)
+scalar_f64_swizzle2 :: proc "contextless" (f: f64, c0, c1: Scalar_Components) -> Vector2f64 {
return {f, f}
}
-scalar_f64_swizzle3 :: proc(f: f64, c0, c1, c2: Scalar_Components) -> Vector3f64 {
+@(require_results)
+scalar_f64_swizzle3 :: proc "contextless" (f: f64, c0, c1, c2: Scalar_Components) -> Vector3f64 {
return {f, f, f}
}
-scalar_f64_swizzle4 :: proc(f: f64, c0, c1, c2, c3: Scalar_Components) -> Vector4f64 {
+@(require_results)
+scalar_f64_swizzle4 :: proc "contextless" (f: f64, c0, c1, c2, c3: Scalar_Components) -> Vector4f64 {
return {f, f, f, f}
}
-vector2f64_swizzle1 :: proc(v: Vector2f64, c0: Vector2_Components) -> f64 {
+@(require_results)
+vector2f64_swizzle1 :: proc "contextless" (v: Vector2f64, c0: Vector2_Components) -> f64 {
return v[c0]
}
-vector2f64_swizzle2 :: proc(v: Vector2f64, c0, c1: Vector2_Components) -> Vector2f64 {
+@(require_results)
+vector2f64_swizzle2 :: proc "contextless" (v: Vector2f64, c0, c1: Vector2_Components) -> Vector2f64 {
return {v[c0], v[c1]}
}
-vector2f64_swizzle3 :: proc(v: Vector2f64, c0, c1, c2: Vector2_Components) -> Vector3f64 {
+@(require_results)
+vector2f64_swizzle3 :: proc "contextless" (v: Vector2f64, c0, c1, c2: Vector2_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]}
}
-vector2f64_swizzle4 :: proc(v: Vector2f64, c0, c1, c2, c3: Vector2_Components) -> Vector4f64 {
+@(require_results)
+vector2f64_swizzle4 :: proc "contextless" (v: Vector2f64, c0, c1, c2, c3: Vector2_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]}
}
-vector3f64_swizzle1 :: proc(v: Vector3f64, c0: Vector3_Components) -> f64 {
+@(require_results)
+vector3f64_swizzle1 :: proc "contextless" (v: Vector3f64, c0: Vector3_Components) -> f64 {
return v[c0]
}
-vector3f64_swizzle2 :: proc(v: Vector3f64, c0, c1: Vector3_Components) -> Vector2f64 {
+@(require_results)
+vector3f64_swizzle2 :: proc "contextless" (v: Vector3f64, c0, c1: Vector3_Components) -> Vector2f64 {
return {v[c0], v[c1]}
}
-vector3f64_swizzle3 :: proc(v: Vector3f64, c0, c1, c2: Vector3_Components) -> Vector3f64 {
+@(require_results)
+vector3f64_swizzle3 :: proc "contextless" (v: Vector3f64, c0, c1, c2: Vector3_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]}
}
-vector3f64_swizzle4 :: proc(v: Vector3f64, c0, c1, c2, c3: Vector3_Components) -> Vector4f64 {
+@(require_results)
+vector3f64_swizzle4 :: proc "contextless" (v: Vector3f64, c0, c1, c2, c3: Vector3_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]}
}
-vector4f64_swizzle1 :: proc(v: Vector4f64, c0: Vector4_Components) -> f64 {
+@(require_results)
+vector4f64_swizzle1 :: proc "contextless" (v: Vector4f64, c0: Vector4_Components) -> f64 {
return v[c0]
}
-vector4f64_swizzle2 :: proc(v: Vector4f64, c0, c1: Vector4_Components) -> Vector2f64 {
+@(require_results)
+vector4f64_swizzle2 :: proc "contextless" (v: Vector4f64, c0, c1: Vector4_Components) -> Vector2f64 {
return {v[c0], v[c1]}
}
-vector4f64_swizzle3 :: proc(v: Vector4f64, c0, c1, c2: Vector4_Components) -> Vector3f64 {
+@(require_results)
+vector4f64_swizzle3 :: proc "contextless" (v: Vector4f64, c0, c1, c2: Vector4_Components) -> Vector3f64 {
return {v[c0], v[c1], v[c2]}
}
-vector4f64_swizzle4 :: proc(v: Vector4f64, c0, c1, c2, c3: Vector4_Components) -> Vector4f64 {
+@(require_results)
+vector4f64_swizzle4 :: proc "contextless" (v: Vector4f64, c0, c1, c2, c3: Vector4_Components) -> Vector4f64 {
return {v[c0], v[c1], v[c2], v[c3]}
}
diff --git a/core/math/math.odin b/core/math/math.odin
index 12fe1bfd7..05177378f 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -42,90 +42,91 @@ min :: builtin.min
max :: builtin.max
clamp :: builtin.clamp
-sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
-sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
-sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }
-sqrt_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(sqrt_f32(f32(x))) }
-sqrt_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(sqrt_f64(f64(x))) }
-sqrt_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(sqrt_f64(f64(x))) }
-sqrt :: proc{
+@(require_results) sqrt_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(sqrt_f16(f16(x))) }
+@(require_results) sqrt_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(sqrt_f16(f16(x))) }
+@(require_results) sqrt_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(sqrt_f32(f32(x))) }
+@(require_results) sqrt_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(sqrt_f32(f32(x))) }
+@(require_results) sqrt_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(sqrt_f64(f64(x))) }
+@(require_results) sqrt_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(sqrt_f64(f64(x))) }
+sqrt :: proc{
sqrt_f16, sqrt_f16le, sqrt_f16be,
sqrt_f32, sqrt_f32le, sqrt_f32be,
sqrt_f64, sqrt_f64le, sqrt_f64be,
}
-sin_f16le :: proc "contextless" (Īø: f16le) -> f16le { return #force_inline f16le(sin_f16(f16(Īø))) }
-sin_f16be :: proc "contextless" (Īø: f16be) -> f16be { return #force_inline f16be(sin_f16(f16(Īø))) }
-sin_f32le :: proc "contextless" (Īø: f32le) -> f32le { return #force_inline f32le(sin_f32(f32(Īø))) }
-sin_f32be :: proc "contextless" (Īø: f32be) -> f32be { return #force_inline f32be(sin_f32(f32(Īø))) }
-sin_f64le :: proc "contextless" (Īø: f64le) -> f64le { return #force_inline f64le(sin_f64(f64(Īø))) }
-sin_f64be :: proc "contextless" (Īø: f64be) -> f64be { return #force_inline f64be(sin_f64(f64(Īø))) }
-sin :: proc{
+@(require_results) sin_f16le :: proc "contextless" (Īø: f16le) -> f16le { return #force_inline f16le(sin_f16(f16(Īø))) }
+@(require_results) sin_f16be :: proc "contextless" (Īø: f16be) -> f16be { return #force_inline f16be(sin_f16(f16(Īø))) }
+@(require_results) sin_f32le :: proc "contextless" (Īø: f32le) -> f32le { return #force_inline f32le(sin_f32(f32(Īø))) }
+@(require_results) sin_f32be :: proc "contextless" (Īø: f32be) -> f32be { return #force_inline f32be(sin_f32(f32(Īø))) }
+@(require_results) sin_f64le :: proc "contextless" (Īø: f64le) -> f64le { return #force_inline f64le(sin_f64(f64(Īø))) }
+@(require_results) sin_f64be :: proc "contextless" (Īø: f64be) -> f64be { return #force_inline f64be(sin_f64(f64(Īø))) }
+sin :: proc{
sin_f16, sin_f16le, sin_f16be,
sin_f32, sin_f32le, sin_f32be,
sin_f64, sin_f64le, sin_f64be,
}
-cos_f16le :: proc "contextless" (Īø: f16le) -> f16le { return #force_inline f16le(cos_f16(f16(Īø))) }
-cos_f16be :: proc "contextless" (Īø: f16be) -> f16be { return #force_inline f16be(cos_f16(f16(Īø))) }
-cos_f32le :: proc "contextless" (Īø: f32le) -> f32le { return #force_inline f32le(cos_f32(f32(Īø))) }
-cos_f32be :: proc "contextless" (Īø: f32be) -> f32be { return #force_inline f32be(cos_f32(f32(Īø))) }
-cos_f64le :: proc "contextless" (Īø: f64le) -> f64le { return #force_inline f64le(cos_f64(f64(Īø))) }
-cos_f64be :: proc "contextless" (Īø: f64be) -> f64be { return #force_inline f64be(cos_f64(f64(Īø))) }
-cos :: proc{
+@(require_results) cos_f16le :: proc "contextless" (Īø: f16le) -> f16le { return #force_inline f16le(cos_f16(f16(Īø))) }
+@(require_results) cos_f16be :: proc "contextless" (Īø: f16be) -> f16be { return #force_inline f16be(cos_f16(f16(Īø))) }
+@(require_results) cos_f32le :: proc "contextless" (Īø: f32le) -> f32le { return #force_inline f32le(cos_f32(f32(Īø))) }
+@(require_results) cos_f32be :: proc "contextless" (Īø: f32be) -> f32be { return #force_inline f32be(cos_f32(f32(Īø))) }
+@(require_results) cos_f64le :: proc "contextless" (Īø: f64le) -> f64le { return #force_inline f64le(cos_f64(f64(Īø))) }
+@(require_results) cos_f64be :: proc "contextless" (Īø: f64be) -> f64be { return #force_inline f64be(cos_f64(f64(Īø))) }
+cos :: proc{
cos_f16, cos_f16le, cos_f16be,
cos_f32, cos_f32le, cos_f32be,
cos_f64, cos_f64le, cos_f64be,
}
-pow_f16le :: proc "contextless" (x, power: f16le) -> f16le { return #force_inline f16le(pow_f16(f16(x), f16(power))) }
-pow_f16be :: proc "contextless" (x, power: f16be) -> f16be { return #force_inline f16be(pow_f16(f16(x), f16(power))) }
-pow_f32le :: proc "contextless" (x, power: f32le) -> f32le { return #force_inline f32le(pow_f32(f32(x), f32(power))) }
-pow_f32be :: proc "contextless" (x, power: f32be) -> f32be { return #force_inline f32be(pow_f32(f32(x), f32(power))) }
-pow_f64le :: proc "contextless" (x, power: f64le) -> f64le { return #force_inline f64le(pow_f64(f64(x), f64(power))) }
-pow_f64be :: proc "contextless" (x, power: f64be) -> f64be { return #force_inline f64be(pow_f64(f64(x), f64(power))) }
-pow :: proc{
+@(require_results) pow_f16le :: proc "contextless" (x, power: f16le) -> f16le { return #force_inline f16le(pow_f16(f16(x), f16(power))) }
+@(require_results) pow_f16be :: proc "contextless" (x, power: f16be) -> f16be { return #force_inline f16be(pow_f16(f16(x), f16(power))) }
+@(require_results) pow_f32le :: proc "contextless" (x, power: f32le) -> f32le { return #force_inline f32le(pow_f32(f32(x), f32(power))) }
+@(require_results) pow_f32be :: proc "contextless" (x, power: f32be) -> f32be { return #force_inline f32be(pow_f32(f32(x), f32(power))) }
+@(require_results) pow_f64le :: proc "contextless" (x, power: f64le) -> f64le { return #force_inline f64le(pow_f64(f64(x), f64(power))) }
+@(require_results) pow_f64be :: proc "contextless" (x, power: f64be) -> f64be { return #force_inline f64be(pow_f64(f64(x), f64(power))) }
+pow :: proc{
pow_f16, pow_f16le, pow_f16be,
pow_f32, pow_f32le, pow_f32be,
pow_f64, pow_f64le, pow_f64be,
}
-fmuladd_f16le :: proc "contextless" (a, b, c: f16le) -> f16le { return #force_inline f16le(fmuladd_f16(f16(a), f16(b), f16(c))) }
-fmuladd_f16be :: proc "contextless" (a, b, c: f16be) -> f16be { return #force_inline f16be(fmuladd_f16(f16(a), f16(b), f16(c))) }
-fmuladd_f32le :: proc "contextless" (a, b, c: f32le) -> f32le { return #force_inline f32le(fmuladd_f32(f32(a), f32(b), f32(c))) }
-fmuladd_f32be :: proc "contextless" (a, b, c: f32be) -> f32be { return #force_inline f32be(fmuladd_f32(f32(a), f32(b), f32(c))) }
-fmuladd_f64le :: proc "contextless" (a, b, c: f64le) -> f64le { return #force_inline f64le(fmuladd_f64(f64(a), f64(b), f64(c))) }
-fmuladd_f64be :: proc "contextless" (a, b, c: f64be) -> f64be { return #force_inline f64be(fmuladd_f64(f64(a), f64(b), f64(c))) }
-fmuladd :: proc{
+@(require_results) fmuladd_f16le :: proc "contextless" (a, b, c: f16le) -> f16le { return #force_inline f16le(fmuladd_f16(f16(a), f16(b), f16(c))) }
+@(require_results) fmuladd_f16be :: proc "contextless" (a, b, c: f16be) -> f16be { return #force_inline f16be(fmuladd_f16(f16(a), f16(b), f16(c))) }
+@(require_results) fmuladd_f32le :: proc "contextless" (a, b, c: f32le) -> f32le { return #force_inline f32le(fmuladd_f32(f32(a), f32(b), f32(c))) }
+@(require_results) fmuladd_f32be :: proc "contextless" (a, b, c: f32be) -> f32be { return #force_inline f32be(fmuladd_f32(f32(a), f32(b), f32(c))) }
+@(require_results) fmuladd_f64le :: proc "contextless" (a, b, c: f64le) -> f64le { return #force_inline f64le(fmuladd_f64(f64(a), f64(b), f64(c))) }
+@(require_results) fmuladd_f64be :: proc "contextless" (a, b, c: f64be) -> f64be { return #force_inline f64be(fmuladd_f64(f64(a), f64(b), f64(c))) }
+fmuladd :: proc{
fmuladd_f16, fmuladd_f16le, fmuladd_f16be,
fmuladd_f32, fmuladd_f32le, fmuladd_f32be,
fmuladd_f64, fmuladd_f64le, fmuladd_f64be,
}
-exp_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(exp_f16(f16(x))) }
-exp_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(exp_f16(f16(x))) }
-exp_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(exp_f32(f32(x))) }
-exp_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(exp_f32(f32(x))) }
-exp_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(exp_f64(f64(x))) }
-exp_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(exp_f64(f64(x))) }
-exp :: proc{
+@(require_results) exp_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(exp_f16(f16(x))) }
+@(require_results) exp_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(exp_f16(f16(x))) }
+@(require_results) exp_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(exp_f32(f32(x))) }
+@(require_results) exp_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(exp_f32(f32(x))) }
+@(require_results) exp_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(exp_f64(f64(x))) }
+@(require_results) exp_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(exp_f64(f64(x))) }
+exp :: proc{
exp_f16, exp_f16le, exp_f16be,
exp_f32, exp_f32le, exp_f32be,
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{
+@(require_results) pow10_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(pow10_f16(f16(x))) }
+@(require_results) pow10_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(pow10_f16(f16(x))) }
+@(require_results) pow10_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(pow10_f32(f32(x))) }
+@(require_results) pow10_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(pow10_f32(f32(x))) }
+@(require_results) pow10_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(pow10_f64(f64(x))) }
+@(require_results) 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,
}
+@(require_results)
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
@static pow10_pos_tab := [?]f16{
1e00, 1e01, 1e02, 1e03, 1e04,
@@ -146,6 +147,7 @@ pow10_f16 :: proc "contextless" (n: f16) -> f16 {
return 0
}
+@(require_results)
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
@static pow10_pos_tab := [?]f32{
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
@@ -173,6 +175,7 @@ pow10_f32 :: proc "contextless" (n: f32) -> f32 {
return 0
}
+@(require_results)
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
@static pow10_tab := [?]f64{
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
@@ -202,6 +205,7 @@ pow10_f64 :: proc "contextless" (n: f64) -> f64 {
+@(require_results)
ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
mask :: F64_MASK
shift :: F64_SHIFT
@@ -236,14 +240,14 @@ ldexp_f64 :: proc "contextless" (val: f64, exp: int) -> f64 {
x |= u64(exp+bias) << shift
return m * transmute(f64)x
}
-ldexp_f16 :: proc "contextless" (val: f16, exp: int) -> f16 { return f16(ldexp_f64(f64(val), exp)) }
-ldexp_f32 :: proc "contextless" (val: f32, exp: int) -> f32 { return f32(ldexp_f64(f64(val), exp)) }
-ldexp_f16le :: proc "contextless" (val: f16le, exp: int) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)) }
-ldexp_f16be :: proc "contextless" (val: f16be, exp: int) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)) }
-ldexp_f32le :: proc "contextless" (val: f32le, exp: int) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)) }
-ldexp_f32be :: proc "contextless" (val: f32be, exp: int) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)) }
-ldexp_f64le :: proc "contextless" (val: f64le, exp: int) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)) }
-ldexp_f64be :: proc "contextless" (val: f64be, exp: int) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)) }
+@(require_results) ldexp_f16 :: proc "contextless" (val: f16, exp: int) -> f16 { return f16(ldexp_f64(f64(val), exp)) }
+@(require_results) ldexp_f32 :: proc "contextless" (val: f32, exp: int) -> f32 { return f32(ldexp_f64(f64(val), exp)) }
+@(require_results) ldexp_f16le :: proc "contextless" (val: f16le, exp: int) -> f16le { return #force_inline f16le(ldexp_f16(f16(val), exp)) }
+@(require_results) ldexp_f16be :: proc "contextless" (val: f16be, exp: int) -> f16be { return #force_inline f16be(ldexp_f16(f16(val), exp)) }
+@(require_results) ldexp_f32le :: proc "contextless" (val: f32le, exp: int) -> f32le { return #force_inline f32le(ldexp_f32(f32(val), exp)) }
+@(require_results) ldexp_f32be :: proc "contextless" (val: f32be, exp: int) -> f32be { return #force_inline f32be(ldexp_f32(f32(val), exp)) }
+@(require_results) ldexp_f64le :: proc "contextless" (val: f64le, exp: int) -> f64le { return #force_inline f64le(ldexp_f64(f64(val), exp)) }
+@(require_results) ldexp_f64be :: proc "contextless" (val: f64be, exp: int) -> f64be { return #force_inline f64be(ldexp_f64(f64(val), exp)) }
// ldexp is the inverse of frexp
// it returns val * 2**exp.
//
@@ -260,82 +264,84 @@ ldexp :: proc{
}
-log_f16 :: proc "contextless" (x, base: f16) -> f16 { return ln(x) / ln(base) }
-log_f16le :: proc "contextless" (x, base: f16le) -> f16le { return f16le(log_f16(f16(x), f16(base))) }
-log_f16be :: proc "contextless" (x, base: f16be) -> f16be { return f16be(log_f16(f16(x), f16(base))) }
+@(require_results) log_f16 :: proc "contextless" (x, base: f16) -> f16 { return ln(x) / ln(base) }
+@(require_results) log_f16le :: proc "contextless" (x, base: f16le) -> f16le { return f16le(log_f16(f16(x), f16(base))) }
+@(require_results) log_f16be :: proc "contextless" (x, base: f16be) -> f16be { return f16be(log_f16(f16(x), f16(base))) }
-log_f32 :: proc "contextless" (x, base: f32) -> f32 { return ln(x) / ln(base) }
-log_f32le :: proc "contextless" (x, base: f32le) -> f32le { return f32le(log_f32(f32(x), f32(base))) }
-log_f32be :: proc "contextless" (x, base: f32be) -> f32be { return f32be(log_f32(f32(x), f32(base))) }
+@(require_results) log_f32 :: proc "contextless" (x, base: f32) -> f32 { return ln(x) / ln(base) }
+@(require_results) log_f32le :: proc "contextless" (x, base: f32le) -> f32le { return f32le(log_f32(f32(x), f32(base))) }
+@(require_results) log_f32be :: proc "contextless" (x, base: f32be) -> f32be { return f32be(log_f32(f32(x), f32(base))) }
-log_f64 :: proc "contextless" (x, base: f64) -> f64 { return ln(x) / ln(base) }
-log_f64le :: proc "contextless" (x, base: f64le) -> f64le { return f64le(log_f64(f64(x), f64(base))) }
-log_f64be :: proc "contextless" (x, base: f64be) -> f64be { return f64be(log_f64(f64(x), f64(base))) }
-log :: proc{
+@(require_results) log_f64 :: proc "contextless" (x, base: f64) -> f64 { return ln(x) / ln(base) }
+@(require_results) log_f64le :: proc "contextless" (x, base: f64le) -> f64le { return f64le(log_f64(f64(x), f64(base))) }
+@(require_results) log_f64be :: proc "contextless" (x, base: f64be) -> f64be { return f64be(log_f64(f64(x), f64(base))) }
+log :: proc{
log_f16, log_f16le, log_f16be,
log_f32, log_f32le, log_f32be,
log_f64, log_f64le, log_f64be,
}
-log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
-log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
-log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
+@(require_results) log2_f16 :: proc "contextless" (x: f16) -> f16 { return log(f16(x), f16(2.0)) }
+@(require_results) log2_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log_f16(f16(x), f16(2.0))) }
+@(require_results) log2_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log_f16(f16(x), f16(2.0))) }
-log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
-log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
-log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
+@(require_results) log2_f32 :: proc "contextless" (x: f32) -> f32 { return log(f32(x), f32(2.0)) }
+@(require_results) log2_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log_f32(f32(x), f32(2.0))) }
+@(require_results) log2_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log_f32(f32(x), f32(2.0))) }
-log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
-log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
-log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
+@(require_results) log2_f64 :: proc "contextless" (x: f64) -> f64 { return log(f64(x), f64(2.0)) }
+@(require_results) log2_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log_f64(f64(x), f64(2.0))) }
+@(require_results) log2_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log_f64(f64(x), f64(2.0))) }
-log2 :: proc{
+log2 :: proc{
log2_f16, log2_f16le, log2_f16be,
log2_f32, log2_f32le, log2_f32be,
log2_f64, log2_f64le, log2_f64be,
}
-log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
-log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
-log10_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log10_f16(f16(x))) }
+@(require_results) log10_f16 :: proc "contextless" (x: f16) -> f16 { return ln(x)/LN10 }
+@(require_results) log10_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log10_f16(f16(x))) }
+@(require_results) log10_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log10_f16(f16(x))) }
-log10_f32 :: proc "contextless" (x: f32) -> f32 { return ln(x)/LN10 }
-log10_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log10_f32(f32(x))) }
-log10_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log10_f32(f32(x))) }
+@(require_results) log10_f32 :: proc "contextless" (x: f32) -> f32 { return ln(x)/LN10 }
+@(require_results) log10_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log10_f32(f32(x))) }
+@(require_results) log10_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log10_f32(f32(x))) }
-log10_f64 :: proc "contextless" (x: f64) -> f64 { return ln(x)/LN10 }
-log10_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log10_f64(f64(x))) }
-log10_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log10_f64(f64(x))) }
-log10 :: proc{
+@(require_results) log10_f64 :: proc "contextless" (x: f64) -> f64 { return ln(x)/LN10 }
+@(require_results) log10_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log10_f64(f64(x))) }
+@(require_results) log10_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log10_f64(f64(x))) }
+log10 :: proc{
log10_f16, log10_f16le, log10_f16be,
log10_f32, log10_f32le, log10_f32be,
log10_f64, log10_f64le, log10_f64be,
}
-tan_f16 :: proc "contextless" (Īø: f16) -> f16 { return sin(Īø)/cos(Īø) }
-tan_f16le :: proc "contextless" (Īø: f16le) -> f16le { return f16le(tan_f16(f16(Īø))) }
-tan_f16be :: proc "contextless" (Īø: f16be) -> f16be { return f16be(tan_f16(f16(Īø))) }
+@(require_results) tan_f16 :: proc "contextless" (Īø: f16) -> f16 { return sin(Īø)/cos(Īø) }
+@(require_results) tan_f16le :: proc "contextless" (Īø: f16le) -> f16le { return f16le(tan_f16(f16(Īø))) }
+@(require_results) tan_f16be :: proc "contextless" (Īø: f16be) -> f16be { return f16be(tan_f16(f16(Īø))) }
-tan_f32 :: proc "contextless" (Īø: f32) -> f32 { return sin(Īø)/cos(Īø) }
-tan_f32le :: proc "contextless" (Īø: f32le) -> f32le { return f32le(tan_f32(f32(Īø))) }
-tan_f32be :: proc "contextless" (Īø: f32be) -> f32be { return f32be(tan_f32(f32(Īø))) }
+@(require_results) tan_f32 :: proc "contextless" (Īø: f32) -> f32 { return sin(Īø)/cos(Īø) }
+@(require_results) tan_f32le :: proc "contextless" (Īø: f32le) -> f32le { return f32le(tan_f32(f32(Īø))) }
+@(require_results) tan_f32be :: proc "contextless" (Īø: f32be) -> f32be { return f32be(tan_f32(f32(Īø))) }
-tan_f64 :: proc "contextless" (Īø: f64) -> f64 { return sin(Īø)/cos(Īø) }
-tan_f64le :: proc "contextless" (Īø: f64le) -> f64le { return f64le(tan_f64(f64(Īø))) }
-tan_f64be :: proc "contextless" (Īø: f64be) -> f64be { return f64be(tan_f64(f64(Īø))) }
-tan :: proc{
+@(require_results) tan_f64 :: proc "contextless" (Īø: f64) -> f64 { return sin(Īø)/cos(Īø) }
+@(require_results) tan_f64le :: proc "contextless" (Īø: f64le) -> f64le { return f64le(tan_f64(f64(Īø))) }
+@(require_results) tan_f64be :: proc "contextless" (Īø: f64be) -> f64be { return f64be(tan_f64(f64(Īø))) }
+tan :: proc{
tan_f16, tan_f16le, tan_f16be,
tan_f32, tan_f32le, tan_f32be,
tan_f64, tan_f64le, tan_f64be,
}
-lerp :: proc "contextless" (a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t }
-saturate :: proc "contextless" (a: $T) -> (x: T) { return clamp(a, 0, 1) }
+@(require_results) lerp :: proc "contextless" (a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t }
+@(require_results) saturate :: proc "contextless" (a: $T) -> (x: T) { return clamp(a, 0, 1) }
-unlerp :: proc "contextless" (a, b, x: $T) -> (t: T) where intrinsics.type_is_float(T), !intrinsics.type_is_array(T) {
+@(require_results)
+unlerp :: proc "contextless" (a, b, x: $T) -> (t: T) where intrinsics.type_is_float(T), !intrinsics.type_is_array(T) {
return (x-a)/(b-a)
}
+@(require_results)
remap :: proc "contextless" (old_value, old_min, old_max, new_min, new_max: $T) -> (x: T) where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
old_range := old_max - old_min
new_range := new_max - new_min
@@ -345,32 +351,39 @@ remap :: proc "contextless" (old_value, old_min, old_max, new_min, new_max: $T)
return ((old_value - old_min) / old_range) * new_range + new_min
}
+@(require_results)
wrap :: proc "contextless" (x, y: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
tmp := mod(x, y)
return y + tmp if tmp < 0 else tmp
}
+@(require_results)
angle_diff :: proc "contextless" (a, b: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
dist := wrap(b - a, TAU)
return wrap(dist*2, TAU) - dist
}
+@(require_results)
angle_lerp :: proc "contextless" (a, b, t: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
return a + angle_diff(a, b) * t
}
+@(require_results)
step :: proc "contextless" (edge, x: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
return 0 if x < edge else 1
}
+@(require_results)
smoothstep :: proc "contextless" (edge0, edge1, x: $T) -> T where intrinsics.type_is_numeric(T), !intrinsics.type_is_array(T) {
t := clamp((x - edge0) / (edge1 - edge0), 0, 1)
return t * t * (3 - 2*t)
}
+@(require_results)
bias :: proc "contextless" (t, b: $T) -> T where intrinsics.type_is_numeric(T) {
return t / (((1/b) - 2) * (1 - t) + 1)
}
+@(require_results)
gain :: proc "contextless" (t, g: $T) -> T where intrinsics.type_is_numeric(T) {
if t < 0.5 {
return bias(t*2, g)*0.5
@@ -379,51 +392,47 @@ gain :: proc "contextless" (t, g: $T) -> T where intrinsics.type_is_numeric(T) {
}
-sign_f16 :: proc "contextless" (x: f16) -> f16 { return f16(int(0 < x) - int(x < 0)) }
-sign_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(int(0 < x) - int(x < 0)) }
-sign_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(int(0 < x) - int(x < 0)) }
-sign_f32 :: proc "contextless" (x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)) }
-sign_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(int(0 < x) - int(x < 0)) }
-sign_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(int(0 < x) - int(x < 0)) }
-sign_f64 :: proc "contextless" (x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)) }
-sign_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(int(0 < x) - int(x < 0)) }
-sign_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(int(0 < x) - int(x < 0)) }
-sign :: proc{
+@(require_results) sign_f16 :: proc "contextless" (x: f16) -> f16 { return f16(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f32 :: proc "contextless" (x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f64 :: proc "contextless" (x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(int(0 < x) - int(x < 0)) }
+@(require_results) sign_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(int(0 < x) - int(x < 0)) }
+sign :: proc{
sign_f16, sign_f16le, sign_f16be,
sign_f32, sign_f32le, sign_f32be,
sign_f64, sign_f64le, sign_f64be,
}
-sign_bit_f16 :: proc "contextless" (x: f16) -> bool {
- return (transmute(u16)x) & (1<<15) != 0
-}
-sign_bit_f16le :: proc "contextless" (x: f16le) -> bool { return #force_inline sign_bit_f16(f16(x)) }
-sign_bit_f16be :: proc "contextless" (x: f16be) -> bool { return #force_inline sign_bit_f16(f16(x)) }
-sign_bit_f32 :: proc "contextless" (x: f32) -> bool {
- return (transmute(u32)x) & (1<<31) != 0
-}
-sign_bit_f32le :: proc "contextless" (x: f32le) -> bool { return #force_inline sign_bit_f32(f32(x)) }
-sign_bit_f32be :: proc "contextless" (x: f32be) -> bool { return #force_inline sign_bit_f32(f32(x)) }
-sign_bit_f64 :: proc "contextless" (x: f64) -> bool {
- return (transmute(u64)x) & (1<<63) != 0
-}
-sign_bit_f64le :: proc "contextless" (x: f64le) -> bool { return #force_inline sign_bit_f64(f64(x)) }
-sign_bit_f64be :: proc "contextless" (x: f64be) -> bool { return #force_inline sign_bit_f64(f64(x)) }
-sign_bit :: proc{
+@(require_results) sign_bit_f16 :: proc "contextless" (x: f16) -> bool { return (transmute(u16)x) & (1<<15) != 0 }
+@(require_results) sign_bit_f16le :: proc "contextless" (x: f16le) -> bool { return #force_inline sign_bit_f16(f16(x)) }
+@(require_results) sign_bit_f16be :: proc "contextless" (x: f16be) -> bool { return #force_inline sign_bit_f16(f16(x)) }
+@(require_results) sign_bit_f32 :: proc "contextless" (x: f32) -> bool { return (transmute(u32)x) & (1<<31) != 0 }
+@(require_results) sign_bit_f32le :: proc "contextless" (x: f32le) -> bool { return #force_inline sign_bit_f32(f32(x)) }
+@(require_results) sign_bit_f32be :: proc "contextless" (x: f32be) -> bool { return #force_inline sign_bit_f32(f32(x)) }
+@(require_results) sign_bit_f64 :: proc "contextless" (x: f64) -> bool { return (transmute(u64)x) & (1<<63) != 0 }
+@(require_results) sign_bit_f64le :: proc "contextless" (x: f64le) -> bool { return #force_inline sign_bit_f64(f64(x)) }
+@(require_results) sign_bit_f64be :: proc "contextless" (x: f64be) -> bool { return #force_inline sign_bit_f64(f64(x)) }
+sign_bit :: proc{
sign_bit_f16, sign_bit_f16le, sign_bit_f16be,
sign_bit_f32, sign_bit_f32le, sign_bit_f32be,
sign_bit_f64, sign_bit_f64le, sign_bit_f64be,
}
-copy_sign_f16 :: proc "contextless" (x, y: f16) -> f16 {
+@(require_results)
+copy_sign_f16 :: proc "contextless" (x, y: f16) -> f16 {
ix := transmute(u16)x
iy := transmute(u16)y
ix &= 0x7fff
ix |= iy & 0x8000
return transmute(f16)ix
}
-copy_sign_f16le :: proc "contextless" (x, y: f16le) -> f16le { return #force_inline f16le(copy_sign_f16(f16(x), f16(y))) }
-copy_sign_f16be :: proc "contextless" (x, y: f16be) -> f16be { return #force_inline f16be(copy_sign_f16(f16(x), f16(y))) }
+@(require_results) copy_sign_f16le :: proc "contextless" (x, y: f16le) -> f16le { return #force_inline f16le(copy_sign_f16(f16(x), f16(y))) }
+@(require_results) copy_sign_f16be :: proc "contextless" (x, y: f16be) -> f16be { return #force_inline f16be(copy_sign_f16(f16(x), f16(y))) }
+@(require_results)
copy_sign_f32 :: proc "contextless" (x, y: f32) -> f32 {
ix := transmute(u32)x
iy := transmute(u32)y
@@ -431,53 +440,55 @@ copy_sign_f32 :: proc "contextless" (x, y: f32) -> f32 {
ix |= iy & 0x8000_0000
return transmute(f32)ix
}
-copy_sign_f32le :: proc "contextless" (x, y: f32le) -> f32le { return #force_inline f32le(copy_sign_f32(f32(x), f32(y))) }
-copy_sign_f32be :: proc "contextless" (x, y: f32be) -> f32be { return #force_inline f32be(copy_sign_f32(f32(x), f32(y))) }
-copy_sign_f64 :: proc "contextless" (x, y: f64) -> f64 {
+@(require_results) copy_sign_f32le :: proc "contextless" (x, y: f32le) -> f32le { return #force_inline f32le(copy_sign_f32(f32(x), f32(y))) }
+@(require_results) copy_sign_f32be :: proc "contextless" (x, y: f32be) -> f32be { return #force_inline f32be(copy_sign_f32(f32(x), f32(y))) }
+@(require_results)
+copy_sign_f64 :: proc "contextless" (x, y: f64) -> f64 {
ix := transmute(u64)x
iy := transmute(u64)y
ix &= 0x7fff_ffff_ffff_ffff
ix |= iy & 0x8000_0000_0000_0000
return transmute(f64)ix
}
-copy_sign_f64le :: proc "contextless" (x, y: f64le) -> f64le { return #force_inline f64le(copy_sign_f64(f64(x), f64(y))) }
-copy_sign_f64be :: proc "contextless" (x, y: f64be) -> f64be { return #force_inline f64be(copy_sign_f64(f64(x), f64(y))) }
-copy_sign :: proc{
+@(require_results) copy_sign_f64le :: proc "contextless" (x, y: f64le) -> f64le { return #force_inline f64le(copy_sign_f64(f64(x), f64(y))) }
+@(require_results) copy_sign_f64be :: proc "contextless" (x, y: f64be) -> f64be { return #force_inline f64be(copy_sign_f64(f64(x), f64(y))) }
+copy_sign :: proc{
copy_sign_f16, copy_sign_f16le, copy_sign_f16be,
copy_sign_f32, copy_sign_f32le, copy_sign_f32be,
copy_sign_f64, copy_sign_f64le, copy_sign_f64be,
}
-to_radians_f16 :: proc "contextless" (degrees: f16) -> f16 { return degrees * RAD_PER_DEG }
-to_radians_f16le :: proc "contextless" (degrees: f16le) -> f16le { return degrees * RAD_PER_DEG }
-to_radians_f16be :: proc "contextless" (degrees: f16be) -> f16be { return degrees * RAD_PER_DEG }
-to_radians_f32 :: proc "contextless" (degrees: f32) -> f32 { return degrees * RAD_PER_DEG }
-to_radians_f32le :: proc "contextless" (degrees: f32le) -> f32le { return degrees * RAD_PER_DEG }
-to_radians_f32be :: proc "contextless" (degrees: f32be) -> f32be { return degrees * RAD_PER_DEG }
-to_radians_f64 :: proc "contextless" (degrees: f64) -> f64 { return degrees * RAD_PER_DEG }
-to_radians_f64le :: proc "contextless" (degrees: f64le) -> f64le { return degrees * RAD_PER_DEG }
-to_radians_f64be :: proc "contextless" (degrees: f64be) -> f64be { return degrees * RAD_PER_DEG }
-to_degrees_f16 :: proc "contextless" (radians: f16) -> f16 { return radians * DEG_PER_RAD }
-to_degrees_f16le :: proc "contextless" (radians: f16le) -> f16le { return radians * DEG_PER_RAD }
-to_degrees_f16be :: proc "contextless" (radians: f16be) -> f16be { return radians * DEG_PER_RAD }
-to_degrees_f32 :: proc "contextless" (radians: f32) -> f32 { return radians * DEG_PER_RAD }
-to_degrees_f32le :: proc "contextless" (radians: f32le) -> f32le { return radians * DEG_PER_RAD }
-to_degrees_f32be :: proc "contextless" (radians: f32be) -> f32be { return radians * DEG_PER_RAD }
-to_degrees_f64 :: proc "contextless" (radians: f64) -> f64 { return radians * DEG_PER_RAD }
-to_degrees_f64le :: proc "contextless" (radians: f64le) -> f64le { return radians * DEG_PER_RAD }
-to_degrees_f64be :: proc "contextless" (radians: f64be) -> f64be { return radians * DEG_PER_RAD }
-to_radians :: proc{
+@(require_results) to_radians_f16 :: proc "contextless" (degrees: f16) -> f16 { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f16le :: proc "contextless" (degrees: f16le) -> f16le { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f16be :: proc "contextless" (degrees: f16be) -> f16be { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f32 :: proc "contextless" (degrees: f32) -> f32 { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f32le :: proc "contextless" (degrees: f32le) -> f32le { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f32be :: proc "contextless" (degrees: f32be) -> f32be { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f64 :: proc "contextless" (degrees: f64) -> f64 { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f64le :: proc "contextless" (degrees: f64le) -> f64le { return degrees * RAD_PER_DEG }
+@(require_results) to_radians_f64be :: proc "contextless" (degrees: f64be) -> f64be { return degrees * RAD_PER_DEG }
+@(require_results) to_degrees_f16 :: proc "contextless" (radians: f16) -> f16 { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f16le :: proc "contextless" (radians: f16le) -> f16le { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f16be :: proc "contextless" (radians: f16be) -> f16be { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f32 :: proc "contextless" (radians: f32) -> f32 { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f32le :: proc "contextless" (radians: f32le) -> f32le { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f32be :: proc "contextless" (radians: f32be) -> f32be { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f64 :: proc "contextless" (radians: f64) -> f64 { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f64le :: proc "contextless" (radians: f64le) -> f64le { return radians * DEG_PER_RAD }
+@(require_results) to_degrees_f64be :: proc "contextless" (radians: f64be) -> f64be { return radians * DEG_PER_RAD }
+to_radians :: proc{
to_radians_f16, to_radians_f16le, to_radians_f16be,
to_radians_f32, to_radians_f32le, to_radians_f32be,
to_radians_f64, to_radians_f64le, to_radians_f64be,
}
-to_degrees :: proc{
+to_degrees :: proc{
to_degrees_f16, to_degrees_f16le, to_degrees_f16be,
to_degrees_f32, to_degrees_f32le, to_degrees_f32be,
to_degrees_f64, to_degrees_f64le, to_degrees_f64be,
}
-trunc_f16 :: proc "contextless" (x: f16) -> f16 {
+@(require_results)
+trunc_f16 :: proc "contextless" (x: f16) -> f16 {
trunc_internal :: proc "contextless" (f: f16) -> f16 {
mask :: F16_MASK
shift :: F16_SHIFT
@@ -506,10 +517,11 @@ trunc_f16 :: proc "contextless" (x: f16) -> f16 {
}
return trunc_internal(x)
}
-trunc_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(trunc_f16(f16(x))) }
-trunc_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(trunc_f16(f16(x))) }
+@(require_results) trunc_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(trunc_f16(f16(x))) }
+@(require_results) trunc_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(trunc_f16(f16(x))) }
-trunc_f32 :: proc "contextless" (x: f32) -> f32 {
+@(require_results)
+trunc_f32 :: proc "contextless" (x: f32) -> f32 {
trunc_internal :: proc "contextless" (f: f32) -> f32 {
mask :: F32_MASK
shift :: F32_SHIFT
@@ -538,10 +550,11 @@ trunc_f32 :: proc "contextless" (x: f32) -> f32 {
}
return trunc_internal(x)
}
-trunc_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(trunc_f32(f32(x))) }
-trunc_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(trunc_f32(f32(x))) }
+@(require_results) trunc_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(trunc_f32(f32(x))) }
+@(require_results) trunc_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(trunc_f32(f32(x))) }
-trunc_f64 :: proc "contextless" (x: f64) -> f64 {
+@(require_results)
+trunc_f64 :: proc "contextless" (x: f64) -> f64 {
trunc_internal :: proc "contextless" (f: f64) -> f64 {
mask :: F64_MASK
shift :: F64_SHIFT
@@ -570,68 +583,78 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
}
return trunc_internal(x)
}
-trunc_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))) }
-trunc_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))) }
+@(require_results) trunc_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(trunc_f64(f64(x))) }
+@(require_results) trunc_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(trunc_f64(f64(x))) }
// Removes the fractional part of the value, i.e. rounds towards zero.
-trunc :: proc{
+trunc :: proc{
trunc_f16, trunc_f16le, trunc_f16be,
trunc_f32, trunc_f32le, trunc_f32be,
trunc_f64, trunc_f64le, trunc_f64be,
}
+@(require_results)
round_f16 :: proc "contextless" (x: f16) -> f16 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f16le :: proc "contextless" (x: f16le) -> f16le {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f16be :: proc "contextless" (x: f16be) -> f16be {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f32 :: proc "contextless" (x: f32) -> f32 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f32le :: proc "contextless" (x: f32le) -> f32le {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f32be :: proc "contextless" (x: f32be) -> f32be {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f64 :: proc "contextless" (x: f64) -> f64 {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f64le :: proc "contextless" (x: f64le) -> f64le {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
+@(require_results)
round_f64be :: proc "contextless" (x: f64be) -> f64be {
return ceil(x - 0.5) if x < 0 else floor(x + 0.5)
}
-round :: proc{
+round :: proc{
round_f16, round_f16le, round_f16be,
round_f32, round_f32le, round_f32be,
round_f64, round_f64le, round_f64be,
}
-ceil_f16 :: proc "contextless" (x: f16) -> f16 { return -floor(-x) }
-ceil_f16le :: proc "contextless" (x: f16le) -> f16le { return -floor(-x) }
-ceil_f16be :: proc "contextless" (x: f16be) -> f16be { return -floor(-x) }
+@(require_results) ceil_f16 :: proc "contextless" (x: f16) -> f16 { return -floor(-x) }
+@(require_results) ceil_f16le :: proc "contextless" (x: f16le) -> f16le { return -floor(-x) }
+@(require_results) ceil_f16be :: proc "contextless" (x: f16be) -> f16be { return -floor(-x) }
-ceil_f32 :: proc "contextless" (x: f32) -> f32 { return -floor(-x) }
-ceil_f32le :: proc "contextless" (x: f32le) -> f32le { return -floor(-x) }
-ceil_f32be :: proc "contextless" (x: f32be) -> f32be { return -floor(-x) }
+@(require_results) ceil_f32 :: proc "contextless" (x: f32) -> f32 { return -floor(-x) }
+@(require_results) ceil_f32le :: proc "contextless" (x: f32le) -> f32le { return -floor(-x) }
+@(require_results) ceil_f32be :: proc "contextless" (x: f32be) -> f32be { return -floor(-x) }
-ceil_f64 :: proc "contextless" (x: f64) -> f64 { return -floor(-x) }
-ceil_f64le :: proc "contextless" (x: f64le) -> f64le { return -floor(-x) }
-ceil_f64be :: proc "contextless" (x: f64be) -> f64be { return -floor(-x) }
+@(require_results) ceil_f64 :: proc "contextless" (x: f64) -> f64 { return -floor(-x) }
+@(require_results) ceil_f64le :: proc "contextless" (x: f64le) -> f64le { return -floor(-x) }
+@(require_results) ceil_f64be :: proc "contextless" (x: f64be) -> f64be { return -floor(-x) }
-ceil :: proc{
+ceil :: proc{
ceil_f16, ceil_f16le, ceil_f16be,
ceil_f32, ceil_f32le, ceil_f32be,
ceil_f64, ceil_f64le, ceil_f64be,
}
+@(require_results)
floor_f16 :: proc "contextless" (x: f16) -> f16 {
if x == 0 || is_nan(x) || is_inf(x) {
return x
@@ -646,9 +669,10 @@ floor_f16 :: proc "contextless" (x: f16) -> f16 {
d, _ := modf(x)
return d
}
-floor_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(floor_f16(f16(x))) }
-floor_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(floor_f16(f16(x))) }
-floor_f32 :: proc "contextless" (x: f32) -> f32 {
+@(require_results) floor_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(floor_f16(f16(x))) }
+@(require_results) floor_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(floor_f16(f16(x))) }
+@(require_results)
+floor_f32 :: proc "contextless" (x: f32) -> f32 {
if x == 0 || is_nan(x) || is_inf(x) {
return x
}
@@ -662,8 +686,9 @@ floor_f32 :: proc "contextless" (x: f32) -> f32 {
d, _ := modf(x)
return d
}
-floor_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(floor_f32(f32(x))) }
-floor_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(floor_f32(f32(x))) }
+@(require_results) floor_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(floor_f32(f32(x))) }
+@(require_results) floor_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(floor_f32(f32(x))) }
+@(require_results)
floor_f64 :: proc "contextless" (x: f64) -> f64 {
if x == 0 || is_nan(x) || is_inf(x) {
return x
@@ -678,15 +703,16 @@ floor_f64 :: proc "contextless" (x: f64) -> f64 {
d, _ := modf(x)
return d
}
-floor_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(floor_f64(f64(x))) }
-floor_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(floor_f64(f64(x))) }
-floor :: proc{
+@(require_results) floor_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(floor_f64(f64(x))) }
+@(require_results) floor_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(floor_f64(f64(x))) }
+floor :: proc{
floor_f16, floor_f16le, floor_f16be,
floor_f32, floor_f32le, floor_f32be,
floor_f64, floor_f64le, floor_f64be,
}
+@(require_results)
floor_div :: proc "contextless" (x, y: $T) -> T
where intrinsics.type_is_integer(T) {
a := x / y
@@ -697,6 +723,7 @@ floor_div :: proc "contextless" (x, y: $T) -> T
return a
}
+@(require_results)
floor_mod :: proc "contextless" (x, y: $T) -> T
where intrinsics.type_is_integer(T) {
r := x % y
@@ -706,6 +733,7 @@ floor_mod :: proc "contextless" (x, y: $T) -> T
return r
}
+@(require_results)
divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
where intrinsics.type_is_integer(T) {
div = x / y
@@ -713,6 +741,7 @@ divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
return
}
+@(require_results)
floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
where intrinsics.type_is_integer(T) {
div = x / y
@@ -725,6 +754,7 @@ floor_divmod :: #force_inline proc "contextless" (x, y: $T) -> (div, mod: T)
}
+@(require_results)
modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
shift :: F16_SHIFT
mask :: F16_MASK
@@ -751,15 +781,18 @@ modf_f16 :: proc "contextless" (x: f16) -> (int: f16, frac: f16) {
frac = x - int
return
}
+@(require_results)
modf_f16le :: proc "contextless" (x: f16le) -> (int: f16le, frac: f16le) {
i, f := #force_inline modf_f16(f16(x))
return f16le(i), f16le(f)
}
+@(require_results)
modf_f16be :: proc "contextless" (x: f16be) -> (int: f16be, frac: f16be) {
i, f := #force_inline modf_f16(f16(x))
return f16be(i), f16be(f)
}
-modf_f32 :: proc "contextless" (x: f32) -> (int: f32, frac: f32) {
+@(require_results)
+modf_f32 :: proc "contextless" (x: f32) -> (int: f32, frac: f32) {
shift :: F32_SHIFT
mask :: F32_MASK
bias :: F32_BIAS
@@ -785,14 +818,17 @@ modf_f32 :: proc "contextless" (x: f32) -> (int: f32, frac: f32) {
frac = x - int
return
}
+@(require_results)
modf_f32le :: proc "contextless" (x: f32le) -> (int: f32le, frac: f32le) {
i, f := #force_inline modf_f32(f32(x))
return f32le(i), f32le(f)
}
+@(require_results)
modf_f32be :: proc "contextless" (x: f32be) -> (int: f32be, frac: f32be) {
i, f := #force_inline modf_f32(f32(x))
return f32be(i), f32be(f)
}
+@(require_results)
modf_f64 :: proc "contextless" (x: f64) -> (int: f64, frac: f64) {
shift :: F64_SHIFT
mask :: F64_MASK
@@ -819,10 +855,12 @@ modf_f64 :: proc "contextless" (x: f64) -> (int: f64, frac: f64) {
frac = x - int
return
}
+@(require_results)
modf_f64le :: proc "contextless" (x: f64le) -> (int: f64le, frac: f64le) {
i, f := #force_inline modf_f64(f64(x))
return f64le(i), f64le(f)
}
+@(require_results)
modf_f64be :: proc "contextless" (x: f64be) -> (int: f64be, frac: f64be) {
i, f := #force_inline modf_f64(f64(x))
return f64be(i), f64be(f)
@@ -834,7 +872,8 @@ modf :: proc{
}
split_decimal :: modf
-mod_f16 :: proc "contextless" (x, y: f16) -> (n: f16) {
+@(require_results)
+mod_f16 :: proc "contextless" (x, y: f16) -> (n: f16) {
z := abs(y)
n = remainder(abs(x), z)
if sign(n) < 0 {
@@ -842,9 +881,10 @@ mod_f16 :: proc "contextless" (x, y: f16) -> (n: f16) {
}
return copy_sign(n, x)
}
-mod_f16le :: proc "contextless" (x, y: f16le) -> (n: f16le) { return #force_inline f16le(mod_f16(f16(x), f16(y))) }
-mod_f16be :: proc "contextless" (x, y: f16be) -> (n: f16be) { return #force_inline f16be(mod_f16(f16(x), f16(y))) }
-mod_f32 :: proc "contextless" (x, y: f32) -> (n: f32) {
+@(require_results) mod_f16le :: proc "contextless" (x, y: f16le) -> (n: f16le) { return #force_inline f16le(mod_f16(f16(x), f16(y))) }
+@(require_results) mod_f16be :: proc "contextless" (x, y: f16be) -> (n: f16be) { return #force_inline f16be(mod_f16(f16(x), f16(y))) }
+@(require_results)
+mod_f32 :: proc "contextless" (x, y: f32) -> (n: f32) {
z := abs(y)
n = remainder(abs(x), z)
if sign(n) < 0 {
@@ -852,9 +892,12 @@ mod_f32 :: proc "contextless" (x, y: f32) -> (n: f32) {
}
return copy_sign(n, x)
}
+@(require_results)
mod_f32le :: proc "contextless" (x, y: f32le) -> (n: f32le) { return #force_inline f32le(mod_f32(f32(x), f32(y))) }
+@(require_results)
mod_f32be :: proc "contextless" (x, y: f32be) -> (n: f32be) { return #force_inline f32be(mod_f32(f32(x), f32(y))) }
-mod_f64 :: proc "contextless" (x, y: f64) -> (n: f64) {
+@(require_results)
+mod_f64 :: proc "contextless" (x, y: f64) -> (n: f64) {
z := abs(y)
n = remainder(abs(x), z)
if sign(n) < 0 {
@@ -862,29 +905,32 @@ mod_f64 :: proc "contextless" (x, y: f64) -> (n: f64) {
}
return copy_sign(n, x)
}
+@(require_results)
mod_f64le :: proc "contextless" (x, y: f64le) -> (n: f64le) { return #force_inline f64le(mod_f64(f64(x), f64(y))) }
+@(require_results)
mod_f64be :: proc "contextless" (x, y: f64be) -> (n: f64be) { return #force_inline f64be(mod_f64(f64(x), f64(y))) }
-mod :: proc{
+mod :: proc{
mod_f16, mod_f16le, mod_f16be,
mod_f32, mod_f32le, mod_f32be,
mod_f64, mod_f64le, mod_f64be,
}
-remainder_f16 :: proc "contextless" (x, y: f16 ) -> f16 { return x - round(x/y) * y }
-remainder_f16le :: proc "contextless" (x, y: f16le) -> f16le { return x - round(x/y) * y }
-remainder_f16be :: proc "contextless" (x, y: f16be) -> f16be { return x - round(x/y) * y }
-remainder_f32 :: proc "contextless" (x, y: f32 ) -> f32 { return x - round(x/y) * y }
-remainder_f32le :: proc "contextless" (x, y: f32le) -> f32le { return x - round(x/y) * y }
-remainder_f32be :: proc "contextless" (x, y: f32be) -> f32be { return x - round(x/y) * y }
-remainder_f64 :: proc "contextless" (x, y: f64 ) -> f64 { return x - round(x/y) * y }
-remainder_f64le :: proc "contextless" (x, y: f64le) -> f64le { return x - round(x/y) * y }
-remainder_f64be :: proc "contextless" (x, y: f64be) -> f64be { return x - round(x/y) * y }
-remainder :: proc{
+@(require_results) remainder_f16 :: proc "contextless" (x, y: f16 ) -> f16 { return x - round(x/y) * y }
+@(require_results) remainder_f16le :: proc "contextless" (x, y: f16le) -> f16le { return x - round(x/y) * y }
+@(require_results) remainder_f16be :: proc "contextless" (x, y: f16be) -> f16be { return x - round(x/y) * y }
+@(require_results) remainder_f32 :: proc "contextless" (x, y: f32 ) -> f32 { return x - round(x/y) * y }
+@(require_results) remainder_f32le :: proc "contextless" (x, y: f32le) -> f32le { return x - round(x/y) * y }
+@(require_results) remainder_f32be :: proc "contextless" (x, y: f32be) -> f32be { return x - round(x/y) * y }
+@(require_results) remainder_f64 :: proc "contextless" (x, y: f64 ) -> f64 { return x - round(x/y) * y }
+@(require_results) remainder_f64le :: proc "contextless" (x, y: f64le) -> f64le { return x - round(x/y) * y }
+@(require_results) remainder_f64be :: proc "contextless" (x, y: f64be) -> f64be { return x - round(x/y) * y }
+remainder :: proc{
remainder_f16, remainder_f16le, remainder_f16be,
remainder_f32, remainder_f32le, remainder_f32be,
remainder_f64, remainder_f64le, remainder_f64be,
}
+@(require_results)
gcd :: proc "contextless" (x, y: $T) -> T
where intrinsics.type_is_ordered_numeric(T) {
x, y := x, y
@@ -895,23 +941,27 @@ gcd :: proc "contextless" (x, y: $T) -> T
return abs(x)
}
+@(require_results)
lcm :: proc "contextless" (x, y: $T) -> T
where intrinsics.type_is_ordered_numeric(T) {
return x / gcd(x, y) * y
}
+@(require_results)
normalize_f16 :: proc "contextless" (x: f16) -> (y: f16, exponent: int) {
if abs(x) < F16_MIN {
return x * (1< (y: f32, exponent: int) {
if abs(x) < F32_MIN {
return x * (1< (y: f64, exponent: int) {
if abs(x) < F64_MIN {
return x * (1< (y: f64, exponent: int) {
return x, 0
}
-normalize_f16le :: proc "contextless" (x: f16le) -> (y: f16le, exponent: int) { y0, e := normalize_f16(f16(x)); return f16le(y0), e }
-normalize_f16be :: proc "contextless" (x: f16be) -> (y: f16be, exponent: int) { y0, e := normalize_f16(f16(x)); return f16be(y0), e }
-normalize_f32le :: proc "contextless" (x: f32le) -> (y: f32le, exponent: int) { y0, e := normalize_f32(f32(x)); return f32le(y0), e }
-normalize_f32be :: proc "contextless" (x: f32be) -> (y: f32be, exponent: int) { y0, e := normalize_f32(f32(x)); return f32be(y0), e }
-normalize_f64le :: proc "contextless" (x: f64le) -> (y: f64le, exponent: int) { y0, e := normalize_f64(f64(x)); return f64le(y0), e }
-normalize_f64be :: proc "contextless" (x: f64be) -> (y: f64be, exponent: int) { y0, e := normalize_f64(f64(x)); return f64be(y0), e }
+@(require_results) normalize_f16le :: proc "contextless" (x: f16le) -> (y: f16le, exponent: int) { y0, e := normalize_f16(f16(x)); return f16le(y0), e }
+@(require_results) normalize_f16be :: proc "contextless" (x: f16be) -> (y: f16be, exponent: int) { y0, e := normalize_f16(f16(x)); return f16be(y0), e }
+@(require_results) normalize_f32le :: proc "contextless" (x: f32le) -> (y: f32le, exponent: int) { y0, e := normalize_f32(f32(x)); return f32le(y0), e }
+@(require_results) normalize_f32be :: proc "contextless" (x: f32be) -> (y: f32be, exponent: int) { y0, e := normalize_f32(f32(x)); return f32be(y0), e }
+@(require_results) normalize_f64le :: proc "contextless" (x: f64le) -> (y: f64le, exponent: int) { y0, e := normalize_f64(f64(x)); return f64le(y0), e }
+@(require_results) normalize_f64be :: proc "contextless" (x: f64be) -> (y: f64be, exponent: int) { y0, e := normalize_f64(f64(x)); return f64be(y0), e }
normalize :: proc{
normalize_f16,
@@ -938,30 +988,37 @@ normalize :: proc{
normalize_f64be,
}
+@(require_results)
frexp_f16 :: proc "contextless" (x: f16) -> (significand: f16, exponent: int) {
f, e := frexp_f64(f64(x))
return f16(f), e
}
+@(require_results)
frexp_f16le :: proc "contextless" (x: f16le) -> (significand: f16le, exponent: int) {
f, e := frexp_f64(f64(x))
return f16le(f), e
}
+@(require_results)
frexp_f16be :: proc "contextless" (x: f16be) -> (significand: f16be, exponent: int) {
f, e := frexp_f64(f64(x))
return f16be(f), e
}
+@(require_results)
frexp_f32 :: proc "contextless" (x: f32) -> (significand: f32, exponent: int) {
f, e := frexp_f64(f64(x))
return f32(f), e
}
+@(require_results)
frexp_f32le :: proc "contextless" (x: f32le) -> (significand: f32le, exponent: int) {
f, e := frexp_f64(f64(x))
return f32le(f), e
}
+@(require_results)
frexp_f32be :: proc "contextless" (x: f32be) -> (significand: f32be, exponent: int) {
f, e := frexp_f64(f64(x))
return f32be(f), e
}
+@(require_results)
frexp_f64 :: proc "contextless" (f: f64) -> (significand: f64, exponent: int) {
mask :: F64_MASK
shift :: F64_SHIFT
@@ -983,10 +1040,12 @@ frexp_f64 :: proc "contextless" (f: f64) -> (significand: f64, exponent: int) {
significand = transmute(f64)x
return
}
+@(require_results)
frexp_f64le :: proc "contextless" (x: f64le) -> (significand: f64le, exponent: int) {
f, e := frexp_f64(f64(x))
return f64le(f), e
}
+@(require_results)
frexp_f64be :: proc "contextless" (x: f64be) -> (significand: f64be, exponent: int) {
f, e := frexp_f64(f64(x))
return f64be(f), e
@@ -1011,6 +1070,7 @@ frexp :: proc{
+@(require_results)
binomial :: proc "contextless" (n, k: int) -> int {
switch {
case k <= 0: return 1
@@ -1024,6 +1084,7 @@ binomial :: proc "contextless" (n, k: int) -> int {
return b
}
+@(require_results)
factorial :: proc "contextless" (n: int) -> int {
when size_of(int) == size_of(i64) {
@static table := [21]int{
@@ -1069,7 +1130,8 @@ factorial :: proc "contextless" (n: int) -> int {
return table[n]
}
-classify_f16 :: proc "contextless" (x: f16) -> Float_Class {
+@(require_results)
+classify_f16 :: proc "contextless" (x: f16) -> Float_Class {
switch {
case x == 0:
i := transmute(i16)x
@@ -1093,8 +1155,9 @@ classify_f16 :: proc "contextless" (x: f16) -> Float_Class {
}
return .Normal
}
-classify_f16le :: proc "contextless" (x: f16le) -> Float_Class { return #force_inline classify_f16(f16(x)) }
-classify_f16be :: proc "contextless" (x: f16be) -> Float_Class { return #force_inline classify_f16(f16(x)) }
+@(require_results) classify_f16le :: proc "contextless" (x: f16le) -> Float_Class { return #force_inline classify_f16(f16(x)) }
+@(require_results) classify_f16be :: proc "contextless" (x: f16be) -> Float_Class { return #force_inline classify_f16(f16(x)) }
+@(require_results)
classify_f32 :: proc "contextless" (x: f32) -> Float_Class {
switch {
case x == 0:
@@ -1119,8 +1182,9 @@ classify_f32 :: proc "contextless" (x: f32) -> Float_Class {
}
return .Normal
}
-classify_f32le :: proc "contextless" (x: f32le) -> Float_Class { return #force_inline classify_f32(f32(x)) }
-classify_f32be :: proc "contextless" (x: f32be) -> Float_Class { return #force_inline classify_f32(f32(x)) }
+@(require_results) classify_f32le :: proc "contextless" (x: f32le) -> Float_Class { return #force_inline classify_f32(f32(x)) }
+@(require_results) classify_f32be :: proc "contextless" (x: f32be) -> Float_Class { return #force_inline classify_f32(f32(x)) }
+@(require_results)
classify_f64 :: proc "contextless" (x: f64) -> Float_Class {
switch {
case x == 0:
@@ -1144,26 +1208,26 @@ classify_f64 :: proc "contextless" (x: f64) -> Float_Class {
}
return .Normal
}
-classify_f64le :: proc "contextless" (x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)) }
-classify_f64be :: proc "contextless" (x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)) }
+@(require_results) classify_f64le :: proc "contextless" (x: f64le) -> Float_Class { return #force_inline classify_f64(f64(x)) }
+@(require_results) classify_f64be :: proc "contextless" (x: f64be) -> Float_Class { return #force_inline classify_f64(f64(x)) }
// Returns the `Float_Class` of the value, i.e. whether normal, subnormal, zero, negative zero, NaN, infinity or
// negative infinity.
-classify :: proc{
+classify :: proc{
classify_f16, classify_f16le, classify_f16be,
classify_f32, classify_f32le, classify_f32be,
classify_f64, classify_f64le, classify_f64be,
}
-is_nan_f16 :: proc "contextless" (x: f16) -> bool { return classify(x) == .NaN }
-is_nan_f16le :: proc "contextless" (x: f16le) -> bool { return classify(x) == .NaN }
-is_nan_f16be :: proc "contextless" (x: f16be) -> bool { return classify(x) == .NaN }
-is_nan_f32 :: proc "contextless" (x: f32) -> bool { return classify(x) == .NaN }
-is_nan_f32le :: proc "contextless" (x: f32le) -> bool { return classify(x) == .NaN }
-is_nan_f32be :: proc "contextless" (x: f32be) -> bool { return classify(x) == .NaN }
-is_nan_f64 :: proc "contextless" (x: f64) -> bool { return classify(x) == .NaN }
-is_nan_f64le :: proc "contextless" (x: f64le) -> bool { return classify(x) == .NaN }
-is_nan_f64be :: proc "contextless" (x: f64be) -> bool { return classify(x) == .NaN }
-is_nan :: proc{
+@(require_results) is_nan_f16 :: proc "contextless" (x: f16) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f16le :: proc "contextless" (x: f16le) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f16be :: proc "contextless" (x: f16be) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f32 :: proc "contextless" (x: f32) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f32le :: proc "contextless" (x: f32le) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f32be :: proc "contextless" (x: f32be) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f64 :: proc "contextless" (x: f64) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f64le :: proc "contextless" (x: f64le) -> bool { return classify(x) == .NaN }
+@(require_results) is_nan_f64be :: proc "contextless" (x: f64be) -> bool { return classify(x) == .NaN }
+is_nan :: proc{
is_nan_f16, is_nan_f16le, is_nan_f16be,
is_nan_f32, is_nan_f32le, is_nan_f32be,
is_nan_f64, is_nan_f64le, is_nan_f64be,
@@ -1173,6 +1237,7 @@ is_nan :: proc{
// If sign > 0, is_inf reports whether f is positive infinity.
// If sign < 0, is_inf reports whether f is negative infinity.
// If sign == 0, is_inf reports whether f is either infinity.
+@(require_results)
is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
class := classify(x)
switch {
@@ -1183,13 +1248,16 @@ is_inf_f16 :: proc "contextless" (x: f16, sign: int = 0) -> bool {
}
return class == .Inf || class == .Neg_Inf
}
+@(require_results)
is_inf_f16le :: proc "contextless" (x: f16le, sign: int = 0) -> bool {
return #force_inline is_inf_f16(f16(x), sign)
}
+@(require_results)
is_inf_f16be :: proc "contextless" (x: f16be, sign: int = 0) -> bool {
return #force_inline is_inf_f16(f16(x), sign)
}
+@(require_results)
is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
class := classify(x)
switch {
@@ -1200,13 +1268,16 @@ is_inf_f32 :: proc "contextless" (x: f32, sign: int = 0) -> bool {
}
return class == .Inf || class == .Neg_Inf
}
+@(require_results)
is_inf_f32le :: proc "contextless" (x: f32le, sign: int = 0) -> bool {
return #force_inline is_inf_f32(f32(x), sign)
}
+@(require_results)
is_inf_f32be :: proc "contextless" (x: f32be, sign: int = 0) -> bool {
return #force_inline is_inf_f32(f32(x), sign)
}
+@(require_results)
is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
class := classify(x)
switch {
@@ -1217,9 +1288,11 @@ is_inf_f64 :: proc "contextless" (x: f64, sign: int = 0) -> bool {
}
return class == .Inf || class == .Neg_Inf
}
+@(require_results)
is_inf_f64le :: proc "contextless" (x: f64le, sign: int = 0) -> bool {
return #force_inline is_inf_f64(f64(x), sign)
}
+@(require_results)
is_inf_f64be :: proc "contextless" (x: f64be, sign: int = 0) -> bool {
return #force_inline is_inf_f64(f64(x), sign)
}
@@ -1229,24 +1302,31 @@ is_inf :: proc{
is_inf_f64, is_inf_f64le, is_inf_f64be,
}
+@(require_results)
inf_f16 :: proc "contextless" (sign: int) -> f16 {
return f16(inf_f64(sign))
}
+@(require_results)
inf_f16le :: proc "contextless" (sign: int) -> f16le {
return f16le(inf_f64(sign))
}
+@(require_results)
inf_f16be :: proc "contextless" (sign: int) -> f16be {
return f16be(inf_f64(sign))
}
+@(require_results)
inf_f32 :: proc "contextless" (sign: int) -> f32 {
return f32(inf_f64(sign))
}
+@(require_results)
inf_f32le :: proc "contextless" (sign: int) -> f32le {
return f32le(inf_f64(sign))
}
+@(require_results)
inf_f32be :: proc "contextless" (sign: int) -> f32be {
return f32be(inf_f64(sign))
}
+@(require_results)
inf_f64 :: proc "contextless" (sign: int) -> f64 {
if sign >= 0 {
return 0h7ff00000_00000000
@@ -1254,45 +1334,58 @@ inf_f64 :: proc "contextless" (sign: int) -> f64 {
return 0hfff00000_00000000
}
}
+@(require_results)
inf_f64le :: proc "contextless" (sign: int) -> f64le {
return f64le(inf_f64(sign))
}
+@(require_results)
inf_f64be :: proc "contextless" (sign: int) -> f64be {
return f64be(inf_f64(sign))
}
+@(require_results)
nan_f16 :: proc "contextless" () -> f16 {
return f16(nan_f64())
}
+@(require_results)
nan_f16le :: proc "contextless" () -> f16le {
return f16le(nan_f64())
}
+@(require_results)
nan_f16be :: proc "contextless" () -> f16be {
return f16be(nan_f64())
}
+@(require_results)
nan_f32 :: proc "contextless" () -> f32 {
return f32(nan_f64())
}
+@(require_results)
nan_f32le :: proc "contextless" () -> f32le {
return f32le(nan_f64())
}
+@(require_results)
nan_f32be :: proc "contextless" () -> f32be {
return f32be(nan_f64())
}
+@(require_results)
nan_f64 :: proc "contextless" () -> f64 {
return 0h7ff80000_00000001
}
+@(require_results)
nan_f64le :: proc "contextless" () -> f64le {
return f64le(nan_f64())
}
+@(require_results)
nan_f64be :: proc "contextless" () -> f64be {
return f64be(nan_f64())
}
+@(require_results)
is_power_of_two :: proc "contextless" (x: int) -> bool {
return x > 0 && (x & (x-1)) == 0
}
+@(require_results)
next_power_of_two :: proc "contextless" (x: int) -> int {
k := x -1
when size_of(int) == 8 {
@@ -1307,6 +1400,7 @@ next_power_of_two :: proc "contextless" (x: int) -> int {
return k
}
+@(require_results)
sum :: proc "contextless" (x: $T/[]$E) -> (res: E)
where intrinsics.type_is_numeric(E) {
for i in x {
@@ -1315,6 +1409,7 @@ sum :: proc "contextless" (x: $T/[]$E) -> (res: E)
return
}
+@(require_results)
prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
where intrinsics.type_is_numeric(E) {
res = 1
@@ -1332,6 +1427,7 @@ cumsum_inplace :: proc "contextless" (x: $T/[]$E)
}
+@(require_results)
cumsum :: proc "contextless" (dst, src: $T/[]$E) -> T
where intrinsics.type_is_numeric(E) {
N := min(len(dst), len(src))
@@ -1345,31 +1441,38 @@ cumsum :: proc "contextless" (dst, src: $T/[]$E) -> T
}
+@(require_results)
atan2_f16 :: proc "contextless" (y, x: f16) -> f16 {
// TODO(bill): Better atan2_f16
return f16(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f16le :: proc "contextless" (y, x: f16le) -> f16le {
// TODO(bill): Better atan2_f16
return f16le(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f16be :: proc "contextless" (y, x: f16be) -> f16be {
// TODO(bill): Better atan2_f16
return f16be(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f32 :: proc "contextless" (y, x: f32) -> f32 {
// TODO(bill): Better atan2_f32
return f32(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f32le :: proc "contextless" (y, x: f32le) -> f32le {
// TODO(bill): Better atan2_f32
return f32le(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f32be :: proc "contextless" (y, x: f32be) -> f32be {
// TODO(bill): Better atan2_f32
return f32be(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
// TODO(bill): Faster atan2_f64 if possible
@@ -1455,10 +1558,12 @@ atan2_f64 :: proc "contextless" (y, x: f64) -> f64 {
}
return q
}
+@(require_results)
atan2_f64le :: proc "contextless" (y, x: f64le) -> f64le {
// TODO(bill): Better atan2_f32
return f64le(atan2_f64(f64(y), f64(x)))
}
+@(require_results)
atan2_f64be :: proc "contextless" (y, x: f64be) -> f64be {
// TODO(bill): Better atan2_f32
return f64be(atan2_f64(f64(y), f64(x)))
@@ -1471,12 +1576,14 @@ atan2 :: proc{
atan2_f16le, atan2_f16be,
}
+@(require_results)
atan :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return atan2(x, 1)
}
+@(require_results)
asin_f64 :: proc "contextless" (x: f64) -> f64 {
/* origin: FreeBSD /usr/src/lib/msun/src/e_asin.c */
/*
@@ -1548,27 +1655,35 @@ asin_f64 :: proc "contextless" (x: f64) -> f64 {
}
return -x if hx >> 31 != 0 else x
}
+@(require_results)
asin_f64le :: proc "contextless" (x: f64le) -> f64le {
return f64le(asin_f64(f64(x)))
}
+@(require_results)
asin_f64be :: proc "contextless" (x: f64be) -> f64be {
return f64be(asin_f64(f64(x)))
}
+@(require_results)
asin_f32 :: proc "contextless" (x: f32) -> f32 {
return f32(asin_f64(f64(x)))
}
+@(require_results)
asin_f32le :: proc "contextless" (x: f32le) -> f32le {
return f32le(asin_f64(f64(x)))
}
+@(require_results)
asin_f32be :: proc "contextless" (x: f32be) -> f32be {
return f32be(asin_f64(f64(x)))
}
+@(require_results)
asin_f16 :: proc "contextless" (x: f16) -> f16 {
return f16(asin_f64(f64(x)))
}
+@(require_results)
asin_f16le :: proc "contextless" (x: f16le) -> f16le {
return f16le(asin_f64(f64(x)))
}
+@(require_results)
asin_f16be :: proc "contextless" (x: f16be) -> f16be {
return f16be(asin_f64(f64(x)))
}
@@ -1580,6 +1695,7 @@ asin :: proc{
}
+@(require_results)
acos_f64 :: proc "contextless" (x: f64) -> f64 {
/* origin: FreeBSD /usr/src/lib/msun/src/e_acos.c */
/*
@@ -1653,27 +1769,35 @@ acos_f64 :: proc "contextless" (x: f64) -> f64 {
w = R(z)*s+c
return 2*(df+w)
}
+@(require_results)
acos_f64le :: proc "contextless" (x: f64le) -> f64le {
return f64le(acos_f64(f64(x)))
}
+@(require_results)
acos_f64be :: proc "contextless" (x: f64be) -> f64be {
return f64be(acos_f64(f64(x)))
}
+@(require_results)
acos_f32 :: proc "contextless" (x: f32) -> f32 {
return f32(acos_f64(f64(x)))
}
+@(require_results)
acos_f32le :: proc "contextless" (x: f32le) -> f32le {
return f32le(acos_f64(f64(x)))
}
+@(require_results)
acos_f32be :: proc "contextless" (x: f32be) -> f32be {
return f32be(acos_f64(f64(x)))
}
+@(require_results)
acos_f16 :: proc "contextless" (x: f16) -> f16 {
return f16(acos_f64(f64(x)))
}
+@(require_results)
acos_f16le :: proc "contextless" (x: f16le) -> f16le {
return f16le(acos_f64(f64(x)))
}
+@(require_results)
acos_f16be :: proc "contextless" (x: f16be) -> f16be {
return f16be(acos_f64(f64(x)))
}
@@ -1684,14 +1808,17 @@ acos :: proc{
acos_f16le, acos_f16be,
}
+@(require_results)
sinh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return copy_sign(((exp(x) - exp(-x))*0.5), x)
}
+@(require_results)
cosh :: proc "contextless" (x: $T) -> T where intrinsics.type_is_float(T) {
return ((exp(x) + exp(-x))*0.5)
}
+@(require_results)
tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
P0 :: -9.64399179425052238628e-1
P1 :: -9.92877231001918586564e1
@@ -1727,6 +1854,7 @@ tanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
return T(z)
}
+@(require_results)
asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/s_asinh.c
@@ -1773,6 +1901,7 @@ asinh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
return T(temp)
}
+@(require_results)
acosh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/e_acosh.c
@@ -1804,6 +1933,7 @@ acosh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
return T(log1p(t + sqrt(2*t + t*t)))
}
+@(require_results)
atanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/e_atanh.c
@@ -1848,6 +1978,7 @@ atanh :: proc "contextless" (y: $T) -> T where intrinsics.type_is_float(T) {
return T(temp)
}
+@(require_results)
ilogb_f16 :: proc "contextless" (val: f16) -> int {
switch {
case val == 0: return int(min(i32))
@@ -1857,6 +1988,7 @@ ilogb_f16 :: proc "contextless" (val: f16) -> int {
x, exp := normalize_f16(val)
return int(((transmute(u16)x)>>F16_SHIFT)&F16_MASK) - F16_BIAS + exp
}
+@(require_results)
ilogb_f32 :: proc "contextless" (val: f32) -> int {
switch {
case val == 0: return int(min(i32))
@@ -1866,6 +1998,7 @@ ilogb_f32 :: proc "contextless" (val: f32) -> int {
x, exp := normalize_f32(val)
return int(((transmute(u32)x)>>F32_SHIFT)&F32_MASK) - F32_BIAS + exp
}
+@(require_results)
ilogb_f64 :: proc "contextless" (val: f64) -> int {
switch {
case val == 0: return int(min(i32))
@@ -1875,12 +2008,12 @@ ilogb_f64 :: proc "contextless" (val: f64) -> int {
x, exp := normalize_f64(val)
return int(((transmute(u64)x)>>F64_SHIFT)&F64_MASK) - F64_BIAS + exp
}
-ilogb_f16le :: proc "contextless" (value: f16le) -> int { return ilogb_f16(f16(value)) }
-ilogb_f16be :: proc "contextless" (value: f16be) -> int { return ilogb_f16(f16(value)) }
-ilogb_f32le :: proc "contextless" (value: f32le) -> int { return ilogb_f32(f32(value)) }
-ilogb_f32be :: proc "contextless" (value: f32be) -> int { return ilogb_f32(f32(value)) }
-ilogb_f64le :: proc "contextless" (value: f64le) -> int { return ilogb_f64(f64(value)) }
-ilogb_f64be :: proc "contextless" (value: f64be) -> int { return ilogb_f64(f64(value)) }
+@(require_results) ilogb_f16le :: proc "contextless" (value: f16le) -> int { return ilogb_f16(f16(value)) }
+@(require_results) ilogb_f16be :: proc "contextless" (value: f16be) -> int { return ilogb_f16(f16(value)) }
+@(require_results) ilogb_f32le :: proc "contextless" (value: f32le) -> int { return ilogb_f32(f32(value)) }
+@(require_results) ilogb_f32be :: proc "contextless" (value: f32be) -> int { return ilogb_f32(f32(value)) }
+@(require_results) ilogb_f64le :: proc "contextless" (value: f64le) -> int { return ilogb_f64(f64(value)) }
+@(require_results) ilogb_f64be :: proc "contextless" (value: f64be) -> int { return ilogb_f64(f64(value)) }
ilogb :: proc {
ilogb_f16,
ilogb_f32,
@@ -1893,6 +2026,7 @@ ilogb :: proc {
ilogb_f64be,
}
+@(require_results)
logb_f16 :: proc "contextless" (val: f16) -> f16 {
switch {
case val == 0: return inf_f16(-1)
@@ -1901,6 +2035,7 @@ logb_f16 :: proc "contextless" (val: f16) -> f16 {
}
return f16(ilogb(val))
}
+@(require_results)
logb_f32 :: proc "contextless" (val: f32) -> f32 {
switch {
case val == 0: return inf_f32(-1)
@@ -1909,6 +2044,7 @@ logb_f32 :: proc "contextless" (val: f32) -> f32 {
}
return f32(ilogb(val))
}
+@(require_results)
logb_f64 :: proc "contextless" (val: f64) -> f64 {
switch {
case val == 0: return inf_f64(-1)
@@ -1917,12 +2053,12 @@ logb_f64 :: proc "contextless" (val: f64) -> f64 {
}
return f64(ilogb(val))
}
-logb_f16le :: proc "contextless" (value: f16le) -> f16le { return f16le(logb_f16(f16(value))) }
-logb_f16be :: proc "contextless" (value: f16be) -> f16be { return f16be(logb_f16(f16(value))) }
-logb_f32le :: proc "contextless" (value: f32le) -> f32le { return f32le(logb_f32(f32(value))) }
-logb_f32be :: proc "contextless" (value: f32be) -> f32be { return f32be(logb_f32(f32(value))) }
-logb_f64le :: proc "contextless" (value: f64le) -> f64le { return f64le(logb_f64(f64(value))) }
-logb_f64be :: proc "contextless" (value: f64be) -> f64be { return f64be(logb_f64(f64(value))) }
+@(require_results) logb_f16le :: proc "contextless" (value: f16le) -> f16le { return f16le(logb_f16(f16(value))) }
+@(require_results) logb_f16be :: proc "contextless" (value: f16be) -> f16be { return f16be(logb_f16(f16(value))) }
+@(require_results) logb_f32le :: proc "contextless" (value: f32le) -> f32le { return f32le(logb_f32(f32(value))) }
+@(require_results) logb_f32be :: proc "contextless" (value: f32be) -> f32be { return f32be(logb_f32(f32(value))) }
+@(require_results) logb_f64le :: proc "contextless" (value: f64le) -> f64le { return f64le(logb_f64(f64(value))) }
+@(require_results) logb_f64be :: proc "contextless" (value: f64be) -> f64be { return f64be(logb_f64(f64(value))) }
logb :: proc {
logb_f16,
logb_f32,
@@ -1935,6 +2071,7 @@ logb :: proc {
logb_f64be,
}
+@(require_results)
nextafter_f16 :: proc "contextless" (x, y: f16) -> (r: f16) {
switch {
case is_nan(x) || is_nan(y):
@@ -1950,6 +2087,7 @@ nextafter_f16 :: proc "contextless" (x, y: f16) -> (r: f16) {
}
return
}
+@(require_results)
nextafter_f32 :: proc "contextless" (x, y: f32) -> (r: f32) {
switch {
case is_nan(x) || is_nan(y):
@@ -1965,6 +2103,7 @@ nextafter_f32 :: proc "contextless" (x, y: f32) -> (r: f32) {
}
return
}
+@(require_results)
nextafter_f64 :: proc "contextless" (x, y: f64) -> (r: f64) {
switch {
case is_nan(x) || is_nan(y):
@@ -1980,12 +2119,12 @@ nextafter_f64 :: proc "contextless" (x, y: f64) -> (r: f64) {
}
return
}
-nextafter_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(nextafter_f16(f16(x), f16(y))) }
-nextafter_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(nextafter_f16(f16(x), f16(y))) }
-nextafter_f32le :: proc "contextless" (x, y: f32le) -> (r: f32le) { return f32le(nextafter_f32(f32(x), f32(y))) }
-nextafter_f32be :: proc "contextless" (x, y: f32be) -> (r: f32be) { return f32be(nextafter_f32(f32(x), f32(y))) }
-nextafter_f64le :: proc "contextless" (x, y: f64le) -> (r: f64le) { return f64le(nextafter_f64(f64(x), f64(y))) }
-nextafter_f64be :: proc "contextless" (x, y: f64be) -> (r: f64be) { return f64be(nextafter_f64(f64(x), f64(y))) }
+@(require_results) nextafter_f16le :: proc "contextless" (x, y: f16le) -> (r: f16le) { return f16le(nextafter_f16(f16(x), f16(y))) }
+@(require_results) nextafter_f16be :: proc "contextless" (x, y: f16be) -> (r: f16be) { return f16be(nextafter_f16(f16(x), f16(y))) }
+@(require_results) nextafter_f32le :: proc "contextless" (x, y: f32le) -> (r: f32le) { return f32le(nextafter_f32(f32(x), f32(y))) }
+@(require_results) nextafter_f32be :: proc "contextless" (x, y: f32be) -> (r: f32be) { return f32be(nextafter_f32(f32(x), f32(y))) }
+@(require_results) nextafter_f64le :: proc "contextless" (x, y: f64le) -> (r: f64le) { return f64le(nextafter_f64(f64(x), f64(y))) }
+@(require_results) nextafter_f64be :: proc "contextless" (x, y: f64be) -> (r: f64be) { return f64be(nextafter_f64(f64(x), f64(y))) }
nextafter :: proc{
nextafter_f16, nextafter_f16le, nextafter_f16be,
@@ -1993,21 +2132,24 @@ nextafter :: proc{
nextafter_f64, nextafter_f64le, nextafter_f64be,
}
+@(require_results)
signbit_f16 :: proc "contextless" (x: f16) -> bool {
return (transmute(u16)x)&(1<<15) != 0
}
+@(require_results)
signbit_f32 :: proc "contextless" (x: f32) -> bool {
return (transmute(u32)x)&(1<<31) != 0
}
+@(require_results)
signbit_f64 :: proc "contextless" (x: f64) -> bool {
return (transmute(u64)x)&(1<<63) != 0
}
-signbit_f16le :: proc "contextless" (x: f16le) -> bool { return signbit_f16(f16(x)) }
-signbit_f32le :: proc "contextless" (x: f32le) -> bool { return signbit_f32(f32(x)) }
-signbit_f64le :: proc "contextless" (x: f64le) -> bool { return signbit_f64(f64(x)) }
-signbit_f16be :: proc "contextless" (x: f16be) -> bool { return signbit_f16(f16(x)) }
-signbit_f32be :: proc "contextless" (x: f32be) -> bool { return signbit_f32(f32(x)) }
-signbit_f64be :: proc "contextless" (x: f64be) -> bool { return signbit_f64(f64(x)) }
+@(require_results) signbit_f16le :: proc "contextless" (x: f16le) -> bool { return signbit_f16(f16(x)) }
+@(require_results) signbit_f32le :: proc "contextless" (x: f32le) -> bool { return signbit_f32(f32(x)) }
+@(require_results) signbit_f64le :: proc "contextless" (x: f64le) -> bool { return signbit_f64(f64(x)) }
+@(require_results) signbit_f16be :: proc "contextless" (x: f16be) -> bool { return signbit_f16(f16(x)) }
+@(require_results) signbit_f32be :: proc "contextless" (x: f32be) -> bool { return signbit_f32(f32(x)) }
+@(require_results) signbit_f64be :: proc "contextless" (x: f64be) -> bool { return signbit_f64(f64(x)) }
signbit :: proc{
signbit_f16, signbit_f16le, signbit_f16be,
diff --git a/core/math/math_basic.odin b/core/math/math_basic.odin
index c9d2e632d..785c43b10 100644
--- a/core/math/math_basic.odin
+++ b/core/math/math_basic.odin
@@ -5,54 +5,58 @@ import "core:intrinsics"
@(default_calling_convention="none")
foreign _ {
- @(link_name="llvm.sin.f16")
+ @(link_name="llvm.sin.f16", require_results)
sin_f16 :: proc(Īø: f16) -> f16 ---
- @(link_name="llvm.sin.f32")
+ @(link_name="llvm.sin.f32", require_results)
sin_f32 :: proc(Īø: f32) -> f32 ---
- @(link_name="llvm.sin.f64")
+ @(link_name="llvm.sin.f64", require_results)
sin_f64 :: proc(Īø: f64) -> f64 ---
- @(link_name="llvm.cos.f16")
+ @(link_name="llvm.cos.f16", require_results)
cos_f16 :: proc(Īø: f16) -> f16 ---
- @(link_name="llvm.cos.f32")
+ @(link_name="llvm.cos.f32", require_results)
cos_f32 :: proc(Īø: f32) -> f32 ---
- @(link_name="llvm.cos.f64")
+ @(link_name="llvm.cos.f64", require_results)
cos_f64 :: proc(Īø: f64) -> f64 ---
- @(link_name="llvm.pow.f16")
+ @(link_name="llvm.pow.f16", require_results)
pow_f16 :: proc(x, power: f16) -> f16 ---
- @(link_name="llvm.pow.f32")
+ @(link_name="llvm.pow.f32", require_results)
pow_f32 :: proc(x, power: f32) -> f32 ---
- @(link_name="llvm.pow.f64")
+ @(link_name="llvm.pow.f64", require_results)
pow_f64 :: proc(x, power: f64) -> f64 ---
- @(link_name="llvm.fmuladd.f16")
+ @(link_name="llvm.fmuladd.f16", require_results)
fmuladd_f16 :: proc(a, b, c: f16) -> f16 ---
- @(link_name="llvm.fmuladd.f32")
+ @(link_name="llvm.fmuladd.f32", require_results)
fmuladd_f32 :: proc(a, b, c: f32) -> f32 ---
- @(link_name="llvm.fmuladd.f64")
+ @(link_name="llvm.fmuladd.f64", require_results)
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
- @(link_name="llvm.exp.f16")
+ @(link_name="llvm.exp.f16", require_results)
exp_f16 :: proc(x: f16) -> f16 ---
- @(link_name="llvm.exp.f32")
+ @(link_name="llvm.exp.f32", require_results)
exp_f32 :: proc(x: f32) -> f32 ---
- @(link_name="llvm.exp.f64")
+ @(link_name="llvm.exp.f64", require_results)
exp_f64 :: proc(x: f64) -> f64 ---
}
+@(require_results)
sqrt_f16 :: proc "contextless" (x: f16) -> f16 {
return intrinsics.sqrt(x)
}
+@(require_results)
sqrt_f32 :: proc "contextless" (x: f32) -> f32 {
return intrinsics.sqrt(x)
}
+@(require_results)
sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
return intrinsics.sqrt(x)
}
+@(require_results)
ln_f64 :: proc "contextless" (x: f64) -> f64 {
// The original C code, the long comment, and the constants
// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
@@ -154,14 +158,14 @@ ln_f64 :: proc "contextless" (x: f64) -> f64 {
return k*LN2_HI - ((hfsq - (s*(hfsq+R) + k*LN2_LO)) - f)
}
-ln_f16 :: proc "contextless" (x: f16) -> f16 { return #force_inline f16(ln_f64(f64(x))) }
-ln_f32 :: proc "contextless" (x: f32) -> f32 { return #force_inline f32(ln_f64(f64(x))) }
-ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
-ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
-ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
-ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
-ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
-ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
+@(require_results) ln_f16 :: proc "contextless" (x: f16) -> f16 { return #force_inline f16(ln_f64(f64(x))) }
+@(require_results) ln_f32 :: proc "contextless" (x: f32) -> f32 { return #force_inline f32(ln_f64(f64(x))) }
+@(require_results) ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
+@(require_results) ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
+@(require_results) ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
+@(require_results) ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
+@(require_results) ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
+@(require_results) ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
ln :: proc{
ln_f16, ln_f16le, ln_f16be,
ln_f32, ln_f32le, ln_f32be,
diff --git a/core/math/math_basic_js.odin b/core/math/math_basic_js.odin
index ec572f898..acd3c2b39 100644
--- a/core/math/math_basic_js.odin
+++ b/core/math/math_basic_js.odin
@@ -7,46 +7,47 @@ foreign import "odin_env"
@(default_calling_convention="c")
foreign odin_env {
- @(link_name="sin")
+ @(link_name="sin", require_results)
sin_f64 :: proc(Īø: f64) -> f64 ---
- @(link_name="cos")
+ @(link_name="cos", require_results)
cos_f64 :: proc(Īø: f64) -> f64 ---
- @(link_name="pow")
+ @(link_name="pow", require_results)
pow_f64 :: proc(x, power: f64) -> f64 ---
- @(link_name="fmuladd")
+ @(link_name="fmuladd", require_results)
fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---
- @(link_name="ln")
+ @(link_name="ln", require_results)
ln_f64 :: proc(x: f64) -> f64 ---
- @(link_name="exp")
+ @(link_name="exp", require_results)
exp_f64 :: proc(x: f64) -> f64 ---
}
+@(require_results)
sqrt_f64 :: proc "contextless" (x: f64) -> f64 {
return intrinsics.sqrt(x)
}
-sqrt_f16 :: proc "c" (x: f16) -> f16 { return f16(sqrt_f64(f64(x))) }
-sin_f16 :: proc "c" (Īø: f16) -> f16 { return f16(sin_f64(f64(Īø))) }
-cos_f16 :: proc "c" (Īø: f16) -> f16 { return f16(cos_f64(f64(Īø))) }
-pow_f16 :: proc "c" (x, power: f16) -> f16 { return f16(pow_f64(f64(x), f64(power))) }
-fmuladd_f16 :: proc "c" (a, b, c: f16) -> f16 { return f16(fmuladd_f64(f64(a), f64(a), f64(c))) }
-ln_f16 :: proc "c" (x: f16) -> f16 { return f16(ln_f64(f64(x))) }
-exp_f16 :: proc "c" (x: f16) -> f16 { return f16(exp_f64(f64(x))) }
+@(require_results) sqrt_f16 :: proc "c" (x: f16) -> f16 { return f16(sqrt_f64(f64(x))) }
+@(require_results) sin_f16 :: proc "c" (Īø: f16) -> f16 { return f16(sin_f64(f64(Īø))) }
+@(require_results) cos_f16 :: proc "c" (Īø: f16) -> f16 { return f16(cos_f64(f64(Īø))) }
+@(require_results) pow_f16 :: proc "c" (x, power: f16) -> f16 { return f16(pow_f64(f64(x), f64(power))) }
+@(require_results) fmuladd_f16 :: proc "c" (a, b, c: f16) -> f16 { return f16(fmuladd_f64(f64(a), f64(a), f64(c))) }
+@(require_results) ln_f16 :: proc "c" (x: f16) -> f16 { return f16(ln_f64(f64(x))) }
+@(require_results) exp_f16 :: proc "c" (x: f16) -> f16 { return f16(exp_f64(f64(x))) }
-sqrt_f32 :: proc "c" (x: f32) -> f32 { return f32(sqrt_f64(f64(x))) }
-sin_f32 :: proc "c" (Īø: f32) -> f32 { return f32(sin_f64(f64(Īø))) }
-cos_f32 :: proc "c" (Īø: f32) -> f32 { return f32(cos_f64(f64(Īø))) }
-pow_f32 :: proc "c" (x, power: f32) -> f32 { return f32(pow_f64(f64(x), f64(power))) }
-fmuladd_f32 :: proc "c" (a, b, c: f32) -> f32 { return f32(fmuladd_f64(f64(a), f64(a), f64(c))) }
-ln_f32 :: proc "c" (x: f32) -> f32 { return f32(ln_f64(f64(x))) }
-exp_f32 :: proc "c" (x: f32) -> f32 { return f32(exp_f64(f64(x))) }
+@(require_results) sqrt_f32 :: proc "c" (x: f32) -> f32 { return f32(sqrt_f64(f64(x))) }
+@(require_results) sin_f32 :: proc "c" (Īø: f32) -> f32 { return f32(sin_f64(f64(Īø))) }
+@(require_results) cos_f32 :: proc "c" (Īø: f32) -> f32 { return f32(cos_f64(f64(Īø))) }
+@(require_results) pow_f32 :: proc "c" (x, power: f32) -> f32 { return f32(pow_f64(f64(x), f64(power))) }
+@(require_results) fmuladd_f32 :: proc "c" (a, b, c: f32) -> f32 { return f32(fmuladd_f64(f64(a), f64(a), f64(c))) }
+@(require_results) ln_f32 :: proc "c" (x: f32) -> f32 { return f32(ln_f64(f64(x))) }
+@(require_results) exp_f32 :: proc "c" (x: f32) -> f32 { return f32(exp_f64(f64(x))) }
-ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
-ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
-ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
-ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
-ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
-ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
+@(require_results) ln_f16le :: proc "contextless" (x: f16le) -> f16le { return #force_inline f16le(ln_f64(f64(x))) }
+@(require_results) ln_f16be :: proc "contextless" (x: f16be) -> f16be { return #force_inline f16be(ln_f64(f64(x))) }
+@(require_results) ln_f32le :: proc "contextless" (x: f32le) -> f32le { return #force_inline f32le(ln_f64(f64(x))) }
+@(require_results) ln_f32be :: proc "contextless" (x: f32be) -> f32be { return #force_inline f32be(ln_f64(f64(x))) }
+@(require_results) ln_f64le :: proc "contextless" (x: f64le) -> f64le { return #force_inline f64le(ln_f64(f64(x))) }
+@(require_results) ln_f64be :: proc "contextless" (x: f64be) -> f64be { return #force_inline f64be(ln_f64(f64(x))) }
ln :: proc{
ln_f16, ln_f16le, ln_f16be,
ln_f32, ln_f32le, ln_f32be,
diff --git a/core/math/math_erf.odin b/core/math/math_erf.odin
index cdade59c5..b5a4663ae 100644
--- a/core/math/math_erf.odin
+++ b/core/math/math_erf.odin
@@ -117,13 +117,14 @@ erf :: proc{
erf_f64,
}
-erf_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erf_f64(f64(x))) }
-erf_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erf_f64(f64(x))) }
-erf_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erf_f64(f64(x))) }
-erf_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erf_f64(f64(x))) }
-erf_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erf_f64(f64(x))) }
-erf_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erf_f64(f64(x))) }
+@(require_results) erf_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erf_f64(f64(x))) }
+@(require_results) erf_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erf_f64(f64(x))) }
+@(require_results) erf_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erf_f64(f64(x))) }
+@(require_results) erf_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erf_f64(f64(x))) }
+@(require_results) erf_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erf_f64(f64(x))) }
+@(require_results) erf_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erf_f64(f64(x))) }
+@(require_results)
erf_f64 :: proc "contextless" (x: f64) -> f64 {
erx :: 0h3FEB0AC160000000
// Coefficients for approximation to erf in [0, 0.84375]
@@ -268,13 +269,14 @@ erfc :: proc{
erfc_f64,
}
-erfc_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erfc_f64(f64(x))) }
-erfc_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erfc_f64(f64(x))) }
-erfc_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erfc_f64(f64(x))) }
-erfc_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erfc_f64(f64(x))) }
-erfc_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erfc_f64(f64(x))) }
-erfc_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erfc_f64(f64(x))) }
+@(require_results) erfc_f16 :: proc "contextless" (x: f16) -> f16 { return f16(erfc_f64(f64(x))) }
+@(require_results) erfc_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(erfc_f64(f64(x))) }
+@(require_results) erfc_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(erfc_f64(f64(x))) }
+@(require_results) erfc_f32 :: proc "contextless" (x: f32) -> f32 { return f32(erfc_f64(f64(x))) }
+@(require_results) erfc_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(erfc_f64(f64(x))) }
+@(require_results) erfc_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(erfc_f64(f64(x))) }
+@(require_results)
erfc_f64 :: proc "contextless" (x: f64) -> f64 {
erx :: 0h3FEB0AC160000000
// Coefficients for approximation to erf in [0, 0.84375]
diff --git a/core/math/math_gamma.odin b/core/math/math_gamma.odin
index 6b783cc25..00d4b7316 100644
--- a/core/math/math_gamma.odin
+++ b/core/math/math_gamma.odin
@@ -65,7 +65,7 @@ package math
// The polynomial is valid for 33 <= x <= 172; larger values are only used
// in reciprocal and produce denormalized floats. The lower precision there
// masks any imprecision in the polynomial.
-@(private="file")
+@(private="file", require_results)
stirling :: proc "contextless" (x: f64) -> (f64, f64) {
@(static) gamS := [?]f64{
+7.87311395793093628397e-04,
@@ -93,6 +93,7 @@ stirling :: proc "contextless" (x: f64) -> (f64, f64) {
return y1, SQRT_TWO_PI * w * y2
}
+@(require_results)
gamma_f64 :: proc "contextless" (x: f64) -> f64 {
is_neg_int :: proc "contextless" (x: f64) -> bool {
if x < 0 {
@@ -210,14 +211,14 @@ gamma_f64 :: proc "contextless" (x: f64) -> f64 {
}
-gamma_f16 :: proc "contextless" (x: f16) -> f16 { return f16(gamma_f64(f64(x))) }
-gamma_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(gamma_f64(f64(x))) }
-gamma_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(gamma_f64(f64(x))) }
-gamma_f32 :: proc "contextless" (x: f32) -> f32 { return f32(gamma_f64(f64(x))) }
-gamma_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(gamma_f64(f64(x))) }
-gamma_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(gamma_f64(f64(x))) }
-gamma_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(gamma_f64(f64(x))) }
-gamma_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(gamma_f64(f64(x))) }
+@(require_results) gamma_f16 :: proc "contextless" (x: f16) -> f16 { return f16(gamma_f64(f64(x))) }
+@(require_results) gamma_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(gamma_f64(f64(x))) }
+@(require_results) gamma_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(gamma_f64(f64(x))) }
+@(require_results) gamma_f32 :: proc "contextless" (x: f32) -> f32 { return f32(gamma_f64(f64(x))) }
+@(require_results) gamma_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(gamma_f64(f64(x))) }
+@(require_results) gamma_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(gamma_f64(f64(x))) }
+@(require_results) gamma_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(gamma_f64(f64(x))) }
+@(require_results) gamma_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(gamma_f64(f64(x))) }
gamma :: proc{
gamma_f16, gamma_f16le, gamma_f16be,
diff --git a/core/math/math_lgamma.odin b/core/math/math_lgamma.odin
index 98b2731c9..0705d8564 100644
--- a/core/math/math_lgamma.odin
+++ b/core/math/math_lgamma.odin
@@ -80,7 +80,9 @@ package math
//
+@(require_results)
lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
+ @(require_results)
sin_pi :: proc "contextless" (x: f64) -> f64 {
if x < 0.25 {
return -sin(PI * x)
@@ -345,14 +347,14 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
}
-lgamma_f16 :: proc "contextless" (x: f16) -> (lgamma: f16, sign: int) { r, s := lgamma_f64(f64(x)); return f16(r), s }
-lgamma_f32 :: proc "contextless" (x: f32) -> (lgamma: f32, sign: int) { r, s := lgamma_f64(f64(x)); return f32(r), s }
-lgamma_f16le :: proc "contextless" (x: f16le) -> (lgamma: f16le, sign: int) { r, s := lgamma_f64(f64(x)); return f16le(r), s }
-lgamma_f16be :: proc "contextless" (x: f16be) -> (lgamma: f16be, sign: int) { r, s := lgamma_f64(f64(x)); return f16be(r), s }
-lgamma_f32le :: proc "contextless" (x: f32le) -> (lgamma: f32le, sign: int) { r, s := lgamma_f64(f64(x)); return f32le(r), s }
-lgamma_f32be :: proc "contextless" (x: f32be) -> (lgamma: f32be, sign: int) { r, s := lgamma_f64(f64(x)); return f32be(r), s }
-lgamma_f64le :: proc "contextless" (x: f64le) -> (lgamma: f64le, sign: int) { r, s := lgamma_f64(f64(x)); return f64le(r), s }
-lgamma_f64be :: proc "contextless" (x: f64be) -> (lgamma: f64be, sign: int) { r, s := lgamma_f64(f64(x)); return f64be(r), s }
+@(require_results) lgamma_f16 :: proc "contextless" (x: f16) -> (lgamma: f16, sign: int) { r, s := lgamma_f64(f64(x)); return f16(r), s }
+@(require_results) lgamma_f32 :: proc "contextless" (x: f32) -> (lgamma: f32, sign: int) { r, s := lgamma_f64(f64(x)); return f32(r), s }
+@(require_results) lgamma_f16le :: proc "contextless" (x: f16le) -> (lgamma: f16le, sign: int) { r, s := lgamma_f64(f64(x)); return f16le(r), s }
+@(require_results) lgamma_f16be :: proc "contextless" (x: f16be) -> (lgamma: f16be, sign: int) { r, s := lgamma_f64(f64(x)); return f16be(r), s }
+@(require_results) lgamma_f32le :: proc "contextless" (x: f32le) -> (lgamma: f32le, sign: int) { r, s := lgamma_f64(f64(x)); return f32le(r), s }
+@(require_results) lgamma_f32be :: proc "contextless" (x: f32be) -> (lgamma: f32be, sign: int) { r, s := lgamma_f64(f64(x)); return f32be(r), s }
+@(require_results) lgamma_f64le :: proc "contextless" (x: f64le) -> (lgamma: f64le, sign: int) { r, s := lgamma_f64(f64(x)); return f64le(r), s }
+@(require_results) lgamma_f64be :: proc "contextless" (x: f64be) -> (lgamma: f64be, sign: int) { r, s := lgamma_f64(f64(x)); return f64be(r), s }
lgamma :: proc{
lgamma_f16, lgamma_f16le, lgamma_f16be,
diff --git a/core/math/math_log1p.odin b/core/math/math_log1p.odin
index a4a1aa2ae..fc10ed364 100644
--- a/core/math/math_log1p.odin
+++ b/core/math/math_log1p.odin
@@ -90,15 +90,16 @@ log1p :: proc {
log1p_f64le,
log1p_f64be,
}
-log1p_f16 :: proc "contextless" (x: f16) -> f16 { return f16(log1p_f64(f64(x))) }
-log1p_f32 :: proc "contextless" (x: f32) -> f32 { return f32(log1p_f64(f64(x))) }
-log1p_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log1p_f64(f64(x))) }
-log1p_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log1p_f64(f64(x))) }
-log1p_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log1p_f64(f64(x))) }
-log1p_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log1p_f64(f64(x))) }
-log1p_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log1p_f64(f64(x))) }
-log1p_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log1p_f64(f64(x))) }
+@(require_results) log1p_f16 :: proc "contextless" (x: f16) -> f16 { return f16(log1p_f64(f64(x))) }
+@(require_results) log1p_f32 :: proc "contextless" (x: f32) -> f32 { return f32(log1p_f64(f64(x))) }
+@(require_results) log1p_f16le :: proc "contextless" (x: f16le) -> f16le { return f16le(log1p_f64(f64(x))) }
+@(require_results) log1p_f16be :: proc "contextless" (x: f16be) -> f16be { return f16be(log1p_f64(f64(x))) }
+@(require_results) log1p_f32le :: proc "contextless" (x: f32le) -> f32le { return f32le(log1p_f64(f64(x))) }
+@(require_results) log1p_f32be :: proc "contextless" (x: f32be) -> f32be { return f32be(log1p_f64(f64(x))) }
+@(require_results) log1p_f64le :: proc "contextless" (x: f64le) -> f64le { return f64le(log1p_f64(f64(x))) }
+@(require_results) log1p_f64be :: proc "contextless" (x: f64be) -> f64be { return f64be(log1p_f64(f64(x))) }
+@(require_results)
log1p_f64 :: proc "contextless" (x: f64) -> f64 {
SQRT2_M1 :: 0h3fda827999fcef34 // sqrt(2)-1
SQRT2_HALF_M1 :: 0hbfd2bec333018866 // sqrt(2)/2-1
diff --git a/core/math/noise/opensimplex2.odin b/core/math/noise/opensimplex2.odin
index d90dafdf5..d28356f2c 100644
--- a/core/math/noise/opensimplex2.odin
+++ b/core/math/noise/opensimplex2.odin
@@ -20,6 +20,7 @@ Vec4 :: [4]f64
/*
2D Simplex noise, standard lattice orientation.
*/
+@(require_results)
noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
// Get points for A2* lattice
skew := SKEW_2D * (coord.x + coord.y)
@@ -35,6 +36,7 @@ noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
unless your map is centered around an equator. It's a subtle
difference, but the option is here to make it an easy choice.
*/
+@(require_results)
noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) {
// Skew transform and rotation baked into one.
xx := coord.x * ROOT_2_OVER_2
@@ -51,6 +53,7 @@ noise_2d_improve_x :: proc(seed: i64, coord: Vec2) -> (value: f32) {
If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, y, Z)`.
For a time varied animation, call `noise_3d_improve_xz(x, y, T)`.
*/
+@(require_results)
noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) {
/*
Re-orient the cubic lattices without skewing, so Z points up the main lattice diagonal,
@@ -75,6 +78,7 @@ noise_3d_improve_xy :: proc(seed: i64, coord: Vec3) -> (value: f32) {
If Z is vertical in world coordinates, call `noise_3d_improve_xz(x, Z, y)` or use `noise_3d_improve_xy`.
For a time varied animation, call `noise_3d_improve_xz(x, T, y)` or use `noise_3d_improve_xy`.
*/
+@(require_results)
noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) {
/*
Re-orient the cubic lattices without skewing, so Y points up the main lattice diagonal,
@@ -96,6 +100,7 @@ noise_3d_improve_xz :: proc(seed: i64, coord: Vec3) -> (value: f32) {
Use `noise_3d_improve_xy` or `noise_3d_improve_xz` instead, wherever appropriate.
They have less diagonal bias. This function's best use is as a fallback.
*/
+@(require_results)
noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
/*
Re-orient the cubic lattices via rotation, to produce a familiar look.
@@ -114,6 +119,7 @@ noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
Recommended for time-varied animations which texture a 3D object (W=time)
in a space where Z is vertical.
*/
+@(require_results)
noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) {
xy := coord.x + coord.y
s2 := xy * -0.21132486540518699998
@@ -133,6 +139,7 @@ noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32)
Recommended for time-varied animations which texture a 3D object (W=time)
in a space where Y is vertical.
*/
+@(require_results)
noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
xz := coord.x + coord.z
s2 := xz * -0.21132486540518699998
@@ -152,6 +159,7 @@ noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32)
Recommended for time-varied animations which texture a 3D object (W=time)
where there isn't a clear distinction between horizontal and vertical
*/
+@(require_results)
noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
xyz := coord.x + coord.y + coord.z
ww := coord.w * 0.2236067977499788
@@ -164,6 +172,7 @@ noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
/*
4D OpenSimplex2 noise, fallback lattice orientation.
*/
+@(require_results)
noise_4d_fallback :: proc(seed: i64, coord: Vec4) -> (value: f32) {
// Get points for A4 lattice
skew := f64(SKEW_4D) * (coord.x + coord.y + coord.z + coord.w)
diff --git a/core/math/rand/distributions.odin b/core/math/rand/distributions.odin
index ada89afad..9365e8b76 100644
--- a/core/math/rand/distributions.odin
+++ b/core/math/rand/distributions.odin
@@ -7,6 +7,7 @@ float32_uniform :: float32_range
// Triangular Distribution
// See: http://wikipedia.org/wiki/Triangular_distribution
+@(require_results)
float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
if hi-lo == 0 {
return lo
@@ -24,6 +25,7 @@ float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64
}
// Triangular Distribution
// See: http://wikipedia.org/wiki/Triangular_distribution
+@(require_results)
float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
if hi-lo == 0 {
return lo
@@ -41,20 +43,24 @@ float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32
// Normal/Gaussian Distribution
+@(require_results)
float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
return norm_float64(r) * stddev + mean
}
// Normal/Gaussian Distribution
+@(require_results)
float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
return f32(float64_normal(f64(mean), f64(stddev), r))
}
// Log Normal Distribution
+@(require_results)
float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
return math.exp(float64_normal(mean, stddev, r))
}
// Log Normal Distribution
+@(require_results)
float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
return f32(float64_log_normal(f64(mean), f64(stddev), r))
}
@@ -65,6 +71,7 @@ float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
// Return values range from
// 0 to positive infinity if lambda > 0
// negative infinity to 0 if lambda <= 0
+@(require_results)
float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
return - math.ln(1 - float64(r)) / lambda
}
@@ -73,6 +80,7 @@ float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
// Return values range from
// 0 to positive infinity if lambda > 0
// negative infinity to 0 if lambda <= 0
+@(require_results)
float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
return f32(float64_exponential(f64(lambda), r))
}
@@ -87,6 +95,7 @@ float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
// math.gamma(alpha) * math.pow(beta, alpha)
//
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
+@(require_results)
float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
if alpha <= 0 || beta <= 0 {
panic(#procedure + ": alpha and beta must be > 0.0")
@@ -152,6 +161,7 @@ float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
// math.gamma(alpha) * math.pow(beta, alpha)
//
// mean is alpha*beta, variance is math.pow(alpha*beta, 2)
+@(require_results)
float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
return f32(float64_gamma(f64(alpha), f64(beta), r))
}
@@ -162,6 +172,7 @@ float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
// Required: alpha > 0 and beta > 0
//
// Return values range between 0 and 1
+@(require_results)
float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
if alpha <= 0 || beta <= 0 {
panic(#procedure + ": alpha and beta must be > 0.0")
@@ -178,6 +189,7 @@ float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
// Required: alpha > 0 and beta > 0
//
// Return values range between 0 and 1
+@(require_results)
float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
return f32(float64_beta(f64(alpha), f64(beta), r))
}
@@ -185,22 +197,26 @@ float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
// Pareto distribution, `alpha` is the shape parameter.
// https://wikipedia.org/wiki/Pareto_distribution
+@(require_results)
float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
return math.pow(1 - float64(r), -1.0 / alpha)
}
// Pareto distribution, `alpha` is the shape parameter.
// https://wikipedia.org/wiki/Pareto_distribution
+@(require_results)
float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
return f32(float64_pareto(f64(alpha), r))
}
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
+@(require_results)
float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
u := 1 - float64(r)
return alpha * math.pow(-math.ln(u), 1.0/beta)
}
// Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
+@(require_results)
float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
return f32(float64_weibull(f64(alpha), f64(beta), r))
}
@@ -210,6 +226,7 @@ float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
// `mean_angle` is the in mean angle between 0 and 2pi radians
// `kappa` is the concentration parameter which must be >= 0
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
+@(require_results)
float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
// Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
@@ -245,6 +262,7 @@ float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
// `mean_angle` is the in mean angle between 0 and 2pi radians
// `kappa` is the concentration parameter which must be >= 0
// When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
+@(require_results)
float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
}
@@ -252,6 +270,7 @@ float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
// Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+@(require_results)
float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
assert(gamma > 0)
@@ -261,6 +280,7 @@ float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
}
// Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+@(require_results)
float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
}
@@ -268,12 +288,14 @@ float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
// Log Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+@(require_results)
float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
assert(gamma > 0)
return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
}
// Log Cauchy-Lorentz Distribution
// `x_0` is the location, `gamma` is the scale where `gamma` > 0
+@(require_results)
float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
}
@@ -281,6 +303,7 @@ float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
// Laplace Distribution
// `b` is the scale where `b` > 0
+@(require_results)
float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
assert(b > 0)
p := float64(r)-0.5
@@ -288,6 +311,7 @@ float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
}
// Laplace Distribution
// `b` is the scale where `b` > 0
+@(require_results)
float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
return f32(float64_laplace(f64(mean), f64(b), r))
}
@@ -296,6 +320,7 @@ float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
// Gompertz Distribution
// `eta` is the shape, `b` is the scale
// Both `eta` and `b` must be > 0
+@(require_results)
float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
if eta <= 0 || b <= 0 {
panic(#procedure + ": eta and b must be > 0.0")
@@ -307,6 +332,7 @@ float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
// Gompertz Distribution
// `eta` is the shape, `b` is the scale
// Both `eta` and `b` must be > 0
+@(require_results)
float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
return f32(float64_gompertz(f64(eta), f64(b), r))
}
diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin
index c0f92e99c..719debe75 100644
--- a/core/math/rand/exp.odin
+++ b/core/math/rand/exp.odin
@@ -15,6 +15,7 @@ import "core:math"
// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
// https://www.jstatsoft.org/article/view/v005i08 [web page]
//
+@(require_results)
exp_float64 :: proc(r: ^Rand = nil) -> f64 {
re :: 7.69711747013104972
diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin
index a9edd0f19..f96163fe9 100644
--- a/core/math/rand/normal.odin
+++ b/core/math/rand/normal.odin
@@ -17,6 +17,7 @@ import "core:math"
// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
// https://www.jstatsoft.org/article/view/v005i08 [web page]
//
+@(require_results)
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
rn :: 3.442619855899
diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin
index 2d92d29ff..2fec1efc5 100644
--- a/core/math/rand/rand.odin
+++ b/core/math/rand/rand.odin
@@ -1,6 +1,10 @@
+/*
+Package core:math/rand implements various random number generators
+*/
package rand
import "core:intrinsics"
+import "core:mem"
Rand :: struct {
state: u64,
@@ -12,16 +16,82 @@ Rand :: struct {
@(private)
global_rand := create(u64(intrinsics.read_cycle_counter()))
+/*
+Sets the seed used by the global random number generator.
+
+Inputs:
+- seed: The seed value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ set_global_seed_example :: proc() {
+ rand.set_global_seed(1)
+ fmt.println(rand.uint64())
+ }
+
+Possible Output:
+
+ 10
+
+*/
set_global_seed :: proc(seed: u64) {
init(&global_rand, seed)
}
-create :: proc(seed: u64) -> Rand {
+/*
+Creates a new random number generator.
+
+Inputs:
+- seed: The seed value to create the random number generator with
+
+Returns:
+- res: The created random number generator
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ create_example :: proc() {
+ my_rand := rand.create(1)
+ fmt.println(rand.uint64(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+
+*/
+@(require_results)
+create :: proc(seed: u64) -> (res: Rand) {
r: Rand
init(&r, seed)
return r
}
+/*
+Initialises a random number generator.
+
+Inputs:
+- r: The random number generator to initialise
+- seed: The seed value to initialise this random number generator
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ init_example :: proc() {
+ my_rand: rand.Rand
+ rand.init(&my_rand, 1)
+ fmt.println(rand.uint64(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+
+*/
init :: proc(r: ^Rand, seed: u64) {
r.state = 0
r.inc = (seed << 1) | 1
@@ -30,6 +100,35 @@ init :: proc(r: ^Rand, seed: u64) {
_random(r)
}
+/*
+Initialises a random number generator to use the system random number generator.
+The system random number generator is platform specific.
+On `linux` refer to the `getrandom` syscall.
+On `darwin` refer to `getentropy`.
+On `windows` refer to `BCryptGenRandom`.
+
+All other platforms wi
+
+Inputs:
+- r: The random number generator to use the system random number generator
+
+WARNING: Panics if the system is not either `windows`, `darwin` or `linux`
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ init_as_system_example :: proc() {
+ my_rand: rand.Rand
+ rand.init_as_system(&my_rand)
+ fmt.println(rand.uint64(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+
+*/
init_as_system :: proc(r: ^Rand) {
if !#defined(_system_random) {
panic(#procedure + " is not supported on this platform yet")
@@ -60,15 +159,99 @@ _random :: proc(r: ^Rand) -> u32 {
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 31))
}
-uint32 :: proc(r: ^Rand = nil) -> u32 { return _random(r) }
+/*
+Generates a random 32 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
-uint64 :: proc(r: ^Rand = nil) -> u64 {
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random unsigned 32 bit value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ uint32_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.uint32())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.uint32(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+ 389
+
+*/
+@(require_results)
+uint32 :: proc(r: ^Rand = nil) -> (val: u32) { return _random(r) }
+
+/*
+Generates a random 64 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random unsigned 64 bit value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ uint64_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.uint64())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.uint64(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+ 389
+
+*/
+@(require_results)
+uint64 :: proc(r: ^Rand = nil) -> (val: u64) {
a := u64(_random(r))
b := u64(_random(r))
return (a<<32) | b
}
-uint128 :: proc(r: ^Rand = nil) -> u128 {
+/*
+Generates a random 128 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random unsigned 128 bit value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ uint128_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.uint128())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.uint128(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+ 389
+
+*/
+@(require_results)
+uint128 :: proc(r: ^Rand = nil) -> (val: u128) {
a := u128(_random(r))
b := u128(_random(r))
c := u128(_random(r))
@@ -76,11 +259,126 @@ uint128 :: proc(r: ^Rand = nil) -> u128 {
return (a<<96) | (b<<64) | (c<<32) | d
}
-int31 :: proc(r: ^Rand = nil) -> i32 { return i32(uint32(r) << 1 >> 1) }
-int63 :: proc(r: ^Rand = nil) -> i64 { return i64(uint64(r) << 1 >> 1) }
-int127 :: proc(r: ^Rand = nil) -> i128 { return i128(uint128(r) << 1 >> 1) }
+/*
+Generates a random 31 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
+The sign bit will always be set to 0, thus all generated numbers will be positive.
-int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random 31 bit value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int31_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int31())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int31(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+ 389
+
+*/
+@(require_results) int31 :: proc(r: ^Rand = nil) -> (val: i32) { return i32(uint32(r) << 1 >> 1) }
+/*
+Generates a random 63 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
+The sign bit will always be set to 0, thus all generated numbers will be positive.
+
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random 63 bit value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int63_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int63())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int63(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+ 389
+
+*/
+@(require_results) int63 :: proc(r: ^Rand = nil) -> (val: i64) { return i64(uint64(r) << 1 >> 1) }
+/*
+Generates a random 127 bit value using the provided random number generator. If no generator is provided the global random number generator will be used.
+The sign bit will always be set to 0, thus all generated numbers will be positive.
+
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random 127 bit value
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int127_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int127())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int127(&my_rand))
+ }
+
+Possible Output:
+
+ 10
+ 389
+
+*/
+@(require_results) int127 :: proc(r: ^Rand = nil) -> (val: i128) { return i128(uint128(r) << 1 >> 1) }
+
+/*
+Generates a random 31 bit value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- n: The upper bound of the generated number, this value is exclusive
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random 31 bit value in the range `(0, n]`
+
+WARNING: Panics if n is less than 0
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int31_max_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int31_max(16))
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int31_max(1024, &my_rand))
+ }
+
+Possible Output:
+
+ 6
+ 500
+
+*/
+@(require_results)
+int31_max :: proc(n: i32, r: ^Rand = nil) -> (val: i32) {
if n <= 0 {
panic("Invalid argument to int31_max")
}
@@ -94,8 +392,38 @@ int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
}
return v % n
}
+/*
+Generates a random 63 bit value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
-int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
+Inputs:
+- n: The upper bound of the generated number, this value is exclusive
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random 63 bit value in the range `(0, n]`
+
+WARNING: Panics if n is less than 0
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int63_max_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int63_max(16))
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int63_max(1024, &my_rand))
+ }
+
+Possible Output:
+
+ 6
+ 500
+
+*/
+@(require_results)
+int63_max :: proc(n: i64, r: ^Rand = nil) -> (val: i64) {
if n <= 0 {
panic("Invalid argument to int63_max")
}
@@ -109,8 +437,38 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
}
return v % n
}
+/*
+Generates a random 127 bit value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
-int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
+Inputs:
+- n: The upper bound of the generated number, this value is exclusive
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random 127 bit value in the range `(0, n]`
+
+WARNING: Panics if n is less than 0
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int127_max_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int127_max(16))
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int127_max(1024, &my_rand))
+ }
+
+Possible Output:
+
+ 6
+ 500
+
+*/
+@(require_results)
+int127_max :: proc(n: i128, r: ^Rand = nil) -> (val: i128) {
if n <= 0 {
panic("Invalid argument to int127_max")
}
@@ -124,8 +482,38 @@ int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
}
return v % n
}
+/*
+Generates a random integer value in the range `(0, n]` using the provided random number generator. If no generator is provided the global random number generator will be used.
-int_max :: proc(n: int, r: ^Rand = nil) -> int {
+Inputs:
+- n: The upper bound of the generated number, this value is exclusive
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random integer value in the range `(0, n]`
+
+WARNING: Panics if n is less than 0
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ int_max_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.int_max(16))
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.int_max(1024, &my_rand))
+ }
+
+Possible Output:
+
+ 6
+ 500
+
+*/
+@(require_results)
+int_max :: proc(n: int, r: ^Rand = nil) -> (val: int) {
if n <= 0 {
panic("Invalid argument to int_max")
}
@@ -136,14 +524,154 @@ int_max :: proc(n: int, r: ^Rand = nil) -> int {
}
}
-// Uniform random distribution [0, 1)
-float64 :: proc(r: ^Rand = nil) -> f64 { return f64(int63_max(1<<53, r)) / (1 << 53) }
-// Uniform random distribution [0, 1)
-float32 :: proc(r: ^Rand = nil) -> f32 { return f32(float64(r)) }
+/*
+Generates a random double floating point value in the range `(0, 1]` using the provided random number generator. If no generator is provided the global random number generator will be used.
-float64_range :: proc(lo, hi: f64, r: ^Rand = nil) -> f64 { return (hi-lo)*float64(r) + lo }
-float32_range :: proc(lo, hi: f32, r: ^Rand = nil) -> f32 { return (hi-lo)*float32(r) + lo }
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+Returns:
+- val: A random double floating point value in the range `(0, 1]`
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ float64_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.float64())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.float64(&my_rand))
+ }
+
+Possible Output:
+
+ 0.043
+ 0.511
+
+*/
+@(require_results) float64 :: proc(r: ^Rand = nil) -> (val: f64) { return f64(int63_max(1<<53, r)) / (1 << 53) }
+
+/*
+Generates a random single floating point value in the range `(0, 1]` using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random single floating point value in the range `(0, 1]`
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ float32_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.float32())
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.float32(&my_rand))
+ }
+
+Possible Output:
+
+ 0.043
+ 0.511
+
+*/
+@(require_results) float32 :: proc(r: ^Rand = nil) -> (val: f32) { return f32(float64(r)) }
+
+/*
+Generates a random double floating point value in the range `(low, high]` using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- low: The lower bounds of the value, this value is inclusive
+- high: The upper bounds of the value, this value is exclusive
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random double floating point value in the range `(low, high]`
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ float64_range_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.float64_range(-10, 300))
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.float64_range(600, 900, &my_rand))
+ }
+
+Possible Output:
+
+ 15.312
+ 673.130
+
+*/
+@(require_results) float64_range :: proc(low, high: f64, r: ^Rand = nil) -> (val: f64) { return (high-low)*float64(r) + low }
+/*
+Generates a random single floating point value in the range `(low, high]` using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- low: The lower bounds of the value, this value is inclusive
+- high: The upper bounds of the value, this value is exclusive
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- val: A random single floating point value in the range `(low, high]`
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ float32_range_example :: proc() {
+ // Using the global random number generator
+ fmt.println(rand.float32_range(-10, 300))
+ // Using local random number generator
+ my_rand := rand.create(1)
+ fmt.println(rand.float32_range(600, 900, &my_rand))
+ }
+
+Possible Output:
+
+ 15.312
+ 673.130
+
+*/
+@(require_results) float32_range :: proc(low, high: f32, r: ^Rand = nil) -> (val: f32) { return (high-low)*float32(r) + low }
+
+/*
+Fills a byte slice with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- p: The byte slice to fill
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- n: The number of bytes generated
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ read_example :: proc() {
+ // Using the global random number generator
+ data: [8]byte
+ n := rand.read(data[:])
+ fmt.println(n)
+ fmt.println(data)
+ }
+
+Possible Output:
+
+ 8
+ [32, 4, 59, 7, 1, 2, 2, 119]
+
+*/
+@(require_results)
read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
pos := i8(0)
val := i64(0)
@@ -159,18 +687,81 @@ read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
return
}
-// perm returns a slice of n ints in a pseudo-random permutation of integers in the range [0, n)
-perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> []int {
- m := make([]int, n, allocator)
+/*
+Creates a slice of `int` filled with random values using the provided random number generator. If no generator is provided the global random number generator will be used.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- n: The size of the created slice
+- r: The random number generator to use, or nil for the global generator
+- allocator: (default: context.allocator)
+
+Returns:
+- res: A slice filled with random values
+- err: An allocator error if one occured, `nil` otherwise
+
+Example:
+ import "core:math/rand"
+ import "core:mem"
+ import "core:fmt"
+
+ perm_example :: proc() -> (err: mem.Allocator_Error) {
+ // Using the global random number generator and using the context allocator
+ data := rand.perm(4) or_return
+ fmt.println(data)
+ defer delete(data, context.allocator)
+
+ // Using local random number generator and temp allocator
+ my_rand := rand.create(1)
+ data_tmp := rand.perm(4, &my_rand, context.temp_allocator) or_return
+ fmt.println(data_tmp)
+
+ return
+ }
+
+Possible Output:
+
+ [7201011, 3, 9123, 231131]
+ [19578, 910081, 131, 7]
+
+*/
+@(require_results)
+perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> (res: []int, err: mem.Allocator_Error) #optional_allocator_error {
+ m := make([]int, n, allocator) or_return
for i := 0; i < n; i += 1 {
j := int_max(i+1, r)
m[i] = m[j]
m[j] = i
}
- return m
+ return m, {}
}
+/*
+Randomizes the ordering of elements for the provided slice. If no generator is provided the global random number generator will be used.
+Inputs:
+- array: The slice to randomize
+- r: The random number generator to use, or nil for the global generator
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ shuffle_example :: proc() {
+ // Using the global random number generator
+ data: [4]int = { 1, 2, 3, 4 }
+ fmt.println(data) // the contents are in order
+ rand.shuffle(data[:])
+ fmt.println(data) // the contents have been shuffled
+ }
+
+Possible Output:
+
+ [1, 2, 3, 4]
+ [2, 4, 3, 1]
+
+*/
shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
n := i64(len(array))
if n < 2 {
@@ -183,11 +774,43 @@ shuffle :: proc(array: $T/[]$E, r: ^Rand = nil) {
}
}
-// Returns a random element from the given slice
+/*
+Returns a random element from the provided slice. If no generator is provided the global random number generator will be used.
+
+Inputs:
+- array: The slice to choose an element from
+- r: The random number generator to use, or nil for the global generator
+
+Returns:
+- res: A random element from `array`
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ choice_example :: proc() {
+ // Using the global random number generator
+ data: [4]int = { 1, 2, 3, 4 }
+ fmt.println(rand.choice(data[:]))
+ fmt.println(rand.choice(data[:]))
+ fmt.println(rand.choice(data[:]))
+ fmt.println(rand.choice(data[:]))
+ }
+
+
+Possible Output:
+
+ 3
+ 2
+ 2
+ 4
+
+*/
+@(require_results)
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)]
-}
\ No newline at end of file
+}
diff --git a/core/math/rand/system_darwin.odin b/core/math/rand/system_darwin.odin
index f51e4473e..a2890d1b4 100644
--- a/core/math/rand/system_darwin.odin
+++ b/core/math/rand/system_darwin.odin
@@ -2,6 +2,7 @@ package rand
import "core:sys/darwin"
+@(require_results)
_system_random :: proc() -> u32 {
for {
value: u32
diff --git a/core/math/rand/system_linux.odin b/core/math/rand/system_linux.odin
index bfdc8872b..0e34228dc 100644
--- a/core/math/rand/system_linux.odin
+++ b/core/math/rand/system_linux.odin
@@ -2,6 +2,7 @@ package rand
import "core:sys/unix"
+@(require_results)
_system_random :: proc() -> u32 {
for {
value: u32
diff --git a/core/math/rand/system_windows.odin b/core/math/rand/system_windows.odin
index ee9cd0294..b6af9e6a4 100644
--- a/core/math/rand/system_windows.odin
+++ b/core/math/rand/system_windows.odin
@@ -2,6 +2,7 @@ package rand
import win32 "core:sys/windows"
+@(require_results)
_system_random :: proc() -> u32 {
value: u32
status := win32.BCryptGenRandom(nil, ([^]u8)(&value), 4, win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)
diff --git a/core/mem/alloc.odin b/core/mem/alloc.odin
index c7a21dcd4..57e82a5f8 100644
--- a/core/mem/alloc.odin
+++ b/core/mem/alloc.odin
@@ -56,19 +56,22 @@ Allocator :: struct {
DEFAULT_ALIGNMENT :: 2*align_of(rawptr)
DEFAULT_PAGE_SIZE ::
- 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64 else
+ 64 * 1024 when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 else
16 * 1024 when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 else
4 * 1024
-alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
- data, _ := runtime.mem_alloc(size, alignment, allocator, loc)
- return raw_data(data)
+@(require_results)
+alloc :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) {
+ data, err := runtime.mem_alloc(size, alignment, allocator, loc)
+ return raw_data(data), err
}
+@(require_results)
alloc_bytes :: proc(size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
return runtime.mem_alloc(size, alignment, allocator, loc)
}
+@(require_results)
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)
}
@@ -93,15 +96,18 @@ free_all :: proc(allocator := context.allocator, loc := #caller_location) -> All
return runtime.mem_free_all(allocator, loc)
}
-resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> rawptr {
- data, _ := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
- return raw_data(data)
+@(require_results)
+resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (rawptr, Allocator_Error) {
+ data, err := runtime.mem_resize(ptr, old_size, new_size, alignment, allocator, loc)
+ return raw_data(data), err
}
+@(require_results)
resize_bytes :: proc(old_data: []byte, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
return runtime.mem_resize(raw_data(old_data), len(old_data), new_size, alignment, allocator, loc)
}
+@(require_results)
query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: Allocator_Mode_Set) {
if allocator.procedure != nil {
allocator.procedure(allocator.data, .Query_Features, 0, 0, &set, 0, loc)
@@ -110,6 +116,7 @@ query_features :: proc(allocator: Allocator, loc := #caller_location) -> (set: A
return nil
}
+@(require_results)
query_info :: proc(pointer: rawptr, allocator: Allocator, loc := #caller_location) -> (props: Allocator_Query_Info) {
props.pointer = pointer
if allocator.procedure != nil {
@@ -146,14 +153,17 @@ delete :: proc{
}
+@(require_results)
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) {
return new_aligned(T, align_of(T), allocator, loc)
}
+@(require_results)
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := alloc_bytes(size_of(T), alignment, allocator, loc) or_return
t = (^T)(raw_data(data))
return
}
+@(require_results)
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
backing := alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return
t = (^T)(raw_data(backing))
@@ -164,6 +174,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
return nil, .Out_Of_Memory
}
+@(require_results)
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
@@ -173,15 +184,19 @@ make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocat
slice = transmute(T)Raw_Slice{raw_data(data), len}
return
}
+@(require_results)
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) {
return make_aligned(T, len, align_of(E), allocator, loc)
}
+@(require_results)
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, 16, allocator, loc)
}
+@(require_results)
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)
}
+@(require_results)
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) {
runtime.make_dynamic_array_error_loc(loc, len, cap)
data := alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
@@ -192,14 +207,15 @@ 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 = 1< T {
+@(require_results)
+make_map :: proc($T: typeid/map[$K]$E, #any_int cap: int = 1< (m: T, err: Allocator_Error) {
runtime.make_map_expr_error_loc(loc, cap)
context.allocator = allocator
- m: T
- reserve_map(&m, cap, loc)
- return m
+ err = reserve_map(&m, cap, loc)
+ return
}
+@(require_results)
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) {
runtime.make_slice_error_loc(loc, len)
data := alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
@@ -220,30 +236,14 @@ make :: proc{
}
-
-default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> rawptr {
- if old_memory == nil {
- return alloc(new_size, alignment, allocator, loc)
- }
-
- if new_size == 0 {
- free(old_memory, allocator, loc)
- return nil
- }
-
- if new_size == old_size {
- return old_memory
- }
-
- new_memory := alloc(new_size, alignment, allocator, loc)
- if new_memory == nil {
- return nil
- }
-
- copy(new_memory, old_memory, min(old_size, new_size))
- free(old_memory, allocator, loc)
- return new_memory
+@(require_results)
+default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> (res: rawptr, err: Allocator_Error) {
+ data: []byte
+ data, err = default_resize_bytes_align(([^]byte)(old_memory)[:old_size], new_size, alignment, allocator, loc)
+ res = raw_data(data)
+ return
}
+@(require_results)
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
old_memory := raw_data(old_data)
old_size := len(old_data)
diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin
index 66da12959..603c2a6c7 100644
--- a/core/mem/allocators.odin
+++ b/core/mem/allocators.odin
@@ -2,6 +2,7 @@ package mem
import "core:intrinsics"
import "core:runtime"
+import "core:sync"
nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
@@ -46,6 +47,7 @@ init_arena :: proc(a: ^Arena, data: []byte) {
a.temp_count = 0
}
+@(require_results)
arena_allocator :: proc(arena: ^Arena) -> Allocator {
return Allocator{
procedure = arena_allocator_proc,
@@ -100,6 +102,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil
}
+@(require_results)
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
tmp: Arena_Temp_Memory
tmp.arena = a
@@ -286,6 +289,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil
}
+@(require_results)
scratch_allocator :: proc(allocator: ^Scratch_Allocator) -> Allocator {
return Allocator{
procedure = scratch_allocator_proc,
@@ -325,6 +329,7 @@ init_stack :: proc(s: ^Stack, data: []byte) {
s.peak_used = 0
}
+@(require_results)
stack_allocator :: proc(stack: ^Stack) -> Allocator {
return Allocator{
procedure = stack_allocator_proc,
@@ -490,6 +495,7 @@ init_small_stack :: proc(s: ^Small_Stack, data: []byte) {
s.peak_used = 0
}
+@(require_results)
small_stack_allocator :: proc(stack: ^Small_Stack) -> Allocator {
return Allocator{
procedure = small_stack_allocator_proc,
@@ -673,6 +679,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
}
+@(require_results)
dynamic_pool_allocator :: proc(pool: ^Dynamic_Pool) -> Allocator {
return Allocator{
procedure = dynamic_pool_allocator_proc,
@@ -705,12 +712,13 @@ dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
}
-dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> rawptr {
+@(require_results)
+dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocator_Error) {
data, err := dynamic_pool_alloc_bytes(pool, bytes)
- assert(err == nil)
- return raw_data(data)
+ return raw_data(data), err
}
+@(require_results)
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
if block_allocator.procedure == nil {
@@ -836,6 +844,7 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
return nil, nil
}
+@(require_results)
panic_allocator :: proc() -> Allocator {
return Allocator{
procedure = panic_allocator_proc,
@@ -860,6 +869,7 @@ Tracking_Allocator :: struct {
backing: Allocator,
allocation_map: map[rawptr]Tracking_Allocator_Entry,
bad_free_array: [dynamic]Tracking_Allocator_Bad_Free_Entry,
+ mutex: sync.Mutex,
clear_on_free_all: bool,
}
@@ -880,11 +890,14 @@ tracking_allocator_destroy :: proc(t: ^Tracking_Allocator) {
tracking_allocator_clear :: proc(t: ^Tracking_Allocator) {
+ sync.mutex_lock(&t.mutex)
clear(&t.allocation_map)
clear(&t.bad_free_array)
+ sync.mutex_unlock(&t.mutex)
}
+@(require_results)
tracking_allocator :: proc(data: ^Tracking_Allocator) -> Allocator {
return Allocator{
data = data,
@@ -896,6 +909,9 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> (result: []byte, err: Allocator_Error) {
data := (^Tracking_Allocator)(allocator_data)
+
+ sync.mutex_guard(&data.mutex)
+
if mode == .Query_Info {
info := (^Allocator_Query_Info)(old_memory)
if info != nil && info.pointer != nil {
diff --git a/core/mem/doc.odin b/core/mem/doc.odin
index fe53dee83..295a69e96 100644
--- a/core/mem/doc.odin
+++ b/core/mem/doc.odin
@@ -18,6 +18,7 @@ _main :: proc() {
main :: proc() {
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)
+ defer mem.tracking_allocator_destroy(&track)
context.allocator = mem.tracking_allocator(&track)
_main()
@@ -31,4 +32,4 @@ main :: proc() {
}
```
*/
-package mem
\ No newline at end of file
+package mem
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index bc77ca287..a06579d71 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -53,14 +53,17 @@ compare :: proc "contextless" (a, b: []byte) -> int {
return res
}
+@(require_results)
compare_byte_ptrs :: proc "contextless" (a, b: ^byte, n: int) -> int #no_bounds_check {
return runtime.memory_compare(a, b, n)
}
+@(require_results)
check_zero :: proc(data: []byte) -> bool {
return check_zero_ptr(raw_data(data), len(data))
}
+@(require_results)
check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
switch {
case len <= 0:
@@ -101,11 +104,13 @@ check_zero_ptr :: proc(ptr: rawptr, len: int) -> bool {
return true
}
+@(require_results)
simple_equal :: proc "contextless" (a, b: $T) -> bool where intrinsics.type_is_simple_compare(T) {
a, b := a, b
return compare_byte_ptrs((^byte)(&a), (^byte)(&b), size_of(T)) == 0
}
+@(require_results)
compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int {
return compare_byte_ptrs((^byte)(a), (^byte)(b), n)
}
@@ -113,20 +118,24 @@ compare_ptrs :: proc "contextless" (a, b: rawptr, n: int) -> int {
ptr_offset :: intrinsics.ptr_offset
ptr_sub :: intrinsics.ptr_sub
+@(require_results)
slice_ptr :: proc "contextless" (ptr: ^$T, len: int) -> []T {
return ([^]T)(ptr)[:len]
}
+@(require_results)
byte_slice :: #force_inline proc "contextless" (data: rawptr, #any_int len: int) -> []byte {
return ([^]u8)(data)[:max(len, 0)]
}
+@(require_results)
slice_to_bytes :: proc "contextless" (slice: $E/[]$T) -> []byte {
s := transmute(Raw_Slice)slice
s.len *= size_of(T)
return transmute([]byte)s
}
+@(require_results)
slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
when size_of(A) == 0 || size_of(B) == 0 {
return nil
@@ -137,11 +146,13 @@ slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
}
}
+@(require_results)
slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) {
s := transmute(Raw_Slice)slice
return (^T)(s.data), s.len
}
+@(require_results)
buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E {
return transmute([dynamic]E)Raw_Dynamic_Array{
data = raw_data(backing),
@@ -154,10 +165,12 @@ buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E {
}
}
+@(require_results)
ptr_to_bytes :: proc "contextless" (ptr: ^$T, len := 1) -> []byte {
return transmute([]byte)Raw_Slice{ptr, len*size_of(T)}
}
+@(require_results)
any_to_bytes :: proc "contextless" (val: any) -> []byte {
ti := type_info_of(val.id)
size := ti != nil ? ti.size : 0
@@ -165,6 +178,7 @@ any_to_bytes :: proc "contextless" (val: any) -> []byte {
}
+@(require_results)
is_power_of_two :: proc "contextless" (x: uintptr) -> bool {
if x <= 0 {
return false
@@ -172,10 +186,12 @@ is_power_of_two :: proc "contextless" (x: uintptr) -> bool {
return (x & (x-1)) == 0
}
+@(require_results)
align_forward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_forward_uintptr(uintptr(ptr), align))
}
+@(require_results)
align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
assert(is_power_of_two(align))
@@ -187,33 +203,41 @@ align_forward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
return p
}
+@(require_results)
align_forward_int :: proc(ptr, align: int) -> int {
return int(align_forward_uintptr(uintptr(ptr), uintptr(align)))
}
+@(require_results)
align_forward_uint :: proc(ptr, align: uint) -> uint {
return uint(align_forward_uintptr(uintptr(ptr), uintptr(align)))
}
+@(require_results)
align_backward :: proc(ptr: rawptr, align: uintptr) -> rawptr {
return rawptr(align_backward_uintptr(uintptr(ptr), align))
}
+@(require_results)
align_backward_uintptr :: proc(ptr, align: uintptr) -> uintptr {
return align_forward_uintptr(ptr - align + 1, align)
}
+@(require_results)
align_backward_int :: proc(ptr, align: int) -> int {
return int(align_backward_uintptr(uintptr(ptr), uintptr(align)))
}
+@(require_results)
align_backward_uint :: proc(ptr, align: uint) -> uint {
return uint(align_backward_uintptr(uintptr(ptr), uintptr(align)))
}
+@(require_results)
context_from_allocator :: proc(a: Allocator) -> type_of(context) {
context.allocator = a
return context
}
+@(require_results)
reinterpret_copy :: proc "contextless" ($T: typeid, ptr: rawptr) -> (value: T) {
copy(&value, ptr, size_of(T))
return
@@ -222,6 +246,7 @@ reinterpret_copy :: proc "contextless" ($T: typeid, ptr: rawptr) -> (value: T) {
Fixed_Byte_Buffer :: distinct [dynamic]byte
+@(require_results)
make_fixed_byte_buffer :: proc "contextless" (backing: []byte) -> Fixed_Byte_Buffer {
s := transmute(Raw_Slice)backing
d: Raw_Dynamic_Array
@@ -237,11 +262,13 @@ make_fixed_byte_buffer :: proc "contextless" (backing: []byte) -> Fixed_Byte_Buf
+@(require_results)
align_formula :: proc "contextless" (size, align: int) -> int {
result := size + align-1
return result - result%align
}
+@(require_results)
calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, header_size: int) -> int {
p, a := ptr, align
modulo := p & (a-1)
@@ -267,6 +294,7 @@ calc_padding_with_header :: proc "contextless" (ptr: uintptr, align: uintptr, he
+@(require_results, deprecated="prefer 'slice.clone'")
clone_slice :: proc(slice: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> (new_slice: T) {
new_slice, _ = make(T, len(slice), allocator, loc)
runtime.copy(new_slice, slice)
diff --git a/core/mem/virtual/arena.odin b/core/mem/virtual/arena.odin
index 0ddb116fd..e73a39660 100644
--- a/core/mem/virtual/arena.odin
+++ b/core/mem/virtual/arena.odin
@@ -1,6 +1,7 @@
package mem_virtual
import "core:mem"
+import "core:sync"
Arena_Kind :: enum uint {
Growing = 0, // Chained memory blocks (singly linked list).
@@ -8,6 +9,13 @@ Arena_Kind :: enum uint {
Buffer = 2, // Uses a fixed sized buffer.
}
+/*
+ Arena is a generalized arena allocator that supports 3 different variants.
+
+ Growing: A linked list of `Memory_Block`s allocated with virtual memory.
+ Static: A single `Memory_Block` allocated with virtual memory.
+ Buffer: A single `Memory_Block` created from a user provided []byte.
+*/
Arena :: struct {
kind: Arena_Kind,
curr_block: ^Memory_Block,
@@ -15,6 +23,7 @@ Arena :: struct {
total_reserved: uint,
minimum_block_size: uint,
temp_count: uint,
+ mutex: sync.Mutex,
}
@@ -27,6 +36,8 @@ DEFAULT_ARENA_STATIC_RESERVE_SIZE :: mem.Gigabyte when size_of(uintptr) == 8 els
+// Initialization of an `Arena` to be a `.Growing` variant.
+// A growing arena is a linked list of `Memory_Block`s allocated with virtual memory.
@(require_results)
arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE) -> (err: Allocator_Error) {
arena.kind = .Growing
@@ -37,6 +48,8 @@ arena_init_growing :: proc(arena: ^Arena, reserved: uint = DEFAULT_ARENA_GROWING
}
+// Initialization of an `Arena` to be a `.Static` variant.
+// A static arena contains a single `Memory_Block` allocated with virtual memory.
@(require_results)
arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEFAULT_ARENA_STATIC_COMMIT_SIZE) -> (err: Allocator_Error) {
arena.kind = .Static
@@ -46,6 +59,8 @@ arena_init_static :: proc(arena: ^Arena, reserved: uint, commit_size: uint = DEF
return
}
+// Initialization of an `Arena` to be a `.Buffer` variant.
+// A buffer arena contains single `Memory_Block` created from a user provided []byte.
@(require_results)
arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Error) {
if len(buffer) < size_of(Memory_Block) {
@@ -69,6 +84,7 @@ arena_init_buffer :: proc(arena: ^Arena, buffer: []byte) -> (err: Allocator_Erro
return
}
+// Allocates memory from the provided arena.
@(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)
@@ -78,6 +94,8 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
return nil, nil
}
+ sync.mutex_guard(&arena.mutex)
+
switch arena.kind {
case .Growing:
if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.reserved {
@@ -102,7 +120,7 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
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
+ arena_init_static(arena, reserved=arena.minimum_block_size, commit_size=DEFAULT_ARENA_STATIC_COMMIT_SIZE) or_return
}
fallthrough
case .Buffer:
@@ -115,7 +133,10 @@ arena_alloc :: proc(arena: ^Arena, size: uint, alignment: uint, loc := #caller_l
return
}
+// Resets the memory of a Static or Buffer arena to a specific `pos`ition (offset) and zeroes the previously used memory.
arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location) -> bool {
+ sync.mutex_guard(&arena.mutex)
+
if arena.curr_block != nil {
assert(arena.kind != .Growing, "expected a non .Growing arena", loc)
@@ -134,48 +155,72 @@ arena_static_reset_to :: proc(arena: ^Arena, pos: uint, loc := #caller_location)
return false
}
+// Frees the last memory block of a Growing Arena
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.total_used -= free_block.used
+ arena.total_reserved -= free_block.reserved
+
arena.curr_block = free_block.prev
memory_block_dealloc(free_block)
}
}
-arena_free_all :: proc(arena: ^Arena) {
+// Deallocates all but the first memory block of the arena and resets the allocator's usage to 0.
+arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
switch arena.kind {
case .Growing:
- for arena.curr_block != nil {
- arena_growing_free_last_memory_block(arena)
+ sync.mutex_guard(&arena.mutex)
+ // NOTE(bill): Free all but the first memory block (if it exists)
+ for arena.curr_block != nil && arena.curr_block.prev != nil {
+ arena_growing_free_last_memory_block(arena, loc)
}
- arena.total_reserved = 0
+ // Zero the first block's memory
+ if arena.curr_block != nil {
+ mem.zero(arena.curr_block.base, int(arena.curr_block.used))
+ arena.curr_block.used = 0
+ }
+ arena.total_used = 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 {
+// Frees all of the memory allocated by the arena and zeros all of the values of an arena.
+// A buffer based arena does not `delete` the provided `[]byte` bufffer.
+arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
+ sync.mutex_guard(&arena.mutex)
+ switch arena.kind {
+ case .Growing:
+ for arena.curr_block != nil {
+ arena_growing_free_last_memory_block(arena, loc)
+ }
+ case .Static:
memory_block_dealloc(arena.curr_block)
+ case .Buffer:
+ // nothing
}
- arena.curr_block = nil
+ arena.curr_block = nil
arena.total_used = 0
arena.total_reserved = 0
arena.temp_count = 0
}
+// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
arena_growing_bootstrap_new :: proc{
arena_growing_bootstrap_new_by_offset,
arena_growing_bootstrap_new_by_name,
}
+// Ability to bootstrap allocate a struct with an arena within the struct itself using the static variant strategy.
arena_static_bootstrap_new :: proc{
arena_static_bootstrap_new_by_offset,
arena_static_bootstrap_new_by_name,
}
+// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(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
@@ -191,11 +236,13 @@ arena_growing_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintp
return
}
+// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(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)
}
+// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(require_results)
arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintptr, reserved: uint) -> (ptr: ^T, err: Allocator_Error) {
bootstrap: Arena
@@ -211,17 +258,20 @@ arena_static_bootstrap_new_by_offset :: proc($T: typeid, offset_to_arena: uintpt
return
}
+// Ability to bootstrap allocate a struct with an arena within the struct itself using the growing variant strategy.
@(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)
}
+// Create an `Allocator` from the provided `Arena`
@(require_results)
arena_allocator :: proc(arena: ^Arena) -> mem.Allocator {
return mem.Allocator{arena_allocator_proc, arena}
}
+// The allocator procedured by an `Allocator` produced by `arena_allocator`
arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int,
@@ -233,17 +283,17 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
switch mode {
case .Alloc, .Alloc_Non_Zeroed:
- return arena_alloc(arena, size, alignment)
+ return arena_alloc(arena, size, alignment, location)
case .Free:
err = .Mode_Not_Implemented
case .Free_All:
- arena_free_all(arena)
+ arena_free_all(arena, location)
case .Resize:
old_data := ([^]byte)(old_memory)
switch {
case old_data == nil:
- return arena_alloc(arena, size, alignment)
+ return arena_alloc(arena, size, alignment, location)
case size == old_size:
// return old memory
data = old_data[:size]
@@ -257,7 +307,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
return
}
- new_memory := arena_alloc(arena, size, alignment) or_return
+ new_memory := arena_alloc(arena, size, alignment, location) or_return
if new_memory == nil {
return
}
@@ -278,15 +328,20 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
+// An `Arena_Temp` is a way to produce temporary watermarks to reset a arena to a previous state.
+// All uses of an `Arena_Temp` must be handled by ending them with `arena_temp_end` or ignoring them with `arena_temp_ignore`.
Arena_Temp :: struct {
arena: ^Arena,
block: ^Memory_Block,
used: uint,
}
+// Begins the section of temporary arena memory.
@(require_results)
arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena_Temp) {
assert(arena != nil, "nil arena", loc)
+ sync.mutex_guard(&arena.mutex)
+
temp.arena = arena
temp.block = arena.curr_block
if arena.curr_block != nil {
@@ -296,36 +351,51 @@ arena_temp_begin :: proc(arena: ^Arena, loc := #caller_location) -> (temp: Arena
return
}
+// Ends the section of temporary arena memory by resetting the memory to the stored position.
arena_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
assert(temp.arena != nil, "nil arena", loc)
arena := temp.arena
+ sync.mutex_guard(&arena.mutex)
- memory_block_found := false
- for block := arena.curr_block; block != nil; block = block.prev {
- if block == temp.block {
- memory_block_found = true
- break
+ if temp.block != nil {
+ 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)
}
- }
- 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)
- }
+ 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
+ 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
}
+// Ignore the use of a `arena_temp_begin` entirely by __not__ resetting to the stored position.
+arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
+ assert(temp.arena != nil, "nil arena", loc)
+ arena := temp.arena
+ sync.mutex_guard(&arena.mutex)
+
+ assert(arena.temp_count > 0, "double-use of arena_temp_end", loc)
+ arena.temp_count -= 1
+}
+
+// Asserts that all uses of `Arena_Temp` has been used by an `Arena`
arena_check_temp :: proc(arena: ^Arena, loc := #caller_location) {
assert(arena.temp_count == 0, "Arena_Temp not been ended", loc)
}
diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin
index 5e831a0c3..12815b7f1 100644
--- a/core/mem/virtual/virtual.odin
+++ b/core/mem/virtual/virtual.odin
@@ -7,6 +7,7 @@ DEFAULT_PAGE_SIZE := uint(4096)
Allocator_Error :: mem.Allocator_Error
+@(require_results)
reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
return _reserve(size)
}
@@ -15,6 +16,7 @@ commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
return _commit(data, size)
}
+@(require_results)
reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
data = reserve(size) or_return
commit(raw_data(data), size) or_return
@@ -57,6 +59,7 @@ Memory_Block_Flag :: enum u32 {
Memory_Block_Flags :: distinct bit_set[Memory_Block_Flag; u32]
+@(require_results)
memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags) -> (block: ^Memory_Block, err: Allocator_Error) {
align_formula :: proc "contextless" (size, align: uint) -> uint {
result := size + align-1
@@ -100,6 +103,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
return &pmblock.block, nil
}
+@(require_results)
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)
@@ -160,7 +164,7 @@ memory_block_dealloc :: proc(block_to_free: ^Memory_Block) {
-@(private)
+@(private, require_results)
safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
z, did_overflow := intrinsics.overflow_add(x, y)
return z, !did_overflow
diff --git a/core/mem/virtual/virtual_bsd.odin b/core/mem/virtual/virtual_bsd.odin
new file mode 100644
index 000000000..103e48074
--- /dev/null
+++ b/core/mem/virtual/virtual_bsd.odin
@@ -0,0 +1,24 @@
+//+build freebsd, openbsd
+//+private
+package mem_virtual
+
+
+
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
+ return nil, nil
+}
+
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
+ return nil
+}
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
+}
+_release :: proc "contextless" (data: rawptr, size: uint) {
+}
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+ return false
+}
+
+_platform_memory_init :: proc() {
+
+}
diff --git a/core/net/addr.odin b/core/net/addr.odin
new file mode 100644
index 000000000..e6b503aaf
--- /dev/null
+++ b/core/net/addr.odin
@@ -0,0 +1,744 @@
+// +build windows, linux, darwin
+package net
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:strconv"
+import "core:strings"
+import "core:fmt"
+
+/*
+ Expects an IPv4 address with no leading or trailing whitespace:
+ - a.b.c.d
+ - a.b.c.d:port
+ - [a.b.c.d]:port
+
+ If the IP address is bracketed, the port must be present and valid (though it will be ignored):
+ - [a.b.c.d] will be treated as a parsing failure.
+
+ The port, if present, is required to be a base 10 number in the range 0-65535, inclusive.
+
+ If `allow_non_decimal` is false, `aton` is told each component must be decimal and max 255.
+*/
+parse_ip4_address :: proc(address_and_maybe_port: string, allow_non_decimal := false) -> (addr: IP4_Address, ok: bool) {
+ res := aton(address_and_maybe_port, .IP4, !allow_non_decimal) or_return
+ return res.?
+}
+
+/*
+ Parses an IP address in "non-decimal" `inet_aton` form.
+
+ e.g."00377.0x0ff.65534" = 255.255.255.254
+ 00377 = 255 in octal
+ 0x0ff = 255 in hexadecimal
+ This leaves 16 bits worth of address
+ .65534 then accounts for the last two digits
+
+ For the address part the allowed forms are:
+ a.b.c.d - where each part represents a byte
+ a.b.c - where `a` & `b` represent a byte and `c` a u16
+ a.b - where `a` represents a byte and `b` supplies the trailing 24 bits
+ a - where `a` gives the entire 32-bit value
+
+ The port, if present, is required to be a base 10 number in the range 0-65535, inclusive.
+*/
+aton :: proc(address_and_maybe_port: string, family: Address_Family, allow_decimal_only := false) -> (addr: Address, ok: bool) {
+ switch family {
+ case .IP4:
+ // There is no valid address shorter than `0.0.0.0`.
+ if len(address_and_maybe_port) < 7 {
+ return {}, false
+ }
+
+ address, _ := split_port(address_and_maybe_port) or_return // This call doesn't allocate
+
+ buf: [4]u64 = {}
+ i := 0
+
+ max_value := u64(max(u32))
+ bases := DEFAULT_DIGIT_BASES
+
+ if allow_decimal_only {
+ max_value = 255
+ bases = {.Dec}
+ }
+
+ for len(address) > 0 {
+ if i == 4 {
+ return {}, false
+ }
+
+ // Decimal-only addresses may not have a leading zero.
+ if allow_decimal_only && len(address) > 1 && address[0] == '0' && address[1] != '.' {
+ return
+ }
+
+ number, consumed, number_ok := parse_ip_component(address, max_value, bases)
+ if !number_ok || consumed == 0 {
+ return {}, false
+ }
+
+ buf[i] = number
+
+ address = address[consumed:]
+
+ if len(address) > 0 && address[0] == '.' {
+ address = address[1:]
+ }
+ i += 1
+ }
+
+ // Distribute parts.
+ switch i {
+ case 1:
+ buf[1] = buf[0] & 0xffffff
+ buf[0] >>= 24
+ fallthrough
+ case 2:
+ buf[2] = buf[1] & 0xffff
+ buf[1] >>= 16
+ fallthrough
+ case 3:
+ buf[3] = buf[2] & 0xff
+ buf[2] >>= 8
+ }
+
+ a: [4]u8 = ---
+ for v, i in buf {
+ if v > 255 { return {}, false }
+ a[i] = u8(v)
+ }
+ return IP4_Address(a), true
+
+ case .IP6:
+ return parse_ip6_address(address_and_maybe_port)
+
+ case:
+ return nil, false
+ }
+}
+
+/*
+ The minimum length of a valid IPv6 address string is 2, e.g. `::`
+
+ The maximum length of a valid IPv6 address string is 45, when it embeds an IPv4,
+ e.g. `0000:0000:0000:0000:0000:ffff:255.255.255.255`
+
+ An IPv6 address must contain at least 3 pieces, e.g. `::`,
+ and at most 9 (using `::` for a trailing or leading 0)
+*/
+IPv6_MIN_STRING_LENGTH :: 2
+IPv6_MAX_STRING_LENGTH :: 45
+IPv6_MIN_COLONS :: 2
+IPv6_PIECE_COUNT :: 8
+
+parse_ip6_address :: proc(address_and_maybe_port: string) -> (addr: IP6_Address, ok: bool) {
+ // If we have an IPv6 address of the form [IP]:Port, first get us just the IP.
+ address, _ := split_port(address_and_maybe_port) or_return
+
+ // Early bailouts based on length and number of pieces.
+ if len(address) < IPv6_MIN_STRING_LENGTH || len(address) > IPv6_MAX_STRING_LENGTH { return }
+
+ /*
+ Do a pre-pass on the string that checks how many `:` and `.` we have,
+ if they're in the right order, and if the things between them are digits as expected.
+
+ It's not strictly necessary considering we could use `strings.split`,
+ but this way we can avoid using an allocator and return earlier on bogus input. Win-win.
+ */
+ colon_count := 0
+ dot_count := 0
+
+ pieces_temp: [IPv6_PIECE_COUNT + 1]string
+
+ piece_start := 0
+ piece_end := 0
+
+ for ch, i in address {
+ switch ch {
+ case '0'..='9', 'a'..='f', 'A'..='F':
+ piece_end += 1
+
+ case ':':
+ // If we see a `:` after a `.`, it means an IPv4 part was sandwiched between IPv6, instead of it being the tail: invalid.
+ if dot_count > 0 { return }
+
+ pieces_temp[colon_count] = address[piece_start:piece_end]
+
+ colon_count += 1
+ if colon_count > IPv6_PIECE_COUNT { return }
+
+ // If there's anything left, put it in the next piece.
+ piece_start = i + 1
+ piece_end = piece_start
+
+ case '.':
+ // IPv4 address is treated as one piece. No need to update `piece_*`.
+ dot_count += 1
+
+ case: // Invalid character, return early
+ return
+ }
+ }
+
+ if colon_count < IPv6_MIN_COLONS { return }
+
+ // Assign the last piece string.
+ pieces_temp[colon_count] = address[piece_start:]
+
+ // `pieces` now holds the same output as it would if had used `strings.split`.
+ pieces := pieces_temp[:colon_count + 1]
+
+ // Check if we have what looks like an embedded IPv4 address.
+ ipv4: IP4_Address
+ have_ipv4: bool
+
+ if dot_count > 0 {
+ /*
+ If we have an IPv4 address accounting for the last 32 bits,
+ this means we can have at most 6 IPv6 pieces, like so: `x:x:X:x:x:x:d.d.d.d`
+
+ Or, put differently: 6 pieces IPv6 (5 colons), a colon, 1 piece IPv4 (3 dots),
+ for a total of 6 colons and 3 dots.
+ */
+ if dot_count != 3 || colon_count > 6 { return }
+
+ /*
+ Try to parse IPv4 address.
+ If successful, we have our least significant 32 bits.
+ If not, it invalidates the whole address and we can bail.
+ */
+ ipv4, have_ipv4 = parse_ip4_address(pieces_temp[colon_count])
+ if !have_ipv4 { return }
+ }
+
+ // Check for `::` being used more than once, and save the skip.
+ zero_skip := -1
+ for i in 1.. 0 { return }
+ }
+
+ // Adjust for trailing `::`.
+ if pieces[colon_count] == "" {
+ after_skip -= 1
+ // Trailing `:` can only be part of `::`.
+ if after_skip > 0 { return }
+ }
+
+ /*
+ Calculate how many zero pieces we skipped.
+ It should be at least one, considering we encountered a `::`.
+ */
+ num_skipped = IPv6_PIECE_COUNT - before_skip - after_skip
+ if num_skipped < 1 { return }
+
+ } else {
+ /*
+ No zero skip means everything is part of "before the skip".
+ An IPv4 "piece" accounts for 2 IPv6 pieces we haven't added to the pieces slice, so add 1.
+ */
+ piece_count := colon_count + 1
+ if have_ipv4 {
+ piece_count += 1
+ }
+
+ // Do we have the complete set?
+ if piece_count != IPv6_PIECE_COUNT { return }
+
+ // Validate leading and trailing empty parts, as they can only be part of a `::`.
+ if pieces[0] == "" || pieces[colon_count] == "" { return }
+
+
+ before_skip = piece_count
+ after_skip = 0
+ num_skipped = 0
+ }
+
+ // Now try to parse the pieces into a 8 16-bit pieces.
+ piece_values: [IPv6_PIECE_COUNT]u16be
+
+ idx := 0
+ val_idx := 0
+
+ for _ in 0.. 4 { return }
+
+ if piece != "" {
+ val, _ := parse_ip_component(piece, 65535, {.IPv6}) or_return
+ piece_values[val_idx] = u16be(val)
+ }
+
+ idx += 1
+ val_idx += 1
+ }
+
+ if before_skip == 0 {
+ idx += 1
+ }
+
+ if num_skipped > 0 {
+ idx += 1
+ val_idx += num_skipped
+ }
+
+ if after_skip > 0 {
+ for _ in 0.. 4 { return }
+
+ if piece != "" {
+ val, _ := parse_ip_component(piece, 65535, {.IPv6}) or_return
+ piece_values[val_idx] = u16be(val)
+ }
+
+ idx += 1
+ val_idx += 1
+ }
+ }
+
+ // Distribute IPv4 address into last two pieces, if applicable.
+ if have_ipv4 {
+ val := u16(ipv4[0]) << 8
+ val |= u16(ipv4[1])
+ piece_values[6] = u16be(val)
+
+ val = u16(ipv4[2]) << 8
+ val |= u16(ipv4[3])
+ piece_values[7] = u16be(val)
+ }
+ return transmute(IP6_Address)piece_values, true
+}
+
+/*
+ Try parsing as an IPv6 address.
+ If it's determined not to be, try as an IPv4 address, optionally in non-decimal format.
+*/
+parse_address :: proc(address_and_maybe_port: string, non_decimal_address := false) -> Address {
+ if addr6, ok6 := parse_ip6_address(address_and_maybe_port); ok6 {
+ return addr6
+ }
+ if addr4, ok4 := parse_ip4_address(address_and_maybe_port, non_decimal_address); ok4 {
+ return addr4
+ }
+ return nil
+}
+
+parse_endpoint :: proc(endpoint_str: string) -> (ep: Endpoint, ok: bool) {
+ if addr_str, port, split_ok := split_port(endpoint_str); split_ok {
+ if addr := parse_address(addr_str); addr != nil {
+ return Endpoint { address = addr, port = port }, true
+ }
+ }
+ return
+}
+
+Host :: struct {
+ hostname: string,
+ port: int,
+}
+Host_Or_Endpoint :: union {
+ Host,
+ Endpoint,
+}
+
+// Takes a string consisting of a hostname or IP address, and an optional port,
+// and return the component parts in a useful form.
+parse_hostname_or_endpoint :: proc(endpoint_str: string) -> (target: Host_Or_Endpoint, err: Parse_Endpoint_Error) {
+ host, port, port_ok := split_port(endpoint_str)
+ if !port_ok {
+ return nil, .Bad_Port
+ }
+ if addr := parse_address(host); addr != nil {
+ return Endpoint{addr, port}, .None
+ }
+ if !validate_hostname(host) {
+ return nil, .Bad_Hostname
+ }
+ return Host{host, port}, .None
+}
+
+
+// Takes an endpoint string and returns its parts.
+// Returns ok=false if port is not a number.
+split_port :: proc(endpoint_str: string) -> (addr_or_host: string, port: int, ok: bool) {
+ // IP6 [addr_or_host]:port
+ if i := strings.last_index(endpoint_str, "]:"); i >= 0 {
+ addr_or_host = endpoint_str[1:i]
+ port, ok = strconv.parse_int(endpoint_str[i+2:], 10)
+
+ if port > 65535 {
+ ok = false
+ }
+ return
+ }
+
+ if n := strings.count(endpoint_str, ":"); n == 1 {
+ // IP4 addr_or_host:port
+ i := strings.last_index(endpoint_str, ":")
+ assert(i != -1)
+
+ addr_or_host = endpoint_str[:i]
+ port, ok = strconv.parse_int(endpoint_str[i+1:], 10)
+
+ if port > 65535 {
+ ok = false
+ }
+ return
+ } else if n > 1 {
+ // IP6 address without port
+ }
+
+ // No port
+ addr_or_host = endpoint_str
+ port = 0
+ ok = true
+ return
+}
+
+// Joins an address or hostname with a port.
+join_port :: proc(address_or_host: string, port: int, allocator := context.allocator) -> string {
+ addr_or_host, _, ok := split_port(address_or_host)
+ if !ok do return addr_or_host
+
+ b := strings.builder_make(allocator)
+
+ addr := parse_address(addr_or_host)
+ if addr == nil {
+ // hostname
+ fmt.sbprintf(&b, "%v:%v", addr_or_host, port)
+ } else {
+ switch in addr {
+ case IP4_Address:
+ fmt.sbprintf(&b, "%v:%v", address_to_string(addr), port)
+ case IP6_Address:
+ fmt.sbprintf(&b, "[%v]:%v", address_to_string(addr), port)
+ }
+ }
+ return strings.to_string(b)
+}
+
+
+
+// TODO(tetra): Do we need this?
+map_to_ip6 :: proc(addr: Address) -> Address {
+ if addr6, ok := addr.(IP6_Address); ok {
+ return addr6
+ }
+ addr4 := addr.(IP4_Address)
+ addr4_u16 := transmute([2]u16be) addr4
+ addr6: IP6_Address
+ addr6[4] = 0xffff
+ copy(addr6[5:], addr4_u16[:])
+ return addr6
+}
+
+/*
+ Returns a temporarily-allocated string representation of the address.
+
+ See RFC 5952 section 4 for IPv6 representation recommendations.
+*/
+address_to_string :: proc(addr: Address, allocator := context.temp_allocator) -> string {
+ b := strings.builder_make(allocator)
+ switch v in addr {
+ case IP4_Address:
+ fmt.sbprintf(&b, "%v.%v.%v.%v", v[0], v[1], v[2], v[3])
+ case IP6_Address:
+ // First find the longest run of zeroes.
+ Zero_Run :: struct {
+ start: int,
+ end: int,
+ }
+
+ /*
+ We're dealing with 0-based indices, appropriately enough for runs of zeroes.
+ Still, it means we need to initialize runs with some value outside of the possible range.
+ */
+ run := Zero_Run{-1, -1}
+ best := Zero_Run{-1, -1}
+
+ addr := transmute([8]u16be)v
+
+ last := u16be(1)
+ for val, i in addr {
+ /*
+ If we encounter adjacent zeroes, then start a new run if not already in one.
+ Also remember the rightmost index regardless, because it'll be the new
+ frontier of both new and existing runs.
+ */
+ if last == 0 && val == 0 {
+ run.end = i
+ if run.start == -1 {
+ run.start = i - 1
+ }
+ }
+
+ /*
+ If we're in a run check if its length is better than the best recorded so far.
+ If so, update the best run's start and end.
+ */
+ if run.start != -1 {
+ length_to_beat := best.end - best.start
+ length := run.end - run.start
+
+ if length > length_to_beat {
+ best = run
+ }
+ }
+
+ // If we were in a run, this is where we reset it.
+ if val != 0 {
+ run = {-1, -1}
+ }
+
+ last = val
+ }
+
+ for val, i in addr {
+ if best.start == i || best.end == i {
+ // For the left and right side of the best zero run, print a `:`.
+ fmt.sbprint(&b, ":")
+ } else if i < best.start {
+ /*
+ If we haven't made it to the best run yet, print the digit.
+ Make sure we only print a `:` after the digit if it's not
+ immediately followed by the run's own leftmost `:`.
+ */
+ fmt.sbprintf(&b, "%x", val)
+ if i < best.start - 1 {
+ fmt.sbprintf(&b, ":")
+ }
+ } else if i > best.end {
+ /*
+ If there are any digits after the zero run, print them.
+ But don't print the `:` at the end of the IP number.
+ */
+ fmt.sbprintf(&b, "%x", val)
+ if i != 7 {
+ fmt.sbprintf(&b, ":")
+ }
+ }
+ }
+ }
+ return strings.to_string(b)
+}
+
+// Returns a temporarily-allocated string representation of the endpoint.
+// If there's a port, uses the `[address]:port` format.
+endpoint_to_string :: proc(ep: Endpoint, allocator := context.temp_allocator) -> string {
+ if ep.port == 0 {
+ return address_to_string(ep.address, allocator)
+ } else {
+ s := address_to_string(ep.address, context.temp_allocator)
+ b := strings.builder_make(allocator)
+ switch a in ep.address {
+ case IP4_Address: fmt.sbprintf(&b, "%v:%v", s, ep.port)
+ case IP6_Address: fmt.sbprintf(&b, "[%v]:%v", s, ep.port)
+ }
+ return strings.to_string(b)
+ }
+}
+
+to_string :: proc{address_to_string, endpoint_to_string}
+
+
+family_from_address :: proc(addr: Address) -> Address_Family {
+ switch in addr {
+ case IP4_Address: return .IP4
+ case IP6_Address: return .IP6
+ case:
+ unreachable()
+ }
+}
+family_from_endpoint :: proc(ep: Endpoint) -> Address_Family {
+ return family_from_address(ep.address)
+}
+
+
+Digit_Parse_Base :: enum u8 {
+ Dec = 0, // No prefix
+ Oct = 1, // Leading zero
+ Hex = 2, // 0x prefix
+ IPv6 = 3, // Unprefixed IPv6 piece hex. Can't be used with other bases.
+}
+Digit_Parse_Bases :: bit_set[Digit_Parse_Base; u8]
+DEFAULT_DIGIT_BASES :: Digit_Parse_Bases{.Dec, .Oct, .Hex}
+
+/*
+ Parses a single unsigned number in requested `bases` from `input`.
+ `max_value` represents the maximum allowed value for this number.
+
+ Returns the `value`, the `bytes_consumed` so far, and `ok` to signal success or failure.
+
+ An out-of-range or invalid number will return the accumulated value so far (which can be out of range),
+ the number of bytes consumed leading up the error, and `ok = false`.
+
+ When `.` or `:` are encountered, they'll be considered valid separators and will stop parsing,
+ returning the valid number leading up to it.
+
+ Other non-digit characters are treated as an error.
+
+ Octal numbers are expected to have a leading zero, with no 'o' format specifier.
+ Hexadecimal numbers are expected to be preceded by '0x' or '0X'.
+ Numbers will otherwise be considered to be in base 10.
+*/
+parse_ip_component :: proc(input: string, max_value := u64(max(u32)), bases := DEFAULT_DIGIT_BASES) -> (value: u64, bytes_consumed: int, ok: bool) {
+ // Default to base 10
+ base := u64(10)
+ input := input
+
+ /*
+ We keep track of the number of prefix bytes and digit bytes separately.
+ This way if a prefix is consumed and we encounter a separator or the end of the string,
+ the number is only considered valid if at least 1 digit byte has been consumed and the value is within range.
+ */
+ prefix_bytes := 0
+ digit_bytes := 0
+
+ /*
+ IPv6 hex bytes are unprefixed and can't be disambiguated from octal or hex unless the digit is out of range.
+ If we got the `.IPv6` option, skip prefix scanning and other flags aren't also used.
+ */
+ if .IPv6 in bases {
+ if bases != {.IPv6} { return } // Must be used on its own.
+ base = 16
+ } else {
+ // Scan for and consume prefix, if applicable.
+ if len(input) >= 2 && input[0] == '0' {
+ if .Hex in bases && (input[1] == 'x' || input[1] == 'X') {
+ base = 16
+ input = input[2:]
+ prefix_bytes = 2
+ }
+ if prefix_bytes == 0 && .Oct in bases {
+ base = 8
+ input = input[1:]
+ prefix_bytes = 1
+ }
+ }
+ }
+
+ parse_loop: for ch in input {
+ switch ch {
+ case '0'..='7':
+ digit_bytes += 1
+ value = value * base + u64(ch - '0')
+
+ case '8'..='9':
+ digit_bytes += 1
+
+ if base == 8 {
+ // Out of range for octal numbers.
+ return value, digit_bytes + prefix_bytes, false
+ }
+ value = value * base + u64(ch - '0')
+
+ case 'a'..='f':
+ digit_bytes += 1
+
+ if base == 8 || base == 10 {
+ // Out of range for octal and decimal numbers.
+ return value, digit_bytes + prefix_bytes, false
+ }
+ value = value * base + (u64(ch - 'a') + 10)
+
+ case 'A'..='F':
+ digit_bytes += 1
+
+ if base == 8 || base == 10 {
+ // Out of range for octal and decimal numbers.
+ return value, digit_bytes + prefix_bytes, false
+ }
+ value = value * base + (u64(ch - 'A') + 10)
+
+ case '.', ':':
+ /*
+ Number separator. Return early.
+ We don't need to check if the number is in range.
+ We do that each time through the loop.
+ */
+ break parse_loop
+
+ case:
+ // Invalid character encountered.
+ return value, digit_bytes + prefix_bytes, false
+ }
+
+ if value > max_value {
+ // Out-of-range number.
+ return value, digit_bytes + prefix_bytes, false
+ }
+ }
+
+ // If we consumed at least 1 digit byte, `value` *should* continue a valid number in an appropriate base in the allowable range.
+ return value, digit_bytes + prefix_bytes, digit_bytes >= 1
+}
+
+// Returns an address for each interface that can be bound to.
+get_network_interfaces :: proc() -> []Address {
+ // TODO: Implement using `enumerate_interfaces` and returning only the addresses of active interfaces.
+ return nil
+}
\ No newline at end of file
diff --git a/core/net/common.odin b/core/net/common.odin
new file mode 100644
index 000000000..70a027138
--- /dev/null
+++ b/core/net/common.odin
@@ -0,0 +1,416 @@
+// +build windows, linux, darwin
+package net
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+
+ This file collects structs, enums and settings applicable to the entire package in one handy place.
+ Platform-specific ones can be found in their respective `*_windows.odin` and similar files.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:runtime"
+
+/*
+ TUNEABLES - See also top of `dns.odin` for DNS configuration.
+
+ Determines the default value for whether dial_tcp() and accept_tcp() will set TCP_NODELAY on the new
+ socket, and the client socket, respectively.
+ This can also be set on a per-socket basis using the 'options' optional parameter to those procedures.
+
+ When TCP_NODELAY is set, data will be sent out to the peer as quickly as possible, rather than being
+ coalesced into fewer network packets.
+
+ This makes the networking layer more eagerly send data when you ask it to,
+ which can reduce latency by up to 200ms.
+
+ This does mean that a lot of small writes will negatively effect throughput however,
+ since the Nagle algorithm will be disabled, and each write becomes one
+ IP packet. This will increase traffic by a factor of 40, with IP and TCP
+ headers for each payload.
+
+ However, you can avoid this by buffering things up yourself if you wish to send a lot of
+ short data chunks, when TCP_NODELAY is enabled on that socket.
+*/
+
+ODIN_NET_TCP_NODELAY_DEFAULT :: #config(ODIN_NET_TCP_NODELAY_DEFAULT, true)
+
+// COMMON DEFINITIONS
+Maybe :: runtime.Maybe
+
+Network_Error :: union #shared_nil {
+ General_Error,
+ Platform_Error,
+ Create_Socket_Error,
+ Dial_Error,
+ Listen_Error,
+ Accept_Error,
+ Bind_Error,
+ TCP_Send_Error,
+ UDP_Send_Error,
+ TCP_Recv_Error,
+ UDP_Recv_Error,
+ Shutdown_Error,
+ Socket_Option_Error,
+ Set_Blocking_Error,
+ Parse_Endpoint_Error,
+ Resolve_Error,
+ DNS_Error,
+}
+
+General_Error :: enum u32 {
+ None = 0,
+ Unable_To_Enumerate_Network_Interfaces = 1,
+}
+
+// `Platform_Error` is used to wrap errors returned by the different platforms that don't fit a common error.
+Platform_Error :: enum u32 {}
+
+Parse_Endpoint_Error :: enum {
+ None = 0,
+ Bad_Port = 1,
+ Bad_Address,
+ Bad_Hostname,
+}
+
+Resolve_Error :: enum u32 {
+ None = 0,
+ Unable_To_Resolve = 1,
+}
+
+DNS_Error :: enum u32 {
+ Invalid_Hostname_Error = 1,
+ Invalid_Hosts_Config_Error,
+ Invalid_Resolv_Config_Error,
+ Connection_Error,
+ Server_Error,
+ System_Error,
+}
+
+// SOCKET OPTIONS & DEFINITIONS
+TCP_Options :: struct {
+ no_delay: bool,
+}
+
+default_tcp_options := TCP_Options {
+ no_delay = ODIN_NET_TCP_NODELAY_DEFAULT,
+}
+
+/*
+ To allow freely using `Socket` in your own data structures in a cross-platform manner,
+ we treat it as a handle large enough to accomodate OS-specific notions of socket handles.
+
+ The platform code will perform the cast so you don't have to.
+*/
+Socket :: distinct i64
+
+TCP_Socket :: distinct Socket
+UDP_Socket :: distinct Socket
+
+Socket_Protocol :: enum {
+ TCP,
+ UDP,
+}
+
+Any_Socket :: union {
+ TCP_Socket,
+ UDP_Socket,
+}
+
+/*
+ ADDRESS DEFINITIONS
+*/
+
+IP4_Address :: distinct [4]u8
+IP6_Address :: distinct [8]u16be
+Address :: union {IP4_Address, IP6_Address}
+
+IP4_Loopback := IP4_Address{127, 0, 0, 1}
+IP6_Loopback := IP6_Address{0, 0, 0, 0, 0, 0, 0, 1}
+
+IP4_Any := IP4_Address{}
+IP6_Any := IP6_Address{}
+
+Endpoint :: struct {
+ address: Address,
+ port: int,
+}
+
+Address_Family :: enum {
+ IP4,
+ IP6,
+}
+
+Netmask :: distinct Address
+
+/*
+ INTERFACE / LINK STATE
+*/
+Network_Interface :: struct {
+ adapter_name: string, // On Windows this is a GUID that we could parse back into its u128 for more compact storage.
+ friendly_name: string,
+ description: string,
+ dns_suffix: string,
+
+ physical_address: string, // MAC address, etc.
+ mtu: u32,
+
+ unicast: [dynamic]Lease,
+ multicast: [dynamic]Address,
+ anycast: [dynamic]Address,
+
+ gateways: [dynamic]Address,
+ dhcp_v4: Address,
+ dhcp_v6: Address,
+
+ tunnel_type: Tunnel_Type,
+
+ link: struct {
+ state: Link_State,
+ transmit_speed: u64,
+ receive_speed: u64,
+ },
+}
+
+// Empty bit set is unknown state.
+Link_States :: enum u32 {
+ Up = 1,
+ Down = 2,
+ Testing = 3,
+ Dormant = 4,
+ Not_Present = 5,
+ Lower_Layer_Down = 6,
+ Loopback = 7,
+}
+Link_State :: bit_set[Link_States; u32]
+
+Lease :: struct {
+ address: Address,
+ netmask: Netmask,
+ lifetime: struct {
+ valid: u32,
+ preferred: u32,
+ lease: u32,
+ },
+ origin: struct {
+ prefix: Prefix_Origin,
+ suffix: Suffix_Origin,
+ },
+ address_duplication: Address_Duplication,
+}
+
+Tunnel_Type :: enum i32 {
+ None = 0,
+ Other = 1,
+ Direct = 2,
+ IPv4_To_IPv6 = 11,
+ ISA_TAP = 13,
+ Teredo = 14,
+ IP_HTTPS = 15,
+}
+
+Prefix_Origin :: enum i32 {
+ Other = 0,
+ Manual = 1,
+ Well_Known = 2,
+ DHCP = 3,
+ Router_Advertisement = 4,
+ Unchanged = 16,
+}
+
+Suffix_Origin :: enum i32 {
+ Other = 0,
+ Manual = 1,
+ Well_Known = 2,
+ DHCP = 3,
+ Link_Layer_Address = 4,
+ Random = 5,
+ Unchanged = 16,
+}
+
+Address_Duplication :: enum i32 {
+ Invalid = 0,
+ Tentative = 1,
+ Duplicate = 2,
+ Deprecated = 3,
+ Preferred = 4,
+}
+
+// DNS DEFINITIONS
+DNS_Configuration :: struct {
+ // Configuration files.
+ resolv_conf: string,
+ hosts_file: string,
+
+ // TODO: Allow loading these up with `reload_configuration()` call or the like,
+ // so we don't have to do it each call.
+ name_servers: []Endpoint,
+ hosts_file_entries: []DNS_Record,
+}
+
+DNS_Record_Type :: enum u16 {
+ DNS_TYPE_A = 0x1, // IP4 address.
+ DNS_TYPE_NS = 0x2, // IP6 address.
+ DNS_TYPE_CNAME = 0x5, // Another host name.
+ DNS_TYPE_MX = 0xf, // Arbitrary binary data or text.
+ DNS_TYPE_AAAA = 0x1c, // Address of a name (DNS) server.
+ DNS_TYPE_TEXT = 0x10, // Address and preference priority of a mail exchange server.
+ DNS_TYPE_SRV = 0x21, // Address, port, priority, and weight of a host that provides a particular service.
+
+ IP4 = DNS_TYPE_A,
+ IP6 = DNS_TYPE_AAAA,
+ CNAME = DNS_TYPE_CNAME,
+ TXT = DNS_TYPE_TEXT,
+ NS = DNS_TYPE_NS,
+ MX = DNS_TYPE_MX,
+ SRV = DNS_TYPE_SRV,
+}
+
+// Base DNS Record. All DNS responses will carry a hostname and TTL (time to live) field.
+DNS_Record_Base :: struct {
+ record_name: string,
+ ttl_seconds: u32, // The time in seconds that this service will take to update, after the record is updated.
+}
+
+// An IP4 address that the domain name maps to. There can be any number of these.
+DNS_Record_IP4 :: struct {
+ using base: DNS_Record_Base,
+ address: IP4_Address,
+}
+
+// An IPv6 address that the domain name maps to. There can be any number of these.
+DNS_Record_IP6 :: struct {
+ using base: DNS_Record_Base,
+ address: IP6_Address,
+}
+
+/*
+ Another domain name that the domain name maps to.
+ Domains can be pointed to another domain instead of directly to an IP address.
+ `get_dns_records` will recursively follow these if you request this type of record.
+*/
+DNS_Record_CNAME :: struct {
+ using base: DNS_Record_Base,
+ host_name: string,
+}
+
+/*
+ Arbitrary string data that is associated with the domain name.
+ Commonly of the form `key=value` to be parsed, though there is no specific format for them.
+ These can be used for any purpose.
+*/
+DNS_Record_TXT :: struct {
+ using base: DNS_Record_Base,
+ value: string,
+}
+
+/*
+ Domain names of other DNS servers that are associated with the domain name.
+ TODO(tetra): Expand on what these records are used for, and when you should use pay attention to these.
+*/
+DNS_Record_NS :: struct {
+ using base: DNS_Record_Base,
+ host_name: string,
+}
+
+// Domain names for email servers that are associated with the domain name.
+// These records also have values which ranks them in the order they should be preferred. Lower is more-preferred.
+DNS_Record_MX :: struct {
+ using base: DNS_Record_Base,
+ host_name: string,
+ preference: int,
+}
+
+/*
+ An endpoint for a service that is available through the domain name.
+ This is the way to discover the services that a domain name provides.
+
+ Clients MUST attempt to contact the host with the lowest priority that they can reach.
+ If two hosts have the same priority, they should be contacted in the order according to their weight.
+ Hosts with larger weights should have a proportionally higher chance of being contacted by clients.
+ A weight of zero indicates a very low weight, or, when there is no choice (to reduce visual noise).
+
+ The host may be "." to indicate that it is "decidedly not available" on this domain.
+*/
+DNS_Record_SRV :: struct {
+ // base contains the full name of this record.
+ // e.g: _sip._tls.example.com
+ using base: DNS_Record_Base,
+
+ // The hostname or address where this service can be found.
+ target: string,
+ // The port on which this service can be found.
+ port: int,
+
+ service_name: string, // NOTE(tetra): These are substrings of 'record_name'
+ protocol_name: string, // NOTE(tetra): These are substrings of 'record_name'
+
+ // Lower is higher priority
+ priority: int,
+ // Relative weight of this host compared to other of same priority; the chance of using this host should be proporitional to this weight.
+ // The number of seconds that it will take to update the record.
+ weight: int,
+}
+
+DNS_Record :: union {
+ DNS_Record_IP4,
+ DNS_Record_IP6,
+ DNS_Record_CNAME,
+ DNS_Record_TXT,
+ DNS_Record_NS,
+ DNS_Record_MX,
+ DNS_Record_SRV,
+}
+
+DNS_Response_Code :: enum u16be {
+ No_Error,
+ Format_Error,
+ Server_Failure,
+ Name_Error,
+ Not_Implemented,
+ Refused,
+}
+
+DNS_Query :: enum u16be {
+ Host_Address = 1,
+ Authoritative_Name_Server = 2,
+ Mail_Destination = 3,
+ Mail_Forwarder = 4,
+ CNAME = 5,
+ All = 255,
+}
+
+DNS_Header :: struct {
+ id: u16be,
+ is_response: bool,
+ opcode: u16be,
+ is_authoritative: bool,
+ is_truncated: bool,
+ is_recursion_desired: bool,
+ is_recursion_available: bool,
+ response_code: DNS_Response_Code,
+}
+
+DNS_Record_Header :: struct #packed {
+ type: u16be,
+ class: u16be,
+ ttl: u32be,
+ length: u16be,
+}
+
+DNS_Host_Entry :: struct {
+ name: string,
+ addr: Address,
+}
\ No newline at end of file
diff --git a/core/net/dns.odin b/core/net/dns.odin
new file mode 100644
index 000000000..6afa844fe
--- /dev/null
+++ b/core/net/dns.odin
@@ -0,0 +1,870 @@
+// +build windows, linux, darwin
+package net
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:mem"
+import "core:strings"
+import "core:time"
+import "core:os"
+/*
+ Default configuration for DNS resolution.
+*/
+when ODIN_OS == .Windows {
+ DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
+ resolv_conf = "",
+ hosts_file = "%WINDIR%\\system32\\drivers\\etc\\hosts",
+ }
+} else when ODIN_OS == .Linux || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
+ DEFAULT_DNS_CONFIGURATION :: DNS_Configuration{
+ resolv_conf = "/etc/resolv.conf",
+ hosts_file = "/etc/hosts",
+ }
+} else {
+ #panic("Please add a configuration for this OS.")
+}
+
+@(init)
+init_dns_configuration :: proc() {
+ /*
+ Resolve %ENVIRONMENT% placeholders in their paths.
+ */
+ dns_configuration.resolv_conf, _ = replace_environment_path(dns_configuration.resolv_conf)
+ dns_configuration.hosts_file, _ = replace_environment_path(dns_configuration.hosts_file)
+}
+
+destroy_dns_configuration :: proc() {
+ delete(dns_configuration.resolv_conf)
+ delete(dns_configuration.hosts_file)
+}
+
+dns_configuration := DEFAULT_DNS_CONFIGURATION
+
+// Always allocates for consistency.
+replace_environment_path :: proc(path: string, allocator := context.allocator) -> (res: string, ok: bool) {
+ // Nothing to replace. Return a clone of the original.
+ if strings.count(path, "%") != 2 {
+ return strings.clone(path, allocator), true
+ }
+
+ left := strings.index(path, "%") + 1
+ assert(left > 0 && left <= len(path)) // should be covered by there being two %
+
+ right := strings.index(path[left:], "%") + 1
+ assert(right > 0 && right <= len(path)) // should be covered by there being two %
+
+ env_key := path[left: right]
+ env_val := os.get_env(env_key, allocator)
+ defer delete(env_val)
+
+ res, _ = strings.replace(path, path[left - 1: right + 1], env_val, 1, allocator)
+ return res, true
+}
+
+
+/*
+ Resolves a hostname to exactly one IP4 and IP6 endpoint.
+ It's then up to you which one you use.
+ Note that which address you use to open a socket, determines the type of the socket you get.
+
+ Returns `ok=false` if the host name could not be resolved to any endpoints.
+
+ Returned endpoints have the same port as provided in the string, or 0 if absent.
+ If you want to use a specific port, just modify the field after the call to this procedure.
+
+ If the hostname part of the endpoint is actually a string representation of an IP address, DNS resolution will be skipped.
+ This allows you to pass both strings like "example.com:9000" and "1.2.3.4:9000" to this function end reliably get
+ back an endpoint in both cases.
+*/
+resolve :: proc(hostname_and_maybe_port: string) -> (ep4, ep6: Endpoint, err: Network_Error) {
+ target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
+ switch t in target {
+ case Endpoint:
+ // NOTE(tetra): The hostname was actually an IP address; nothing to resolve, so just return it.
+ switch in t.address {
+ case IP4_Address: ep4 = t
+ case IP6_Address: ep6 = t
+ case: unreachable()
+ }
+ return
+
+ case Host:
+ err4, err6: Network_Error = ---, ---
+ ep4, err4 = resolve_ip4(t.hostname)
+ ep6, err6 = resolve_ip6(t.hostname)
+ ep4.port = t.port if err4 == nil else 0
+ ep6.port = t.port if err6 == nil else 0
+ if err4 != nil && err6 != nil {
+ err = err4
+ }
+ return
+ }
+ unreachable()
+}
+
+resolve_ip4 :: proc(hostname_and_maybe_port: string) -> (ep4: Endpoint, err: Network_Error) {
+ target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
+ switch t in target {
+ case Endpoint:
+ // NOTE(tetra): The hostname was actually an IP address; nothing to resolve, so just return it.
+ switch in t.address {
+ case IP4_Address:
+ return t, nil
+ case IP6_Address:
+ err = .Unable_To_Resolve
+ return
+ }
+ case Host:
+ recs, _ := get_dns_records_from_os(t.hostname, .IP4, context.temp_allocator)
+ if len(recs) == 0 {
+ err = .Unable_To_Resolve
+ return
+ }
+ ep4 = {
+ address = recs[0].(DNS_Record_IP4).address,
+ port = t.port,
+ }
+ return
+ }
+ unreachable()
+}
+
+resolve_ip6 :: proc(hostname_and_maybe_port: string) -> (ep6: Endpoint, err: Network_Error) {
+ target := parse_hostname_or_endpoint(hostname_and_maybe_port) or_return
+ switch t in target {
+ case Endpoint:
+ // NOTE(tetra): The hostname was actually an IP address; nothing to resolve, so just return it.
+ switch in t.address {
+ case IP4_Address:
+ err = .Unable_To_Resolve
+ return
+ case IP6_Address:
+ return t, nil
+ }
+ case Host:
+ recs, _ := get_dns_records_from_os(t.hostname, .IP6, context.temp_allocator)
+ if len(recs) == 0 {
+ err = .Unable_To_Resolve
+ return
+ }
+ ep6 = {
+ address = recs[0].(DNS_Record_IP6).address,
+ port = t.port,
+ }
+ return
+ }
+ unreachable()
+}
+
+/*
+ Performs a recursive DNS query for records of a particular type for the hostname using the OS.
+
+ NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
+ meaning that DNS queries for a hostname will resolve through CNAME records until an
+ IP address is reached.
+
+ IMPORTANT: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
+ See `destroy_records`.
+*/
+get_dns_records_from_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+ return _get_dns_records_os(hostname, type, allocator)
+}
+
+/*
+ A generic DNS client usable on any platform.
+ Performs a recursive DNS query for records of a particular type for the hostname.
+
+ NOTE: This procedure instructs the DNS resolver to recursively perform CNAME requests on our behalf,
+ meaning that DNS queries for a hostname will resolve through CNAME records until an
+ IP address is reached.
+
+ IMPORTANT: This procedure allocates memory for each record returned; deleting just the returned slice is not enough!
+ See `destroy_records`.
+*/
+get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type, name_servers: []Endpoint, host_overrides: []DNS_Record, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+ context.allocator = allocator
+
+ if type != .SRV {
+ // NOTE(tetra): 'hostname' can contain underscores when querying SRV records
+ ok := validate_hostname(hostname)
+ if !ok {
+ return nil, .Invalid_Hostname_Error
+ }
+ }
+
+ hdr := DNS_Header{
+ id = 0,
+ is_response = false,
+ opcode = 0,
+ is_authoritative = false,
+ is_truncated = false,
+ is_recursion_desired = true,
+ is_recursion_available = false,
+ response_code = DNS_Response_Code.No_Error,
+ }
+
+ id, bits := pack_dns_header(hdr)
+ dns_hdr := [6]u16be{}
+ dns_hdr[0] = id
+ dns_hdr[1] = bits
+ dns_hdr[2] = 1
+
+ dns_query := [2]u16be{ u16be(type), 1 }
+
+ output := [(size_of(u16be) * 6) + NAME_MAX + (size_of(u16be) * 2)]u8{}
+ b := strings.builder_from_slice(output[:])
+
+ strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_hdr[:]))
+ ok := encode_hostname(&b, hostname)
+ if !ok {
+ return nil, .Invalid_Hostname_Error
+ }
+ strings.write_bytes(&b, mem.slice_data_cast([]u8, dns_query[:]))
+
+ dns_packet := output[:strings.builder_len(b)]
+
+ dns_response_buf := [4096]u8{}
+ dns_response: []u8
+ for name_server in name_servers {
+ conn, sock_err := make_unbound_udp_socket(family_from_endpoint(name_server))
+ if sock_err != nil {
+ return nil, .Connection_Error
+ }
+ defer close(conn)
+
+ _, send_err := send(conn, dns_packet[:], name_server)
+ if send_err != nil {
+ continue
+ }
+
+ set_err := set_option(conn, .Receive_Timeout, time.Second * 1)
+ if set_err != nil {
+ return nil, .Connection_Error
+ }
+
+ recv_sz, _, recv_err := recv_udp(conn, dns_response_buf[:])
+ if recv_err == UDP_Recv_Error.Timeout {
+ continue
+ } else if recv_err != nil {
+ continue
+ }
+
+ if recv_sz == 0 {
+ continue
+ }
+
+ dns_response = dns_response_buf[:recv_sz]
+
+ rsp, _ok := parse_response(dns_response, type)
+ if !_ok {
+ return nil, .Server_Error
+ }
+
+ if len(rsp) == 0 {
+ continue
+ }
+
+ return rsp[:], nil
+ }
+
+ return
+}
+
+// `records` slice is also destroyed.
+destroy_dns_records :: proc(records: []DNS_Record, allocator := context.allocator) {
+ context.allocator = allocator
+
+ for rec in records {
+ switch r in rec {
+ case DNS_Record_IP4:
+ delete(r.base.record_name)
+
+ case DNS_Record_IP6:
+ delete(r.base.record_name)
+
+ case DNS_Record_CNAME:
+ delete(r.base.record_name)
+ delete(r.host_name)
+
+ case DNS_Record_TXT:
+ delete(r.base.record_name)
+ delete(r.value)
+
+ case DNS_Record_NS:
+ delete(r.base.record_name)
+ delete(r.host_name)
+
+ case DNS_Record_MX:
+ delete(r.base.record_name)
+ delete(r.host_name)
+
+ case DNS_Record_SRV:
+ delete(r.record_name)
+ delete(r.target)
+ }
+ }
+
+ delete(records, allocator)
+}
+
+/*
+ TODO(cloin): Does the DNS Resolver need to recursively hop through CNAMEs to get the IP
+ or is that what recursion desired does? Do we need to handle recursion unavailable?
+ How do we deal with is_authoritative / is_truncated?
+*/
+
+NAME_MAX :: 255
+LABEL_MAX :: 63
+
+pack_dns_header :: proc(hdr: DNS_Header) -> (id: u16be, bits: u16be) {
+ id = hdr.id
+ bits = hdr.opcode << 1 | u16be(hdr.response_code)
+ if hdr.is_response {
+ bits |= 1 << 15
+ }
+ if hdr.is_authoritative {
+ bits |= 1 << 10
+ }
+ if hdr.is_truncated {
+ bits |= 1 << 9
+ }
+ if hdr.is_recursion_desired {
+ bits |= 1 << 8
+ }
+ if hdr.is_recursion_available {
+ bits |= 1 << 7
+ }
+
+ return id, bits
+}
+
+unpack_dns_header :: proc(id: u16be, bits: u16be) -> (hdr: DNS_Header) {
+ hdr.id = id
+ hdr.is_response = (bits & (1 << 15)) != 0
+ hdr.opcode = (bits >> 11) & 0xF
+ hdr.is_authoritative = (bits & (1 << 10)) != 0
+ hdr.is_truncated = (bits & (1 << 9)) != 0
+ hdr.is_recursion_desired = (bits & (1 << 8)) != 0
+ hdr.is_recursion_available = (bits & (1 << 7)) != 0
+ hdr.response_code = DNS_Response_Code(bits & 0xF)
+
+ return hdr
+}
+
+load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) {
+ context.allocator = allocator
+
+ res := os.read_entire_file_from_filename(resolv_conf_path) or_return
+ defer delete(res)
+ resolv_str := string(res)
+
+ id_str := "nameserver"
+ id_len := len(id_str)
+
+ _name_servers := make([dynamic]Endpoint, 0, allocator)
+ for line in strings.split_lines_iterator(&resolv_str) {
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+
+ if len(line) < id_len || strings.compare(line[:id_len], id_str) != 0 {
+ continue
+ }
+
+ server_ip_str := strings.trim_left_space(line[id_len:])
+ if len(server_ip_str) == 0 {
+ continue
+ }
+
+ addr := parse_address(server_ip_str)
+ if addr == nil {
+ continue
+ }
+
+ endpoint := Endpoint{
+ addr,
+ 53,
+ }
+ append(&_name_servers, endpoint)
+ }
+
+ return _name_servers[:], true
+}
+
+load_hosts :: proc(hosts_file_path: string, allocator := context.allocator) -> (hosts: []DNS_Host_Entry, ok: bool) {
+ context.allocator = allocator
+
+ res := os.read_entire_file_from_filename(hosts_file_path, allocator) or_return
+ defer delete(res)
+
+ _hosts := make([dynamic]DNS_Host_Entry, 0, allocator)
+ hosts_str := string(res)
+ for line in strings.split_lines_iterator(&hosts_str) {
+ if len(line) == 0 || line[0] == '#' {
+ continue
+ }
+
+ splits := strings.fields(line)
+ defer delete(splits)
+
+ ip_str := splits[0]
+ addr := parse_address(ip_str)
+ if addr == nil {
+ continue
+ }
+
+ for hostname in splits[1:] {
+ if len(hostname) == 0 {
+ continue
+ }
+
+ append(&_hosts, DNS_Host_Entry{hostname, addr})
+ }
+ }
+
+ return _hosts[:], true
+}
+
+// www.google.com -> 3www6google3com0
+encode_hostname :: proc(b: ^strings.Builder, hostname: string) -> (ok: bool) {
+ _hostname := hostname
+ for section in strings.split_iterator(&_hostname, ".") {
+ if len(section) > LABEL_MAX {
+ return
+ }
+
+ strings.write_byte(b, u8(len(section)))
+ strings.write_string(b, section)
+ }
+ strings.write_byte(b, 0)
+
+ return true
+}
+
+skip_hostname :: proc(packet: []u8, start_idx: int) -> (encode_size: int, ok: bool) {
+ out_size := 0
+
+ cur_idx := start_idx
+ iteration_max := 0
+ top: for cur_idx < len(packet) {
+ if packet[cur_idx] == 0 {
+ out_size += 1
+ break
+ }
+
+ if iteration_max > 255 {
+ return
+ }
+
+ if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
+ return
+ }
+
+ switch packet[cur_idx] {
+ case 0xC0:
+ out_size += 2
+ break top
+ case:
+ label_size := int(packet[cur_idx]) + 1
+ idx2 := cur_idx + label_size
+
+ if idx2 < cur_idx + 1 || idx2 > len(packet) {
+ return
+ }
+
+ out_size += label_size
+ cur_idx = idx2
+ }
+
+ iteration_max += 1
+ }
+
+ if start_idx + out_size > len(packet) {
+ return
+ }
+
+ return out_size, true
+}
+
+decode_hostname :: proc(packet: []u8, start_idx: int, allocator := context.allocator) -> (hostname: string, encode_size: int, ok: bool) {
+ output := [NAME_MAX]u8{}
+ b := strings.builder_from_slice(output[:])
+
+ // If you're on level 0, update out_bytes, everything through a pointer
+ // doesn't count towards this hostname's packet length
+
+ // Evaluate tokens to generate the hostname
+ out_size := 0
+ level := 0
+ print_size := 0
+ cur_idx := start_idx
+ iteration_max := 0
+ labels_added := 0
+ for cur_idx < len(packet) {
+ if packet[cur_idx] == 0 {
+
+ if (level == 0) {
+ out_size += 1
+ }
+
+ break
+ }
+
+ if iteration_max > 255 {
+ return
+ }
+
+ if packet[cur_idx] > 63 && packet[cur_idx] != 0xC0 {
+ return
+ }
+
+ switch packet[cur_idx] {
+
+ // This is a offset to more data in the packet, jump to it
+ case 0xC0:
+ pkt := packet[cur_idx:cur_idx+2]
+ val := (^u16be)(raw_data(pkt))^
+ offset := int(val & 0x3FFF)
+ if offset > len(packet) {
+ return
+ }
+
+ cur_idx = offset
+
+ if (level == 0) {
+ out_size += 2
+ level += 1
+ }
+
+ // This is a label, insert it into the hostname
+ case:
+ label_size := int(packet[cur_idx])
+ idx2 := cur_idx + label_size + 1
+ if idx2 < cur_idx + 1 || idx2 > len(packet) {
+ return
+ }
+
+ if print_size + label_size + 1 > NAME_MAX {
+ return
+ }
+
+ if labels_added > 0 {
+ strings.write_byte(&b, '.')
+ }
+ strings.write_bytes(&b, packet[cur_idx+1:idx2])
+ print_size += label_size + 1
+ labels_added += 1
+
+ cur_idx = idx2
+
+ if (level == 0) {
+ out_size += label_size + 1
+ }
+ }
+
+ iteration_max += 1
+ }
+
+ if start_idx + out_size > len(packet) {
+ return
+ }
+
+ return strings.clone(strings.to_string(b), allocator), out_size, true
+}
+
+// Uses RFC 952 & RFC 1123
+validate_hostname :: proc(hostname: string) -> (ok: bool) {
+ if len(hostname) > 255 || len(hostname) == 0 {
+ return
+ }
+
+ if hostname[0] == '-' {
+ return
+ }
+
+ _hostname := hostname
+ for label in strings.split_iterator(&_hostname, ".") {
+ if len(label) > 63 || len(label) == 0 {
+ return
+ }
+
+ for ch in label {
+ switch ch {
+ case:
+ return
+ case 'a'..='z', 'A'..='Z', '0'..='9', '-':
+ continue
+ }
+ }
+ }
+
+ return true
+}
+
+parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil) -> (record: DNS_Record, ok: bool) {
+ record_buf := packet[cur_off^:]
+
+ srv_record_name, hn_sz := decode_hostname(packet, cur_off^, context.temp_allocator) or_return
+ // TODO(tetra): Not sure what we should call this.
+ // Is it really only used in SRVs?
+ // Maybe some refactoring is required?
+
+ ahdr_sz := size_of(DNS_Record_Header)
+ if len(record_buf) - hn_sz < ahdr_sz {
+ return
+ }
+
+ record_hdr_bytes := record_buf[hn_sz:hn_sz+ahdr_sz]
+ record_hdr := cast(^DNS_Record_Header)raw_data(record_hdr_bytes)
+
+ data_sz := record_hdr.length
+ data_off := cur_off^ + int(hn_sz) + int(ahdr_sz)
+ data := packet[data_off:data_off+int(data_sz)]
+ cur_off^ += int(hn_sz) + int(ahdr_sz) + int(data_sz)
+
+ if u16be(filter) != record_hdr.type {
+ return nil, true
+ }
+
+ _record: DNS_Record
+ #partial switch DNS_Record_Type(record_hdr.type) {
+ case .IP4:
+ if len(data) != 4 {
+ return
+ }
+
+ addr := (^IP4_Address)(raw_data(data))^
+
+ _record = DNS_Record_IP4{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ address = addr,
+ }
+
+ case .IP6:
+ if len(data) != 16 {
+ return
+ }
+
+ addr := (^IP6_Address)(raw_data(data))^
+
+ _record = DNS_Record_IP6{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ address = addr,
+ }
+
+ case .CNAME:
+ hostname, _ := decode_hostname(packet, data_off) or_return
+
+ _record = DNS_Record_CNAME{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ host_name = hostname,
+ }
+
+ case .TXT:
+ _record = DNS_Record_TXT{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ value = strings.clone(string(data)),
+ }
+
+ case .NS:
+ name, _ := decode_hostname(packet, data_off) or_return
+
+ _record = DNS_Record_NS{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ host_name = name,
+ }
+
+ case .SRV:
+ if len(data) <= 6 {
+ return
+ }
+
+ _data := mem.slice_data_cast([]u16be, data)
+
+ priority, weight, port := _data[0], _data[1], _data[2]
+ target, _ := decode_hostname(packet, data_off + (size_of(u16be) * 3)) or_return
+
+ // NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname'
+ // The record name is the name of the record.
+ // Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up
+ // by making this request in the first place.
+
+ // NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name.
+ // It's already cloned, after all. I wouldn't put them on the temp allocator like this.
+
+ parts := strings.split_n(srv_record_name, ".", 3, context.temp_allocator)
+ if len(parts) != 3 {
+ return
+ }
+ service_name, protocol_name := parts[0], parts[1]
+
+ _record = DNS_Record_SRV{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ target = target,
+ service_name = service_name,
+ protocol_name = protocol_name,
+ priority = int(priority),
+ weight = int(weight),
+ port = int(port),
+ }
+
+ case .MX:
+ if len(data) <= 2 {
+ return
+ }
+
+ preference: u16be = mem.slice_data_cast([]u16be, data)[0]
+ hostname, _ := decode_hostname(packet, data_off + size_of(u16be)) or_return
+
+ _record = DNS_Record_MX{
+ base = DNS_Record_Base{
+ record_name = strings.clone(srv_record_name),
+ ttl_seconds = u32(record_hdr.ttl),
+ },
+ host_name = hostname,
+ preference = int(preference),
+ }
+
+ case:
+ return
+
+ }
+
+ return _record, true
+}
+
+/*
+ DNS Query Response Format:
+ - DNS_Header (packed)
+ - Query Count
+ - Answer Count
+ - Authority Count
+ - Additional Count
+ - Query[]
+ - Hostname -- encoded
+ - Type
+ - Class
+ - Answer[]
+ - DNS Record Data
+ - Authority[]
+ - DNS Record Data
+ - Additional[]
+ - DNS Record Data
+
+ DNS Record Data:
+ - DNS_Record_Header
+ - Data[]
+*/
+
+parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
+ context.allocator = allocator
+
+ HEADER_SIZE_BYTES :: 12
+ if len(response) < HEADER_SIZE_BYTES {
+ return
+ }
+
+ _records := make([dynamic]DNS_Record, 0)
+
+ dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
+ hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
+ if !hdr.is_response {
+ return
+ }
+
+ question_count := int(dns_hdr_chunks[2])
+ if question_count != 1 {
+ return
+ }
+ answer_count := int(dns_hdr_chunks[3])
+ authority_count := int(dns_hdr_chunks[4])
+ additional_count := int(dns_hdr_chunks[5])
+
+ cur_idx := HEADER_SIZE_BYTES
+
+ for _ in 0..
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+import "core:strings"
+
+@(private)
+_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+ context.allocator = allocator
+
+ if type != .SRV {
+ // NOTE(tetra): 'hostname' can contain underscores when querying SRV records
+ ok := validate_hostname(hostname)
+ if !ok {
+ return nil, .Invalid_Hostname_Error
+ }
+ }
+
+ name_servers, resolve_ok := load_resolv_conf(dns_configuration.resolv_conf)
+ defer delete(name_servers)
+ if !resolve_ok {
+ return nil, .Invalid_Resolv_Config_Error
+ }
+ if len(name_servers) == 0 {
+ return
+ }
+
+ hosts, hosts_ok := load_hosts(dns_configuration.hosts_file)
+ defer delete(hosts)
+ if !hosts_ok {
+ return nil, .Invalid_Hosts_Config_Error
+ }
+
+ host_overrides := make([dynamic]DNS_Record)
+ for host in hosts {
+ if strings.compare(host.name, hostname) != 0 {
+ continue
+ }
+
+ if type == .IP4 && family_from_address(host.addr) == .IP4 {
+ record := DNS_Record_IP4{
+ base = {
+ record_name = strings.clone(hostname),
+ ttl_seconds = 0,
+ },
+ address = host.addr.(IP4_Address),
+ }
+ append(&host_overrides, record)
+ } else if type == .IP6 && family_from_address(host.addr) == .IP6 {
+ record := DNS_Record_IP6{
+ base = {
+ record_name = strings.clone(hostname),
+ ttl_seconds = 0,
+ },
+ address = host.addr.(IP6_Address),
+ }
+ append(&host_overrides, record)
+ }
+ }
+
+ if len(host_overrides) > 0 {
+ return host_overrides[:], nil
+ }
+
+ return get_dns_records_from_nameservers(hostname, type, name_servers, host_overrides[:])
+}
diff --git a/core/net/dns_windows.odin b/core/net/dns_windows.odin
new file mode 100644
index 000000000..72d67c54a
--- /dev/null
+++ b/core/net/dns_windows.odin
@@ -0,0 +1,159 @@
+//+build windows
+package net
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:strings"
+import "core:mem"
+
+import win "core:sys/windows"
+
+@(private)
+_get_dns_records_os :: proc(hostname: string, type: DNS_Record_Type, allocator := context.allocator) -> (records: []DNS_Record, err: DNS_Error) {
+ context.allocator = allocator
+
+ host_cstr := strings.clone_to_cstring(hostname, context.temp_allocator)
+ rec: ^win.DNS_RECORD
+ res := win.DnsQuery_UTF8(host_cstr, u16(type), 0, nil, &rec, nil)
+
+ switch u32(res) {
+ case 0:
+ // okay
+ case win.ERROR_INVALID_NAME:
+ return nil, .Invalid_Hostname_Error
+ case win.DNS_INFO_NO_RECORDS:
+ return
+ case:
+ return nil, .System_Error
+ }
+ defer win.DnsRecordListFree(rec, 1) // 1 means that we're freeing a list... because the proc name isn't enough.
+
+ count := 0
+ for r := rec; r != nil; r = r.pNext {
+ if r.wType != u16(type) do continue // NOTE(tetra): Should never happen, but...
+ count += 1
+ }
+
+ recs := make([dynamic]DNS_Record, 0, count)
+ if recs == nil do return nil, .System_Error // return no results if OOM.
+
+ for r := rec; r != nil; r = r.pNext {
+ if r.wType != u16(type) do continue // NOTE(tetra): Should never happen, but...
+
+ base_record := DNS_Record_Base{
+ record_name = strings.clone(string(r.pName)),
+ ttl_seconds = r.dwTtl,
+ }
+
+ switch DNS_Record_Type(r.wType) {
+ case .IP4:
+ addr := IP4_Address(transmute([4]u8)r.Data.A)
+ record := DNS_Record_IP4{
+ base = base_record,
+ address = addr,
+ }
+ append(&recs, record)
+
+ case .IP6:
+ addr := IP6_Address(transmute([8]u16be) r.Data.AAAA)
+ record := DNS_Record_IP6{
+ base = base_record,
+ address = addr,
+ }
+ append(&recs, record)
+
+ case .CNAME:
+
+ hostname := strings.clone(string(r.Data.CNAME))
+ record := DNS_Record_CNAME{
+ base = base_record,
+ host_name = hostname,
+ }
+ append(&recs, record)
+
+ case .TXT:
+ n := r.Data.TXT.dwStringCount
+ ptr := &r.Data.TXT.pStringArray
+ c_strs := mem.slice_ptr(ptr, int(n))
+
+ for cstr in c_strs {
+ record := DNS_Record_TXT{
+ base = base_record,
+ value = strings.clone(string(cstr)),
+ }
+ append(&recs, record)
+ }
+
+ case .NS:
+ hostname := strings.clone(string(r.Data.NS))
+ record := DNS_Record_NS{
+ base = base_record,
+ host_name = hostname,
+ }
+ append(&recs, record)
+
+ case .MX:
+ /*
+ TODO(tetra): Order by preference priority? (Prefer hosts with lower preference values.)
+ Or maybe not because you're supposed to just use the first one that works
+ and which order they're in changes every few calls.
+ */
+
+ record := DNS_Record_MX{
+ base = base_record,
+ host_name = strings.clone(string(r.Data.MX.pNameExchange)),
+ preference = int(r.Data.MX.wPreference),
+ }
+ append(&recs, record)
+
+ case .SRV:
+ target := strings.clone(string(r.Data.SRV.pNameTarget)) // The target hostname/address that the service can be found on
+ priority := int(r.Data.SRV.wPriority)
+ weight := int(r.Data.SRV.wWeight)
+ port := int(r.Data.SRV.wPort)
+
+ // NOTE(tetra): Srv record name should be of the form '_servicename._protocol.hostname'
+ // The record name is the name of the record.
+ // Not to be confused with the _target_ of the record, which is--in combination with the port--what we're looking up
+ // by making this request in the first place.
+
+ // NOTE(Jeroen): Service Name and Protocol Name can probably just be string slices into the record name.
+ // It's already cloned, after all. I wouldn't put them on the temp allocator like this.
+
+ parts := strings.split_n(base_record.record_name, ".", 3, context.temp_allocator)
+ if len(parts) != 3 {
+ continue
+ }
+ service_name, protocol_name := parts[0], parts[1]
+
+ append(&recs, DNS_Record_SRV {
+ base = base_record,
+ target = target,
+ port = port,
+ service_name = service_name,
+ protocol_name = protocol_name,
+ priority = priority,
+ weight = weight,
+
+ })
+ }
+ }
+
+ records = recs[:]
+ return
+}
\ No newline at end of file
diff --git a/core/net/doc.odin b/core/net/doc.odin
new file mode 100644
index 000000000..0f1b33172
--- /dev/null
+++ b/core/net/doc.odin
@@ -0,0 +1,46 @@
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+
+ Features:
+ - Supports Windows, Linux and OSX.
+ - Opening and closing of TCP and UDP sockets.
+ - Sending to and receiving from these sockets.
+ - DNS name lookup, using either the OS or our own resolver.
+
+ Planned:
+ - Nonblocking IO
+ - `Connection` struct
+ A "fat socket" struct that remembers how you opened it, etc, instead of just being a handle.
+ - IP Range structs, CIDR/class ranges, netmask calculator and associated helper procedures.
+ - Use `context.temp_allocator` instead of stack-based arenas?
+ And check it's the default temp allocator or can give us 4 MiB worth of memory
+ without punting to the main allocator by comparing their addresses in an @(init) procedure.
+ Panic if this assumption is not met.
+
+ - Document assumptions about libc usage (or avoidance thereof) for each platform.
+
+ Assumptions:
+ - For performance reasons this package relies on the `context.temp_allocator` in some places.
+
+ You can replace the default `context.temp_allocator` with your own as long as it meets
+ this requirement: A minimum of 4 MiB of scratch space that's expected not to be freed.
+
+ If this expectation is not met, the package's @(init) procedure will attempt to detect
+ this and panic to avoid temp allocations prematurely overwriting data and garbling results,
+ or worse. This means that should you replace the temp allocator with an insufficient one,
+ we'll do our best to loudly complain the first time you try it.
+*/
+package net
\ No newline at end of file
diff --git a/core/net/errors_darwin.odin b/core/net/errors_darwin.odin
new file mode 100644
index 000000000..39cf4c665
--- /dev/null
+++ b/core/net/errors_darwin.odin
@@ -0,0 +1,206 @@
+package net
+// +build darwin
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:c"
+import "core:os"
+
+Create_Socket_Error :: enum c.int {
+ None = 0,
+ Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT),
+ No_Socket_Descriptors_Available = c.int(os.EMFILE),
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ No_Memory_Available_Available = c.int(os.ENOMEM),
+ Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT),
+ Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT),
+ Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT),
+}
+
+Dial_Error :: enum c.int {
+ None = 0,
+ Port_Required = -1,
+
+ Address_In_Use = c.int(os.EADDRINUSE),
+ In_Progress = c.int(os.EINPROGRESS),
+ Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL),
+ Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT),
+ Refused = c.int(os.ECONNREFUSED),
+ Is_Listening_Socket = c.int(os.EACCES),
+ Already_Connected = c.int(os.EISCONN),
+ Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline
+ Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Timeout = c.int(os.ETIMEDOUT),
+
+ // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+ Would_Block = c.int(os.EWOULDBLOCK),
+}
+
+Bind_Error :: enum c.int {
+ None = 0,
+ Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint.
+ Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine.
+ Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
+ Address_Family_Mismatch = c.int(os.EFAULT), // The address family of the address does not match that of the socket.
+ Already_Bound = c.int(os.EINVAL), // The socket is already bound to an address.
+ No_Ports_Available = c.int(os.ENOBUFS), // There are not enough ephemeral ports available.
+}
+
+Listen_Error :: enum c.int {
+ None = 0,
+ Address_In_Use = c.int(os.EADDRINUSE),
+ Already_Connected = c.int(os.EISCONN),
+ No_Socket_Descriptors_Available = c.int(os.EMFILE),
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Nonlocal_Address = c.int(os.EADDRNOTAVAIL),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP),
+}
+
+Accept_Error :: enum c.int {
+ None = 0,
+ // TODO(tetra): Is this error actually possible here? Or is like Linux, in which case we can remove it.
+ Reset = c.int(os.ECONNRESET),
+ Not_Listening = c.int(os.EINVAL),
+ No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE),
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP),
+
+ // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+ Would_Block = c.int(os.EWOULDBLOCK),
+}
+
+TCP_Recv_Error :: enum c.int {
+ None = 0,
+ Shutdown = c.int(os.ESHUTDOWN),
+ Not_Connected = c.int(os.ENOTCONN),
+
+ // TODO(tetra): Is this error actually possible here?
+ Connection_Broken = c.int(os.ENETRESET),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Aborted = c.int(os.ECONNABORTED),
+
+ // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
+ Connection_Closed = c.int(os.ECONNRESET),
+ Offline = c.int(os.ENETDOWN),
+ Host_Unreachable = c.int(os.EHOSTUNREACH),
+ Interrupted = c.int(os.EINTR),
+
+ // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ Timeout = c.int(os.EWOULDBLOCK),
+}
+
+UDP_Recv_Error :: enum c.int {
+ None = 0,
+ Truncated = c.int(os.EMSGSIZE), // The buffer is too small to fit the entire message, and the message was truncated.
+ Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+ Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
+ Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory.
+ Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
+
+ // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
+ // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ Timeout = c.int(os.EWOULDBLOCK),
+ Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't.
+}
+
+// TODO
+TCP_Send_Error :: enum c.int {
+ None = 0,
+
+ // TODO: merge with other errors?
+ Aborted = c.int(os.ECONNABORTED),
+ Connection_Closed = c.int(os.ECONNRESET),
+ Not_Connected = c.int(os.ENOTCONN),
+ Shutdown = c.int(os.ESHUTDOWN),
+
+ // The send queue was full.
+ // This is usually a transient issue.
+ //
+ // This also shouldn't normally happen on Linux, as data is dropped if it
+ // doesn't fit in the send queue.
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Offline = c.int(os.ENETDOWN),
+ Host_Unreachable = c.int(os.EHOSTUNREACH),
+ Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
+
+ // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
+ Timeout = c.int(os.EWOULDBLOCK),
+ Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+}
+
+// TODO
+UDP_Send_Error :: enum c.int {
+ None = 0,
+ Truncated = c.int(os.EMSGSIZE), // The message is too big. No data was sent.
+
+ // TODO: not sure what the exact circumstances for this is yet
+ Network_Unreachable = c.int(os.ENETUNREACH),
+ No_Outbound_Ports_Available = c.int(os.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send.
+
+ // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
+ // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ Timeout = c.int(os.EWOULDBLOCK),
+ Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+ Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
+ Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory.
+ Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
+
+ // The send queue was full.
+ // This is usually a transient issue.
+ //
+ // This also shouldn't normally happen on Linux, as data is dropped if it
+ // doesn't fit in the send queue.
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage the send queue.
+}
+
+Shutdown_Manner :: enum c.int {
+ Receive = c.int(os.SHUT_RD),
+ Send = c.int(os.SHUT_WR),
+ Both = c.int(os.SHUT_RDWR),
+}
+
+Shutdown_Error :: enum c.int {
+ None = 0,
+ Aborted = c.int(os.ECONNABORTED),
+ Reset = c.int(os.ECONNRESET),
+ Offline = c.int(os.ENETDOWN),
+ Not_Connected = c.int(os.ENOTCONN),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Invalid_Manner = c.int(os.EINVAL),
+}
+
+Socket_Option_Error :: enum c.int {
+ None = 0,
+ Offline = c.int(os.ENETDOWN),
+ Timeout_When_Keepalive_Set = c.int(os.ENETRESET),
+ Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT),
+ Reset_When_Keepalive_Set = c.int(os.ENOTCONN),
+ Not_Socket = c.int(os.ENOTSOCK),
+}
+
+Set_Blocking_Error :: enum c.int {
+ None = 0,
+
+ // TODO: Add errors for `set_blocking`
+}
\ No newline at end of file
diff --git a/core/net/errors_linux.odin b/core/net/errors_linux.odin
new file mode 100644
index 000000000..9366f4435
--- /dev/null
+++ b/core/net/errors_linux.odin
@@ -0,0 +1,201 @@
+package net
+// +build linux
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:c"
+import "core:os"
+
+Create_Socket_Error :: enum c.int {
+ None = 0,
+ Family_Not_Supported_For_This_Socket = c.int(os.EAFNOSUPPORT),
+ No_Socket_Descriptors_Available = c.int(os.EMFILE),
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ No_Memory_Available_Available = c.int(os.ENOMEM),
+ Protocol_Unsupported_By_System = c.int(os.EPROTONOSUPPORT),
+ Wrong_Protocol_For_Socket = c.int(os.EPROTONOSUPPORT),
+ Family_And_Socket_Type_Mismatch = c.int(os.EPROTONOSUPPORT),
+}
+
+Dial_Error :: enum c.int {
+ None = 0,
+ Port_Required = -1,
+
+ Address_In_Use = c.int(os.EADDRINUSE),
+ In_Progress = c.int(os.EINPROGRESS),
+ Cannot_Use_Any_Address = c.int(os.EADDRNOTAVAIL),
+ Wrong_Family_For_Socket = c.int(os.EAFNOSUPPORT),
+ Refused = c.int(os.ECONNREFUSED),
+ Is_Listening_Socket = c.int(os.EACCES),
+ Already_Connected = c.int(os.EISCONN),
+ Network_Unreachable = c.int(os.ENETUNREACH), // Device is offline
+ Host_Unreachable = c.int(os.EHOSTUNREACH), // Remote host cannot be reached
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Timeout = c.int(os.ETIMEDOUT),
+
+ // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+ Would_Block = c.int(os.EWOULDBLOCK),
+}
+
+Bind_Error :: enum c.int {
+ None = 0,
+ Address_In_Use = c.int(os.EADDRINUSE), // Another application is currently bound to this endpoint.
+ Given_Nonlocal_Address = c.int(os.EADDRNOTAVAIL), // The address is not a local address on this machine.
+ Broadcast_Disabled = c.int(os.EACCES), // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
+ Address_Family_Mismatch = c.int(os.EFAULT), // The address family of the address does not match that of the socket.
+ Already_Bound = c.int(os.EINVAL), // The socket is already bound to an address.
+ No_Ports_Available = c.int(os.ENOBUFS), // There are not enough ephemeral ports available.
+}
+
+Listen_Error :: enum c.int {
+ None = 0,
+ Address_In_Use = c.int(os.EADDRINUSE),
+ Already_Connected = c.int(os.EISCONN),
+ No_Socket_Descriptors_Available = c.int(os.EMFILE),
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Nonlocal_Address = c.int(os.EADDRNOTAVAIL),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Listening_Not_Supported_For_This_Socket = c.int(os.EOPNOTSUPP),
+}
+
+Accept_Error :: enum c.int {
+ None = 0,
+ Not_Listening = c.int(os.EINVAL),
+ No_Socket_Descriptors_Available_For_Client_Socket = c.int(os.EMFILE),
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Not_Connection_Oriented_Socket = c.int(os.EOPNOTSUPP),
+
+ // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+ Would_Block = c.int(os.EWOULDBLOCK),
+}
+
+TCP_Recv_Error :: enum c.int {
+ None = 0,
+ Shutdown = c.int(os.ESHUTDOWN),
+ Not_Connected = c.int(os.ENOTCONN),
+ Connection_Broken = c.int(os.ENETRESET),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Aborted = c.int(os.ECONNABORTED),
+
+ // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
+ Connection_Closed = c.int(os.ECONNRESET),
+ Offline = c.int(os.ENETDOWN),
+ Host_Unreachable = c.int(os.EHOSTUNREACH),
+ Interrupted = c.int(os.EINTR),
+ Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+}
+
+UDP_Recv_Error :: enum c.int {
+ None = 0,
+
+ // The buffer is too small to fit the entire message, and the message was truncated.
+ // When this happens, the rest of message is lost.
+ Buffer_Too_Small = c.int(os.EMSGSIZE),
+ Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+ Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
+ Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory.
+ Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
+
+ // The send timeout duration passed before all data was received. See Socket_Option.Receive_Timeout.
+ // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ Timeout = c.int(os.EWOULDBLOCK),
+ Socket_Not_Bound = c.int(os.EINVAL), // The socket must be bound for this operation, but isn't.
+}
+
+// TODO
+TCP_Send_Error :: enum c.int {
+ None = 0,
+ // TODO(tetra): merge with other errors?
+ Aborted = c.int(os.ECONNABORTED),
+ Connection_Closed = c.int(os.ECONNRESET),
+ Not_Connected = c.int(os.ENOTCONN),
+ Shutdown = c.int(os.ESHUTDOWN),
+
+ // The send queue was full.
+ // This is usually a transient issue.
+ //
+ // This also shouldn't normally happen on Linux, as data is dropped if it
+ // doesn't fit in the send queue.
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ Offline = c.int(os.ENETDOWN),
+ Host_Unreachable = c.int(os.EHOSTUNREACH), // A signal occurred before any data was transmitted. See signal(7).
+ Interrupted = c.int(os.EINTR), // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
+ Timeout = c.int(os.EWOULDBLOCK), // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+}
+
+// TODO
+UDP_Send_Error :: enum c.int {
+ None = 0,
+ Message_Too_Long = c.int(os.EMSGSIZE), // The message is too big. No data was sent.
+
+ // TODO: not sure what the exact circumstances for this is yet
+ Network_Unreachable = c.int(os.ENETUNREACH),
+ No_Outbound_Ports_Available = c.int(os.EAGAIN), // There are no more emphemeral outbound ports available to bind the socket to, in order to send.
+
+ // The send timeout duration passed before all data was sent. See Socket_Option.Send_Timeout.
+ // NOTE: No, really. Presumably this means something different for nonblocking sockets...
+ Timeout = c.int(os.EWOULDBLOCK),
+ Not_Socket = c.int(os.ENOTSOCK), // The so-called socket is not an open socket.
+ Not_Descriptor = c.int(os.EBADF), // The so-called socket is, in fact, not even a valid descriptor.
+ Bad_Buffer = c.int(os.EFAULT), // The buffer did not point to a valid location in memory.
+ Interrupted = c.int(os.EINTR), // A signal occurred before any data was transmitted. See signal(7).
+
+ // The send queue was full.
+ // This is usually a transient issue.
+ //
+ // This also shouldn't normally happen on Linux, as data is dropped if it
+ // doesn't fit in the send queue.
+ No_Buffer_Space_Available = c.int(os.ENOBUFS),
+ No_Memory_Available = c.int(os.ENOMEM), // No memory was available to properly manage the send queue.
+}
+
+Shutdown_Manner :: enum c.int {
+ Receive = c.int(os.SHUT_RD),
+ Send = c.int(os.SHUT_WR),
+ Both = c.int(os.SHUT_RDWR),
+}
+
+Shutdown_Error :: enum c.int {
+ None = 0,
+ Aborted = c.int(os.ECONNABORTED),
+ Reset = c.int(os.ECONNRESET),
+ Offline = c.int(os.ENETDOWN),
+ Not_Connected = c.int(os.ENOTCONN),
+ Not_Socket = c.int(os.ENOTSOCK),
+ Invalid_Manner = c.int(os.EINVAL),
+}
+
+Socket_Option_Error :: enum c.int {
+ None = 0,
+ Offline = c.int(os.ENETDOWN),
+ Timeout_When_Keepalive_Set = c.int(os.ENETRESET),
+ Invalid_Option_For_Socket = c.int(os.ENOPROTOOPT),
+ Reset_When_Keepalive_Set = c.int(os.ENOTCONN),
+ Not_Socket = c.int(os.ENOTSOCK),
+}
+
+Set_Blocking_Error :: enum c.int {
+ None = 0,
+
+ // TODO: add errors occuring on followig calls:
+ // flags, _ := os.fcntl(sd, os.F_GETFL, 0)
+ // os.fcntl(sd, os.F_SETFL, flags | int(os.O_NONBLOCK))
+}
\ No newline at end of file
diff --git a/core/net/errors_windows.odin b/core/net/errors_windows.odin
new file mode 100644
index 000000000..272aa0a96
--- /dev/null
+++ b/core/net/errors_windows.odin
@@ -0,0 +1,273 @@
+package net
+// +build windows
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:c"
+import win "core:sys/windows"
+
+Create_Socket_Error :: enum c.int {
+ None = 0,
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+ Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT,
+ No_Socket_Descriptors_Available = win.WSAEMFILE,
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Protocol_Unsupported_By_System = win.WSAEPROTONOSUPPORT,
+ Wrong_Protocol_For_Socket = win.WSAEPROTOTYPE,
+ Family_And_Socket_Type_Mismatch = win.WSAESOCKTNOSUPPORT,
+}
+
+Dial_Error :: enum c.int {
+ None = 0,
+ Port_Required = -1,
+ Address_In_Use = win.WSAEADDRINUSE,
+ In_Progress = win.WSAEALREADY,
+ Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL,
+ Wrong_Family_For_Socket = win.WSAEAFNOSUPPORT,
+ Refused = win.WSAECONNREFUSED,
+ Is_Listening_Socket = win.WSAEINVAL,
+ Already_Connected = win.WSAEISCONN,
+ Network_Unreachable = win.WSAENETUNREACH, // Device is offline
+ Host_Unreachable = win.WSAEHOSTUNREACH, // Remote host cannot be reached
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Not_Socket = win.WSAENOTSOCK,
+ Timeout = win.WSAETIMEDOUT,
+ Would_Block = win.WSAEWOULDBLOCK, // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+}
+
+Bind_Error :: enum c.int {
+ None = 0,
+ Address_In_Use = win.WSAEADDRINUSE, // Another application is currently bound to this endpoint.
+ Given_Nonlocal_Address = win.WSAEADDRNOTAVAIL, // The address is not a local address on this machine.
+ Broadcast_Disabled = win.WSAEACCES, // To bind a UDP socket to the broadcast address, the appropriate socket option must be set.
+ Address_Family_Mismatch = win.WSAEFAULT, // The address family of the address does not match that of the socket.
+ Already_Bound = win.WSAEINVAL, // The socket is already bound to an address.
+ No_Ports_Available = win.WSAENOBUFS, // There are not enough ephemeral ports available.
+}
+
+Listen_Error :: enum c.int {
+ None = 0,
+ Address_In_Use = win.WSAEADDRINUSE,
+ Already_Connected = win.WSAEISCONN,
+ No_Socket_Descriptors_Available = win.WSAEMFILE,
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Nonlocal_Address = win.WSAEADDRNOTAVAIL,
+ Not_Socket = win.WSAENOTSOCK,
+ Listening_Not_Supported_For_This_Socket = win.WSAEOPNOTSUPP,
+}
+
+Accept_Error :: enum c.int {
+ None = 0,
+ Not_Listening = win.WSAEINVAL,
+ No_Socket_Descriptors_Available_For_Client_Socket = win.WSAEMFILE,
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Not_Socket = win.WSAENOTSOCK,
+ Not_Connection_Oriented_Socket = win.WSAEOPNOTSUPP,
+
+ // TODO: we may need special handling for this; maybe make a socket a struct with metadata?
+ Would_Block = win.WSAEWOULDBLOCK,
+}
+
+TCP_Recv_Error :: enum c.int {
+ None = 0,
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+ Not_Connected = win.WSAENOTCONN,
+ Bad_Buffer = win.WSAEFAULT,
+ Keepalive_Failure = win.WSAENETRESET,
+ Not_Socket = win.WSAENOTSOCK,
+ Shutdown = win.WSAESHUTDOWN,
+ Would_Block = win.WSAEWOULDBLOCK,
+
+ // TODO: not functionally different from Reset; merge?
+ Aborted = win.WSAECONNABORTED,
+ Timeout = win.WSAETIMEDOUT,
+
+ // TODO(tetra): Determine when this is different from the syscall returning n=0 and maybe normalize them?
+ Connection_Closed = win.WSAECONNRESET,
+
+ // TODO: verify can actually happen
+ Host_Unreachable = win.WSAEHOSTUNREACH,
+}
+
+UDP_Recv_Error :: enum c.int {
+ None = 0,
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+
+ // TODO: not functionally different from Reset; merge?
+ // UDP packets are limited in size, and the length of the incoming message exceeded it.
+ Aborted = win.WSAECONNABORTED,
+ Truncated = win.WSAEMSGSIZE,
+ Remote_Not_Listening = win.WSAECONNRESET, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data.
+ Shutdown = win.WSAESHUTDOWN,
+ Broadcast_Disabled = win.WSAEACCES, // A broadcast address was specified, but the .Broadcast socket option isn't set.
+ Bad_Buffer = win.WSAEFAULT,
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Not_Socket = win.WSAENOTSOCK, // The socket is not valid socket handle.
+ Would_Block = win.WSAEWOULDBLOCK,
+ Host_Unreachable = win.WSAEHOSTUNREACH, // The remote host cannot be reached from this host at this time.
+ Offline = win.WSAENETUNREACH, // The network cannot be reached from this host at this time.
+ Timeout = win.WSAETIMEDOUT,
+
+ // TODO: can this actually happen? The socket isn't bound; an unknown flag specified; or MSG_OOB specified with SO_OOBINLINE enabled.
+ Incorrectly_Configured = win.WSAEINVAL,
+ TTL_Expired = win.WSAENETRESET, // The message took more hops than was allowed (the Time To Live) to reach the remote endpoint.
+}
+
+// TODO: consider merging some errors to make handling them easier
+// TODO: verify once more what errors to actually expose
+TCP_Send_Error :: enum c.int {
+ None = 0,
+
+ // TODO: not functionally different from Reset; merge?
+ Aborted = win.WSAECONNABORTED,
+ Not_Connected = win.WSAENOTCONN,
+ Shutdown = win.WSAESHUTDOWN,
+ Connection_Closed = win.WSAECONNRESET,
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+ Host_Unreachable = win.WSAEHOSTUNREACH,
+
+ // TODO: verify possible, as not mentioned in docs
+ Offline = win.WSAENETUNREACH,
+ Timeout = win.WSAETIMEDOUT,
+
+ // A broadcast address was specified, but the .Broadcast socket option isn't set.
+ Broadcast_Disabled = win.WSAEACCES,
+ Bad_Buffer = win.WSAEFAULT,
+
+ // Connection is broken due to keepalive activity detecting a failure during the operation.
+ Keepalive_Failure = win.WSAENETRESET, // TODO: not functionally different from Reset; merge?
+ Not_Socket = win.WSAENOTSOCK, // The so-called socket is not an open socket.
+}
+
+UDP_Send_Error :: enum c.int {
+ None = 0,
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+
+ // TODO: not functionally different from Reset; merge?
+ Aborted = win.WSAECONNABORTED, // UDP packets are limited in size, and len(buf) exceeded it.
+ Message_Too_Long = win.WSAEMSGSIZE, // The machine at the remote endpoint doesn't have the given port open to receiving UDP data.
+ Remote_Not_Listening = win.WSAECONNRESET,
+ Shutdown = win.WSAESHUTDOWN, // A broadcast address was specified, but the .Broadcast socket option isn't set.
+ Broadcast_Disabled = win.WSAEACCES,
+ Bad_Buffer = win.WSAEFAULT, // Connection is broken due to keepalive activity detecting a failure during the operation.
+
+ // TODO: not functionally different from Reset; merge?
+ Keepalive_Failure = win.WSAENETRESET,
+ No_Buffer_Space_Available = win.WSAENOBUFS,
+ Not_Socket = win.WSAENOTSOCK, // The socket is not valid socket handle.
+
+ // This socket is unidirectional and cannot be used to send any data.
+ // TODO: verify possible; decide whether to keep if not
+ Receive_Only = win.WSAEOPNOTSUPP,
+ Would_Block = win.WSAEWOULDBLOCK,
+ Host_Unreachable = win.WSAEHOSTUNREACH, // The remote host cannot be reached from this host at this time.
+ Cannot_Use_Any_Address = win.WSAEADDRNOTAVAIL, // Attempt to send to the Any address.
+ Family_Not_Supported_For_This_Socket = win.WSAEAFNOSUPPORT, // The address is of an incorrect address family for this socket.
+ Offline = win.WSAENETUNREACH, // The network cannot be reached from this host at this time.
+ Timeout = win.WSAETIMEDOUT,
+}
+
+Shutdown_Manner :: enum c.int {
+ Receive = win.SD_RECEIVE,
+ Send = win.SD_SEND,
+ Both = win.SD_BOTH,
+}
+
+Shutdown_Error :: enum c.int {
+ None = 0,
+ Aborted = win.WSAECONNABORTED,
+ Reset = win.WSAECONNRESET,
+ Offline = win.WSAENETDOWN,
+ Not_Connected = win.WSAENOTCONN,
+ Not_Socket = win.WSAENOTSOCK,
+ Invalid_Manner = win.WSAEINVAL,
+}
+
+Socket_Option :: enum c.int {
+ // bool: Whether the address that this socket is bound to can be reused by other sockets.
+ // This allows you to bypass the cooldown period if a program dies while the socket is bound.
+ Reuse_Address = win.SO_REUSEADDR,
+
+ // bool: Whether other programs will be inhibited from binding the same endpoint as this socket.
+ Exclusive_Addr_Use = win.SO_EXCLUSIVEADDRUSE,
+
+ // bool: When true, keepalive packets will be automatically be sent for this connection. TODO: verify this understanding
+ Keep_Alive = win.SO_KEEPALIVE,
+
+ // bool: When true, client connections will immediately be sent a TCP/IP RST response, rather than being accepted.
+ Conditional_Accept = win.SO_CONDITIONAL_ACCEPT,
+
+ // bool: If true, when the socket is closed, but data is still waiting to be sent, discard that data.
+ Dont_Linger = win.SO_DONTLINGER,
+
+ // bool: When true, 'out-of-band' data sent over the socket will be read by a normal net.recv() call, the same as normal 'in-band' data.
+ Out_Of_Bounds_Data_Inline = win.SO_OOBINLINE,
+
+ // bool: When true, disables send-coalescing, therefore reducing latency.
+ TCP_Nodelay = win.TCP_NODELAY,
+
+ // win.LINGER: Customizes how long (if at all) the socket will remain open when there
+ // is some remaining data waiting to be sent, and net.close() is called.
+ Linger = win.SO_LINGER,
+
+ // win.DWORD: The size, in bytes, of the OS-managed receive-buffer for this socket.
+ Receive_Buffer_Size = win.SO_RCVBUF,
+
+ // win.DWORD: The size, in bytes, of the OS-managed send-buffer for this socket.
+ Send_Buffer_Size = win.SO_SNDBUF,
+
+ // win.DWORD: For blocking sockets, the time in milliseconds to wait for incoming data to be received, before giving up and returning .Timeout.
+ // For non-blocking sockets, ignored.
+ // Use a value of zero to potentially wait forever.
+ Receive_Timeout = win.SO_RCVTIMEO,
+
+ // win.DWORD: For blocking sockets, the time in milliseconds to wait for outgoing data to be sent, before giving up and returning .Timeout.
+ // For non-blocking sockets, ignored.
+ // Use a value of zero to potentially wait forever.
+ Send_Timeout = win.SO_SNDTIMEO,
+
+ // bool: Allow sending to, receiving from, and binding to, a broadcast address.
+ Broadcast = win.SO_BROADCAST,
+}
+
+Socket_Option_Error :: enum c.int {
+ None = 0,
+ Linger_Only_Supports_Whole_Seconds = 1,
+
+ // The given value is too big or small to be given to the OS.
+ Value_Out_Of_Range,
+
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+ Timeout_When_Keepalive_Set = win.WSAENETRESET,
+ Invalid_Option_For_Socket = win.WSAENOPROTOOPT,
+ Reset_When_Keepalive_Set = win.WSAENOTCONN,
+ Not_Socket = win.WSAENOTSOCK,
+}
+
+Set_Blocking_Error :: enum c.int {
+ None = 0,
+
+ Network_Subsystem_Failure = win.WSAENETDOWN,
+ Blocking_Call_In_Progress = win.WSAEINPROGRESS,
+ Not_Socket = win.WSAENOTSOCK,
+
+ // TODO: are those errors possible?
+ Network_Subsystem_Not_Initialized = win.WSAENOTINITIALISED,
+ Invalid_Argument_Pointer = win.WSAEFAULT,
+}
\ No newline at end of file
diff --git a/core/net/interface.odin b/core/net/interface.odin
new file mode 100644
index 000000000..df7d0223e
--- /dev/null
+++ b/core/net/interface.odin
@@ -0,0 +1,79 @@
+// +build windows, linux, darwin
+package net
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:strings"
+
+MAX_INTERFACE_ENUMERATION_TRIES :: 3
+
+/*
+ `enumerate_interfaces` retrieves a list of network interfaces with their associated properties.
+*/
+enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+ return _enumerate_interfaces(allocator)
+}
+
+/*
+ `destroy_interfaces` cleans up a list of network interfaces retrieved by e.g. `enumerate_interfaces`.
+*/
+destroy_interfaces :: proc(interfaces: []Network_Interface, allocator := context.allocator) {
+ context.allocator = allocator
+
+ for i in interfaces {
+ delete(i.adapter_name)
+ delete(i.friendly_name)
+ delete(i.description)
+ delete(i.dns_suffix)
+
+ delete(i.physical_address)
+
+ delete(i.unicast)
+ delete(i.multicast)
+ delete(i.anycast)
+ delete(i.gateways)
+ }
+ delete(interfaces, allocator)
+}
+
+/*
+ Turns a slice of bytes (from e.g. `get_adapters_addresses`) into a "XX:XX:XX:..." string.
+*/
+physical_address_to_string :: proc(phy_addr: []u8, allocator := context.allocator) -> (phy_string: string) {
+ context.allocator = allocator
+
+ MAC_HEX := "0123456789ABCDEF"
+
+ if len(phy_addr) == 0 {
+ return ""
+ }
+
+ buf: strings.Builder
+
+ for b, i in phy_addr {
+ if i > 0 {
+ strings.write_rune(&buf, ':')
+ }
+
+ hi := rune(MAC_HEX[b >> 4])
+ lo := rune(MAC_HEX[b & 15])
+ strings.write_rune(&buf, hi)
+ strings.write_rune(&buf, lo)
+ }
+ return strings.to_string(buf)
+}
diff --git a/core/net/interface_darwin.odin b/core/net/interface_darwin.odin
new file mode 100644
index 000000000..90d996a4a
--- /dev/null
+++ b/core/net/interface_darwin.odin
@@ -0,0 +1,32 @@
+package net
+//+build darwin
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+
+*/
+
+@(private)
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+ context.allocator = allocator
+
+
+ // TODO: Implement. Can probably use the (current) Linux implementation,
+ // which will itself be switched over to talking to the kernel via NETLINK protocol
+ // once we have raw sockets.
+
+ unimplemented()
+}
\ No newline at end of file
diff --git a/core/net/interface_linux.odin b/core/net/interface_linux.odin
new file mode 100644
index 000000000..e889cfa97
--- /dev/null
+++ b/core/net/interface_linux.odin
@@ -0,0 +1,140 @@
+package net
+//+build linux
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+
+ This file uses `getifaddrs` libc call to enumerate interfaces.
+ TODO: When we have raw sockets, split off into its own file for Linux so we can use the NETLINK protocol and bypass libc.
+*/
+
+import "core:os"
+import "core:strings"
+
+@(private)
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+ context.allocator = allocator
+
+ head: ^os.ifaddrs
+
+ if res := os._getifaddrs(&head); res < 0 {
+ return {}, .Unable_To_Enumerate_Network_Interfaces
+ }
+
+ /*
+ Unlike Windows, *nix regrettably doesn't return all it knows about an interface in one big struct.
+ We're going to have to iterate over a list and coalesce information as we go.
+ */
+ ifaces: map[string]^Network_Interface
+ defer delete(ifaces)
+
+ for ifaddr := head; ifaddr != nil; ifaddr = ifaddr.next {
+ adapter_name := string(ifaddr.name)
+
+ /*
+ Check if we have seen this interface name before so we can reuse the `Network_Interface`.
+ Else, create a new one.
+ */
+ if adapter_name not_in ifaces {
+ ifaces[adapter_name] = new(Network_Interface)
+ ifaces[adapter_name].adapter_name = strings.clone(adapter_name)
+ }
+ iface := ifaces[adapter_name]
+
+ address: Address
+ netmask: Netmask
+
+ if ifaddr.address != nil {
+ switch int(ifaddr.address.sa_family) {
+ case os.AF_INET, os.AF_INET6:
+ address = _sockaddr_basic_to_endpoint(ifaddr.address).address
+
+ case os.AF_PACKET:
+ /*
+ For some obscure reason the 64-bit `getifaddrs` call returns a pointer to a
+ 32-bit `RTNL_LINK_STATS` structure, which of course means that tx/rx byte count
+ is truncated beyond usefulness.
+
+ We're not going to retrieve stats now. Instead this serves as a reminder to use
+ the NETLINK protocol for this purpose.
+
+ But in case you were curious:
+ stats := transmute(^os.rtnl_link_stats)ifaddr.data
+ fmt.println(stats)
+ */
+ case:
+ }
+ }
+
+ if ifaddr.netmask != nil {
+ switch int(ifaddr.netmask.sa_family) {
+ case os.AF_INET, os.AF_INET6:
+ netmask = Netmask(_sockaddr_basic_to_endpoint(ifaddr.netmask).address)
+ case:
+ }
+ }
+
+ if ifaddr.broadcast_or_dest != nil && .BROADCAST in ifaddr.flags {
+ switch int(ifaddr.broadcast_or_dest.sa_family) {
+ case os.AF_INET, os.AF_INET6:
+ broadcast := _sockaddr_basic_to_endpoint(ifaddr.broadcast_or_dest).address
+ append(&iface.multicast, broadcast)
+ case:
+ }
+ }
+
+ if address != nil {
+ lease := Lease{
+ address = address,
+ netmask = netmask,
+ }
+ append(&iface.unicast, lease)
+ }
+
+ /*
+ TODO: Refine this based on the type of adapter.
+ */
+ state := Link_State{}
+
+ if .UP in ifaddr.flags {
+ state |= {.Up}
+ }
+
+ if .DORMANT in ifaddr.flags {
+ state |= {.Dormant}
+ }
+
+ if .LOOPBACK in ifaddr.flags {
+ state |= {.Loopback}
+ }
+ iface.link.state = state
+ }
+
+ /*
+ Free the OS structures.
+ */
+ os._freeifaddrs(head)
+
+ /*
+ Turn the map into a slice to return.
+ */
+ _interfaces := make([dynamic]Network_Interface, 0, allocator)
+ for _, iface in ifaces {
+ append(&_interfaces, iface^)
+ free(iface)
+ }
+ return _interfaces[:], {}
+}
\ No newline at end of file
diff --git a/core/net/interface_windows.odin b/core/net/interface_windows.odin
new file mode 100644
index 000000000..f8bac253a
--- /dev/null
+++ b/core/net/interface_windows.odin
@@ -0,0 +1,177 @@
+package net
+//+build windows
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import sys "core:sys/windows"
+import strings "core:strings"
+
+_enumerate_interfaces :: proc(allocator := context.allocator) -> (interfaces: []Network_Interface, err: Network_Error) {
+ context.allocator = allocator
+
+ buf: []u8
+ defer delete(buf)
+
+ buf_size: u32
+ res: u32
+
+ gaa: for _ in 1..=MAX_INTERFACE_ENUMERATION_TRIES {
+ res = sys.get_adapters_addresses(
+ .Unspecified, // Return both IPv4 and IPv6 adapters.
+ sys.GAA_Flags{
+ .Include_Prefix, // (XP SP1+) Return a list of IP address prefixes on this adapter. When this flag is set, IP address prefixes are returned for both IPv6 and IPv4 addresses.
+ .Include_Gateways, // (Vista+) Return the addresses of default gateways.
+ .Include_Tunnel_Binding_Order, // (Vista+) Return the adapter addresses sorted in tunnel binding order.
+ },
+ nil, // Reserved
+ (^sys.IP_Adapter_Addresses)(raw_data(buf)),
+ &buf_size,
+ )
+
+ switch res {
+ case 111: // ERROR_BUFFER_OVERFLOW:
+ delete(buf)
+ buf = make([]u8, buf_size)
+ case 0:
+ break gaa
+ case:
+ return {}, Platform_Error(res)
+ }
+ }
+
+ if res != 0 {
+ return {}, .Unable_To_Enumerate_Network_Interfaces
+ }
+
+ _interfaces := make([dynamic]Network_Interface, 0, allocator)
+ for adapter := (^sys.IP_Adapter_Addresses)(raw_data(buf)); adapter != nil; adapter = adapter.Next {
+ friendly_name, err1 := sys.wstring_to_utf8(sys.wstring(adapter.FriendlyName), 256, allocator)
+ if err1 != nil { return {}, Platform_Error(err1) }
+
+ description, err2 := sys.wstring_to_utf8(sys.wstring(adapter.Description), 256, allocator)
+ if err2 != nil { return {}, Platform_Error(err2) }
+
+ dns_suffix, err3 := sys.wstring_to_utf8(sys.wstring(adapter.DnsSuffix), 256, allocator)
+ if err3 != nil { return {}, Platform_Error(err3) }
+
+ interface := Network_Interface{
+ adapter_name = strings.clone(string(adapter.AdapterName)),
+ friendly_name = friendly_name,
+ description = description,
+ dns_suffix = dns_suffix,
+
+ mtu = adapter.MTU,
+
+ link = {
+ transmit_speed = adapter.TransmitLinkSpeed,
+ receive_speed = adapter.ReceiveLinkSpeed,
+ },
+ }
+
+ if adapter.PhysicalAddressLength > 0 && adapter.PhysicalAddressLength <= len(adapter.PhysicalAddress) {
+ interface.physical_address = physical_address_to_string(adapter.PhysicalAddress[:adapter.PhysicalAddressLength])
+ }
+
+ for u_addr := (^sys.IP_ADAPTER_UNICAST_ADDRESS_LH)(adapter.FirstUnicastAddress); u_addr != nil; u_addr = u_addr.Next {
+ win_addr := parse_socket_address(u_addr.Address)
+
+ lease := Lease{
+ address = win_addr.address,
+ origin = {
+ prefix = Prefix_Origin(u_addr.PrefixOrigin),
+ suffix = Suffix_Origin(u_addr.SuffixOrigin),
+ },
+ lifetime = {
+ valid = u_addr.ValidLifetime,
+ preferred = u_addr.PreferredLifetime,
+ lease = u_addr.LeaseLifetime,
+ },
+ address_duplication = Address_Duplication(u_addr.DadState),
+ }
+ append(&interface.unicast, lease)
+ }
+
+ for a_addr := (^sys.IP_ADAPTER_ANYCAST_ADDRESS_XP)(adapter.FirstAnycastAddress); a_addr != nil; a_addr = a_addr.Next {
+ addr := parse_socket_address(a_addr.Address)
+ append(&interface.anycast, addr.address)
+ }
+
+ for m_addr := (^sys.IP_ADAPTER_MULTICAST_ADDRESS_XP)(adapter.FirstMulticastAddress); m_addr != nil; m_addr = m_addr.Next {
+ addr := parse_socket_address(m_addr.Address)
+ append(&interface.multicast, addr.address)
+ }
+
+ for g_addr := (^sys.IP_ADAPTER_GATEWAY_ADDRESS_LH)(adapter.FirstGatewayAddress); g_addr != nil; g_addr = g_addr.Next {
+ addr := parse_socket_address(g_addr.Address)
+ append(&interface.gateways, addr.address)
+ }
+
+ interface.dhcp_v4 = parse_socket_address(adapter.Dhcpv4Server).address
+ interface.dhcp_v6 = parse_socket_address(adapter.Dhcpv6Server).address
+
+ switch adapter.OperStatus {
+ case .Up: interface.link.state = {.Up}
+ case .Down: interface.link.state = {.Down}
+ case .Testing: interface.link.state = {.Testing}
+ case .Dormant: interface.link.state = {.Dormant}
+ case .NotPresent: interface.link.state = {.Not_Present}
+ case .LowerLayerDown: interface.link.state = {.Lower_Layer_Down}
+ case .Unknown: fallthrough
+ case: interface.link.state = {}
+ }
+
+ interface.tunnel_type = Tunnel_Type(adapter.TunnelType)
+
+ append(&_interfaces, interface)
+ }
+
+ return _interfaces[:], {}
+}
+
+/*
+ Interpret SOCKET_ADDRESS as an Address
+*/
+parse_socket_address :: proc(addr_in: sys.SOCKET_ADDRESS) -> (addr: Endpoint) {
+ if addr_in.lpSockaddr == nil {
+ return // Empty or invalid address type
+ }
+
+ sock := addr_in.lpSockaddr^
+
+ switch sock.sa_family {
+ case u16(sys.AF_INET):
+ win_addr := cast(^sys.sockaddr_in)addr_in.lpSockaddr
+ port := int(win_addr.sin_port)
+ return Endpoint {
+ address = IP4_Address(transmute([4]byte)win_addr.sin_addr),
+ port = port,
+ }
+
+ case u16(sys.AF_INET6):
+ win_addr := cast(^sys.sockaddr_in6)addr_in.lpSockaddr
+ port := int(win_addr.sin6_port)
+ return Endpoint {
+ address = IP6_Address(transmute([8]u16be)win_addr.sin6_addr),
+ port = port,
+ }
+
+
+ case: return // Empty or invalid address type
+ }
+ unreachable()
+}
\ No newline at end of file
diff --git a/core/net/socket.odin b/core/net/socket.odin
new file mode 100644
index 000000000..8cdf7cceb
--- /dev/null
+++ b/core/net/socket.odin
@@ -0,0 +1,183 @@
+// +build windows, linux, darwin
+package net
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022-2023 Tetralux
+ Copyright 2022-2023 Colin Davidson
+ Copyright 2022-2023 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+any_socket_to_socket :: proc(socket: Any_Socket) -> Socket {
+ switch s in socket {
+ case TCP_Socket: return Socket(s)
+ case UDP_Socket: return Socket(s)
+ case:
+ // TODO(tetra): Bluetooth, Raw
+ return Socket({})
+ }
+}
+
+/*
+ Expects both hostname and port to be present in the `hostname_and_port` parameter, either as:
+ `a.host.name:9999`, or as `1.2.3.4:9999`, or IP6 equivalent.
+
+ Calls `parse_hostname_or_endpoint` and `resolve`, then `dial_tcp_from_endpoint`.
+*/
+dial_tcp_from_hostname_and_port_string :: proc(hostname_and_port: string, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+ target := parse_hostname_or_endpoint(hostname_and_port) or_return
+ switch t in target {
+ case Endpoint:
+ return dial_tcp_from_endpoint(t, options)
+ case Host:
+ if t.port == 0 {
+ return 0, .Port_Required
+ }
+ ep4, ep6 := resolve(t.hostname) or_return
+ ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
+ ep.port = t.port
+ return dial_tcp_from_endpoint(ep, options)
+ }
+ unreachable()
+}
+
+/*
+ Expects the `hostname` as a string and `port` as a `int`.
+ `parse_hostname_or_endpoint` is called and the `hostname` will be resolved into an IP.
+
+ If a `hostname` of form `a.host.name:9999` is given, the port will be ignored in favor of the explicit `port` param.
+*/
+dial_tcp_from_hostname_with_port_override :: proc(hostname: string, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+ target := parse_hostname_or_endpoint(hostname) or_return
+ switch t in target {
+ case Endpoint:
+ return dial_tcp_from_endpoint({t.address, port}, options)
+ case Host:
+ if port == 0 {
+ return 0, .Port_Required
+ }
+ ep4, ep6 := resolve(t.hostname) or_return
+ ep := ep4 if ep4.address != nil else ep6 // NOTE(tetra): We don't know what family the server uses, so we just default to IP4.
+ ep.port = port
+ return dial_tcp_from_endpoint(ep, options)
+ }
+ unreachable()
+}
+
+// Dial from an Address
+dial_tcp_from_address_and_port :: proc(address: Address, port: int, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+ return dial_tcp_from_endpoint({address, port}, options)
+}
+
+dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+ return _dial_tcp_from_endpoint(endpoint, options)
+}
+
+dial_tcp :: proc{
+ dial_tcp_from_endpoint,
+ dial_tcp_from_address_and_port,
+ dial_tcp_from_hostname_and_port_string,
+ dial_tcp_from_hostname_with_port_override,
+}
+
+create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+ return _create_socket(family, protocol)
+}
+
+bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+ return _bind(socket, ep)
+}
+
+/*
+ This type of socket becomes bound when you try to send data.
+ It is likely what you want if you want to send data unsolicited.
+
+ This is like a client TCP socket, except that it can send data to any remote endpoint without needing to establish a connection first.
+*/
+make_unbound_udp_socket :: proc(family: Address_Family) -> (socket: UDP_Socket, err: Network_Error) {
+ sock := create_socket(family, .UDP) or_return
+ socket = sock.(UDP_Socket)
+ return
+}
+
+/*
+ This type of socket is bound immediately, which enables it to receive data on the port.
+ Since it's UDP, it's also able to send data without receiving any first.
+
+ This is like a listening TCP socket, except that data packets can be sent and received without needing to establish a connection first.
+ The `bound_address` is the address of the network interface that you want to use, or a loopback address if you don't care which to use.
+*/
+make_bound_udp_socket :: proc(bound_address: Address, port: int) -> (socket: UDP_Socket, err: Network_Error) {
+ if bound_address == nil {
+ return {}, .Bad_Address
+ }
+ socket = make_unbound_udp_socket(family_from_address(bound_address)) or_return
+ bind(socket, {bound_address, port}) or_return
+ return
+}
+
+listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
+ assert(backlog > 0 && backlog < int(max(i32)))
+
+ return _listen_tcp(interface_endpoint, backlog)
+}
+
+accept_tcp :: proc(socket: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+ return _accept_tcp(socket, options)
+}
+
+close :: proc(socket: Any_Socket) {
+ _close(socket)
+}
+
+recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+ return _recv_tcp(socket, buf)
+}
+
+recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+ return _recv_udp(socket, buf)
+}
+
+recv :: proc{recv_tcp, recv_udp}
+
+/*
+ Repeatedly sends data until the entire buffer is sent.
+ If a send fails before all data is sent, returns the amount sent up to that point.
+*/
+send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+ return _send_tcp(socket, buf)
+}
+
+/*
+ Sends a single UDP datagram packet.
+
+ Datagrams are limited in size; attempting to send more than this limit at once will result in a Message_Too_Long error.
+ UDP packets are not guarenteed to be received in order.
+*/
+send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+ return _send_udp(socket, buf, to)
+}
+
+send :: proc{send_tcp, send_udp}
+
+shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+ return _shutdown(socket, manner)
+}
+
+set_option :: proc(socket: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+ return _set_option(socket, option, value, loc)
+}
+
+set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+ return _set_blocking(socket, should_block)
+}
\ No newline at end of file
diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin
new file mode 100644
index 000000000..081892afd
--- /dev/null
+++ b/core/net/socket_darwin.odin
@@ -0,0 +1,371 @@
+package net
+// +build darwin
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+Socket_Option :: enum c.int {
+ Reuse_Address = c.int(os.SO_REUSEADDR),
+ Keep_Alive = c.int(os.SO_KEEPALIVE),
+ Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE),
+ TCP_Nodelay = c.int(os.TCP_NODELAY),
+ Linger = c.int(os.SO_LINGER),
+ Receive_Buffer_Size = c.int(os.SO_RCVBUF),
+ Send_Buffer_Size = c.int(os.SO_SNDBUF),
+ Receive_Timeout = c.int(os.SO_RCVTIMEO),
+ Send_Timeout = c.int(os.SO_SNDTIMEO),
+}
+
+@(private)
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+ c_type, c_protocol, c_family: int
+
+ switch family {
+ case .IP4: c_family = os.AF_INET
+ case .IP6: c_family = os.AF_INET6
+ case:
+ unreachable()
+ }
+
+ switch protocol {
+ case .TCP: c_type = os.SOCK_STREAM; c_protocol = os.IPPROTO_TCP
+ case .UDP: c_type = os.SOCK_DGRAM; c_protocol = os.IPPROTO_UDP
+ case:
+ unreachable()
+ }
+
+ sock, ok := os.socket(c_family, c_type, c_protocol)
+ if ok != os.ERROR_NONE {
+ err = Create_Socket_Error(ok)
+ return
+ }
+
+ switch protocol {
+ case .TCP: return TCP_Socket(sock), nil
+ case .UDP: return UDP_Socket(sock), nil
+ case:
+ unreachable()
+ }
+}
+
+@(private)
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) {
+ if endpoint.port == 0 {
+ return 0, .Port_Required
+ }
+
+ family := family_from_endpoint(endpoint)
+ sock := create_socket(family, .TCP) or_return
+ skt = sock.(TCP_Socket)
+
+ // NOTE(tetra): This is so that if we crash while the socket is open, we can
+ // bypass the cooldown period, and allow the next run of the program to
+ // use the same address immediately.
+ _ = set_option(skt, .Reuse_Address, true)
+
+ sockaddr := _endpoint_to_sockaddr(endpoint)
+ res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
+ if res != os.ERROR_NONE {
+ err = Dial_Error(res)
+ return
+ }
+
+ return
+}
+
+@(private)
+_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+ sockaddr := _endpoint_to_sockaddr(ep)
+ s := any_socket_to_socket(skt)
+ res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
+ if res != os.ERROR_NONE {
+ err = Bind_Error(res)
+ }
+ return
+}
+
+@(private)
+_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) {
+ assert(backlog > 0 && i32(backlog) < max(i32))
+
+ family := family_from_endpoint(interface_endpoint)
+ sock := create_socket(family, .TCP) or_return
+ skt = sock.(TCP_Socket)
+
+ // NOTE(tetra): This is so that if we crash while the socket is open, we can
+ // bypass the cooldown period, and allow the next run of the program to
+ // use the same address immediately.
+ //
+ // TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
+ set_option(sock, .Reuse_Address, true) or_return
+
+ bind(sock, interface_endpoint) or_return
+
+ res := os.listen(os.Socket(skt), backlog)
+ if res != os.ERROR_NONE {
+ err = Listen_Error(res)
+ return
+ }
+
+ return
+}
+
+@(private)
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+ sockaddr: os.SOCKADDR_STORAGE_LH
+ sockaddrlen := c.int(size_of(sockaddr))
+
+ client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
+ if ok != os.ERROR_NONE {
+ err = Accept_Error(ok)
+ return
+ }
+ client = TCP_Socket(client_sock)
+ source = _sockaddr_to_endpoint(&sockaddr)
+ return
+}
+
+@(private)
+_close :: proc(skt: Any_Socket) {
+ s := any_socket_to_socket(skt)
+ os.close(os.Handle(os.Socket(s)))
+}
+
+@(private)
+_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+ if len(buf) <= 0 {
+ return
+ }
+ res, ok := os.recv(os.Socket(skt), buf, 0)
+ if ok != os.ERROR_NONE {
+ err = TCP_Recv_Error(ok)
+ return
+ }
+ return int(res), nil
+}
+
+@(private)
+_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+ if len(buf) <= 0 {
+ return
+ }
+
+ from: os.SOCKADDR_STORAGE_LH
+ fromsize := c.int(size_of(from))
+ res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
+ if ok != os.ERROR_NONE {
+ err = UDP_Recv_Error(ok)
+ return
+ }
+
+ bytes_read = int(res)
+ remote_endpoint = _sockaddr_to_endpoint(&from)
+ return
+}
+
+@(private)
+_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+ for bytes_written < len(buf) {
+ limit := min(int(max(i32)), len(buf) - bytes_written)
+ remaining := buf[bytes_written:][:limit]
+ res, ok := os.send(os.Socket(skt), remaining, 0)
+ if ok != os.ERROR_NONE {
+ err = TCP_Send_Error(ok)
+ return
+ }
+ bytes_written += int(res)
+ }
+ return
+}
+
+@(private)
+_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+ toaddr := _endpoint_to_sockaddr(to)
+ for bytes_written < len(buf) {
+ limit := min(1<<31, len(buf) - bytes_written)
+ remaining := buf[bytes_written:][:limit]
+ res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
+ if ok != os.ERROR_NONE {
+ err = UDP_Send_Error(ok)
+ return
+ }
+ bytes_written += int(res)
+ }
+ return
+}
+
+@(private)
+_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+ s := any_socket_to_socket(skt)
+ res := os.shutdown(os.Socket(s), int(manner))
+ if res != os.ERROR_NONE {
+ return Shutdown_Error(res)
+ }
+ return
+}
+
+@(private)
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+ level := os.SOL_SOCKET if option != .TCP_Nodelay else os.IPPROTO_TCP
+
+ // NOTE(tetra, 2022-02-15): On Linux, you cannot merely give a single byte for a bool;
+ // it _has_ to be a b32.
+ // I haven't tested if you can give more than that.
+ bool_value: b32
+ int_value: i32
+ timeval_value: os.Timeval
+
+ ptr: rawptr
+ len: os.socklen_t
+
+ switch option {
+ case
+ .Reuse_Address,
+ .Keep_Alive,
+ .Out_Of_Bounds_Data_Inline,
+ .TCP_Nodelay:
+ // TODO: verify whether these are options or not on Linux
+ // .Broadcast,
+ // .Conditional_Accept,
+ // .Dont_Linger:
+ switch x in value {
+ case bool, b8:
+ x2 := x
+ bool_value = b32((^bool)(&x2)^)
+ case b16:
+ bool_value = b32(x)
+ case b32:
+ bool_value = b32(x)
+ case b64:
+ bool_value = b32(x)
+ case:
+ panic("set_option() value must be a boolean here", loc)
+ }
+ ptr = &bool_value
+ len = size_of(bool_value)
+ case
+ .Linger,
+ .Send_Timeout,
+ .Receive_Timeout:
+ t, ok := value.(time.Duration)
+ if !ok do panic("set_option() value must be a time.Duration here", loc)
+
+ micros := i64(time.duration_microseconds(t))
+ timeval_value.microseconds = int(micros % 1e6)
+ timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6
+
+ ptr = &timeval_value
+ len = size_of(timeval_value)
+ case
+ .Receive_Buffer_Size,
+ .Send_Buffer_Size:
+ // TODO: check for out of range values and return .Value_Out_Of_Range?
+ switch i in value {
+ case i8, u8: i2 := i; int_value = os.socklen_t((^u8)(&i2)^)
+ case i16, u16: i2 := i; int_value = os.socklen_t((^u16)(&i2)^)
+ case i32, u32: i2 := i; int_value = os.socklen_t((^u32)(&i2)^)
+ case i64, u64: i2 := i; int_value = os.socklen_t((^u64)(&i2)^)
+ case i128, u128: i2 := i; int_value = os.socklen_t((^u128)(&i2)^)
+ case int, uint: i2 := i; int_value = os.socklen_t((^uint)(&i2)^)
+ case:
+ panic("set_option() value must be an integer here", loc)
+ }
+ ptr = &int_value
+ len = size_of(int_value)
+ }
+
+ skt := any_socket_to_socket(s)
+ res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
+ if res != os.ERROR_NONE {
+ return Socket_Option_Error(res)
+ }
+
+ return nil
+}
+
+@(private)
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+ socket := any_socket_to_socket(socket)
+
+ flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
+ if getfl_err != os.ERROR_NONE {
+ return Set_Blocking_Error(getfl_err)
+ }
+
+ if should_block {
+ flags &= ~int(os.O_NONBLOCK)
+ } else {
+ flags |= int(os.O_NONBLOCK)
+ }
+
+ _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
+ if setfl_err != os.ERROR_NONE {
+ return Set_Blocking_Error(setfl_err)
+ }
+
+ return nil
+}
+
+@private
+_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) {
+ switch a in ep.address {
+ case IP4_Address:
+ (^os.sockaddr_in)(&sockaddr)^ = os.sockaddr_in {
+ sin_port = u16be(ep.port),
+ sin_addr = transmute(os.in_addr) a,
+ sin_family = u8(os.AF_INET),
+ sin_len = size_of(os.sockaddr_in),
+ }
+ return
+ case IP6_Address:
+ (^os.sockaddr_in6)(&sockaddr)^ = os.sockaddr_in6 {
+ sin6_port = u16be(ep.port),
+ sin6_addr = transmute(os.in6_addr) a,
+ sin6_family = u8(os.AF_INET6),
+ sin6_len = size_of(os.sockaddr_in6),
+ }
+ return
+ }
+ unreachable()
+}
+
+@private
+_sockaddr_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
+ switch native_addr.family {
+ case u8(os.AF_INET):
+ addr := cast(^os.sockaddr_in) native_addr
+ port := int(addr.sin_port)
+ ep = Endpoint {
+ address = IP4_Address(transmute([4]byte) addr.sin_addr),
+ port = port,
+ }
+ case u8(os.AF_INET6):
+ addr := cast(^os.sockaddr_in6) native_addr
+ port := int(addr.sin6_port)
+ ep = Endpoint {
+ address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
+ port = port,
+ }
+ case:
+ panic("native_addr is neither IP4 or IP6 address")
+ }
+ return
+}
diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin
new file mode 100644
index 000000000..b7141e8ba
--- /dev/null
+++ b/core/net/socket_linux.odin
@@ -0,0 +1,407 @@
+package net
+// +build linux
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+Socket_Option :: enum c.int {
+ Reuse_Address = c.int(os.SO_REUSEADDR),
+ Keep_Alive = c.int(os.SO_KEEPALIVE),
+ Out_Of_Bounds_Data_Inline = c.int(os.SO_OOBINLINE),
+ TCP_Nodelay = c.int(os.TCP_NODELAY),
+ Linger = c.int(os.SO_LINGER),
+ Receive_Buffer_Size = c.int(os.SO_RCVBUF),
+ Send_Buffer_Size = c.int(os.SO_SNDBUF),
+ Receive_Timeout = c.int(os.SO_RCVTIMEO_NEW),
+ Send_Timeout = c.int(os.SO_SNDTIMEO_NEW),
+}
+
+@(private)
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+ c_type, c_protocol, c_family: int
+
+ switch family {
+ case .IP4: c_family = os.AF_INET
+ case .IP6: c_family = os.AF_INET6
+ case:
+ unreachable()
+ }
+
+ switch protocol {
+ case .TCP: c_type = os.SOCK_STREAM; c_protocol = os.IPPROTO_TCP
+ case .UDP: c_type = os.SOCK_DGRAM; c_protocol = os.IPPROTO_UDP
+ case:
+ unreachable()
+ }
+
+ sock, ok := os.socket(c_family, c_type, c_protocol)
+ if ok != os.ERROR_NONE {
+ err = Create_Socket_Error(ok)
+ return
+ }
+
+ switch protocol {
+ case .TCP: return TCP_Socket(sock), nil
+ case .UDP: return UDP_Socket(sock), nil
+ case:
+ unreachable()
+ }
+}
+
+@(private)
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (skt: TCP_Socket, err: Network_Error) {
+ if endpoint.port == 0 {
+ return 0, .Port_Required
+ }
+
+ family := family_from_endpoint(endpoint)
+ sock := create_socket(family, .TCP) or_return
+ skt = sock.(TCP_Socket)
+
+ // NOTE(tetra): This is so that if we crash while the socket is open, we can
+ // bypass the cooldown period, and allow the next run of the program to
+ // use the same address immediately.
+ _ = set_option(skt, .Reuse_Address, true)
+
+ sockaddr := _endpoint_to_sockaddr(endpoint)
+ res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
+ if res != os.ERROR_NONE {
+ err = Dial_Error(res)
+ return
+ }
+
+ if options.no_delay {
+ _ = _set_option(sock, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
+ }
+
+ return
+}
+
+@(private)
+_bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+ sockaddr := _endpoint_to_sockaddr(ep)
+ s := any_socket_to_socket(skt)
+ res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), size_of(sockaddr))
+ if res != os.ERROR_NONE {
+ err = Bind_Error(res)
+ }
+ return
+}
+
+@(private)
+_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_Socket, err: Network_Error) {
+ assert(backlog > 0 && i32(backlog) < max(i32))
+
+ family := family_from_endpoint(interface_endpoint)
+ sock := create_socket(family, .TCP) or_return
+ skt = sock.(TCP_Socket)
+
+ // NOTE(tetra): This is so that if we crash while the socket is open, we can
+ // bypass the cooldown period, and allow the next run of the program to
+ // use the same address immediately.
+ //
+ // TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
+ set_option(sock, .Reuse_Address, true) or_return
+
+ bind(sock, interface_endpoint) or_return
+
+ res := os.listen(os.Socket(skt), backlog)
+ if res != os.ERROR_NONE {
+ err = Listen_Error(res)
+ return
+ }
+
+ return
+}
+
+@(private)
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+ sockaddr: os.SOCKADDR_STORAGE_LH
+ sockaddrlen := c.int(size_of(sockaddr))
+
+ client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
+ if ok != os.ERROR_NONE {
+ err = Accept_Error(ok)
+ return
+ }
+ client = TCP_Socket(client_sock)
+ source = _sockaddr_storage_to_endpoint(&sockaddr)
+ if options.no_delay {
+ _ = _set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
+ }
+ return
+}
+
+@(private)
+_close :: proc(skt: Any_Socket) {
+ s := any_socket_to_socket(skt)
+ os.close(os.Handle(os.Socket(s)))
+}
+
+@(private)
+_recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+ if len(buf) <= 0 {
+ return
+ }
+ res, ok := os.recv(os.Socket(skt), buf, 0)
+ if ok != os.ERROR_NONE {
+ err = TCP_Recv_Error(ok)
+ return
+ }
+ return int(res), nil
+}
+
+@(private)
+_recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+ if len(buf) <= 0 {
+ return
+ }
+
+ from: os.SOCKADDR_STORAGE_LH = ---
+ fromsize := c.int(size_of(from))
+
+ // NOTE(tetra): On Linux, if the buffer is too small to fit the entire datagram payload, the rest is silently discarded,
+ // and no error is returned.
+ // However, if you pass MSG_TRUNC here, 'res' will be the size of the incoming message, rather than how much was read.
+ // We can use this fact to detect this condition and return .Buffer_Too_Small.
+ res, ok := os.recvfrom(os.Socket(skt), buf, os.MSG_TRUNC, cast(^os.SOCKADDR) &from, &fromsize)
+ if ok != os.ERROR_NONE {
+ err = UDP_Recv_Error(ok)
+ return
+ }
+
+ bytes_read = int(res)
+ remote_endpoint = _sockaddr_storage_to_endpoint(&from)
+
+ if bytes_read > len(buf) {
+ // NOTE(tetra): The buffer has been filled, with a partial message.
+ bytes_read = len(buf)
+ err = .Buffer_Too_Small
+ }
+
+ return
+}
+
+@(private)
+_send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+ for bytes_written < len(buf) {
+ limit := min(int(max(i32)), len(buf) - bytes_written)
+ remaining := buf[bytes_written:][:limit]
+ res, ok := os.send(os.Socket(skt), remaining, 0)
+ if ok != os.ERROR_NONE {
+ err = TCP_Send_Error(ok)
+ return
+ }
+ bytes_written += int(res)
+ }
+ return
+}
+
+@(private)
+_send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+ toaddr := _endpoint_to_sockaddr(to)
+ res, os_err := os.sendto(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &toaddr, size_of(toaddr))
+ if os_err != os.ERROR_NONE {
+ err = UDP_Send_Error(os_err)
+ return
+ }
+ bytes_written = int(res)
+ return
+}
+
+@(private)
+_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+ s := any_socket_to_socket(skt)
+ res := os.shutdown(os.Socket(s), int(manner))
+ if res != os.ERROR_NONE {
+ return Shutdown_Error(res)
+ }
+ return
+}
+
+@(private)
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+ level := os.SOL_SOCKET if option != .TCP_Nodelay else os.IPPROTO_TCP
+
+ // NOTE(tetra, 2022-02-15): On Linux, you cannot merely give a single byte for a bool;
+ // it _has_ to be a b32.
+ // I haven't tested if you can give more than that.
+ bool_value: b32
+ int_value: i32
+ timeval_value: os.Timeval
+
+ ptr: rawptr
+ len: os.socklen_t
+
+ switch option {
+ case
+ .Reuse_Address,
+ .Keep_Alive,
+ .Out_Of_Bounds_Data_Inline,
+ .TCP_Nodelay:
+ // TODO: verify whether these are options or not on Linux
+ // .Broadcast,
+ // .Conditional_Accept,
+ // .Dont_Linger:
+ switch x in value {
+ case bool, b8:
+ x2 := x
+ bool_value = b32((^bool)(&x2)^)
+ case b16:
+ bool_value = b32(x)
+ case b32:
+ bool_value = b32(x)
+ case b64:
+ bool_value = b32(x)
+ case:
+ panic("set_option() value must be a boolean here", loc)
+ }
+ ptr = &bool_value
+ len = size_of(bool_value)
+ case
+ .Linger,
+ .Send_Timeout,
+ .Receive_Timeout:
+ t, ok := value.(time.Duration)
+ if !ok do panic("set_option() value must be a time.Duration here", loc)
+
+ micros := i64(time.duration_microseconds(t))
+ timeval_value.microseconds = int(micros % 1e6)
+ timeval_value.seconds = (micros - i64(timeval_value.microseconds)) / 1e6
+
+ ptr = &timeval_value
+ len = size_of(timeval_value)
+ case
+ .Receive_Buffer_Size,
+ .Send_Buffer_Size:
+ // TODO: check for out of range values and return .Value_Out_Of_Range?
+ switch i in value {
+ case i8, u8: i2 := i; int_value = os.socklen_t((^u8)(&i2)^)
+ case i16, u16: i2 := i; int_value = os.socklen_t((^u16)(&i2)^)
+ case i32, u32: i2 := i; int_value = os.socklen_t((^u32)(&i2)^)
+ case i64, u64: i2 := i; int_value = os.socklen_t((^u64)(&i2)^)
+ case i128, u128: i2 := i; int_value = os.socklen_t((^u128)(&i2)^)
+ case int, uint: i2 := i; int_value = os.socklen_t((^uint)(&i2)^)
+ case:
+ panic("set_option() value must be an integer here", loc)
+ }
+ ptr = &int_value
+ len = size_of(int_value)
+ }
+
+ skt := any_socket_to_socket(s)
+ res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
+ if res != os.ERROR_NONE {
+ return Socket_Option_Error(res)
+ }
+
+ return nil
+}
+
+@(private)
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+ socket := any_socket_to_socket(socket)
+
+ flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
+ if getfl_err != os.ERROR_NONE {
+ return Set_Blocking_Error(getfl_err)
+ }
+
+ if should_block {
+ flags &= ~int(os.O_NONBLOCK)
+ } else {
+ flags |= int(os.O_NONBLOCK)
+ }
+
+ _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
+ if setfl_err != os.ERROR_NONE {
+ return Set_Blocking_Error(setfl_err)
+ }
+
+ return nil
+}
+
+@(private)
+_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: os.SOCKADDR_STORAGE_LH) {
+ switch a in ep.address {
+ case IP4_Address:
+ (^os.sockaddr_in)(&sockaddr)^ = os.sockaddr_in {
+ sin_port = u16be(ep.port),
+ sin_addr = transmute(os.in_addr) a,
+ sin_family = u16(os.AF_INET),
+ }
+ return
+ case IP6_Address:
+ (^os.sockaddr_in6)(&sockaddr)^ = os.sockaddr_in6 {
+ sin6_port = u16be(ep.port),
+ sin6_addr = transmute(os.in6_addr) a,
+ sin6_family = u16(os.AF_INET6),
+ }
+ return
+ }
+ unreachable()
+}
+
+@(private)
+_sockaddr_storage_to_endpoint :: proc(native_addr: ^os.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
+ switch native_addr.ss_family {
+ case u16(os.AF_INET):
+ addr := cast(^os.sockaddr_in) native_addr
+ port := int(addr.sin_port)
+ ep = Endpoint {
+ address = IP4_Address(transmute([4]byte) addr.sin_addr),
+ port = port,
+ }
+ case u16(os.AF_INET6):
+ addr := cast(^os.sockaddr_in6) native_addr
+ port := int(addr.sin6_port)
+ ep = Endpoint {
+ address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
+ port = port,
+ }
+ case:
+ panic("native_addr is neither IP4 or IP6 address")
+ }
+ return
+}
+
+@(private)
+_sockaddr_basic_to_endpoint :: proc(native_addr: ^os.SOCKADDR) -> (ep: Endpoint) {
+ switch native_addr.sa_family {
+ case u16(os.AF_INET):
+ addr := cast(^os.sockaddr_in) native_addr
+ port := int(addr.sin_port)
+ ep = Endpoint {
+ address = IP4_Address(transmute([4]byte) addr.sin_addr),
+ port = port,
+ }
+ case u16(os.AF_INET6):
+ addr := cast(^os.sockaddr_in6) native_addr
+ port := int(addr.sin6_port)
+ ep = Endpoint {
+ address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
+ port = port,
+ }
+ case:
+ panic("native_addr is neither IP4 or IP6 address")
+ }
+ return
+}
diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin
new file mode 100644
index 000000000..3b9623749
--- /dev/null
+++ b/core/net/socket_windows.odin
@@ -0,0 +1,367 @@
+package net
+// +build windows
+
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:c"
+import win "core:sys/windows"
+import "core:time"
+
+@(init, private)
+ensure_winsock_initialized :: proc() {
+ win.ensure_winsock_initialized()
+}
+
+@(private)
+_create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (socket: Any_Socket, err: Network_Error) {
+ c_type, c_protocol, c_family: c.int
+
+ switch family {
+ case .IP4: c_family = win.AF_INET
+ case .IP6: c_family = win.AF_INET6
+ case:
+ unreachable()
+ }
+
+ switch protocol {
+ case .TCP: c_type = win.SOCK_STREAM; c_protocol = win.IPPROTO_TCP
+ case .UDP: c_type = win.SOCK_DGRAM; c_protocol = win.IPPROTO_UDP
+ case:
+ unreachable()
+ }
+
+ sock := win.socket(c_family, c_type, c_protocol)
+ if sock == win.INVALID_SOCKET {
+ err = Create_Socket_Error(win.WSAGetLastError())
+ return
+ }
+
+ switch protocol {
+ case .TCP: return TCP_Socket(sock), nil
+ case .UDP: return UDP_Socket(sock), nil
+ case:
+ unreachable()
+ }
+}
+
+@(private)
+_dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_options) -> (socket: TCP_Socket, err: Network_Error) {
+ if endpoint.port == 0 {
+ err = .Port_Required
+ return
+ }
+
+ family := family_from_endpoint(endpoint)
+ sock := create_socket(family, .TCP) or_return
+ socket = sock.(TCP_Socket)
+
+ // NOTE(tetra): This is so that if we crash while the socket is open, we can
+ // bypass the cooldown period, and allow the next run of the program to
+ // use the same address immediately.
+ _ = set_option(socket, .Reuse_Address, true)
+
+ sockaddr := _endpoint_to_sockaddr(endpoint)
+ res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
+ if res < 0 {
+ err = Dial_Error(win.WSAGetLastError())
+ return
+ }
+
+ if options.no_delay {
+ _ = set_option(sock, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
+ }
+
+ return
+}
+
+@(private)
+_bind :: proc(socket: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
+ sockaddr := _endpoint_to_sockaddr(ep)
+ sock := any_socket_to_socket(socket)
+ res := win.bind(win.SOCKET(sock), &sockaddr, size_of(sockaddr))
+ if res < 0 {
+ err = Bind_Error(win.WSAGetLastError())
+ }
+ return
+}
+
+@(private)
+_listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
+ family := family_from_endpoint(interface_endpoint)
+ sock := create_socket(family, .TCP) or_return
+ socket = sock.(TCP_Socket)
+
+ // NOTE(tetra): While I'm not 100% clear on it, my understanding is that this will
+ // prevent hijacking of the server's endpoint by other applications.
+ set_option(socket, .Exclusive_Addr_Use, true) or_return
+
+ bind(sock, interface_endpoint) or_return
+
+ if res := win.listen(win.SOCKET(socket), i32(backlog)); res == win.SOCKET_ERROR {
+ err = Listen_Error(win.WSAGetLastError())
+ }
+ return
+}
+
+@(private)
+_accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client: TCP_Socket, source: Endpoint, err: Network_Error) {
+ for {
+ sockaddr: win.SOCKADDR_STORAGE_LH
+ sockaddrlen := c.int(size_of(sockaddr))
+ client_sock := win.accept(win.SOCKET(sock), &sockaddr, &sockaddrlen)
+ if int(client_sock) == win.SOCKET_ERROR {
+ e := win.WSAGetLastError()
+ if e == win.WSAECONNRESET {
+ // NOTE(tetra): Reset just means that a client that connection immediately lost the connection.
+ // There's no need to concern the user with this, so we handle it for them.
+ // On Linux, this error isn't possible in the first place according the man pages, so we also
+ // can do this to match the behaviour.
+ continue
+ }
+ err = Accept_Error(e)
+ return
+ }
+ client = TCP_Socket(client_sock)
+ source = _sockaddr_to_endpoint(&sockaddr)
+ if options.no_delay {
+ _ = set_option(client, .TCP_Nodelay, true) // NOTE(tetra): Not vital to succeed; error ignored
+ }
+ return
+ }
+}
+
+@(private)
+_close :: proc(socket: Any_Socket) {
+ if s := any_socket_to_socket(socket); s != {} {
+ win.closesocket(win.SOCKET(s))
+ }
+}
+
+@(private)
+_recv_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Network_Error) {
+ if len(buf) <= 0 {
+ return
+ }
+ res := win.recv(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0)
+ if res < 0 {
+ err = TCP_Recv_Error(win.WSAGetLastError())
+ return
+ }
+ return int(res), nil
+}
+
+@(private)
+_recv_udp :: proc(socket: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endpoint: Endpoint, err: Network_Error) {
+ if len(buf) <= 0 {
+ return
+ }
+
+ from: win.SOCKADDR_STORAGE_LH
+ fromsize := c.int(size_of(from))
+ res := win.recvfrom(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0, &from, &fromsize)
+ if res < 0 {
+ err = UDP_Recv_Error(win.WSAGetLastError())
+ return
+ }
+
+ bytes_read = int(res)
+ remote_endpoint = _sockaddr_to_endpoint(&from)
+ return
+}
+
+@(private)
+_send_tcp :: proc(socket: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Network_Error) {
+ for bytes_written < len(buf) {
+ limit := min(int(max(i32)), len(buf) - bytes_written)
+ remaining := buf[bytes_written:]
+ res := win.send(win.SOCKET(socket), raw_data(remaining), c.int(limit), 0)
+ if res < 0 {
+ err = TCP_Send_Error(win.WSAGetLastError())
+ return
+ }
+ bytes_written += int(res)
+ }
+ return
+}
+
+@(private)
+_send_udp :: proc(socket: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written: int, err: Network_Error) {
+ if len(buf) > int(max(c.int)) {
+ // NOTE(tetra): If we don't guard this, we'll return (0, nil) instead, which is misleading.
+ err = .Message_Too_Long
+ return
+ }
+ toaddr := _endpoint_to_sockaddr(to)
+ res := win.sendto(win.SOCKET(socket), raw_data(buf), c.int(len(buf)), 0, &toaddr, size_of(toaddr))
+ if res < 0 {
+ err = UDP_Send_Error(win.WSAGetLastError())
+ return
+ }
+ bytes_written = int(res)
+ return
+}
+
+@(private)
+_shutdown :: proc(socket: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
+ s := any_socket_to_socket(socket)
+ res := win.shutdown(win.SOCKET(s), c.int(manner))
+ if res < 0 {
+ return Shutdown_Error(win.WSAGetLastError())
+ }
+ return
+}
+
+@(private)
+_set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #caller_location) -> Network_Error {
+ level := win.SOL_SOCKET if option != .TCP_Nodelay else win.IPPROTO_TCP
+
+ bool_value: b32
+ int_value: i32
+ linger_value: win.LINGER
+
+ ptr: rawptr
+ len: c.int
+
+ switch option {
+ case
+ .Reuse_Address,
+ .Exclusive_Addr_Use,
+ .Keep_Alive,
+ .Out_Of_Bounds_Data_Inline,
+ .TCP_Nodelay,
+ .Broadcast,
+ .Conditional_Accept,
+ .Dont_Linger:
+ switch x in value {
+ case bool, b8:
+ x2 := x
+ bool_value = b32((^bool)(&x2)^)
+ case b16:
+ bool_value = b32(x)
+ case b32:
+ bool_value = b32(x)
+ case b64:
+ bool_value = b32(x)
+ case:
+ panic("set_option() value must be a boolean here", loc)
+ }
+ ptr = &bool_value
+ len = size_of(bool_value)
+ case .Linger:
+ t, ok := value.(time.Duration)
+ if !ok do panic("set_option() value must be a time.Duration here", loc)
+
+ num_secs := i64(time.duration_seconds(t))
+ if time.Duration(num_secs * 1e9) != t do return .Linger_Only_Supports_Whole_Seconds
+ if num_secs > i64(max(u16)) do return .Value_Out_Of_Range
+ linger_value.l_onoff = 1
+ linger_value.l_linger = c.ushort(num_secs)
+
+ ptr = &linger_value
+ len = size_of(linger_value)
+ case
+ .Receive_Timeout,
+ .Send_Timeout:
+ t, ok := value.(time.Duration)
+ if !ok do panic("set_option() value must be a time.Duration here", loc)
+
+ int_value = i32(time.duration_milliseconds(t))
+ ptr = &int_value
+ len = size_of(int_value)
+
+ case
+ .Receive_Buffer_Size,
+ .Send_Buffer_Size:
+ switch i in value {
+ case i8, u8: i2 := i; int_value = c.int((^u8)(&i2)^)
+ case i16, u16: i2 := i; int_value = c.int((^u16)(&i2)^)
+ case i32, u32: i2 := i; int_value = c.int((^u32)(&i2)^)
+ case i64, u64: i2 := i; int_value = c.int((^u64)(&i2)^)
+ case i128, u128: i2 := i; int_value = c.int((^u128)(&i2)^)
+ case int, uint: i2 := i; int_value = c.int((^uint)(&i2)^)
+ case:
+ panic("set_option() value must be an integer here", loc)
+ }
+ ptr = &int_value
+ len = size_of(int_value)
+ }
+
+ socket := any_socket_to_socket(s)
+ res := win.setsockopt(win.SOCKET(socket), c.int(level), c.int(option), ptr, len)
+ if res < 0 {
+ return Socket_Option_Error(win.WSAGetLastError())
+ }
+
+ return nil
+}
+
+@(private)
+_set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_Error) {
+ socket := any_socket_to_socket(socket)
+ arg: win.DWORD = 0 if should_block else 1
+ res := win.ioctlsocket(win.SOCKET(socket), transmute(win.c_long)win.FIONBIO, &arg)
+ if res == win.SOCKET_ERROR {
+ return Set_Blocking_Error(win.WSAGetLastError())
+ }
+
+ return nil
+}
+
+@(private)
+_endpoint_to_sockaddr :: proc(ep: Endpoint) -> (sockaddr: win.SOCKADDR_STORAGE_LH) {
+ switch a in ep.address {
+ case IP4_Address:
+ (^win.sockaddr_in)(&sockaddr)^ = win.sockaddr_in {
+ sin_port = u16be(win.USHORT(ep.port)),
+ sin_addr = transmute(win.in_addr) a,
+ sin_family = u16(win.AF_INET),
+ }
+ return
+ case IP6_Address:
+ (^win.sockaddr_in6)(&sockaddr)^ = win.sockaddr_in6 {
+ sin6_port = u16be(win.USHORT(ep.port)),
+ sin6_addr = transmute(win.in6_addr) a,
+ sin6_family = u16(win.AF_INET6),
+ }
+ return
+ }
+ unreachable()
+}
+
+@(private)
+_sockaddr_to_endpoint :: proc(native_addr: ^win.SOCKADDR_STORAGE_LH) -> (ep: Endpoint) {
+ switch native_addr.ss_family {
+ case u16(win.AF_INET):
+ addr := cast(^win.sockaddr_in) native_addr
+ port := int(addr.sin_port)
+ ep = Endpoint {
+ address = IP4_Address(transmute([4]byte) addr.sin_addr),
+ port = port,
+ }
+ case u16(win.AF_INET6):
+ addr := cast(^win.sockaddr_in6) native_addr
+ port := int(addr.sin6_port)
+ ep = Endpoint {
+ address = IP6_Address(transmute([8]u16be) addr.sin6_addr),
+ port = port,
+ }
+ case:
+ panic("native_addr is neither IP4 or IP6 address")
+ }
+ return
+}
\ No newline at end of file
diff --git a/core/net/url.odin b/core/net/url.odin
new file mode 100644
index 000000000..ed39f7859
--- /dev/null
+++ b/core/net/url.odin
@@ -0,0 +1,209 @@
+package net
+/*
+ Package net implements cross-platform Berkeley Sockets, DNS resolution and associated procedures.
+ For other protocols and their features, see subdirectories of this package.
+*/
+
+/*
+ Copyright 2022 Tetralux
+ Copyright 2022 Colin Davidson
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Tetralux: Initial implementation
+ Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
+ Jeroen van Rijn: Cross platform unification, code style, documentation
+*/
+
+import "core:strings"
+import "core:strconv"
+import "core:unicode/utf8"
+import "core:encoding/hex"
+
+split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host, path: string, queries: map[string]string) {
+ s := url
+
+ i := strings.last_index(s, "://")
+ if i >= 0 {
+ scheme = s[:i]
+ s = s[i+3:]
+ }
+
+ i = strings.index(s, "?")
+ if i != -1 {
+ query_str := s[i+1:]
+ s = s[:i]
+ if query_str != "" {
+ queries_parts := strings.split(query_str, "&")
+ defer delete(queries_parts)
+ queries = make(map[string]string, len(queries_parts), allocator)
+ for q in queries_parts {
+ parts := strings.split(q, "=")
+ defer delete(parts)
+ switch len(parts) {
+ case 1: queries[parts[0]] = "" // NOTE(tetra): Query not set to anything, was but present.
+ case 2: queries[parts[0]] = parts[1] // NOTE(tetra): Query set to something.
+ case: break
+ }
+ }
+ }
+ }
+
+ i = strings.index(s, "/")
+ if i == -1 {
+ host = s
+ path = "/"
+ } else {
+ host = s[:i]
+ path = s[i:]
+ }
+
+ return
+}
+
+join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
+ using strings
+
+ b := builder_make(allocator)
+ builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
+
+ write_string(&b, scheme)
+ write_string(&b, "://")
+ write_string(&b, trim_space(host))
+
+ if path != "" {
+ if path[0] != '/' do write_string(&b, "/")
+ write_string(&b, trim_space(path))
+ }
+
+
+ query_length := len(queries)
+ if query_length > 0 do write_string(&b, "?")
+ i := 0
+ for query_name, query_value in queries {
+ write_string(&b, query_name)
+ if query_value != "" {
+ write_string(&b, "=")
+ write_string(&b, query_value)
+ }
+ if i < query_length - 1 {
+ write_string(&b, "&")
+ }
+ i += 1
+ }
+
+ return to_string(b)
+}
+
+percent_encode :: proc(s: string, allocator := context.allocator) -> string {
+ using strings
+
+ b := builder_make(allocator)
+ builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
+
+ for ch in s {
+ switch ch {
+ case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~':
+ write_rune(&b, ch)
+ case:
+ bytes, n := utf8.encode_rune(ch)
+ for byte in bytes[:n] {
+ buf: [2]u8 = ---
+ t := strconv.append_int(buf[:], i64(byte), 16)
+ write_rune(&b, '%')
+ write_string(&b, t)
+ }
+ }
+ }
+
+ return to_string(b)
+}
+
+percent_decode :: proc(encoded_string: string, allocator := context.allocator) -> (decoded_string: string, ok: bool) {
+ using strings
+
+ b := builder_make(allocator)
+ builder_grow(&b, len(encoded_string))
+ defer if !ok do builder_destroy(&b)
+
+ s := encoded_string
+
+ for len(s) > 0 {
+ i := index_byte(s, '%')
+ if i == -1 {
+ write_string(&b, s) // no '%'s; the string is already decoded
+ break
+ }
+
+ write_string(&b, s[:i])
+ s = s[i:]
+
+ if len(s) == 0 do return // percent without anything after it
+ s = s[1:]
+
+ if s[0] == '%' {
+ write_byte(&b, '%')
+ s = s[1:]
+ continue
+ }
+
+ if len(s) < 2 do return // percent without encoded value
+
+ val := hex.decode_sequence(s[:2]) or_return
+ write_byte(&b, val)
+ s = s[2:]
+ }
+
+ ok = true
+ decoded_string = to_string(b)
+ return
+}
+
+//
+// TODO: encoding/base64 is broken...
+//
+
+// // TODO(tetra): The whole "table" stuff in encoding/base64 is too impenetrable for me to
+// // make a table for this ... sigh - so this'll do for now.
+/*
+base64url_encode :: proc(data: []byte, allocator := context.allocator) -> string {
+ out := transmute([]byte) base64.encode(data, base64.ENC_TABLE, allocator);
+ for b, i in out {
+ switch b {
+ case '+': out[i] = '-';
+ case '/': out[i] = '_';
+ }
+ }
+ i := len(out)-1;
+ for ; i >= 0; i -= 1 {
+ if out[i] != '=' do break;
+ }
+ return string(out[:i+1]);
+}
+
+base64url_decode :: proc(s: string, allocator := context.allocator) -> []byte {
+ size := len(s);
+ padding := 0;
+ for size % 4 != 0 {
+ size += 1; // TODO: SPEED
+ padding += 1;
+ }
+
+ temp := make([]byte, size, context.temp_allocator);
+ copy(temp, transmute([]byte) s);
+
+ for b, i in temp {
+ switch b {
+ case '-': temp[i] = '+';
+ case '_': temp[i] = '/';
+ }
+ }
+
+ for in 0..padding-1 {
+ temp[len(temp)-1] = '=';
+ }
+
+ return base64.decode(string(temp), base64.DEC_TABLE, allocator);
+}
+*/
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index 99b2cde98..58d977171 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -740,6 +740,7 @@ Struct_Type :: struct {
where_clauses: []^Expr,
is_packed: bool,
is_raw_union: bool,
+ is_no_copy: bool,
fields: ^Field_List,
name_count: int,
}
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index 5ec6bc335..b8c0b8087 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -82,7 +82,11 @@ clone_node :: proc(node: ^Node) -> ^Node {
panic("Cannot clone this node type")
}
- res := cast(^Node)mem.alloc(size, align)
+ res := cast(^Node)(mem.alloc(size, align) or_else nil)
+ if res == nil {
+ // allocation failure
+ return nil
+ }
src: rawptr = node
if node.derived != nil {
src = (^rawptr)(&node.derived)^
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index b70c8528e..15a33d86b 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -1425,7 +1425,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return es
case "force_inline", "force_no_inline":
- expr := parse_inlining_operand(p, true, tok)
+ expr := parse_inlining_operand(p, true, tag)
es := ast.new(ast.Expr_Stmt, expr.pos, expr.end)
es.expr = expr
return es
@@ -2527,6 +2527,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
align: ^ast.Expr
is_packed: bool
is_raw_union: bool
+ is_no_copy: bool
fields: ^ast.Field_List
name_count: int
@@ -2560,6 +2561,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
}
is_raw_union = true
+ case "no_copy":
+ if is_no_copy {
+ error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
+ }
+ is_no_copy = true
case:
error(p, tag.pos, "invalid struct tag '#%s", tag.text)
}
@@ -2594,6 +2600,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
st.align = align
st.is_packed = is_packed
st.is_raw_union = is_raw_union
+ st.is_no_copy = is_no_copy
st.fields = fields
st.name_count = name_count
st.where_token = where_token
diff --git a/core/os/dir_freebsd.odin b/core/os/dir_freebsd.odin
index c664ffb34..2965182cd 100644
--- a/core/os/dir_freebsd.odin
+++ b/core/os/dir_freebsd.odin
@@ -50,7 +50,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
continue
}
- fullpath := make([]byte, len(dirpath)+1+len(filename))
+ fullpath := make([]byte, len(dirpath)+1+len(filename), context.temp_allocator)
copy(fullpath, dirpath)
copy(fullpath[len(dirpath):], "/")
copy(fullpath[len(dirpath)+1:], filename)
diff --git a/core/os/dir_linux.odin b/core/os/dir_linux.odin
index 74c410a51..4971fa9d5 100644
--- a/core/os/dir_linux.odin
+++ b/core/os/dir_linux.odin
@@ -2,6 +2,7 @@ package os
import "core:strings"
import "core:mem"
+import "core:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
dirp: Dir
@@ -51,6 +52,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
continue
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
defer delete(fullpath, context.temp_allocator)
diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin
index 9333548cf..531a5cd82 100644
--- a/core/os/dir_windows.odin
+++ b/core/os/dir_windows.odin
@@ -2,6 +2,7 @@ package os
import win32 "core:sys/windows"
import "core:strings"
+import "core:runtime"
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) {
@@ -65,13 +66,16 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
n = -1
size = 100
}
- dfi := make([dynamic]File_Info, 0, size)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath: []u16
- wpath, err = cleanpath_from_handle_u16(fd)
+ wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
if len(wpath) == 0 || err != ERROR_NONE {
return
}
+
+ dfi := make([dynamic]File_Info, 0, size)
+
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator)
copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\'
diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin
index 6e14127ed..ff20f126a 100644
--- a/core/os/env_windows.odin
+++ b/core/os/env_windows.odin
@@ -1,6 +1,7 @@
package os
import win32 "core:sys/windows"
+import "core:runtime"
// lookup_env gets the value of the environment variable named by the key
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
@@ -18,6 +19,8 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return "", false
}
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+
b := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 {
@@ -87,6 +90,7 @@ environ :: proc(allocator := context.allocator) -> []string {
// clear_env deletes all environment variables
clear_env :: proc() {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
envs := environ(context.temp_allocator)
for env in envs {
for j in 1.. bool {
@@ -219,6 +220,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
@(private)
MAX_RW :: 1<<30
+ERROR_EOF :: 38
@(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
@@ -227,11 +229,6 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
buf = buf[:MAX_RW]
}
- curr_offset, e := seek(fd, offset, 1)
- if e != 0 {
- return 0, e
- }
- defer seek(fd, curr_offset, 0)
o := win32.OVERLAPPED{
OffsetHigh = u32(offset>>32),
@@ -242,6 +239,7 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd)
done: win32.DWORD
+ e: Errno
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError())
done = 0
@@ -255,11 +253,6 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
buf = buf[:MAX_RW]
}
- curr_offset, e := seek(fd, offset, 1)
- if e != 0 {
- return 0, e
- }
- defer seek(fd, curr_offset, 0)
o := win32.OVERLAPPED{
OffsetHigh = u32(offset>>32),
@@ -268,6 +261,7 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd)
done: win32.DWORD
+ e: Errno
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError())
done = 0
@@ -275,6 +269,16 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
return int(done), e
}
+/*
+read_at returns n: 0, err: 0 on EOF
+on Windows, read_at changes the position of the file cursor, on *nix, it does not.
+
+ bytes: [8]u8{}
+ read_at(fd, bytes, 0)
+ read(fd, bytes)
+
+will read from the location twice on *nix, and from two different locations on Windows
+*/
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET
@@ -283,6 +287,10 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
b, offset := data, offset
for len(b) > 0 {
m, e := pread(fd, b, offset)
+ if e == ERROR_EOF {
+ err = 0
+ break
+ }
if e != 0 {
err = e
break
@@ -293,6 +301,16 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
}
return
}
+
+/*
+on Windows, write_at changes the position of the file cursor, on *nix, it does not.
+
+ bytes: [8]u8{}
+ write_at(fd, bytes, 0)
+ write(fd, bytes)
+
+will write to the location twice on *nix, and to two different locations on Windows
+*/
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET
@@ -327,6 +345,7 @@ get_std_handle :: proc "contextless" (h: uint) -> Handle {
exists :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
@@ -334,6 +353,7 @@ exists :: proc(path: string) -> bool {
}
is_file :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
@@ -344,6 +364,7 @@ is_file :: proc(path: string) -> bool {
}
is_dir :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
attribs := win32.GetFileAttributesW(wpath)
@@ -359,6 +380,8 @@ is_dir :: proc(path: string) -> bool {
get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+
sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
@@ -387,6 +410,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
change_directory :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.SetCurrentDirectoryW(wpath) {
@@ -396,6 +420,7 @@ change_directory :: proc(path: string) -> (err: Errno) {
}
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -407,6 +432,7 @@ make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
remove_directory :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.RemoveDirectoryW(wpath) {
@@ -479,12 +505,14 @@ fix_long_path :: proc(path: string) -> string {
link :: proc(old_name, new_name: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil))
}
unlink :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.DeleteFileW(wpath) {
@@ -496,6 +524,7 @@ unlink :: proc(path: string) -> (err: Errno) {
rename :: proc(old_path, new_path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
diff --git a/core/os/os.odin b/core/os/os.odin
index dd20de17c..b71ea261e 100644
--- a/core/os/os.odin
+++ b/core/os/os.odin
@@ -93,7 +93,7 @@ file_size_from_path :: proc(path: string) -> i64 {
return length
}
-read_entire_file_from_filename :: proc(name: string, allocator := context.allocator) -> (data: []byte, success: bool) {
+read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator
fd, err := open(name, O_RDONLY, 0)
@@ -102,10 +102,10 @@ read_entire_file_from_filename :: proc(name: string, allocator := context.alloca
}
defer close(fd)
- return read_entire_file_from_handle(fd, allocator)
+ return read_entire_file_from_handle(fd, allocator, loc)
}
-read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, success: bool) {
+read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator
length: i64
@@ -118,7 +118,7 @@ read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator)
return nil, true
}
- data = make([]byte, int(length), allocator)
+ data = make([]byte, int(length), allocator, loc)
if data == nil {
return nil, false
}
@@ -194,7 +194,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
ptr := uintptr(aligned_mem)
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
diff := int(aligned_ptr - ptr)
- if (size + diff) > space {
+ if (size + diff) > space || allocated_mem == nil {
return nil, .Out_Of_Memory
}
@@ -216,7 +216,7 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
new_memory = aligned_alloc(new_size, new_alignment, p) or_return
-
+
// NOTE: heap_resize does not zero the new memory, so we do it
if new_size > old_size {
new_region := mem.raw_data(new_memory[old_size:])
@@ -261,3 +261,7 @@ heap_allocator :: proc() -> mem.Allocator {
data = nil,
}
}
+
+processor_core_count :: proc() -> int {
+ return _processor_core_count()
+}
diff --git a/core/os/os2/env.odin b/core/os/os2/env.odin
index f25290a59..54c26981b 100644
--- a/core/os/os2/env.odin
+++ b/core/os/os2/env.odin
@@ -1,10 +1,12 @@
package os2
+import "core:runtime"
+
// get_env retrieves the value of the environment variable named by the key
// It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator
-get_env :: proc(key: string, allocator := context.allocator) -> string {
+get_env :: proc(key: string, allocator: runtime.Allocator) -> string {
value, _ := lookup_env(key, allocator)
return value
}
@@ -13,7 +15,7 @@ get_env :: proc(key: string, allocator := context.allocator) -> string {
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator
-lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
return _lookup_env(key, allocator)
}
@@ -36,7 +38,7 @@ clear_env :: proc() {
// environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
-environ :: proc(allocator := context.allocator) -> []string {
+environ :: proc(allocator: runtime.Allocator) -> []string {
return _environ(allocator)
}
diff --git a/core/os/os2/env_linux.odin b/core/os/os2/env_linux.odin
index 1833ac4dc..e7165b583 100644
--- a/core/os/os2/env_linux.odin
+++ b/core/os/os2/env_linux.odin
@@ -1,7 +1,9 @@
//+private
package os2
-_get_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+import "core:runtime"
+
+_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
//TODO
return
}
@@ -20,7 +22,7 @@ _clear_env :: proc() {
//TODO
}
-_environ :: proc(allocator := context.allocator) -> []string {
+_environ :: proc(allocator: runtime.Allocator) -> []string {
//TODO
return nil
}
diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin
index f58922fac..105063343 100644
--- a/core/os/os2/env_windows.odin
+++ b/core/os/os2/env_windows.odin
@@ -65,7 +65,19 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
}
defer win32.FreeEnvironmentStringsW(envs)
- r := make([dynamic]string, 0, 50, allocator)
+ n := 0
+ for from, i, p := 0, 0, envs; true; i += 1 {
+ c := ([^]u16)(p)[i]
+ if c == 0 {
+ if i <= from {
+ break
+ }
+ n += 1
+ from = i + 1
+ }
+ }
+
+ r := make([dynamic]string, 0, n, allocator)
for from, i, p := 0, 0, envs; true; i += 1 {
c := ([^]u16)(p)[i]
if c == 0 {
diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin
index 27c16e72e..6500e7ccc 100644
--- a/core/os/os2/errors_windows.odin
+++ b/core/os/os2/errors_windows.odin
@@ -37,14 +37,18 @@ _get_platform_error :: proc() -> Error {
case win32.ERROR_NOT_SUPPORTED:
return .Unsupported
+ case win32.ERROR_HANDLE_EOF:
+ return .EOF
+
+ case win32.ERROR_INVALID_HANDLE:
+ return .Invalid_File
+
case
win32.ERROR_BAD_ARGUMENTS,
win32.ERROR_INVALID_PARAMETER,
win32.ERROR_NOT_ENOUGH_MEMORY,
- win32.ERROR_INVALID_HANDLE,
win32.ERROR_NO_MORE_FILES,
win32.ERROR_LOCK_VIOLATION,
- win32.ERROR_HANDLE_EOF,
win32.ERROR_BROKEN_PIPE,
win32.ERROR_CALL_NOT_IMPLEMENTED,
win32.ERROR_INSUFFICIENT_BUFFER,
diff --git a/core/os/os2/file.odin b/core/os/os2/file.odin
index eb6d9e366..da822374a 100644
--- a/core/os/os2/file.odin
+++ b/core/os/os2/file.odin
@@ -8,12 +8,6 @@ File :: struct {
impl: _File,
}
-Seek_From :: enum {
- Start = 0, // seek relative to the origin of the file
- Current = 1, // seek relative to the current offset
- End = 2, // seek relative to the end
-}
-
File_Mode :: distinct u32
File_Mode_Dir :: File_Mode(1<<16)
File_Mode_Named_Pipe :: File_Mode(1<<17)
@@ -72,54 +66,68 @@ fd :: proc(f: ^File) -> uintptr {
return _fd(f)
}
-
-close :: proc(f: ^File) -> Error {
- return _close(f)
-}
-
name :: proc(f: ^File) -> string {
return _name(f)
}
-seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
- return _seek(f, offset, whence)
+close :: proc(f: ^File) -> Error {
+ if f != nil {
+ return io.close(f.impl.stream)
+ }
+ return nil
+}
+
+seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
+ if f != nil {
+ return io.seek(f.impl.stream, offset, whence)
+ }
+ return 0, .Invalid_File
}
read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
- return _read(f, p)
+ if f != nil {
+ return io.read(f.impl.stream, p)
+ }
+ return 0, .Invalid_File
}
read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
- return _read_at(f, p, offset)
-}
-
-read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
- return _read_from(f, r)
+ if f != nil {
+ return io.read_at(f.impl.stream, p, offset)
+ }
+ return 0, .Invalid_File
}
write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
- return _write(f, p)
+ if f != nil {
+ return io.write(f.impl.stream, p)
+ }
+ return 0, .Invalid_File
}
write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
- return _write_at(f, p, offset)
-}
-
-write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
- return _write_to(f, w)
+ if f != nil {
+ return io.write_at(f.impl.stream, p, offset)
+ }
+ return 0, .Invalid_File
}
file_size :: proc(f: ^File) -> (n: i64, err: Error) {
- return _file_size(f)
-}
-
-
-sync :: proc(f: ^File) -> Error {
- return _sync(f)
+ if f != nil {
+ return io.size(f.impl.stream)
+ }
+ return 0, .Invalid_File
}
flush :: proc(f: ^File) -> Error {
- return _flush(f)
+ if f != nil {
+ return io.flush(f.impl.stream)
+ }
+ return nil
+}
+
+sync :: proc(f: ^File) -> Error {
+ return _sync(f)
}
truncate :: proc(f: ^File, size: i64) -> Error {
diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin
index 0f2e810f4..ddd827bce 100644
--- a/core/os/os2/file_linux.odin
+++ b/core/os/os2/file_linux.odin
@@ -9,19 +9,20 @@ import "core:sys/unix"
INVALID_HANDLE :: -1
-_O_RDONLY :: 0o0
-_O_WRONLY :: 0o1
-_O_RDWR :: 0o2
-_O_CREAT :: 0o100
-_O_EXCL :: 0o200
-_O_TRUNC :: 0o1000
-_O_APPEND :: 0o2000
-_O_NONBLOCK :: 0o4000
-_O_LARGEFILE :: 0o100000
-_O_DIRECTORY :: 0o200000
-_O_NOFOLLOW :: 0o400000
-_O_SYNC :: 0o4010000
-_O_CLOEXEC :: 0o2000000
+_O_RDONLY :: 0o00000000
+_O_WRONLY :: 0o00000001
+_O_RDWR :: 0o00000002
+_O_CREAT :: 0o00000100
+_O_EXCL :: 0o00000200
+_O_NOCTTY :: 0o00000400
+_O_TRUNC :: 0o00001000
+_O_APPEND :: 0o00002000
+_O_NONBLOCK :: 0o00004000
+_O_LARGEFILE :: 0o00100000
+_O_DIRECTORY :: 0o00200000
+_O_NOFOLLOW :: 0o00400000
+_O_SYNC :: 0o04010000
+_O_CLOEXEC :: 0o02000000
_O_PATH :: 0o10000000
_AT_FDCWD :: -100
@@ -32,6 +33,8 @@ _File :: struct {
name: string,
fd: int,
allocator: runtime.Allocator,
+
+ stream: io.Stream,
}
_file_allocator :: proc() -> runtime.Allocator {
@@ -39,12 +42,13 @@ _file_allocator :: proc() -> runtime.Allocator {
}
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
- flags_i: int
+ // Just default to using O_NOCTTY because needing to open a controlling
+ // terminal would be incredibly rare. This has no effect on files while
+ // allowing us to open serial devices.
+ flags_i: int = _O_NOCTTY
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
case O_RDONLY: flags_i = _O_RDONLY
case O_WRONLY: flags_i = _O_WRONLY
@@ -58,7 +62,7 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error
flags_i |= (_O_TRUNC * int(.Trunc in flags))
flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
- fd := unix.sys_open(name_cstr, flags_i, int(perm))
+ fd := unix.sys_open(name_cstr, flags_i, uint(perm))
if fd < 0 {
return nil, _get_platform_error(fd)
}
@@ -71,6 +75,10 @@ _new_file :: proc(fd: uintptr, _: string) -> ^File {
file.impl.fd = int(fd)
file.impl.allocator = _file_allocator()
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
+ file.impl.stream = {
+ data = file,
+ procedure = _file_stream_proc,
+ }
return file
}
@@ -100,7 +108,7 @@ _name :: proc(f: ^File) -> string {
return f.impl.name if f != nil else ""
}
-_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
if res < 0 {
return -1, _get_platform_error(int(res))
@@ -108,18 +116,18 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
return res, nil
}
-_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
if len(p) == 0 {
return 0, nil
}
- n = unix.sys_read(f.impl.fd, &p[0], len(p))
+ n := unix.sys_read(f.impl.fd, &p[0], len(p))
if n < 0 {
return -1, _get_platform_error(n)
}
- return n, nil
+ return i64(n), nil
}
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
@@ -130,30 +138,25 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
if m < 0 {
return -1, _get_platform_error(m)
}
- n += m
+ n += i64(m)
b = b[m:]
offset += i64(m)
}
return
}
-_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
- //TODO
- return
-}
-
-_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
if len(p) == 0 {
return 0, nil
}
- n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
+ n := unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
if n < 0 {
return -1, _get_platform_error(n)
}
- return int(n), nil
+ return i64(n), nil
}
-_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
+_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
if offset < 0 {
return 0, .Invalid_Offset
}
@@ -164,18 +167,13 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
if m < 0 {
return -1, _get_platform_error(m)
}
- n += m
+ n += i64(m)
b = b[m:]
offset += i64(m)
}
return
}
-_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
- //TODO
- return
-}
-
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
s: _Stat = ---
res := unix.sys_fstat(f.impl.fd, &s)
@@ -198,10 +196,7 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
}
_remove :: proc(name: string) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
if fd < 0 {
@@ -216,45 +211,27 @@ _remove :: proc(name: string) -> Error {
}
_rename :: proc(old_name, new_name: string) -> Error {
- old_name_cstr, old_allocated := _name_to_cstring(old_name)
- new_name_cstr, new_allocated := _name_to_cstring(new_name)
- defer if old_allocated {
- delete(old_name_cstr)
- }
- defer if new_allocated {
- delete(new_name_cstr)
- }
+ old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
+ new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
return _ok_or_error(unix.sys_rename(old_name_cstr, new_name_cstr))
}
_link :: proc(old_name, new_name: string) -> Error {
- old_name_cstr, old_allocated := _name_to_cstring(old_name)
- new_name_cstr, new_allocated := _name_to_cstring(new_name)
- defer if old_allocated {
- delete(old_name_cstr)
- }
- defer if new_allocated {
- delete(new_name_cstr)
- }
+ old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
+ new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
return _ok_or_error(unix.sys_link(old_name_cstr, new_name_cstr))
}
_symlink :: proc(old_name, new_name: string) -> Error {
- old_name_cstr, old_allocated := _name_to_cstring(old_name)
- new_name_cstr, new_allocated := _name_to_cstring(new_name)
- defer if old_allocated {
- delete(old_name_cstr)
- }
- defer if new_allocated {
- delete(new_name_cstr)
- }
+ old_name_cstr := strings.clone_to_cstring(old_name, context.temp_allocator)
+ new_name_cstr := strings.clone_to_cstring(new_name, context.temp_allocator)
return _ok_or_error(unix.sys_symlink(old_name_cstr, new_name_cstr))
}
-_read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (string, Error) {
+_read_link_cstr :: proc(name_cstr: cstring, allocator: runtime.Allocator) -> (string, Error) {
bufsz : uint = 256
buf := make([]byte, bufsz, allocator)
for {
@@ -272,27 +249,18 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (
}
}
-_read_link :: proc(name: string, allocator := context.allocator) -> (string, Error) {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+_read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
return _read_link_cstr(name_cstr, allocator)
}
_unlink :: proc(name: string) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
return _ok_or_error(unix.sys_unlink(name_cstr))
}
_chdir :: proc(name: string) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
return _ok_or_error(unix.sys_chdir(name_cstr))
}
@@ -301,32 +269,23 @@ _fchdir :: proc(f: ^File) -> Error {
}
_chmod :: proc(name: string, mode: File_Mode) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
- return _ok_or_error(unix.sys_chmod(name_cstr, int(mode)))
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
+ return _ok_or_error(unix.sys_chmod(name_cstr, uint(mode)))
}
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
- return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode)))
+ return _ok_or_error(unix.sys_fchmod(f.impl.fd, uint(mode)))
}
// NOTE: will throw error without super user priviledges
_chown :: proc(name: string, uid, gid: int) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
}
// NOTE: will throw error without super user priviledges
_lchown :: proc(name: string, uid, gid: int) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
}
@@ -336,10 +295,7 @@ _fchown :: proc(f: ^File, uid, gid: int) -> Error {
}
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
times := [2]Unix_File_Time {
{ atime._nsec, 0 },
{ mtime._nsec, 0 },
@@ -356,18 +312,12 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
}
_exists :: proc(name: string) -> bool {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
return unix.sys_access(name_cstr, F_OK) == 0
}
_is_file :: proc(name: string) -> bool {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
@@ -386,10 +336,7 @@ _is_file_fd :: proc(fd: int) -> bool {
}
_is_dir :: proc(name: string) -> bool {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
s: _Stat
res := unix.sys_stat(name_cstr, &s)
if res < 0 {
@@ -411,12 +358,53 @@ _is_dir_fd :: proc(fd: int) -> bool {
// defined as 512, however, it is well known that paths can exceed that limit.
// So, in theory you could have a path larger than the entire temp_allocator's
// buffer. Therefor, any large paths will use context.allocator.
-_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
- if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
- cname = strings.clone_to_cstring(name)
- allocated = true
- return
- }
- cname = strings.clone_to_cstring(name, context.temp_allocator)
- return
+@(private="file")
+_temp_name_to_cstring :: proc(name: string) -> (cname: cstring) {
+ return strings.clone_to_cstring(name, context.temp_allocator)
}
+
+
+@(private="package")
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ f := (^File)(stream_data)
+ ferr: Error
+ i: int
+ switch mode {
+ case .Read:
+ n, ferr = _read(f, p)
+ err = error_to_io_error(ferr)
+ return
+ case .Read_At:
+ n, ferr = _read_at(f, p, offset)
+ err = error_to_io_error(ferr)
+ return
+ case .Write:
+ n, ferr = _write(f, p)
+ err = error_to_io_error(ferr)
+ return
+ case .Write_At:
+ n, ferr = _write_at(f, p, offset)
+ err = error_to_io_error(ferr)
+ return
+ case .Seek:
+ n, ferr = _seek(f, offset, whence)
+ err = error_to_io_error(ferr)
+ return
+ case .Size:
+ n, ferr = _file_size(f)
+ err = error_to_io_error(ferr)
+ return
+ case .Flush:
+ ferr = _flush(f)
+ err = error_to_io_error(ferr)
+ return
+ case .Close, .Destroy:
+ ferr = _close(f)
+ err = error_to_io_error(ferr)
+ return
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Destroy, .Query})
+ }
+ return 0, .Empty
+}
+
diff --git a/core/os/os2/file_stream.odin b/core/os/os2/file_stream.odin
index 7edbd68fa..da1e3344f 100644
--- a/core/os/os2/file_stream.odin
+++ b/core/os/os2/file_stream.odin
@@ -3,17 +3,15 @@ package os2
import "core:io"
to_stream :: proc(f: ^File) -> (s: io.Stream) {
- s.stream_data = f
- s.stream_vtable = &_file_stream_vtable
+ if f != nil {
+ assert(f.impl.stream.procedure != nil)
+ s = f.impl.stream
+ }
return
}
-to_writer :: proc(f: ^File) -> (s: io.Writer) {
- return {to_stream(f)}
-}
-to_reader :: proc(f: ^File) -> (s: io.Reader) {
- return {to_stream(f)}
-}
+to_writer :: to_stream
+to_reader :: to_stream
@(private)
@@ -23,71 +21,3 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
}
return ferr.(io.Error) or_else .Unknown
}
-
-
-@(private)
-_file_stream_vtable := io.Stream_VTable{
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- f := (^File)(s.stream_data)
- ferr: Error
- n, ferr = read(f, p)
- err = error_to_io_error(ferr)
- return
- },
- impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- f := (^File)(s.stream_data)
- ferr: Error
- n, ferr = read_at(f, p, offset)
- err = error_to_io_error(ferr)
- return
- },
- impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- f := (^File)(s.stream_data)
- ferr: Error
- n, ferr = write_to(f, w)
- err = error_to_io_error(ferr)
- return
- },
- impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- f := (^File)(s.stream_data)
- ferr: Error
- n, ferr = write(f, p)
- err = error_to_io_error(ferr)
- return
- },
- impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- f := (^File)(s.stream_data)
- ferr: Error
- n, ferr = write_at(f, p, offset)
- err = error_to_io_error(ferr)
- return
- },
- impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
- f := (^File)(s.stream_data)
- ferr: Error
- n, ferr = read_from(f, r)
- err = error_to_io_error(ferr)
- return
- },
- impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
- f := (^File)(s.stream_data)
- n, ferr := seek(f, offset, Seek_From(whence))
- err := error_to_io_error(ferr)
- return n, err
- },
- impl_size = proc(s: io.Stream) -> i64 {
- f := (^File)(s.stream_data)
- sz, _ := file_size(f)
- return sz
- },
- impl_flush = proc(s: io.Stream) -> io.Error {
- f := (^File)(s.stream_data)
- ferr := flush(f)
- return error_to_io_error(ferr)
- },
- impl_close = proc(s: io.Stream) -> io.Error {
- f := (^File)(s.stream_data)
- ferr := close(f)
- return error_to_io_error(ferr)
- },
-}
diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin
index 9f9064244..60c3efe44 100644
--- a/core/os/os2/file_util.odin
+++ b/core/os/os2/file_util.odin
@@ -1,6 +1,7 @@
package os2
import "core:mem"
+import "core:runtime"
import "core:strconv"
import "core:unicode/utf8"
@@ -74,7 +75,7 @@ read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
-read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
+read_entire_file :: proc(name: string, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
f, ferr := open(name)
if ferr != nil {
return nil, ferr
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index e4ae4856a..600ecde21 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -38,6 +38,8 @@ _File :: struct {
wname: win32.wstring,
kind: _File_Kind,
+ stream: io.Stream,
+
allocator: runtime.Allocator,
rw_mutex: sync.RW_Mutex, // read write calls
@@ -144,6 +146,11 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
}
f.impl.kind = kind
+ f.impl.stream = {
+ data = f,
+ procedure = _file_stream_proc,
+ }
+
return f
}
@@ -181,7 +188,7 @@ _name :: proc(f: ^File) -> string {
return f.impl.name if f != nil else ""
}
-_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
+_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
handle := _handle(f)
if handle == win32.INVALID_HANDLE {
return 0, .Invalid_File
@@ -208,7 +215,7 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
return i64(hi)<<32 + i64(dw_ptr), nil
}
-_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 {
return 0, nil
@@ -274,7 +281,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
n, err := read_console(handle, p[total_read:][:to_read])
total_read += n
if err != nil {
- return int(total_read), err
+ return i64(total_read), err
}
} else {
ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
@@ -287,11 +294,11 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
}
}
- return int(total_read), nil
+ return i64(total_read), err
}
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
- pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
+_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
+ pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) {
buf := data
if len(buf) > MAX_RW {
buf = buf[:MAX_RW]
@@ -313,7 +320,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
err = _get_platform_error()
done = 0
}
- n = int(done)
+ n = i64(done)
return
}
@@ -329,12 +336,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
return
}
-_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
- // TODO(bill)
- return
-}
-
-_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
+_write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
if len(p) == 0 {
return
}
@@ -352,17 +354,17 @@ _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
if single_write_length <= 0 || !e {
- n = int(total_write)
+ n = i64(total_write)
err = _get_platform_error()
return
}
total_write += i64(single_write_length)
}
- return int(total_write), nil
+ return i64(total_write), nil
}
-_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
- pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
+_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
+ pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) {
buf := data
if len(buf) > MAX_RW {
buf = buf[:MAX_RW]
@@ -382,7 +384,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
err = _get_platform_error()
done = 0
}
- n = int(done)
+ n = i64(done)
return
}
@@ -397,11 +399,6 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
return
}
-_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
- // TODO(bill)
- return
-}
-
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
length: win32.LARGE_INTEGER
handle := _handle(f)
@@ -727,3 +724,51 @@ _is_dir :: proc(path: string) -> bool {
}
return false
}
+
+
+@(private="package")
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ f := (^File)(stream_data)
+ ferr: Error
+ i: int
+ switch mode {
+ case .Read:
+ n, ferr = _read(f, p)
+ err = error_to_io_error(ferr)
+ return
+ case .Read_At:
+ n, ferr = _read_at(f, p, offset)
+ err = error_to_io_error(ferr)
+ return
+ case .Write:
+ n, ferr = _write(f, p)
+ err = error_to_io_error(ferr)
+ return
+ case .Write_At:
+ n, ferr = _write_at(f, p, offset)
+ err = error_to_io_error(ferr)
+ return
+ case .Seek:
+ n, ferr = _seek(f, offset, whence)
+ err = error_to_io_error(ferr)
+ return
+ case .Size:
+ n, ferr = _file_size(f)
+ err = error_to_io_error(ferr)
+ return
+ case .Flush:
+ ferr = _flush(f)
+ err = error_to_io_error(ferr)
+ return
+ case .Close:
+ ferr = _close(f)
+ err = error_to_io_error(ferr)
+ return
+ case .Query:
+ return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
+ case .Destroy:
+ return 0, .Empty
+ }
+ return 0, .Empty
+}
+
diff --git a/core/os/os2/heap_linux.odin b/core/os/os2/heap_linux.odin
index b631268a1..136c3e6cb 100644
--- a/core/os/os2/heap_linux.odin
+++ b/core/os/os2/heap_linux.odin
@@ -166,7 +166,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
ptr := uintptr(aligned_mem)
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
diff := int(aligned_ptr - ptr)
- if (size + diff) > space {
+ if (size + diff) > space || allocated_mem == nil {
return nil, .Out_Of_Memory
}
diff --git a/core/os/os2/heap_windows.odin b/core/os/os2/heap_windows.odin
index 0f154cd8c..eba403c1d 100644
--- a/core/os/os2/heap_windows.odin
+++ b/core/os/os2/heap_windows.odin
@@ -52,7 +52,7 @@ _heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
ptr := uintptr(aligned_mem)
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
diff := int(aligned_ptr - ptr)
- if (size + diff) > space {
+ if (size + diff) > space || allocated_mem == nil {
return nil, .Out_Of_Memory
}
diff --git a/core/os/os2/path_linux.odin b/core/os/os2/path_linux.odin
index 2f59d1f13..2a0ef29d8 100644
--- a/core/os/os2/path_linux.odin
+++ b/core/os/os2/path_linux.odin
@@ -31,11 +31,8 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error {
return .Invalid_Argument
}
- path_cstr, allocated := _name_to_cstring(path)
- defer if allocated {
- delete(path_cstr)
- }
- return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777)))
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
}
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
@@ -49,7 +46,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
switch new_dfd {
case -ENOENT:
- if res := unix.sys_mkdirat(dfd, cstring(&path[0]), perm); res < 0 {
+ if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
return _get_platform_error(res)
}
has_created^ = true
@@ -83,9 +80,6 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
} else {
path_bytes = make([]u8, len(path) + 1, context.temp_allocator)
}
- defer if allocated {
- delete(path_bytes)
- }
// NULL terminate the byte slice to make it a valid cstring
copy(path_bytes, path)
@@ -182,10 +176,7 @@ _remove_all :: proc(path: string) -> Error {
return nil
}
- path_cstr, allocated := _name_to_cstring(path)
- defer if allocated {
- delete(path_cstr)
- }
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
switch fd {
@@ -211,7 +202,7 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
#no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
- return strings.string_from_nul_terminated_ptr(&buf[0], len(buf)), nil
+ return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil
}
if res != -ERANGE {
return "", _get_platform_error(res)
@@ -222,14 +213,11 @@ _getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
}
_setwd :: proc(dir: string) -> Error {
- dir_cstr, allocated := _name_to_cstring(dir)
- defer if allocated {
- delete(dir_cstr)
- }
+ dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
return _ok_or_error(unix.sys_chdir(dir_cstr))
}
-_get_full_path :: proc(fd: int, allocator := context.allocator) -> string {
+_get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
PROC_FD_PATH :: "/proc/self/fd/"
buf: [32]u8
diff --git a/core/os/os2/path_windows.odin b/core/os/os2/path_windows.odin
index 2dc667822..a2306784e 100644
--- a/core/os/os2/path_windows.odin
+++ b/core/os/os2/path_windows.odin
@@ -23,7 +23,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) {
if len(p) == len(`\\?\c:`) {
if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' {
- s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return
+ s = strings.concatenate({p, `\`}, _file_allocator()) or_return
allocated = true
return
}
diff --git a/core/os/os2/stat_linux.odin b/core/os/os2/stat_linux.odin
index b627cef15..530e0e7d0 100644
--- a/core/os/os2/stat_linux.odin
+++ b/core/os/os2/stat_linux.odin
@@ -3,6 +3,7 @@ package os2
import "core:time"
import "core:runtime"
+import "core:strings"
import "core:sys/unix"
import "core:path/filepath"
@@ -83,7 +84,7 @@ _Stat :: struct {
}
-_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
+_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
return _fstat_internal(f.impl.fd, allocator)
}
@@ -111,11 +112,8 @@ _fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Er
}
// NOTE: _stat and _lstat are using _fstat to avoid a race condition when populating fullpath
-_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
fd := unix.sys_open(name_cstr, _O_RDONLY)
if fd < 0 {
@@ -125,11 +123,9 @@ _stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error
return _fstat_internal(fd, allocator)
}
-_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) {
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
+
fd := unix.sys_open(name_cstr, _O_RDONLY | _O_PATH | _O_NOFOLLOW)
if fd < 0 {
return {}, _get_platform_error(fd)
@@ -143,10 +139,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
}
_stat_internal :: proc(name: string) -> (s: _Stat, res: int) {
- name_cstr, allocated := _name_to_cstring(name)
- defer if allocated {
- delete(name_cstr)
- }
+ name_cstr := strings.clone_to_cstring(name, context.temp_allocator)
res = unix.sys_stat(name_cstr, &s)
return
}
diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin
index 00cccd2a7..0e9f126aa 100644
--- a/core/os/os2/user.odin
+++ b/core/os/os2/user.odin
@@ -8,12 +8,12 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
case .Windows:
dir = get_env("LocalAppData", allocator)
if dir != "" {
- dir = strings.clone_safe(dir, allocator) or_return
+ dir = strings.clone(dir, allocator) or_return
}
case .Darwin:
dir = get_env("HOME", allocator)
if dir != "" {
- dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
+ dir = strings.concatenate({dir, "/Library/Caches"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)
@@ -22,7 +22,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error
if dir == "" {
return
}
- dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return
+ dir = strings.concatenate({dir, "/.cache"}, allocator) or_return
}
}
if dir == "" {
@@ -36,12 +36,12 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
case .Windows:
dir = get_env("AppData", allocator)
if dir != "" {
- dir = strings.clone_safe(dir, allocator) or_return
+ dir = strings.clone(dir, allocator) or_return
}
case .Darwin:
dir = get_env("HOME", allocator)
if dir != "" {
- dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
+ dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)
@@ -50,7 +50,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
if dir == "" {
return
}
- dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return
+ dir = strings.concatenate({dir, "/.config"}, allocator) or_return
}
}
if dir == "" {
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index b40edb410..ab05756ae 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -67,6 +67,7 @@ ENOPROTOOPT: Errno : 42 /* Protocol not available */
EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */
ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */
ENOTSUP: Errno : 45 /* Operation not supported */
+EOPNOTSUPP:: ENOTSUP
EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */
EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */
EADDRINUSE: Errno : 48 /* Address already in use */
@@ -179,6 +180,95 @@ RTLD_NODELETE :: 0x80
RTLD_NOLOAD :: 0x10
RTLD_FIRST :: 0x100
+SOL_SOCKET :: 0xFFFF
+
+SOCK_STREAM :: 1
+SOCK_DGRAM :: 2
+SOCK_RAW :: 3
+SOCK_RDM :: 4
+SOCK_SEQPACKET :: 5
+
+SO_DEBUG :: 0x0001
+SO_ACCEPTCONN :: 0x0002
+SO_REUSEADDR :: 0x0004
+SO_KEEPALIVE :: 0x0008
+SO_DONTROUTE :: 0x0010
+SO_BROADCAST :: 0x0020
+SO_USELOOPBACK :: 0x0040
+SO_LINGER :: 0x0080
+SO_OOBINLINE :: 0x0100
+SO_REUSEPORT :: 0x0200
+SO_TIMESTAMP :: 0x0400
+
+SO_DONTTRUNC :: 0x2000
+SO_WANTMORE :: 0x4000
+SO_WANTOOBFLAG :: 0x8000
+SO_SNDBUF :: 0x1001
+SO_RCVBUF :: 0x1002
+SO_SNDLOWAT :: 0x1003
+SO_RCVLOWAT :: 0x1004
+SO_SNDTIMEO :: 0x1005
+SO_RCVTIMEO :: 0x1006
+SO_ERROR :: 0x1007
+SO_TYPE :: 0x1008
+SO_PRIVSTATE :: 0x1009
+SO_NREAD :: 0x1020
+SO_NKE :: 0x1021
+
+AF_UNSPEC :: 0
+AF_LOCAL :: 1
+AF_UNIX :: AF_LOCAL
+AF_INET :: 2
+AF_IMPLINK :: 3
+AF_PUP :: 4
+AF_CHAOS :: 5
+AF_NS :: 6
+AF_ISO :: 7
+AF_OSI :: AF_ISO
+AF_ECMA :: 8
+AF_DATAKIT :: 9
+AF_CCITT :: 10
+AF_SNA :: 11
+AF_DECnet :: 12
+AF_DLI :: 13
+AF_LAT :: 14
+AF_HYLINK :: 15
+AF_APPLETALK :: 16
+AF_ROUTE :: 17
+AF_LINK :: 18
+pseudo_AF_XTP :: 19
+AF_COIP :: 20
+AF_CNT :: 21
+pseudo_AF_RTIP :: 22
+AF_IPX :: 23
+AF_SIP :: 24
+pseudo_AF_PIP :: 25
+pseudo_AF_BLUE :: 26
+AF_NDRV :: 27
+AF_ISDN :: 28
+AF_E164 :: AF_ISDN
+pseudo_AF_KEY :: 29
+AF_INET6 :: 30
+AF_NATM :: 31
+AF_SYSTEM :: 32
+AF_NETBIOS :: 33
+AF_PPP :: 34
+
+TCP_NODELAY :: 0x01
+TCP_MAXSEG :: 0x02
+TCP_NOPUSH :: 0x04
+TCP_NOOPT :: 0x08
+
+IPPROTO_ICMP :: 1
+IPPROTO_TCP :: 6
+IPPROTO_UDP :: 17
+
+SHUT_RD :: 0
+SHUT_WR :: 1
+SHUT_RDWR :: 2
+
+F_GETFL: int : 3 /* Get file flags */
+F_SETFL: int : 4 /* Set file flags */
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments()
@@ -224,6 +314,58 @@ Dirent :: struct {
Dir :: distinct rawptr // DIR*
+SOCKADDR :: struct #packed {
+ len: c.char,
+ family: c.char,
+ sa_data: [14]c.char,
+}
+
+SOCKADDR_STORAGE_LH :: struct #packed {
+ len: c.char,
+ family: c.char,
+ __ss_pad1: [6]c.char,
+ __ss_align: i64,
+ __ss_pad2: [112]c.char,
+}
+
+sockaddr_in :: struct #packed {
+ sin_len: c.char,
+ sin_family: c.char,
+ sin_port: u16be,
+ sin_addr: in_addr,
+ sin_zero: [8]c.char,
+}
+
+sockaddr_in6 :: struct #packed {
+ sin6_len: c.char,
+ sin6_family: c.char,
+ sin6_port: u16be,
+ sin6_flowinfo: c.uint,
+ sin6_addr: in6_addr,
+ sin6_scope_id: c.uint,
+}
+
+in_addr :: struct #packed {
+ s_addr: u32,
+}
+
+in6_addr :: struct #packed {
+ s6_addr: [16]u8,
+}
+
+Timeval :: struct {
+ seconds: i64,
+ microseconds: int,
+}
+
+Linger :: struct {
+ onoff: int,
+ linger: int,
+}
+
+Socket :: distinct int
+socklen_t :: c.int
+
// File type
S_IFMT :: 0o170000 // Type of file mask
S_IFIFO :: 0o010000 // Named pipe (fifo)
@@ -273,12 +415,14 @@ F_OK :: 0 // Test for file existance
F_GETPATH :: 50 // return the full path of the fd
foreign libc {
- @(link_name="__error") __error :: proc() -> ^int ---
+ @(link_name="__error") __error :: proc() -> ^c.int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
- @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
- @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
+ @(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
+ @(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
+ @(link_name="pread") _unix_pread :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int ---
+ @(link_name="pwrite") _unix_pwrite :: proc(handle: Handle, buffer: rawptr, count: c.size_t, offset: i64) -> int ---
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---
@@ -296,7 +440,7 @@ foreign libc {
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
- @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
+ @(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
@(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
@(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
@@ -314,6 +458,20 @@ foreign libc {
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
+ @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
+
+ @(link_name="socket") _unix_socket :: proc(domain: int, type: int, protocol: int) -> int ---
+ @(link_name="listen") _unix_listen :: proc(socket: int, backlog: int) -> int ---
+ @(link_name="accept") _unix_accept :: proc(socket: int, addr: rawptr, addr_len: rawptr) -> int ---
+ @(link_name="connect") _unix_connect :: proc(socket: int, addr: rawptr, addr_len: socklen_t) -> int ---
+ @(link_name="bind") _unix_bind :: proc(socket: int, addr: rawptr, addr_len: socklen_t) -> int ---
+ @(link_name="setsockopt") _unix_setsockopt :: proc(socket: int, level: int, opt_name: int, opt_val: rawptr, opt_len: socklen_t) -> int ---
+ @(link_name="getsockopt") _unix_getsockopt :: proc(socket: int, level: int, opt_name: int, opt_val: rawptr, opt_len: socklen_t) -> int ---
+ @(link_name="recvfrom") _unix_recvfrom :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int, addr: rawptr, addr_len: ^socklen_t) -> c.ssize_t ---
+ @(link_name="recv") _unix_recv :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t ---
+ @(link_name="sendto") _unix_sendto :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int, addr: rawptr, addr_len: socklen_t) -> c.ssize_t ---
+ @(link_name="send") _unix_send :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t ---
+ @(link_name="shutdown") _unix_shutdown :: proc(socket: int, how: int) -> int ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
@@ -334,7 +492,7 @@ foreign dl {
}
get_last_error :: proc "contextless" () -> int {
- return __error()^
+ return int(__error()^)
}
get_last_error_string :: proc() -> string {
@@ -352,6 +510,7 @@ open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno
flags = O_RDONLY
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, i32(flags), u16(mode))
if handle == -1 {
@@ -384,45 +543,51 @@ close :: proc(fd: Handle) -> bool {
@(private)
MAX_RW :: 0x7fffffff // The limit on Darwin is max(i32), trying to read/write more than that fails.
-write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
- assert(fd != -1)
-
- bytes_total := len(data)
- bytes_written_total := 0
-
- for bytes_written_total < bytes_total {
- bytes_to_write := min(bytes_total - bytes_written_total, MAX_RW)
- slice := data[bytes_written_total:bytes_written_total + bytes_to_write]
- bytes_written := _unix_write(fd, raw_data(slice), bytes_to_write)
- if bytes_written == -1 {
- return bytes_written_total, 1
- }
- bytes_written_total += bytes_written
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
}
- return bytes_written_total, 0
+ bytes_written := _unix_write(fd, raw_data(data), c.size_t(len(data)))
+ if bytes_written < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_written, ERROR_NONE
}
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
- assert(fd != -1)
-
- bytes_total := len(data)
- bytes_read_total := 0
-
- for bytes_read_total < bytes_total {
- bytes_to_read := min(bytes_total - bytes_read_total, MAX_RW)
- slice := data[bytes_read_total:bytes_read_total + bytes_to_read]
- bytes_read := _unix_read(fd, raw_data(slice), bytes_to_read)
- if bytes_read == -1 {
- return bytes_read_total, 1
- }
- if bytes_read == 0 {
- break
- }
- bytes_read_total += bytes_read
+ if len(data) == 0 {
+ return 0, ERROR_NONE
}
- return bytes_read_total, 0
+ bytes_read := _unix_read(fd, raw_data(data), c.size_t(len(data)))
+ if bytes_read < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_read, ERROR_NONE
+}
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_read := _unix_pread(fd, raw_data(data), c.size_t(len(data)), offset)
+ if bytes_read < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_read, ERROR_NONE
+}
+
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_written := _unix_pwrite(fd, raw_data(data), c.size_t(len(data)), offset)
+ if bytes_written < 0 {
+ return -1, Errno(get_last_error())
+ }
+ return bytes_written, ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
@@ -507,24 +672,28 @@ is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle}
exists :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cpath, O_RDONLY)
return res == 0
}
rename :: proc(old: string, new: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_cstr := strings.clone_to_cstring(old, context.temp_allocator)
new_cstr := strings.clone_to_cstring(new, context.temp_allocator)
return _unix_rename(old_cstr, new_cstr) != -1
}
remove :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _unix_remove(path_cstr) != -1
}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat
@@ -537,6 +706,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat
@@ -602,6 +772,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = 256
@@ -623,14 +794,14 @@ _readlink :: proc(path: string) -> (string, Errno) {
}
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
- buf : [256]byte
- res := _unix__fcntl(fd, F_GETPATH, &buf[0])
- if res != 0 {
- return "", Errno(get_last_error())
+ buf: [DARWIN_MAXPATHLEN]byte
+ _, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0])))
+ if err != ERROR_NONE {
+ return "", err
}
path := strings.clone_from_cstring(cstring(&buf[0]))
- return path, ERROR_NONE
+ return path, err
}
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
@@ -639,6 +810,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
rel = "."
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := _unix_realpath(rel_cstr, nil)
@@ -654,6 +826,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _unix_access(cstr, mask) == 0
}
@@ -678,6 +851,7 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
@@ -700,6 +874,7 @@ get_current_directory :: proc() -> string {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
+ delete(buf)
return ""
}
resize(&buf, len(buf)+page_size)
@@ -708,6 +883,7 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
@@ -717,6 +893,7 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
}
make_directory :: proc(path: string, mode: u16 = 0o775) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
if res == -1 {
@@ -741,12 +918,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, flags)
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -771,11 +950,127 @@ get_page_size :: proc() -> int {
return page_size
}
+@(private)
+_processor_core_count :: proc() -> int {
+ count : int = 0
+ count_size := size_of(count)
+ if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
+ if count > 0 {
+ return count
+ }
+ }
+
+ return 1
+}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
- for arg, i in runtime.args__ {
- res[i] = string(arg)
+ for _, i in res {
+ res[i] = string(runtime.args__[i])
}
return res
}
+
+socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Errno) {
+ result := _unix_socket(domain, type, protocol)
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return Socket(result), ERROR_NONE
+}
+
+connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) {
+ result := _unix_connect(int(sd), addr, len)
+ if result < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) {
+ result := _unix_bind(int(sd), addr, len)
+ if result < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Errno) {
+ result := _unix_accept(int(sd), rawptr(addr), len)
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return Socket(result), ERROR_NONE
+}
+
+listen :: proc(sd: Socket, backlog: int) -> (Errno) {
+ result := _unix_listen(int(sd), backlog)
+ if result < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> (Errno) {
+ result := _unix_setsockopt(int(sd), level, optname, optval, optlen)
+ if result < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+getsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Errno {
+ result := _unix_getsockopt(int(sd), level, optname, optval, optlen)
+ if result < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Errno) {
+ result := _unix_recvfrom(int(sd), raw_data(data), len(data), flags, addr, addr_size)
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return u32(result), ERROR_NONE
+}
+
+recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) {
+ result := _unix_recv(int(sd), raw_data(data), len(data), flags)
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return u32(result), ERROR_NONE
+}
+
+sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Errno) {
+ result := _unix_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen)
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return u32(result), ERROR_NONE
+}
+
+send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) {
+ result := _unix_send(int(sd), raw_data(data), len(data), 0)
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return u32(result), ERROR_NONE
+}
+
+shutdown :: proc(sd: Socket, how: int) -> (Errno) {
+ result := _unix_shutdown(int(sd), how)
+ if result < 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) {
+ result := _unix__fcntl(Handle(fd), c.int(cmd), uintptr(arg))
+ if result < 0 {
+ return 0, Errno(get_last_error())
+ }
+ return int(result), ERROR_NONE
+}
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index 2a0381a5d..c2ea82bf5 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -287,6 +287,7 @@ foreign libc {
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+ @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
@@ -308,6 +309,7 @@ get_last_error :: proc "contextless" () -> int {
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 {
@@ -360,6 +362,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
}
rename :: proc(old_path, new_path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr)
@@ -370,6 +373,7 @@ rename :: proc(old_path, new_path: string) -> Errno {
}
remove :: proc(path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
if res == -1 {
@@ -379,6 +383,7 @@ remove :: proc(path: string) -> Errno {
}
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
if res == -1 {
@@ -388,6 +393,7 @@ make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
}
remove_directory :: proc(path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
if res == -1 {
@@ -473,6 +479,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
@@ -484,6 +491,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized
@@ -549,6 +557,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
+
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = MAX_PATH
@@ -566,7 +576,8 @@ _readlink :: proc(path: string) -> (string, Errno) {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
}
}
- unreachable()
+
+ return "", Errno{}
}
// XXX FreeBSD
@@ -579,6 +590,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
if rel == "" {
rel = "."
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
@@ -595,6 +607,8 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask))
if result == -1 {
@@ -625,6 +639,8 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
+
path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
@@ -650,6 +666,7 @@ get_current_directory :: proc() -> string {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
+ delete(buf)
return ""
}
resize(&buf, len(buf)+page_size)
@@ -658,6 +675,7 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 do return Errno(get_last_error())
@@ -674,12 +692,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -702,6 +722,19 @@ get_page_size :: proc() -> int {
return page_size
}
+@(private)
+_processor_core_count :: proc() -> int {
+ count : int = 0
+ count_size := size_of(count)
+ if _sysctlbyname("hw.logicalcpu", &count, &count_size, nil, 0) == 0 {
+ if count > 0 {
+ return count
+ }
+ }
+
+ return 1
+}
+
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
diff --git a/core/os/os_js.odin b/core/os/os_js.odin
index ba65aa073..5d7eb784e 100644
--- a/core/os/os_js.odin
+++ b/core/os/os_js.odin
@@ -1,4 +1,277 @@
-//+js
+//+build js
package os
-#panic("package os does not support a js target")
\ No newline at end of file
+import "core:intrinsics"
+import "core:runtime"
+import "core:unicode/utf16"
+
+is_path_separator :: proc(c: byte) -> bool {
+ return c == '/' || c == '\\'
+}
+
+open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+close :: proc(fd: Handle) -> Errno {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+flush :: proc(fd: Handle) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+@(private="file")
+read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+@(private)
+MAX_RW :: 1<<30
+
+@(private)
+pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+@(private)
+pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+// NOTE(bill): Uses startup to initialize it
+//stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE))
+//stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
+//stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
+
+
+get_std_handle :: proc "contextless" (h: uint) -> Handle {
+ context = runtime.default_context()
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+exists :: proc(path: string) -> bool {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+is_file :: proc(path: string) -> bool {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+is_dir :: proc(path: string) -> bool {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
+//@private cwd_lock := win32.SRWLOCK{} // zero is initialized
+
+get_current_directory :: proc(allocator := context.allocator) -> string {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+change_directory :: proc(path: string) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+remove_directory :: proc(path: string) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+@(private)
+is_abs :: proc(path: string) -> bool {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+@(private)
+fix_long_path :: proc(path: string) -> string {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+link :: proc(old_name, new_name: string) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+unlink :: proc(path: string) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+rename :: proc(old_path, new_path: string) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+truncate :: proc(path: string, length: i64) -> (err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+remove :: proc(name: string) -> Errno {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+pipe :: proc() -> (r, w: Handle, err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+Handle :: distinct uintptr
+File_Time :: distinct u64
+Errno :: distinct int
+
+
+INVALID_HANDLE :: ~Handle(0)
+
+
+
+O_RDONLY :: 0x00000
+O_WRONLY :: 0x00001
+O_RDWR :: 0x00002
+O_CREATE :: 0x00040
+O_EXCL :: 0x00080
+O_NOCTTY :: 0x00100
+O_TRUNC :: 0x00200
+O_NONBLOCK :: 0x00800
+O_APPEND :: 0x00400
+O_SYNC :: 0x01000
+O_ASYNC :: 0x02000
+O_CLOEXEC :: 0x80000
+
+
+ERROR_NONE: Errno : 0
+ERROR_FILE_NOT_FOUND: Errno : 2
+ERROR_PATH_NOT_FOUND: Errno : 3
+ERROR_ACCESS_DENIED: Errno : 5
+ERROR_INVALID_HANDLE: Errno : 6
+ERROR_NOT_ENOUGH_MEMORY: Errno : 8
+ERROR_NO_MORE_FILES: Errno : 18
+ERROR_HANDLE_EOF: Errno : 38
+ERROR_NETNAME_DELETED: Errno : 64
+ERROR_FILE_EXISTS: Errno : 80
+ERROR_INVALID_PARAMETER: Errno : 87
+ERROR_BROKEN_PIPE: Errno : 109
+ERROR_BUFFER_OVERFLOW: Errno : 111
+ERROR_INSUFFICIENT_BUFFER: Errno : 122
+ERROR_MOD_NOT_FOUND: Errno : 126
+ERROR_PROC_NOT_FOUND: Errno : 127
+ERROR_DIR_NOT_EMPTY: Errno : 145
+ERROR_ALREADY_EXISTS: Errno : 183
+ERROR_ENVVAR_NOT_FOUND: Errno : 203
+ERROR_MORE_DATA: Errno : 234
+ERROR_OPERATION_ABORTED: Errno : 995
+ERROR_IO_PENDING: Errno : 997
+ERROR_NOT_FOUND: Errno : 1168
+ERROR_PRIVILEGE_NOT_HELD: Errno : 1314
+WSAEACCES: Errno : 10013
+WSAECONNRESET: Errno : 10054
+
+// Windows reserves errors >= 1<<29 for application use
+ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0
+ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
+ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
+
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments()
+
+
+
+
+
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
+ unimplemented("core:os procedure not supported on JS target")
+}
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ unimplemented("core:os procedure not supported on JS target")
+}
+heap_free :: proc(ptr: rawptr) {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+get_page_size :: proc() -> int {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+@(private)
+_processor_core_count :: proc() -> int {
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+exit :: proc "contextless" (code: int) -> ! {
+ context = runtime.default_context()
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+current_thread_id :: proc "contextless" () -> int {
+ context = runtime.default_context()
+ unimplemented("core:os procedure not supported on JS target")
+}
+
+
+
+_alloc_command_line_arguments :: proc() -> []string {
+ return nil
+}
+
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index ac40b8cde..e0b60fd36 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -14,6 +14,7 @@ Handle :: distinct i32
Pid :: distinct i32
File_Time :: distinct u64
Errno :: distinct i32
+Socket :: distinct int
INVALID_HANDLE :: ~Handle(0)
@@ -171,6 +172,64 @@ SEEK_DATA :: 3
SEEK_HOLE :: 4
SEEK_MAX :: SEEK_HOLE
+
+AF_UNSPEC: int : 0
+AF_UNIX: int : 1
+AF_LOCAL: int : AF_UNIX
+AF_INET: int : 2
+AF_INET6: int : 10
+AF_PACKET: int : 17
+AF_BLUETOOTH: int : 31
+
+SOCK_STREAM: int : 1
+SOCK_DGRAM: int : 2
+SOCK_RAW: int : 3
+SOCK_RDM: int : 4
+SOCK_SEQPACKET: int : 5
+SOCK_PACKET: int : 10
+
+INADDR_ANY: c.ulong : 0
+INADDR_BROADCAST: c.ulong : 0xffffffff
+INADDR_NONE: c.ulong : 0xffffffff
+INADDR_DUMMY: c.ulong : 0xc0000008
+
+IPPROTO_IP: int : 0
+IPPROTO_ICMP: int : 1
+IPPROTO_TCP: int : 6
+IPPROTO_UDP: int : 17
+IPPROTO_IPV6: int : 41
+IPPROTO_ETHERNET: int : 143
+IPPROTO_RAW: int : 255
+
+SHUT_RD: int : 0
+SHUT_WR: int : 1
+SHUT_RDWR: int : 2
+
+
+SOL_SOCKET: int : 1
+SO_DEBUG: int : 1
+SO_REUSEADDR: int : 2
+SO_DONTROUTE: int : 5
+SO_BROADCAST: int : 6
+SO_SNDBUF: int : 7
+SO_RCVBUF: int : 8
+SO_KEEPALIVE: int : 9
+SO_OOBINLINE: int : 10
+SO_LINGER: int : 13
+SO_REUSEPORT: int : 15
+SO_RCVTIMEO_NEW: int : 66
+SO_SNDTIMEO_NEW: int : 67
+
+TCP_NODELAY: int : 1
+TCP_CORK: int : 3
+
+MSG_TRUNC : int : 0x20
+
+// TODO: add remaining fcntl commands
+// reference: https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/fcntl.h
+F_GETFL: int : 3 /* Get file flags */
+F_SETFL: int : 4 /* Set file flags */
+
// NOTE(zangent): These are OS specific!
// Do not mix these up!
RTLD_LAZY :: 0x001
@@ -178,6 +237,13 @@ RTLD_NOW :: 0x002
RTLD_BINDING_MASK :: 0x3
RTLD_GLOBAL :: 0x100
+socklen_t :: c.int
+
+Timeval :: struct {
+ seconds: i64,
+ microseconds: int,
+}
+
// "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments()
@@ -217,6 +283,102 @@ Dirent :: struct {
name: [256]byte,
}
+ADDRESS_FAMILY :: u16
+SOCKADDR :: struct #packed {
+ sa_family: ADDRESS_FAMILY,
+ sa_data: [14]c.char,
+}
+
+SOCKADDR_STORAGE_LH :: struct #packed {
+ ss_family: ADDRESS_FAMILY,
+ __ss_pad1: [6]c.char,
+ __ss_align: i64,
+ __ss_pad2: [112]c.char,
+}
+
+sockaddr_in :: struct #packed {
+ sin_family: ADDRESS_FAMILY,
+ sin_port: u16be,
+ sin_addr: in_addr,
+ sin_zero: [8]c.char,
+}
+
+sockaddr_in6 :: struct #packed {
+ sin6_family: ADDRESS_FAMILY,
+ sin6_port: u16be,
+ sin6_flowinfo: c.ulong,
+ sin6_addr: in6_addr,
+ sin6_scope_id: c.ulong,
+}
+
+in_addr :: struct #packed {
+ s_addr: u32,
+}
+
+in6_addr :: struct #packed {
+ s6_addr: [16]u8,
+}
+
+rtnl_link_stats :: struct #packed {
+ rx_packets: u32,
+ tx_packets: u32,
+ rx_bytes: u32,
+ tx_bytes: u32,
+ rx_errors: u32,
+ tx_errors: u32,
+ rx_dropped: u32,
+ tx_dropped: u32,
+ multicast: u32,
+ collisions: u32,
+ rx_length_errors: u32,
+ rx_over_errors: u32,
+ rx_crc_errors: u32,
+ rx_frame_errors: u32,
+ rx_fifo_errors: u32,
+ rx_missed_errors: u32,
+ tx_aborted_errors: u32,
+ tx_carrier_errors: u32,
+ tx_fifo_errors: u32,
+ tx_heartbeat_errors: u32,
+ tx_window_errors: u32,
+ rx_compressed: u32,
+ tx_compressed: u32,
+ rx_nohandler: u32,
+}
+
+SIOCGIFFLAG :: enum c.int {
+ UP = 0, /* Interface is up. */
+ BROADCAST = 1, /* Broadcast address valid. */
+ DEBUG = 2, /* Turn on debugging. */
+ LOOPBACK = 3, /* Is a loopback net. */
+ POINT_TO_POINT = 4, /* Interface is point-to-point link. */
+ NO_TRAILERS = 5, /* Avoid use of trailers. */
+ RUNNING = 6, /* Resources allocated. */
+ NOARP = 7, /* No address resolution protocol. */
+ PROMISC = 8, /* Receive all packets. */
+ ALL_MULTI = 9, /* Receive all multicast packets. Unimplemented. */
+ MASTER = 10, /* Master of a load balancer. */
+ SLAVE = 11, /* Slave of a load balancer. */
+ MULTICAST = 12, /* Supports multicast. */
+ PORTSEL = 13, /* Can set media type. */
+ AUTOMEDIA = 14, /* Auto media select active. */
+ DYNAMIC = 15, /* Dialup device with changing addresses. */
+ LOWER_UP = 16,
+ DORMANT = 17,
+ ECHO = 18,
+}
+SIOCGIFFLAGS :: bit_set[SIOCGIFFLAG; c.int]
+
+ifaddrs :: struct {
+ next: ^ifaddrs,
+ name: cstring,
+ flags: SIOCGIFFLAGS,
+ address: ^SOCKADDR,
+ netmask: ^SOCKADDR,
+ broadcast_or_dest: ^SOCKADDR, // Broadcast or Point-to-Point address
+ data: rawptr, // Address-specific data.
+}
+
Dir :: distinct rawptr // DIR*
// File type
@@ -236,13 +398,13 @@ S_IRUSR :: 0o0400 // R for owner
S_IWUSR :: 0o0200 // W for owner
S_IXUSR :: 0o0100 // X for owner
- // Read, write, execute/search by group
+// Read, write, execute/search by group
S_IRWXG :: 0o0070 // RWX mask for group
S_IRGRP :: 0o0040 // R for group
S_IWGRP :: 0o0020 // W for group
S_IXGRP :: 0o0010 // X for group
- // Read, write, execute/search by others
+// Read, write, execute/search by others
S_IRWXO :: 0o0007 // RWX mask for other
S_IROTH :: 0o0004 // R for other
S_IWOTH :: 0o0002 // W for other
@@ -270,140 +432,19 @@ AT_FDCWD :: ~uintptr(99) /* -100 */
AT_REMOVEDIR :: uintptr(0x200)
AT_SYMLINK_NOFOLLOW :: uintptr(0x100)
-_unix_personality :: proc(persona: u64) -> int {
- return int(intrinsics.syscall(unix.SYS_personality, uintptr(persona)))
+pollfd :: struct {
+ fd: c.int,
+ events: c.short,
+ revents: c.short,
}
-_unix_fork :: proc() -> Pid {
- when ODIN_ARCH != .arm64 {
- res := int(intrinsics.syscall(unix.SYS_fork))
- } else {
- res := int(intrinsics.syscall(unix.SYS_clone, unix.SIGCHLD))
- }
- return -1 if res < 0 else Pid(res)
-}
-
-_unix_open :: proc(path: cstring, flags: int, mode: int = 0o000) -> Handle {
- when ODIN_ARCH != .arm64 {
- res := int(intrinsics.syscall(unix.SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
- } else { // NOTE: arm64 does not have open
- res := int(intrinsics.syscall(unix.SYS_openat, AT_FDCWD, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
- }
- return -1 if res < 0 else Handle(res)
-}
-
-_unix_close :: proc(fd: Handle) -> int {
- return int(intrinsics.syscall(unix.SYS_close, uintptr(fd)))
-}
-
-_unix_read :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
- return int(intrinsics.syscall(unix.SYS_read, uintptr(fd), uintptr(buf), uintptr(size)))
-}
-
-_unix_write :: proc(fd: Handle, buf: rawptr, size: uint) -> int {
- return int(intrinsics.syscall(unix.SYS_write, uintptr(fd), uintptr(buf), uintptr(size)))
-}
-
-_unix_seek :: proc(fd: Handle, offset: i64, whence: int) -> i64 {
- when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
- return i64(intrinsics.syscall(unix.SYS_lseek, uintptr(fd), uintptr(offset), uintptr(whence)))
- } else {
- low := uintptr(offset & 0xFFFFFFFF)
- high := uintptr(offset >> 32)
- result: i64
- res := i64(intrinsics.syscall(unix.SYS__llseek, uintptr(fd), high, low, uintptr(&result), uintptr(whence)))
- return -1 if res < 0 else result
- }
-}
-
-_unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == .amd64 {
- return int(intrinsics.syscall(unix.SYS_stat, uintptr(rawptr(path)), uintptr(stat)))
- } else when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_stat64, uintptr(rawptr(path)), uintptr(stat)))
- } else { // NOTE: arm64 does not have stat
- return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), 0))
- }
-}
-
-_unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
- return int(intrinsics.syscall(unix.SYS_fstat, uintptr(fd), uintptr(stat)))
- } else {
- return int(intrinsics.syscall(unix.SYS_fstat64, uintptr(fd), uintptr(stat)))
- }
-}
-
-_unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> int {
- when ODIN_ARCH == .amd64 {
- return int(intrinsics.syscall(unix.SYS_lstat, uintptr(rawptr(path)), uintptr(stat)))
- } else when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_lstat64, uintptr(rawptr(path)), uintptr(stat)))
- } else { // NOTE: arm64 does not have any lstat
- return int(intrinsics.syscall(unix.SYS_fstatat, AT_FDCWD, uintptr(rawptr(path)), uintptr(stat), AT_SYMLINK_NOFOLLOW))
- }
-}
-
-_unix_readlink :: proc(path: cstring, buf: rawptr, bufsiz: uint) -> int {
- when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_readlink, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
- } else { // NOTE: arm64 does not have readlink
- return int(intrinsics.syscall(unix.SYS_readlinkat, AT_FDCWD, uintptr(rawptr(path)), uintptr(buf), uintptr(bufsiz)))
- }
-}
-
-_unix_access :: proc(path: cstring, mask: int) -> int {
- when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_access, uintptr(rawptr(path)), uintptr(mask)))
- } else { // NOTE: arm64 does not have access
- return int(intrinsics.syscall(unix.SYS_faccessat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mask)))
- }
-}
-
-_unix_getcwd :: proc(buf: rawptr, size: uint) -> int {
- return int(intrinsics.syscall(unix.SYS_getcwd, uintptr(buf), uintptr(size)))
-}
-
-_unix_chdir :: proc(path: cstring) -> int {
- return int(intrinsics.syscall(unix.SYS_chdir, uintptr(rawptr(path))))
-}
-
-_unix_rename :: proc(old, new: cstring) -> int {
- when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_rename, uintptr(rawptr(old)), uintptr(rawptr(new))))
- } else { // NOTE: arm64 does not have rename
- return int(intrinsics.syscall(unix.SYS_renameat, AT_FDCWD, uintptr(rawptr(old)), uintptr(rawptr(new))))
- }
-}
-
-_unix_unlink :: proc(path: cstring) -> int {
- when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_unlink, uintptr(rawptr(path))))
- } else { // NOTE: arm64 does not have unlink
- return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), 0))
- }
-}
-
-_unix_rmdir :: proc(path: cstring) -> int {
- when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_rmdir, uintptr(rawptr(path))))
- } else { // NOTE: arm64 does not have rmdir
- return int(intrinsics.syscall(unix.SYS_unlinkat, AT_FDCWD, uintptr(rawptr(path)), AT_REMOVEDIR))
- }
-}
-
-_unix_mkdir :: proc(path: cstring, mode: u32) -> int {
- when ODIN_ARCH != .arm64 {
- return int(intrinsics.syscall(unix.SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
- } else { // NOTE: arm64 does not have mkdir
- return int(intrinsics.syscall(unix.SYS_mkdirat, AT_FDCWD, uintptr(rawptr(path)), uintptr(mode)))
- }
-}
+sigset_t :: distinct u64
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="get_nprocs") _unix_get_nprocs :: proc() -> c.int ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@@ -414,6 +455,7 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+ @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@@ -425,6 +467,9 @@ foreign dl {
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
+
+ @(link_name="getifaddrs") _getifaddrs :: proc(ifap: ^^ifaddrs) -> (c.int) ---
+ @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) ---
}
is_path_separator :: proc(r: rune) -> bool {
@@ -446,7 +491,7 @@ get_last_error :: proc "contextless" () -> int {
}
personality :: proc(persona: u64) -> (Errno) {
- res := _unix_personality(persona)
+ res := unix.sys_personality(persona)
if res == -1 {
return _get_errno(res)
}
@@ -454,28 +499,48 @@ personality :: proc(persona: u64) -> (Errno) {
}
fork :: proc() -> (Pid, Errno) {
- pid := _unix_fork()
+ pid := unix.sys_fork()
if pid == -1 {
- return -1, _get_errno(int(pid))
+ return -1, _get_errno(pid)
}
- return pid, ERROR_NONE
+ return Pid(pid), ERROR_NONE
}
-open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path, context.temp_allocator)
- handle := _unix_open(cstr, flags, mode)
- if handle < 0 {
- return INVALID_HANDLE, _get_errno(int(handle))
+execvp :: proc(path: string, args: []string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ args_cstrs := make([]cstring, len(args) + 2, context.temp_allocator)
+ args_cstrs[0] = strings.clone_to_cstring(path, context.temp_allocator)
+ for i := 0; i < len(args); i += 1 {
+ args_cstrs[i+1] = strings.clone_to_cstring(args[i], context.temp_allocator)
}
- return handle, ERROR_NONE
+
+ _unix_execvp(path_cstr, raw_data(args_cstrs))
+ return Errno(get_last_error())
+}
+
+
+open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ handle := unix.sys_open(cstr, flags, uint(mode))
+ if handle < 0 {
+ return INVALID_HANDLE, _get_errno(handle)
+ }
+ return Handle(handle), ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
- return _get_errno(_unix_close(fd))
+ return _get_errno(unix.sys_close(int(fd)))
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
- bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_read := unix.sys_read(int(fd), raw_data(data), len(data))
if bytes_read < 0 {
return -1, _get_errno(bytes_read)
}
@@ -486,50 +551,78 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
- bytes_written := _unix_write(fd, &data[0], uint(len(data)))
+
+ bytes_written := unix.sys_write(int(fd), raw_data(data), len(data))
if bytes_written < 0 {
return -1, _get_errno(bytes_written)
}
- return int(bytes_written), ERROR_NONE
+ return bytes_written, ERROR_NONE
+}
+read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_read := unix.sys_pread(int(fd), raw_data(data), len(data), offset)
+ if bytes_read < 0 {
+ return -1, _get_errno(bytes_read)
+ }
+ return bytes_read, ERROR_NONE
+}
+
+write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+
+ bytes_written := unix.sys_pwrite(int(fd), raw_data(data), uint(len(data)), offset)
+ if bytes_written < 0 {
+ return -1, _get_errno(bytes_written)
+ }
+ return bytes_written, ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, whence)
+ res := unix.sys_lseek(int(fd), offset, whence)
if res < 0 {
return -1, _get_errno(int(res))
}
- return res, ERROR_NONE
+ return i64(res), ERROR_NONE
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
- // deliberately uninitialized; the syscall fills this buffer for us
- s: OS_Stat = ---
- result := _unix_fstat(fd, &s)
- if result < 0 {
- return 0, _get_errno(result)
- }
- return max(s.size, 0), ERROR_NONE
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
+ result := unix.sys_fstat(int(fd), rawptr(&s))
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return max(s.size, 0), ERROR_NONE
}
rename :: proc(old_path, new_path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
- return _get_errno(_unix_rename(old_path_cstr, new_path_cstr))
+ return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr))
}
remove :: proc(path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _get_errno(_unix_unlink(path_cstr))
+ return _get_errno(unix.sys_unlink(path_cstr))
}
make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _get_errno(_unix_mkdir(path_cstr, mode))
+ return _get_errno(unix.sys_mkdir(path_cstr, uint(mode)))
}
remove_directory :: proc(path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _get_errno(_unix_rmdir(path_cstr))
+ return _get_errno(unix.sys_rmdir(path_cstr))
}
is_file_handle :: proc(fd: Handle) -> bool {
@@ -581,8 +674,9 @@ is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle}
exists :: proc(path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator)
- res := _unix_access(cpath, O_RDONLY)
+ res := unix.sys_access(cpath, O_RDONLY)
return res == 0
}
@@ -616,11 +710,12 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_stat(cstr, &s)
+ result := unix.sys_stat(cstr, &s)
if result < 0 {
return s, _get_errno(result)
}
@@ -629,11 +724,12 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_lstat(cstr, &s)
+ result := unix.sys_lstat(cstr, &s)
if result < 0 {
return s, _get_errno(result)
}
@@ -644,7 +740,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
// deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = ---
- result := _unix_fstat(fd, &s)
+ result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 {
return s, _get_errno(result)
}
@@ -696,12 +792,13 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = 256
buf := make([]byte, bufsz)
for {
- rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ rc := unix.sys_readlink(path_cstr, &(buf[0]), bufsz)
if rc < 0 {
delete(buf)
return "", _get_errno(rc)
@@ -731,6 +828,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
if rel == "" {
rel = "."
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
@@ -747,8 +845,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- result := _unix_access(cstr, mask)
+ result := unix.sys_access(cstr, mask)
if result < 0 {
return false, _get_errno(result)
}
@@ -777,6 +876,7 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
cstr := _unix_getenv(path_str)
@@ -792,6 +892,7 @@ get_env :: proc(key: string, allocator := context.allocator) -> (value: string)
}
set_env :: proc(key, value: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator)
res := _unix_putenv(strings.unsafe_string_to_cstring(s))
if res < 0 {
@@ -801,6 +902,7 @@ set_env :: proc(key, value: string) -> Errno {
}
unset_env :: proc(key: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
s := strings.clone_to_cstring(key, context.temp_allocator)
res := _unix_putenv(s)
if res < 0 {
@@ -816,12 +918,13 @@ get_current_directory :: proc() -> string {
page_size := get_page_size()
buf := make([dynamic]u8, page_size)
for {
- #no_bounds_check res := _unix_getcwd(&buf[0], uint(len(buf)))
+ #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
if res >= 0 {
- return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
+ return strings.string_from_null_terminated_ptr(&buf[0], len(buf))
}
if _get_errno(res) != ERANGE {
+ delete(buf)
return ""
}
resize(&buf, len(buf)+page_size)
@@ -830,8 +933,9 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
- res := _unix_chdir(cstr)
+ res := unix.sys_chdir(cstr)
if res < 0 {
return _get_errno(res)
}
@@ -848,12 +952,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -878,6 +984,10 @@ get_page_size :: proc() -> int {
return page_size
}
+@(private)
+_processor_core_count :: proc() -> int {
+ return int(_unix_get_nprocs())
+}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
@@ -886,3 +996,118 @@ _alloc_command_line_arguments :: proc() -> []string {
}
return res
}
+
+socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Errno) {
+ result := unix.sys_socket(domain, type, protocol)
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return Socket(result), ERROR_NONE
+}
+
+bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) {
+ result := unix.sys_bind(int(sd), addr, len)
+ if result < 0 {
+ return _get_errno(result)
+ }
+ return ERROR_NONE
+}
+
+
+connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) {
+ result := unix.sys_connect(int(sd), addr, len)
+ if result < 0 {
+ return _get_errno(result)
+ }
+ return ERROR_NONE
+}
+
+accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Errno) {
+ result := unix.sys_accept(int(sd), rawptr(addr), len)
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return Socket(result), ERROR_NONE
+}
+
+listen :: proc(sd: Socket, backlog: int) -> (Errno) {
+ result := unix.sys_listen(int(sd), backlog)
+ if result < 0 {
+ return _get_errno(result)
+ }
+ return ERROR_NONE
+}
+
+setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> (Errno) {
+ result := unix.sys_setsockopt(int(sd), level, optname, optval, optlen)
+ if result < 0 {
+ return _get_errno(result)
+ }
+ return ERROR_NONE
+}
+
+
+recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Errno) {
+ result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, addr, uintptr(addr_size))
+ if result < 0 {
+ return 0, _get_errno(int(result))
+ }
+ return u32(result), ERROR_NONE
+}
+
+recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) {
+ result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, nil, 0)
+ if result < 0 {
+ return 0, _get_errno(int(result))
+ }
+ return u32(result), ERROR_NONE
+}
+
+
+sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Errno) {
+ result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen)
+ if result < 0 {
+ return 0, _get_errno(int(result))
+ }
+ return u32(result), ERROR_NONE
+}
+
+send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) {
+ result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0)
+ if result < 0 {
+ return 0, _get_errno(int(result))
+ }
+ return u32(result), ERROR_NONE
+}
+
+shutdown :: proc(sd: Socket, how: int) -> (Errno) {
+ result := unix.sys_shutdown(int(sd), how)
+ if result < 0 {
+ return _get_errno(result)
+ }
+ return ERROR_NONE
+}
+
+fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) {
+ result := unix.sys_fcntl(fd, cmd, arg)
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return result, ERROR_NONE
+}
+
+poll :: proc(fds: []pollfd, timeout: int) -> (int, Errno) {
+ result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout)
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return result, ERROR_NONE
+}
+
+ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) {
+ result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t))
+ if result < 0 {
+ return 0, _get_errno(result)
+ }
+ return result, ERROR_NONE
+}
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
index 0375107ca..957873a0b 100644
--- a/core/os/os_openbsd.odin
+++ b/core/os/os_openbsd.odin
@@ -269,6 +269,7 @@ foreign libc {
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
@@ -307,6 +308,7 @@ fork :: proc() -> (Pid, Errno) {
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 {
@@ -359,6 +361,7 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
}
rename :: proc(old_path, new_path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr)
@@ -369,6 +372,7 @@ rename :: proc(old_path, new_path: string) -> Errno {
}
remove :: proc(path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr)
if res == -1 {
@@ -378,6 +382,7 @@ remove :: proc(path: string) -> Errno {
}
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode)
if res == -1 {
@@ -387,6 +392,7 @@ make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
}
remove_directory :: proc(path: string) -> Errno {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr)
if res == -1 {
@@ -472,6 +478,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized
@@ -485,6 +492,7 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
// deliberately uninitialized
@@ -551,6 +559,7 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
bufsz : uint = MAX_PATH
@@ -568,7 +577,6 @@ _readlink :: proc(path: string) -> (string, Errno) {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
}
}
- unreachable()
}
// XXX OpenBSD
@@ -582,6 +590,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
rel = "."
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := _unix_realpath(rel_cstr, nil)
@@ -597,6 +606,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask))
if res == -1 {
@@ -627,6 +637,7 @@ heap_free :: proc(ptr: rawptr) {
}
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
@@ -648,6 +659,7 @@ get_current_directory :: proc() -> string {
return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
+ delete(buf)
return ""
}
resize(&buf, len(buf) + MAX_PATH)
@@ -656,6 +668,7 @@ get_current_directory :: proc() -> string {
}
set_current_directory :: proc(path: string) -> (err: Errno) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
if res == -1 {
@@ -674,12 +687,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
@@ -704,6 +719,12 @@ get_page_size :: proc() -> int {
return page_size
}
+_SC_NPROCESSORS_ONLN :: 503
+
+@(private)
+_processor_core_count :: proc() -> int {
+ return int(_sysconf(_SC_NPROCESSORS_ONLN))
+}
_alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__))
@@ -711,4 +732,4 @@ _alloc_command_line_arguments :: proc() -> []string {
res[i] = string(arg)
}
return res
-}
\ No newline at end of file
+}
diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin
index 70f7481f0..c407acdb4 100644
--- a/core/os/os_wasi.odin
+++ b/core/os/os_wasi.odin
@@ -89,7 +89,10 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
current_thread_id :: proc "contextless" () -> int {
return 0
}
-
+@(private)
+_processor_core_count :: proc() -> int {
+ return 1
+}
file_size :: proc(fd: Handle) -> (i64, Errno) {
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin
index f48f46fa3..7b4c2f6c2 100644
--- a/core/os/os_windows.odin
+++ b/core/os/os_windows.odin
@@ -3,6 +3,7 @@ package os
import win32 "core:sys/windows"
import "core:runtime"
+import "core:intrinsics"
Handle :: distinct uintptr
File_Time :: distinct u64
@@ -126,7 +127,29 @@ get_page_size :: proc() -> int {
return page_size
}
+@(private)
+_processor_core_count :: proc() -> int {
+ length : win32.DWORD = 0
+ result := win32.GetLogicalProcessorInformation(nil, &length)
+ thread_count := 0
+ if !result && win32.GetLastError() == 122 && length > 0 {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+ processors := make([]win32.SYSTEM_LOGICAL_PROCESSOR_INFORMATION, length, context.temp_allocator)
+
+ result = win32.GetLogicalProcessorInformation(&processors[0], &length)
+ if result {
+ for processor in processors {
+ if processor.Relationship == .RelationProcessorCore {
+ thread := intrinsics.count_ones(processor.ProcessorMask)
+ thread_count += int(thread)
+ }
+ }
+ }
+ }
+
+ return thread_count
+}
exit :: proc "contextless" (code: int) -> ! {
runtime._cleanup_runtime_contextless()
@@ -214,4 +237,4 @@ is_windows_10 :: proc() -> bool {
is_windows_11 :: proc() -> bool {
osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
-}
\ No newline at end of file
+}
diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin
index c63fb4cf0..efea329ce 100644
--- a/core/os/stat_windows.odin
+++ b/core/os/stat_windows.odin
@@ -1,6 +1,7 @@
package os
import "core:time"
+import "core:runtime"
import win32 "core:sys/windows"
@(private)
@@ -11,6 +12,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
if name == "" {
name = "."
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
p := win32.utf8_to_utf16(name, context.temp_allocator)
buf := make([dynamic]u16, 100)
defer delete(buf)
@@ -36,6 +38,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
context.allocator = allocator
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator)
fa: win32.WIN32_FILE_ATTRIBUTE_DATA
@@ -132,14 +135,15 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
@(private)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
- buf, err := cleanpath_from_handle_u16(fd)
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
+ buf, err := cleanpath_from_handle_u16(fd, context.temp_allocator)
if err != 0 {
return "", err
}
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
}
@(private)
-cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
+cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
if fd == 0 {
return nil, ERROR_INVALID_HANDLE
}
@@ -149,7 +153,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
if n == 0 {
return nil, Errno(win32.GetLastError())
}
- buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator)
+ buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
return buf[:buf_len], ERROR_NONE
}
diff --git a/core/os/stream.odin b/core/os/stream.odin
index 9506ed3a3..2b4c83663 100644
--- a/core/os/stream.odin
+++ b/core/os/stream.odin
@@ -4,66 +4,60 @@ import "core:io"
stream_from_handle :: proc(fd: Handle) -> io.Stream {
s: io.Stream
- s.stream_data = rawptr(uintptr(fd))
- s.stream_vtable = &_file_stream_vtable
+ s.data = rawptr(uintptr(fd))
+ s.procedure = _file_stream_proc
return s
}
@(private)
-_file_stream_vtable := io.Stream_VTable{
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
- os_err: Errno
- n, os_err = read(fd, p)
- return
- },
- impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- when ODIN_OS == .Windows || ODIN_OS == .WASI {
- fd := Handle(uintptr(s.stream_data))
- os_err: Errno
- n, os_err = read_at(fd, p, offset)
- }
- return
- },
- impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- fd := Handle(uintptr(s.stream_data))
- os_err: Errno
- n, os_err = write(fd, p)
- return
- },
- impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- when ODIN_OS == .Windows || ODIN_OS == .WASI {
- fd := Handle(uintptr(s.stream_data))
- os_err: Errno
- n, os_err = write_at(fd, p, offset)
- _ = os_err
- }
- return
- },
- impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
- fd := Handle(uintptr(s.stream_data))
- n, os_err := seek(fd, offset, int(whence))
- _ = os_err
- return n, nil
- },
- impl_size = proc(s: io.Stream) -> i64 {
- fd := Handle(uintptr(s.stream_data))
- sz, _ := file_size(fd)
- return sz
- },
- impl_flush = proc(s: io.Stream) -> io.Error {
+_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ fd := Handle(uintptr(stream_data))
+ n_int: int
+ os_err: Errno
+ switch mode {
+ case .Close:
+ close(fd)
+ case .Flush:
when ODIN_OS == .Windows {
- fd := Handle(uintptr(s.stream_data))
flush(fd)
} else {
// TOOD(bill): other operating systems
}
- return nil
- },
- impl_close = proc(s: io.Stream) -> io.Error {
- fd := Handle(uintptr(s.stream_data))
- close(fd)
- return nil
- },
+ case .Read:
+ n_int, os_err = read(fd, p)
+ n = i64(n_int)
+ if os_err != 0 {
+ err = .Unknown
+ }
+ case .Read_At:
+ when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
+ n_int, os_err = read_at(fd, p, offset)
+ n = i64(n_int)
+ }
+ case .Write:
+ n_int, os_err = write(fd, p)
+ n = i64(n_int)
+ case .Write_At:
+ when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
+ n_int, os_err = write_at(fd, p, offset)
+ n = i64(n_int)
+ }
+ case .Seek:
+ n, os_err = seek(fd, offset, int(whence))
+ case .Size:
+ n, os_err = file_size(fd)
+ case .Destroy:
+ err = .Empty
+ case .Query:
+ when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
+ return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query})
+ } else {
+ return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
+ }
+ }
+ if err == nil && os_err != 0 {
+ err = .Unknown
+ }
+ return
}
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index c932f202a..fe67ebe2c 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -218,7 +218,6 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
// glob ignores file system errors
//
-
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
context.allocator = allocator
@@ -261,6 +260,8 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
+
+// Internal implementation of `glob`, not meant to be used by the user. Prefer `glob`.
_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
context.allocator = allocator
diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin
index 32e4a8a37..370de59a2 100644
--- a/core/path/filepath/path.odin
+++ b/core/path/filepath/path.odin
@@ -20,6 +20,8 @@ is_slash :: proc(c: byte) -> bool {
return c == '\\' || c == '/'
}
+// Splits path immediate following the last separator; separating the path into a directory and file.
+// If no separator is found, `dir` will be empty and `path` set to `path`.
split :: proc(path: string) -> (dir, file: string) {
vol := volume_name(path)
i := len(path) - 1
@@ -29,10 +31,18 @@ split :: proc(path: string) -> (dir, file: string) {
return path[:i+1], path[i+1:]
}
+/*
+ Returns leading volume name.
+
+ e.g.
+ "C:\foo\bar\baz" will return "C:" on Windows.
+ Everything else will be "".
+*/
volume_name :: proc(path: string) -> string {
return path[:volume_name_len(path)]
}
+// Returns the length of the volume name in bytes.
volume_name_len :: proc(path: string) -> int {
if ODIN_OS == .Windows {
if len(path) < 2 {
@@ -74,7 +84,7 @@ volume_name_len :: proc(path: string) -> int {
/*
Gets the file name and extension from a path.
- i.e:
+ e.g.
'path/to/name.tar.gz' -> 'name.tar.gz'
'path/to/name.txt' -> 'name.txt'
'path/to/name' -> 'name'
@@ -114,7 +124,7 @@ base :: proc(path: string) -> string {
Only the last dot is considered when splitting the file extension.
See `short_stem`.
- i.e:
+ e.g.
'name.tar.gz' -> 'name.tar'
'name.txt' -> 'name'
@@ -147,7 +157,7 @@ stem :: proc(path: string) -> string {
The first dot is used to split off the file extension, unlike `stem` which uses the last dot.
- i.e:
+ e.g.
'name.tar.gz' -> 'name'
'name.txt' -> 'name'
@@ -170,7 +180,7 @@ short_stem :: proc(path: string) -> string {
Only the last dot is considered when splitting the file extension.
See `long_ext`.
- i.e:
+ e.g.
'name.tar.gz' -> '.gz'
'name.txt' -> '.txt'
@@ -193,7 +203,7 @@ ext :: proc(path: string) -> string {
The first dot is used to split off the file extension, unlike `ext` which uses the last dot.
- i.e:
+ e.g.
'name.tar.gz' -> '.tar.gz'
'name.txt' -> '.txt'
@@ -219,6 +229,21 @@ long_ext :: proc(path: string) -> string {
return ""
}
+/*
+ Returns the shortest path name equivalent to `path` through solely lexical processing.
+ It applies the folliwng rules until none of them can be applied:
+
+ * Replace multiple separators with a single one
+ * Remove each current directory (`.`) path name element
+ * Remove each inner parent directory (`..`) path and the preceding paths
+ * Remove `..` that begin at the root of a path
+ * All possible separators are replaced with the OS specific separator
+
+ The return path ends in a slash only if it represents the root of a directory (`C:\` on Windows and `/` on *nix systems).
+
+ If the result of the path is an empty string, the returned path with be `"."`.
+
+*/
clean :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
@@ -299,6 +324,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
return cleaned
}
+// Returns the result of replacing each forward slash `/` character in the path with the separate OS specific character.
from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) {
if SEPARATOR == '/' {
return path, false
@@ -306,6 +332,7 @@ from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: s
return strings.replace_all(path, "/", SEPARATOR_STRING, allocator)
}
+// Returns the result of replacing each OS specific separator with a forward slash `/` character.
to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) {
if SEPARATOR == '/' {
return path, false
@@ -320,6 +347,13 @@ Relative_Error :: enum {
Cannot_Relate,
}
+/*
+ Returns a relative path that is lexically equivalent to the `target_path` when joined with the `base_path` with an OS specific separator.
+
+ e.g. `join(base_path, rel(base_path, target_path))` is equivalent to `target_path`
+
+ On failure, the `Relative_Error` will be state it cannot compute the necessary relative path.
+*/
rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (string, Relative_Error) {
context.allocator = allocator
base_clean, target_clean := clean(base_path), clean(target_path)
@@ -398,6 +432,11 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
return target[t0:], .None
}
+/*
+ Returns all but the last element path, usually the path's directory. Once the final element has been removed,
+ `dir` calls `clean` on the path and trailing separators are removed. If the path consists purely of separators,
+ then `"."` is returned.
+*/
dir :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
vol := volume_name(path)
diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin
index 8faf6097c..898f34b6a 100644
--- a/core/path/filepath/path_unix.odin
+++ b/core/path/filepath/path_unix.odin
@@ -7,6 +7,7 @@ when ODIN_OS == .Darwin {
foreign import libc "system:c"
}
+import "core:runtime"
import "core:strings"
SEPARATOR :: '/'
@@ -41,6 +42,7 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
join :: proc(elems: []string, allocator := context.allocator) -> string {
for e, i in elems {
if e != "" {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
p := strings.join(elems[i:], SEPARATOR_STRING, context.temp_allocator)
return clean(p, allocator)
}
diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin
index cdfe3ddbb..e7dd4ab3e 100644
--- a/core/path/filepath/path_windows.odin
+++ b/core/path/filepath/path_windows.odin
@@ -1,6 +1,7 @@
package filepath
import "core:strings"
+import "core:runtime"
import "core:os"
import win32 "core:sys/windows"
@@ -60,25 +61,25 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
}
p := win32.utf8_to_utf16(name, ta)
- buf := make([dynamic]u16, 100, ta)
- for {
- n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
- if n == 0 {
- delete(buf)
- return "", os.Errno(win32.GetLastError())
- }
- if n <= u32(len(buf)) {
- return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE
- }
- resize(&buf, len(buf)*2)
+ n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
+ if n == 0 {
+ return "", os.Errno(win32.GetLastError())
}
- return
+ buf := make([]u16, n, ta)
+ n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
+ if n == 0 {
+ delete(buf)
+ return "", os.Errno(win32.GetLastError())
+ }
+
+ return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE
}
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := temp_full_path(path)
if err != 0 {
return "", false
@@ -99,6 +100,8 @@ join :: proc(elems: []string, allocator := context.allocator) -> string {
join_non_empty :: proc(elems: []string, allocator := context.allocator) -> string {
context.allocator = allocator
+
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
if len(elems[0]) == 2 && elems[0][1] == ':' {
i := 1
diff --git a/core/path/slashpath/path.odin b/core/path/slashpath/path.odin
index 865f619bf..5f5ab2ae9 100644
--- a/core/path/slashpath/path.odin
+++ b/core/path/slashpath/path.odin
@@ -5,6 +5,7 @@
// To manipulate operating system specific paths, use the path/filepath package
package slashpath
+import "core:runtime"
import "core:strings"
// is_separator checks whether the byte is a valid separator character
@@ -150,8 +151,9 @@ join :: proc(elems: []string, allocator := context.allocator) -> string {
context.allocator = allocator
for elem, i in elems {
if elem != "" {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
s := strings.join(elems[i:], "/", context.temp_allocator)
- return clean(s)
+ return clean(s, allocator)
}
}
return ""
diff --git a/core/prof/spall/doc.odin b/core/prof/spall/doc.odin
new file mode 100644
index 000000000..0f3cc8bb8
--- /dev/null
+++ b/core/prof/spall/doc.odin
@@ -0,0 +1,26 @@
+/*
+import "core:prof/spall"
+
+spall_ctx: spall.Context
+spall_buffer: spall.Buffer
+
+foo :: proc() {
+ spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
+}
+
+main :: proc() {
+ spall_ctx = spall.context_create("trace_test.spall")
+ defer spall.context_destroy(&spall_ctx)
+
+ buffer_backing := make([]u8, spall.BUFFER_DEFAULT_SIZE)
+ spall_buffer = spall.buffer_create(buffer_backing)
+ defer spall.buffer_destroy(&spall_ctx, &spall_buffer)
+
+ spall.SCOPED_EVENT(&spall_ctx, &spall_buffer, #procedure)
+
+ for i := 0; i < 9001; i += 1 {
+ foo()
+ }
+}
+*/
+package spall
diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin
new file mode 100644
index 000000000..19a05d70a
--- /dev/null
+++ b/core/prof/spall/spall.odin
@@ -0,0 +1,222 @@
+package spall
+
+import "core:os"
+import "core:time"
+import "core:intrinsics"
+import "core:mem"
+
+// File Format
+
+MANUAL_MAGIC :: u64le(0x0BADF00D)
+
+Manual_Header :: struct #packed {
+ magic: u64le,
+ version: u64le,
+ timestamp_scale: f64le,
+ reserved: u64le,
+}
+
+Manual_Event_Type :: enum u8 {
+ Invalid = 0,
+
+ Begin = 3,
+ End = 4,
+ Instant = 5,
+
+ Pad_Skip = 7,
+}
+
+Begin_Event :: struct #packed {
+ type: Manual_Event_Type,
+ category: u8,
+ pid: u32le,
+ tid: u32le,
+ ts: f64le,
+ name_len: u8,
+ args_len: u8,
+}
+BEGIN_EVENT_MAX :: size_of(Begin_Event) + 255 + 255
+
+End_Event :: struct #packed {
+ type: Manual_Event_Type,
+ pid: u32le,
+ tid: u32le,
+ ts: f64le,
+}
+
+Pad_Skip :: struct #packed {
+ type: Manual_Event_Type,
+ size: u32le,
+}
+
+// User Interface
+
+Context :: struct {
+ precise_time: bool,
+ timestamp_scale: f64,
+ fd: os.Handle,
+}
+
+Buffer :: struct {
+ data: []u8,
+ head: int,
+ tid: u32,
+ pid: u32,
+}
+
+BUFFER_DEFAULT_SIZE :: 0x10_0000
+
+
+context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok {
+ fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600)
+ if err != os.ERROR_NONE {
+ return
+ }
+
+ ctx.fd = fd
+ ctx.precise_time = precise_time
+ ctx.timestamp_scale = timestamp_scale
+
+ temp := [size_of(Manual_Header)]u8{}
+ _build_header(temp[:], ctx.timestamp_scale)
+ os.write(ctx.fd, temp[:])
+ ok = true
+ return
+}
+
+context_create_with_sleep :: proc(filename: string, sleep := 2 * time.Second) -> (ctx: Context, ok: bool) #optional_ok {
+ freq, freq_ok := time.tsc_frequency(sleep)
+ timestamp_scale: f64 = ((1 / f64(freq)) * 1_000_000) if freq_ok else 1
+ return context_create_with_scale(filename, freq_ok, timestamp_scale)
+}
+
+context_create :: proc{context_create_with_scale, context_create_with_sleep}
+
+context_destroy :: proc(ctx: ^Context) {
+ if ctx == nil {
+ return
+ }
+
+ os.close(ctx.fd)
+ ctx^ = Context{}
+}
+
+buffer_create :: proc(data: []byte, tid: u32 = 0, pid: u32 = 0) -> (buffer: Buffer, ok: bool) #optional_ok {
+ assert(len(data) >= 1024)
+ buffer.data = data
+ buffer.tid = tid
+ buffer.pid = pid
+ buffer.head = 0
+ ok = true
+ return
+}
+
+buffer_flush :: proc(ctx: ^Context, buffer: ^Buffer) {
+ start := _trace_now(ctx)
+ os.write(ctx.fd, buffer.data[:buffer.head])
+ buffer.head = 0
+ end := _trace_now(ctx)
+
+ buffer.head += _build_begin(buffer.data[buffer.head:], "Spall Trace Buffer Flush", "", start, buffer.tid, buffer.pid)
+ buffer.head += _build_end(buffer.data[buffer.head:], end, buffer.tid, buffer.pid)
+}
+
+buffer_destroy :: proc(ctx: ^Context, buffer: ^Buffer) {
+ buffer_flush(ctx, buffer)
+
+ buffer^ = Buffer{}
+}
+
+
+
+@(deferred_in=_scoped_buffer_end)
+SCOPED_EVENT :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) -> bool {
+ _buffer_begin(ctx, buffer, name, args, location)
+ return true
+}
+
+@(private)
+_scoped_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer, _, _: string, _ := #caller_location) {
+ _buffer_end(ctx, buffer)
+}
+
+
+_trace_now :: proc "contextless" (ctx: ^Context) -> f64 {
+ if !ctx.precise_time {
+ return f64(time.tick_now()._nsec) / 1_000
+ }
+
+ return f64(intrinsics.read_cycle_counter())
+}
+
+_build_header :: proc "contextless" (buffer: []u8, timestamp_scale: f64) -> (header_size: int, ok: bool) #optional_ok {
+ header_size = size_of(Manual_Header)
+ if header_size > len(buffer) {
+ return 0, false
+ }
+
+ hdr := (^Manual_Header)(raw_data(buffer))
+ hdr.magic = MANUAL_MAGIC
+ hdr.version = 1
+ hdr.timestamp_scale = f64le(timestamp_scale)
+ hdr.reserved = 0
+ ok = true
+ return
+}
+
+_build_begin :: proc "contextless" (buffer: []u8, name: string, args: string, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
+ ev := (^Begin_Event)(raw_data(buffer))
+ name_len := min(len(name), 255)
+ args_len := min(len(args), 255)
+
+ event_size = size_of(Begin_Event) + name_len + args_len
+ if event_size > len(buffer) {
+ return 0, false
+ }
+
+ ev.type = .Begin
+ ev.pid = u32le(pid)
+ ev.tid = u32le(tid)
+ ev.ts = f64le(ts)
+ ev.name_len = u8(name_len)
+ ev.args_len = u8(args_len)
+ mem.copy(raw_data(buffer[size_of(Begin_Event):]), raw_data(name), name_len)
+ mem.copy(raw_data(buffer[size_of(Begin_Event)+name_len:]), raw_data(args), args_len)
+ ok = true
+
+ return
+}
+
+_build_end :: proc "contextless" (buffer: []u8, ts: f64, tid: u32, pid: u32) -> (event_size: int, ok: bool) #optional_ok {
+ ev := (^End_Event)(raw_data(buffer))
+ event_size = size_of(End_Event)
+ if event_size > len(buffer) {
+ return 0, false
+ }
+
+ ev.type = .End
+ ev.pid = u32le(pid)
+ ev.tid = u32le(tid)
+ ev.ts = f64le(ts)
+ ok = true
+
+ return
+}
+
+_buffer_begin :: proc(ctx: ^Context, buffer: ^Buffer, name: string, args: string = "", location := #caller_location) {
+ if buffer.head + BEGIN_EVENT_MAX > len(buffer.data) {
+ buffer_flush(ctx, buffer)
+ }
+ name := location.procedure if name == "" else name
+ buffer.head += _build_begin(buffer.data[buffer.head:], name, args, _trace_now(ctx), buffer.tid, buffer.pid)
+}
+
+_buffer_end :: proc(ctx: ^Context, buffer: ^Buffer) {
+ ts := _trace_now(ctx)
+
+ if buffer.head + size_of(End_Event) > len(buffer.data) {
+ buffer_flush(ctx, buffer)
+ }
+
+ buffer.head += _build_end(buffer.data[buffer.head:], ts, buffer.tid, buffer.pid)
+}
diff --git a/core/reflect/iterator.odin b/core/reflect/iterator.odin
index 9fb6489ca..2e143284a 100644
--- a/core/reflect/iterator.odin
+++ b/core/reflect/iterator.odin
@@ -2,6 +2,7 @@ package reflect
import "core:runtime"
+@(require_results)
iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
if val == nil || it == nil {
return
@@ -41,6 +42,7 @@ iterate_array :: proc(val: any, it: ^int) -> (elem: any, index: int, ok: bool) {
return
}
+@(require_results)
iterate_map :: proc(val: any, it: ^int) -> (key, value: any, ok: bool) {
if val == nil || it == nil {
return
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 0312ffd95..2eb31c21b 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -25,7 +25,8 @@ Type_Info_Array :: runtime.Type_Info_Array
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array
Type_Info_Slice :: runtime.Type_Info_Slice
-Type_Info_Tuple :: runtime.Type_Info_Tuple
+Type_Info_Parameters :: runtime.Type_Info_Parameters
+Type_Info_Tuple :: runtime.Type_Info_Parameters
Type_Info_Struct :: runtime.Type_Info_Struct
Type_Info_Union :: runtime.Type_Info_Union
Type_Info_Enum :: runtime.Type_Info_Enum
@@ -74,6 +75,7 @@ Type_Kind :: enum {
}
+@(require_results)
type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T)
if ti != nil {
@@ -95,7 +97,7 @@ type_kind :: proc(T: typeid) -> Type_Kind {
case Type_Info_Enumerated_Array: return .Enumerated_Array
case Type_Info_Dynamic_Array: return .Dynamic_Array
case Type_Info_Slice: return .Slice
- case Type_Info_Tuple: return .Tuple
+ case Type_Info_Parameters: return .Tuple
case Type_Info_Struct: return .Struct
case Type_Info_Union: return .Union
case Type_Info_Enum: return .Enum
@@ -113,11 +115,13 @@ type_kind :: proc(T: typeid) -> Type_Kind {
}
// TODO(bill): Better name
+@(require_results)
underlying_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_base(T))
}
// TODO(bill): Better name
+@(require_results)
backing_type_kind :: proc(T: typeid) -> Type_Kind {
return type_kind(runtime.typeid_core(T))
}
@@ -128,13 +132,14 @@ type_info_core :: runtime.type_info_core
type_info_base_without_enum :: type_info_core
-when !ODIN_DISALLOW_RTTI {
+when !ODIN_NO_RTTI {
typeid_base :: runtime.typeid_base
typeid_core :: runtime.typeid_core
typeid_base_without_enum :: typeid_core
}
+@(require_results)
any_base :: proc(v: any) -> any {
v := v
if v != nil {
@@ -142,6 +147,7 @@ any_base :: proc(v: any) -> any {
}
return v
}
+@(require_results)
any_core :: proc(v: any) -> any {
v := v
if v != nil {
@@ -150,6 +156,7 @@ any_core :: proc(v: any) -> any {
return v
}
+@(require_results)
typeid_elem :: proc(id: typeid) -> typeid {
ti := type_info_of(id)
if ti == nil { return nil }
@@ -179,6 +186,7 @@ typeid_elem :: proc(id: typeid) -> typeid {
}
+@(require_results)
size_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.size
@@ -186,6 +194,7 @@ size_of_typeid :: proc(T: typeid) -> int {
return 0
}
+@(require_results)
align_of_typeid :: proc(T: typeid) -> int {
if ti := type_info_of(T); ti != nil {
return ti.align
@@ -193,6 +202,7 @@ align_of_typeid :: proc(T: typeid) -> int {
return 1
}
+@(require_results)
as_bytes :: proc(v: any) -> []byte {
if v != nil {
sz := size_of_typeid(v.id)
@@ -201,10 +211,12 @@ as_bytes :: proc(v: any) -> []byte {
return nil
}
+@(require_results)
any_data :: #force_inline proc(v: any) -> (data: rawptr, id: typeid) {
return v.data, v.id
}
+@(require_results)
is_nil :: proc(v: any) -> bool {
if v == nil {
return true
@@ -221,6 +233,7 @@ is_nil :: proc(v: any) -> bool {
return true
}
+@(require_results)
length :: proc(val: any) -> int {
if val == nil { return 0 }
@@ -256,6 +269,7 @@ length :: proc(val: any) -> int {
return 0
}
+@(require_results)
capacity :: proc(val: any) -> int {
if val == nil { return 0 }
@@ -282,6 +296,7 @@ capacity :: proc(val: any) -> int {
}
+@(require_results)
index :: proc(val: any, i: int, loc := #caller_location) -> any {
if val == nil { return nil }
@@ -341,6 +356,7 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
return nil
}
+@(require_results)
deref :: proc(val: any) -> any {
if val != nil {
ti := type_info_base(type_info_of(val.id))
@@ -370,6 +386,7 @@ Struct_Field :: struct {
is_using: bool,
}
+@(require_results)
struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -384,6 +401,7 @@ struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) {
return
}
+@(require_results)
struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -401,6 +419,7 @@ struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) {
return
}
+@(require_results)
struct_field_value_by_name :: proc(a: any, field: string, allow_using := false) -> any {
if a == nil { return nil }
@@ -430,8 +449,16 @@ struct_field_value_by_name :: proc(a: any, field: string, allow_using := false)
return nil
}
+@(require_results)
+struct_field_value :: proc(a: any, field: Struct_Field) -> any {
+ if a == nil { return nil }
+ return any {
+ rawptr(uintptr(a.data) + field.offset),
+ field.type.id,
+ }
+}
-
+@(require_results)
struct_field_names :: proc(T: typeid) -> []string {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -440,6 +467,7 @@ struct_field_names :: proc(T: typeid) -> []string {
return nil
}
+@(require_results)
struct_field_types :: proc(T: typeid) -> []^Type_Info {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -449,6 +477,7 @@ struct_field_types :: proc(T: typeid) -> []^Type_Info {
}
+@(require_results)
struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -457,6 +486,7 @@ struct_field_tags :: proc(T: typeid) -> []Struct_Tag {
return nil
}
+@(require_results)
struct_field_offsets :: proc(T: typeid) -> []uintptr {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -465,6 +495,7 @@ struct_field_offsets :: proc(T: typeid) -> []uintptr {
return nil
}
+@(require_results)
struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
ti := runtime.type_info_base(type_info_of(T))
if s, ok := ti.variant.(runtime.Type_Info_Struct); ok {
@@ -481,11 +512,13 @@ struct_fields_zipped :: proc(T: typeid) -> (fields: #soa[]Struct_Field) {
+@(require_results)
struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag) {
value, _ = struct_tag_lookup(tag, key)
return
}
+@(require_results)
struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, ok: bool) {
for t := tag; t != ""; /**/ {
i := 0
@@ -544,6 +577,7 @@ struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: Struct_Tag, o
}
+@(require_results)
enum_string :: proc(a: any) -> string {
if a == nil { return "" }
ti := runtime.type_info_base(type_info_of(a.id))
@@ -562,6 +596,7 @@ enum_string :: proc(a: any) -> string {
}
// Given a enum type and a value name, get the enum value.
+@(require_results)
enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, ok: bool) {
ti := type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -578,6 +613,7 @@ enum_from_name :: proc($Enum_Type: typeid, name: string) -> (value: Enum_Type, o
return
}
+@(require_results)
enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info_Enum_Value, ok: bool) {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -594,6 +630,7 @@ enum_from_name_any :: proc(Enum_Type: typeid, name: string) -> (value: Type_Info
}
+@(require_results)
enum_field_names :: proc(Enum_Type: typeid) -> []string {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -601,6 +638,7 @@ enum_field_names :: proc(Enum_Type: typeid) -> []string {
}
return nil
}
+@(require_results)
enum_field_values :: proc(Enum_Type: typeid) -> []Type_Info_Enum_Value {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -614,6 +652,7 @@ Enum_Field :: struct {
value: Type_Info_Enum_Value,
}
+@(require_results)
enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
ti := runtime.type_info_base(type_info_of(Enum_Type))
if eti, eti_ok := ti.variant.(runtime.Type_Info_Enum); eti_ok {
@@ -624,15 +663,18 @@ enum_fields_zipped :: proc(Enum_Type: typeid) -> (fields: #soa[]Enum_Field) {
+@(require_results)
union_variant_type_info :: proc(a: any) -> ^Type_Info {
id := union_variant_typeid(a)
return type_info_of(id)
}
+@(require_results)
type_info_union_is_pure_maybe :: proc(info: runtime.Type_Info_Union) -> bool {
return len(info.variants) == 1 && is_pointer(info.variants[0])
}
+@(require_results)
union_variant_typeid :: proc(a: any) -> typeid {
if a == nil { return nil }
@@ -661,9 +703,10 @@ union_variant_typeid :: proc(a: any) -> typeid {
case: unimplemented()
}
- if a.data != nil && tag != 0 {
- i := tag if info.no_nil else tag-1
- return info.variants[i].id
+ if info.no_nil {
+ return info.variants[tag].id
+ } else if tag != 0 {
+ return info.variants[tag-1].id
}
return nil
@@ -671,6 +714,7 @@ union_variant_typeid :: proc(a: any) -> typeid {
panic("expected a union to reflect.union_variant_typeid")
}
+@(require_results)
get_union_variant_raw_tag :: proc(a: any) -> i64 {
if a == nil { return -1 }
@@ -701,6 +745,7 @@ get_union_variant_raw_tag :: proc(a: any) -> i64 {
panic("expected a union to reflect.get_union_variant_raw_tag")
}
+@(require_results)
get_union_variant :: proc(a: any) -> any {
if a == nil {
return nil
@@ -712,6 +757,7 @@ get_union_variant :: proc(a: any) -> any {
return any{a.data, id}
}
+@(require_results)
get_union_as_ptr_variants :: proc(val: ^$T) -> (res: intrinsics.type_convert_variants_to_pointers(T)) where intrinsics.type_is_union(T) {
ptr := rawptr(val)
tag := get_union_variant_raw_tag(val^)
@@ -852,6 +898,7 @@ set_union_value :: proc(dst: any, value: any) -> bool {
+@(require_results)
as_bool :: proc(a: any) -> (value: bool, valid: bool) {
if a == nil { return }
a := a
@@ -874,6 +921,7 @@ as_bool :: proc(a: any) -> (value: bool, valid: bool) {
return
}
+@(require_results)
as_int :: proc(a: any) -> (value: int, valid: bool) {
v: i64
v, valid = as_i64(a)
@@ -881,6 +929,7 @@ as_int :: proc(a: any) -> (value: int, valid: bool) {
return
}
+@(require_results)
as_uint :: proc(a: any) -> (value: uint, valid: bool) {
v: u64
v, valid = as_u64(a)
@@ -888,6 +937,7 @@ as_uint :: proc(a: any) -> (value: uint, valid: bool) {
return
}
+@(require_results)
as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
if a == nil { return }
a := a
@@ -995,6 +1045,7 @@ as_i64 :: proc(a: any) -> (value: i64, valid: bool) {
return
}
+@(require_results)
as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
if a == nil { return }
a := a
@@ -1104,6 +1155,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
}
+@(require_results)
as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
if a == nil { return }
a := a
@@ -1210,6 +1262,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
}
+@(require_results)
as_string :: proc(a: any) -> (value: string, valid: bool) {
if a == nil { return }
a := a
@@ -1229,6 +1282,7 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
return
}
+@(require_results)
relative_pointer_to_absolute :: proc(a: any) -> rawptr {
if a == nil { return nil }
a := a
@@ -1243,6 +1297,7 @@ relative_pointer_to_absolute :: proc(a: any) -> rawptr {
}
+@(require_results)
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
if ptr^ == 0 {
@@ -1285,6 +1340,7 @@ relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid)
+@(require_results)
as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
if a == nil { return }
a := a
@@ -1312,6 +1368,7 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
}
+@(require_results)
as_raw_data :: proc(a: any) -> (value: rawptr, valid: bool) {
if a == nil { return }
a := a
@@ -1348,9 +1405,11 @@ ne :: not_equal
DEFAULT_EQUAL_MAX_RECURSION_LEVEL :: 32
+@(require_results)
not_equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
return !equal(a, b, including_indirect_array_recursion, recursion_level)
}
+@(require_results)
equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_level := 0) -> bool {
if a == nil && b == nil {
return true
@@ -1387,7 +1446,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
switch v in t.variant {
case Type_Info_Named:
unreachable()
- case Type_Info_Tuple:
+ case Type_Info_Parameters:
unreachable()
case Type_Info_Any:
if !including_indirect_array_recursion {
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index f53b18e0d..cad9b1f66 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -3,17 +3,16 @@ package reflect
import "core:io"
import "core:strings"
+@(require_results)
are_types_identical :: proc(a, b: ^Type_Info) -> bool {
if a == b {
return true
}
- if (a == nil && b != nil) ||
- (a != nil && b == nil) {
+ if a == nil || b == nil {
return false
}
-
switch {
case a.size != b.size, a.align != b.align:
return false
@@ -102,8 +101,8 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
y := b.variant.(Type_Info_Slice) or_return
return are_types_identical(x.elem, y.elem)
- case Type_Info_Tuple:
- y := b.variant.(Type_Info_Tuple) or_return
+ case Type_Info_Parameters:
+ y := b.variant.(Type_Info_Parameters) or_return
if len(x.types) != len(y.types) { return false }
for _, i in x.types {
xt, yt := x.types[i], y.types[i]
@@ -180,6 +179,7 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
return false
}
+@(require_results)
is_signed :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch i in type_info_base(info).variant {
@@ -188,6 +188,7 @@ is_signed :: proc(info: ^Type_Info) -> bool {
}
return false
}
+@(require_results)
is_unsigned :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch i in type_info_base(info).variant {
@@ -197,6 +198,7 @@ is_unsigned :: proc(info: ^Type_Info) -> bool {
return false
}
+@(require_results)
is_byte :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch i in type_info_base(info).variant {
@@ -206,66 +208,79 @@ is_byte :: proc(info: ^Type_Info) -> bool {
}
+@(require_results)
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Integer)
return ok
}
+@(require_results)
is_rune :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Rune)
return ok
}
+@(require_results)
is_float :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Float)
return ok
}
+@(require_results)
is_complex :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Complex)
return ok
}
+@(require_results)
is_quaternion :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Quaternion)
return ok
}
+@(require_results)
is_any :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Any)
return ok
}
+@(require_results)
is_string :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_String)
return ok
}
+@(require_results)
is_cstring :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
v, ok := type_info_base(info).variant.(Type_Info_String)
return ok && v.is_cstring
}
+@(require_results)
is_boolean :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Boolean)
return ok
}
+@(require_results)
is_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Pointer)
return ok
}
+@(require_results)
is_multi_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Multi_Pointer)
return ok
}
+@(require_results)
is_soa_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Soa_Pointer)
return ok
}
+@(require_results)
is_pointer_internally :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
#partial switch v in info.variant {
@@ -277,76 +292,97 @@ is_pointer_internally :: proc(info: ^Type_Info) -> bool {
}
return false
}
+@(require_results)
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Procedure)
return ok
}
+@(require_results)
is_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Array)
return ok
}
+@(require_results)
is_enumerated_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Enumerated_Array)
return ok
}
+@(require_results)
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Dynamic_Array)
return ok
}
+@(require_results)
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Map)
return ok
}
+@(require_results)
is_bit_set :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Bit_Set)
return ok
}
+@(require_results)
is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Slice)
return ok
}
-is_tuple :: proc(info: ^Type_Info) -> bool {
+@(require_results)
+is_parameters :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
- _, ok := type_info_base(info).variant.(Type_Info_Tuple)
+ _, ok := type_info_base(info).variant.(Type_Info_Parameters)
return ok
}
+@(require_results, deprecated="prefer is_parameters")
+is_tuple :: proc(info: ^Type_Info) -> bool {
+ if info == nil { return false }
+ _, ok := type_info_base(info).variant.(Type_Info_Parameters)
+ return ok
+}
+@(require_results)
is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
s, ok := type_info_base(info).variant.(Type_Info_Struct)
return ok && !s.is_raw_union
}
+@(require_results)
is_raw_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
s, ok := type_info_base(info).variant.(Type_Info_Struct)
return ok && s.is_raw_union
}
+@(require_results)
is_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Union)
return ok
}
+@(require_results)
is_enum :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Enum)
return ok
}
+@(require_results)
is_simd_vector :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
return ok
}
+@(require_results)
is_relative_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
return ok
}
+@(require_results)
is_relative_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Relative_Slice)
@@ -460,7 +496,7 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
if info.params == nil {
io.write_string(w, "()", &n) or_return
} else {
- t := info.params.variant.(Type_Info_Tuple)
+ t := info.params.variant.(Type_Info_Parameters)
io.write_string(w, "(", &n) or_return
for t, i in t.types {
if i > 0 {
@@ -474,7 +510,7 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
io.write_string(w, " -> ", &n) or_return
write_type(w, info.results, &n) or_return
}
- case Type_Info_Tuple:
+ case Type_Info_Parameters:
count := len(info.names)
if count != 1 {
io.write_string(w, "(", &n) or_return
@@ -527,7 +563,7 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
case .None: // Ignore
case .Fixed:
io.write_string(w, "#soa[", &n) or_return
- io.write_i64(w, i64(info.soa_len), 10 &n) or_return
+ io.write_i64(w, i64(info.soa_len), 10) or_return
io.write_byte(w, ']', &n) or_return
write_type(w, info.soa_base_type, &n) or_return
return
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index a74bf4285..83504c9ee 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -83,8 +83,8 @@ Type_Info_Multi_Pointer :: struct {
elem: ^Type_Info,
}
Type_Info_Procedure :: struct {
- params: ^Type_Info, // Type_Info_Tuple
- results: ^Type_Info, // Type_Info_Tuple
+ params: ^Type_Info, // Type_Info_Parameters
+ results: ^Type_Info, // Type_Info_Parameters
variadic: bool,
convention: Calling_Convention,
}
@@ -104,10 +104,12 @@ Type_Info_Enumerated_Array :: struct {
}
Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}
Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int}
-Type_Info_Tuple :: struct { // Only used for procedures parameters and results
+
+Type_Info_Parameters :: struct { // Only used for procedures parameters and results
types: []^Type_Info,
names: []string,
}
+Type_Info_Tuple :: Type_Info_Parameters // Will be removed eventually
Type_Info_Struct :: struct {
types: []^Type_Info,
@@ -117,6 +119,7 @@ Type_Info_Struct :: struct {
tags: []string,
is_packed: bool,
is_raw_union: bool,
+ is_no_copy: bool,
custom_align: bool,
equal: Equal_Proc, // set only when the struct has .Comparable set but does not have .Simple_Compare set
@@ -208,7 +211,7 @@ Type_Info :: struct {
Type_Info_Enumerated_Array,
Type_Info_Dynamic_Array,
Type_Info_Slice,
- Type_Info_Tuple,
+ Type_Info_Parameters,
Type_Info_Struct,
Type_Info_Union,
Type_Info_Enum,
@@ -422,7 +425,7 @@ Raw_Map :: struct {
// 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
+ len: uintptr, // 8-bytes on 64-bits, 4-bytes on 32-bits
allocator: Allocator, // 16-bytes on 64-bits, 8-bytes on 32-bits
}
@@ -468,7 +471,7 @@ Odin_OS_Type :: type_of(ODIN_OS)
arm32,
arm64,
wasm32,
- wasm64,
+ wasm64p32,
}
*/
Odin_Arch_Type :: type_of(ODIN_ARCH)
@@ -505,11 +508,8 @@ Odin_Endian_Type :: type_of(ODIN_ENDIAN)
foreign {
@(link_name="__$startup_runtime")
_startup_runtime :: proc "odin" () ---
-}
-
-@(link_name="__$cleanup_runtime")
-_cleanup_runtime :: proc() {
- default_temp_allocator_destroy(&global_default_temp_allocator_data)
+ @(link_name="__$cleanup_runtime")
+ _cleanup_runtime :: proc "odin" () ---
}
_cleanup_runtime_contextless :: proc "contextless" () {
@@ -566,7 +566,7 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
return &type_table[n]
}
-when !ODIN_DISALLOW_RTTI {
+when !ODIN_NO_RTTI {
typeid_base :: proc "contextless" (id: typeid) -> typeid {
ti := type_info_of(id)
ti = type_info_base(ti)
@@ -622,7 +622,9 @@ __init_context :: proc "contextless" (c: ^Context) {
c.allocator.data = nil
c.temp_allocator.procedure = default_temp_allocator_proc
- c.temp_allocator.data = &global_default_temp_allocator_data
+ when !NO_DEFAULT_TEMP_ALLOCATOR {
+ c.temp_allocator.data = &global_default_temp_allocator_data
+ }
when !ODIN_DISABLE_ASSERT {
c.assertion_failure_proc = default_assertion_failure_proc
diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin
index cbcb162a8..9f2899bcc 100644
--- a/core/runtime/core_builtin.odin
+++ b/core/runtime/core_builtin.odin
@@ -6,7 +6,7 @@ import "core:intrinsics"
Maybe :: union($T: typeid) {T}
-@builtin
+@(builtin, require_results)
container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
where intrinsics.type_has_field(T, field_name),
intrinsics.type_field_type(T, field_name) == Field_Type {
@@ -15,14 +15,23 @@ container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: type
}
-@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
+when !NO_DEFAULT_TEMP_ALLOCATOR {
+ @thread_local global_default_temp_allocator_data: Default_Temp_Allocator
+}
-@builtin
+@(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
- default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator)
+ when !NO_DEFAULT_TEMP_ALLOCATOR {
+ default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator)
+ }
}
+// `copy_slice` is a built-in procedure that copies elements from a source slice `src` to a destination slice `dst`.
+// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
+// of len(src) and len(dst).
+//
+// Prefer the procedure group `copy`.
@builtin
copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
n := max(0, min(len(dst), len(src)))
@@ -31,6 +40,11 @@ copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
}
return n
}
+// `copy_from_string` is a built-in procedure that copies elements from a source slice `src` to a destination string `dst`.
+// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
+// of len(src) and len(dst).
+//
+// Prefer the procedure group `copy`.
@builtin
copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
n := max(0, min(len(dst), len(src)))
@@ -39,11 +53,20 @@ copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int
}
return n
}
+// `copy` is a built-in procedure that copies elements from a source slice `src` to a destination slice/string `dst`.
+// The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
+// of len(src) and len(dst).
@builtin
copy :: proc{copy_slice, copy_from_string}
+// `unordered_remove` removed the element at the specified `index`. It does so by replacing the current end value
+// with the old value, and reducing the length of the dynamic array by 1.
+//
+// Note: This is an O(1) operation.
+// Note: If you the elements to remain in their order, use `ordered_remove`.
+// Note: If the index is out of bounds, this procedure will panic.
@builtin
unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
bounds_check_error_loc(loc, index, len(array))
@@ -53,7 +76,11 @@ unordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_loca
}
(^Raw_Dynamic_Array)(array).len -= 1
}
-
+// `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
+//
+// Note: This is an O(N) operation.
+// Note: If you the elements do not have to remain in their order, prefer `unordered_remove`.
+// Note: If the index is out of bounds, this procedure will panic.
@builtin
ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_location) #no_bounds_check {
bounds_check_error_loc(loc, index, len(array))
@@ -63,6 +90,10 @@ ordered_remove :: proc(array: ^$D/[dynamic]$T, index: int, loc := #caller_locati
(^Raw_Dynamic_Array)(array).len -= 1
}
+// `remove_range` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements.
+//
+// Note: This is an O(N) operation.
+// Note: If the range is out of bounds, this procedure will panic.
@builtin
remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_location) #no_bounds_check {
slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
@@ -76,15 +107,20 @@ remove_range :: proc(array: ^$D/[dynamic]$T, lo, hi: int, loc := #caller_locatio
}
+// `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
+//
+// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
@builtin
pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
- assert(len(array) > 0, "", loc)
+ assert(len(array) > 0, loc=loc)
res = array[len(array)-1]
(^Raw_Dynamic_Array)(array).len -= 1
return res
}
+// `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
+// If the operation is not possible, it will return false.
@builtin
pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
if len(array) == 0 {
@@ -95,9 +131,12 @@ pop_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check
return
}
+// `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1.
+//
+// Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
@builtin
pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
- assert(len(array) > 0, "", loc)
+ assert(len(array) > 0, loc=loc)
res = array[0]
if len(array) > 1 {
copy(array[0:], array[1:])
@@ -106,6 +145,8 @@ pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #
return res
}
+// `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
+// If the operation is not possible, it will return false.
@builtin
pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
if len(array) == 0 {
@@ -120,12 +161,15 @@ pop_front_safe :: proc(array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_
}
+// `clear` will set the length of a passed dynamic array or map to `0`
@builtin
clear :: proc{clear_dynamic_array, clear_map}
+// `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
@builtin
reserve :: proc{reserve_dynamic_array, reserve_map}
+// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
@builtin
resize :: proc{resize_dynamic_array}
@@ -133,36 +177,56 @@ resize :: proc{resize_dynamic_array}
@builtin
shrink :: proc{shrink_dynamic_array, shrink_map}
+// `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation.
@builtin
free :: proc{mem_free}
+// `free_all` will try to free/reset all of the memory of the given `allocator` if the allocator supports this operation.
@builtin
free_all :: proc{mem_free_all}
+// `delete_string` will try to free the underlying data of the passed string, with the given `allocator` if the allocator supports this operation.
+//
+// Note: Prefer the procedure group `delete`.
@builtin
delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free_with_size(raw_data(str), len(str), allocator, loc)
}
+// `delete_cstring` will try to free the underlying data of the passed string, with the given `allocator` if the allocator supports this operation.
+//
+// Note: Prefer the procedure group `delete`.
@builtin
delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free((^byte)(str), allocator, loc)
}
+// `delete_dynamic_array` will try to free the underlying data of the passed dynamic array, with the given `allocator` if the allocator supports this operation.
+//
+// Note: Prefer the procedure group `delete`.
@builtin
delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
}
+// `delete_slice` will try to free the underlying data of the passed sliced, with the given `allocator` if the allocator supports this operation.
+//
+// Note: Prefer the procedure group `delete`.
@builtin
delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
}
+// `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation.
+//
+// Note: Prefer the procedure group `delete`.
@builtin
delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
}
+// `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation.
+//
+// Note: Prefer `delete` over the specific `delete_*` procedures where possible.
@builtin
delete :: proc{
delete_string,
@@ -175,17 +239,18 @@ delete :: proc{
// The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
// return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
-@builtin
+@(builtin, require_results)
new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (^T, Allocator_Error) #optional_allocator_error {
return new_aligned(T, align_of(T), allocator, loc)
}
+@(require_results)
new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
data := mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return
t = (^T)(raw_data(data))
return
}
-@builtin
+@(builtin, require_results)
new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
t_data := mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return
t = (^T)(raw_data(t_data))
@@ -197,6 +262,7 @@ new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_locat
DEFAULT_RESERVE_CAPACITY :: 16
+@(require_results)
make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
make_slice_error_loc(loc, len)
data, err := mem_alloc_bytes(size_of(E)*len, alignment, allocator, loc)
@@ -207,19 +273,35 @@ make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocat
return transmute(T)s, err
}
-@(builtin)
+// `make_slice` allocates and initializes a slice. Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
return make_aligned(T, len, align_of(E), allocator, loc)
}
-@(builtin)
+// `make_dynamic_array` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
return make_dynamic_array_len_cap(T, 0, DEFAULT_RESERVE_CAPACITY, allocator, loc)
}
-@(builtin)
+// `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (T, Allocator_Error) #optional_allocator_error {
return make_dynamic_array_len_cap(T, len, len, allocator, loc)
}
-@(builtin)
+// `make_dynamic_array_len_cap` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
make_dynamic_array_error_loc(loc, len, cap)
data := mem_alloc_bytes(size_of(E)*cap, align_of(E), allocator, loc) or_return
@@ -230,16 +312,25 @@ make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #a
array = transmute(T)s
return
}
-@(builtin)
-make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1< T {
+// `make_map` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
+make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1< (m: T, err: Allocator_Error) #optional_allocator_error {
make_map_expr_error_loc(loc, capacity)
context.allocator = allocator
- m: T
- reserve_map(&m, capacity, loc)
- return m
+ err = reserve_map(&m, capacity, loc)
+ return
}
-@(builtin)
+// `make_multi_pointer` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// This is "similar" to doing `raw_data(make([]E, len, allocator))`.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_allocator_error {
make_slice_error_loc(loc, len)
data := mem_alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
@@ -251,8 +342,9 @@ make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := con
}
-// The make built-in procedure allocates and initializes a value of type slice, dynamic array, or map (only)
-// Similar to new, the first argument is a type, not a value. Unlike new, make's return type is the same as the
+// `make` built-in procedure allocates and initializes a value of type slice, dynamic array, map, or multi-pointer (only).
+//
+// Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
// type of its argument, not a pointer to it.
// Make uses the specified allocator, default is context.allocator, default is context.allocator
@builtin
@@ -267,6 +359,9 @@ make :: proc{
+// `clear_map` will set the length of a passed map to `0`
+//
+// Note: Prefer the procedure group `clear`
@builtin
clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
if m == nil {
@@ -275,21 +370,21 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
map_clear_dynamic((^Raw_Map)(m), map_info(T))
}
+// `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`).
+//
+// Note: Prefer the procedure group `reserve`
@builtin
-reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) {
- if m != nil {
- __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
- }
+reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error {
+ return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
}
-/*
- Shrinks the capacity of a map down to the current length.
-*/
+// Shrinks the capacity of a map down to the current length.
+//
+// Note: Prefer the procedure group `shrink`
@builtin
-shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool) {
+shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
if m != nil {
- err := map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
- did_shrink = err == nil
+ return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
}
return
}
@@ -312,78 +407,85 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
@builtin
-append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
+append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
- return 0
+ return 0, nil
}
when size_of(E) == 0 {
+ array := (^Raw_Dynamic_Array)(array)
array.len += 1
- return 1
+ return 1, nil
} else {
if cap(array) < len(array)+1 {
cap := 2 * cap(array) + max(8, 1)
- _ = reserve(array, cap, loc)
+ err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
}
if cap(array)-len(array) > 0 {
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
- assert(condition=data != nil, loc=loc)
+ assert(data != nil, loc=loc)
data[a.len] = arg
}
a.len += 1
- return 1
+ return 1, err
}
- return 0
+ return 0, err
}
}
@builtin
-append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
+append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
- return 0
+ return 0, nil
}
arg_len := len(args)
if arg_len <= 0 {
- return 0
+ return 0, nil
}
when size_of(E) == 0 {
+ array := (^Raw_Dynamic_Array)(array)
array.len += arg_len
- return arg_len
+ return arg_len, nil
} else {
if cap(array) < len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len)
- _ = reserve(array, cap, loc)
+ err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
}
arg_len = min(cap(array)-len(array), arg_len)
if arg_len > 0 {
a := (^Raw_Dynamic_Array)(array)
when size_of(E) != 0 {
data := ([^]E)(a.data)
- assert(condition=data != nil, loc=loc)
+ assert(data != nil, loc=loc)
intrinsics.mem_copy(&data[a.len], raw_data(args), size_of(E) * arg_len)
}
a.len += arg_len
}
- return arg_len
+ return arg_len, err
}
}
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
@builtin
-append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> int {
+append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
args := transmute([]E)arg
- return append_elems(array=array, args=args, loc=loc)
+ return append_elems(array, ..args, loc=loc)
}
// The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
@builtin
-append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int) {
+append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
+ n_arg: int
for arg in args {
- n += append(array = array, args = transmute([]E)(arg), loc = loc)
+ n_arg, err = append(array, ..transmute([]E)(arg), loc=loc)
+ n += n_arg
+ if err != nil {
+ return
+ }
}
return
}
@@ -393,18 +495,18 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
@builtin
-append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> int {
+append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
- return 0
+ return 0, nil
}
prev_len := len(array)
- resize(array, len(array)+1)
- return len(array)-prev_len
+ resize(array, len(array)+1, loc) or_return
+ return len(array)-prev_len, nil
}
@builtin
-inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if array == nil {
return
}
@@ -412,18 +514,17 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
m :: 1
new_size := n + m
- if resize(array, new_size, loc) {
- when size_of(E) != 0 {
- copy(array[index + m:], array[index:])
- array[index] = arg
- }
- ok = true
+ resize(array, new_size, loc) or_return
+ when size_of(E) != 0 {
+ copy(array[index + m:], array[index:])
+ array[index] = arg
}
+ ok = true
return
}
@builtin
-inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if array == nil {
return
}
@@ -436,18 +537,17 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
m := len(args)
new_size := n + m
- if resize(array, new_size, loc) {
- when size_of(E) != 0 {
- copy(array[index + m:], array[index:])
- copy(array[index:], args)
- }
- ok = true
+ resize(array, new_size, loc) or_return
+ when size_of(E) != 0 {
+ copy(array[index + m:], array[index:])
+ copy(array[index:], args)
}
+ ok = true
return
}
@builtin
-inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if array == nil {
return
}
@@ -460,11 +560,10 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
m := len(arg)
new_size := n + m
- if resize(array, new_size, loc) {
- copy(array[index+m:], array[index:])
- copy(array[index:], arg)
- ok = true
- }
+ resize(array, new_size, loc) or_return
+ copy(array[index+m:], array[index:])
+ copy(array[index:], arg)
+ ok = true
return
}
@@ -473,11 +572,12 @@ inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
@builtin
-assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if index < len(array) {
array[index] = arg
ok = true
- } else if resize(array, index+1, loc) {
+ } else {
+ resize(array, index+1, loc) or_return
array[index] = arg
ok = true
}
@@ -486,11 +586,12 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
@builtin
-assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if index+len(args) < len(array) {
copy(array[index:], args)
ok = true
- } else if resize(array, index+1+len(args), loc) {
+ } else {
+ resize(array, index+1+len(args), loc) or_return
copy(array[index:], args)
ok = true
}
@@ -499,13 +600,14 @@ assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
@builtin
-assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool) #no_bounds_check {
+assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
if len(args) == 0 {
ok = true
} else if index+len(args) < len(array) {
copy(array[index:], args)
ok = true
- } else if resize(array, index+1+len(args), loc) {
+ } else {
+ resize(array, index+1+len(args), loc) or_return
copy(array[index:], args)
ok = true
}
@@ -517,6 +619,9 @@ assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
+// `clear_dynamic_array` will set the length of a passed dynamic array to `0`
+//
+// Note: Prefer the procedure group `clear`.
@builtin
clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
if array != nil {
@@ -524,15 +629,18 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
}
}
+// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
+//
+// Note: Prefer the procedure group `reserve`.
@builtin
-reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
if array == nil {
- return false
+ return nil
}
a := (^Raw_Dynamic_Array)(array)
if capacity <= a.cap {
- return true
+ return nil
}
if a.allocator.procedure == nil {
@@ -544,26 +652,29 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
new_size := capacity * size_of(E)
allocator := a.allocator
- new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
- if new_data == nil || err != nil {
- return false
+ new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
+ if new_data == nil && new_size > 0 {
+ return .Out_Of_Memory
}
a.data = raw_data(new_data)
a.cap = capacity
- return true
+ return nil
}
+// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
+//
+// Note: Prefer the procedure group `resize`
@builtin
-resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool {
+resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
if array == nil {
- return false
+ return nil
}
a := (^Raw_Dynamic_Array)(array)
if length <= a.cap {
a.len = max(length, 0)
- return true
+ return nil
}
if a.allocator.procedure == nil {
@@ -575,15 +686,15 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
new_size := length * size_of(E)
allocator := a.allocator
- new_data, err := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc)
- if new_data == nil || err != nil {
- return false
+ new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
+ if new_data == nil && new_size > 0 {
+ return .Out_Of_Memory
}
a.data = raw_data(new_data)
a.len = length
a.cap = length
- return true
+ return nil
}
/*
@@ -594,8 +705,10 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
Returns false if `cap(array) < new_cap`, or the allocator report failure.
If `len(array) < new_cap`, then `len(array)` will be left unchanged.
+
+ Note: Prefer the procedure group `shrink`
*/
-shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool) {
+shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
if array == nil {
return
}
@@ -615,15 +728,12 @@ 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 := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc)
- if err != nil {
- return
- }
+ new_data := mem_resize(a.data, old_size, new_size, align_of(E), a.allocator, loc) or_return
a.data = raw_data(new_data)
a.len = min(new_cap, a.len)
a.cap = new_cap
- return true
+ return true, nil
}
@builtin
diff --git a/core/runtime/core_builtin_matrix.odin b/core/runtime/core_builtin_matrix.odin
index 53589587c..7d60d625c 100644
--- a/core/runtime/core_builtin_matrix.odin
+++ b/core/runtime/core_builtin_matrix.odin
@@ -37,12 +37,12 @@ inverse :: proc{
matrix4x4_inverse,
}
-@(builtin)
+@(builtin, require_results)
hermitian_adjoint :: proc "contextless" (m: $M/matrix[$N, N]$T) -> M where intrinsics.type_is_complex(T), N >= 1 {
return conj(transpose(m))
}
-@(builtin)
+@(builtin, require_results)
matrix_trace :: proc "contextless" (m: $M/matrix[$N, N]$T) -> (trace: T) {
for i in 0.. (trace: T) {
return
}
-@(builtin)
+@(builtin, require_results)
matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, row, column: int) -> (minor: T) where N > 1 {
K :: N-1
cut_down: matrix[K, K]T
@@ -66,23 +66,23 @@ matrix_minor :: proc "contextless" (m: $M/matrix[$N, N]$T, row, column: int) ->
-@(builtin)
+@(builtin, require_results)
matrix1x1_determinant :: proc "contextless" (m: $M/matrix[1, 1]$T) -> (det: T) {
return m[0, 0]
}
-@(builtin)
+@(builtin, require_results)
matrix2x2_determinant :: proc "contextless" (m: $M/matrix[2, 2]$T) -> (det: T) {
return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
-@(builtin)
+@(builtin, require_results)
matrix3x3_determinant :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (det: T) {
a := +m[0, 0] * (m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1])
b := -m[0, 1] * (m[1, 0] * m[2, 2] - m[1, 2] * m[2, 0])
c := +m[0, 2] * (m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0])
return a + b + c
}
-@(builtin)
+@(builtin, require_results)
matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
a := adjugate(m)
#no_bounds_check for i in 0..<4 {
@@ -94,13 +94,13 @@ matrix4x4_determinant :: proc "contextless" (m: $M/matrix[4, 4]$T) -> (det: T) {
-@(builtin)
+@(builtin, require_results)
matrix1x1_adjugate :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
y = x
return
}
-@(builtin)
+@(builtin, require_results)
matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
y[0, 0] = +x[1, 1]
y[0, 1] = -x[1, 0]
@@ -109,7 +109,7 @@ matrix2x2_adjugate :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
return
}
-@(builtin)
+@(builtin, require_results)
matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
y[0, 0] = +(m[1, 1] * m[2, 2] - m[2, 1] * m[1, 2])
y[0, 1] = -(m[1, 0] * m[2, 2] - m[2, 0] * m[1, 2])
@@ -124,7 +124,7 @@ matrix3x3_adjugate :: proc "contextless" (m: $M/matrix[3, 3]$T) -> (y: M) {
}
-@(builtin)
+@(builtin, require_results)
matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
for i in 0..<4 {
for j in 0..<4 {
@@ -135,13 +135,13 @@ matrix4x4_adjugate :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) {
return
}
-@(builtin)
+@(builtin, require_results)
matrix1x1_inverse_transpose :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
y[0, 0] = 1/x[0, 0]
return
}
-@(builtin)
+@(builtin, require_results)
matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
@@ -159,7 +159,7 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
return
}
-@(builtin)
+@(builtin, require_results)
matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
d := determinant(x)
@@ -180,7 +180,7 @@ matrix3x3_inverse_transpose :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y:
return
}
-@(builtin)
+@(builtin, require_results)
matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
d: T
@@ -204,13 +204,13 @@ matrix4x4_inverse_transpose :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y:
return
}
-@(builtin)
+@(builtin, require_results)
matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
y[0, 0] = 1/x[0, 0]
return
}
-@(builtin)
+@(builtin, require_results)
matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
@@ -228,7 +228,7 @@ matrix2x2_inverse :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y: M) {
return
}
-@(builtin)
+@(builtin, require_results)
matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
d := determinant(x)
@@ -249,7 +249,7 @@ matrix3x3_inverse :: proc "contextless" (x: $M/matrix[3, 3]$T) -> (y: M) #no_bou
return
}
-@(builtin)
+@(builtin, require_results)
matrix4x4_inverse :: proc "contextless" (x: $M/matrix[4, 4]$T) -> (y: M) #no_bounds_check {
a := adjugate(x)
d: T
diff --git a/core/runtime/core_builtin_soa.odin b/core/runtime/core_builtin_soa.odin
index a6b356642..ee2a5f2d0 100644
--- a/core/runtime/core_builtin_soa.odin
+++ b/core/runtime/core_builtin_soa.odin
@@ -50,6 +50,7 @@ Raw_SOA_Footer_Dynamic_Array :: struct {
allocator: Allocator,
}
+@(builtin, require_results)
raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Slice) {
if array == nil {
return nil
@@ -58,6 +59,7 @@ raw_soa_footer_slice :: proc(array: ^$T/#soa[]$E) -> (footer: ^Raw_SOA_Footer_Sl
footer = (^Raw_SOA_Footer_Slice)(uintptr(array) + field_count*size_of(rawptr))
return
}
+@(builtin, require_results)
raw_soa_footer_dynamic_array :: proc(array: ^$T/#soa[dynamic]$E) -> (footer: ^Raw_SOA_Footer_Dynamic_Array) {
if array == nil {
return nil
@@ -78,7 +80,7 @@ raw_soa_footer :: proc{
-@builtin
+@(builtin, require_results)
make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
if length <= 0 {
return
@@ -137,32 +139,31 @@ make_soa_aligned :: proc($T: typeid/#soa[]$E, length: int, alignment: int, alloc
return
}
-@builtin
+@(builtin, require_results)
make_soa_slice :: proc($T: typeid/#soa[]$E, length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
return make_soa_aligned(T, length, align_of(E), allocator, loc)
}
-@builtin
-make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+@(builtin, require_results)
+make_soa_dynamic_array :: proc($T: typeid/#soa[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
context.allocator = allocator
- reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc)
- return
+ reserve_soa(&array, DEFAULT_RESERVE_CAPACITY, loc) or_return
+ return array, nil
}
-@builtin
-make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, auto_cast length: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+@(builtin, require_results)
+make_soa_dynamic_array_len :: proc($T: typeid/#soa[dynamic]$E, #any_int length: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
context.allocator = allocator
- resize_soa(&array, length, loc)
- return
+ resize_soa(&array, length, loc) or_return
+ return array, nil
}
-@builtin
-make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, auto_cast length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T) {
+@(builtin, require_results)
+make_soa_dynamic_array_len_cap :: proc($T: typeid/#soa[dynamic]$E, #any_int length, capacity: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
context.allocator = allocator
- if reserve_soa(&array, capacity, loc) {
- resize_soa(&array, length, loc)
- }
- return
+ reserve_soa(&array, capacity, loc) or_return
+ resize_soa(&array, length, loc) or_return
+ return array, nil
}
@@ -176,27 +177,25 @@ make_soa :: proc{
@builtin
-resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> bool {
+resize_soa :: proc(array: ^$T/#soa[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
if array == nil {
- return false
- }
- if !reserve_soa(array, length, loc) {
- return false
+ return nil
}
+ reserve_soa(array, length, loc) or_return
footer := raw_soa_footer(array)
footer.len = length
- return true
+ return nil
}
@builtin
-reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> bool {
+reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
if array == nil {
- return false
+ return nil
}
old_cap := cap(array)
if capacity <= old_cap {
- return true
+ return nil
}
if array.allocator.procedure == nil {
@@ -207,7 +206,7 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
footer := raw_soa_footer(array)
if size_of(E) == 0 {
footer.cap = capacity
- return true
+ return nil
}
ti := type_info_of(typeid_of(T))
@@ -238,13 +237,10 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
old_data := (^rawptr)(array)^
- new_bytes, err := array.allocator.procedure(
+ new_bytes := array.allocator.procedure(
array.allocator.data, .Alloc, new_size, max_align,
nil, old_size, loc,
- )
- if new_bytes == nil || err != nil {
- return false
- }
+ ) or_return
new_data := raw_data(new_bytes)
@@ -269,31 +265,28 @@ reserve_soa :: proc(array: ^$T/#soa[dynamic]$E, capacity: int, loc := #caller_lo
new_offset += type.size * capacity
}
- _, err = array.allocator.procedure(
+ array.allocator.procedure(
array.allocator.data, .Free, 0, max_align,
old_data, old_size, loc,
- )
+ ) or_return
- return true
+ return nil
}
@builtin
-append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) {
+append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
- return
+ return 0, nil
}
- arg_len := 1
-
- if cap(array) <= len(array)+arg_len {
- cap := 2 * cap(array) + max(8, arg_len)
- _ = reserve_soa(array, cap, loc)
+ if cap(array) <= len(array) + 1 {
+ cap := 2 * cap(array) + 8
+ err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
}
- arg_len = min(cap(array)-len(array), arg_len)
footer := raw_soa_footer(array)
- if size_of(E) > 0 && arg_len > 0 {
+ if size_of(E) > 0 && cap(array)-len(array) > 0 {
ti := type_info_of(typeid_of(T))
ti = type_info_base(ti)
si := &ti.variant.(Type_Info_Struct)
@@ -326,12 +319,14 @@ append_soa_elem :: proc(array: ^$T/#soa[dynamic]$E, arg: E, loc := #caller_locat
soa_offset += type.size * cap(array)
item_offset += type.size
}
+ footer.len += 1
+ return 1, err
}
- footer.len += arg_len
+ return 0, err
}
@builtin
-append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) {
+append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
if array == nil {
return
}
@@ -343,7 +338,7 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
if cap(array) <= len(array)+arg_len {
cap := 2 * cap(array) + max(8, arg_len)
- _ = reserve_soa(array, cap, loc)
+ err = reserve_soa(array, cap, loc) // do not 'or_return' here as it could be a partial success
}
arg_len = min(cap(array)-len(array), arg_len)
@@ -380,8 +375,8 @@ append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, args: ..E, loc := #caller_l
item_offset += type.size
}
}
-
footer.len += arg_len
+ return arg_len, err
}
@@ -393,21 +388,23 @@ append_soa :: proc{
}
-delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) {
+delete_soa_slice :: proc(array: $T/#soa[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
when intrinsics.type_struct_field_count(E) != 0 {
array := array
ptr := (^rawptr)(&array)^
- free(ptr, allocator, loc)
+ free(ptr, allocator, loc) or_return
}
+ return nil
}
-delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) {
+delete_soa_dynamic_array :: proc(array: $T/#soa[dynamic]$E, loc := #caller_location) -> Allocator_Error {
when intrinsics.type_struct_field_count(E) != 0 {
array := array
ptr := (^rawptr)(&array)^
footer := raw_soa_footer(&array)
- free(ptr, footer.allocator, loc)
+ free(ptr, footer.allocator, loc) or_return
}
+ return nil
}
diff --git a/core/runtime/default_allocators_arena.odin b/core/runtime/default_allocators_arena.odin
new file mode 100644
index 000000000..1c36c4f7c
--- /dev/null
+++ b/core/runtime/default_allocators_arena.odin
@@ -0,0 +1,304 @@
+package runtime
+
+import "core:intrinsics"
+
+DEFAULT_ARENA_GROWING_MINIMUM_BLOCK_SIZE :: uint(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE)
+
+Memory_Block :: struct {
+ prev: ^Memory_Block,
+ allocator: Allocator,
+ base: [^]byte,
+ used: uint,
+ capacity: uint,
+}
+
+Arena :: struct {
+ backing_allocator: Allocator,
+ curr_block: ^Memory_Block,
+ total_used: uint,
+ total_capacity: uint,
+ minimum_block_size: uint,
+ temp_count: uint,
+}
+
+@(private, require_results)
+safe_add :: #force_inline proc "contextless" (x, y: uint) -> (uint, bool) {
+ z, did_overflow := intrinsics.overflow_add(x, y)
+ return z, !did_overflow
+}
+
+@(require_results)
+memory_block_alloc :: proc(allocator: Allocator, capacity: uint, loc := #caller_location) -> (block: ^Memory_Block, err: Allocator_Error) {
+ total_size := uint(capacity + size_of(Memory_Block))
+ base_offset := uintptr(size_of(Memory_Block))
+
+ min_alignment: int = max(16, align_of(Memory_Block))
+ data := mem_alloc(int(total_size), min_alignment, allocator, loc) or_return
+ block = (^Memory_Block)(raw_data(data))
+ end := uintptr(raw_data(data)[len(data):])
+
+ block.allocator = allocator
+ block.base = ([^]byte)(uintptr(block) + base_offset)
+ block.capacity = uint(end - uintptr(block.base))
+
+ // Should be zeroed
+ assert(block.used == 0)
+ assert(block.prev == nil)
+ return
+}
+
+memory_block_dealloc :: proc(block_to_free: ^Memory_Block, loc := #caller_location) {
+ if block_to_free != nil {
+ allocator := block_to_free.allocator
+ mem_free(block_to_free, allocator, loc)
+ }
+}
+
+@(require_results)
+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:])
+ mask := alignment-1
+ if ptr & mask != 0 {
+ alignment_offset = uint(alignment - (ptr & mask))
+ }
+ return alignment_offset
+
+ }
+ if block == nil {
+ return nil, .Out_Of_Memory
+ }
+ alignment_offset := calc_alignment_offset(block, uintptr(alignment))
+ size, size_ok := safe_add(min_size, alignment_offset)
+ if !size_ok {
+ err = .Out_Of_Memory
+ return
+ }
+
+ if to_be_used, ok := safe_add(block.used, size); !ok || to_be_used > block.capacity {
+ err = .Out_Of_Memory
+ return
+ }
+ data = block.base[block.used+alignment_offset:][:min_size]
+ block.used += size
+ return
+}
+
+@(require_results)
+arena_alloc :: proc(arena: ^Arena, size, alignment: uint, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
+ align_forward_uint :: proc "contextless" (ptr, align: uint) -> uint {
+ p := ptr
+ modulo := p & (align-1)
+ if modulo != 0 {
+ p += align - modulo
+ }
+ return p
+ }
+
+ assert(alignment & (alignment-1) == 0, "non-power of two alignment", loc)
+
+ size := size
+ if size == 0 {
+ return
+ }
+
+ if arena.curr_block == nil || (safe_add(arena.curr_block.used, size) or_else 0) > arena.curr_block.capacity {
+ size = 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)
+
+ if arena.backing_allocator.procedure == nil {
+ arena.backing_allocator = default_allocator()
+ }
+
+ new_block := memory_block_alloc(arena.backing_allocator, block_size, loc) or_return
+ new_block.prev = arena.curr_block
+ arena.curr_block = new_block
+ arena.total_capacity += new_block.capacity
+ }
+
+ 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
+ return
+}
+
+// `arena_init` will initialize the arena with a usuable block.
+// This procedure is not necessary to use the Arena as the default zero as `arena_alloc` will set things up if necessary
+@(require_results)
+arena_init :: proc(arena: ^Arena, size: uint, backing_allocator: Allocator, loc := #caller_location) -> Allocator_Error {
+ arena^ = {}
+ arena.backing_allocator = backing_allocator
+ arena.minimum_block_size = max(size, 1<<12) // minimum block size of 4 KiB
+ new_block := memory_block_alloc(arena.backing_allocator, arena.minimum_block_size, loc) or_return
+ arena.curr_block = new_block
+ arena.total_capacity += new_block.capacity
+ return nil
+}
+
+
+arena_free_last_memory_block :: proc(arena: ^Arena, loc := #caller_location) {
+ if free_block := arena.curr_block; free_block != nil {
+ arena.curr_block = free_block.prev
+
+ arena.total_capacity -= free_block.capacity
+ memory_block_dealloc(free_block, loc)
+ }
+}
+
+// `arena_free_all` will free all but the first memory block, and then reset the memory block
+arena_free_all :: proc(arena: ^Arena, loc := #caller_location) {
+ for arena.curr_block != nil && arena.curr_block.prev != nil {
+ arena_free_last_memory_block(arena, loc)
+ }
+
+ if arena.curr_block != nil {
+ intrinsics.mem_zero(arena.curr_block.base, arena.curr_block.used)
+ arena.curr_block.used = 0
+ }
+ arena.total_used = 0
+}
+
+arena_destroy :: proc(arena: ^Arena, loc := #caller_location) {
+ for arena.curr_block != nil {
+ free_block := arena.curr_block
+ arena.curr_block = free_block.prev
+
+ arena.total_capacity -= free_block.capacity
+ memory_block_dealloc(free_block, loc)
+ }
+ arena.total_used = 0
+ arena.total_capacity = 0
+}
+
+arena_allocator :: proc(arena: ^Arena) -> Allocator {
+ return Allocator{arena_allocator_proc, arena}
+}
+
+arena_allocator_proc :: proc(allocator_data: rawptr, mode: 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, location)
+ case .Free:
+ err = .Mode_Not_Implemented
+ case .Free_All:
+ arena_free_all(arena, location)
+ case .Resize:
+ old_data := ([^]byte)(old_memory)
+
+ switch {
+ case old_data == nil:
+ return arena_alloc(arena, size, alignment, location)
+ 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, location) or_return
+ if new_memory == nil {
+ return
+ }
+ copy(new_memory, old_data[:old_size])
+ return new_memory, nil
+ case .Query_Features:
+ set := (^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) {
+ if temp.arena == nil {
+ assert(temp.block == nil)
+ assert(temp.used == 0)
+ return
+ }
+ arena := temp.arena
+
+ if temp.block != nil {
+ 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_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.capacity-block.used)
+ intrinsics.mem_zero(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
+}
+
+// Ignore the use of a `arena_temp_begin` entirely
+arena_temp_ignore :: proc(temp: Arena_Temp, loc := #caller_location) {
+ assert(temp.arena != nil, "nil arena", loc)
+ arena := temp.arena
+
+ 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)
+}
diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin
index b71cd103a..c90f0388d 100644
--- a/core/runtime/default_temporary_allocator.odin
+++ b/core/runtime/default_temporary_allocator.odin
@@ -1,159 +1,38 @@
package runtime
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 4 * Megabyte)
+NO_DEFAULT_TEMP_ALLOCATOR: bool : ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
-
-when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+when NO_DEFAULT_TEMP_ALLOCATOR {
Default_Temp_Allocator :: struct {}
- default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}
+ default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {}
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {}
default_temp_allocator_proc :: nil_allocator_proc
+
+ @(require_results)
+ default_temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: Arena_Temp) {
+ return
+ }
+
+ default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
+ }
} else {
Default_Temp_Allocator :: struct {
- data: []byte,
- curr_offset: int,
- prev_allocation: rawptr,
- backup_allocator: Allocator,
- leaked_allocations: [dynamic][]byte,
+ arena: Arena,
}
- default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {
- s.data = make_aligned([]byte, size, 2*align_of(rawptr), backup_allocator)
- s.curr_offset = 0
- s.prev_allocation = nil
- s.backup_allocator = backup_allocator
- s.leaked_allocations.allocator = backup_allocator
+ default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backing_allocator := context.allocator) {
+ _ = arena_init(&s.arena, uint(size), backing_allocator)
}
default_temp_allocator_destroy :: proc(s: ^Default_Temp_Allocator) {
- if s == nil {
- return
+ if s != nil {
+ arena_destroy(&s.arena)
+ s^ = {}
}
- for ptr in s.leaked_allocations {
- free(raw_data(ptr), s.backup_allocator)
- }
- delete(s.leaked_allocations)
- delete(s.data, s.backup_allocator)
- s^ = {}
- }
-
- @(private)
- default_temp_allocator_alloc :: proc(s: ^Default_Temp_Allocator, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
- size := size
- size = align_forward_int(size, alignment)
-
- switch {
- case s.curr_offset+size <= len(s.data):
- start := uintptr(raw_data(s.data))
- ptr := start + uintptr(s.curr_offset)
- ptr = align_forward_uintptr(ptr, uintptr(alignment))
- mem_zero(rawptr(ptr), size)
-
- s.prev_allocation = rawptr(ptr)
- offset := int(ptr - start)
- s.curr_offset = offset + size
- return byte_slice(rawptr(ptr), size), .None
-
- case size <= len(s.data):
- start := uintptr(raw_data(s.data))
- ptr := align_forward_uintptr(start, uintptr(alignment))
- mem_zero(rawptr(ptr), size)
-
- s.prev_allocation = rawptr(ptr)
- offset := int(ptr - start)
- s.curr_offset = offset + size
- return byte_slice(rawptr(ptr), size), .None
- }
- a := s.backup_allocator
- if a.procedure == nil {
- a = context.allocator
- s.backup_allocator = a
- }
-
- data, err := mem_alloc_bytes(size, alignment, a, loc)
- if err != nil {
- return data, err
- }
- if s.leaked_allocations == nil {
- s.leaked_allocations = make([dynamic][]byte, a)
- }
- append(&s.leaked_allocations, data)
-
- // TODO(bill): Should leaks be notified about?
- if logger := context.logger; logger.lowest_level <= .Warning {
- if logger.procedure != nil {
- logger.procedure(logger.data, .Warning, "default temp allocator resorted to backup_allocator" , logger.options, loc)
- }
- }
-
- return data, .None
- }
-
- @(private)
- default_temp_allocator_free :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, loc := #caller_location) -> Allocator_Error {
- if old_memory == nil {
- return .None
- }
-
- start := uintptr(raw_data(s.data))
- end := start + uintptr(len(s.data))
- old_ptr := uintptr(old_memory)
-
- if s.prev_allocation == old_memory {
- s.curr_offset = int(uintptr(s.prev_allocation) - start)
- s.prev_allocation = nil
- return .None
- }
-
- if start <= old_ptr && old_ptr < end {
- // NOTE(bill): Cannot free this pointer but it is valid
- return .None
- }
-
- if len(s.leaked_allocations) != 0 {
- for data, i in s.leaked_allocations {
- ptr := raw_data(data)
- if ptr == old_memory {
- free(ptr, s.backup_allocator)
- ordered_remove(&s.leaked_allocations, i)
- return .None
- }
- }
- }
- return .Invalid_Pointer
- // panic("invalid pointer passed to default_temp_allocator");
- }
-
- @(private)
- default_temp_allocator_free_all :: proc(s: ^Default_Temp_Allocator, loc := #caller_location) {
- s.curr_offset = 0
- s.prev_allocation = nil
- for data in s.leaked_allocations {
- free(raw_data(data), s.backup_allocator)
- }
- clear(&s.leaked_allocations)
- }
-
- @(private)
- default_temp_allocator_resize :: proc(s: ^Default_Temp_Allocator, old_memory: rawptr, old_size, size, alignment: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
- begin := uintptr(raw_data(s.data))
- end := begin + uintptr(len(s.data))
- old_ptr := uintptr(old_memory)
- if old_memory == s.prev_allocation && old_ptr & uintptr(alignment)-1 == 0 {
- if old_ptr+uintptr(size) < end {
- s.curr_offset = int(old_ptr-begin)+size
- return byte_slice(old_memory, size), .None
- }
- }
- data, err := default_temp_allocator_alloc(s, size, alignment, loc)
- if err == .None {
- copy(data, byte_slice(old_memory, old_size))
- err = default_temp_allocator_free(s, old_memory, loc)
- }
- return data, err
}
default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
@@ -161,40 +40,40 @@ when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR
old_memory: rawptr, old_size: int, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
s := (^Default_Temp_Allocator)(allocator_data)
+ return arena_allocator_proc(&s.arena, mode, size, alignment, old_memory, old_size, loc)
+ }
- if s.data == nil {
- default_temp_allocator_init(s, DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, default_allocator())
+ @(require_results)
+ default_temp_allocator_temp_begin :: proc(loc := #caller_location) -> (temp: Arena_Temp) {
+ if context.temp_allocator.data == &global_default_temp_allocator_data {
+ temp = arena_temp_begin(&global_default_temp_allocator_data.arena, loc)
}
-
- switch mode {
- 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)
-
- case .Free_All:
- default_temp_allocator_free_all(s, loc)
-
- case .Resize:
- data, err = default_temp_allocator_resize(s, old_memory, old_size, size, alignment, loc)
-
- case .Query_Features:
- set := (^Allocator_Mode_Set)(old_memory)
- if set != nil {
- set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
- }
-
- case .Query_Info:
- return nil, .Mode_Not_Implemented
- }
-
return
}
+
+ default_temp_allocator_temp_end :: proc(temp: Arena_Temp, loc := #caller_location) {
+ arena_temp_end(temp, loc)
+ }
+
+ @(fini, private)
+ _destroy_temp_allocator_fini :: proc() {
+ default_temp_allocator_destroy(&global_default_temp_allocator_data)
+ }
}
+@(deferred_out=default_temp_allocator_temp_end)
+DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD :: #force_inline proc(ignore := false, loc := #caller_location) -> (Arena_Temp, Source_Code_Location) {
+ if ignore {
+ return {}, loc
+ } else {
+ return default_temp_allocator_temp_begin(loc), loc
+ }
+}
+
+
default_temp_allocator :: proc(allocator: ^Default_Temp_Allocator) -> Allocator {
return Allocator{
procedure = default_temp_allocator_proc,
- data = allocator,
+ data = allocator,
}
}
diff --git a/core/runtime/dynamic_map_internal.odin b/core/runtime/dynamic_map_internal.odin
index fb4ad29a9..05c03028f 100644
--- a/core/runtime/dynamic_map_internal.odin
+++ b/core/runtime/dynamic_map_internal.odin
@@ -184,7 +184,7 @@ map_cell_index_static :: #force_inline proc "contextless" (cells: [^]Map_Cell($T
// len() for map
@(require_results)
map_len :: #force_inline proc "contextless" (m: Raw_Map) -> int {
- return m.len
+ return int(m.len)
}
// cap() for map
@@ -207,8 +207,8 @@ map_load_factor :: #force_inline proc "contextless" (log2_capacity: uintptr) ->
}
@(require_results)
-map_resize_threshold :: #force_inline proc "contextless" (m: Raw_Map) -> int {
- return int(map_load_factor(map_log2_cap(m)))
+map_resize_threshold :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
+ return map_load_factor(map_log2_cap(m))
}
// The data stores the log2 capacity in the lower six bits. This is primarily
@@ -251,6 +251,26 @@ map_hash_is_valid :: #force_inline proc "contextless" (hash: Map_Hash) -> bool {
return (hash != 0) & (hash & TOMBSTONE_MASK == 0)
}
+@(require_results)
+map_seed :: #force_inline proc "contextless" (m: Raw_Map) -> uintptr {
+ return map_seed_from_map_data(map_data(m))
+}
+
+// splitmix for uintptr
+@(require_results)
+map_seed_from_map_data :: #force_inline proc "contextless" (data: uintptr) -> uintptr {
+ when size_of(uintptr) == size_of(u64) {
+ mix := data + 0x9e3779b97f4a7c15
+ mix = (mix ~ (mix >> 30)) * 0xbf58476d1ce4e5b9
+ mix = (mix ~ (mix >> 27)) * 0x94d049bb133111eb
+ return mix ~ (mix >> 31)
+ } else {
+ mix := data + 0x9e3779b9
+ mix = (mix ~ (mix >> 16)) * 0x21f0aaad
+ mix = (mix ~ (mix >> 15)) * 0x735a2d97
+ return mix ~ (mix >> 15)
+ }
+}
// Computes the desired position in the array. This is just index % capacity,
// but a procedure as there's some math involved here to recover the capacity.
@@ -394,32 +414,71 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
tk := map_cell_index_dynamic(sk, info.ks, 1)
tv := map_cell_index_dynamic(sv, info.vs, 1)
-
for {
hp := &hs[pos]
element_hash := hp^
if map_hash_is_empty(element_hash) {
- k_dst := map_cell_index_dynamic(ks, info.ks, pos)
- v_dst := map_cell_index_dynamic(vs, info.vs, pos)
- intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
- intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
+ kp := map_cell_index_dynamic(ks, info.ks, pos)
+ vp := map_cell_index_dynamic(vs, info.vs, pos)
+ intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
+ intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
hp^ = h
- return result if result != 0 else v_dst
+ return result if result != 0 else vp
+ }
+
+ if map_hash_is_deleted(element_hash) {
+ next_pos := (pos + 1) & mask
+
+ // backward shift
+ for !map_hash_is_empty(hs[next_pos]) {
+ probe_distance := map_probe_distance(m^, hs[next_pos], next_pos)
+ if probe_distance == 0 {
+ break
+ }
+ probe_distance -= 1
+
+ kp := map_cell_index_dynamic(ks, info.ks, pos)
+ vp := map_cell_index_dynamic(vs, info.vs, pos)
+ kn := map_cell_index_dynamic(ks, info.ks, next_pos)
+ vn := map_cell_index_dynamic(vs, info.vs, next_pos)
+
+ if distance > probe_distance {
+ if result == 0 {
+ result = vp
+ }
+ // move stored into pos; store next
+ intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
+ intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
+ hs[pos] = h
+
+ intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kn), size_of_k)
+ intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(vn), size_of_v)
+ h = hs[next_pos]
+ } else {
+ // move next back 1
+ intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(kn), size_of_k)
+ intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(vn), size_of_v)
+ hs[pos] = hs[next_pos]
+ distance = probe_distance
+ }
+ hs[next_pos] = 0
+ pos = (pos + 1) & mask
+ next_pos = (next_pos + 1) & mask
+ distance += 1
+ }
+
+ kp := map_cell_index_dynamic(ks, info.ks, pos)
+ vp := map_cell_index_dynamic(vs, info.vs, pos)
+ intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
+ intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
+ hs[pos] = h
+
+ return result if result != 0 else vp
}
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
- if map_hash_is_deleted(element_hash) {
- k_dst := map_cell_index_dynamic(ks, info.ks, pos)
- v_dst := map_cell_index_dynamic(vs, info.vs, pos)
- intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
- intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
- hp^ = h
-
- return result if result != 0 else v_dst
- }
-
if result == 0 {
result = map_cell_index_dynamic(vs, info.vs, pos)
}
@@ -503,6 +562,7 @@ map_reserve_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_
}
k := map_cell_index_dynamic(ks, info.ks, i)
v := map_cell_index_dynamic(vs, info.vs, i)
+ hash = info.key_hasher(rawptr(k), map_seed(resized))
_ = map_insert_hash_dynamic(&resized, info, hash, k, v)
// Only need to do this comparison on each actually added pair, so do not
// fold it into the for loop comparator as a micro-optimization.
@@ -550,6 +610,7 @@ map_shrink_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_I
k := map_cell_index_dynamic(ks, info.ks, i)
v := map_cell_index_dynamic(vs, info.vs, i)
+ hash = info.key_hasher(rawptr(k), map_seed(shrunk))
_ = map_insert_hash_dynamic(&shrunk, info, hash, k, v)
// Only need to do this comparison on each actually added pair, so do not
// fold it into the for loop comparator as a micro-optimization.
@@ -581,7 +642,7 @@ map_lookup_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
if map_len(m) == 0 {
return 0, false
}
- h := info.key_hasher(rawptr(k), 0)
+ h := info.key_hasher(rawptr(k), map_seed(m))
p := map_desired_position(m, h)
d := uintptr(0)
c := (uintptr(1) << map_log2_cap(m)) - 1
@@ -604,7 +665,7 @@ map_exists_dynamic :: proc "contextless" (m: Raw_Map, #no_alias info: ^Map_Info,
if map_len(m) == 0 {
return false
}
- h := info.key_hasher(rawptr(k), 0)
+ h := info.key_hasher(rawptr(k), map_seed(m))
p := map_desired_position(m, h)
d := uintptr(0)
c := (uintptr(1) << map_log2_cap(m)) - 1
@@ -637,7 +698,6 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
{ // coalesce tombstones
// HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots
- // TODO(bill): we should do backward shift deletion and not rely on tombstone slots
mask := (uintptr(1)< (stored_key: K, store
info := intrinsics.type_map_info(T)
key := key
- h := info.key_hasher(&key, 0)
+ h := info.key_hasher(&key, map_seed(rm))
pos := map_desired_position(rm, h)
distance := uintptr(0)
mask := (uintptr(1) << map_log2_cap(rm)) - 1
@@ -762,15 +822,15 @@ __dynamic_map_get :: proc "contextless" (#no_alias m: ^Raw_Map, #no_alias info:
}
// IMPORTANT: USED WITHIN THE COMPILER
-__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> Allocator_Error {
+__dynamic_map_check_grow :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, loc := #caller_location) -> (err: Allocator_Error, has_grown: bool) {
if m.len >= map_resize_threshold(m^) {
- return map_grow_dynamic(m, info, loc)
+ return map_grow_dynamic(m, info, loc), true
}
- return nil
+ return nil, false
}
__dynamic_map_set_without_hash :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_Info, key, value: rawptr, loc := #caller_location) -> rawptr {
- return __dynamic_map_set(m, info, info.key_hasher(key, 0), key, value, loc)
+ return __dynamic_map_set(m, info, info.key_hasher(key, map_seed(m^)), key, value, loc)
}
@@ -781,9 +841,14 @@ __dynamic_map_set :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^Map_In
return found
}
- if __dynamic_map_check_grow(m, info, loc) != nil {
+ hash := hash
+ err, has_grown := __dynamic_map_check_grow(m, info, loc)
+ if err != nil {
return nil
}
+ if has_grown {
+ hash = info.key_hasher(key, map_seed(m^))
+ }
result := map_insert_hash_dynamic(m, info, hash, uintptr(key), uintptr(value))
m.len += 1
diff --git a/core/runtime/entry_wasm.odin b/core/runtime/entry_wasm.odin
index 125abc756..235d5611b 100644
--- a/core/runtime/entry_wasm.odin
+++ b/core/runtime/entry_wasm.odin
@@ -1,5 +1,5 @@
//+private
-//+build wasm32, wasm64
+//+build wasm32, wasm64p32
package runtime
import "core:intrinsics"
diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin
index 8539c724d..c189642af 100644
--- a/core/runtime/error_checks.odin
+++ b/core/runtime/error_checks.odin
@@ -22,7 +22,7 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
return
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) {
+ handle_error :: proc "contextless" (file: string, line, column: i32, index, count: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Index ")
print_i64(i64(index))
@@ -83,7 +83,7 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
return
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) {
+ handle_error :: proc "contextless" (file: string, line, column: i32, low, high, max: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Invalid dynamic array indices ")
print_i64(i64(low))
@@ -104,7 +104,7 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
return
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
+ handle_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Matrix indices [")
print_i64(i64(row_index))
@@ -122,13 +122,13 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
}
-when ODIN_DISALLOW_RTTI {
+when ODIN_NO_RTTI {
type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32) {
if ok {
return
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32) {
+ handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Invalid type assertion\n")
type_assertion_trap()
@@ -141,7 +141,7 @@ when ODIN_DISALLOW_RTTI {
return
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32) {
+ handle_error :: proc "contextless" (file: string, line, column: i32) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Invalid type assertion\n")
type_assertion_trap()
@@ -154,7 +154,7 @@ when ODIN_DISALLOW_RTTI {
return
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) {
+ handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
print_string(" Invalid type assertion from ")
print_typeid(from)
@@ -199,7 +199,7 @@ when ODIN_DISALLOW_RTTI {
}
@(cold)
- handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) -> ! {
actual := variant_type(from, from_data)
@@ -225,7 +225,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
return
}
@(cold)
- handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
+ handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) -> ! {
print_caller_location(loc)
print_string(" Invalid slice length for make: ")
print_i64(i64(len))
@@ -240,7 +240,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #
return
}
@(cold)
- handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
+ handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) -> ! {
print_caller_location(loc)
print_string(" Invalid dynamic array parameters for make: ")
print_i64(i64(len))
@@ -257,7 +257,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
return
}
@(cold)
- handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
+ handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) -> ! {
print_caller_location(loc)
print_string(" Invalid map capacity for make: ")
print_i64(i64(cap))
diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin
index cb04ff0aa..71ad9386a 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -3,7 +3,7 @@ package runtime
import "core:intrinsics"
@(private="file")
-IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64
+IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32
@(private)
RUNTIME_LINKAGE :: "strong" when (
@@ -184,32 +184,33 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
return
}
-mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
+mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
if allocator.procedure == nil {
return nil, nil
}
if new_size == 0 {
if ptr != nil {
- _, err := allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
- return nil, err
+ _, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
+ return
}
- return nil, nil
+ return
} else if ptr == nil {
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
- return ([^]byte)(ptr)[:old_size], nil
+ data = ([^]byte)(ptr)[:old_size]
+ return
}
- data, err := allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
+ data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
if err == .Mode_Not_Implemented {
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
if err != nil {
- return data, err
+ return
}
copy(data, ([^]byte)(ptr)[:old_size])
_, err = allocator.procedure(allocator.data, .Free, 0, 0, ptr, old_size, loc)
}
- return data, err
+ return
}
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
@@ -223,7 +224,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
when size_of(uint) == 8 {
if word_length := length >> 3; word_length != 0 {
- for i in 0.. bool {
return true
} else {
if word_length := length >> 2; word_length != 0 {
- for i in 0.. (rune, int
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4
}
+string_decode_last_rune :: proc "contextless" (s: string) -> (rune, int) {
+ RUNE_ERROR :: '\ufffd'
+ RUNE_SELF :: 0x80
+ UTF_MAX :: 4
+
+ r: rune
+ size: int
+ start, end, limit: int
+
+ end = len(s)
+ if end == 0 {
+ return RUNE_ERROR, 0
+ }
+ start = end-1
+ r = rune(s[start])
+ if r < RUNE_SELF {
+ return r, 1
+ }
+
+ limit = max(end - UTF_MAX, 0)
+
+ for start-=1; start >= limit; start-=1 {
+ if (s[start] & 0xc0) != RUNE_SELF {
+ break
+ }
+ }
+
+ start = max(start, 0)
+ r, size = string_decode_rune(s[start:end])
+ if start+size != end {
+ return RUNE_ERROR, 1
+ }
+ return r, size
+}
+
+
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
return -x if x < 0 else x
}
diff --git a/core/runtime/os_specific_js.odin b/core/runtime/os_specific_js.odin
index a9ba871f9..246141d87 100644
--- a/core/runtime/os_specific_js.odin
+++ b/core/runtime/os_specific_js.odin
@@ -5,7 +5,7 @@ foreign import "odin_env"
_os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
foreign odin_env {
- write :: proc "c" (fd: u32, p: []byte) ---
+ write :: proc "contextless" (fd: u32, p: []byte) ---
}
write(1, data)
return len(data), 0
diff --git a/core/runtime/os_specific_windows.odin b/core/runtime/os_specific_windows.odin
index 6e7474257..732715793 100644
--- a/core/runtime/os_specific_windows.odin
+++ b/core/runtime/os_specific_windows.odin
@@ -112,7 +112,7 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
ptr := uintptr(aligned_mem)
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
diff := int(aligned_ptr - ptr)
- if (size + diff) > space {
+ if (size + diff) > space || allocated_mem == nil {
return nil, .Out_Of_Memory
}
diff --git a/core/runtime/print.odin b/core/runtime/print.odin
index 959dad3a9..732ed9c12 100644
--- a/core/runtime/print.odin
+++ b/core/runtime/print.odin
@@ -2,6 +2,82 @@ package runtime
_INTEGER_DIGITS :: "0123456789abcdefghijklmnopqrstuvwxyz"
+@(private="file")
+_INTEGER_DIGITS_VAR := _INTEGER_DIGITS
+
+when !ODIN_NO_RTTI {
+ print_any_single :: proc "contextless" (arg: any) {
+ x := arg
+ if loc, ok := x.(Source_Code_Location); ok {
+ print_caller_location(loc)
+ return
+ }
+ x.id = typeid_base(x.id)
+ switch v in x {
+ case typeid: print_typeid(v)
+ case ^Type_Info: print_type(v)
+
+ case string: print_string(v)
+ case cstring: print_string(string(v))
+ case []byte: print_string(string(v))
+
+ case rune: print_rune(v)
+
+ case u8: print_u64(u64(v))
+ case u16: print_u64(u64(v))
+ case u16le: print_u64(u64(v))
+ case u16be: print_u64(u64(v))
+ case u32: print_u64(u64(v))
+ case u32le: print_u64(u64(v))
+ case u32be: print_u64(u64(v))
+ case u64: print_u64(u64(v))
+ case u64le: print_u64(u64(v))
+ case u64be: print_u64(u64(v))
+
+ case i8: print_i64(i64(v))
+ case i16: print_i64(i64(v))
+ case i16le: print_i64(i64(v))
+ case i16be: print_i64(i64(v))
+ case i32: print_i64(i64(v))
+ case i32le: print_i64(i64(v))
+ case i32be: print_i64(i64(v))
+ case i64: print_i64(i64(v))
+ case i64le: print_i64(i64(v))
+ case i64be: print_i64(i64(v))
+
+ case int: print_int(v)
+ case uint: print_uint(v)
+ case uintptr: print_uintptr(v)
+
+ case bool: print_string("true" if v else "false")
+ case b8: print_string("true" if v else "false")
+ case b16: print_string("true" if v else "false")
+ case b32: print_string("true" if v else "false")
+ case b64: print_string("true" if v else "false")
+
+ case:
+ ti := type_info_of(x.id)
+ #partial switch v in ti.variant {
+ case Type_Info_Pointer:
+ print_uintptr((^uintptr)(x.data)^)
+ return
+ }
+
+ print_string("")
+ }
+ }
+ println_any :: proc "contextless" (args: ..any) {
+ loop: for arg, i in args {
+ if i != 0 {
+ print_string(" ")
+ }
+ print_any_single(arg)
+ }
+ print_string("\n")
+ }
+}
+
+
encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
r := c
@@ -38,14 +114,14 @@ encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
return buf, 4
}
-print_string :: proc "contextless" (str: string) -> (int, _OS_Errno) {
- return os_write(transmute([]byte)str)
+print_string :: proc "contextless" (str: string) -> (n: int) {
+ n, _ = os_write(transmute([]byte)str)
+ return
}
-print_strings :: proc "contextless" (args: ..string) -> (n: int, err: _OS_Errno) {
+print_strings :: proc "contextless" (args: ..string) -> (n: int) {
for str in args {
- m: int
- m, err = os_write(transmute([]byte)str)
+ m, err := os_write(transmute([]byte)str)
n += m
if err != 0 {
break
@@ -54,8 +130,9 @@ print_strings :: proc "contextless" (args: ..string) -> (n: int, err: _OS_Errno)
return
}
-print_byte :: proc "contextless" (b: byte) -> (int, _OS_Errno) {
- return os_write([]byte{b})
+print_byte :: proc "contextless" (b: byte) -> (n: int) {
+ n, _ = os_write([]byte{b})
+ return
}
print_encoded_rune :: proc "contextless" (r: rune) {
@@ -74,11 +151,10 @@ print_encoded_rune :: proc "contextless" (r: rune) {
if r <= 0 {
print_string("\\x00")
} else if r < 32 {
- digits := _INTEGER_DIGITS
n0, n1 := u8(r) >> 4, u8(r) & 0xf
print_string("\\x")
- print_byte(digits[n0])
- print_byte(digits[n1])
+ print_byte(_INTEGER_DIGITS_VAR[n0])
+ print_byte(_INTEGER_DIGITS_VAR[n1])
} else {
print_rune(r)
}
@@ -86,7 +162,7 @@ print_encoded_rune :: proc "contextless" (r: rune) {
print_byte('\'')
}
-print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check {
+print_rune :: proc "contextless" (r: rune) -> int #no_bounds_check {
RUNE_SELF :: 0x80
if r < RUNE_SELF {
@@ -94,29 +170,27 @@ print_rune :: proc "contextless" (r: rune) -> (int, _OS_Errno) #no_bounds_check
}
b, n := encode_rune(r)
- return os_write(b[:n])
+ m, _ := os_write(b[:n])
+ return m
}
print_u64 :: proc "contextless" (x: u64) #no_bounds_check {
- digits := _INTEGER_DIGITS
-
a: [129]byte
i := len(a)
b := u64(10)
u := x
for u >= b {
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
}
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
os_write(a[i:])
}
print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
- digits := _INTEGER_DIGITS
b :: i64(10)
u := x
@@ -126,10 +200,10 @@ print_i64 :: proc "contextless" (x: i64) #no_bounds_check {
a: [129]byte
i := len(a)
for u >= b {
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
u /= b
}
- i -= 1; a[i] = digits[u % b]
+ i -= 1; a[i] = _INTEGER_DIGITS_VAR[u % b]
if neg {
i -= 1; a[i] = '-'
}
@@ -160,7 +234,7 @@ print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
}
}
print_typeid :: proc "contextless" (id: typeid) {
- when ODIN_DISALLOW_RTTI {
+ when ODIN_NO_RTTI {
if id == nil {
print_string("nil")
} else {
@@ -236,7 +310,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
if info.params == nil {
print_string("()")
} else {
- t := info.params.variant.(Type_Info_Tuple)
+ t := info.params.variant.(Type_Info_Parameters)
print_byte('(')
for t, i in t.types {
if i > 0 { print_string(", ") }
@@ -248,7 +322,7 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_string(" -> ")
print_type(info.results)
}
- case Type_Info_Tuple:
+ case Type_Info_Parameters:
count := len(info.names)
if count != 1 { print_byte('(') }
for name, i in info.names {
diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin
index 510abcbb9..3a433c3bf 100644
--- a/core/runtime/procs.odin
+++ b/core/runtime/procs.odin
@@ -25,7 +25,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
RtlMoveMemory(dst, src, len)
return dst
}
-} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64) {
+} else when ODIN_NO_CRT || (ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32) {
@(link_name="memset", linkage="strong", require)
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
if ptr != nil && len != 0 {
diff --git a/core/runtime/procs_js_wasm32.odin b/core/runtime/procs_js.odin
similarity index 93%
rename from core/runtime/procs_js_wasm32.odin
rename to core/runtime/procs_js.odin
index b8acebc87..d3e12410c 100644
--- a/core/runtime/procs_js_wasm32.odin
+++ b/core/runtime/procs_js.odin
@@ -1,4 +1,4 @@
-//+build js wasm32
+//+build js
package runtime
init_default_context_for_js: Context
diff --git a/core/runtime/procs_wasm32.odin b/core/runtime/procs_wasm.odin
similarity index 82%
rename from core/runtime/procs_wasm32.odin
rename to core/runtime/procs_wasm.odin
index 2a4210c1e..26dcfef77 100644
--- a/core/runtime/procs_wasm32.odin
+++ b/core/runtime/procs_wasm.odin
@@ -1,4 +1,4 @@
-//+build wasm32
+//+build wasm32, wasm64p32
package runtime
@(private="file")
@@ -8,7 +8,7 @@ ti_int :: struct #raw_union {
}
@(link_name="__ashlti3", linkage="strong")
-__ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
+__ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
bits_in_dword :: size_of(u32)*8
b := u32(b_)
@@ -29,7 +29,7 @@ __ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
@(link_name="__multi3", linkage="strong")
-__multi3 :: proc "c" (a, b: i128) -> i128 {
+__multi3 :: proc "contextless" (a, b: i128) -> i128 {
x, y, r: ti_int
x.all = a
diff --git a/core/slice/ptr.odin b/core/slice/ptr.odin
index 214b745f7..e2f1c3e7b 100644
--- a/core/slice/ptr.odin
+++ b/core/slice/ptr.odin
@@ -73,24 +73,26 @@ ptr_rotate :: proc(left: int, mid: ^$T, right: int) {
left, mid, right := left, mid, right
// TODO(bill): Optimization with a buffer for smaller ranges
- if left >= right {
- for {
- ptr_swap_non_overlapping(ptr_sub(mid, right), mid, right)
- mid = ptr_sub(mid, right)
+ for left > 0 && right > 0 {
+ if left >= right {
+ for {
+ ptr_swap_non_overlapping(ptr_sub(mid, right), mid, right * size_of(T))
+ mid = ptr_sub(mid, right)
- left -= right
- if left < right {
- break
+ left -= right
+ if left < right {
+ break
+ }
}
- }
- } else {
- for {
- ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left)
- mid = ptr_add(mid, left)
+ } else {
+ for {
+ ptr_swap_non_overlapping(ptr_sub(mid, left), mid, left * size_of(T))
+ mid = ptr_add(mid, left)
- right -= left
- if right < left {
- break
+ right -= left
+ if right < left {
+ break
+ }
}
}
}
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 032a8ca6e..412c90fc8 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -13,6 +13,7 @@ _ :: mem
/*
Turn a pointer and a length into a slice.
*/
+@(require_results)
from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
return ([^]T)(ptr)[:count]
}
@@ -20,6 +21,7 @@ from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
/*
Turn a pointer and a length into a byte slice.
*/
+@(require_results)
bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
return ([^]byte)(ptr)[:byte_count]
}
@@ -29,6 +31,7 @@ bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
See `slice.reinterpret` to go the other way.
*/
+@(require_results)
to_bytes :: proc "contextless" (s: []$T) -> []byte {
return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
}
@@ -51,10 +54,15 @@ to_bytes :: proc "contextless" (s: []$T) -> []byte {
assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
```
*/
+@(require_results)
reinterpret :: proc "contextless" ($T: typeid/[]$U, s: []$V) -> []U {
- bytes := to_bytes(s)
- n := len(bytes) / size_of(U)
- return ([^]U)(raw_data(bytes))[:n]
+ when size_of(U) == 0 || size_of(V) == 0 {
+ return nil
+ } else {
+ bytes := to_bytes(s)
+ n := len(bytes) / size_of(U)
+ return ([^]U)(raw_data(bytes))[:n]
+ }
}
@@ -77,17 +85,18 @@ swap_between :: proc(a, b: $T/[]$E) {
reverse :: proc(array: $T/[]$E) {
n := len(array)/2
for i in 0.. bool where intrinsics.type_is_comparable(E) {
_, found := linear_search(array, value)
return found
}
+@(require_results)
linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
where intrinsics.type_is_comparable(T) #no_bounds_check {
for x, i in array {
@@ -98,6 +107,7 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
return -1, false
}
+@(require_results)
linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) #no_bounds_check {
for x, i in array {
if f(x) {
@@ -107,6 +117,7 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
return -1, false
}
+@(require_results)
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
where intrinsics.type_is_ordered(T) #no_bounds_check {
@@ -147,6 +158,7 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
}
+@(require_results)
equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) {
if len(a) != len(b) {
return false
@@ -163,6 +175,7 @@ equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_comparable(E) {
}
}
+@(require_results)
simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_compare(E) {
if len(a) != len(b) {
return false
@@ -177,6 +190,7 @@ simple_equal :: proc(a, b: $T/[]$E) -> bool where intrinsics.type_is_simple_comp
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{1, 2, 3}) -> 3
slice.prefix_length([]u8{1, 2, 3, 4}, []u8{2, 3, 4}) -> 0
*/
+@(require_results)
prefix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_comparable(E) {
_len := builtin.min(len(a), len(b))
@@ -186,6 +200,7 @@ prefix_length :: proc(a, b: $T/[]$E) -> (n: int) where intrinsics.type_is_compar
return
}
+@(require_results)
has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) {
n := len(needle)
if len(array) >= n {
@@ -195,6 +210,7 @@ has_prefix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_c
}
+@(require_results)
has_suffix :: proc(array: $T/[]$E, needle: E) -> bool where intrinsics.type_is_comparable(E) {
array := array
m, n := len(array), len(needle)
@@ -218,8 +234,10 @@ rotate_left :: proc(array: $T/[]$E, mid: int) {
n := len(array)
m := mid %% n
k := n - m
- p := raw_data(array)
- ptr_rotate(mid, ptr_add(p, mid), k)
+ // FIXME: (ap29600) this cast is a temporary fix for the compiler not matching
+ // [^T] with $P/^$T
+ p := cast(^E)raw_data(array)
+ ptr_rotate(m, ptr_add(p, m), k)
}
rotate_right :: proc(array: $T/[]$E, k: int) {
rotate_left(array, -k)
@@ -231,7 +249,8 @@ swap_with_slice :: proc(a, b: $T/[]$E, loc := #caller_location) {
ptr_swap_non_overlapping(raw_data(a), raw_data(b), len(a)*size_of(E))
}
-concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T) {
+@(require_results)
+concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T, err: mem.Allocator_Error) #optional_allocator_error {
if len(a) == 0 {
return
}
@@ -239,7 +258,7 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T) {
for s in a {
n += len(s)
}
- res = make(T, n, allocator)
+ res = make(T, n, allocator) or_return
i := 0
for s in a {
i += copy(res[i:], s)
@@ -248,22 +267,24 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T) {
}
// copies a slice into a new slice
-clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
- d := make([]E, len(a), allocator)
+@(require_results)
+clone :: proc(a: $T/[]$E, allocator := context.allocator) -> ([]E, mem.Allocator_Error) #optional_allocator_error {
+ d, err := make([]E, len(a), allocator)
copy(d[:], a)
- return d
+ return d, err
}
// copies slice into a new dynamic array
-clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
- d := make([dynamic]E, len(a), allocator)
+clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> ([dynamic]E, mem.Allocator_Error) #optional_allocator_error {
+ d, err := make([dynamic]E, len(a), allocator)
copy(d[:], a)
- return d
+ return d, err
}
to_dynamic :: clone_to_dynamic
// Converts slice into a dynamic array without cloning or allocating memory
+@(require_results)
into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E {
s := transmute(mem.Raw_Slice)a
d := mem.Raw_Dynamic_Array{
@@ -276,43 +297,51 @@ into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E {
}
+@(require_results)
length :: proc(a: $T/[]$E) -> int {
return len(a)
}
+@(require_results)
is_empty :: proc(a: $T/[]$E) -> bool {
return len(a) == 0
}
-
+@(require_results)
split_at :: proc(array: $T/[]$E, index: int) -> (a, b: T) {
return array[:index], array[index:]
}
+@(require_results)
split_first :: proc(array: $T/[]$E) -> (first: E, rest: T) {
return array[0], array[1:]
}
+@(require_results)
split_last :: proc(array: $T/[]$E) -> (rest: T, last: E) {
n := len(array)-1
return array[:n], array[n]
}
+@(require_results)
first :: proc(array: $T/[]$E) -> E {
return array[0]
}
+@(require_results)
last :: proc(array: $T/[]$E) -> E {
return array[len(array)-1]
}
+@(require_results)
first_ptr :: proc(array: $T/[]$E) -> ^E {
if len(array) != 0 {
return &array[0]
}
return nil
}
+@(require_results)
last_ptr :: proc(array: $T/[]$E) -> ^E {
if len(array) != 0 {
return &array[len(array)-1]
@@ -320,6 +349,7 @@ last_ptr :: proc(array: $T/[]$E) -> ^E {
return nil
}
+@(require_results)
get :: proc(array: $T/[]$E, index: int) -> (value: E, ok: bool) {
if uint(index) < len(array) {
value = array[index]
@@ -327,6 +357,7 @@ get :: proc(array: $T/[]$E, index: int) -> (value: E, ok: bool) {
}
return
}
+@(require_results)
get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
if uint(index) < len(array) {
value = &array[index]
@@ -335,19 +366,22 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
return
}
+@(require_results)
as_ptr :: proc(array: $T/[]$E) -> [^]E {
return raw_data(array)
}
-mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> []V {
- r := make([]V, len(s), allocator)
+@(require_results)
+mapper :: proc(s: $S/[]$U, f: proc(U) -> $V, allocator := context.allocator) -> (r: []V, err: mem.Allocator_Error) #optional_allocator_error {
+ r = make([]V, len(s), allocator) or_return
for v, i in s {
r[i] = f(v)
}
- return r
+ return
}
+@(require_results)
reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V {
r := initializer
for v in s {
@@ -356,6 +390,7 @@ reduce :: proc(s: $S/[]$U, initializer: $V, f: proc(V, U) -> V) -> V {
return r
}
+@(require_results)
filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -> S {
r := make([dynamic]U, 0, 0, allocator)
for v in s {
@@ -366,10 +401,11 @@ filter :: proc(s: $S/[]$U, f: proc(U) -> bool, allocator := context.allocator) -
return r[:]
}
-scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> []V {
- if len(s) == 0 { return {} }
+@(require_results)
+scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := context.allocator) -> (res: []V, err: mem.Allocator_Error) #optional_allocator_error {
+ if len(s) == 0 { return }
- res := make([]V, len(s), allocator)
+ res = make([]V, len(s), allocator) or_return
p := as_ptr(s)
q := as_ptr(res)
r := initializer
@@ -381,10 +417,11 @@ scanner :: proc (s: $S/[]$U, initializer: $V, f: proc(V, U) -> V, allocator := c
q = q[1:]
}
- return res
+ return
}
+@(require_results)
min :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T) #optional_ok {
if len(s) != 0 {
res = s[0]
@@ -395,6 +432,7 @@ min :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T
}
return
}
+@(require_results)
max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T) #optional_ok {
if len(s) != 0 {
res = s[0]
@@ -406,6 +444,7 @@ max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T
return
}
+@(require_results)
min_max :: proc(s: $S/[]$T) -> (min, max: T, ok: bool) where intrinsics.type_is_ordered(T) {
if len(s) != 0 {
min, max = s[0], s[0]
@@ -418,6 +457,7 @@ min_max :: proc(s: $S/[]$T) -> (min, max: T, ok: bool) where intrinsics.type_is_
return
}
+@(require_results)
any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
for v in s {
if v == value {
@@ -427,6 +467,7 @@ any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable
return false
}
+@(require_results)
none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
for v in s {
if v == value {
@@ -436,6 +477,7 @@ none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparabl
return true
}
+@(require_results)
all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
if len(s) == 0 {
return false
@@ -449,6 +491,7 @@ all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable
}
+@(require_results)
any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
for v in s {
if f(v) {
@@ -458,6 +501,7 @@ any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
return false
}
+@(require_results)
none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
for v in s {
if f(v) {
@@ -467,6 +511,7 @@ none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
return true
}
+@(require_results)
all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
if len(s) == 0 {
return false
@@ -480,6 +525,7 @@ all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
}
+@(require_results)
count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_comparable(T) {
for v in s {
if v == value {
@@ -489,6 +535,7 @@ count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_compara
return
}
+@(require_results)
count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) {
for v in s {
if f(v) {
@@ -499,6 +546,7 @@ count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) {
}
+@(require_results)
dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
where intrinsics.type_is_numeric(T) {
if len(a) != len(b) {
@@ -512,7 +560,8 @@ dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
// Convert a pointer to an enumerated array to a slice of the element type
+@(require_results)
enumerated_array :: proc(ptr: ^$T) -> []intrinsics.type_elem_type(T)
where intrinsics.type_is_enumerated_array(T) {
return ([^]intrinsics.type_elem_type(T))(ptr)[:len(T)]
-}
\ No newline at end of file
+}
diff --git a/core/slice/sort.odin b/core/slice/sort.odin
index 8177205ed..515eddcc3 100644
--- a/core/slice/sort.odin
+++ b/core/slice/sort.odin
@@ -6,6 +6,7 @@ Ordering :: enum {
Greater = +1,
}
+@(require_results)
cmp :: proc(a, b: $E) -> Ordering where ORD(E) {
switch {
case a < b:
@@ -16,6 +17,7 @@ cmp :: proc(a, b: $E) -> Ordering where ORD(E) {
return .Equal
}
+@(require_results)
cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) {
return proc(a, b: E) -> Ordering {
switch {
@@ -144,6 +146,7 @@ stable_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
}
}
+@(require_results)
is_sorted :: proc(array: $T/[]$E) -> bool where ORD(E) {
for i := len(array)-1; i > 0; i -= 1 {
if array[i] < array[i-1] {
@@ -153,6 +156,7 @@ is_sorted :: proc(array: $T/[]$E) -> bool where ORD(E) {
return true
}
+@(require_results)
is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
if less(array[i], array[i-1]) {
@@ -163,6 +167,8 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
}
is_sorted_by_cmp :: is_sorted_cmp
+
+@(require_results)
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
if cmp(array[i], array[i-1]) == .Less {
@@ -181,7 +187,7 @@ reverse_sort :: proc(data: $T/[]$E) where ORD(E) {
}
-reverse_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) where ORD(E) {
+reverse_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
context._internal = rawptr(less)
sort_by(data, proc(i, j: E) -> bool {
k := (proc(i, j: E) -> bool)(context._internal)
@@ -189,7 +195,7 @@ reverse_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) where ORD(E)
})
}
-reverse_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) where ORD(E) {
+reverse_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
context._internal = rawptr(cmp)
sort_by_cmp(data, proc(i, j: E) -> Ordering {
k := (proc(i, j: E) -> Ordering)(context._internal)
@@ -215,6 +221,7 @@ reverse_sort_by_key :: proc(data: $T/[]$E, key: proc(E) -> $K) where ORD(K) {
})
}
+@(require_results)
is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K) {
for i := len(array)-1; i > 0; i -= 1 {
if key(array[i]) < key(array[i-1]) {
@@ -224,7 +231,7 @@ is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K
return true
}
-@(private)
+@(private, require_results)
_max_depth :: proc(n: int) -> (depth: int) { // 2*ceil(log2(n+1))
for i := n; i > 0; i >>= 1 {
depth += 1
diff --git a/core/sort/map.odin b/core/sort/map.odin
deleted file mode 100644
index 32f5e09a2..000000000
--- a/core/sort/map.odin
+++ /dev/null
@@ -1,36 +0,0 @@
-package sort
-
-import "core:intrinsics"
-import "core:runtime"
-import "core:slice"
-
-_ :: runtime
-_ :: slice
-
-map_entries_by_key :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(K) {
- Entry :: struct {
- hash: uintptr,
- next: int,
- key: K,
- value: V,
- }
-
- header := runtime.__get_map_header(m)
- entries := (^[dynamic]Entry)(&header.m.entries)
- slice.sort_by_key(entries[:], proc(e: Entry) -> K { return e.key })
- runtime.__dynamic_map_reset_entries(header, loc)
-}
-
-map_entries_by_value :: proc(m: ^$M/map[$K]$V, loc := #caller_location) where intrinsics.type_is_ordered(V) {
- Entry :: struct {
- hash: uintptr,
- next: int,
- key: K,
- value: V,
- }
-
- header := runtime.__get_map_header(m)
- entries := (^[dynamic]Entry)(&header.m.entries)
- slice.sort_by_key(entries[:], proc(e: Entry) -> V { return e.value })
- runtime.__dynamic_map_reset_entries(header, loc)
-}
\ No newline at end of file
diff --git a/core/strconv/decimal/decimal.odin b/core/strconv/decimal/decimal.odin
index 03fde3012..4130da306 100644
--- a/core/strconv/decimal/decimal.odin
+++ b/core/strconv/decimal/decimal.odin
@@ -8,7 +8,17 @@ Decimal :: struct {
decimal_point: int,
neg, trunc: bool,
}
+/*
+Sets a Decimal from a given string `s`. The string is expected to represent a float. Stores parsed number in the given Decimal structure.
+If parsing fails, the Decimal will be left in an undefined state.
+**Inputs**
+- d: Pointer to a Decimal struct where the parsed result will be stored
+- s: The input string representing the floating-point number
+
+**Returns**
+- ok: A boolean indicating whether the parsing was successful
+*/
set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
d^ = {}
@@ -91,7 +101,16 @@ set :: proc(d: ^Decimal, s: string) -> (ok: bool) {
return i == len(s)
}
+/*
+Converts a Decimal to a string representation, using the provided buffer as storage.
+**Inputs**
+- buf: A byte slice buffer to hold the resulting string
+- a: The struct to be converted to a string
+
+**Returns**
+- A string representation of the Decimal
+*/
decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
digit_zero :: proc(buf: []byte) -> int {
for _, i in buf {
@@ -100,7 +119,6 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
return len(buf)
}
-
n := 10 + a.count + abs(a.decimal_point)
// TODO(bill): make this work with a buffer that's not big enough
@@ -129,8 +147,12 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
return string(b[0:w])
}
+/*
+Trims trailing zeros in the given Decimal, updating the count and decimal_point values as needed.
-// trim trailing zeros
+**Inputs**
+- a: Pointer to the Decimal struct to be trimmed
+*/
trim :: proc(a: ^Decimal) {
for a.count > 0 && a.digits[a.count-1] == '0' {
a.count -= 1
@@ -139,8 +161,15 @@ trim :: proc(a: ^Decimal) {
a.decimal_point = 0
}
}
+/*
+Converts a given u64 integer `idx` to its Decimal representation in the provided Decimal struct.
+**Used for internal Decimal Operations.**
+**Inputs**
+- a: Where the result will be stored
+- idx: The value to be assigned to the Decimal
+*/
assign :: proc(a: ^Decimal, idx: u64) {
buf: [64]byte
n := 0
@@ -160,9 +189,15 @@ assign :: proc(a: ^Decimal, idx: u64) {
a.decimal_point = a.count
trim(a)
}
+/*
+Shifts the Decimal value to the right by k positions.
+**Used for internal Decimal Operations.**
-
+**Inputs**
+- a: The Decimal struct to be shifted
+- k: The number of positions to shift right
+*/
shift_right :: proc(a: ^Decimal, k: uint) {
r := 0 // read index
w := 0 // write index
@@ -214,25 +249,136 @@ shift_right :: proc(a: ^Decimal, k: uint) {
trim(a)
}
-shift_left :: proc(a: ^Decimal, k: uint) {
- // NOTE(bill): used to determine buffer size required for the decimal from the binary shift
- // 'k' means `1< bool #no_bounds_check {
+ for i in 0..= len(b) {
+ return true
+ }
+ if b[i] != s[i] {
+ return b[i] < s[i]
+ }
+ }
+ return false
+ }
+
+ assert(k < 61)
+
+ delta := _shift_left_offsets[k].delta
+ if prefix_less(a.digits[:a.count], _shift_left_offsets[k].cutoff) {
+ delta -= 1
+ }
+
+ read_index := a.count
+ write_index := a.count+delta
n: uint
- for r -= 1; r >= 0; r -= 1 {
- n += (uint(a.digits[r]) - '0') << k
+ for read_index -= 1; read_index >= 0; read_index -= 1 {
+ n += (uint(a.digits[read_index]) - '0') << k
quo := n/10
rem := n - 10*quo
- w -= 1
- if w < d {
- a.digits[w] = byte('0' + rem)
+ write_index -= 1
+ if write_index < len(a.digits) {
+ a.digits[write_index] = byte('0' + rem)
} else if rem != 0 {
a.trunc = true
}
@@ -242,24 +388,27 @@ shift_left :: proc(a: ^Decimal, k: uint) {
for n > 0 {
quo := n/10
rem := n - 10*quo
- w -= 1
- if w < d {
- a.digits[w] = byte('0' + rem)
+ write_index -= 1
+ if write_index < len(a.digits) {
+ a.digits[write_index] = byte('0' + rem)
} else if rem != 0 {
a.trunc = true
}
n = quo
}
- // NOTE(bill): Remove unused buffer size
- assert(w >= 0)
- capacity -= w
+ a.decimal_point += delta
- a.count = min(a.count+capacity, d)
- a.decimal_point += capacity
+ a.count = clamp(a.count, 0, len(a.digits))
trim(a)
}
+/*
+Shifts the decimal of the input value by the specified number of places
+**Inputs**
+- a: The Decimal to be modified
+- i: The number of places to shift the decimal (positive for left shift, negative for right shift)
+*/
shift :: proc(a: ^Decimal, i: int) {
uint_size :: 8*size_of(uint)
max_shift :: uint_size-4
@@ -283,7 +432,15 @@ shift :: proc(a: ^Decimal, i: int) {
shift_right(a, uint(-k))
}
}
+/*
+Determines if the Decimal can be rounded up at the given digit index
+**Inputs**
+- a: The Decimal to check
+- nd: The digit index to consider for rounding up
+
+**Returns** Boolean if can be rounded up at the given index (>=5)
+*/
can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
if nd < 0 || nd >= a.count { return false }
if a.digits[nd] == '5' && nd+1 == a.count {
@@ -295,7 +452,13 @@ can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
return a.digits[nd] >= '5'
}
+/*
+Rounds the Decimal at the given digit index
+**Inputs**
+- a: The Decimal to be modified
+- nd: The digit index to round
+*/
round :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return }
if can_round_up(a, nd) {
@@ -304,7 +467,13 @@ round :: proc(a: ^Decimal, nd: int) {
round_down(a, nd)
}
}
+/*
+Rounds the Decimal up at the given digit index
+**Inputs**
+- a: The Decimal to be modified
+- nd: The digit index to round up
+*/
round_up :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return }
@@ -321,15 +490,60 @@ round_up :: proc(a: ^Decimal, nd: int) {
a.count = 1
a.decimal_point += 1
}
+/*
+Rounds down the decimal value to the specified number of decimal places
+**Inputs**
+- a: The Decimal value to be rounded down
+- nd: The number of decimal places to round down to
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv/decimal"
+ round_down_example :: proc() {
+ d: decimal.Decimal
+ str := [64]u8{}
+ ok := decimal.set(&d, "123.456")
+ decimal.round_down(&d, 5)
+ fmt.println(decimal.decimal_to_string(str[:], &d))
+ }
+
+Output:
+
+ 123.45
+
+*/
round_down :: proc(a: ^Decimal, nd: int) {
if nd < 0 || nd >= a.count { return }
a.count = nd
trim(a)
}
+/*
+Extracts the rounded integer part of a decimal value
+**Inputs**
+- a: A pointer to the Decimal value to extract the rounded integer part from
-// Extract integer part, rounded appropriately. There are no guarantees about overflow.
+WARNING: There are no guarantees about overflow.
+
+**Returns** The rounded integer part of the input decimal value
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv/decimal"
+ rounded_integer_example :: proc() {
+ d: decimal.Decimal
+ ok := decimal.set(&d, "123.456")
+ fmt.println(decimal.rounded_integer(&d))
+ }
+
+Output:
+
+ 123
+
+*/
rounded_integer :: proc(a: ^Decimal) -> u64 {
if a.decimal_point > 20 {
return 0xffff_ffff_ffff_ffff
diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin
index 489124fbf..70febf832 100644
--- a/core/strconv/generic_float.odin
+++ b/core/strconv/generic_float.odin
@@ -20,7 +20,29 @@ _f16_info := Float_Info{10, 5, -15}
_f32_info := Float_Info{23, 8, -127}
_f64_info := Float_Info{52, 11, -1023}
+/*
+Converts a floating-point number to a string with the specified format and precision.
+**Inputs**
+
+buf: A byte slice to store the resulting string
+val: The floating-point value to be converted
+fmt: The formatting byte, accepted values are 'e', 'E', 'f', 'F', 'g', 'G'
+precision: The number of decimal places to round to
+bit_size: The size of the floating-point number in bits, valid values are 16, 32, 64
+
+Example:
+
+ buf: [32]byte
+ val := 3.141592
+ fmt := 'f'
+ precision := 2
+ bit_size := 64
+ result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14"
+
+**Returns**
+- A byte slice containing the formatted string
+*/
generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte {
bits: u64
flt: ^Float_Info
@@ -95,8 +117,20 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int)
return format_digits(buf, shortest, neg, digs, prec, fmt)
}
+/*
+Converts a decimal floating-point number into a byte buffer with the given format
+**Inputs**
+- buf: The byte buffer to store the formatted number
+- shortest: If true, generates the shortest representation of the number
+- neg: If true, the number is negative
+- digs: The decimal number to be formatted
+- precision: The number of digits after the decimal point
+- fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G')
+**Returns**
+- A byte slice containing the formatted decimal floating-point number
+*/
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte {
Buffer :: struct {
b: []byte,
@@ -217,7 +251,15 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic
}
+/*
+Rounds the given decimal number to its shortest representation, considering the provided floating-point format
+**Inputs**
+- d: The decimal number to round
+- mant: The mantissa of the floating-point number
+- exp: The exponent of the floating-point number
+- flt: Pointer to the Float_Info structure containing information about the floating-point format
+*/
round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Info) {
if mant == 0 { // If mantissa is zero, the number is zero
d.count = 0
@@ -284,16 +326,26 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf
}
}
+/*
+Converts a decimal number to its floating-point representation with the given format and returns the resulting bits
+**Inputs**
+- d: Pointer to the decimal number to convert
+- info: Pointer to the Float_Info structure containing information about the floating-point format
+
+**Returns**
+- b: The bits representing the floating-point number
+- overflow: A boolean indicating whether an overflow occurred during conversion
+*/
@(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)< (bits: u64) {
+ bits = mant & (u64(1)< bool {
mant^ = 0
@@ -303,7 +355,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
mant: u64
exp: int
- if d.decimal_point == 0 {
+ if d.count == 0 {
mant = 0
exp = info.bias
b = end(d, mant, exp, info)
@@ -326,7 +378,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
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)
+ decimal.shift(d, -n)
exp += n
}
for d.decimal_point < 0 || d.decimal_point == 0 && d.digits[0] < '5' {
diff --git a/core/strconv/integers.odin b/core/strconv/integers.odin
index 2b6d0786e..98a432ac5 100644
--- a/core/strconv/integers.odin
+++ b/core/strconv/integers.odin
@@ -3,14 +3,24 @@ package strconv
Int_Flag :: enum {
Prefix,
Plus,
- Space,
}
Int_Flags :: bit_set[Int_Flag]
MAX_BASE :: 32
digits := "0123456789abcdefghijklmnopqrstuvwxyz"
+/*
+Determines whether the given unsigned 64-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
+**Inputs**
+- x: The unsigned 64-bit integer to check for negativity
+- is_signed: A boolean indicating if the input should be treated as a signed integer
+- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64)
+
+**Returns**
+- u: The absolute value of the input integer
+- neg: A boolean indicating whether the input integer is negative
+*/
is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) {
u = x
if is_signed {
@@ -37,7 +47,21 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64,
}
return
}
+/*
+Appends the string representation of an integer to a buffer with specified base, flags, and digit set.
+**Inputs**
+- buf: The buffer to append the integer representation to
+- x: The integer value to convert
+- base: The base for the integer representation (2 <= base <= MAX_BASE)
+- is_signed: A boolean indicating if the input should be treated as a signed integer
+- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64)
+- digits: The digit set used for the integer representation
+- flags: The Int_Flags bit set to control integer formatting
+
+**Returns**
+- The string containing the integer representation appended to the buffer
+*/
append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits")
@@ -73,15 +97,24 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i
i-=1; a[i] = '-'
case .Plus in flags:
i-=1; a[i] = '+'
- case .Space in flags:
- i-=1; a[i] = ' '
}
out := a[i:]
copy(buf, out)
return string(buf[0:len(out)])
}
+/*
+Determines whether the given unsigned 128-bit integer is a negative value by interpreting it as a signed integer with the specified bit size.
+**Inputs**
+- x: The unsigned 128-bit integer to check for negativity
+- is_signed: A boolean indicating if the input should be treated as a signed integer
+- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128)
+
+**Returns**
+- u: The absolute value of the input integer
+- neg: A boolean indicating whether the input integer is negative
+*/
is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) {
u = x
if is_signed {
@@ -112,9 +145,21 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u:
}
return
}
+/*
+Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set.
-// import "core:runtime"
+**Inputs**
+- buf: The buffer to append the integer representation to
+- x: The 128-bit integer value to convert
+- base: The base for the integer representation (2 <= base <= MAX_BASE)
+- is_signed: A boolean indicating if the input should be treated as a signed integer
+- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128)
+- digits: The digit set used for the integer representation
+- flags: The Int_Flags bit set to control integer formatting
+**Returns**
+- The string containing the integer representation appended to the buffer
+*/
append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string {
if base < 2 || base > MAX_BASE {
panic("strconv: illegal base passed to append_bits")
@@ -157,8 +202,6 @@ append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_si
i-=1; a[i] = '-'
case .Plus in flags:
i-=1; a[i] = '+'
- case .Space in flags:
- i-=1; a[i] = ' '
}
out := a[i:]
diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin
index 0f27d5a1d..eae9f9504 100644
--- a/core/strconv/strconv.odin
+++ b/core/strconv/strconv.odin
@@ -2,7 +2,19 @@ package strconv
import "core:unicode/utf8"
import "decimal"
+/*
+Parses a boolean value from the input string
+**Inputs**
+- s: The input string
+ - true: "1", "t", "T", "true", "TRUE", "True"
+ - false: "0", "f", "F", "false", "FALSE", "False"
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+**Returns**
+- result: The parsed boolean value (default: false)
+- ok: A boolean indicating whether the parsing was successful
+*/
parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) {
switch s {
case "1", "t", "T", "true", "TRUE", "True":
@@ -14,7 +26,14 @@ parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool)
}
return
}
+/*
+Finds the integer value of the given rune
+**Inputs**
+- r: The input rune to find the integer value of
+
+**Returns** The integer value of the given rune
+*/
_digit_value :: proc(r: rune) -> int {
ri := int(r)
v: int = 16
@@ -25,16 +44,31 @@ _digit_value :: proc(r: rune) -> int {
}
return v
}
+/*
+Parses an integer value from the input string in the given base, without a prefix
-// Parses an integer value from a string, in the given base, without a prefix.
-//
-// Returns ok=false if no numeric value of the appropriate base could be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_i64_of_base("-1234eeee", 10);
-// assert(n == -1234 && ok);
-// ```
+**Inputs**
+- str: The input string to parse the integer value from
+- base: The base of the integer value to be parsed (must be between 1 and 16)
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_i64_of_base_example :: proc() {
+ n, ok := strconv.parse_i64_of_base("-1234e3", 10)
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ -1234 false
+
+**Returns**
+- value: Parses an integer value from a string, in the given base, without a prefix.
+- ok: ok=false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
+*/
parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) {
assert(base <= 16, "base must be 1-16")
@@ -80,19 +114,34 @@ parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64,
ok = len(s) == 0
return
}
+/*
+Parses an integer value from the input string in base 10, unless there's a prefix
-// Parses a integer value from a string, in base 10, unless there's a prefix.
-//
-// Returns ok=false if a valid integer could not be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_i64_maybe_prefixed("1234");
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_i64_maybe_prefixed("0xeeee");
-// assert(n == 0xeeee && ok);
-// ```
+**Inputs**
+- str: The input string to parse the integer value from
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_i64_maybe_prefixed_example :: proc() {
+ n, ok := strconv.parse_i64_maybe_prefixed("1234")
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_i64_maybe_prefixed("0xeeee")
+ fmt.println(n,ok)
+ }
+
+Output:
+
+ 1234 true
+ 61166 true
+
+**Returns**
+- value: The parsed integer value
+- ok: ok=false if a valid integer could not be found, or if the input string contained more than just the number.
+*/
parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) {
s := str
defer if n != nil { n^ = len(str)-len(s) }
@@ -146,22 +195,38 @@ parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok:
ok = len(s) == 0
return
}
-
+//
parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base}
+/*
+Parses an unsigned 64-bit integer value from the input string without a prefix, using the specified base
-// Parses an unsigned integer value from a string, in the given base, and
-// without a prefix.
-//
-// Returns ok=false if no numeric value of the appropriate base could be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_u64_of_base("1234eeee", 10);
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_u64_of_base("5678eeee", 16);
-// assert(n == 0x5678eeee && ok);
-// ```
+**Inputs**
+- str: The input string to parse
+- base: The base of the number system to use for parsing
+ - Must be between 1 and 16 (inclusive)
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_u64_of_base_example :: proc() {
+ n, ok := strconv.parse_u64_of_base("1234e3", 10)
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_u64_of_base("5678eee",16)
+ fmt.println(n,ok)
+ }
+
+Output:
+
+ 1234 false
+ 90672878 true
+
+**Returns**
+- value: The parsed uint64 value
+- ok: A boolean indicating whether the parsing was successful
+*/
parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) {
assert(base <= 16, "base must be 1-16")
s := str
@@ -193,19 +258,37 @@ parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64,
ok = len(s) == 0
return
}
+/*
+Parses an unsigned 64-bit integer value from the input string, using the specified base or inferring the base from a prefix
-// Parses an unsigned integer value from a string in base 10, unless there's a prefix.
-//
-// Returns ok=false if a valid integer could not be found, if the value was negative,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_u64_maybe_prefixed("1234");
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_u64_maybe_prefixed("0xeeee");
-// assert(n == 0xeeee && ok);
-// ```
+**Inputs**
+- str: The input string to parse
+- base: The base of the number system to use for parsing (default: 0)
+ - If base is 0, it will be inferred based on the prefix in the input string (e.g. '0x' for hexadecimal)
+ - If base is not 0, it will be used for parsing regardless of any prefix in the input string
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_u64_maybe_prefixed_example :: proc() {
+ n, ok := strconv.parse_u64_maybe_prefixed("1234")
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_u64_maybe_prefixed("0xee")
+ fmt.println(n,ok)
+ }
+
+Output:
+
+ 1234 true
+ 238 true
+
+**Returns**
+- value: The parsed uint64 value
+- ok: ok=false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number.
+*/
parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) {
s := str
defer if n != nil { n^ = len(str)-len(s) }
@@ -248,26 +331,42 @@ parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok:
ok = len(s) == 0
return
}
-
+//
parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base}
+/*
+Parses a signed integer value from the input string, using the specified base or inferring the base from a prefix
-// Parses an integer value from a string in the given base, or
-// - if the string has a prefix (e.g: '0x') then that will determine the base;
-// - otherwise, assumes base 10.
-//
-// Returns ok=false if no appropriate value could be found, or if the input string
-// contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_int("1234"); // without prefix, inferred base 10
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_int("ffff", 16); // without prefix, explicit base
-// assert(n == 0xffff && ok);
-//
-// n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base
-// assert(n == 0xffff && ok);
-// ```
+**Inputs**
+- s: The input string to parse
+- base: The base of the number system to use for parsing (default: 0)
+ - If base is 0, it will be inferred based on the prefix in the input string (e.g. '0x' for hexadecimal)
+ - If base is not 0, it will be used for parsing regardless of any prefix in the input string
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_int_example :: proc() {
+ n, ok := strconv.parse_int("1234") // without prefix, inferred base 10
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_int("ffff", 16) // without prefix, explicit base
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_int("0xffff") // with prefix and inferred base
+ fmt.println(n,ok)
+ }
+
+Output:
+
+ 1234 true
+ 65535 true
+ 65535 true
+
+**Returns**
+- value: The parsed int value
+- ok: `false` if no appropriate value could be found, or if the input string contained more than just the number.
+*/
parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) {
v: i64 = ---
switch base {
@@ -277,27 +376,41 @@ parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool)
value = int(v)
return
}
+/*
+Parses an unsigned integer value from the input string, using the specified base or inferring the base from a prefix
+**Inputs**
+- s: The input string to parse
+- base: The base of the number system to use for parsing (default: 0, inferred)
+ - If base is 0, it will be inferred based on the prefix in the input string (e.g. '0x' for hexadecimal)
+ - If base is not 0, it will be used for parsing regardless of any prefix in the input string
-// Parses an unsigned integer value from a string in the given base, or
-// - if the string has a prefix (e.g: '0x') then that will determine the base;
-// - otherwise, assumes base 10.
-//
-// Returns ok=false if:
-// - no appropriate value could be found; or
-// - the value was negative.
-// - the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_uint("1234"); // without prefix, inferred base 10
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_uint("ffff", 16); // without prefix, explicit base
-// assert(n == 0xffff && ok);
-//
-// n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base
-// assert(n == 0xffff && ok);
-// ```
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_uint_example :: proc() {
+ n, ok := strconv.parse_uint("1234") // without prefix, inferred base 10
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_uint("ffff", 16) // without prefix, explicit base
+ fmt.println(n,ok)
+
+ n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base
+ fmt.println(n,ok)
+ }
+
+Output:
+
+ 1234 true
+ 65535 true
+ 65535 true
+
+**Returns**
+
+value: The parsed uint value
+ok: `false` if no appropriate value could be found; the value was negative; he input string contained more than just the number
+*/
parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) {
v: u64 = ---
switch base {
@@ -307,17 +420,31 @@ parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: boo
value = uint(v)
return
}
+/*
+Parses an integer value from a string in the given base, without any prefix
+**Inputs**
+- str: The input string containing the integer value
+- base: The base (radix) to use for parsing the integer (1-16)
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
-// Parses an integer value from a string, in the given base, without a prefix.
-//
-// Returns ok=false if no numeric value of the appropriate base could be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_i128_of_base("-1234eeee", 10);
-// assert(n == -1234 && ok);
-// ```
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_i128_of_base_example :: proc() {
+ n, ok := strconv.parse_i128_of_base("-1234eeee", 10)
+ fmt.println(n,ok)
+ }
+
+Output:
+
+ -1234 false
+
+**Returns**
+- value: The parsed i128 value
+- ok: false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
+*/
parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) {
assert(base <= 16, "base must be 1-16")
@@ -361,19 +488,34 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12
ok = len(s) == 0
return
}
+/*
+Parses an integer value from a string in base 10, unless there's a prefix
-// Parses a integer value from a string, in base 10, unless there's a prefix.
-//
-// Returns ok=false if a valid integer could not be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_i128_maybe_prefixed("1234");
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_i128_maybe_prefixed("0xeeee");
-// assert(n == 0xeeee && ok);
-// ```
+**Inputs**
+- str: The input string containing the integer value
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_i128_maybe_prefixed_example :: proc() {
+ n, ok := strconv.parse_i128_maybe_prefixed("1234")
+ fmt.println(n, ok)
+
+ n, ok = strconv.parse_i128_maybe_prefixed("0xeeee")
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 1234 true
+ 61166 true
+
+**Returns**
+- value: The parsed i128 value
+- ok: `false` if a valid integer could not be found, or if the input string contained more than just the number.
+*/
parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) {
s := str
defer if n != nil { n^ = len(str)-len(s) }
@@ -427,22 +569,37 @@ parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, o
ok = len(s) == 0
return
}
-
+//
parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base}
+/*
+Parses an unsigned integer value from a string in the given base, without any prefix
-// Parses an unsigned integer value from a string, in the given base, and
-// without a prefix.
-//
-// Returns ok=false if no numeric value of the appropriate base could be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_u128_of_base("1234eeee", 10);
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_u128_of_base("5678eeee", 16);
-// assert(n == 0x5678eeee && ok);
-// ```
+**Inputs**
+- str: The input string containing the integer value
+- base: The base (radix) to use for parsing the integer (1-16)
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_u128_of_base_example :: proc() {
+ n, ok := strconv.parse_u128_of_base("1234eeee", 10)
+ fmt.println(n, ok)
+
+ n, ok = strconv.parse_u128_of_base("5678eeee", 16)
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 1234 false
+ 1450766062 true
+
+**Returns**
+- value: The parsed u128 value
+- ok: `false` if no numeric value of the appropriate base could be found, or if the input string contained more than just the number.
+*/
parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) {
assert(base <= 16, "base must be 1-16")
s := str
@@ -474,19 +631,34 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12
ok = len(s) == 0
return
}
+/*
+Parses an unsigned integer value from a string in base 10, unless there's a prefix
-// Parses an unsigned integer value from a string in base 10, unless there's a prefix.
-//
-// Returns ok=false if a valid integer could not be found, if the value was negative,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_u128_maybe_prefixed("1234");
-// assert(n == 1234 && ok);
-//
-// n, ok = strconv.parse_u128_maybe_prefixed("0xeeee");
-// assert(n == 0xeeee && ok);
-// ```
+**Inputs**
+- str: The input string containing the integer value
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_u128_maybe_prefixed_example :: proc() {
+ n, ok := strconv.parse_u128_maybe_prefixed("1234")
+ fmt.println(n, ok)
+
+ n, ok = strconv.parse_u128_maybe_prefixed("5678eeee")
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 1234 true
+ 5678 false
+
+**Returns**
+- value: The parsed u128 value
+- ok: false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number.
+*/
parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) {
s := str
defer if n != nil { n^ = len(str)-len(s) }
@@ -529,46 +701,153 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o
ok = len(s) == 0
return
}
-
+//
parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base}
+/*
+Converts a byte to lowercase
+**Inputs**
+- ch: A byte character to be converted to lowercase.
+**Returns**
+- A lowercase byte character.
+*/
@(private)
lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch }
+/*
+Parses a 32-bit floating point number from a string
+**Inputs**
+- s: The input string containing a 32-bit floating point number.
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
+Example:
-// Parses a 32-bit floating point number from a string.
-//
-// Returns ok=false if a base 10 float could not be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_f32("12.34eee");
-// assert(n == 12.34 && ok);
-//
-// n, ok = strconv.parse_f32("12.34");
-// assert(n == 12.34 && ok);
-// ```
+ import "core:fmt"
+ import "core:strconv"
+ parse_f32_example :: proc() {
+ n, ok := strconv.parse_f32("1234eee")
+ fmt.println(n, ok)
+
+ n, ok = strconv.parse_f32("5678e2")
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 0.000 false
+ 567800.000 true
+
+**Returns**
+- value: The parsed 32-bit floating point number.
+- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
+*/
parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) {
v: f64 = ---
v, ok = parse_f64(s, n)
return f32(v), ok
}
+/*
+Parses a 64-bit floating point number from a string
-// Parses a 64-bit floating point number from a string.
-//
-// Returns ok=false if a base 10 float could not be found,
-// or if the input string contained more than just the number.
-//
-// ```
-// n, ok := strconv.parse_f32("12.34eee");
-// assert(n == 12.34 && ok);
-//
-// n, ok = strconv.parse_f32("12.34");
-// assert(n == 12.34 && ok);
-// ```
+**Inputs**
+- str: The input string containing a 64-bit floating point number.
+- n: An optional pointer to an int to store the length of the parsed substring (default: nil).
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_f64_example :: proc() {
+ n, ok := strconv.parse_f64("1234eee")
+ fmt.println(n, ok)
+
+ n, ok = strconv.parse_f64("5678e2")
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 0.000 false
+ 567800.000 true
+
+**Returns**
+- value: The parsed 64-bit floating point number.
+- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
+*/
parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
+ nr: int
+ value, nr, ok = parse_f64_prefix(str)
+ if ok && len(str) != nr {
+ ok = false
+ }
+ if n != nil { n^ = nr }
+ return
+}
+/*
+Parses a 32-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful
+
+**Inputs**
+- str: The input string containing a 32-bit floating point number.
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_f32_prefix_example :: proc() {
+ n, _, ok := strconv.parse_f32_prefix("1234eee")
+ fmt.println(n, ok)
+
+ n, _, ok = strconv.parse_f32_prefix("5678e2")
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 0.000 false
+ 567800.000 true
+
+
+**Returns**
+- value: The parsed 32-bit floating point number.
+- nr: The length of the parsed substring.
+- ok: A boolean indicating whether the parsing was successful.
+*/
+parse_f32_prefix :: proc(str: string) -> (value: f32, nr: int, ok: bool) {
+ f: f64
+ f, nr, ok = parse_f64_prefix(str)
+ value = f32(f)
+ return
+}
+/*
+Parses a 64-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful
+
+**Inputs**
+- str: The input string containing a 64-bit floating point number.
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ parse_f64_prefix_example :: proc() {
+ n, _, ok := strconv.parse_f64_prefix("12.34eee")
+ fmt.println(n, ok)
+
+ n, _, ok = strconv.parse_f64_prefix("12.34e2")
+ fmt.println(n, ok)
+ }
+
+Output:
+
+ 0.000 false
+ 1234.000 true
+
+**Returns**
+- value: The parsed 64-bit floating point number.
+- nr: The length of the parsed substring.
+- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number.
+*/
+parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int {
n := len(prefix)
if n > len(s) {
@@ -678,8 +957,8 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
saw_digits = true
nd += 1
if nd_mant < MAX_MANT_DIGITS {
- MAX_MANT_DIGITS *= 16
- MAX_MANT_DIGITS += int(lower(c) - 'a' + 10)
+ mantissa *= 16
+ mantissa += u64(lower(c) - 'a' + 10)
nd_mant += 1
} else {
trunc = true
@@ -729,12 +1008,11 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
if mantissa != 0 {
exp = decimal_point - nd_mant
}
- // TODO(bill): check underscore correctness
ok = true
return
}
- parse_hex :: proc(s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
+ parse_hex :: proc "contextless" (s: string, mantissa: u64, exp: int, neg, trunc: bool) -> (f64, bool) {
info := &_f64_info
mantissa, exp := mantissa, exp
@@ -751,7 +1029,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
mantissa |= 1
}
- for mantissa >> (info.mantbits+2) == 0 {
+ for mantissa != 0 && mantissa >> (info.mantbits+2) == 0 {
mantissa = mantissa>>1 | mantissa&1
exp += 1
}
@@ -795,9 +1073,6 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
}
- nr: int
- defer if n != nil { n^ = nr }
-
if value, nr, ok = check_special(str); ok {
return
}
@@ -808,7 +1083,8 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
mantissa, exp, neg, trunc, hex, nr = parse_components(str) or_return
if hex {
- return parse_hex(str, mantissa, exp, neg, trunc)
+ value, ok = parse_hex(str, mantissa, exp, neg, trunc)
+ return
}
trunc_block: if !trunc {
@@ -819,7 +1095,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
}
if mantissa>>_f64_info.mantbits != 0 {
- return
+ break trunc_block
}
f := f64(mantissa)
if neg {
@@ -827,7 +1103,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
}
switch {
case exp == 0:
- return f, true
+ return f, nr, true
case exp > 0 && exp <= 15+22:
if exp > 22 {
f *= pow10[exp-22]
@@ -836,12 +1112,11 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
if f > 1e15 || f < 1e-15 {
break trunc_block
}
- return f * pow10[exp], true
+ return f * pow10[exp], nr, true
case -22 <= exp && exp < 0:
- return f / pow10[-exp], true
+ return f / pow10[-exp], nr, true
}
}
-
d: decimal.Decimal
decimal.set(&d, str[:nr])
b, overflow := decimal_to_float_bits(&d, &_f64_info)
@@ -849,8 +1124,30 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) {
ok = !overflow
return
}
+/*
+Appends a boolean value as a string to the given buffer
+**Inputs**
+- buf: The buffer to append the boolean value to
+- b: The boolean value to be appended
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ append_bool_example :: proc() {
+ buf: [6]byte
+ result := strconv.append_bool(buf[:], true)
+ fmt.println(result, buf)
+ }
+
+Output:
+
+ true [116, 114, 117, 101, 0, 0]
+
+**Returns**
+- The resulting string after appending the boolean value
+*/
append_bool :: proc(buf: []byte, b: bool) -> string {
n := 0
if b {
@@ -860,32 +1157,197 @@ append_bool :: proc(buf: []byte, b: bool) -> string {
}
return string(buf[:n])
}
+/*
+Appends an unsigned integer value as a string to the given buffer with the specified base
+**Inputs**
+- buf: The buffer to append the unsigned integer value to
+- u: The unsigned integer value to be appended
+- base: The base to use for converting the integer value
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ append_uint_example :: proc() {
+ buf: [4]byte
+ result := strconv.append_uint(buf[:], 42, 16)
+ fmt.println(result, buf)
+ }
+
+Output:
+
+ 2a [50, 97, 0, 0]
+
+**Returns**
+- The resulting string after appending the unsigned integer value
+*/
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil)
}
+/*
+Appends a signed integer value as a string to the given buffer with the specified base
+
+**Inputs**
+- buf: The buffer to append the signed integer value to
+- i: The signed integer value to be appended
+- base: The base to use for converting the integer value
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ append_int_example :: proc() {
+ buf: [4]byte
+ result := strconv.append_int(buf[:], -42, 10)
+ fmt.println(result, buf)
+ }
+
+Output:
+
+ -42 [45, 52, 50, 0]
+
+**Returns**
+- The resulting string after appending the signed integer value
+*/
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil)
}
+/*
+Converts an integer value to a string and stores it in the given buffer
+**Inputs**
+- buf: The buffer to store the resulting string
+- i: The integer value to be converted
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ itoa_example :: proc() {
+ buf: [4]byte
+ result := strconv.itoa(buf[:], 42)
+ fmt.println(result, buf) // "42"
+ }
+
+Output:
+
+ 42 [52, 50, 0, 0]
+
+**Returns**
+- The resulting string after converting the integer value
+*/
itoa :: proc(buf: []byte, i: int) -> string {
return append_int(buf, i64(i), 10)
}
+/*
+Converts a string to an integer value
+
+**Inputs**
+- s: The string to be converted
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ atoi_example :: proc() {
+ fmt.println(strconv.atoi("42"))
+ }
+
+Output:
+
+ 42
+
+**Returns**
+- The resulting integer value
+*/
atoi :: proc(s: string) -> int {
v, _ := parse_int(s)
return v
}
+/*
+Converts a string to a float64 value
+
+**Inputs**
+- s: The string to be converted
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ atof_example :: proc() {
+ fmt.println(strconv.atof("3.14"))
+ }
+
+Output:
+
+ 3.140
+
+**Returns**
+- The resulting float64 value after converting the string
+*/
atof :: proc(s: string) -> f64 {
v, _ := parse_f64(s)
return v
}
-
+// Alias to `append_float`
ftoa :: append_float
+/*
+Appends a float64 value as a string to the given buffer with the specified format and precision
+
+**Inputs**
+- buf: The buffer to append the float64 value to
+- f: The float64 value to be appended
+- fmt: The byte specifying the format to use for the conversion
+- prec: The precision to use for the conversion
+- bit_size: The size of the float in bits (32 or 64)
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ append_float_example :: proc() {
+ buf: [8]byte
+ result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64)
+ fmt.println(result, buf)
+ }
+
+Output:
+
+ +3.14 [43, 51, 46, 49, 52, 0, 0, 0]
+
+**Returns**
+- The resulting string after appending the float
+*/
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return string(generic_ftoa(buf, f, fmt, prec, bit_size))
}
+/*
+Appends a quoted string representation of the input string to a given byte slice and returns the result as a string
+**Inputs**
+- buf: The byte slice to which the quoted string will be appended
+- str: The input string to be quoted
+!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ quote_example :: proc() {
+ buf: [20]byte
+ result := strconv.quote(buf[:], "hello")
+ fmt.println(result, buf)
+ }
+
+Output:
+
+ "'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0]
+
+**Returns**
+- The resulting string after appending the quoted string representation
+*/
quote :: proc(buf: []byte, str: string) -> string {
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
if i^ >= len(buf) {
@@ -923,7 +1385,30 @@ quote :: proc(buf: []byte, str: string) -> string {
write_byte(buf, &i, c)
return string(buf[:i])
}
+/*
+Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string
+**Inputs**
+- buf: The byte slice to which the quoted rune will be appended
+- r: The input rune to be quoted
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ quote_rune_example :: proc() {
+ buf: [4]byte
+ result := strconv.quote_rune(buf[:], 'A')
+ fmt.println(result, buf)
+ }
+
+Output:
+
+ 'A' [39, 65, 39, 0]
+
+**Returns**
+- The resulting string after appending the quoted rune representation
+*/
quote_rune :: proc(buf: []byte, r: rune) -> string {
write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) {
if i^ < len(buf) {
@@ -979,10 +1464,35 @@ quote_rune :: proc(buf: []byte, r: rune) -> string {
return string(buf[:i])
}
+/*
+Unquotes a single character from the input string, considering the given quote character
+**Inputs**
+- str: The input string containing the character to unquote
+- quote: The quote character to consider (e.g., '"')
+Example:
+ import "core:fmt"
+ import "core:strconv"
+ unquote_char_example :: proc() {
+ src:="\'The\' raven"
+ r, multiple_bytes, tail_string, success := strconv.unquote_char(src,'\'')
+ fmt.println("Source:", src)
+ fmt.printf("r: <%v>, multiple_bytes:%v, tail_string:<%s>, success:%v\n",r, multiple_bytes, tail_string, success)
+ }
+Output:
+
+ Source: 'The' raven
+ r: <'>, multiple_bytes:false, tail_string:, success:true
+
+**Returns**
+- r: The unquoted rune
+- multiple_bytes: A boolean indicating if the rune has multiple bytes
+- tail_string: The remaining portion of the input string after unquoting the character
+- success: A boolean indicating whether the unquoting was successful
+*/
unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) {
hex_to_int :: proc(c: byte) -> int {
switch c {
@@ -1077,7 +1587,54 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
tail_string = s
return
}
+/*
+Unquotes the input string considering any type of quote character and returns the unquoted string
+**Inputs**
+- lit: The input string to unquote
+- allocator: (default: context.allocator)
+
+WARNING: This procedure gives unexpected results if the quotes are not the first and last characters.
+
+Example:
+
+ import "core:fmt"
+ import "core:strconv"
+ unquote_string_example :: proc() {
+ src:="\"The raven Huginn is black.\""
+ s, allocated, ok := strconv.unquote_string(src)
+ fmt.println(src)
+ fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n\n", s, allocated, ok)
+
+ src="\'The raven Huginn\' is black."
+ s, allocated, ok = strconv.unquote_string(src)
+ fmt.println(src)
+ fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n\n", s, allocated, ok)
+
+ src="The raven \'Huginn\' is black."
+ s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results
+ fmt.println(src)
+ fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok)
+ }
+
+Output:
+
+ "The raven Huginn is black."
+ Unquoted: , alloc:false, ok:true
+
+ 'The raven Huginn' is black.
+ Unquoted: , alloc:false, ok:true
+
+ The raven 'Huginn' is black.
+ Unquoted: , alloc:false, ok:true
+
+**Returns**
+- res: The resulting unquoted string
+- allocated: A boolean indicating if the resulting string was allocated using the provided allocator
+- success: A boolean indicating whether the unquoting was successful
+
+NOTE: If unquoting is unsuccessful, the allocated memory for the result will be freed.
+*/
unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) {
contains_rune :: proc(s: string, r: rune) -> int {
for c, offset in s {
diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin
index 9b59666f3..247b38527 100644
--- a/core/strings/ascii_set.odin
+++ b/core/strings/ascii_set.odin
@@ -3,9 +3,22 @@ package strings
import "core:unicode/utf8"
+/*
+Ascii_Set is designed to store ASCII characters efficiently as a bit-array
+Each bit in the array corresponds to a specific ASCII character, where the value of the bit (0 or 1)
+indicates if the character is present in the set or not.
+*/
Ascii_Set :: distinct [8]u32
+/*
+Creates an Ascii_Set with unique characters from the input string.
-// create an ascii set of all unique characters in the string
+Inputs:
+- chars: A string containing characters to include in the Ascii_Set.
+
+Returns:
+- as: An Ascii_Set with unique characters from the input string.
+- ok: false if any character in the input string is not a valid ASCII character.
+*/
ascii_set_make :: proc(chars: string) -> (as: Ascii_Set, ok: bool) #no_bounds_check {
for i in 0.. (as: Ascii_Set, ok: bool) #no_bounds_ch
ok = true
return
}
+/*
+Determines if a given char is contained within an Ascii_Set.
-// returns true when the `c` byte is contained in the `as` ascii set
-ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> bool #no_bounds_check {
+Inputs:
+- as: The Ascii_Set to search.
+- c: The char to check for in the Ascii_Set.
+
+Returns:
+- res: A boolean indicating if the byte is contained in the Ascii_Set (true) or not (false).
+*/
+ascii_set_contains :: proc(as: Ascii_Set, c: byte) -> (res: bool) #no_bounds_check {
return as[c>>5] & (1<<(c&31)) != 0
-}
\ No newline at end of file
+}
diff --git a/core/strings/builder.odin b/core/strings/builder.odin
index 02177ba8c..28c56f6f9 100644
--- a/core/strings/builder.odin
+++ b/core/strings/builder.odin
@@ -3,176 +3,352 @@ package strings
import "core:runtime"
import "core:unicode/utf8"
import "core:strconv"
+import "core:mem"
import "core:io"
-
-Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
-
/*
- dynamic byte buffer / string builder with helper procedures
- the dynamic array is wrapped inside the struct to be more opaque
- you can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
+Type definition for a procedure that flushes a Builder
+
+Inputs:
+- b: A pointer to the Builder
+
+Returns:
+A boolean indicating whether the Builder should be reset
+*/
+Builder_Flush_Proc :: #type proc(b: ^Builder) -> (do_reset: bool)
+/*
+A dynamic byte buffer / string builder with helper procedures
+The dynamic array is wrapped inside the struct to be more opaque
+You can use `fmt.sbprint*` procedures with a `^strings.Builder` directly
*/
Builder :: struct {
buf: [dynamic]byte,
}
+/*
+Produces a Builder with a default length of 0 and cap of 16
-// return a builder, default length 0 / cap 16 are done through make
-builder_make_none :: proc(allocator := context.allocator) -> Builder {
- return Builder{buf=make([dynamic]byte, allocator)}
+*Allocates Using Provided Allocator*
+
+Inputs:
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The new Builder
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+builder_make_none :: proc(allocator := context.allocator) -> (res: Builder, err: mem.Allocator_Error) #optional_allocator_error {
+ return Builder{buf=make([dynamic]byte, allocator) or_return }, nil
}
+/*
+Produces a Builder with a specified length and cap of max(16,len) byte buffer
-// return a builder, with a set length `len` and cap 16 byte buffer
-builder_make_len :: proc(len: int, allocator := context.allocator) -> Builder {
- return Builder{buf=make([dynamic]byte, len, allocator)}
+*Allocates Using Provided Allocator*
+
+Inputs:
+- len: The desired length of the Builder's buffer
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The new Builder
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+builder_make_len :: proc(len: int, allocator := context.allocator) -> (res: Builder, err: mem.Allocator_Error) #optional_allocator_error {
+ return Builder{buf=make([dynamic]byte, len, allocator) or_return }, nil
}
+/*
+Produces a Builder with a specified length and cap
-// return a builder, with a set length `len` byte buffer and a custom `cap`
-builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
- return Builder{buf=make([dynamic]byte, len, cap, allocator)}
+*Allocates Using Provided Allocator*
+
+Inputs:
+- len: The desired length of the Builder's buffer
+- cap: The desired capacity of the Builder's buffer, cap is max(cap, len)
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The new Builder
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+builder_make_len_cap :: proc(len, cap: int, allocator := context.allocator) -> (res: Builder, err: mem.Allocator_Error) #optional_allocator_error {
+ return Builder{buf=make([dynamic]byte, len, cap, allocator) or_return }, nil
}
+/*
+Produces a String Builder
-// overload simple `builder_make_*` with or without len / cap parameters
+*Allocates Using Provided Allocator*
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+ builder_make_example :: proc() {
+ sb := strings.builder_make()
+ strings.write_byte(&sb, 'a')
+ strings.write_string(&sb, " slice of ")
+ strings.write_f64(&sb, 3.14,'g',true) // See `fmt.fmt_float` byte codes
+ strings.write_string(&sb, " is ")
+ strings.write_int(&sb, 180)
+ strings.write_rune(&sb,'°')
+ the_string :=strings.to_string(sb)
+ fmt.println(the_string)
+ }
+
+Output:
+
+ a slice of +3.14 is 180°
+
+*/
builder_make :: proc{
builder_make_none,
builder_make_len,
builder_make_len_cap,
}
+/*
+Initializes a Builder with a length of 0 and cap of 16
+It replaces the existing `buf`
-// initialize a builder, default length 0 / cap 16 are done through make
-// replaces the existing `buf`
-builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> ^Builder {
- b.buf = make([dynamic]byte, allocator)
- return b
+*Allocates Using Provided Allocator*
+
+Inputs:
+- b: A pointer to the Builder
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A pointer to the initialized Builder
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+builder_init_none :: proc(b: ^Builder, allocator := context.allocator) -> (res: ^Builder, err: mem.Allocator_Error) #optional_allocator_error {
+ b.buf = make([dynamic]byte, allocator) or_return
+ return b, nil
}
+/*
+Initializes a Builder with a specified length and cap, which is max(len,16)
+It replaces the existing `buf`
-// initialize a builder, with a set length `len` and cap 16 byte buffer
-// replaces the existing `buf`
-builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> ^Builder {
- b.buf = make([dynamic]byte, len, allocator)
- return b
+*Allocates Using Provided Allocator*
+
+Inputs:
+- b: A pointer to the Builder
+- len: The desired length of the Builder's buffer
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A pointer to the initialized Builder
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+builder_init_len :: proc(b: ^Builder, len: int, allocator := context.allocator) -> (res: ^Builder, err: mem.Allocator_Error) #optional_allocator_error {
+ b.buf = make([dynamic]byte, len, allocator) or_return
+ return b, nil
}
+/*
+Initializes a Builder with a specified length and cap
+It replaces the existing `buf`
-// initialize a builder, with a set length `len` byte buffer and a custom `cap`
-// replaces the existing `buf`
-builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> ^Builder {
- b.buf = make([dynamic]byte, len, cap, allocator)
- return b
+Inputs:
+- b: A pointer to the Builder
+- len: The desired length of the Builder's buffer
+- cap: The desired capacity of the Builder's buffer, actual max(len,cap)
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A pointer to the initialized Builder
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+builder_init_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) -> (res: ^Builder, err: mem.Allocator_Error) #optional_allocator_error {
+ b.buf = make([dynamic]byte, len, cap, allocator) or_return
+ return b, nil
}
-
-// overload simple `builder_init_*` with or without len / ap parameters
+// Overload simple `builder_init_*` with or without len / ap parameters
builder_init :: proc{
builder_init_none,
builder_init_len,
builder_init_len_cap,
}
-
@(private)
-_builder_stream_vtable := io.Stream_VTable{
- impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- b := (^Builder)(s.stream_data)
- n = write_bytes(b, p)
- if n < len(p) {
+_builder_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ b := (^Builder)(stream_data)
+ #partial switch mode {
+ case .Write:
+ n = i64(write_bytes(b, p))
+ if n < i64(len(p)) {
err = .EOF
}
return
- },
- impl_write_byte = proc(s: io.Stream, c: byte) -> (err: io.Error) {
- b := (^Builder)(s.stream_data)
- n := write_byte(b, c)
- if n == 0 {
- err = .EOF
- }
+ case .Size:
+ n = i64(len(b.buf))
return
- },
- impl_size = proc(s: io.Stream) -> i64 {
- b := (^Builder)(s.stream_data)
- return i64(len(b.buf))
- },
- impl_destroy = proc(s: io.Stream) -> io.Error {
- b := (^Builder)(s.stream_data)
- delete(b.buf)
- return .None
- },
+ case .Destroy:
+ builder_destroy(b)
+ return
+ case .Query:
+ return io.query_utility({.Write, .Size, .Destroy, .Query})
+ }
+ return 0, .Empty
}
-// return an `io.Stream` from a builder
-to_stream :: proc(b: ^Builder) -> io.Stream {
- return io.Stream{stream_vtable=&_builder_stream_vtable, stream_data=b}
-}
+/*
+Returns an io.Stream from a Builder
-// return an `io.Writer` from a builder
-to_writer :: proc(b: ^Builder) -> io.Writer {
+Inputs:
+- b: A pointer to the Builder
+
+Returns:
+- res: the io.Stream
+*/
+to_stream :: proc(b: ^Builder) -> (res: io.Stream) {
+ return io.Stream{procedure=_builder_stream_proc, data=b}
+}
+/*
+Returns an io.Writer from a Builder
+
+Inputs:
+- b: A pointer to the Builder
+
+Returns:
+- res: The io.Writer
+*/
+to_writer :: proc(b: ^Builder) -> (res: io.Writer) {
return io.to_writer(to_stream(b))
}
+/*
+Deletes the Builder byte buffer content
-// delete and clear the builder byte buffer content
+Inputs:
+- b: A pointer to the Builder
+*/
builder_destroy :: proc(b: ^Builder) {
delete(b.buf)
- clear(&b.buf)
+ b.buf = nil
}
+/*
+Reserves the Builder byte buffer to a specific capacity, when it's higher than before
-// reserve the builfer byte buffer to a specific cap, when it's higher than before
+Inputs:
+- b: A pointer to the Builder
+- cap: The desired capacity for the Builder's buffer
+*/
builder_grow :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap)
}
+/*
+Clears the Builder byte buffer content (sets len to zero)
-// clear the builder byte buffer content
+Inputs:
+- b: A pointer to the Builder
+*/
builder_reset :: proc(b: ^Builder) {
clear(&b.buf)
}
-
/*
- create an empty builder with the same slice length as its cap
- uses the `mem.nil_allocator` to avoid allocation and keep a fixed length
- used in `fmt.bprint*`
-
- bytes: [8]byte // <-- gets filled
- builder := strings.builder_from_bytes(bytes[:])
- strings.write_byte(&builder, 'a') -> "a"
- strings.write_byte(&builder, 'b') -> "ab"
-*/
-builder_from_bytes :: proc(backing: []byte) -> Builder {
- s := transmute(runtime.Raw_Slice)backing
- d := runtime.Raw_Dynamic_Array{
- data = s.data,
- len = 0,
- cap = s.len,
- allocator = runtime.nil_allocator(),
- }
- return Builder{
- buf = transmute([dynamic]byte)d,
- }
-}
-builder_from_slice :: builder_from_bytes
+Creates a Builder from a slice of bytes with the same slice length as its capacity. Used in fmt.bprint*
-// cast the builder byte buffer to a string and return it
-to_string :: proc(b: Builder) -> string {
+*Uses Nil Allocator - Does NOT allocate*
+
+Inputs:
+- backing: A slice of bytes to be used as the backing buffer
+
+Returns:
+- res: The new Builder
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+ builder_from_bytes_example :: proc() {
+ bytes: [8]byte // <-- gets filled
+ builder := strings.builder_from_bytes(bytes[:])
+ strings.write_byte(&builder, 'a')
+ fmt.println(strings.to_string(builder)) // -> "a"
+ strings.write_byte(&builder, 'b')
+ fmt.println(strings.to_string(builder)) // -> "ab"
+ }
+
+Output:
+
+ a
+ ab
+
+*/
+builder_from_bytes :: proc(backing: []byte) -> (res: Builder) {
+ return Builder{ buf = mem.buffer_from_slice(backing) }
+}
+// Alias to `builder_from_bytes`
+builder_from_slice :: builder_from_bytes
+/*
+Casts the Builder byte buffer to a string and returns it
+
+Inputs:
+- b: A Builder
+
+Returns:
+- res: The contents of the Builder's buffer, as a string
+*/
+to_string :: proc(b: Builder) -> (res: string) {
return string(b.buf[:])
}
+/*
+Returns the length of the Builder's buffer, in bytes
-// return the length of the builder byte buffer
-builder_len :: proc(b: Builder) -> int {
+Inputs:
+- b: A Builder
+
+Returns:
+- res: The length of the Builder's buffer
+*/
+builder_len :: proc(b: Builder) -> (res: int) {
return len(b.buf)
}
+/*
+Returns the capacity of the Builder's buffer, in bytes
-// return the cap of the builder byte buffer
-builder_cap :: proc(b: Builder) -> int {
+Inputs:
+- b: A Builder
+
+Returns:
+- res: The capacity of the Builder's buffer
+*/
+builder_cap :: proc(b: Builder) -> (res: int) {
return cap(b.buf)
}
+/*
+The free space left in the Builder's buffer, in bytes
-// returns the space left in the builder byte buffer to use up
-builder_space :: proc(b: Builder) -> int {
+Inputs:
+- b: A Builder
+
+Returns:
+- res: The available space left in the Builder's buffer
+*/
+builder_space :: proc(b: Builder) -> (res: int) {
return cap(b.buf) - len(b.buf)
}
-
/*
- appends a byte to the builder, returns the append diff
+Appends a byte to the Builder and returns the number of bytes appended
+
+Inputs:
+- b: A pointer to the Builder
+- x: The byte to be appended
+
+Returns:
+- n: The number of bytes appended
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_byte_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_byte(&builder, 'a') // 1
+ strings.write_byte(&builder, 'b') // 1
+ fmt.println(strings.to_string(builder)) // -> ab
+ }
+
+Output:
+
+ ab
- builder := strings.builder_make()
- strings.write_byte(&builder, 'a') // 1
- strings.write_byte(&builder, 'b') // 1
- strings.write_byte(&builder, 'c') // 1
- fmt.println(strings.to_string(builder)) // -> abc
*/
write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
n0 := len(b.buf)
@@ -180,14 +356,29 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
n1 := len(b.buf)
return n1-n0
}
-
/*
- appends a slice of bytes to the builder, returns the append diff
+Appends a slice of bytes to the Builder and returns the number of bytes appended
- builder := strings.builder_make()
- bytes := [?]byte { 'a', 'b', 'c' }
- strings.write_bytes(&builder, bytes[:]) // 3
- fmt.println(strings.to_string(builder)) // -> abc
+Inputs:
+- b: A pointer to the Builder
+- x: The slice of bytes to be appended
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_bytes_example :: proc() {
+ builder := strings.builder_make()
+ bytes := [?]byte { 'a', 'b', 'c' }
+ strings.write_bytes(&builder, bytes[:]) // 3
+ fmt.println(strings.to_string(builder)) // -> abc
+ }
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of bytes appended
*/
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n0 := len(b.buf)
@@ -195,42 +386,100 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n1 := len(b.buf)
return n1-n0
}
-
/*
- appends a single rune into the builder, returns written rune size and an `io.Error`
+Appends a single rune to the Builder and returns the number of bytes written and an `io.Error`
+
+Inputs:
+- b: A pointer to the Builder
+- r: The rune to be appended
+
+Returns:
+- res: The number of bytes written
+- err: An io.Error if one occured, `nil` otherwise
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_rune_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_rune(&builder, 'Ƥ') // 2 None
+ strings.write_rune(&builder, 'b') // 1 None
+ fmt.println(strings.to_string(builder)) // -> Ƥb
+ }
+
+Output:
+
+ Ƥb
- builder := strings.builder_make()
- strings.write_rune(&builder, 'Ƥ') // 2 None
- strings.write_rune(&builder, 'b') // 1 None
- strings.write_rune(&builder, 'c') // 1 None
- fmt.println(strings.to_string(builder)) // -> Ƥbc
*/
-write_rune :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
+write_rune :: proc(b: ^Builder, r: rune) -> (res: int, err: io.Error) {
return io.write_rune(to_writer(b), r)
}
-
/*
- appends a quoted rune into the builder, returns written size
+Appends a quoted rune to the Builder and returns the number of bytes written
+
+Inputs:
+- b: A pointer to the Builder
+- r: The rune to be appended
+
+Returns:
+- n: The number of bytes written
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_quoted_rune_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_string(&builder, "abc") // 3
+ strings.write_quoted_rune(&builder, 'Ƥ') // 4
+ strings.write_string(&builder, "abc") // 3
+ fmt.println(strings.to_string(builder)) // -> abc'Ƥ'abc
+ }
+
+Output:
+
+ abc'Ƥ'abc
- builder := strings.builder_make()
- strings.write_string(&builder, "abc") // 3
- strings.write_quoted_rune(&builder, 'Ƥ') // 4
- strings.write_string(&builder, "abc") // 3
- fmt.println(strings.to_string(builder)) // -> abc'Ƥ'abc
*/
write_quoted_rune :: proc(b: ^Builder, r: rune) -> (n: int) {
return io.write_quoted_rune(to_writer(b), r)
}
-
-
/*
- appends a string to the builder, return the written byte size
-
- builder := strings.builder_make()
- strings.write_string(&builder, "a") // 1
- strings.write_string(&builder, "bc") // 2
- strings.write_string(&builder, "xyz") // 3
- fmt.println(strings.to_string(builder)) // -> abcxyz
+Appends a string to the Builder and returns the number of bytes written
+
+Inputs:
+- b: A pointer to the Builder
+- s: The string to be appended
+
+Returns:
+- n: The number of bytes written
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_string_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_string(&builder, "a") // 1
+ strings.write_string(&builder, "bc") // 2
+ fmt.println(strings.to_string(builder)) // -> abc
+ }
+
+Output:
+
+ abc
+
*/
write_string :: proc(b: ^Builder, s: string) -> (n: int) {
n0 := len(b.buf)
@@ -238,10 +487,15 @@ write_string :: proc(b: ^Builder, s: string) -> (n: int) {
n1 := len(b.buf)
return n1-n0
}
+/*
+Pops and returns the last byte in the Builder or 0 when the Builder is empty
+Inputs:
+- b: A pointer to the Builder
-// pops and returns the last byte in the builder
-// returns 0 when the builder is empty
+Returns:
+- r: The last byte in the Builder or 0 if empty
+*/
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0
@@ -252,9 +506,16 @@ pop_byte :: proc(b: ^Builder) -> (r: byte) {
d.len = max(d.len-1, 0)
return
}
+/*
+Pops the last rune in the Builder and returns the popped rune and its rune width or (0, 0) if empty
-// pops the last rune in the builder and returns the popped rune and its rune width
-// returns 0, 0 when the builder is empty
+Inputs:
+- b: A pointer to the Builder
+
+Returns:
+- r: The popped rune
+- width: The rune width or 0 if the builder was empty
+*/
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
if len(b.buf) == 0 {
return 0, 0
@@ -265,41 +526,116 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
d.len = max(d.len-width, 0)
return
}
-
@(private)
DIGITS_LOWER := "0123456789abcdefx"
-
/*
- append a quoted string into the builder, return the written byte size
+Inputs:
+- b: A pointer to the Builder
+- str: The string to be quoted and appended
+- quote: The optional quote character (default is double quotes)
+
+Returns:
+- n: The number of bytes written
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_quoted_string_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_quoted_string(&builder, "a") // 3
+ strings.write_quoted_string(&builder, "bc", '\'') // 4
+ strings.write_quoted_string(&builder, "xyz") // 5
+ fmt.println(strings.to_string(builder))
+ }
+
+Output:
+
+ "a"'bc'"xyz"
- builder := strings.builder_make()
- strings.write_quoted_string(&builder, "a") // 3
- strings.write_quoted_string(&builder, "bc", '\'') // 4
- strings.write_quoted_string(&builder, "xyz") // 5
- fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
*/
write_quoted_string :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
n, _ = io.write_quoted_string(to_writer(b), str, quote)
return
}
+/*
+Appends a rune to the Builder and returns the number of bytes written
+Inputs:
+- b: A pointer to the Builder
+- r: The rune to be appended
+- write_quote: Optional boolean flag to wrap in single-quotes (') (default is true)
-// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
+Returns:
+- n: The number of bytes written
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_encoded_rune_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_encoded_rune(&builder, 'a', false) // 1
+ strings.write_encoded_rune(&builder, '\"', true) // 3
+ strings.write_encoded_rune(&builder, 'x', false) // 1
+ fmt.println(strings.to_string(builder))
+ }
+
+Output:
+
+ a'"'x
+
+*/
write_encoded_rune :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
return
}
+/*
+Appends an escaped rune to the Builder and returns the number of bytes written
-// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
-// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
-// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
+Inputs:
+- b: A pointer to the Builder
+- r: The rune to be appended
+- quote: The quote character
+- html_safe: Optional boolean flag to encode '<', '>', '&' as digits (default is false)
+
+**Usage**
+- '\a' will be written as such
+- `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
+- `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of bytes written
+*/
write_escaped_rune :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
return
}
+/*
+Writes a f64 value to the Builder and returns the number of characters written
-// writes a f64 value into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- f: The f64 value to be appended
+- fmt: The format byte
+- prec: The precision
+- bit_size: The bit size
+- always_signed: Optional boolean flag to always include the sign (default is false)
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of characters written
+*/
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)
@@ -310,8 +646,20 @@ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_
}
return write_string(b, s)
}
+/*
+Writes a f16 value to the Builder and returns the number of characters written
-// writes a f16 value into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- f: The f16 value to be appended
+- fmt: The format byte
+- always_signed: Optional boolean flag to always include the sign
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of characters written
+*/
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))
@@ -320,8 +668,38 @@ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n:
}
return write_string(b, s)
}
+/*
+Writes a f32 value to the Builder and returns the number of characters written
-// writes a f32 value into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- f: The f32 value to be appended
+- fmt: The format byte
+- always_signed: Optional boolean flag to always include the sign
+
+Returns:
+- n: The number of characters written
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ write_f32_example :: proc() {
+ builder := strings.builder_make()
+ strings.write_f32(&builder, 3.14159, 'f') // 6
+ strings.write_string(&builder, " - ") // 3
+ strings.write_f32(&builder, -0.123, 'e') // 8
+ fmt.println(strings.to_string(builder)) // -> 3.14159012 - -1.23000003e-01
+ }
+
+Output:
+
+ 3.14159012 - -1.23000003e-01
+
+*/
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))
@@ -330,8 +708,20 @@ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n:
}
return write_string(b, s)
}
+/*
+Writes a f32 value to the Builder and returns the number of characters written
-// writes a f64 value into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- f: The f32 value to be appended
+- fmt: The format byte
+- always_signed: Optional boolean flag to always include the sign
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of characters written
+*/
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))
@@ -340,30 +730,71 @@ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n:
}
return write_string(b, s)
}
+/*
+Writes a u64 value to the Builder and returns the number of characters written
+Inputs:
+- b: A pointer to the Builder
+- i: The u64 value to be appended
+- base: The optional base for the numeric representation
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
-// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
+Returns:
+- n: The number of characters written
+*/
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(b, s)
}
+/*
+Writes a i64 value to the Builder and returns the number of characters written
-// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- i: The i64 value to be appended
+- base: The optional base for the numeric representation
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of characters written
+*/
write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil)
return write_string(b, s)
}
+/*
+Writes a uint value to the Builder and returns the number of characters written
-// writes a uint value `i` in `base` = 10 into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- i: The uint value to be appended
+- base: The optional base for the numeric representation
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of characters written
+*/
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
return write_u64(b, u64(i), base)
}
+/*
+Writes a int value to the Builder and returns the number of characters written
-// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
+Inputs:
+- b: A pointer to the Builder
+- i: The int value to be appended
+- base: The optional base for the numeric representation
+
+NOTE: The backing dynamic array may be fixed in capacity or fail to resize, `n` states the number actually written.
+
+Returns:
+- n: The number of characters written
+*/
write_int :: proc(b: ^Builder, i: int, base: int = 10) -> (n: int) {
return write_i64(b, i64(i), base)
}
-
diff --git a/core/strings/conversion.odin b/core/strings/conversion.odin
index ab827490d..2e82fff58 100644
--- a/core/strings/conversion.odin
+++ b/core/strings/conversion.odin
@@ -1,16 +1,33 @@
package strings
import "core:io"
+import "core:mem"
import "core:unicode"
import "core:unicode/utf8"
-to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> string {
+/*
+Converts invalid UTF-8 sequences in the input string `s` to the `replacement` string.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: Input string that may contain invalid UTF-8 sequences.
+- replacement: String to replace invalid UTF-8 sequences with.
+- allocator: (default: context.allocator).
+
+WARNING: Allocation does not occur when len(s) == 0
+
+Returns:
+- res: A valid UTF-8 string with invalid sequences replaced by `replacement`.
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if len(s) == 0 {
- return ""
+ return "", nil
}
b: Builder
- builder_init(&b, 0, 0, allocator)
+ builder_init(&b, 0, 0, allocator) or_return
s := s
for c, i in s {
@@ -33,7 +50,7 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
invalid := false
- for i := 0; i < len(s); /**/ {
+ for i := 0; i < len(s); /**/{
c := s[i]
if c < utf8.RUNE_SELF {
i += 1
@@ -55,55 +72,110 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
write_string(&b, s[i:][:w])
i += w
}
- return to_string(b)
+ return to_string(b), nil
}
-
/*
- returns the input string `s` with all runes set to lowered case
- always allocates using the `allocator`
+Converts the input string `s` to all lowercase characters.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: Input string to be converted.
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The new string with all characters converted to lowercase
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_lower_example :: proc() {
+ fmt.println(strings.to_lower("TeST"))
+ }
+
+Output:
+
+ test
- strings.to_lower("test") -> test
- strings.to_lower("Test") -> test
*/
-to_lower :: proc(s: string, allocator := context.allocator) -> string {
+to_lower :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
for r in s {
write_rune(&b, unicode.to_lower(r))
}
- return to_string(b)
+ return to_string(b), nil
}
-
/*
- returns the input string `s` with all runes set to upper case
- always allocates using the `allocator`
+Converts the input string `s` to all uppercase characters.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: Input string to be converted.
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The new string with all characters converted to uppercase
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_upper_example :: proc() {
+ fmt.println(strings.to_upper("Test"))
+ }
+
+Output:
+
+ TEST
- strings.to_lower("test") -> TEST
- strings.to_lower("Test") -> TEST
*/
-to_upper :: proc(s: string, allocator := context.allocator) -> string {
+to_upper :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
for r in s {
write_rune(&b, unicode.to_upper(r))
}
- return to_string(b)
+ return to_string(b), nil
}
+/*
+Checks if the rune `r` is a delimiter (' ', '-', or '_').
-// returns true when the `c` rune is a space, '-' or '_'
-// useful when treating strings like words in a text editor or html paths
-is_delimiter :: proc(c: rune) -> bool {
- return c == '-' || c == '_' || is_space(c)
+Inputs:
+- r: Rune to check for delimiter status.
+
+Returns:
+- res: True if `r` is a delimiter, false otherwise.
+*/
+is_delimiter :: proc(r: rune) -> (res: bool) {
+ return r == '-' || r == '_' || is_space(r)
}
+/*
+Checks if the rune `r` is a non-alphanumeric or space character.
-// returns true when the `r` rune is a non alpha or `unicode.is_space` rune
-is_separator :: proc(r: rune) -> bool {
+Inputs:
+- r: Rune to check for separator status.
+
+Returns:
+- res: True if `r` is a non-alpha or `unicode.is_space` rune.
+*/
+is_separator :: proc(r: rune) -> (res: bool) {
if r <= 0x7f {
switch r {
- case '0'..='9': return false
- case 'a'..='z': return false
- case 'A'..='Z': return false
- case '_': return false
+ case '0' ..= '9':
+ return false
+ case 'a' ..= 'z':
+ return false
+ case 'A' ..= 'Z':
+ return false
+ case '_':
+ return false
}
return true
}
@@ -115,12 +187,46 @@ is_separator :: proc(r: rune) -> bool {
return unicode.is_space(r)
}
-
/*
- iterator that loops through the string and calls the callback with the `prev`, `curr` and `next` rune
- on empty string `s` the callback gets called once with empty runes
+Iterates over a string, calling a callback for each rune with the previous, current, and next runes as arguments.
+
+Inputs:
+- w: An io.Writer to be used by the callback for writing output.
+- s: The input string to be iterated over.
+- callback: A procedure to be called for each rune in the string, with arguments (w: io.Writer, prev, curr, next: rune).
+The callback can utilize the provided io.Writer to write output during the iteration.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+ import "core:io"
+
+ string_case_iterator_example :: proc() {
+ my_callback :: proc(w: io.Writer, prev, curr, next: rune) {
+ fmt.println("my_callback", curr) // <-- Custom logic here
+ }
+ s := "hello"
+ b: strings.Builder
+ strings.builder_init_len(&b, len(s))
+ w := strings.to_writer(&b)
+ strings.string_case_iterator(w, s, my_callback)
+ }
+
+Output:
+
+ my_callback h
+ my_callback e
+ my_callback l
+ my_callback l
+ my_callback o
+
*/
-string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Writer, prev, curr, next: rune)) {
+string_case_iterator :: proc(
+ w: io.Writer,
+ s: string,
+ callback: proc(w: io.Writer, prev, curr, next: rune),
+) {
prev, curr: rune
for next in s {
if curr == 0 {
@@ -139,15 +245,26 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write
callback(w, prev, curr, 0)
}
}
-
+// Alias to `to_camel_case`
to_lower_camel_case :: to_camel_case
+/*
+Converts the input string `s` to "lowerCamelCase".
-// converts the `s` string to "lowerCamelCase"
-to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: Input string to be converted.
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+to_camel_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := s
s = trim_space(s)
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
w := to_writer(&b)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
@@ -162,17 +279,28 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
}
})
- return to_string(b)
+ return to_string(b), nil
}
-
+// Alias to `to_pascal_case`
to_upper_camel_case :: to_pascal_case
+/*
+Converts the input string `s` to "UpperCamelCase" (PascalCase).
-// converts the `s` string to "PascalCase"
-to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: Input string to be converted.
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+to_pascal_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := s
s = trim_space(s)
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
w := to_writer(&b)
string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
@@ -187,23 +315,51 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
}
})
- return to_string(b)
+ return to_string(b), nil
}
+/*
+Returns a string converted to a delimiter-separated case with configurable casing
-/*
- returns the `s` string to words seperated by the given `delimiter` rune
- all runes will be upper or lowercased based on the `all_uppercase` bool
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to be converted
+- delimiter: The rune to be used as the delimiter between words
+- all_upper_case: A boolean indicating if the output should be all uppercased (true) or lowercased (false)
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_delimiter_case_example :: proc() {
+ fmt.println(strings.to_delimiter_case("Hello World", '_', false))
+ fmt.println(strings.to_delimiter_case("Hello World", ' ', true))
+ fmt.println(strings.to_delimiter_case("aBC", '_', false))
+ }
+
+Output:
+
+ hello_world
+ HELLO WORLD
+ a_bc
- strings.to_delimiter_case("Hello World", '_', false) -> hello_world
- strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
- strings.to_delimiter_case("Hello World", ' ', true) -> HELLO WORLD
- strings.to_delimiter_case("aBC", '_', false) -> a_b_c
*/
-to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allocator := context.allocator) -> string {
+to_delimiter_case :: proc(
+ s: string,
+ delimiter: rune,
+ all_upper_case: bool,
+ allocator := context.allocator,
+) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := s
s = trim_space(s)
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
w := to_writer(&b)
adjust_case := unicode.to_upper if all_upper_case else unicode.to_lower
@@ -235,75 +391,178 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
io.write_rune(w, adjust_case(curr))
}
- return to_string(b)
+ return to_string(b), nil
}
+/*
+Converts a string to "snake_case" with all runes lowercased
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to be converted
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_snake_case_example :: proc() {
+ fmt.println(strings.to_snake_case("HelloWorld"))
+ fmt.println(strings.to_snake_case("Hello World"))
+ }
+
+Output:
+
+ hello_world
+ hello_world
-/*
- converts the `s` string to "snake_case" with all runes lowercased
-
- strings.to_snake_case("HelloWorld") -> hello_world
- strings.to_snake_case("Hello World") -> hello_world
*/
-to_snake_case :: proc(s: string, allocator := context.allocator) -> string {
+to_snake_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
return to_delimiter_case(s, '_', false, allocator)
}
-
+// Alias for `to_upper_snake_case`
to_screaming_snake_case :: to_upper_snake_case
+/*
+Converts a string to "SNAKE_CASE" with all runes uppercased
-// converts the `s` string to "SNAKE_CASE" with all runes uppercased
-to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to be converted
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_upper_snake_case_example :: proc() {
+ fmt.println(strings.to_upper_snake_case("HelloWorld"))
+ }
+
+Output:
+
+ HELLO_WORLD
+
+*/
+to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
return to_delimiter_case(s, '_', true, allocator)
}
+/*
+Converts a string to "kebab-case" with all runes lowercased
-// converts the `s` string to "kebab-case" with all runes lowercased
-to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to be converted
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_kebab_case_example :: proc() {
+ fmt.println(strings.to_kebab_case("HelloWorld"))
+ }
+
+Output:
+
+ hello-world
+
+*/
+to_kebab_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
return to_delimiter_case(s, '-', false, allocator)
}
+/*
+Converts a string to "KEBAB-CASE" with all runes uppercased
-// converts the `s` string to "KEBAB-CASE" with all runes uppercased
-to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to be converted
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_upper_kebab_case_example :: proc() {
+ fmt.println(strings.to_upper_kebab_case("HelloWorld"))
+ }
+
+Output:
+
+ HELLO-WORLD
+
+*/
+to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
return to_delimiter_case(s, '-', true, allocator)
}
+/*
+Converts a string to "Ada_Case"
-// converts the `s` string to "Ada_case"
-to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
- delimiter :: '_'
+*Allocates Using Provided Allocator*
+Inputs:
+- s: The input string to be converted
+- allocator: (default: context.allocator).
+
+Returns:
+- res: The converted string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ to_ada_case_example :: proc() {
+ fmt.println(strings.to_ada_case("HelloWorld"))
+ }
+
+Output:
+
+ Hello_World
+
+*/
+to_ada_case :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := s
s = trim_space(s)
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
w := to_writer(&b)
- prev, curr: rune
-
- for next in s {
- if is_delimiter(curr) {
- if !is_delimiter(prev) {
- io.write_rune(w, delimiter)
+ string_case_iterator(w, s, proc(w: io.Writer, prev, curr, next: rune) {
+ if !is_delimiter(curr) {
+ if is_delimiter(prev) || prev == 0 || (unicode.is_lower(prev) && unicode.is_upper(curr)) {
+ if prev != 0 {
+ io.write_rune(w, '_')
+ }
+ io.write_rune(w, unicode.to_upper(curr))
+ } else {
+ io.write_rune(w, unicode.to_lower(curr))
}
- } else if unicode.is_upper(curr) {
- if unicode.is_lower(prev) || (unicode.is_upper(prev) && unicode.is_lower(next)) {
- io.write_rune(w, delimiter)
- }
- io.write_rune(w, unicode.to_upper(curr))
- } else if curr != 0 {
- io.write_rune(w, unicode.to_lower(curr))
}
+ })
- prev = curr
- curr = next
- }
-
- if len(s) > 0 {
- if unicode.is_upper(curr) && unicode.is_lower(prev) && prev != 0 {
- io.write_rune(w, delimiter)
- io.write_rune(w, unicode.to_upper(curr))
- } else {
- io.write_rune(w, unicode.to_lower(curr))
- }
- }
-
- return to_string(b)
+ return to_string(b), nil
}
-
diff --git a/core/strings/intern.odin b/core/strings/intern.odin
index 5e9193a0d..73f91d4d4 100644
--- a/core/strings/intern.odin
+++ b/core/strings/intern.odin
@@ -1,50 +1,108 @@
package strings
import "core:runtime"
+import "core:mem"
-// custom string entry struct
+// Custom string entry struct
Intern_Entry :: struct {
len: int,
str: [1]byte, // string is allocated inline with the entry to keep allocations simple
}
+/*
+Intern is a more memory efficient string map
-// "intern" is a more memory efficient string map
-// `allocator` is used to allocate the actual `Intern_Entry` strings
+Uses Specified Allocator for `Intern_Entry` strings
+
+Fields:
+- allocator: The allocator used for the Intern_Entry strings
+- entries: A map of strings to interned string entries
+*/
Intern :: struct {
allocator: runtime.Allocator,
entries: map[string]^Intern_Entry,
}
+/*
+Initializes the entries map and sets the allocator for the string entries
-// initialize the entries map and set the allocator for the string entries
-intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) {
+*Allocates Using Provided Allocators*
+
+Inputs:
+- m: A pointer to the Intern struct to be initialized
+- allocator: The allocator for the Intern_Entry strings (Default: context.allocator)
+- map_allocator: The allocator for the map of entries (Default: context.allocator)
+
+Returns:
+- err: An allocator error if one occured, `nil` otherwise
+*/
+intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) -> (err: mem.Allocator_Error) {
m.allocator = allocator
- m.entries = make(map[string]^Intern_Entry, 16, map_allocator)
+ m.entries = make(map[string]^Intern_Entry, 16, map_allocator) or_return
+ return nil
}
+/*
+Frees the map and all its content allocated using the `.allocator`.
-// free the map and all its content allocated using the `.allocator`
+Inputs:
+- m: A pointer to the Intern struct to be destroyed
+*/
intern_destroy :: proc(m: ^Intern) {
for _, value in m.entries {
free(value, m.allocator)
}
delete(m.entries)
}
+/*
+Returns an interned copy of the given text, adding it to the map if not already present.
-// returns the `text` string from the intern map - gets set if it didnt exist yet
-// the returned string lives as long as the map entry lives
+*Allocate using the Intern's Allocator (First time string is seen only)*
+
+Inputs:
+- m: A pointer to the Intern struct
+- text: The string to be interned
+
+NOTE: The returned string lives as long as the map entry lives.
+
+Returns:
+- str: The interned string
+- err: An allocator error if one occured, `nil` otherwise
+*/
intern_get :: proc(m: ^Intern, text: string) -> (str: string, err: runtime.Allocator_Error) {
entry := _intern_get_entry(m, text) or_return
#no_bounds_check return string(entry.str[:entry.len]), nil
}
+/*
+Returns an interned copy of the given text as a cstring, adding it to the map if not already present.
-// returns the `text` cstring from the intern map - gets set if it didnt exist yet
-// the returned cstring lives as long as the map entry lives
+*Allocate using the Intern's Allocator (First time string is seen only)*
+
+Inputs:
+- m: A pointer to the Intern struct
+- text: The string to be interned
+
+NOTE: The returned cstring lives as long as the map entry lives
+
+Returns:
+- str: The interned cstring
+- err: An allocator error if one occured, `nil` otherwise
+*/
intern_get_cstring :: proc(m: ^Intern, text: string) -> (str: cstring, err: runtime.Allocator_Error) {
entry := _intern_get_entry(m, text) or_return
return cstring(&entry.str[0]), nil
}
+/*
+Internal function to lookup whether the text string exists in the map, returns the entry
+Sets and allocates the entry if it wasn't set yet
-// looks up wether the `text` string exists in the map, returns the entry
-// sets & allocates the entry if it wasnt set yet
+*Allocate using the Intern's Allocator (First time string is seen only)*
+
+Inputs:
+- m: A pointer to the Intern struct
+- text: The string to be looked up or interned
+
+Returns:
+- new_entry: The interned cstring
+- err: An allocator error if one occured, `nil` otherwise
+*/
_intern_get_entry :: proc(m: ^Intern, text: string) -> (new_entry: ^Intern_Entry, err: runtime.Allocator_Error) #no_bounds_check {
if prev, ok := m.entries[text]; ok {
return prev, nil
diff --git a/core/strings/reader.odin b/core/strings/reader.odin
index 038740526..bb49bf917 100644
--- a/core/strings/reader.odin
+++ b/core/strings/reader.odin
@@ -4,59 +4,109 @@ import "core:io"
import "core:unicode/utf8"
/*
- io stream data for a string reader that can read based on bytes or runes
- implements the vtable when using the io.Reader variants
- "read" calls advance the current reading offset `i`
+io stream data for a string reader that can read based on bytes or runes
+implements the vtable when using the `io.Reader` variants
+"read" calls advance the current reading offset `i`
*/
Reader :: struct {
s: string, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
+/*
+Initializes a string Reader with the provided string
-// init the reader to the string `s`
+Inputs:
+- r: A pointer to a Reader struct
+- s: The input string to be read
+*/
reader_init :: proc(r: ^Reader, s: string) {
r.s = s
r.i = 0
r.prev_rune = -1
}
+/*
+Converts a Reader into an `io.Stream`
-// returns a stream from the reader data
+Inputs:
+- r: A pointer to a Reader struct
+
+Returns:
+- s: An io.Stream for the given Reader
+*/
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
- s.stream_data = r
- s.stream_vtable = &_reader_vtable
+ s.data = r
+ s.procedure = _reader_proc
return
}
+/*
+Initializes a string Reader and returns an `io.Reader` for the given string
-// init a reader to the string `s` and return an io.Reader
-to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
+Inputs:
+- r: A pointer to a Reader struct
+- s: The input string to be read
+
+Returns:
+- res: An io.Reader for the given string
+*/
+to_reader :: proc(r: ^Reader, s: string) -> (res: io.Reader) {
reader_init(r, s)
rr, _ := io.to_reader(reader_to_stream(r))
return rr
}
+/*
+Initializes a string Reader and returns an `io.Reader_At` for the given string
-// init a reader to the string `s` and return an io.Reader_At
-to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
+Inputs:
+- r: A pointer to a Reader struct
+- s: The input string to be read
+
+Returns:
+- res: An `io.Reader_At` for the given string
+*/
+to_reader_at :: proc(r: ^Reader, s: string) -> (res: io.Reader_At) {
reader_init(r, s)
rr, _ := io.to_reader_at(reader_to_stream(r))
return rr
}
+/*
+Returns the remaining length of the Reader
-// remaining length of the reader
-reader_length :: proc(r: ^Reader) -> int {
+Inputs:
+- r: A pointer to a Reader struct
+
+Returns:
+- res: The remaining length of the Reader
+*/
+reader_length :: proc(r: ^Reader) -> (res: int) {
if r.i >= i64(len(r.s)) {
return 0
}
return int(i64(len(r.s)) - r.i)
}
+/*
+Returns the length of the string stored in the Reader
-// returns the string length stored by the reader
-reader_size :: proc(r: ^Reader) -> i64 {
+Inputs:
+- r: A pointer to a Reader struct
+
+Returns:
+- res: The length of the string stored in the Reader
+*/
+reader_size :: proc(r: ^Reader) -> (res: i64) {
return i64(len(r.s))
}
+/*
+Reads len(p) bytes from the Reader's string and copies into the provided slice.
-// reads len(p) bytes into the slice from the string in the reader
-// returns `n` amount of read bytes and an io.Error
+Inputs:
+- r: A pointer to a Reader struct
+- p: A byte slice to copy data into
+
+Returns:
+- n: The number of bytes read
+- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
+*/
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF
@@ -66,9 +116,18 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
r.i += i64(n)
return
}
+/*
+Reads len(p) bytes from the Reader's string and copies into the provided slice, at the specified offset from the current index.
-// reads len(p) bytes into the slice from the string in the reader at an offset
-// returns `n` amount of read bytes and an io.Error
+Inputs:
+- r: A pointer to a Reader struct
+- p: A byte slice to copy data into
+- off: The offset from which to read
+
+Returns:
+- n: The number of bytes read
+- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
+*/
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset
@@ -82,9 +141,17 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
}
return
}
+/*
+Reads and returns a single byte from the Reader's string
-// reads and returns a single byte - error when out of bounds
-reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
+Inputs:
+- r: A pointer to a Reader struct
+
+Returns:
+- The byte read from the Reader
+- err: An `io.Error` if an error occurs while reading, including `.EOF`, otherwise `nil` denotes success.
+*/
+reader_read_byte :: proc(r: ^Reader) -> (res: byte, err: io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
return 0, .EOF
@@ -93,9 +160,16 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.i += 1
return b, nil
}
+/*
+Decrements the Reader's index (i) by 1
-// decreases the reader offset - error when below 0
-reader_unread_byte :: proc(r: ^Reader) -> io.Error {
+Inputs:
+- r: A pointer to a Reader struct
+
+Returns:
+- err: An `io.Error` if `r.i <= 0` (`.Invalid_Unread`), otherwise `nil` denotes success.
+*/
+reader_unread_byte :: proc(r: ^Reader) -> (err: io.Error) {
if r.i <= 0 {
return .Invalid_Unread
}
@@ -103,9 +177,18 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error {
r.i -= 1
return nil
}
+/*
+Reads and returns a single rune and its `size` from the Reader's string
-// reads and returns a single rune and the rune size - error when out bounds
-reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
+Inputs:
+- r: A pointer to a Reader struct
+
+Returns:
+- rr: The rune read from the Reader
+- size: The size of the rune in bytes
+- err: An `io.Error` if an error occurs while reading
+*/
+reader_read_rune :: proc(r: ^Reader) -> (rr: rune, size: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1
return 0, 0, .EOF
@@ -115,14 +198,22 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
r.i += 1
return rune(c), 1, nil
}
- ch, size = utf8.decode_rune_in_string(r.s[r.i:])
+ rr, size = utf8.decode_rune_in_string(r.s[r.i:])
r.i += i64(size)
return
}
+/*
+Decrements the Reader's index (i) by the size of the last read rune
-// decreases the reader offset by the last rune
-// can only be used once and after a valid read_rune call
-reader_unread_rune :: proc(r: ^Reader) -> io.Error {
+Inputs:
+- r: A pointer to a Reader struct
+
+WARNING: May only be used once and after a valid `read_rune` call
+
+Returns:
+- err: An `io.Error` if an error occurs while unreading (`.Invalid_Unread`), else `nil` denotes success.
+*/
+reader_unread_rune :: proc(r: ^Reader) -> (err: io.Error) {
if r.i <= 0 {
return .Invalid_Unread
}
@@ -133,9 +224,19 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
r.prev_rune = -1
return nil
}
+/*
+Seeks the Reader's index to a new position
-// seeks the reader offset to a wanted offset
-reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
+Inputs:
+- r: A pointer to a Reader struct
+- offset: The new offset position
+- whence: The reference point for the new position (`.Start`, `.Current`, or `.End`)
+
+Returns:
+- The absolute offset after seeking
+- err: An `io.Error` if an error occurs while seeking (`.Invalid_Whence`, `.Invalid_Offset`)
+*/
+reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (res: i64, err: io.Error) {
r.prev_rune = -1
abs: i64
switch whence {
@@ -155,8 +256,19 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
r.i = abs
return abs, nil
}
+/*
+Writes the remaining content of the Reader's string into the provided `io.Writer`
-// writes the string content left to read into the io.Writer `w`
+Inputs:
+- r: A pointer to a Reader struct
+- w: The io.Writer to write the remaining content into
+
+WARNING: Panics if writer writes more bytes than remainig length of string.
+
+Returns:
+- n: The number of bytes written
+- err: An io.Error if an error occurs while writing (`.Short_Write`)
+*/
reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
@@ -175,43 +287,28 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
}
return
}
+/*
+VTable containing implementations for various `io.Stream` methods
+This VTable is used by the Reader struct to provide its functionality
+as an `io.Stream`.
+*/
@(private)
-_reader_vtable := io.Stream_VTable{
- impl_size = proc(s: io.Stream) -> i64 {
- r := (^Reader)(s.stream_data)
- return reader_size(r)
- },
- impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read(r, p)
- },
- impl_read_at = proc(s: io.Stream, p: []byte, off: i64) -> (n: int, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read_at(r, p, off)
- },
- impl_read_byte = proc(s: io.Stream) -> (byte, io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read_byte(r)
- },
- impl_unread_byte = proc(s: io.Stream) -> io.Error {
- r := (^Reader)(s.stream_data)
- return reader_unread_byte(r)
- },
- impl_read_rune = proc(s: io.Stream) -> (ch: rune, size: int, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_read_rune(r)
- },
- impl_unread_rune = proc(s: io.Stream) -> io.Error {
- r := (^Reader)(s.stream_data)
- return reader_unread_rune(r)
- },
- impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_seek(r, offset, whence)
- },
- impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
- r := (^Reader)(s.stream_data)
- return reader_write_to(r, w)
- },
+_reader_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
+ r := (^Reader)(stream_data)
+ #partial switch mode {
+ case .Size:
+ n = reader_size(r)
+ return
+ case .Read:
+ return io._i64_err(reader_read(r, p))
+ case .Read_At:
+ return io._i64_err(reader_read_at(r, p, offset))
+ case .Seek:
+ n, err = reader_seek(r, offset, whence)
+ return
+ case .Query:
+ return io.query_utility({.Size, .Read, .Read_At, .Seek, .Query})
+ }
+ return 0, .Empty
}
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index ba357e027..6daa5f9c9 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1,178 +1,409 @@
-// simple procedures to manipulate UTF-8 encoded strings
+// Procedures to manipulate UTF-8 encoded strings
package strings
import "core:io"
import "core:mem"
-import "core:slice"
import "core:unicode"
import "core:unicode/utf8"
-// returns a clone of the string `s` allocated using the `allocator`
-clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> string {
- c := make([]byte, len(s), allocator, loc)
- copy(c, s)
- return string(c[:len(s)])
-}
+/*
+Clones a string
-// returns a clone of the string `s` allocated using the `allocator`
-clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (str: string, err: mem.Allocator_Error) {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to be cloned
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: #caller_location)
+
+Returns:
+- res: The cloned string
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
c := make([]byte, len(s), allocator, loc) or_return
copy(c, s)
return string(c[:len(s)]), nil
}
+/*
+Clones a string safely (returns early with an allocation error on failure)
-// returns a clone of the string `s` allocated using the `allocator` as a cstring
-// a nul byte is appended to the clone, to make the cstring safe
-clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> cstring {
- c := make([]byte, len(s)+1, allocator, loc)
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to be cloned
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: #caller_location)
+
+Returns:
+- res: The cloned string
+- err: An allocator error if one occured, `nil` otherwise
+*/
+@(deprecated="Prefer clone. It now returns an optional allocator error")
+clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) {
+ return clone(s, allocator, loc)
+}
+/*
+Clones a string and appends a null-byte to make it a cstring
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to be cloned
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: #caller_location)
+
+Returns:
+- res: A cloned cstring with an appended null-byte
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: cstring, err: mem.Allocator_Error) #optional_allocator_error {
+ c := make([]byte, len(s)+1, allocator, loc) or_return
copy(c, s)
c[len(s)] = 0
- return cstring(&c[0])
+ return cstring(&c[0]), nil
}
+/*
+Transmutes a raw pointer into a string. Non-allocating.
-// returns a string from a byte pointer `ptr` and byte length `len`
-// the string is valid as long as the parameters stay alive
-string_from_ptr :: proc(ptr: ^byte, len: int) -> string {
+Inputs:
+- ptr: A pointer to the start of the byte sequence
+- len: The length of the byte sequence
+
+NOTE: The created string is only valid as long as the pointer and length are valid.
+
+Returns:
+- res: A string created from the byte pointer and length
+*/
+string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
return transmute(string)mem.Raw_String{ptr, len}
}
+/*
+Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0.. string {
+NOTE: The created string is only valid as long as the pointer and length are valid.
+ The string is truncated at the first null-byte encountered.
+
+Inputs:
+- ptr: A pointer to the start of the null-terminated byte sequence
+- len: The length of the byte sequence
+
+Returns:
+- res: A string created from the null-terminated byte pointer and length
+*/
+string_from_null_terminated_ptr :: proc(ptr: ^byte, len: int) -> (res: string) {
s := transmute(string)mem.Raw_String{ptr, len}
s = truncate_to_byte(s, 0)
return s
}
+/*
+Gets the raw byte pointer for the start of a string `str`
-// returns the raw ^byte start of the string `str`
-ptr_from_string :: proc(str: string) -> ^byte {
+Inputs:
+- str: The input string
+
+Returns:
+- res: A pointer to the start of the string's bytes
+*/
+@(deprecated="Prefer the builtin raw_data.")
+ptr_from_string :: proc(str: string) -> (res: ^byte) {
d := transmute(mem.Raw_String)str
return d.data
}
+/*
+Converts a string `str` to a cstring
-// returns the transmute of string `str` to a cstring
-// not safe since the origin string may not contain a nul byte
-unsafe_string_to_cstring :: proc(str: string) -> cstring {
+Inputs:
+- str: The input string
+
+WARNING: This is unsafe because the original string may not contain a null-byte.
+
+Returns:
+- res: The converted cstring
+*/
+unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) {
d := transmute(mem.Raw_String)str
return cstring(d.data)
}
+/*
+Truncates a string `str` at the first occurrence of char/byte `b`
-// returns a string truncated to the first time it finds the byte `b`
-// uses the `len` of the string `str` when it couldn't find the input
-truncate_to_byte :: proc(str: string, b: byte) -> string {
+Inputs:
+- str: The input string
+- b: The byte to truncate the string at
+
+NOTE: Failure to find the byte results in returning the entire string.
+
+Returns:
+- res: The truncated string
+*/
+truncate_to_byte :: proc(str: string, b: byte) -> (res: string) {
n := index_byte(str, b)
if n < 0 {
n = len(str)
}
return str[:n]
}
+/*
+Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found
-// returns a string truncated to the first time it finds the rune `r`
-// uses the `len` of the string `str` when it couldn't find the input
-truncate_to_rune :: proc(str: string, r: rune) -> string {
+Inputs:
+- str: The input string
+- r: The rune to truncate the string at
+
+Returns:
+- res: The truncated string
+*/
+truncate_to_rune :: proc(str: string, r: rune) -> (res: string) {
n := index_rune(str, r)
if n < 0 {
n = len(str)
}
return str[:n]
}
+/*
+Clones a byte array `s` and appends a null-byte
-// returns a cloned string of the byte array `s` using the `allocator`
-// appends a leading nul byte
-clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> string {
- c := make([]byte, len(s)+1, allocator, loc)
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The byte array to be cloned
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: `#caller_location`)
+
+Returns:
+- res: The cloned string from the byte array with a null-byte
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
+ c := make([]byte, len(s)+1, allocator, loc) or_return
copy(c, s)
c[len(s)] = 0
- return string(c[:len(s)])
+ return string(c[:len(s)]), nil
}
+/*
+Clones a cstring `s` as a string
-// returns a clone of the cstring `s` using the `allocator` as a string
-clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The cstring to be cloned
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: `#caller_location`)
+
+Returns:
+- res: The cloned string from the cstring
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
return clone(string(s), allocator, loc)
}
+/*
+Clones a string from a byte pointer `ptr` and a byte length `len`
-// returns a cloned string from the pointer `ptr` and a byte length `len` using the `allocator`
-// same to `string_from_ptr` but allocates
-clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- ptr: A pointer to the start of the byte sequence
+- len: The length of the byte sequence
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: `#caller_location`)
+
+NOTE: Same as `string_from_ptr`, but perform an additional `clone` operation
+
+Returns:
+- res: The cloned string from the byte pointer and length
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := string_from_ptr(ptr, len)
return clone(s, allocator, loc)
}
-
-// overload to clone from a `string`, `[]byte`, `cstring` or a `^byte + length` to a string
+// Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length
clone_from :: proc{
clone,
clone_from_bytes,
clone_from_cstring,
clone_from_ptr,
}
+/*
+Clones a string from a null-terminated cstring `ptr` and a byte length `len`
-// returns a cloned string from the cstring `ptr` and a byte length `len` using the `allocator`
-// truncates till the first nul byte it finds or the byte len
-clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- ptr: A pointer to the start of the null-terminated cstring
+- len: The byte length of the cstring
+- allocator: (default: context.allocator)
+- loc: The caller location for debugging purposes (default: `#caller_location`)
+
+NOTE: Truncates at the first null-byte encountered or the byte length.
+
+Returns:
+- res: The cloned string from the null-terminated cstring and byte length
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := string_from_ptr((^u8)(ptr), len)
s = truncate_to_byte(s, 0)
return clone(s, allocator, loc)
}
+/*
+Compares two strings, returning a value representing which one comes first lexicographically.
+-1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
-// Compares two strings, returning a value representing which one comes first lexiographically.
-// -1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
-compare :: proc(lhs, rhs: string) -> int {
+Inputs:
+- lhs: First string for comparison
+- rhs: Second string for comparison
+
+Returns:
+- result: `-1` if `lhs` comes first, `1` if `rhs` comes first, or `0` if they are equal
+*/
+compare :: proc(lhs, rhs: string) -> (result: int) {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
}
+/*
+Checks if rune `r` in the string `s`
-// returns the byte offset of the rune `r` in the string `s`, -1 when not found
-contains_rune :: proc(s: string, r: rune) -> int {
- for c, offset in s {
+Inputs:
+- s: The input string
+- r: The rune to search for
+
+Returns:
+- result: `true` if the rune `r` in the string `s`, `false` otherwise
+*/
+contains_rune :: proc(s: string, r: rune) -> (result: bool) {
+ for c in s {
if c == r {
- return offset
+ return true
}
}
- return -1
+ return false
}
-
/*
- returns true when the string `substr` is contained inside the string `s`
+Returns true when the string `substr` is contained inside the string `s`
+
+Inputs:
+- s: The input string
+- substr: The substring to search for
+
+Returns:
+- res: `true` if `substr` is contained inside the string `s`, `false` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ contains_example :: proc() {
+ fmt.println(strings.contains("testing", "test"))
+ fmt.println(strings.contains("testing", "ing"))
+ fmt.println(strings.contains("testing", "text"))
+ }
+
+Output:
+
+ true
+ true
+ false
- strings.contains("testing", "test") -> true
- strings.contains("testing", "ing") -> true
- strings.contains("testing", "text") -> false
*/
-contains :: proc(s, substr: string) -> bool {
+contains :: proc(s, substr: string) -> (res: bool) {
return index(s, substr) >= 0
}
-
/*
- returns true when the string `s` contains any of the characters inside the string `chars`
-
- strings.contains_any("test", "test") -> true
- strings.contains_any("test", "ts") -> true
- strings.contains_any("test", "et") -> true
- strings.contains_any("test", "a") -> false
+Returns `true` when the string `s` contains any of the characters inside the string `chars`
+
+Inputs:
+- s: The input string
+- chars: The characters to search for
+
+Returns:
+- res: `true` if the string `s` contains any of the characters in `chars`, `false` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ contains_any_example :: proc() {
+ fmt.println(strings.contains_any("test", "test"))
+ fmt.println(strings.contains_any("test", "ts"))
+ fmt.println(strings.contains_any("test", "et"))
+ fmt.println(strings.contains_any("test", "a"))
+ }
+
+Output:
+
+ true
+ true
+ true
+ false
+
*/
-contains_any :: proc(s, chars: string) -> bool {
+contains_any :: proc(s, chars: string) -> (res: bool) {
return index_any(s, chars) >= 0
}
-
/*
- returns the utf8 rune count of the string `s`
+Returns the UTF-8 rune count of the string `s`
+
+Inputs:
+- s: The input string
+
+Returns:
+- res: The UTF-8 rune count of the string `s`
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ rune_count_example :: proc() {
+ fmt.println(strings.rune_count("test"))
+ fmt.println(strings.rune_count("testƶ")) // where len("testƶ") == 6
+ }
+
+Output:
+
+ 4
+ 5
- strings.rune_count("test") -> 4
- strings.rune_count("testƶ") -> 5, where len("testƶ") -> 6
*/
-rune_count :: proc(s: string) -> int {
+rune_count :: proc(s: string) -> (res: int) {
return utf8.rune_count_in_string(s)
}
-
/*
- returns wether the strings `u` and `v` are the same alpha characters
- works with utf8 string content and ignores different casings
+Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings
+Works with UTF-8 string content
+
+Inputs:
+- u: The first string for comparison
+- v: The second string for comparison
+
+Returns:
+- res: `true` if the strings `u` and `v` are the same alpha characters (ignoring case)
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ equal_fold_example :: proc() {
+ fmt.println(strings.equal_fold("test", "test"))
+ fmt.println(strings.equal_fold("Test", "test"))
+ fmt.println(strings.equal_fold("Test", "tEsT"))
+ fmt.println(strings.equal_fold("test", "tes"))
+ }
+
+Output:
+
+ true
+ true
+ true
+ false
- strings.equal_fold("test", "test") -> true
- strings.equal_fold("Test", "test") -> true
- strings.equal_fold("Test", "tEsT") -> true
- strings.equal_fold("test", "tes") -> false
*/
-equal_fold :: proc(u, v: string) -> bool {
+equal_fold :: proc(u, v: string) -> (res: bool) {
s, t := u, v
loop: for s != "" && t != "" {
sr, tr: rune
@@ -214,14 +445,35 @@ equal_fold :: proc(u, v: string) -> bool {
return s == t
}
-
/*
- return the prefix length common between strings `a` and `b`.
+Returns the prefix length common between strings `a` and `b`
+
+Inputs:
+- a: The first input string
+- b: The second input string
+
+Returns:
+- n: The prefix length common between strings `a` and `b`
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ prefix_length_example :: proc() {
+ fmt.println(strings.prefix_length("testing", "test"))
+ fmt.println(strings.prefix_length("testing", "te"))
+ fmt.println(strings.prefix_length("telephone", "te"))
+ fmt.println(strings.prefix_length("testing", "est"))
+ }
+
+Output:
+
+ 4
+ 2
+ 2
+ 0
- strings.prefix_length("testing", "test") -> 4
- strings.prefix_length("testing", "te") -> 2
- strings.prefix_length("telephone", "te") -> 2
- strings.prefix_length("testing", "est") -> 0
*/
prefix_length :: proc(a, b: string) -> (n: int) {
_len := min(len(a), len(b))
@@ -246,60 +498,104 @@ prefix_length :: proc(a, b: string) -> (n: int) {
}
return
}
-
/*
- return true when the string `prefix` is contained at the start of the string `s`
+Determines if a string `s` starts with a given `prefix`
+
+Inputs:
+- s: The string to check for the `prefix`
+- prefix: The prefix to look for
+
+Returns:
+- result: `true` if the string `s` starts with the `prefix`, otherwise `false`
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ has_prefix_example :: proc() {
+ fmt.println(strings.has_prefix("testing", "test"))
+ fmt.println(strings.has_prefix("testing", "te"))
+ fmt.println(strings.has_prefix("telephone", "te"))
+ fmt.println(strings.has_prefix("testing", "est"))
+ }
+
+Output:
+
+ true
+ true
+ true
+ false
- strings.has_prefix("testing", "test") -> true
- strings.has_prefix("testing", "te") -> true
- strings.has_prefix("telephone", "te") -> true
- strings.has_prefix("testing", "est") -> false
*/
-has_prefix :: proc(s, prefix: string) -> bool {
+has_prefix :: proc(s, prefix: string) -> (result: bool) {
return len(s) >= len(prefix) && s[0:len(prefix)] == prefix
}
-
/*
- returns true when the string `suffix` is contained at the end of the string `s`
- good example to use this is for file extensions
+Determines if a string `s` ends with a given `suffix`
+
+Inputs:
+- s: The string to check for the `suffix`
+- suffix: The suffix to look for
+
+Returns:
+- result: `true` if the string `s` ends with the `suffix`, otherwise `false`
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ has_suffix_example :: proc() {
+ fmt.println(strings.has_suffix("todo.txt", ".txt"))
+ fmt.println(strings.has_suffix("todo.doc", ".txt"))
+ fmt.println(strings.has_suffix("todo.doc.txt", ".txt"))
+ }
+
+Output:
+
+ true
+ false
+ true
- strings.has_suffix("todo.txt", ".txt") -> true
- strings.has_suffix("todo.doc", ".txt") -> false
- strings.has_suffix("todo.doc.txt", ".txt") -> true
*/
-has_suffix :: proc(s, suffix: string) -> bool {
+has_suffix :: proc(s, suffix: string) -> (result: bool) {
return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix
}
-
/*
- returns a combined string from the slice of strings `a` seperated with the `sep` string
- allocates the string using the `allocator`
+Joins a slice of strings `a` with a `sep` string
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- a: A slice of strings to join
+- sep: The separator string
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A combined string from the slice of strings `a` separated with the `sep` string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ join_example :: proc() {
+ a := [?]string { "a", "b", "c" }
+ fmt.println(strings.join(a[:], " "))
+ fmt.println(strings.join(a[:], "-"))
+ fmt.println(strings.join(a[:], "..."))
+ }
+
+Output:
+
+ a b c
+ a-b-c
+ a...b...c
- a := [?]string { "a", "b", "c" }
- b := strings.join(a[:], " ") -> "a b c"
- c := strings.join(a[:], "-") -> "a-b-c"
- d := strings.join(a[:], "...") -> "a...b...c"
*/
-join :: proc(a: []string, sep: string, allocator := context.allocator) -> string {
- if len(a) == 0 {
- return ""
- }
-
- n := len(sep) * (len(a) - 1)
- for s in a {
- n += len(s)
- }
-
- b := make([]byte, n, allocator)
- i := copy(b, a[0])
- for s in a[1:] {
- i += copy(b[i:], sep)
- i += copy(b[i:], s)
- }
- return string(b)
-}
-
-join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (str: string, err: mem.Allocator_Error) {
+join :: proc(a: []string, sep: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if len(a) == 0 {
return "", nil
}
@@ -317,58 +613,118 @@ join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (
}
return string(b), nil
}
-
/*
- returns a combined string from the slice of strings `a` without a seperator
- allocates the string using the `allocator`
-
+Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure
- a := [?]string { "a", "b", "c" }
- b := strings.concatenate(a[:]) -> "abc"
+*Allocates Using Provided Allocator*
+
+Inputs:
+- a: A slice of strings to join
+- sep: The separator string
+- allocator: (default is context.allocator)
+
+Returns:
+- str: A combined string from the slice of strings `a` separated with the `sep` string
+- err: An allocator error if one occured, `nil` otherwise
*/
-concatenate :: proc(a: []string, allocator := context.allocator) -> string {
+@(deprecated="Prefer join. It now returns an optional allocator error")
+join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
+ return join(a, sep, allocator)
+}
+/*
+Returns a combined string from the slice of strings `a` without a separator
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- a: A slice of strings to concatenate
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The concatenated string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ concatenate_example :: proc() {
+ a := [?]string { "a", "b", "c" }
+ fmt.println(strings.concatenate(a[:]))
+ }
+
+Output:
+
+ abc
+
+*/
+concatenate :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if len(a) == 0 {
- return ""
+ return "", nil
}
n := 0
for s in a {
n += len(s)
}
- b := make([]byte, n, allocator)
+ b := make([]byte, n, allocator) or_return
i := 0
for s in a {
i += copy(b[i:], s)
}
- return string(b)
+ return string(b), nil
}
+/*
+Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails
+*Allocates Using Provided Allocator*
+
+Inputs:
+- a: A slice of strings to concatenate
+- allocator: (default is context.allocator)
+
+Returns:
+The concatenated string, and an error if allocation fails
+*/
+@(deprecated="Prefer concatenate. It now returns an optional allocator error")
concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) {
- if len(a) == 0 {
- return "", nil
- }
-
- n := 0
- for s in a {
- n += len(s)
- }
- b := make([]byte, n, allocator) or_return
- i := 0
- for s in a {
- i += copy(b[i:], s)
- }
- return string(b), nil
+ return concatenate(a, allocator)
}
-
/*
- `rune_offset` and `rune_length` are in runes, not bytes.
- If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`.
+Returns a substring of the input string `s` with the specified rune offset and length
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to cut
+- rune_offset: The starting rune index (default is 0). In runes, not bytes.
+- rune_length: The number of runes to include in the substring (default is 0, which returns the remainder of the string). In runes, not bytes.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The substring
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ cut_example :: proc() {
+ fmt.println(strings.cut("some example text", 0, 4)) // -> "some"
+ fmt.println(strings.cut("some example text", 2, 2)) // -> "me"
+ fmt.println(strings.cut("some example text", 5, 7)) // -> "example"
+ }
+
+Output:
+
+ some
+ me
+ example
- strings.cut("some example text", 0, 4) -> "some"
- strings.cut("some example text", 2, 2) -> "me"
- strings.cut("some example text", 5, 7) -> "example"
*/
-cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string) {
+cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
s := s; rune_length := rune_length
context.allocator = allocator
@@ -384,7 +740,7 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator :
// We're asking for a substring starting after the end of the input string.
// That's just an empty string.
if rune_offset >= rune_count {
- return ""
+ return "", nil
}
// If we don't specify the length of the substring, use the remainder.
@@ -396,7 +752,7 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator :
// But we do know it's bounded by the number of runes * 4 bytes,
// and can be no more than the size of the input string.
bytes_needed := min(rune_length * 4, len(s))
- buf := make([]u8, bytes_needed)
+ buf := make([]u8, bytes_needed) or_return
byte_offset := 0
for i := 0; i < rune_count; i += 1 {
@@ -417,15 +773,34 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0), allocator :
}
s = s[w:]
}
- return string(buf[:byte_offset])
+ return string(buf[:byte_offset]), nil
}
+/*
+Splits the input string `s` into a slice of substrings separated by the specified `sep` string
+*Allocates Using Provided Allocator*
+
+*Used Internally - Private Function*
+
+Inputs:
+- s: The input string to split
+- sep: The separator string
+- sep_save: A flag determining if the separator should be saved in the resulting substrings
+- n: The maximum number of substrings to return, returns `nil` without alloc when `n=0`
+- allocator: (default is context.allocator)
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Returns:
+- res: The slice of substrings
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
@private
-_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string {
+_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) {
s, n := s_, n_
if n == 0 {
- return nil
+ return nil, nil
}
if sep == "" {
@@ -434,7 +809,7 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
n = l
}
- res := make([dynamic]string, n, allocator)
+ res := make([]string, n, allocator) or_return
for i := 0; i < n-1; i += 1 {
_, w := utf8.decode_rune_in_string(s)
res[i] = s[:w]
@@ -443,14 +818,14 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
if n > 0 {
res[n-1] = s
}
- return res[:]
+ return res[:], nil
}
if n < 0 {
n = count(s, sep) + 1
}
- res := make([dynamic]string, n, allocator)
+ res = make([]string, n, allocator) or_return
n -= 1
@@ -465,60 +840,164 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
}
res[i] = s
- return res[:i+1]
+ return res[:i+1], nil
}
-
/*
- Splits a string into parts, based on a separator.
- Returned strings are substrings of 's'.
- ```
- s := "aaa.bbb.ccc.ddd.eee" // 5 parts
- ss := split(s, ".")
- fmt.println(ss) // [aaa, bbb, ccc, ddd, eee]
- ```
+Splits a string into parts based on a separator.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to split.
+- sep: The separator string used to split the input string.
+- allocator: (default is context.allocator).
+
+Returns:
+- res: The slice of strings, each representing a part of the split string.
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_example :: proc() {
+ s := "aaa.bbb.ccc.ddd.eee" // 5 parts
+ ss := strings.split(s, ".")
+ fmt.println(ss)
+ }
+
+Output:
+
+ ["aaa", "bbb", "ccc", "ddd", "eee"]
+
*/
-split :: proc(s, sep: string, allocator := context.allocator) -> []string {
+split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, 0, -1, allocator)
}
-
/*
- Splits a string into a total of 'n' parts, based on a separator.
- Returns fewer parts if there wasn't enough occurrences of the separator.
- Returned strings are substrings of 's'.
- ```
- s := "aaa.bbb.ccc.ddd.eee" // 5 parts present
- ss := split_n(s, ".", 3) // total of 3 wanted
- fmt.println(ss) // [aaa, bbb, ccc.ddd.eee]
- ```
+Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to split.
+- sep: The separator string used to split the input string.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The slice of strings, each representing a part of the split string.
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_n_example :: proc() {
+ s := "aaa.bbb.ccc.ddd.eee" // 5 parts present
+ ss := strings.split_n(s, ".",3) // total of 3 wanted
+ fmt.println(ss)
+ }
+
+Output:
+
+ ["aaa", "bbb", "ccc.ddd.eee"]
+
*/
-split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
+split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, 0, n, allocator)
}
-
/*
- splits the string `s` after the seperator string `sep` appears
- returns the slice of split strings allocated using `allocator`
+Splits a string into parts after the separator, retaining it in the substrings.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to split.
+- sep: The separator string used to split the input string.
+- allocator: (default is context.allocator).
+
+Returns:
+- res: The slice of strings, each representing a part of the split string after the separator
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_after_example :: proc() {
+ a := "aaa.bbb.ccc.ddd.eee" // 5 parts
+ aa := strings.split_after(a, ".")
+ fmt.println(aa)
+ }
+
+Output:
+
+ ["aaa.", "bbb.", "ccc.", "ddd.", "eee"]
- a := "aaa.bbb.ccc.ddd.eee"
- aa := strings.split_after(a, ".")
- fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee]
*/
-split_after :: proc(s, sep: string, allocator := context.allocator) -> []string {
+split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, len(sep), -1, allocator)
}
-
/*
- splits the string `s` after the seperator string `sep` appears into a total of `n` parts
- returns the slice of split strings allocated using `allocator`
+Splits a string into a total of `n` parts after the separator.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to split.
+- sep: The separator string used to split the input string.
+- n: The maximum number of parts to split the string into.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The slice of strings with `n` parts or fewer if there weren't
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_after_n_example :: proc() {
+ a := "aaa.bbb.ccc.ddd.eee"
+ aa := strings.split_after_n(a, ".", 3)
+ fmt.println(aa)
+ }
+
+Output:
+
+ ["aaa.", "bbb.", "ccc.ddd.eee"]
- a := "aaa.bbb.ccc.ddd.eee"
- aa := strings.split_after(a, ".")
- fmt.eprintln(aa) // [aaa., bbb., ccc., ddd., eee]
*/
-split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
+split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
return _split(s, sep, len(sep), n, allocator)
}
+/*
+Searches for the first occurrence of `sep` in the given string and returns the substring
+up to (but not including) the separator, as well as a boolean indicating success.
+*Used Internally - Private Function*
+
+Inputs:
+- s: Pointer to the input string, which is modified during the search.
+- sep: The separator string to search for.
+- sep_save: Number of characters from the separator to include in the result.
+
+Returns:
+- res: The resulting substring
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+*/
@private
_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
// stop once the string is empty or nil
@@ -546,15 +1025,37 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string,
}
return
}
-
/*
- split the ^string `s` by the byte seperator `sep` in an iterator fashion
- consumes the original string till the end, leaving the string `s` with len == 0
+Splits the input string by the byte separator in an iterator fashion.
- text := "a.b.c.d.e"
- for str in strings.split_by_byte_iterator(&text, '.') {
- fmt.eprintln(str) // every loop -> a b c d e
+Inputs:
+- s: Pointer to the input string, which is modified during the search.
+- sep: The byte separator to search for.
+
+Returns:
+- res: The resulting substring
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_by_byte_iterator_example :: proc() {
+ text := "a.b.c.d.e"
+ for str in strings.split_by_byte_iterator(&text, '.') {
+ fmt.println(str) // every loop -> a b c d e
+ }
}
+
+Output:
+
+ a
+ b
+ c
+ d
+ e
+
*/
split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
m := index_byte(s^, sep)
@@ -570,36 +1071,89 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
}
return
}
-
/*
- split the ^string `s` by the seperator string `sep` in an iterator fashion
- consumes the original string till the end
+Splits the input string by the separator string in an iterator fashion.
- text := "a.b.c.d.e"
- for str in strings.split_iterator(&text, ".") {
- fmt.eprintln(str) // every loop -> a b c d e
+Inputs:
+- s: Pointer to the input string, which is modified during the search.
+- sep: The separator string to search for.
+
+Returns:
+- res: The resulting substring
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_iterator_example :: proc() {
+ text := "a.b.c.d.e"
+ for str in strings.split_iterator(&text, ".") {
+ fmt.println(str)
+ }
}
+
+Output:
+
+ a
+ b
+ c
+ d
+ e
+
*/
-split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
+split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
return _split_iterator(s, sep, 0)
}
-
/*
- split the ^string `s` after every seperator string `sep` in an iterator fashion
- consumes the original string till the end
+Splits the input string after every separator string in an iterator fashion.
- text := "a.b.c.d.e"
- for str in strings.split_after_iterator(&text, ".") {
- fmt.eprintln(str) // every loop -> a. b. c. d. e
+Inputs:
+- s: Pointer to the input string, which is modified during the search.
+- sep: The separator string to search for.
+
+Returns:
+- res: The resulting substring
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_after_iterator_example :: proc() {
+ text := "a.b.c.d.e"
+ for str in strings.split_after_iterator(&text, ".") {
+ fmt.println(str)
+ }
}
+
+Output:
+
+ a.
+ b.
+ c.
+ d.
+ e
+
*/
-split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
+split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) {
return _split_iterator(s, sep, len(sep))
}
+/*
+Trims the carriage return character from the end of the input string.
+*Used Internally - Private Function*
+Inputs:
+- s: The input string to trim.
+
+Returns:
+- res: The trimmed string as a slice of the original.
+*/
@(private)
-_trim_cr :: proc(s: string) -> string {
+_trim_cr :: proc(s: string) -> (res: string) {
n := len(s)
if n > 0 {
if s[n-1] == '\r' {
@@ -608,116 +1162,266 @@ _trim_cr :: proc(s: string) -> string {
}
return s
}
-
/*
- split the string `s` at every line break '\n'
- return an allocated slice of strings
+Splits the input string at every line break `\n`.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to split.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The slice (allocated) of the split string (slices into original string)
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_lines_example :: proc() {
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines(a)
+ fmt.println(b)
+ }
+
+Output:
+
+ ["a", "b", "c", "d", "e"]
- a := "a\nb\nc\nd\ne"
- b := strings.split_lines(a)
- fmt.eprintln(b) // [a, b, c, d, e]
*/
-split_lines :: proc(s: string, allocator := context.allocator) -> []string {
+split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
sep :: "\n"
- lines := _split(s, sep, 0, -1, allocator)
+ lines := _split(s, sep, 0, -1, allocator) or_return
for line in &lines {
line = _trim_cr(line)
}
- return lines
+ return lines, nil
}
-
/*
- split the string `s` at every line break '\n' for `n` parts
- return an allocated slice of strings
+Splits the input string at every line break `\n` for `n` parts.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to split.
+- n: The number of parts to split into.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The slice (allocated) of the split string (slices into original string)
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_lines_n_example :: proc() {
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines_n(a, 3)
+ fmt.println(b)
+ }
+
+Output:
+
+ ["a", "b", "c\nd\ne"]
- a := "a\nb\nc\nd\ne"
- b := strings.split_lines_n(a, 3)
- fmt.eprintln(b) // [a, b, c, d\ne\n]
*/
-split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> []string {
+split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
sep :: "\n"
- lines := _split(s, sep, 0, n, allocator)
+ lines := _split(s, sep, 0, n, allocator) or_return
for line in &lines {
line = _trim_cr(line)
}
- return lines
+ return lines, nil
}
-
/*
- split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
- return an allocated slice of strings
+Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to split.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The slice (allocated) of the split string (slices into original string), with `\n` included
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_lines_after_example :: proc() {
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines_after(a)
+ fmt.println(b)
+ }
+
+Output:
+
+ ["a\n", "b\n", "c\n", "d\n", "e"]
- a := "a\nb\nc\nd\ne"
- b := strings.split_lines_after(a)
- fmt.eprintln(b) // [a\n, b\n, c\n, d\n, e\n]
*/
-split_lines_after :: proc(s: string, allocator := context.allocator) -> []string {
+split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
sep :: "\n"
- lines := _split(s, sep, len(sep), -1, allocator)
+ lines := _split(s, sep, len(sep), -1, allocator) or_return
for line in &lines {
line = _trim_cr(line)
}
- return lines
+ return lines, nil
}
-
/*
- split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
- only runs for `n` parts
- return an allocated slice of strings
+Splits the input string at every line break `\n` leaving the `\n` in the resulting strings.
+Only runs for n parts.
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string to split.
+- n: The number of parts to split into.
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The slice (allocated) of the split string (slices into original string), with `\n` included
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_lines_after_n_example :: proc() {
+ a := "a\nb\nc\nd\ne"
+ b := strings.split_lines_after_n(a, 3)
+ fmt.println(b)
+ }
+
+Output:
+
+ ["a\n", "b\n", "c\nd\ne"]
- a := "a\nb\nc\nd\ne"
- b := strings.split_lines_after_n(a, 3)
- fmt.eprintln(b) // [a\n, b\n, c\n, d\ne\n]
*/
-split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> []string {
+split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error {
sep :: "\n"
- lines := _split(s, sep, len(sep), n, allocator)
+ lines := _split(s, sep, len(sep), n, allocator) or_return
for line in &lines {
line = _trim_cr(line)
}
- return lines
+ return lines, nil
}
-
/*
- split the string `s` at every line break '\n'
- returns the current split string every iteration till the string is consumed
+Splits the input string at every line break `\n`.
+Returns the current split string every iteration until the string is consumed.
- text := "a\nb\nc\nd\ne"
- for str in strings.split_lines_iterator(&text) {
- fmt.eprintln(text) // every loop -> a b c d e
+Inputs:
+- s: Pointer to the input string, which is modified during the search.
+
+Returns:
+- line: The resulting substring
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_lines_iterator_example :: proc() {
+ text := "a\nb\nc\nd\ne"
+ for str in strings.split_lines_iterator(&text) {
+ fmt.print(str) // every loop -> a b c d e
+ }
+ fmt.print("\n")
}
+
+Output:
+
+ abcde
+
*/
split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
sep :: "\n"
line = _split_iterator(s, sep, 0) or_return
return _trim_cr(line), true
}
-
/*
- split the string `s` at every line break '\n'
- returns the current split string every iteration till the string is consumed
+Splits the input string at every line break `\n`.
+Returns the current split string with line breaks included every iteration until the string is consumed.
- text := "a\nb\nc\nd\ne"
- for str in strings.split_lines_after_iterator(&text) {
- fmt.eprintln(text) // every loop -> a\n b\n c\n d\n e\n
+Inputs:
+- s: Pointer to the input string, which is modified during the search.
+
+Returns:
+- line: The resulting substring with line breaks included
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_lines_after_iterator_example :: proc() {
+ text := "a\nb\nc\nd\ne\n"
+ for str in strings.split_lines_after_iterator(&text) {
+ fmt.print(str) // every loop -> a\n b\n c\n d\n e\n
+ }
}
+
+Output:
+
+ a
+ b
+ c
+ d
+ e
+
*/
split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) {
sep :: "\n"
line = _split_iterator(s, sep, len(sep)) or_return
return _trim_cr(line), true
}
-
/*
- returns the byte offset of the first byte `c` in the string `s` it finds, -1 when not found
- can't find utf8 based runes
+Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found.
+NOTE: Can't find UTF-8 based runes.
+
+Inputs:
+- s: The input string to search in.
+- c: The byte to search for.
+
+Returns:
+- res: The byte offset of the first occurrence of `c` in `s`, or -1 if not found.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ index_byte_example :: proc() {
+ fmt.println(strings.index_byte("test", 't'))
+ fmt.println(strings.index_byte("test", 'e'))
+ fmt.println(strings.index_byte("test", 'x'))
+ fmt.println(strings.index_byte("teƤst", 'Ƥ'))
+ }
+
+Output:
+
+ 0
+ 1
+ -1
+ -1
- strings.index_byte("test", 't') -> 0
- strings.index_byte("test", 'e') -> 1
- strings.index_byte("test", 'x') -> -1
- strings.index_byte("teƤst", 'Ƥ') -> -1
*/
-index_byte :: proc(s: string, c: byte) -> int {
+index_byte :: proc(s: string, c: byte) -> (res: int) {
for i := 0; i < len(s); i += 1 {
if s[i] == c {
return i
@@ -725,17 +1429,39 @@ index_byte :: proc(s: string, c: byte) -> int {
}
return -1
}
-
/*
- returns the byte offset of the last byte `c` in the string `s` it finds, -1 when not found
- can't find utf8 based runes
+Returns the byte offset of the last byte `c` in the string `s`, -1 when not found.
+
+Inputs:
+- s: The input string to search in.
+- c: The byte to search for.
+
+Returns:
+- res: The byte offset of the last occurrence of `c` in `s`, or -1 if not found.
+
+NOTE: Can't find UTF-8 based runes.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ last_index_byte_example :: proc() {
+ fmt.println(strings.last_index_byte("test", 't'))
+ fmt.println(strings.last_index_byte("test", 'e'))
+ fmt.println(strings.last_index_byte("test", 'x'))
+ fmt.println(strings.last_index_byte("teƤst", 'Ƥ'))
+ }
+
+Output:
+
+ 3
+ 1
+ -1
+ -1
- strings.index_byte("test", 't') -> 3
- strings.index_byte("test", 'e') -> 1
- strings.index_byte("test", 'x') -> -1
- strings.index_byte("teƤst", 'Ƥ') -> -1
*/
-last_index_byte :: proc(s: string, c: byte) -> int {
+last_index_byte :: proc(s: string, c: byte) -> (res: int) {
for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c {
return i
@@ -743,22 +1469,46 @@ last_index_byte :: proc(s: string, c: byte) -> int {
}
return -1
}
-
-
/*
- returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found
- avoids invalid runes
+Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found.
+Invalid runes return -1
+
+Inputs:
+- s: The input string to search in.
+- r: The rune to search for.
+
+Returns:
+- res: The byte offset of the first occurrence of `r` in `s`, or -1 if not found.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ index_rune_example :: proc() {
+ fmt.println(strings.index_rune("abcƤdef", 'x'))
+ fmt.println(strings.index_rune("abcƤdef", 'a'))
+ fmt.println(strings.index_rune("abcƤdef", 'b'))
+ fmt.println(strings.index_rune("abcƤdef", 'c'))
+ fmt.println(strings.index_rune("abcƤdef", 'Ƥ'))
+ fmt.println(strings.index_rune("abcƤdef", 'd'))
+ fmt.println(strings.index_rune("abcƤdef", 'e'))
+ fmt.println(strings.index_rune("abcƤdef", 'f'))
+ }
+
+Output:
+
+ -1
+ 0
+ 1
+ 2
+ 3
+ 5
+ 6
+ 7
- strings.index_rune("abcƤdef", 'x') -> -1
- strings.index_rune("abcƤdef", 'a') -> 0
- strings.index_rune("abcƤdef", 'b') -> 1
- strings.index_rune("abcƤdef", 'c') -> 2
- strings.index_rune("abcƤdef", 'Ƥ') -> 3
- strings.index_rune("abcƤdef", 'd') -> 5
- strings.index_rune("abcƤdef", 'e') -> 6
- strings.index_rune("abcƤdef", 'f') -> 7
*/
-index_rune :: proc(s: string, r: rune) -> int {
+index_rune :: proc(s: string, r: rune) -> (res: int) {
switch {
case u32(r) < utf8.RUNE_SELF:
return index_byte(s, byte(r))
@@ -780,16 +1530,37 @@ index_rune :: proc(s: string, r: rune) -> int {
}
@private PRIME_RABIN_KARP :: 16777619
-
/*
- returns the byte offset of the string `substr` in the string `s`, -1 when not found
-
- strings.index("test", "t") -> 0
- strings.index("test", "te") -> 0
- strings.index("test", "st") -> 2
- strings.index("test", "tt") -> -1
+Returns the byte offset of the string `substr` in the string `s`, -1 when not found.
+
+Inputs:
+- s: The input string to search in.
+- substr: The substring to search for.
+
+Returns:
+- res: The byte offset of the first occurrence of `substr` in `s`, or -1 if not found.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ index_example :: proc() {
+ fmt.println(strings.index("test", "t"))
+ fmt.println(strings.index("test", "te"))
+ fmt.println(strings.index("test", "st"))
+ fmt.println(strings.index("test", "tt"))
+ }
+
+Output:
+
+ 0
+ 0
+ 2
+ -1
+
*/
-index :: proc(s, substr: string) -> int {
+index :: proc(s, substr: string) -> (res: int) {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i])
@@ -838,16 +1609,37 @@ index :: proc(s, substr: string) -> int {
}
return -1
}
-
/*
- returns the last byte offset of the string `substr` in the string `s`, -1 when not found
-
- strings.index("test", "t") -> 3
- strings.index("test", "te") -> 0
- strings.index("test", "st") -> 2
- strings.index("test", "tt") -> -1
+Returns the last byte offset of the string `substr` in the string `s`, -1 when not found.
+
+Inputs:
+- s: The input string to search in.
+- substr: The substring to search for.
+
+Returns:
+- res: The byte offset of the last occurrence of `substr` in `s`, or -1 if not found.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ last_index_example :: proc() {
+ fmt.println(strings.last_index("test", "t"))
+ fmt.println(strings.last_index("test", "te"))
+ fmt.println(strings.last_index("test", "st"))
+ fmt.println(strings.last_index("test", "tt"))
+ }
+
+Output:
+
+ 3
+ 0
+ 2
+ -1
+
*/
-last_index :: proc(s, substr: string) -> int {
+last_index :: proc(s, substr: string) -> (res: int) {
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := len(s) - 1; i >= 0; i -= 1 {
hash = hash*PRIME_RABIN_KARP + u32(s[i])
@@ -894,17 +1686,39 @@ last_index :: proc(s, substr: string) -> int {
}
return -1
}
-
/*
- returns the index of any first char of `chars` found in `s`, -1 if not found
-
- strings.index_any("test", "s") -> 2
- strings.index_any("test", "se") -> 1
- strings.index_any("test", "et") -> 0
- strings.index_any("test", "set") -> 0
- strings.index_any("test", "x") -> -1
+Returns the index of any first char of `chars` found in `s`, -1 if not found.
+
+Inputs:
+- s: The input string to search in.
+- chars: The characters to look for
+
+Returns:
+- res: The index of the first character of `chars` found in `s`, or -1 if not found.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ index_any_example :: proc() {
+ fmt.println(strings.index_any("test", "s"))
+ fmt.println(strings.index_any("test", "se"))
+ fmt.println(strings.index_any("test", "et"))
+ fmt.println(strings.index_any("test", "set"))
+ fmt.println(strings.index_any("test", "x"))
+ }
+
+Output:
+
+ 2
+ 1
+ 0
+ 0
+ -1
+
*/
-index_any :: proc(s, chars: string) -> int {
+index_any :: proc(s, chars: string) -> (res: int) {
if chars == "" {
return -1
}
@@ -935,18 +1749,39 @@ index_any :: proc(s, chars: string) -> int {
}
return -1
}
-
/*
- returns the last matching index in `s` of any char in `chars` found in `s`, -1 if not found
- iterates the string in reverse
+Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse.
+
+Inputs:
+- s: The string to search in
+- chars: The characters to look for
+
+Returns:
+- res: The index of the last matching character, or -1 if not found
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ last_index_any_example :: proc() {
+ fmt.println(strings.last_index_any("test", "s"))
+ fmt.println(strings.last_index_any("test", "se"))
+ fmt.println(strings.last_index_any("test", "et"))
+ fmt.println(strings.last_index_any("test", "set"))
+ fmt.println(strings.last_index_any("test", "x"))
+ }
+
+Output:
+
+ 2
+ 2
+ 3
+ 3
+ -1
- strings.last_index_any("test", "s") -> 2
- strings.last_index_any("test", "se") -> 2
- strings.last_index_any("test", "et") -> 3
- strings.last_index_any("test", "set") -> 3
- strings.last_index_any("test", "x") -> -1
*/
-last_index_any :: proc(s, chars: string) -> int {
+last_index_any :: proc(s, chars: string) -> (res: int) {
if chars == "" {
return -1
}
@@ -994,18 +1829,79 @@ last_index_any :: proc(s, chars: string) -> int {
}
return -1
}
-
/*
- returns the count of the string `substr` found in the string `s`
- returns the rune_count + 1 of the string `s` on empty `substr`
+Finds the first occurrence of any substring in `substrs` within `s`
- strings.count("abbccc", "a") -> 1
- strings.count("abbccc", "b") -> 2
- strings.count("abbccc", "c") -> 3
- strings.count("abbccc", "ab") -> 1
- strings.count("abbccc", " ") -> 0
+Inputs:
+- s: The string to search in
+- substrs: The substrings to look for
+
+Returns:
+- idx: the index of the first matching substring
+- width: the length of the found substring
*/
-count :: proc(s, substr: string) -> int {
+index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) {
+ idx = -1
+ if s == "" || len(substrs) <= 0 {
+ return
+ }
+ // disallow "" substr
+ for substr in substrs {
+ if len(substr) == 0 {
+ return
+ }
+ }
+
+ lowest_index := len(s)
+ found := false
+ for substr in substrs {
+ if i := index(s, substr); i >= 0 {
+ if i < lowest_index {
+ lowest_index = i
+ width = len(substr)
+ found = true
+ }
+ }
+ }
+
+ if found {
+ idx = lowest_index
+ }
+ return
+}
+/*
+Counts the number of non-overlapping occurrences of `substr` in `s`
+
+Inputs:
+- s: The string to search in
+- substr: The substring to count
+
+Returns:
+- res: The number of occurrences of `substr` in `s`, returns the rune_count + 1 of the string `s` on empty `substr`
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ count_example :: proc() {
+ fmt.println(strings.count("abbccc", "a"))
+ fmt.println(strings.count("abbccc", "b"))
+ fmt.println(strings.count("abbccc", "c"))
+ fmt.println(strings.count("abbccc", "ab"))
+ fmt.println(strings.count("abbccc", " "))
+ }
+
+Output:
+
+ 1
+ 2
+ 3
+ 1
+ 0
+
+*/
+count :: proc(s, substr: string) -> (res: int) {
if len(substr) == 0 { // special case
return rune_count(s) + 1
}
@@ -1039,50 +1935,122 @@ count :: proc(s, substr: string) -> int {
}
return n
}
-
/*
- repeats the string `s` multiple `count` times and returns the allocated string
- panics when `count` is below 0
+Repeats the string `s` `count` times, concatenating the result
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to repeat
+- count: The number of times to repeat `s`
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The concatenated repeated string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+WARNING: Panics if count < 0
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ repeat_example :: proc() {
+ fmt.println(strings.repeat("abc", 2))
+ }
+
+Output:
+
+ abcabc
- strings.repeat("abc", 2) -> "abcabc"
*/
-repeat :: proc(s: string, count: int, allocator := context.allocator) -> string {
+repeat :: proc(s: string, count: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if count < 0 {
panic("strings: negative repeat count")
} else if count > 0 && (len(s)*count)/count != len(s) {
panic("strings: repeat count will cause an overflow")
}
- b := make([]byte, len(s)*count, allocator)
+ b := make([]byte, len(s)*count, allocator) or_return
i := copy(b, s)
for i < len(b) { // 2^N trick to reduce the need to copy
copy(b[i:], b[:i])
i *= 2
}
- return string(b)
+ return string(b), nil
}
-
/*
- replaces all instances of `old` in the string `s` with the `new` string
- returns the `output` string and true when an a allocation through a replace happened
+Replaces all occurrences of `old` in `s` with `new`
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The string to modify
+- old: The substring to replace
+- new: The substring to replace `old` with
+- allocator: The allocator to use for the new string (default is context.allocator)
+
+Returns:
+- output: The modified string
+- was_allocation: `true` if an allocation occurred during the replacement, `false` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ replace_all_example :: proc() {
+ fmt.println(strings.replace_all("xyzxyz", "xyz", "abc"))
+ fmt.println(strings.replace_all("xyzxyz", "abc", "xyz"))
+ fmt.println(strings.replace_all("xyzxyz", "xy", "z"))
+ }
+
+Output:
+
+ abcabc true
+ xyzxyz false
+ zzzz true
- strings.replace_all("xyzxyz", "xyz", "abc") -> "abcabc", true
- strings.replace_all("xyzxyz", "abc", "xyz") -> "xyzxyz", false
- strings.replace_all("xyzxyz", "xy", "z") -> "zzzz", true
*/
replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, old, new, -1, allocator)
}
-
/*
- replaces `n` instances of `old` in the string `s` with the `new` string
- if n < 0, no limit on the number of replacements
- returns the `output` string and true when an a allocation through a replace happened
+Replaces n instances of old in the string s with the new string
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- old: The substring to be replaced
+- new: The replacement string
+- n: The number of instances to replace (if `n < 0`, no limit on the number of replacements)
+- allocator: (default: context.allocator)
+
+Returns:
+- output: The modified string
+- was_allocation: `true` if an allocation occurred during the replacement, `false` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ replace_example :: proc() {
+ fmt.println(strings.replace("xyzxyz", "xyz", "abc", 2))
+ fmt.println(strings.replace("xyzxyz", "xyz", "abc", 1))
+ fmt.println(strings.replace("xyzxyz", "abc", "xyz", -1))
+ fmt.println(strings.replace("xyzxyz", "xy", "z", -1))
+ }
+
+Output:
+
+ abcabc true
+ abcxyz true
+ xyzxyz false
+ zzzz true
- strings.replace("xyzxyz", "xyz", "abc", 2) -> "abcabc", true
- strings.replace("xyzxyz", "xyz", "abc", 1) -> "abcxyz", true
- strings.replace("xyzxyz", "abc", "xyz", -1) -> "xyzxyz", false
- strings.replace("xyzxyz", "xy", "z", -1) -> "zzzz", true
*/
replace :: proc(s, old, new: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
if old == new || n == 0 {
@@ -1123,45 +2091,108 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
output = string(t[0:w])
return
}
-
/*
- removes the `key` string `n` times from the `s` string
- if n < 0, no limit on the number of removes
- returns the `output` string and true when an a allocation through a remove happened
+Removes the key string `n` times from the `s` string
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- key: The substring to be removed
+- n: The number of instances to remove (if `n < 0`, no limit on the number of removes)
+- allocator: (default: context.allocator)
+
+Returns:
+- output: The modified string
+- was_allocation: `true` if an allocation occurred during the replacement, `false` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ remove_example :: proc() {
+ fmt.println(strings.remove("abcabc", "abc", 1))
+ fmt.println(strings.remove("abcabc", "abc", -1))
+ fmt.println(strings.remove("abcabc", "a", -1))
+ fmt.println(strings.remove("abcabc", "x", -1))
+ }
+
+Output:
+
+ abc true
+ true
+ bcbc true
+ abcabc false
- strings.remove("abcabc", "abc", 1) -> "abc", true
- strings.remove("abcabc", "abc", -1) -> "", true
- strings.remove("abcabc", "a", -1) -> "bcbc", true
- strings.remove("abcabc", "x", -1) -> "abcabc", false
*/
remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, key, "", n, allocator)
}
-
/*
- removes all the `key` string instanes from the `s` string
- returns the `output` string and true when an a allocation through a remove happened
+Removes all the `key` string instances from the `s` string
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- key: The substring to be removed
+- allocator: (default: context.allocator)
+
+Returns:
+- output: The modified string
+- was_allocation: `true` if an allocation occurred during the replacement, `false` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ remove_all_example :: proc() {
+ fmt.println(strings.remove_all("abcabc", "abc"))
+ fmt.println(strings.remove_all("abcabc", "a"))
+ fmt.println(strings.remove_all("abcabc", "x"))
+ }
+
+Output:
+
+ true
+ bcbc true
+ abcabc false
- strings.remove("abcabc", "abc") -> "", true
- strings.remove("abcabc", "a") -> "bcbc", true
- strings.remove("abcabc", "x") -> "abcabc", false
*/
remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return remove(s, key, -1, allocator)
}
-
+// Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ')
@(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true}
-// return true when the `r` rune is '\t', '\n', '\v', '\f', '\r' or ' '
-is_ascii_space :: proc(r: rune) -> bool {
+/*
+Returns true when the `r` rune is an ASCII whitespace character.
+
+Inputs:
+- r: the rune to test
+
+Returns:
+-res: `true` if `r` is a whitespace character, `false` if otherwise
+*/
+is_ascii_space :: proc(r: rune) -> (res: bool) {
if r < utf8.RUNE_SELF {
return _ascii_space[u8(r)]
}
return false
}
-// returns true when the `r` rune is any asci or utf8 based whitespace
-is_space :: proc(r: rune) -> bool {
+/*
+Returns true when the `r` rune is an ASCII or UTF-8 whitespace character.
+
+Inputs:
+- r: the rune to test
+
+Returns:
+-res: `true` if `r` is a whitespace character, `false` if otherwise
+*/
+is_space :: proc(r: rune) -> (res: bool) {
if r < 0x2000 {
switch r {
case '\t', '\n', '\v', '\f', '\r', ' ', 0x85, 0xa0, 0x1680:
@@ -1179,25 +2210,56 @@ is_space :: proc(r: rune) -> bool {
return false
}
-// returns true when the `r` rune is a nul byte
-is_null :: proc(r: rune) -> bool {
+/*
+Returns true when the `r` rune is `0x0`
+
+Inputs:
+- r: the rune to test
+
+Returns:
+-res: `true` if `r` is `0x0`, `false` if otherwise
+*/
+is_null :: proc(r: rune) -> (res: bool) {
return r == 0x0000
}
/*
- runs trough the `s` string linearly and watches wether the `p` procedure matches the `truth` bool
- returns the rune offset or -1 when no match was found
+Find the index of the first rune `r` in string `s` for which procedure `p` returns the same as truth, or -1 if no such rune appears.
- call :: proc(r: rune) -> bool {
- return r == 'a'
+Inputs:
+- s: The input string
+- p: A procedure that takes a rune and returns a boolean
+- truth: The boolean value to be matched (default: `true`)
+
+Returns:
+- res: The index of the first matching rune, or -1 if no match was found
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ index_proc_example :: proc() {
+ call :: proc(r: rune) -> bool {
+ return r == 'a'
+ }
+ fmt.println(strings.index_proc("abcabc", call))
+ fmt.println(strings.index_proc("cbacba", call))
+ fmt.println(strings.index_proc("cbacba", call, false))
+ fmt.println(strings.index_proc("abcabc", call, false))
+ fmt.println(strings.index_proc("xyz", call))
}
- strings.index_proc("abcabc", call) -> 0
- strings.index_proc("cbacba", call) -> 2
- strings.index_proc("cbacba", call, false) -> 0
- strings.index_proc("abcabc", call, false) -> 1
- strings.index_proc("xyz", call) -> -1
+
+Output:
+
+ 0
+ 2
+ 0
+ 1
+ -1
+
*/
-index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
+index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) {
for r, i in s {
if p(r) == truth {
return i
@@ -1205,9 +2267,8 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
}
return -1
}
-
-// same as `index_proc` but with a `p` procedure taking a rawptr for state
-index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
+// Same as `index_proc`, but the procedure p takes a raw pointer for state
+index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
for r, i in s {
if p(state, r) == truth {
return i
@@ -1215,9 +2276,8 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r
}
return -1
}
-
-// same as `index_proc` but runs through the string in reverse
-last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
+// Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth
+last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
r, size := utf8.decode_last_rune_in_string(s[:i])
@@ -1228,9 +2288,8 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int
}
return -1
}
-
-// same as `index_proc_with_state` but runs through the string in reverse
-last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> int {
+// Same as `index_proc_with_state`, runs through the string in reverse
+last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
r, size := utf8.decode_last_rune_in_string(s[:i])
@@ -1241,48 +2300,86 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
}
return -1
}
-
/*
- trims the input string `s` until the procedure `p` returns false
- does not allocate - only returns a cut variant of the input string
- returns an empty string when no match was found at all
+Trims the input string `s` from the left until the procedure `p` returns false
- find :: proc(r: rune) -> bool {
- return r != 'i'
+Inputs:
+- s: The input string
+- p: A procedure that takes a rune and returns a boolean
+
+Returns:
+- res: The trimmed string as a slice of the original
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ trim_left_proc_example :: proc() {
+ find :: proc(r: rune) -> bool {
+ return r == 'x'
+ }
+ fmt.println(strings.trim_left_proc("xxxxxxtesting", find))
}
- strings.trim_left_proc("testing", find) -> "ing"
+
+Output:
+
+ testing
+
*/
-trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
+trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
i := index_proc(s, p, false)
if i == -1 {
return ""
}
return s[i:]
}
-
/*
- trims the input string `s` until the procedure `p` with state returns false
- returns an empty string when no match was found at all
+Trims the input string `s` from the left until the procedure `p` with state returns false
+
+Inputs:
+- s: The input string
+- p: A procedure that takes a raw pointer and a rune and returns a boolean
+- state: The raw pointer to be passed to the procedure `p`
+
+Returns:
+- res: The trimmed string as a slice of the original
*/
-trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
+trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> (res: string) {
i := index_proc_with_state(s, p, state, false)
if i == -1 {
return ""
}
return s[i:]
}
-
/*
- trims the input string `s` from the right until the procedure `p` returns false
- does not allocate - only returns a cut variant of the input string
- returns an empty string when no match was found at all
+Trims the input string `s` from the right until the procedure `p` returns `false`
- find :: proc(r: rune) -> bool {
- return r != 't'
+Inputs:
+- s: The input string
+- p: A procedure that takes a rune and returns a boolean
+
+Returns:
+- res: The trimmed string as a slice of the original
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ trim_right_proc_example :: proc() {
+ find :: proc(r: rune) -> bool {
+ return r != 't'
+ }
+ fmt.println(strings.trim_right_proc("testing", find))
}
- strings.trim_left_proc("testing", find) -> "test"
+
+Output:
+
+ test
+
*/
-trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
+trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) {
i := last_index_proc(s, p, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
_, w := utf8.decode_rune_in_string(s[i:])
@@ -1292,12 +2389,18 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
}
return s[0:i]
}
-
/*
- trims the input string `s` from the right until the procedure `p` with state returns false
- returns an empty string when no match was found at all
+Trims the input string `s` from the right until the procedure `p` with state returns `false`
+
+Inputs:
+- s: The input string
+- p: A procedure that takes a raw pointer and a rune and returns a boolean
+- state: The raw pointer to be passed to the procedure `p`
+
+Returns:
+- res: The trimmed string as a slice of the original, empty when no match
*/
-trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
+trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> (res: string) {
i := last_index_proc_with_state(s, p, state, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
_, w := utf8.decode_rune_in_string(s[i:])
@@ -1307,9 +2410,8 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta
}
return s[0:i]
}
-
-// procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
-is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
+// Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison
+is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) {
if state == nil {
return false
}
@@ -1321,98 +2423,296 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
}
return false
}
+/*
+Trims the cutset string from the `s` string
-// trims the `cutset` string from the `s` string
-trim_left :: proc(s: string, cutset: string) -> string {
+Inputs:
+- s: The input string
+- cutset: The set of characters to be trimmed from the left of the input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_left :: proc(s: string, cutset: string) -> (res: string) {
if s == "" || cutset == "" {
return s
}
state := cutset
return trim_left_proc_with_state(s, is_in_cutset, &state)
}
+/*
+Trims the cutset string from the `s` string from the right
-// trims the `cutset` string from the `s` string from the right
-trim_right :: proc(s: string, cutset: string) -> string {
+Inputs:
+- s: The input string
+- cutset: The set of characters to be trimmed from the right of the input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_right :: proc(s: string, cutset: string) -> (res: string) {
if s == "" || cutset == "" {
return s
}
state := cutset
return trim_right_proc_with_state(s, is_in_cutset, &state)
}
+/*
+Trims the cutset string from the `s` string, both from left and right
-// trims the `cutset` string from the `s` string, both from left and right
-trim :: proc(s: string, cutset: string) -> string {
+Inputs:
+- s: The input string
+- cutset: The set of characters to be trimmed from both sides of the input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim :: proc(s: string, cutset: string) -> (res: string) {
return trim_right(trim_left(s, cutset), cutset)
}
+/*
+Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t"
-// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t"
-trim_left_space :: proc(s: string) -> string {
+Inputs:
+- s: The input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_left_space :: proc(s: string) -> (res: string) {
return trim_left_proc(s, is_space)
}
+/*
+Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz"
-// trims from the right until a valid non space rune: "\t\txyz\t\t" -> "\t\txyz"
-trim_right_space :: proc(s: string) -> string {
+Inputs:
+- s: The input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_right_space :: proc(s: string) -> (res: string) {
return trim_right_proc(s, is_space)
}
+/*
+Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz"
-// trims from both sides until a valid non space rune: "\t\txyz\t\t" -> "xyz"
-trim_space :: proc(s: string) -> string {
+Inputs:
+- s: The input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_space :: proc(s: string) -> (res: string) {
return trim_right_space(trim_left_space(s))
}
+/*
+Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00"
-// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00"
-trim_left_null :: proc(s: string) -> string {
+Inputs:
+- s: The input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_left_null :: proc(s: string) -> (res: string) {
return trim_left_proc(s, is_null)
}
+/*
+Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing"
-// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing"
-trim_right_null :: proc(s: string) -> string {
+Inputs:
+- s: The input string
+
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_right_null :: proc(s: string) -> (res: string) {
return trim_right_proc(s, is_null)
}
+/*
+Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing"
-// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing"
-trim_null :: proc(s: string) -> string {
+Inputs:
+- s: The input string
+Returns:
+- res: The trimmed string as a slice of the original
+*/
+trim_null :: proc(s: string) -> (res: string) {
return trim_right_null(trim_left_null(s))
}
-
/*
- trims a `prefix` string from the start of the `s` string and returns the trimmed string
- returns the input string `s` when no prefix was found
+Trims a `prefix` string from the start of the `s` string and returns the trimmed string
+
+Inputs:
+- s: The input string
+- prefix: The prefix string to be removed
+
+Returns:
+- res: The trimmed string as a slice of original, or the input string if no prefix was found
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ trim_prefix_example :: proc() {
+ fmt.println(strings.trim_prefix("testing", "test"))
+ fmt.println(strings.trim_prefix("testing", "abc"))
+ }
+
+Output:
+
+ ing
+ testing
- strings.trim_prefix("testing", "test") -> "ing"
- strings.trim_prefix("testing", "abc") -> "testing"
*/
-trim_prefix :: proc(s, prefix: string) -> string {
+trim_prefix :: proc(s, prefix: string) -> (res: string) {
if has_prefix(s, prefix) {
return s[len(prefix):]
}
return s
}
-
/*
- trims a `suffix` string from the end of the `s` string and returns the trimmed string
- returns the input string `s` when no suffix was found
+Trims a `suffix` string from the end of the `s` string and returns the trimmed string
+
+Inputs:
+- s: The input string
+- suffix: The suffix string to be removed
+
+Returns:
+- res: The trimmed string as a slice of original, or the input string if no suffix was found
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ trim_suffix_example :: proc() {
+ fmt.println(strings.trim_suffix("todo.txt", ".txt"))
+ fmt.println(strings.trim_suffix("todo.doc", ".txt"))
+ }
+
+Output:
+
+ todo
+ todo.doc
- strings.trim_suffix("todo.txt", ".txt") -> "todo"
- strings.trim_suffix("todo.doc", ".txt") -> "todo.doc"
*/
-trim_suffix :: proc(s, suffix: string) -> string {
+trim_suffix :: proc(s, suffix: string) -> (res: string) {
if has_suffix(s, suffix) {
return s[:len(s)-len(suffix)]
}
return s
}
-
/*
- splits the input string `s` by all possible `substrs` []string
- returns the allocated []string, nil on any empty substring or no matches
+Splits the input string `s` by all possible `substrs` and returns an allocated array of strings
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- substrs: An array of substrings used for splitting
+- allocator: (default is context.allocator)
+
+Returns:
+- res: An array of strings, or nil on empty substring or no matches
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: Allocation occurs for the array, the splits are all views of the original string.
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_multi_example :: proc() {
+ splits := [?]string { "---", "~~~", ".", "_", "," }
+ res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:])
+ fmt.println(res) // -> [testing, this, out, nice, done, last]
+ }
+
+Output:
+
+ ["testing", "this", "out", "nice", "done", "last"]
- splits := [?]string { "---", "~~~", ".", "_", "," }
- res := strings.split_multi("testing,this.out_nice---done~~~last", splits[:])
- fmt.eprintln(res) // -> [testing, this, out, nice, done, last]
*/
-split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> (buf: []string) #no_bounds_check {
+split_multi :: proc(s: string, substrs: []string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error #no_bounds_check {
if s == "" || len(substrs) <= 0 {
+ return nil, nil
+ }
+
+ // disallow "" substr
+ for substr in substrs {
+ if len(substr) == 0 {
+ return nil, nil
+ }
+ }
+
+ // calculate the needed len of `results`
+ n := 1
+ for it := s; len(it) > 0; {
+ i, w := index_multi(it, substrs)
+ if i < 0 {
+ break
+ }
+ n += 1
+ it = it[i+w:]
+ }
+
+ results := make([dynamic]string, 0, n, allocator) or_return
+ {
+ it := s
+ for len(it) > 0 {
+ i, w := index_multi(it, substrs)
+ if i < 0 {
+ break
+ }
+ part := it[:i]
+ append(&results, part)
+ it = it[i+w:]
+ }
+ append(&results, it)
+ }
+ assert(len(results) == n)
+ return results[:], nil
+}
+/*
+Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match.
+
+Inputs:
+- it: A pointer to the input string
+- substrs: An array of substrings used for splitting
+
+Returns:
+- res: The split string
+- ok: `true` if an iteration result was returned, `false` if the iterator has reached the end
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ split_multi_iterate_example :: proc() {
+ it := "testing,this.out_nice---done~~~last"
+ splits := [?]string { "---", "~~~", ".", "_", "," }
+ for str in strings.split_multi_iterate(&it, splits[:]) {
+ fmt.println(str)
+ }
+ }
+
+Output:
+
+ testing
+ this
+ out
+ nice
+ done
+ last
+
+*/
+split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: bool) #no_bounds_check {
+ if it == nil || len(it) == 0 || len(substrs) <= 0 {
return
}
@@ -1423,137 +2723,52 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator
}
}
- // TODO maybe remove duplicate substrs
- // sort substrings by string size, largest to smallest
- temp_substrs := slice.clone(substrs, context.temp_allocator)
- slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
- return len(a) > len(b)
- })
-
- substrings_found: int
- temp := s
-
- // count substr results found in string
- first_pass: for len(temp) > 0 {
- for substr in temp_substrs {
- size := len(substr)
-
- // check range and compare string to substr
- if size <= len(temp) && temp[:size] == substr {
- substrings_found += 1
- temp = temp[size:]
- continue first_pass
- }
- }
-
- // step through string
- _, skip := utf8.decode_rune_in_string(temp[:])
- temp = temp[skip:]
+ // calculate the needed len of `results`
+ i, w := index_multi(it^, substrs)
+ if i >= 0 {
+ res = it[:i]
+ it^ = it[i+w:]
+ } else {
+ // last value
+ res = it^
+ it^ = it[len(it):]
}
-
- // skip when no results
- if substrings_found < 1 {
- return
- }
-
- buf = make([]string, substrings_found + 1, allocator)
- buf_index: int
- temp = s
- temp_old := temp
-
- // gather results in the same fashion
- second_pass: for len(temp) > 0 {
- for substr in temp_substrs {
- size := len(substr)
-
- // check range and compare string to substr
- if size <= len(temp) && temp[:size] == substr {
- buf[buf_index] = temp_old[:len(temp_old) - len(temp)]
- buf_index += 1
- temp = temp[size:]
- temp_old = temp
- continue second_pass
- }
- }
-
- // step through string
- _, skip := utf8.decode_rune_in_string(temp[:])
- temp = temp[skip:]
- }
-
- buf[buf_index] = temp_old[:]
-
- return buf
-}
-
-// state for the split multi iterator
-Split_Multi :: struct {
- temp: string,
- temp_old: string,
- substrs: []string,
-}
-
-// returns split multi state with sorted `substrs`
-split_multi_init :: proc(s: string, substrs: []string) -> Split_Multi {
- // sort substrings, largest to smallest
- temp_substrs := slice.clone(substrs, context.temp_allocator)
- slice.sort_by(temp_substrs, proc(a, b: string) -> bool {
- return len(a) > len(b)
- })
-
- return {
- temp = s,
- temp_old = s,
- substrs = temp_substrs,
- }
-}
-
-/*
- splits the input string `s` by all possible `substrs` []string in an iterator fashion
- returns the split string every iteration, the full string on no match
-
- splits := [?]string { "---", "~~~", ".", "_", "," }
- state := strings.split_multi_init("testing,this.out_nice---done~~~last", splits[:])
- for str in strings.split_multi_iterate(&state) {
- fmt.eprintln(str) // every iteration -> [testing, this, out, nice, done, last]
- }
-*/
-split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check {
- pass: for len(temp) > 0 {
- for substr in substrs {
- size := len(substr)
-
- // check range and compare string to substr
- if size <= len(temp) && temp[:size] == substr {
- res = temp_old[:len(temp_old) - len(temp)]
- temp = temp[size:]
- temp_old = temp
- ok = true
- return
- }
- }
-
- // step through string
- _, skip := utf8.decode_rune_in_string(temp[:])
- temp = temp[skip:]
- }
-
- // allow last iteration
- if temp_old != "" {
- res = temp_old[:]
- ok = true
- temp_old = ""
- }
-
+ ok = true
return
}
+/*
+Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once.
-// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
-// Adjacent invalid bytes are only replaced once
-scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- replacement: The string used to replace invalid UTF-8 characters
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A new string with invalid UTF-8 characters replaced
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ scrub_example :: proc() {
+ text := "Hello\xC0\x80World"
+ fmt.println(strings.scrub(text, "?")) // -> "Hello?World"
+ }
+
+Output:
+
+ Hello?
+
+*/
+scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
str := s
b: Builder
- builder_init(&b, 0, len(s), allocator)
+ builder_init(&b, 0, len(s), allocator) or_return
has_error := false
cursor := 0
@@ -1579,20 +2794,41 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
str = str[w:]
}
- return to_string(b)
+ return to_string(b), nil
}
-
/*
- returns a reversed version of the `s` string
+Reverses the input string `s`
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A reversed version of the input string
+- err: An optional allocator error if one occured, `nil` otherwise
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ reverse_example :: proc() {
+ a := "abcxyz"
+ b := strings.reverse(a)
+ fmt.println(a, b)
+ }
+
+Output:
+
+ abcxyz zyxcba
- a := "abcxyz"
- b := strings.reverse(a)
- fmt.eprintln(a, b) // abcxyz zyxcba
*/
-reverse :: proc(s: string, allocator := context.allocator) -> string {
+reverse :: proc(s: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
str := s
n := len(str)
- buf := make([]byte, n)
+ buf := make([]byte, n) or_return
i := n
for len(str) > 0 {
@@ -1601,28 +2837,50 @@ reverse :: proc(s: string, allocator := context.allocator) -> string {
copy(buf[i:], str[:w])
str = str[w:]
}
- return string(buf)
+ return string(buf), nil
}
-
/*
- expands the string to a grid spaced by `tab_size` whenever a `\t` character appears
- returns the tabbed string, panics on tab_size <= 0
+Expands the input string by replacing tab characters with spaces to align to a specified tab size
+
+*Allocates Using Provided Allocator*
+
+Inputs:
+- s: The input string
+- tab_size: The number of spaces to use for each tab character
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A new string with tab characters expanded to the specified tab size
+- err: An optional allocator error if one occured, `nil` otherwise
+
+WARNING: Panics if tab_size <= 0
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ expand_tabs_example :: proc() {
+ text := "abc1\tabc2\tabc3"
+ fmt.println(strings.expand_tabs(text, 4))
+ }
+
+Output:
+
+ abc1 abc2 abc3
- strings.expand_tabs("abc1\tabc2\tabc3", 4) -> abc1 abc2 abc3
- strings.expand_tabs("abc1\tabc2\tabc3", 5) -> abc1 abc2 abc3
- strings.expand_tabs("abc1\tabc2\tabc3", 6) -> abc1 abc2 abc3
*/
-expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string {
+expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
if tab_size <= 0 {
panic("tab size must be positive")
}
if s == "" {
- return ""
+ return "", nil
}
b: Builder
- builder_init(&b, allocator)
+ builder_init(&b, allocator) or_return
writer := to_writer(&b)
str := s
column: int
@@ -1651,18 +2909,45 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
str = str[w:]
}
- return to_string(b)
+ return to_string(b), nil
}
-
/*
- splits the `str` string by the seperator `sep` string and returns 3 parts
- `head`: before the split, `match`: the seperator, `tail`: the end of the split
- returns the input string when the `sep` was not found
+Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string.
+
+Inputs:
+- str: The input string
+- sep: The separator string
+
+Returns:
+- head: the string before the split
+- match: the seperator string
+- tail: the string after the split
+
+Example:
+
+ import "core:fmt"
+ import "core:strings"
+
+ partition_example :: proc() {
+ text := "testing this out"
+ head, match, tail := strings.partition(text, " this ") // -> head: "testing", match: " this ", tail: "out"
+ fmt.println(head, match, tail)
+ head, match, tail = strings.partition(text, "hi") // -> head: "testing t", match: "hi", tail: "s out"
+ fmt.println(head, match, tail)
+ head, match, tail = strings.partition(text, "xyz") // -> head: "testing this out", match: "", tail: ""
+ fmt.println(head)
+ fmt.println(match == "")
+ fmt.println(tail == "")
+ }
+
+Output:
+
+ testing this out
+ testing t hi s out
+ testing this out
+ true
+ true
- text := "testing this out"
- strings.partition(text, " this ") -> head: "testing", match: " this ", tail: "out"
- strings.partition(text, "hi") -> head: "testing t", match: "hi", tail: "s out"
- strings.partition(text, "xyz") -> head: "testing this out", match: "", tail: ""
*/
partition :: proc(str, sep: string) -> (head, match, tail: string) {
i := index(str, sep)
@@ -1676,11 +2961,24 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) {
tail = str[i+len(sep):]
return
}
-
+// Alias for centre_justify
center_justify :: centre_justify // NOTE(bill): Because Americans exist
+/*
+Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length.
-// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length
-centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- str: The input string
+- length: The desired length of the centered string, in runes
+- pad: The string used for padding on both sides
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A new string centered within a field of the specified length
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
n := rune_count(str)
if n >= length || pad == "" {
return clone(str, allocator)
@@ -1690,8 +2988,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
pad_len := rune_count(pad)
b: Builder
- builder_init(&b, allocator)
- builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
+ builder_init(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator) or_return
w := to_writer(&b)
@@ -1699,11 +2996,24 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte
io.write_string(w, str)
write_pad_string(w, pad, pad_len, (remains+1)/2)
- return to_string(b)
+ return to_string(b), nil
}
+/*
+Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length.
-// left_justify returns a string with a pad string at right side if the str's rune length is smaller than length
-left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- str: The input string
+- length: The desired length of the left-justified string
+- pad: The string used for padding on the right side
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A new string left-justified within a field of the specified length
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
n := rune_count(str)
if n >= length || pad == "" {
return clone(str, allocator)
@@ -1714,18 +3024,31 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context
b: Builder
builder_init(&b, allocator)
- builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
+ builder_init(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator) or_return
w := to_writer(&b)
io.write_string(w, str)
write_pad_string(w, pad, pad_len, remains)
- return to_string(b)
+ return to_string(b), nil
}
+/*
+Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length.
-// right_justify returns a string with a pad string at left side if the str's rune length is smaller than length
-right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
+*Allocates Using Provided Allocator*
+
+Inputs:
+- str: The input string
+- length: The desired length of the right-justified string
+- pad: The string used for padding on the left side
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A new string right-justified within a field of the specified length
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
n := rune_count(str)
if n >= length || pad == "" {
return clone(str, allocator)
@@ -1736,19 +3059,24 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex
b: Builder
builder_init(&b, allocator)
- builder_grow(&b, len(str) + (remains/pad_len + 1)*len(pad))
+ builder_init(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator) or_return
w := to_writer(&b)
write_pad_string(w, pad, pad_len, remains)
io.write_string(w, str)
- return to_string(b)
+ return to_string(b), nil
}
+/*
+Writes a given pad string a specified number of times to an `io.Writer`
-
-
-
+Inputs:
+- w: The io.Writer to write the pad string to
+- pad: The pad string to be written
+- pad_len: The length of the pad string, in runes
+- remains: The number of times to write the pad string, in runes
+*/
@private
write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
repeats := remains / pad_len
@@ -1766,11 +3094,20 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) {
p = p[width:]
}
}
+/*
+Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space`
+*Allocates Using Provided Allocator*
-// fields splits the string s around each instance of one or more consecutive white space character, defined by unicode.is_space
-// returning a slice of substrings of s or an empty slice if s only contains white space
-fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds_check {
+Inputs:
+- s: The input string
+- allocator: (default is context.allocator)
+
+Returns:
+- res: A slice of substrings of the input string, or an empty slice if the input string only contains white space
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+fields :: proc(s: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error #no_bounds_check {
n := 0
was_space := 1
set_bits := u8(0)
@@ -1789,10 +3126,10 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds
}
if n == 0 {
- return nil
+ return nil, nil
}
- a := make([]string, n, allocator)
+ a := make([]string, n, allocator) or_return
na := 0
field_start := 0
i := 0
@@ -1816,18 +3153,26 @@ fields :: proc(s: string, allocator := context.allocator) -> []string #no_bounds
if field_start < len(s) {
a[na] = s[field_start:]
}
- return a
+ return a, nil
}
+/*
+Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)`
+*Allocates Using Provided Allocator*
-// fields_proc splits the string s at each run of unicode code points `ch` satisfying f(ch)
-// returns a slice of substrings of s
-// If all code points in s satisfy f(ch) or string is empty, an empty slice is returned
-//
-// fields_proc makes no guarantee about the order in which it calls f(ch)
-// it assumes that `f` always returns the same value for a given ch
-fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> []string #no_bounds_check {
- substrings := make([dynamic]string, 0, 32, allocator)
+Inputs:
+- s: The input string
+- f: A predicate function to determine the split points
+- allocator: (default is context.allocator)
+
+NOTE: fields_proc makes no guarantee about the order in which it calls `f(r)`, it assumes that `f` always returns the same value for a given `r`
+
+Returns:
+- res: A slice of substrings of the input string, or an empty slice if all code points in the input string satisfy the predicate or if the input string is empty
+- err: An optional allocator error if one occured, `nil` otherwise
+*/
+fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error #no_bounds_check {
+ substrings := make([dynamic]string, 0, 32, allocator) or_return
start, end := -1, -1
for r, offset in s {
@@ -1850,12 +3195,18 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
append(&substrings, s[start : len(s)])
}
- return substrings[:]
+ return substrings[:], nil
}
+/*
+Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters
+Inputs:
+- s: A mutable string reference to be iterated
-// `fields_iterator` returns the first run of characters in `s` that does not contain white space, defined by `unicode.is_space`
-// `s` will then start from any space after the substring, or be an empty string if the substring was the remaining characters
+Returns:
+- field: The first non-space substring found
+- ok: A boolean indicating if a non-space substring was found
+*/
fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
start, end := -1, -1
for r, offset in s {
@@ -1884,11 +3235,24 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
s^ = s[len(s):]
return
}
+/*
+Computes the Levenshtein edit distance between two strings
-// `levenshtein_distance` returns the Levenshtein edit distance between 2 strings.
-// This is a single-row-version of the WagnerāFischer algorithm, based on C code by Martin Ettl.
-// Note: allocator isn't used if the length of string b in runes is smaller than 64.
-levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> int {
+*Allocates Using Provided Allocator (deletion occurs internal to proc)*
+
+NOTE: Does not perform internal allocation if length of string `b`, in runes, is smaller than 64
+
+Inputs:
+- a, b: The two strings to compare
+- allocator: (default is context.allocator)
+
+Returns:
+- res: The Levenshtein edit distance between the two strings
+- err: An optional allocator error if one occured, `nil` otherwise
+
+NOTE: This implementation is a single-row-version of the WagnerāFischer algorithm, based on C code by Martin Ettl.
+*/
+levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> (res: int, err: mem.Allocator_Error) #optional_allocator_error {
LEVENSHTEIN_DEFAULT_COSTS: []int : {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
@@ -1902,16 +3266,16 @@ levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> in
m, n := utf8.rune_count_in_string(a), utf8.rune_count_in_string(b)
if m == 0 {
- return n
+ return n, nil
}
if n == 0 {
- return m
+ return m, nil
}
costs: []int
if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
- costs = make([]int, n + 1, allocator)
+ costs = make([]int, n + 1, allocator) or_return
for k in 0..=n {
costs[k] = k
}
@@ -1944,5 +3308,5 @@ levenshtein_distance :: proc(a, b: string, allocator := context.allocator) -> in
i += 1
}
- return costs[n]
+ return costs[n], nil
}
diff --git a/core/sync/extended.odin b/core/sync/extended.odin
index 7370d6544..c76ab504b 100644
--- a/core/sync/extended.odin
+++ b/core/sync/extended.odin
@@ -1,11 +1,13 @@
package sync
import "core:time"
+import vg "core:sys/valgrind"
+_ :: vg
// A Wait_Group waits for a collection of threads to finish
//
// A Wait_Group must not be copied after first use
-Wait_Group :: struct {
+Wait_Group :: struct #no_copy {
counter: int,
mutex: Mutex,
cond: Cond,
@@ -99,7 +101,7 @@ Example:
fmt.println("Finished")
}
*/
-Barrier :: struct {
+Barrier :: struct #no_copy {
mutex: Mutex,
cond: Cond,
index: int,
@@ -108,6 +110,9 @@ Barrier :: struct {
}
barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_barrier_resize_pre(b, uint(thread_count))
+ }
b.index = 0
b.generation_id = 0
b.thread_count = thread_count
@@ -116,6 +121,9 @@ barrier_init :: proc "contextless" (b: ^Barrier, thread_count: int) {
// Block the current thread until all threads have rendezvoused
// Barrier can be reused after all threads rendezvoused once, and can be used continuously
barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_barrier_wait_pre(b)
+ }
guard(&b.mutex)
local_gen := b.generation_id
b.index += 1
@@ -133,7 +141,7 @@ barrier_wait :: proc "contextless" (b: ^Barrier) -> (is_leader: bool) {
}
-Auto_Reset_Event :: struct {
+Auto_Reset_Event :: struct #no_copy {
// status == 0: Event is reset and no threads are waiting
// status == 1: Event is signaled
// status == -N: Event is reset and N threads are waiting
@@ -164,7 +172,7 @@ auto_reset_event_wait :: proc "contextless" (e: ^Auto_Reset_Event) {
-Ticket_Mutex :: struct {
+Ticket_Mutex :: struct #no_copy {
ticket: uint,
serving: uint,
}
@@ -186,7 +194,7 @@ ticket_mutex_guard :: proc "contextless" (m: ^Ticket_Mutex) -> bool {
}
-Benaphore :: struct {
+Benaphore :: struct #no_copy {
counter: i32,
sema: Sema,
}
@@ -214,7 +222,7 @@ benaphore_guard :: proc "contextless" (m: ^Benaphore) -> bool {
return true
}
-Recursive_Benaphore :: struct {
+Recursive_Benaphore :: struct #no_copy {
counter: int,
owner: int,
recursion: i32,
@@ -276,7 +284,7 @@ recursive_benaphore_guard :: proc "contextless" (m: ^Recursive_Benaphore) -> boo
// Once is a data value that will perform exactly on action.
//
// A Once must not be copied after first use.
-Once :: struct {
+Once :: struct #no_copy {
m: Mutex,
done: bool,
}
@@ -364,7 +372,7 @@ once_do_with_data_contextless :: proc "contextless" (o: ^Once, fn: proc "context
// blocks for the specified duration.
// * The `unpark` procedure automatically makes the token available if it
// was not already.
-Parker :: struct {
+Parker :: struct #no_copy {
state: Futex,
}
diff --git a/core/sync/futex_openbsd.odin b/core/sync/futex_openbsd.odin
index e02221277..4883a0841 100644
--- a/core/sync/futex_openbsd.odin
+++ b/core/sync/futex_openbsd.odin
@@ -3,7 +3,6 @@
package sync
import "core:c"
-import "core:os"
import "core:time"
FUTEX_WAIT :: 1
@@ -14,11 +13,16 @@ FUTEX_PRIVATE_FLAG :: 128
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
+ETIMEDOUT :: 60
+
+
foreign import libc "system:c"
foreign libc {
@(link_name="futex")
_unix_futex :: proc "c" (f: ^Futex, op: c.int, val: u32, timeout: rawptr) -> c.int ---
+
+ @(link_name="__errno") __errno :: proc() -> ^int ---
}
_futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
@@ -28,7 +32,7 @@ _futex_wait :: proc "contextless" (f: ^Futex, expected: u32) -> bool {
return true
}
- if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ if __errno()^ == ETIMEDOUT {
return false
}
@@ -54,7 +58,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
return true
}
- if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ if __errno()^ == ETIMEDOUT {
return false
}
diff --git a/core/sync/futex_wasm.odin b/core/sync/futex_wasm.odin
index 621f4edaa..248542836 100644
--- a/core/sync/futex_wasm.odin
+++ b/core/sync/futex_wasm.odin
@@ -1,5 +1,5 @@
//+private
-//+build wasm32, wasm64
+//+build wasm32, wasm64p32
package sync
import "core:intrinsics"
diff --git a/core/sync/primitives.odin b/core/sync/primitives.odin
index 00d7812a8..b8bcfad70 100644
--- a/core/sync/primitives.odin
+++ b/core/sync/primitives.odin
@@ -11,7 +11,7 @@ current_thread_id :: proc "contextless" () -> int {
// The zero value for a Mutex is an unlocked mutex
//
// A Mutex must not be copied after first use
-Mutex :: struct {
+Mutex :: struct #no_copy {
impl: _Mutex,
}
@@ -47,7 +47,7 @@ mutex_guard :: proc "contextless" (m: ^Mutex) -> bool {
// The zero value for a RW_Mutex is an unlocked mutex
//
// A RW_Mutex must not be copied after first use
-RW_Mutex :: struct {
+RW_Mutex :: struct #no_copy {
impl: _RW_Mutex,
}
@@ -111,7 +111,7 @@ rw_mutex_shared_guard :: proc "contextless" (m: ^RW_Mutex) -> bool {
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// A Recursive_Mutex must not be copied after first use
-Recursive_Mutex :: struct {
+Recursive_Mutex :: struct #no_copy {
impl: _Recursive_Mutex,
}
@@ -144,7 +144,7 @@ recursive_mutex_guard :: proc "contextless" (m: ^Recursive_Mutex) -> bool {
// waiting for signalling the occurence of an event
//
// A Cond must not be copied after first use
-Cond :: struct {
+Cond :: struct #no_copy {
impl: _Cond,
}
@@ -172,7 +172,7 @@ cond_broadcast :: proc "contextless" (c: ^Cond) {
// Posting to the semaphore increases the count by one, or the provided amount.
//
// A Sema must not be copied after first use
-Sema :: struct {
+Sema :: struct #no_copy {
impl: _Sema,
}
@@ -236,4 +236,4 @@ _panic :: proc "contextless" (msg: string) -> ! {
runtime.print_string(msg)
runtime.print_byte('\n')
runtime.trap()
-}
\ No newline at end of file
+}
diff --git a/core/sync/primitives_atomic.odin b/core/sync/primitives_atomic.odin
index 1abad5875..1b7cdfe35 100644
--- a/core/sync/primitives_atomic.odin
+++ b/core/sync/primitives_atomic.odin
@@ -13,7 +13,7 @@ Atomic_Mutex_State :: enum Futex {
// The zero value for a Atomic_Mutex is an unlocked mutex
//
// An Atomic_Mutex must not be copied after first use
-Atomic_Mutex :: struct {
+Atomic_Mutex :: struct #no_copy {
state: Atomic_Mutex_State,
}
@@ -109,7 +109,7 @@ Atomic_RW_Mutex_State_Reader_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_S
// The zero value for an Atomic_RW_Mutex is an unlocked mutex
//
// An Atomic_RW_Mutex must not be copied after first use
-Atomic_RW_Mutex :: struct {
+Atomic_RW_Mutex :: struct #no_copy {
state: Atomic_RW_Mutex_State,
mutex: Atomic_Mutex,
sema: Atomic_Sema,
@@ -222,7 +222,7 @@ atomic_rw_mutex_shared_guard :: proc "contextless" (m: ^Atomic_RW_Mutex) -> bool
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// An Atomic_Recursive_Mutex must not be copied after first use
-Atomic_Recursive_Mutex :: struct {
+Atomic_Recursive_Mutex :: struct #no_copy {
owner: int,
recursion: int,
mutex: Mutex,
@@ -285,7 +285,7 @@ atomic_recursive_mutex_guard :: proc "contextless" (m: ^Atomic_Recursive_Mutex)
// waiting for signalling the occurence of an event
//
// An Atomic_Cond must not be copied after first use
-Atomic_Cond :: struct {
+Atomic_Cond :: struct #no_copy {
state: Futex,
}
@@ -320,7 +320,7 @@ atomic_cond_broadcast :: proc "contextless" (c: ^Atomic_Cond) {
// Posting to the semaphore increases the count by one, or the provided amount.
//
// An Atomic_Sema must not be copied after first use
-Atomic_Sema :: struct {
+Atomic_Sema :: struct #no_copy {
count: Futex,
}
diff --git a/core/sync/primitives_internal.odin b/core/sync/primitives_internal.odin
index d613d821d..23483aef5 100644
--- a/core/sync/primitives_internal.odin
+++ b/core/sync/primitives_internal.odin
@@ -2,20 +2,31 @@
package sync
import "core:time"
+import vg "core:sys/valgrind"
+_ :: vg
_Sema :: struct {
atomic: Atomic_Sema,
}
_sema_post :: proc "contextless" (s: ^Sema, count := 1) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_sem_post_pre(s)
+ }
atomic_sema_post(&s.impl.atomic, count)
}
_sema_wait :: proc "contextless" (s: ^Sema) {
atomic_sema_wait(&s.impl.atomic)
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_sem_wait_post(s)
+ }
}
_sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration) -> bool {
+ when ODIN_VALGRIND_SUPPORT {
+ defer vg.helgrind_sem_wait_post(s)
+ }
return atomic_sema_wait_with_timeout(&s.impl.atomic, duration)
}
@@ -26,6 +37,11 @@ _Recursive_Mutex :: struct {
}
_recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_mutex_lock_pre(m, false)
+ defer vg.helgrind_mutex_lock_post(m)
+ }
+
tid := Futex(current_thread_id())
for {
prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
@@ -41,6 +57,11 @@ _recursive_mutex_lock :: proc "contextless" (m: ^Recursive_Mutex) {
}
_recursive_mutex_unlock :: proc "contextless" (m: ^Recursive_Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_mutex_unlock_pre(m)
+ defer vg.helgrind_mutex_unlock_post(m)
+ }
+
m.impl.recursion -= 1
if m.impl.recursion != 0 {
return
@@ -71,14 +92,26 @@ when ODIN_OS != .Windows {
}
_mutex_lock :: proc "contextless" (m: ^Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_mutex_lock_pre(m, false)
+ defer vg.helgrind_mutex_lock_post(m)
+ }
atomic_mutex_lock(&m.impl.mutex)
}
_mutex_unlock :: proc "contextless" (m: ^Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_mutex_unlock_pre(m)
+ defer vg.helgrind_mutex_unlock_post(m)
+ }
atomic_mutex_unlock(&m.impl.mutex)
}
_mutex_try_lock :: proc "contextless" (m: ^Mutex) -> bool {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_mutex_lock_pre(m, true)
+ defer vg.helgrind_mutex_lock_post(m)
+ }
return atomic_mutex_try_lock(&m.impl.mutex)
}
@@ -87,18 +120,32 @@ when ODIN_OS != .Windows {
}
_cond_wait :: proc "contextless" (c: ^Cond, m: ^Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ _ = vg.helgrind_cond_wait_pre(c, m)
+ defer _ = vg.helgrind_cond_wait_post(c, m)
+ }
atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
}
_cond_wait_with_timeout :: proc "contextless" (c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ when ODIN_VALGRIND_SUPPORT {
+ _ = vg.helgrind_cond_wait_pre(c, m)
+ defer _ = vg.helgrind_cond_wait_post(c, m)
+ }
return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
}
_cond_signal :: proc "contextless" (c: ^Cond) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_cond_signal_pre(c)
+ }
atomic_cond_signal(&c.impl.cond)
}
_cond_broadcast :: proc "contextless" (c: ^Cond) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_cond_broadcast_pre(c)
+ }
atomic_cond_broadcast(&c.impl.cond)
}
@@ -108,11 +155,17 @@ when ODIN_OS != .Windows {
}
_rw_mutex_lock :: proc "contextless" (rw: ^RW_Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_rwlock_lock_pre(rw, true)
+ }
atomic_rw_mutex_lock(&rw.impl.mutex)
}
_rw_mutex_unlock :: proc "contextless" (rw: ^RW_Mutex) {
atomic_rw_mutex_unlock(&rw.impl.mutex)
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_rwlock_unlock_post(rw, true)
+ }
}
_rw_mutex_try_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
@@ -120,11 +173,17 @@ when ODIN_OS != .Windows {
}
_rw_mutex_shared_lock :: proc "contextless" (rw: ^RW_Mutex) {
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_rwlock_lock_pre(rw, false)
+ }
atomic_rw_mutex_shared_lock(&rw.impl.mutex)
}
_rw_mutex_shared_unlock :: proc "contextless" (rw: ^RW_Mutex) {
atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
+ when ODIN_VALGRIND_SUPPORT {
+ vg.helgrind_rwlock_unlock_post(rw, false)
+ }
}
_rw_mutex_try_shared_lock :: proc "contextless" (rw: ^RW_Mutex) -> bool {
diff --git a/core/sync/primitives_openbsd.odin b/core/sync/primitives_openbsd.odin
index 4072a14e8..ff3ff837f 100644
--- a/core/sync/primitives_openbsd.odin
+++ b/core/sync/primitives_openbsd.odin
@@ -2,8 +2,14 @@
//+private
package sync
-import "core:os"
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+ @(link_name="getthrid", private="file")
+ _unix_getthrid :: proc() -> int ---
+}
_current_thread_id :: proc "contextless" () -> int {
- return os.current_thread_id()
+ return _unix_getthrid()
}
diff --git a/core/sync/primitives_wasm.odin b/core/sync/primitives_wasm.odin
index ac36404d9..f8d9ab657 100644
--- a/core/sync/primitives_wasm.odin
+++ b/core/sync/primitives_wasm.odin
@@ -1,5 +1,5 @@
//+private
-//+build wasm32, wasm64
+//+build wasm32, wasm64p32
package sync
_current_thread_id :: proc "contextless" () -> int {
diff --git a/core/sys/darwin/xnu_system_call_helpers.odin b/core/sys/darwin/xnu_system_call_helpers.odin
index 9522b9908..c225c77fb 100644
--- a/core/sys/darwin/xnu_system_call_helpers.odin
+++ b/core/sys/darwin/xnu_system_call_helpers.odin
@@ -97,6 +97,7 @@ clone_to_cstring :: proc(s: string, allocator: runtime.Allocator, loc := #caller
sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cmode: u32 = 0
cflags: u32 = 0
@@ -132,30 +133,35 @@ sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, b
}
sys_mkdir :: proc(path: string, mode: Permission) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cflags := _sys_permission_mode(mode)
return syscall_mkdir(cpath, cflags) != -1
}
sys_mkdir_at :: proc(fd: c.int, path: string, mode: Permission) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cflags := _sys_permission_mode(mode)
return syscall_mkdir_at(fd, cpath, cflags) != -1
}
sys_rmdir :: proc(path: string, mode: Permission) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cflags := _sys_permission_mode(mode)
return syscall_rmdir(cpath, cflags) != -1
}
sys_rename :: proc(path: string, new_path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cnpath: cstring = clone_to_cstring(new_path, context.temp_allocator)
return syscall_rename(cpath, cnpath) != -1
}
sys_rename_at :: proc(fd: c.int, path: string, to_fd: c.int, new_path: string) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cnpath: cstring = clone_to_cstring(new_path, context.temp_allocator)
return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
@@ -166,12 +172,14 @@ sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
}
sys_chmod :: proc(path: string, mode: Permission) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
cmode := _sys_permission_mode(mode)
return syscall_chmod(cpath, cmode) != -1
}
sys_lstat :: proc(path: string, status: ^stat) -> bool {
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath: cstring = clone_to_cstring(path, context.temp_allocator)
return syscall_lstat(cpath, status) != -1
}
diff --git a/core/sys/darwin/xnu_system_call_numbers.odin b/core/sys/darwin/xnu_system_call_numbers.odin
index b90373fdc..429d18964 100644
--- a/core/sys/darwin/xnu_system_call_numbers.odin
+++ b/core/sys/darwin/xnu_system_call_numbers.odin
@@ -1,6 +1,6 @@
package darwin
-unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr {
+unix_offset_syscall :: proc "contextless" (number: System_Call_Number) -> uintptr {
return uintptr(number) + uintptr(0x2000000)
}
diff --git a/core/sys/darwin/xnu_system_call_wrappers.odin b/core/sys/darwin/xnu_system_call_wrappers.odin
index 685f75ffa..c7a6d6bc4 100644
--- a/core/sys/darwin/xnu_system_call_wrappers.odin
+++ b/core/sys/darwin/xnu_system_call_wrappers.odin
@@ -229,191 +229,191 @@ _Proc_Bsdinfo :: struct {
/*--==========================================================================--*/
-syscall_fsync :: #force_inline proc(fildes: c.int) -> bool {
+syscall_fsync :: #force_inline proc "contextless" (fildes: c.int) -> bool {
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.fsync), uintptr(fildes)))
}
-syscall_write :: #force_inline proc (fildes: c.int, buf: ^byte, nbyte: u64) -> bool {
+syscall_write :: #force_inline proc "contextless" (fildes: c.int, buf: ^byte, nbyte: u64) -> bool {
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.write), uintptr(fildes), uintptr(buf), uintptr(nbyte)))
}
-syscall_read :: #force_inline proc(fildes: c.int, buf: ^byte, nbyte: u64) -> i64 {
+syscall_read :: #force_inline proc "contextless" (fildes: c.int, buf: ^byte, nbyte: u64) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.read), uintptr(fildes), uintptr(buf), uintptr(nbyte))
}
-syscall_open :: #force_inline proc(path: cstring, oflag: u32, mode: u32) -> c.int {
+syscall_open :: #force_inline proc "contextless" (path: cstring, oflag: u32, mode: u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.open), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
}
-syscall_close :: #force_inline proc(fd: c.int) -> bool {
+syscall_close :: #force_inline proc "contextless" (fd: c.int) -> bool {
return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.close), uintptr(fd)))
}
-syscall_fchmod :: #force_inline proc(fildes: c.int, mode: u32) -> c.int {
+syscall_fchmod :: #force_inline proc "contextless" (fildes: c.int, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.fchmod), uintptr(fildes), uintptr(mode)))
}
-syscall_chmod :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+syscall_chmod :: #force_inline proc "contextless" (path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.chmod), transmute(uintptr)path, uintptr(mode)))
}
-syscall_mkdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+syscall_mkdir :: #force_inline proc "contextless" (path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), transmute(uintptr)path, uintptr(mode)))
}
-syscall_mkdir_at :: #force_inline proc(fd: c.int, path: cstring, mode: u32) -> c.int {
+syscall_mkdir_at :: #force_inline proc "contextless" (fd: c.int, path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.mkdir), uintptr(fd), transmute(uintptr)path, uintptr(mode)))
}
-syscall_rmdir :: #force_inline proc(path: cstring, mode: u32) -> c.int {
+syscall_rmdir :: #force_inline proc "contextless" (path: cstring, mode: u32) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rmdir), transmute(uintptr)path, uintptr(mode)))
}
-syscall_rename :: #force_inline proc(path_old: cstring, path_new: cstring) -> c.int {
+syscall_rename :: #force_inline proc "contextless" (path_old: cstring, path_new: cstring) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.rename), transmute(uintptr)path_old, transmute(uintptr)path_new))
}
-syscall_rename_at :: #force_inline proc(from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int {
+syscall_rename_at :: #force_inline proc "contextless" (from_fd: c.int, from: cstring, to_fd: c.int, to: cstring) -> c.int {
return (cast(c.int)intrinsics.syscall(unix_offset_syscall(.renameat), uintptr(from_fd), transmute(uintptr)from, uintptr(to_fd), transmute(uintptr)to))
}
-syscall_lseek :: #force_inline proc(fd: c.int, offset: i64, whence: c.int) -> i64 {
+syscall_lseek :: #force_inline proc "contextless" (fd: c.int, offset: i64, whence: c.int) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence))
}
-syscall_gettid :: #force_inline proc() -> u64 {
+syscall_gettid :: #force_inline proc "contextless" () -> u64 {
return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
}
-syscall_fstat :: #force_inline proc(fd: c.int, status: ^stat) -> c.int {
+syscall_fstat :: #force_inline proc "contextless" (fd: c.int, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstat), uintptr(fd), uintptr(status))
}
-syscall_lstat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
+syscall_lstat :: #force_inline proc "contextless" (path: cstring, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.lstat), transmute(uintptr)path, uintptr(status))
}
-syscall_stat :: #force_inline proc(path: cstring, status: ^stat) -> c.int {
+syscall_stat :: #force_inline proc "contextless" (path: cstring, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.stat), transmute(uintptr)path, uintptr(status))
}
-syscall_fstatat :: #force_inline proc(fd: c.int, path: cstring, status: ^stat) -> c.int {
+syscall_fstatat :: #force_inline proc "contextless" (fd: c.int, path: cstring, status: ^stat) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fstatat), uintptr(fd), transmute(uintptr)path, uintptr(status))
}
-syscall_link :: #force_inline proc(path: cstring, to_link: cstring) -> c.int {
+syscall_link :: #force_inline proc "contextless" (path: cstring, to_link: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.link), transmute(uintptr)path, transmute(uintptr)to_link)
}
-syscall_linkat :: #force_inline proc(fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int {
+syscall_linkat :: #force_inline proc "contextless" (fd: c.int, path: cstring, fd2: c.int, to_link: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.linkat), uintptr(fd), transmute(uintptr)path, uintptr(fd2), transmute(uintptr)to_link)
}
-syscall_readlink :: #force_inline proc(path: cstring, buf: ^u8, buf_size: u64) -> i64 {
+syscall_readlink :: #force_inline proc "contextless" (path: cstring, buf: ^u8, buf_size: u64) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlink), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
}
-syscall_readlinkat :: #force_inline proc(fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 {
+syscall_readlinkat :: #force_inline proc "contextless" (fd: c.int, path: cstring, buf: ^u8, buf_size: u64) -> i64 {
return cast(i64)intrinsics.syscall(unix_offset_syscall(.readlinkat), uintptr(fd), transmute(uintptr)path, uintptr(buf), uintptr(buf_size))
}
-syscall_access :: #force_inline proc(path: cstring, mode: c.int) -> c.int {
+syscall_access :: #force_inline proc "contextless" (path: cstring, mode: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.access), transmute(uintptr)path, uintptr(mode))
}
-syscall_faccessat :: #force_inline proc(fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int {
+syscall_faccessat :: #force_inline proc "contextless" (fd: c.int, path: cstring, mode: c.int, flag: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.faccessat), uintptr(fd), transmute(uintptr)path, uintptr(mode), uintptr(flag))
}
-syscall_getdirentries :: #force_inline proc(fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int {
+syscall_getdirentries :: #force_inline proc "contextless" (fd: c.int, buf: ^u8, nbytes: c.int, base_pointer: ^u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getdirentries), uintptr(fd), uintptr(buf), uintptr(nbytes), uintptr(base_pointer))
}
-syscall_truncate :: #force_inline proc (path: cstring, length: off_t) -> c.int {
+syscall_truncate :: #force_inline proc "contextless" (path: cstring, length: off_t) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.truncate), transmute(uintptr)path, uintptr(length))
}
-syscall_ftruncate :: #force_inline proc (fd: c.int, length: off_t) -> c.int {
+syscall_ftruncate :: #force_inline proc "contextless" (fd: c.int, length: off_t) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.ftruncate), uintptr(fd), uintptr(length))
}
-syscall_sysctl :: #force_inline proc (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int {
+syscall_sysctl :: #force_inline proc "contextless" (name: ^c.int, namelen: c.uint, oldp: rawptr, oldlenp: ^i64, newp: ^i8, newlen: i64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctl), uintptr(name), uintptr(namelen), uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
}
-syscall_copyfile :: #force_inline proc(from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int {
+syscall_copyfile :: #force_inline proc "contextless" (from: cstring, to: cstring, state: rawptr, flags: u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.copyfile), transmute(uintptr)from, transmute(uintptr)to, uintptr(state), uintptr(flags))
}
// think about this? last arg should be more than one
-syscall_fcntl :: #force_inline proc(fd: c.int, cmd: c.int, other: rawptr) -> c.int {
+syscall_fcntl :: #force_inline proc "contextless" (fd: c.int, cmd: c.int, other: rawptr) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fsctl), uintptr(fd), uintptr(cmd), uintptr(other))
}
-syscall_exit :: #force_inline proc(code: c.int) {
+syscall_exit :: #force_inline proc "contextless" (code: c.int) {
intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code))
}
-syscall_kill :: #force_inline proc(pid: pid_t, sig: c.int) -> c.int {
+syscall_kill :: #force_inline proc "contextless" (pid: pid_t, sig: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.kill), uintptr(pid), uintptr(sig))
}
-syscall_dup :: #force_inline proc(fd: c.int) -> c.int {
+syscall_dup :: #force_inline proc "contextless" (fd: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.dup), uintptr(fd))
}
-syscall_execve :: #force_inline proc(path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int {
+syscall_execve :: #force_inline proc "contextless" (path: cstring, argv: [^]cstring, env: [^]cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.execve), transmute(uintptr)path, transmute(uintptr)argv, transmute(uintptr)env)
}
-syscall_munmap :: #force_inline proc(addr: rawptr, len: u64) -> c.int {
+syscall_munmap :: #force_inline proc "contextless" (addr: rawptr, len: u64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len))
}
-syscall_mmap :: #force_inline proc(addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 {
+syscall_mmap :: #force_inline proc "contextless" (addr: ^u8, len: u64, port: c.int, flags: c.int, fd: int, offset: off_t) -> ^u8 {
return cast(^u8)intrinsics.syscall(unix_offset_syscall(.mmap), uintptr(addr), uintptr(len), uintptr(port), uintptr(flags), uintptr(fd), uintptr(offset))
}
-syscall_flock :: #force_inline proc(fd: c.int, operation: c.int) -> c.int {
+syscall_flock :: #force_inline proc "contextless" (fd: c.int, operation: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.flock), uintptr(fd), uintptr(operation))
}
-syscall_utimes :: #force_inline proc(path: cstring, times: ^timeval) -> c.int {
+syscall_utimes :: #force_inline proc "contextless" (path: cstring, times: ^timeval) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.utimes), transmute(uintptr)path, uintptr(times))
}
-syscall_futimes :: #force_inline proc(fd: c.int, times: ^timeval) -> c.int {
+syscall_futimes :: #force_inline proc "contextless" (fd: c.int, times: ^timeval) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.futimes), uintptr(fd), uintptr(times))
}
-syscall_adjtime :: #force_inline proc(delta: ^timeval, old_delta: ^timeval) -> c.int {
+syscall_adjtime :: #force_inline proc "contextless" (delta: ^timeval, old_delta: ^timeval) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.adjtime), uintptr(delta), uintptr(old_delta))
}
-syscall_sysctlbyname :: #force_inline proc(name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int {
+syscall_sysctlbyname :: #force_inline proc "contextless" (name: cstring, oldp: rawptr, oldlenp: ^i64, newp: rawptr, newlen: i64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.sysctlbyname), transmute(uintptr)name, uintptr(oldp), uintptr(oldlenp), uintptr(newp), uintptr(newlen))
}
-syscall_proc_info :: #force_inline proc(num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int {
+syscall_proc_info :: #force_inline proc "contextless" (num: c.int, pid: u32, flavor: c.int, arg: u64, buffer: rawptr, buffer_size: c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.proc_info), uintptr(num), uintptr(pid), uintptr(flavor), uintptr(arg), uintptr(buffer), uintptr(buffer_size))
}
-syscall_openat :: #force_inline proc(fd: int, path: cstring, oflag: u32, mode: u32) -> c.int {
+syscall_openat :: #force_inline proc "contextless" (fd: int, path: cstring, oflag: u32, mode: u32) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.openat), uintptr(fd), transmute(uintptr)path, uintptr(oflag), uintptr(mode))
}
-syscall_getentropy :: #force_inline proc(buf: [^]u8, buflen: u64) -> c.int {
+syscall_getentropy :: #force_inline proc "contextless" (buf: [^]u8, buflen: u64) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(buf), uintptr(buflen))
}
-syscall_pipe :: #force_inline proc(fds: [^]c.int) -> c.int {
+syscall_pipe :: #force_inline proc "contextless" (fds: [^]c.int) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(&fds[0]), uintptr(&fds[1]))
}
-syscall_chdir :: #force_inline proc(path: cstring) -> c.int {
+syscall_chdir :: #force_inline proc "contextless" (path: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), transmute(uintptr)path)
}
-syscall_fchdir :: #force_inline proc(fd: c.int, path: cstring) -> c.int {
+syscall_fchdir :: #force_inline proc "contextless" (fd: c.int, path: cstring) -> c.int {
return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(fd), transmute(uintptr)path)
-}
\ No newline at end of file
+}
diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin
index 8cd723203..5a11863d4 100644
--- a/core/sys/info/cpu_intel.odin
+++ b/core/sys/info/cpu_intel.odin
@@ -38,7 +38,7 @@ cpu_name: Maybe(string)
@(init, private)
init_cpu_features :: proc "c" () {
is_set :: #force_inline proc "c" (hwc: u32, value: u32) -> bool {
- return hwc&value != 0
+ return hwc&(1 << value) != 0
}
try_set :: #force_inline proc "c" (set: ^CPU_Features, feature: CPU_Feature, hwc: u32, value: u32) {
if is_set(hwc, value) {
@@ -74,8 +74,15 @@ init_cpu_features :: proc "c" () {
return
}
+ // In certain rare cases (reason unknown), XGETBV generates an
+ // illegal instruction, even if OSXSAVE is set per CPUID.
+ //
+ // When Chrome ran into this problem, the problem went away
+ // after they started checking both OSXSAVE and XSAVE.
+ //
+ // See: crbug.com/375968
os_supports_avx := false
- if .os_xsave in set {
+ if .os_xsave in set && is_set(26, ecx1) {
eax, _ := xgetbv(0)
os_supports_avx = is_set(1, eax) && is_set(2, eax)
}
diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin
index d226f668c..7a56f1e23 100644
--- a/core/sys/info/platform_darwin.odin
+++ b/core/sys/info/platform_darwin.odin
@@ -4,6 +4,7 @@ package sysinfo
import sys "core:sys/unix"
import "core:strconv"
import "core:strings"
+import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -41,6 +42,8 @@ init_os_version :: proc () {
major_ok, minor_ok, patch_ok: bool
+ tmp := runtime.default_temp_allocator_temp_begin()
+
triplet := strings.split(string(cstring(&version_bits[0])), ".", context.temp_allocator)
if len(triplet) != 3 {
have_kernel_version = false
@@ -54,6 +57,8 @@ init_os_version :: proc () {
}
}
+ runtime.default_temp_allocator_temp_end(tmp)
+
if !have_kernel_version {
// We don't know the kernel version, but we do know the build
strings.write_string(&b, "macOS Unknown (build ")
diff --git a/core/sys/info/platform_freebsd.odin b/core/sys/info/platform_freebsd.odin
index 0ca2841be..1d53da998 100644
--- a/core/sys/info/platform_freebsd.odin
+++ b/core/sys/info/platform_freebsd.odin
@@ -4,6 +4,7 @@ package sysinfo
import sys "core:sys/unix"
import "core:strings"
import "core:strconv"
+import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -47,6 +48,8 @@ init_os_version :: proc () {
return
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
// Parse kernel version
release := string(cstring(raw_data(kernel_version_buf[:])))
version_bits := strings.split_n(release, "-", 2, context.temp_allocator)
diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin
index d1f9ca599..e9cc1dbc9 100644
--- a/core/sys/info/platform_linux.odin
+++ b/core/sys/info/platform_linux.odin
@@ -4,6 +4,7 @@ package sysinfo
import "core:c"
import sys "core:sys/unix"
import "core:intrinsics"
+import "core:runtime"
import "core:os"
import "core:strings"
import "core:strconv"
@@ -69,6 +70,8 @@ init_os_version :: proc () {
l := strings.builder_len(b)
strings.write_string(&b, string(cstring(&uts.release[0])))
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
// Parse kernel version, as substrings of the version info in `version_string_buf`
version_bits := strings.split_n(strings.to_string(b)[l:], "-", 2, context.temp_allocator)
if len(version_bits) > 1 {
diff --git a/core/sys/info/platform_openbsd.odin b/core/sys/info/platform_openbsd.odin
index 080fad9b7..dbca6eaf3 100644
--- a/core/sys/info/platform_openbsd.odin
+++ b/core/sys/info/platform_openbsd.odin
@@ -4,6 +4,7 @@ package sysinfo
import sys "core:sys/unix"
import "core:strings"
import "core:strconv"
+import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -32,7 +33,9 @@ init_os_version :: proc () {
version := string(cstring(raw_data(kernel_version_buf[:])))
strings.write_string(&b, version)
- // // Parse kernel version
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
+ // Parse kernel version
triplet := strings.split(version, ".", context.temp_allocator)
if len(triplet) == 2 {
major, major_ok := strconv.parse_int(triplet[0])
diff --git a/core/sys/info/platform_windows.odin b/core/sys/info/platform_windows.odin
index f7f689346..9c1c7b04c 100644
--- a/core/sys/info/platform_windows.odin
+++ b/core/sys/info/platform_windows.odin
@@ -7,6 +7,7 @@ import "core:strings"
import "core:unicode/utf16"
import "core:fmt"
+import "core:runtime"
@(private)
version_string_buf: [1024]u8
@@ -222,11 +223,10 @@ init_os_version :: proc () {
// Grab Windows DisplayVersion (like 20H02)
format_display_version :: proc (b: ^strings.Builder) -> (version: string) {
- dv, ok := read_reg(
+ dv, ok := read_reg_string(
sys.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"DisplayVersion",
- string,
)
defer delete(dv) // It'll be interned into `version_string_buf`
@@ -242,11 +242,10 @@ init_os_version :: proc () {
// Grab build number and UBR
format_build_number :: proc (b: ^strings.Builder, major_build: int) -> (ubr: int) {
- res, ok := read_reg(
+ res, ok := read_reg_i32(
sys.HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
"UBR",
- i32,
)
if ok {
@@ -288,17 +287,17 @@ init_gpu_info :: proc() {
for {
key := fmt.tprintf("%v\\%04d", GPU_INFO_BASE, gpu_index)
- if vendor, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "ProviderName", string); ok {
+ if vendor, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "ProviderName"); ok {
append(&gpu_list, GPU{vendor_name = vendor})
} else {
break
}
- if desc, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc", string); ok {
+ if desc, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc"); ok {
gpu_list[gpu_index].model_name = desc
}
- if vram, ok := read_reg(sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize", i64); ok {
+ if vram, ok := read_reg_i64(sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize"); ok {
gpu_list[gpu_index].total_ram = int(vram)
}
gpu_index += 1
@@ -307,69 +306,93 @@ init_gpu_info :: proc() {
}
@(private)
-read_reg :: proc(hkey: sys.HKEY, subkey, val: string, $T: typeid) -> (res: T, ok: bool) {
- BUF_SIZE :: 1024
-
+read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok: bool) {
if len(subkey) == 0 || len(val) == 0 {
- return {}, false
+ return
}
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
+ BUF_SIZE :: 1024
key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
utf16.encode_string(key_name_wide, subkey)
utf16.encode_string(val_name_wide, val)
- when T == string {
- result_wide := make([]u16, BUF_SIZE, context.temp_allocator)
- result_size := sys.DWORD(BUF_SIZE * size_of(u16))
+ result_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+ result_size := sys.DWORD(BUF_SIZE * size_of(u16))
- status := sys.RegGetValueW(
- hkey,
- &key_name_wide[0],
- &val_name_wide[0],
- sys.RRF_RT_REG_SZ,
- nil,
- raw_data(result_wide[:]),
- &result_size,
- )
- if status != 0 {
- // Couldn't retrieve string
- return
- }
-
- // Result string will be allocated for the caller.
- result_utf8 := make([]u8, BUF_SIZE * 4, context.temp_allocator)
- utf16.decode_to_utf8(result_utf8, result_wide[:result_size])
- return strings.clone_from_cstring(cstring(raw_data(result_utf8))), true
-
- } else when T == i32 {
- result_size := sys.DWORD(size_of(i32))
- status := sys.RegGetValueW(
- hkey,
- &key_name_wide[0],
- &val_name_wide[0],
- sys.RRF_RT_REG_DWORD,
- nil,
- &res,
- &result_size,
- )
- return res, status == 0
-
- } else when T == i64 {
- result_size := sys.DWORD(size_of(i64))
- status := sys.RegGetValueW(
- hkey,
- &key_name_wide[0],
- &val_name_wide[0],
- sys.RRF_RT_REG_QWORD,
- nil,
- &res,
- &result_size,
- )
- return res, status == 0
- } else {
- #assert(false, "Unhandled type for read_reg")
+ status := sys.RegGetValueW(
+ hkey,
+ &key_name_wide[0],
+ &val_name_wide[0],
+ sys.RRF_RT_REG_SZ,
+ nil,
+ raw_data(result_wide[:]),
+ &result_size,
+ )
+ if status != 0 {
+ // Couldn't retrieve string
+ return
}
- return
-}
\ No newline at end of file
+
+ // Result string will be allocated for the caller.
+ result_utf8 := make([]u8, BUF_SIZE * 4, context.temp_allocator)
+ utf16.decode_to_utf8(result_utf8, result_wide[:result_size])
+ return strings.clone_from_cstring(cstring(raw_data(result_utf8))), true
+}
+@(private)
+read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool) {
+ if len(subkey) == 0 || len(val) == 0 {
+ return
+ }
+
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
+ BUF_SIZE :: 1024
+ key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+ val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+
+ utf16.encode_string(key_name_wide, subkey)
+ utf16.encode_string(val_name_wide, val)
+
+ result_size := sys.DWORD(size_of(i32))
+ status := sys.RegGetValueW(
+ hkey,
+ &key_name_wide[0],
+ &val_name_wide[0],
+ sys.RRF_RT_REG_DWORD,
+ nil,
+ &res,
+ &result_size,
+ )
+ return res, status == 0
+}
+@(private)
+read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool) {
+ if len(subkey) == 0 || len(val) == 0 {
+ return
+ }
+
+ runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
+
+ BUF_SIZE :: 1024
+ key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+ val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator)
+
+ utf16.encode_string(key_name_wide, subkey)
+ utf16.encode_string(val_name_wide, val)
+
+ result_size := sys.DWORD(size_of(i64))
+ status := sys.RegGetValueW(
+ hkey,
+ &key_name_wide[0],
+ &val_name_wide[0],
+ sys.RRF_RT_REG_QWORD,
+ nil,
+ &res,
+ &result_size,
+ )
+ return res, status == 0
+}
diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin
index 8ce3ca3cb..3083c084b 100644
--- a/core/sys/unix/syscalls_linux.odin
+++ b/core/sys/unix/syscalls_linux.odin
@@ -1518,6 +1518,7 @@ when ODIN_ARCH == .amd64 {
#panic("Unsupported architecture")
}
+
// syscall related constants
AT_FDCWD :: ~uintptr(99)
AT_REMOVEDIR :: uintptr(0x200)
@@ -1563,15 +1564,184 @@ MADV_WIPEONFORK :: 18
MADV_KEEPONFORK :: 19
MADV_HWPOISON :: 100
+// pipe2 flags
+O_CLOEXEC :: 0o2000000
+
+// poll events
+POLLIN :: 0x0001
+POLLPRI :: 0x0002
+POLLOUT :: 0x0004
+POLLERR :: 0x0008
+POLLHUP :: 0x0010
+POLLNVAL :: 0x0020
+POLLRDNORM :: 0x0040
+POLLRDBAND :: 0x0080
+POLLWRNORM :: 0x0100
+POLLWRBAND :: 0x0200
+POLLMSG :: 0x0400
+POLLREMOVE :: 0x1000
+POLLRDHUP :: 0x2000
+POLLFREE :: 0x4000
+POLL_BUSY_LOOP :: 0x8000
+
+// perf event data
+Perf_Sample :: struct #raw_union {
+ period: u64,
+ frequency: u64,
+}
+Perf_Wakeup :: struct #raw_union {
+ events: u32,
+ watermark: u32,
+}
+Perf_Field1 :: struct #raw_union {
+ breakpoint_addr: u64,
+ kprobe_func: u64,
+ uprobe_path: u64,
+ config1: u64,
+}
+Perf_Field2 :: struct #raw_union {
+ breakpoint_len: u64,
+ kprobe_addr: u64,
+ uprobe_offset: u64,
+ config2: u64,
+}
+Perf_Event_Attr :: struct #packed {
+ type: u32,
+ size: u32,
+ config: u64,
+ sample: Perf_Sample,
+ sample_type: u64,
+ read_format: u64,
+ flags: Perf_Flags,
+ wakeup: Perf_Wakeup,
+ breakpoint_type: u32,
+ field1: Perf_Field1,
+ field2: Perf_Field2,
+ branch_sample_type: u64,
+ sample_regs_user: u64,
+ sample_stack_user: u32,
+ clock_id: i32,
+ sample_regs_intr: u64,
+ aux_watermark: u32,
+ sample_max_stack: u16,
+ _padding: u16,
+}
+
+Perf_Event_Flags :: distinct bit_set[Perf_Event_Flag; u64]
+Perf_Event_Flag :: enum u64 {
+ Bit0 = 0,
+ Bit0_Is_Deprecated = 1,
+ User_Rdpmc = 2,
+ User_Time = 3,
+ User_Time_Zero = 4,
+ User_Time_Short = 5,
+}
+Perf_Capabilities :: struct #raw_union {
+ capabilities: u64,
+ flags: Perf_Event_Flags,
+}
+Perf_Event_mmap_Page :: struct #packed {
+ version: u32,
+ compat_version: u32,
+ lock: u32,
+ index: u32,
+ offset: i64,
+ time_enabled: u64,
+ time_running: u64,
+ cap: Perf_Capabilities,
+ pmc_width: u16,
+ time_shift: u16,
+ time_mult: u32,
+ time_offset: u64,
+ time_zero: u64,
+ size: u32,
+ reserved1: u32,
+ time_cycles: u64,
+ time_mask: u64,
+ reserved2: [116*8]u8,
+ data_head: u64,
+ data_tail: u64,
+ data_offset: u64,
+ data_size: u64,
+ aux_head: u64,
+ aux_tail: u64,
+ aux_offset: u64,
+ aux_size: u64,
+}
+
+Perf_Type_Id :: enum u32 {
+ Hardware = 0,
+ Software = 1,
+ Tracepoint = 2,
+ HW_Cache = 3,
+ Raw = 4,
+ Breakpoint = 5,
+}
+
+Perf_Hardware_Id :: enum u64 {
+ CPU_Cycles = 0,
+ Instructions = 1,
+ Cache_References = 2,
+ Cache_Misses = 3,
+ Branch_Instructions = 4,
+ Branch_Misses = 5,
+ Bus_Cycles = 6,
+ Stalled_Cycles_Frontend = 7,
+ Stalled_Cycles_Backend = 8,
+ Ref_CPU_Cycles = 9,
+}
+
+Perf_Flags :: distinct bit_set[Perf_Flag; u64]
+Perf_Flag :: enum u64 {
+ Disabled = 0,
+ Inherit = 1,
+ Pinned = 2,
+ Exclusive = 3,
+ Exclude_User = 4,
+ Exclude_Kernel = 5,
+ Exclude_HV = 6,
+ Exclude_Idle = 7,
+ mmap = 8,
+ Comm = 9,
+ Freq = 10,
+ Inherit_Stat = 11,
+ Enable_On_Exec = 12,
+ Task = 13,
+ Watermark = 14,
+ Precise_IP_0 = 15,
+ Precise_IP_1 = 16,
+ mmap_Data = 17,
+ Sample_Id_All = 18,
+ Exclude_Host = 19,
+ Exclude_Guest = 20,
+ Exclude_Callchain_Kernel = 21,
+ Exclude_Callchain_User = 22,
+ mmap2 = 23,
+ Comm_Exec = 24,
+ Use_Clockid = 25,
+ Context_Switch = 26,
+ Write_Backward = 27,
+ Namespaces = 28,
+ KSymbol = 29,
+ BPF_Event = 30,
+ Aux_Output = 31,
+ CGroup = 32,
+ Text_Poke = 33,
+ Build_Id = 34,
+ Inherit_Thread = 35,
+ Remove_On_Exec = 36,
+ Sigtrap = 37,
+}
+
sys_gettid :: proc "contextless" () -> int {
- return cast(int)intrinsics.syscall(SYS_gettid)
+ return int(intrinsics.syscall(SYS_gettid))
}
-sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: int, flags: uint) -> int {
- return cast(int)intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags))
+sys_getrandom :: proc "contextless" (buf: [^]byte, buflen: uint, flags: int) -> int {
+ return int(intrinsics.syscall(SYS_getrandom, uintptr(buf), uintptr(buflen), uintptr(flags)))
}
-sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) -> int {
+sys_open :: proc "contextless" (path: cstring, flags: int, mode: uint = 0o000) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_open, uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
} else { // NOTE: arm64 does not have open
@@ -1579,7 +1749,7 @@ sys_open :: proc "contextless" (path: cstring, flags: int, mode: int = 0o000) ->
}
}
-sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: int = 0o000) -> int {
+sys_openat :: proc "contextless" (dfd: int, path: cstring, flags: int, mode: uint = 0o000) -> int {
return int(intrinsics.syscall(SYS_openat, uintptr(dfd), uintptr(rawptr(path)), uintptr(flags), uintptr(mode)))
}
@@ -1691,7 +1861,7 @@ sys_fchdir :: proc "contextless" (fd: int) -> int {
return int(intrinsics.syscall(SYS_fchdir, uintptr(fd)))
}
-sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
+sys_chmod :: proc "contextless" (path: cstring, mode: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_chmod, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have chmod
@@ -1699,7 +1869,7 @@ sys_chmod :: proc "contextless" (path: cstring, mode: int) -> int {
}
}
-sys_fchmod :: proc "contextless" (fd: int, mode: int) -> int {
+sys_fchmod :: proc "contextless" (fd: int, mode: uint) -> int {
return int(intrinsics.syscall(SYS_fchmod, uintptr(fd), uintptr(mode)))
}
@@ -1759,7 +1929,7 @@ sys_rmdir :: proc "contextless" (path: cstring) -> int {
}
}
-sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
+sys_mkdir :: proc "contextless" (path: cstring, mode: uint) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
} else { // NOTE: arm64 does not have mkdir
@@ -1767,11 +1937,11 @@ sys_mkdir :: proc "contextless" (path: cstring, mode: int) -> int {
}
}
-sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: int) -> int {
+sys_mkdirat :: proc "contextless" (dfd: int, path: cstring, mode: uint) -> int {
return int(intrinsics.syscall(SYS_mkdirat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode)))
}
-sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
+sys_mknod :: proc "contextless" (path: cstring, mode: uint, dev: int) -> int {
when ODIN_ARCH != .arm64 {
return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
} else { // NOTE: arm64 does not have mknod
@@ -1779,7 +1949,7 @@ sys_mknod :: proc "contextless" (path: cstring, mode: int, dev: int) -> int {
}
}
-sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: int, dev: int) -> int {
+sys_mknodat :: proc "contextless" (dfd: int, path: cstring, mode: uint, dev: int) -> int {
return int(intrinsics.syscall(SYS_mknodat, uintptr(dfd), uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
}
@@ -1818,6 +1988,16 @@ sys_fork :: proc "contextless" () -> int {
return int(intrinsics.syscall(SYS_clone, SIGCHLD))
}
}
+sys_pipe2 :: proc "contextless" (fds: rawptr, flags: int) -> int {
+ return int(intrinsics.syscall(SYS_pipe2, uintptr(fds), uintptr(flags)))
+}
+sys_dup2 :: proc "contextless" (oldfd: int, newfd: int) -> int {
+ when ODIN_ARCH != .arm64 {
+ return int(intrinsics.syscall(SYS_dup2, uintptr(oldfd), uintptr(newfd)))
+ } else {
+ return int(intrinsics.syscall(SYS_dup3, uintptr(oldfd), uintptr(newfd), 0))
+ }
+}
sys_mmap :: proc "contextless" (addr: rawptr, length: uint, prot, flags, fd: int, offset: uintptr) -> int {
return int(intrinsics.syscall(SYS_mmap, uintptr(addr), uintptr(length), uintptr(prot), uintptr(flags), uintptr(fd), offset))
@@ -1846,6 +2026,71 @@ sys_utimensat :: proc "contextless" (dfd: int, path: cstring, times: rawptr, fla
return int(intrinsics.syscall(SYS_utimensat, uintptr(dfd), uintptr(rawptr(path)), uintptr(times), uintptr(flags)))
}
+sys_socket :: proc "contextless" (domain: int, type: int, protocol: int) -> int {
+ return int(intrinsics.syscall(SYS_socket, uintptr(domain), uintptr(type), uintptr(protocol)))
+}
+
+sys_connect :: proc "contextless" (sd: int, addr: rawptr, len: i32) -> int {
+ return int(intrinsics.syscall(SYS_connect, uintptr(sd), uintptr(addr), uintptr(len)))
+}
+
+sys_accept :: proc "contextless" (sd: int, addr: rawptr, len: rawptr) -> int {
+ return int(intrinsics.syscall(SYS_accept4, uintptr(sd), uintptr(addr), uintptr(len), uintptr(0)))
+}
+
+sys_listen :: proc "contextless" (sd: int, backlog: int) -> int {
+ return int(intrinsics.syscall(SYS_listen, uintptr(sd), uintptr(backlog)))
+}
+
+sys_bind :: proc "contextless" (sd: int, addr: rawptr, len: i32) -> int {
+ return int(intrinsics.syscall(SYS_bind, uintptr(sd), uintptr(addr), uintptr(len)))
+}
+
+sys_setsockopt :: proc "contextless" (sd: int, level: int, optname: int, optval: rawptr, optlen: i32) -> int {
+ return int(intrinsics.syscall(SYS_setsockopt, uintptr(sd), uintptr(level), uintptr(optname), uintptr(optval), uintptr(optlen)))
+}
+
+sys_recvfrom :: proc "contextless" (sd: int, buf: rawptr, len: uint, flags: int, addr: rawptr, alen: uintptr) -> i64 {
+ return i64(intrinsics.syscall(SYS_recvfrom, uintptr(sd), uintptr(buf), uintptr(len), uintptr(flags), uintptr(addr), uintptr(alen)))
+}
+
+sys_sendto :: proc "contextless" (sd: int, buf: rawptr, len: uint, flags: int, addr: rawptr, alen: i32) -> i64 {
+ return i64(intrinsics.syscall(SYS_sendto, uintptr(sd), uintptr(buf), uintptr(len), uintptr(flags), uintptr(addr), uintptr(alen)))
+}
+
+sys_shutdown :: proc "contextless" (sd: int, how: int) -> int {
+ return int(intrinsics.syscall(SYS_shutdown, uintptr(sd), uintptr(how)))
+}
+
+sys_perf_event_open :: proc "contextless" (event_attr: rawptr, pid: i32, cpu: i32, group_fd: i32, flags: u32) -> int {
+ return int(intrinsics.syscall(SYS_perf_event_open, uintptr(event_attr), uintptr(pid), uintptr(cpu), uintptr(group_fd), uintptr(flags)))
+}
+
+sys_personality :: proc(persona: u64) -> int {
+ return int(intrinsics.syscall(SYS_personality, uintptr(persona)))
+}
+
+sys_fcntl :: proc "contextless" (fd: int, cmd: int, arg: int) -> int {
+ return int(intrinsics.syscall(SYS_fcntl, uintptr(fd), uintptr(cmd), uintptr(arg)))
+}
+
+sys_poll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: int) -> int {
+ // NOTE: specialcased here because `arm64` does not have `poll`
+ when ODIN_ARCH == .arm64 {
+ seconds := i64(timeout / 1_000)
+ nanoseconds := i64((timeout % 1000) * 1_000_000)
+ timeout_spec := timespec{seconds, nanoseconds}
+
+ return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(&timeout_spec), uintptr(0), uintptr(8)))
+ } else {
+ return int(intrinsics.syscall(SYS_poll, uintptr(fds), uintptr(nfds), uintptr(timeout)))
+ }
+}
+
+sys_ppoll :: proc "contextless" (fds: rawptr, nfds: uint, timeout: rawptr, sigmask: rawptr, sigsetsize: uint) -> int {
+ return int(intrinsics.syscall(SYS_ppoll, uintptr(fds), uintptr(nfds), uintptr(timeout), uintptr(sigmask), uintptr(sigsetsize)))
+}
+
get_errno :: proc "contextless" (res: int) -> i32 {
if res < 0 && res > -4096 {
return i32(-res)
diff --git a/core/sys/wasm/wasi/wasi_api.odin b/core/sys/wasm/wasi/wasi_api.odin
index d4f0d19cf..e9ceb4667 100644
--- a/core/sys/wasm/wasi/wasi_api.odin
+++ b/core/sys/wasm/wasi/wasi_api.odin
@@ -968,7 +968,7 @@ prestat_t :: struct {
},
}
-@(default_calling_convention="c")
+@(default_calling_convention="contextless")
foreign wasi {
/**
* Read command-line argument data.
@@ -1306,7 +1306,7 @@ foreign wasi {
* Returns the number of arguments and the size of the argument string
* data, or an error.
*/
-args_sizes_get :: proc "c" () -> (num_args, size_of_args: size_t, err: errno_t) {
+args_sizes_get :: proc "contextless" () -> (num_args, size_of_args: size_t, err: errno_t) {
err = wasi_args_sizes_get(&num_args, &size_of_args)
return
}
@@ -1316,7 +1316,7 @@ args_sizes_get :: proc "c" () -> (num_args, size_of_args: size_t, err: errno_t)
* Returns the number of environment variable arguments and the size of the
* environment variable data.
*/
-environ_sizes_get :: proc "c" () -> (num_envs, size_of_envs: size_t, err: errno_t) {
+environ_sizes_get :: proc "contextless" () -> (num_envs, size_of_envs: size_t, err: errno_t) {
err = wasi_environ_sizes_get(&num_envs, &size_of_envs)
return
}
@@ -1328,7 +1328,7 @@ environ_sizes_get :: proc "c" () -> (num_envs, size_of_envs: size_t, err: errno_
* @return
* The resolution of the clock, or an error if one happened.
*/
-clock_res_get :: proc "c" (
+clock_res_get :: proc "contextless" (
/**
* The clock for which to return the resolution.
*/
@@ -1343,7 +1343,7 @@ clock_res_get :: proc "c" (
* @return
* The time value of the clock.
*/
-clock_time_get :: proc "c" (
+clock_time_get :: proc "contextless" (
/**
* The clock for which to return the time.
*/
@@ -1362,7 +1362,7 @@ clock_time_get :: proc "c" (
* @return
* The buffer where the file descriptor's attributes are stored.
*/
-fd_fdstat_get :: proc "c" (
+fd_fdstat_get :: proc "contextless" (
fd: fd_t,
) -> (stat: fdstat_t, err: errno_t) {
err = wasi_fd_fdstat_get(fd, &stat)
@@ -1373,7 +1373,7 @@ fd_fdstat_get :: proc "c" (
* @return
* The buffer where the file's attributes are stored.
*/
-fd_filestat_get :: proc "c" (
+fd_filestat_get :: proc "contextless" (
fd: fd_t,
) -> (stat: filestat_t, err: errno_t) {
err = wasi_fd_filestat_get(fd, &stat)
@@ -1389,7 +1389,7 @@ fd_filestat_get :: proc "c" (
* @return
* The number of bytes read.
*/
-fd_pread :: proc "c" (
+fd_pread :: proc "contextless" (
fd: fd_t,
/**
* List of scatter/gather vectors in which to store data.
@@ -1408,7 +1408,7 @@ fd_pread :: proc "c" (
* @return
* The buffer where the description is stored.
*/
-fd_prestat_get :: proc "c" (
+fd_prestat_get :: proc "contextless" (
fd: fd_t,
) -> (desc: prestat_t, err: errno_t) {
err = wasi_fd_prestat_get(fd, &desc)
@@ -1420,7 +1420,7 @@ fd_prestat_get :: proc "c" (
* @return
* The number of bytes written.
*/
-fd_pwrite :: proc "c" (
+fd_pwrite :: proc "contextless" (
fd: fd_t,
/**
* List of scatter/gather vectors from which to retrieve data.
@@ -1440,7 +1440,7 @@ fd_pwrite :: proc "c" (
* @return
* The number of bytes read.
*/
-fd_read :: proc "c" (
+fd_read :: proc "contextless" (
fd: fd_t,
/**
* List of scatter/gather vectors to which to store data.
@@ -1463,7 +1463,7 @@ fd_read :: proc "c" (
* @return
* The number of bytes stored in the read buffer. If less than the size of the read buffer, the end of the directory has been reached.
*/
-fd_readdir :: proc "c" (
+fd_readdir :: proc "contextless" (
fd: fd_t,
/**
* The buffer where directory entries are stored
@@ -1483,7 +1483,7 @@ fd_readdir :: proc "c" (
* @return
* The new offset of the file descriptor, relative to the start of the file.
*/
-fd_seek :: proc "c" (
+fd_seek :: proc "contextless" (
fd: fd_t,
/**
* The number of bytes to move.
@@ -1503,7 +1503,7 @@ fd_seek :: proc "c" (
* @return
* The current offset of the file descriptor, relative to the start of the file.
*/
-fd_tell :: proc "c" (
+fd_tell :: proc "contextless" (
fd: fd_t,
) -> (offset: filesize_t, err: errno_t) {
err = wasi_fd_tell(fd, &offset)
@@ -1513,7 +1513,7 @@ fd_tell :: proc "c" (
* Write to a file descriptor.
* Note: This is similar to `writev` in POSIX.
*/
-fd_write :: proc "c" (
+fd_write :: proc "contextless" (
fd: fd_t,
/**
* List of scatter/gather vectors from which to retrieve data.
@@ -1529,7 +1529,7 @@ fd_write :: proc "c" (
* @return
* The buffer where the file's attributes are stored.
*/
-path_filestat_get :: proc "c" (
+path_filestat_get :: proc "contextless" (
fd: fd_t,
/**
* Flags determining the method of how the path is resolved.
@@ -1554,7 +1554,7 @@ path_filestat_get :: proc "c" (
* @return
* The file descriptor of the file that has been opened.
*/
-path_open :: proc "c" (
+path_open :: proc "contextless" (
fd: fd_t,
/**
* Flags determining the method of how the path is resolved.
@@ -1591,7 +1591,7 @@ path_open :: proc "c" (
* @return
* The number of bytes placed in the buffer.
*/
-path_readlink :: proc "c" (
+path_readlink :: proc "contextless" (
fd: fd_t,
/**
* The path of the symbolic link from which to read.
@@ -1610,7 +1610,7 @@ path_readlink :: proc "c" (
* @return
* The number of events stored.
*/
-poll_oneoff :: proc "c" (
+poll_oneoff :: proc "contextless" (
/**
* The events to which to subscribe.
*/
@@ -1634,7 +1634,7 @@ poll_oneoff :: proc "c" (
* @return
* Number of bytes stored in ri_data and message flags.
*/
-sock_recv :: proc "c" (
+sock_recv :: proc "contextless" (
fd: fd_t,
/**
* List of scatter/gather vectors to which to store data.
@@ -1655,7 +1655,7 @@ sock_recv :: proc "c" (
* @return
* Number of bytes transmitted.
*/
-sock_send :: proc "c" (
+sock_send :: proc "contextless" (
fd: fd_t,
/**
* List of scatter/gather vectors to which to retrieve data
@@ -1675,7 +1675,7 @@ sock_send :: proc "c" (
-@(default_calling_convention="c")
+@(default_calling_convention="contextless")
foreign wasi {
@(link_name="args_sizes_get")
wasi_args_sizes_get :: proc(
diff --git a/core/sys/windows/advapi32.odin b/core/sys/windows/advapi32.odin
index e98aa6c43..dc7ec1e08 100644
--- a/core/sys/windows/advapi32.odin
+++ b/core/sys/windows/advapi32.odin
@@ -52,7 +52,7 @@ foreign advapi32 {
dwCreationFlags: DWORD,
lpEnvironment: LPVOID,
lpCurrentDirectory: wstring,
- lpStartupInfo: LPSTARTUPINFO,
+ lpStartupInfo: LPSTARTUPINFOW,
lpProcessInformation: LPPROCESS_INFORMATION,
) -> BOOL ---
@@ -67,7 +67,7 @@ foreign advapi32 {
dwCreationFlags: DWORD,
lpEnvironment: LPVOID,
lpCurrentDirectory: wstring,
- lpStartupInfo: LPSTARTUPINFO,
+ lpStartupInfo: LPSTARTUPINFOW,
lpProcessInformation: LPPROCESS_INFORMATION,
) -> BOOL ---
diff --git a/core/sys/windows/dnsapi.odin b/core/sys/windows/dnsapi.odin
new file mode 100644
index 000000000..623fa2889
--- /dev/null
+++ b/core/sys/windows/dnsapi.odin
@@ -0,0 +1,10 @@
+// +build windows
+package sys_windows
+
+foreign import "system:Dnsapi.lib"
+
+@(default_calling_convention="std")
+foreign Dnsapi {
+ DnsQuery_UTF8 :: proc(name: cstring, type: u16, options: DWORD, extra: PVOID, results: ^^DNS_RECORD, reserved: PVOID) -> DNS_STATUS ---
+ DnsRecordListFree :: proc(list: ^DNS_RECORD, options: DWORD) ---
+}
diff --git a/core/sys/windows/dwmapi.odin b/core/sys/windows/dwmapi.odin
index 468b5bb87..9b5916ab1 100644
--- a/core/sys/windows/dwmapi.odin
+++ b/core/sys/windows/dwmapi.odin
@@ -3,7 +3,45 @@ package sys_windows
foreign import dwmapi "system:Dwmapi.lib"
+DWMWINDOWATTRIBUTE :: enum {
+ DWMWA_NCRENDERING_ENABLED,
+ DWMWA_NCRENDERING_POLICY,
+ DWMWA_TRANSITIONS_FORCEDISABLED,
+ DWMWA_ALLOW_NCPAINT,
+ DWMWA_CAPTION_BUTTON_BOUNDS,
+ DWMWA_NONCLIENT_RTL_LAYOUT,
+ DWMWA_FORCE_ICONIC_REPRESENTATION,
+ DWMWA_FLIP3D_POLICY,
+ DWMWA_EXTENDED_FRAME_BOUNDS,
+ DWMWA_HAS_ICONIC_BITMAP,
+ DWMWA_DISALLOW_PEEK,
+ DWMWA_EXCLUDED_FROM_PEEK,
+ DWMWA_CLOAK,
+ DWMWA_CLOAKED,
+ DWMWA_FREEZE_REPRESENTATION,
+ DWMWA_PASSIVE_UPDATE_MODE,
+ DWMWA_USE_HOSTBACKDROPBRUSH,
+ DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
+ DWMWA_WINDOW_CORNER_PREFERENCE = 33,
+ DWMWA_BORDER_COLOR,
+ DWMWA_CAPTION_COLOR,
+ DWMWA_TEXT_COLOR,
+ DWMWA_VISIBLE_FRAME_BORDER_THICKNESS,
+ DWMWA_SYSTEMBACKDROP_TYPE,
+ DWMWA_LAST,
+}
+
+DWMNCRENDERINGPOLICY :: enum {
+ DWMNCRP_USEWINDOWSTYLE,
+ DWMNCRP_DISABLED,
+ DWMNCRP_ENABLED,
+ DWMNCRP_LAST,
+}
+
@(default_calling_convention="stdcall")
foreign dwmapi {
DwmFlush :: proc() -> HRESULT ---
+ DwmIsCompositionEnabled :: proc(pfEnabled: ^BOOL) -> HRESULT ---
+ DwmExtendFrameIntoClientArea :: proc(hWnd: HWND, pMarInset: PMARGINS) -> HRESULT ---
+ DwmSetWindowAttribute :: proc(hWnd: HWND, dwAttribute: DWORD, pvAttribute: LPCVOID, cbAttribute: DWORD) -> HRESULT ---
}
diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin
index d83d2aa6c..9e2294c71 100644
--- a/core/sys/windows/gdi32.odin
+++ b/core/sys/windows/gdi32.odin
@@ -79,6 +79,8 @@ foreign gdi32 {
TextOutW :: proc(hdc: HDC, x, y: c_int, lpString: LPCWSTR, c: c_int) -> BOOL ---
GetTextExtentPoint32W :: proc(hdc: HDC, lpString: LPCWSTR, c: c_int, psizl: LPSIZE) -> BOOL ---
GetTextMetricsW :: proc(hdc: HDC, lptm: LPTEXTMETRICW) -> BOOL ---
+
+ CreateSolidBrush :: proc(color: COLORREF) -> HBRUSH ---
}
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
diff --git a/core/sys/windows/ip_helper.odin b/core/sys/windows/ip_helper.odin
new file mode 100644
index 000000000..d8f93f533
--- /dev/null
+++ b/core/sys/windows/ip_helper.odin
@@ -0,0 +1,234 @@
+// +build windows
+package sys_windows
+
+foreign import "system:iphlpapi.lib"
+
+Address_Family :: enum u32 {
+ Unspecified = 0, // Return both IPv4 and IPv6 addresses associated with adapters with them enabled.
+ IPv4 = 2, // Return only IPv4 addresses associated with adapters with it enabled.
+ IPv6 = 23, // Return only IPv6 addresses associated with adapters with it enabled.
+}
+
+GAA_Flag :: enum u32 {
+ Skip_Unicast = 0, // Do not return unicast addresses.
+ Skip_Anycast = 1, // Do not return IPv6 anycast addresses.
+ Skip_Multicast = 2, // Do not return multicast addresses.
+ Skip_DNS_Server = 3, // Do not return addresses of DNS servers.
+ Include_Prefix = 4, // (XP SP1+) Return a list of IP address prefixes on this adapter. When this flag is set, IP address prefixes are returned for both IPv6 and IPv4 addresses.
+ Skip_Friendly_Name = 5, // Do not return the adapter friendly name.
+ Include_WINS_info = 6, // (Vista+) Return addresses of Windows Internet Name Service (WINS) servers.
+ Include_Gateways = 7, // (Vista+) Return the addresses of default gateways.
+ Include_All_Interfaces = 8, // (Vista+) Return addresses for all NDIS interfaces.
+ Include_All_Compartments = 9, // (Reserved, Unsupported) Return addresses in all routing compartments.
+ Include_Tunnel_Binding_Order = 10, // (Vista+) Return the adapter addresses sorted in tunnel binding order.
+}
+GAA_Flags :: bit_set[GAA_Flag; u32]
+
+IP_Adapter_Addresses :: struct {
+ Raw: struct #raw_union {
+ Alignment: u64,
+ Anonymous: struct {
+ Length: u32,
+ IfIndex: u32,
+ },
+ },
+ Next: ^IP_Adapter_Addresses,
+ AdapterName: cstring,
+ FirstUnicastAddress: ^IP_ADAPTER_UNICAST_ADDRESS_LH,
+ FirstAnycastAddress: ^IP_ADAPTER_ANYCAST_ADDRESS_XP,
+ FirstMulticastAddress: ^IP_ADAPTER_MULTICAST_ADDRESS_XP,
+ FirstDnsServerAddress: ^IP_ADAPTER_DNS_SERVER_ADDRESS_XP,
+ DnsSuffix: ^u16,
+ Description: ^u16,
+ FriendlyName: ^u16,
+ PhysicalAddress: [8]u8,
+ PhysicalAddressLength: u32,
+ Anonymous2: struct #raw_union {
+ Flags: u32,
+ Anonymous: struct {
+ _bitfield: u32,
+ },
+ },
+ MTU: u32,
+ IfType: u32,
+ OperStatus: IF_OPER_STATUS,
+ Ipv6IfIndex: u32,
+ ZoneIndices: [16]u32,
+ FirstPrefix: rawptr, // ^IP_ADAPTER_PREFIX_XP,
+ TransmitLinkSpeed: u64,
+ ReceiveLinkSpeed: u64,
+ FirstWinsServerAddress: rawptr, // ^IP_ADAPTER_WINS_SERVER_ADDRESS_LH,
+ FirstGatewayAddress: ^IP_ADAPTER_GATEWAY_ADDRESS_LH,
+ Ipv4Metric: u32,
+ Ipv6Metric: u32,
+ Luid: NET_LUID_LH,
+ Dhcpv4Server: SOCKET_ADDRESS,
+ CompartmentId: u32,
+ NetworkGuid: GUID,
+ ConnectionType: NET_IF_CONNECTION_TYPE,
+ TunnelType: TUNNEL_TYPE,
+ Dhcpv6Server: SOCKET_ADDRESS,
+ Dhcpv6ClientDuid: [130]u8,
+ Dhcpv6ClientDuidLength: u32,
+ Dhcpv6Iaid: u32,
+ FirstDnsSuffix: rawptr, // ^IP_ADAPTER_DNS_SUFFIX,
+}
+
+IP_ADAPTER_UNICAST_ADDRESS_LH :: struct {
+ Anonymous: struct #raw_union {
+ Alignment: u64,
+ Anonymous: struct {
+ Length: u32,
+ Flags: u32,
+ },
+ },
+ Next: ^IP_ADAPTER_UNICAST_ADDRESS_LH,
+ Address: SOCKET_ADDRESS,
+ PrefixOrigin: NL_PREFIX_ORIGIN,
+ SuffixOrigin: NL_SUFFIX_ORIGIN,
+ DadState: NL_DAD_STATE,
+ ValidLifetime: u32,
+ PreferredLifetime: u32,
+ LeaseLifetime: u32,
+ OnLinkPrefixLength: u8,
+}
+
+IP_ADAPTER_ANYCAST_ADDRESS_XP :: struct {
+ Anonymous: struct #raw_union {
+ Alignment: u64,
+ Anonymous: struct {
+ Length: u32,
+ Flags: u32,
+ },
+ },
+ Next: ^IP_ADAPTER_ANYCAST_ADDRESS_XP,
+ Address: SOCKET_ADDRESS,
+}
+
+IP_ADAPTER_MULTICAST_ADDRESS_XP :: struct {
+ Anonymous: struct #raw_union {
+ Alignment: u64,
+ Anonymous: struct {
+ Length: u32,
+ Flags: u32,
+ },
+ },
+ Next: ^IP_ADAPTER_MULTICAST_ADDRESS_XP,
+ Address: SOCKET_ADDRESS,
+}
+
+IP_ADAPTER_GATEWAY_ADDRESS_LH :: struct {
+ Anonymous: struct #raw_union {
+ Alignment: u64,
+ Anonymous: struct {
+ Length: u32,
+ Reserved: u32,
+ },
+ },
+ Next: ^IP_ADAPTER_GATEWAY_ADDRESS_LH,
+ Address: SOCKET_ADDRESS,
+}
+
+IP_ADAPTER_DNS_SERVER_ADDRESS_XP :: struct {
+ Anonymous: struct #raw_union {
+ Alignment: u64,
+ Anonymous: struct {
+ Length: u32,
+ Reserved: u32,
+ },
+ },
+ Next: ^IP_ADAPTER_DNS_SERVER_ADDRESS_XP,
+ Address: SOCKET_ADDRESS,
+}
+
+IF_OPER_STATUS :: enum i32 {
+ Up = 1,
+ Down = 2,
+ Testing = 3,
+ Unknown = 4,
+ Dormant = 5,
+ NotPresent = 6,
+ LowerLayerDown = 7,
+}
+
+NET_LUID_LH :: struct #raw_union {
+ Value: u64,
+ Info: struct {
+ _bitfield: u64,
+ },
+}
+
+SOCKET_ADDRESS :: struct {
+ lpSockaddr: ^SOCKADDR,
+ iSockaddrLength: i32,
+}
+
+NET_IF_CONNECTION_TYPE :: enum i32 {
+ NET_IF_CONNECTION_DEDICATED = 1,
+ NET_IF_CONNECTION_PASSIVE = 2,
+ NET_IF_CONNECTION_DEMAND = 3,
+ NET_IF_CONNECTION_MAXIMUM = 4,
+}
+
+TUNNEL_TYPE :: enum i32 {
+ TUNNEL_TYPE_NONE = 0,
+ TUNNEL_TYPE_OTHER = 1,
+ TUNNEL_TYPE_DIRECT = 2,
+ TUNNEL_TYPE_6TO4 = 11,
+ TUNNEL_TYPE_ISATAP = 13,
+ TUNNEL_TYPE_TEREDO = 14,
+ TUNNEL_TYPE_IPHTTPS = 15,
+}
+NL_PREFIX_ORIGIN :: enum i32 {
+ IpPrefixOriginOther = 0,
+ IpPrefixOriginManual = 1,
+ IpPrefixOriginWellKnown = 2,
+ IpPrefixOriginDhcp = 3,
+ IpPrefixOriginRouterAdvertisement = 4,
+ IpPrefixOriginUnchanged = 16,
+}
+
+NL_SUFFIX_ORIGIN :: enum i32 {
+ NlsoOther = 0,
+ NlsoManual = 1,
+ NlsoWellKnown = 2,
+ NlsoDhcp = 3,
+ NlsoLinkLayerAddress = 4,
+ NlsoRandom = 5,
+ IpSuffixOriginOther = 0,
+ IpSuffixOriginManual = 1,
+ IpSuffixOriginWellKnown = 2,
+ IpSuffixOriginDhcp = 3,
+ IpSuffixOriginLinkLayerAddress = 4,
+ IpSuffixOriginRandom = 5,
+ IpSuffixOriginUnchanged = 16,
+}
+
+NL_DAD_STATE :: enum i32 {
+ NldsInvalid = 0,
+ NldsTentative = 1,
+ NldsDuplicate = 2,
+ NldsDeprecated = 3,
+ NldsPreferred = 4,
+ IpDadStateInvalid = 0,
+ IpDadStateTentative = 1,
+ IpDadStateDuplicate = 2,
+ IpDadStateDeprecated = 3,
+ IpDadStatePreferred = 4,
+}
+
+@(default_calling_convention = "std")
+foreign iphlpapi {
+ /*
+ The GetAdaptersAddresses function retrieves the addresses associated with the adapters on the local computer.
+ See: https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getadaptersaddresses
+ */
+ @(link_name="GetAdaptersAddresses") get_adapters_addresses :: proc(
+ family: Address_Family,
+ flags: GAA_Flags,
+ _reserved: rawptr,
+ adapter_addresses: [^]IP_Adapter_Addresses,
+ size: ^u32,
+ ) -> ULONG ---
+
+}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 3eb42a3ad..beed3a7e5 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -2,6 +2,24 @@
package sys_windows
foreign import kernel32 "system:Kernel32.lib"
+foreign import one_core "system:OneCore.lib"
+
+FOREGROUND_BLUE :: WORD(0x0001)
+FOREGROUND_GREEN :: WORD(0x0002)
+FOREGROUND_RED :: WORD(0x0004)
+FOREGROUND_INTENSITY :: WORD(0x0008)
+BACKGROUND_BLUE :: WORD(0x0010)
+BACKGROUND_GREEN :: WORD(0x0020)
+BACKGROUND_RED :: WORD(0x0040)
+BACKGROUND_INTENSITY :: WORD(0x0080)
+COMMON_LVB_LEADING_BYTE :: WORD(0x0100)
+COMMON_LVB_TRAILING_BYTE :: WORD(0x0200)
+COMMON_LVB_GRID_HORIZONTAL :: WORD(0x0400)
+COMMON_LVB_GRID_LVERTICAL :: WORD(0x0800)
+COMMON_LVB_GRID_RVERTICAL :: WORD(0x1000)
+COMMON_LVB_REVERSE_VIDEO :: WORD(0x4000)
+COMMON_LVB_UNDERSCORE :: WORD(0x8000)
+COMMON_LVB_SBCSDBCS :: WORD(0x0300)
@(default_calling_convention="stdcall")
foreign kernel32 {
@@ -26,7 +44,10 @@ foreign kernel32 {
dwMode: DWORD) -> BOOL ---
SetConsoleCursorPosition :: proc(hConsoleHandle: HANDLE,
dwCursorPosition: COORD) -> BOOL ---
-
+ SetConsoleTextAttribute :: proc(hConsoleOutput: HANDLE,
+ wAttributes: WORD) -> BOOL ---
+ SetConsoleOutputCP :: proc(wCodePageID: UINT) -> BOOL ---
+
GetFileInformationByHandle :: proc(hFile: HANDLE, lpFileInformation: LPBY_HANDLE_FILE_INFORMATION) -> BOOL ---
SetHandleInformation :: proc(hObject: HANDLE,
dwMask: DWORD,
@@ -173,9 +194,10 @@ foreign kernel32 {
dwCreationFlags: DWORD,
lpEnvironment: LPVOID,
lpCurrentDirectory: LPCWSTR,
- lpStartupInfo: LPSTARTUPINFO,
+ lpStartupInfo: LPSTARTUPINFOW,
lpProcessInformation: LPPROCESS_INFORMATION,
) -> BOOL ---
+ GetStartupInfoW :: proc(lpStartupInfo: LPSTARTUPINFOW) ---
GetEnvironmentVariableW :: proc(n: LPCWSTR, v: LPWSTR, nsize: DWORD) -> DWORD ---
SetEnvironmentVariableW :: proc(n: LPCWSTR, v: LPCWSTR) -> BOOL ---
GetEnvironmentStringsW :: proc() -> LPWCH ---
@@ -315,6 +337,13 @@ foreign kernel32 {
lpOverlapped: LPOVERLAPPED,
lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE,
) -> BOOL ---
+ FindFirstChangeNotificationW :: proc(
+ lpPathName: LPWSTR,
+ bWatchSubtree: BOOL,
+ dwNotifyFilter: DWORD,
+ ) -> HANDLE ---
+ FindNextChangeNotification :: proc(hChangeHandle: HANDLE) -> BOOL ---
+ FindCloseChangeNotification :: proc(hChangeHandle: HANDLE) -> BOOL ---
InitializeSRWLock :: proc(SRWLock: ^SRWLOCK) ---
AcquireSRWLockExclusive :: proc(SRWLock: ^SRWLOCK) ---
@@ -363,6 +392,11 @@ foreign kernel32 {
GenerateConsoleCtrlEvent :: proc(dwCtrlEvent: DWORD, dwProcessGroupId: DWORD) -> BOOL ---
FreeConsole :: proc() -> BOOL ---
GetConsoleWindow :: proc() -> HWND ---
+ GetConsoleScreenBufferInfo :: proc(hConsoleOutput: HANDLE, lpConsoleScreenBufferInfo: PCONSOLE_SCREEN_BUFFER_INFO) -> BOOL ---
+ SetConsoleScreenBufferSize :: proc(hConsoleOutput: HANDLE, dwSize: COORD) -> BOOL ---
+ SetConsoleWindowInfo :: proc(hConsoleOutput: HANDLE, bAbsolute : BOOL, lpConsoleWindow: ^SMALL_RECT) -> BOOL ---
+ GetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL ---
+ SetConsoleCursorInfo :: proc(hConsoleOutput: HANDLE, lpConsoleCursorInfo: PCONSOLE_CURSOR_INFO) -> BOOL ---
GetDiskFreeSpaceExW :: proc(
lpDirectoryName: LPCWSTR,
@@ -370,8 +404,89 @@ foreign kernel32 {
lpTotalNumberOfBytes: PULARGE_INTEGER,
lpTotalNumberOfFreeBytes: PULARGE_INTEGER,
) -> BOOL ---
+
+ GetLogicalProcessorInformation :: proc(buffer: ^SYSTEM_LOGICAL_PROCESSOR_INFORMATION, returnedLength: PDWORD) -> BOOL ---
+
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setfilecompletionnotificationmodes)
+ SetFileCompletionNotificationModes :: proc(FileHandle: HANDLE, Flags: u8) -> BOOL ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-createiocompletionport)
+ CreateIoCompletionPort :: proc(FileHandle: HANDLE, ExistingCompletionPort: HANDLE, CompletionKey: ^uintptr, NumberOfConcurrentThreads: DWORD) -> HANDLE ---
+ //[MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus)
+ GetQueuedCompletionStatus :: proc(CompletionPort: HANDLE, lpNumberOfBytesTransferred: ^DWORD, lpCompletionKey: uintptr, lpOverlapped: ^^OVERLAPPED, dwMilliseconds: DWORD) -> BOOL ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatusex)
+ GetQueuedCompletionStatusEx :: proc(CompletionPort: HANDLE, lpCompletionPortEntries: ^OVERLAPPED_ENTRY, ulCount: c_ulong, ulNumEntriesRemoved: ^c_ulong, dwMilliseconds: DWORD, fAlertable: BOOL) -> BOOL ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-postqueuedcompletionstatus)
+ PostQueuedCompletionStatus :: proc(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: c_ulong, lpOverlapped: ^OVERLAPPED) -> BOOL ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-gethandleinformation)
+ GetHandleInformation :: proc(hObject: HANDLE, lpdwFlags: ^DWORD) -> BOOL ---
}
+DEBUG_PROCESS :: 0x00000001
+DEBUG_ONLY_THIS_PROCESS :: 0x00000002
+CREATE_SUSPENDED :: 0x00000004
+DETACHED_PROCESS :: 0x00000008
+CREATE_NEW_CONSOLE :: 0x00000010
+NORMAL_PRIORITY_CLASS :: 0x00000020
+IDLE_PRIORITY_CLASS :: 0x00000040
+HIGH_PRIORITY_CLASS :: 0x00000080
+REALTIME_PRIORITY_CLASS :: 0x00000100
+CREATE_NEW_PROCESS_GROUP :: 0x00000200
+CREATE_UNICODE_ENVIRONMENT :: 0x00000400
+CREATE_SEPARATE_WOW_VDM :: 0x00000800
+CREATE_SHARED_WOW_VDM :: 0x00001000
+CREATE_FORCEDOS :: 0x00002000
+BELOW_NORMAL_PRIORITY_CLASS :: 0x00004000
+ABOVE_NORMAL_PRIORITY_CLASS :: 0x00008000
+INHERIT_PARENT_AFFINITY :: 0x00010000
+INHERIT_CALLER_PRIORITY :: 0x00020000 // Deprecated
+CREATE_PROTECTED_PROCESS :: 0x00040000
+EXTENDED_STARTUPINFO_PRESENT :: 0x00080000
+PROCESS_MODE_BACKGROUND_BEGIN :: 0x00100000
+PROCESS_MODE_BACKGROUND_END :: 0x00200000
+CREATE_SECURE_PROCESS :: 0x00400000
+CREATE_BREAKAWAY_FROM_JOB :: 0x01000000
+CREATE_PRESERVE_CODE_AUTHZ_LEVEL :: 0x02000000
+CREATE_DEFAULT_ERROR_MODE :: 0x04000000
+CREATE_NO_WINDOW :: 0x08000000
+PROFILE_USER :: 0x10000000
+PROFILE_KERNEL :: 0x20000000
+PROFILE_SERVER :: 0x40000000
+CREATE_IGNORE_SYSTEM_DEFAULT :: 0x80000000
+
+THREAD_BASE_PRIORITY_LOWRT :: 15 // value that gets a thread to LowRealtime-1
+THREAD_BASE_PRIORITY_MAX :: 2 // maximum thread base priority boost
+THREAD_BASE_PRIORITY_MIN :: (-2) // minimum thread base priority boost
+THREAD_BASE_PRIORITY_IDLE :: (-15) // value that gets a thread to idle
+
+THREAD_PRIORITY_LOWEST :: THREAD_BASE_PRIORITY_MIN
+THREAD_PRIORITY_BELOW_NORMAL :: (THREAD_PRIORITY_LOWEST+1)
+THREAD_PRIORITY_NORMAL :: 0
+THREAD_PRIORITY_HIGHEST :: THREAD_BASE_PRIORITY_MAX
+THREAD_PRIORITY_ABOVE_NORMAL :: (THREAD_PRIORITY_HIGHEST-1)
+THREAD_PRIORITY_ERROR_RETURN :: (MAXLONG)
+THREAD_PRIORITY_TIME_CRITICAL :: THREAD_BASE_PRIORITY_LOWRT
+THREAD_PRIORITY_IDLE :: THREAD_BASE_PRIORITY_IDLE
+THREAD_MODE_BACKGROUND_BEGIN :: 0x00010000
+THREAD_MODE_BACKGROUND_END :: 0x00020000
+
+COPY_FILE_FAIL_IF_EXISTS :: 0x00000001
+COPY_FILE_RESTARTABLE :: 0x00000002
+COPY_FILE_OPEN_SOURCE_FOR_WRITE :: 0x00000004
+COPY_FILE_ALLOW_DECRYPTED_DESTINATION :: 0x00000008
+COPY_FILE_COPY_SYMLINK :: 0x00000800
+COPY_FILE_NO_BUFFERING :: 0x00001000
+COPY_FILE_REQUEST_SECURITY_PRIVILEGES :: 0x00002000
+COPY_FILE_RESUME_FROM_PAUSE :: 0x00004000
+COPY_FILE_NO_OFFLOAD :: 0x00040000
+COPY_FILE_IGNORE_EDP_BLOCK :: 0x00400000
+COPY_FILE_IGNORE_SOURCE_ENCRYPTION :: 0x00800000
+COPY_FILE_DONT_REQUEST_DEST_WRITE_DAC :: 0x02000000
+COPY_FILE_REQUEST_COMPRESSED_TRAFFIC :: 0x10000000
+COPY_FILE_OPEN_AND_COPY_REPARSE_POINT :: 0x00200000
+COPY_FILE_DIRECTORY :: 0x00000080
+COPY_FILE_SKIP_ALTERNATE_STREAMS :: 0x00008000
+COPY_FILE_DISABLE_PRE_ALLOCATION :: 0x04000000
+COPY_FILE_ENABLE_LOW_FREE_SPACE_MODE :: 0x08000000
SECTION_QUERY :: DWORD(0x0001)
SECTION_MAP_WRITE :: DWORD(0x0002)
@@ -777,7 +892,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
}*/
@(default_calling_convention="stdcall")
-foreign kernel32 {
+foreign one_core {
QueryVirtualMemoryInformation :: proc(
Process: HANDLE,
VirtualAddress: PVOID,
@@ -999,3 +1114,49 @@ foreign kernel32 {
ConvertThreadToFiber :: proc(lpParameter: LPVOID) -> LPVOID ---
SwitchToFiber :: proc(lpFiber: LPVOID) ---
}
+
+LOGICAL_PROCESSOR_RELATIONSHIP :: enum c_int {
+ RelationProcessorCore,
+ RelationNumaNode,
+ RelationCache,
+ RelationProcessorPackage,
+ RelationGroup,
+ RelationProcessorDie,
+ RelationNumaNodeEx,
+ RelationProcessorModule,
+ RelationAll = 0xffff,
+}
+
+PROCESSOR_CACHE_TYPE :: enum c_int {
+ CacheUnified,
+ CacheInstruction,
+ CacheData,
+ CacheTrace,
+}
+
+CACHE_DESCRIPTOR :: struct {
+ Level: BYTE,
+ Associativity: BYTE,
+ LineSize: WORD,
+ Size: DWORD,
+ Type: PROCESSOR_CACHE_TYPE,
+}
+
+ProcessorCore :: struct {
+ Flags: BYTE,
+}
+NumaNode :: struct {
+ NodeNumber: DWORD,
+}
+DUMMYUNIONNAME_u :: struct #raw_union {
+ Core: ProcessorCore,
+ Node: NumaNode,
+ Cache: CACHE_DESCRIPTOR,
+ Reserved: [2]ULONGLONG,
+}
+
+SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
+ ProcessorMask: ULONG_PTR,
+ Relationship: LOGICAL_PROCESSOR_RELATIONSHIP,
+ DummyUnion: DUMMYUNIONNAME_u,
+}
diff --git a/core/sys/windows/shell32.odin b/core/sys/windows/shell32.odin
index 28da58a50..0a6f90a44 100644
--- a/core/sys/windows/shell32.odin
+++ b/core/sys/windows/shell32.odin
@@ -22,4 +22,39 @@ foreign shell32 {
) -> c_int ---
SHFileOperationW :: proc(lpFileOp: LPSHFILEOPSTRUCTW) -> c_int ---
SHGetFolderPathW :: proc(hwnd: HWND, csidl: c_int, hToken: HANDLE, dwFlags: DWORD, pszPath: LPWSTR) -> HRESULT ---
+ SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR ---
+
+ Shell_NotifyIconW :: proc(dwMessage: DWORD, lpData: ^NOTIFYICONDATAW) -> BOOL ---
}
+
+APPBARDATA :: struct {
+ cbSize: DWORD,
+ hWnd: HWND,
+ uCallbackMessage: UINT,
+ uEdge: UINT,
+ rc: RECT,
+ lParam: LPARAM,
+}
+PAPPBARDATA :: ^APPBARDATA
+
+ABM_NEW :: 0x00000000
+ABM_REMOVE :: 0x00000001
+ABM_QUERYPOS :: 0x00000002
+ABM_SETPOS :: 0x00000003
+ABM_GETSTATE :: 0x00000004
+ABM_GETTASKBARPOS :: 0x00000005
+ABM_ACTIVATE :: 0x00000006
+ABM_GETAUTOHIDEBAR :: 0x00000007
+ABM_SETAUTOHIDEBAR :: 0x00000008
+ABM_WINDOWPOSCHANGED :: 0x0000009
+ABM_SETSTATE :: 0x0000000a
+ABN_STATECHANGE :: 0x0000000
+ABN_POSCHANGED :: 0x0000001
+ABN_FULLSCREENAPP :: 0x0000002
+ABN_WINDOWARRANGE :: 0x0000003
+ABS_AUTOHIDE :: 0x0000001
+ABS_ALWAYSONTOP :: 0x0000002
+ABE_LEFT :: 0
+ABE_TOP :: 1
+ABE_RIGHT :: 2
+ABE_BOTTOM :: 3
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 210c39aaa..11b87e0dc 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -38,6 +38,7 @@ HHOOK :: distinct HANDLE
HKEY :: distinct HANDLE
HDESK :: distinct HANDLE
HFONT :: distinct HANDLE
+HRGN :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -110,7 +111,7 @@ LPOVERLAPPED :: ^OVERLAPPED
LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
-LPSTARTUPINFO :: ^STARTUPINFO
+LPSTARTUPINFOW :: ^STARTUPINFOW
LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT
VOID :: rawptr
PVOID :: rawptr
@@ -144,8 +145,6 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK
-MMRESULT :: UINT
-
CREATE_WAITABLE_TIMER_MANUAL_RESET :: 0x00000001
CREATE_WAITABLE_TIMER_HIGH_RESOLUTION :: 0x00000002
@@ -153,10 +152,6 @@ 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
-
TRUE :: BOOL(true)
FALSE :: BOOL(false)
@@ -167,6 +162,8 @@ SIZE :: struct {
PSIZE :: ^SIZE
LPSIZE :: ^SIZE
+MAXLONG :: 0x7fffffff
+
FILE_ATTRIBUTE_READONLY: DWORD : 0x00000001
FILE_ATTRIBUTE_HIDDEN: DWORD : 0x00000002
FILE_ATTRIBUTE_SYSTEM: DWORD : 0x00000004
@@ -264,26 +261,6 @@ GET_FILEEX_INFO_LEVELS :: distinct i32
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1
-// String resource number bases (internal use)
-
-MMSYSERR_BASE :: 0
-WAVERR_BASE :: 32
-MIDIERR_BASE :: 64
-TIMERR_BASE :: 96
-JOYERR_BASE :: 160
-MCIERR_BASE :: 256
-MIXERR_BASE :: 1024
-
-MCI_STRING_OFFSET :: 512
-MCI_VD_OFFSET :: 1024
-MCI_CD_OFFSET :: 1088
-MCI_WAVE_OFFSET :: 1152
-MCI_SEQ_OFFSET :: 1216
-
-// timer error return values
-TIMERR_NOERROR :: 0 // no error
-TIMERR_NOCANDO :: TIMERR_BASE + 1 // request not completed
-TIMERR_STRUCT :: TIMERR_BASE + 33 // time struct size
DIAGNOSTIC_REASON_VERSION :: 0
@@ -727,6 +704,14 @@ CWPRETSTRUCT :: struct {
hwnd: HWND,
}
+MSLLHOOKSTRUCT :: struct {
+ pt: POINT,
+ mouseData: DWORD,
+ flags: DWORD,
+ time: DWORD,
+ dwExtraInfo: ULONG_PTR,
+}
+
KBDLLHOOKSTRUCT :: struct {
vkCode: DWORD,
scanCode: DWORD,
@@ -735,6 +720,59 @@ KBDLLHOOKSTRUCT :: struct {
dwExtraInfo: ULONG_PTR,
}
+MOUSEINPUT :: struct {
+ dx: LONG,
+ dy: LONG,
+ mouseData: DWORD,
+ dwFlags: DWORD,
+ time: DWORD,
+ dwExtraInfo: ULONG_PTR,
+}
+
+KEYBDINPUT :: struct {
+ wVk: WORD,
+ wScan: WORD,
+ dwFlags: DWORD,
+ time: DWORD,
+ dwExtraInfo: ULONG_PTR,
+}
+
+HARDWAREINPUT :: struct {
+ uMsg: DWORD,
+ wParamL: WORD,
+ wParamH: WORD,
+}
+
+INPUT_TYPE :: enum DWORD {
+ MOUSE = 0,
+ KEYBOARD = 1,
+ HARDWARE = 2,
+}
+
+INPUT :: struct {
+ type: INPUT_TYPE,
+ using _: struct #raw_union {
+ mi: MOUSEINPUT,
+ ki: KEYBDINPUT,
+ hi: HARDWAREINPUT,
+ },
+}
+
+MOUSEEVENTF_MOVE :: 0x0001
+MOUSEEVENTF_LEFTDOWN :: 0x0002
+MOUSEEVENTF_LEFTUP :: 0x0004
+MOUSEEVENTF_RIGHTDOWN :: 0x0008
+MOUSEEVENTF_RIGHTUP :: 0x0010
+MOUSEEVENTF_MIDDLEDOWN :: 0x0020
+MOUSEEVENTF_MIDDLEUP :: 0x0040
+MOUSEEVENTF_XDOWN :: 0x0080
+MOUSEEVENTF_XUP :: 0x0100
+MOUSEEVENTF_WHEEL :: 0x0800
+MOUSEEVENTF_HWHEEL :: 0x1000
+MOUSEEVENTF_MOVE_NOCOALESCE :: 0x2000
+MOUSEEVENTF_VIRTUALDESK :: 0x4000
+MOUSEEVENTF_ABSOLUTE :: 0x8000
+
WNDCLASSA :: struct {
style: UINT,
lpfnWndProc: WNDPROC,
@@ -802,6 +840,104 @@ MSG :: struct {
LPMSG :: ^MSG
+NOTIFYICONDATAW :: struct {
+ cbSize: DWORD,
+ hWnd: HWND,
+ uID: UINT,
+ uFlags: UINT,
+ uCallbackMessage: UINT,
+ hIcon: HICON,
+ szTip: [128]WCHAR,
+ dwState: DWORD,
+ dwStateMask: DWORD,
+ szInfo: [256]WCHAR,
+ using _: struct #raw_union {
+ uTimeout: UINT,
+ uVersion: UINT,
+ },
+ szInfoTitle: [64]WCHAR,
+ dwInfoFlags: DWORD,
+ guidItem: GUID,
+ hBalloonIcon: HICON,
+}
+
+NIF_MESSAGE :: 0x00000001
+NIF_ICON :: 0x00000002
+NIF_TIP :: 0x00000004
+NIF_STATE :: 0x00000008
+NIF_INFO :: 0x00000010
+NIF_GUID :: 0x00000020
+NIF_REALTIME :: 0x00000040
+NIF_SHOWTIP :: 0x00000080
+
+NIM_ADD :: 0x00000000
+NIM_MODIFY :: 0x00000001
+NIM_DELETE :: 0x00000002
+NIM_SETFOCUS :: 0x00000003
+NIM_SETVERSION :: 0x00000004
+
+// Menu flags for Add/Check/EnableMenuItem()
+MF_INSERT :: 0x00000000
+MF_CHANGE :: 0x00000080
+MF_APPEND :: 0x00000100
+MF_DELETE :: 0x00000200
+MF_REMOVE :: 0x00001000
+
+MF_BYCOMMAND :: 0x00000000
+MF_BYPOSITION :: 0x00000400
+
+MF_SEPARATOR :: 0x00000800
+
+MF_ENABLED :: 0x00000000
+MF_GRAYED :: 0x00000001
+MF_DISABLED :: 0x00000002
+
+MF_UNCHECKED :: 0x00000000
+MF_CHECKED :: 0x00000008
+MF_USECHECKBITMAPS :: 0x00000200
+
+MF_STRING :: 0x00000000
+MF_BITMAP :: 0x00000004
+MF_OWNERDRAW :: 0x00000100
+
+MF_POPUP :: 0x00000010
+MF_MENUBARBREAK :: 0x00000020
+MF_MENUBREAK :: 0x00000040
+
+MF_UNHILITE :: 0x00000000
+MF_HILITE :: 0x00000080
+
+MF_DEFAULT :: 0x00001000
+MF_SYSMENU :: 0x00002000
+MF_HELP :: 0x00004000
+MF_RIGHTJUSTIFY :: 0x00004000
+
+MF_MOUSESELECT :: 0x00008000
+MF_END :: 0x00000080 // Obsolete -- only used by old RES files
+
+// Flags for TrackPopupMenu
+TPM_LEFTBUTTON :: 0x0000
+TPM_RIGHTBUTTON :: 0x0002
+TPM_LEFTALIGN :: 0x0000
+TPM_CENTERALIGN :: 0x0004
+TPM_RIGHTALIGN :: 0x0008
+TPM_TOPALIGN :: 0x0000
+TPM_VCENTERALIGN :: 0x0010
+TPM_BOTTOMALIGN :: 0x0020
+
+TPM_HORIZONTAL :: 0x0000 /* Horz alignment matters more */
+TPM_VERTICAL :: 0x0040 /* Vert alignment matters more */
+TPM_NONOTIFY :: 0x0080 /* Don't send any notification msgs */
+TPM_RETURNCMD :: 0x0100
+TPM_RECURSE :: 0x0001
+TPM_HORPOSANIMATION :: 0x0400
+TPM_HORNEGANIMATION :: 0x0800
+TPM_VERPOSANIMATION :: 0x1000
+TPM_VERNEGANIMATION :: 0x2000
+TPM_NOANIMATION :: 0x4000
+TPM_LAYOUTRTL :: 0x8000
+TPM_WORKAREA :: 0x10000
+
// WM_NCHITTEST and MOUSEHOOKSTRUCT Mouse Position Codes
HTERROR :: -2
HTTRANSPARENT :: -1
@@ -1867,30 +2003,6 @@ BI_BITFIELDS :: 3
BI_JPEG :: 4
BI_PNG :: 5
-WSA_FLAG_OVERLAPPED: DWORD : 0x01
-WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80
-
-WSADESCRIPTION_LEN :: 256
-WSASYS_STATUS_LEN :: 128
-WSAPROTOCOL_LEN: DWORD : 255
-INVALID_SOCKET :: ~SOCKET(0)
-
-WSAEACCES: c_int : 10013
-WSAEINVAL: c_int : 10022
-WSAEWOULDBLOCK: c_int : 10035
-WSAEPROTOTYPE: c_int : 10041
-WSAEADDRINUSE: c_int : 10048
-WSAEADDRNOTAVAIL: c_int : 10049
-WSAECONNABORTED: c_int : 10053
-WSAECONNRESET: c_int : 10054
-WSAENOTCONN: c_int : 10057
-WSAESHUTDOWN: c_int : 10058
-WSAETIMEDOUT: c_int : 10060
-WSAECONNREFUSED: c_int : 10061
-WSATRY_AGAIN: c_int : 11002
-
-MAX_PROTOCOL_CHAIN: DWORD : 7
-
MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
FSCTL_GET_REPARSE_POINT: DWORD : 0x900a8
IO_REPARSE_TAG_SYMLINK: DWORD : 0xa000000c
@@ -1932,7 +2044,6 @@ TLS_OUT_OF_INDEXES: DWORD : 0xFFFFFFFF
DLL_THREAD_DETACH: DWORD : 3
DLL_PROCESS_DETACH: DWORD : 0
-CREATE_SUSPENDED :: DWORD(0x00000004)
INFINITE :: ~DWORD(0)
@@ -1941,51 +2052,8 @@ DUPLICATE_SAME_ACCESS: DWORD : 0x00000002
CONDITION_VARIABLE_INIT :: CONDITION_VARIABLE{}
SRWLOCK_INIT :: SRWLOCK{}
-DETACHED_PROCESS: DWORD : 0x00000008
-CREATE_NEW_CONSOLE: DWORD : 0x00000010
-CREATE_NO_WINDOW: DWORD : 0x08000000
-CREATE_NEW_PROCESS_GROUP: DWORD : 0x00000200
-CREATE_UNICODE_ENVIRONMENT: DWORD : 0x00000400
STARTF_USESTDHANDLES: DWORD : 0x00000100
-AF_INET: c_int : 2
-AF_INET6: c_int : 23
-SD_BOTH: c_int : 2
-SD_RECEIVE: c_int : 0
-SD_SEND: c_int : 1
-SOCK_DGRAM: c_int : 2
-SOCK_STREAM: c_int : 1
-SOL_SOCKET: c_int : 0xffff
-SO_RCVTIMEO: c_int : 0x1006
-SO_SNDTIMEO: c_int : 0x1005
-SO_REUSEADDR: c_int : 0x0004
-IPPROTO_IP: c_int : 0
-IPPROTO_TCP: c_int : 6
-IPPROTO_IPV6: c_int : 41
-TCP_NODELAY: c_int : 0x0001
-IP_TTL: c_int : 4
-IPV6_V6ONLY: c_int : 27
-SO_ERROR: c_int : 0x1007
-SO_BROADCAST: c_int : 0x0020
-IP_MULTICAST_LOOP: c_int : 11
-IPV6_MULTICAST_LOOP: c_int : 11
-IP_MULTICAST_TTL: c_int : 10
-IP_ADD_MEMBERSHIP: c_int : 12
-IP_DROP_MEMBERSHIP: c_int : 13
-IPV6_ADD_MEMBERSHIP: c_int : 12
-IPV6_DROP_MEMBERSHIP: c_int : 13
-MSG_PEEK: c_int : 0x2
-
-ip_mreq :: struct {
- imr_multiaddr: in_addr,
- imr_interface: in_addr,
-}
-
-ipv6_mreq :: struct {
- ipv6mr_multiaddr: in6_addr,
- ipv6mr_interface: c_uint,
-}
-
VOLUME_NAME_DOS: DWORD : 0x0
MOVEFILE_REPLACE_EXISTING: DWORD : 1
@@ -2346,8 +2414,7 @@ PROCESS_INFORMATION :: struct {
dwThreadId: DWORD,
}
-// FYI: This is STARTUPINFOW, not STARTUPINFOA
-STARTUPINFO :: struct {
+STARTUPINFOW :: struct {
cb: DWORD,
lpReserved: LPWSTR,
lpDesktop: LPWSTR,
@@ -2368,11 +2435,6 @@ STARTUPINFO :: struct {
hStdError: HANDLE,
}
-SOCKADDR :: struct {
- sa_family: ADDRESS_FAMILY,
- sa_data: [14]CHAR,
-}
-
FILETIME :: struct {
dwLowDateTime: DWORD,
dwHighDateTime: DWORD,
@@ -2383,6 +2445,20 @@ FILETIME_as_unix_nanoseconds :: proc "contextless" (ft: FILETIME) -> i64 {
return (t - 116444736000000000) * 100
}
+OBJECT_ATTRIBUTES :: struct {
+ Length: c_ulong,
+ RootDirectory: HANDLE,
+ ObjectName: ^UNICODE_STRING,
+ Attributes: c_ulong,
+ SecurityDescriptor: rawptr,
+ SecurityQualityOfService: rawptr,
+}
+
+UNICODE_STRING :: struct {
+ Length: u16,
+ MaximumLength: u16,
+ Buffer: ^u16,
+}
OVERLAPPED :: struct {
Internal: ^c_ulong,
@@ -2392,6 +2468,13 @@ OVERLAPPED :: struct {
hEvent: HANDLE,
}
+OVERLAPPED_ENTRY :: struct {
+ lpCompletionKey: c_ulong,
+ lpOverlapped: ^OVERLAPPED,
+ Internal: c_ulong,
+ dwNumberOfBytesTransferred: DWORD,
+}
+
LPOVERLAPPED_COMPLETION_ROUTINE :: #type proc "stdcall" (
dwErrorCode: DWORD,
dwNumberOfBytesTransfered: DWORD,
@@ -2405,74 +2488,6 @@ ADDRESS_MODE :: enum c_int {
AddrModeFlat,
}
-SOCKADDR_STORAGE_LH :: struct {
- ss_family: ADDRESS_FAMILY,
- __ss_pad1: [6]CHAR,
- __ss_align: i64,
- __ss_pad2: [112]CHAR,
-}
-
-ADDRINFOA :: struct {
- ai_flags: c_int,
- ai_family: c_int,
- ai_socktype: c_int,
- ai_protocol: c_int,
- ai_addrlen: size_t,
- ai_canonname: ^c_char,
- ai_addr: ^SOCKADDR,
- ai_next: ^ADDRINFOA,
-}
-
-PADDRINFOEXW :: ^ADDRINFOEXW
-LPADDRINFOEXW :: ^ADDRINFOEXW
-ADDRINFOEXW :: struct {
- ai_flags: c_int,
- ai_family: c_int,
- ai_socktype: c_int,
- ai_protocol: c_int,
- ai_addrlen: size_t,
- ai_canonname: wstring,
- ai_addr: ^sockaddr,
- ai_blob: rawptr,
- ai_bloblen: size_t,
- ai_provider: LPGUID,
- ai_next: ^ADDRINFOEXW,
-}
-
-LPLOOKUPSERVICE_COMPLETION_ROUTINE :: #type proc "stdcall" (
- dwErrorCode: DWORD,
- dwNumberOfBytesTransfered: DWORD,
- lpOverlapped: LPOVERLAPPED,
-)
-
-sockaddr :: struct {
- sa_family: USHORT,
- sa_data: [14]byte,
-}
-
-sockaddr_in :: struct {
- sin_family: ADDRESS_FAMILY,
- sin_port: USHORT,
- sin_addr: in_addr,
- sin_zero: [8]CHAR,
-}
-
-sockaddr_in6 :: struct {
- sin6_family: ADDRESS_FAMILY,
- sin6_port: USHORT,
- sin6_flowinfo: c_ulong,
- sin6_addr: in6_addr,
- sin6_scope_id: c_ulong,
-}
-
-in_addr :: struct {
- s_addr: u32,
-}
-
-in6_addr :: struct {
- s6_addr: [16]u8,
-}
-
EXCEPTION_DISPOSITION :: enum c_int {
ExceptionContinueExecution,
ExceptionContinueSearch,
@@ -2563,6 +2578,27 @@ FILE_ATTRIBUTE_TAG_INFO :: struct {
ReparseTag: DWORD,
}
+PADDRINFOEXW :: ^ADDRINFOEXW
+LPADDRINFOEXW :: ^ADDRINFOEXW
+ADDRINFOEXW :: struct {
+ ai_flags: c_int,
+ ai_family: c_int,
+ ai_socktype: c_int,
+ ai_protocol: c_int,
+ ai_addrlen: size_t,
+ ai_canonname: wstring,
+ ai_addr: ^sockaddr,
+ ai_blob: rawptr,
+ ai_bloblen: size_t,
+ ai_provider: LPGUID,
+ ai_next: ^ADDRINFOEXW,
+}
+
+LPLOOKUPSERVICE_COMPLETION_ROUTINE :: #type proc "stdcall" (
+ dwErrorCode: DWORD,
+ dwNumberOfBytesTransfered: DWORD,
+ lpOverlapped: LPOVERLAPPED,
+)
// https://docs.microsoft.com/en-gb/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
@@ -3883,3 +3919,262 @@ COORD :: struct {
X: SHORT,
Y: SHORT,
}
+
+SMALL_RECT :: struct {
+ Left: SHORT,
+ Top: SHORT,
+ Right: SHORT,
+ Bottom: SHORT,
+}
+
+CONSOLE_SCREEN_BUFFER_INFO :: struct {
+ dwSize: COORD,
+ dwCursorPosition: COORD,
+ wAttributes: WORD,
+ srWindow: SMALL_RECT,
+ dwMaximumWindowSize: COORD,
+}
+
+CONSOLE_CURSOR_INFO :: struct {
+ dwSize: DWORD,
+ bVisible: BOOL,
+}
+
+
+PCONSOLE_SCREEN_BUFFER_INFO :: ^CONSOLE_SCREEN_BUFFER_INFO
+PCONSOLE_CURSOR_INFO :: ^CONSOLE_CURSOR_INFO
+
+//
+// Networking
+//
+WSA_FLAG_OVERLAPPED :: 1
+WSA_FLAG_MULTIPOINT_C_ROOT :: 2
+WSA_FLAG_MULTIPOINT_C_LEAF :: 4
+WSA_FLAG_MULTIPOINT_D_ROOT :: 8
+WSA_FLAG_MULTIPOINT_D_LEAF :: 16
+WSA_FLAG_ACCESS_SYSTEM_SECURITY :: 32
+WSA_FLAG_NO_HANDLE_INHERIT :: 128
+WSADESCRIPTION_LEN :: 256
+WSASYS_STATUS_LEN :: 128
+WSAPROTOCOL_LEN :: 255
+INVALID_SOCKET :: ~SOCKET(0)
+SOMAXCONN :: 128 // The number of messages that can be queued in memory after being received; use 2-4 for Bluetooth.
+ // This is for the 'backlog' parameter to listen().
+SOCKET_ERROR :: -1
+
+// Networking errors
+WSAEINTR :: 10004 // Call interrupted. CancelBlockingCall was called. (This is different on Linux.)
+WSAEACCES :: 10013 // If you try to bind a Udp socket to the broadcast address without the socket option set.
+WSAEFAULT :: 10014 // A pointer that was passed to a WSA function is invalid, such as a buffer size is smaller than you said it was
+WSAEINVAL :: 10022 // Invalid argument supplied
+WSAEMFILE :: 10024 // SOCKET handles exhausted
+WSAEWOULDBLOCK :: 10035 // No data is ready yet
+WSAENOTSOCK :: 10038 // Not a socket.
+WSAEINPROGRESS :: 10036 // WS1.1 call is in progress or callback function is still being processed
+WSAEALREADY :: 10037 // Already connecting in parallel.
+WSAEMSGSIZE :: 10040 // Message was truncated because it exceeded max datagram size.
+WSAEPROTOTYPE :: 10041 // Wrong protocol for the provided socket
+WSAENOPROTOOPT :: 10042 // TODO
+WSAEPROTONOSUPPORT :: 10043 // Protocol not supported
+WSAESOCKTNOSUPPORT :: 10044 // Socket type not supported in the given address family
+WSAEAFNOSUPPORT :: 10047 // Address family not supported
+WSAEOPNOTSUPP :: 10045 // Attempt to accept on non-stream socket, etc.
+WSAEADDRINUSE :: 10048 // Endpoint being bound is in use by another socket.
+WSAEADDRNOTAVAIL :: 10049 // Not a valid local IP address on this computer.
+WSAENETDOWN :: 10050 // Network subsystem failure on the local machine.
+WSAENETUNREACH :: 10051 // The local machine is not connected to the network.
+WSAENETRESET :: 10052 // Keepalive failure detected, or TTL exceeded when receiving UDP packets.
+WSAECONNABORTED :: 10053 // Connection has been aborted by software in the host machine.
+WSAECONNRESET :: 10054 // The connection was reset while trying to accept, read or write.
+WSAENOBUFS :: 10055 // No buffer space is available. The outgoing queue may be full in which case you should probably try again after a pause.
+WSAEISCONN :: 10056 // The socket is already connected.
+WSAENOTCONN :: 10057 // The socket is not connected yet, or no address was supplied to sendto.
+WSAESHUTDOWN :: 10058 // The socket has been shutdown in the direction required.
+WSAETIMEDOUT :: 10060 // The timeout duration was reached before any data was received / before all data was sent.
+WSAECONNREFUSED :: 10061 // The remote machine is not listening on that endpoint.
+WSAEHOSTDOWN :: 10064 // Destination host was down.
+WSAEHOSTUNREACH :: 10065 // The remote machine is not connected to the network.
+WSAENOTINITIALISED :: 10093 // Needs WSAStartup call
+WSAEINVALIDPROCTABLE :: 10104 // Invalid or incomplete procedure table was returned
+WSAEINVALIDPROVIDER :: 10105 // Service provider version is not 2.2
+WSAEPROVIDERFAILEDINIT :: 10106 // Service provider failed to initialize
+
+// Address families
+AF_UNSPEC : c_int : 0 // Unspecified
+AF_INET : c_int : 2 // IPv4
+AF_INET6 : c_int : 23 // IPv6
+AF_IRDA : c_int : 26 // Infrared
+AF_BTH : c_int : 32 // Bluetooth
+
+// Socket types
+SOCK_STREAM : c_int : 1 // TCP
+SOCK_DGRAM : c_int : 2 // UDP
+SOCK_RAW : c_int : 3 // Requires options IP_HDRINCL for v4, IPV6_HDRINCL for v6, on the socket
+SOCK_RDM : c_int : 4 // Requires "Reliable Multicast Protocol" to be installed - see WSAEnumProtocols
+SOCK_SEQPACKET : c_int : 5 // Provides psuedo-stream packet based on DGRAMs.
+
+// Protocols
+IPPROTO_IP : c_int : 0
+IPPROTO_ICMP : c_int : 1 // (AF_UNSPEC, AF_INET, AF_INET6) + SOCK_RAW | not specified
+IPPROTO_IGMP : c_int : 2 // (AF_UNSPEC, AF_INET, AF_INET6) + SOCK_RAW | not specified
+BTHPROTO_RFCOMM : c_int : 3 // Bluetooth: AF_BTH + SOCK_STREAM
+IPPROTO_TCP : c_int : 6 // (AF_INET, AF_INET6) + SOCK_STREAM
+IPPROTO_UDP : c_int : 17 // (AF_INET, AF_INET6) + SOCK_DGRAM
+IPPROTO_ICMPV6 : c_int : 58 // (AF_UNSPEC, AF_INET, AF_INET6) + SOCK_RAW
+IPPROTO_RM : c_int : 113 // AF_INET + SOCK_RDM [requires "Reliable Multicast Protocol" to be installed - see WSAEnumProtocols]
+
+// Shutdown manners
+SD_RECEIVE : c_int : 0
+SD_SEND : c_int : 1
+SD_BOTH : c_int : 2
+
+// Socket 'levels'
+SOL_SOCKET : c_int : 0xffff // Socket options for any socket.
+IPPROTO_IPV6 : c_int : 41 // Socket options for IPV6.
+
+// Options for any sockets
+SO_ACCEPTCONN : c_int : 0x0002
+SO_REUSEADDR : c_int : 0x0004
+SO_KEEPALIVE : c_int : 0x0008
+SO_SNDTIMEO : c_int : 0x1005
+SO_RCVTIMEO : c_int : 0x1006
+SO_EXCLUSIVEADDRUSE : c_int : ~SO_REUSEADDR
+SO_CONDITIONAL_ACCEPT : c_int : 0x3002
+SO_DONTLINGER : c_int : ~SO_LINGER
+SO_OOBINLINE : c_int : 0x0100
+SO_LINGER : c_int : 0x0080
+SO_RCVBUF : c_int : 0x1002
+SO_SNDBUF : c_int : 0x1001
+SO_ERROR : c_int : 0x1007
+SO_BROADCAST : c_int : 0x0020
+
+TCP_NODELAY: c_int : 0x0001
+IP_TTL: c_int : 4
+IPV6_V6ONLY: c_int : 27
+IP_MULTICAST_LOOP: c_int : 11
+IPV6_MULTICAST_LOOP: c_int : 11
+IP_MULTICAST_TTL: c_int : 10
+IP_ADD_MEMBERSHIP: c_int : 12
+
+IPV6_ADD_MEMBERSHIP: c_int : 12
+IPV6_DROP_MEMBERSHIP: c_int : 13
+
+MAX_PROTOCOL_CHAIN: DWORD : 7
+
+// Used with the SO_LINGER socket option to setsockopt().
+LINGER :: struct {
+ l_onoff: c.ushort,
+ l_linger: c.ushort,
+}
+// Send/Receive flags.
+MSG_OOB : c_int : 1 // `send`/`recv` should process out-of-band data.
+MSG_PEEK : c_int : 2 // `recv` should not remove the data from the buffer. Only valid for non-overlapped operations.
+
+
+SOCKET :: distinct uintptr // TODO
+socklen_t :: c_int
+ADDRESS_FAMILY :: USHORT
+
+ip_mreq :: struct {
+ imr_multiaddr: in_addr,
+ imr_interface: in_addr,
+}
+
+ipv6_mreq :: struct {
+ ipv6mr_multiaddr: in6_addr,
+ ipv6mr_interface: c_uint,
+}
+
+SOCKADDR_STORAGE_LH :: struct {
+ ss_family: ADDRESS_FAMILY,
+ __ss_pad1: [6]CHAR,
+ __ss_align: i64,
+ __ss_pad2: [112]CHAR,
+}
+
+ADDRINFOA :: struct {
+ ai_flags: c_int,
+ ai_family: c_int,
+ ai_socktype: c_int,
+ ai_protocol: c_int,
+ ai_addrlen: size_t,
+ ai_canonname: ^c_char,
+ ai_addr: ^SOCKADDR,
+ ai_next: ^ADDRINFOA,
+}
+
+sockaddr :: struct {
+ sa_family: USHORT,
+ sa_data: [14]byte,
+}
+
+sockaddr_in :: struct {
+ sin_family: ADDRESS_FAMILY,
+ sin_port: u16be,
+ sin_addr: in_addr,
+ sin_zero: [8]CHAR,
+}
+sockaddr_in6 :: struct {
+ sin6_family: ADDRESS_FAMILY,
+ sin6_port: u16be,
+ sin6_flowinfo: c_ulong,
+ sin6_addr: in6_addr,
+ sin6_scope_id: c_ulong,
+}
+
+in_addr :: struct {
+ s_addr: u32,
+}
+
+in6_addr :: struct {
+ s6_addr: [16]u8,
+}
+
+
+DNS_STATUS :: distinct DWORD // zero is success
+DNS_INFO_NO_RECORDS :: 9501
+DNS_QUERY_NO_RECURSION :: 0x00000004
+
+DNS_RECORD :: struct {
+ pNext: ^DNS_RECORD,
+ pName: cstring,
+ wType: WORD,
+ wDataLength: USHORT,
+ Flags: DWORD,
+ dwTtl: DWORD,
+ _: DWORD,
+ Data: struct #raw_union {
+ CNAME: DNS_PTR_DATAA,
+ A: u32be, // Ipv4 Address
+ AAAA: u128be, // Ipv6 Address
+ TXT: DNS_TXT_DATAA,
+ NS: DNS_PTR_DATAA,
+ MX: DNS_MX_DATAA,
+ SRV: DNS_SRV_DATAA,
+ },
+}
+
+DNS_TXT_DATAA :: struct {
+ dwStringCount: DWORD,
+ pStringArray: cstring,
+}
+
+DNS_PTR_DATAA :: cstring
+
+DNS_MX_DATAA :: struct {
+ pNameExchange: cstring, // the hostname
+ wPreference: WORD, // lower values preferred
+ _: WORD, // padding.
+}
+DNS_SRV_DATAA :: struct {
+ pNameTarget: cstring,
+ wPriority: u16,
+ wWeight: u16,
+ wPort: u16,
+ _: WORD, // padding
+}
+
+SOCKADDR :: struct {
+ sa_family: ADDRESS_FAMILY,
+ sa_data: [14]CHAR,
+}
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
index 61f2a3dac..29fa4da17 100644
--- a/core/sys/windows/user32.odin
+++ b/core/sys/windows/user32.odin
@@ -38,6 +38,7 @@ foreign user32 {
DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
+ IsWindow :: proc(hWnd: HWND) -> BOOL ---
BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
@@ -51,6 +52,8 @@ foreign user32 {
TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
+ WaitMessage :: proc() -> BOOL ---
+
PeekMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
PeekMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT, wRemoveMsg: UINT) -> BOOL ---
@@ -109,6 +112,12 @@ foreign user32 {
GetDlgCtrlID :: proc(hWnd: HWND) -> c_int ---
GetDlgItem :: proc(hDlg: HWND, nIDDlgItem: c_int) -> HWND ---
+ CreatePopupMenu :: proc() -> HMENU ---
+ DestroyMenu :: proc(hMenu: HMENU) -> BOOL ---
+ AppendMenuW :: proc(hMenu: HMENU, uFlags: UINT, uIDNewItem: UINT_PTR, lpNewItem: LPCWSTR) -> BOOL ---
+ TrackPopupMenu :: proc(hMenu: HMENU, uFlags: UINT, x: int, y: int, nReserved: int, hWnd: HWND, prcRect: ^RECT) -> i32 ---
+ RegisterWindowMessageW :: proc(lpString: LPCWSTR) -> UINT ---
+
GetUpdateRect :: proc(hWnd: HWND, lpRect: LPRECT, bErase: BOOL) -> BOOL ---
ValidateRect :: proc(hWnd: HWND, lpRect: ^RECT) -> BOOL ---
InvalidateRect :: proc(hWnd: HWND, lpRect: ^RECT, bErase: BOOL) -> BOOL ---
@@ -123,6 +132,8 @@ foreign user32 {
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
+
+ GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
@@ -203,6 +214,20 @@ foreign user32 {
GetRawInputDeviceList :: proc(pRawInputDeviceList: PRAWINPUTDEVICELIST, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
GetRegisteredRawInputDevices :: proc(pRawInputDevices: PRAWINPUTDEVICE, puiNumDevices: PUINT, cbSize: UINT) -> UINT ---
RegisterRawInputDevices :: proc(pRawInputDevices: PCRAWINPUTDEVICE, uiNumDevices: UINT, cbSize: UINT) -> BOOL ---
+
+ SendInput :: proc(cInputs: UINT, pInputs: [^]INPUT, cbSize: c_int) -> UINT ---
+
+ SetLayeredWindowAttributes :: proc(hWnd: HWND, crKey: COLORREF, bAlpha: BYTE, dwFlags: DWORD) -> BOOL ---
+
+ FillRect :: proc(hDC: HDC, lprc: ^RECT, hbr: HBRUSH) -> int ---
+ EqualRect :: proc(lprc1: ^RECT, lprc2: ^RECT) -> BOOL ---
+
+ GetWindowInfo :: proc(hwnd: HWND, pwi: PWINDOWINFO) -> BOOL ---
+ GetWindowPlacement :: proc(hWnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
+ SetWindowPlacement :: proc(hwnd: HWND, lpwndpl: ^WINDOWPLACEMENT) -> BOOL ---
+ SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
+ CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
+ GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
}
CreateWindowW :: #force_inline proc "stdcall" (
@@ -433,3 +458,26 @@ RI_MOUSE_BUTTON_5_DOWN :: 0x0100
RI_MOUSE_BUTTON_5_UP :: 0x0200
RI_MOUSE_WHEEL :: 0x0400
RI_MOUSE_HWHEEL :: 0x0800
+
+WINDOWPLACEMENT :: struct {
+ length: UINT,
+ flags: UINT,
+ showCmd: UINT,
+ ptMinPosition: POINT,
+ ptMaxPosition: POINT,
+ rcNormalPosition: RECT,
+}
+
+WINDOWINFO :: struct {
+ cbSize: DWORD,
+ rcWindow: RECT,
+ rcClient: RECT,
+ dwStyle: DWORD,
+ dwExStyle: DWORD,
+ dwWindowStatus: DWORD,
+ cxWindowBorders: UINT,
+ cyWindowBorders: UINT,
+ atomWindowType: ATOM,
+ wCreatorVersion: WORD,
+}
+PWINDOWINFO :: ^WINDOWINFO
diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin
index 298588cb6..9c9d8f7b4 100644
--- a/core/sys/windows/util.odin
+++ b/core/sys/windows/util.odin
@@ -457,8 +457,8 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
// err := GetLastError();
// fmt.printf("GetLastError: %v\n", err);
}
- si := STARTUPINFO{}
- si.cb = size_of(STARTUPINFO)
+ si := STARTUPINFOW{}
+ si.cb = size_of(STARTUPINFOW)
pi := pi
ok = bool(CreateProcessAsUserW(
@@ -485,3 +485,24 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
return false
}
}
+
+ensure_winsock_initialized :: proc() {
+ @static gate := false
+ @static initted := false
+
+ if initted {
+ return
+ }
+
+ for intrinsics.atomic_compare_exchange_strong(&gate, false, true) {
+ intrinsics.cpu_relax()
+ }
+ defer intrinsics.atomic_store(&gate, false)
+
+ unused_info: WSADATA
+ version_requested := WORD(2) << 8 | 2
+ res := WSAStartup(version_requested, &unused_info)
+ assert(res == 0, "unable to initialized Winsock2")
+
+ initted = true
+}
diff --git a/core/sys/windows/ux_theme.odin b/core/sys/windows/ux_theme.odin
new file mode 100644
index 000000000..39b489bc0
--- /dev/null
+++ b/core/sys/windows/ux_theme.odin
@@ -0,0 +1,12 @@
+// +build windows
+package sys_windows
+
+foreign import uxtheme "system:UxTheme.lib"
+
+MARGINS :: distinct [4]int
+PMARGINS :: ^MARGINS
+
+@(default_calling_convention="stdcall")
+foreign uxtheme {
+ IsThemeActive :: proc() -> BOOL ---
+}
diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin
index 64ace19fc..445470f6e 100644
--- a/core/sys/windows/winmm.odin
+++ b/core/sys/windows/winmm.odin
@@ -3,9 +3,170 @@ package sys_windows
foreign import winmm "system:Winmm.lib"
+MMRESULT :: UINT
+
@(default_calling_convention="stdcall")
foreign winmm {
+ timeGetDevCaps :: proc(ptc: LPTIMECAPS, cbtc: UINT) -> MMRESULT ---
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
timeGetTime :: proc() -> DWORD ---
}
+
+LPTIMECAPS :: ^TIMECAPS
+TIMECAPS :: struct {
+ wPeriodMin: UINT,
+ wPeriodMax: UINT,
+}
+
+// String resource number bases (internal use)
+MMSYSERR_BASE :: 0
+WAVERR_BASE :: 32
+MIDIERR_BASE :: 64
+TIMERR_BASE :: 96
+JOYERR_BASE :: 160
+MCIERR_BASE :: 256
+MIXERR_BASE :: 1024
+
+MCI_STRING_OFFSET :: 512
+MCI_VD_OFFSET :: 1024
+MCI_CD_OFFSET :: 1088
+MCI_WAVE_OFFSET :: 1152
+MCI_SEQ_OFFSET :: 1216
+
+/* general error return values */
+MMSYSERR_NOERROR :: 0 /* no error */
+MMSYSERR_ERROR :: MMSYSERR_BASE + 1 /* unspecified error */
+MMSYSERR_BADDEVICEID :: MMSYSERR_BASE + 2 /* device ID out of range */
+MMSYSERR_NOTENABLED :: MMSYSERR_BASE + 3 /* driver failed enable */
+MMSYSERR_ALLOCATED :: MMSYSERR_BASE + 4 /* device already allocated */
+MMSYSERR_INVALHANDLE :: MMSYSERR_BASE + 5 /* device handle is invalid */
+MMSYSERR_NODRIVER :: MMSYSERR_BASE + 6 /* no device driver present */
+MMSYSERR_NOMEM :: MMSYSERR_BASE + 7 /* memory allocation error */
+MMSYSERR_NOTSUPPORTED :: MMSYSERR_BASE + 8 /* function isn't supported */
+MMSYSERR_BADERRNUM :: MMSYSERR_BASE + 9 /* error value out of range */
+MMSYSERR_INVALFLAG :: MMSYSERR_BASE + 10 /* invalid flag passed */
+MMSYSERR_INVALPARAM :: MMSYSERR_BASE + 11 /* invalid parameter passed */
+MMSYSERR_HANDLEBUSY :: MMSYSERR_BASE + 12 /* handle being used simultaneously on another thread (eg callback) */
+MMSYSERR_INVALIDALIAS :: MMSYSERR_BASE + 13 /* specified alias not found */
+MMSYSERR_BADDB :: MMSYSERR_BASE + 14 /* bad registry database */
+MMSYSERR_KEYNOTFOUND :: MMSYSERR_BASE + 15 /* registry key not found */
+MMSYSERR_READERROR :: MMSYSERR_BASE + 16 /* registry read error */
+MMSYSERR_WRITEERROR :: MMSYSERR_BASE + 17 /* registry write error */
+MMSYSERR_DELETEERROR :: MMSYSERR_BASE + 18 /* registry delete error */
+MMSYSERR_VALNOTFOUND :: MMSYSERR_BASE + 19 /* registry value not found */
+MMSYSERR_NODRIVERCB :: MMSYSERR_BASE + 20 /* driver does not call DriverCallback */
+MMSYSERR_MOREDATA :: MMSYSERR_BASE + 21 /* more data to be returned */
+MMSYSERR_LASTERROR :: MMSYSERR_BASE + 21 /* last error in range */
+
+/* waveform audio error return values */
+WAVERR_BADFORMAT :: WAVERR_BASE + 0 /* unsupported wave format */
+WAVERR_STILLPLAYING :: WAVERR_BASE + 1 /* still something playing */
+WAVERR_UNPREPARED :: WAVERR_BASE + 2 /* header not prepared */
+WAVERR_SYNC :: WAVERR_BASE + 3 /* device is synchronous */
+WAVERR_LASTERROR :: WAVERR_BASE + 3 /* last error in range */
+
+/* MIDI error return values */
+MIDIERR_UNPREPARED :: MIDIERR_BASE + 0 /* header not prepared */
+MIDIERR_STILLPLAYING :: MIDIERR_BASE + 1 /* still something playing */
+MIDIERR_NOMAP :: MIDIERR_BASE + 2 /* no configured instruments */
+MIDIERR_NOTREADY :: MIDIERR_BASE + 3 /* hardware is still busy */
+MIDIERR_NODEVICE :: MIDIERR_BASE + 4 /* port no longer connected */
+MIDIERR_INVALIDSETUP :: MIDIERR_BASE + 5 /* invalid MIF */
+MIDIERR_BADOPENMODE :: MIDIERR_BASE + 6 /* operation unsupported w/ open mode */
+MIDIERR_DONT_CONTINUE :: MIDIERR_BASE + 7 /* thru device 'eating' a message */
+MIDIERR_LASTERROR :: MIDIERR_BASE + 7 /* last error in range */
+
+/* timer error return values */
+TIMERR_NOERROR :: 0 /* no error */
+TIMERR_NOCANDO :: TIMERR_BASE + 1 /* request not completed */
+TIMERR_STRUCT :: TIMERR_BASE + 33 /* time struct size */
+
+/* joystick error return values */
+JOYERR_NOERROR :: 0 /* no error */
+JOYERR_PARMS :: JOYERR_BASE + 5 /* bad parameters */
+JOYERR_NOCANDO :: JOYERR_BASE + 6 /* request not completed */
+JOYERR_UNPLUGGED :: JOYERR_BASE + 7 /* joystick is unplugged */
+
+/* MCI error return values */
+MCIERR_INVALID_DEVICE_ID :: MCIERR_BASE + 1
+MCIERR_UNRECOGNIZED_KEYWORD :: MCIERR_BASE + 3
+MCIERR_UNRECOGNIZED_COMMAND :: MCIERR_BASE + 5
+MCIERR_HARDWARE :: MCIERR_BASE + 6
+MCIERR_INVALID_DEVICE_NAME :: MCIERR_BASE + 7
+MCIERR_OUT_OF_MEMORY :: MCIERR_BASE + 8
+MCIERR_DEVICE_OPEN :: MCIERR_BASE + 9
+MCIERR_CANNOT_LOAD_DRIVER :: MCIERR_BASE + 10
+MCIERR_MISSING_COMMAND_STRING :: MCIERR_BASE + 11
+MCIERR_PARAM_OVERFLOW :: MCIERR_BASE + 12
+MCIERR_MISSING_STRING_ARGUMENT :: MCIERR_BASE + 13
+MCIERR_BAD_INTEGER :: MCIERR_BASE + 14
+MCIERR_PARSER_INTERNAL :: MCIERR_BASE + 15
+MCIERR_DRIVER_INTERNAL :: MCIERR_BASE + 16
+MCIERR_MISSING_PARAMETER :: MCIERR_BASE + 17
+MCIERR_UNSUPPORTED_FUNCTION :: MCIERR_BASE + 18
+MCIERR_FILE_NOT_FOUND :: MCIERR_BASE + 19
+MCIERR_DEVICE_NOT_READY :: MCIERR_BASE + 20
+MCIERR_INTERNAL :: MCIERR_BASE + 21
+MCIERR_DRIVER :: MCIERR_BASE + 22
+MCIERR_CANNOT_USE_ALL :: MCIERR_BASE + 23
+MCIERR_MULTIPLE :: MCIERR_BASE + 24
+MCIERR_EXTENSION_NOT_FOUND :: MCIERR_BASE + 25
+MCIERR_OUTOFRANGE :: MCIERR_BASE + 26
+MCIERR_FLAGS_NOT_COMPATIBLE :: MCIERR_BASE + 28
+MCIERR_FILE_NOT_SAVED :: MCIERR_BASE + 30
+MCIERR_DEVICE_TYPE_REQUIRED :: MCIERR_BASE + 31
+MCIERR_DEVICE_LOCKED :: MCIERR_BASE + 32
+MCIERR_DUPLICATE_ALIAS :: MCIERR_BASE + 33
+MCIERR_BAD_CONSTANT :: MCIERR_BASE + 34
+MCIERR_MUST_USE_SHAREABLE :: MCIERR_BASE + 35
+MCIERR_MISSING_DEVICE_NAME :: MCIERR_BASE + 36
+MCIERR_BAD_TIME_FORMAT :: MCIERR_BASE + 37
+MCIERR_NO_CLOSING_QUOTE :: MCIERR_BASE + 38
+MCIERR_DUPLICATE_FLAGS :: MCIERR_BASE + 39
+MCIERR_INVALID_FILE :: MCIERR_BASE + 40
+MCIERR_NULL_PARAMETER_BLOCK :: MCIERR_BASE + 41
+MCIERR_UNNAMED_RESOURCE :: MCIERR_BASE + 42
+MCIERR_NEW_REQUIRES_ALIAS :: MCIERR_BASE + 43
+MCIERR_NOTIFY_ON_AUTO_OPEN :: MCIERR_BASE + 44
+MCIERR_NO_ELEMENT_ALLOWED :: MCIERR_BASE + 45
+MCIERR_NONAPPLICABLE_FUNCTION :: MCIERR_BASE + 46
+MCIERR_ILLEGAL_FOR_AUTO_OPEN :: MCIERR_BASE + 47
+MCIERR_FILENAME_REQUIRED :: MCIERR_BASE + 48
+MCIERR_EXTRA_CHARACTERS :: MCIERR_BASE + 49
+MCIERR_DEVICE_NOT_INSTALLED :: MCIERR_BASE + 50
+MCIERR_GET_CD :: MCIERR_BASE + 51
+MCIERR_SET_CD :: MCIERR_BASE + 52
+MCIERR_SET_DRIVE :: MCIERR_BASE + 53
+MCIERR_DEVICE_LENGTH :: MCIERR_BASE + 54
+MCIERR_DEVICE_ORD_LENGTH :: MCIERR_BASE + 55
+MCIERR_NO_INTEGER :: MCIERR_BASE + 56
+MCIERR_WAVE_OUTPUTSINUSE :: MCIERR_BASE + 64
+MCIERR_WAVE_SETOUTPUTINUSE :: MCIERR_BASE + 65
+MCIERR_WAVE_INPUTSINUSE :: MCIERR_BASE + 66
+MCIERR_WAVE_SETINPUTINUSE :: MCIERR_BASE + 67
+MCIERR_WAVE_OUTPUTUNSPECIFIED :: MCIERR_BASE + 68
+MCIERR_WAVE_INPUTUNSPECIFIED :: MCIERR_BASE + 69
+MCIERR_WAVE_OUTPUTSUNSUITABLE :: MCIERR_BASE + 70
+MCIERR_WAVE_SETOUTPUTUNSUITABLE :: MCIERR_BASE + 71
+MCIERR_WAVE_INPUTSUNSUITABLE :: MCIERR_BASE + 72
+MCIERR_WAVE_SETINPUTUNSUITABLE :: MCIERR_BASE + 73
+MCIERR_SEQ_DIV_INCOMPATIBLE :: MCIERR_BASE + 80
+MCIERR_SEQ_PORT_INUSE :: MCIERR_BASE + 81
+MCIERR_SEQ_PORT_NONEXISTENT :: MCIERR_BASE + 82
+MCIERR_SEQ_PORT_MAPNODEVICE :: MCIERR_BASE + 83
+MCIERR_SEQ_PORT_MISCERROR :: MCIERR_BASE + 84
+MCIERR_SEQ_TIMER :: MCIERR_BASE + 85
+MCIERR_SEQ_PORTUNSPECIFIED :: MCIERR_BASE + 86
+MCIERR_SEQ_NOMIDIPRESENT :: MCIERR_BASE + 87
+MCIERR_NO_WINDOW :: MCIERR_BASE + 90
+MCIERR_CREATEWINDOW :: MCIERR_BASE + 91
+MCIERR_FILE_READ :: MCIERR_BASE + 92
+MCIERR_FILE_WRITE :: MCIERR_BASE + 93
+MCIERR_NO_IDENTITY :: MCIERR_BASE + 94
+
+/* MMRESULT error return values specific to the mixer API */
+MIXERR_INVALLINE :: (MIXERR_BASE + 0)
+MIXERR_INVALCONTROL :: (MIXERR_BASE + 1)
+MIXERR_INVALVALUE :: (MIXERR_BASE + 2)
+MIXERR_LASTERROR :: (MIXERR_BASE + 2)
\ No newline at end of file
diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin
index 09af86bce..631ef4241 100644
--- a/core/sys/windows/ws2_32.odin
+++ b/core/sys/windows/ws2_32.odin
@@ -1,18 +1,78 @@
// +build windows
package sys_windows
-foreign import ws2_32 "system:Ws2_32.lib"
+// Define flags to be used with the WSAAsyncSelect() call.
+FD_READ :: 0x01
+FD_WRITE :: 0x02
+FD_OOB :: 0x04
+FD_ACCEPT :: 0x08
+FD_CONNECT :: 0x10
+FD_CLOSE :: 0x20
+FD_MAX_EVENTS :: 10
+INADDR_LOOPBACK :: 0x7f000001
+
+// Event flag definitions for WSAPoll().
+POLLRDNORM :: 0x0100
+POLLRDBAND :: 0x0200
+POLLIN :: (POLLRDNORM | POLLRDBAND)
+POLLPRI :: 0x0400
+POLLWRNORM :: 0x0010
+POLLOUT :: (POLLWRNORM)
+POLLWRBAND :: 0x0020
+POLLERR :: 0x0001
+POLLHUP :: 0x0002
+POLLNVAL :: 0x0004
+
+WSA_POLLFD::struct{
+ fd:SOCKET,
+ events:c_short,
+ revents:c_short,
+}
+
+WSANETWORKEVENTS :: struct {
+ lNetworkEvents: c_long,
+ iErrorCode: [FD_MAX_EVENTS]c_int,
+}
+
+WSAEVENT :: HANDLE
+
+WSAID_ACCEPTEX :: GUID{0xb5367df1, 0xcbac, 0x11cf, {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+WSAID_GETACCEPTEXSOCKADDRS :: GUID{0xb5367df2, 0xcbac, 0x11cf, {0x95, 0xca, 0x00, 0x80, 0x5f, 0x48, 0xa1, 0x92}}
+SIO_GET_EXTENSION_FUNCTION_POINTER :: IOC_INOUT | IOC_WS2 | 6
+IOC_OUT :: 0x40000000
+IOC_IN :: 0x80000000
+IOC_INOUT :: (IOC_IN | IOC_OUT)
+IOC_WS2 :: 0x08000000
+/*
+Example Load:
+ load_accept_ex :: proc(listener: SOCKET, fn_acceptex: rawptr) {
+ bytes: u32
+ guid_accept_ex := WSAID_ACCEPTEX
+ rc := WSAIoctl(listener, SIO_GET_EXTENSION_FUNCTION_POINTER, &guid_accept_ex, size_of(guid_accept_ex),
+ fn_acceptex, size_of(fn_acceptex), &bytes, nil, nil,)
+ assert(rc != windows.SOCKET_ERROR)
+ }
+*/
+
+foreign import ws2_32 "system:Ws2_32.lib"
@(default_calling_convention="stdcall")
foreign ws2_32 {
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsastartup)
WSAStartup :: proc(wVersionRequested: WORD, lpWSAData: LPWSADATA) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsacleanup)
WSACleanup :: proc() -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsagetlasterror)
WSAGetLastError :: proc() -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll)
+ WSAPoll :: proc(fdArray: ^WSA_POLLFD, fds: c_ulong, timeout: c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaduplicatesocketw)
WSADuplicateSocketW :: proc(
s: SOCKET,
dwProcessId: DWORD,
lpProtocolInfo: LPWSAPROTOCOL_INFO,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasend)
WSASend :: proc(
s: SOCKET,
lpBuffers: LPWSABUF,
@@ -22,6 +82,7 @@ foreign ws2_32 {
lpOverlapped: LPWSAOVERLAPPED,
lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecv)
WSARecv :: proc(
s: SOCKET,
lpBuffers: LPWSABUF,
@@ -31,6 +92,7 @@ foreign ws2_32 {
lpOverlapped: LPWSAOVERLAPPED,
lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsasocketw)
WSASocketW :: proc(
af: c_int,
kind: c_int,
@@ -39,35 +101,55 @@ foreign ws2_32 {
g: GROUP,
dwFlags: DWORD,
) -> SOCKET ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaioctl)
+ WSAIoctl :: proc(s: SOCKET, dwIoControlCode: DWORD, lpvInBuffer: rawptr, cbInBuffer: DWORD, lpvOutBuffer: rawptr, cbOutBuffer: DWORD, lpcbBytesReturned: ^DWORD, lpOverlapped: ^OVERLAPPED, lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaeventselect)
+ WSAEventSelect :: proc(s: SOCKET, hEventObject: WSAEVENT, lNetworkEvents: i32) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsawaitformultipleevents)
+ WSAWaitForMultipleEvents :: proc(cEvents: DWORD, lphEvents: ^WSAEVENT, fWaitAll: BOOL, dwTimeout: DWORD, fAlertable: BOOL) -> DWORD ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaenumnetworkevents)
+ WSAEnumNetworkEvents :: proc(s: SOCKET, hEventObject: WSAEVENT, lpNetworkEvents: ^WSANETWORKEVENTS) -> c_int ---
+ //[MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsagetoverlappedresult)
+ WSAGetOverlappedResult :: proc(s: SOCKET, lpOverlapped: ^OVERLAPPED, lpcbTransfer: ^DWORD, fWait: BOOL, lpdwFlags: ^DWORD) -> BOOL ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-socket)
socket :: proc(
af: c_int,
type: c_int,
protocol: c_int,
) -> SOCKET ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ioctlsocket)
ioctlsocket :: proc(s: SOCKET, cmd: c_long, argp: ^c_ulong) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-closesocket)
closesocket :: proc(socket: SOCKET) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv)
recv :: proc(socket: SOCKET, buf: rawptr, len: c_int, flags: c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-send)
send :: proc(socket: SOCKET, buf: rawptr, len: c_int, flags: c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-recvfrom)
recvfrom :: proc(
socket: SOCKET,
buf: rawptr,
len: c_int,
flags: c_int,
- addr: ^SOCKADDR,
+ addr: ^SOCKADDR_STORAGE_LH,
addrlen: ^c_int,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-sendto)
sendto :: proc(
socket: SOCKET,
buf: rawptr,
len: c_int,
flags: c_int,
- addr: ^SOCKADDR,
+ addr: ^SOCKADDR_STORAGE_LH,
addrlen: c_int,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-shutdown)
shutdown :: proc(socket: SOCKET, how: c_int) -> c_int ---
- accept :: proc(socket: SOCKET, address: ^SOCKADDR, address_len: ^c_int) -> SOCKET ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-accept)
+ accept :: proc(socket: SOCKET, address: ^SOCKADDR_STORAGE_LH, address_len: ^c_int) -> SOCKET ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-setsockopt)
setsockopt :: proc(
s: SOCKET,
level: c_int,
@@ -75,19 +157,28 @@ foreign ws2_32 {
optval: rawptr,
optlen: c_int,
) -> c_int ---
- getsockname :: proc(socket: SOCKET, address: ^SOCKADDR, address_len: ^c_int) -> c_int ---
- getpeername :: proc(socket: SOCKET, address: ^SOCKADDR, address_len: ^c_int) -> c_int ---
- bind :: proc(socket: SOCKET, address: ^SOCKADDR, address_len: socklen_t) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockname)
+ getsockname :: proc(socket: SOCKET, address: ^SOCKADDR_STORAGE_LH, address_len: ^c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getpeername)
+ getpeername :: proc(socket: SOCKET, address: ^SOCKADDR_STORAGE_LH, address_len: ^c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-bind)
+ bind :: proc(socket: SOCKET, address: ^SOCKADDR_STORAGE_LH, address_len: socklen_t) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-listen)
listen :: proc(socket: SOCKET, backlog: c_int) -> c_int ---
- connect :: proc(socket: SOCKET, address: ^SOCKADDR, len: c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-connect)
+ connect :: proc(socket: SOCKET, address: ^SOCKADDR_STORAGE_LH, len: c_int) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfo)
getaddrinfo :: proc(
node: cstring,
service: cstring,
hints: ^ADDRINFOA,
res: ^^ADDRINFOA,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-freeaddrinfo)
freeaddrinfo :: proc(res: ^ADDRINFOA) ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-freeaddrinfoexw)
FreeAddrInfoExW :: proc(pAddrInfoEx: PADDRINFOEXW) ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ws2tcpip/nf-ws2tcpip-getaddrinfoexw)
GetAddrInfoExW :: proc(
pName: PCWSTR,
pServiceName: PCWSTR,
@@ -99,7 +190,7 @@ foreign ws2_32 {
lpOverlapped: LPOVERLAPPED,
lpCompletionRoutine: LPLOOKUPSERVICE_COMPLETION_ROUTINE,
lpHandle: LPHANDLE) -> INT ---
-
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-select)
select :: proc(
nfds: c_int,
readfds: ^fd_set,
@@ -107,6 +198,7 @@ foreign ws2_32 {
exceptfds: ^fd_set,
timeout: ^timeval,
) -> c_int ---
+ // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-getsockopt)
getsockopt :: proc(
s: SOCKET,
level: c_int,
@@ -114,5 +206,4 @@ foreign ws2_32 {
optval: ^c_char,
optlen: ^c_int,
) -> c_int ---
-
}
diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin
index 525eae685..17bcfce26 100644
--- a/core/testing/runner_windows.odin
+++ b/core/testing/runner_windows.odin
@@ -191,7 +191,7 @@ run_internal_test :: proc(t: ^T, it: Internal_Test) {
global_exception_handler = win32.AddVectoredExceptionHandler(0, exception_handler_proc)
context.assertion_failure_proc = proc(prefix, message: string, loc: runtime.Source_Code_Location) -> ! {
- errorf(t=global_current_t, format="%s %s", args={prefix, message}, loc=loc)
+ errorf(global_current_t, "%s %s", prefix, message, loc=loc)
intrinsics.trap()
}
diff --git a/core/testing/testing.odin b/core/testing/testing.odin
index 37f9fe4d9..0ceb58e33 100644
--- a/core/testing/testing.odin
+++ b/core/testing/testing.odin
@@ -46,15 +46,15 @@ errorf :: proc(t: ^T, format: string, args: ..any, loc := #caller_location) {
}
fail :: proc(t: ^T, loc := #caller_location) {
- error(t=t, args={"FAIL"}, loc=loc)
+ error(t, "FAIL", loc=loc)
t.error_count += 1
}
fail_now :: proc(t: ^T, msg := "", loc := #caller_location) {
if msg != "" {
- error(t=t, args={"FAIL:", msg}, loc=loc)
+ error(t, "FAIL:", msg, loc=loc)
} else {
- error(t=t, args={"FAIL"}, loc=loc)
+ error(t, "FAIL", loc=loc)
}
t.error_count += 1
if t._fail_now != nil {
@@ -84,14 +84,14 @@ cleanup :: proc(t: ^T, procedure: proc(rawptr), user_data: rawptr) {
expect :: proc(t: ^T, ok: bool, msg: string = "", loc := #caller_location) -> bool {
if !ok {
- error(t=t, args={msg}, loc=loc)
+ error(t, msg, loc=loc)
}
return ok
}
expect_value :: proc(t: ^T, value, expected: $T, loc := #caller_location) -> bool where intrinsics.type_is_comparable(T) {
ok := value == expected
if !ok {
- errorf(t=t, format="expected %v, got %v", args={expected, value}, loc=loc)
+ errorf(t, "expected %v, got %v", expected, value, loc=loc)
}
return ok
}
diff --git a/core/text/edit/text_edit.odin b/core/text/edit/text_edit.odin
index b53e2f8bc..8520ba674 100644
--- a/core/text/edit/text_edit.odin
+++ b/core/text/edit/text_edit.odin
@@ -113,15 +113,16 @@ set_text :: proc(s: ^State, text: string) {
}
-undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) {
+undo_state_push :: proc(s: ^State, undo: ^[dynamic]^Undo_State) -> mem.Allocator_Error {
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 := (^Undo_State)(mem.alloc(size_of(Undo_State) + len(text), align_of(Undo_State), s.undo_text_allocator) or_return)
item.selection = s.selection
item.len = len(text)
#no_bounds_check {
runtime.copy(item.text[:len(text)], text)
}
- append(undo, item)
+ append(undo, item) or_return
+ return nil
}
undo :: proc(s: ^State, undo, redo: ^[dynamic]^Undo_State) {
@@ -219,7 +220,7 @@ selection_delete :: proc(s: ^State) {
translate_position :: proc(s: ^State, pos: int, t: Translation) -> int {
is_continuation_byte :: proc(b: byte) -> bool {
- return b <= 0x80 && b < 0xc0
+ return b >= 0x80 && b < 0xc0
}
is_space :: proc(b: byte) -> bool {
return b == ' ' || b == '\t' || b == '\n'
@@ -410,4 +411,4 @@ perform_command :: proc(s: ^State, cmd: Command) {
case .Select_Line_Start: select_to(s, .Soft_Line_Start)
case .Select_Line_End: select_to(s, .Soft_Line_End)
}
-}
\ No newline at end of file
+}
diff --git a/core/text/match/strlib.odin b/core/text/match/strlib.odin
index b8c2861fa..654996bc7 100644
--- a/core/text/match/strlib.odin
+++ b/core/text/match/strlib.odin
@@ -266,6 +266,7 @@ match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error)
return INVALID, .Invalid_Pattern_Capture
}
+
schar, ssize := utf8_peek(ms.src[s:]) or_return
pchar, psize := utf8_peek(ms.pattern[p:]) or_return
@@ -274,9 +275,9 @@ match_balance :: proc(ms: ^Match_State, s, p: int) -> (unused: int, err: Error)
return INVALID, .OK
}
- s_begin := s
cont := 1
- s := s + ssize
+ s := s
+ s += ssize
begin := pchar
end, _ := utf8_peek(ms.pattern[p + psize:]) or_return
diff --git a/core/text/table/doc.odin b/core/text/table/doc.odin
new file mode 100644
index 000000000..9b5c1f932
--- /dev/null
+++ b/core/text/table/doc.odin
@@ -0,0 +1,100 @@
+/*
+ package table implements ascii/markdown/html/custom rendering of tables.
+
+ ---
+
+ Custom rendering example:
+
+ ```odin
+ tbl := init(&Table{})
+ padding(tbl, 0, 1)
+ row(tbl, "A_LONG_ENUM", "= 54,", "// A comment about A_LONG_ENUM")
+ row(tbl, "AN_EVEN_LONGER_ENUM", "= 1,", "// A comment about AN_EVEN_LONGER_ENUM")
+ build(tbl)
+ for row in 0..
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ oskarnp: Initial implementation.
+*/
+
+package text_table
+
+import "core:io"
+import "core:fmt"
+import "core:mem"
+import "core:mem/virtual"
+import "core:runtime"
+
+Cell :: struct {
+ text: string,
+ alignment: Cell_Alignment,
+}
+
+Cell_Alignment :: enum {
+ Left,
+ Center,
+ Right,
+}
+
+Table :: struct {
+ lpad, rpad: int, // Cell padding (left/right)
+ cells: [dynamic]Cell,
+ caption: string,
+ nr_rows, nr_cols: int,
+ has_header_row: bool,
+ table_allocator: runtime.Allocator, // Used for allocating cells/colw
+ format_allocator: runtime.Allocator, // Used for allocating Cell.text when applicable
+
+ dirty: bool, // True if build() needs to be called before rendering
+
+ // The following are computed on build()
+ colw: [dynamic]int, // Width of each column (including padding, excluding borders)
+ tblw: int, // Width of entire table (including padding, excluding borders)
+}
+
+init :: proc{init_with_allocator, init_with_virtual_arena, init_with_mem_arena}
+
+init_with_allocator :: proc(tbl: ^Table, format_allocator := context.temp_allocator, table_allocator := context.allocator) -> ^Table {
+ tbl.table_allocator = table_allocator
+ tbl.cells = make([dynamic]Cell, tbl.table_allocator)
+ tbl.colw = make([dynamic]int, tbl.table_allocator)
+ tbl.format_allocator = format_allocator
+ return tbl
+}
+init_with_virtual_arena :: proc(tbl: ^Table, format_arena: ^virtual.Arena, table_allocator := context.allocator) -> ^Table {
+ return init_with_allocator(tbl, virtual.arena_allocator(format_arena), table_allocator)
+}
+init_with_mem_arena :: proc(tbl: ^Table, format_arena: ^mem.Arena, table_allocator := context.allocator) -> ^Table {
+ return init_with_allocator(tbl, mem.arena_allocator(format_arena), table_allocator)
+}
+
+destroy :: proc(tbl: ^Table) {
+ free_all(tbl.format_allocator)
+ delete(tbl.cells)
+ delete(tbl.colw)
+}
+
+caption :: proc(tbl: ^Table, value: string) {
+ tbl.caption = value
+ tbl.dirty = true
+}
+
+padding :: proc(tbl: ^Table, lpad, rpad: int) {
+ tbl.lpad = lpad
+ tbl.rpad = rpad
+ tbl.dirty = true
+}
+
+get_cell :: proc(tbl: ^Table, row, col: int, loc := #caller_location) -> ^Cell {
+ assert(col >= 0 && col < tbl.nr_cols, "cell column out of range", loc)
+ assert(row >= 0 && row < tbl.nr_rows, "cell row out of range", loc)
+ resize(&tbl.cells, tbl.nr_cols * tbl.nr_rows)
+ return &tbl.cells[row*tbl.nr_cols + col]
+}
+
+set_cell_value_and_alignment :: proc(tbl: ^Table, row, col: int, value: string, alignment: Cell_Alignment) {
+ cell := get_cell(tbl, row, col)
+ cell.text = format(tbl, "%v", value)
+ cell.alignment = alignment
+ tbl.dirty = true
+}
+
+set_cell_value :: proc(tbl: ^Table, row, col: int, value: any, loc := #caller_location) {
+ cell := get_cell(tbl, row, col, loc)
+ switch val in value {
+ case nil:
+ cell.text = ""
+ case string:
+ cell.text = string(val)
+ case cstring:
+ cell.text = string(val)
+ case:
+ cell.text = format(tbl, "%v", val)
+ if cell.text == "" {
+ fmt.eprintf("{} text/table: format() resulted in empty string (arena out of memory?)\n", loc)
+ }
+ }
+ tbl.dirty = true
+}
+
+set_cell_alignment :: proc(tbl: ^Table, row, col: int, alignment: Cell_Alignment, loc := #caller_location) {
+ cell := get_cell(tbl, row, col, loc)
+ cell.alignment = alignment
+ tbl.dirty = true
+}
+
+format :: proc(tbl: ^Table, _fmt: string, args: ..any, loc := #caller_location) -> string {
+ context.allocator = tbl.format_allocator
+ return fmt.aprintf(_fmt, ..args)
+}
+
+header :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {
+ if (tbl.has_header_row && tbl.nr_rows != 1) || (!tbl.has_header_row && tbl.nr_rows != 0) {
+ panic("Cannot add headers after rows have been added", loc)
+ }
+
+ if tbl.nr_rows == 0 {
+ tbl.nr_rows += 1
+ tbl.has_header_row = true
+ }
+
+ col := tbl.nr_cols
+ tbl.nr_cols += len(values)
+ for val in values {
+ set_cell_value(tbl, header_row(tbl), col, val, loc)
+ col += 1
+ }
+
+ tbl.dirty = true
+}
+
+row :: proc(tbl: ^Table, values: ..any, loc := #caller_location) {
+ if tbl.nr_cols == 0 {
+ if len(values) == 0 {
+ panic("Cannot create row without values unless knowing amount of columns in advance")
+ } else {
+ tbl.nr_cols = len(values)
+ }
+ }
+ tbl.nr_rows += 1
+ for col in 0.. int {
+ return tbl.nr_rows - 1
+}
+
+header_row :: proc(tbl: ^Table) -> int {
+ return 0 if tbl.has_header_row else -1
+}
+
+first_row :: proc(tbl: ^Table) -> int {
+ return header_row(tbl)+1 if tbl.has_header_row else 0
+}
+
+build :: proc(tbl: ^Table) {
+ tbl.dirty = false
+
+ resize(&tbl.colw, tbl.nr_cols)
+ mem.zero_slice(tbl.colw[:])
+
+ for row in 0.. tbl.colw[col] {
+ tbl.colw[col] = w
+ }
+ }
+ }
+
+ colw_sum := 0
+ for v in tbl.colw {
+ colw_sum += v
+ }
+
+ tbl.tblw = max(colw_sum, len(tbl.caption) + tbl.lpad + tbl.rpad)
+
+ // Resize columns to match total width of table
+ remain := tbl.tblw-colw_sum
+ for col := 0; remain > 0; col = (col + 1) % tbl.nr_cols {
+ tbl.colw[col] += 1
+ remain -= 1
+ }
+
+ return
+}
+
+write_html_table :: proc(w: io.Writer, tbl: ^Table) {
+ if tbl.dirty {
+ build(tbl)
+ }
+
+ io.write_string(w, "\n")
+ if tbl.caption != "" {
+ io.write_string(w, "")
+ io.write_string(w, tbl.caption)
+ io.write_string(w, "\n")
+ }
+
+ align_attribute :: proc(cell: ^Cell) -> string {
+ switch cell.alignment {
+ case .Left: return ` align="left"`
+ case .Center: return ` align="center"`
+ case .Right: return ` align="right"`
+ }
+ unreachable()
+ }
+
+ if tbl.has_header_row {
+ io.write_string(w, "\n")
+ io.write_string(w, " \n")
+ for col in 0..")
+ io.write_string(w, cell.text)
+ io.write_string(w, "\n")
+ }
+ io.write_string(w, "
\n")
+ io.write_string(w, "\n")
+ }
+
+ io.write_string(w, "\n")
+ for row in 0..\n")
+ for col in 0..")
+ io.write_string(w, cell.text)
+ io.write_string(w, "\n")
+ }
+ io.write_string(w, " \n")
+ }
+ io.write_string(w, " \n")
+
+ io.write_string(w, "
\n")
+}
+
+write_ascii_table :: proc(w: io.Writer, tbl: ^Table) {
+ if tbl.dirty {
+ build(tbl)
+ }
+
+ write_caption_separator :: proc(w: io.Writer, tbl: ^Table) {
+ io.write_byte(w, '+')
+ write_byte_repeat(w, tbl.tblw + tbl.nr_cols - 1, '-')
+ io.write_byte(w, '+')
+ io.write_byte(w, '\n')
+ }
+
+ write_table_separator :: proc(w: io.Writer, tbl: ^Table) {
+ for col in 0.. io.Writer {
+ return io.to_writer(os.stream_from_handle(os.stdout))
+}
+
+strings_builder_writer :: proc(b: ^strings.Builder) -> io.Writer {
+ return strings.to_writer(b)
+}
diff --git a/core/thread/thread.odin b/core/thread/thread.odin
index 90230ae75..fd8e59a5d 100644
--- a/core/thread/thread.odin
+++ b/core/thread/thread.odin
@@ -10,16 +10,50 @@ Thread_Proc :: #type proc(^Thread)
MAX_USER_ARGUMENTS :: 8
+Thread_State :: enum u8 {
+ Started,
+ Joined,
+ Done,
+ Self_Cleanup,
+}
+
Thread :: struct {
using specific: Thread_Os_Specific,
+ flags: bit_set[Thread_State; u8],
id: int,
procedure: Thread_Proc,
+
+ /*
+ These are values that the user can set as they wish, after the thread has been created.
+ This data is easily available to the thread proc.
+
+ These fields can be assigned to directly.
+
+ Should be set after the thread is created, but before it is started.
+ */
data: rawptr,
user_index: int,
user_args: [MAX_USER_ARGUMENTS]rawptr,
- init_context: Maybe(runtime.Context),
+ /*
+ The context to be used as 'context' in the thread proc.
+ This field can be assigned to directly, after the thread has been created, but __before__ the thread has been started.
+ This field must not be changed after the thread has started.
+
+ NOTE: If you __don't__ set this, the temp allocator will be managed for you;
+ If you __do__ set this, then you're expected to handle whatever allocators you set, yourself.
+
+ IMPORTANT:
+ By default, the thread proc will get the same context as `main()` gets.
+ In this situation, the thread will get a new temporary allocator which will be cleaned up when the thread dies.
+ ***This does NOT happen when you set `init_context`.***
+ This means that if you set `init_context`, but still have the `temp_allocator` field set to the default temp allocator,
+ then you'll need to call `runtime.default_temp_allocator_destroy(auto_cast the_thread.init_context.temp_allocator.data)` manually,
+ in order to prevent any memory leaks.
+ This call ***must*** be done ***in the thread proc*** because the default temporary allocator uses thread local state!
+ */
+ init_context: Maybe(runtime.Context),
creation_allocator: mem.Allocator,
}
@@ -32,6 +66,12 @@ Thread_Priority :: enum {
High,
}
+/*
+ Creates a thread in a suspended state with the given priority.
+ To start the thread, call `thread.start()`.
+
+ See `thread.create_and_start()`.
+*/
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
return _create(procedure, priority)
}
@@ -68,125 +108,46 @@ yield :: proc() {
run :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
- thread_proc :: proc(t: ^Thread) {
- fn := cast(proc())t.data
- fn()
- destroy(t)
- }
- t := create(thread_proc, priority)
- t.data = rawptr(fn)
- t.init_context = init_context
- start(t)
+ create_and_start(fn, init_context, priority, true)
}
run_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) {
- thread_proc :: proc(t: ^Thread) {
- fn := cast(proc(rawptr))t.data
- assert(t.user_index >= 1)
- data := t.user_args[0]
- fn(data)
- destroy(t)
- }
- t := create(thread_proc, priority)
- t.data = rawptr(fn)
- t.user_index = 1
- t.user_args = data
- t.init_context = init_context
- start(t)
+ create_and_start_with_data(data, fn, init_context, priority, true)
}
run_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T) <= size_of(rawptr) {
- thread_proc :: proc(t: ^Thread) {
- fn := cast(proc(T))t.data
- assert(t.user_index >= 1)
- data := (^T)(&t.user_args[0])^
- fn(data)
- destroy(t)
- }
- t := create(thread_proc, priority)
- t.data = rawptr(fn)
- t.user_index = 1
- data := data
- mem.copy(&t.user_args[0], &data, size_of(data))
- t.init_context = init_context
- start(t)
+ create_and_start_with_poly_data(data, fn, init_context, priority, true)
}
run_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr) {
- thread_proc :: proc(t: ^Thread) {
- fn := cast(proc(T1, T2))t.data
- assert(t.user_index >= 2)
- arg1 := (^T1)(&t.user_args[0])^
- arg2 := (^T2)(&t.user_args[1])^
- fn(arg1, arg2)
- destroy(t)
- }
- t := create(thread_proc, priority)
- t.data = rawptr(fn)
- t.user_index = 2
- arg1, arg2 := arg1, arg2
- mem.copy(&t.user_args[0], &arg1, size_of(arg1))
- mem.copy(&t.user_args[1], &arg2, size_of(arg2))
- t.init_context = init_context
- start(t)
+ create_and_start_with_poly_data2(arg1, arg2, fn, init_context, priority, true)
}
run_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
- thread_proc :: proc(t: ^Thread) {
- fn := cast(proc(T1, T2, T3))t.data
- assert(t.user_index >= 3)
- arg1 := (^T1)(&t.user_args[0])^
- arg2 := (^T2)(&t.user_args[1])^
- arg3 := (^T3)(&t.user_args[2])^
- fn(arg1, arg2, arg3)
- destroy(t)
- }
- t := create(thread_proc, priority)
- t.data = rawptr(fn)
- t.user_index = 3
- arg1, arg2, arg3 := arg1, arg2, arg3
- mem.copy(&t.user_args[0], &arg1, size_of(arg1))
- mem.copy(&t.user_args[1], &arg2, size_of(arg2))
- mem.copy(&t.user_args[2], &arg3, size_of(arg3))
- t.init_context = init_context
- start(t)
+ create_and_start_with_poly_data3(arg1, arg2, arg3, fn, init_context, priority, true)
}
run_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal)
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
- thread_proc :: proc(t: ^Thread) {
- fn := cast(proc(T1, T2, T3, T4))t.data
- assert(t.user_index >= 4)
- arg1 := (^T1)(&t.user_args[0])^
- arg2 := (^T2)(&t.user_args[1])^
- arg3 := (^T3)(&t.user_args[2])^
- arg4 := (^T4)(&t.user_args[3])^
- fn(arg1, arg2, arg3, arg4)
- destroy(t)
- }
- t := create(thread_proc, priority)
- t.data = rawptr(fn)
- t.user_index = 4
- arg1, arg2, arg3, arg4 := arg1, arg2, arg3, arg4
- mem.copy(&t.user_args[0], &arg1, size_of(arg1))
- mem.copy(&t.user_args[1], &arg2, size_of(arg2))
- mem.copy(&t.user_args[2], &arg3, size_of(arg3))
- mem.copy(&t.user_args[3], &arg4, size_of(arg4))
- t.init_context = init_context
- start(t)
+ create_and_start_with_poly_data4(arg1, arg2, arg3, arg4, fn, init_context, priority, true)
}
-
-create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
- t := create(fn, priority)
+create_and_start :: proc(fn: proc(), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
+ thread_proc :: proc(t: ^Thread) {
+ fn := cast(proc())t.data
+ fn()
+ }
+ t := create(thread_proc, priority)
+ t.data = rawptr(fn)
+ if self_cleanup do t.flags += {.Self_Cleanup}
t.init_context = init_context
start(t)
return t
@@ -195,7 +156,7 @@ create_and_start :: proc(fn: Thread_Proc, init_context: Maybe(runtime.Context) =
-create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread {
+create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(rawptr))t.data
assert(t.user_index >= 1)
@@ -206,12 +167,13 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co
t.data = rawptr(fn)
t.user_index = 1
t.user_args = data
+ if self_cleanup do t.flags += {.Self_Cleanup}
t.init_context = init_context
start(t)
return t
}
-create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread
+create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
where size_of(T) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) {
fn := cast(proc(T))t.data
@@ -224,12 +186,13 @@ create_and_start_with_poly_data :: proc(data: $T, fn: proc(data: T), init_contex
t.user_index = 1
data := data
mem.copy(&t.user_args[0], &data, size_of(data))
+ if self_cleanup do t.flags += {.Self_Cleanup}
t.init_context = init_context
start(t)
return t
}
-create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread
+create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr) {
thread_proc :: proc(t: ^Thread) {
@@ -245,12 +208,13 @@ create_and_start_with_poly_data2 :: proc(arg1: $T1, arg2: $T2, fn: proc(T1, T2),
arg1, arg2 := arg1, arg2
mem.copy(&t.user_args[0], &arg1, size_of(arg1))
mem.copy(&t.user_args[1], &arg2, size_of(arg2))
+ if self_cleanup do t.flags += {.Self_Cleanup}
t.init_context = init_context
start(t)
return t
}
-create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread
+create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: proc(arg1: T1, arg2: T2, arg3: T3), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
@@ -269,11 +233,12 @@ create_and_start_with_poly_data3 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, fn: pr
mem.copy(&t.user_args[0], &arg1, size_of(arg1))
mem.copy(&t.user_args[1], &arg2, size_of(arg2))
mem.copy(&t.user_args[2], &arg3, size_of(arg3))
+ if self_cleanup do t.flags += {.Self_Cleanup}
t.init_context = init_context
start(t)
return t
}
-create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal) -> ^Thread
+create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4: $T4, fn: proc(arg1: T1, arg2: T2, arg3: T3, arg4: T4), init_context: Maybe(runtime.Context) = nil, priority := Thread_Priority.Normal, self_cleanup := false) -> ^Thread
where size_of(T1) <= size_of(rawptr),
size_of(T2) <= size_of(rawptr),
size_of(T3) <= size_of(rawptr) {
@@ -294,7 +259,38 @@ create_and_start_with_poly_data4 :: proc(arg1: $T1, arg2: $T2, arg3: $T3, arg4:
mem.copy(&t.user_args[1], &arg2, size_of(arg2))
mem.copy(&t.user_args[2], &arg3, size_of(arg3))
mem.copy(&t.user_args[3], &arg4, size_of(arg4))
+ if self_cleanup do t.flags += {.Self_Cleanup}
t.init_context = init_context
start(t)
return t
}
+
+
+_select_context_for_thread :: proc(init_context: Maybe(runtime.Context)) -> runtime.Context {
+ ctx, ok := init_context.?
+ if !ok {
+ return runtime.default_context()
+ }
+
+ /*
+ NOTE(tetra, 2023-05-31):
+ Ensure that the temp allocator is thread-safe when the user provides a specific initial context to use.
+ Without this, the thread will use the same temp allocator state as the parent thread, and thus, bork it up.
+ */
+ if ctx.temp_allocator.procedure == runtime.default_temp_allocator_proc {
+ ctx.temp_allocator.data = &runtime.global_default_temp_allocator_data
+ }
+ return ctx
+}
+
+_maybe_destroy_default_temp_allocator :: proc(init_context: Maybe(runtime.Context)) {
+ if init_context != nil {
+ // NOTE(tetra, 2023-05-31): If the user specifies a custom context for the thread,
+ // then it's entirely up to them to handle whatever allocators they're using.
+ return
+ }
+
+ if context.temp_allocator.procedure == runtime.default_temp_allocator_proc {
+ runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
+ }
+}
diff --git a/core/thread/thread_js.odin b/core/thread/thread_js.odin
new file mode 100644
index 000000000..3c4935495
--- /dev/null
+++ b/core/thread/thread_js.odin
@@ -0,0 +1,47 @@
+//+build js
+package thread
+
+import "core:intrinsics"
+import "core:sync"
+import "core:mem"
+
+Thread_Os_Specific :: struct {}
+
+_thread_priority_map := [Thread_Priority]i32{
+ .Normal = 0,
+ .Low = -2,
+ .High = +2,
+}
+
+_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_start :: proc(t: ^Thread) {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_is_done :: proc(t: ^Thread) -> bool {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_join :: proc(t: ^Thread) {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_join_multiple :: proc(threads: ..^Thread) {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_destroy :: proc(thread: ^Thread) {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_terminate :: proc(using thread : ^Thread, exit_code: int) {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
+_yield :: proc() {
+ unimplemented("core:thread procedure not supported on js target")
+}
+
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index 8c7058f17..6e734a03a 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -2,32 +2,24 @@
// +private
package thread
-import "core:runtime"
import "core:intrinsics"
import "core:sync"
import "core:sys/unix"
CAS :: intrinsics.atomic_compare_exchange_strong
-Thread_State :: enum u8 {
- Started,
- Joined,
- Done,
-}
-
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
Thread_Os_Specific :: struct #align 16 {
unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
cond: sync.Cond,
mutex: sync.Mutex,
- flags: bit_set[Thread_State; u8],
}
//
// Creates a thread which will run the given procedure.
// It then waits for `start` to be called.
//
-_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
+_create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
t := (^Thread)(t)
@@ -36,8 +28,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_DISABLE, nil) == 0
}
- context = runtime.default_context()
-
sync.lock(&t.mutex)
t.id = sync.current_thread_id()
@@ -46,9 +36,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
sync.wait(&t.cond, &t.mutex)
}
- init_context := t.init_context
- context = init_context.? or_else runtime.default_context()
-
when ODIN_OS != .Darwin {
// Enable thread's cancelability.
if can_set_thread_cancel_state {
@@ -57,14 +44,27 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
}
}
- t.procedure(t)
+ {
+ init_context := t.init_context
+
+ // NOTE(tetra, 2023-05-31): Must do this AFTER thread.start() is called, so that the user can set the init_context, etc!
+ // Here on Unix, we start the OS thread in a running state, and so we manually have it wait on a condition
+ // variable above. We must perform that waiting BEFORE we select the context!
+ context = _select_context_for_thread(init_context)
+ defer _maybe_destroy_default_temp_allocator(init_context)
+
+ t.procedure(t)
+ }
intrinsics.atomic_store(&t.flags, t.flags + { .Done })
sync.unlock(&t.mutex)
- if init_context == nil && context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
- runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
+ if .Self_Cleanup in t.flags {
+ t.unix_thread = {}
+ // NOTE(ftphikari): It doesn't matter which context 'free' received, right?
+ context = {}
+ free(t, t.creation_allocator)
}
return nil
diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin
index 68382444c..0d004c8c3 100644
--- a/core/thread/thread_windows.odin
+++ b/core/thread/thread_windows.odin
@@ -2,22 +2,14 @@
//+private
package thread
-import "core:runtime"
import "core:intrinsics"
import "core:sync"
import win32 "core:sys/windows"
-Thread_State :: enum u8 {
- Started,
- Joined,
- Done,
-}
-
Thread_Os_Specific :: struct {
win32_thread: win32.HANDLE,
win32_thread_id: win32.DWORD,
mutex: sync.Mutex,
- flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
@@ -26,24 +18,36 @@ _thread_priority_map := [Thread_Priority]i32{
.High = +2,
}
-_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
+_create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
win32_thread_id: win32.DWORD
__windows_thread_entry_proc :: proc "stdcall" (t_: rawptr) -> win32.DWORD {
t := (^Thread)(t_)
- context = t.init_context.? or_else runtime.default_context()
-
+
t.id = sync.current_thread_id()
- t.procedure(t)
+ {
+ init_context := t.init_context
+
+ // NOTE(tetra, 2023-05-31): Must do this AFTER thread.start() is called, so that the user can set the init_context, etc!
+ // Here on Windows, the thread is created in a suspended state, and so we can select the context anywhere before the call
+ // to t.procedure().
+ context = _select_context_for_thread(init_context)
+ defer _maybe_destroy_default_temp_allocator(init_context)
+
+ t.procedure(t)
+ }
intrinsics.atomic_store(&t.flags, t.flags + {.Done})
- if t.init_context == nil {
- if context.temp_allocator.data == &runtime.global_default_temp_allocator_data {
- runtime.default_temp_allocator_destroy(auto_cast context.temp_allocator.data)
- }
+ if .Self_Cleanup in t.flags {
+ win32.CloseHandle(t.win32_thread)
+ t.win32_thread = win32.INVALID_HANDLE
+ // NOTE(ftphikari): It doesn't matter which context 'free' received, right?
+ context = {}
+ free(t, t.creation_allocator)
}
+
return 0
}
@@ -62,7 +66,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
thread.procedure = procedure
thread.win32_thread = win32_thread
thread.win32_thread_id = win32_thread_id
- thread.init_context = context
ok := win32.SetThreadPriority(win32_thread, _thread_priority_map[priority])
assert(ok == true)
diff --git a/core/time/perf.odin b/core/time/perf.odin
index 53406646f..87192093a 100644
--- a/core/time/perf.odin
+++ b/core/time/perf.odin
@@ -1,11 +1,11 @@
package time
import "core:runtime"
+import "core:intrinsics"
Tick :: struct {
_nsec: i64, // relative amount
}
-
tick_now :: proc "contextless" () -> Tick {
return _tick_now()
}
@@ -40,6 +40,59 @@ _tick_duration_end :: proc "contextless" (d: ^Duration, t: Tick) {
d^ = tick_since(t)
}
+when ODIN_ARCH == .amd64 {
+ @(private)
+ x86_has_invariant_tsc :: proc "contextless" () -> bool {
+ eax, _, _, _ := intrinsics.x86_cpuid(0x80_000_000, 0)
+
+ // Is this processor *really* ancient?
+ if eax < 0x80_000_007 {
+ return false
+ }
+
+ // check if the invariant TSC bit is set
+ _, _, _, edx := intrinsics.x86_cpuid(0x80_000_007, 0)
+ return (edx & (1 << 8)) != 0
+ }
+}
+
+when ODIN_OS != .Darwin && ODIN_OS != .Linux && ODIN_OS != .FreeBSD {
+ _get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ return 0, false
+ }
+}
+
+has_invariant_tsc :: proc "contextless" () -> bool {
+ when ODIN_ARCH == .amd64 {
+ return x86_has_invariant_tsc()
+ }
+
+ return false
+}
+
+tsc_frequency :: proc "contextless" (fallback_sleep := 2 * Second) -> (u64, bool) {
+ if !has_invariant_tsc() {
+ return 0, false
+ }
+
+ hz, ok := _get_tsc_frequency()
+ if !ok {
+ // fallback to approximate TSC
+ tsc_begin := intrinsics.read_cycle_counter()
+ tick_begin := tick_now()
+
+ sleep(fallback_sleep)
+
+ tsc_end := intrinsics.read_cycle_counter()
+ tick_end := tick_now()
+
+ time_diff := u128(duration_nanoseconds(tick_diff(tick_begin, tick_end)))
+ hz = u64((u128(tsc_end - tsc_begin) * 1_000_000_000) / time_diff)
+ }
+
+ return hz, true
+}
+
/*
Benchmark helpers
*/
@@ -94,4 +147,4 @@ benchmark :: proc(options: ^Benchmark_Options, allocator := context.allocator) -
options->teardown(allocator) or_return
}
return
-}
\ No newline at end of file
+}
diff --git a/core/time/time.odin b/core/time/time.odin
index 74c80c8f7..6c424a62e 100644
--- a/core/time/time.odin
+++ b/core/time/time.odin
@@ -170,6 +170,12 @@ day :: proc "contextless" (t: Time) -> (day: int) {
return
}
+weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
+ abs := _time_abs(t)
+ sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
+ return Weekday(int(sec) / SECONDS_PER_DAY)
+}
+
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
clock_from_time :: proc "contextless" (t: Time) -> (hour, min, sec: int) {
diff --git a/core/time/tsc_darwin.odin b/core/time/tsc_darwin.odin
new file mode 100644
index 000000000..9e54ee8f7
--- /dev/null
+++ b/core/time/tsc_darwin.odin
@@ -0,0 +1,21 @@
+//+private
+//+build darwin
+package time
+
+import "core:c"
+
+foreign import libc "System.framework"
+foreign libc {
+ @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
+}
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ tmp_freq : u64 = 0
+ tmp_size : i64 = size_of(tmp_freq)
+ ret := _sysctlbyname("machdep.tsc.frequency", &tmp_freq, &tmp_size, nil, 0)
+ if ret < 0 {
+ return 0, false
+ }
+
+ return tmp_freq, true
+}
diff --git a/core/time/tsc_freebsd.odin b/core/time/tsc_freebsd.odin
new file mode 100644
index 000000000..f4d6ccc3a
--- /dev/null
+++ b/core/time/tsc_freebsd.odin
@@ -0,0 +1,21 @@
+//+private
+//+build freebsd
+package time
+
+import "core:c"
+
+foreign import libc "system:c"
+foreign libc {
+ @(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
+}
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ tmp_freq : u64 = 0
+ tmp_size : i64 = size_of(tmp_freq)
+ ret := _sysctlbyname("machdep.tsc_freq", &tmp_freq, &tmp_size, nil, 0)
+ if ret < 0 {
+ return 0, false
+ }
+
+ return tmp_freq, true
+}
diff --git a/core/time/tsc_linux.odin b/core/time/tsc_linux.odin
new file mode 100644
index 000000000..c5f2902e9
--- /dev/null
+++ b/core/time/tsc_linux.odin
@@ -0,0 +1,35 @@
+//+private
+//+build linux
+package time
+
+import "core:intrinsics"
+import "core:sys/unix"
+
+_get_tsc_frequency :: proc "contextless" () -> (u64, bool) {
+ perf_attr := unix.Perf_Event_Attr{}
+ perf_attr.type = u32(unix.Perf_Type_Id.Hardware)
+ perf_attr.config = u64(unix.Perf_Hardware_Id.Instructions)
+ perf_attr.size = size_of(perf_attr)
+ perf_attr.flags = {.Disabled, .Exclude_Kernel, .Exclude_HV}
+ fd := unix.sys_perf_event_open(&perf_attr, 0, -1, -1, 0)
+ if fd == -1 {
+ return 0, false
+ }
+ defer unix.sys_close(fd)
+
+ page_size : uint = 4096
+ ret := unix.sys_mmap(nil, page_size, unix.PROT_READ, unix.MAP_SHARED, fd, 0)
+ if ret < 0 && ret > -4096 {
+ return 0, false
+ }
+ addr := rawptr(uintptr(ret))
+ defer unix.sys_munmap(addr, page_size)
+
+ event_page := (^unix.Perf_Event_mmap_Page)(addr)
+ if .User_Time not_in event_page.cap.flags {
+ return 0, false
+ }
+
+ frequency := u64((u128(1_000_000_000) << u128(event_page.time_shift)) / u128(event_page.time_mult))
+ return frequency, true
+}
diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin
index a0da5c5d1..3642b8078 100644
--- a/core/unicode/utf8/utf8.odin
+++ b/core/unicode/utf8/utf8.odin
@@ -10,6 +10,11 @@ UTF_MAX :: 4
SURROGATE_MIN :: 0xd800
SURROGATE_MAX :: 0xdfff
+// A high/leading surrogate is in range SURROGATE_MIN..SURROGATE_HIGH_MAX,
+// A low/trailing surrogate is in range SURROGATE_LOW_MIN..SURROGATE_MAX.
+SURROGATE_HIGH_MAX :: 0xdbff
+SURROGATE_LOW_MIN :: 0xdc00
+
T1 :: 0b0000_0000
TX :: 0b1000_0000
T2 :: 0b1100_0000
@@ -54,7 +59,7 @@ accept_sizes := [256]u8{
0xf5..=0xff = 0xf1,
}
-encode_rune :: proc(c: rune) -> ([4]u8, int) {
+encode_rune :: proc "contextless" (c: rune) -> ([4]u8, int) {
r := c
buf: [4]u8
@@ -95,10 +100,10 @@ decode_rune :: proc{
decode_rune_in_string,
decode_rune_in_bytes,
}
-decode_rune_in_string :: #force_inline proc(s: string) -> (rune, int) {
+decode_rune_in_string :: #force_inline proc "contextless" (s: string) -> (rune, int) {
return decode_rune_in_bytes(transmute([]u8)s)
}
-decode_rune_in_bytes :: proc(s: []u8) -> (rune, int) {
+decode_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) {
n := len(s)
if n < 1 {
return RUNE_ERROR, 0
@@ -135,7 +140,7 @@ decode_rune_in_bytes :: proc(s: []u8) -> (rune, int) {
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4
}
-string_to_runes :: proc(s: string, allocator := context.allocator) -> (runes: []rune) {
+string_to_runes :: proc "odin" (s: string, allocator := context.allocator) -> (runes: []rune) {
n := rune_count_in_string(s)
runes = make([]rune, n, allocator)
@@ -147,7 +152,7 @@ string_to_runes :: proc(s: string, allocator := context.allocator) -> (runes: []
return
}
-runes_to_string :: proc(runes: []rune, allocator := context.allocator) -> string {
+runes_to_string :: proc "odin" (runes: []rune, allocator := context.allocator) -> string {
byte_count := 0
for r in runes {
_, w := encode_rune(r)
@@ -171,10 +176,10 @@ decode_last_rune :: proc{
decode_last_rune_in_bytes,
}
-decode_last_rune_in_string :: #force_inline proc(s: string) -> (rune, int) {
+decode_last_rune_in_string :: #force_inline proc "contextless" (s: string) -> (rune, int) {
return decode_last_rune_in_bytes(transmute([]u8)s)
}
-decode_last_rune_in_bytes :: proc(s: []u8) -> (rune, int) {
+decode_last_rune_in_bytes :: proc "contextless" (s: []u8) -> (rune, int) {
r: rune
size: int
start, end, limit: int
@@ -206,7 +211,7 @@ decode_last_rune_in_bytes :: proc(s: []u8) -> (rune, int) {
return r, size
}
-rune_at_pos :: proc(s: string, pos: int) -> rune {
+rune_at_pos :: proc "contextless" (s: string, pos: int) -> rune {
if pos < 0 {
return RUNE_ERROR
}
@@ -221,7 +226,7 @@ rune_at_pos :: proc(s: string, pos: int) -> rune {
return RUNE_ERROR
}
-rune_string_at_pos :: proc(s: string, pos: int) -> string {
+rune_string_at_pos :: proc "contextless" (s: string, pos: int) -> string {
if pos < 0 {
return ""
}
@@ -237,14 +242,14 @@ rune_string_at_pos :: proc(s: string, pos: int) -> string {
return ""
}
-rune_at :: proc(s: string, byte_index: int) -> rune {
+rune_at :: proc "contextless" (s: string, byte_index: int) -> rune {
r, _ := decode_rune_in_string(s[byte_index:])
return r
}
// Returns the byte position of rune at position pos in s with an optional start byte position.
// Returns -1 if it runs out of the string.
-rune_offset :: proc(s: string, pos: int, start: int = 0) -> int {
+rune_offset :: proc "contextless" (s: string, pos: int, start: int = 0) -> int {
if pos < 0 {
return -1
}
@@ -259,7 +264,7 @@ rune_offset :: proc(s: string, pos: int, start: int = 0) -> int {
return -1
}
-valid_rune :: proc(r: rune) -> bool {
+valid_rune :: proc "contextless" (r: rune) -> bool {
if r < 0 {
return false
} else if SURROGATE_MIN <= r && r <= SURROGATE_MAX {
@@ -270,7 +275,7 @@ valid_rune :: proc(r: rune) -> bool {
return true
}
-valid_string :: proc(s: string) -> bool {
+valid_string :: proc "contextless" (s: string) -> bool {
n := len(s)
for i := 0; i < n; {
si := s[i]
@@ -303,7 +308,7 @@ valid_string :: proc(s: string) -> bool {
return true
}
-rune_start :: #force_inline proc(b: u8) -> bool {
+rune_start :: #force_inline proc "contextless" (b: u8) -> bool {
return b&0xc0 != 0x80
}
@@ -315,7 +320,7 @@ rune_count :: proc{
rune_count_in_string :: #force_inline proc(s: string) -> int {
return rune_count_in_bytes(transmute([]u8)s)
}
-rune_count_in_bytes :: proc(s: []u8) -> int {
+rune_count_in_bytes :: proc "contextless" (s: []u8) -> int {
count := 0
n := len(s)
@@ -354,7 +359,7 @@ rune_count_in_bytes :: proc(s: []u8) -> int {
}
-rune_size :: proc(r: rune) -> int {
+rune_size :: proc "contextless" (r: rune) -> int {
switch {
case r < 0: return -1
case r <= 1<<7 - 1: return 1
@@ -375,7 +380,7 @@ full_rune :: proc{
// full_rune_in_bytes reports if the bytes in b begin with a full utf-8 encoding of a rune or not
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
-full_rune_in_bytes :: proc(b: []byte) -> bool {
+full_rune_in_bytes :: proc "contextless" (b: []byte) -> bool {
n := len(b)
if n == 0 {
return false
@@ -395,7 +400,7 @@ full_rune_in_bytes :: proc(b: []byte) -> bool {
// full_rune_in_string reports if the bytes in s begin with a full utf-8 encoding of a rune or not
// An invalid encoding is considered a full rune since it will convert as an error rune of width 1 (RUNE_ERROR)
-full_rune_in_string :: proc(s: string) -> bool {
+full_rune_in_string :: proc "contextless" (s: string) -> bool {
return full_rune_in_bytes(transmute([]byte)s)
}
diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin
index 9f1865d46..60850c806 100644
--- a/examples/all/all_main.odin
+++ b/examples/all/all_main.odin
@@ -19,6 +19,8 @@ import priority_queue "core:container/priority_queue"
import queue "core:container/queue"
import small_array "core:container/small_array"
import lru "core:container/lru"
+import list "core:container/intrusive/list"
+import topological_sort "core:container/topological_sort"
import crypto "core:crypto"
import blake "core:crypto/blake"
@@ -48,7 +50,10 @@ import crypto_util "core:crypto/util"
import whirlpool "core:crypto/whirlpool"
import x25519 "core:crypto/x25519"
+import pe "core:debug/pe"
+
import dynlib "core:dynlib"
+import net "core:net"
import base32 "core:encoding/base32"
import base64 "core:encoding/base64"
@@ -57,9 +62,11 @@ import hxa "core:encoding/hxa"
import json "core:encoding/json"
import varint "core:encoding/varint"
import xml "core:encoding/xml"
+import endian "core:encoding/endian"
import fmt "core:fmt"
import hash "core:hash"
+import xxhash "core:hash/xxhash"
import image "core:image"
import netpbm "core:image/netpbm"
@@ -79,9 +86,10 @@ import glm "core:math/linalg/glsl"
import hlm "core:math/linalg/hlsl"
import noise "core:math/noise"
import rand "core:math/rand"
+import ease "core:math/ease"
import mem "core:mem"
-// import virtual "core:mem/virtual"
+import virtual "core:mem/virtual"
import ast "core:odin/ast"
import doc_format "core:odin/doc-format"
@@ -90,6 +98,8 @@ import odin_parser "core:odin/parser"
import odin_printer "core:odin/printer"
import odin_tokenizer "core:odin/tokenizer"
+import spall "core:prof/spall"
+
import os "core:os"
import slashpath "core:path/slashpath"
@@ -107,6 +117,9 @@ import sync "core:sync"
import testing "core:testing"
import scanner "core:text/scanner"
import i18n "core:text/i18n"
+import match "core:text/match"
+import table "core:text/table"
+import edit "core:text/edit"
import thread "core:thread"
import time "core:time"
@@ -133,6 +146,8 @@ _ :: priority_queue
_ :: queue
_ :: small_array
_ :: lru
+_ :: list
+_ :: topological_sort
_ :: crypto
_ :: blake
_ :: blake2b
@@ -160,7 +175,9 @@ _ :: tiger2
_ :: crypto_util
_ :: whirlpool
_ :: x25519
+_ :: pe
_ :: dynlib
+_ :: net
_ :: base32
_ :: base64
_ :: csv
@@ -168,8 +185,10 @@ _ :: hxa
_ :: json
_ :: varint
_ :: xml
+_ :: endian
_ :: fmt
_ :: hash
+_ :: xxhash
_ :: image
_ :: netpbm
_ :: png
@@ -186,7 +205,9 @@ _ :: glm
_ :: hlm
_ :: noise
_ :: rand
+_ :: ease
_ :: mem
+_ :: virtual
_ :: ast
_ :: doc_format
_ :: odin_format
@@ -194,6 +215,7 @@ _ :: odin_parser
_ :: odin_printer
_ :: odin_tokenizer
_ :: os
+_ :: spall
_ :: slashpath
_ :: filepath
_ :: reflect
@@ -208,10 +230,13 @@ _ :: sync
_ :: testing
_ :: scanner
_ :: i18n
+_ :: match
+_ :: table
+_ :: edit
_ :: thread
_ :: time
_ :: sysinfo
_ :: unicode
_ :: utf8
_ :: utf8string
-_ :: utf16
\ No newline at end of file
+_ :: utf16
diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin
index bd5921e6a..fe1080e20 100644
--- a/examples/all/all_vendor.odin
+++ b/examples/all/all_vendor.odin
@@ -1,7 +1,27 @@
package all
-import botan "vendor:botan"
+import botan_bindings "vendor:botan/bindings"
+import botan_blake2b "vendor:botan/blake2b"
+import gost "vendor:botan/gost"
+import keccak "vendor:botan/keccak"
+import md4 "vendor:botan/md4"
+import md5 "vendor:botan/md5"
+import ripemd "vendor:botan/ripemd"
+import sha1 "vendor:botan/sha1"
+import sha2 "vendor:botan/sha2"
+import sha3 "vendor:botan/sha3"
+import shake "vendor:botan/shake"
+import siphash "vendor:botan/siphash"
+import skein512 "vendor:botan/skein512"
+import sm3 "vendor:botan/sm3"
+import streebog "vendor:botan/streebog"
+import tiger "vendor:botan/tiger"
+import whirlpool "vendor:botan/whirlpool"
+
+import cgltf "vendor:cgltf"
+// import commonmark "vendor:commonmark"
import ENet "vendor:ENet"
+import exr "vendor:OpenEXRCore"
import ggpo "vendor:ggpo"
import gl "vendor:OpenGL"
import glfw "vendor:glfw"
@@ -9,8 +29,7 @@ import microui "vendor:microui"
import miniaudio "vendor:miniaudio"
import PM "vendor:portmidi"
import rl "vendor:raylib"
-import exr "vendor:OpenEXRCore"
-import cgltf "vendor:cgltf"
+import zlib "vendor:zlib"
import SDL "vendor:sdl2"
import SDLNet "vendor:sdl2/net"
@@ -22,10 +41,35 @@ import vk "vendor:vulkan"
import NS "vendor:darwin/Foundation"
import MTL "vendor:darwin/Metal"
+import MTK "vendor:darwin/MetalKit"
import CA "vendor:darwin/QuartzCore"
-_ :: botan
+// NOTE(bill): only one can be checked at a time
+import lua_5_4 "vendor:lua/5.4"
+
+_ :: botan_bindings
+_ :: botan_blake2b
+_ :: gost
+_ :: keccak
+_ :: md4
+_ :: md5
+_ :: ripemd
+_ :: sha1
+_ :: sha2
+_ :: sha3
+_ :: shake
+_ :: siphash
+_ :: skein512
+_ :: sm3
+_ :: streebog
+_ :: tiger
+_ :: whirlpool
+
+
+_ :: cgltf
+// _ :: commonmark
_ :: ENet
+_ :: exr
_ :: ggpo
_ :: gl
_ :: glfw
@@ -33,8 +77,7 @@ _ :: microui
_ :: miniaudio
_ :: PM
_ :: rl
-_ :: exr
-_ :: cgltf
+_ :: zlib
_ :: SDL
_ :: SDLNet
@@ -46,4 +89,7 @@ _ :: vk
_ :: NS
_ :: MTL
-_ :: CA
\ No newline at end of file
+_ :: MTK
+_ :: CA
+
+_ :: lua_5_4
\ No newline at end of file
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 457aa786a..7c98ca728 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -352,7 +352,7 @@ control_flow :: proc() {
if false {
f, err := os.open("my_file.txt")
- if err != 0 {
+ if err != os.ERROR_NONE {
// handle error
}
defer os.close(f)
@@ -1175,13 +1175,13 @@ threading_example :: proc() {
N :: 3
pool: thread.Pool
- thread.pool_init(pool=&pool, thread_count=N, allocator=context.allocator)
+ thread.pool_init(&pool, allocator=context.allocator, thread_count=N)
defer thread.pool_destroy(&pool)
for i in 0..<30 {
// be mindful of the allocator used for tasks. The allocator needs to be thread safe, or be owned by the task for exclusive use
- thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i, allocator=context.allocator)
+ thread.pool_add_task(&pool, allocator=context.allocator, procedure=task_proc, data=nil, user_index=i)
}
thread.pool_start(&pool)
@@ -2145,10 +2145,8 @@ or_return_operator :: proc() {
return -345 * z, zerr
}
- // If the other return values need to be set depending on what the end value is,
- // the 'defer if' idiom is can be used
defer if err != nil {
- n = -1
+ fmt.println("Error in", #procedure, ":" , err)
}
n = 123
diff --git a/misc/old_demos/demo001.odin b/misc/old_demos/demo001.odin
deleted file mode 100644
index a3aea1cb7..000000000
--- a/misc/old_demos/demo001.odin
+++ /dev/null
@@ -1,337 +0,0 @@
-import "core:fmt.odin";
-import "core:os.odin";
-import "core:mem.odin";
-// import "http_test.odin" as ht;
-// import "game.odin" as game;
-// import "punity.odin" as pn;
-
-main :: proc() {
- struct_padding();
- bounds_checking();
- type_introspection();
- any_type();
- crazy_introspection();
- namespaces_and_files();
- miscellany();
-
- /*
- ht.run();
- game.run();
- {
- init :: proc(c: ^pn.Core) {}
- step :: proc(c: ^pn.Core) {}
-
- pn.run(init, step);
- }
- */
-}
-
-struct_padding :: proc() {
- {
- A :: struct {
- a: u8,
- b: u32,
- c: u16,
- }
-
- B :: struct {
- a: [7]u8,
- b: [3]u16,
- c: u8,
- d: u16,
- }
-
- fmt.println("size_of(A):", size_of(A));
- fmt.println("size_of(B):", size_of(B));
-
- // n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
- }
- {
- A :: struct #ordered {
- a: u8,
- b: u32,
- c: u16,
- }
-
- B :: struct #ordered {
- a: [7]u8,
- b: [3]u16,
- c: u8,
- d: u16,
- }
-
- fmt.println("size_of(A):", size_of(A));
- fmt.println("size_of(B):", size_of(B));
-
- // C-style structure layout
- }
- {
- A :: struct #packed {
- a: u8,
- b: u32,
- c: u16,
- }
-
- B :: struct #packed {
- a: [7]u8,
- b: [3]u16,
- c: u8,
- d: u16,
- }
-
- fmt.println("size_of(A):", size_of(A));
- fmt.println("size_of(B):", size_of(B));
-
- // Useful for explicit layout
- }
-
- // Member sorting by priority
- // Alignment desc.
- // Size desc.
- // source order asc.
-
- /*
- A :: struct {
- a: u8
- b: u32
- c: u16
- }
-
- B :: struct {
- a: [7]u8
- b: [3]u16
- c: u8
- d: u16
- }
-
- Equivalent too
-
- A :: struct #ordered {
- b: u32
- c: u16
- a: u8
- }
-
- B :: struct #ordered {
- b: [3]u16
- d: u16
- a: [7]u8
- c: u8
- }
- */
-}
-
-bounds_checking :: proc() {
- x: [4]int;
- // x[-1] = 0; // Compile Time
- // x[4] = 0; // Compile Time
-
- {
- a, b := -1, 4;
- // x[a] = 0; // Runtime Time
- // x[b] = 0; // Runtime Time
- }
-
- // Works for arrays, strings, slices, and related procedures & operations
-
- {
- base: [10]int;
- s := base[2..6];
- a, b := -1, 6;
-
- #no_bounds_check {
- s[a] = 0;
- // #bounds_check s[b] = 0;
- }
-
- #no_bounds_check
- if s[a] == 0 {
- // Do whatever
- }
-
- // Bounds checking can be toggled explicit
- // on a per statement basis.
- // _any statement_
- }
-}
-
-type_introspection :: proc() {
- {
- info: ^Type_Info;
- x: int;
-
- info = type_info_of(int); // by type
- info = type_info_of(x); // by value
- // See: runtime.odin
-
- match i in info.variant {
- case Type_Info_Integer:
- fmt.println("integer!");
- case Type_Info_Float:
- fmt.println("float!");
- case:
- fmt.println("potato!");
- }
-
- // Unsafe cast
- integer_info := cast(^Type_Info_Integer)cast(rawptr)info;
- }
-
- {
- Vector2 :: struct { x, y: f32 }
- Vector3 :: struct { x, y, z: f32 }
-
- v1: Vector2;
- v2: Vector3;
- v3: Vector3;
-
- t1 := type_info_of(v1);
- t2 := type_info_of(v2);
- t3 := type_info_of(v3);
-
- fmt.println();
- fmt.print("Type of v1 is:\n\t", t1);
-
- fmt.println();
- fmt.print("Type of v2 is:\n\t", t2);
-
- fmt.println("\n");
- fmt.println("t1 == t2:", t1 == t2);
- fmt.println("t2 == t3:", t2 == t3);
- }
-}
-
-any_type :: proc() {
- a: any;
-
- x: int = 123;
- y: f64 = 6.28;
- z: string = "Yo-Yo Ma";
- // All types can be implicit cast to `any`
- a = x;
- a = y;
- a = z;
- a = a; // This the "identity" type, it doesn't get converted
-
- a = 123; // Literals are copied onto the stack first
-
- // any has two members
- // data - rawptr to the data
- // type_info - pointer to the type info
-
- fmt.println(x, y, z);
- // See: fmt.odin
- // For variadic any procedures in action
-}
-
-crazy_introspection :: proc() {
- {
- Fruit :: enum {
- APPLE,
- BANANA,
- GRAPE,
- MELON,
- PEACH,
- TOMATO,
- }
-
- s: string;
- // s = enum_to_string(Fruit.PEACH);
- fmt.println(s);
-
- f := Fruit.GRAPE;
- // s = enum_to_string(f);
- fmt.println(s);
-
- fmt.println(f);
- // See: runtime.odin
- }
-
-
- {
- // NOTE(bill): This is not safe code and I would not recommend this at all
- // I'd recommend you use `match type` to get the subtype rather than
- // casting pointers
-
- Fruit :: enum {
- APPLE,
- BANANA,
- GRAPE,
- MELON,
- PEACH,
- TOMATO,
- }
-
- fruit_ti := type_info_of(Fruit);
- name := fruit_ti.variant.(Type_Info_Named).name;
- info, _ := type_info_base(fruit_ti).variant.(Type_Info_Enum);
-
- fmt.printf("%s :: enum %T {\n", name, info.base);
- for _, i in info.values {
- fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
- }
- fmt.printf("}\n");
-
- // NOTE(bill): look at that type-safe printf!
- }
-
- {
- Vector3 :: struct {x, y, z: f32}
-
- a := Vector3{x = 1, y = 4, z = 9};
- fmt.println(a);
- b := Vector3{x = 9, y = 3, z = 1};
- fmt.println(b);
-
- // NOTE(bill): See fmt.odin
- }
-
- // n.b. This pretty much "solves" serialization (to strings)
-}
-
-// #import "test.odin"
-
-namespaces_and_files :: proc() {
-
- // test.thing()
- // test.format.println()
- // test.println()
- /*
- // Non-exporting import
- #import "file.odin"
- #import "file.odin" as file
- #import "file.odin" as .
- #import "file.odin" as _
-
- // Exporting import
- #include "file.odin"
- */
-
- // Talk about scope rules and diagram
-}
-
-miscellany :: proc() {
- /*
- win32 `__imp__` prefix
- #dll_import
- #dll_export
-
- Change exported name/symbol for linking
- #link_name
-
- Custom calling conventions
- #stdcall
- #fastcall
-
- Runtime stuff
- #shared_global_scope
- */
-
- // assert(false)
- // #assert(false)
- // panic("Panic message goes here")
-}
-
-
-
-
diff --git a/misc/old_demos/demo002.odin b/misc/old_demos/demo002.odin
deleted file mode 100644
index a790aadf3..000000000
--- a/misc/old_demos/demo002.odin
+++ /dev/null
@@ -1,879 +0,0 @@
-// Demo 002
-export "core:fmt.odin";
-export "core:math.odin";
-export "core:mem.odin";
-// export "game.odin"
-
-#thread_local tls_int: int;
-
-main :: proc() {
- // Forenotes
-
- // Semicolons are now optional
- // Rule for when a semicolon is expected after a statement
- // - If the next token is not on the same line
- // - if the next token is a closing brace }
- // - Otherwise, a semicolon is needed
- //
- // Expections:
- // for, if, match
- // if x := thing(); x < 123 {}
- // for i := 0; i < 123; i++ {}
-
- // Q: Should I use the new rule or go back to the old one without optional semicolons?
-
-
- // #thread_local - see runtime.odin and above at `tls_int`
- // #foreign_system_library - see win32.odin
-
- // struct_compound_literals();
- // enumerations();
- // variadic_procedures();
- // new_builtins();
- // match_statement();
- // namespacing();
- // subtyping();
- // tagged_unions();
-}
-
-struct_compound_literals :: proc() {
- Thing :: struct {
- id: int,
- x: f32,
- name: string,
- };
- {
- t1: Thing;
- t1.id = 1;
-
- t3 := Thing{};
- t4 := Thing{1, 2, "Fred"};
- // t5 := Thing{1, 2};
-
- t6 := Thing{
- name = "Tom",
- x = 23,
- };
- }
-}
-
-enumerations :: proc() {
- {
- Fruit :: enum {
- APPLE, // 0
- BANANA, // 1
- PEAR, // 2
- };
-
- f := Fruit.APPLE;
- // g12: int = Fruit.BANANA
- g: int = cast(int)Fruit.BANANA;
- // However, you can use enums are index values as _any_ integer allowed
- }
- {
- Fruit1 :: enum int {
- APPLE,
- BANANA,
- PEAR,
- }
-
- Fruit2 :: enum u8 {
- APPLE,
- BANANA,
- PEAR,
- }
-
- Fruit3 :: enum u8 {
- APPLE = 1,
- BANANA, // 2
- PEAR = 5,
- TOMATO, // 6
- }
- }
-
- // Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)?
-}
-
-variadic_procedures :: proc() {
- print_ints :: proc(args: ..int) {
- for arg, i in args {
- if i > 0 do print(", ");
- print(arg);
- }
- }
-
- print_ints(); // nl()
- print_ints(1); nl();
- print_ints(1, 2, 3); nl();
-
- print_prefix_f32s :: proc(prefix: string, args: ..f32) {
- print(prefix);
- print(": ");
- for arg, i in args {
- if i > 0 do print(", ");
- print(arg);
- }
- }
-
- print_prefix_f32s("a"); nl();
- print_prefix_f32s("b", 1); nl();
- print_prefix_f32s("c", 1, 2, 3); nl();
-
- // Internally, the variadic procedures get allocated to an array on the stack,
- // and this array is passed a slice
-
- // This is first step for a `print` procedure but I do not have an `any` type
- // yet as this requires a few other things first - i.e. introspection
-
- // NOTE(bill): I haven't yet added the feature of expanding a slice or array into
- // a variadic a parameter but it's pretty trivial to add
-}
-
-new_builtins :: proc() {
- {
- a := new(int);
- b := make([]int, 12);
- c := make([]int, 12, 16);
-
- defer free(a);
- defer free(b);
- defer free(c);
-
- // NOTE(bill): These use the current context's allocator not the default allocator
- // see runtime.odin
-
- // Q: Should this be `free` rather than `free` and should I overload it for slices too?
-
- push_allocator default_allocator() {
- a := new(int);
- defer free(a);
-
- // Do whatever
-
- }
- }
-
- {
- a: int = 123;
- b: type_of(a) = 321;
-
- // NOTE(bill): This matches the current naming scheme
- // size_of
- // align_of
- // offset_of
- //
- // size_of_val
- // align_of_val
- // offset_of_val
- // type_of_val
- }
-
- {
- // Compile time assert
- COND :: true;
- #assert(COND);
- // #assert(!COND)
-
- // Runtime assert
- x := true;
- assert(x);
- // assert(!x);
- }
-
- {
- x: ^u32 = nil;
- y := x+100;
- z := y-x;
- w := slice_ptr(x, 12);
- t := slice_ptr(x, 12, 16);
-
- // NOTE(bill): These are here because I've removed:
- // pointer arithmetic
- // pointer indexing
- // pointer slicing
-
- // Reason
-
- a: [16]int;
- a[1] = 1;
- b := &a;
- // Auto pointer deref
- // consistent with record members
- assert(b[1] == 1);
-
- // Q: Should I add them back in at the cost of inconsitency?
- }
-
- {
- a, b := -1, 2;
- print(min(a, b)); nl();
- print(max(a, b)); nl();
- print(abs(a)); nl();
-
- // These work at compile time too
- A :: -1;
- B :: 2;
- C :: min(A, B);
- D :: max(A, B);
- E :: abs(A);
-
- print(C); nl();
- print(D); nl();
- print(E); nl();
- }
-}
-
-
-match_statement :: proc() {
- // NOTE(bill): `match` statements are similar to `switch` statements
- // in other languages but there are few differences
-
- {
- match x := 5; x {
- case 1: // cases must be constant expression
- print("1!\n");
- // break by default
-
- case 2:
- s := "2!\n"; // Each case has its own scope
- print(s);
- break; // explicit break
-
- case 3, 4: // multiple cases
- print("3 or 4!\n");
-
- case 5:
- print("5!\n");
- fallthrough; // explicit fallthrough
-
- case:
- print("default!\n");
- }
-
-
-
- match x := 1.5; x {
- case 1.5:
- print("1.5!\n");
- // break by default
- case TAU:
- print("Ļ!\n");
- case:
- print("default!\n");
- }
-
-
-
- match x := "Hello"; x {
- case "Hello":
- print("greeting\n");
- // break by default
- case "Goodbye":
- print("farewell\n");
- case:
- print("???\n");
- }
-
-
-
-
-
-
- a := 53;
- match {
- case a == 1:
- print("one\n");
- case a == 2:
- print("a couple\n");
- case a < 7, a == 7:
- print("a few\n");
- case a < 12: // intentional bug
- print("several\n");
- case a >= 12 && a < 100:
- print("dozens\n");
- case a >= 100 && a < 1000:
- print("hundreds\n");
- case:
- print("a fuck ton\n");
- }
-
- // Identical to this
-
- b := 53;
- if b == 1 {
- print("one\n");
- } else if b == 2 {
- print("a couple\n");
- } else if b < 7 || b == 7 {
- print("a few\n");
- } else if b < 12 { // intentional bug
- print("several\n");
- } else if b >= 12 && b < 100 {
- print("dozens\n");
- } else if b >= 100 && b < 1000 {
- print("hundreds\n");
- } else {
- print("a fuck ton\n");
- }
-
- // However, match statements allow for `break` and `fallthrough` unlike
- // an if statement
- }
-}
-
-Vector3 :: struct {x, y, z: f32}
-
-print_floats :: proc(args: ..f32) {
- for arg, i in args {
- if i > 0 do print(", ");
- print(arg);
- }
- println();
-}
-
-namespacing :: proc() {
- {
- Thing :: #type struct {
- x: f32,
- name: string,
- };
-
- a: Thing;
- a.x = 3;
- {
- Thing :: #type struct {
- y: int,
- test: bool,
- };
-
- b: Thing; // Uses this scope's Thing
- b.test = true;
- }
- }
-/*
- {
- Entity :: struct {
- Guid :: int
- Nested :: struct {
- MyInt :: int
- i: int
- }
-
- CONSTANT :: 123
-
-
- guid: Guid
- name: string
- pos: Vector3
- vel: Vector3
- nested: Nested
- }
-
- guid: Entity.Guid = Entity.CONSTANT
- i: Entity.Nested.MyInt
-
-
-
- {
- using Entity
- guid: Guid = CONSTANT
- using Nested
- i: MyInt
- }
-
-
- {
- using Entity.Nested
- guid: Entity.Guid = Entity.CONSTANT
- i: MyInt
- }
-
-
- {
- e: Entity
- using e
- guid = 27832
- name = "Bob"
-
- print(e.guid as int); nl()
- print(e.name); nl()
- }
-
- {
- using e: Entity
- guid = 78456
- name = "Thing"
-
- print(e.guid as int); nl()
- print(e.name); nl()
- }
- }
-
- {
- Entity :: struct {
- Guid :: int
- Nested :: struct {
- MyInt :: int
- i: int
- }
-
- CONSTANT :: 123
-
-
- guid: Guid
- name: string
- using pos: Vector3
- vel: Vector3
- using nested: ^Nested
- }
-
- e := Entity{nested = new(Entity.Nested)}
- e.x = 123
- e.i = Entity.CONSTANT
- }
-
-*/
-
- {
- Entity :: struct {
- position: Vector3
- }
-
- print_pos_1 :: proc(entity: ^Entity) {
- print("print_pos_1: ");
- print_floats(entity.position.x, entity.position.y, entity.position.z);
- }
-
- print_pos_2 :: proc(entity: ^Entity) {
- using entity;
- print("print_pos_2: ");
- print_floats(position.x, position.y, position.z);
- }
-
- print_pos_3 :: proc(using entity: ^Entity) {
- print("print_pos_3: ");
- print_floats(position.x, position.y, position.z);
- }
-
- print_pos_4 :: proc(using entity: ^Entity) {
- using position;
- print("print_pos_4: ");
- print_floats(x, y, z);
- }
-
- e := Entity{position = Vector3{1, 2, 3}};
- print_pos_1(&e);
- print_pos_2(&e);
- print_pos_3(&e);
- print_pos_4(&e);
-
- // This is similar to C++'s `this` pointer that is implicit and only available in methods
- }
-}
-
-subtyping :: proc() {
- {
- // C way for subtyping/subclassing
-
- Entity :: struct {
- position: Vector3,
- }
-
- Frog :: struct {
- entity: Entity,
- jump_height: f32,
- }
-
- f: Frog;
- f.entity.position = Vector3{1, 2, 3};
-
- using f.entity;
- position = Vector3{1, 2, 3};
-
- }
-
- {
- // C++ way for subtyping/subclassing
-
- Entity :: struct {
- position: Vector3
- }
-
- Frog :: struct {
- using entity: Entity,
- jump_height: f32,
- }
-
- f: Frog;
- f.position = Vector3{1, 2, 3};
-
-
- print_pos :: proc(using entity: Entity) {
- print("print_pos: ");
- print_floats(position.x, position.y, position.z);
- }
-
- print_pos(f.entity);
- // print_pos(f);
-
- // Subtype Polymorphism
- }
-
- {
- // More than C++ way for subtyping/subclassing
-
- Entity :: struct {
- position: Vector3,
- }
-
- Frog :: struct {
- jump_height: f32,
- using entity: ^Entity, // Doesn't have to be first member!
- }
-
- f: Frog;
- f.entity = new(Entity);
- f.position = Vector3{1, 2, 3};
-
-
- print_pos :: proc(using entity: ^Entity) {
- print("print_pos: ");
- print_floats(position.x, position.y, position.z);
- }
-
- print_pos(f.entity);
- // print_pos(^f);
- // print_pos(f);
- }
-
- {
- // More efficient subtyping
-
- Entity :: struct {
- position: Vector3,
- }
-
- Frog :: struct {
- jump_height: f32,
- using entity: ^Entity,
- }
-
- MAX_ENTITES :: 64;
- entities: [MAX_ENTITES]Entity;
- entity_count := 0;
-
- next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
- e := &entities[entity_count^];
- entity_count^ += 1;
- return e;
- }
-
- f: Frog;
- f.entity = next_entity(entities[..], &entity_count);
- f.position = Vector3{3, 4, 6};
-
- using f.position;
- print_floats(x, y, z);
- }
-
- /*{
- // Down casting
-
- Entity :: struct {
- position: Vector3,
- }
-
- Frog :: struct {
- jump_height: f32,
- using entity: Entity,
- }
-
- f: Frog;
- f.jump_height = 564;
- e := ^f.entity;
-
- frog := down_cast(^Frog)e;
- print("down_cast: ");
- print(frog.jump_height); nl();
-
- // NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
- // Q: Should I completely remove `down_cast` as I added it in about 30 minutes
- }*/
-
- {
- // Multiple "inheritance"/subclassing
-
- Entity :: struct {
- position: Vector3,
- }
- Climber :: struct {
- speed: f32,
- }
-
- Frog :: struct {
- using entity: Entity,
- using climber: Climber,
- }
- }
-}
-
-tagged_unions :: proc() {
- {
- Entity_Kind :: enum {
- INVALID,
- FROG,
- GIRAFFE,
- HELICOPTER,
- }
-
- Entity :: struct {
- kind: Entity_Kind
- using data: struct #raw_union {
- frog: struct {
- jump_height: f32,
- colour: u32,
- },
- giraffe: struct {
- neck_length: f32,
- spot_count: int,
- },
- helicopter: struct {
- blade_count: int,
- weight: f32,
- pilot_name: string,
- },
- }
- }
-
- e: Entity;
- e.kind = Entity_Kind.FROG;
- e.frog.jump_height = 12;
-
- f: type_of(e.frog);
-
- // But this is very unsafe and extremely cumbersome to write
- // In C++, I use macros to alleviate this but it's not a solution
- }
-
- {
- Frog :: struct {
- jump_height: f32,
- colour: u32,
- }
- Giraffe :: struct {
- neck_length: f32,
- spot_count: int,
- }
- Helicopter :: struct {
- blade_count: int,
- weight: f32,
- pilot_name: string,
- }
- Entity :: union {Frog, Giraffe, Helicopter};
-
- f1: Frog = Frog{12, 0xff9900};
- f2: Entity = Frog{12, 0xff9900}; // Implicit cast
- f3 := cast(Entity)Frog{12, 0xff9900}; // Explicit cast
-
- // f3.Frog.jump_height = 12 // There are "members" of a union
-
-
-
- e, f, g, h: Entity;
- f = Frog{12, 0xff9900};
- g = Giraffe{2.1, 23};
- h = Helicopter{4, 1000, "Frank"};
-
-
-
-
- // Requires a pointer to the union
- // `x` will be a pointer to type of the case
-
- match x in &f {
- case Frog:
- print("Frog!\n");
- print(x.jump_height); nl();
- // x.jump_height = 3;
- print(x.jump_height); nl();
- case Giraffe:
- print("Giraffe!\n");
- case Helicopter:
- print("ROFLCOPTER!\n");
- case:
- print("invalid entity\n");
- }
-
-
- // Q: Allow for a non pointer version with takes a copy instead?
- // Or it takes the pointer the data and not a copy
-
-
- // fp := cast(^Frog)^f; // Unsafe
- // print(fp.jump_height); nl();
-
-
- // Internals of a tagged union
- /*
- struct {
- data: [size_of_biggest_tag]u8,
- tag_index: int,
- }
- */
- // This is to allow for pointer casting if needed
-
-
- // Advantage over subtyping version
- MAX_ENTITES :: 64;
- entities: [MAX_ENTITES]Entity;
-
- entities[0] = Frog{};
- entities[1] = Helicopter{};
- // etc.
- }
-
-
- {
- // Transliteration of code from this actual compiler
- // Some stuff is missing
- Type :: struct {};
- Scope :: struct {};
- Token :: struct {};
- AstNode :: struct {};
- ExactValue :: struct {};
-
- Entity_Kind :: enum {
- Invalid,
- Constant,
- Variable,
- Using_Variable,
- TypeName,
- Procedure,
- Builtin,
- Count,
- }
-
- Guid :: i64;
- Entity :: struct {
-
- kind: Entity_Kind,
- guid: Guid,
-
- scope: ^Scope,
- token: Token,
- type_: ^Type,
-
- using data: struct #raw_union {
- Constant: struct {
- value: ExactValue,
- },
- Variable: struct {
- visited: bool, // Cycle detection
- used: bool, // Variable is used
- is_field: bool, // Is struct field
- anonymous: bool, // Variable is an anonymous
- },
- Using_Variable: struct {
- },
- TypeName: struct {
- },
- Procedure: struct {
- used: bool,
- },
- Builtin: struct {
- id: int,
- },
- },
- }
-
- // Plus all the constructing procedures that go along with them!!!!
- // It's a nightmare
- }
-
- {
- Type :: struct {};
- Scope :: struct {};
- Token :: struct {};
- AstNode :: struct {};
- ExactValue :: struct {};
-
-
- Guid :: i64;
- Entity_Base :: struct {
- }
-
-
- Constant :: struct {
- value: ExactValue,
- }
- Variable :: struct {
- visited: bool, // Cycle detection
- used: bool, // Variable is used
- is_field: bool, // Is struct field
- anonymous: bool, // Variable is an anonymous
- }
- Using_Variable :: struct {
- }
- TypeName :: struct {
- }
- Procedure :: struct {
- used: bool,
- }
- Builtin :: struct {
- id: int,
- }
-
- Entity :: struct {
- guid: Guid,
-
- scope: ^Scope,
- token: Token,
- type_: ^Type,
-
- variant: union {Constant, Variable, Using_Variable, TypeName, Procedure, Builtin},
- }
-
- e := Entity{
- variant = Variable{
- used = true,
- anonymous = false,
- },
- };
-
-
-
- // Q: Allow a "base" type to be added to a union?
- // Or even `using` on union to get the same properties?
- }
-
-
- {
- // `Raw` unions still have uses, especially for mathematic types
-
- Vector2 :: struct #raw_union {
- using xy_: struct { x, y: f32 },
- e: [2]f32,
- v: [vector 2]f32,
- }
-
- Vector3 :: struct #raw_union {
- using xyz_: struct { x, y, z: f32 },
- xy: Vector2,
- e: [3]f32,
- v: [vector 3]f32,
- }
-
- v2: Vector2;
- v2.x = 1;
- v2.e[0] = 1;
- v2.v[0] = 1;
-
- v3: Vector3;
- v3.x = 1;
- v3.e[0] = 1;
- v3.v[0] = 1;
- v3.xy.x = 1;
- }
-}
-
-nl :: proc() { println(); }
diff --git a/misc/old_demos/demo004.odin b/misc/old_demos/demo004.odin
deleted file mode 100644
index c9acc9a15..000000000
--- a/misc/old_demos/demo004.odin
+++ /dev/null
@@ -1,66 +0,0 @@
-import "core:fmt.odin";
-import "core:utf8.odin";
-import "core:hash.odin";
-import "core:mem.odin";
-
-main :: proc() {
- { // New Standard Library stuff
- s := "Hello";
- fmt.println(s,
- utf8.valid_string(s),
- hash.murmur64(cast([]u8)s));
-
- // utf8.odin
- // hash.odin
- // - crc, fnv, fnva, murmur
- // mem.odin
- // - Custom allocators
- // - Helpers
- }
-
- {
- arena: mem.Arena;
- mem.init_arena_from_context(&arena, mem.megabytes(16)); // Uses default allocator
- defer mem.destroy_arena(&arena);
-
- push_allocator mem.arena_allocator(&arena) {
- x := new(int);
- x^ = 1337;
-
- fmt.println(x^);
- }
-
- /*
- push_allocator x {
- ..
- }
-
- is equivalent to:
-
- {
- prev_allocator := __context.allocator
- __context.allocator = x
- defer __context.allocator = prev_allocator
-
- ..
- }
- */
-
- // You can also "push" a context
-
- c := context; // Create copy of the allocator
- c.allocator = mem.arena_allocator(&arena);
-
- push_context c {
- x := new(int);
- x^ = 365;
-
- fmt.println(x^);
- }
- }
-
- // Backend improvements
- // - Minimal dependency building (only build what is needed)
- // - Numerous bugs fixed
- // - Mild parsing recovery after bad syntax error
-}
diff --git a/misc/old_demos/demo005.odin b/misc/old_demos/demo005.odin
deleted file mode 100644
index c8273b03b..000000000
--- a/misc/old_demos/demo005.odin
+++ /dev/null
@@ -1,283 +0,0 @@
-import "core:fmt.odin";
-import "core:utf8.odin";
-// import "core:atomic.odin";
-// import "core:hash.odin";
-// import "core:math.odin";
-// import "core:mem.odin";
-// import "core:opengl.odin";
-// import "core:os.odin";
-// import "core:sync.odin";
-// import win32 "core:sys/windows.odin";
-
-main :: proc() {
- // syntax();
- procedure_overloading();
-}
-
-syntax :: proc() {
- // Cyclic type checking
- // Uncomment to see the error
- // A :: struct {b: B};
- // B :: struct {a: A};
-
- x: int;
- y := cast(f32)x;
- z := transmute(u32)y;
- // down_cast, union_cast are similar too
-
-
-
- // Basic directives
- fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
- // NOTE: new and improved `printf`
- // TODO: It does need accurate float printing
-
-
-
- // record fields use the same syntax a procedure signatures
- Thing1 :: struct {
- x: f32,
- y: int,
- z: ^[]int,
- };
- Thing2 :: struct {x: f32, y: int, z: ^[]int};
-
- // Slice interals are now just a `ptr+len+cap`
- slice: []int; #assert(size_of(slice) == 3*size_of(int));
-
- // Helper type - Help the reader understand what it is quicker
- My_Int :: #type int;
- My_Proc :: #type proc(int) -> f32;
-
-
- // All declarations with : are either variable or constant
- // To make these declarations syntactically consistent
- v_variable := 123;
- c_constant :: 123;
- c_type1 :: int;
- c_type2 :: []int;
- c_proc :: proc() { /* code here */ };
-
-
-/*
- x += 1;
- x -= 1;
- // ++ and -- have been removed
- // x++;
- // x--;
- // Question: Should they be added again?
- // They were removed as they are redundant and statements, not expressions
- // like in C/C++
-*/
-
- // You can now build files as a `.dll`
- // `odin build_dll demo.odin`
-
-
- // New vector syntax
- u, v: [vector 3]f32;
- v[0] = 123;
- v.x = 123; // valid for all vectors with count 1 to 4
-
- // Next part
- prefixes();
-}
-
-
-Prefix_Type :: struct {x: int, y: f32, z: rawptr};
-
-#thread_local my_tls: Prefix_Type;
-
-prefixes :: proc() {
- using var: Prefix_Type;
- var.x = 123;
- x = 123;
-
-
- foo :: proc(using pt: Prefix_Type) {
- }
-
-
-
- // Same as C99's `restrict`
- bar :: proc(#no_alias a, b: ^int) {
- // Assumes a never equals b so it can perform optimizations with that fact
- }
-
-
- when_statements();
-}
-
-
-
-
-
-when_statements :: proc() {
- X :: 123 + 12;
- Y :: X/5;
- COND :: Y > 0;
-
- when COND {
- fmt.println("Y > 0");
- } else {
- fmt.println("Y <= 0");
- }
-
-
- when false {
- this_code_does_not_exist(123, 321);
- but_its_syntax_is_valid();
- x :: ^^^^int;
- }
-
- foreign_procedures();
-}
-
-when ODIN_OS == "windows" {
- foreign_system_library win32_user "user32.lib";
-}
-// NOTE: This is done on purpose for two reasons:
-// * Makes it clear where the platform specific stuff is
-// * Removes the need to solve the travelling salesman problem when importing files :P
-
-foreign_procedures :: proc() {
- foreign win32_user {
- ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 ---;
- show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #link_name "ShowWindow" ---;
- }
- // NOTE: If that library doesn't get used, it doesn't get linked with
- // NOTE: There is not link checking yet to see if that procedure does come from that library
-
- // See sys/windows.odin for more examples
-
- special_expressions();
-}
-
-special_expressions :: proc() {
-/*
- // Block expression
- x := {
- a: f32 = 123;
- b := a-123;
- c := b/a;
- give c;
- }; // semicolon is required as it's an expression
-
- y := if x < 50 {
- give x;
- } else {
- // TODO: Type cohesion is not yet finished
- give 123;
- }; // semicolon is required as it's an expression
-*/
-
- // This is allows for inline blocks of code and will be a useful feature to have when
- // macros will be implemented into the language
-
- loops();
-}
-
-loops :: proc() {
- // The C-style for loop
- for i := 0; i < 123; i += 1 {
- break;
- }
- for i := 0; i < 123; {
- break;
- }
- for false {
- break;
- }
- for {
- break;
- }
-
- for i in 0..123 { // 123 exclusive
- }
-
- for i in 0..123-1 { // 122 inclusive
- }
-
- for val, idx in 12..16 {
- fmt.println(val, idx);
- }
-
- primes := [?]int{2, 3, 5, 7, 11, 13, 17, 19};
-
- for p in primes {
- fmt.println(p);
- }
-
- // Pointers to arrays, slices, or strings are allowed
- for _ in &primes {
- // ignore the value and just iterate across it
- }
-
-
-
- name := "ä½ å„½ļ¼äøē";
- fmt.println(name);
- for r in name {
- #assert(type_of(r) == rune);
- fmt.printf("%r\n", r);
- }
-
- when false {
- for i, size := 0; i < name.count; i += size {
- r: rune;
- r, size = utf8.decode_rune(name[i..]);
- fmt.printf("%r\n", r);
- }
- }
-
- procedure_overloading();
-}
-
-
-procedure_overloading :: proc() {
- THINGF :: 14451.1;
- THINGI :: 14451;
-
- foo :: proc() {
- fmt.printf("Zero args\n");
- }
- foo :: proc(i: int) {
- fmt.printf("int arg, i=%d\n", i);
- }
- foo :: proc(f: f64) {
- i := cast(int)f;
- fmt.printf("f64 arg, f=%d\n", i);
- }
-
- foo();
- foo(THINGF);
- // foo(THINGI); // 14451 is just a number so it could go to either procedures
- foo(cast(int)THINGI);
-
-
-
-
- foo :: proc(x: ^i32) -> (int, int) {
- fmt.println("^int");
- return 123, cast(int)(x^);
- }
- foo :: proc(x: rawptr) {
- fmt.println("rawptr");
- }
-
-
- a: i32 = 123;
- b: f32;
- c: rawptr;
- fmt.println(foo(&a));
- foo(&b);
- foo(c);
- // foo(nil); // nil could go to numerous types thus the ambiguity
-
- f: proc();
- f = foo; // The correct `foo` to chosen
- f();
-
-
- // See math.odin and atomic.odin for more examples
-}
diff --git a/misc/old_demos/demo006.odin b/misc/old_demos/demo006.odin
deleted file mode 100644
index c2f64151b..000000000
--- a/misc/old_demos/demo006.odin
+++ /dev/null
@@ -1,310 +0,0 @@
-// import "core:atomic.odin";
-import "core:hash.odin";
-import "core:mem.odin";
-import "core:opengl.odin";
-import "core:strconv.odin";
-import "core:sync.odin";
-import win32 "core:sys/windows.odin";
-
-import "core:fmt.odin";
-import "core:os.odin";
-import "core:math.odin";
-
-
-main :: proc() {
-when true {
-/*
- Added:
- * Unexported entities and fields using an underscore prefix
- - See `sync.odin` and explain
-
- Removed:
- * Maybe/option types
- * Remove `type` keyword and other "reserved" keywords
- * ..< and .. removed and replace with .. (half-closed range)
-
- Changed:
- * `#assert` and `assert` return the value of the condition for semantic reasons
- * thread_local -> #thread_local
- * #include -> #load
- * Files only get checked if they are actually used
- * match x in y {} // For type match statements
- * Version numbering now starts from 0.1.0 and uses the convention:
- - major.minor.patch
- * Core library additions to Windows specific stuff
- */
-
- {
- Fruit :: enum {
- APPLE,
- BANANA,
- COCONUT,
- }
- fmt.println(Fruit.names);
- }
-
- {
- A :: struct {x, y: f32};
- B :: struct #align 16 {x, y: f32};
- fmt.println("align_of(A) =", align_of(A));
- fmt.println("align_of(B) =", align_of(B));
- }
-
- {
- // Removal of ..< and ..
- for i in 0..16 {
- }
- // Is similar to
- for i := 0; i < 16; i += 1 {
- }
- }
-
- {
- thing: for i in 0..10 {
- for j in i+1..10 {
- if j == 2 {
- fmt.println(i, j);
- continue thing;
- }
- if j == 3 {
- break thing;
- }
- }
- }
-
- // Works with, `for`, `for in`, `match`, `match in`
- // NOTE(bill): This solves most of the problems I need `goto` for
- }
-
- {
- t := type_info_of(int);
- match i in t.variant {
- case Type_Info_Integer, Type_Info_Float:
- fmt.println("It's a number");
- }
-
-
- x: any = 123;
- foo: match i in x {
- case int, f32:
- fmt.println("It's an int or f32");
- break foo;
- }
- }
-
- {
- cond := true;
- x: int;
- if cond {
- x = 3;
- } else {
- x = 4;
- }
-
-
- // Ternary operator
- y := cond ? 3 : 4;
-
- FOO :: true ? 123 : 432; // Constant ternary expression
- fmt.println("Ternary values:", y, FOO);
- }
-
- {
- // Slices now store a capacity
- buf: [256]u8;
- s: []u8;
- s = buf[..0]; // == buf[0..0];
- fmt.println("count =", len(s));
- fmt.println("capacity =", cap(s));
- append(&s, 1, 2, 3);
- fmt.println(s);
-
- s = buf[1..2..3];
- fmt.println("count =", len(s));
- fmt.println("capacity =", cap(s));
- fmt.println(s);
-
- clear(&s); // Sets count to zero
- }
-
- {
- Foo :: struct {
- x, y, z: f32,
- ok: bool,
- flags: u32,
- }
- foo_array: [256]Foo;
- foo_as_bytes: []u8 = mem.slice_to_bytes(foo_array[..]);
- // Useful for things like
- // os.write(handle, foo_as_bytes);
-
- foo_slice := mem.slice_ptr(cast(^Foo)&foo_as_bytes[0], len(foo_as_bytes)/size_of(Foo), cap(foo_as_bytes)/size_of(Foo));
- // Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
- // And if so what would the syntax be?
- // slice_transmute([]Foo, foo_as_bytes);
- }
-
- {
- Vec3 :: [vector 3]f32;
-
- x := Vec3{1, 2, 3};
- y := Vec3{4, 5, 6};
- fmt.println(x < y);
- fmt.println(x + y);
- fmt.println(x - y);
- fmt.println(x * y);
- fmt.println(x / y);
-
- for i in x {
- fmt.println(i);
- }
-
- #assert(size_of([vector 7]bool) >= size_of([7]bool));
- #assert(size_of([vector 7]i32) >= size_of([7]i32));
- // align_of([vector 7]i32) != align_of([7]i32) // this may be the case
- }
-
- {
- // fmt.* changes
- // bprint* returns `string`
-
- data: [256]u8;
- str := fmt.bprintf(data[..], "Hellope %d %s %c", 123, "others", '!');
- fmt.println(str);
- }
-
- {
- x: [dynamic]f64;
- reserve(&x, 16);
- defer free(x); // `free` is overloaded for numerous types
- // Number literals can have underscores in them for readability
- append(&x, 2_000_000.500_000, 123, 5, 7); // variadic append
-
- for p, i in x {
- if i > 0 { fmt.print(", "); }
- fmt.print(p);
- }
- fmt.println();
- }
-
- {
- // Dynamic array "literals"
- x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
- defer free(x);
- fmt.println(x); // fmt.print* supports printing of dynamic types
- clear(&x);
- fmt.println(x);
- }
-
- {
- m: map[f32]int;
- reserve(&m, 16);
- defer free(m);
-
- m[1.0] = 1278;
- m[2.0] = 7643;
- m[3.0] = 564;
- _, ok := m[3.0];
- c := m[3.0];
- assert(ok && c == 564);
-
- fmt.print("map[");
- i := 0;
- for val, key in m {
- if i > 0 {
- fmt.print(", ");
- }
- fmt.printf("%v=%v", key, val);
- i += 1;
- }
- fmt.println("]");
- }
- {
- m := map[string]u32{
- "a" = 56,
- "b" = 13453,
- "c" = 7654,
- };
- defer free(m);
-
- c := m["c"];
- _, ok := m["c"];
- assert(ok && c == 7654);
- fmt.println(m);
-
- delete(&m, "c"); // deletes entry with key "c"
- _, found := m["c"];
- assert(!found);
-
- fmt.println(m);
- clear(&m);
- fmt.println(m);
-
- // NOTE: Fixed size maps are planned but we have not yet implemented
- // them as we have had no need for them as of yet
- }
-
- {
- Vector3 :: struct{x, y, z: f32};
- Quaternion :: struct{x, y, z, w: f32};
-
- // Variants
- Frog :: struct {
- ribbit_volume: f32,
- jump_height: f32,
- }
- Door :: struct {
- openness: f32,
- }
- Map :: struct {
- width, height: f32,
- place_positions: []Vector3,
- place_names: []string,
- }
-
- Entity :: struct {
- // Common Fields
- id: u64,
- name: string,
- using position: Vector3,
- orientation: Quaternion,
- flags: u32,
-
- variant: union { Frog, Door, Map },
- }
-
- entity: Entity;
- entity.id = 1337;
- // implicit conversion from variant to base type
- entity.variant = Frog{
- ribbit_volume = 0.5,
- jump_height = 2.1,
- /*other data */
- };
-
- entity.name = "Frank";
- entity.position = Vector3{1, 4, 9};
-
- match e in entity.variant {
- case Frog:
- fmt.println("Ribbit");
- case Door:
- fmt.println("Creak");
- case Map:
- fmt.println("Rustle");
- case:
- fmt.println("Just a normal entity");
- }
-
- if frog, ok := entity.variant.(Frog); ok {
- fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, entity.position);
- }
-
- // Panics if not the correct type
- frog: Frog;
- frog = entity.variant.(Frog);
- frog, _ = entity.variant.(Frog); // ignore error and force cast
- }
-}
-}
-
diff --git a/misc/old_demos/demo007.odin b/misc/old_demos/demo007.odin
deleted file mode 100644
index d19446ecb..000000000
--- a/misc/old_demos/demo007.odin
+++ /dev/null
@@ -1,570 +0,0 @@
-import "core:fmt.odin"
-import "core:strconv.odin"
-import "core:mem.odin"
-import "core:bits.odin"
-import "core:hash.odin"
-import "core:math.odin"
-import "core:os.odin"
-import "core:raw.odin"
-import "core:sort.odin"
-import "core:strings.odin"
-import "core:types.odin"
-import "core:utf16.odin"
-import "core:utf8.odin"
-
-when ODIN_OS == "windows" {
- import "core:atomics.odin"
- import "core:opengl.odin"
- import "core:thread.odin"
- import win32 "core:sys/windows.odin"
-}
-
-general_stuff :: proc() {
- { // `do` for inline statmes rather than block
- foo :: proc() do fmt.println("Foo!");
- if false do foo();
- for false do foo();
- when false do foo();
-
- if false do foo();
- else do foo();
- }
-
- { // Removal of `++` and `--` (again)
- x: int;
- x += 1;
- x -= 1;
- }
- { // Casting syntaxes
- i := i32(137);
- ptr := &i;
-
- fp1 := (^f32)(ptr);
- // ^f32(ptr) == ^(f32(ptr))
- fp2 := cast(^f32)ptr;
-
- f1 := (^f32)(ptr)^;
- f2 := (cast(^f32)ptr)^;
-
- // Questions: Should there be two ways to do it?
- }
-
- /*
- * Remove *_val_of built-in procedures
- * size_of, align_of, offset_of
- * type_of, type_info_of
- */
-
- { // `expand_to_tuple` built-in procedure
- Foo :: struct {
- x: int,
- b: bool,
- }
- f := Foo{137, true};
- x, b := expand_to_tuple(f);
- fmt.println(f);
- fmt.println(x, b);
- fmt.println(expand_to_tuple(f));
- }
-
- {
- // .. half-closed range
- // .. open range
-
- for in 0..2 {} // 0, 1
- for in 0..2 {} // 0, 1, 2
- }
-}
-
-default_struct_values :: proc() {
- {
- Vector3 :: struct {
- x: f32,
- y: f32,
- z: f32,
- }
- v: Vector3;
- fmt.println(v);
- }
- {
- // Default values must be constants
- Vector3 :: struct {
- x: f32 = 1,
- y: f32 = 4,
- z: f32 = 9,
- }
- v: Vector3;
- fmt.println(v);
-
- v = Vector3{};
- fmt.println(v);
-
- // Uses the same semantics as a default values in a procedure
- v = Vector3{137};
- fmt.println(v);
-
- v = Vector3{z = 137};
- fmt.println(v);
- }
-
- {
- Vector3 :: struct {
- x := 1.0,
- y := 4.0,
- z := 9.0,
- }
- stack_default: Vector3;
- stack_literal := Vector3{};
- heap_one := new(Vector3); defer free(heap_one);
- heap_two := new_clone(Vector3{}); defer free(heap_two);
-
- fmt.println("stack_default - ", stack_default);
- fmt.println("stack_literal - ", stack_literal);
- fmt.println("heap_one - ", heap_one^);
- fmt.println("heap_two - ", heap_two^);
-
-
- N :: 4;
- stack_array: [N]Vector3;
- heap_array := new([N]Vector3); defer free(heap_array);
- heap_slice := make([]Vector3, N); defer free(heap_slice);
- fmt.println("stack_array[1] - ", stack_array[1]);
- fmt.println("heap_array[1] - ", heap_array[1]);
- fmt.println("heap_slice[1] - ", heap_slice[1]);
- }
-}
-
-
-
-
-union_type :: proc() {
- {
- val: union{int, bool};
- val = 137;
- if i, ok := val.(int); ok {
- fmt.println(i);
- }
- val = true;
- fmt.println(val);
-
- val = nil;
-
- switch v in val {
- case int: fmt.println("int", v);
- case bool: fmt.println("bool", v);
- case: fmt.println("nil");
- }
- }
- {
- // There is a duality between `any` and `union`
- // An `any` has a pointer to the data and allows for any type (open)
- // A `union` has as binary blob to store the data and allows only certain types (closed)
- // The following code is with `any` but has the same syntax
- val: any;
- val = 137;
- if i, ok := val.(int); ok {
- fmt.println(i);
- }
- val = true;
- fmt.println(val);
-
- val = nil;
-
- switch v in val {
- case int: fmt.println("int", v);
- case bool: fmt.println("bool", v);
- case: fmt.println("nil");
- }
- }
-
- Vector3 :: struct {x, y, z: f32};
- Quaternion :: struct {x, y, z: f32, w: f32 = 1};
-
- // More realistic examples
- {
- // NOTE(bill): For the above basic examples, you may not have any
- // particular use for it. However, my main use for them is not for these
- // simple cases. My main use is for hierarchical types. Many prefer
- // subtyping, embedding the base data into the derived types. Below is
- // an example of this for a basic game Entity.
-
- Entity :: struct {
- id: u64,
- name: string,
- position: Vector3,
- orientation: Quaternion,
-
- derived: any,
- }
-
- Frog :: struct {
- using entity: Entity,
- jump_height: f32,
- }
-
- Monster :: struct {
- using entity: Entity,
- is_robot: bool,
- is_zombie: bool,
- }
-
- // See `parametric_polymorphism` procedure for details
- new_entity :: proc(T: type) -> ^Entity {
- t := new(T);
- t.derived = t^;
- return t;
- }
-
- entity := new_entity(Monster);
-
- switch e in entity.derived {
- case Frog:
- fmt.println("Ribbit");
- case Monster:
- if e.is_robot do fmt.println("Robotic");
- if e.is_zombie do fmt.println("Grrrr!");
- }
- }
-
- {
- // NOTE(bill): A union can be used to achieve something similar. Instead
- // of embedding the base data into the derived types, the derived data
- // in embedded into the base type. Below is the same example of the
- // basic game Entity but using an union.
-
- Entity :: struct {
- id: u64,
- name: string,
- position: Vector3,
- orientation: Quaternion,
-
- derived: union {Frog, Monster},
- }
-
- Frog :: struct {
- using entity: ^Entity,
- jump_height: f32,
- }
-
- Monster :: struct {
- using entity: ^Entity,
- is_robot: bool,
- is_zombie: bool,
- }
-
- // See `parametric_polymorphism` procedure for details
- new_entity :: proc(T: type) -> ^Entity {
- t := new(Entity);
- t.derived = T{entity = t};
- return t;
- }
-
- entity := new_entity(Monster);
-
- switch e in entity.derived {
- case Frog:
- fmt.println("Ribbit");
- case Monster:
- if e.is_robot do fmt.println("Robotic");
- if e.is_zombie do fmt.println("Grrrr!");
- }
-
- // NOTE(bill): As you can see, the usage code has not changed, only its
- // memory layout. Both approaches have their own advantages but they can
- // be used together to achieve different results. The subtyping approach
- // can allow for a greater control of the memory layout and memory
- // allocation, e.g. storing the derivatives together. However, this is
- // also its disadvantage. You must either preallocate arrays for each
- // derivative separation (which can be easily missed) or preallocate a
- // bunch of "raw" memory; determining the maximum size of the derived
- // types would require the aid of metaprogramming. Unions solve this
- // particular problem as the data is stored with the base data.
- // Therefore, it is possible to preallocate, e.g. [100]Entity.
-
- // It should be noted that the union approach can have the same memory
- // layout as the any and with the same type restrictions by using a
- // pointer type for the derivatives.
-
- /*
- Entity :: struct {
- ..
- derived: union{^Frog, ^Monster};
- }
-
- Frog :: struct {
- using entity: Entity;
- ..
- }
- Monster :: struct {
- using entity: Entity;
- ..
-
- }
- new_entity :: proc(T: type) -> ^Entity {
- t := new(T);
- t.derived = t;
- return t;
- }
- */
- }
-}
-
-parametric_polymorphism :: proc() {
- print_value :: proc(value: $T) {
- fmt.printf("print_value: %T %v\n", value, value);
- }
-
- v1: int = 1;
- v2: f32 = 2.1;
- v3: f64 = 3.14;
- v4: string = "message";
-
- print_value(v1);
- print_value(v2);
- print_value(v3);
- print_value(v4);
-
- fmt.println();
-
- add :: proc(p, q: $T) -> T {
- x: T = p + q;
- return x;
- }
-
- a := add(3, 4);
- fmt.printf("a: %T = %v\n", a, a);
-
- b := add(3.2, 4.3);
- fmt.printf("b: %T = %v\n", b, b);
-
- // This is how `new` is implemented
- alloc_type :: proc(T: type) -> ^T {
- t := cast(^T)alloc(size_of(T), align_of(T));
- t^ = T{}; // Use default initialization value
- return t;
- }
-
- copy_slice :: proc(dst, src: []$T) -> int {
- n := min(len(dst), len(src));
- if n > 0 {
- mem.copy(&dst[0], &src[0], n*size_of(T));
- }
- return n;
- }
-
- double_params :: proc(a: $A, b: $B) -> A {
- return a + A(b);
- }
-
- fmt.println(double_params(12, 1.345));
-
-
-
- { // Polymorphic Types and Type Specialization
- Table_Slot :: struct(Key, Value: type) {
- occupied: bool,
- hash: u32,
- key: Key,
- value: Value,
- }
- TABLE_SIZE_MIN :: 32;
- Table :: struct(Key, Value: type) {
- count: int,
- allocator: Allocator,
- slots: []Table_Slot(Key, Value),
- }
-
- // Only allow types that are specializations of a (polymorphic) slice
- make_slice :: proc(T: type/[]$E, len: int) -> T {
- return make(T, len);
- }
-
-
- // Only allow types that are specializations of `Table`
- allocate :: proc(table: ^$T/Table, capacity: int) {
- c := context;
- if table.allocator.procedure != nil do c.allocator = table.allocator;
-
- push_context c {
- table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
- }
- }
-
- expand :: proc(table: ^$T/Table) {
- c := context;
- if table.allocator.procedure != nil do c.allocator = table.allocator;
-
- push_context c {
- old_slots := table.slots;
-
- cap := max(2*cap(table.slots), TABLE_SIZE_MIN);
- allocate(table, cap);
-
- for s in old_slots do if s.occupied {
- put(table, s.key, s.value);
- }
-
- free(old_slots);
- }
- }
-
- // Polymorphic determination of a polymorphic struct
- // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
- put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
- hash := get_hash(key); // Ad-hoc method which would fail in a different scope
- index := find_index(table, key, hash);
- if index < 0 {
- if f64(table.count) >= 0.75*f64(cap(table.slots)) {
- expand(table);
- }
- assert(table.count <= cap(table.slots));
-
- hash := get_hash(key);
- index = int(hash % u32(cap(table.slots)));
-
- for table.slots[index].occupied {
- if index += 1; index >= cap(table.slots) {
- index = 0;
- }
- }
-
- table.count += 1;
- }
-
- slot := &table.slots[index];
- slot.occupied = true;
- slot.hash = hash;
- slot.key = key;
- slot.value = value;
- }
-
-
- // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
- find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
- hash := get_hash(key);
- index := find_index(table, key, hash);
- if index < 0 {
- return Value{}, false;
- }
- return table.slots[index].value, true;
- }
-
- find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
- if cap(table.slots) <= 0 do return -1;
-
- index := int(hash % u32(cap(table.slots)));
- for table.slots[index].occupied {
- if table.slots[index].hash == hash {
- if table.slots[index].key == key {
- return index;
- }
- }
-
- if index += 1; index >= cap(table.slots) {
- index = 0;
- }
- }
-
- return -1;
- }
-
- get_hash :: proc(s: string) -> u32 { // fnv32a
- h: u32 = 0x811c9dc5;
- for i in 0..len(s) {
- h = (h ~ u32(s[i])) * 0x01000193;
- }
- return h;
- }
-
-
- table: Table(string, int);
-
- for i in 0..36 do put(&table, "Hellope", i);
- for i in 0..42 do put(&table, "World!", i);
-
- found, _ := find(&table, "Hellope");
- fmt.printf("`found` is %v\n", found);
-
- found, _ = find(&table, "World!");
- fmt.printf("`found` is %v\n", found);
-
- // I would not personally design a hash table like this in production
- // but this is a nice basic example
- // A better approach would either use a `u64` or equivalent for the key
- // and let the user specify the hashing function or make the user store
- // the hashing procedure with the table
- }
-}
-
-
-
-
-prefix_table := [?]string{
- "White",
- "Red",
- "Green",
- "Blue",
- "Octarine",
- "Black",
-};
-
-threading_example :: proc() {
- when ODIN_OS == "windows" {
- unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
- __bounds_check_error_loc(loc, index, len(array));
- array[index] = array[len(array)-1];
- pop(array);
- }
- ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
- __bounds_check_error_loc(loc, index, len(array));
- copy(array[index..], array[index+1..]);
- pop(array);
- }
-
- worker_proc :: proc(t: ^thread.Thread) -> int {
- for iteration in 1..5 {
- fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
- fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
- // win32.sleep(1);
- }
- return 0;
- }
-
- threads := make([]^thread.Thread, 0, len(prefix_table));
- defer free(threads);
-
- for i in 0..len(prefix_table) {
- if t := thread.create(worker_proc); t != nil {
- t.init_context = context;
- t.use_init_context = true;
- t.user_index = len(threads);
- append(&threads, t);
- thread.start(t);
- }
- }
-
- for len(threads) > 0 {
- for i := 0; i < len(threads); /**/ {
- if t := threads[i]; thread.is_done(t) {
- fmt.printf("Thread %d is done\n", t.user_index);
- thread.destroy(t);
-
- ordered_remove(&threads, i);
- } else {
- i += 1;
- }
- }
- }
- }
-}
-
-main :: proc() {
- when false {
- fmt.println("\n# general_stuff"); general_stuff();
- fmt.println("\n# default_struct_values"); default_struct_values();
- fmt.println("\n# union_type"); union_type();
- fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
- fmt.println("\n# threading_example"); threading_example();
- }
-}
-
diff --git a/misc/old_demos/demo008.odin b/misc/old_demos/demo008.odin
deleted file mode 100644
index a122414e7..000000000
--- a/misc/old_demos/demo008.odin
+++ /dev/null
@@ -1,778 +0,0 @@
-import "core:fmt.odin"
-import "core:strconv.odin"
-import "core:mem.odin"
-import "core:bits.odin"
-import "core:hash.odin"
-import "core:math.odin"
-import "core:math/rand.odin"
-import "core:os.odin"
-import "core:raw.odin"
-import "core:sort.odin"
-import "core:strings.odin"
-import "core:types.odin"
-import "core:utf16.odin"
-import "core:utf8.odin"
-
-// File scope `when` statements
-when ODIN_OS == "windows" {
- import "core:atomics.odin"
- import "core:thread.odin"
- import win32 "core:sys/windows.odin"
-}
-
-@(link_name="general_stuff")
-general_stuff :: proc() {
- fmt.println("# general_stuff");
- { // `do` for inline statements rather than block
- foo :: proc() do fmt.println("Foo!");
- if false do foo();
- for false do foo();
- when false do foo();
-
- if false do foo();
- else do foo();
- }
-
- { // Removal of `++` and `--` (again)
- x: int;
- x += 1;
- x -= 1;
- }
- { // Casting syntaxes
- i := i32(137);
- ptr := &i;
-
- _ = (^f32)(ptr);
- // ^f32(ptr) == ^(f32(ptr))
- _ = cast(^f32)ptr;
-
- _ = (^f32)(ptr)^;
- _ = (cast(^f32)ptr)^;
-
- // Questions: Should there be two ways to do it?
- }
-
- /*
- * Remove *_val_of built-in procedures
- * size_of, align_of, offset_of
- * type_of, type_info_of
- */
-
- { // `expand_to_tuple` built-in procedure
- Foo :: struct {
- x: int,
- b: bool,
- }
- f := Foo{137, true};
- x, b := expand_to_tuple(f);
- fmt.println(f);
- fmt.println(x, b);
- fmt.println(expand_to_tuple(f));
- }
-
- {
- // .. half-closed range
- // .. open range
-
- for in 0..2 {} // 0, 1
- for in 0..2 {} // 0, 1, 2
- }
-
- { // Multiple sized booleans
-
- x0: bool; // default
- x1: b8 = true;
- x2: b16 = false;
- x3: b32 = true;
- x4: b64 = false;
-
- fmt.printf("x1: %T = %v;\n", x1, x1);
- fmt.printf("x2: %T = %v;\n", x2, x2);
- fmt.printf("x3: %T = %v;\n", x3, x3);
- fmt.printf("x4: %T = %v;\n", x4, x4);
-
- // Having specific sized booleans is very useful when dealing with foreign code
- // and to enforce specific alignment for a boolean, especially within a struct
- }
-
- { // `distinct` types
- // Originally, all type declarations would create a distinct type unless #type_alias was present.
- // Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present.
- // If the type expression is `struct`, `union`, `enum`, or `proc`, the types will always been distinct.
-
- Int32 :: i32;
- #assert(Int32 == i32);
-
- My_Int32 :: distinct i32;
- #assert(My_Int32 != i32);
-
- My_Struct :: struct{x: int};
- #assert(My_Struct != struct{x: int});
- }
-}
-
-default_struct_values :: proc() {
- fmt.println("# default_struct_values");
- {
- Vector3 :: struct {
- x: f32,
- y: f32,
- z: f32,
- }
- v: Vector3;
- fmt.println(v);
- }
- {
- // Default values must be constants
- Vector3 :: struct {
- x: f32 = 1,
- y: f32 = 4,
- z: f32 = 9,
- }
- v: Vector3;
- fmt.println(v);
-
- v = Vector3{};
- fmt.println(v);
-
- // Uses the same semantics as a default values in a procedure
- v = Vector3{137};
- fmt.println(v);
-
- v = Vector3{z = 137};
- fmt.println(v);
- }
-
- {
- Vector3 :: struct {
- x := 1.0,
- y := 4.0,
- z := 9.0,
- }
- stack_default: Vector3;
- stack_literal := Vector3{};
- heap_one := new(Vector3); defer free(heap_one);
- heap_two := new_clone(Vector3{}); defer free(heap_two);
-
- fmt.println("stack_default - ", stack_default);
- fmt.println("stack_literal - ", stack_literal);
- fmt.println("heap_one - ", heap_one^);
- fmt.println("heap_two - ", heap_two^);
-
-
- N :: 4;
- stack_array: [N]Vector3;
- heap_array := new([N]Vector3); defer free(heap_array);
- heap_slice := make([]Vector3, N); defer free(heap_slice);
- fmt.println("stack_array[1] - ", stack_array[1]);
- fmt.println("heap_array[1] - ", heap_array[1]);
- fmt.println("heap_slice[1] - ", heap_slice[1]);
- }
-}
-
-
-
-
-union_type :: proc() {
- fmt.println("\n# union_type");
- {
- val: union{int, bool};
- val = 137;
- if i, ok := val.(int); ok {
- fmt.println(i);
- }
- val = true;
- fmt.println(val);
-
- val = nil;
-
- switch v in val {
- case int: fmt.println("int", v);
- case bool: fmt.println("bool", v);
- case: fmt.println("nil");
- }
- }
- {
- // There is a duality between `any` and `union`
- // An `any` has a pointer to the data and allows for any type (open)
- // A `union` has as binary blob to store the data and allows only certain types (closed)
- // The following code is with `any` but has the same syntax
- val: any;
- val = 137;
- if i, ok := val.(int); ok {
- fmt.println(i);
- }
- val = true;
- fmt.println(val);
-
- val = nil;
-
- switch v in val {
- case int: fmt.println("int", v);
- case bool: fmt.println("bool", v);
- case: fmt.println("nil");
- }
- }
-
- Vector3 :: struct {x, y, z: f32};
- Quaternion :: struct {x, y, z: f32, w: f32 = 1};
-
- // More realistic examples
- {
- // NOTE(bill): For the above basic examples, you may not have any
- // particular use for it. However, my main use for them is not for these
- // simple cases. My main use is for hierarchical types. Many prefer
- // subtyping, embedding the base data into the derived types. Below is
- // an example of this for a basic game Entity.
-
- Entity :: struct {
- id: u64,
- name: string,
- position: Vector3,
- orientation: Quaternion,
-
- derived: any,
- }
-
- Frog :: struct {
- using entity: Entity,
- jump_height: f32,
- }
-
- Monster :: struct {
- using entity: Entity,
- is_robot: bool,
- is_zombie: bool,
- }
-
- // See `parametric_polymorphism` procedure for details
- new_entity :: proc(T: type) -> ^Entity {
- t := new(T);
- t.derived = t^;
- return t;
- }
-
- entity := new_entity(Monster);
-
- switch e in entity.derived {
- case Frog:
- fmt.println("Ribbit");
- case Monster:
- if e.is_robot do fmt.println("Robotic");
- if e.is_zombie do fmt.println("Grrrr!");
- }
- }
-
- {
- // NOTE(bill): A union can be used to achieve something similar. Instead
- // of embedding the base data into the derived types, the derived data
- // in embedded into the base type. Below is the same example of the
- // basic game Entity but using an union.
-
- Entity :: struct {
- id: u64,
- name: string,
- position: Vector3,
- orientation: Quaternion,
-
- derived: union {Frog, Monster},
- }
-
- Frog :: struct {
- using entity: ^Entity,
- jump_height: f32,
- }
-
- Monster :: struct {
- using entity: ^Entity,
- is_robot: bool,
- is_zombie: bool,
- }
-
- // See `parametric_polymorphism` procedure for details
- new_entity :: proc(T: type) -> ^Entity {
- t := new(Entity);
- t.derived = T{entity = t};
- return t;
- }
-
- entity := new_entity(Monster);
-
- switch e in entity.derived {
- case Frog:
- fmt.println("Ribbit");
- case Monster:
- if e.is_robot do fmt.println("Robotic");
- if e.is_zombie do fmt.println("Grrrr!");
- }
-
- // NOTE(bill): As you can see, the usage code has not changed, only its
- // memory layout. Both approaches have their own advantages but they can
- // be used together to achieve different results. The subtyping approach
- // can allow for a greater control of the memory layout and memory
- // allocation, e.g. storing the derivatives together. However, this is
- // also its disadvantage. You must either preallocate arrays for each
- // derivative separation (which can be easily missed) or preallocate a
- // bunch of "raw" memory; determining the maximum size of the derived
- // types would require the aid of metaprogramming. Unions solve this
- // particular problem as the data is stored with the base data.
- // Therefore, it is possible to preallocate, e.g. [100]Entity.
-
- // It should be noted that the union approach can have the same memory
- // layout as the any and with the same type restrictions by using a
- // pointer type for the derivatives.
-
- /*
- Entity :: struct {
- ..
- derived: union{^Frog, ^Monster},
- }
-
- Frog :: struct {
- using entity: Entity,
- ..
- }
- Monster :: struct {
- using entity: Entity,
- ..
-
- }
- new_entity :: proc(T: type) -> ^Entity {
- t := new(T);
- t.derived = t;
- return t;
- }
- */
- }
-}
-
-parametric_polymorphism :: proc() {
- fmt.println("# parametric_polymorphism");
-
- print_value :: proc(value: $T) {
- fmt.printf("print_value: %T %v\n", value, value);
- }
-
- v1: int = 1;
- v2: f32 = 2.1;
- v3: f64 = 3.14;
- v4: string = "message";
-
- print_value(v1);
- print_value(v2);
- print_value(v3);
- print_value(v4);
-
- fmt.println();
-
- add :: proc(p, q: $T) -> T {
- x: T = p + q;
- return x;
- }
-
- a := add(3, 4);
- fmt.printf("a: %T = %v\n", a, a);
-
- b := add(3.2, 4.3);
- fmt.printf("b: %T = %v\n", b, b);
-
- // This is how `new` is implemented
- alloc_type :: proc(T: type) -> ^T {
- t := cast(^T)alloc(size_of(T), align_of(T));
- t^ = T{}; // Use default initialization value
- return t;
- }
-
- copy_slice :: proc(dst, src: []$T) -> int {
- return mem.copy(&dst[0], &src[0], n*size_of(T));
- }
-
- double_params :: proc(a: $A, b: $B) -> A {
- return a + A(b);
- }
-
- fmt.println(double_params(12, 1.345));
-
-
-
- { // Polymorphic Types and Type Specialization
- Table_Slot :: struct(Key, Value: type) {
- occupied: bool,
- hash: u32,
- key: Key,
- value: Value,
- }
- TABLE_SIZE_MIN :: 32;
- Table :: struct(Key, Value: type) {
- count: int,
- allocator: Allocator,
- slots: []Table_Slot(Key, Value),
- }
-
- // Only allow types that are specializations of a (polymorphic) slice
- make_slice :: proc(T: type/[]$E, len: int) -> T {
- return make(T, len);
- }
-
-
- // Only allow types that are specializations of `Table`
- allocate :: proc(table: ^$T/Table, capacity: int) {
- c := context;
- if table.allocator.procedure != nil do c.allocator = table.allocator;
-
- context <- c {
- table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN));
- }
- }
-
- expand :: proc(table: ^$T/Table) {
- c := context;
- if table.allocator.procedure != nil do c.allocator = table.allocator;
-
- context <- c {
- old_slots := table.slots;
-
- cap := max(2*len(table.slots), TABLE_SIZE_MIN);
- allocate(table, cap);
-
- for s in old_slots do if s.occupied {
- put(table, s.key, s.value);
- }
-
- free(old_slots);
- }
- }
-
- // Polymorphic determination of a polymorphic struct
- // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
- put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
- hash := get_hash(key); // Ad-hoc method which would fail in a different scope
- index := find_index(table, key, hash);
- if index < 0 {
- if f64(table.count) >= 0.75*f64(len(table.slots)) {
- expand(table);
- }
- assert(table.count <= len(table.slots));
-
- hash := get_hash(key);
- index = int(hash % u32(len(table.slots)));
-
- for table.slots[index].occupied {
- if index += 1; index >= len(table.slots) {
- index = 0;
- }
- }
-
- table.count += 1;
- }
-
- slot := &table.slots[index];
- slot.occupied = true;
- slot.hash = hash;
- slot.key = key;
- slot.value = value;
- }
-
-
- // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
- find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
- hash := get_hash(key);
- index := find_index(table, key, hash);
- if index < 0 {
- return Value{}, false;
- }
- return table.slots[index].value, true;
- }
-
- find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
- if len(table.slots) <= 0 do return -1;
-
- index := int(hash % u32(len(table.slots)));
- for table.slots[index].occupied {
- if table.slots[index].hash == hash {
- if table.slots[index].key == key {
- return index;
- }
- }
-
- if index += 1; index >= len(table.slots) {
- index = 0;
- }
- }
-
- return -1;
- }
-
- get_hash :: proc(s: string) -> u32 { // fnv32a
- h: u32 = 0x811c9dc5;
- for i in 0..len(s) {
- h = (h ~ u32(s[i])) * 0x01000193;
- }
- return h;
- }
-
-
- table: Table(string, int);
-
- for i in 0..36 do put(&table, "Hellope", i);
- for i in 0..42 do put(&table, "World!", i);
-
- found, _ := find(&table, "Hellope");
- fmt.printf("`found` is %v\n", found);
-
- found, _ = find(&table, "World!");
- fmt.printf("`found` is %v\n", found);
-
- // I would not personally design a hash table like this in production
- // but this is a nice basic example
- // A better approach would either use a `u64` or equivalent for the key
- // and let the user specify the hashing function or make the user store
- // the hashing procedure with the table
- }
-}
-
-
-
-
-prefix_table := [?]string{
- "White",
- "Red",
- "Green",
- "Blue",
- "Octarine",
- "Black",
-};
-
-threading_example :: proc() {
- when ODIN_OS == "windows" {
- fmt.println("# threading_example");
-
- unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
- __bounds_check_error_loc(loc, index, len(array));
- array[index] = array[len(array)-1];
- pop(array);
- }
- ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) {
- __bounds_check_error_loc(loc, index, len(array));
- copy(array[index..], array[index+1..]);
- pop(array);
- }
-
- worker_proc :: proc(t: ^thread.Thread) -> int {
- for iteration in 1..5 {
- fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
- fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
- // win32.sleep(1);
- }
- return 0;
- }
-
- threads := make([dynamic]^thread.Thread, 0, len(prefix_table));
- defer free(threads);
-
- for in prefix_table {
- if t := thread.create(worker_proc); t != nil {
- t.init_context = context;
- t.use_init_context = true;
- t.user_index = len(threads);
- append(&threads, t);
- thread.start(t);
- }
- }
-
- for len(threads) > 0 {
- for i := 0; i < len(threads); /**/ {
- if t := threads[i]; thread.is_done(t) {
- fmt.printf("Thread %d is done\n", t.user_index);
- thread.destroy(t);
-
- ordered_remove(&threads, i);
- } else {
- i += 1;
- }
- }
- }
- }
-}
-
-array_programming :: proc() {
- fmt.println("# array_programming");
- {
- a := [3]f32{1, 2, 3};
- b := [3]f32{5, 6, 7};
- c := a * b;
- d := a + b;
- e := 1 + (c - d) / 2;
- fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5]
- }
-
- {
- a := [3]f32{1, 2, 3};
- b := swizzle(a, 2, 1, 0);
- assert(b == [3]f32{3, 2, 1});
-
- c := swizzle(a, 0, 0);
- assert(c == [2]f32{1, 1});
- assert(c == 1);
- }
-
- {
- Vector3 :: distinct [3]f32;
- a := Vector3{1, 2, 3};
- b := Vector3{5, 6, 7};
- c := (a * b)/2 + 1;
- d := c.x + c.y + c.z;
- fmt.printf("%.1f\n", d); // 22.0
-
- cross :: proc(a, b: Vector3) -> Vector3 {
- i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1);
- j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0);
- return i - j;
- }
-
- blah :: proc(a: Vector3) -> f32 {
- return a.x + a.y + a.z;
- }
-
- x := cross(a, b);
- fmt.println(x);
- fmt.println(blah(x));
- }
-}
-
-
-using println in import "core:fmt.odin"
-
-using_in :: proc() {
- fmt.println("# using in");
- using print in fmt;
-
- println("Hellope1");
- print("Hellope2\n");
-
- Foo :: struct {
- x, y: int,
- b: bool,
- }
- f: Foo;
- f.x, f.y = 123, 321;
- println(f);
- using x, y in f;
- x, y = 456, 654;
- println(f);
-}
-
-named_proc_return_parameters :: proc() {
- fmt.println("# named proc return parameters");
-
- foo0 :: proc() -> int {
- return 123;
- }
- foo1 :: proc() -> (a: int) {
- a = 123;
- return;
- }
- foo2 :: proc() -> (a, b: int) {
- // Named return values act like variables within the scope
- a = 321;
- b = 567;
- return b, a;
- }
- fmt.println("foo0 =", foo0()); // 123
- fmt.println("foo1 =", foo1()); // 123
- fmt.println("foo2 =", foo2()); // 567 321
-}
-
-
-enum_export :: proc() {
- fmt.println("# enum #export");
-
- Foo :: enum #export {A, B, C};
-
- f0 := A;
- f1 := B;
- f2 := C;
- fmt.println(f0, f1, f2);
-}
-
-explicit_procedure_overloading :: proc() {
- fmt.println("# explicit procedure overloading");
-
- add_ints :: proc(a, b: int) -> int {
- x := a + b;
- fmt.println("add_ints", x);
- return x;
- }
- add_floats :: proc(a, b: f32) -> f32 {
- x := a + b;
- fmt.println("add_floats", x);
- return x;
- }
- add_numbers :: proc(a: int, b: f32, c: u8) -> int {
- x := int(a) + int(b) + int(c);
- fmt.println("add_numbers", x);
- return x;
- }
-
- add :: proc[add_ints, add_floats, add_numbers];
-
- add(int(1), int(2));
- add(f32(1), f32(2));
- add(int(1), f32(2), u8(3));
-
- add(1, 2); // untyped ints coerce to int tighter than f32
- add(1.0, 2.0); // untyped floats coerce to f32 tighter than int
- add(1, 2, 3); // three parameters
-
- // Ambiguous answers
- // add(1.0, 2);
- // add(1, 2.0);
-}
-
-complete_switch :: proc() {
- fmt.println("# complete_switch");
- { // enum
- Foo :: enum #export {
- A,
- B,
- C,
- D,
- }
-
- b := Foo.B;
- f := Foo.A;
- #complete switch f {
- case A: fmt.println("A");
- case B: fmt.println("B");
- case C: fmt.println("C");
- case D: fmt.println("D");
- case: fmt.println("?");
- }
- }
- { // union
- Foo :: union {int, bool};
- f: Foo = 123;
- #complete switch in f {
- case int: fmt.println("int");
- case bool: fmt.println("bool");
- case:
- }
- }
-}
-
-
-main :: proc() {
- when true {
- general_stuff();
- default_struct_values();
- union_type();
- parametric_polymorphism();
- threading_example();
- array_programming();
- using_in();
- named_proc_return_parameters();
- enum_export();
- explicit_procedure_overloading();
- complete_switch();
- }
-}
diff --git a/misc/old_demos/old_runtime.odin b/misc/old_demos/old_runtime.odin
deleted file mode 100644
index e605e7820..000000000
--- a/misc/old_demos/old_runtime.odin
+++ /dev/null
@@ -1,412 +0,0 @@
-#include "win32.odin"
-
-assume :: proc(cond: bool) #foreign "llvm.assume"
-
-__debug_trap :: proc() #foreign "llvm.debugtrap"
-__trap :: proc() #foreign "llvm.trap"
-read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter"
-
-bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16"
-bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32"
-bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64"
-
-byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16"
-byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32"
-byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
-
-fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
-fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
-
-// TODO(bill): make custom heap procedures
-heap_alloc :: proc(len: int) -> rawptr #foreign "malloc"
-heap_dealloc :: proc(ptr: rawptr) #foreign "free"
-
-memory_zero :: proc(data: rawptr, len: int) {
- d := slice_ptr(data as ^byte, len)
- for i := 0; i < len; i++ {
- d[i] = 0
- }
-}
-
-memory_compare :: proc(dst, src: rawptr, len: int) -> int {
- s1, s2: ^byte = dst, src
- for i := 0; i < len; i++ {
- a := ptr_offset(s1, i)^
- b := ptr_offset(s2, i)^
- if a != b {
- return (a - b) as int
- }
- }
- return 0
-}
-
-memory_copy :: proc(dst, src: rawptr, n: int) #inline {
- if dst == src {
- return
- }
-
- v128b :: type {4}u32
- #assert(align_of(v128b) == 16)
-
- d, s: ^byte = dst, src
-
- for ; s as uint % 16 != 0 && n != 0; n-- {
- d^ = s^
- d, s = ptr_offset(d, 1), ptr_offset(s, 1)
- }
-
- if d as uint % 16 == 0 {
- for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 {
- (d as ^v128b)^ = (s as ^v128b)^
- }
-
- if n&8 != 0 {
- (d as ^u64)^ = (s as ^u64)^
- d, s = ptr_offset(d, 8), ptr_offset(s, 8)
- }
- if n&4 != 0 {
- (d as ^u32)^ = (s as ^u32)^;
- d, s = ptr_offset(d, 4), ptr_offset(s, 4)
- }
- if n&2 != 0 {
- (d as ^u16)^ = (s as ^u16)^
- d, s = ptr_offset(d, 2), ptr_offset(s, 2)
- }
- if n&1 != 0 {
- d^ = s^
- d, s = ptr_offset(d, 1), ptr_offset(s, 1)
- }
- return;
- }
-
- // IMPORTANT NOTE(bill): Little endian only
- LS :: proc(a, b: u32) -> u32 #inline { return a << b }
- RS :: proc(a, b: u32) -> u32 #inline { return a >> b }
- /* NOTE(bill): Big endian version
- LS :: proc(a, b: u32) -> u32 #inline { return a >> b; }
- RS :: proc(a, b: u32) -> u32 #inline { return a << b; }
- */
-
- w, x: u32
-
- if d as uint % 4 == 1 {
- w = (s as ^u32)^
- d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
- d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
- d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
- n -= 3
-
- for n > 16 {
- d32 := d as ^u32
- s32 := ptr_offset(s, 1) as ^u32
- x = s32^; d32^ = LS(w, 24) | RS(x, 8)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- w = s32^; d32^ = LS(x, 24) | RS(w, 8)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- x = s32^; d32^ = LS(w, 24) | RS(x, 8)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- w = s32^; d32^ = LS(x, 24) | RS(w, 8)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-
- d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
- }
-
- } else if d as uint % 4 == 2 {
- w = (s as ^u32)^
- d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
- d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
- n -= 2
-
- for n > 17 {
- d32 := d as ^u32
- s32 := ptr_offset(s, 2) as ^u32
- x = s32^; d32^ = LS(w, 16) | RS(x, 16)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- w = s32^; d32^ = LS(x, 16) | RS(w, 16)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- x = s32^; d32^ = LS(w, 16) | RS(x, 16)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- w = s32^; d32^ = LS(x, 16) | RS(w, 16)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-
- d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
- }
-
- } else if d as uint % 4 == 3 {
- w = (s as ^u32)^
- d^ = s^
- n -= 1
-
- for n > 18 {
- d32 := d as ^u32
- s32 := ptr_offset(s, 3) as ^u32
- x = s32^; d32^ = LS(w, 8) | RS(x, 24)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- w = s32^; d32^ = LS(x, 8) | RS(w, 24)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- x = s32^; d32^ = LS(w, 8) | RS(x, 24)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
- w = s32^; d32^ = LS(x, 8) | RS(w, 24)
- d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
-
- d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
- }
- }
-
- if n&16 != 0 {
- (d as ^v128b)^ = (s as ^v128b)^
- d, s = ptr_offset(d, 16), ptr_offset(s, 16)
- }
- if n&8 != 0 {
- (d as ^u64)^ = (s as ^u64)^
- d, s = ptr_offset(d, 8), ptr_offset(s, 8)
- }
- if n&4 != 0 {
- (d as ^u32)^ = (s as ^u32)^;
- d, s = ptr_offset(d, 4), ptr_offset(s, 4)
- }
- if n&2 != 0 {
- (d as ^u16)^ = (s as ^u16)^
- d, s = ptr_offset(d, 2), ptr_offset(s, 2)
- }
- if n&1 != 0 {
- d^ = s^
- }
-}
-
-memory_move :: proc(dst, src: rawptr, n: int) #inline {
- d, s: ^byte = dst, src
- if d == s {
- return
- }
- if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s {
- memory_copy(d, s, n)
- return
- }
-
- // TODO(bill): Vectorize the shit out of this
- if d < s {
- if s as int % size_of(int) == d as int % size_of(int) {
- for d as int % size_of(int) != 0 {
- if n == 0 {
- return
- }
- n--
- d^ = s^
- d, s = ptr_offset(d, 1), ptr_offset(s, 1)
- }
- di, si := d as ^int, s as ^int
- for n >= size_of(int) {
- di^ = si^
- di, si = ptr_offset(di, 1), ptr_offset(si, 1)
- n -= size_of(int)
- }
- }
- for ; n > 0; n-- {
- d^ = s^
- d, s = ptr_offset(d, 1), ptr_offset(s, 1)
- }
- } else {
- if s as int % size_of(int) == d as int % size_of(int) {
- for ptr_offset(d, n) as int % size_of(int) != 0 {
- if n == 0 {
- return
- }
- n--
- d^ = s^
- d, s = ptr_offset(d, 1), ptr_offset(s, 1)
- }
- for n >= size_of(int) {
- n -= size_of(int)
- di := ptr_offset(d, n) as ^int
- si := ptr_offset(s, n) as ^int
- di^ = si^
- }
- for ; n > 0; n-- {
- d^ = s^
- d, s = ptr_offset(d, 1), ptr_offset(s, 1)
- }
- }
- for n > 0 {
- n--
- dn := ptr_offset(d, n)
- sn := ptr_offset(s, n)
- dn^ = sn^
- }
- }
-}
-
-__string_eq :: proc(a, b: string) -> bool {
- if len(a) != len(b) {
- return false
- }
- if ^a[0] == ^b[0] {
- return true
- }
- return memory_compare(^a[0], ^b[0], len(a)) == 0
-}
-
-__string_cmp :: proc(a, b : string) -> int {
- min_len := len(a)
- if len(b) < min_len {
- min_len = len(b)
- }
- for i := 0; i < min_len; i++ {
- x := a[i]
- y := b[i]
- if x < y {
- return -1
- } else if x > y {
- return +1
- }
- }
-
- if len(a) < len(b) {
- return -1
- } else if len(a) > len(b) {
- return +1
- }
- return 0
-}
-
-__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
-__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
-__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
-__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
-__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 }
-
-
-
-
-Allocation_Mode :: type enum {
- ALLOC,
- DEALLOC,
- DEALLOC_ALL,
- RESIZE,
-}
-
-
-
-Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
- size, alignment: int,
- old_memory: rawptr, old_size: int, flags: u64) -> rawptr
-
-Allocator :: type struct {
- procedure: Allocator_Proc;
- data: rawptr
-}
-
-
-Context :: type struct {
- thread_ptr: rawptr
-
- user_data: rawptr
- user_index: int
-
- allocator: Allocator
-}
-
-#thread_local context: Context
-
-DEFAULT_ALIGNMENT :: 2*size_of(int)
-
-
-__check_context :: proc() {
- if context.allocator.procedure == null {
- context.allocator = __default_allocator()
- }
- if context.thread_ptr == null {
- // TODO(bill):
- // context.thread_ptr = current_thread_pointer()
- }
-}
-
-
-alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) }
-
-alloc_align :: proc(size, alignment: int) -> rawptr #inline {
- __check_context()
- a := context.allocator
- return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
-}
-
-dealloc :: proc(ptr: rawptr) #inline {
- __check_context()
- a := context.allocator
- _ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
-}
-dealloc_all :: proc(ptr: rawptr) #inline {
- __check_context()
- a := context.allocator
- _ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
-}
-
-
-resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) }
-resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
- __check_context()
- a := context.allocator
- return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
-}
-
-
-
-default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
- if old_memory == null {
- return alloc_align(new_size, alignment)
- }
-
- if new_size == 0 {
- dealloc(old_memory)
- return null
- }
-
- if new_size == old_size {
- return old_memory
- }
-
- new_memory := alloc_align(new_size, alignment)
- if new_memory == null {
- return null
- }
-
- memory_copy(new_memory, old_memory, min(old_size, new_size));
- dealloc(old_memory)
- return new_memory
-}
-
-
-__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
- size, alignment: int,
- old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
- using Allocation_Mode
- match mode {
- case ALLOC:
- return heap_alloc(size)
- case RESIZE:
- return default_resize_align(old_memory, old_size, size, alignment)
- case DEALLOC:
- heap_dealloc(old_memory)
- case DEALLOC_ALL:
- // NOTE(bill): Does nothing
- }
-
- return null
-}
-
-__default_allocator :: proc() -> Allocator {
- return Allocator{
- __default_allocator_proc,
- null,
- }
-}
-
-
-
-
-__assert :: proc(msg: string) {
- file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
- // TODO(bill): Which is better?
- // __trap()
- __debug_trap()
-}
diff --git a/misc/old_stuff/demo_backup.odin b/misc/old_stuff/demo_backup.odin
deleted file mode 100644
index b8bbbb02d..000000000
--- a/misc/old_stuff/demo_backup.odin
+++ /dev/null
@@ -1,430 +0,0 @@
-import (
- "fmt.odin";
- "atomics.odin";
- "bits.odin";
- "decimal.odin";
- "hash.odin";
- "math.odin";
- "mem.odin";
- "opengl.odin";
- "os.odin";
- "raw.odin";
- "strconv.odin";
- "strings.odin";
- "sync.odin";
- "sort.odin";
- "types.odin";
- "utf8.odin";
- "utf16.odin";
-/*
-*/
-)
-
-
-general_stuff :: proc() {
- // Complex numbers
- a := 3 + 4i;
- b: complex64 = 3 + 4i;
- c: complex128 = 3 + 4i;
- d := complex(2, 3);
-
- e := a / conj(a);
- fmt.println("(3+4i)/(3-4i) =", e);
- fmt.println(real(e), "+", imag(e), "i");
-
-
- // C-style variadic procedures
- foreign __llvm_core {
- // The variadic part allows for extra type checking too which C does not provide
- c_printf :: proc(fmt: ^u8, #c_vararg args: ..any) -> i32 #link_name "printf" ---;
- }
- str := "%d\n\x00";
- // c_printf(&str[0], i32(789456123));
-
-
- Foo :: struct {
- x: int;
- y: f32;
- z: string;
- }
- foo := Foo{123, 0.513, "A string"};
- x, y, z := expand_to_tuple(foo);
- fmt.println(x, y, z);
- #assert(type_of(x) == int);
- #assert(type_of(y) == f32);
- #assert(type_of(z) == string);
-
-
- // By default, all variables are zeroed
- // This can be overridden with the "uninitialized value"
- // This is similar to `nil` but applied to everything
- undef_int: int = ---;
-
-
- // Context system is now implemented using Implicit Parameter Passing (IPP)
- // The previous implementation was Thread Local Storage (TLS)
- // IPP has the advantage that it works on systems without TLS and that you can
- // link the context to the stack frame and thus look at previous contexts
- //
- // It does mean that a pointer is implicitly passed procedures with the default
- // Odin calling convention (#cc_odin)
- // This can be overridden with something like #cc_contextless or #cc_c if performance
- // is worried about
-
-}
-
-foreign_blocks :: proc() {
- // See sys/windows.odin
-}
-
-default_arguments :: proc() {
- hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
- fmt.println("\nTesting default arguments:");
- hello(1, 2);
- hello(1);
- hello();
-}
-
-named_arguments :: proc() {
- Colour :: enum {
- Red,
- Orange,
- Yellow,
- Green,
- Blue,
- Octarine,
- };
- using Colour;
-
- make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
- fmt.println();
- fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
- }
-
- make_character("Frank", "”Ay, caramba!", Blue, Green);
-
-
- // As the procedures have more and more parameters, it is very easy
- // to get many of the arguments in the wrong order especialy if the
- // types are the same
- make_character("”Ay, caramba!", "Frank", Green, Blue);
-
- // Named arguments help to disambiguate this problem
- make_character(catch_phrase = "”Ay, caramba!", name = "Frank",
- least_favourite_colour = Green, favourite_colour = Blue);
-
-
- // The named arguments can be specifed in any order.
- make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
- least_favourite_colour = Green, name = "Dennis");
-
-
- // NOTE: You cannot mix named arguments with normal values
- /*
- make_character("Dennis",
- favourite_colour = Octarine, catch_phrase = "U wot m8!",
- least_favourite_colour = Green);
- */
-
-
- // Named arguments can also aid with default arguments
- numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
- d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
- g_str := g ? "true" : "false";
- fmt.printf("How many?! %s: %v\n", s, g_str);
- }
-
- numerous_things("First");
- numerous_things(s = "Second", g = true);
-
-
- // Default values can be placed anywhere, not just at the end like in other languages
- weird :: proc(pre: string, mid: int = 0, post: string) {
- fmt.println(pre, mid, post);
- }
-
- weird("How many things", 42, "huh?");
- weird(pre = "Prefix", post = "Pat");
-
-}
-
-
-default_return_values :: proc() {
- foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
- match x {
- case 0: return;
- case 1: return "Goodbye";
- case 2: return "Goodbye", "cruel world..";
- case 3: return second = "cruel world..", first = "Goodbye";
- }
-
- return second = "my old friend.";
- }
-
- fmt.printf("%s %s\n", foo(0));
- fmt.printf("%s %s\n", foo(1));
- fmt.printf("%s %s\n", foo(2));
- fmt.printf("%s %s\n", foo(3));
- fmt.printf("%s %s\n", foo(4));
- fmt.println();
-
-
- // A more "real" example
- Error :: enum {
- None,
- WhyTheNumberThree,
- TenIsTooBig,
- };
-
- Entity :: struct {
- name: string;
- id: u32;
- }
-
- some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
- match {
- case input == 3: return err = Error.WhyTheNumberThree;
- case input >= 10: return err = Error.TenIsTooBig;
- }
-
- e := new(Entity);
- e.id = u32(input);
-
- return result = e;
- }
-}
-
-call_location :: proc() {
- amazing :: proc(n: int, using loc := #caller_location) {
- fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
- fully_pathed_filename, line, column);
- fmt.printf("Normal -> %d\n", n);
- fmt.printf("Amazing -> %d\n", n+1);
- fmt.println();
- }
-
- loc := #location(main);
- fmt.println("`main` is located at", loc);
-
- fmt.println("This line is located at", #location());
- fmt.println();
-
- amazing(3);
- amazing(4, #location(call_location));
-
- // See _preload.odin for the implementations of `assert` and `panic`
-
-}
-
-
-explicit_parametric_polymorphic_procedures :: proc() {
- // This is how `new` is actually implemented, see _preload.odin
- alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
-
- int_ptr := alloc_type(int);
- defer free(int_ptr);
- int_ptr^ = 137;
- fmt.println(int_ptr, int_ptr^);
-
- // Named arguments work too!
- another_ptr := alloc_type(T = f32);
- defer free(another_ptr);
-
-
- add :: proc(T: type, args: ..T) -> T {
- res: T;
- for arg in args do res += arg;
- return res;
- }
-
- fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
-
- swap :: proc(T: type, a, b: ^T) {
- tmp := a^;
- a^ = b^;
- b^ = tmp;
- }
-
- a, b: int = 3, 4;
- fmt.println("Pre-swap:", a, b);
- swap(int, &a, &b);
- fmt.println("Post-swap:", a, b);
- a, b = b, a; // Or use this syntax for this silly example case
-
-
- Vector2 :: struct {x, y: f32;};
- {
- // A more complicated example using subtyping
- // Something like this could be used in a game
-
- Entity :: struct {
- using position: Vector2;
- flags: u64;
- id: u64;
- derived: any;
- }
-
- Rock :: struct {
- using entity: Entity;
- heavy: bool;
- }
- Door :: struct {
- using entity: Entity;
- open: bool;
- }
- Monster :: struct {
- using entity: Entity;
- is_robot: bool;
- is_zombie: bool;
- }
-
- new_entity :: proc(T: type, x, y: f32) -> ^T {
- result := new(T);
- result.derived = result^;
- result.x = x;
- result.y = y;
-
- return result;
- }
-
- entities: [dynamic]^Entity;
-
- rock := new_entity(Rock, 3, 5);
-
- // Named arguments work too!
- door := new_entity(T = Door, x = 3, y = 6);
-
- // And named arguments can be any order
- monster := new_entity(
- y = 1,
- x = 2,
- T = Monster,
- );
-
- append(&entities, rock, door, monster);
-
- fmt.println("Subtyping");
- for entity in entities {
- match e in entity.derived {
- case Rock: fmt.println("Rock", e.x, e.y);
- case Door: fmt.println("Door", e.x, e.y);
- case Monster: fmt.println("Monster", e.x, e.y);
- }
- }
- }
- {
- Entity :: struct {
- using position: Vector2;
- flags: u64;
- id: u64;
- variant: union { Rock, Door, Monster };
- }
-
- Rock :: struct {
- using entity: ^Entity;
- heavy: bool;
- }
- Door :: struct {
- using entity: ^Entity;
- open: bool;
- }
- Monster :: struct {
- using entity: ^Entity;
- is_robot: bool;
- is_zombie: bool;
- }
-
- new_entity :: proc(T: type, x, y: f32) -> ^T {
- result := new(Entity);
- result.variant = T{entity = result};
- result.x = x;
- result.y = y;
-
- return cast(^T)&result.variant;
- }
-
- entities: [dynamic]^Entity;
-
- rock := new_entity(Rock, 3, 5);
-
- // Named arguments work too!
- door := new_entity(T = Door, x = 3, y = 6);
-
- // And named arguments can be any order
- monster := new_entity(
- y = 1,
- x = 2,
- T = Monster,
- );
-
- append(&entities, rock, door, monster);
-
- fmt.println("Union");
- for entity in entities {
- match e in entity.variant {
- case Rock: fmt.println("Rock", e.x, e.y);
- case Door: fmt.println("Door", e.x, e.y);
- case Monster: fmt.println("Monster", e.x, e.y);
- }
- }
- }
-}
-
-
-implicit_polymorphic_assignment :: proc() {
- yep :: proc(p: proc(x: int)) {
- p(123);
- }
-
- frank :: proc(x: $T) do fmt.println("frank ->", x);
- tim :: proc(x, y: $T) do fmt.println("tim ->", x, y);
- yep(frank);
- // yep(tim);
-}
-
-
-
-
-main :: proc() {
-/*
- foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y);
- foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
- foo :: proc(x: type) do fmt.println("#3", type_info(x));
-
- f :: foo;
-
- f(y = 3785.1546, x = 123);
- f(x = int, y = 897.513);
- f(x = f32);
-
- general_stuff();
- foreign_blocks();
- default_arguments();
- named_arguments();
- default_return_values();
- call_location();
- explicit_parametric_polymorphic_procedures();
- implicit_polymorphic_assignment();
-
-
- // Command line argument(s)!
- // -opt=0,1,2,3
-*/
-/*
- program := "+ + * - /";
- accumulator := 0;
-
- for token in program {
- match token {
- case '+': accumulator += 1;
- case '-': accumulator -= 1;
- case '*': accumulator *= 2;
- case '/': accumulator /= 2;
- case: // Ignore everything else
- }
- }
-
- fmt.printf("The program \"%s\" calculates the value %d\n",
- program, accumulator);
-*/
-}
diff --git a/misc/remove_libraries_for_other_platforms.sh b/misc/remove_libraries_for_other_platforms.sh
new file mode 100755
index 000000000..db2e33ccd
--- /dev/null
+++ b/misc/remove_libraries_for_other_platforms.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+OS=$(uname)
+
+panic() {
+ printf "%s\n" "$1"
+ exit 1
+}
+
+assert_vendor() {
+ if [ $(basename $(pwd)) != 'vendor' ]; then
+ panic "Not in vendor directory!"
+ fi
+}
+
+remove_windows_libraries() {
+ find . -type f -name '*.dll' | xargs rm -f
+ find . -type f -name '*.lib' | xargs rm -f
+ find . -type d -name 'windows' | xargs rm -rf
+}
+
+remove_macos_libraries() {
+ find . -type f -name '*.dylib' | xargs rm -f
+ find . -type d -name '*macos*' | xargs rm -rf
+}
+
+remove_linux_libraries() {
+ find . -type f -name '*.so' | xargs rm -f
+ find . -type d -name 'linux' | xargs rm -rf
+}
+
+case $OS in
+ Linux)
+ assert_vendor
+ remove_windows_libraries
+ remove_macos_libraries
+ ;;
+ Darwin)
+ assert_vendor
+ remove_windows_libraries
+ remove_linux_libraries
+ ;;
+ OpenBSD)
+ assert_vendor
+ remove_windows_libraries
+ remove_macos_libraries
+ remove_linux_libraries
+ ;;
+ FreeBSD)
+ assert_vendor
+ remove_windows_libraries
+ remove_macos_libraries
+ remove_linux_libraries
+ ;;
+*)
+ panic "Platform unsupported!"
+esac
diff --git a/misc/shell.bat b/misc/shell.bat
index 60f603bc1..bfb444396 100644
--- a/misc/shell.bat
+++ b/misc/shell.bat
@@ -7,5 +7,7 @@ rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxil
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
set _NO_DEBUG_HEAP=1
+set ODIN_IGNORE_MSVC_CHECK=1
+
rem set path=w:\Odin\misc;%path%
cls
diff --git a/src/array.cpp b/src/array.cpp
index d08bd647f..d8e25d25d 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -10,45 +10,45 @@ struct Array {
T &operator[](isize index) {
#if !defined(NO_ARRAY_BOUNDS_CHECK)
- GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+ GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count);
#endif
return data[index];
}
T const &operator[](isize index) const {
#if !defined(NO_ARRAY_BOUNDS_CHECK)
- GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
+ GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count);
#endif
return data[index];
}
};
-template void array_init (Array *array, gbAllocator const &a);
-template void array_init (Array *array, gbAllocator const &a, isize count);
-template void array_init (Array *array, gbAllocator const &a, isize count, isize capacity);
-template Array array_make (gbAllocator const &a);
-template Array array_make (gbAllocator const &a, isize count);
-template Array array_make (gbAllocator const &a, isize count, isize capacity);
-template Array array_make_from_ptr (T *data, isize count, isize capacity);
-template void array_free (Array *array);
-template void array_add (Array *array, T const &t);
-template T * array_add_and_get (Array *array);
-template void array_add_elems (Array *array, T const *elems, isize elem_count);
-template T array_pop (Array *array);
-template void array_clear (Array *array);
-template void array_reserve (Array *array, isize capacity);
-template void array_resize (Array *array, isize count);
-template void array_set_capacity (Array *array, isize capacity);
-template Array array_slice (Array const &array, isize lo, isize hi);
-template Array array_clone (gbAllocator const &a, Array const &array);
+template gb_internal void array_init (Array *array, gbAllocator const &a);
+template gb_internal void array_init (Array *array, gbAllocator const &a, isize count);
+template gb_internal void array_init (Array *array, gbAllocator const &a, isize count, isize capacity);
+template gb_internal Array array_make (gbAllocator const &a);
+template gb_internal Array array_make (gbAllocator const &a, isize count);
+template gb_internal Array array_make (gbAllocator const &a, isize count, isize capacity);
+template gb_internal Array array_make_from_ptr (T *data, isize count, isize capacity);
+template gb_internal void array_free (Array *array);
+template gb_internal void array_add (Array *array, T const &t);
+template gb_internal T * array_add_and_get (Array *array);
+template gb_internal void array_add_elems (Array *array, T const *elems, isize elem_count);
+template gb_internal T array_pop (Array *array);
+template gb_internal void array_clear (Array *array);
+template gb_internal void array_reserve (Array *array, isize capacity);
+template gb_internal void array_resize (Array *array, isize count);
+template gb_internal void array_set_capacity (Array *array, isize capacity);
+template gb_internal Array array_slice (Array const &array, isize lo, isize hi);
+template gb_internal Array array_clone (gbAllocator const &a, Array const &array);
-template void array_ordered_remove (Array *array, isize index);
-template void array_unordered_remove(Array