diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 89b7ffa10..885612f13 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -1,3 +1,4 @@
# These are supported funding model platforms
+github: odin-lang
patreon: gingerbill
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6742b56f3..d72775636 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -7,9 +7,9 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Download LLVM, botan
- run: sudo apt-get install llvm-11 clang-11 llvm libbotan-2-dev botan
+ run: sudo apt-get install llvm-11 clang-11 libbotan-2-dev botan
- name: build odin
- run: make release
+ run: ./build_odin.sh release
- name: Odin version
run: ./odin version
timeout-minutes: 1
@@ -17,13 +17,16 @@ jobs:
run: ./odin report
timeout-minutes: 1
- name: Odin check
- run: ./odin check examples/demo/demo.odin -vet
+ run: ./odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
- run: ./odin run examples/demo/demo.odin
+ run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
- run: ./odin run examples/demo/demo.odin -debug
+ run: ./odin run examples/demo -debug
+ timeout-minutes: 10
+ - name: Odin check examples/all
+ run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
run: |
@@ -35,6 +38,20 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
+ - name: Odin issues tests
+ run: |
+ cd tests/issues
+ ./run.sh
+ timeout-minutes: 10
+ - name: Odin check examples/all for Linux i386
+ run: ./odin check examples/all -vet -strict-style -target:linux_i386
+ timeout-minutes: 10
+ - name: Odin check examples/all for FreeBSD amd64
+ run: ./odin check examples/all -vet -strict-style -target:freebsd_amd64
+ timeout-minutes: 10
+ - name: Odin check examples/all for OpenBSD amd64
+ run: ./odin check examples/all -vet -strict-style -target:openbsd_amd64
+ timeout-minutes: 10
build_macOS:
runs-on: macos-latest
steps:
@@ -46,7 +63,7 @@ jobs:
TMP_PATH=$(xcrun --show-sdk-path)/user/include
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
- name: build odin
- run: make release
+ run: ./build_odin.sh release
- name: Odin version
run: ./odin version
timeout-minutes: 1
@@ -54,13 +71,16 @@ jobs:
run: ./odin report
timeout-minutes: 1
- name: Odin check
- run: ./odin check examples/demo/demo.odin -vet
+ run: ./odin check examples/demo -vet
timeout-minutes: 10
- name: Odin run
- run: ./odin run examples/demo/demo.odin
+ run: ./odin run examples/demo
timeout-minutes: 10
- name: Odin run -debug
- run: ./odin run examples/demo/demo.odin -debug
+ run: ./odin run examples/demo -debug
+ timeout-minutes: 10
+ - name: Odin check examples/all
+ run: ./odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
run: |
@@ -72,8 +92,19 @@ jobs:
cd tests/vendor
make
timeout-minutes: 10
+ - name: Odin issues tests
+ run: |
+ cd tests/issues
+ ./run.sh
+ timeout-minutes: 10
+ - name: Odin check examples/all for Darwin arm64
+ run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
+ timeout-minutes: 10
+ - name: Odin check examples/all for Linux arm64
+ run: ./odin check examples/all -vet -strict-style -target:linux_arm64
+ timeout-minutes: 10
build_windows:
- runs-on: windows-latest
+ runs-on: windows-2019
steps:
- uses: actions/checkout@v1
- name: build Odin
@@ -91,19 +122,25 @@ jobs:
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
- odin check examples/demo/demo.odin -vet
+ 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
- odin run examples/demo/demo.odin
+ 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
- odin run examples/demo/demo.odin -debug
+ 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
+ odin check examples/all -strict-style
timeout-minutes: 10
- name: Core library tests
shell: cmd
@@ -126,3 +163,16 @@ jobs:
cd tests\core\math\big
call build.bat
timeout-minutes: 10
+ - name: Odin issues tests
+ shell: cmd
+ run: |
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+ cd tests\issues
+ call run.bat
+ timeout-minutes: 10
+ - name: Odin check examples/all for Windows 32bits
+ shell: cmd
+ run: |
+ call "C:\Program Files (x86)\Microsoft Visual Studio\2019\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 2b33c45a8..3c4185830 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -7,7 +7,7 @@ on:
jobs:
build_windows:
- runs-on: windows-latest
+ runs-on: windows-2019
steps:
- uses: actions/checkout@v1
- name: build Odin
@@ -19,7 +19,7 @@ jobs:
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
- odin run examples/demo/demo.odin
+ odin run examples/demo
- name: Copy artifacts
run: |
rm bin/llvm/windows/LLVM-C.lib
@@ -41,11 +41,11 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: (Linux) Download LLVM
- run: sudo apt-get install llvm-11 clang-11 llvm
+ run: sudo apt-get install llvm-11 clang-11
- name: build odin
run: make nightly
- name: Odin run
- run: ./odin run examples/demo/demo.odin
+ run: ./odin run examples/demo
- name: Copy artifacts
run: |
mkdir dist
@@ -72,7 +72,7 @@ jobs:
- name: build odin
run: make nightly
- name: Odin run
- run: ./odin run examples/demo/demo.odin
+ run: ./odin run examples/demo
- name: Copy artifacts
run: |
mkdir dist
@@ -129,7 +129,7 @@ jobs:
run: |
echo Authorizing B2 account
b2 authorize-account "$APPID" "$APPKEY"
-
+
echo Uploading artifcates to B2
chmod +x ./ci/upload_create_nightly.sh
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
@@ -141,7 +141,7 @@ jobs:
echo Creating nightly.json
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
-
+
echo Uploading nightly.json
b2 upload-file "$BUCKET" nightly.json nightly.json
diff --git a/.gitignore b/.gitignore
index 0d606498e..e8b3d3050 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,9 @@
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
+# For macOS
+.DS_Store
+
# Build results
[Dd]ebug/
[Dd]ebugPublic/
@@ -276,3 +279,5 @@ shared/
*.ll
*.sublime-workspace
+examples/bug/
+build.sh
diff --git a/LICENSE b/LICENSE
index 9ca94bcdf..9a87ab8da 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
+Copyright (c) 2016-2022 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
diff --git a/Makefile b/Makefile
index 23fb7be66..91010a620 100644
--- a/Makefile
+++ b/Makefile
@@ -1,58 +1,19 @@
-GIT_SHA=$(shell git rev-parse --short HEAD)
-DISABLED_WARNINGS=-Wno-switch -Wno-macro-redefined -Wno-unused-value
-LDFLAGS=-pthread -ldl -lm -lstdc++
-CFLAGS=-std=c++14 -DGIT_SHA=\"$(GIT_SHA)\"
-CFLAGS:=$(CFLAGS) -DODIN_VERSION_RAW=\"dev-$(shell date +"%Y-%m")\"
-CC=clang
-
-OS=$(shell uname)
-
-ifeq ($(OS), Darwin)
- LLVM_CONFIG=llvm-config
- ifneq ($(shell llvm-config --version | grep '^11\.'),)
- LLVM_CONFIG=llvm-config
- else
- $(error "Requirement: llvm-config must be version 11")
- endif
-
- LDFLAGS:=$(LDFLAGS) -liconv
- CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
- LDFLAGS:=$(LDFLAGS) -lLLVM-C
-endif
-ifeq ($(OS), Linux)
- LLVM_CONFIG=llvm-config-11
- ifneq ($(shell which llvm-config-11 2>/dev/null),)
- LLVM_CONFIG=llvm-config-11
- else ifneq ($(shell which llvm-config-11-64 2>/dev/null),)
- LLVM_CONFIG=llvm-config-11-64
- else
- ifneq ($(shell llvm-config --version | grep '^11\.'),)
- LLVM_CONFIG=llvm-config
- else
- $(error "Requirement: llvm-config must be version 11")
- endif
- endif
-
- CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
- LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
-endif
-
-all: debug demo
+all: debug
demo:
- ./odin run examples/demo/demo.odin
+ ./odin run examples/demo/demo.odin -file
report:
./odin report
debug:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -g $(LDFLAGS) -o odin
+ ./build_odin.sh debug
release:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 $(LDFLAGS) -o odin
+ ./build_odin.sh release
release_native:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -O3 -march=native $(LDFLAGS) -o odin
+ ./build_odin.sh release-native
nightly:
- $(CC) src/main.cpp src/libtommath.cpp $(DISABLED_WARNINGS) $(CFLAGS) -DNIGHTLY -O3 $(LDFLAGS) -o odin
+ ./build_odin.sh nightly
diff --git a/README.md b/README.md
index b1d0a39a1..9e46f80d0 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@
-
+
@@ -58,6 +58,10 @@ main :: proc() {
Instructions for downloading and installing the Odin compiler and libraries.
+#### [Nightly Builds](https://odin-lang.org/docs/nightly/)
+
+Get the latest nightly builds of Odin.
+
### Learning Odin
#### [Overview of Odin](https://odin-lang.org/docs/overview)
@@ -68,6 +72,10 @@ An overview of the Odin programming language.
Answers to common questions about Odin.
+#### [Packages](https://pkg.odin-lang.org/)
+
+Documentation for all the official packages part of the [core](https://pkg.odin-lang.org/core/) and [vendor](https://pkg.odin-lang.org/vendor/) library collections.
+
#### [The Odin Wiki](https://github.com/odin-lang/Odin/wiki)
A wiki maintained by the Odin community.
@@ -76,15 +84,9 @@ A wiki maintained by the Odin community.
Get live support and talk with other odiners on the Odin Discord.
-### References
-
-#### [Language Specification](https://odin-lang.org/docs/spec/)
-
-The official Odin Language specification.
-
### Articles
-#### [The Odin Blog](https://odin-lang.org/blog)
+#### [The Odin Blog](https://odin-lang.org/news/)
The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.
diff --git a/build_odin.sh b/build_odin.sh
new file mode 100755
index 000000000..aef3f2836
--- /dev/null
+++ b/build_odin.sh
@@ -0,0 +1,150 @@
+#!/bin/bash
+set -eu
+
+GIT_SHA=$(git rev-parse --short HEAD)
+DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
+LDFLAGS="-pthread -lm -lstdc++"
+CFLAGS="-std=c++14 -DGIT_SHA=\"$GIT_SHA\""
+CFLAGS="$CFLAGS -DODIN_VERSION_RAW=\"dev-$(date +"%Y-%m")\""
+CC=clang
+OS=$(uname)
+
+panic() {
+ printf "%s\n" "$1"
+ exit 1
+}
+
+version() { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
+
+config_darwin() {
+ ARCH=$(uname -m)
+ LLVM_CONFIG=llvm-config
+
+ # allow for arm only llvm's with version 13
+ if [ ARCH == arm64 ]; then
+ MIN_LLVM_VERSION=("13.0.0")
+ else
+ # allow for x86 / amd64 all llvm versions begining from 11
+ MIN_LLVM_VERSION=("11.1.0")
+ fi
+
+ if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; 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"
+ fi
+ fi
+
+ LDFLAGS="$LDFLAGS -liconv -ldl"
+ CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ LDFLAGS="$LDFLAGS -lLLVM-C"
+}
+
+config_freebsd() {
+ LLVM_CONFIG=/usr/local/bin/llvm-config11
+
+ CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+}
+
+config_openbsd() {
+ LLVM_CONFIG=/usr/local/bin/llvm-config
+
+ LDFLAGS="$LDFLAGS -liconv"
+ CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+}
+
+config_linux() {
+ if which llvm-config > /dev/null 2>&1; then
+ LLVM_CONFIG=llvm-config
+ elif which llvm-config-11 > /dev/null 2>&1; then
+ LLVM_CONFIG=llvm-config-11
+ elif which llvm-config-11-64 > /dev/null 2>&1; then
+ LLVM_CONFIG=llvm-config-11-64
+ else
+ panic "Unable to find LLVM-config"
+ fi
+
+ MIN_LLVM_VERSION=("11.0.0")
+ if [ $(version $($LLVM_CONFIG --version)) -lt $(version $MIN_LLVM_VERSION) ]; then
+ echo "Tried to use " $(which $LLVM_CONFIG) "version" $($LLVM_CONFIG --version)
+ panic "Requirement: llvm-config must be base version greater than 11"
+ fi
+
+ LDFLAGS="$LDFLAGS -ldl"
+ CFLAGS="$CFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
+ LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
+}
+
+build_odin() {
+ case $1 in
+ debug)
+ EXTRAFLAGS="-g"
+ ;;
+ release)
+ EXTRAFLAGS="-O3"
+ ;;
+ release-native)
+ EXTRAFLAGS="-O3 -march=native"
+ ;;
+ nightly)
+ EXTRAFLAGS="-DNIGHTLY -O3"
+ ;;
+ *)
+ panic "Build mode unsupported!"
+ esac
+
+ set -x
+ $CC src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CFLAGS $EXTRAFLAGS $LDFLAGS -o odin
+ set +x
+}
+
+run_demo() {
+ ./odin run examples/demo/demo.odin -file
+}
+
+case $OS in
+Linux)
+ config_linux
+ ;;
+Darwin)
+ config_darwin
+ ;;
+OpenBSD)
+ config_openbsd
+ ;;
+FreeBSD)
+ config_freebsd
+ ;;
+*)
+ panic "Platform unsupported!"
+esac
+
+if [[ $# -eq 0 ]]; then
+ build_odin debug
+ run_demo
+ exit 0
+fi
+
+if [[ $# -eq 1 ]]; then
+ case $1 in
+ report)
+ if [[ ! -f "./odin" ]]; then
+ build_odin debug
+ fi
+
+ ./odin report
+ exit 0
+ ;;
+ *)
+ build_odin $1
+ ;;
+ esac
+
+ run_demo
+ exit 0
+else
+ panic "Too many arguments!"
+fi
diff --git a/core/bufio/scanner.odin b/core/bufio/scanner.odin
index cc3b4533a..86e6d8eb0 100644
--- a/core/bufio/scanner.odin
+++ b/core/bufio/scanner.odin
@@ -8,6 +8,7 @@ import "core:intrinsics"
// Extra errors returns by scanning procedures
Scanner_Extra_Error :: enum i32 {
+ None,
Negative_Advance,
Advanced_Too_Far,
Bad_Read_Count,
@@ -15,7 +16,7 @@ Scanner_Extra_Error :: enum i32 {
Too_Short,
}
-Scanner_Error :: union {
+Scanner_Error :: union #shared_nil {
io.Error,
Scanner_Extra_Error,
}
@@ -68,7 +69,7 @@ scanner_destroy :: proc(s: ^Scanner) {
// Returns the first non-EOF error that was encounted by the scanner
scanner_error :: proc(s: ^Scanner) -> Scanner_Error {
switch s._err {
- case .EOF, .None:
+ case .EOF, nil:
return nil
}
return s._err
@@ -93,10 +94,6 @@ scanner_text :: proc(s: ^Scanner) -> string {
// scanner_scan advances the scanner
scanner_scan :: proc(s: ^Scanner) -> bool {
set_err :: proc(s: ^Scanner, err: Scanner_Error) {
- err := err
- if err == .None {
- err = nil
- }
switch s._err {
case nil, .EOF:
s._err = err
diff --git a/core/builtin/builtin.odin b/core/builtin/builtin.odin
index 74283720f..259fdef37 100644
--- a/core/builtin/builtin.odin
+++ b/core/builtin/builtin.odin
@@ -2,7 +2,7 @@
package builtin
nil :: nil;
-false :: 0!==0;
+false :: 0!=0;
true :: 0==0;
ODIN_OS :: ODIN_OS;
diff --git a/core/bytes/bytes.odin b/core/bytes/bytes.odin
index cbc1e2506..09a3ed259 100644
--- a/core/bytes/bytes.odin
+++ b/core/bytes/bytes.odin
@@ -5,9 +5,8 @@ import "core:unicode"
import "core:unicode/utf8"
clone :: proc(s: []byte, allocator := context.allocator, loc := #caller_location) -> []byte {
- c := make([]byte, len(s)+1, allocator, loc)
+ c := make([]byte, len(s), allocator, loc)
copy(c, s)
- c[len(s)] = 0
return c[:len(s)]
}
@@ -219,61 +218,37 @@ split_after_n :: proc(s, sep: []byte, n: int, allocator := context.allocator) ->
@private
-_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save, n: int) -> (res: []byte, ok: bool) {
- s, n := s, n
-
- if n == 0 {
- return
- }
-
- if sep == nil {
+_split_iterator :: proc(s: ^[]byte, sep: []byte, sep_save: int) -> (res: []byte, ok: bool) {
+ if len(sep) == 0 {
res = s[:]
ok = true
s^ = s[len(s):]
return
}
- if n < 0 {
- n = count(s^, sep) + 1
- }
-
- n -= 1
-
- i := 0
- for ; i < n; i += 1 {
- m := index(s^, sep)
- if m < 0 {
- break
- }
+ m := index(s^, sep)
+ if m < 0 {
+ // not found
+ res = s[:]
+ ok = len(res) != 0
+ s^ = s[len(s):]
+ } else {
res = s[:m+sep_save]
ok = true
s^ = s[m+len(sep):]
- return
}
- res = s[:]
- ok = res != nil
- s^ = s[len(s):]
return
}
split_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
- return _split_iterator(s, sep, 0, -1)
-}
-
-split_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
- return _split_iterator(s, sep, 0, n)
+ return _split_iterator(s, sep, 0)
}
split_after_iterator :: proc(s: ^[]byte, sep: []byte) -> ([]byte, bool) {
- return _split_iterator(s, sep, len(sep), -1)
+ return _split_iterator(s, sep, len(sep))
}
-split_after_n_iterator :: proc(s: ^[]byte, sep: []byte, n: int) -> ([]byte, bool) {
- return _split_iterator(s, sep, len(sep), n)
-}
-
-
index_byte :: proc(s: []byte, c: byte) -> int {
for i := 0; i < len(s); i += 1 {
@@ -1143,7 +1118,7 @@ fields_proc :: proc(s: []byte, f: proc(rune) -> bool, allocator := context.alloc
}
if start >= 0 {
- append(&subslices, s[start : end])
+ append(&subslices, s[start : len(s)])
}
return subslices[:]
diff --git a/core/c/c.odin b/core/c/c.odin
index d135fa93c..05732476f 100644
--- a/core/c/c.odin
+++ b/core/c/c.odin
@@ -3,22 +3,24 @@ package c
import builtin "core:builtin"
char :: builtin.u8 // assuming -funsigned-char
+
+schar :: builtin.i8
short :: builtin.i16
int :: builtin.i32
-long :: builtin.i32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.i64
+long :: builtin.i32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.i64
longlong :: builtin.i64
uchar :: builtin.u8
ushort :: builtin.u16
uint :: builtin.u32
-ulong :: builtin.u32 when (ODIN_OS == "windows" || size_of(builtin.rawptr) == 4) else builtin.u64
+ulong :: builtin.u32 when (ODIN_OS == .Windows || size_of(builtin.rawptr) == 4) else builtin.u64
ulonglong :: builtin.u64
bool :: builtin.bool
size_t :: builtin.uint
ssize_t :: builtin.int
-wchar_t :: builtin.u16 when (ODIN_OS == "windows") else builtin.u32
+wchar_t :: builtin.u16 when (ODIN_OS == .Windows) else builtin.u32
float :: builtin.f32
double :: builtin.f64
@@ -46,7 +48,7 @@ int_least64_t :: builtin.i64
uint_least64_t :: builtin.u64
// Same on Windows, Linux, and FreeBSD
-when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
+when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
int_fast8_t :: builtin.i8
uint_fast8_t :: builtin.u8
int_fast16_t :: builtin.i32
diff --git a/core/c/frontend/preprocessor/preprocess.odin b/core/c/frontend/preprocessor/preprocess.odin
index 62b4183bc..9651cc81c 100644
--- a/core/c/frontend/preprocessor/preprocess.odin
+++ b/core/c/frontend/preprocessor/preprocess.odin
@@ -956,7 +956,7 @@ substitute_token :: proc(cpp: ^Preprocessor, tok: ^Token, args: ^Macro_Arg) -> ^
continue
}
- if tok.lit == "__VA__OPT__" && tok.next.lit == "(" {
+ if tok.lit == "__VA_OPT__" && tok.next.lit == "(" {
opt_arg := read_macro_arg_one(cpp, &tok, tok.next.next, true)
if has_varargs(args) {
for t := opt_arg.tok; t.kind != .EOF; t = t.next {
diff --git a/core/c/libc/complex.odin b/core/c/libc/complex.odin
index 62b28f0cd..22c422cce 100644
--- a/core/c/libc/complex.odin
+++ b/core/c/libc/complex.odin
@@ -2,9 +2,9 @@ package libc
// 7.3 Complex arithmetic
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
diff --git a/core/c/libc/ctype.odin b/core/c/libc/ctype.odin
index 05d9dcd37..185385a5e 100644
--- a/core/c/libc/ctype.odin
+++ b/core/c/libc/ctype.odin
@@ -1,8 +1,8 @@
package libc
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
diff --git a/core/c/libc/errno.odin b/core/c/libc/errno.odin
index 8ebe2f734..53437f42f 100644
--- a/core/c/libc/errno.odin
+++ b/core/c/libc/errno.odin
@@ -2,9 +2,9 @@ package libc
// 7.5 Errors
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
@@ -14,7 +14,7 @@ when ODIN_OS == "windows" {
// EDOM,
// EILSEQ
// ERANGE
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@@ -27,7 +27,20 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
ERANGE :: 34
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .OpenBSD {
+ @(private="file")
+ @(default_calling_convention="c")
+ foreign libc {
+ @(link_name="__errno")
+ _get_errno :: proc() -> ^int ---
+ }
+
+ EDOM :: 33
+ EILSEQ :: 84
+ ERANGE :: 34
+}
+
+when ODIN_OS == .Windows {
@(private="file")
@(default_calling_convention="c")
foreign libc {
@@ -40,7 +53,7 @@ when ODIN_OS == "windows" {
ERANGE :: 34
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
@(private="file")
@(default_calling_convention="c")
foreign libc {
diff --git a/core/c/libc/math.odin b/core/c/libc/math.odin
index ee702b82e..97f77236f 100644
--- a/core/c/libc/math.odin
+++ b/core/c/libc/math.odin
@@ -4,9 +4,9 @@ package libc
import "core:intrinsics"
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
diff --git a/core/c/libc/setjmp.odin b/core/c/libc/setjmp.odin
index dcd4a9c64..c2cd1d047 100644
--- a/core/c/libc/setjmp.odin
+++ b/core/c/libc/setjmp.odin
@@ -2,14 +2,14 @@ package libc
// 7.13 Nonlocal jumps
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
@(default_calling_convention="c")
foreign libc {
// 7.13.1 Save calling environment
diff --git a/core/c/libc/signal.odin b/core/c/libc/signal.odin
index e1044dc02..186b74d8c 100644
--- a/core/c/libc/signal.odin
+++ b/core/c/libc/signal.odin
@@ -2,9 +2,9 @@ package libc
// 7.14 Signal handling
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
@@ -21,7 +21,7 @@ foreign libc {
raise :: proc(sig: int) -> int ---
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
@@ -34,7 +34,7 @@ when ODIN_OS == "windows" {
SIGTERM :: 15
}
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
@@ -47,7 +47,7 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" {
SIGTERM :: 15
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
SIG_ERR :: rawptr(~uintptr(0))
SIG_DFL :: rawptr(uintptr(0))
SIG_IGN :: rawptr(uintptr(1))
diff --git a/core/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin
index fbe1ef7ea..6e1581c58 100644
--- a/core/c/libc/stdatomic.odin
+++ b/core/c/libc/stdatomic.odin
@@ -47,29 +47,30 @@ kill_dependency :: #force_inline proc(value: $T) -> T {
// 7.17.4 Fences
atomic_thread_fence :: #force_inline proc(order: memory_order) {
- switch (order) {
- case .relaxed:
- return
- case .consume:
- intrinsics.atomic_fence_acq()
- case .acquire:
- intrinsics.atomic_fence_acq()
- case .release:
- intrinsics.atomic_fence_rel()
- case .acq_rel:
- intrinsics.atomic_fence_acqrel()
- case .seq_cst:
- intrinsics.atomic_fence_acqrel()
+ assert(order != .relaxed)
+ assert(order != .consume)
+ #partial switch order {
+ case .acquire: intrinsics.atomic_thread_fence(.Acquire)
+ case .release: intrinsics.atomic_thread_fence(.Release)
+ case .acq_rel: intrinsics.atomic_thread_fence(.Acq_Rel)
+ case .seq_cst: intrinsics.atomic_thread_fence(.Seq_Cst)
}
}
atomic_signal_fence :: #force_inline proc(order: memory_order) {
- atomic_thread_fence(order)
+ assert(order != .relaxed)
+ assert(order != .consume)
+ #partial switch order {
+ case .acquire: intrinsics.atomic_signal_fence(.Acquire)
+ case .release: intrinsics.atomic_signal_fence(.Release)
+ case .acq_rel: intrinsics.atomic_signal_fence(.Acq_Rel)
+ case .seq_cst: intrinsics.atomic_signal_fence(.Seq_Cst)
+ }
}
// 7.17.5 Lock-free property
atomic_is_lock_free :: #force_inline proc(obj: ^$T) -> bool {
- return size_of(T) <= 8 && (intrinsics.type_is_integer(T) || intrinsics.type_is_pointer(T))
+ return intrinsics.atomic_type_is_lock_free(T)
}
// 7.17.6 Atomic integer types
@@ -121,13 +122,10 @@ atomic_store_explicit :: #force_inline proc(object: ^$T, desired: T, order: memo
assert(order != .acquire)
assert(order != .acq_rel)
- #partial switch (order) {
- case .relaxed:
- intrinsics.atomic_store_relaxed(object, desired)
- case .release:
- intrinsics.atomic_store_rel(object, desired)
- case .seq_cst:
- intrinsics.atomic_store(object, desired)
+ #partial switch order {
+ case .relaxed: intrinsics.atomic_store_explicit(object, desired, .Relaxed)
+ case .release: intrinsics.atomic_store_explicit(object, desired, .Release)
+ case .seq_cst: intrinsics.atomic_store_explicit(object, desired, .Seq_Cst)
}
}
@@ -139,36 +137,26 @@ atomic_load_explicit :: #force_inline proc(object: ^$T, order: memory_order) {
assert(order != .release)
assert(order != .acq_rel)
- #partial switch (order) {
- case .relaxed:
- return intrinsics.atomic_load_relaxed(object)
- case .consume:
- return intrinsics.atomic_load_acq(object)
- case .acquire:
- return intrinsics.atomic_load_acq(object)
- case .seq_cst:
- return intrinsics.atomic_load(object)
+ #partial switch order {
+ case .relaxed: return intrinsics.atomic_load_explicit(object, .Relaxed)
+ case .consume: return intrinsics.atomic_load_explicit(object, .Consume)
+ case .acquire: return intrinsics.atomic_load_explicit(object, .Acquire)
+ case .seq_cst: return intrinsics.atomic_load_explicit(object, .Seq_Cst)
}
}
atomic_exchange :: #force_inline proc(object: ^$T, desired: T) -> T {
- return intrinsics.atomic_xchg(object, desired)
+ return intrinsics.atomic_exchange(object, desired)
}
atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_xchg_relaxed(object, desired)
- case .consume:
- return intrinsics.atomic_xchg_acq(object, desired)
- case .acquire:
- return intrinsics.atomic_xchg_acq(object, desired)
- case .release:
- return intrinsics.atomic_xchg_rel(object, desired)
- case .acq_rel:
- return intrinsics.atomic_xchg_acqrel(object, desired)
- case .seq_cst:
- return intrinsics.atomic_xchg(object, desired)
+ switch order {
+ case .relaxed: return intrinsics.atomic_exchange_explicit(object, desired, .Relaxed)
+ case .consume: return intrinsics.atomic_exchange_explicit(object, desired, .Consume)
+ case .acquire: return intrinsics.atomic_exchange_explicit(object, desired, .Acquire)
+ case .release: return intrinsics.atomic_exchange_explicit(object, desired, .Release)
+ case .acq_rel: return intrinsics.atomic_exchange_explicit(object, desired, .Acq_Rel)
+ case .seq_cst: return intrinsics.atomic_exchange_explicit(object, desired, .Seq_Cst)
}
return false
}
@@ -189,102 +177,104 @@ atomic_exchange_explicit :: #force_inline proc(object: ^$T, desired: T, order: m
// [success = seq_cst, failure = acquire] => failacq
// [success = acquire, failure = relaxed] => acq_failrelaxed
// [success = acq_rel, failure = relaxed] => acqrel_failrelaxed
-atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) {
- value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
+atomic_compare_exchange_strong :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
+ value, ok := intrinsics.atomic_compare_exchange_strong(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
-atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) {
+atomic_compare_exchange_strong_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
- #partial switch (failure) {
+ #partial switch failure {
case .seq_cst:
assert(success != .relaxed)
- #partial switch (success) {
+ #partial switch success {
case .seq_cst:
- value, ok := intrinsics.atomic_cxchg(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
- value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
- value, ok := intrinsics.atomic_cxchg_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
- value, ok := intrinsics.atomic_cxchg_rel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchg_acqrel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
- #partial switch (success) {
+ #partial switch success {
case .relaxed:
- value, ok := intrinsics.atomic_cxchg_relaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
- value, ok := intrinsics.atomic_cxchg_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
- value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
- value, ok := intrinsics.atomic_cxchg_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchg_acqrel_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
- fallthrough
+ assert(success == .seq_cst)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
- value, ok := intrinsics.atomic_cxchg_failacq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_strong_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
return ok
}
-atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) {
- value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
+atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desired: T) -> bool {
+ value, ok := intrinsics.atomic_compare_exchange_weak(object, expected^, desired)
if !ok { expected^ = value }
return ok
}
-atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) {
+atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
value: T; ok: bool
- #partial switch (failure) {
+ #partial switch failure {
case .seq_cst:
assert(success != .relaxed)
- #partial switch (success) {
+ #partial switch success {
case .seq_cst:
- value, ok := intrinsics.atomic_cxchgweak(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Seq_Cst)
case .acquire:
- value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Seq_Cst)
case .consume:
- value, ok := intrinsics.atomic_cxchgweak_acq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Seq_Cst)
case .release:
- value, ok := intrinsics.atomic_cxchgweak_rel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Release, .Seq_Cst)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchgweak_acqrel(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Seq_Cst)
}
case .relaxed:
assert(success != .release)
- #partial switch (success) {
+ #partial switch success {
case .relaxed:
- value, ok := intrinsics.atomic_cxchgweak_relaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Relaxed, .Relaxed)
case .seq_cst:
- value, ok := intrinsics.atomic_cxchgweak_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Relaxed)
case .acquire:
- value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acquire, .Relaxed)
case .consume:
- value, ok := intrinsics.atomic_cxchgweak_acq_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Consume, .Relaxed)
case .acq_rel:
- value, ok := intrinsics.atomic_cxchgweak_acqrel_failrelaxed(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Acq_Rel, .Relaxed)
}
case .consume:
- fallthrough
+ assert(success == .seq_cst)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Consume)
case .acquire:
assert(success == .seq_cst)
- value, ok := intrinsics.atomic_cxchgweak_failacq(object, expected^, desired)
+ value, ok = intrinsics.atomic_compare_exchange_weak_explicit(object, expected^, desired, .Seq_Cst, .Acquire)
}
if !ok { expected^ = value }
@@ -297,19 +287,14 @@ atomic_fetch_add :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_add_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_add_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_add_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_add_acq(object, operand)
- case .release:
- return intrinsics.atomic_add_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_add_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_add(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_add_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_add_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_add_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_add_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_add_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_add_explicit(object, operand, .Seq_Cst)
}
}
@@ -318,19 +303,14 @@ atomic_fetch_sub :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_sub_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_sub_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_sub_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_sub_acq(object, operand)
- case .release:
- return intrinsics.atomic_sub_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_sub_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_sub(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_sub_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_sub_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_sub_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_sub_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_sub_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_sub_explicit(object, operand, .Seq_Cst)
}
}
@@ -339,19 +319,14 @@ atomic_fetch_or :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_or_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_or_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_or_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_or_acq(object, operand)
- case .release:
- return intrinsics.atomic_or_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_or_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_or(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_or_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_or_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_or_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_or_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_or_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_or_explicit(object, operand, .Seq_Cst)
}
}
@@ -360,19 +335,14 @@ atomic_fetch_xor :: #force_inline proc(object: ^$T, operand: T) -> T {
}
atomic_fetch_xor_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_xor_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_xor_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_xor_acq(object, operand)
- case .release:
- return intrinsics.atomic_xor_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_xor_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_xor(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_xor_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_xor_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_xor_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_xor_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_xor_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_xor_explicit(object, operand, .Seq_Cst)
}
}
@@ -380,19 +350,14 @@ atomic_fetch_and :: #force_inline proc(object: ^$T, operand: T) -> T {
return intrinsics.atomic_and(object, operand)
}
atomic_fetch_and_explicit :: #force_inline proc(object: ^$T, operand: T, order: memory_order) -> T {
- switch (order) {
- case .relaxed:
- return intrinsics.atomic_and_relaxed(object, operand)
- case .consume:
- return intrinsics.atomic_and_acq(object, operand)
- case .acquire:
- return intrinsics.atomic_and_acq(object, operand)
- case .release:
- return intrinsics.atomic_and_rel(object, operand)
- case .acq_rel:
- return intrinsics.atomic_and_acqrel(object, operand)
- case .seq_cst:
- return intrinsics.atomic_and(object, operand)
+ switch order {
+ case .relaxed: return intrinsics.atomic_and_explicit(object, operand, .Relaxed)
+ case .consume: return intrinsics.atomic_and_explicit(object, operand, .Consume)
+ case .acquire: return intrinsics.atomic_and_explicit(object, operand, .Acquire)
+ case .release: return intrinsics.atomic_and_explicit(object, operand, .Release)
+ case .acq_rel: return intrinsics.atomic_and_explicit(object, operand, .Acq_Rel)
+ case: fallthrough
+ case .seq_cst: return intrinsics.atomic_and_explicit(object, operand, .Seq_Cst)
}
}
diff --git a/core/c/libc/stdio.odin b/core/c/libc/stdio.odin
index 4a39c22e9..01d0cd427 100644
--- a/core/c/libc/stdio.odin
+++ b/core/c/libc/stdio.odin
@@ -1,8 +1,8 @@
package libc
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
@@ -13,7 +13,7 @@ when ODIN_OS == "windows" {
FILE :: struct {}
// MSVCRT compatible.
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
_IOFBF :: 0x0000
_IONBF :: 0x0004
_IOLBF :: 0x0040
@@ -48,7 +48,7 @@ when ODIN_OS == "windows" {
}
// GLIBC and MUSL compatible.
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
fpos_t :: struct #raw_union { _: [16]char, _: longlong, _: double, }
_IOFBF :: 0
@@ -78,7 +78,57 @@ when ODIN_OS == "linux" {
}
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .OpenBSD {
+ fpos_t :: distinct i64
+
+ _IOFBF :: 0
+ _IOLBF :: 1
+ _IONBF :: 1
+
+ BUFSIZ :: 1024
+
+ EOF :: int(-1)
+
+ FOPEN_MAX :: 20
+ FILENAME_MAX :: 1024
+
+ SEEK_SET :: 0
+ SEEK_CUR :: 1
+ SEEK_END :: 2
+
+ foreign libc {
+ stderr: ^FILE
+ stdin: ^FILE
+ stdout: ^FILE
+ }
+}
+
+when ODIN_OS == .FreeBSD {
+ fpos_t :: distinct i64
+
+ _IOFBF :: 0
+ _IOLBF :: 1
+ _IONBF :: 1
+
+ BUFSIZ :: 1024
+
+ EOF :: int(-1)
+
+ FOPEN_MAX :: 20
+ FILENAME_MAX :: 1024
+
+ SEEK_SET :: 0
+ SEEK_CUR :: 1
+ SEEK_END :: 2
+
+ foreign libc {
+ stderr: ^FILE
+ stdin: ^FILE
+ stdout: ^FILE
+ }
+}
+
+when ODIN_OS == .Darwin {
fpos_t :: distinct i64
_IOFBF :: 0
@@ -149,7 +199,7 @@ foreign libc {
putchar :: proc() -> int ---
puts :: proc(s: cstring) -> int ---
ungetc :: proc(c: int, stream: ^FILE) -> int ---
- fread :: proc(ptr: rawptr, size: size_t, stream: ^FILE) -> size_t ---
+ fread :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
// 7.21.9 File positioning functions
diff --git a/core/c/libc/stdlib.odin b/core/c/libc/stdlib.odin
index b368c0cee..a278db0ef 100644
--- a/core/c/libc/stdlib.odin
+++ b/core/c/libc/stdlib.odin
@@ -2,15 +2,15 @@ package libc
// 7.22 General utilities
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
RAND_MAX :: 0x7fff
@(private="file")
@@ -24,7 +24,7 @@ when ODIN_OS == "windows" {
}
}
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
RAND_MAX :: 0x7fffffff
// GLIBC and MUSL only
@@ -40,7 +40,7 @@ when ODIN_OS == "linux" {
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
RAND_MAX :: 0x7fffffff
// GLIBC and MUSL only
diff --git a/core/c/libc/string.odin b/core/c/libc/string.odin
index c91124dcb..8f83ee1b9 100644
--- a/core/c/libc/string.odin
+++ b/core/c/libc/string.odin
@@ -4,9 +4,9 @@ import "core:runtime"
// 7.24 String handling
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
diff --git a/core/c/libc/threads.odin b/core/c/libc/threads.odin
index ad305b517..a7a45150d 100644
--- a/core/c/libc/threads.odin
+++ b/core/c/libc/threads.odin
@@ -5,7 +5,7 @@ package libc
thrd_start_t :: proc "c" (rawptr) -> int
tss_dtor_t :: proc "c" (rawptr)
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc {
"system:libucrt.lib",
"system:msvcprt.lib"
@@ -74,7 +74,7 @@ when ODIN_OS == "windows" {
}
// GLIBC and MUSL compatible constants and types.
-when ODIN_OS == "linux" {
+when ODIN_OS == .Linux {
foreign import libc {
"system:c",
"system:pthread"
@@ -138,6 +138,6 @@ when ODIN_OS == "linux" {
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
// TODO: find out what this is meant to be!
}
diff --git a/core/c/libc/time.odin b/core/c/libc/time.odin
index 96e80e216..b337e139a 100644
--- a/core/c/libc/time.odin
+++ b/core/c/libc/time.odin
@@ -2,9 +2,9 @@ package libc
// 7.27 Date and time
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
@@ -12,7 +12,7 @@ when ODIN_OS == "windows" {
// We enforce 64-bit time_t and timespec as there is no reason to use 32-bit as
// we approach the 2038 problem. Windows has defaulted to this since VC8 (2005).
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign libc {
// 7.27.2 Time manipulation functions
clock :: proc() -> clock_t ---
@@ -45,7 +45,7 @@ when ODIN_OS == "windows" {
}
}
-when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
+when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
@(default_calling_convention="c")
foreign libc {
// 7.27.2 Time manipulation functions
@@ -63,7 +63,12 @@ when ODIN_OS == "linux" || ODIN_OS == "freebsd" || ODIN_OS == "darwin" {
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
}
- CLOCKS_PER_SEC :: 1000000
+ when ODIN_OS == .OpenBSD {
+ CLOCKS_PER_SEC :: 100
+ } else {
+ CLOCKS_PER_SEC :: 1000000
+ }
+
TIME_UTC :: 1
time_t :: distinct i64
diff --git a/core/c/libc/types.odin b/core/c/libc/types.odin
index 7199cf57b..a49e52fb6 100644
--- a/core/c/libc/types.odin
+++ b/core/c/libc/types.odin
@@ -3,6 +3,8 @@ package libc
import "core:c"
char :: c.char // assuming -funsigned-char
+
+schar :: c.schar
short :: c.short
int :: c.int
long :: c.long
diff --git a/core/c/libc/uchar.odin b/core/c/libc/uchar.odin
index e49c29e51..a10969ceb 100644
--- a/core/c/libc/uchar.odin
+++ b/core/c/libc/uchar.odin
@@ -2,9 +2,9 @@ package libc
// 7.28 Unicode utilities
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
diff --git a/core/c/libc/wchar.odin b/core/c/libc/wchar.odin
index fc206e494..f2aa8410e 100644
--- a/core/c/libc/wchar.odin
+++ b/core/c/libc/wchar.odin
@@ -2,9 +2,9 @@ package libc
// 7.29 Extended multibyte and wide character utilities
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
diff --git a/core/c/libc/wctype.odin b/core/c/libc/wctype.odin
index 47f17e0b4..43aee9dc6 100644
--- a/core/c/libc/wctype.odin
+++ b/core/c/libc/wctype.odin
@@ -2,27 +2,34 @@ package libc
// 7.30 Wide character classification and mapping utilities
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import libc "system:libucrt.lib"
-} else when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
wctrans_t :: distinct wchar_t
wctype_t :: distinct ushort
-}
-when ODIN_OS == "linux" {
+} else when ODIN_OS == .Linux {
wctrans_t :: distinct intptr_t
wctype_t :: distinct ulong
-}
-when ODIN_OS == "darwin" {
+} else when ODIN_OS == .Darwin {
wctrans_t :: distinct int
wctype_t :: distinct u32
+
+} else when ODIN_OS == .OpenBSD {
+ wctrans_t :: distinct rawptr
+ wctype_t :: distinct rawptr
+
+} else when ODIN_OS == .FreeBSD {
+ wctrans_t :: distinct int
+ wctype_t :: distinct ulong
+
}
@(default_calling_convention="c")
diff --git a/core/compress/common.odin b/core/compress/common.odin
index 41f292b6f..f4e378269 100644
--- a/core/compress/common.odin
+++ b/core/compress/common.odin
@@ -5,6 +5,9 @@
List of contributors:
Jeroen van Rijn: Initial implementation, optimization.
*/
+
+
+// package compress is a collection of utilities to aid with other compression packages
package compress
import "core:io"
@@ -44,7 +47,7 @@ when size_of(uintptr) == 8 {
}
-Error :: union {
+Error :: union #shared_nil {
General_Error,
Deflate_Error,
ZLIB_Error,
@@ -55,6 +58,7 @@ Error :: union {
}
General_Error :: enum {
+ None = 0,
File_Not_Found,
Cannot_Open_File,
File_Too_Short,
@@ -73,6 +77,7 @@ General_Error :: enum {
}
GZIP_Error :: enum {
+ None = 0,
Invalid_GZIP_Signature,
Reserved_Flag_Set,
Invalid_Extra_Data,
@@ -97,6 +102,7 @@ GZIP_Error :: enum {
}
ZIP_Error :: enum {
+ None = 0,
Invalid_ZIP_File_Signature,
Unexpected_Signature,
Insert_Next_Disk,
@@ -104,6 +110,7 @@ ZIP_Error :: enum {
}
ZLIB_Error :: enum {
+ None = 0,
Unsupported_Window_Size,
FDICT_Unsupported,
Unsupported_Compression_Level,
@@ -111,6 +118,7 @@ ZLIB_Error :: enum {
}
Deflate_Error :: enum {
+ None = 0,
Huffman_Bad_Sizes,
Huffman_Bad_Code_Lengths,
Inflate_Error,
@@ -120,7 +128,6 @@ Deflate_Error :: enum {
BType_3,
}
-
// General I/O context for ZLIB, LZW, etc.
Context_Memory_Input :: struct #packed {
input_data: []u8,
@@ -136,7 +143,12 @@ Context_Memory_Input :: struct #packed {
size_packed: i64,
size_unpacked: i64,
}
-#assert(size_of(Context_Memory_Input) == 64)
+when size_of(rawptr) == 8 {
+ #assert(size_of(Context_Memory_Input) == 64)
+} else {
+ // e.g. `-target:windows_i386`
+ #assert(size_of(Context_Memory_Input) == 52)
+}
Context_Stream_Input :: struct #packed {
input_data: []u8,
@@ -171,8 +183,6 @@ Context_Stream_Input :: struct #packed {
This simplifies end-of-stream handling where bits may be left in the bit buffer.
*/
-// TODO: Make these return compress.Error errors.
-
input_size_from_memory :: proc(z: ^Context_Memory_Input) -> (res: i64, err: Error) {
return i64(len(z.input_data)), nil
}
@@ -470,4 +480,4 @@ discard_to_next_byte_lsb_from_stream :: proc(z: ^Context_Stream_Input) {
consume_bits_lsb(z, discard)
}
-discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream};
\ No newline at end of file
+discard_to_next_byte_lsb :: proc{discard_to_next_byte_lsb_from_memory, discard_to_next_byte_lsb_from_stream}
diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin
index 1a72500bf..4482d4a7e 100644
--- a/core/compress/gzip/gzip.odin
+++ b/core/compress/gzip/gzip.odin
@@ -66,7 +66,8 @@ OS :: enum u8 {
_Unknown = 14,
Unknown = 255,
}
-OS_Name :: #partial [OS]string{
+OS_Name :: #sparse[OS]string{
+ ._Unknown = "",
.FAT = "FAT",
.Amiga = "Amiga",
.VMS = "VMS/OpenVMS",
@@ -99,7 +100,7 @@ E_GZIP :: compress.GZIP_Error
E_ZLIB :: compress.ZLIB_Error
E_Deflate :: compress.Deflate_Error
-GZIP_MAX_PAYLOAD_SIZE :: int(max(u32le))
+GZIP_MAX_PAYLOAD_SIZE :: i64(max(u32le))
load :: proc{load_from_slice, load_from_file, load_from_context}
@@ -135,7 +136,7 @@ load_from_context :: proc(z: ^$C, buf: ^bytes.Buffer, known_gzip_size := -1, exp
z.output = buf
- if expected_output_size > GZIP_MAX_PAYLOAD_SIZE {
+ if i64(expected_output_size) > i64(GZIP_MAX_PAYLOAD_SIZE) {
return E_GZIP.Payload_Size_Exceeds_Max_Payload
}
diff --git a/core/compress/shoco/model.odin b/core/compress/shoco/model.odin
new file mode 100644
index 000000000..bbc38903d
--- /dev/null
+++ b/core/compress/shoco/model.odin
@@ -0,0 +1,148 @@
+/*
+ This file was generated, so don't edit this by hand.
+ Transliterated from https://github.com/Ed-von-Schleck/shoco/blob/master/shoco_model.h,
+ which is an English word model.
+*/
+
+// package shoco is an implementation of the shoco short string compressor
+package shoco
+
+DEFAULT_MODEL :: Shoco_Model {
+ min_char = 39,
+ max_char = 122,
+ characters_by_id = {
+ 'e', 'a', 'i', 'o', 't', 'h', 'n', 'r', 's', 'l', 'u', 'c', 'w', 'm', 'd', 'b', 'p', 'f', 'g', 'v', 'y', 'k', '-', 'H', 'M', 'T', '\'', 'B', 'x', 'I', 'W', 'L',
+ },
+ ids_by_character = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 26, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 27, -1, -1, -1, -1, -1, 23, 29, -1, -1, 31, 24, -1, -1, -1, -1, -1, -1, 25, -1, -1, 30, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 15, 11, 14, 0, 17, 18, 5, 2, -1, 21, 9, 13, 6, 3, 16, -1, 7, 8, 4, 10, 19, 12, 28, 20, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ },
+ successors_by_bigram = {
+ 7, 4, 12, -1, 6, -1, 1, 0, 3, 5, -1, 9, -1, 8, 2, -1, 15, 14, -1, 10, 11, -1, -1, -1, -1, -1, -1, -1, 13, -1, -1, -1,
+ 1, -1, 6, -1, 1, -1, 0, 3, 2, 4, 15, 11, -1, 9, 5, 10, 13, -1, 12, 8, 7, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 9, 11, -1, 4, 2, -1, 0, 8, 1, 5, -1, 6, -1, 3, 7, 15, -1, 12, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 14, 7, 5, -1, 1, 2, 8, 9, 0, 15, 6, 4, 11, -1, 12, 3, -1, 10, -1, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 4, 3, 1, 5, 0, -1, 6, 10, 9, 7, 12, 11, -1, -1, -1, -1, 13, -1, -1, 8, -1, 15, -1, -1, -1, 14, -1, -1, -1, -1, -1,
+ 0, 1, 2, 3, 4, -1, -1, 5, 9, 10, 6, -1, -1, 8, 15, 11, -1, 14, -1, -1, 7, -1, 13, -1, -1, -1, 12, -1, -1, -1, -1, -1,
+ 2, 8, 7, 4, 3, -1, 9, -1, 6, 11, -1, 5, -1, -1, 0, -1, -1, 14, 1, 15, 10, 12, -1, -1, -1, -1, 13, -1, -1, -1, -1, -1,
+ 0, 3, 1, 2, 6, -1, 9, 8, 4, 12, 13, 10, -1, 11, 7, -1, -1, 15, 14, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 6, 3, 4, 1, 2, -1, -1, 5, 10, 7, 9, 11, 12, -1, -1, 8, 14, -1, -1, 15, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 6, 2, 5, 9, -1, -1, -1, 10, 1, 8, -1, 12, 14, 4, -1, 15, 7, -1, 13, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 8, 10, 9, 15, 1, -1, 4, 0, 3, 2, -1, 6, -1, 12, 11, 13, 7, 14, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 3, 6, 0, 4, 2, -1, 7, 13, 8, 9, 11, -1, -1, 15, -1, -1, -1, -1, -1, 10, 5, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 3, 0, 1, 4, -1, 2, 5, 6, 7, 8, -1, 14, -1, -1, 9, 15, -1, 12, -1, -1, -1, 10, 11, -1, -1, -1, 13, -1, -1, -1, -1, -1,
+ 0, 1, 3, 2, 15, -1, 12, -1, 7, 14, 4, -1, -1, 9, -1, 8, 5, 10, -1, -1, 6, -1, 13, -1, -1, -1, 11, -1, -1, -1, -1, -1,
+ 0, 3, 1, 2, -1, -1, 12, 6, 4, 9, 7, -1, -1, 14, 8, -1, -1, 15, 11, 13, 5, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 5, 7, 2, 10, 13, -1, 6, 8, 1, 3, -1, -1, 14, 15, 11, -1, -1, -1, 12, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 2, 6, 3, 7, 10, -1, 1, 9, 4, 8, -1, -1, 15, -1, 12, 5, -1, -1, -1, 11, -1, 13, -1, -1, -1, 14, -1, -1, -1, -1, -1,
+ 1, 3, 4, 0, 7, -1, 12, 2, 11, 8, 6, 13, -1, -1, -1, -1, -1, 5, -1, -1, 10, 15, 9, -1, -1, -1, 14, -1, -1, -1, -1, -1,
+ 1, 3, 5, 2, 13, 0, 9, 4, 7, 6, 8, -1, -1, 15, -1, 11, -1, -1, 10, -1, 14, -1, 12, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 2, 1, 3, -1, -1, -1, 6, -1, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 1, 11, 4, 0, 3, -1, 13, 12, 2, 7, -1, -1, 15, 10, 5, 8, 14, -1, -1, -1, -1, -1, 9, -1, -1, -1, 6, -1, -1, -1, -1, -1,
+ 0, 9, 2, 14, 15, 4, 1, 13, 3, 5, -1, -1, 10, -1, -1, -1, -1, 6, 12, -1, 7, -1, 8, -1, -1, -1, 11, -1, -1, -1, -1, -1,
+ -1, 2, 14, -1, 1, 5, 8, 7, 4, 12, -1, 6, 9, 11, 13, 3, 10, 15, -1, -1, -1, -1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 0, 1, 3, 2, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 4, 3, 1, 5, -1, -1, -1, 0, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 2, 8, 4, 1, -1, 0, -1, 6, -1, -1, 5, -1, 7, -1, -1, -1, -1, -1, -1, -1, 10, -1, -1, 9, -1, -1, -1, -1, -1, -1, -1, -1,
+ 12, 5, -1, -1, 1, -1, -1, 7, 0, 3, -1, 2, -1, 4, 6, -1, -1, -1, -1, 8, -1, -1, 15, -1, 13, 9, -1, -1, -1, -1, -1, 11,
+ 1, 3, 2, 4, -1, -1, -1, 5, -1, 7, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1,
+ 5, 3, 4, 12, 1, 6, -1, -1, -1, -1, 8, 2, -1, -1, -1, -1, 0, 9, -1, -1, 11, -1, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, 0, -1, 1, 12, 3, -1, -1, -1, -1, 5, -1, -1, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, 4, -1, -1, 6, -1, 10,
+ 2, 3, 1, 4, -1, 0, -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 7, -1, -1, -1, -1, -1, -1, -1, -1, 6, -1, -1,
+ 5, 1, 3, 0, -1, -1, -1, -1, -1, -1, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, -1, -1, -1, -1, -1, 9, -1, -1, 6, -1, 7,
+ },
+ successors_reversed = {
+ 's', 't', 'c', 'l', 'm', 'a', 'd', 'r', 'v', 'T', 'A', 'L', 'e', 'M', 'Y', '-',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '-', 't', 'a', 'b', 's', 'h', 'c', 'r', 'n', 'w', 'p', 'm', 'l', 'd', 'i', 'f',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'u', 'e', 'i', 'a', 'o', 'r', 'y', 'l', 'I', 'E', 'R', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'e', 'a', 'o', 'i', 'u', 'A', 'y', 'E', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 't', 'n', 'f', 's', '\'', 'm', 'I', 'N', 'A', 'E', 'L', 'Z', 'r', 'V', 'R', 'C',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'o', 'a', 'y', 'i', 'u', 'e', 'I', 'L', 'D', '\'', 'E', 'Y', '\x00', '\x00', '\x00', '\x00',
+ 'r', 'i', 'y', 'a', 'e', 'o', 'u', 'Y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'h', 'o', 'e', 'E', 'i', 'u', 'r', 'w', 'a', 'H', 'y', 'R', 'Z', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'h', 'i', 'e', 'a', 'o', 'r', 'I', 'y', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'n', 't', 's', 'r', 'l', 'd', 'i', 'y', 'v', 'm', 'b', 'c', 'g', 'p', 'k', 'u',
+ 'e', 'l', 'o', 'u', 'y', 'a', 'r', 'i', 's', 'j', 't', 'b', 'v', 'h', 'm', 'd',
+ 'o', 'e', 'h', 'a', 't', 'k', 'i', 'r', 'l', 'u', 'y', 'c', 'q', 's', '-', 'd',
+ 'e', 'i', 'o', 'a', 's', 'y', 'r', 'u', 'd', 'l', '-', 'g', 'n', 'v', 'm', 'f',
+ 'r', 'n', 'd', 's', 'a', 'l', 't', 'e', 'm', 'c', 'v', 'y', 'i', 'x', 'f', 'p',
+ 'o', 'e', 'r', 'a', 'i', 'f', 'u', 't', 'l', '-', 'y', 's', 'n', 'c', '\'', 'k',
+ 'h', 'e', 'o', 'a', 'r', 'i', 'l', 's', 'u', 'n', 'g', 'b', '-', 't', 'y', 'm',
+ 'e', 'a', 'i', 'o', 't', 'r', 'u', 'y', 'm', 's', 'l', 'b', '\'', '-', 'f', 'd',
+ 'n', 's', 't', 'm', 'o', 'l', 'c', 'd', 'r', 'e', 'g', 'a', 'f', 'v', 'z', 'b',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'e', 'n', 'i', 's', 'h', 'l', 'f', 'y', '-', 'a', 'w', '\'', 'g', 'r', 'o', 't',
+ 'e', 'l', 'i', 'y', 'd', 'o', 'a', 'f', 'u', 't', 's', 'k', 'w', 'v', 'm', 'p',
+ 'e', 'a', 'o', 'i', 'u', 'p', 'y', 's', 'b', 'm', 'f', '\'', 'n', '-', 'l', 't',
+ 'd', 'g', 'e', 't', 'o', 'c', 's', 'i', 'a', 'n', 'y', 'l', 'k', '\'', 'f', 'v',
+ 'u', 'n', 'r', 'f', 'm', 't', 'w', 'o', 's', 'l', 'v', 'd', 'p', 'k', 'i', 'c',
+ 'e', 'r', 'a', 'o', 'l', 'p', 'i', 't', 'u', 's', 'h', 'y', 'b', '-', '\'', 'm',
+ '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'e', 'i', 'o', 'a', 's', 'y', 't', 'd', 'r', 'n', 'c', 'm', 'l', 'u', 'g', 'f',
+ 'e', 't', 'h', 'i', 'o', 's', 'a', 'u', 'p', 'c', 'l', 'w', 'm', 'k', 'f', 'y',
+ 'h', 'o', 'e', 'i', 'a', 't', 'r', 'u', 'y', 'l', 's', 'w', 'c', 'f', '\'', '-',
+ 'r', 't', 'l', 's', 'n', 'g', 'c', 'p', 'e', 'i', 'a', 'd', 'm', 'b', 'f', 'o',
+ 'e', 'i', 'a', 'o', 'y', 'u', 'r', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00',
+ 'a', 'i', 'h', 'e', 'o', 'n', 'r', 's', 'l', 'd', 'k', '-', 'f', '\'', 'c', 'b',
+ 'p', 't', 'c', 'a', 'i', 'e', 'h', 'q', 'u', 'f', '-', 'y', 'o', '\x00', '\x00', '\x00',
+ 'o', 'e', 's', 't', 'i', 'd', '\'', 'l', 'b', '-', 'm', 'a', 'r', 'n', 'p', 'w',
+ },
+
+ character_count = 32,
+ successor_count = 16,
+
+ max_successor_n = 7,
+ packs = {
+ { 0x80000000, 1, 2, { 26, 24, 24, 24, 24, 24, 24, 24 }, { 15, 3, 0, 0, 0, 0, 0, 0 }, 0xc0, 0x80 },
+ { 0xc0000000, 2, 4, { 25, 22, 19, 16, 16, 16, 16, 16 }, { 15, 7, 7, 7, 0, 0, 0, 0 }, 0xe0, 0xc0 },
+ { 0xe0000000, 4, 8, { 23, 19, 15, 11, 8, 5, 2, 0 }, { 31, 15, 15, 15, 7, 7, 7, 3 }, 0xf0, 0xe0 },
+ },
+}
\ No newline at end of file
diff --git a/core/compress/shoco/shoco.odin b/core/compress/shoco/shoco.odin
new file mode 100644
index 000000000..f94ce70b7
--- /dev/null
+++ b/core/compress/shoco/shoco.odin
@@ -0,0 +1,318 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+
+ An implementation of [shoco](https://github.com/Ed-von-Schleck/shoco) by Christian Schramm.
+*/
+
+// package shoco is an implementation of the shoco short string compressor
+package shoco
+
+import "core:intrinsics"
+import "core:compress"
+
+Shoco_Pack :: struct {
+ word: u32,
+ bytes_packed: i8,
+ bytes_unpacked: i8,
+ offsets: [8]u16,
+ masks: [8]i16,
+ header_mask: u8,
+ header: u8,
+}
+
+Shoco_Model :: struct {
+ min_char: u8,
+ max_char: u8,
+ characters_by_id: []u8,
+ ids_by_character: [256]i16,
+ successors_by_bigram: []i8,
+ successors_reversed: []u8,
+
+ character_count: u8,
+ successor_count: u8,
+ max_successor_n: i8,
+ packs: []Shoco_Pack,
+}
+
+compress_bound :: proc(uncompressed_size: int) -> (worst_case_compressed_size: int) {
+ // Worst case compression happens when input is non-ASCII (128-255)
+ // Encoded as 0x00 + the byte in question.
+ return uncompressed_size * 2
+}
+
+decompress_bound :: proc(compressed_size: int, model := DEFAULT_MODEL) -> (maximum_decompressed_size: int) {
+ // Best case compression is 2:1
+ most: f64
+ for pack in model.packs {
+ val := f64(compressed_size) / f64(pack.bytes_packed) * f64(pack.bytes_unpacked)
+ most = max(most, val)
+ }
+ return int(most)
+}
+
+find_best_encoding :: proc(indices: []i16, n_consecutive: i8, model := DEFAULT_MODEL) -> (res: int) {
+ for p := len(model.packs); p > 0; p -= 1 {
+ pack := model.packs[p - 1]
+ if n_consecutive >= pack.bytes_unpacked {
+ have_index := true
+ for i := 0; i < int(pack.bytes_unpacked); i += 1 {
+ if indices[i] > pack.masks[i] {
+ have_index = false
+ break
+ }
+ }
+ if have_index {
+ return p - 1
+ }
+ }
+ }
+ return -1
+}
+
+validate_model :: proc(model: Shoco_Model) -> (int, compress.Error) {
+ if len(model.characters_by_id) != int(model.character_count) {
+ return 0, .Unknown_Compression_Method
+ }
+
+ if len(model.successors_by_bigram) != int(model.character_count) * int(model.character_count) {
+ return 0, .Unknown_Compression_Method
+ }
+
+ if len(model.successors_reversed) != int(model.successor_count) * int(model.max_char - model.min_char) {
+ return 0, .Unknown_Compression_Method
+ }
+
+ // Model seems legit.
+ return 0, nil
+}
+
+// Decompresses into provided buffer.
+decompress_slice_to_output_buffer :: proc(input: []u8, output: []u8, model := DEFAULT_MODEL) -> (size: int, err: compress.Error) {
+ inp, inp_end := 0, len(input)
+ out, out_end := 0, len(output)
+
+ validate_model(model) or_return
+
+ for inp < inp_end {
+ val := transmute(i8)input[inp]
+ mark := int(-1)
+
+ for val < 0 {
+ val <<= 1
+ mark += 1
+ }
+
+ if mark > len(model.packs) {
+ return out, .Unknown_Compression_Method
+ }
+
+ if mark < 0 {
+ if out >= out_end {
+ return out, .Output_Too_Short
+ }
+
+ // Ignore the sentinel value for non-ASCII chars
+ if input[inp] == 0x00 {
+ inp += 1
+ if inp >= inp_end {
+ return out, .Stream_Too_Short
+ }
+ }
+ output[out] = input[inp]
+ inp, out = inp + 1, out + 1
+
+ } else {
+ pack := model.packs[mark]
+
+ if out + int(pack.bytes_unpacked) > out_end {
+ return out, .Output_Too_Short
+ } else if inp + int(pack.bytes_packed) > inp_end {
+ return out, .Stream_Too_Short
+ }
+
+ code := intrinsics.unaligned_load((^u32)(&input[inp]))
+ when ODIN_ENDIAN == .Little {
+ code = intrinsics.byte_swap(code)
+ }
+
+ // Unpack the leading char
+ offset := pack.offsets[0]
+ mask := pack.masks[0]
+
+ last_chr := model.characters_by_id[(code >> offset) & u32(mask)]
+ output[out] = last_chr
+
+ // Unpack the successor chars
+ for i := 1; i < int(pack.bytes_unpacked); i += 1 {
+ offset = pack.offsets[i]
+ mask = pack.masks[i]
+
+ index_major := u32(last_chr - model.min_char) * u32(model.successor_count)
+ index_minor := (code >> offset) & u32(mask)
+
+ last_chr = model.successors_reversed[index_major + index_minor]
+
+ output[out + i] = last_chr
+ }
+
+ out += int(pack.bytes_unpacked)
+ inp += int(pack.bytes_packed)
+ }
+ }
+
+ return out, nil
+}
+
+decompress_slice_to_string :: proc(input: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (res: string, err: compress.Error) {
+ context.allocator = allocator
+
+ if len(input) == 0 {
+ return "", .Stream_Too_Short
+ }
+
+ max_output_size := decompress_bound(len(input), model)
+
+ buf: [dynamic]u8
+ if !resize(&buf, max_output_size) {
+ return "", .Out_Of_Memory
+ }
+
+ length, result := decompress_slice_to_output_buffer(input, buf[:])
+ resize(&buf, length)
+ return string(buf[:]), result
+}
+decompress :: proc{decompress_slice_to_output_buffer, decompress_slice_to_string}
+
+compress_string_to_buffer :: proc(input: string, output: []u8, model := DEFAULT_MODEL, allocator := context.allocator) -> (size: int, err: compress.Error) {
+ inp, inp_end := 0, len(input)
+ out, out_end := 0, len(output)
+ output := output
+
+ validate_model(model) or_return
+
+ indices := make([]i16, model.max_successor_n + 1)
+ defer delete(indices)
+
+ last_resort := false
+
+ encode: for inp < inp_end {
+ if last_resort {
+ last_resort = false
+
+ if input[inp] & 0x80 == 0x80 {
+ // Non-ASCII case
+ if out + 2 > out_end {
+ return out, .Output_Too_Short
+ }
+
+ // Put in a sentinel byte
+ output[out] = 0x00
+ out += 1
+ } else {
+ // An ASCII byte
+ if out + 1 > out_end {
+ return out, .Output_Too_Short
+ }
+ }
+ output[out] = input[inp]
+ out, inp = out + 1, inp + 1
+ } else {
+ // Find the longest string of known successors
+ indices[0] = model.ids_by_character[input[inp]]
+ last_chr_index := indices[0]
+
+ if last_chr_index < 0 {
+ last_resort = true
+ continue encode
+ }
+
+ rest := inp_end - inp
+ n_consecutive: i8 = 1
+ for ; n_consecutive <= model.max_successor_n; n_consecutive += 1 {
+ if inp_end > 0 && int(n_consecutive) == rest {
+ break
+ }
+
+ current_index := model.ids_by_character[input[inp + int(n_consecutive)]]
+ if current_index < 0 { // '\0' is always -1
+ break
+ }
+
+ successor_index := model.successors_by_bigram[last_chr_index * i16(model.character_count) + current_index]
+ if successor_index < 0 {
+ break
+ }
+
+ indices[n_consecutive] = i16(successor_index)
+ last_chr_index = current_index
+ }
+
+ if n_consecutive < 2 {
+ last_resort = true
+ continue encode
+ }
+
+ pack_n := find_best_encoding(indices, n_consecutive)
+ if pack_n >= 0 {
+ if out + int(model.packs[pack_n].bytes_packed) > out_end {
+ return out, .Output_Too_Short
+ }
+
+ pack := model.packs[pack_n]
+ code := pack.word
+
+ for i := 0; i < int(pack.bytes_unpacked); i += 1 {
+ code |= u32(indices[i]) << pack.offsets[i]
+ }
+
+ // In the little-endian world, we need to swap what's in the register to match the memory representation.
+ when ODIN_ENDIAN == .Little {
+ code = intrinsics.byte_swap(code)
+ }
+ out_ptr := raw_data(output[out:])
+
+ switch pack.bytes_packed {
+ case 4:
+ intrinsics.unaligned_store(transmute(^u32)out_ptr, code)
+ case 2:
+ intrinsics.unaligned_store(transmute(^u16)out_ptr, u16(code))
+ case 1:
+ intrinsics.unaligned_store(transmute(^u8)out_ptr, u8(code))
+ case:
+ return out, .Unknown_Compression_Method
+ }
+
+ out += int(pack.bytes_packed)
+ inp += int(pack.bytes_unpacked)
+ } else {
+ last_resort = true
+ continue encode
+ }
+ }
+ }
+ return out, nil
+}
+
+compress_string :: proc(input: string, model := DEFAULT_MODEL, allocator := context.allocator) -> (output: []u8, err: compress.Error) {
+ context.allocator = allocator
+
+ if len(input) == 0 {
+ return {}, .Stream_Too_Short
+ }
+
+ max_output_size := compress_bound(len(input))
+
+ buf: [dynamic]u8
+ if !resize(&buf, max_output_size) {
+ return {}, .Out_Of_Memory
+ }
+
+ length, result := compress_string_to_buffer(input, buf[:])
+ resize(&buf, length)
+ 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 9ae980042..855eef7a8 100644
--- a/core/compress/zlib/zlib.odin
+++ b/core/compress/zlib/zlib.odin
@@ -47,10 +47,10 @@ Options :: struct {
level: u8,
}
-Error :: compress.Error
-E_General :: compress.General_Error
-E_ZLIB :: compress.ZLIB_Error
-E_Deflate :: compress.Deflate_Error
+Error :: compress.Error
+General_Error :: compress.General_Error
+ZLIB_Error :: compress.ZLIB_Error
+Deflate_Error :: compress.Deflate_Error
DEFLATE_MAX_CHUNK_SIZE :: 65535
DEFLATE_MAX_LITERAL_SIZE :: 65535
@@ -111,9 +111,9 @@ ZFAST_MASK :: ((1 << ZFAST_BITS) - 1)
*/
Huffman_Table :: struct {
fast: [1 << ZFAST_BITS]u16,
- firstcode: [16]u16,
+ firstcode: [17]u16,
maxcode: [17]int,
- firstsymbol: [16]u16,
+ firstsymbol: [17]u16,
size: [288]u8,
value: [288]u16,
}
@@ -244,7 +244,7 @@ allocate_huffman_table :: proc(allocator := context.allocator) -> (z: ^Huffman_T
@(optimization_mode="speed")
build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
sizes: [HUFFMAN_MAX_BITS+1]int
- next_code: [HUFFMAN_MAX_BITS]int
+ next_code: [HUFFMAN_MAX_BITS+1]int
k := int(0)
@@ -256,21 +256,21 @@ build_huffman :: proc(z: ^Huffman_Table, code_lengths: []u8) -> (err: Error) {
}
sizes[0] = 0
- for i in 1..<(HUFFMAN_MAX_BITS+1) {
+ for i in 1 ..< HUFFMAN_MAX_BITS {
if sizes[i] > (1 << uint(i)) {
- return E_Deflate.Huffman_Bad_Sizes
+ return .Huffman_Bad_Sizes
}
}
code := int(0)
- for i in 1..= (1 << u16(i)) {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
}
z.maxcode[i] = code << (HUFFMAN_MAX_BITS - uint(i))
@@ -314,15 +314,15 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
s += 1
}
if s >= 16 {
- return 0, E_Deflate.Bad_Huffman_Code
+ return 0, .Bad_Huffman_Code
}
// code size is s, so:
b := (k >> (16-s)) - int(t.firstcode[s]) + int(t.firstsymbol[s])
if b >= size_of(t.size) {
- return 0, E_Deflate.Bad_Huffman_Code
+ return 0, .Bad_Huffman_Code
}
if t.size[b] != s {
- return 0, E_Deflate.Bad_Huffman_Code
+ return 0, .Bad_Huffman_Code
}
compress.consume_bits_lsb(z, s)
@@ -335,11 +335,11 @@ decode_huffman_slowpath :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Erro
decode_huffman :: proc(z: ^$C, t: ^Huffman_Table) -> (r: u16, err: Error) #no_bounds_check {
if z.num_bits < 16 {
if z.num_bits > 63 {
- return 0, E_ZLIB.Code_Buffer_Malformed
+ return 0, .Code_Buffer_Malformed
}
compress.refill_lsb(z)
if z.num_bits > 63 {
- return 0, E_General.Stream_Too_Short
+ return 0, .Stream_Too_Short
}
}
#no_bounds_check b := t.fast[z.code_buffer & ZFAST_MASK]
@@ -361,7 +361,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if value < 256 {
e := write_byte(z, u8(value))
if e != .None {
- return E_General.Output_Too_Short
+ return .Output_Too_Short
}
} else {
if value == 256 {
@@ -377,7 +377,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
value, e = decode_huffman(z, z_offset)
if e != nil {
- return E_Deflate.Bad_Huffman_Code
+ return .Bad_Huffman_Code
}
distance := Z_DIST_BASE[value]
@@ -387,7 +387,7 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
if z.bytes_written < i64(distance) {
// Distance is longer than we've decoded so far.
- return E_Deflate.Bad_Distance
+ return .Bad_Distance
}
/*
@@ -405,14 +405,14 @@ parse_huffman_block :: proc(z: ^$C, z_repeat, z_offset: ^Huffman_Table) -> (err:
c := z.output.buf[z.bytes_written - i64(distance)]
e := repl_byte(z, length, c)
if e != .None {
- return E_General.Output_Too_Short
+ return .Output_Too_Short
}
}
} else {
if length > 0 {
e := repl_bytes(z, length, distance)
if e != .None {
- return E_General.Output_Too_Short
+ return .Output_Too_Short
}
}
}
@@ -432,25 +432,25 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
if !raw {
size, size_err := compress.input_size(ctx)
if size < 6 || size_err != nil {
- return E_General.Stream_Too_Short
+ return .Stream_Too_Short
}
cmf, _ := compress.read_u8(ctx)
method := Compression_Method(cmf & 0xf)
if method != .DEFLATE {
- return E_General.Unknown_Compression_Method
+ return .Unknown_Compression_Method
}
if cinfo := (cmf >> 4) & 0xf; cinfo > 7 {
- return E_ZLIB.Unsupported_Window_Size
+ return .Unsupported_Window_Size
}
flg, _ := compress.read_u8(ctx)
fcheck := flg & 0x1f
fcheck_computed := (cmf << 8 | flg) & 0x1f
if fcheck != fcheck_computed {
- return E_General.Checksum_Failed
+ return .Checksum_Failed
}
/*
@@ -458,7 +458,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
They're application specific and PNG doesn't use them.
*/
if fdict := (flg >> 5) & 1; fdict != 0 {
- return E_ZLIB.FDICT_Unsupported
+ return .FDICT_Unsupported
}
// flevel := Compression_Level((flg >> 6) & 3);
@@ -485,7 +485,7 @@ inflate_from_context :: proc(using ctx: ^compress.Context_Memory_Input, raw := f
output_hash := hash.adler32(ctx.output.buf[:])
if output_hash != u32(adler) {
- return E_General.Checksum_Failed
+ return .Checksum_Failed
}
}
return nil
@@ -538,23 +538,24 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
final = compress.read_bits_lsb(z, 1)
type = compress.read_bits_lsb(z, 2)
- // fmt.printf("Final: %v | Type: %v\n", final, type);
+ // fmt.printf("Final: %v | Type: %v\n", final, type)
switch type {
case 0:
+ // fmt.printf("Method 0: STORED\n")
// Uncompressed block
// Discard bits until next byte boundary
compress.discard_to_next_byte_lsb(z)
- uncompressed_len := i16(compress.read_bits_lsb(z, 16))
- length_check := i16(compress.read_bits_lsb(z, 16))
+ uncompressed_len := u16(compress.read_bits_lsb(z, 16))
+ length_check := u16(compress.read_bits_lsb(z, 16))
- // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check);
+ // fmt.printf("LEN: %v, ~LEN: %v, NLEN: %v, ~NLEN: %v\n", uncompressed_len, ~uncompressed_len, length_check, ~length_check)
if ~uncompressed_len != length_check {
- return E_Deflate.Len_Nlen_Mismatch
+ return .Len_Nlen_Mismatch
}
/*
@@ -567,10 +568,12 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
write_byte(z, u8(lit))
uncompressed_len -= 1
}
+ assert(uncompressed_len == 0)
+
case 3:
- return E_Deflate.BType_3
+ return .BType_3
case:
- // log.debugf("Err: %v | Final: %v | Type: %v\n", err, final, type);
+ // fmt.printf("Err: %v | Final: %v | Type: %v\n", err, final, type)
if type == 1 {
// Use fixed code lengths.
build_huffman(z_repeat, Z_FIXED_LENGTH[:]) or_return
@@ -601,7 +604,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
c = decode_huffman(z, codelength_ht) or_return
if c < 0 || c >= 19 {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
if c < 16 {
lencodes[n] = u8(c)
@@ -613,7 +616,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 16:
c = u16(compress.read_bits_no_refill_lsb(z, 2) + 3)
if n == 0 {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
fill = lencodes[n - 1]
case 17:
@@ -621,11 +624,11 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
case 18:
c = u16(compress.read_bits_no_refill_lsb(z, 7) + 11)
case:
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
if ntot - n < u32(c) {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
nc := n + u32(c)
@@ -636,7 +639,7 @@ inflate_raw :: proc(z: ^$C, expected_output_size := -1, allocator := context.all
}
if n != ntot {
- return E_Deflate.Huffman_Bad_Code_Lengths
+ return .Huffman_Bad_Code_Lengths
}
build_huffman(z_repeat, lencodes[:hlit]) or_return
@@ -674,4 +677,4 @@ inflate_from_byte_array_raw :: proc(input: []u8, buf: ^bytes.Buffer, raw := fals
return inflate_raw(z=&ctx, expected_output_size=expected_output_size)
}
-inflate :: proc{inflate_from_context, inflate_from_byte_array};
\ No newline at end of file
+inflate :: proc{inflate_from_context, inflate_from_byte_array}
diff --git a/core/container/array.odin b/core/container/array.odin
deleted file mode 100644
index 2d5a64ec3..000000000
--- a/core/container/array.odin
+++ /dev/null
@@ -1,216 +0,0 @@
-package container
-
-import "core:mem"
-import "core:runtime"
-
-Array :: struct($T: typeid) {
- data: ^T,
- len: int,
- cap: int,
- allocator: mem.Allocator,
-}
-
-ARRAY_DEFAULT_CAPACITY :: 16
-
-/*
-array_init :: proc {
- array_init_none,
- array_init_len,
- array_init_len_cap,
-}
-array_init
-array_delete
-array_len
-array_cap
-array_space
-array_slice
-array_get
-array_get_ptr
-array_set
-array_reserve
-array_resize
-array_push = array_append :: proc{
- array_push_back,
- array_push_back_elems,
-}
-array_push_front
-array_pop_back
-array_pop_front
-array_consume
-array_trim
-array_clear
-array_clone
-array_set_capacity
-array_grow
-*/
-
-
-array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
- array_init_len_cap(a, 0, ARRAY_DEFAULT_CAPACITY, allocator)
-}
-array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
- array_init_len_cap(a, len, len, allocator)
-}
-array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
- a.allocator = allocator
- a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator))
- a.len = len
- a.cap = cap
-}
-
-array_init :: proc{array_init_none, array_init_len, array_init_len_cap}
-
-array_delete :: proc(a: $A/Array) {
- mem.free(a.data, a.allocator)
-}
-
-array_len :: proc(a: $A/Array) -> int {
- return a.len
-}
-
-array_cap :: proc(a: $A/Array) -> int {
- return a.cap
-}
-
-array_space :: proc(a: $A/Array) -> int {
- return a.cap - a.len
-}
-
-array_slice :: proc(a: $A/Array($T)) -> []T {
- s := mem.Raw_Slice{a.data, a.len}
- return transmute([]T)s
-}
-
-array_cap_slice :: proc(a: $A/Array($T)) -> []T {
- s := mem.Raw_Slice{a.data, a.cap}
- return transmute([]T)s
-}
-
-array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
- runtime.bounds_check_error_loc(loc, index, array_len(a))
- return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^
-}
-array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
- runtime.bounds_check_error_loc(loc, index, array_len(a))
- return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))
-}
-
-array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) {
- runtime.bounds_check_error_loc(loc, index, array_len(a^))
- (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item
-}
-
-
-array_reserve :: proc(a: ^$A/Array, capacity: int) {
- if capacity > a.len {
- array_set_capacity(a, capacity)
- }
-}
-
-array_resize :: proc(a: ^$A/Array, length: int) {
- if length > a.len {
- array_set_capacity(a, length)
- }
- a.len = length
-}
-
-
-
-array_push_back :: proc(a: ^$A/Array($T), item: T) {
- if array_space(a^) == 0 {
- array_grow(a)
- }
-
- a.len += 1
- array_set(a, a.len-1, item)
-}
-
-array_push_front :: proc(a: ^$A/Array($T), item: T) {
- if array_space(a^) == 0 {
- array_grow(a)
- }
-
- a.len += 1
- data := array_slice(a^)
- copy(data[1:], data[:])
- data[0] = item
-}
-
-array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
- assert(condition=a.len > 0, loc=loc)
- item := array_get(a^, a.len-1)
- a.len -= 1
- return item
-}
-
-array_pop_front :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
- assert(condition=a.len > 0, loc=loc)
- item := array_get(a^, 0)
- s := array_slice(a^)
- copy(s[:], s[1:])
- a.len -= 1
- return item
-}
-
-
-array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
- assert(condition=a.len >= count, loc=loc)
- a.len -= count
-}
-
-
-array_trim :: proc(a: ^$A/Array($T)) {
- array_set_capacity(a, a.len)
-}
-
-array_clear :: proc(a: ^$A/Array($T)) {
- array_resize(a, 0)
-}
-
-array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
- res: A
- array_init(&res, array_len(a), array_len(a), allocator)
- copy(array_slice(res), array_slice(a))
- return res
-}
-
-array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
- if array_space(a^) < len(items) {
- array_grow(a, a.len + len(items))
- }
- offset := a.len
- data := array_cap_slice(a^)
- n := copy(data[a.len:], items)
- a.len += n
-}
-
-array_push :: proc{array_push_back, array_push_back_elems}
-array_append :: proc{array_push_back, array_push_back_elems}
-
-array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
- if new_capacity == a.cap {
- return
- }
-
- if new_capacity < a.len {
- array_resize(a, new_capacity)
- }
-
- new_data: ^T
- if new_capacity > 0 {
- if a.allocator.procedure == nil {
- a.allocator = context.allocator
- }
- new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator))
- if new_data != nil {
- mem.copy(new_data, a.data, size_of(T)*a.len)
- }
- }
- mem.free(a.data, a.allocator)
- a.data = new_data
- a.cap = new_capacity
-}
-array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
- new_capacity := max(array_len(a^)*2 + 8, min_capacity)
- array_set_capacity(a, new_capacity)
-}
diff --git a/core/container/bit_array/bit_array.odin b/core/container/bit_array/bit_array.odin
new file mode 100644
index 000000000..98ef4b542
--- /dev/null
+++ b/core/container/bit_array/bit_array.odin
@@ -0,0 +1,239 @@
+package dynamic_bit_array
+
+import "core:intrinsics"
+import "core:mem"
+
+/*
+ Note that these constants are dependent on the backing being a u64.
+*/
+@(private="file")
+INDEX_SHIFT :: 6
+
+@(private="file")
+INDEX_MASK :: 63
+
+@(private="file")
+NUM_BITS :: 64
+
+Bit_Array :: struct {
+ bits: [dynamic]u64,
+ bias: int,
+ max_index: int,
+ free_pointer: bool,
+}
+
+Bit_Array_Iterator :: struct {
+ array: ^Bit_Array,
+ word_idx: int,
+ bit_idx: uint,
+}
+
+/*
+ In:
+ - ba: ^Bit_Array - the array to iterate over
+
+ Out:
+ - it: ^Bit_Array_Iterator - the iterator that holds iteration state
+*/
+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.
+
+ 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
+*/
+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
+ if index > it.array.max_index { return false, 0, false }
+
+ word := it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+ set = (word >> it.bit_idx & 1) == 1
+
+ it.bit_idx += 1
+ if it.bit_idx >= NUM_BITS {
+ it.bit_idx = 0
+ it.word_idx += 1
+ }
+
+ return set, index, true
+}
+
+/*
+ In:
+ - it: ^Bit_Array_Iterator - the iterator struct that holds the state.
+
+ 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
+*/
+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.
+
+ 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
+*/
+iterate_by_unset:: proc (it: ^Bit_Array_Iterator) -> (index: int, ok: bool) {
+ return iterate_internal_(it, false)
+}
+
+@(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,
+ // 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 {
+ it.word_idx += 1
+ it.bit_idx = 0
+ word = it.array.bits[it.word_idx] if len(it.array.bits) > it.word_idx else 0
+ 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
+ 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
+ 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
+
+ it.bit_idx += 1
+ if it.bit_idx >= NUM_BITS {
+ it.bit_idx = 0
+ it.word_idx += 1
+ }
+ 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.
+
+ Out:
+ - res: The bit you're interested in.
+ - ok: Whether the index was valid. Returns `false` if the index is smaller than the bias.
+
+ The `ok` return value may be ignored.
+*/
+get :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (res: bool, ok: bool) {
+ 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
+
+ /*
+ If we `get` a bit that doesn't fit in the Bit Array, it's naturally `false`.
+ This early-out prevents unnecessary resizing.
+ */
+ if leg_index + 1 > len(ba.bits) { return false, true }
+
+ val := u64(1 << uint(bit_index))
+ res = ba.bits[leg_index] & val == val
+
+ return res, true
+}
+
+/*
+ In:
+ - ba: ^Bit_Array - a pointer to the Bit Array
+ - index: The bit index. Can be an enum member.
+
+ Out:
+ - ok: Whether or not we managed to set requested bit.
+
+ `set` automatically resizes the Bit Array to accommodate the requested index if needed.
+*/
+set :: proc(ba: ^Bit_Array, #any_int index: uint, allocator := context.allocator) -> (ok: bool) {
+
+ idx := int(index) - ba.bias
+
+ if ba == nil || int(index) < ba.bias { return false }
+ context.allocator = allocator
+
+ leg_index := idx >> INDEX_SHIFT
+ bit_index := idx & INDEX_MASK
+
+ resize_if_needed(ba, leg_index) or_return
+
+ ba.max_index = max(idx, ba.max_index)
+ ba.bits[leg_index] |= 1 << uint(bit_index)
+ return true
+}
+
+/*
+ A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+*/
+create :: proc(max_index: int, min_index := 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
+
+ res = new(Bit_Array)
+ res.bias = min_index
+ res.max_index = max_index
+ res.free_pointer = true
+ return res, resize_if_needed(res, legs)
+}
+
+/*
+ Sets all bits to `false`.
+*/
+clear :: proc(ba: ^Bit_Array) {
+ if ba == nil { return }
+ mem.zero_slice(ba.bits[:])
+}
+
+/*
+ Releases the memory used by the Bit Array.
+*/
+destroy :: proc(ba: ^Bit_Array) {
+ if ba == nil { return }
+ delete(ba.bits)
+ if ba.free_pointer { // Only free if this Bit_Array was created using `create`, not when on the stack.
+ free(ba)
+ }
+}
+
+/*
+ Resizes the Bit Array. For internal use.
+ If you want to reserve the memory for a given-sized Bit Array up front, you can use `create`.
+*/
+@(private="file")
+resize_if_needed :: proc(ba: ^Bit_Array, legs: int, allocator := context.allocator) -> (ok: bool) {
+ if ba == nil { return false }
+
+ context.allocator = allocator
+
+ if legs + 1 > len(ba.bits) {
+ resize(&ba.bits, legs + 1)
+ }
+ return len(ba.bits) > legs
+}
diff --git a/core/container/bit_array/doc.odin b/core/container/bit_array/doc.odin
new file mode 100644
index 000000000..52e252d8a
--- /dev/null
+++ b/core/container/bit_array/doc.odin
@@ -0,0 +1,53 @@
+package dynamic_bit_array
+
+/*
+ The Bit Array can be used in several ways:
+
+ -- By default you don't need to instantiate a Bit Array:
+
+ package test
+
+ import "core:fmt"
+ import "core:container/bit_array"
+
+ main :: proc() {
+ using bit_array
+
+ bits: Bit_Array
+
+ // returns `true`
+ fmt.println(set(&bits, 42))
+
+ // returns `false`, `false`, because this Bit Array wasn't created to allow negative indices.
+ was_set, was_retrieved := get(&bits, -1)
+ fmt.println(was_set, was_retrieved)
+ destroy(&bits)
+ }
+
+ -- A Bit Array can optionally allow for negative indices, if the mininum value was given during creation:
+
+ package test
+
+ import "core:fmt"
+ import "core:container/bit_array"
+
+ main :: proc() {
+ Foo :: enum int {
+ Negative_Test = -42,
+ Bar = 420,
+ Leaves = 69105,
+ }
+
+ using bit_array
+
+ bits := create(int(max(Foo)), int(min(Foo)))
+ defer destroy(bits)
+
+ fmt.printf("Set(Bar): %v\n", set(bits, Foo.Bar))
+ fmt.printf("Get(Bar): %v, %v\n", get(bits, Foo.Bar))
+ fmt.printf("Set(Negative_Test): %v\n", set(bits, Foo.Negative_Test))
+ fmt.printf("Get(Leaves): %v, %v\n", get(bits, Foo.Leaves))
+ fmt.printf("Get(Negative_Test): %v, %v\n", get(bits, Foo.Negative_Test))
+ fmt.printf("Freed.\n")
+ }
+*/
\ No newline at end of file
diff --git a/core/container/bloom_filter.odin b/core/container/bloom_filter.odin
deleted file mode 100644
index 8af7aeb85..000000000
--- a/core/container/bloom_filter.odin
+++ /dev/null
@@ -1,80 +0,0 @@
-package container
-
-import "core:mem"
-
-Bloom_Hash_Proc :: #type proc(data: []byte) -> u32
-
-Bloom_Hash :: struct {
- hash_proc: Bloom_Hash_Proc,
- next: ^Bloom_Hash,
-}
-
-Bloom_Filter :: struct {
- allocator: mem.Allocator,
- hash: ^Bloom_Hash,
- bits: []byte,
-}
-
-bloom_filter_init :: proc(b: ^Bloom_Filter, size: int, allocator := context.allocator) {
- b.allocator = allocator
- b.bits = make([]byte, size, allocator)
-}
-
-bloom_filter_destroy :: proc(b: ^Bloom_Filter) {
- context.allocator = b.allocator
- delete(b.bits)
- for b.hash != nil {
- hash := b.hash
- b.hash = b.hash.next
- free(hash)
- }
-}
-
-bloom_filter_add_hash_proc :: proc(b: ^Bloom_Filter, hash_proc: Bloom_Hash_Proc) {
- context.allocator = b.allocator
- h := new(Bloom_Hash)
- h.hash_proc = hash_proc
-
- head := &b.hash
- for head^ != nil {
- head = &(head^.next)
- }
- head^ = h
-}
-
-bloom_filter_add :: proc(b: ^Bloom_Filter, item: []byte) {
- #no_bounds_check for h := b.hash; h != nil; h = h.next {
- hash := h.hash_proc(item)
- hash %= u32(len(b.bits) * 8)
- b.bits[hash >> 3] |= 1 << (hash & 3)
- }
-}
-
-bloom_filter_add_string :: proc(b: ^Bloom_Filter, item: string) {
- bloom_filter_add(b, transmute([]byte)item)
-}
-
-bloom_filter_add_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) {
- item := mem.slice_ptr((^byte)(data), size)
- bloom_filter_add(b, item)
-}
-
-bloom_filter_test :: proc(b: ^Bloom_Filter, item: []byte) -> bool {
- #no_bounds_check for h := b.hash; h != nil; h = h.next {
- hash := h.hash_proc(item)
- hash %= u32(len(b.bits) * 8)
- if (b.bits[hash >> 3] & (1 << (hash & 3)) == 0) {
- return false
- }
- }
- return true
-}
-
-bloom_filter_test_string :: proc(b: ^Bloom_Filter, item: string) -> bool {
- return bloom_filter_test(b, transmute([]byte)item)
-}
-
-bloom_filter_test_raw :: proc(b: ^Bloom_Filter, data: rawptr, size: int) -> bool {
- item := mem.slice_ptr((^byte)(data), size)
- return bloom_filter_test(b, item)
-}
diff --git a/core/container/lru/lru_cache.odin b/core/container/lru/lru_cache.odin
new file mode 100644
index 000000000..b59f29f0c
--- /dev/null
+++ b/core/container/lru/lru_cache.odin
@@ -0,0 +1,201 @@
+package container_lru
+
+import "core:runtime"
+import "core:intrinsics"
+_ :: runtime
+_ :: intrinsics
+
+Node :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
+ prev, next: ^Node(Key, Value),
+ key: Key,
+ value: Value,
+}
+
+// Cache is an LRU cache. It automatically removes entries as new entries are
+// added if the capacity is reached. Entries are removed based on how recently
+// they were used where the oldest entries are removed first.
+Cache :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
+ head: ^Node(Key, Value),
+ tail: ^Node(Key, Value),
+
+ entries: map[Key]^Node(Key, Value),
+
+ count: int,
+ capacity: int,
+
+ node_allocator: runtime.Allocator,
+
+ on_remove: proc(key: Key, value: Value, user_data: rawptr),
+ on_remove_user_data: rawptr,
+}
+
+// init initializes a Cache
+init :: proc(c: ^$C/Cache($Key, $Value), capacity: int, entries_allocator := context.allocator, node_allocator := context.allocator) {
+ c.entries.allocator = entries_allocator
+ c.node_allocator = node_allocator
+ c.capacity = capacity
+}
+
+// destroy deinitializes a Cachem
+destroy :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
+ clear(c, call_on_remove)
+ delete(c.entries)
+}
+
+// clear the contents of a Cache
+clear :: proc(c: ^$C/Cache($Key, $Value), call_on_remove: bool) {
+ for _, node in c.entries {
+ if call_on_remove {
+ _call_on_remove(c, node)
+ }
+ free(node, c.node_allocator)
+ }
+ runtime.clear(&c.entries)
+ c.head = nil
+ c.tail = nil
+ c.count = 0
+}
+
+// set the given key value pair. This operation updates the recent usage of the item.
+set :: proc(c: ^$C/Cache($Key, $Value), key: Key, value: Value) -> runtime.Allocator_Error {
+ if e, ok := c.entries[key]; ok {
+ e.value = value
+ _pop_node(c, e)
+ _push_front_node(c, e)
+ return nil
+ }
+
+ e : ^Node(Key, Value) = nil
+ assert(c.count <= c.capacity)
+ if c.count == c.capacity {
+ e = c.tail
+ _remove_node(c, e)
+ }
+ else {
+ c.count += 1
+ e = new(Node(Key, Value), c.node_allocator) or_return
+ }
+
+ e.key = key
+ e.value = value
+ _push_front_node(c, e)
+ c.entries[key] = e
+
+ return nil
+}
+
+// get a value from the cache from a given key. This operation updates the usage of the item.
+get :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
+ e: ^Node(Key, Value)
+ e, ok = c.entries[key]
+ if !ok {
+ return
+ }
+ _pop_node(c, e)
+ _push_front_node(c, e)
+ return e.value, true
+}
+
+// get_ptr gets the pointer to a value the cache from a given key. This operation updates the usage of the item.
+get_ptr :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: ^Value, ok: bool) #optional_ok {
+ e: ^Node(Key, Value)
+ e, ok = c.entries[key]
+ if !ok {
+ return
+ }
+ _pop_node(c, e)
+ _push_front_node(c, e)
+ return &e.value, true
+}
+
+// peek gets the value from the cache from a given key without updating the recent usage.
+peek :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> (value: Value, ok: bool) #optional_ok {
+ e: ^Node(Key, Value)
+ e, ok = c.entries[key]
+ if !ok {
+ return
+ }
+ return e.value, true
+}
+
+// exists checks for the existence of a value from a given key without updating the recent usage.
+exists :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
+ return key in c.entries
+}
+
+// remove removes an item from the cache.
+remove :: proc(c: ^$C/Cache($Key, $Value), key: Key) -> bool {
+ e, ok := c.entries[key]
+ if !ok {
+ return false
+ }
+ _remove_node(c, e)
+ free(node, c.node_allocator)
+ c.count -= 1
+ return true
+}
+
+
+@(private)
+_remove_node :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
+ if c.head == node {
+ c.head = node.next
+ }
+ if c.tail == node {
+ c.tail = node.prev
+ }
+ if node.prev != nil {
+ node.prev.next = node.next
+ }
+ if node.next != nil {
+ node.next.prev = node.prev
+ }
+ node.prev = nil
+ node.next = nil
+
+ delete_key(&c.entries, node.key)
+
+ _call_on_remove(c, node)
+}
+
+@(private)
+_call_on_remove :: proc(c: ^$C/Cache($Key, $Value), node: ^Node(Key, Value)) {
+ if c.on_remove != nil {
+ c.on_remove(node.key, node.value, c.on_remove_user_data)
+ }
+}
+
+@(private)
+_push_front_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
+ if c.head != nil {
+ e.next = c.head
+ e.next.prev = e
+ }
+ c.head = e
+ if c.tail == nil {
+ c.tail = e
+ }
+ e.prev = nil
+}
+
+@(private)
+_pop_node :: proc(c: ^$C/Cache($Key, $Value), e: ^Node(Key, Value)) {
+ if e == nil {
+ return
+ }
+ if c.head == e {
+ c.head = e.next
+ }
+ if c.tail == e {
+ c.tail = e.prev
+ }
+ if e.prev != nil {
+ e.prev.next = e.next
+ }
+
+ if e.next != nil {
+ e.next.prev = e.prev
+ }
+ e.prev = nil
+ e.next = nil
+}
\ No newline at end of file
diff --git a/core/container/map.odin b/core/container/map.odin
deleted file mode 100644
index 54cbc22fc..000000000
--- a/core/container/map.odin
+++ /dev/null
@@ -1,377 +0,0 @@
-package container
-
-import "core:intrinsics"
-_ :: intrinsics
-
-
-Map :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
- hash: Array(int),
- entries: Array(Map_Entry(Key, Value)),
-}
-
-Map_Entry :: struct($Key, $Value: typeid) where intrinsics.type_is_valid_map_key(Key) {
- hash: uintptr,
- next: int,
- key: Key,
- value: Value,
-}
-
-
-/*
-map_init :: proc{
- map_init_none,
- map_init_cap,
-}
-map_delete
-
-map_has
-map_get
-map_get_default
-map_get_ptr
-map_set
-map_remove
-map_reserve
-map_clear
-
-// Multi Map
-
-multi_map_find_first
-multi_map_find_next
-multi_map_count
-multi_map_get :: proc{
- multi_map_get_array,
- multi_map_get_slice,
-};
-multi_map_get_as_slice
-multi_map_insert
-multi_map_remove
-multi_map_remove_all
-
-*/
-
-map_init :: proc{map_init_none, map_init_cap}
-
-map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) {
- m.hash.allocator = allocator
- m.entries.allocator = allocator
-}
-
-map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) {
- m.hash.allocator = allocator
- m.entries.allocator = allocator
- map_reserve(m, cap)
-}
-
-map_delete :: proc(m: $M/Map($Key, $Value)) {
- array_delete(m.hash)
- array_delete(m.entries)
-}
-
-
-map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool {
- return _map_find_or_fail(m, key) >= 0
-}
-
-map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok {
- i := _map_find_or_fail(m, key)
- if i < 0 {
- return {}, false
- }
- return array_get(m.entries, i).value, true
-}
-
-map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok {
- i := _map_find_or_fail(m, key)
- if i < 0 {
- return default, false
- }
- return array_get(m.entries, i).value, true
-}
-
-map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value {
- i := _map_find_or_fail(m, key)
- if i < 0 {
- return nil
- }
- return array_get_ptr(m.entries, i).value
-}
-
-map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
- if array_len(m.hash) == 0 {
- _map_grow(m)
- }
-
- i := _map_find_or_make(m, key)
- array_get_ptr(m.entries, i).value = value
- if _map_full(m^) {
- _map_grow(m)
- }
-}
-
-map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) {
- fr := _map_find_key(m^, key)
- if fr.entry_index >= 0 {
- _map_erase(m, fr)
- }
-}
-
-
-map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) {
- nm: M
- map_init(&nm, m.hash.allocator)
- array_resize(&nm.hash, new_size)
- array_reserve(&nm.entries, array_len(m.entries))
-
- for i in 0.. ^Map_Entry(Key, Value) {
- i := _map_find_or_fail(m, key)
- if i < 0 {
- return nil
- }
- return array_get_ptr(m.entries, i)
-}
-
-multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> ^Map_Entry(Key, Value) {
- i := e.next
- for i >= 0 {
- it := array_get_ptr(m.entries, i)
- if it.hash == e.hash && it.key == e.key {
- return it
- }
- i = it.next
- }
- return nil
-}
-
-multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
- n := 0
- e := multi_map_find_first(m, key)
- for e != nil {
- n += 1
- e = multi_map_find_next(m, e)
- }
- return n
-}
-
-multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}
-
-multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) {
- if items == nil {
- return
- }
- e := multi_map_find_first(m, key)
- for e != nil {
- array_append(items, e.value)
- e = multi_map_find_next(m, e)
- }
-}
-
-multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) {
- e := multi_map_find_first(m, key)
- i := 0
- for e != nil && i < len(items) {
- items[i] = e.value
- i += 1
- e = multi_map_find_next(m, e)
- }
-}
-
-multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value {
- items: Array(Value)
- array_init(&items, 0)
-
- e := multi_map_find_first(m, key)
- for e != nil {
- array_append(&items, e.value)
- e = multi_map_find_next(m, e)
- }
-
- return array_slice(items)
-}
-
-
-multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) {
- if array_len(m.hash) == 0 {
- _map_grow(m)
- }
-
- i := _map_make(m, key)
- array_get_ptr(m.entries, i).value = value
- if _map_full(m^) {
- _map_grow(m)
- }
-}
-
-multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) {
- fr := _map_find_entry(m, e)
- if fr.entry_index >= 0 {
- _map_erase(m, fr)
- }
-}
-
-multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) {
- for map_exist(m^, key) {
- map_remove(m, key)
- }
-}
-
-
-/// Internal
-
-
-Map_Find_Result :: struct {
- hash_index: int,
- entry_prev: int,
- entry_index: int,
-}
-
-_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) {
- hasher := intrinsics.type_hasher_proc(Key)
-
- e: Map_Entry(Key, Value)
- e.key = key
- e.hash = hasher(&e.key, 0)
- e.next = -1
- idx := array_len(m.entries)
- array_push(&m.entries, e)
- return idx
-}
-
-_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
- if fr.entry_prev < 0 {
- array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
- } else {
- array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
- }
-
- if fr.entry_index == array_len(m.entries)-1 {
- array_pop_back(&m.entries)
- return
- }
-
- array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
- last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key)
-
- if last.entry_prev < 0 {
- array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
- } else {
- array_set(&m.hash, last.hash_index, fr.entry_index)
- }
-}
-
-
-_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) {
- fr: Map_Find_Result
- fr.hash_index = -1
- fr.entry_prev = -1
- fr.entry_index = -1
-
- if array_len(m.hash) == 0 {
- return fr
- }
-
- hasher := intrinsics.type_hasher_proc(Key)
-
- key := key
- hash := hasher(&key, 0)
-
- fr.hash_index = int(hash % uintptr(array_len(m.hash)))
- fr.entry_index = array_get(m.hash, fr.hash_index)
- for fr.entry_index >= 0 {
- it := array_get_ptr(m.entries, fr.entry_index)
- if it.hash == hash && it.key == key {
- return fr
- }
- fr.entry_prev = fr.entry_index
- fr.entry_index = it.next
- }
- return fr
-}
-
-_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Key, Value)) -> Map_Find_Result {
- fr: Map_Find_Result
- fr.hash_index = -1
- fr.entry_prev = -1
- fr.entry_index = -1
-
- if array_len(m.hash) == 0 {
- return fr
- }
-
- fr.hash_index = int(e.hash % uintptr(array_len(m.hash)))
- fr.entry_index = array_get(m.hash, fr.hash_index)
- for fr.entry_index >= 0 {
- it := array_get_ptr(m.entries, fr.entry_index)
- if it == e {
- return fr
- }
- fr.entry_prev = fr.entry_index
- fr.entry_index = it.next
- }
- return fr
-}
-
-_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int {
- return _map_find_key(m, key).entry_index
-}
-_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
- fr := _map_find_key(m^, key)
- if fr.entry_index >= 0 {
- return fr.entry_index
- }
-
- i := _map_add_entry(m, key)
- if fr.entry_prev < 0 {
- array_set(&m.hash, fr.hash_index, i)
- } else {
- array_get_ptr(m.entries, fr.entry_prev).next = i
- }
- return i
-}
-
-
-_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int {
- fr := _map_find_key(m^, key)
- i := _map_add_entry(m, key)
-
- if fr.entry_prev < 0 {
- array_set(&m.hash, fr.hash_index, i)
- } else {
- array_get_ptr(m.entries, fr.entry_prev).next = i
- }
-
- array_get_ptr(m.entries, i).next = fr.entry_index
-
- return i
-}
-
-
-_map_full :: proc(m: $M/Map($Key, $Value)) -> bool {
- // TODO(bill): Determine good max load factor
- return array_len(m.entries) >= (array_len(m.hash) / 4)*3
-}
-
-_map_grow :: proc(m: ^$M/Map($Key, $Value)) {
- new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
- map_reserve(m, new_size)
-}
-
-
diff --git a/core/container/priority_queue.odin b/core/container/priority_queue.odin
deleted file mode 100644
index c54e964a6..000000000
--- a/core/container/priority_queue.odin
+++ /dev/null
@@ -1,121 +0,0 @@
-package container
-
-Priority_Queue :: struct($T: typeid) {
- data: Array(T),
- len: int,
- priority: proc(item: T) -> int,
-}
-
-priority_queue_init_none :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, allocator := context.allocator) {
- queue_init_len(q, f, 0, allocator)
-}
-priority_queue_init_len :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, allocator := context.allocator) {
- queue_init_len_cap(q, f, 0, 16, allocator)
-}
-priority_queue_init_len_cap :: proc(q: ^$Q/Priority_Queue($T), f: proc(item: T) -> int, len: int, cap: int, allocator := context.allocator) {
- array_init(&q.data, len, cap, allocator)
- q.len = len
- q.priority = f
-}
-
-priority_queue_init :: proc{priority_queue_init_none, priority_queue_init_len, priority_queue_init_len_cap}
-
-
-priority_queue_delete :: proc(q: $Q/Priority_Queue($T)) {
- array_delete(q.data)
-}
-
-priority_queue_clear :: proc(q: ^$Q/Priority_Queue($T)) {
- q.len = 0
-}
-
-priority_queue_len :: proc(q: $Q/Priority_Queue($T)) -> int {
- return q.len
-}
-
-priority_queue_cap :: proc(q: $Q/Priority_Queue($T)) -> int {
- return array_cap(q.data)
-}
-
-priority_queue_space :: proc(q: $Q/Priority_Queue($T)) -> int {
- return array_len(q.data) - q.len
-}
-
-priority_queue_reserve :: proc(q: ^$Q/Priority_Queue($T), capacity: int) {
- if capacity > q.len {
- array_resize(&q.data, new_capacity)
- }
-}
-
-priority_queue_resize :: proc(q: ^$Q/Priority_Queue($T), length: int) {
- if length > q.len {
- array_resize(&q.data, new_capacity)
- }
- q.len = length
-}
-
-_priority_queue_grow :: proc(q: ^$Q/Priority_Queue($T), min_capacity: int = 0) {
- new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
- array_resize(&q.data, new_capacity)
-}
-
-
-priority_queue_push :: proc(q: ^$Q/Priority_Queue($T), item: T) {
- if array_len(q.data) - q.len == 0 {
- _priority_queue_grow(q)
- }
-
- s := array_slice(q.data)
- s[q.len] = item
-
- i := q.len
- for i > 0 {
- p := (i - 1) / 2
- if q.priority(s[p]) <= q.priority(item) {
- break
- }
- s[i] = s[p]
- i = p
- }
-
- q.len += 1
- if q.len > 0 {
- s[i] = item
- }
-}
-
-
-
-priority_queue_pop :: proc(q: ^$Q/Priority_Queue($T)) -> T {
- assert(q.len > 0)
-
- s := array_slice(q.data)
- min := s[0]
- root := s[q.len-1]
- q.len -= 1
-
- i := 0
- for i * 2 + 1 < q.len {
- a := i * 2 + 1
- b := i * 2 + 2
- c := b < q.len && q.priority(s[b]) < q.priority(s[a]) ? b : a
-
- if q.priority(s[c]) >= q.priority(root) {
- break
- }
- s[i] = s[c]
- i = c
- }
-
- if q.len > 0 {
- s[i] = root
- }
- return min
-}
-
-priority_queue_peek :: proc(q: ^$Q/Priority_Queue($T)) -> T {
- assert(q.len > 0)
-
- s := array_slice(q.data)
- return s[0]
-}
diff --git a/core/container/priority_queue/priority_queue.odin b/core/container/priority_queue/priority_queue.odin
new file mode 100644
index 000000000..e324287f3
--- /dev/null
+++ b/core/container/priority_queue/priority_queue.odin
@@ -0,0 +1,143 @@
+package container_priority_queue
+
+import "core:builtin"
+
+Priority_Queue :: struct($T: typeid) {
+ queue: [dynamic]T,
+
+ less: proc(a, b: T) -> bool,
+ swap: proc(q: []T, i, j: int),
+}
+
+DEFAULT_CAPACITY :: 16
+
+default_swap_proc :: proc($T: typeid) -> proc(q: []T, i, j: int) {
+ return proc(q: []T, i, j: int) {
+ q[i], q[j] = q[j], q[i]
+ }
+}
+
+init :: proc(pq: ^$Q/Priority_Queue($T), less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int), capacity := DEFAULT_CAPACITY, allocator := context.allocator) {
+ if pq.queue.allocator.procedure == nil {
+ pq.queue.allocator = allocator
+ }
+ reserve(pq, capacity)
+ pq.less = less
+ pq.swap = swap
+}
+
+init_from_dynamic_array :: proc(pq: ^$Q/Priority_Queue($T), queue: [dynamic]T, less: proc(a, b: T) -> bool, swap: proc(q: []T, i, j: int)) {
+ pq.queue = queue
+ pq.less = less
+ pq.swap = swap
+ n := builtin.len(pq.queue)
+ for i := n/2 - 1; i >= 0; i -= 1 {
+ _shift_down(pq, i, n)
+ }
+}
+
+destroy :: proc(pq: ^$Q/Priority_Queue($T)) {
+ clear(pq)
+ delete(pq.queue)
+}
+
+reserve :: proc(pq: ^$Q/Priority_Queue($T), capacity: int) {
+ builtin.reserve(&pq.queue, capacity)
+}
+clear :: proc(pq: ^$Q/Priority_Queue($T)) {
+ builtin.clear(&pq.queue)
+}
+len :: proc(pq: $Q/Priority_Queue($T)) -> int {
+ return builtin.len(pq.queue)
+}
+cap :: proc(pq: $Q/Priority_Queue($T)) -> int {
+ return builtin.cap(pq.queue)
+}
+
+_shift_down :: proc(pq: ^$Q/Priority_Queue($T), i0, n: int) -> bool {
+ // O(n log n)
+ if 0 > i0 || i0 > n {
+ return false
+ }
+
+ i := i0
+ queue := pq.queue[:]
+
+ for {
+ j1 := 2*i + 1
+ if j1 < 0 || j1 >= n {
+ break
+ }
+ j := j1
+ if j2 := j1+1; j2 < n && pq.less(queue[j2], queue[j1]) {
+ j = j2
+ }
+ if !pq.less(queue[j], queue[i]) {
+ break
+ }
+
+ pq.swap(queue, i, j)
+ i = j
+ }
+ return i > i0
+}
+
+_shift_up :: proc(pq: ^$Q/Priority_Queue($T), j: int) {
+ j := j
+ queue := pq.queue[:]
+ n := builtin.len(queue)
+ for 0 <= j {
+ i := (j-1)/2
+ if i == j || !pq.less(queue[j], queue[i]) {
+ break
+ }
+ pq.swap(queue, i, j)
+ j = i
+ }
+}
+
+// NOTE(bill): When an element at index 'i' has changed its value, this will fix the
+// the heap ordering. This is using a basic "heapsort" with shift up and a shift down parts.
+fix :: proc(pq: ^$Q/Priority_Queue($T), i: int) {
+ if !_shift_down(pq, i, builtin.len(pq.queue)) {
+ _shift_up(pq, i)
+ }
+}
+
+push :: proc(pq: ^$Q/Priority_Queue($T), value: T) {
+ append(&pq.queue, value)
+ _shift_up(pq, builtin.len(pq.queue)-1)
+}
+
+pop :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T) {
+ assert(condition=builtin.len(pq.queue)>0, loc=loc)
+
+ n := builtin.len(pq.queue)-1
+ pq.swap(pq.queue[:], 0, n)
+ _shift_down(pq, 0, n)
+ return builtin.pop(&pq.queue)
+}
+
+pop_safe :: proc(pq: ^$Q/Priority_Queue($T), loc := #caller_location) -> (value: T, ok: bool) {
+ if builtin.len(pq.queue) > 0 {
+ n := builtin.len(pq.queue)-1
+ pq.swap(pq.queue[:], 0, n)
+ _shift_down(pq, 0, n)
+ return builtin.pop_safe(&pq.queue)
+ }
+ return
+}
+
+remove :: proc(pq: ^$Q/Priority_Queue($T), i: int) -> (value: T, ok: bool) {
+ n := builtin.len(pq.queue)
+ if 0 <= i && i < n {
+ if n != i {
+ pq.swap(pq.queue[:], i, n)
+ _shift_down(pq, i, n)
+ _shift_up(pq, i)
+ }
+ value, ok = builtin.pop_safe(&pq.queue)
+ }
+ return
+}
+
diff --git a/core/container/queue.odin b/core/container/queue.odin
deleted file mode 100644
index bab4a18e6..000000000
--- a/core/container/queue.odin
+++ /dev/null
@@ -1,175 +0,0 @@
-package container
-
-Queue :: struct($T: typeid) {
- data: Array(T),
- len: int,
- offset: int,
-}
-
-/*
-queue_init :: proc{
- queue_init_none,
- queue_init_len,
- queue_init_len_cap,
-}
-queue_delete
-queue_clear
-queue_len
-queue_cap
-queue_space
-queue_get
-queue_set
-queue_reserve
-queue_resize
-queue_push :: proc{
- queue_push_back,
- queue_push_elems,
-};
-queue_push_front
-queue_pop_front
-queue_pop_back
-queue_consume
-*/
-
-queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
- queue_init_len(q, 0, allocator)
-}
-queue_init_len :: proc(q: ^$Q/Queue($T), len: int, allocator := context.allocator) {
- queue_init_len_cap(q, 0, 16, allocator)
-}
-queue_init_len_cap :: proc(q: ^$Q/Queue($T), len: int, cap: int, allocator := context.allocator) {
- array_init(&q.data, len, cap, allocator)
- q.len = len
- q.offset = 0
-}
-
-queue_init :: proc{queue_init_none, queue_init_len, queue_init_len_cap}
-
-queue_delete :: proc(q: $Q/Queue($T)) {
- array_delete(q.data)
-}
-
-queue_clear :: proc(q: ^$Q/Queue($T)) {
- q.len = 0
-}
-
-queue_len :: proc(q: $Q/Queue($T)) -> int {
- return q.len
-}
-
-queue_cap :: proc(q: $Q/Queue($T)) -> int {
- return array_cap(q.data)
-}
-
-queue_space :: proc(q: $Q/Queue($T)) -> int {
- return array_len(q.data) - q.len
-}
-
-queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
- i := (index + q.offset) % array_len(q.data)
- data := array_slice(q.data)
- return data[i]
-}
-
-queue_set :: proc(q: ^$Q/Queue($T), index: int, item: T) {
- i := (index + q.offset) % array_len(q.data)
- data := array_slice(q.data)
- data[i] = item
-}
-
-
-queue_reserve :: proc(q: ^$Q/Queue($T), capacity: int) {
- if capacity > q.len {
- _queue_increase_capacity(q, capacity)
- }
-}
-
-queue_resize :: proc(q: ^$Q/Queue($T), length: int) {
- if length > q.len {
- _queue_increase_capacity(q, length)
- }
- q.len = length
-}
-
-queue_push_back :: proc(q: ^$Q/Queue($T), item: T) {
- if queue_space(q^) == 0 {
- _queue_grow(q)
- }
-
- queue_set(q, q.len, item)
- q.len += 1
-}
-
-queue_push_front :: proc(q: ^$Q/Queue($T), item: T) {
- if queue_space(q^) == 0 {
- _queue_grow(q)
- }
-
- q.offset = (q.offset - 1 + array_len(q.data)) % array_len(q.data)
- q.len += 1
- queue_set(q, 0, item)
-}
-
-queue_pop_front :: proc(q: ^$Q/Queue($T)) -> T {
- assert(q.len > 0)
- item := queue_get(q^, 0)
- q.offset = (q.offset + 1) % array_len(q.data)
- q.len -= 1
- if q.len == 0 {
- q.offset = 0
- }
- return item
-}
-
-queue_pop_back :: proc(q: ^$Q/Queue($T)) -> T {
- assert(q.len > 0)
- item := queue_get(q^, q.len-1)
- q.len -= 1
- return item
-}
-
-queue_consume :: proc(q: ^$Q/Queue($T), count: int) {
- q.offset = (q.offset + count) & array_len(q.data)
- q.len -= count
-}
-
-
-queue_push_elems :: proc(q: ^$Q/Queue($T), items: ..T) {
- if queue_space(q^) < len(items) {
- _queue_grow(q, q.len + len(items))
- }
- size := array_len(q.data)
- insert := (q.offset + q.len) % size
-
- to_insert := len(items)
- if insert + to_insert > size {
- to_insert = size - insert
- }
-
- the_items := items[:]
-
- data := array_slice(q.data)
-
- q.len += copy(data[insert:][:to_insert], the_items)
- the_items = the_items[to_insert:]
- q.len += copy(data[:], the_items)
-}
-
-queue_push :: proc{queue_push_back, queue_push_elems}
-
-
-
-_queue_increase_capacity :: proc(q: ^$Q/Queue($T), new_capacity: int) {
- end := array_len(q.data)
- array_resize(&q.data, new_capacity)
- if q.offset + q.len > end {
- end_items := q.len + end
- data := array_slice(q.data)
- copy(data[new_capacity-end_items:][:end_items], data[q.offset:][:end_items])
- q.offset += new_capacity - end
- }
-}
-_queue_grow :: proc(q: ^$Q/Queue($T), min_capacity: int = 0) {
- new_capacity := max(array_len(q.data)*2 + 8, min_capacity)
- _queue_increase_capacity(q, new_capacity)
-}
diff --git a/core/container/queue/queue.odin b/core/container/queue/queue.odin
new file mode 100644
index 000000000..8ca3a85ac
--- /dev/null
+++ b/core/container/queue/queue.odin
@@ -0,0 +1,209 @@
+package container_queue
+
+import "core:builtin"
+import "core:runtime"
+_ :: runtime
+
+// Dynamically resizable double-ended queue/ring-buffer
+Queue :: struct($T: typeid) {
+ data: [dynamic]T,
+ len: uint,
+ offset: uint,
+}
+
+DEFAULT_CAPACITY :: 16
+
+// Procedure to initialize a queue
+init :: proc(q: ^$Q/Queue($T), capacity := DEFAULT_CAPACITY, allocator := context.allocator) -> bool {
+ if q.data.allocator.procedure == nil {
+ q.data.allocator = allocator
+ }
+ clear(q)
+ return reserve(q, capacity)
+}
+
+// Procedure to initialize a queue from a fixed backing slice
+init_from_slice :: proc(q: ^$Q/Queue($T), backing: []T) -> bool {
+ clear(q)
+ q.data = transmute([dynamic]T)runtime.Raw_Dynamic_Array{
+ data = raw_data(backing),
+ len = builtin.len(backing),
+ cap = builtin.len(backing),
+ allocator = {procedure=runtime.nil_allocator_proc, data=nil},
+ }
+ return true
+}
+
+// Procedure to destroy a queue
+destroy :: proc(q: ^$Q/Queue($T)) {
+ delete(q.data)
+}
+
+// The length of the queue
+len :: proc(q: $Q/Queue($T)) -> int {
+ return int(q.len)
+}
+
+// The current capacity of the queue
+cap :: proc(q: $Q/Queue($T)) -> int {
+ return builtin.len(q.data)
+}
+
+// Remaining space in the queue (cap-len)
+space :: proc(q: $Q/Queue($T)) -> int {
+ return builtin.len(q.data) - int(q.len)
+}
+
+// Reserve enough space for at least the specified capacity
+reserve :: proc(q: ^$Q/Queue($T), capacity: int) -> bool {
+ if uint(capacity) > q.len {
+ return _grow(q, uint(capacity))
+ }
+ return true
+}
+
+
+get :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> T {
+ runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+ idx := (uint(i)+q.offset)%builtin.len(q.data)
+ return q.data[idx]
+}
+set :: proc(q: ^$Q/Queue($T), #any_int i: int, val: T, loc := #caller_location) {
+ runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+ idx := (uint(i)+q.offset)%builtin.len(q.data)
+ q.data[idx] = val
+}
+get_ptr :: proc(q: ^$Q/Queue($T), #any_int i: int, loc := #caller_location) -> ^T {
+ runtime.bounds_check_error_loc(loc, i, builtin.len(q.data))
+
+ idx := (uint(i)+q.offset)%builtin.len(q.data)
+ return &q.data[idx]
+}
+
+// Push an element to the back of the queue
+push_back :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+ if space(q^) == 0 {
+ _grow(q) or_return
+ }
+ idx := (q.offset+uint(q.len))%builtin.len(q.data)
+ q.data[idx] = elem
+ q.len += 1
+ return true
+}
+
+// Push an element to the front of the queue
+push_front :: proc(q: ^$Q/Queue($T), elem: T) -> bool {
+ 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
+}
+
+
+// Pop an element from the back of the queue
+pop_back :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
+ assert(condition=q.len > 0, loc=loc)
+ q.len -= 1
+ idx := (q.offset+uint(q.len))%builtin.len(q.data)
+ elem = q.data[idx]
+ return
+}
+// Safely pop an element from the back of the queue
+pop_back_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
+ if q.len > 0 {
+ q.len -= 1
+ idx := (q.offset+uint(q.len))%builtin.len(q.data)
+ elem = q.data[idx]
+ ok = true
+ }
+ return
+}
+
+// Pop an element from the front of the queue
+pop_front :: proc(q: ^$Q/Queue($T), loc := #caller_location) -> (elem: T) {
+ assert(condition=q.len > 0, loc=loc)
+ elem = q.data[q.offset]
+ q.offset = (q.offset+1)%builtin.len(q.data)
+ q.len -= 1
+ return
+}
+// Safely pop an element from the front of the queue
+pop_front_safe :: proc(q: ^$Q/Queue($T)) -> (elem: T, ok: bool) {
+ if q.len > 0 {
+ elem = q.data[q.offset]
+ q.offset = (q.offset+1)%builtin.len(q.data)
+ q.len -= 1
+ ok = true
+ }
+ return
+}
+
+// Push multiple elements to the front of the queue
+push_back_elems :: proc(q: ^$Q/Queue($T), elems: ..T) -> bool {
+ n := uint(builtin.len(elems))
+ if space(q^) < int(n) {
+ _grow(q, q.len + n) or_return
+ }
+
+ sz := uint(builtin.len(q.data))
+ insert_from := (q.offset + q.len) % sz
+ insert_to := n
+ if insert_from + insert_to > sz {
+ insert_to = sz - insert_from
+ }
+ copy(q.data[insert_from:], elems[:insert_to])
+ copy(q.data[:insert_from], elems[insert_to:])
+ q.len += n
+ return true
+}
+
+// Consume `n` elements from the front of the queue
+consume_front :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
+ assert(condition=int(q.len) >= n, loc=loc)
+ if n > 0 {
+ nu := uint(n)
+ q.offset = (q.offset + nu) % builtin.len(q.data)
+ q.len -= nu
+ }
+}
+
+// Consume `n` elements from the back of the queue
+consume_back :: proc(q: ^$Q/Queue($T), n: int, loc := #caller_location) {
+ assert(condition=int(q.len) >= n, loc=loc)
+ if n > 0 {
+ q.len -= uint(n)
+ }
+}
+
+
+
+append_elem :: push_back
+append_elems :: push_back_elems
+push :: proc{push_back, push_back_elems}
+append :: proc{push_back, push_back_elems}
+
+
+// Clear the contents of the queue
+clear :: proc(q: ^$Q/Queue($T)) {
+ q.len = 0
+ q.offset = 0
+}
+
+
+// Internal growinh procedure
+_grow :: proc(q: ^$Q/Queue($T), min_capacity: uint = 0) -> bool {
+ 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
+ if q.offset + q.len > n {
+ diff := n - q.offset
+ copy(q.data[new_capacity-diff:], q.data[q.offset:][:diff])
+ q.offset += new_capacity - n
+ }
+ return true
+}
diff --git a/core/container/ring.odin b/core/container/ring.odin
deleted file mode 100644
index 61492ec84..000000000
--- a/core/container/ring.odin
+++ /dev/null
@@ -1,74 +0,0 @@
-package container
-
-
-Ring :: struct($T: typeid) {
- next, prev: ^Ring(T),
- value: T,
-}
-
-ring_init :: proc(r: ^$R/Ring) -> ^R {
- r.prev, r.next = r, r
- return r
-}
-
-ring_next :: proc(r: ^$R/Ring) -> ^R {
- if r.next == nil {
- return ring_init(r)
- }
- return r.next
-}
-ring_prev :: proc(r: ^$R/Ring) -> ^R {
- if r.prev == nil {
- return ring_init(r)
- }
- return r.prev
-}
-
-
-ring_move :: proc(r: ^$R/Ring, n: int) -> ^R {
- r := r
- if r.next == nil {
- return ring_init(r)
- }
-
- switch {
- case n < 0:
- for _ in n..<0 {
- r = r.prev
- }
- case n > 0:
- for _ in 0.. ^R {
- n := ring_next(r)
- if s != nil {
- p := ring_prev(s)
- r.next = s
- s.prev = r
- n.prev = p
- p.next = n
- }
- return n
-}
-ring_unlink :: proc(r: ^$R/Ring, n: int) -> ^R {
- if n <= 0 {
- return nil
- }
- return ring_link(r, ring_move(r, n+1))
-}
-ring_len :: proc(r: ^$R/Ring) -> int {
- n := 0
- if r != nil {
- n = 1
- for p := ring_next(r); p != r; p = p.next {
- n += 1
- }
- }
- return n
-}
-
diff --git a/core/container/set.odin b/core/container/set.odin
deleted file mode 100644
index 562ac5409..000000000
--- a/core/container/set.odin
+++ /dev/null
@@ -1,240 +0,0 @@
-package container
-
-Set :: struct {
- hash: Array(int),
- entries: Array(Set_Entry),
-}
-
-Set_Entry :: struct {
- key: u64,
- next: int,
-}
-
-
-/*
-set_init :: proc{
- set_init_none,
- set_init_cap,
-}
-set_delete
-
-set_in
-set_not_in
-set_add
-set_remove
-set_reserve
-set_clear
-*/
-
-set_init :: proc{set_init_none, set_init_cap}
-
-set_init_none :: proc(m: ^Set, allocator := context.allocator) {
- m.hash.allocator = allocator
- m.entries.allocator = allocator
-}
-
-set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
- m.hash.allocator = allocator
- m.entries.allocator = allocator
- set_reserve(m, cap)
-}
-
-set_delete :: proc(m: Set) {
- array_delete(m.hash)
- array_delete(m.entries)
-}
-
-
-set_in :: proc(m: Set, key: u64) -> bool {
- return _set_find_or_fail(m, key) >= 0
-}
-set_not_in :: proc(m: Set, key: u64) -> bool {
- return _set_find_or_fail(m, key) < 0
-}
-
-set_add :: proc(m: ^Set, key: u64) {
- if array_len(m.hash) == 0 {
- _set_grow(m)
- }
-
- _ = _set_find_or_make(m, key)
- if _set_full(m^) {
- _set_grow(m)
- }
-}
-
-set_remove :: proc(m: ^Set, key: u64) {
- fr := _set_find_key(m^, key)
- if fr.entry_index >= 0 {
- _set_erase(m, fr)
- }
-}
-
-
-set_reserve :: proc(m: ^Set, new_size: int) {
- nm: Set
- set_init(&nm, m.hash.allocator)
- array_resize(&nm.hash, new_size)
- array_reserve(&nm.entries, array_len(m.entries))
-
- for i in 0.. bool {
- a_entries := array_slice(a.entries)
- b_entries := array_slice(b.entries)
- if len(a_entries) != len(b_entries) {
- return false
- }
- for e in a_entries {
- if set_not_in(b, e.key) {
- return false
- }
- }
-
- return true
-}
-
-
-
-/// Internal
-
-_set_add_entry :: proc(m: ^Set, key: u64) -> int {
- e: Set_Entry
- e.key = key
- e.next = -1
- idx := array_len(m.entries)
- array_push(&m.entries, e)
- return idx
-}
-
-_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
- if fr.entry_prev < 0 {
- array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next)
- } else {
- array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next
- }
-
- if fr.entry_index == array_len(m.entries)-1 {
- array_pop_back(&m.entries)
- return
- }
-
- array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1))
- last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key)
-
- if last.entry_prev < 0 {
- array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index
- } else {
- array_set(&m.hash, last.hash_index, fr.entry_index)
- }
-}
-
-
-_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
- fr: Map_Find_Result
- fr.hash_index = -1
- fr.entry_prev = -1
- fr.entry_index = -1
-
- if array_len(m.hash) == 0 {
- return fr
- }
-
- fr.hash_index = int(key % u64(array_len(m.hash)))
- fr.entry_index = array_get(m.hash, fr.hash_index)
- for fr.entry_index >= 0 {
- it := array_get_ptr(m.entries, fr.entry_index)
- if it.key == key {
- return fr
- }
- fr.entry_prev = fr.entry_index
- fr.entry_index = it.next
- }
- return fr
-}
-
-_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
- fr: Map_Find_Result
- fr.hash_index = -1
- fr.entry_prev = -1
- fr.entry_index = -1
-
- if array_len(m.hash) == 0 {
- return fr
- }
-
- fr.hash_index = int(e.key % u64(array_len(m.hash)))
- fr.entry_index = array_get(m.hash, fr.hash_index)
- for fr.entry_index >= 0 {
- it := array_get_ptr(m.entries, fr.entry_index)
- if it == e {
- return fr
- }
- fr.entry_prev = fr.entry_index
- fr.entry_index = it.next
- }
- return fr
-}
-
-_set_find_or_fail :: proc(m: Set, key: u64) -> int {
- return _set_find_key(m, key).entry_index
-}
-_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
- fr := _set_find_key(m^, key)
- if fr.entry_index >= 0 {
- return fr.entry_index
- }
-
- i := _set_add_entry(m, key)
- if fr.entry_prev < 0 {
- array_set(&m.hash, fr.hash_index, i)
- } else {
- array_get_ptr(m.entries, fr.entry_prev).next = i
- }
- return i
-}
-
-
-_set_make :: proc(m: ^Set, key: u64) -> int {
- fr := _set_find_key(m^, key)
- i := _set_add_entry(m, key)
-
- if fr.entry_prev < 0 {
- array_set(&m.hash, fr.hash_index, i)
- } else {
- array_get_ptr(m.entries, fr.entry_prev).next = i
- }
-
- array_get_ptr(m.entries, i).next = fr.entry_index
-
- return i
-}
-
-
-_set_full :: proc(m: Set) -> bool {
- // TODO(bill): Determine good max load factor
- return array_len(m.entries) >= (array_len(m.hash) / 4)*3
-}
-
-_set_grow :: proc(m: ^Set) {
- new_size := array_len(m.entries) * 4 + 7 // TODO(bill): Determine good grow rate
- set_reserve(m, new_size)
-}
-
-
diff --git a/core/container/small_array.odin b/core/container/small_array.odin
deleted file mode 100644
index 43b879d2d..000000000
--- a/core/container/small_array.odin
+++ /dev/null
@@ -1,95 +0,0 @@
-package container
-
-Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
- data: [N]T,
- len: int,
-}
-
-
-small_array_len :: proc(a: $A/Small_Array) -> int {
- return a.len
-}
-
-small_array_cap :: proc(a: $A/Small_Array) -> int {
- return len(a.data)
-}
-
-small_array_space :: proc(a: $A/Small_Array) -> int {
- return len(a.data) - a.len
-}
-
-small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
- return a.data[:a.len]
-}
-
-
-small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
- return a.data[index]
-}
-small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
- return &a.data[index]
-}
-
-small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
- a.data[index] = item
-}
-
-small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
- a.len = min(length, len(a.data))
-}
-
-
-small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
- if a.len < len(a.data) {
- a.len += 1
- a.data[a.len-1] = item
- return true
- }
- return false
-}
-
-small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
- if a.len < len(a.data) {
- a.len += 1
- data := small_array_slice(a)
- copy(data[1:], data[:])
- data[0] = item
- return true
- }
- return false
-}
-
-small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
- assert(condition=a.len > 0, loc=loc)
- item := a.data[a.len-1]
- a.len -= 1
- return item
-}
-
-small_array_pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
- assert(condition=a.len > 0, loc=loc)
- item := a.data[0]
- s := small_array_slice(a)
- copy(s[:], s[1:])
- a.len -= 1
- return item
-}
-
-
-small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
- assert(condition=a.len >= count, loc=loc)
- a.len -= count
-}
-
-small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
- small_array_resize(a, 0)
-}
-
-small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
- n := copy(a.data[a.len:], items[:])
- a.len += n
-}
-
-small_array_push :: proc{small_array_push_back, small_array_push_back_elems}
-small_array_append :: proc{small_array_push_back, small_array_push_back_elems}
-
diff --git a/core/container/small_array/small_array.odin b/core/container/small_array/small_array.odin
new file mode 100644
index 000000000..5cd421c84
--- /dev/null
+++ b/core/container/small_array/small_array.odin
@@ -0,0 +1,117 @@
+package container_small_array
+
+import "core:builtin"
+
+Small_Array :: struct($N: int, $T: typeid) where N >= 0 {
+ data: [N]T,
+ len: int,
+}
+
+
+len :: proc(a: $A/Small_Array) -> int {
+ return a.len
+}
+
+cap :: proc(a: $A/Small_Array) -> int {
+ return builtin.len(a.data)
+}
+
+space :: proc(a: $A/Small_Array) -> int {
+ return builtin.len(a.data) - a.len
+}
+
+slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
+ return a.data[:a.len]
+}
+
+
+get :: proc(a: $A/Small_Array($N, $T), index: int) -> T {
+ return a.data[index]
+}
+get_ptr :: proc(a: ^$A/Small_Array($N, $T), index: int) -> ^T {
+ return &a.data[index]
+}
+
+set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T) {
+ a.data[index] = item
+}
+
+resize :: proc(a: ^$A/Small_Array, length: int) {
+ a.len = min(length, builtin.len(a.data))
+}
+
+
+push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
+ if a.len < cap(a^) {
+ a.data[a.len] = item
+ a.len += 1
+ return true
+ }
+ return false
+}
+
+push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
+ if a.len < cap(a^) {
+ a.len += 1
+ data := slice(a)
+ copy(data[1:], data[:])
+ data[0] = item
+ return true
+ }
+ return false
+}
+
+pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
+ assert(condition=(N > 0 && a.len > 0), loc=loc)
+ item := a.data[a.len-1]
+ a.len -= 1
+ return item
+}
+
+pop_front :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
+ assert(condition=(N > 0 && a.len > 0), loc=loc)
+ item := a.data[0]
+ s := slice(a)
+ copy(s[:], s[1:])
+ a.len -= 1
+ return item
+}
+
+pop_back_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (item: T, ok: bool) {
+ if N > 0 && a.len > 0 {
+ item = a.data[a.len-1]
+ a.len -= 1
+ ok = true
+ }
+ return
+}
+
+pop_front_safe :: proc(a: ^$A/Small_Array($N, $T)) -> (T, bool) {
+ if N > 0 && a.len > 0 {
+ item = a.data[0]
+ s := slice(a)
+ copy(s[:], s[1:])
+ a.len -= 1
+ ok = true
+ }
+ return
+}
+
+consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
+ assert(condition=a.len >= count, loc=loc)
+ a.len -= count
+}
+
+clear :: proc(a: ^$A/Small_Array($N, $T)) {
+ resize(a, 0)
+}
+
+push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
+ n := copy(a.data[a.len:], items[:])
+ a.len += n
+}
+
+append_elem :: push_back
+append_elems :: push_back_elems
+push :: proc{push_back, push_back_elems}
+append :: proc{push_back, push_back_elems}
\ No newline at end of file
diff --git a/core/container/topological_sort/topological_sort.odin b/core/container/topological_sort/topological_sort.odin
new file mode 100644
index 000000000..4b69930d5
--- /dev/null
+++ b/core/container/topological_sort/topological_sort.odin
@@ -0,0 +1,98 @@
+// The following is a generic O(V+E) topological sorter implementation.
+// This is the fastest known method for topological sorting and Odin's
+// map type is being used to accelerate lookups.
+package container_topological_sort
+
+import "core:intrinsics"
+import "core:runtime"
+_ :: intrinsics
+_ :: runtime
+
+
+Relations :: struct($K: typeid) where intrinsics.type_is_valid_map_key(K) {
+ dependents: map[K]bool,
+ dependencies: int,
+}
+
+Sorter :: struct(K: typeid) where intrinsics.type_is_valid_map_key(K) {
+ relations: map[K]Relations(K),
+ dependents_allocator: runtime.Allocator,
+}
+
+@(private="file")
+make_relations :: proc(sorter: ^$S/Sorter($K)) -> (r: Relations(K)) {
+ r.dependents.allocator = sorter.dependents_allocator
+ return
+}
+
+
+init :: proc(sorter: ^$S/Sorter($K)) {
+ sorter.relations = make(map[K]Relations(K))
+ sorter.dependents_allocator = context.allocator
+}
+
+destroy :: proc(sorter: ^$S/Sorter($K)) {
+ for _, v in &sorter.relations {
+ delete(v.dependents)
+ }
+ delete(sorter.relations)
+}
+
+add_key :: proc(sorter: ^$S/Sorter($K), key: K) -> bool {
+ if key in sorter.relations {
+ return false
+ }
+ sorter.relations[key] = make_relations(sorter)
+ return true
+}
+
+add_dependency :: proc(sorter: ^$S/Sorter($K), key, dependency: K) -> bool {
+ if key == dependency {
+ return false
+ }
+
+ find := &sorter.relations[dependency]
+ if find == nil {
+ find = map_insert(&sorter.relations, dependency, make_relations(sorter))
+ }
+
+ if find.dependents[key] {
+ return true
+ }
+ find.dependents[key] = true
+
+ find = &sorter.relations[key]
+ if find == nil {
+ find = map_insert(&sorter.relations, key, make_relations(sorter))
+ }
+
+ find.dependencies += 1
+
+ return true
+}
+
+sort :: proc(sorter: ^$S/Sorter($K)) -> (sorted, cycled: [dynamic]K) {
+ relations := &sorter.relations
+
+ for k, v in relations {
+ if v.dependencies == 0 {
+ append(&sorted, k)
+ }
+ }
+
+ for root in &sorted do for k, _ in relations[root].dependents {
+ relation := &relations[k]
+ relation.dependencies -= 1
+ if relation.dependencies == 0 {
+ append(&sorted, k)
+ }
+ }
+
+ for k, v in relations {
+ if v.dependencies != 0 {
+ append(&cycled, k)
+ }
+ }
+
+ return
+}
\ No newline at end of file
diff --git a/core/crypto/README.md b/core/crypto/README.md
index 5955f9c56..ddcb12d81 100644
--- a/core/crypto/README.md
+++ b/core/crypto/README.md
@@ -32,9 +32,11 @@ Please see the chart below for the options.
#### High level API
Each hash algorithm contains a procedure group named `hash`, or if the algorithm provides more than one digest size `hash_`\*.
-Included in these groups are four procedures.
+Included in these groups are six procedures.
* `hash_string` - Hash a given string and return the computed hash. Just calls `hash_bytes` internally
* `hash_bytes` - Hash a given byte slice and return the computed hash
+* `hash_string_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. Just calls `hash_bytes_to_buffer` internally
+* `hash_bytes_to_buffer` - Hash a given string and put the computed hash in the second proc parameter. The destination buffer has to be at least as big as the digest size of the hash
* `hash_stream` - Takes a stream from io.Stream and returns the computed hash from it
* `hash_file` - Takes a file handle and returns the computed hash from it. A second optional boolean parameter controls if the file is streamed (this is the default) or read at once (set to true)
@@ -59,6 +61,10 @@ main :: proc() {
// Compute the hash, using the high level API
computed_hash := md4.hash(input)
+ // Variant that takes a destination buffer, instead of returning the computed hash
+ hash := make([]byte, md4.DIGEST_SIZE) // @note: Destination buffer has to be at least as big as the digest size of the hash
+ md4.hash(input, hash[:])
+
// Compute the hash, using the low level API
ctx: md4.Md4_Context
computed_hash_low: [16]byte
diff --git a/core/crypto/_fiat/field_poly1305/field.odin b/core/crypto/_fiat/field_poly1305/field.odin
index bfb7cf1f9..ca458e079 100644
--- a/core/crypto/_fiat/field_poly1305/field.odin
+++ b/core/crypto/_fiat/field_poly1305/field.odin
@@ -22,7 +22,7 @@ fe_from_bytes :: #force_inline proc (out1: ^Tight_Field_Element, arg1: []byte, a
assert(len(arg1) == 16)
- when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
+ when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
// While it may be unwise to do deserialization here on our
// own when fiat-crypto provides equivalent functionality,
// doing it this way provides a little under 3x performance
diff --git a/core/crypto/_sha3/_sha3.odin b/core/crypto/_sha3/_sha3.odin
index 76e09bf24..9846aca42 100644
--- a/core/crypto/_sha3/_sha3.odin
+++ b/core/crypto/_sha3/_sha3.odin
@@ -52,7 +52,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
t: u64 = ---
bc: [5]u64 = ---
- when ODIN_ENDIAN != "little" {
+ when ODIN_ENDIAN != .Little {
v: uintptr = ---
for i = 0; i < 25; i += 1 {
v := uintptr(&st[i])
@@ -98,7 +98,7 @@ keccakf :: proc "contextless" (st: ^[25]u64) {
st[0] ~= keccakf_rndc[r]
}
- when ODIN_ENDIAN != "little" {
+ when ODIN_ENDIAN != .Little {
for i = 0; i < 25; i += 1 {
v = uintptr(&st[i])
t = st[i]
diff --git a/core/crypto/blake/blake.odin b/core/crypto/blake/blake.odin
index 9d53f8a89..5fc0a02b9 100644
--- a/core/crypto/blake/blake.odin
+++ b/core/crypto/blake/blake.odin
@@ -17,16 +17,21 @@ import "core:io"
High level API
*/
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
// hash_string_224 will hash the given input and return the
// computed hash
-hash_string_224 :: proc "contextless" (data: string) -> [28]byte {
+hash_string_224 :: proc "contextless" (data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
-hash_bytes_224 :: proc "contextless" (data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Blake256_Context
ctx.is224 = true
init(&ctx)
@@ -35,10 +40,29 @@ hash_bytes_224 :: proc "contextless" (data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Blake256_Context
+ ctx.is224 = true
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Blake256_Context
ctx.is224 = true
init(&ctx)
@@ -57,7 +81,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224 will read the file provided by the given handle
// and compute a hash
-hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
@@ -65,7 +89,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
return hash_bytes_224(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -73,18 +97,20 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
+ hash_bytes_to_buffer_224,
+ hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc "contextless" (data: string) -> [32]byte {
+hash_string_256 :: proc "contextless" (data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc "contextless" (data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Blake256_Context
ctx.is224 = false
init(&ctx)
@@ -93,10 +119,29 @@ hash_bytes_256 :: proc "contextless" (data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Blake256_Context
+ ctx.is224 = false
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Blake256_Context
ctx.is224 = false
init(&ctx)
@@ -115,7 +160,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -123,7 +168,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -131,18 +176,20 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
-hash_string_384 :: proc "contextless" (data: string) -> [48]byte {
+hash_string_384 :: proc "contextless" (data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
-hash_bytes_384 :: proc "contextless" (data: []byte) -> [48]byte {
- hash: [48]byte
+hash_bytes_384 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_384]byte {
+ hash: [DIGEST_SIZE_384]byte
ctx: Blake512_Context
ctx.is384 = true
init(&ctx)
@@ -151,10 +198,29 @@ hash_bytes_384 :: proc "contextless" (data: []byte) -> [48]byte {
return hash
}
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+ ctx: Blake512_Context
+ ctx.is384 = true
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
- hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+ hash: [DIGEST_SIZE_384]byte
ctx: Blake512_Context
ctx.is384 = true
init(&ctx)
@@ -173,7 +239,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
// hash_file_384 will read the file provided by the given handle
// and compute a hash
-hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
@@ -181,7 +247,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
return hash_bytes_384(buf[:]), ok
}
}
- return [48]byte{}, false
+ return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -189,18 +255,20 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
+ hash_bytes_to_buffer_384,
+ hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc "contextless" (data: string) -> [64]byte {
+hash_string_512 :: proc "contextless" (data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc "contextless" (data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc "contextless" (data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: Blake512_Context
ctx.is384 = false
init(&ctx)
@@ -209,10 +277,29 @@ hash_bytes_512 :: proc "contextless" (data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: Blake512_Context
+ ctx.is384 = false
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: Blake512_Context
ctx.is384 = false
init(&ctx)
@@ -231,7 +318,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -239,7 +326,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -247,6 +334,8 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
diff --git a/core/crypto/blake2b/blake2b.odin b/core/crypto/blake2b/blake2b.odin
index 85f9611f9..e75d74197 100644
--- a/core/crypto/blake2b/blake2b.odin
+++ b/core/crypto/blake2b/blake2b.odin
@@ -20,16 +20,18 @@ import "../_blake2"
High level API
*/
+DIGEST_SIZE :: 64
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [64]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
@@ -40,10 +42,32 @@ hash_bytes :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: _blake2.Blake2b_Context
+ cfg: _blake2.Blake2_Config
+ cfg.size = _blake2.BLAKE2B_SIZE
+ ctx.cfg = cfg
+ _blake2.init(&ctx)
+ _blake2.update(&ctx, data)
+ _blake2.final(&ctx, hash)
+}
+
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2b_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2B_SIZE
@@ -64,7 +88,7 @@ hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -72,7 +96,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -80,6 +104,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
diff --git a/core/crypto/blake2s/blake2s.odin b/core/crypto/blake2s/blake2s.odin
index 72d15b227..831335081 100644
--- a/core/crypto/blake2s/blake2s.odin
+++ b/core/crypto/blake2s/blake2s.odin
@@ -20,16 +20,18 @@ import "../_blake2"
High level API
*/
+DIGEST_SIZE :: 32
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [32]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
@@ -40,10 +42,32 @@ hash_bytes :: proc(data: []byte) -> [32]byte {
return hash
}
+
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: _blake2.Blake2s_Context
+ cfg: _blake2.Blake2_Config
+ cfg.size = _blake2.BLAKE2S_SIZE
+ ctx.cfg = cfg
+ _blake2.init(&ctx)
+ _blake2.update(&ctx, data)
+ _blake2.final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: _blake2.Blake2s_Context
cfg: _blake2.Blake2_Config
cfg.size = _blake2.BLAKE2S_SIZE
@@ -64,7 +88,7 @@ hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -72,7 +96,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -80,6 +104,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
diff --git a/core/crypto/chacha20/chacha20.odin b/core/crypto/chacha20/chacha20.odin
index f6f551692..229949c22 100644
--- a/core/crypto/chacha20/chacha20.odin
+++ b/core/crypto/chacha20/chacha20.odin
@@ -346,7 +346,7 @@ _do_blocks :: proc (ctx: ^Context, dst, src: []byte, nr_blocks: int) {
// Until dedicated assembly can be written leverage the fact that
// the callers of this routine ensure that src/dst are valid.
- when ODIN_ARCH == "386" || ODIN_ARCH == "amd64" {
+ when ODIN_ARCH == .i386 || ODIN_ARCH == .amd64 {
// util.PUT_U32_LE/util.U32_LE are not required on little-endian
// systems that also happen to not be strict about aligned
// memory access.
diff --git a/core/crypto/gost/gost.odin b/core/crypto/gost/gost.odin
index c687e9080..1d0274fae 100644
--- a/core/crypto/gost/gost.odin
+++ b/core/crypto/gost/gost.odin
@@ -18,16 +18,18 @@ import "core:io"
High level API
*/
+DIGEST_SIZE :: 32
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [32]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Gost_Context
init(&ctx)
update(&ctx, data)
@@ -35,10 +37,28 @@ hash_bytes :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Gost_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Gost_Context
init(&ctx)
buf := make([]byte, 512)
@@ -56,7 +76,7 @@ hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -64,7 +84,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -72,6 +92,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
diff --git a/core/crypto/groestl/groestl.odin b/core/crypto/groestl/groestl.odin
index 0d305a1d1..8e5a2440d 100644
--- a/core/crypto/groestl/groestl.odin
+++ b/core/crypto/groestl/groestl.odin
@@ -17,16 +17,21 @@ import "core:io"
High level API
*/
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
// hash_string_224 will hash the given input and return the
// computed hash
-hash_string_224 :: proc(data: string) -> [28]byte {
+hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
-hash_bytes_224 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
@@ -35,10 +40,29 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Groestl_Context
+ ctx.hashbitlen = 224
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Groestl_Context
ctx.hashbitlen = 224
init(&ctx)
@@ -57,7 +81,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224 will read the file provided by the given handle
// and compute a hash
-hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
@@ -65,7 +89,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
return hash_bytes_224(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -73,18 +97,20 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
+ hash_bytes_to_buffer_224,
+ hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
@@ -93,10 +119,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Groestl_Context
+ ctx.hashbitlen = 256
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Groestl_Context
ctx.hashbitlen = 256
init(&ctx)
@@ -115,7 +160,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -123,7 +168,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -131,18 +176,20 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
-hash_string_384 :: proc(data: string) -> [48]byte {
+hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
-hash_bytes_384 :: proc(data: []byte) -> [48]byte {
- hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+ hash: [DIGEST_SIZE_384]byte
ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
@@ -151,10 +198,29 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
return hash
}
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+ ctx: Groestl_Context
+ ctx.hashbitlen = 384
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
- hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+ hash: [DIGEST_SIZE_384]byte
ctx: Groestl_Context
ctx.hashbitlen = 384
init(&ctx)
@@ -173,7 +239,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
// hash_file_384 will read the file provided by the given handle
// and compute a hash
-hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
@@ -181,7 +247,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
return hash_bytes_384(buf[:]), ok
}
}
- return [48]byte{}, false
+ return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -189,18 +255,20 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
+ hash_bytes_to_buffer_384,
+ hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
@@ -209,10 +277,29 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: Groestl_Context
+ ctx.hashbitlen = 512
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: Groestl_Context
ctx.hashbitlen = 512
init(&ctx)
@@ -231,7 +318,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -239,7 +326,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -247,6 +334,8 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
diff --git a/core/crypto/haval/haval.odin b/core/crypto/haval/haval.odin
index 76532d4cd..811ecf95d 100644
--- a/core/crypto/haval/haval.odin
+++ b/core/crypto/haval/haval.odin
@@ -20,16 +20,22 @@ import "../util"
High level API
*/
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_192 :: 24
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+
// hash_string_128_3 will hash the given input and return the
// computed hash
-hash_string_128_3 :: proc(data: string) -> [16]byte {
+hash_string_128_3 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128_3(transmute([]byte)(data))
}
// hash_bytes_128_3 will hash the given input and return the
// computed hash
-hash_bytes_128_3 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128_3 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: Haval_Context
ctx.hashbitlen = 128
ctx.rounds = 3
@@ -40,10 +46,31 @@ hash_bytes_128_3 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128_3 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128_3 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128_3(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128_3 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128_3 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 128
+ ctx.rounds = 3
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_128_3 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128_3 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: Haval_Context
ctx.hashbitlen = 128
ctx.rounds = 3
@@ -64,7 +91,7 @@ hash_stream_128_3 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128_3 will read the file provided by the given handle
// and compute a hash
-hash_file_128_3 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128_3 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128_3(os.stream_from_handle(hd))
} else {
@@ -72,7 +99,7 @@ hash_file_128_3 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool
return hash_bytes_128_3(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128_3 :: proc {
@@ -80,18 +107,20 @@ hash_128_3 :: proc {
hash_file_128_3,
hash_bytes_128_3,
hash_string_128_3,
+ hash_bytes_to_buffer_128_3,
+ hash_string_to_buffer_128_3,
}
// hash_string_128_4 will hash the given input and return the
// computed hash
-hash_string_128_4 :: proc(data: string) -> [16]byte {
+hash_string_128_4 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128_4(transmute([]byte)(data))
}
// hash_bytes_128_4 will hash the given input and return the
// computed hash
-hash_bytes_128_4 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128_4 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: Haval_Context
ctx.hashbitlen = 128
ctx.rounds = 4
@@ -102,10 +131,31 @@ hash_bytes_128_4 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128_4 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128_4 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128_4(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128_4 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128_4 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 128
+ ctx.rounds = 4
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_128_4 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128_4 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: Haval_Context
ctx.hashbitlen = 128
ctx.rounds = 4
@@ -126,7 +176,7 @@ hash_stream_128_4 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128_4 will read the file provided by the given handle
// and compute a hash
-hash_file_128_4 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128_4 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128_4(os.stream_from_handle(hd))
} else {
@@ -134,7 +184,7 @@ hash_file_128_4 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool
return hash_bytes_128_4(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128_4 :: proc {
@@ -142,18 +192,20 @@ hash_128_4 :: proc {
hash_file_128_4,
hash_bytes_128_4,
hash_string_128_4,
+ hash_bytes_to_buffer_128_4,
+ hash_string_to_buffer_128_4,
}
// hash_string_128_5 will hash the given input and return the
// computed hash
-hash_string_128_5 :: proc(data: string) -> [16]byte {
+hash_string_128_5 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128_5(transmute([]byte)(data))
}
// hash_bytes_128_5 will hash the given input and return the
// computed hash
-hash_bytes_128_5 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128_5 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: Haval_Context
ctx.hashbitlen = 128
ctx.rounds = 5
@@ -164,10 +216,31 @@ hash_bytes_128_5 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128_5 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128_5 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128_5(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128_5 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128_5 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 128
+ ctx.rounds = 5
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_128_5 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128_5 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: Haval_Context
ctx.hashbitlen = 128
ctx.rounds = 5
@@ -188,7 +261,7 @@ hash_stream_128_5 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128_5 will read the file provided by the given handle
// and compute a hash
-hash_file_128_5 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128_5 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128_5(os.stream_from_handle(hd))
} else {
@@ -196,7 +269,7 @@ hash_file_128_5 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool
return hash_bytes_128_5(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128_5 :: proc {
@@ -204,18 +277,20 @@ hash_128_5 :: proc {
hash_file_128_5,
hash_bytes_128_5,
hash_string_128_5,
+ hash_bytes_to_buffer_128_5,
+ hash_string_to_buffer_128_5,
}
// hash_string_160_3 will hash the given input and return the
// computed hash
-hash_string_160_3 :: proc(data: string) -> [20]byte {
+hash_string_160_3 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160_3(transmute([]byte)(data))
}
// hash_bytes_160_3 will hash the given input and return the
// computed hash
-hash_bytes_160_3 :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes_160_3 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+ hash: [DIGEST_SIZE_160]byte
ctx: Haval_Context
ctx.hashbitlen = 160
ctx.rounds = 3
@@ -226,10 +301,31 @@ hash_bytes_160_3 :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer_160_3 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160_3 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_160_3(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_160_3 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160_3 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 160
+ ctx.rounds = 3
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_160_3 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_160_3 :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream_160_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+ hash: [DIGEST_SIZE_160]byte
ctx: Haval_Context
ctx.hashbitlen = 160
ctx.rounds = 3
@@ -250,7 +346,7 @@ hash_stream_160_3 :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file_160_3 will read the file provided by the given handle
// and compute a hash
-hash_file_160_3 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file_160_3 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160_3(os.stream_from_handle(hd))
} else {
@@ -258,7 +354,7 @@ hash_file_160_3 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool
return hash_bytes_160_3(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE_160]byte{}, false
}
hash_160_3 :: proc {
@@ -266,18 +362,20 @@ hash_160_3 :: proc {
hash_file_160_3,
hash_bytes_160_3,
hash_string_160_3,
+ hash_bytes_to_buffer_160_3,
+ hash_string_to_buffer_160_3,
}
// hash_string_160_4 will hash the given input and return the
// computed hash
-hash_string_160_4 :: proc(data: string) -> [20]byte {
+hash_string_160_4 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160_4(transmute([]byte)(data))
}
// hash_bytes_160_4 will hash the given input and return the
// computed hash
-hash_bytes_160_4 :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes_160_4 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+ hash: [DIGEST_SIZE_160]byte
ctx: Haval_Context
ctx.hashbitlen = 160
ctx.rounds = 4
@@ -288,10 +386,31 @@ hash_bytes_160_4 :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer_160_4 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160_4 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_160_4(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_160_4 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160_4 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 160
+ ctx.rounds = 4
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_160_4 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_160_4 :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream_160_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+ hash: [DIGEST_SIZE_160]byte
ctx: Haval_Context
ctx.hashbitlen = 160
ctx.rounds = 4
@@ -312,7 +431,7 @@ hash_stream_160_4 :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file_160_4 will read the file provided by the given handle
// and compute a hash
-hash_file_160_4 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file_160_4 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160_4(os.stream_from_handle(hd))
} else {
@@ -320,7 +439,7 @@ hash_file_160_4 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool
return hash_bytes_160_4(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE_160]byte{}, false
}
hash_160_4 :: proc {
@@ -328,18 +447,20 @@ hash_160_4 :: proc {
hash_file_160_4,
hash_bytes_160_4,
hash_string_160_4,
+ hash_bytes_to_buffer_160_4,
+ hash_string_to_buffer_160_4,
}
// hash_string_160_5 will hash the given input and return the
// computed hash
-hash_string_160_5 :: proc(data: string) -> [20]byte {
+hash_string_160_5 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160_5(transmute([]byte)(data))
}
// hash_bytes_160_5 will hash the given input and return the
// computed hash
-hash_bytes_160_5 :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes_160_5 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+ hash: [DIGEST_SIZE_160]byte
ctx: Haval_Context
ctx.hashbitlen = 160
ctx.rounds = 5
@@ -350,10 +471,31 @@ hash_bytes_160_5 :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer_160_5 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160_5 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_160_5(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_160_5 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160_5 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 160
+ ctx.rounds = 5
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_160_5 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_160_5 :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream_160_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+ hash: [DIGEST_SIZE_160]byte
ctx: Haval_Context
ctx.hashbitlen = 160
ctx.rounds = 5
@@ -374,7 +516,7 @@ hash_stream_160_5 :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file_160_5 will read the file provided by the given handle
// and compute a hash
-hash_file_160_5 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file_160_5 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160_5(os.stream_from_handle(hd))
} else {
@@ -382,7 +524,7 @@ hash_file_160_5 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool
return hash_bytes_160_5(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE_160]byte{}, false
}
hash_160_5 :: proc {
@@ -390,18 +532,20 @@ hash_160_5 :: proc {
hash_file_160_5,
hash_bytes_160_5,
hash_string_160_5,
+ hash_bytes_to_buffer_160_5,
+ hash_string_to_buffer_160_5,
}
// hash_string_192_3 will hash the given input and return the
// computed hash
-hash_string_192_3 :: proc(data: string) -> [24]byte {
+hash_string_192_3 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192_3(transmute([]byte)(data))
}
// hash_bytes_192_3 will hash the given input and return the
// computed hash
-hash_bytes_192_3 :: proc(data: []byte) -> [24]byte {
- hash: [24]byte
+hash_bytes_192_3 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+ hash: [DIGEST_SIZE_192]byte
ctx: Haval_Context
ctx.hashbitlen = 192
ctx.rounds = 3
@@ -412,10 +556,31 @@ hash_bytes_192_3 :: proc(data: []byte) -> [24]byte {
return hash
}
+// hash_string_to_buffer_192_3 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192_3 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_192_3(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_192_3 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192_3 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 192
+ ctx.rounds = 3
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_192_3 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_192_3 :: proc(s: io.Stream) -> ([24]byte, bool) {
- hash: [24]byte
+hash_stream_192_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+ hash: [DIGEST_SIZE_192]byte
ctx: Haval_Context
ctx.hashbitlen = 192
ctx.rounds = 3
@@ -436,7 +601,7 @@ hash_stream_192_3 :: proc(s: io.Stream) -> ([24]byte, bool) {
// hash_file_192_3 will read the file provided by the given handle
// and compute a hash
-hash_file_192_3 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
+hash_file_192_3 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192_3(os.stream_from_handle(hd))
} else {
@@ -444,7 +609,7 @@ hash_file_192_3 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool
return hash_bytes_192_3(buf[:]), ok
}
}
- return [24]byte{}, false
+ return [DIGEST_SIZE_192]byte{}, false
}
hash_192_3 :: proc {
@@ -452,18 +617,20 @@ hash_192_3 :: proc {
hash_file_192_3,
hash_bytes_192_3,
hash_string_192_3,
+ hash_bytes_to_buffer_192_3,
+ hash_string_to_buffer_192_3,
}
// hash_string_192_4 will hash the given input and return the
// computed hash
-hash_string_192_4 :: proc(data: string) -> [24]byte {
+hash_string_192_4 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192_4(transmute([]byte)(data))
}
// hash_bytes_192_4 will hash the given input and return the
// computed hash
-hash_bytes_192_4 :: proc(data: []byte) -> [24]byte {
- hash: [24]byte
+hash_bytes_192_4 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+ hash: [DIGEST_SIZE_192]byte
ctx: Haval_Context
ctx.hashbitlen = 192
ctx.rounds = 4
@@ -474,10 +641,31 @@ hash_bytes_192_4 :: proc(data: []byte) -> [24]byte {
return hash
}
+// hash_string_to_buffer_192_4 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192_4 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_192_4(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_192_4 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192_4 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 192
+ ctx.rounds = 4
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_192_4 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_192_4 :: proc(s: io.Stream) -> ([24]byte, bool) {
- hash: [24]byte
+hash_stream_192_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+ hash: [DIGEST_SIZE_192]byte
ctx: Haval_Context
ctx.hashbitlen = 192
ctx.rounds = 4
@@ -498,7 +686,7 @@ hash_stream_192_4 :: proc(s: io.Stream) -> ([24]byte, bool) {
// hash_file_192_4 will read the file provided by the given handle
// and compute a hash
-hash_file_192_4 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
+hash_file_192_4 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192_4(os.stream_from_handle(hd))
} else {
@@ -506,7 +694,7 @@ hash_file_192_4 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool
return hash_bytes_192_4(buf[:]), ok
}
}
- return [24]byte{}, false
+ return [DIGEST_SIZE_192]byte{}, false
}
hash_192_4 :: proc {
@@ -514,18 +702,20 @@ hash_192_4 :: proc {
hash_file_192_4,
hash_bytes_192_4,
hash_string_192_4,
+ hash_bytes_to_buffer_192_4,
+ hash_string_to_buffer_192_4,
}
// hash_string_192_5 will hash the given input and return the
// computed hash
-hash_string_192_5 :: proc(data: string) -> [24]byte {
+hash_string_192_5 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192_5(transmute([]byte)(data))
}
-// hash_bytes_224_5 will hash the given input and return the
+// hash_bytes_2DIGEST_SIZE_192_5 will hash the given input and return the
// computed hash
-hash_bytes_192_5 :: proc(data: []byte) -> [24]byte {
- hash: [24]byte
+hash_bytes_192_5 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+ hash: [DIGEST_SIZE_192]byte
ctx: Haval_Context
ctx.hashbitlen = 192
ctx.rounds = 5
@@ -536,10 +726,31 @@ hash_bytes_192_5 :: proc(data: []byte) -> [24]byte {
return hash
}
+// hash_string_to_buffer_192_5 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192_5 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_192_5(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_192_5 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192_5 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 192
+ ctx.rounds = 5
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_192_5 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_192_5 :: proc(s: io.Stream) -> ([24]byte, bool) {
- hash: [24]byte
+hash_stream_192_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+ hash: [DIGEST_SIZE_192]byte
ctx: Haval_Context
ctx.hashbitlen = 192
ctx.rounds = 5
@@ -560,7 +771,7 @@ hash_stream_192_5 :: proc(s: io.Stream) -> ([24]byte, bool) {
// hash_file_192_5 will read the file provided by the given handle
// and compute a hash
-hash_file_192_5 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
+hash_file_192_5 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192_5(os.stream_from_handle(hd))
} else {
@@ -568,7 +779,7 @@ hash_file_192_5 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool
return hash_bytes_192_5(buf[:]), ok
}
}
- return [24]byte{}, false
+ return [DIGEST_SIZE_192]byte{}, false
}
hash_192_5 :: proc {
@@ -576,18 +787,20 @@ hash_192_5 :: proc {
hash_file_192_5,
hash_bytes_192_5,
hash_string_192_5,
+ hash_bytes_to_buffer_192_5,
+ hash_string_to_buffer_192_5,
}
// hash_string_224_3 will hash the given input and return the
// computed hash
-hash_string_224_3 :: proc(data: string) -> [28]byte {
+hash_string_224_3 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224_3(transmute([]byte)(data))
}
// hash_bytes_224_3 will hash the given input and return the
// computed hash
-hash_bytes_224_3 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224_3 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Haval_Context
ctx.hashbitlen = 224
ctx.rounds = 3
@@ -598,10 +811,31 @@ hash_bytes_224_3 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224_3 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224_3 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224_3(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224_3 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224_3 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 224
+ ctx.rounds = 3
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224_3 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224_3 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Haval_Context
ctx.hashbitlen = 224
ctx.rounds = 3
@@ -622,7 +856,7 @@ hash_stream_224_3 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224_3 will read the file provided by the given handle
// and compute a hash
-hash_file_224_3 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224_3 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224_3(os.stream_from_handle(hd))
} else {
@@ -630,7 +864,7 @@ hash_file_224_3 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool
return hash_bytes_224_3(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224_3 :: proc {
@@ -638,18 +872,20 @@ hash_224_3 :: proc {
hash_file_224_3,
hash_bytes_224_3,
hash_string_224_3,
+ hash_bytes_to_buffer_224_3,
+ hash_string_to_buffer_224_3,
}
// hash_string_224_4 will hash the given input and return the
// computed hash
-hash_string_224_4 :: proc(data: string) -> [28]byte {
+hash_string_224_4 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224_4(transmute([]byte)(data))
}
// hash_bytes_224_4 will hash the given input and return the
// computed hash
-hash_bytes_224_4 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224_4 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Haval_Context
ctx.hashbitlen = 224
ctx.rounds = 4
@@ -660,10 +896,31 @@ hash_bytes_224_4 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224_4 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224_4 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224_4(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224_4 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224_4 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 224
+ ctx.rounds = 4
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224_4 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224_4 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Haval_Context
ctx.hashbitlen = 224
ctx.rounds = 4
@@ -684,7 +941,7 @@ hash_stream_224_4 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224_4 will read the file provided by the given handle
// and compute a hash
-hash_file_224_4 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224_4 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224_4(os.stream_from_handle(hd))
} else {
@@ -692,7 +949,7 @@ hash_file_224_4 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool
return hash_bytes_224_4(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224_4 :: proc {
@@ -700,18 +957,20 @@ hash_224_4 :: proc {
hash_file_224_4,
hash_bytes_224_4,
hash_string_224_4,
+ hash_bytes_to_buffer_224_4,
+ hash_string_to_buffer_224_4,
}
// hash_string_224_5 will hash the given input and return the
// computed hash
-hash_string_224_5 :: proc(data: string) -> [28]byte {
+hash_string_224_5 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224_5(transmute([]byte)(data))
}
// hash_bytes_224_5 will hash the given input and return the
// computed hash
-hash_bytes_224_5 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224_5 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Haval_Context
ctx.hashbitlen = 224
ctx.rounds = 5
@@ -722,10 +981,31 @@ hash_bytes_224_5 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224_5 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224_5 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224_5(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224_5 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224_5 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 224
+ ctx.rounds = 5
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224_5 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224_5 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Haval_Context
ctx.hashbitlen = 224
ctx.rounds = 5
@@ -746,7 +1026,7 @@ hash_stream_224_5 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224_5 will read the file provided by the given handle
// and compute a hash
-hash_file_224_5 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224_5 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224_5(os.stream_from_handle(hd))
} else {
@@ -754,7 +1034,7 @@ hash_file_224_5 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool
return hash_bytes_224_5(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224_5 :: proc {
@@ -762,18 +1042,20 @@ hash_224_5 :: proc {
hash_file_224_5,
hash_bytes_224_5,
hash_string_224_5,
+ hash_bytes_to_buffer_224_5,
+ hash_string_to_buffer_224_5,
}
// hash_string_256_3 will hash the given input and return the
// computed hash
-hash_string_256_3 :: proc(data: string) -> [32]byte {
+hash_string_256_3 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256_3(transmute([]byte)(data))
}
// hash_bytes_256_3 will hash the given input and return the
// computed hash
-hash_bytes_256_3 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256_3 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Haval_Context
ctx.hashbitlen = 256
ctx.rounds = 3
@@ -784,10 +1066,31 @@ hash_bytes_256_3 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256_3 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256_3 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256_3(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256_3 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256_3 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 256
+ ctx.rounds = 3
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256_3 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256_3 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256_3 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Haval_Context
ctx.hashbitlen = 256
ctx.rounds = 3
@@ -808,7 +1111,7 @@ hash_stream_256_3 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256_3 will read the file provided by the given handle
// and compute a hash
-hash_file_256_3 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256_3 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256_3(os.stream_from_handle(hd))
} else {
@@ -816,7 +1119,7 @@ hash_file_256_3 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool
return hash_bytes_256_3(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256_3 :: proc {
@@ -824,18 +1127,20 @@ hash_256_3 :: proc {
hash_file_256_3,
hash_bytes_256_3,
hash_string_256_3,
+ hash_bytes_to_buffer_256_3,
+ hash_string_to_buffer_256_3,
}
// hash_string_256_4 will hash the given input and return the
// computed hash
-hash_string_256_4 :: proc(data: string) -> [32]byte {
+hash_string_256_4 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256_4(transmute([]byte)(data))
}
// hash_bytes_256_4 will hash the given input and return the
// computed hash
-hash_bytes_256_4 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256_4 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Haval_Context
ctx.hashbitlen = 256
ctx.rounds = 4
@@ -846,10 +1151,31 @@ hash_bytes_256_4 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256_4 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256_4 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256_4(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256_4 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256_4 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 256
+ ctx.rounds = 4
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256_4 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256_4 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256_4 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Haval_Context
ctx.hashbitlen = 256
ctx.rounds = 4
@@ -870,7 +1196,7 @@ hash_stream_256_4 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256_4 will read the file provided by the given handle
// and compute a hash
-hash_file_256_4 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256_4 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256_4(os.stream_from_handle(hd))
} else {
@@ -878,7 +1204,7 @@ hash_file_256_4 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool
return hash_bytes_256_4(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256_4 :: proc {
@@ -886,18 +1212,20 @@ hash_256_4 :: proc {
hash_file_256_4,
hash_bytes_256_4,
hash_string_256_4,
+ hash_bytes_to_buffer_256_4,
+ hash_string_to_buffer_256_4,
}
// hash_string_256_5 will hash the given input and return the
// computed hash
-hash_string_256_5 :: proc(data: string) -> [32]byte {
+hash_string_256_5 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256_5(transmute([]byte)(data))
}
// hash_bytes_256_5 will hash the given input and return the
// computed hash
-hash_bytes_256_5 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256_5 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Haval_Context
ctx.hashbitlen = 256
ctx.rounds = 5
@@ -908,10 +1236,32 @@ hash_bytes_256_5 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256_5 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256_5 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256_5(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256_5 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256_5 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Haval_Context
+ ctx.hashbitlen = 256
+ ctx.rounds = 5
+ init(&ctx)
+ ctx.str_len = u32(len(data))
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
+
// hash_stream_256_5 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256_5 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256_5 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Haval_Context
ctx.hashbitlen = 256
ctx.rounds = 5
@@ -932,7 +1282,7 @@ hash_stream_256_5 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256_5 will read the file provided by the given handle
// and compute a hash
-hash_file_256_5 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256_5 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256_5(os.stream_from_handle(hd))
} else {
@@ -940,7 +1290,7 @@ hash_file_256_5 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool
return hash_bytes_256_5(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256_5 :: proc {
@@ -948,6 +1298,8 @@ hash_256_5 :: proc {
hash_file_256_5,
hash_bytes_256_5,
hash_string_256_5,
+ hash_bytes_to_buffer_256_5,
+ hash_string_to_buffer_256_5,
}
/*
@@ -980,7 +1332,7 @@ update :: proc(ctx: ^Haval_Context, data: []byte) {
}
ctx.count[1] += str_len >> 29
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
if rmd_len + str_len >= 128 {
copy(util.slice_to_bytes(ctx.block[:])[rmd_len:], data[:fill_len])
block(ctx, ctx.rounds)
diff --git a/core/crypto/jh/jh.odin b/core/crypto/jh/jh.odin
index f251424d2..42c2d1d34 100644
--- a/core/crypto/jh/jh.odin
+++ b/core/crypto/jh/jh.odin
@@ -17,16 +17,21 @@ import "core:io"
High level API
*/
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
// hash_string_224 will hash the given input and return the
// computed hash
-hash_string_224 :: proc(data: string) -> [28]byte {
+hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
-hash_bytes_224 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
@@ -35,10 +40,29 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Jh_Context
+ ctx.hashbitlen = 224
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Jh_Context
ctx.hashbitlen = 224
init(&ctx)
@@ -57,7 +81,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224 will read the file provided by the given handle
// and compute a hash
-hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
@@ -65,7 +89,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
return hash_bytes_224(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -73,18 +97,20 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
+ hash_bytes_to_buffer_224,
+ hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
@@ -93,10 +119,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Jh_Context
+ ctx.hashbitlen = 256
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Jh_Context
ctx.hashbitlen = 256
init(&ctx)
@@ -115,7 +160,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -123,7 +168,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -131,18 +176,20 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
-hash_string_384 :: proc(data: string) -> [48]byte {
+hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
-hash_bytes_384 :: proc(data: []byte) -> [48]byte {
- hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+ hash: [DIGEST_SIZE_384]byte
ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
@@ -151,10 +198,29 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
return hash
}
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+ ctx: Jh_Context
+ ctx.hashbitlen = 384
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
- hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+ hash: [DIGEST_SIZE_384]byte
ctx: Jh_Context
ctx.hashbitlen = 384
init(&ctx)
@@ -173,7 +239,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
// hash_file_384 will read the file provided by the given handle
// and compute a hash
-hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
@@ -181,7 +247,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
return hash_bytes_384(buf[:]), ok
}
}
- return [48]byte{}, false
+ return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -189,18 +255,20 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
+ hash_bytes_to_buffer_384,
+ hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
@@ -209,10 +277,29 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: Jh_Context
+ ctx.hashbitlen = 512
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: Jh_Context
ctx.hashbitlen = 512
init(&ctx)
@@ -231,7 +318,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -239,7 +326,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -247,6 +334,8 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
diff --git a/core/crypto/keccak/keccak.odin b/core/crypto/keccak/keccak.odin
index 19c4c7dda..aeb5aac52 100644
--- a/core/crypto/keccak/keccak.odin
+++ b/core/crypto/keccak/keccak.odin
@@ -21,18 +21,23 @@ import "../_sha3"
High level API
*/
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
// hash_string_224 will hash the given input and return the
// computed hash
-hash_string_224 :: proc(data: string) -> [28]byte {
+hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
-hash_bytes_224 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 28
+ ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
@@ -40,12 +45,32 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_224
+ ctx.is_keccak = true
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 28
+ ctx.mdlen = DIGEST_SIZE_224
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
@@ -63,7 +88,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224 will read the file provided by the given handle
// and compute a hash
-hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
@@ -71,7 +96,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
return hash_bytes_224(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -79,20 +104,22 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
+ hash_bytes_to_buffer_224,
+ hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 32
+ ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
@@ -100,12 +127,32 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_256
+ ctx.is_keccak = true
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 32
+ ctx.mdlen = DIGEST_SIZE_256
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
@@ -123,7 +170,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -131,7 +178,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -139,20 +186,22 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
-hash_string_384 :: proc(data: string) -> [48]byte {
+hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
-hash_bytes_384 :: proc(data: []byte) -> [48]byte {
- hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+ hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 48
+ ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
@@ -160,12 +209,32 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
return hash
}
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_384
+ ctx.is_keccak = true
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
- hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+ hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 48
+ ctx.mdlen = DIGEST_SIZE_384
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
@@ -183,7 +252,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
// hash_file_384 will read the file provided by the given handle
// and compute a hash
-hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
@@ -191,7 +260,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
return hash_bytes_384(buf[:]), ok
}
}
- return [48]byte{}, false
+ return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -199,20 +268,22 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
+ hash_bytes_to_buffer_384,
+ hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 64
+ ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
_sha3.init(&ctx)
_sha3.update(&ctx, data)
@@ -220,12 +291,32 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_512
+ ctx.is_keccak = true
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 64
+ ctx.mdlen = DIGEST_SIZE_512
ctx.is_keccak = true
_sha3.init(&ctx)
buf := make([]byte, 512)
@@ -243,7 +334,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -251,7 +342,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -259,13 +350,15 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
Low level API
*/
-Sha3_Context :: _sha3.Sha3_Context
+Keccak_Context :: _sha3.Sha3_Context
init :: proc(ctx: ^_sha3.Sha3_Context) {
ctx.is_keccak = true
diff --git a/core/crypto/md2/md2.odin b/core/crypto/md2/md2.odin
index 5e027c13c..711e6e9f6 100644
--- a/core/crypto/md2/md2.odin
+++ b/core/crypto/md2/md2.odin
@@ -17,16 +17,18 @@ import "core:io"
High level API
*/
+DIGEST_SIZE :: 16
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [16]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Md2_Context
// init(&ctx) No-op
update(&ctx, data)
@@ -34,10 +36,28 @@ hash_bytes :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Md2_Context
+ // init(&ctx) No-op
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Md2_Context
// init(&ctx) No-op
buf := make([]byte, 512)
@@ -55,7 +75,7 @@ hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -63,7 +83,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -71,6 +91,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
@@ -86,7 +108,7 @@ update :: proc(ctx: ^Md2_Context, data: []byte) {
for i := 0; i < len(data); i += 1 {
ctx.data[ctx.datalen] = data[i]
ctx.datalen += 1
- if (ctx.datalen == 16) {
+ if (ctx.datalen == DIGEST_SIZE) {
transform(ctx, ctx.data[:])
ctx.datalen = 0
}
@@ -94,14 +116,14 @@ update :: proc(ctx: ^Md2_Context, data: []byte) {
}
final :: proc(ctx: ^Md2_Context, hash: []byte) {
- to_pad := byte(16 - ctx.datalen)
- for ctx.datalen < 16 {
+ to_pad := byte(DIGEST_SIZE - ctx.datalen)
+ for ctx.datalen < DIGEST_SIZE {
ctx.data[ctx.datalen] = to_pad
ctx.datalen += 1
}
transform(ctx, ctx.data[:])
transform(ctx, ctx.checksum[:])
- for i := 0; i < 16; i += 1 {
+ for i := 0; i < DIGEST_SIZE; i += 1 {
hash[i] = ctx.state[i]
}
}
@@ -111,9 +133,9 @@ final :: proc(ctx: ^Md2_Context, hash: []byte) {
*/
Md2_Context :: struct {
- data: [16]byte,
- state: [16 * 3]byte,
- checksum: [16]byte,
+ data: [DIGEST_SIZE]byte,
+ state: [DIGEST_SIZE * 3]byte,
+ checksum: [DIGEST_SIZE]byte,
datalen: int,
}
@@ -140,20 +162,20 @@ PI_TABLE := [?]byte {
transform :: proc(ctx: ^Md2_Context, data: []byte) {
j,k,t: byte
- for j = 0; j < 16; j += 1 {
- ctx.state[j + 16] = data[j]
- ctx.state[j + 16 * 2] = (ctx.state[j + 16] ~ ctx.state[j])
+ for j = 0; j < DIGEST_SIZE; j += 1 {
+ ctx.state[j + DIGEST_SIZE] = data[j]
+ ctx.state[j + DIGEST_SIZE * 2] = (ctx.state[j + DIGEST_SIZE] ~ ctx.state[j])
}
t = 0
- for j = 0; j < 16 + 2; j += 1 {
- for k = 0; k < 16 * 3; k += 1 {
+ for j = 0; j < DIGEST_SIZE + 2; j += 1 {
+ for k = 0; k < DIGEST_SIZE * 3; k += 1 {
ctx.state[k] ~= PI_TABLE[t]
t = ctx.state[k]
}
t = (t + j) & 0xff
}
- t = ctx.checksum[16 - 1]
- for j = 0; j < 16; j += 1 {
+ t = ctx.checksum[DIGEST_SIZE - 1]
+ for j = 0; j < DIGEST_SIZE; j += 1 {
ctx.checksum[j] ~= PI_TABLE[data[j] ~ t]
t = ctx.checksum[j]
}
diff --git a/core/crypto/md4/md4.odin b/core/crypto/md4/md4.odin
index 813db578a..b2651225b 100644
--- a/core/crypto/md4/md4.odin
+++ b/core/crypto/md4/md4.odin
@@ -21,16 +21,18 @@ import "../util"
High level API
*/
+DIGEST_SIZE :: 16
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [16]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Md4_Context
init(&ctx)
update(&ctx, data)
@@ -38,10 +40,28 @@ hash_bytes :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Md4_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Md4_Context
init(&ctx)
buf := make([]byte, 512)
@@ -59,7 +79,7 @@ hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -67,7 +87,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -75,6 +95,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
@@ -171,9 +193,9 @@ HH :: #force_inline proc "contextless"(a, b, c, d, x: u32, s : int) -> u32 {
transform :: proc(ctx: ^Md4_Context, data: []byte) {
a, b, c, d, i, j: u32
- m: [16]u32
+ m: [DIGEST_SIZE]u32
- for i, j = 0, 0; i < 16; i += 1 {
+ for i, j = 0, 0; i < DIGEST_SIZE; i += 1 {
m[i] = u32(data[j]) | (u32(data[j + 1]) << 8) | (u32(data[j + 2]) << 16) | (u32(data[j + 3]) << 24)
j += 4
}
diff --git a/core/crypto/md5/md5.odin b/core/crypto/md5/md5.odin
index a41ed16f8..30a556102 100644
--- a/core/crypto/md5/md5.odin
+++ b/core/crypto/md5/md5.odin
@@ -20,16 +20,18 @@ import "../util"
High level API
*/
+DIGEST_SIZE :: 16
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [16]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Md5_Context
init(&ctx)
update(&ctx, data)
@@ -37,10 +39,28 @@ hash_bytes :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Md5_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Md5_Context
init(&ctx)
buf := make([]byte, 512)
@@ -58,7 +78,7 @@ hash_stream :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -66,7 +86,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -74,6 +94,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
@@ -176,9 +198,9 @@ II :: #force_inline proc "contextless" (a, b, c, d, m: u32, s: int, t: u32) -> u
transform :: proc(ctx: ^Md5_Context, data: []byte) {
i, j: u32
- m: [16]u32
+ m: [DIGEST_SIZE]u32
- for i, j = 0, 0; i < 16; i+=1 {
+ for i, j = 0, 0; i < DIGEST_SIZE; i+=1 {
m[i] = u32(data[j]) + u32(data[j + 1]) << 8 + u32(data[j + 2]) << 16 + u32(data[j + 3]) << 24
j += 4
}
diff --git a/core/crypto/rand_generic.odin b/core/crypto/rand_generic.odin
index 98890b5b1..52abfe4d7 100644
--- a/core/crypto/rand_generic.odin
+++ b/core/crypto/rand_generic.odin
@@ -1,6 +1,6 @@
package crypto
-when ODIN_OS != "linux" {
+when ODIN_OS != .Linux && ODIN_OS != .OpenBSD && ODIN_OS != .Windows {
_rand_bytes :: proc (dst: []byte) {
unimplemented("crypto: rand_bytes not supported on this OS")
}
diff --git a/core/crypto/rand_openbsd.odin b/core/crypto/rand_openbsd.odin
new file mode 100644
index 000000000..bae97e8f0
--- /dev/null
+++ b/core/crypto/rand_openbsd.odin
@@ -0,0 +1,12 @@
+package crypto
+
+import "core:c"
+
+foreign import libc "system:c"
+foreign libc {
+ arc4random_buf :: proc "c" (buf: rawptr, nbytes: c.size_t) ---
+}
+
+_rand_bytes :: proc (dst: []byte) {
+ arc4random_buf(raw_data(dst), len(dst))
+}
diff --git a/core/crypto/rand_windows.odin b/core/crypto/rand_windows.odin
new file mode 100644
index 000000000..53b58c776
--- /dev/null
+++ b/core/crypto/rand_windows.odin
@@ -0,0 +1,23 @@
+package crypto
+
+import win32 "core:sys/windows"
+import "core:os"
+import "core:fmt"
+
+_rand_bytes :: proc(dst: []byte) {
+ ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
+ if ret != os.ERROR_NONE {
+ switch ret {
+ case os.ERROR_INVALID_HANDLE:
+ // The handle to the first parameter is invalid.
+ // This should not happen here, since we explicitly pass nil to it
+ panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
+ case os.ERROR_INVALID_PARAMETER:
+ // One of the parameters was invalid
+ panic("crypto: BCryptGenRandom Invalid parameter")
+ case:
+ // Unknown error
+ panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret))
+ }
+ }
+}
diff --git a/core/crypto/ripemd/ripemd.odin b/core/crypto/ripemd/ripemd.odin
index a9a5d1126..702d29037 100644
--- a/core/crypto/ripemd/ripemd.odin
+++ b/core/crypto/ripemd/ripemd.odin
@@ -19,16 +19,21 @@ import "../util"
High level API
*/
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_320 :: 40
+
// hash_string_128 will hash the given input and return the
// computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: Ripemd128_Context
init(&ctx)
update(&ctx, data)
@@ -36,10 +41,28 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: Ripemd128_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: Ripemd128_Context
init(&ctx)
buf := make([]byte, 512)
@@ -57,7 +80,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128 will read the file provided by the given handle
// and compute a hash
-hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
@@ -65,7 +88,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
return hash_bytes_128(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -73,18 +96,20 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
+ hash_bytes_to_buffer_128,
+ hash_string_to_buffer_128,
}
// hash_string_160 will hash the given input and return the
// computed hash
-hash_string_160 :: proc(data: string) -> [20]byte {
+hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160(transmute([]byte)(data))
}
// hash_bytes_160 will hash the given input and return the
// computed hash
-hash_bytes_160 :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+ hash: [DIGEST_SIZE_160]byte
ctx: Ripemd160_Context
init(&ctx)
update(&ctx, data)
@@ -92,10 +117,28 @@ hash_bytes_160 :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer_160 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_160 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+ ctx: Ripemd160_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_160 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+ hash: [DIGEST_SIZE_160]byte
ctx: Ripemd160_Context
init(&ctx)
buf := make([]byte, 512)
@@ -113,7 +156,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file_160 will read the file provided by the given handle
// and compute a hash
-hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160(os.stream_from_handle(hd))
} else {
@@ -121,7 +164,7 @@ hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool)
return hash_bytes_160(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE_160]byte{}, false
}
hash_160 :: proc {
@@ -129,18 +172,20 @@ hash_160 :: proc {
hash_file_160,
hash_bytes_160,
hash_string_160,
+ hash_bytes_to_buffer_160,
+ hash_string_to_buffer_160,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Ripemd256_Context
init(&ctx)
update(&ctx, data)
@@ -148,10 +193,28 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Ripemd256_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Ripemd256_Context
init(&ctx)
buf := make([]byte, 512)
@@ -169,7 +232,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -177,7 +240,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -185,18 +248,20 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_320 will hash the given input and return the
// computed hash
-hash_string_320 :: proc(data: string) -> [40]byte {
+hash_string_320 :: proc(data: string) -> [DIGEST_SIZE_320]byte {
return hash_bytes_320(transmute([]byte)(data))
}
// hash_bytes_320 will hash the given input and return the
// computed hash
-hash_bytes_320 :: proc(data: []byte) -> [40]byte {
- hash: [40]byte
+hash_bytes_320 :: proc(data: []byte) -> [DIGEST_SIZE_320]byte {
+ hash: [DIGEST_SIZE_320]byte
ctx: Ripemd320_Context
init(&ctx)
update(&ctx, data)
@@ -204,10 +269,28 @@ hash_bytes_320 :: proc(data: []byte) -> [40]byte {
return hash
}
+// hash_string_to_buffer_320 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_320 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_320(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_320 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_320 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_320, "Size of destination buffer is smaller than the digest size")
+ ctx: Ripemd320_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_320 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
- hash: [40]byte
+hash_stream_320 :: proc(s: io.Stream) -> ([DIGEST_SIZE_320]byte, bool) {
+ hash: [DIGEST_SIZE_320]byte
ctx: Ripemd320_Context
init(&ctx)
buf := make([]byte, 512)
@@ -225,7 +308,7 @@ hash_stream_320 :: proc(s: io.Stream) -> ([40]byte, bool) {
// hash_file_320 will read the file provided by the given handle
// and compute a hash
-hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool) {
+hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_320]byte, bool) {
if !load_at_once {
return hash_stream_320(os.stream_from_handle(hd))
} else {
@@ -233,7 +316,7 @@ hash_file_320 :: proc(hd: os.Handle, load_at_once := false) -> ([40]byte, bool)
return hash_bytes_320(buf[:]), ok
}
}
- return [40]byte{}, false
+ return [DIGEST_SIZE_320]byte{}, false
}
hash_320 :: proc {
@@ -241,6 +324,8 @@ hash_320 :: proc {
hash_file_320,
hash_bytes_320,
hash_string_320,
+ hash_bytes_to_buffer_320,
+ hash_string_to_buffer_320,
}
/*
diff --git a/core/crypto/sha1/sha1.odin b/core/crypto/sha1/sha1.odin
index 736b207a3..b0dbd7dc8 100644
--- a/core/crypto/sha1/sha1.odin
+++ b/core/crypto/sha1/sha1.odin
@@ -19,16 +19,19 @@ import "../util"
/*
High level API
*/
+
+DIGEST_SIZE :: 20
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [20]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Sha1_Context
init(&ctx)
update(&ctx, data)
@@ -36,10 +39,28 @@ hash_bytes :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Sha1_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Sha1_Context
init(&ctx)
buf := make([]byte, 512)
@@ -57,7 +78,7 @@ hash_stream :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -65,7 +86,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -73,6 +94,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
diff --git a/core/crypto/sha2/sha2.odin b/core/crypto/sha2/sha2.odin
index 8b7ccf38a..7c7b2da81 100644
--- a/core/crypto/sha2/sha2.odin
+++ b/core/crypto/sha2/sha2.odin
@@ -21,16 +21,21 @@ import "../util"
High level API
*/
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
// hash_string_224 will hash the given input and return the
// computed hash
-hash_string_224 :: proc(data: string) -> [28]byte {
+hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
-hash_bytes_224 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: Sha256_Context
ctx.is224 = true
init(&ctx)
@@ -39,10 +44,29 @@ hash_bytes_224 :: proc(data: []byte) -> [28]byte {
return hash
}
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: Sha256_Context
+ ctx.is224 = true
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
@@ -61,7 +85,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224 will read the file provided by the given handle
// and compute a hash
-hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
@@ -69,7 +93,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
return hash_bytes_224(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -77,18 +101,20 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
+ hash_bytes_to_buffer_224,
+ hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Sha256_Context
ctx.is224 = false
init(&ctx)
@@ -97,10 +123,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Sha256_Context
+ ctx.is224 = false
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
@@ -119,7 +164,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -127,7 +172,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -135,18 +180,20 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
-hash_string_384 :: proc(data: string) -> [48]byte {
+hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
-hash_bytes_384 :: proc(data: []byte) -> [48]byte {
- hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+ hash: [DIGEST_SIZE_384]byte
ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
@@ -155,10 +202,29 @@ hash_bytes_384 :: proc(data: []byte) -> [48]byte {
return hash
}
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+ ctx: Sha512_Context
+ ctx.is384 = true
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
- hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+ hash: [DIGEST_SIZE_384]byte
ctx: Sha512_Context
ctx.is384 = true
init(&ctx)
@@ -177,7 +243,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
// hash_file_384 will read the file provided by the given handle
// and compute a hash
-hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
@@ -185,7 +251,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
return hash_bytes_384(buf[:]), ok
}
}
- return [48]byte{}, false
+ return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -193,18 +259,20 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
+ hash_bytes_to_buffer_384,
+ hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
@@ -213,10 +281,29 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: Sha512_Context
+ ctx.is384 = false
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: Sha512_Context
ctx.is384 = false
init(&ctx)
@@ -235,7 +322,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -243,7 +330,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -251,6 +338,8 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
diff --git a/core/crypto/sha3/sha3.odin b/core/crypto/sha3/sha3.odin
index 1becf7640..1202f8b23 100644
--- a/core/crypto/sha3/sha3.odin
+++ b/core/crypto/sha3/sha3.odin
@@ -20,30 +20,54 @@ import "../_sha3"
High level API
*/
+DIGEST_SIZE_224 :: 28
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_384 :: 48
+DIGEST_SIZE_512 :: 64
+
// hash_string_224 will hash the given input and return the
// computed hash
-hash_string_224 :: proc(data: string) -> [28]byte {
+hash_string_224 :: proc(data: string) -> [DIGEST_SIZE_224]byte {
return hash_bytes_224(transmute([]byte)(data))
}
// hash_bytes_224 will hash the given input and return the
// computed hash
-hash_bytes_224 :: proc(data: []byte) -> [28]byte {
- hash: [28]byte
+hash_bytes_224 :: proc(data: []byte) -> [DIGEST_SIZE_224]byte {
+ hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 28
+ ctx.mdlen = DIGEST_SIZE_224
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
+// hash_string_to_buffer_224 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_224 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_224(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_224 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_224 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_224, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_224
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_224 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
- hash: [28]byte
+hash_stream_224 :: proc(s: io.Stream) -> ([DIGEST_SIZE_224]byte, bool) {
+ hash: [DIGEST_SIZE_224]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 28
+ ctx.mdlen = DIGEST_SIZE_224
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
@@ -60,7 +84,7 @@ hash_stream_224 :: proc(s: io.Stream) -> ([28]byte, bool) {
// hash_file_224 will read the file provided by the given handle
// and compute a hash
-hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool) {
+hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_224]byte, bool) {
if !load_at_once {
return hash_stream_224(os.stream_from_handle(hd))
} else {
@@ -68,7 +92,7 @@ hash_file_224 :: proc(hd: os.Handle, load_at_once := false) -> ([28]byte, bool)
return hash_bytes_224(buf[:]), ok
}
}
- return [28]byte{}, false
+ return [DIGEST_SIZE_224]byte{}, false
}
hash_224 :: proc {
@@ -76,32 +100,53 @@ hash_224 :: proc {
hash_file_224,
hash_bytes_224,
hash_string_224,
+ hash_bytes_to_buffer_224,
+ hash_string_to_buffer_224,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 32
+ ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_256
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 32
+ ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
@@ -118,7 +163,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -126,7 +171,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -134,32 +179,53 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_384 will hash the given input and return the
// computed hash
-hash_string_384 :: proc(data: string) -> [48]byte {
+hash_string_384 :: proc(data: string) -> [DIGEST_SIZE_384]byte {
return hash_bytes_384(transmute([]byte)(data))
}
// hash_bytes_384 will hash the given input and return the
// computed hash
-hash_bytes_384 :: proc(data: []byte) -> [48]byte {
- hash: [48]byte
+hash_bytes_384 :: proc(data: []byte) -> [DIGEST_SIZE_384]byte {
+ hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 48
+ ctx.mdlen = DIGEST_SIZE_384
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
+// hash_string_to_buffer_384 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_384 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_384(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_384 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_384 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_384, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_384
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_384 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
- hash: [48]byte
+hash_stream_384 :: proc(s: io.Stream) -> ([DIGEST_SIZE_384]byte, bool) {
+ hash: [DIGEST_SIZE_384]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 48
+ ctx.mdlen = DIGEST_SIZE_384
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
@@ -176,7 +242,7 @@ hash_stream_384 :: proc(s: io.Stream) -> ([48]byte, bool) {
// hash_file_384 will read the file provided by the given handle
// and compute a hash
-hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool) {
+hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_384]byte, bool) {
if !load_at_once {
return hash_stream_384(os.stream_from_handle(hd))
} else {
@@ -184,7 +250,7 @@ hash_file_384 :: proc(hd: os.Handle, load_at_once := false) -> ([48]byte, bool)
return hash_bytes_384(buf[:]), ok
}
}
- return [48]byte{}, false
+ return [DIGEST_SIZE_384]byte{}, false
}
hash_384 :: proc {
@@ -192,32 +258,53 @@ hash_384 :: proc {
hash_file_384,
hash_bytes_384,
hash_string_384,
+ hash_bytes_to_buffer_384,
+ hash_string_to_buffer_384,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 64
+ ctx.mdlen = DIGEST_SIZE_512
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.final(&ctx, hash[:])
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_512
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.final(&ctx, hash)
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 64
+ ctx.mdlen = DIGEST_SIZE_512
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
@@ -234,7 +321,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -242,7 +329,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -250,6 +337,8 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
diff --git a/core/crypto/shake/shake.odin b/core/crypto/shake/shake.odin
index ff477b1a9..525dcfbd3 100644
--- a/core/crypto/shake/shake.odin
+++ b/core/crypto/shake/shake.odin
@@ -20,18 +20,21 @@ import "../_sha3"
High level API
*/
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_256 :: 32
+
// hash_string_128 will hash the given input and return the
// computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 16
+ ctx.mdlen = DIGEST_SIZE_128
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
@@ -39,12 +42,32 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_128
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.shake_xof(&ctx)
+ _sha3.shake_out(&ctx, hash)
+}
+
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 16
+ ctx.mdlen = DIGEST_SIZE_128
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
@@ -62,7 +85,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128 will read the file provided by the given handle
// and compute a hash
-hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
@@ -70,7 +93,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
return hash_bytes_128(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -78,20 +101,22 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
+ hash_bytes_to_buffer_128,
+ hash_string_to_buffer_128,
}
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 32
+ ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
_sha3.update(&ctx, data)
_sha3.shake_xof(&ctx)
@@ -99,12 +124,32 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: _sha3.Sha3_Context
+ ctx.mdlen = DIGEST_SIZE_256
+ _sha3.init(&ctx)
+ _sha3.update(&ctx, data)
+ _sha3.shake_xof(&ctx)
+ _sha3.shake_out(&ctx, hash)
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: _sha3.Sha3_Context
- ctx.mdlen = 32
+ ctx.mdlen = DIGEST_SIZE_256
_sha3.init(&ctx)
buf := make([]byte, 512)
defer delete(buf)
@@ -122,7 +167,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -130,7 +175,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -138,13 +183,15 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
/*
Low level API
*/
-Sha3_Context :: _sha3.Sha3_Context
+Shake_Context :: _sha3.Sha3_Context
init :: proc(ctx: ^_sha3.Sha3_Context) {
_sha3.init(ctx)
diff --git a/core/crypto/siphash/siphash.odin b/core/crypto/siphash/siphash.odin
new file mode 100644
index 000000000..a6c75f315
--- /dev/null
+++ b/core/crypto/siphash/siphash.odin
@@ -0,0 +1,335 @@
+package siphash
+
+/*
+ Copyright 2022 zhibog
+ Made available under the BSD-3 license.
+
+ List of contributors:
+ zhibog: Initial implementation.
+
+ Implementation of the SipHash hashing algorithm, as defined at and
+
+ Use the specific procedures for a certain setup. The generic procdedures will default to Siphash 2-4
+*/
+
+import "core:crypto"
+import "core:crypto/util"
+
+/*
+ High level API
+*/
+
+KEY_SIZE :: 16
+DIGEST_SIZE :: 8
+
+// sum_string_1_3 will hash the given message with the key and return
+// the computed hash as a u64
+sum_string_1_3 :: proc(msg, key: string) -> u64 {
+ return sum_bytes_1_3(transmute([]byte)(msg), transmute([]byte)(key))
+}
+
+// sum_bytes_1_3 will hash the given message with the key and return
+// the computed hash as a u64
+sum_bytes_1_3 :: proc (msg, key: []byte) -> u64 {
+ ctx: Context
+ hash: u64
+ init(&ctx, key, 1, 3)
+ update(&ctx, msg)
+ final(&ctx, &hash)
+ return hash
+}
+
+// sum_string_to_buffer_1_3 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_string_to_buffer_1_3 :: proc(msg, key: string, dst: []byte) {
+ sum_bytes_to_buffer_1_3(transmute([]byte)(msg), transmute([]byte)(key), dst)
+}
+
+// sum_bytes_to_buffer_1_3 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_bytes_to_buffer_1_3 :: proc(msg, key, dst: []byte) {
+ assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
+ hash := sum_bytes_1_3(msg, key)
+ _collect_output(dst[:], hash)
+}
+
+sum_1_3 :: proc {
+ sum_string_1_3,
+ sum_bytes_1_3,
+ sum_string_to_buffer_1_3,
+ sum_bytes_to_buffer_1_3,
+}
+
+// verify_u64_1_3 will check if the supplied tag matches with the output you
+// will get from the provided message and key
+verify_u64_1_3 :: proc (tag: u64 msg, key: []byte) -> bool {
+ return sum_bytes_1_3(msg, key) == tag
+}
+
+// verify_bytes will check if the supplied tag matches with the output you
+// will get from the provided message and key
+verify_bytes_1_3 :: proc (tag, msg, key: []byte) -> bool {
+ derived_tag: [8]byte
+ sum_bytes_to_buffer_1_3(msg, key, derived_tag[:])
+ return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+verify_1_3 :: proc {
+ verify_bytes_1_3,
+ verify_u64_1_3,
+}
+
+// sum_string_2_4 will hash the given message with the key and return
+// the computed hash as a u64
+sum_string_2_4 :: proc(msg, key: string) -> u64 {
+ return sum_bytes_2_4(transmute([]byte)(msg), transmute([]byte)(key))
+}
+
+// sum_bytes_2_4 will hash the given message with the key and return
+// the computed hash as a u64
+sum_bytes_2_4 :: proc (msg, key: []byte) -> u64 {
+ ctx: Context
+ hash: u64
+ init(&ctx, key, 2, 4)
+ update(&ctx, msg)
+ final(&ctx, &hash)
+ return hash
+}
+
+// sum_string_to_buffer_2_4 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_string_to_buffer_2_4 :: proc(msg, key: string, dst: []byte) {
+ sum_bytes_to_buffer_2_4(transmute([]byte)(msg), transmute([]byte)(key), dst)
+}
+
+// sum_bytes_to_buffer_2_4 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_bytes_to_buffer_2_4 :: proc(msg, key, dst: []byte) {
+ assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
+ hash := sum_bytes_2_4(msg, key)
+ _collect_output(dst[:], hash)
+}
+
+sum_2_4 :: proc {
+ sum_string_2_4,
+ sum_bytes_2_4,
+ sum_string_to_buffer_2_4,
+ sum_bytes_to_buffer_2_4,
+}
+
+sum_string :: sum_string_2_4
+sum_bytes :: sum_bytes_2_4
+sum_string_to_buffer :: sum_string_to_buffer_2_4
+sum_bytes_to_buffer :: sum_bytes_to_buffer_2_4
+sum :: proc {
+ sum_string,
+ sum_bytes,
+ sum_string_to_buffer,
+ sum_bytes_to_buffer,
+}
+
+// verify_u64_2_4 will check if the supplied tag matches with the output you
+// will get from the provided message and key
+verify_u64_2_4 :: proc (tag: u64 msg, key: []byte) -> bool {
+ return sum_bytes_2_4(msg, key) == tag
+}
+
+// verify_bytes will check if the supplied tag matches with the output you
+// will get from the provided message and key
+verify_bytes_2_4 :: proc (tag, msg, key: []byte) -> bool {
+ derived_tag: [8]byte
+ sum_bytes_to_buffer_2_4(msg, key, derived_tag[:])
+ return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+verify_2_4 :: proc {
+ verify_bytes_2_4,
+ verify_u64_2_4,
+}
+
+verify_bytes :: verify_bytes_2_4
+verify_u64 :: verify_u64_2_4
+verify :: proc {
+ verify_bytes,
+ verify_u64,
+}
+
+// sum_string_4_8 will hash the given message with the key and return
+// the computed hash as a u64
+sum_string_4_8 :: proc(msg, key: string) -> u64 {
+ return sum_bytes_4_8(transmute([]byte)(msg), transmute([]byte)(key))
+}
+
+// sum_bytes_4_8 will hash the given message with the key and return
+// the computed hash as a u64
+sum_bytes_4_8 :: proc (msg, key: []byte) -> u64 {
+ ctx: Context
+ hash: u64
+ init(&ctx, key, 4, 8)
+ update(&ctx, msg)
+ final(&ctx, &hash)
+ return hash
+}
+
+// sum_string_to_buffer_4_8 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_string_to_buffer_4_8 :: proc(msg, key: string, dst: []byte) {
+ sum_bytes_to_buffer_4_8(transmute([]byte)(msg), transmute([]byte)(key), dst)
+}
+
+// sum_bytes_to_buffer_4_8 will hash the given message with the key and write
+// the computed hash into the provided destination buffer
+sum_bytes_to_buffer_4_8 :: proc(msg, key, dst: []byte) {
+ assert(len(dst) >= DIGEST_SIZE, "crypto/siphash: Destination buffer needs to be at least of size 8")
+ hash := sum_bytes_4_8(msg, key)
+ _collect_output(dst[:], hash)
+}
+
+sum_4_8 :: proc {
+ sum_string_4_8,
+ sum_bytes_4_8,
+ sum_string_to_buffer_4_8,
+ sum_bytes_to_buffer_4_8,
+}
+
+// verify_u64_4_8 will check if the supplied tag matches with the output you
+// will get from the provided message and key
+verify_u64_4_8 :: proc (tag: u64 msg, key: []byte) -> bool {
+ return sum_bytes_4_8(msg, key) == tag
+}
+
+// verify_bytes will check if the supplied tag matches with the output you
+// will get from the provided message and key
+verify_bytes_4_8 :: proc (tag, msg, key: []byte) -> bool {
+ derived_tag: [8]byte
+ sum_bytes_to_buffer_4_8(msg, key, derived_tag[:])
+ return crypto.compare_constant_time(derived_tag[:], tag) == 1
+}
+
+verify_4_8 :: proc {
+ verify_bytes_4_8,
+ verify_u64_4_8,
+}
+
+/*
+ Low level API
+*/
+
+init :: proc(ctx: ^Context, key: []byte, c_rounds, d_rounds: int) {
+ assert(len(key) == KEY_SIZE, "crypto/siphash: Invalid key size, want 16")
+ ctx.c_rounds = c_rounds
+ ctx.d_rounds = d_rounds
+ is_valid_setting := (ctx.c_rounds == 1 && ctx.d_rounds == 3) ||
+ (ctx.c_rounds == 2 && ctx.d_rounds == 4) ||
+ (ctx.c_rounds == 4 && ctx.d_rounds == 8)
+ assert(is_valid_setting, "crypto/siphash: Incorrect rounds set up. Valid pairs are (1,3), (2,4) and (4,8)")
+ ctx.k0 = util.U64_LE(key[:8])
+ ctx.k1 = util.U64_LE(key[8:])
+ ctx.v0 = 0x736f6d6570736575 ~ ctx.k0
+ ctx.v1 = 0x646f72616e646f6d ~ ctx.k1
+ ctx.v2 = 0x6c7967656e657261 ~ ctx.k0
+ ctx.v3 = 0x7465646279746573 ~ ctx.k1
+ ctx.is_initialized = true
+}
+
+update :: proc(ctx: ^Context, data: []byte) {
+ assert(ctx.is_initialized, "crypto/siphash: Context is not initalized")
+ ctx.last_block = len(data) / 8 * 8
+ ctx.buf = data
+ i := 0
+ m: u64
+ for i < ctx.last_block {
+ m = u64(ctx.buf[i] & 0xff)
+ i += 1
+
+ for r in u64(1)..<8 {
+ m |= u64(ctx.buf[i] & 0xff) << (r * 8)
+ i += 1
+ }
+
+ ctx.v3 ~= m
+ for _ in 0..= ctx.last_block; i -= 1 {
+ m <<= 8
+ m |= u64(ctx.buf[i] & 0xff)
+ }
+ m |= u64(len(ctx.buf) << 56)
+
+ ctx.v3 ~= m
+
+ for _ in 0.. byte {
+ return byte(into >> (((~byte_num) & (size_of(u64) - 1)) << 3))
+}
+
+_collect_output :: #force_inline proc "contextless" (dst: []byte, hash: u64) {
+ dst[0] = _get_byte(7, hash)
+ dst[1] = _get_byte(6, hash)
+ dst[2] = _get_byte(5, hash)
+ dst[3] = _get_byte(4, hash)
+ dst[4] = _get_byte(3, hash)
+ dst[5] = _get_byte(2, hash)
+ dst[6] = _get_byte(1, hash)
+ dst[7] = _get_byte(0, hash)
+}
+
+_compress :: #force_inline proc "contextless" (ctx: ^Context) {
+ ctx.v0 += ctx.v1
+ ctx.v1 = util.ROTL64(ctx.v1, 13)
+ ctx.v1 ~= ctx.v0
+ ctx.v0 = util.ROTL64(ctx.v0, 32)
+ ctx.v2 += ctx.v3
+ ctx.v3 = util.ROTL64(ctx.v3, 16)
+ ctx.v3 ~= ctx.v2
+ ctx.v0 += ctx.v3
+ ctx.v3 = util.ROTL64(ctx.v3, 21)
+ ctx.v3 ~= ctx.v0
+ ctx.v2 += ctx.v1
+ ctx.v1 = util.ROTL64(ctx.v1, 17)
+ ctx.v1 ~= ctx.v2
+ ctx.v2 = util.ROTL64(ctx.v2, 32)
+}
diff --git a/core/crypto/sm3/sm3.odin b/core/crypto/sm3/sm3.odin
index c72bd4f15..74c9f22e2 100644
--- a/core/crypto/sm3/sm3.odin
+++ b/core/crypto/sm3/sm3.odin
@@ -15,16 +15,22 @@ import "core:io"
import "../util"
+/*
+ High level API
+*/
+
+DIGEST_SIZE :: 32
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [32]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Sm3_Context
init(&ctx)
update(&ctx, data)
@@ -32,10 +38,28 @@ hash_bytes :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Sm3_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Sm3_Context
init(&ctx)
buf := make([]byte, 512)
@@ -53,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -61,7 +85,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -69,6 +93,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
@@ -146,9 +172,6 @@ Sm3_Context :: struct {
length: u64,
}
-BLOCK_SIZE_IN_BYTES :: 64
-BLOCK_SIZE_IN_32 :: 16
-
IV := [8]u32 {
0x7380166f, 0x4914b2b9, 0x172442d7, 0xda8a0600,
0xa96f30bc, 0x163138aa, 0xe38dee4d, 0xb0fb0e4e,
diff --git a/core/crypto/streebog/streebog.odin b/core/crypto/streebog/streebog.odin
index b90ef8e86..f85977cba 100644
--- a/core/crypto/streebog/streebog.odin
+++ b/core/crypto/streebog/streebog.odin
@@ -19,16 +19,19 @@ import "../util"
High level API
*/
+DIGEST_SIZE_256 :: 32
+DIGEST_SIZE_512 :: 64
+
// hash_string_256 will hash the given input and return the
// computed hash
-hash_string_256 :: proc(data: string) -> [32]byte {
+hash_string_256 :: proc(data: string) -> [DIGEST_SIZE_256]byte {
return hash_bytes_256(transmute([]byte)(data))
}
// hash_bytes_256 will hash the given input and return the
// computed hash
-hash_bytes_256 :: proc(data: []byte) -> [32]byte {
- hash: [32]byte
+hash_bytes_256 :: proc(data: []byte) -> [DIGEST_SIZE_256]byte {
+ hash: [DIGEST_SIZE_256]byte
ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
@@ -37,10 +40,29 @@ hash_bytes_256 :: proc(data: []byte) -> [32]byte {
return hash
}
+// hash_string_to_buffer_256 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_256 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_256(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_256 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_256 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_256, "Size of destination buffer is smaller than the digest size")
+ ctx: Streebog_Context
+ ctx.is256 = true
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash[:])
+}
+
// hash_stream_256 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
- hash: [32]byte
+hash_stream_256 :: proc(s: io.Stream) -> ([DIGEST_SIZE_256]byte, bool) {
+ hash: [DIGEST_SIZE_256]byte
ctx: Streebog_Context
ctx.is256 = true
init(&ctx)
@@ -59,7 +81,7 @@ hash_stream_256 :: proc(s: io.Stream) -> ([32]byte, bool) {
// hash_file_256 will read the file provided by the given handle
// and compute a hash
-hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool) {
+hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_256]byte, bool) {
if !load_at_once {
return hash_stream_256(os.stream_from_handle(hd))
} else {
@@ -67,7 +89,7 @@ hash_file_256 :: proc(hd: os.Handle, load_at_once := false) -> ([32]byte, bool)
return hash_bytes_256(buf[:]), ok
}
}
- return [32]byte{}, false
+ return [DIGEST_SIZE_256]byte{}, false
}
hash_256 :: proc {
@@ -75,18 +97,20 @@ hash_256 :: proc {
hash_file_256,
hash_bytes_256,
hash_string_256,
+ hash_bytes_to_buffer_256,
+ hash_string_to_buffer_256,
}
// hash_string_512 will hash the given input and return the
// computed hash
-hash_string_512 :: proc(data: string) -> [64]byte {
+hash_string_512 :: proc(data: string) -> [DIGEST_SIZE_512]byte {
return hash_bytes_512(transmute([]byte)(data))
}
// hash_bytes_512 will hash the given input and return the
// computed hash
-hash_bytes_512 :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes_512 :: proc(data: []byte) -> [DIGEST_SIZE_512]byte {
+ hash: [DIGEST_SIZE_512]byte
ctx: Streebog_Context
init(&ctx)
update(&ctx, data)
@@ -94,10 +118,28 @@ hash_bytes_512 :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer_512 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_512 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_512(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_512 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_512 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_512, "Size of destination buffer is smaller than the digest size")
+ ctx: Streebog_Context
+ init(&ctx)
+ update(&ctx, data)
+ final(&ctx, hash[:])
+}
+
// hash_stream_512 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream_512 :: proc(s: io.Stream) -> ([DIGEST_SIZE_512]byte, bool) {
+ hash: [DIGEST_SIZE_512]byte
ctx: Streebog_Context
init(&ctx)
buf := make([]byte, 512)
@@ -115,7 +157,7 @@ hash_stream_512 :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file_512 will read the file provided by the given handle
// and compute a hash
-hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_512]byte, bool) {
if !load_at_once {
return hash_stream_512(os.stream_from_handle(hd))
} else {
@@ -123,7 +165,7 @@ hash_file_512 :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool)
return hash_bytes_512(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE_512]byte{}, false
}
hash_512 :: proc {
@@ -131,6 +173,8 @@ hash_512 :: proc {
hash_file_512,
hash_bytes_512,
hash_string_512,
+ hash_bytes_to_buffer_512,
+ hash_string_to_buffer_512,
}
/*
diff --git a/core/crypto/tiger/tiger.odin b/core/crypto/tiger/tiger.odin
index ecd7f5583..cf6159fad 100644
--- a/core/crypto/tiger/tiger.odin
+++ b/core/crypto/tiger/tiger.odin
@@ -19,16 +19,20 @@ import "../_tiger"
High level API
*/
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_192 :: 24
+
// hash_string_128 will hash the given input and return the
// computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
@@ -37,10 +41,29 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: _tiger.Tiger_Context
+ ctx.ver = 1
+ _tiger.init(&ctx)
+ _tiger.update(&ctx, data)
+ _tiger.final(&ctx, hash)
+}
+
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
@@ -59,7 +82,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128 will read the file provided by the given handle
// and compute a hash
-hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
@@ -67,7 +90,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
return hash_bytes_128(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -75,18 +98,20 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
+ hash_bytes_to_buffer_128,
+ hash_string_to_buffer_128,
}
// hash_string_160 will hash the given input and return the
// computed hash
-hash_string_160 :: proc(data: string) -> [20]byte {
+hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160(transmute([]byte)(data))
}
// hash_bytes_160 will hash the given input and return the
// computed hash
-hash_bytes_160 :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+ hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
@@ -95,10 +120,29 @@ hash_bytes_160 :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer_160 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_160 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+ ctx: _tiger.Tiger_Context
+ ctx.ver = 1
+ _tiger.init(&ctx)
+ _tiger.update(&ctx, data)
+ _tiger.final(&ctx, hash)
+}
+
// hash_stream_160 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+ hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
@@ -117,7 +161,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file_160 will read the file provided by the given handle
// and compute a hash
-hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160(os.stream_from_handle(hd))
} else {
@@ -125,7 +169,7 @@ hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool)
return hash_bytes_160(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE_160]byte{}, false
}
hash_160 :: proc {
@@ -133,18 +177,20 @@ hash_160 :: proc {
hash_file_160,
hash_bytes_160,
hash_string_160,
+ hash_bytes_to_buffer_160,
+ hash_string_to_buffer_160,
}
// hash_string_192 will hash the given input and return the
// computed hash
-hash_string_192 :: proc(data: string) -> [24]byte {
+hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192(transmute([]byte)(data))
}
// hash_bytes_192 will hash the given input and return the
// computed hash
-hash_bytes_192 :: proc(data: []byte) -> [24]byte {
- hash: [24]byte
+hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+ hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
@@ -153,10 +199,29 @@ hash_bytes_192 :: proc(data: []byte) -> [24]byte {
return hash
}
+// hash_string_to_buffer_192 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_192 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+ ctx: _tiger.Tiger_Context
+ ctx.ver = 1
+ _tiger.init(&ctx)
+ _tiger.update(&ctx, data)
+ _tiger.final(&ctx, hash)
+}
+
// hash_stream_192 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
- hash: [24]byte
+hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+ hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 1
_tiger.init(&ctx)
@@ -175,7 +240,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
// hash_file_192 will read the file provided by the given handle
// and compute a hash
-hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
+hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192(os.stream_from_handle(hd))
} else {
@@ -183,7 +248,7 @@ hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool)
return hash_bytes_192(buf[:]), ok
}
}
- return [24]byte{}, false
+ return [DIGEST_SIZE_192]byte{}, false
}
hash_192 :: proc {
@@ -191,6 +256,8 @@ hash_192 :: proc {
hash_file_192,
hash_bytes_192,
hash_string_192,
+ hash_bytes_to_buffer_192,
+ hash_string_to_buffer_192,
}
/*
diff --git a/core/crypto/tiger2/tiger2.odin b/core/crypto/tiger2/tiger2.odin
index a93e19319..e8f2c4edb 100644
--- a/core/crypto/tiger2/tiger2.odin
+++ b/core/crypto/tiger2/tiger2.odin
@@ -19,16 +19,20 @@ import "../_tiger"
High level API
*/
+DIGEST_SIZE_128 :: 16
+DIGEST_SIZE_160 :: 20
+DIGEST_SIZE_192 :: 24
+
// hash_string_128 will hash the given input and return the
// computed hash
-hash_string_128 :: proc(data: string) -> [16]byte {
+hash_string_128 :: proc(data: string) -> [DIGEST_SIZE_128]byte {
return hash_bytes_128(transmute([]byte)(data))
}
// hash_bytes_128 will hash the given input and return the
// computed hash
-hash_bytes_128 :: proc(data: []byte) -> [16]byte {
- hash: [16]byte
+hash_bytes_128 :: proc(data: []byte) -> [DIGEST_SIZE_128]byte {
+ hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
@@ -37,10 +41,29 @@ hash_bytes_128 :: proc(data: []byte) -> [16]byte {
return hash
}
+// hash_string_to_buffer_128 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_128 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_128(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_128 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_128 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_128, "Size of destination buffer is smaller than the digest size")
+ ctx: _tiger.Tiger_Context
+ ctx.ver = 2
+ _tiger.init(&ctx)
+ _tiger.update(&ctx, data)
+ _tiger.final(&ctx, hash)
+}
+
// hash_stream_128 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
- hash: [16]byte
+hash_stream_128 :: proc(s: io.Stream) -> ([DIGEST_SIZE_128]byte, bool) {
+ hash: [DIGEST_SIZE_128]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
@@ -59,7 +82,7 @@ hash_stream_128 :: proc(s: io.Stream) -> ([16]byte, bool) {
// hash_file_128 will read the file provided by the given handle
// and compute a hash
-hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool) {
+hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_128]byte, bool) {
if !load_at_once {
return hash_stream_128(os.stream_from_handle(hd))
} else {
@@ -67,7 +90,7 @@ hash_file_128 :: proc(hd: os.Handle, load_at_once := false) -> ([16]byte, bool)
return hash_bytes_128(buf[:]), ok
}
}
- return [16]byte{}, false
+ return [DIGEST_SIZE_128]byte{}, false
}
hash_128 :: proc {
@@ -75,18 +98,20 @@ hash_128 :: proc {
hash_file_128,
hash_bytes_128,
hash_string_128,
+ hash_bytes_to_buffer_128,
+ hash_string_to_buffer_128,
}
// hash_string_160 will hash the given input and return the
// computed hash
-hash_string_160 :: proc(data: string) -> [20]byte {
+hash_string_160 :: proc(data: string) -> [DIGEST_SIZE_160]byte {
return hash_bytes_160(transmute([]byte)(data))
}
// hash_bytes_160 will hash the given input and return the
// computed hash
-hash_bytes_160 :: proc(data: []byte) -> [20]byte {
- hash: [20]byte
+hash_bytes_160 :: proc(data: []byte) -> [DIGEST_SIZE_160]byte {
+ hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
@@ -95,10 +120,29 @@ hash_bytes_160 :: proc(data: []byte) -> [20]byte {
return hash
}
+// hash_string_to_buffer_160 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_160 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_160(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_160 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_160 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_160, "Size of destination buffer is smaller than the digest size")
+ ctx: _tiger.Tiger_Context
+ ctx.ver = 2
+ _tiger.init(&ctx)
+ _tiger.update(&ctx, data)
+ _tiger.final(&ctx, hash)
+}
+
// hash_stream_160 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
- hash: [20]byte
+hash_stream_160 :: proc(s: io.Stream) -> ([DIGEST_SIZE_160]byte, bool) {
+ hash: [DIGEST_SIZE_160]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
@@ -117,7 +161,7 @@ hash_stream_160 :: proc(s: io.Stream) -> ([20]byte, bool) {
// hash_file_160 will read the file provided by the given handle
// and compute a hash
-hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool) {
+hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_160]byte, bool) {
if !load_at_once {
return hash_stream_160(os.stream_from_handle(hd))
} else {
@@ -125,7 +169,7 @@ hash_file_160 :: proc(hd: os.Handle, load_at_once := false) -> ([20]byte, bool)
return hash_bytes_160(buf[:]), ok
}
}
- return [20]byte{}, false
+ return [DIGEST_SIZE_160]byte{}, false
}
hash_160 :: proc {
@@ -133,18 +177,20 @@ hash_160 :: proc {
hash_file_160,
hash_bytes_160,
hash_string_160,
+ hash_bytes_to_buffer_160,
+ hash_string_to_buffer_160,
}
// hash_string_192 will hash the given input and return the
// computed hash
-hash_string_192 :: proc(data: string) -> [24]byte {
+hash_string_192 :: proc(data: string) -> [DIGEST_SIZE_192]byte {
return hash_bytes_192(transmute([]byte)(data))
}
// hash_bytes_192 will hash the given input and return the
// computed hash
-hash_bytes_192 :: proc(data: []byte) -> [24]byte {
- hash: [24]byte
+hash_bytes_192 :: proc(data: []byte) -> [DIGEST_SIZE_192]byte {
+ hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
@@ -153,10 +199,29 @@ hash_bytes_192 :: proc(data: []byte) -> [24]byte {
return hash
}
+// hash_string_to_buffer_192 will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer_192 :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer_192(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer_192 will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer_192 :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE_192, "Size of destination buffer is smaller than the digest size")
+ ctx: _tiger.Tiger_Context
+ ctx.ver = 2
+ _tiger.init(&ctx)
+ _tiger.update(&ctx, data)
+ _tiger.final(&ctx, hash)
+}
+
// hash_stream_192 will read the stream in chunks and compute a
// hash from its contents
-hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
- hash: [24]byte
+hash_stream_192 :: proc(s: io.Stream) -> ([DIGEST_SIZE_192]byte, bool) {
+ hash: [DIGEST_SIZE_192]byte
ctx: _tiger.Tiger_Context
ctx.ver = 2
_tiger.init(&ctx)
@@ -175,7 +240,7 @@ hash_stream_192 :: proc(s: io.Stream) -> ([24]byte, bool) {
// hash_file_192 will read the file provided by the given handle
// and compute a hash
-hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool) {
+hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE_192]byte, bool) {
if !load_at_once {
return hash_stream_192(os.stream_from_handle(hd))
} else {
@@ -183,7 +248,7 @@ hash_file_192 :: proc(hd: os.Handle, load_at_once := false) -> ([24]byte, bool)
return hash_bytes_192(buf[:]), ok
}
}
- return [24]byte{}, false
+ return [DIGEST_SIZE_192]byte{}, false
}
hash_192 :: proc {
@@ -191,6 +256,8 @@ hash_192 :: proc {
hash_file_192,
hash_bytes_192,
hash_string_192,
+ hash_bytes_to_buffer_192,
+ hash_string_to_buffer_192,
}
/*
diff --git a/core/crypto/whirlpool/whirlpool.odin b/core/crypto/whirlpool/whirlpool.odin
index 43ad2a0a5..0cfef7c6b 100644
--- a/core/crypto/whirlpool/whirlpool.odin
+++ b/core/crypto/whirlpool/whirlpool.odin
@@ -19,16 +19,18 @@ import "../util"
High level API
*/
+DIGEST_SIZE :: 64
+
// hash_string will hash the given input and return the
// computed hash
-hash_string :: proc(data: string) -> [64]byte {
+hash_string :: proc(data: string) -> [DIGEST_SIZE]byte {
return hash_bytes(transmute([]byte)(data))
}
// hash_bytes will hash the given input and return the
// computed hash
-hash_bytes :: proc(data: []byte) -> [64]byte {
- hash: [64]byte
+hash_bytes :: proc(data: []byte) -> [DIGEST_SIZE]byte {
+ hash: [DIGEST_SIZE]byte
ctx: Whirlpool_Context
// init(&ctx) No-op
update(&ctx, data)
@@ -36,10 +38,28 @@ hash_bytes :: proc(data: []byte) -> [64]byte {
return hash
}
+// hash_string_to_buffer will hash the given input and assign the
+// computed hash to the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_string_to_buffer :: proc(data: string, hash: []byte) {
+ hash_bytes_to_buffer(transmute([]byte)(data), hash)
+}
+
+// hash_bytes_to_buffer will hash the given input and write the
+// computed hash into the second parameter.
+// It requires that the destination buffer is at least as big as the digest size
+hash_bytes_to_buffer :: proc(data, hash: []byte) {
+ assert(len(hash) >= DIGEST_SIZE, "Size of destination buffer is smaller than the digest size")
+ ctx: Whirlpool_Context
+ // init(&ctx) No-op
+ update(&ctx, data)
+ final(&ctx, hash)
+}
+
// hash_stream will read the stream in chunks and compute a
// hash from its contents
-hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
- hash: [64]byte
+hash_stream :: proc(s: io.Stream) -> ([DIGEST_SIZE]byte, bool) {
+ hash: [DIGEST_SIZE]byte
ctx: Whirlpool_Context
// init(&ctx) No-op
buf := make([]byte, 512)
@@ -57,7 +77,7 @@ hash_stream :: proc(s: io.Stream) -> ([64]byte, bool) {
// hash_file will read the file provided by the given handle
// and compute a hash
-hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
+hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([DIGEST_SIZE]byte, bool) {
if !load_at_once {
return hash_stream(os.stream_from_handle(hd))
} else {
@@ -65,7 +85,7 @@ hash_file :: proc(hd: os.Handle, load_at_once := false) -> ([64]byte, bool) {
return hash_bytes(buf[:]), ok
}
}
- return [64]byte{}, false
+ return [DIGEST_SIZE]byte{}, false
}
hash :: proc {
@@ -73,6 +93,8 @@ hash :: proc {
hash_file,
hash_bytes,
hash_string,
+ hash_bytes_to_buffer,
+ hash_string_to_buffer,
}
/*
diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin
index bb8affb79..e52ade153 100644
--- a/core/dynlib/lib_unix.odin
+++ b/core/dynlib/lib_unix.odin
@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
package dynlib
import "core:os"
diff --git a/core/encoding/hxa/doc.odin b/core/encoding/hxa/doc.odin
index 16b94a243..230d6ea66 100644
--- a/core/encoding/hxa/doc.odin
+++ b/core/encoding/hxa/doc.odin
@@ -27,7 +27,7 @@
// Construction history, or BSP trees would make the format too large to serve its purpose.
// The facilities of the formats to store meta data should make the format flexible enough
// for most uses. Adding HxA support should be something anyone can do in a days work.
-
+//
// Structure:
// ----------
// HxA is designed to be extremely simple to parse, and is therefore based around conventions. It has
@@ -45,17 +45,17 @@
// of a number of named layers. All layers in the stack have the same number of elements. Each layer
// describes one property of the primitive. Each layer can have multiple channels and each layer can
// store data of a different type.
-
+//
// HaX stores 3 kinds of nodes
// - Pixel data.
// - Polygon geometry data.
// - Meta data only.
-
+//
// Pixel Nodes stores pixels in a layer stack. A layer may store things like Albedo, Roughness,
// Reflectance, Light maps, Masks, Normal maps, and Displacement. Layers use the channels of the
// layers to store things like color. The length of the layer stack is determined by the type and
// dimensions stored in the
-
+//
// Geometry data is stored in 3 separate layer stacks for: vertex data, corner data and face data. The
// vertex data stores things like verities, blend shapes, weight maps, and vertex colors. The first
// layer in a vertex stack has to be a 3 channel layer named "position" describing the base position
@@ -63,7 +63,7 @@
// for things like UV, normals, and adjacency. The first layer in a corner stack has to be a 1 channel
// integer layer named "index" describing the vertices used to form polygons. The last value in each
// polygon has a negative - 1 index to indicate the end of the polygon.
-
+//
// Example:
// A quad and a tri with the vertex index:
// [0, 1, 2, 3] [1, 4, 2]
@@ -72,7 +72,7 @@
// The face stack stores values per face. the length of the face stack has to match the number of
// negative values in the index layer in the corner stack. The face stack can be used to store things
// like material index.
-
+//
// Storage
// -------
// All data is stored in little endian byte order with no padding. The layout mirrors the structs
diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin
index ef7edc8b7..abe295530 100644
--- a/core/encoding/hxa/read.odin
+++ b/core/encoding/hxa/read.odin
@@ -39,6 +39,9 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_value :: proc(r: ^Reader, $T: typeid) -> (value: T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T) {
+ if r.print_error {
+ fmt.eprintf("file '%s' failed to read value at offset %v\n", r.filename, r.offset)
+ }
err = .Short_Read
return
}
@@ -51,6 +54,10 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
read_array :: proc(r: ^Reader, $T: typeid, count: int) -> (value: []T, err: Read_Error) {
remaining := len(r.data) - r.offset
if remaining < size_of(T)*count {
+ if r.print_error {
+ fmt.eprintf("file '%s' failed to read array of %d elements at offset %v\n",
+ r.filename, count, r.offset)
+ }
err = .Short_Read
return
}
@@ -82,7 +89,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Meta_Value_Type) or_return
if type > max(Meta_Value_Type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is ", r.filename, u8(type), u8(max(Meta_Value_Type)))
+ fmt.eprintf("HxA Error: file '%s' has meta value type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Meta_Value_Type)))
}
err = .Invalid_Data
return
@@ -114,7 +122,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Layer_Data_Type) or_return
if type > max(type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is ", r.filename, u8(type), u8(max(Layer_Data_Type)))
+ fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Layer_Data_Type)))
}
err = .Invalid_Data
return
@@ -134,13 +143,23 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
}
if len(data) < size_of(Header) {
+ if print_error {
+ fmt.eprintf("HxA Error: file '%s' has no header\n", filename)
+ }
+ err = .Short_Read
return
}
context.allocator = allocator
header := cast(^Header)raw_data(data)
- assert(header.magic_number == MAGIC_NUMBER)
+ if (header.magic_number != MAGIC_NUMBER) {
+ if print_error {
+ fmt.eprintf("HxA Error: file '%s' has invalid magic number 0x%x\n", filename, header.magic_number)
+ }
+ err = .Invalid_Data
+ return
+ }
r := &Reader{
filename = filename,
@@ -150,6 +169,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
}
node_count := 0
+ file.header = header^
file.nodes = make([]Node, header.internal_node_count)
defer if err != nil {
nodes_destroy(file.nodes)
@@ -162,7 +182,8 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato
type := read_value(r, Node_Type) or_return
if type > max(Node_Type) {
if r.print_error {
- fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is ", r.filename, u8(type), u8(max(Node_Type)))
+ fmt.eprintf("HxA Error: file '%s' has node type %d. Maximum value is %d\n",
+ r.filename, u8(type), u8(max(Node_Type)))
}
err = .Invalid_Data
return
diff --git a/core/encoding/hxa/write.odin b/core/encoding/hxa/write.odin
index e774018b2..5bb950e81 100644
--- a/core/encoding/hxa/write.odin
+++ b/core/encoding/hxa/write.odin
@@ -84,7 +84,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_metadata :: proc(w: ^Writer, meta_data: []Meta) {
for m in meta_data {
- name_len := max(len(m.name), 255)
+ name_len := min(len(m.name), 255)
write_value(w, u8(name_len))
write_string(w, m.name[:name_len])
@@ -127,7 +127,7 @@ write_internal :: proc(w: ^Writer, file: File) {
write_layer_stack :: proc(w: ^Writer, layers: Layer_Stack) {
write_value(w, u32(len(layers)))
for layer in layers {
- name_len := max(len(layer.name), 255)
+ name_len := min(len(layer.name), 255)
write_value(w, u8(name_len))
write_string(w, layer .name[:name_len])
@@ -152,7 +152,7 @@ write_internal :: proc(w: ^Writer, file: File) {
return
}
- write_value(w, &Header{
+ write_value(w, Header{
magic_number = MAGIC_NUMBER,
version = LATEST_VERSION,
internal_node_count = u32le(len(file.nodes)),
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index adbcb95be..9c54f35f0 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -8,17 +8,18 @@ import "core:strings"
import "core:io"
Marshal_Data_Error :: enum {
+ None,
Unsupported_Type,
}
-Marshal_Error :: union {
+Marshal_Error :: union #shared_nil {
Marshal_Data_Error,
io.Error,
}
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
b := strings.make_builder(allocator)
- defer if err != .None {
+ defer if err != nil {
strings.destroy_builder(&b)
}
@@ -27,7 +28,7 @@ marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: M
if len(b.buf) != 0 {
data = b.buf[:]
}
- return data, .None
+ return data, nil
}
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
@@ -285,8 +286,8 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false
- case .Little: return ODIN_ENDIAN != "little"
- case .Big: return ODIN_ENDIAN != "big"
+ case .Little: return ODIN_ENDIAN != .Little
+ case .Big: return ODIN_ENDIAN != .Big
}
}
return false
diff --git a/core/encoding/json/parser.odin b/core/encoding/json/parser.odin
index c682ec9bd..7bf88c565 100644
--- a/core/encoding/json/parser.odin
+++ b/core/encoding/json/parser.odin
@@ -354,6 +354,12 @@ unquote_string :: proc(token: Token, spec: Specification, allocator := context.a
b := bytes_make(len(s) + 2*utf8.UTF_MAX, 1, allocator) or_return
w := copy(b, s[0:i])
+
+ if len(b) == 0 && allocator.data == nil {
+ // `unmarshal_count_array` calls us with a nil allocator
+ return string(b[:w]), nil
+ }
+
loop: for i < len(s) {
c := s[i]
switch {
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index fe3137b7e..bd48011f1 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -52,11 +52,11 @@ unmarshal_any :: proc(data: []byte, v: any, spec := DEFAULT_SPECIFICATION, alloc
if p.spec == .MJSON {
#partial switch p.curr_token.kind {
case .Ident, .String:
- return unmarsal_object(&p, data, .EOF)
+ return unmarshal_object(&p, data, .EOF)
}
}
- return unmarsal_value(&p, data)
+ return unmarshal_value(&p, data)
}
@@ -148,7 +148,7 @@ assign_float :: proc(val: any, f: $T) -> bool {
@(private)
-unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
+unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
val := val
switch dst in &val {
case string:
@@ -198,7 +198,7 @@ unmarsal_string :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Inf
@(private)
-unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
+unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
token := p.curr_token
@@ -257,7 +257,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Ident:
advance_token(p)
if p.spec == .MJSON {
- if unmarsal_string(p, any{v.data, ti.id}, token.text, ti) {
+ if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
return nil
}
}
@@ -266,7 +266,7 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .String:
advance_token(p)
str := unquote_string(token, p.spec, p.allocator) or_return
- if unmarsal_string(p, any{v.data, ti.id}, str, ti) {
+ if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
return nil
}
delete(str, p.allocator)
@@ -274,10 +274,10 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Open_Brace:
- return unmarsal_object(p, v, .Close_Brace)
+ return unmarshal_object(p, v, .Close_Brace)
case .Open_Bracket:
- return unmarsal_array(p, v)
+ return unmarshal_array(p, v)
case:
if p.spec != .JSON {
@@ -312,16 +312,16 @@ unmarsal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
@(private)
-unmarsal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
+unmarshal_expect_token :: proc(p: ^Parser, kind: Token_Kind, loc := #caller_location) -> Token {
prev := p.curr_token
err := expect_token(p, kind)
- assert(err == nil, "unmarsal_expect_token")
+ assert(err == nil, "unmarshal_expect_token")
return prev
}
@(private)
-unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
+unmarshal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unmarshal_Error) {
UNSUPPORTED_TYPE := Unsupported_Type_Error{v.id, p.curr_token}
if end_token == .Close_Brace {
@@ -342,7 +342,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
key, _ := parse_object_key(p, p.allocator)
defer delete(key, p.allocator)
- unmarsal_expect_token(p, .Colon)
+ unmarshal_expect_token(p, .Colon)
fields := reflect.struct_fields_zipped(ti.id)
@@ -378,7 +378,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
field_ptr := rawptr(uintptr(v.data) + offset)
field := any{field_ptr, type.id}
- unmarsal_value(p, field) or_return
+ unmarshal_value(p, field) or_return
if parse_comma(p) {
break struct_loop
@@ -407,11 +407,11 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
map_loop: for p.curr_token.kind != end_token {
key, _ := parse_object_key(p, p.allocator)
- unmarsal_expect_token(p, .Colon)
+ unmarshal_expect_token(p, .Colon)
mem.zero_slice(elem_backing)
- if err := unmarsal_value(p, map_backing_value); err != nil {
+ if err := unmarshal_value(p, map_backing_value); err != nil {
delete(key, p.allocator)
return err
}
@@ -443,7 +443,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
enumerated_array_loop: for p.curr_token.kind != end_token {
key, _ := parse_object_key(p, p.allocator)
- unmarsal_expect_token(p, .Colon)
+ unmarshal_expect_token(p, .Colon)
defer delete(key, p.allocator)
index := -1
@@ -460,7 +460,7 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
index_ptr := rawptr(uintptr(v.data) + uintptr(index*t.elem_size))
index_any := any{index_ptr, t.elem.id}
- unmarsal_value(p, index_any) or_return
+ unmarshal_value(p, index_any) or_return
if parse_comma(p) {
break enumerated_array_loop
@@ -480,10 +480,10 @@ unmarsal_object :: proc(p: ^Parser, v: any, end_token: Token_Kind) -> (err: Unma
@(private)
-unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
+unmarshal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
p_backup := p^
p.allocator = mem.nil_allocator()
- unmarsal_expect_token(p, .Open_Bracket)
+ unmarshal_expect_token(p, .Open_Bracket)
array_length_loop: for p.curr_token.kind != .Close_Bracket {
_, _ = parse_value(p)
length += 1
@@ -497,9 +497,9 @@ unmarsal_count_array :: proc(p: ^Parser) -> (length: uintptr) {
}
@(private)
-unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
+unmarshal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
assign_array :: proc(p: ^Parser, base: rawptr, elem: ^reflect.Type_Info, length: uintptr) -> Unmarshal_Error {
- unmarsal_expect_token(p, .Open_Bracket)
+ unmarshal_expect_token(p, .Open_Bracket)
for idx: uintptr = 0; p.curr_token.kind != .Close_Bracket; idx += 1 {
assert(idx < length)
@@ -507,14 +507,14 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
elem_ptr := rawptr(uintptr(base) + idx*uintptr(elem.size))
elem := any{elem_ptr, elem.id}
- unmarsal_value(p, elem) or_return
+ unmarshal_value(p, elem) or_return
if parse_comma(p) {
break
}
}
- unmarsal_expect_token(p, .Close_Bracket)
+ unmarshal_expect_token(p, .Close_Bracket)
return nil
@@ -524,7 +524,7 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
ti := reflect.type_info_base(type_info_of(v.id))
- length := unmarsal_count_array(p)
+ length := unmarshal_count_array(p)
#partial switch t in ti.variant {
case reflect.Type_Info_Slice:
@@ -578,4 +578,4 @@ unmarsal_array :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
}
return UNSUPPORTED_TYPE
-}
\ No newline at end of file
+}
diff --git a/core/encoding/varint/doc.odin b/core/encoding/varint/doc.odin
new file mode 100644
index 000000000..5e4708a59
--- /dev/null
+++ b/core/encoding/varint/doc.odin
@@ -0,0 +1,28 @@
+/*
+ Implementation of the LEB128 variable integer encoding as used by DWARF encoding and DEX files, among others.
+
+ Author of this Odin package: Jeroen van Rijn
+
+ Example:
+ ```odin
+ import "core:encoding/varint"
+ import "core:fmt"
+
+ main :: proc() {
+ buf: [varint.LEB128_MAX_BYTES]u8
+
+ value := u128(42)
+
+ encode_size, encode_err := varint.encode_uleb128(buf[:], value)
+ assert(encode_size == 1 && encode_err == .None)
+
+ fmt.printf("Encoded as %v\n", buf[:encode_size])
+ decoded_val, decode_size, decode_err := varint.decode_uleb128(buf[:])
+
+ assert(decoded_val == value && decode_size == encode_size && decode_err == .None)
+ fmt.printf("Decoded as %v, using %v byte%v\n", decoded_val, decode_size, "" if decode_size == 1 else "s")
+ }
+ ```
+
+*/
+package varint
\ No newline at end of file
diff --git a/core/encoding/varint/leb128.odin b/core/encoding/varint/leb128.odin
new file mode 100644
index 000000000..f8fcc7de5
--- /dev/null
+++ b/core/encoding/varint/leb128.odin
@@ -0,0 +1,165 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+
+// package varint implements variable length integer encoding and decoding using
+// the LEB128 format as used by DWARF debug info, Android .dex and other file formats.
+package varint
+
+// In theory we should use the bigint package. In practice, varints bigger than this indicate a corrupted file.
+// Instead we'll set limits on the values we'll encode/decode
+// 18 * 7 bits = 126, which means that a possible 19th byte may at most be `0b0000_0011`.
+LEB128_MAX_BYTES :: 19
+
+Error :: enum {
+ None = 0,
+ Buffer_Too_Small = 1,
+ Value_Too_Large = 2,
+}
+
+// Decode a slice of bytes encoding an unsigned LEB128 integer into value and number of bytes used.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_uleb128_buffer :: proc(buf: []u8) -> (val: u128, size: int, err: Error) {
+ if len(buf) == 0 {
+ return 0, 0, .Buffer_Too_Small
+ }
+
+ for v in buf {
+ val, size, err = decode_uleb128_byte(v, size, val)
+ if err != .Buffer_Too_Small {
+ return
+ }
+ }
+
+ if err == .Buffer_Too_Small {
+ val, size = 0, 0
+ }
+ return
+}
+
+// Decodes an unsigned LEB128 integer into value a byte at a time.
+// Returns `.None` when decoded properly, `.Value_Too_Large` when they value
+// exceeds the limits of a u128, and `.Buffer_Too_Small` when it's not yet fully decoded.
+decode_uleb128_byte :: proc(input: u8, offset: int, accumulator: u128) -> (val: u128, size: int, err: Error) {
+ size = offset + 1
+
+ // 18 * 7 bits = 126, which means that a possible 19th byte may at most be 0b0000_0011.
+ if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0b0000_0011 {
+ return 0, 0, .Value_Too_Large
+ }
+
+ val = accumulator | u128(input & 0x7f) << uint(offset * 7)
+
+ if input < 128 {
+ // We're done
+ return
+ }
+
+ // If the buffer runs out before the number ends, return an error.
+ return val, size, .Buffer_Too_Small
+}
+decode_uleb128 :: proc {decode_uleb128_buffer, decode_uleb128_byte}
+
+// Decode a slice of bytes encoding a signed LEB128 integer into value and number of bytes used.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_ileb128_buffer :: proc(buf: []u8) -> (val: i128, size: int, err: Error) {
+ if len(buf) == 0 {
+ return 0, 0, .Buffer_Too_Small
+ }
+
+ for v in buf {
+ val, size, err = decode_ileb128_byte(v, size, val)
+ if err != .Buffer_Too_Small {
+ return
+ }
+ }
+
+ if err == .Buffer_Too_Small {
+ val, size = 0, 0
+ }
+ return
+}
+
+// Decode a a signed LEB128 integer into value and number of bytes used, one byte at a time.
+// Returns `size` == 0 for an invalid value, empty slice, or a varint > 18 bytes.
+decode_ileb128_byte :: proc(input: u8, offset: int, accumulator: i128) -> (val: i128, size: int, err: Error) {
+ size = offset + 1
+ shift := uint(offset * 7)
+
+ // 18 * 7 bits = 126, which including sign means we can have a 19th byte.
+ if size > LEB128_MAX_BYTES || size == LEB128_MAX_BYTES && input > 0x7f {
+ return 0, 0, .Value_Too_Large
+ }
+
+ val = accumulator | i128(input & 0x7f) << shift
+
+ if input < 128 {
+ if input & 0x40 == 0x40 {
+ val |= max(i128) << (shift + 7)
+ }
+ return val, size, .None
+ }
+ return val, size, .Buffer_Too_Small
+}
+decode_ileb128 :: proc{decode_ileb128_buffer, decode_ileb128_byte}
+
+// Encode `val` into `buf` as an unsigned LEB128 encoded series of bytes.
+// `buf` must be appropriately sized.
+encode_uleb128 :: proc(buf: []u8, val: u128) -> (size: int, err: Error) {
+ val := val
+
+ for {
+ size += 1
+
+ if size > len(buf) {
+ return 0, .Buffer_Too_Small
+ }
+
+ low := val & 0x7f
+ val >>= 7
+
+ if val > 0 {
+ low |= 0x80 // more bytes to follow
+ }
+ buf[size - 1] = u8(low)
+
+ if val == 0 { break }
+ }
+ return
+}
+
+@(private)
+SIGN_MASK :: (i128(1) << 121) // sign extend mask
+
+// Encode `val` into `buf` as a signed LEB128 encoded series of bytes.
+// `buf` must be appropriately sized.
+encode_ileb128 :: proc(buf: []u8, val: i128) -> (size: int, err: Error) {
+ val := val
+ more := true
+
+ for more {
+ size += 1
+
+ if size > len(buf) {
+ return 0, .Buffer_Too_Small
+ }
+
+ low := val & 0x7f
+ val >>= 7
+
+ low = (low ~ SIGN_MASK) - SIGN_MASK
+
+ if (val == 0 && low & 0x40 != 0x40) || (val == -1 && low & 0x40 == 0x40) {
+ more = false
+ } else {
+ low |= 0x80
+ }
+
+ buf[size - 1] = u8(low)
+ }
+ return
+}
\ No newline at end of file
diff --git a/core/fmt/doc.odin b/core/fmt/doc.odin
index 5984da950..668fc9bc6 100644
--- a/core/fmt/doc.odin
+++ b/core/fmt/doc.odin
@@ -64,6 +64,7 @@ If not present, the width is whatever is necessary to represent the value.
Precision is specified after the (optional) width followed by a period followed by a decimal number.
If no period is present, a default precision is used.
A period with no following number specifies a precision of 0.
+
Examples:
%f default width, default precision
%8f width 8, default precision
@@ -84,7 +85,6 @@ Other flags:
add leading 0z for dozenal (%#z)
add leading 0x or 0X for hexadecimal (%#x or %#X)
remove leading 0x for %p (%#p)
-
' ' (space) leave a space for elided sign in numbers (% d)
0 pad with leading zeros rather than spaces
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index a9ff6ca47..d006d0ef8 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -11,6 +11,7 @@ import "core:time"
import "core:unicode/utf8"
import "core:intrinsics"
+// Internal data structure that stores the required information for formatted printing
Info :: struct {
minus: bool,
plus: bool,
@@ -31,6 +32,8 @@ Info :: struct {
writer: io.Writer,
arg: any, // Temporary
record_level: int,
+
+ n: int, // bytes written
}
// Custom formatter signature. It returns true if the formatting was successful and false when it could not be done
@@ -46,9 +49,13 @@ 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
set_user_formatters :: proc(m: ^map[typeid]User_Formatter) {
_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.
register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Register_User_Formatter_Error {
if _user_formatters == nil {
return .No_User_Formatter
@@ -61,7 +68,7 @@ register_user_formatter :: proc(id: typeid, formatter: User_Formatter) -> Regist
}
-// aprint* procedures return a string that was allocated with the current context
+// aprint procedure return a string that was allocated with the current context
// They must be freed accordingly
aprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
@@ -69,12 +76,16 @@ aprint :: proc(args: ..any, sep := " ") -> string {
sbprint(buf=&str, args=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
aprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.init_builder(&str)
sbprintln(buf=&str, args=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
aprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.init_builder(&str)
@@ -83,19 +94,21 @@ aprintf :: proc(fmt: string, args: ..any) -> string {
}
-// tprint* procedures return a string that was allocated with the current context's temporary allocator
+// tprint procedure return a string that was allocated with the current context's temporary allocator
tprint :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.init_builder(&str, context.temp_allocator)
sbprint(buf=&str, args=args, sep=sep)
return strings.to_string(str)
}
+// tprintln procedure return a string that was allocated with the current context's temporary allocator
tprintln :: proc(args: ..any, sep := " ") -> string {
str: strings.Builder
strings.init_builder(&str, context.temp_allocator)
sbprintln(buf=&str, args=args, sep=sep)
return strings.to_string(str)
}
+// tprintf procedure return a string that was allocated with the current context's temporary allocator
tprintf :: proc(fmt: string, args: ..any) -> string {
str: strings.Builder
strings.init_builder(&str, context.temp_allocator)
@@ -104,21 +117,24 @@ tprintf :: proc(fmt: string, args: ..any) -> string {
}
-// bprint* procedures return a string using a buffer from an array
+// bprint procedures return a string using a buffer from an array
bprint :: proc(buf: []byte, args: ..any, sep := " ") -> string {
sb := strings.builder_from_slice(buf[0:len(buf)])
return sbprint(buf=&sb, args=args, sep=sep)
}
+// bprintln procedures return a string using a buffer from an array
bprintln :: proc(buf: []byte, args: ..any, sep := " ") -> string {
sb := strings.builder_from_slice(buf[0:len(buf)])
return sbprintln(buf=&sb, args=args, sep=sep)
}
+// bprintf procedures return a string using a buffer from an array
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
sb := strings.builder_from_slice(buf[0:len(buf)])
return sbprintf(&sb, fmt, ..args)
}
+// formatted assert
assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_location) -> bool {
if !condition {
p := context.assertion_failure_proc
@@ -131,6 +147,7 @@ assertf :: proc(condition: bool, fmt: string, args: ..any, loc := #caller_locati
return condition
}
+// formatted panic
panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
p := context.assertion_failure_proc
if p == nil {
@@ -142,24 +159,26 @@ panicf :: proc(fmt: string, args: ..any, loc := #caller_location) -> ! {
-
-
+// sbprint formats using the default print settings and writes to buf
sbprint :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
wprint(w=strings.to_writer(buf), args=args, sep=sep)
return strings.to_string(buf^)
}
+// sbprintln formats using the default print settings and writes to buf
sbprintln :: proc(buf: ^strings.Builder, args: ..any, sep := " ") -> string {
wprintln(w=strings.to_writer(buf), args=args, sep=sep)
return strings.to_string(buf^)
}
+// sbprintf formats according to the specififed format string and writes to buf
sbprintf :: proc(buf: ^strings.Builder, fmt: string, args: ..any) -> string {
wprintf(w=strings.to_writer(buf), fmt=fmt, args=args)
return strings.to_string(buf^)
}
+// wprint formats using the default print settings and writes to w
wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
@@ -179,49 +198,42 @@ wprint :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
// so I am going to keep the same behaviour as `*println` for `*print`
- size0 := io.size(auto_cast w)
-
for _, i in args {
if i > 0 {
- io.write_string(fi.writer, sep)
+ io.write_string(fi.writer, sep, &fi.n)
}
fmt_value(&fi, args[i], 'v')
}
io.flush(auto_cast w)
- size1 := io.size(auto_cast w)
- return int(size1 - size0)
+ return fi.n
}
+// wprintln formats using the default print settings and writes to w
wprintln :: proc(w: io.Writer, args: ..any, sep := " ") -> int {
fi: Info
fi.writer = w
- size0 := io.size(auto_cast w)
-
for _, i in args {
if i > 0 {
- io.write_string(fi.writer, sep)
+ io.write_string(fi.writer, sep, &fi.n)
}
fmt_value(&fi, args[i], 'v')
}
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
io.flush(auto_cast w)
-
- size1 := io.size(auto_cast w)
- return int(size1 - size0)
+ return fi.n
}
+// wprintf formats according to the specififed format string and writes to w
wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi: Info
arg_index: int = 0
end := len(fmt)
was_prev_index := false
- size0 := io.size(auto_cast w)
-
loop: for i := 0; i < end; /**/ {
fi = Info{writer = w, good_arg_index = true, reordered = fi.reordered}
@@ -230,7 +242,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
i += 1
}
if i > prev_i {
- io.write_string(fi.writer, fmt[prev_i:i])
+ io.write_string(fi.writer, fmt[prev_i:i], &fi.n)
}
if i >= end {
break loop
@@ -245,13 +257,13 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
// Skip extra one
i += 1
}
- io.write_byte(fi.writer, char)
+ io.write_byte(fi.writer, char, &fi.n)
continue loop
} else if char == '{' {
if i < end && fmt[i] == char {
// Skip extra one
i += 1
- io.write_byte(fi.writer, char)
+ io.write_byte(fi.writer, char, &fi.n)
continue loop
}
}
@@ -282,7 +294,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
i += 1
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index)
if !fi.width_set {
- io.write_string(w, "%!(BAD WIDTH)")
+ io.write_string(w, "%!(BAD WIDTH)", &fi.n)
}
if fi.width < 0 {
@@ -313,7 +325,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi.prec_set = false
}
if !fi.prec_set {
- io.write_string(fi.writer, "%!(BAD PRECISION)")
+ io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
}
was_prev_index = false
} else {
@@ -326,7 +338,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
}
if i >= end {
- io.write_string(fi.writer, "%!(NO VERB)")
+ io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
break loop
}
@@ -335,11 +347,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
switch {
case verb == '%':
- io.write_byte(fi.writer, '%')
+ io.write_byte(fi.writer, '%', &fi.n)
case !fi.good_arg_index:
- io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)")
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
case arg_index >= len(args):
- io.write_string(fi.writer, "%!(MISSING ARGUMENT)")
+ io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
case:
fmt_arg(&fi, args[arg_index], verb)
arg_index += 1
@@ -355,14 +367,14 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
arg_index = new_arg_index
i = new_i
} else {
- io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ")
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER ", &fi.n)
// Skip over the bad argument
start_index := i
for i < end && fmt[i] != '}' && fmt[i] != ':' {
i += 1
}
fmt_arg(&fi, fmt[start_index:i], 'v')
- io.write_string(fi.writer, ")")
+ io.write_string(fi.writer, ")", &fi.n)
}
}
@@ -395,7 +407,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
i += 1
fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index)
if !fi.width_set {
- io.write_string(fi.writer, "%!(BAD WIDTH)")
+ io.write_string(fi.writer, "%!(BAD WIDTH)", &fi.n)
}
if fi.width < 0 {
@@ -426,7 +438,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
fi.prec_set = false
}
if !fi.prec_set {
- io.write_string(fi.writer, "%!(BAD PRECISION)")
+ io.write_string(fi.writer, "%!(BAD PRECISION)", &fi.n)
}
was_prev_index = false
} else {
@@ -440,7 +452,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
if i >= end {
- io.write_string(fi.writer, "%!(NO VERB)")
+ io.write_string(fi.writer, "%!(NO VERB)", &fi.n)
break loop
}
@@ -450,7 +462,7 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
}
if i >= end {
- io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)")
+ io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n)
break loop
}
@@ -459,11 +471,11 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
switch {
case brace != '}':
- io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)")
+ io.write_string(fi.writer, "%!(MISSING CLOSE BRACE)", &fi.n)
case !fi.good_arg_index:
- io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)")
+ io.write_string(fi.writer, "%!(BAD ARGUMENT NUMBER)", &fi.n)
case arg_index >= len(args):
- io.write_string(fi.writer, "%!(MISSING ARGUMENT)")
+ io.write_string(fi.writer, "%!(MISSING ARGUMENT)", &fi.n)
case:
fmt_arg(&fi, args[arg_index], verb)
arg_index += 1
@@ -472,32 +484,33 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any) -> int {
}
if !fi.reordered && arg_index < len(args) {
- io.write_string(fi.writer, "%!(EXTRA ")
+ io.write_string(fi.writer, "%!(EXTRA ", &fi.n)
for arg, index in args[arg_index:] {
if index > 0 {
- io.write_string(fi.writer, ", ")
+ io.write_string(fi.writer, ", ", &fi.n)
}
if arg == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
} else {
fmt_arg(&fi, args[index], 'v')
}
}
- io.write_string(fi.writer, ")")
+ io.write_string(fi.writer, ")", &fi.n)
}
io.flush(auto_cast w)
- size1 := io.size(auto_cast w)
- return int(size1 - size0)
+ return fi.n
}
+// wprint_type is a utility procedure to write a ^runtime.Type_Info value to w
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
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)
@@ -576,23 +589,23 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
- io.write_string(writer, "%!")
- io.write_rune(writer, verb)
- io.write_byte(writer, '(')
+ io.write_string(writer, "%!", &fi.n)
+ io.write_rune(writer, verb, &fi.n)
+ io.write_byte(writer, '(', &fi.n)
if arg.id != nil {
- reflect.write_typeid(writer, arg.id)
- io.write_byte(writer, '=')
+ reflect.write_typeid(writer, arg.id, &fi.n)
+ io.write_byte(writer, '=', &fi.n)
fmt_value(fi, arg, 'v')
} else {
- io.write_string(writer, "")
+ io.write_string(writer, "", &fi.n)
}
- io.write_byte(writer, ')')
+ io.write_byte(writer, ')', &fi.n)
}
fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
switch verb {
case 't', 'v':
- io.write_string(writer, b ? "true" : "false")
+ fmt_string(fi, b ? "true" : "false", 's')
case:
fmt_bad_verb(fi, verb)
}
@@ -610,7 +623,7 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
}
for i := 0; i < width; i += 1 {
- io.write_byte(fi.writer, pad_byte)
+ io.write_byte(fi.writer, pad_byte, &fi.n)
}
}
@@ -669,8 +682,8 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d
case 16: c = 'x'
}
if c != 0 {
- io.write_byte(fi.writer, '0')
- io.write_byte(fi.writer, c)
+ io.write_byte(fi.writer, '0', &fi.n)
+ io.write_byte(fi.writer, c, &fi.n)
}
}
@@ -735,8 +748,8 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i
case 16: c = 'x'
}
if c != 0 {
- io.write_byte(fi.writer, '0')
- io.write_byte(fi.writer, c)
+ io.write_byte(fi.writer, '0', &fi.n)
+ io.write_byte(fi.writer, c, &fi.n)
}
}
@@ -752,9 +765,9 @@ __DIGITS_UPPER := "0123456789ABCDEFX"
fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
switch verb {
case 'c', 'r', 'v':
- io.write_rune(fi.writer, r)
+ io.write_rune(fi.writer, r, &fi.n)
case 'q':
- strings.write_quoted_rune(fi.writer, r)
+ fi.n += strings.write_quoted_rune(fi.writer, r)
case:
fmt_int(fi, u64(r), false, 32, verb)
}
@@ -776,7 +789,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) {
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb)
} else {
- io.write_string(fi.writer, "U+")
+ io.write_string(fi.writer, "U+", &fi.n)
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_UPPER)
}
@@ -801,7 +814,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb)
} else {
- io.write_string(fi.writer, "U+")
+ io.write_string(fi.writer, "U+", &fi.n)
_fmt_int_128(fi, u, 16, false, bit_size, __DIGITS_UPPER)
}
@@ -812,24 +825,24 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru
_pad :: proc(fi: ^Info, s: string) {
if !fi.width_set {
- io.write_string(fi.writer, s)
+ io.write_string(fi.writer, s, &fi.n)
return
}
width := fi.width - utf8.rune_count_in_string(s)
if fi.minus { // right pad
- io.write_string(fi.writer, s)
+ io.write_string(fi.writer, s, &fi.n)
fmt_write_padding(fi, width)
} else { // left pad
fmt_write_padding(fi, width)
- io.write_string(fi.writer, s)
+ io.write_string(fi.writer, s, &fi.n)
}
}
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
- case 'f', 'F', 'v':
+ case 'f', 'F', 'g', 'G', 'v':
prec: int = 3
if fi.prec_set {
prec = fi.prec
@@ -849,15 +862,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- io.write_string(fi.writer, string(b))
+ 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])
+ 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:]))
+ io.write_string(fi.writer, string(b[1:]), &fi.n)
} else {
_pad(fi, string(b))
}
@@ -885,15 +898,15 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
}
if len(b) > 1 && (b[1] == 'N' || b[1] == 'I') {
- io.write_string(fi.writer, string(b))
+ 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])
+ 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:]))
+ io.write_string(fi.writer, string(b[1:]), &fi.n)
} else {
_pad(fi, string(b))
}
@@ -917,7 +930,7 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
case: panic("Unhandled float size")
}
- io.write_string(fi.writer, "0h")
+ io.write_string(fi.writer, "0h", &fi.n)
_fmt_int(fi, u, 16, false, bit_size, __DIGITS_LOWER if verb == 'h' else __DIGITS_UPPER)
@@ -930,15 +943,31 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
switch verb {
case 's', 'v':
- io.write_string(fi.writer, s)
- if fi.width_set && len(s) < fi.width {
- for _ in 0.. len(s) {
+ if fi.minus {
+ io.write_string(fi.writer, s, &fi.n)
+ }
+
+ for _ in 0.. 0 && space {
- io.write_byte(fi.writer, ' ')
+ io.write_byte(fi.writer, ' ', &fi.n)
}
char_set := __DIGITS_UPPER
if verb == 'x' {
@@ -969,7 +998,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
switch verb {
case 'p', 'v':
if !fi.hash || verb == 'v' {
- io.write_string(fi.writer, "0x")
+ io.write_string(fi.writer, "0x", &fi.n)
}
_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
@@ -1031,7 +1060,7 @@ string_to_enum_value :: proc($T: typeid, s: string) -> (T, bool) {
fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
if v.id == nil || v.data == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
@@ -1044,11 +1073,13 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
case 'i', 'd', 'f':
fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb)
case 's', 'v':
- str, ok := enum_value_to_string(v)
- if !ok {
- str = "%!(BAD ENUM VALUE)"
+ if str, ok := enum_value_to_string(v); ok {
+ fmt_string(fi, str, 's')
+ } else {
+ io.write_string(fi.writer, "%!(BAD ENUM VALUE=", &fi.n)
+ fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, 'i')
+ io.write_string(fi.writer, ")", &fi.n)
}
- io.write_string(fi.writer, str)
}
}
}
@@ -1092,8 +1123,8 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false
- case .Little: return ODIN_ENDIAN != "little"
- case .Big: return ODIN_ENDIAN != "big"
+ case .Little: return ODIN_ENDIAN != .Little
+ case .Big: return ODIN_ENDIAN != .Big
}
}
return false
@@ -1141,12 +1172,12 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
et := runtime.type_info_base(info.elem)
if name != "" {
- io.write_string(fi.writer, name)
+ io.write_string(fi.writer, name, &fi.n)
} else {
- reflect.write_type(fi.writer, type_info)
+ reflect.write_type(fi.writer, type_info, &fi.n)
}
- io.write_byte(fi.writer, '{')
- defer io.write_byte(fi.writer, '}')
+ io.write_byte(fi.writer, '{', &fi.n)
+ defer io.write_byte(fi.writer, '}', &fi.n)
e, is_enum := et.variant.(runtime.Type_Info_Enum)
commas := 0
@@ -1156,21 +1187,21 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
}
if commas > 0 {
- io.write_string(fi.writer, ", ")
+ io.write_string(fi.writer, ", ", &fi.n)
}
if is_enum {
for ev, evi in e.values {
v := u64(ev)
if v == u64(i) {
- io.write_string(fi.writer, e.names[evi])
+ io.write_string(fi.writer, e.names[evi], &fi.n)
commas += 1
continue loop
}
}
}
v := i64(i) + info.lower
- io.write_i64(fi.writer, v, 10)
+ io.write_i64(fi.writer, v, 10, &fi.n)
commas += 1
}
}
@@ -1178,20 +1209,20 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
fmt_write_indent :: proc(fi: ^Info) {
for in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(array_data) + uintptr(i*elem_size)
fmt_arg(fi, any{rawptr(data), elem_id}, verb)
@@ -1223,14 +1254,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
n -= 1
}
for in 0..")
+ io.write_string(fi.writer, "", &fi.n)
return
}
@@ -1256,12 +1287,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
// Built-in Custom Formatters for core library types
switch a in v {
case runtime.Source_Code_Location:
- io.write_string(fi.writer, a.file_path)
- io.write_byte(fi.writer, '(')
- io.write_int(fi.writer, int(a.line))
- io.write_byte(fi.writer, ':')
- io.write_int(fi.writer, int(a.column))
- io.write_byte(fi.writer, ')')
+ 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)
return
case time.Duration:
@@ -1315,7 +1346,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
w -= 1
switch {
case u == 0:
- io.write_string(fi.writer, "0s")
+ io.write_string(fi.writer, "0s", &fi.n)
return
case u < u64(time.Microsecond):
prec = 0
@@ -1354,7 +1385,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
w -= 1
buf[w] = '-'
}
- io.write_string(fi.writer, string(buf[w:]))
+ io.write_string(fi.writer, string(buf[w:]), &fi.n)
return
case time.Time:
@@ -1363,20 +1394,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
h, min, s := time.clock(t)
ns := (t._nsec - (t._nsec/1e9 + time.UNIX_TO_ABSOLUTE)*1e9) % 1e9
write_padded_number(fi, i64(y), 4)
- io.write_byte(fi.writer, '-')
+ io.write_byte(fi.writer, '-', &fi.n)
write_padded_number(fi, i64(mon), 2)
- io.write_byte(fi.writer, '-')
+ io.write_byte(fi.writer, '-', &fi.n)
write_padded_number(fi, i64(d), 2)
- io.write_byte(fi.writer, ' ')
+ io.write_byte(fi.writer, ' ', &fi.n)
write_padded_number(fi, i64(h), 2)
- io.write_byte(fi.writer, ':')
+ io.write_byte(fi.writer, ':', &fi.n)
write_padded_number(fi, i64(min), 2)
- io.write_byte(fi.writer, ':')
+ io.write_byte(fi.writer, ':', &fi.n)
write_padded_number(fi, i64(s), 2)
- io.write_byte(fi.writer, '.')
+ io.write_byte(fi.writer, '.', &fi.n)
write_padded_number(fi, (ns), 9)
- io.write_string(fi.writer, " +0000 UTC")
+ io.write_string(fi.writer, " +0000 UTC", &fi.n)
return
}
@@ -1387,15 +1418,15 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return
}
if b.is_raw_union {
- io.write_string(fi.writer, info.name)
- io.write_string(fi.writer, "{}")
+ io.write_string(fi.writer, info.name, &fi.n)
+ io.write_string(fi.writer, "{}", &fi.n)
return
}
is_soa := b.soa_kind != .None
- io.write_string(fi.writer, info.name)
- io.write_byte(fi.writer, '[' if is_soa else '{')
+ io.write_string(fi.writer, info.name, &fi.n)
+ io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n)
hash := fi.hash; defer fi.hash = hash
indent := fi.indent; defer fi.indent -= 1
@@ -1404,13 +1435,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fi.indent += 1
if hash {
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
}
defer {
if hash {
- for in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if !hash && index > 0 { io.write_string(fi.writer, ", ", &fi.n) }
field_count := -1
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
- io.write_string(fi.writer, base_type_name)
- io.write_byte(fi.writer, '{')
- defer io.write_byte(fi.writer, '}')
+ io.write_string(fi.writer, base_type_name, &fi.n)
+ io.write_byte(fi.writer, '{', &fi.n)
+ defer io.write_byte(fi.writer, '}', &fi.n)
for name, i in b.names {
field_count += 1
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
if hash {
fmt_write_indent(fi)
}
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, name, &fi.n)
+ io.write_string(fi.writer, " = ", &fi.n)
t := b.types[i].variant.(runtime.Type_Info_Array).elem
t_size := uintptr(t.size)
if reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
+ io.write_string(fi.writer, "any{}", &fi.n)
} else {
data := rawptr(uintptr(v.data) + b.offsets[i] + index*t_size)
fmt_arg(fi, any{data, t.id}, 'v')
}
- if hash { io.write_string(fi.writer, ",\n") }
+ if hash { io.write_string(fi.writer, ",\n", &fi.n) }
}
}
} else {
@@ -1466,17 +1497,17 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_write_indent(fi)
}
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, name, &fi.n)
+ io.write_string(fi.writer, " = ", &fi.n)
if t := b.types[i]; reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
+ io.write_string(fi.writer, "any{}", &fi.n)
} else {
data := rawptr(uintptr(v.data) + b.offsets[i])
fmt_arg(fi, any{data, t.id}, 'v')
}
- if hash { io.write_string(fi.writer, ",\n") }
+ if hash { io.write_string(fi.writer, ",\n", &fi.n) }
}
}
@@ -1496,7 +1527,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Pointer:
if v.id == typeid_of(^runtime.Type_Info) {
- reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^)
+ reflect.write_type(fi.writer, (^^runtime.Type_Info)(v.data)^, &fi.n)
} else {
ptr := (^rawptr)(v.data)^
if verb != 'p' && info.elem != nil {
@@ -1510,7 +1541,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
if ptr == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
if fi.record_level < 1 {
@@ -1524,13 +1555,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
if ptr == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
if fi.record_level < 1 {
fi.record_level += 1
defer fi.record_level -= 1
- io.write_byte(fi.writer, '&')
+ io.write_byte(fi.writer, '&', &fi.n)
fmt_value(fi, a, verb)
return
}
@@ -1553,13 +1584,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
if ptr == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
if fi.record_level < 1 {
fi.record_level += 1
defer fi.record_level -= 1
- io.write_byte(fi.writer, '&')
+ io.write_byte(fi.writer, '&', &fi.n)
fmt_value(fi, a, verb)
return
}
@@ -1567,13 +1598,13 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
if ptr == nil {
- io.write_string(fi.writer, "")
+ io.write_string(fi.writer, "", &fi.n)
return
}
if fi.record_level < 1 {
fi.record_level += 1
defer fi.record_level -= 1
- io.write_byte(fi.writer, '&')
+ io.write_byte(fi.writer, '&', &fi.n)
fmt_value(fi, a, verb)
return
}
@@ -1592,11 +1623,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Enumerated_Array:
if fi.hash {
- io.write_string(fi.writer, "[\n")
+ io.write_string(fi.writer, "[\n", &fi.n)
defer {
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
fmt_write_indent(fi)
- io.write_byte(fi.writer, ']')
+ io.write_byte(fi.writer, ']', &fi.n)
}
indent := fi.indent
fi.indent += 1
@@ -1607,32 +1638,32 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i)
if ok {
- io.write_byte(fi.writer, '.')
- io.write_string(fi.writer, idx)
+ io.write_byte(fi.writer, '.', &fi.n)
+ io.write_string(fi.writer, idx, &fi.n)
} else {
- io.write_i64(fi.writer, i64(info.min_value)+i64(i))
+ io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n)
}
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, " = ", &fi.n)
data := uintptr(v.data) + uintptr(i*info.elem_size)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
- io.write_string(fi.writer, ",\n")
+ io.write_string(fi.writer, ",\n", &fi.n)
}
} else {
- io.write_byte(fi.writer, '[')
- defer io.write_byte(fi.writer, ']')
+ io.write_byte(fi.writer, '[', &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
idx, ok := stored_enum_value_to_string(info.index, info.min_value, i)
if ok {
- io.write_byte(fi.writer, '.')
- io.write_string(fi.writer, idx)
+ io.write_byte(fi.writer, '.', &fi.n)
+ io.write_string(fi.writer, idx, &fi.n)
} else {
- io.write_i64(fi.writer, i64(info.min_value)+i64(i))
+ io.write_i64(fi.writer, i64(info.min_value)+i64(i), 10, &fi.n)
}
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, " = ", &fi.n)
data := uintptr(v.data) + uintptr(i*info.elem_size)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
@@ -1651,10 +1682,10 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
case runtime.Type_Info_Simd_Vector:
- io.write_byte(fi.writer, '<')
- defer io.write_byte(fi.writer, '>')
+ io.write_byte(fi.writer, '<', &fi.n)
+ defer io.write_byte(fi.writer, '>', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(v.data) + uintptr(i*info.elem_size)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
@@ -1677,8 +1708,8 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
return
}
- io.write_string(fi.writer, "map[")
- defer io.write_byte(fi.writer, ']')
+ io.write_string(fi.writer, "map[", &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
m := (^mem.Raw_Map)(v.data)
if m != nil {
@@ -1692,14 +1723,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
entry_size := ed.elem_size
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(entries.data) + uintptr(i*entry_size)
key := data + entry_type.offsets[2]
fmt_arg(&Info{writer = fi.writer}, any{rawptr(key), info.key.id}, 'v')
- io.write_string(fi.writer, "=")
+ io.write_string(fi.writer, "=", &fi.n)
value := data + entry_type.offsets[3]
fmt_arg(fi, any{rawptr(value), info.value.id}, 'v')
@@ -1708,21 +1739,21 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Struct:
if info.is_raw_union {
- io.write_string(fi.writer, "(raw_union)")
+ io.write_string(fi.writer, "(raw_union)", &fi.n)
return
}
is_soa := info.soa_kind != .None
- io.write_byte(fi.writer, '[' if is_soa else '{')
- defer io.write_byte(fi.writer, ']' if is_soa else '}')
+ io.write_byte(fi.writer, '[' if is_soa else '{', &fi.n)
+ defer io.write_byte(fi.writer, ']' if is_soa else '}', &fi.n)
fi.indent += 1; defer fi.indent -= 1
hash := fi.hash; defer fi.hash = hash
// fi.hash = false;
- if hash { io.write_byte(fi.writer, '\n') }
+ if hash { io.write_byte(fi.writer, '\n', &fi.n) }
if is_soa {
fi.indent += 1
@@ -1751,33 +1782,33 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
for index in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if !hash && index > 0 { io.write_string(fi.writer, ", ", &fi.n) }
field_count := -1
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
- io.write_string(fi.writer, base_type_name)
- io.write_byte(fi.writer, '{')
- defer io.write_byte(fi.writer, '}')
+ io.write_string(fi.writer, base_type_name, &fi.n)
+ io.write_byte(fi.writer, '{', &fi.n)
+ defer io.write_byte(fi.writer, '}', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
if hash {
fmt_write_indent(fi)
}
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, name, &fi.n)
+ io.write_string(fi.writer, " = ", &fi.n)
if info.soa_kind == .Fixed {
t := info.types[i].variant.(runtime.Type_Info_Array).elem
t_size := uintptr(t.size)
if reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
+ io.write_string(fi.writer, "any{}", &fi.n)
} else {
data := rawptr(uintptr(v.data) + info.offsets[i] + index*t_size)
fmt_arg(fi, any{data, t.id}, 'v')
@@ -1786,7 +1817,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
t := info.types[i].variant.(runtime.Type_Info_Pointer).elem
t_size := uintptr(t.size)
if reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
+ io.write_string(fi.writer, "any{}", &fi.n)
} else {
field_ptr := (^^byte)(uintptr(v.data) + info.offsets[i])^
data := rawptr(uintptr(field_ptr) + index*t_size)
@@ -1794,7 +1825,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
}
- if hash { io.write_string(fi.writer, ",\n") }
+ if hash { io.write_string(fi.writer, ",\n", &fi.n) }
}
}
} else {
@@ -1802,23 +1833,23 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
for name, i in info.names {
field_count += 1
- if !hash && field_count > 0 { io.write_string(fi.writer, ", ") }
+ if !hash && field_count > 0 { io.write_string(fi.writer, ", ", &fi.n) }
if hash {
fmt_write_indent(fi)
}
- io.write_string(fi.writer, name)
- io.write_string(fi.writer, " = ")
+ io.write_string(fi.writer, name, &fi.n)
+ io.write_string(fi.writer, " = ", &fi.n)
if t := info.types[i]; reflect.is_any(t) {
- io.write_string(fi.writer, "any{}")
+ io.write_string(fi.writer, "any{}", &fi.n)
} else {
data := rawptr(uintptr(v.data) + info.offsets[i])
fmt_arg(fi, any{data, t.id}, 'v')
}
if hash {
- io.write_string(fi.writer, ",\n")
+ io.write_string(fi.writer, ",\n", &fi.n)
}
}
}
@@ -1826,14 +1857,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Union:
if type_info.size == 0 {
- io.write_string(fi.writer, "nil")
+ io.write_string(fi.writer, "nil", &fi.n)
return
}
if reflect.type_info_union_is_pure_maybe(info) {
if v.data == nil {
- io.write_string(fi.writer, "nil")
+ io.write_string(fi.writer, "nil", &fi.n)
} else {
id := info.variants[0].id
fmt_arg(fi, any{v.data, id}, verb)
@@ -1859,12 +1890,12 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
assert(tag >= 0)
if v.data == nil {
- io.write_string(fi.writer, "nil")
+ io.write_string(fi.writer, "nil", &fi.n)
} else if info.no_nil {
id := info.variants[tag].id
fmt_arg(fi, any{v.data, id}, verb)
} else if tag == 0 {
- io.write_string(fi.writer, "nil")
+ io.write_string(fi.writer, "nil", &fi.n)
} else {
id := info.variants[tag-1].id
fmt_arg(fi, any{v.data, id}, verb)
@@ -1876,16 +1907,16 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Procedure:
ptr := (^rawptr)(v.data)^
if ptr == nil {
- io.write_string(fi.writer, "nil")
+ io.write_string(fi.writer, "nil", &fi.n)
} else {
- reflect.write_typeid(fi.writer, v.id)
- io.write_string(fi.writer, " @ ")
+ reflect.write_typeid(fi.writer, v.id, &fi.n)
+ io.write_string(fi.writer, " @ ", &fi.n)
fmt_pointer(fi, ptr, 'p')
}
case runtime.Type_Info_Type_Id:
id := (^typeid)(v.data)^
- reflect.write_typeid(fi.writer, id)
+ reflect.write_typeid(fi.writer, id, &fi.n)
case runtime.Type_Info_Bit_Set:
fmt_bit_set(fi, v)
@@ -1902,18 +1933,18 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if verb == 'p' {
fmt_pointer(fi, ptr, 'p')
} else if ptr == nil {
- io.write_string(fi.writer, "[]")
+ io.write_string(fi.writer, "[]", &fi.n)
} else {
len_ptr := uintptr(v.data) + uintptr(info.base_integer.size)
len_any := any{rawptr(len_ptr), info.base_integer.id}
len, _ := reflect.as_int(len_any)
slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice)
- io.write_byte(fi.writer, '[')
- defer io.write_byte(fi.writer, ']')
+ io.write_byte(fi.writer, '[', &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
for i in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(ptr) + uintptr(i*slice_type.elem_size)
fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb)
@@ -1921,32 +1952,32 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
case runtime.Type_Info_Matrix:
- io.write_string(fi.writer, "matrix[")
- defer io.write_byte(fi.writer, ']')
+ io.write_string(fi.writer, "matrix[", &fi.n)
+ defer io.write_byte(fi.writer, ']', &fi.n)
fi.indent += 1
if fi.hash {
// Printed as it is written
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
for row in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
offset := (row + col*info.elem_stride)*info.elem_size
data := uintptr(v.data) + uintptr(offset)
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb)
}
- io.write_string(fi.writer, ",\n")
+ io.write_string(fi.writer, ",\n", &fi.n)
}
} else {
// Printed in Row-Major layout to match text layout
for row in 0.. 0 { io.write_string(fi.writer, "; ") }
+ if row > 0 { io.write_string(fi.writer, "; ", &fi.n) }
for col in 0.. 0 { io.write_string(fi.writer, ", ") }
+ if col > 0 { io.write_string(fi.writer, ", ", &fi.n) }
offset := (row + col*info.elem_stride)*info.elem_size
@@ -1970,10 +2001,10 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
r, i := real(c), imag(c)
fmt_float(fi, r, bits/2, verb)
if !fi.plus && i >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, i, bits/2, verb)
- io.write_rune(fi.writer, 'i')
+ io.write_rune(fi.writer, 'i', &fi.n)
case:
fmt_bad_verb(fi, verb)
@@ -1989,22 +2020,22 @@ fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) {
fmt_float(fi, r, bits/4, verb)
if !fi.plus && i >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, i, bits/4, verb)
- io.write_rune(fi.writer, 'i')
+ io.write_rune(fi.writer, 'i', &fi.n)
if !fi.plus && j >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, j, bits/4, verb)
- io.write_rune(fi.writer, 'j')
+ io.write_rune(fi.writer, 'j', &fi.n)
if !fi.plus && k >= 0 {
- io.write_rune(fi.writer, '+')
+ io.write_rune(fi.writer, '+', &fi.n)
}
fmt_float(fi, k, bits/4, verb)
- io.write_rune(fi.writer, 'k')
+ io.write_rune(fi.writer, 'k', &fi.n)
case:
fmt_bad_verb(fi, verb)
@@ -2024,7 +2055,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
switch a in arg {
case ^runtime.Type_Info: ti = a
}
- reflect.write_type(fi.writer, ti)
+ reflect.write_type(fi.writer, ti, &fi.n)
return
}
@@ -2042,12 +2073,12 @@ 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)
- io.write_byte(fi.writer, '(')
- io.write_i64(fi.writer, i64(a.line), 10)
- io.write_byte(fi.writer, ':')
- io.write_i64(fi.writer, i64(a.column), 10)
- io.write_byte(fi.writer, ')')
+ 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
}
}
@@ -2099,7 +2130,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
case string: fmt_string(fi, a, verb)
case cstring: fmt_cstring(fi, a, verb)
- case typeid: reflect.write_typeid(fi.writer, a)
+ case typeid: reflect.write_typeid(fi.writer, a, &fi.n)
case i16le: fmt_int(fi, u64(a), true, 16, verb)
case u16le: fmt_int(fi, u64(a), false, 16, verb)
diff --git a/core/fmt/fmt_js.odin b/core/fmt/fmt_js.odin
index bcd9688a1..7a9876127 100644
--- a/core/fmt/fmt_js.odin
+++ b/core/fmt/fmt_js.odin
@@ -34,11 +34,16 @@ stderr := io.Writer{
},
}
-// print* procedures return the number of bytes written
+// print formats using the default print settings and writes to stdout
print :: proc(args: ..any, sep := " ") -> int { return wprint(w=stdout, args=args, sep=sep) }
+// println formats using the default print settings and writes to stdout
println :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stdout, args=args, sep=sep) }
+// printf formats according to the specififed format string and writes to stdout
printf :: proc(fmt: string, args: ..any) -> int { return wprintf(stdout, fmt, ..args) }
+// eprint formats using the default print settings and writes to stderr
eprint :: proc(args: ..any, sep := " ") -> int { return wprint(w=stderr, args=args, sep=sep) }
+// eprintln formats using the default print settings and writes to stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return wprintln(w=stderr, args=args, sep=sep) }
+// eprintf formats according to the specififed format string and writes to stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return wprintf(stderr, fmt, ..args) }
diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin
index 7434d939d..f5c8d75bd 100644
--- a/core/fmt/fmt_os.odin
+++ b/core/fmt/fmt_os.odin
@@ -5,15 +5,18 @@ import "core:runtime"
import "core:os"
import "core:io"
+// 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)
}
+// 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)
}
+// fprintf formats according to the specififed format string and writes to fd
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
w := io.to_writer(os.stream_from_handle(fd))
return wprintf(w, fmt, ..args)
@@ -27,11 +30,16 @@ fprint_typeid :: proc(fd: os.Handle, id: typeid) -> (n: int, err: io.Error) {
return wprint_typeid(w, id)
}
-// print* procedures return the number of bytes written
+// 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) }
+// println formats using the default print settings and writes to os.stdout
println :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stdout, args=args, sep=sep) }
+// printf formats according to the specififed format string and writes to os.stdout
printf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stdout, fmt, ..args) }
+// eprint formats using the default print settings and writes to os.stderr
eprint :: proc(args: ..any, sep := " ") -> int { return fprint(fd=os.stderr, args=args, sep=sep) }
+// eprintln formats using the default print settings and writes to os.stderr
eprintln :: proc(args: ..any, sep := " ") -> int { return fprintln(fd=os.stderr, args=args, sep=sep) }
+// eprintf formats according to the specififed format string and writes to os.stderr
eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(os.stderr, fmt, ..args) }
diff --git a/core/hash/hash.odin b/core/hash/hash.odin
index f0d01bd25..f2152f1b6 100644
--- a/core/hash/hash.odin
+++ b/core/hash/hash.odin
@@ -55,6 +55,23 @@ djb2 :: proc(data: []byte, seed := u32(5381)) -> u32 {
return hash
}
+djbx33a :: proc(data: []byte, seed := u32(5381)) -> (result: [16]byte) #no_bounds_check {
+ state := [4]u32{seed, seed, seed, seed}
+
+ s: u32 = 0
+ for p in data {
+ state[s] = (state[s] << 5) + state[s] + u32(p) // hash * 33 + u32(b)
+ s = (s + 1) & 3
+ }
+
+
+ (^u32le)(&result[0])^ = u32le(state[0])
+ (^u32le)(&result[4])^ = u32le(state[1])
+ (^u32le)(&result[8])^ = u32le(state[2])
+ (^u32le)(&result[12])^ = u32le(state[3])
+ return
+}
+
@(optimization_mode="speed")
fnv32 :: proc(data: []byte, seed := u32(0x811c9dc5)) -> u32 {
h: u32 = seed
@@ -134,7 +151,7 @@ murmur32 :: proc(data: []byte, seed := u32(0)) -> u32 {
k1 ~= u32(tail[2]) << 16
fallthrough
case 2:
- k1 ~= u32(tail[2]) << 8
+ k1 ~= u32(tail[1]) << 8
fallthrough
case 1:
k1 ~= u32(tail[0])
diff --git a/core/hash/xxhash/streaming.odin b/core/hash/xxhash/streaming.odin
index 737e37eae..d6df1089f 100644
--- a/core/hash/xxhash/streaming.odin
+++ b/core/hash/xxhash/streaming.odin
@@ -96,7 +96,7 @@ XXH3_128_canonical_from_hash :: proc(hash: XXH128_hash_t) -> (canonical: XXH128_
#assert(size_of(XXH128_canonical) == size_of(XXH128_hash_t))
t := hash
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
t.high = byte_swap(t.high)
t.low = byte_swap(t.low)
}
diff --git a/core/image/common.odin b/core/image/common.odin
index 3ec8e15be..8c77ec48a 100644
--- a/core/image/common.odin
+++ b/core/image/common.odin
@@ -6,6 +6,8 @@
Jeroen van Rijn: Initial implementation, optimization.
Ginger Bill: Cosmetic changes.
*/
+
+// package image implements a general 2D image library to be used with other image related packages
package image
import "core:bytes"
@@ -13,6 +15,32 @@ import "core:mem"
import "core:compress"
import "core:runtime"
+/*
+ 67_108_864 pixels max by default.
+
+ For QOI, the Worst case scenario means all pixels will be encoded as RGBA literals, costing 5 bytes each.
+ This caps memory usage at 320 MiB.
+
+ The tunable is limited to 4_294_836_225 pixels maximum, or 4 GiB per 8-bit channel.
+ It is not advised to tune it this large.
+
+ The 64 Megapixel default is considered to be a decent upper bound you won't run into in practice,
+ except in very specific circumstances.
+
+*/
+MAX_DIMENSIONS :: min(#config(MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
+
+// Color
+RGB_Pixel :: [3]u8
+RGBA_Pixel :: [4]u8
+RGB_Pixel_16 :: [3]u16
+RGBA_Pixel_16 :: [4]u16
+// Grayscale
+G_Pixel :: [1]u8
+GA_Pixel :: [2]u8
+G_Pixel_16 :: [1]u16
+GA_Pixel_16 :: [2]u16
+
Image :: struct {
width: int,
height: int,
@@ -24,15 +52,17 @@ Image :: struct {
For convenience, we return them as u16 so we don't need to switch on the type
in our viewer, and can just test against nil.
*/
- background: Maybe([3]u16),
-
+ background: Maybe(RGB_Pixel_16),
metadata: Image_Metadata,
}
Image_Metadata :: union {
^PNG_Info,
+ ^QOI_Info,
}
+
+
/*
IMPORTANT: `.do_not_expand_*` options currently skip handling of the `alpha_*` options,
therefore Gray+Alpha will be returned as such even if you add `.alpha_drop_if_present`,
@@ -44,13 +74,13 @@ Image_Metadata :: union {
/*
Image_Option:
`.info`
- This option behaves as `.return_ihdr` and `.do_not_decompress_image` and can be used
+ This option behaves as `.return_metadata` and `.do_not_decompress_image` and can be used
to gather an image's dimensions and color information.
`.return_header`
- Fill out img.sidecar.header with the image's format-specific header struct.
+ Fill out img.metadata.header with the image's format-specific header struct.
If we only care about the image specs, we can set `.return_header` +
- `.do_not_decompress_image`, or `.info`, which works as if both of these were set.
+ `.do_not_decompress_image`, or `.info`.
`.return_metadata`
Returns all chunks not needed to decode the data.
@@ -86,7 +116,7 @@ Image_Option:
`.alpha_premultiply`
If the image has an alpha channel, returns image data as follows:
- RGB *= A, Gray = Gray *= A
+ RGB *= A, Gray = Gray *= A
`.blend_background`
If a bKGD chunk is present in a PNG, we normally just set `img.background`
@@ -101,24 +131,29 @@ Image_Option:
*/
Option :: enum {
+ // LOAD OPTIONS
info = 0,
do_not_decompress_image,
return_header,
return_metadata,
- alpha_add_if_missing,
- alpha_drop_if_present,
- alpha_premultiply,
- blend_background,
+ alpha_add_if_missing, // Ignored for QOI. Always returns RGBA8.
+ alpha_drop_if_present, // Unimplemented for QOI. Returns error.
+ alpha_premultiply, // Unimplemented for QOI. Returns error.
+ blend_background, // Ignored for non-PNG formats
// Unimplemented
do_not_expand_grayscale,
do_not_expand_indexed,
do_not_expand_channels,
+
+ // SAVE OPTIONS
+ qoi_all_channels_linear, // QOI, informative info. If not set, defaults to sRGB with linear alpha.
}
Options :: distinct bit_set[Option]
-Error :: union {
+Error :: union #shared_nil {
General_Image_Error,
PNG_Error,
+ QOI_Error,
compress.Error,
compress.General_Error,
@@ -132,9 +167,15 @@ General_Image_Error :: enum {
Invalid_Image_Dimensions,
Image_Dimensions_Too_Large,
Image_Does_Not_Adhere_to_Spec,
+ Invalid_Input_Image,
+ Invalid_Output,
}
+/*
+ PNG-specific definitions
+*/
PNG_Error :: enum {
+ None = 0,
Invalid_PNG_Signature,
IHDR_Not_First_Chunk,
IHDR_Corrupt,
@@ -144,7 +185,9 @@ PNG_Error :: enum {
IDAT_Size_Too_Large,
PLTE_Encountered_Unexpectedly,
PLTE_Invalid_Length,
+ PLTE_Missing,
TRNS_Encountered_Unexpectedly,
+ TNRS_Invalid_Length,
BKGD_Invalid_Length,
Unknown_Color_Type,
Invalid_Color_Bit_Depth_Combo,
@@ -155,9 +198,6 @@ PNG_Error :: enum {
Invalid_Chunk_Length,
}
-/*
- PNG-specific structs
-*/
PNG_Info :: struct {
header: PNG_IHDR,
chunks: [dynamic]PNG_Chunk,
@@ -220,7 +260,7 @@ PNG_Chunk_Type :: enum u32be {
*/
iDOT = 'i' << 24 | 'D' << 16 | 'O' << 8 | 'T',
- CbGI = 'C' << 24 | 'b' << 16 | 'H' << 8 | 'I',
+ CgBI = 'C' << 24 | 'g' << 16 | 'B' << 8 | 'I',
}
PNG_IHDR :: struct #packed {
@@ -248,16 +288,58 @@ PNG_Interlace_Method :: enum u8 {
}
/*
- Functions to help with image buffer calculations
+ QOI-specific definitions
*/
+QOI_Error :: enum {
+ None = 0,
+ Invalid_QOI_Signature,
+ Invalid_Number_Of_Channels, // QOI allows 3 or 4 channel data.
+ Invalid_Bit_Depth, // QOI supports only 8-bit images, error only returned from writer.
+ Invalid_Color_Space, // QOI allows 0 = sRGB or 1 = linear.
+ Corrupt, // More data than pixels to decode into, for example.
+ Missing_Or_Corrupt_Trailer, // Image seemed to have decoded okay, but trailer is missing or corrupt.
+}
+
+QOI_Magic :: u32be(0x716f6966) // "qoif"
+
+QOI_Color_Space :: enum u8 {
+ sRGB = 0,
+ Linear = 1,
+}
+
+QOI_Header :: struct #packed {
+ magic: u32be,
+ width: u32be,
+ height: u32be,
+ channels: u8,
+ color_space: QOI_Color_Space,
+}
+#assert(size_of(QOI_Header) == 14)
+
+QOI_Info :: struct {
+ header: QOI_Header,
+}
+
+TGA_Header :: struct #packed {
+ id_length: u8,
+ color_map_type: u8,
+ data_type_code: u8,
+ color_map_origin: u16le,
+ color_map_length: u16le,
+ color_map_depth: u8,
+ origin: [2]u16le,
+ dimensions: [2]u16le,
+ bits_per_pixel: u8,
+ image_descriptor: u8,
+}
+#assert(size_of(TGA_Header) == 18)
+
+// Function to help with image buffer calculations
compute_buffer_size :: proc(width, height, channels, depth: int, extra_row_bytes := int(0)) -> (size: int) {
size = ((((channels * width * depth) + 7) >> 3) + extra_row_bytes) * height
return
}
-/*
- For when you have an RGB(A) image, but want a particular channel.
-*/
Channel :: enum u8 {
R = 1,
G = 2,
@@ -265,7 +347,13 @@ Channel :: enum u8 {
A = 4,
}
+// When you have an RGB(A) image, but want a particular channel.
return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return nil, false
+ }
+
ok = false
t: bytes.Buffer
@@ -295,7 +383,7 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
o = o[1:]
}
case 16:
- buffer_size := compute_buffer_size(img.width, img.height, 2, 8)
+ buffer_size := compute_buffer_size(img.width, img.height, 1, 16)
t = bytes.Buffer{}
resize(&t.buf, buffer_size)
@@ -323,3 +411,724 @@ return_single_channel :: proc(img: ^Image, channel: Channel) -> (res: ^Image, ok
return res, true
}
+
+// Does the image have 1 or 2 channels, a valid bit depth (8 or 16),
+// Is the pointer valid, are the dimenions valid?
+is_valid_grayscale_image :: proc(img: ^Image) -> (ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return false
+ }
+
+ // Are we a Gray or Gray + Alpha image?
+ if img.channels != 1 && img.channels != 2 {
+ return false
+ }
+
+ // Do we have an acceptable bit depth?
+ if img.depth != 8 && img.depth != 16 {
+ return false
+ }
+
+ // This returns 0 if any of the inputs is zero.
+ bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
+
+ // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ return true
+}
+
+// Does the image have 3 or 4 channels, a valid bit depth (8 or 16),
+// Is the pointer valid, are the dimenions valid?
+is_valid_color_image :: proc(img: ^Image) -> (ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return false
+ }
+
+ // Are we an RGB or RGBA image?
+ if img.channels != 3 && img.channels != 4 {
+ return false
+ }
+
+ // Do we have an acceptable bit depth?
+ if img.depth != 8 && img.depth != 16 {
+ return false
+ }
+
+ // This returns 0 if any of the inputs is zero.
+ bytes_expected := compute_buffer_size(img.width, img.height, img.channels, img.depth)
+
+ // If the dimenions are invalid or the buffer size doesn't match the image characteristics, bail.
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ return true
+}
+
+// Does the image have 1..4 channels, a valid bit depth (8 or 16),
+// Is the pointer valid, are the dimenions valid?
+is_valid_image :: proc(img: ^Image) -> (ok: bool) {
+ // Were we actually given a valid image?
+ if img == nil {
+ return false
+ }
+
+ return is_valid_color_image(img) || is_valid_grayscale_image(img)
+}
+
+Alpha_Key :: union {
+ GA_Pixel,
+ RGBA_Pixel,
+ GA_Pixel_16,
+ RGBA_Pixel_16,
+}
+
+/*
+ Add alpha channel if missing, in-place.
+
+ Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
+ Any other number of channels will be considered an error, returning `false` without modifying the image.
+ If the input image already has an alpha channel, it'll return `true` early (without considering optional keyed alpha).
+
+ If an image doesn't already have an alpha channel:
+ If the optional `alpha_key` is provided, it will be resolved as follows:
+ - For RGB, if pix = key.rgb -> pix = {0, 0, 0, key.a}
+ - For Gray, if pix = key.r -> pix = {0, key.g}
+ Otherwise, an opaque alpha channel will be added.
+*/
+alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if !is_valid_image(img) {
+ return false
+ }
+
+ // We should now have a valid Image with 1..4 channels. Do we already have alpha?
+ if img.channels == 2 || img.channels == 4 {
+ // We're done.
+ return true
+ }
+
+ channels := img.channels + 1
+ bytes_wanted := compute_buffer_size(img.width, img.height, channels, img.depth)
+
+ buf := bytes.Buffer{}
+
+ // Can we allocate the return buffer?
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ switch img.depth {
+ case 8:
+ switch channels {
+ case 2:
+ // Turn Gray into Gray + Alpha
+ inp := mem.slice_data_cast([]G_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]GA_Pixel, buf.buf[:])
+
+ if key, key_ok := alpha_key.(GA_Pixel); key_ok {
+ // We have keyed alpha.
+ o: GA_Pixel
+ for p in inp {
+ if p == key.r {
+ o = GA_Pixel{0, key.g}
+ } else {
+ o = GA_Pixel{p.r, 255}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := GA_Pixel{0, 255}
+ for p in inp {
+ o.r = p.r
+ out[0] = o
+ out = out[1:]
+ }
+ }
+
+ case 4:
+ // Turn RGB into RGBA
+ inp := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
+
+ if key, key_ok := alpha_key.(RGBA_Pixel); key_ok {
+ // We have keyed alpha.
+ o: RGBA_Pixel
+ for p in inp {
+ if p == key.rgb {
+ o = RGBA_Pixel{0, 0, 0, key.a}
+ } else {
+ o = RGBA_Pixel{p.r, p.g, p.b, 255}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := RGBA_Pixel{0, 0, 0, 255}
+ for p in inp {
+ o.rgb = p
+ out[0] = o
+ out = out[1:]
+ }
+ }
+ case:
+ // We shouldn't get here.
+ unreachable()
+ }
+ case 16:
+ switch channels {
+ case 2:
+ // Turn Gray into Gray + Alpha
+ inp := mem.slice_data_cast([]G_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]GA_Pixel_16, buf.buf[:])
+
+ if key, key_ok := alpha_key.(GA_Pixel_16); key_ok {
+ // We have keyed alpha.
+ o: GA_Pixel_16
+ for p in inp {
+ if p == key.r {
+ o = GA_Pixel_16{0, key.g}
+ } else {
+ o = GA_Pixel_16{p.r, 65535}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := GA_Pixel_16{0, 65535}
+ for p in inp {
+ o.r = p.r
+ out[0] = o
+ out = out[1:]
+ }
+ }
+
+ case 4:
+ // Turn RGB into RGBA
+ inp := mem.slice_data_cast([]RGB_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
+
+ if key, key_ok := alpha_key.(RGBA_Pixel_16); key_ok {
+ // We have keyed alpha.
+ o: RGBA_Pixel_16
+ for p in inp {
+ if p == key.rgb {
+ o = RGBA_Pixel_16{0, 0, 0, key.a}
+ } else {
+ o = RGBA_Pixel_16{p.r, p.g, p.b, 65535}
+ }
+ out[0] = o
+ out = out[1:]
+ }
+ } else {
+ // No keyed alpha, just make all pixels opaque.
+ o := RGBA_Pixel_16{0, 0, 0, 65535}
+ for p in inp {
+ o.rgb = p
+ out[0] = o
+ out = out[1:]
+ }
+ }
+ case:
+ // We shouldn't get here.
+ unreachable()
+ }
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel added.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = channels
+ return true
+}
+alpha_apply_keyed_alpha :: alpha_add_if_missing
+
+/*
+ Drop alpha channel if present, in-place.
+
+ Expects 1..4 channels (Gray, Gray + Alpha, RGB, RGBA).
+ Any other number of channels will be considered an error, returning `false` without modifying the image.
+
+ Of the `options`, the following are considered:
+ `.alpha_premultiply`
+ If the image has an alpha channel, returns image data as follows:
+ RGB *= A, Gray = Gray *= A
+
+ `.blend_background`
+ If `img.background` is set, it'll be blended in like this:
+ RGB = (1 - A) * Background + A * RGB
+
+ If an image has 1 (Gray) or 3 (RGB) channels, it'll return early without modifying the image,
+ with one exception: `alpha_key` and `img.background` are present, and `.blend_background` is set.
+
+ In this case a keyed alpha pixel will be replaced with the background color.
+*/
+alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Alpha_Key{}, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if !is_valid_image(img) {
+ return false
+ }
+
+ // Do we have a background to blend?
+ will_it_blend := false
+ switch v in img.background {
+ case RGB_Pixel_16: will_it_blend = true if .blend_background in options else false
+ }
+
+ // Do we have keyed alpha?
+ keyed := false
+ switch v in alpha_key {
+ case GA_Pixel: keyed = true if img.channels == 1 && img.depth == 8 else false
+ case RGBA_Pixel: keyed = true if img.channels == 3 && img.depth == 8 else false
+ case GA_Pixel_16: keyed = true if img.channels == 1 && img.depth == 16 else false
+ case RGBA_Pixel_16: keyed = true if img.channels == 3 && img.depth == 16 else false
+ }
+
+ // We should now have a valid Image with 1..4 channels. Do we have alpha?
+ if img.channels == 1 || img.channels == 3 {
+ if !(will_it_blend && keyed) {
+ // We're done
+ return true
+ }
+ }
+
+ // # of destination channels
+ channels := 1 if img.channels < 3 else 3
+
+ bytes_wanted := compute_buffer_size(img.width, img.height, channels, img.depth)
+ buf := bytes.Buffer{}
+
+ // Can we allocate the return buffer?
+ if !resize(&buf.buf, bytes_wanted) {
+ delete(buf.buf)
+ return false
+ }
+
+ switch img.depth {
+ case 8:
+ switch img.channels {
+ case 1: // Gray to Gray, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]G_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel, buf.buf[:])
+
+ key := alpha_key.(GA_Pixel).r
+ bg := G_Pixel{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel's topmost byte.
+ bg = u8(temp_bg.r >> 8)
+ }
+
+ for p in inp {
+ out[0] = bg if p == key else p
+ out = out[1:]
+ }
+
+ case 2: // Gray + Alpha to Gray, no keyed alpha but we can have a background.
+ inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := f32(0.0)
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel's topmost byte.
+ bg = f32(temp_bg.r >> 8)
+ }
+
+ for p in inp {
+ a := f32(p.g) / 255.0
+ c := ((1.0 - a) * bg + a * f32(p.r))
+ out[0] = u8(c)
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.g) / 255.0
+ c := f32(p.r) * a
+ out[0] = u8(c)
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.r
+ out = out[1:]
+ }
+ }
+
+ case 3: // RGB to RGB, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ key := alpha_key.(RGBA_Pixel)
+ bg := RGB_Pixel{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, squash down to 8 bits.
+ bg = {u8(temp_bg.r >> 8), u8(temp_bg.g >> 8), u8(temp_bg.b >> 8)}
+ }
+
+ for p in inp {
+ out[0] = bg if p == key.rgb else p
+ out = out[1:]
+ }
+
+ case 4: // RGBA to RGB, no keyed alpha but we can have a background or need to premultiply.
+ inp := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := [3]f32{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel's topmost byte.
+ bg = {f32(temp_bg.r >> 8), f32(temp_bg.g >> 8), f32(temp_bg.b >> 8)}
+ }
+
+ for p in inp {
+ a := f32(p.a) / 255.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := ((1.0 - a) * bg + a * rgb)
+
+ out[0] = {u8(c.r), u8(c.g), u8(c.b)}
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.a) / 255.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := rgb * a
+
+ out[0] = {u8(c.r), u8(c.g), u8(c.b)}
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.rgb
+ out = out[1:]
+ }
+ }
+ }
+
+ case 16:
+ switch img.channels {
+ case 1: // Gray to Gray, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]G_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel_16, buf.buf[:])
+
+ key := alpha_key.(GA_Pixel_16).r
+ bg := G_Pixel_16{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel.
+ bg = temp_bg.r
+ }
+
+ for p in inp {
+ out[0] = bg if p == key else p
+ out = out[1:]
+ }
+
+ case 2: // Gray + Alpha to Gray, no keyed alpha but we can have a background.
+ inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]G_Pixel_16, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := f32(0.0)
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, take just the red channel.
+ bg = f32(temp_bg.r)
+ }
+
+ for p in inp {
+ a := f32(p.g) / 65535.0
+ c := ((1.0 - a) * bg + a * f32(p.r))
+ out[0] = u16(c)
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.g) / 65535.0
+ c := f32(p.r) * a
+ out[0] = u16(c)
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.r
+ out = out[1:]
+ }
+ }
+
+ case 3: // RGB to RGB, but we should have keyed alpha + background.
+ inp := mem.slice_data_cast([]RGB_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
+
+ key := alpha_key.(RGBA_Pixel_16)
+ bg := img.background.(RGB_Pixel_16)
+
+ for p in inp {
+ out[0] = bg if p == key.rgb else p
+ out = out[1:]
+ }
+
+ case 4: // RGBA to RGB, no keyed alpha but we can have a background or need to premultiply.
+ inp := mem.slice_data_cast([]RGBA_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
+
+ if will_it_blend {
+ // Blend with background "color", then drop alpha.
+ bg := [3]f32{}
+ if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
+ // Background is RGB 16-bit, convert to [3]f32 to blend.
+ bg = {f32(temp_bg.r), f32(temp_bg.g), f32(temp_bg.b)}
+ }
+
+ for p in inp {
+ a := f32(p.a) / 65535.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := ((1.0 - a) * bg + a * rgb)
+
+ out[0] = {u16(c.r), u16(c.g), u16(c.b)}
+ out = out[1:]
+ }
+
+ } else if .alpha_premultiply in options {
+ // Premultiply component with alpha, then drop alpha.
+ for p in inp {
+ a := f32(p.a) / 65535.0
+ rgb := [3]f32{f32(p.r), f32(p.g), f32(p.b)}
+ c := rgb * a
+
+ out[0] = {u16(c.r), u16(c.g), u16(c.b)}
+ out = out[1:]
+ }
+ } else {
+ // Just drop alpha on the floor.
+ for p in inp {
+ out[0] = p.rgb
+ out = out[1:]
+ }
+ }
+ }
+
+ case:
+ unreachable()
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel dropped.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = channels
+ return true
+}
+
+// Apply palette to 8-bit single-channel image and return an 8-bit RGB image, in-place.
+// If the image given is not a valid 8-bit single channel image, the procedure will return `false` early.
+apply_palette_rgb :: proc(img: ^Image, palette: [256]RGB_Pixel, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if img == nil || img.channels != 1 || img.depth != 8 {
+ return false
+ }
+
+ bytes_expected := compute_buffer_size(img.width, img.height, 1, 8)
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ // 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) {
+ delete(buf.buf)
+ return false
+ }
+
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ // Apply the palette
+ for p, i in img.pixels.buf {
+ out[i] = palette[p]
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel dropped.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = 3
+ return true
+}
+
+// Apply palette to 8-bit single-channel image and return an 8-bit RGBA image, in-place.
+// If the image given is not a valid 8-bit single channel image, the procedure will return `false` early.
+apply_palette_rgba :: proc(img: ^Image, palette: [256]RGBA_Pixel, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if img == nil || img.channels != 1 || img.depth != 8 {
+ return false
+ }
+
+ bytes_expected := compute_buffer_size(img.width, img.height, 1, 8)
+ if bytes_expected == 0 || bytes_expected != len(img.pixels.buf) || img.width * img.height > MAX_DIMENSIONS {
+ return false
+ }
+
+ // 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) {
+ delete(buf.buf)
+ return false
+ }
+
+ out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
+
+ // Apply the palette
+ for p, i in img.pixels.buf {
+ out[i] = palette[p]
+ }
+
+ // If we got here, that means we've now got a buffer with the alpha channel dropped.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels = 4
+ return true
+}
+apply_palette :: proc{apply_palette_rgb, apply_palette_rgba}
+
+
+// Replicates grayscale values into RGB(A) 8- or 16-bit images as appropriate.
+// Returns early with `false` if already an RGB(A) image.
+expand_grayscale :: proc(img: ^Image, allocator := context.allocator) -> (ok: bool) {
+ context.allocator = allocator
+
+ if !is_valid_grayscale_image(img) {
+ return false
+ }
+
+ // We should have 1 or 2 channels of 8- or 16 bits now. We need to turn that into 3 or 4.
+ // 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) {
+ delete(buf.buf)
+ return false
+ }
+
+ switch img.depth {
+ case 8:
+ switch img.channels {
+ case 1: // Turn Gray into RGB
+ out := mem.slice_data_cast([]RGB_Pixel, buf.buf[:])
+
+ for p in img.pixels.buf {
+ out[0] = p // Broadcast gray value into RGB components.
+ out = out[1:]
+ }
+
+ case 2: // Turn Gray + Alpha into RGBA
+ inp := mem.slice_data_cast([]GA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel, buf.buf[:])
+
+ for p in inp {
+ out[0].rgb = p.r // Gray component.
+ out[0].a = p.g // Alpha component.
+ }
+
+ case:
+ unreachable()
+ }
+
+ case 16:
+ switch img.channels {
+ case 1: // Turn Gray into RGB
+ inp := mem.slice_data_cast([]u16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel_16, buf.buf[:])
+
+ for p in inp {
+ out[0] = p // Broadcast gray value into RGB components.
+ out = out[1:]
+ }
+
+ case 2: // Turn Gray + Alpha into RGBA
+ inp := mem.slice_data_cast([]GA_Pixel_16, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel_16, buf.buf[:])
+
+ for p in inp {
+ out[0].rgb = p.r // Gray component.
+ out[0].a = p.g // Alpha component.
+ }
+
+ case:
+ unreachable()
+ }
+
+ case:
+ unreachable()
+ }
+
+
+ // If we got here, that means we've now got a buffer with the extra alpha channel.
+ // Destroy the old pixel buffer and replace it with the new one, and update the channel count.
+ bytes.buffer_destroy(&img.pixels)
+ img.pixels = buf
+ img.channels += 2
+ return true
+}
+
+/*
+ Helper functions to read and write data from/to a Context, etc.
+*/
+@(optimization_mode="speed")
+read_data :: proc(z: $C, $T: typeid) -> (res: T, err: compress.General_Error) {
+ if r, e := compress.read_data(z, T); e != .None {
+ return {}, .Stream_Too_Short
+ } else {
+ return r, nil
+ }
+}
+
+@(optimization_mode="speed")
+read_u8 :: proc(z: $C) -> (res: u8, err: compress.General_Error) {
+ if r, e := compress.read_u8(z); e != .None {
+ return {}, .Stream_Too_Short
+ } else {
+ return r, nil
+ }
+}
+
+write_bytes :: proc(buf: ^bytes.Buffer, data: []u8) -> (err: compress.General_Error) {
+ if len(data) == 0 {
+ return nil
+ } else if len(data) == 1 {
+ if bytes.buffer_write_byte(buf, data[0]) != nil {
+ return compress.General_Error.Resize_Failed
+ }
+ } else if n, _ := bytes.buffer_write(buf, data); n != len(data) {
+ return compress.General_Error.Resize_Failed
+ }
+ return nil
+}
\ No newline at end of file
diff --git a/core/image/png/example.odin b/core/image/png/example.odin
index 5e7dca4c8..17436c260 100644
--- a/core/image/png/example.odin
+++ b/core/image/png/example.odin
@@ -189,7 +189,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
img := image
// PBM 16-bit images are big endian
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap back.
input := mem.slice_data_cast([]u16, img.pixels.buf[:])
@@ -207,7 +207,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
}
mode: int = 0
- when ODIN_OS == "linux" || ODIN_OS == "darwin" {
+ when ODIN_OS == .Linux || ODIN_OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
diff --git a/core/image/png/helpers.odin b/core/image/png/helpers.odin
index ecc0183bc..0ebf0b20b 100644
--- a/core/image/png/helpers.odin
+++ b/core/image/png/helpers.odin
@@ -242,17 +242,16 @@ srgb :: proc(c: image.PNG_Chunk) -> (res: sRGB, ok: bool) {
}
plte :: proc(c: image.PNG_Chunk) -> (res: PLTE, ok: bool) {
- if c.header.type != .PLTE {
+ if c.header.type != .PLTE || c.header.length % 3 != 0 || c.header.length > 768 {
return {}, false
}
- i := 0; j := 0; ok = true
- for j < int(c.header.length) {
- res.entries[i] = {c.data[j], c.data[j+1], c.data[j+2]}
- i += 1; j += 3
+ plte := mem.slice_data_cast([]image.RGB_Pixel, c.data[:])
+ for color, i in plte {
+ res.entries[i] = color
}
- res.used = u16(i)
- return
+ res.used = u16(len(plte))
+ return res, true
}
splt :: proc(c: image.PNG_Chunk) -> (res: sPLT, ok: bool) {
@@ -439,18 +438,18 @@ when false {
flags: int = O_WRONLY|O_CREATE|O_TRUNC
if len(image.pixels) == 0 || len(image.pixels) < image.width * image.height * int(image.channels) {
- return E_PNG.Invalid_Image_Dimensions
+ return .Invalid_Image_Dimensions
}
mode: int = 0
- when ODIN_OS == "linux" || ODIN_OS == "darwin" {
+ when ODIN_OS == .Linux || ODIN_OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
fd, fderr := open(filename, flags, mode)
if fderr != 0 {
- return E_General.Cannot_Open_File
+ return .Cannot_Open_File
}
defer close(fd)
@@ -473,7 +472,7 @@ when false {
case 3: ihdr.color_type = Color_Type{.Color}
case 4: ihdr.color_type = Color_Type{.Color, .Alpha}
case:// Unhandled
- return E_PNG.Unknown_Color_Type
+ return .Unknown_Color_Type
}
h := make_chunk(ihdr, .IHDR)
write_chunk(fd, h)
diff --git a/core/image/png/png.odin b/core/image/png/png.odin
index f77bf7519..ba888cb78 100644
--- a/core/image/png/png.odin
+++ b/core/image/png/png.odin
@@ -6,6 +6,11 @@
Jeroen van Rijn: Initial implementation.
Ginger Bill: Cosmetic changes.
*/
+
+
+// package png implements a PNG image reader
+//
+// The PNG specification is at https://www.w3.org/TR/PNG/.
package png
import "core:compress"
@@ -20,16 +25,10 @@ import "core:io"
import "core:mem"
import "core:intrinsics"
-/*
- 67_108_864 pixels max by default.
- Maximum allowed dimensions are capped at 65535 * 65535.
-*/
-MAX_DIMENSIONS :: min(#config(PNG_MAX_DIMENSIONS, 8192 * 8192), 65535 * 65535)
+// Limit chunk sizes.
+// By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
+// The total number of pixels defaults to 64 Megapixel and can be tuned in image/common.odin.
-/*
- Limit chunk sizes.
- By default: IDAT = 8k x 8k x 16-bits + 8k filter bytes.
-*/
_MAX_IDAT_DEFAULT :: ( 8192 /* Width */ * 8192 /* Height */ * 2 /* 16-bit */) + 8192 /* Filter bytes */
_MAX_IDAT :: (65535 /* Width */ * 65535 /* Height */ * 2 /* 16-bit */) + 65535 /* Filter bytes */
@@ -59,7 +58,7 @@ Row_Filter :: enum u8 {
Paeth = 4,
}
-PLTE_Entry :: [3]u8
+PLTE_Entry :: image.RGB_Pixel
PLTE :: struct #packed {
entries: [256]PLTE_Entry,
@@ -254,7 +253,7 @@ read_header :: proc(ctx: ^$C) -> (image.PNG_IHDR, Error) {
header := (^image.PNG_IHDR)(raw_data(c.data))^
// Validate IHDR
using header
- if width == 0 || height == 0 || u128(width) * u128(height) > MAX_DIMENSIONS {
+ if width == 0 || height == 0 || u128(width) * u128(height) > image.MAX_DIMENSIONS {
return {}, .Invalid_Image_Dimensions
}
@@ -361,6 +360,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
options -= {.info}
}
+ if .return_header in options && .return_metadata in options {
+ options -= {.return_header}
+ }
+
if .alpha_drop_if_present in options && .alpha_add_if_missing in options {
return {}, compress.General_Error.Incompatible_Options
}
@@ -387,7 +390,7 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
idat_length := u64(0)
- c: image.PNG_Chunk
+ c: image.PNG_Chunk
ch: image.PNG_Chunk_Header
e: io.Error
@@ -468,6 +471,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
}
info.header = h
+ if .return_header in options && .return_metadata not_in options && .do_not_decompress_image not_in options {
+ return img, nil
+ }
+
case .PLTE:
seen_plte = true
// PLTE must appear before IDAT and can't appear for color types 0, 4.
@@ -535,9 +542,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
seen_iend = true
case .bKGD:
-
- // TODO: Make sure that 16-bit bKGD + tRNS chunks return u16 instead of u16be
-
c = read_chunk(ctx) or_return
seen_bkgd = true
if .return_metadata in options {
@@ -589,23 +593,36 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
*/
final_image_channels += 1
-
seen_trns = true
+
+ if .Paletted in header.color_type {
+ if len(c.data) > 256 {
+ return img, .TNRS_Invalid_Length
+ }
+ } else if .Color in header.color_type {
+ if len(c.data) != 6 {
+ return img, .TNRS_Invalid_Length
+ }
+ } else if len(c.data) != 2 {
+ return img, .TNRS_Invalid_Length
+ }
+
if info.header.bit_depth < 8 && .Paletted not_in info.header.color_type {
// Rescale tRNS data so key matches intensity
- dsc := depth_scale_table
+ dsc := depth_scale_table
scale := dsc[info.header.bit_depth]
if scale != 1 {
key := mem.slice_data_cast([]u16be, c.data)[0] * u16be(scale)
c.data = []u8{0, u8(key & 255)}
}
}
+
trns = c
- case .iDOT, .CbGI:
+ case .iDOT, .CgBI:
/*
iPhone PNG bastardization that doesn't adhere to spec with broken IDAT chunk.
- We're not going to add support for it. If you have the misfortunte of coming
+ We're not going to add support for it. If you have the misfortune of coming
across one of these files, use a utility to defry it.
*/
return img, .Image_Does_Not_Adhere_to_Spec
@@ -630,6 +647,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, .IDAT_Missing
}
+ if .Paletted in header.color_type && !seen_plte {
+ return img, .PLTE_Missing
+ }
+
/*
Calculate the expected output size, to help `inflate` make better decisions about the output buffer.
We'll also use it to check the returned buffer size is what we expected it to be.
@@ -678,15 +699,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return {}, defilter_error
}
- /*
- Now we'll handle the relocoring of paletted images, handling of tRNS chunks,
- and we'll expand grayscale images to RGB(A).
-
- For the sake of convenience we return only RGB(A) images. In the future we
- may supply an option to return Gray/Gray+Alpha as-is, in which case RGB(A)
- will become the default.
- */
-
if .Paletted in header.color_type && .do_not_expand_indexed in options {
return img, nil
}
@@ -694,7 +706,10 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, nil
}
-
+ /*
+ Now we're going to optionally apply various post-processing stages,
+ to for example expand grayscale, apply a palette, premultiply alpha, etc.
+ */
raw_image_channels := img.channels
out_image_channels := 3
@@ -1199,7 +1214,6 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
return img, nil
}
-
filter_paeth :: #force_inline proc(left, up, up_left: u8) -> u8 {
aa, bb, cc := i16(left), i16(up), i16(up_left)
p := aa + bb - cc
@@ -1611,7 +1625,7 @@ defilter :: proc(img: ^Image, filter_bytes: ^bytes.Buffer, header: ^image.PNG_IH
}
}
}
- when ODIN_ENDIAN == "little" {
+ when ODIN_ENDIAN == .Little {
if img.depth == 16 {
// The pixel components are in Big Endian. Let's byteswap.
input := mem.slice_data_cast([]u16be, img.pixels.buf[:])
diff --git a/core/image/qoi/qoi.odin b/core/image/qoi/qoi.odin
new file mode 100644
index 000000000..fdbaab686
--- /dev/null
+++ b/core/image/qoi/qoi.odin
@@ -0,0 +1,408 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+
+
+// package qoi implements a QOI image reader
+//
+// The QOI specification is at https://qoiformat.org.
+package qoi
+
+import "core:mem"
+import "core:image"
+import "core:compress"
+import "core:bytes"
+import "core:os"
+
+Error :: image.Error
+General :: compress.General_Error
+Image :: image.Image
+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) {
+ context.allocator = allocator
+
+ if img == nil {
+ return .Invalid_Input_Image
+ }
+
+ if output == nil {
+ return .Invalid_Output
+ }
+
+ pixels := img.width * img.height
+ if pixels == 0 || pixels > image.MAX_DIMENSIONS {
+ return .Invalid_Input_Image
+ }
+
+ // QOI supports only 8-bit images with 3 or 4 channels.
+ if img.depth != 8 || img.channels < 3 || img.channels > 4 {
+ return .Invalid_Input_Image
+ }
+
+ if img.channels * pixels != len(img.pixels.buf) {
+ return .Invalid_Input_Image
+ }
+
+ written := 0
+
+ // 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) {
+ return General.Resize_Failed
+ }
+
+ header := image.QOI_Header{
+ magic = image.QOI_Magic,
+ width = u32be(img.width),
+ height = u32be(img.height),
+ channels = u8(img.channels),
+ color_space = .Linear if .qoi_all_channels_linear in options else .sRGB,
+ }
+ header_bytes := transmute([size_of(image.QOI_Header)]u8)header
+
+ copy(output.buf[written:], header_bytes[:])
+ written += size_of(image.QOI_Header)
+
+ /*
+ Encode loop starts here.
+ */
+ seen: [64]RGBA_Pixel
+ pix := RGBA_Pixel{0, 0, 0, 255}
+ prev := pix
+
+ seen[qoi_hash(pix)] = pix
+
+ input := img.pixels.buf[:]
+ run := u8(0)
+
+ for len(input) > 0 {
+ if img.channels == 4 {
+ pix = (^RGBA_Pixel)(raw_data(input))^
+ } else {
+ pix.rgb = (^RGB_Pixel)(raw_data(input))^
+ }
+ input = input[img.channels:]
+
+ if pix == prev {
+ run += 1
+ // As long as the pixel matches the last one, accumulate the run total.
+ // If we reach the max run length or the end of the image, write the run.
+ if run == 62 || len(input) == 0 {
+ // Encode and write run
+ output.buf[written] = u8(QOI_Opcode_Tag.RUN) | (run - 1)
+ written += 1
+ run = 0
+ }
+ } else {
+ if run > 0 {
+ // The pixel differs from the previous one, but we still need to write the pending run.
+ // Encode and write run
+ output.buf[written] = u8(QOI_Opcode_Tag.RUN) | (run - 1)
+ written += 1
+ run = 0
+ }
+
+ index := qoi_hash(pix)
+
+ if seen[index] == pix {
+ // Write indexed pixel
+ output.buf[written] = u8(QOI_Opcode_Tag.INDEX) | index
+ written += 1
+ } else {
+ // Add pixel to index
+ seen[index] = pix
+
+ // If the alpha matches the previous pixel's alpha, we don't need to write a full RGBA literal.
+ if pix.a == prev.a {
+ // Delta
+ d := pix.rgb - prev.rgb
+
+ // DIFF, biased and modulo 256
+ _d := d + 2
+
+ // LUMA, biased and modulo 256
+ _l := RGB_Pixel{ d.r - d.g + 8, d.g + 32, d.b - d.g + 8 }
+
+ if _d.r < 4 && _d.g < 4 && _d.b < 4 {
+ // Delta is between -2 and 1 inclusive
+ output.buf[written] = u8(QOI_Opcode_Tag.DIFF) | _d.r << 4 | _d.g << 2 | _d.b
+ written += 1
+ } else if _l.r < 16 && _l.g < 64 && _l.b < 16 {
+ // Biased luma is between {-8..7, -32..31, -8..7}
+ output.buf[written ] = u8(QOI_Opcode_Tag.LUMA) | _l.g
+ output.buf[written + 1] = _l.r << 4 | _l.b
+ written += 2
+ } else {
+ // Write RGB literal
+ output.buf[written] = u8(QOI_Opcode_Tag.RGB)
+ pix_bytes := transmute([4]u8)pix
+ copy(output.buf[written + 1:], pix_bytes[:3])
+ written += 4
+ }
+ } else {
+ // Write RGBA literal
+ output.buf[written] = u8(QOI_Opcode_Tag.RGBA)
+ pix_bytes := transmute([4]u8)pix
+ copy(output.buf[written + 1:], pix_bytes[:])
+ written += 5
+ }
+ }
+ }
+ prev = pix
+ }
+
+ trailer := []u8{0, 0, 0, 0, 0, 0, 0, 1}
+ copy(output.buf[written:], trailer[:])
+ written += len(trailer)
+
+ resize(&output.buf, written)
+ 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 General.Cannot_Open_File
+}
+
+save :: proc{save_to_memory, save_to_file}
+
+load_from_slice :: proc(slice: []u8, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ ctx := &compress.Context_Memory_Input{
+ input_data = slice,
+ }
+
+ img, err = load_from_context(ctx, options, allocator)
+ return img, err
+}
+
+load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ context.allocator = allocator
+
+ data, ok := os.read_entire_file(filename)
+ defer delete(data)
+
+ if ok {
+ return load_from_slice(data, options)
+ } else {
+ img = new(Image)
+ return img, compress.General_Error.File_Not_Found
+ }
+}
+
+@(optimization_mode="speed")
+load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
+ context.allocator = allocator
+ options := options
+
+ if .info in options {
+ options |= {.return_metadata, .do_not_decompress_image}
+ options -= {.info}
+ }
+
+ if .return_header in options && .return_metadata in options {
+ options -= {.return_header}
+ }
+
+ header := image.read_data(ctx, image.QOI_Header) or_return
+ if header.magic != image.QOI_Magic {
+ return img, .Invalid_QOI_Signature
+ }
+
+ if img == nil {
+ img = new(Image)
+ }
+
+ if .return_metadata in options {
+ info := new(image.QOI_Info)
+ info.header = header
+ img.metadata = info
+ }
+
+ if header.channels != 3 && header.channels != 4 {
+ return img, .Invalid_Number_Of_Channels
+ }
+
+ if header.color_space != .sRGB && header.color_space != .Linear {
+ return img, .Invalid_Color_Space
+ }
+
+ if header.width == 0 || header.height == 0 {
+ return img, .Invalid_Image_Dimensions
+ }
+
+ total_pixels := header.width * header.height
+ if total_pixels > image.MAX_DIMENSIONS {
+ return img, .Image_Dimensions_Too_Large
+ }
+
+ img.width = int(header.width)
+ img.height = int(header.height)
+ img.channels = 4 if .alpha_add_if_missing in options else int(header.channels)
+ img.depth = 8
+
+ if .do_not_decompress_image in options {
+ img.channels = int(header.channels)
+ return
+ }
+
+ bytes_needed := image.compute_buffer_size(int(header.width), int(header.height), img.channels, 8)
+
+ if !resize(&img.pixels.buf, bytes_needed) {
+ return img, mem.Allocator_Error.Out_Of_Memory
+ }
+
+ /*
+ Decode loop starts here.
+ */
+ seen: [64]RGBA_Pixel
+ pix := RGBA_Pixel{0, 0, 0, 255}
+ seen[qoi_hash(pix)] = pix
+ pixels := img.pixels.buf[:]
+
+ decode: for len(pixels) > 0 {
+ data := image.read_u8(ctx) or_return
+
+ tag := QOI_Opcode_Tag(data)
+ #partial switch tag {
+ case .RGB:
+ pix.rgb = image.read_data(ctx, RGB_Pixel) or_return
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case .RGBA:
+ pix = image.read_data(ctx, RGBA_Pixel) or_return
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case:
+ // 2-bit tag
+ tag = QOI_Opcode_Tag(data & QOI_Opcode_Mask)
+ #partial switch tag {
+ case .INDEX:
+ pix = seen[data & 63]
+
+ case .DIFF:
+ diff_r := ((data >> 4) & 3) - 2
+ diff_g := ((data >> 2) & 3) - 2
+ diff_b := ((data >> 0) & 3) - 2
+
+ pix += {diff_r, diff_g, diff_b, 0}
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case .LUMA:
+ data2 := image.read_u8(ctx) or_return
+
+ diff_g := (data & 63) - 32
+ diff_r := diff_g - 8 + ((data2 >> 4) & 15)
+ diff_b := diff_g - 8 + (data2 & 15)
+
+ pix += {diff_r, diff_g, diff_b, 0}
+
+ #no_bounds_check {
+ seen[qoi_hash(pix)] = pix
+ }
+
+ case .RUN:
+ if length := int(data & 63) + 1; (length * img.channels) > len(pixels) {
+ return img, .Corrupt
+ } else {
+ #no_bounds_check for in 0.. (index: u8) {
+ i1 := u16(pixel.r) * 3
+ i2 := u16(pixel.g) * 5
+ i3 := u16(pixel.b) * 7
+ i4 := u16(pixel.a) * 11
+
+ return u8((i1 + i2 + i3 + i4) & 63)
+}
\ No newline at end of file
diff --git a/core/image/tga/tga.odin b/core/image/tga/tga.odin
new file mode 100644
index 000000000..3c860cb62
--- /dev/null
+++ b/core/image/tga/tga.odin
@@ -0,0 +1,103 @@
+/*
+ Copyright 2022 Jeroen van Rijn .
+ Made available under Odin's BSD-3 license.
+
+ List of contributors:
+ Jeroen van Rijn: Initial implementation.
+*/
+
+
+// package tga implements a TGA image writer for 8-bit RGB and RGBA images.
+package tga
+
+import "core:mem"
+import "core:image"
+import "core:compress"
+import "core:bytes"
+import "core:os"
+
+Error :: image.Error
+General :: compress.General_Error
+Image :: image.Image
+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) {
+ context.allocator = allocator
+
+ if img == nil {
+ return .Invalid_Input_Image
+ }
+
+ if output == nil {
+ return .Invalid_Output
+ }
+
+ pixels := img.width * img.height
+ if pixels == 0 || pixels > image.MAX_DIMENSIONS || img.width > 65535 || img.height > 65535 {
+ return .Invalid_Input_Image
+ }
+
+ // Our TGA writer supports only 8-bit images with 3 or 4 channels.
+ if img.depth != 8 || img.channels < 3 || img.channels > 4 {
+ return .Invalid_Input_Image
+ }
+
+ if img.channels * pixels != len(img.pixels.buf) {
+ return .Invalid_Input_Image
+ }
+
+ written := 0
+
+ // Calculate and allocate necessary space.
+ necessary := pixels * img.channels + size_of(image.TGA_Header)
+
+ if !resize(&output.buf, necessary) {
+ return General.Resize_Failed
+ }
+
+ header := image.TGA_Header{
+ data_type_code = 0x02, // Color, uncompressed.
+ dimensions = {u16le(img.width), u16le(img.height)},
+ bits_per_pixel = u8(img.depth * img.channels),
+ image_descriptor = 1 << 5, // Origin is top left.
+ }
+ header_bytes := transmute([size_of(image.TGA_Header)]u8)header
+
+ copy(output.buf[written:], header_bytes[:])
+ written += size_of(image.TGA_Header)
+
+ /*
+ Encode loop starts here.
+ */
+ if img.channels == 3 {
+ pix := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGB_Pixel, output.buf[written:])
+ for p, i in pix {
+ out[i] = p.bgr
+ }
+ } else if img.channels == 4 {
+ pix := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
+ out := mem.slice_data_cast([]RGBA_Pixel, output.buf[written:])
+ for p, i in pix {
+ out[i] = p.bgra
+ }
+ }
+ 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 General.Cannot_Open_File
+}
+
+save :: proc{save_to_memory, save_to_file}
\ No newline at end of file
diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin
index 2da7a7439..c132d4095 100644
--- a/core/intrinsics/intrinsics.odin
+++ b/core/intrinsics/intrinsics.odin
@@ -41,6 +41,8 @@ mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
mem_zero :: proc(ptr: rawptr, len: int) ---
mem_zero_volatile :: proc(ptr: rawptr, len: int) ---
+unaligned_load :: proc(src: ^$T) -> T ---
+unaligned_store :: proc(dst: ^$T, val: T) -> T ---
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
@@ -60,77 +62,46 @@ syscall :: proc(id: uintptr, args: ..uintptr) -> uintptr ---
// Atomics
-atomic_fence :: proc() ---
-atomic_fence_acq :: proc() ---
-atomic_fence_rel :: proc() ---
-atomic_fence_acqrel :: proc() ---
+Atomic_Memory_Order :: enum {
+ Relaxed = 0, // Unordered
+ Consume = 1, // Monotonic
+ Acquire = 2,
+ Release = 3,
+ Acq_Rel = 4,
+ Seq_Cst = 5,
+}
-atomic_store :: proc(dst: ^$T, val: T) ---
-atomic_store_rel :: proc(dst: ^$T, val: T) ---
-atomic_store_relaxed :: proc(dst: ^$T, val: T) ---
-atomic_store_unordered :: proc(dst: ^$T, val: T) ---
+atomic_type_is_lock_free :: proc($T: typeid) -> bool ---
+
+atomic_thread_fence :: proc(order: Atomic_Memory_Order) ---
+atomic_signal_fence :: proc(order: Atomic_Memory_Order) ---
+
+atomic_store :: proc(dst: ^$T, val: T) ---
+atomic_store_explicit :: proc(dst: ^$T, val: T, order: Atomic_Memory_Order) ---
atomic_load :: proc(dst: ^$T) -> T ---
-atomic_load_acq :: proc(dst: ^$T) -> T ---
-atomic_load_relaxed :: proc(dst: ^$T) -> T ---
-atomic_load_unordered :: proc(dst: ^$T) -> T ---
+atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> T ---
-atomic_add :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_add_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_sub_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_and :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_and_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_nand_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_or :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_or_relaxed :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xor_relaxed :: proc(dst; ^$T, val: T) -> T ---
+atomic_add :: proc(dst; ^$T, val: T) -> T ---
+atomic_add_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_sub :: proc(dst; ^$T, val: T) -> T ---
+atomic_sub_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_and :: proc(dst; ^$T, val: T) -> T ---
+atomic_and_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_nand :: proc(dst; ^$T, val: T) -> T ---
+atomic_nand_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_or :: proc(dst; ^$T, val: T) -> T ---
+atomic_or_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_xor :: proc(dst; ^$T, val: T) -> T ---
+atomic_xor_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
+atomic_exchange :: proc(dst; ^$T, val: T) -> T ---
+atomic_exchange_explicit :: proc(dst; ^$T, val: T, order: Atomic_Memory_Order) -> T ---
-atomic_xchg :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_acq :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_rel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_acqrel :: proc(dst; ^$T, val: T) -> T ---
-atomic_xchg_relaxed :: proc(dst; ^$T, val: T) -> T ---
+atomic_compare_exchange_strong :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_strong_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_weak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
+atomic_compare_exchange_weak_explicit :: proc(dst: ^$T, old, new: T, success, failure: Atomic_Memory_Order) -> (T, bool) #optional_ok ---
-atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-
-atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
-atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, bool) #optional_ok ---
// Constant type tests
@@ -182,6 +153,7 @@ type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
type_has_field :: proc($T: typeid, $name: string) -> bool ---
+type_field_type :: proc($T: typeid, $name: string) -> typeid ---
type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) ---
type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) ---
@@ -197,3 +169,12 @@ type_field_index_of :: proc($T: typeid, $name: string) -> uintptr ---
type_equal_proc :: proc($T: typeid) -> (equal: proc "contextless" (rawptr, rawptr) -> bool) where type_is_comparable(T) ---
type_hasher_proc :: proc($T: typeid) -> (hasher: proc "contextless" (data: rawptr, seed: uintptr) -> uintptr) where type_is_comparable(T) ---
+
+
+// WASM targets only
+wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
+wasm_memory_size :: proc(index: uintptr) -> int ---
+
+// Internal compiler use only
+
+__entry_point :: proc() ---
\ No newline at end of file
diff --git a/core/io/io.odin b/core/io/io.odin
index b4757f8e5..e9d839efb 100644
--- a/core/io/io.odin
+++ b/core/io/io.odin
@@ -1,9 +1,13 @@
+// package io provides basic interfaces for generic data stream primitives.
+// The purpose of this package is wrap existing data structures and their
+// operations into an abstracted stream interface.
package io
import "core:intrinsics"
import "core:runtime"
import "core:unicode/utf8"
+// Seek whence values
Seek_From :: enum {
Start = 0, // seek relative to the origin of the file
Current = 1, // seek relative to the current offset
@@ -139,6 +143,10 @@ destroy :: proc(s: Stream) -> Error {
return .Empty
}
+// 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 && s.impl_read != nil {
n, err = s->impl_read(p)
@@ -150,6 +158,7 @@ read :: proc(s: Reader, p: []byte, n_read: ^int = nil) -> (n: int, err: Error) {
return 0, .Empty
}
+// 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 && s.impl_write != nil {
n, err = s->impl_write(p)
@@ -161,6 +170,13 @@ write :: proc(s: Writer, p: []byte, n_written: ^int = nil) -> (n: int, err: Erro
return 0, .Empty
}
+// seek sets the offset of the next read or write to offset.
+//
+// .Start means seek relative to the origin of the file.
+// .Current means seek relative to the current offset.
+// .End means seek relative to the end.
+//
+// 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)
@@ -168,6 +184,8 @@ seek :: proc(s: Seeker, offset: i64, whence: Seek_From) -> (n: i64, err: Error)
return 0, .Empty
}
+// 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()
@@ -184,6 +202,7 @@ flush :: proc(s: Flusher) -> Error {
return .None
}
+// 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
@@ -214,7 +233,12 @@ size :: proc(s: Stream) -> i64 {
-
+// read_at reads len(p) bytes into p starting with the provided offset in the underlying Reader_At stream r.
+// It returns the number of bytes read and any error if occurred.
+//
+// When read_at returns n < len(p), it returns a non-nil Error explaining why.
+//
+// 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
@@ -245,6 +269,11 @@ read_at :: proc(r: Reader_At, p: []byte, offset: i64, n_read: ^int = nil) -> (n:
}
+// write_at writes len(p) bytes into p starting with the provided offset in the underlying Writer_At stream w.
+// It returns the number of bytes written and any error if occurred.
+//
+// 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
@@ -294,6 +323,7 @@ read_from :: proc(w: Reader_From, r: Reader) -> (n: i64, err: Error) {
}
+// read_byte reads and returns the next byte from r.
read_byte :: proc(r: Byte_Reader, n_read: ^int = nil) -> (b: byte, err: Error) {
defer if err == nil && n_read != nil {
n_read^ += 1
@@ -347,6 +377,7 @@ _write_byte :: proc(w: Byte_Writer, c: byte, n_written: ^int = nil) -> (err: Err
return err
}
+// read_rune reads a single UTF-8 encoded Unicode codepoint and returns the rune and its size in bytes.
read_rune :: proc(br: Rune_Reader, n_read: ^int = nil) -> (ch: rune, size: int, err: Error) {
defer if err == nil && n_read != nil {
n_read^ += size
@@ -405,10 +436,12 @@ unread_rune :: proc(s: Rune_Scanner) -> Error {
}
+// 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)
}
+// write_rune writes a UTF-8 encoded rune to w.
write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err: Error) {
defer if err == nil && n_written != nil {
n_written^ += size
@@ -430,12 +463,16 @@ write_rune :: proc(s: Writer, r: rune, n_written: ^int = nil) -> (size: int, err
}
-
+// read_full expected exactly len(buf) bytes from r into buf.
read_full :: proc(r: Reader, buf: []byte) -> (n: int, err: Error) {
return read_at_least(r, buf, len(buf))
}
+// read_at_least reads from r into buf until it has read at least min bytes. It returns the number
+// of bytes copied and an error if fewer bytes were read. `.EOF` is only returned if no bytes were read.
+// `.Unexpected_EOF` is returned when an `.EOF ` is returned by the passed Reader after reading
+// fewer than min bytes. If len(buf) is less than min, `.Short_Buffer` is returned.
read_at_least :: proc(r: Reader, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer
diff --git a/core/math/big/api.odin b/core/math/big/api.odin
index c9be04da0..bf19e83b6 100644
--- a/core/math/big/api.odin
+++ b/core/math/big/api.odin
@@ -2,12 +2,10 @@
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
- An arbitrary precision mathematics implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
-
This file collects public proc maps and their aliases.
*/
+
+
package math_big
/*
diff --git a/core/math/big/common.odin b/core/math/big/common.odin
index 5b7d162bc..74a641d83 100644
--- a/core/math/big/common.odin
+++ b/core/math/big/common.odin
@@ -1,11 +1,9 @@
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
-
- An arbitrary precision mathematics implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
+
+
package math_big
import "core:intrinsics"
@@ -158,13 +156,14 @@ Error :: enum int {
Invalid_Pointer = 2,
Invalid_Argument = 3,
- Assignment_To_Immutable = 4,
- Max_Iterations_Reached = 5,
- Buffer_Overflow = 6,
- Integer_Overflow = 7,
+ Assignment_To_Immutable = 10,
+ Max_Iterations_Reached = 11,
+ Buffer_Overflow = 12,
+ Integer_Overflow = 13,
+ Integer_Underflow = 14,
- Division_by_Zero = 8,
- Math_Domain_Error = 9,
+ Division_by_Zero = 30,
+ Math_Domain_Error = 31,
Cannot_Open_File = 50,
Cannot_Read_File = 51,
@@ -173,7 +172,7 @@ Error :: enum int {
Unimplemented = 127,
}
-Error_String :: #partial [Error]string{
+Error_String :: #sparse[Error]string{
.Okay = "Okay",
.Out_Of_Memory = "Out of memory",
.Invalid_Pointer = "Invalid pointer",
@@ -183,6 +182,7 @@ Error_String :: #partial [Error]string{
.Max_Iterations_Reached = "Max iterations reached",
.Buffer_Overflow = "Buffer overflow",
.Integer_Overflow = "Integer overflow",
+ .Integer_Underflow = "Integer underflow",
.Division_by_Zero = "Division by zero",
.Math_Domain_Error = "Math domain error",
@@ -215,7 +215,7 @@ _MIN_DIGIT_COUNT :: max(3, ((size_of(u128) + _DIGIT_BITS) - 1) / _DIGIT_BITS)
/*
Maximum number of digits.
- Must be small enough such that `_bit_count` does not overflow.
- - Must be small enough such that `_radix_size` for base 2 does not overflow.
+ - Must be small enough such that `_radix_size` for base 2 does not overflow.
`_radix_size` needs two additional bytes for zero termination and sign.
*/
_MAX_BIT_COUNT :: (max(int) - 2)
@@ -251,7 +251,7 @@ Order :: enum i8 {
}
Endianness :: enum i8 {
- Little = -1,
- Platform = 0,
- Big = 1,
-};
\ No newline at end of file
+ Little = -1,
+ Platform = 0,
+ Big = 1,
+}
\ No newline at end of file
diff --git a/core/math/big/doc.odin b/core/math/big/doc.odin
new file mode 100644
index 000000000..0f9b88d01
--- /dev/null
+++ b/core/math/big/doc.odin
@@ -0,0 +1,6 @@
+/*
+A BigInt implementation in Odin.
+For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
+The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
+*/
+package math_big
diff --git a/core/math/big/helpers.odin b/core/math/big/helpers.odin
index 6d13d32bb..6c4b5dd01 100644
--- a/core/math/big/helpers.odin
+++ b/core/math/big/helpers.odin
@@ -1,11 +1,9 @@
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
-
- An arbitrary precision mathematics implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
+
+
package math_big
import "core:intrinsics"
diff --git a/core/math/big/internal.odin b/core/math/big/internal.odin
index 4702e76a3..dbcd16509 100644
--- a/core/math/big/internal.odin
+++ b/core/math/big/internal.odin
@@ -1,12 +1,7 @@
-//+ignore
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
- A BigInt implementation in Odin.
- For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
- The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
-
========================== Low-level routines ==========================
IMPORTANT: `internal_*` procedures make certain assumptions about their input.
@@ -29,11 +24,15 @@
TODO: Handle +/- Infinity and NaN.
*/
+
+
+//+ignore
package math_big
import "core:mem"
import "core:intrinsics"
import rnd "core:math/rand"
+import "core:builtin"
/*
Low-level addition, unsigned. Handbook of Applied Cryptography, algorithm 14.7.
@@ -1880,8 +1879,6 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
where intrinsics.type_is_integer(T) {
context.allocator = allocator
- src := src
-
internal_error_if_immutable(dest) or_return
/*
Most internal procs asssume an Int to have already been initialize,
@@ -1892,13 +1889,27 @@ internal_int_set_from_integer :: proc(dest: ^Int, src: $T, minimize := false, al
dest.flags = {} // We're not -Inf, Inf, NaN or Immutable.
dest.used = 0
- dest.sign = .Zero_or_Positive if src >= 0 else .Negative
- src = internal_abs(src)
+ dest.sign = .Negative if src < 0 else .Zero_or_Positive
- #no_bounds_check for src != 0 {
- dest.digit[dest.used] = DIGIT(src) & _MASK
+ temp := src
+
+ is_maximally_negative := src == min(T)
+ if is_maximally_negative {
+ /*
+ Prevent overflow on abs()
+ */
+ temp += 1
+ }
+ temp = -temp if temp < 0 else temp
+
+ #no_bounds_check for temp != 0 {
+ dest.digit[dest.used] = DIGIT(temp) & _MASK
dest.used += 1
- src >>= _DIGIT_BITS
+ temp >>= _DIGIT_BITS
+ }
+
+ if is_maximally_negative {
+ return internal_sub(dest, dest, 1)
}
internal_zero_unused(dest)
return nil
@@ -2307,28 +2318,69 @@ internal_int_get_i32 :: proc(a: ^Int) -> (res: i32, err: Error) {
}
internal_get_i32 :: proc { internal_int_get_i32, }
+internal_get_low_u32 :: proc(a: ^Int) -> u32 #no_bounds_check {
+ if a == nil {
+ return 0
+ }
+
+ if a.used == 0 {
+ return 0
+ }
+
+ return u32(a.digit[0])
+}
+internal_get_low_u64 :: proc(a: ^Int) -> u64 #no_bounds_check {
+ if a == nil {
+ return 0
+ }
+
+ if a.used == 0 {
+ return 0
+ }
+
+ v := u64(a.digit[0])
+ when size_of(DIGIT) == 4 {
+ if a.used > 1 {
+ return u64(a.digit[1])<<32 | v
+ }
+ }
+ return v
+}
+
/*
TODO: Think about using `count_bits` to check if the value could be returned completely,
and maybe return max(T), .Integer_Overflow if not?
*/
internal_int_get :: proc(a: ^Int, $T: typeid) -> (res: T, err: Error) where intrinsics.type_is_integer(T) {
- size_in_bits := int(size_of(T) * 8)
- i := int((size_in_bits + _DIGIT_BITS - 1) / _DIGIT_BITS)
- i = min(int(a.used), i)
-
- #no_bounds_check for ; i >= 0; i -= 1 {
- res <<= uint(0) if size_in_bits <= _DIGIT_BITS else _DIGIT_BITS
- res |= T(a.digit[i])
- if size_in_bits <= _DIGIT_BITS {
- break
+ /*
+ Calculate target bit size.
+ */
+ target_bit_size := int(size_of(T) * 8)
+ when !intrinsics.type_is_unsigned(T) {
+ if a.sign == .Zero_or_Positive {
+ target_bit_size -= 1
+ }
+ } else {
+ if a.sign == .Negative {
+ return 0, .Integer_Underflow
}
}
+ bits_used := internal_count_bits(a)
+
+ if bits_used > target_bit_size {
+ if a.sign == .Negative {
+ return min(T), .Integer_Underflow
+ }
+ return max(T), .Integer_Overflow
+ }
+
+ for i := a.used; i > 0; i -= 1 {
+ res <<= _DIGIT_BITS
+ res |= T(a.digit[i - 1])
+ }
+
when !intrinsics.type_is_unsigned(T) {
- /*
- Mask off sign bit.
- */
- res ~= 1 << uint(size_in_bits - 1)
/*
Set the sign.
*/
@@ -2594,7 +2646,7 @@ internal_int_shrmod :: proc(quotient, remainder, numerator: ^Int, bits: int, all
Shift by as many digits in the bit count.
*/
if bits >= _DIGIT_BITS {
- internal_shr_digit(quotient, bits / _DIGIT_BITS) or_return
+ _private_int_shr_leg(quotient, bits / _DIGIT_BITS) or_return
}
/*
@@ -2633,37 +2685,6 @@ internal_int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.all
}
internal_shr :: proc { internal_int_shr, }
-/*
- Shift right by `digits` * _DIGIT_BITS bits.
-*/
-internal_int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- if digits <= 0 { return nil }
-
- /*
- If digits > used simply zero and return.
- */
- if digits > quotient.used { return internal_zero(quotient) }
-
- /*
- Much like `int_shl_digit`, this is implemented using a sliding window,
- except the window goes the other way around.
-
- b-2 | b-1 | b0 | b1 | b2 | ... | bb | ---->
- /\ | ---->
- \-------------------/ ---->
- */
-
- #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 {
- quotient.digit[x] = quotient.digit[x + digits]
- }
- quotient.used -= digits
- internal_zero_unused(quotient)
- return internal_clamp(quotient)
-}
-internal_shr_digit :: proc { internal_int_shr_digit, }
-
/*
Shift right by a certain bit count with sign extension.
*/
@@ -2702,7 +2723,7 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca
Shift by as many digits in the bit count as we have.
*/
if bits >= _DIGIT_BITS {
- internal_shl_digit(dest, bits / _DIGIT_BITS) or_return
+ _private_int_shl_leg(dest, bits / _DIGIT_BITS) or_return
}
/*
@@ -2732,45 +2753,6 @@ internal_int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.alloca
}
internal_shl :: proc { internal_int_shl, }
-
-/*
- Shift left by `digits` * _DIGIT_BITS bits.
-*/
-internal_int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- context.allocator = allocator
-
- if digits <= 0 { return nil }
-
- /*
- No need to shift a zero.
- */
- if #force_inline internal_is_zero(quotient) {
- return nil
- }
-
- /*
- Resize `quotient` to accomodate extra digits.
- */
- #force_inline internal_grow(quotient, quotient.used + digits) or_return
-
- /*
- Increment the used by the shift amount then copy upwards.
- */
-
- /*
- Much like `int_shr_digit`, this is implemented using a sliding window,
- except the window goes the other way around.
- */
- #no_bounds_check for x := quotient.used; x > 0; x -= 1 {
- quotient.digit[x+digits-1] = quotient.digit[x-1]
- }
-
- quotient.used += digits
- mem.zero_slice(quotient.digit[:digits])
- return nil
-}
-internal_shl_digit :: proc { internal_int_shl_digit, }
-
/*
Count bits in an `Int`.
Assumes `a` not to be `nil` and to have been initialized.
diff --git a/core/math/big/logical.odin b/core/math/big/logical.odin
index dbcf566c8..b5de4cabf 100644
--- a/core/math/big/logical.odin
+++ b/core/math/big/logical.odin
@@ -8,6 +8,8 @@
This file contains logical operations like `and`, `or` and `xor`.
*/
+
+
package math_big
/*
@@ -86,21 +88,6 @@ int_shr :: proc(dest, source: ^Int, bits: int, allocator := context.allocator) -
}
shr :: proc { int_shr, }
-/*
- Shift right by `digits` * _DIGIT_BITS bits.
-*/
-int_shr_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- /*
- Check that `quotient` is usable.
- */
- assert_if_nil(quotient)
- context.allocator = allocator
-
- internal_clear_if_uninitialized(quotient) or_return
- return #force_inline internal_int_shr_digit(quotient, digits)
-}
-shr_digit :: proc { int_shr_digit, }
-
/*
Shift right by a certain bit count with sign extension.
*/
@@ -124,20 +111,4 @@ int_shl :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (
internal_clear_if_uninitialized(dest, src) or_return
return #force_inline internal_int_shl(dest, src, bits)
}
-shl :: proc { int_shl, }
-
-
-/*
- Shift left by `digits` * _DIGIT_BITS bits.
-*/
-int_shl_digit :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
- /*
- Check that `quotient` is usable.
- */
- assert_if_nil(quotient)
- context.allocator = allocator
-
- internal_clear_if_uninitialized(quotient) or_return
- return #force_inline internal_int_shl_digit(quotient, digits)
-}
-shl_digit :: proc { int_shl_digit, };
\ No newline at end of file
+shl :: proc { int_shl, }
\ No newline at end of file
diff --git a/core/math/big/prime.odin b/core/math/big/prime.odin
index eb0cd644c..3cce69675 100644
--- a/core/math/big/prime.odin
+++ b/core/math/big/prime.odin
@@ -8,6 +8,8 @@
This file contains prime finding operations.
*/
+
+
package math_big
import rnd "core:math/rand"
diff --git a/core/math/big/private.odin b/core/math/big/private.odin
index 14a27f600..419f2103f 100644
--- a/core/math/big/private.odin
+++ b/core/math/big/private.odin
@@ -15,6 +15,8 @@
These aren't exported for the same reasons.
*/
+
+
package math_big
import "core:intrinsics"
@@ -211,12 +213,12 @@ _private_int_mul_toom :: proc(dest, a, b: ^Int, allocator := context.allocator)
/*
P = b1*x^4+ S2*x^3+ S1*x^2+ a1*x + a0;
*/
- internal_shl_digit(b1, 4 * B) or_return
- internal_shl_digit(S2, 3 * B) or_return
+ _private_int_shl_leg(b1, 4 * B) or_return
+ _private_int_shl_leg(S2, 3 * B) or_return
internal_add(b1, b1, S2) or_return
- internal_shl_digit(S1, 2 * B) or_return
+ _private_int_shl_leg(S1, 2 * B) or_return
internal_add(b1, b1, S1) or_return
- internal_shl_digit(a1, 1 * B) or_return
+ _private_int_shl_leg(a1, 1 * B) or_return
internal_add(b1, b1, a1) or_return
internal_add(dest, b1, a0) or_return
@@ -317,8 +319,8 @@ _private_int_mul_karatsuba :: proc(dest, a, b: ^Int, allocator := context.alloca
/*
shift by B.
*/
- internal_shl_digit(t1, B) or_return /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<= n then x = x - n
@@ -2026,7 +2028,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
/*
q1 = x / b**(k-1)
*/
- internal_shr_digit(q, um - 1)
+ _private_int_shr_leg(q, um - 1)
/*
According to HAC this optimization is ok.
@@ -2040,7 +2042,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
/*
q3 = q2 / b**(k+1)
*/
- internal_shr_digit(q, um + 1)
+ _private_int_shr_leg(q, um + 1)
/*
x = x mod b**(k+1), quick (no division)
@@ -2062,7 +2064,7 @@ _private_int_reduce :: proc(x, m, mu: ^Int, allocator := context.allocator) -> (
*/
if internal_is_negative(x) {
internal_set(q, 1) or_return
- internal_shl_digit(q, um + 1) or_return
+ _private_int_shl_leg(q, um + 1) or_return
internal_add(x, x, q) or_return
}
@@ -3192,6 +3194,74 @@ _private_copy_digits :: proc(dest, src: ^Int, digits: int, offset := int(0)) ->
return nil
}
+
+/*
+ Shift left by `digits` * _DIGIT_BITS bits.
+*/
+_private_int_shl_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ if digits <= 0 { return nil }
+
+ /*
+ No need to shift a zero.
+ */
+ if #force_inline internal_is_zero(quotient) {
+ return nil
+ }
+
+ /*
+ Resize `quotient` to accomodate extra digits.
+ */
+ #force_inline internal_grow(quotient, quotient.used + digits) or_return
+
+ /*
+ Increment the used by the shift amount then copy upwards.
+ */
+
+ /*
+ Much like `_private_int_shr_leg`, this is implemented using a sliding window,
+ except the window goes the other way around.
+ */
+ #no_bounds_check for x := quotient.used; x > 0; x -= 1 {
+ quotient.digit[x+digits-1] = quotient.digit[x-1]
+ }
+
+ quotient.used += digits
+ mem.zero_slice(quotient.digit[:digits])
+ return nil
+}
+
+/*
+ Shift right by `digits` * _DIGIT_BITS bits.
+*/
+_private_int_shr_leg :: proc(quotient: ^Int, digits: int, allocator := context.allocator) -> (err: Error) {
+ context.allocator = allocator
+
+ if digits <= 0 { return nil }
+
+ /*
+ If digits > used simply zero and return.
+ */
+ if digits > quotient.used { return internal_zero(quotient) }
+
+ /*
+ Much like `int_shl_digit`, this is implemented using a sliding window,
+ except the window goes the other way around.
+
+ b-2 | b-1 | b0 | b1 | b2 | ... | bb | ---->
+ /\ | ---->
+ \-------------------/ ---->
+ */
+
+ #no_bounds_check for x := 0; x < (quotient.used - digits); x += 1 {
+ quotient.digit[x] = quotient.digit[x + digits]
+ }
+ quotient.used -= digits
+ internal_zero_unused(quotient)
+ return internal_clamp(quotient)
+}
+
/*
======================== End of private procedures =======================
diff --git a/core/math/big/public.odin b/core/math/big/public.odin
index 2673a262f..3227d7bc4 100644
--- a/core/math/big/public.odin
+++ b/core/math/big/public.odin
@@ -8,6 +8,8 @@
This file contains basic arithmetic operations like `add`, `sub`, `mul`, `div`, ...
*/
+
+
package math_big
import "core:intrinsics"
diff --git a/core/math/big/radix.odin b/core/math/big/radix.odin
index 760c49d77..2b758dc35 100644
--- a/core/math/big/radix.odin
+++ b/core/math/big/radix.odin
@@ -12,6 +12,8 @@
- Use Barrett reduction for non-powers-of-two.
- Also look at extracting and splatting several digits at once.
*/
+
+
package math_big
import "core:intrinsics"
diff --git a/core/math/big/rat.odin b/core/math/big/rat.odin
index 121f0ab50..c3efc30aa 100644
--- a/core/math/big/rat.odin
+++ b/core/math/big/rat.odin
@@ -42,9 +42,9 @@ rat_set_f64 :: proc(dst: ^Rat, f: f64, allocator := context.allocator) -> (err:
dst.a.sign = .Negative if f < 0 else .Zero_or_Positive
if shift > 0 {
- internal_int_shl_digit(&dst.b, shift) or_return
+ internal_int_shl(&dst.b, &dst.b, shift) or_return
} else {
- internal_int_shl_digit(&dst.a, -shift) or_return
+ internal_int_shl(&dst.a, &dst.a, -shift) or_return
}
return internal_rat_norm(dst)
@@ -112,14 +112,14 @@ rat_set_u64 :: proc(dst: ^Rat, x: u64, allocator := context.allocator) -> (err:
assert_if_nil(dst)
context.allocator = allocator
internal_set(&dst.a, x) or_return
- internal_set(&dst.a, 1) or_return
+ internal_set(&dst.b, 1) or_return
return
}
rat_set_i64 :: proc(dst: ^Rat, x: i64, allocator := context.allocator) -> (err: Error) {
assert_if_nil(dst)
context.allocator = allocator
internal_set(&dst.a, x) or_return
- internal_set(&dst.a, 1) or_return
+ internal_set(&dst.b, 1) or_return
return
}
@@ -265,7 +265,7 @@ rat_mul_rat :: proc(dst, x, y: ^Rat, allocator := context.allocator) -> (err: Er
return
}
- int_sub(&dst.a, &x.a, &y.a) or_return
+ int_mul(&dst.a, &x.a, &y.a) or_return
internal_int_mul_denom(&dst.b, &x.b, &y.b) or_return
return internal_rat_norm(dst)
}
@@ -389,9 +389,9 @@ internal_rat_to_float :: proc($T: typeid, z: ^Rat, allocator := context.allocato
internal_int_abs(b2, b) or_return
if shift := MSIZE2 - exp; shift > 0 {
- internal_int_shl_digit(a2, shift) or_return
- } else {
- internal_int_shl_digit(b2, -shift) or_return
+ internal_int_shl(a2, a2, shift) or_return
+ } else if shift < 0 {
+ internal_int_shl(b2, b2, -shift) or_return
}
q, r := &Int{}, &Int{}
diff --git a/core/math/big/tune.odin b/core/math/big/tune.odin
index d67ff61b4..78a20c12b 100644
--- a/core/math/big/tune.odin
+++ b/core/math/big/tune.odin
@@ -1,4 +1,3 @@
-//+ignore
/*
Copyright 2021 Jeroen van Rijn .
Made available under Odin's BSD-3 license.
@@ -7,6 +6,8 @@
For the theoretical underpinnings, see Knuth's The Art of Computer Programming, Volume 2, section 4.3.
The code started out as an idiomatic source port of libTomMath, which is in the public domain, with thanks.
*/
+
+//+ignore
package math_big
import "core:time"
diff --git a/core/math/bits/bits.odin b/core/math/bits/bits.odin
index bff984cc7..850e8038a 100644
--- a/core/math/bits/bits.odin
+++ b/core/math/bits/bits.odin
@@ -69,29 +69,29 @@ rotate_left :: proc(x: uint, k: int) -> uint {
}
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) } }
+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) } }
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) } }
+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) } }
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) } }
+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) } }
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) } }
+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) } }
diff --git a/core/math/ease/ease.odin b/core/math/ease/ease.odin
new file mode 100644
index 000000000..5a767b5a9
--- /dev/null
+++ b/core/math/ease/ease.odin
@@ -0,0 +1,483 @@
+// easing procedures and flux easing used for animations
+package ease
+
+import "core:math"
+import "core:intrinsics"
+import "core:time"
+
+@(private) PI_2 :: math.PI / 2
+
+// converted to odin from https://github.com/warrenm/AHEasing
+// with additional enum based call
+
+// Modeled after the parabola y = x^2
+quadratic_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p * p
+}
+
+// Modeled after the parabola y = -x^2 + 2x
+quadratic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return -(p * (p - 2))
+}
+
+// Modeled after the piecewise quadratic
+// y = (1/2)((2x)^2) ; [0, 0.5)
+// y = -(1/2)((2x-1)*(2x-3) - 1) ; [0.5, 1]
+quadratic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 2 * p * p
+ } else {
+ return (-2 * p * p) + (4 * p) - 1
+ }
+}
+
+// Modeled after the cubic y = x^3
+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
+cubic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f + 1
+}
+
+// Modeled after the piecewise cubic
+// y = (1/2)((2x)^3) ; [0, 0.5)
+// y = (1/2)((2x-2)^3 + 2) ; [0.5, 1]
+cubic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 4 * p * p * p
+ } else {
+ f := (2 * p) - 2
+ return 0.5 * f * f * f + 1
+ }
+}
+
+// Modeled after the quartic x^4
+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
+quartic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f * (1 - p) + 1
+}
+
+// Modeled after the piecewise quartic
+// y = (1/2)((2x)^4) ; [0, 0.5)
+// y = -(1/2)((2x-2)^4 - 2) ; [0.5, 1]
+quartic_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ return 8 * p * p * p * p
+ } else {
+ f := p - 1
+ return -8 * f * f * f * f + 1
+ }
+}
+
+// Modeled after the quintic y = x^5
+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
+quintic_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ f := p - 1
+ return f * f * f * f * f + 1
+}
+
+// Modeled after the piecewise quintic
+// y = (1/2)((2x)^5) ; [0, 0.5)
+// y = (1/2)((2x-2)^5 + 2) ; [0.5, 1]
+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
+ } else {
+ f := (2 * p) - 2
+ return 0.5 * f * f * f * f * f + 1
+ }
+}
+
+// Modeled after quarter-cycle of sine wave
+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)
+sine_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sin(p * PI_2)
+}
+
+// Modeled after half sine wave
+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
+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
+circular_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return math.sqrt((2 - p) * p)
+}
+
+// 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]
+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)))
+ } else {
+ return 0.5 * (math.sqrt(-((2 * p) - 3) * ((2 * p) - 1)) + 1)
+ }
+}
+
+// Modeled after the exponential function y = 2^(10(x - 1))
+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
+exponential_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return p == 1.0 ? p : 1 - math.pow(2, -10 * p)
+}
+
+// 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]
+exponential_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p == 0.0 || p == 1.0 {
+ return p
+ }
+
+ if p < 0.5 {
+ return 0.5 * math.pow(2, (20 * p) - 10)
+ } else {
+ return -0.5 * math.pow(2, (-20 * p) + 10) + 1
+ }
+}
+
+// Modeled after the damped sine wave y = sin(13pi/2*x)*pow(2, 10 * (x - 1))
+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
+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
+}
+
+// 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]
+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))
+ } else {
+ return 0.5 * (math.sin(-13 * PI_2 * ((2 * p - 1) + 1)) * math.pow(2, -10 * (2 * p - 1)) + 2)
+ }
+}
+
+// Modeled after the overshooting cubic y = x^3-x*sin(x*pi)
+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))
+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))
+}
+
+// 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]
+back_in_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 0.5 {
+ f := 2 * p
+ return 0.5 * (f * f * f - f * math.sin(f * math.PI))
+ } else {
+ f := (1 - (2*p - 1))
+ return 0.5 * (1 - (f * f * f - f * math.sin(f * math.PI))) + 0.5
+ }
+}
+
+bounce_in :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ return 1 - bounce_out(1 - p)
+}
+
+bounce_out :: proc "contextless" (p: $T) -> T where intrinsics.type_is_float(T) {
+ if p < 4/11.0 {
+ return (121 * p * p)/16.0
+ } else if p < 8/11.0 {
+ return (363/40.0 * p * p) - (99/10.0 * p) + 17/5.0
+ } else if p < 9/10.0 {
+ return (4356/361.0 * p * p) - (35442/1805.0 * p) + 16061/1805.0
+ } else {
+ return (54/5.0 * p * p) - (513/25.0 * p) + 268/25.0
+ }
+}
+
+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)
+ } else {
+ return 0.5 * bounce_out(p * 2 - 1) + 0.5
+ }
+}
+
+// additional enum variant
+
+Ease :: enum {
+ Linear,
+
+ Quadratic_In,
+ Quadratic_Out,
+ Quadratic_In_Out,
+
+ Cubic_In,
+ Cubic_Out,
+ Cubic_In_Out,
+
+ Quartic_In,
+ Quartic_Out,
+ Quartic_In_Out,
+
+ Quintic_In,
+ Quintic_Out,
+ Quintic_In_Out,
+
+ Sine_In,
+ Sine_Out,
+ Sine_In_Out,
+
+ Circular_In,
+ Circular_Out,
+ Circular_In_Out,
+
+ Exponential_In,
+ Exponential_Out,
+ Exponential_In_Out,
+
+ Elastic_In,
+ Elastic_Out,
+ Elastic_In_Out,
+
+ Back_In,
+ Back_Out,
+ Back_In_Out,
+
+ Bounce_In,
+ Bounce_Out,
+ Bounce_In_Out,
+}
+
+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 .Cubic_In: return cubic_in(p)
+ case .Cubic_Out: return cubic_out(p)
+ case .Cubic_In_Out: return cubic_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 .Quintic_In: return quintic_in(p)
+ case .Quintic_Out: return quintic_out(p)
+ case .Quintic_In_Out: return quintic_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 .Circular_In: return circular_in(p)
+ case .Circular_Out: return circular_out(p)
+ case .Circular_In_Out: return circular_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 .Elastic_In: return elastic_in(p)
+ case .Elastic_Out: return elastic_out(p)
+ case .Elastic_In_Out: return elastic_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
+ return 0
+}
+
+Flux_Map :: struct($T: typeid) {
+ values: map[^T]Flux_Tween(T),
+}
+
+Flux_Tween :: struct($T: typeid) {
+ value: ^T,
+ start: T,
+ diff: T,
+ goal: T,
+
+ delay: f64, // in seconds
+ duration: time.Duration,
+
+ progress: f64,
+ rate: f64,
+ type: Ease,
+
+ inited: bool,
+
+ // callbacks, data can be set, will be pushed to callback
+ data: rawptr, // by default gets set to value input
+ on_start: proc(flux: ^Flux_Map(T), data: rawptr),
+ on_update: proc(flux: ^Flux_Map(T), data: rawptr),
+ on_complete: proc(flux: ^Flux_Map(T), data: rawptr),
+}
+
+// init flux map to a float type and a wanted cap
+flux_init :: proc($T: typeid, cap := 8) -> Flux_Map(T) where intrinsics.type_is_float(T) {
+ return {
+ make(map[^T]Flux_Tween(T), cap),
+ }
+}
+
+// delete map content
+flux_destroy :: proc(flux: Flux_Map($T)) where intrinsics.type_is_float(T) {
+ delete(flux.values)
+}
+
+// clear map content, stops all animations
+flux_clear :: proc(flux: ^Flux_Map($T)) where intrinsics.type_is_float(T) {
+ clear(&flux.values)
+}
+
+// append / overwrite existing tween value to parameters
+// rest is initialized in flux_tween_init, inside update
+// return value can be used to set callbacks
+flux_to :: proc(
+ flux: ^Flux_Map($T),
+ value: ^f32,
+ goal: f32,
+ type: Ease = .Quadratic_Out,
+ duration: time.Duration = time.Second,
+ delay: f64 = 0,
+) -> (tween: ^Flux_Tween(T)) where intrinsics.type_is_float(T) {
+ if res, ok := &flux.values[value]; ok {
+ tween = res
+ } else {
+ flux.values[value] = {}
+ tween = &flux.values[value]
+ }
+
+ tween^ = {
+ value = value,
+ goal = goal,
+ duration = duration,
+ delay = delay,
+ type = type,
+ data = value,
+ }
+
+ return
+}
+
+// init internal properties
+flux_tween_init :: proc(tween: ^Flux_Tween($T), duration: time.Duration) where intrinsics.type_is_float(T) {
+ tween.inited = true
+ tween.start = tween.value^
+ tween.diff = tween.goal - tween.value^
+ s := time.duration_seconds(duration)
+ tween.rate = duration > 0 ? 1.0 / s : 0
+ tween.progress = duration > 0 ? 0 : 1
+}
+
+// update all tweens, wait for their delay if one exists
+// calls callbacks in all stages, when they're filled
+// deletes tween from the map after completion
+flux_update :: proc(flux: ^Flux_Map($T), dt: f64) where intrinsics.type_is_float(T) {
+ for key, tween in &flux.values {
+ delay_remainder := f64(0)
+
+ // Update delay if necessary.
+ if tween.delay > 0 {
+ tween.delay -= dt
+
+ if tween.delay < 0 {
+ // We finished the delay, but in doing so consumed part of this frame's `dt` budget.
+ // Keep track of it so we can apply it to this tween without affecting others.
+ delay_remainder = tween.delay
+ // We're done with this delay.
+ tween.delay = 0
+ }
+ }
+
+ // We either had no delay, or the delay has been consumed.
+ if tween.delay <= 0 {
+ if !tween.inited {
+ flux_tween_init(&tween, tween.duration)
+
+ if tween.on_start != nil {
+ tween.on_start(flux, tween.data)
+ }
+ }
+
+ // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be
+ // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt`
+ // to the tween so it advances properly, instead of too much or little.
+ tween.progress += tween.rate * (dt + delay_remainder)
+ x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress)
+ tween.value^ = tween.start + tween.diff * T(x)
+
+ if tween.on_update != nil {
+ tween.on_update(flux, tween.data)
+ }
+
+ if tween.progress >= 1 {
+ delete_key(&flux.values, key)
+
+ if tween.on_complete != nil {
+ tween.on_complete(flux, tween.data)
+ }
+ }
+ }
+ }
+}
+
+// stop a specific key inside the map
+// returns true when it successfully removed the key
+flux_stop :: proc(flux: ^Flux_Map($T), key: ^T) -> bool where intrinsics.type_is_float(T) {
+ if key in flux.values {
+ delete_key(&flux.values, key)
+ return true
+ }
+
+ return false
+}
+
+// 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
+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
+ } else {
+ return 0
+ }
+}
diff --git a/core/math/linalg/extended.odin b/core/math/linalg/extended.odin
index ba64356ce..c2bf5552a 100644
--- a/core/math/linalg/extended.odin
+++ b/core/math/linalg/extended.odin
@@ -103,10 +103,10 @@ max :: proc{max_single, max_double, max_triple}
abs :: proc(a: $T) -> (out: T) where IS_NUMERIC(ELEM_TYPE(T)) {
when IS_ARRAY(T) {
for i in 0.. (c: f64) {
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
inner_product :: dot
-outer_product :: proc(a: $A/[$M]$E, b: $B/[$N]E) -> (out: [M][N]E) where IS_NUMERIC(E) #no_bounds_check {
- for i in 0.. Q where IS_QUATERNION(Q) {
return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0)
@@ -163,65 +157,28 @@ identity :: proc($T: typeid/[$N][N]$E) -> (m: T) #no_bounds_check {
return m
}
-trace :: proc(m: $T/[$N][N]$E) -> (tr: E) {
- for i in 0.. (m: (T when N == M else [M][N]E)) #no_bounds_check {
- for j in 0.. (c: M)
+matrix_mul :: proc(a, b: $M/matrix[$N, N]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
- for i in 0.. (c: M)
+matrix_comp_mul :: proc(a, b: $M/matrix[$I, $J]$E) -> (c: M)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
- for j in 0.. (c: [K][I]E)
+matrix_mul_differ :: proc(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 {
- for k in 0.. (c: B)
+matrix_mul_vector :: proc(a: $A/matrix[$I, $J]$E, b: $B/[J]E) -> (c: B)
where !IS_ARRAY(E), IS_NUMERIC(E) #no_bounds_check {
- for i in 0.. Q where IS_QUATERNION(Q) {
@@ -270,8 +227,8 @@ mul :: proc{
vector_to_ptr :: proc(v: ^$V/[$N]$E) -> ^E where IS_NUMERIC(E), N > 0 #no_bounds_check {
return &v[0]
}
-matrix_to_ptr :: proc(m: ^$A/[$I][$J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
- return &m[0][0]
+matrix_to_ptr :: proc(m: ^$A/matrix[$I, $J]$E) -> ^E where IS_NUMERIC(E), I > 0, J > 0 #no_bounds_check {
+ return &m[0, 0]
}
to_ptr :: proc{vector_to_ptr, matrix_to_ptr}
@@ -330,10 +287,10 @@ array_cast :: proc(v: $A/[$N]$T, $Elem_Type: typeid) -> (w: [N]Elem_Type) #no_bo
return
}
-matrix_cast :: proc(v: $A/[$M][$N]$T, $Elem_Type: typeid) -> (w: [M][N]Elem_Type) #no_bounds_check {
- for i in 0.. (w: matrix[M, N]Elem_Type) #no_bounds_check {
+ for j in 0.. [N]uint { return array_cast(v, ui
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_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) }
diff --git a/core/math/linalg/glsl/linalg_glsl.odin b/core/math/linalg/glsl/linalg_glsl.odin
index 3b4976452..74753f66f 100644
--- a/core/math/linalg/glsl/linalg_glsl.odin
+++ b/core/math/linalg/glsl/linalg_glsl.odin
@@ -473,6 +473,25 @@ floor_dvec3 :: proc "c" (x: dvec3) -> dvec3 { return {floor(x.x), floor(x.y), fl
floor_dvec4 :: proc "c" (x: dvec4) -> dvec4 { return {floor(x.x), floor(x.y), floor(x.z), floor(x.w)} }
+
+round :: proc{
+ round_f32,
+ round_f64,
+ round_vec2,
+ round_vec3,
+ round_vec4,
+ round_dvec2,
+ 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)} }
+
+
ceil :: proc{
ceil_f32,
ceil_f64,
@@ -693,23 +712,22 @@ saturate :: proc{
saturate_uvec3,
saturate_uvec4,
}
-saturate_i32 :: proc "c" (x, y, z: i32) -> i32 { return builtin.clamp(x, 0, 1) }
-saturate_u32 :: proc "c" (x, y, z: u32) -> u32 { return builtin.clamp(x, 0, 1) }
-saturate_f32 :: proc "c" (x, y, z: f32) -> f32 { return builtin.clamp(x, 0, 1) }
-saturate_f64 :: proc "c" (x, y, z: f64) -> f64 { return builtin.clamp(x, 0, 1) }
-saturate_vec2 :: proc "c" (x, y, z: vec2) -> vec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_vec3 :: proc "c" (x, y, z: vec3) -> vec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_vec4 :: proc "c" (x, y, z: vec4) -> vec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_dvec2 :: proc "c" (x, y, z: dvec2) -> dvec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_dvec3 :: proc "c" (x, y, z: dvec3) -> dvec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_dvec4 :: proc "c" (x, y, z: dvec4) -> dvec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_ivec2 :: proc "c" (x, y, z: ivec2) -> ivec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_ivec3 :: proc "c" (x, y, z: ivec3) -> ivec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_ivec4 :: proc "c" (x, y, z: ivec4) -> ivec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_uvec2 :: proc "c" (x, y, z: uvec2) -> uvec2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_uvec3 :: proc "c" (x, y, z: uvec3) -> uvec3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_uvec4 :: proc "c" (x, y, z: uvec4) -> uvec4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-
+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)} }
mix :: proc{
mix_f32,
@@ -1597,7 +1615,7 @@ quatNlerp :: proc "c" (a, b: quat, t: f32) -> (c: quat) {
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 c/builtin.abs(c)
+ return c/quat(builtin.abs(c))
}
quatSlerp :: proc "c" (x, y: quat, t: f32) -> (q: quat) {
@@ -1699,7 +1717,7 @@ dquatNlerp :: proc "c" (a, b: dquat, t: f64) -> (c: dquat) {
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 c/builtin.abs(c)
+ return c/dquat(builtin.abs(c))
}
dquatSlerp :: proc "c" (x, y: dquat, t: f64) -> (q: dquat) {
diff --git a/core/math/linalg/glsl/linalg_glsl_math.odin b/core/math/linalg/glsl/linalg_glsl_math.odin
index 68f43a2f7..968a3fa5e 100644
--- a/core/math/linalg/glsl/linalg_glsl_math.odin
+++ b/core/math/linalg/glsl/linalg_glsl_math.odin
@@ -23,6 +23,7 @@ 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) }
fract_f32 :: proc "c" (x: f32) -> f32 {
@@ -53,6 +54,7 @@ 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) }
fract_f64 :: proc "c" (x: f64) -> f64 {
diff --git a/core/math/linalg/hlsl/linalg_hlsl.odin b/core/math/linalg/hlsl/linalg_hlsl.odin
index 0eb8413a9..3f73dcd1f 100644
--- a/core/math/linalg/hlsl/linalg_hlsl.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl.odin
@@ -551,6 +551,23 @@ floor_double2 :: proc "c" (x: double2) -> double2 { return {floor(x.x), floor(x.
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)} }
+round :: proc{
+ round_float,
+ round_double,
+ round_float2,
+ round_float3,
+ round_float4,
+ round_double2,
+ 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)} }
+
ceil :: proc{
ceil_float,
@@ -570,6 +587,69 @@ ceil_double3 :: proc "c" (x: double3) -> double3 { return {ceil(x.x), ceil(x.y),
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)} }
+
+// isfinite is the opposite of isinf and returns true if the number is neither positive-infinite or negative-infinite
+isfinite :: proc{
+ isfinite_float,
+ isfinite_float2,
+ isfinite_float3,
+ isfinite_float4,
+ isfinite_double,
+ isfinite_double2,
+ isfinite_double3,
+ isfinite_double4,
+}
+
+
+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)} }
+
+// isinf is the opposite of isfinite and returns true if the number is either positive-infinite or negative-infinite
+isinf :: proc{
+ isinf_float,
+ isinf_float2,
+ isinf_float3,
+ isinf_float4,
+ isinf_double,
+ isinf_double2,
+ isinf_double3,
+ isinf_double4,
+}
+
+
+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)} }
+
+// isnan returns true if the input value is the special case of Not-A-Number
+isnan :: proc{
+ isnan_float,
+ isnan_float2,
+ isnan_float3,
+ isnan_float4,
+ isnan_double,
+ isnan_double2,
+ isnan_double3,
+ isnan_double4,
+}
+
fmod :: proc{
fmod_float,
fmod_double,
@@ -772,22 +852,22 @@ saturate :: proc{
saturate_uint3,
saturate_uint4,
}
-saturate_int :: proc "c" (x, y, z: int) -> int { return builtin.clamp(x, 0, 1) }
-saturate_uint :: proc "c" (x, y, z: uint) -> uint { return builtin.clamp(x, 0, 1) }
-saturate_float :: proc "c" (x, y, z: float) -> float { return builtin.clamp(x, 0, 1) }
-saturate_double :: proc "c" (x, y, z: double) -> double { return builtin.clamp(x, 0, 1) }
-saturate_float2 :: proc "c" (x, y, z: float2) -> float2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_float3 :: proc "c" (x, y, z: float3) -> float3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_float4 :: proc "c" (x, y, z: float4) -> float4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_double2 :: proc "c" (x, y, z: double2) -> double2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_double3 :: proc "c" (x, y, z: double3) -> double3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_double4 :: proc "c" (x, y, z: double4) -> double4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_int2 :: proc "c" (x, y, z: int2) -> int2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_int3 :: proc "c" (x, y, z: int3) -> int3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_int4 :: proc "c" (x, y, z: int4) -> int4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
-saturate_uint2 :: proc "c" (x, y, z: uint2) -> uint2 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1)} }
-saturate_uint3 :: proc "c" (x, y, z: uint3) -> uint3 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1)} }
-saturate_uint4 :: proc "c" (x, y, z: uint4) -> uint4 { return {builtin.clamp(x.x, 0, 1), builtin.clamp(x.y, 0, 1), builtin.clamp(x.z, 0, 1), builtin.clamp(x.w, 0, 1)} }
+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)} }
lerp :: proc{
diff --git a/core/math/linalg/hlsl/linalg_hlsl_math.odin b/core/math/linalg/hlsl/linalg_hlsl_math.odin
index d884c3d31..91c542b59 100644
--- a/core/math/linalg/hlsl/linalg_hlsl_math.odin
+++ b/core/math/linalg/hlsl/linalg_hlsl_math.odin
@@ -26,7 +26,9 @@ 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) }
frac_float :: proc "c" (x: float) -> float {
if x >= 0 {
@@ -35,6 +37,7 @@ frac_float :: proc "c" (x: float) -> float {
return math.trunc(-x) + x
}
+
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) }
@@ -59,7 +62,9 @@ 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) }
frac_double :: proc "c" (x: double) -> double {
if x >= 0 {
diff --git a/core/math/linalg/specific.odin b/core/math/linalg/specific.odin
index 5cb68e3a8..a4aaeb012 100644
--- a/core/math/linalg/specific.odin
+++ b/core/math/linalg/specific.odin
@@ -1,5 +1,6 @@
package linalg
+import "core:builtin"
import "core:math"
F16_EPSILON :: 1e-3
@@ -10,25 +11,25 @@ Vector2f16 :: distinct [2]f16
Vector3f16 :: distinct [3]f16
Vector4f16 :: distinct [4]f16
-Matrix1x1f16 :: distinct [1][1]f16
-Matrix1x2f16 :: distinct [1][2]f16
-Matrix1x3f16 :: distinct [1][3]f16
-Matrix1x4f16 :: distinct [1][4]f16
+Matrix1x1f16 :: distinct matrix[1, 1]f16
+Matrix1x2f16 :: distinct matrix[1, 2]f16
+Matrix1x3f16 :: distinct matrix[1, 3]f16
+Matrix1x4f16 :: distinct matrix[1, 4]f16
-Matrix2x1f16 :: distinct [2][1]f16
-Matrix2x2f16 :: distinct [2][2]f16
-Matrix2x3f16 :: distinct [2][3]f16
-Matrix2x4f16 :: distinct [2][4]f16
+Matrix2x1f16 :: distinct matrix[2, 1]f16
+Matrix2x2f16 :: distinct matrix[2, 2]f16
+Matrix2x3f16 :: distinct matrix[2, 3]f16
+Matrix2x4f16 :: distinct matrix[2, 4]f16
-Matrix3x1f16 :: distinct [3][1]f16
-Matrix3x2f16 :: distinct [3][2]f16
-Matrix3x3f16 :: distinct [3][3]f16
-Matrix3x4f16 :: distinct [3][4]f16
+Matrix3x1f16 :: distinct matrix[3, 1]f16
+Matrix3x2f16 :: distinct matrix[3, 2]f16
+Matrix3x3f16 :: distinct matrix[3, 3]f16
+Matrix3x4f16 :: distinct matrix[3, 4]f16
-Matrix4x1f16 :: distinct [4][1]f16
-Matrix4x2f16 :: distinct [4][2]f16
-Matrix4x3f16 :: distinct [4][3]f16
-Matrix4x4f16 :: distinct [4][4]f16
+Matrix4x1f16 :: distinct matrix[4, 1]f16
+Matrix4x2f16 :: distinct matrix[4, 2]f16
+Matrix4x3f16 :: distinct matrix[4, 3]f16
+Matrix4x4f16 :: distinct matrix[4, 4]f16
Matrix1f16 :: Matrix1x1f16
Matrix2f16 :: Matrix2x2f16
@@ -39,25 +40,25 @@ Vector2f32 :: distinct [2]f32
Vector3f32 :: distinct [3]f32
Vector4f32 :: distinct [4]f32
-Matrix1x1f32 :: distinct [1][1]f32
-Matrix1x2f32 :: distinct [1][2]f32
-Matrix1x3f32 :: distinct [1][3]f32
-Matrix1x4f32 :: distinct [1][4]f32
+Matrix1x1f32 :: distinct matrix[1, 1]f32
+Matrix1x2f32 :: distinct matrix[1, 2]f32
+Matrix1x3f32 :: distinct matrix[1, 3]f32
+Matrix1x4f32 :: distinct matrix[1, 4]f32
-Matrix2x1f32 :: distinct [2][1]f32
-Matrix2x2f32 :: distinct [2][2]f32
-Matrix2x3f32 :: distinct [2][3]f32
-Matrix2x4f32 :: distinct [2][4]f32
+Matrix2x1f32 :: distinct matrix[2, 1]f32
+Matrix2x2f32 :: distinct matrix[2, 2]f32
+Matrix2x3f32 :: distinct matrix[2, 3]f32
+Matrix2x4f32 :: distinct matrix[2, 4]f32
-Matrix3x1f32 :: distinct [3][1]f32
-Matrix3x2f32 :: distinct [3][2]f32
-Matrix3x3f32 :: distinct [3][3]f32
-Matrix3x4f32 :: distinct [3][4]f32
+Matrix3x1f32 :: distinct matrix[3, 1]f32
+Matrix3x2f32 :: distinct matrix[3, 2]f32
+Matrix3x3f32 :: distinct matrix[3, 3]f32
+Matrix3x4f32 :: distinct matrix[3, 4]f32
-Matrix4x1f32 :: distinct [4][1]f32
-Matrix4x2f32 :: distinct [4][2]f32
-Matrix4x3f32 :: distinct [4][3]f32
-Matrix4x4f32 :: distinct [4][4]f32
+Matrix4x1f32 :: distinct matrix[4, 1]f32
+Matrix4x2f32 :: distinct matrix[4, 2]f32
+Matrix4x3f32 :: distinct matrix[4, 3]f32
+Matrix4x4f32 :: distinct matrix[4, 4]f32
Matrix1f32 :: Matrix1x1f32
Matrix2f32 :: Matrix2x2f32
@@ -68,25 +69,25 @@ Vector2f64 :: distinct [2]f64
Vector3f64 :: distinct [3]f64
Vector4f64 :: distinct [4]f64
-Matrix1x1f64 :: distinct [1][1]f64
-Matrix1x2f64 :: distinct [1][2]f64
-Matrix1x3f64 :: distinct [1][3]f64
-Matrix1x4f64 :: distinct [1][4]f64
+Matrix1x1f64 :: distinct matrix[1, 1]f64
+Matrix1x2f64 :: distinct matrix[1, 2]f64
+Matrix1x3f64 :: distinct matrix[1, 3]f64
+Matrix1x4f64 :: distinct matrix[1, 4]f64
-Matrix2x1f64 :: distinct [2][1]f64
-Matrix2x2f64 :: distinct [2][2]f64
-Matrix2x3f64 :: distinct [2][3]f64
-Matrix2x4f64 :: distinct [2][4]f64
+Matrix2x1f64 :: distinct matrix[2, 1]f64
+Matrix2x2f64 :: distinct matrix[2, 2]f64
+Matrix2x3f64 :: distinct matrix[2, 3]f64
+Matrix2x4f64 :: distinct matrix[2, 4]f64
-Matrix3x1f64 :: distinct [3][1]f64
-Matrix3x2f64 :: distinct [3][2]f64
-Matrix3x3f64 :: distinct [3][3]f64
-Matrix3x4f64 :: distinct [3][4]f64
+Matrix3x1f64 :: distinct matrix[3, 1]f64
+Matrix3x2f64 :: distinct matrix[3, 2]f64
+Matrix3x3f64 :: distinct matrix[3, 3]f64
+Matrix3x4f64 :: distinct matrix[3, 4]f64
-Matrix4x1f64 :: distinct [4][1]f64
-Matrix4x2f64 :: distinct [4][2]f64
-Matrix4x3f64 :: distinct [4][3]f64
-Matrix4x4f64 :: distinct [4][4]f64
+Matrix4x1f64 :: distinct matrix[4, 1]f64
+Matrix4x2f64 :: distinct matrix[4, 2]f64
+Matrix4x3f64 :: distinct matrix[4, 3]f64
+Matrix4x4f64 :: distinct matrix[4, 4]f64
Matrix1f64 :: Matrix1x1f64
Matrix2f64 :: Matrix2x2f64
@@ -97,20 +98,20 @@ Quaternionf16 :: distinct quaternion64
Quaternionf32 :: distinct quaternion128
Quaternionf64 :: distinct quaternion256
-MATRIX1F16_IDENTITY :: Matrix1f16{{1}}
-MATRIX2F16_IDENTITY :: Matrix2f16{{1, 0}, {0, 1}}
-MATRIX3F16_IDENTITY :: Matrix3f16{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
-MATRIX4F16_IDENTITY :: Matrix4f16{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
+MATRIX1F16_IDENTITY :: Matrix1f16(1)
+MATRIX2F16_IDENTITY :: Matrix2f16(1)
+MATRIX3F16_IDENTITY :: Matrix3f16(1)
+MATRIX4F16_IDENTITY :: Matrix4f16(1)
-MATRIX1F32_IDENTITY :: Matrix1f32{{1}}
-MATRIX2F32_IDENTITY :: Matrix2f32{{1, 0}, {0, 1}}
-MATRIX3F32_IDENTITY :: Matrix3f32{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
-MATRIX4F32_IDENTITY :: Matrix4f32{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
+MATRIX1F32_IDENTITY :: Matrix1f32(1)
+MATRIX2F32_IDENTITY :: Matrix2f32(1)
+MATRIX3F32_IDENTITY :: Matrix3f32(1)
+MATRIX4F32_IDENTITY :: Matrix4f32(1)
-MATRIX1F64_IDENTITY :: Matrix1f64{{1}}
-MATRIX2F64_IDENTITY :: Matrix2f64{{1, 0}, {0, 1}}
-MATRIX3F64_IDENTITY :: Matrix3f64{{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}
-MATRIX4F64_IDENTITY :: Matrix4f64{{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
+MATRIX1F64_IDENTITY :: Matrix1f64(1)
+MATRIX2F64_IDENTITY :: Matrix2f64(1)
+MATRIX3F64_IDENTITY :: Matrix3f64(1)
+MATRIX4F64_IDENTITY :: Matrix4f64(1)
QUATERNIONF16_IDENTITY :: Quaternionf16(1)
QUATERNIONF32_IDENTITY :: Quaternionf32(1)
@@ -478,21 +479,21 @@ angle_from_quaternion_f16 :: proc(q: Quaternionf16) -> f16 {
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
}
- return math.cos(q.x) * 2
+ return math.acos(q.w) * 2
}
angle_from_quaternion_f32 :: proc(q: Quaternionf32) -> f32 {
if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
}
- return math.cos(q.x) * 2
+ return math.acos(q.w) * 2
}
angle_from_quaternion_f64 :: proc(q: Quaternionf64) -> f64 {
if abs(q.w) > math.SQRT_THREE*0.5 {
return math.asin(q.x*q.x + q.y*q.y + q.z*q.z) * 2
}
- return math.cos(q.x) * 2
+ return math.acos(q.w) * 2
}
angle_from_quaternion :: proc{
angle_from_quaternion_f16,
@@ -558,9 +559,9 @@ quaternion_from_forward_and_up_f16 :: proc(forward, up: Vector3f16) -> Quaternio
s := normalize(cross(f, up))
u := cross(s, f)
m := Matrix3f16{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
tr := trace(m)
@@ -571,26 +572,26 @@ quaternion_from_forward_and_up_f16 :: proc(forward, up: Vector3f16) -> Quaternio
case tr > 0:
S := 2 * math.sqrt(1 + tr)
q.w = 0.25 * S
- q.x = (m[2][1] - m[1][2]) / S
- q.y = (m[0][2] - m[2][0]) / S
- q.z = (m[1][0] - m[0][1]) / S
- case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
- S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2])
- q.w = (m[2][1] - m[1][2]) / S
+ q.x = (m[1, 2] - m[2, 1]) / S
+ q.y = (m[2, 0] - m[0, 2]) / S
+ q.z = (m[0, 1] - m[1, 0]) / S
+ case (m[0, 0] > m[1, 1]) && (m[0, 0] > m[2, 2]):
+ S := 2 * math.sqrt(1 + m[0, 0] - m[1, 1] - m[2, 2])
+ q.w = (m[1, 2] - m[2, 1]) / S
q.x = 0.25 * S
- q.y = (m[0][1] + m[1][0]) / S
- q.z = (m[0][2] + m[2][0]) / S
- case m[1][1] > m[2][2]:
- S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2])
- q.w = (m[0][2] - m[2][0]) / S
- q.x = (m[0][1] + m[1][0]) / S
+ q.y = (m[1, 0] + m[0, 1]) / S
+ q.z = (m[2, 0] + m[0, 2]) / S
+ case m[1, 1] > m[2, 2]:
+ S := 2 * math.sqrt(1 + m[1, 1] - m[0, 0] - m[2, 2])
+ q.w = (m[2, 0] - m[0, 2]) / S
+ q.x = (m[1, 0] + m[0, 1]) / S
q.y = 0.25 * S
- q.z = (m[1][2] + m[2][1]) / S
+ q.z = (m[2, 1] + m[1, 2]) / S
case:
- S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1])
- q.w = (m[1][0] - m[0][1]) / S
- q.x = (m[0][2] - m[2][0]) / S
- q.y = (m[1][2] + m[2][1]) / S
+ S := 2 * math.sqrt(1 + m[2, 2] - m[0, 0] - m[1, 1])
+ q.w = (m[0, 1] - m[1, 0]) / S
+ q.x = (m[2, 0] - m[0, 2]) / S
+ q.y = (m[2, 1] + m[1, 2]) / S
q.z = 0.25 * S
}
@@ -601,9 +602,9 @@ quaternion_from_forward_and_up_f32 :: proc(forward, up: Vector3f32) -> Quaternio
s := normalize(cross(f, up))
u := cross(s, f)
m := Matrix3f32{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
tr := trace(m)
@@ -614,26 +615,26 @@ quaternion_from_forward_and_up_f32 :: proc(forward, up: Vector3f32) -> Quaternio
case tr > 0:
S := 2 * math.sqrt(1 + tr)
q.w = 0.25 * S
- q.x = (m[2][1] - m[1][2]) / S
- q.y = (m[0][2] - m[2][0]) / S
- q.z = (m[1][0] - m[0][1]) / S
- case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
- S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2])
- q.w = (m[2][1] - m[1][2]) / S
+ q.x = (m[1, 2] - m[2, 1]) / S
+ q.y = (m[2, 0] - m[0, 2]) / S
+ q.z = (m[0, 1] - m[1, 0]) / S
+ case (m[0, 0] > m[1, 1]) && (m[0, 0] > m[2, 2]):
+ S := 2 * math.sqrt(1 + m[0, 0] - m[1, 1] - m[2, 2])
+ q.w = (m[1, 2] - m[2, 1]) / S
q.x = 0.25 * S
- q.y = (m[0][1] + m[1][0]) / S
- q.z = (m[0][2] + m[2][0]) / S
- case m[1][1] > m[2][2]:
- S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2])
- q.w = (m[0][2] - m[2][0]) / S
- q.x = (m[0][1] + m[1][0]) / S
+ q.y = (m[1, 0] + m[0, 1]) / S
+ q.z = (m[2, 0] + m[0, 2]) / S
+ case m[1, 1] > m[2, 2]:
+ S := 2 * math.sqrt(1 + m[1, 1] - m[0, 0] - m[2, 2])
+ q.w = (m[2, 0] - m[0, 2]) / S
+ q.x = (m[1, 0] + m[0, 1]) / S
q.y = 0.25 * S
- q.z = (m[1][2] + m[2][1]) / S
+ q.z = (m[2, 1] + m[1, 2]) / S
case:
- S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1])
- q.w = (m[1][0] - m[0][1]) / S
- q.x = (m[0][2] - m[2][0]) / S
- q.y = (m[1][2] + m[2][1]) / S
+ S := 2 * math.sqrt(1 + m[2, 2] - m[0, 0] - m[1, 1])
+ q.w = (m[0, 1] - m[1, 0]) / S
+ q.x = (m[2, 0] - m[0, 2]) / S
+ q.y = (m[2, 1] + m[1, 2]) / S
q.z = 0.25 * S
}
@@ -644,9 +645,9 @@ quaternion_from_forward_and_up_f64 :: proc(forward, up: Vector3f64) -> Quaternio
s := normalize(cross(f, up))
u := cross(s, f)
m := Matrix3f64{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
tr := trace(m)
@@ -657,26 +658,26 @@ quaternion_from_forward_and_up_f64 :: proc(forward, up: Vector3f64) -> Quaternio
case tr > 0:
S := 2 * math.sqrt(1 + tr)
q.w = 0.25 * S
- q.x = (m[2][1] - m[1][2]) / S
- q.y = (m[0][2] - m[2][0]) / S
- q.z = (m[1][0] - m[0][1]) / S
- case (m[0][0] > m[1][1]) && (m[0][0] > m[2][2]):
- S := 2 * math.sqrt(1 + m[0][0] - m[1][1] - m[2][2])
- q.w = (m[2][1] - m[1][2]) / S
+ q.x = (m[1, 2] - m[2, 1]) / S
+ q.y = (m[2, 0] - m[0, 2]) / S
+ q.z = (m[0, 1] - m[1, 0]) / S
+ case (m[0, 0] > m[1, 1]) && (m[0, 0] > m[2, 2]):
+ S := 2 * math.sqrt(1 + m[0, 0] - m[1, 1] - m[2, 2])
+ q.w = (m[1, 2] - m[2, 1]) / S
q.x = 0.25 * S
- q.y = (m[0][1] + m[1][0]) / S
- q.z = (m[0][2] + m[2][0]) / S
- case m[1][1] > m[2][2]:
- S := 2 * math.sqrt(1 + m[1][1] - m[0][0] - m[2][2])
- q.w = (m[0][2] - m[2][0]) / S
- q.x = (m[0][1] + m[1][0]) / S
+ q.y = (m[1, 0] + m[0, 1]) / S
+ q.z = (m[2, 0] + m[0, 2]) / S
+ case m[1, 1] > m[2, 2]:
+ S := 2 * math.sqrt(1 + m[1, 1] - m[0, 0] - m[2, 2])
+ q.w = (m[2, 0] - m[0, 2]) / S
+ q.x = (m[1, 0] + m[0, 1]) / S
q.y = 0.25 * S
- q.z = (m[1][2] + m[2][1]) / S
+ q.z = (m[2, 1] + m[1, 2]) / S
case:
- S := 2 * math.sqrt(1 + m[2][2] - m[0][0] - m[1][1])
- q.w = (m[1][0] - m[0][1]) / S
- q.x = (m[0][2] - m[2][0]) / S
- q.y = (m[1][2] + m[2][1]) / S
+ S := 2 * math.sqrt(1 + m[2, 2] - m[0, 0] - m[1, 1])
+ q.w = (m[0, 1] - m[1, 0]) / S
+ q.x = (m[2, 0] - m[0, 2]) / S
+ q.y = (m[2, 1] + m[1, 2]) / S
q.z = 0.25 * S
}
@@ -842,23 +843,23 @@ quaternion_squad :: proc{
quaternion_from_matrix4_f16 :: proc(m: Matrix4f16) -> (q: Quaternionf16) {
m3: Matrix3f16 = ---
- m3[0][0], m3[0][1], m3[0][2] = m[0][0], m[0][1], m[0][2]
- m3[1][0], m3[1][1], m3[1][2] = m[1][0], m[1][1], m[1][2]
- m3[2][0], m3[2][1], m3[2][2] = m[2][0], m[2][1], m[2][2]
+ 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) {
m3: Matrix3f32 = ---
- m3[0][0], m3[0][1], m3[0][2] = m[0][0], m[0][1], m[0][2]
- m3[1][0], m3[1][1], m3[1][2] = m[1][0], m[1][1], m[1][2]
- m3[2][0], m3[2][1], m3[2][2] = m[2][0], m[2][1], m[2][2]
+ 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) {
m3: Matrix3f64 = ---
- m3[0][0], m3[0][1], m3[0][2] = m[0][0], m[0][1], m[0][2]
- m3[1][0], m3[1][1], m3[1][2] = m[1][0], m[1][1], m[1][2]
- m3[2][0], m3[2][1], m3[2][2] = m[2][0], m[2][1], m[2][2]
+ 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 :: proc{
@@ -869,10 +870,10 @@ quaternion_from_matrix4 :: proc{
quaternion_from_matrix3_f16 :: proc(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]
- four_w_squared_minus_1 := m[0][0] + m[1][1] + m[2][2]
+ four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
+ four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
+ four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
+ four_w_squared_minus_1 := m[0, 0] + m[1, 1] + m[2, 2]
biggest_index := 0
four_biggest_squared_minus_1 := four_w_squared_minus_1
@@ -896,32 +897,32 @@ quaternion_from_matrix3_f16 :: proc(m: Matrix3f16) -> (q: Quaternionf16) {
switch biggest_index {
case 0:
q.w = biggest_val
- q.x = (m[1][2] - m[2][1]) * mult
- q.y = (m[2][0] - m[0][2]) * mult
- q.z = (m[0][1] - m[1][0]) * mult
+ q.x = (m[2, 1] - m[1, 2]) * mult
+ q.y = (m[0, 2] - m[2, 0]) * mult
+ q.z = (m[1, 0] - m[0, 1]) * mult
case 1:
- q.w = (m[1][2] - m[2][1]) * mult
+ q.w = (m[2, 1] - m[1, 2]) * mult
q.x = biggest_val
- q.y = (m[0][1] + m[1][0]) * mult
- q.z = (m[2][0] + m[0][2]) * mult
+ q.y = (m[1, 0] + m[0, 1]) * mult
+ q.z = (m[0, 2] + m[2, 0]) * mult
case 2:
- q.w = (m[2][0] - m[0][2]) * mult
- q.x = (m[0][1] + m[1][0]) * mult
+ q.w = (m[0, 2] - m[2, 0]) * mult
+ q.x = (m[1, 0] + m[0, 1]) * mult
q.y = biggest_val
- q.z = (m[1][2] + m[2][1]) * mult
+ q.z = (m[2, 1] + m[1, 2]) * mult
case 3:
- q.w = (m[0][1] - m[1][0]) * mult
- q.x = (m[2][0] + m[0][2]) * mult
- q.y = (m[1][2] + m[2][1]) * mult
+ q.w = (m[1, 0] - m[0, 1]) * mult
+ q.x = (m[0, 2] + m[2, 0]) * mult
+ q.y = (m[2, 1] + m[1, 2]) * mult
q.z = biggest_val
}
return
}
quaternion_from_matrix3_f32 :: proc(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]
- four_w_squared_minus_1 := m[0][0] + m[1][1] + m[2][2]
+ four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
+ four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
+ four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
+ four_w_squared_minus_1 := m[0, 0] + m[1, 1] + m[2, 2]
biggest_index := 0
four_biggest_squared_minus_1 := four_w_squared_minus_1
@@ -945,32 +946,32 @@ quaternion_from_matrix3_f32 :: proc(m: Matrix3f32) -> (q: Quaternionf32) {
switch biggest_index {
case 0:
q.w = biggest_val
- q.x = (m[1][2] - m[2][1]) * mult
- q.y = (m[2][0] - m[0][2]) * mult
- q.z = (m[0][1] - m[1][0]) * mult
+ q.x = (m[2, 1] - m[1, 2]) * mult
+ q.y = (m[0, 2] - m[2, 0]) * mult
+ q.z = (m[1, 0] - m[0, 1]) * mult
case 1:
- q.w = (m[1][2] - m[2][1]) * mult
+ q.w = (m[2, 1] - m[1, 2]) * mult
q.x = biggest_val
- q.y = (m[0][1] + m[1][0]) * mult
- q.z = (m[2][0] + m[0][2]) * mult
+ q.y = (m[1, 0] + m[0, 1]) * mult
+ q.z = (m[0, 2] + m[2, 0]) * mult
case 2:
- q.w = (m[2][0] - m[0][2]) * mult
- q.x = (m[0][1] + m[1][0]) * mult
+ q.w = (m[0, 2] - m[2, 0]) * mult
+ q.x = (m[1, 0] + m[0, 1]) * mult
q.y = biggest_val
- q.z = (m[1][2] + m[2][1]) * mult
+ q.z = (m[2, 1] + m[1, 2]) * mult
case 3:
- q.w = (m[0][1] - m[1][0]) * mult
- q.x = (m[2][0] + m[0][2]) * mult
- q.y = (m[1][2] + m[2][1]) * mult
+ q.w = (m[1, 0] - m[0, 1]) * mult
+ q.x = (m[0, 2] + m[2, 0]) * mult
+ q.y = (m[2, 1] + m[1, 2]) * mult
q.z = biggest_val
}
return
}
quaternion_from_matrix3_f64 :: proc(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]
- four_w_squared_minus_1 := m[0][0] + m[1][1] + m[2][2]
+ four_x_squared_minus_1 := m[0, 0] - m[1, 1] - m[2, 2]
+ four_y_squared_minus_1 := m[1, 1] - m[0, 0] - m[2, 2]
+ four_z_squared_minus_1 := m[2, 2] - m[0, 0] - m[1, 1]
+ four_w_squared_minus_1 := m[0, 0] + m[1, 1] + m[2, 2]
biggest_index := 0
four_biggest_squared_minus_1 := four_w_squared_minus_1
@@ -994,23 +995,23 @@ quaternion_from_matrix3_f64 :: proc(m: Matrix3f64) -> (q: Quaternionf64) {
switch biggest_index {
case 0:
q.w = biggest_val
- q.x = (m[1][2] - m[2][1]) * mult
- q.y = (m[2][0] - m[0][2]) * mult
- q.z = (m[0][1] - m[1][0]) * mult
+ q.x = (m[2, 1] - m[1, 2]) * mult
+ q.y = (m[0, 2] - m[2, 0]) * mult
+ q.z = (m[1, 0] - m[0, 1]) * mult
case 1:
- q.w = (m[1][2] - m[2][1]) * mult
+ q.w = (m[2, 1] - m[1, 2]) * mult
q.x = biggest_val
- q.y = (m[0][1] + m[1][0]) * mult
- q.z = (m[2][0] + m[0][2]) * mult
+ q.y = (m[1, 0] + m[0, 1]) * mult
+ q.z = (m[0, 2] + m[2, 0]) * mult
case 2:
- q.w = (m[2][0] - m[0][2]) * mult
- q.x = (m[0][1] + m[1][0]) * mult
+ q.w = (m[0, 2] - m[2, 0]) * mult
+ q.x = (m[1, 0] + m[0, 1]) * mult
q.y = biggest_val
- q.z = (m[1][2] + m[2][1]) * mult
+ q.z = (m[2, 1] + m[1, 2]) * mult
case 3:
- q.w = (m[0][1] - m[1][0]) * mult
- q.x = (m[2][0] + m[0][2]) * mult
- q.y = (m[1][2] + m[2][1]) * mult
+ q.w = (m[1, 0] - m[0, 1]) * mult
+ q.x = (m[0, 2] + m[2, 0]) * mult
+ q.y = (m[2, 1] + m[1, 2]) * mult
q.z = biggest_val
}
return
@@ -1093,30 +1094,30 @@ quaternion_between_two_vector3 :: proc{
matrix2_inverse_transpose_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[0][1] = -m[0][1] * id
- c[1][0] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[1, 0] = -m[1, 0] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_transpose_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[0][1] = -m[0][1] * id
- c[1][0] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[1, 0] = -m[1, 0] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_transpose_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[0][1] = -m[0][1] * id
- c[1][0] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[1, 0] = -m[1, 0] * id
+ c[0, 1] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_transpose :: proc{
@@ -1127,13 +1128,13 @@ matrix2_inverse_transpose :: proc{
matrix2_determinant_f16 :: proc(m: Matrix2f16) -> f16 {
- return m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant_f32 :: proc(m: Matrix2f32) -> f32 {
- return m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant_f64 :: proc(m: Matrix2f64) -> f64 {
- return m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ return m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
}
matrix2_determinant :: proc{
matrix2_determinant_f16,
@@ -1143,30 +1144,30 @@ matrix2_determinant :: proc{
matrix2_inverse_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[1][0] = -m[0][1] * id
- c[0][1] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[0, 1] = -m[1, 0] * id
+ c[1, 0] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_f32 :: proc(m: Matrix2f32) -> (c: Matrix2f32) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[1][0] = -m[0][1] * id
- c[0][1] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[0, 1] = -m[1, 0] * id
+ c[1, 0] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse_f64 :: proc(m: Matrix2f64) -> (c: Matrix2f64) {
- d := m[0][0]*m[1][1] - m[1][0]*m[0][1]
+ d := m[0, 0]*m[1, 1] - m[0, 1]*m[1, 0]
id := 1.0/d
- c[0][0] = +m[1][1] * id
- c[1][0] = -m[0][1] * id
- c[0][1] = -m[1][0] * id
- c[1][1] = +m[0][0] * id
+ c[0, 0] = +m[1, 1] * id
+ c[0, 1] = -m[1, 0] * id
+ c[1, 0] = -m[0, 1] * id
+ c[1, 1] = +m[0, 0] * id
return c
}
matrix2_inverse :: proc{
@@ -1177,24 +1178,24 @@ matrix2_inverse :: proc{
matrix2_adjoint_f16 :: proc(m: Matrix2f16) -> (c: Matrix2f16) {
- c[0][0] = +m[1][1]
- c[0][1] = -m[1][0]
- c[1][0] = -m[0][1]
- c[1][1] = +m[0][0]
+ 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) {
- c[0][0] = +m[1][1]
- c[0][1] = -m[1][0]
- c[1][0] = -m[0][1]
- c[1][1] = +m[0][0]
+ 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) {
- c[0][0] = +m[1][1]
- c[0][1] = -m[1][0]
- c[1][0] = -m[0][1]
- c[1][1] = +m[0][0]
+ 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 :: proc{
@@ -1215,17 +1216,17 @@ matrix3_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix3f16) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
matrix3_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix3f32) {
@@ -1239,17 +1240,17 @@ matrix3_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix3f32) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
matrix3_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix3f64) {
@@ -1263,17 +1264,17 @@ matrix3_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix3f64) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
return m
}
matrix3_from_quaternion :: proc{
@@ -1300,21 +1301,21 @@ matrix3_inverse :: proc{
matrix3_determinant_f16 :: proc(m: Matrix3f16) -> f16 {
- a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
- c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])
+ 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 {
- a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
- c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])
+ 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 {
- a := +m[0][0] * (m[1][1] * m[2][2] - m[2][1] * m[1][2])
- b := -m[1][0] * (m[0][1] * m[2][2] - m[2][1] * m[0][2])
- c := +m[2][0] * (m[0][1] * m[1][2] - m[1][1] * m[0][2])
+ 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 :: proc{
@@ -1325,39 +1326,39 @@ matrix3_determinant :: proc{
matrix3_adjoint_f16 :: proc(m: Matrix3f16) -> (adjoint: Matrix3f16) {
- adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1])
- adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1])
- adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1])
- adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0])
- adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0])
- adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0])
- adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0])
- adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0])
- adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0])
+ 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])
+ adjoint[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ adjoint[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ adjoint[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ adjoint[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ adjoint[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ 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) {
- adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1])
- adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1])
- adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1])
- adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0])
- adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0])
- adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0])
- adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0])
- adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0])
- adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0])
+ 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])
+ adjoint[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ adjoint[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ adjoint[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ adjoint[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ adjoint[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ 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) {
- adjoint[0][0] = +(m[1][1] * m[2][2] - m[1][2] * m[2][1])
- adjoint[1][0] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1])
- adjoint[2][0] = +(m[0][1] * m[1][2] - m[0][2] * m[1][1])
- adjoint[0][1] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0])
- adjoint[1][1] = +(m[0][0] * m[2][2] - m[0][2] * m[2][0])
- adjoint[2][1] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0])
- adjoint[0][2] = +(m[1][0] * m[2][1] - m[1][1] * m[2][0])
- adjoint[1][2] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0])
- adjoint[2][2] = +(m[0][0] * m[1][1] - m[0][1] * m[1][0])
+ 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])
+ adjoint[1, 0] = -(m[0, 1] * m[2, 2] - m[2, 1] * m[0, 2])
+ adjoint[1, 1] = +(m[0, 0] * m[2, 2] - m[2, 0] * m[0, 2])
+ adjoint[1, 2] = -(m[0, 0] * m[2, 1] - m[2, 0] * m[0, 1])
+ adjoint[2, 0] = +(m[0, 1] * m[1, 2] - m[1, 1] * m[0, 2])
+ adjoint[2, 1] = -(m[0, 0] * m[1, 2] - m[1, 0] * m[0, 2])
+ adjoint[2, 2] = +(m[0, 0] * m[1, 1] - m[1, 0] * m[0, 1])
return adjoint
}
matrix3_adjoint :: proc{
@@ -1369,37 +1370,13 @@ matrix3_adjoint :: proc{
matrix3_inverse_transpose_f16 :: proc(m: Matrix3f16) -> (inverse_transpose: Matrix3f16) {
- adjoint := matrix3_adjoint(m)
- determinant := matrix3_determinant(m)
- inv_determinant := 1.0 / determinant
- for i in 0..<3 {
- for j in 0..<3 {
- inverse_transpose[i][j] = adjoint[i][j] * inv_determinant
- }
- }
- return
+ return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose_f32 :: proc(m: Matrix3f32) -> (inverse_transpose: Matrix3f32) {
- adjoint := matrix3_adjoint(m)
- determinant := matrix3_determinant(m)
- inv_determinant := 1.0 / determinant
- for i in 0..<3 {
- for j in 0..<3 {
- inverse_transpose[i][j] = adjoint[i][j] * inv_determinant
- }
- }
- return
+ return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose_f64 :: proc(m: Matrix3f64) -> (inverse_transpose: Matrix3f64) {
- adjoint := matrix3_adjoint(m)
- determinant := matrix3_determinant(m)
- inv_determinant := 1.0 / determinant
- for i in 0..<3 {
- for j in 0..<3 {
- inverse_transpose[i][j] = adjoint[i][j] * inv_determinant
- }
- }
- return
+ return builtin.inverse_transpose(m)
}
matrix3_inverse_transpose :: proc{
matrix3_inverse_transpose_f16,
@@ -1409,21 +1386,21 @@ matrix3_inverse_transpose :: proc{
matrix3_scale_f16 :: proc(s: Vector3f16) -> (m: Matrix3f16) {
- m[0][0] = s[0]
- m[1][1] = s[1]
- m[2][2] = s[2]
+ 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) {
- m[0][0] = s[0]
- m[1][1] = s[1]
- m[2][2] = s[2]
+ 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) {
- m[0][0] = s[0]
- m[1][1] = s[1]
- m[2][2] = s[2]
+ m[0, 0] = s[0]
+ m[1, 1] = s[1]
+ m[2, 2] = s[2]
return m
}
matrix3_scale :: proc{
@@ -1440,17 +1417,17 @@ matrix3_rotate_f16 :: proc(angle_radians: f16, v: Vector3f16) -> (rot: Matrix3f1
a := normalize(v)
t := a * (1-c)
- rot[0][0] = c + t[0]*a[0]
- rot[0][1] = 0 + t[0]*a[1] + s*a[2]
- rot[0][2] = 0 + t[0]*a[2] - s*a[1]
+ rot[0, 0] = c + t[0]*a[0]
+ rot[1, 0] = 0 + t[0]*a[1] + s*a[2]
+ rot[2, 0] = 0 + t[0]*a[2] - s*a[1]
- rot[1][0] = 0 + t[1]*a[0] - s*a[2]
- rot[1][1] = c + t[1]*a[1]
- rot[1][2] = 0 + t[1]*a[2] + s*a[0]
+ rot[0, 1] = 0 + t[1]*a[0] - s*a[2]
+ rot[1, 1] = c + t[1]*a[1]
+ rot[2, 1] = 0 + t[1]*a[2] + s*a[0]
- rot[2][0] = 0 + t[2]*a[0] + s*a[1]
- rot[2][1] = 0 + t[2]*a[1] - s*a[0]
- rot[2][2] = c + t[2]*a[2]
+ rot[0, 2] = 0 + t[2]*a[0] + s*a[1]
+ rot[1, 2] = 0 + t[2]*a[1] - s*a[0]
+ rot[2, 2] = c + t[2]*a[2]
return rot
}
@@ -1461,17 +1438,17 @@ matrix3_rotate_f32 :: proc(angle_radians: f32, v: Vector3f32) -> (rot: Matrix3f3
a := normalize(v)
t := a * (1-c)
- rot[0][0] = c + t[0]*a[0]
- rot[0][1] = 0 + t[0]*a[1] + s*a[2]
- rot[0][2] = 0 + t[0]*a[2] - s*a[1]
+ rot[0, 0] = c + t[0]*a[0]
+ rot[1, 0] = 0 + t[0]*a[1] + s*a[2]
+ rot[2, 0] = 0 + t[0]*a[2] - s*a[1]
- rot[1][0] = 0 + t[1]*a[0] - s*a[2]
- rot[1][1] = c + t[1]*a[1]
- rot[1][2] = 0 + t[1]*a[2] + s*a[0]
+ rot[0, 1] = 0 + t[1]*a[0] - s*a[2]
+ rot[1, 1] = c + t[1]*a[1]
+ rot[2, 1] = 0 + t[1]*a[2] + s*a[0]
- rot[2][0] = 0 + t[2]*a[0] + s*a[1]
- rot[2][1] = 0 + t[2]*a[1] - s*a[0]
- rot[2][2] = c + t[2]*a[2]
+ rot[0, 2] = 0 + t[2]*a[0] + s*a[1]
+ rot[1, 2] = 0 + t[2]*a[1] - s*a[0]
+ rot[2, 2] = c + t[2]*a[2]
return rot
}
@@ -1482,17 +1459,17 @@ matrix3_rotate_f64 :: proc(angle_radians: f64, v: Vector3f64) -> (rot: Matrix3f6
a := normalize(v)
t := a * (1-c)
- rot[0][0] = c + t[0]*a[0]
- rot[0][1] = 0 + t[0]*a[1] + s*a[2]
- rot[0][2] = 0 + t[0]*a[2] - s*a[1]
+ rot[0, 0] = c + t[0]*a[0]
+ rot[1, 0] = 0 + t[0]*a[1] + s*a[2]
+ rot[2, 0] = 0 + t[0]*a[2] - s*a[1]
- rot[1][0] = 0 + t[1]*a[0] - s*a[2]
- rot[1][1] = c + t[1]*a[1]
- rot[1][2] = 0 + t[1]*a[2] + s*a[0]
+ rot[0, 1] = 0 + t[1]*a[0] - s*a[2]
+ rot[1, 1] = c + t[1]*a[1]
+ rot[2, 1] = 0 + t[1]*a[2] + s*a[0]
- rot[2][0] = 0 + t[2]*a[0] + s*a[1]
- rot[2][1] = 0 + t[2]*a[1] - s*a[0]
- rot[2][2] = c + t[2]*a[2]
+ rot[0, 2] = 0 + t[2]*a[0] + s*a[1]
+ rot[1, 2] = 0 + t[2]*a[1] - s*a[0]
+ rot[2, 2] = c + t[2]*a[2]
return rot
}
@@ -1508,9 +1485,9 @@ matrix3_look_at_f16 :: proc(eye, centre, up: Vector3f16) -> Matrix3f16 {
s := normalize(cross(f, up))
u := cross(s, f)
return Matrix3f16{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
}
matrix3_look_at_f32 :: proc(eye, centre, up: Vector3f32) -> Matrix3f32 {
@@ -1518,9 +1495,9 @@ matrix3_look_at_f32 :: proc(eye, centre, up: Vector3f32) -> Matrix3f32 {
s := normalize(cross(f, up))
u := cross(s, f)
return Matrix3f32{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
}
matrix3_look_at_f64 :: proc(eye, centre, up: Vector3f64) -> Matrix3f64 {
@@ -1528,9 +1505,9 @@ matrix3_look_at_f64 :: proc(eye, centre, up: Vector3f64) -> Matrix3f64 {
s := normalize(cross(f, up))
u := cross(s, f)
return Matrix3f64{
- {+s.x, +u.x, -f.x},
- {+s.y, +u.y, -f.y},
- {+s.z, +u.z, -f.z},
+ +s.x, +s.y, +s.z,
+ +u.x, +u.y, +u.z,
+ -f.x, -f.y, -f.z,
}
}
matrix3_look_at :: proc{
@@ -1551,19 +1528,19 @@ matrix4_from_quaternion_f16 :: proc(q: Quaternionf16) -> (m: Matrix4f16) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
- m[3][3] = 1
+ m[3, 3] = 1
return m
}
@@ -1578,19 +1555,19 @@ matrix4_from_quaternion_f32 :: proc(q: Quaternionf32) -> (m: Matrix4f32) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
- m[3][3] = 1
+ m[3, 3] = 1
return m
}
@@ -1605,19 +1582,19 @@ matrix4_from_quaternion_f64 :: proc(q: Quaternionf64) -> (m: Matrix4f64) {
qwy := q.w * q.y
qwz := q.w * q.z
- m[0][0] = 1 - 2 * (qyy + qzz)
- m[0][1] = 2 * (qxy + qwz)
- m[0][2] = 2 * (qxz - qwy)
+ m[0, 0] = 1 - 2 * (qyy + qzz)
+ m[1, 0] = 2 * (qxy + qwz)
+ m[2, 0] = 2 * (qxz - qwy)
- m[1][0] = 2 * (qxy - qwz)
- m[1][1] = 1 - 2 * (qxx + qzz)
- m[1][2] = 2 * (qyz + qwx)
+ m[0, 1] = 2 * (qxy - qwz)
+ m[1, 1] = 1 - 2 * (qxx + qzz)
+ m[2, 1] = 2 * (qyz + qwx)
- m[2][0] = 2 * (qxz + qwy)
- m[2][1] = 2 * (qyz - qwx)
- m[2][2] = 1 - 2 * (qxx + qyy)
+ m[0, 2] = 2 * (qxz + qwy)
+ m[1, 2] = 2 * (qyz - qwx)
+ m[2, 2] = 1 - 2 * (qxx + qyy)
- m[3][3] = 1
+ m[3, 3] = 1
return m
}
@@ -1992,10 +1969,10 @@ matrix4_look_at_f16 :: proc(eye, centre, up: Vector3f16, flip_z_axis := true) ->
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_f32 :: proc(eye, centre, up: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
@@ -2006,10 +1983,10 @@ matrix4_look_at_f32 :: proc(eye, centre, up: Vector3f32, flip_z_axis := true) ->
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_f64 :: proc(eye, centre, up: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
@@ -2020,10 +1997,10 @@ matrix4_look_at_f64 :: proc(eye, centre, up: Vector3f64, flip_z_axis := true) ->
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at :: proc{
@@ -2041,10 +2018,10 @@ matrix4_look_at_from_fru_f16 :: proc(eye, f, r, u: Vector3f16, flip_z_axis := tr
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := true) -> (m: Matrix4f32) {
@@ -2055,10 +2032,10 @@ matrix4_look_at_from_fru_f32 :: proc(eye, f, r, u: Vector3f32, flip_z_axis := tr
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_from_fru_f64 :: proc(eye, f, r, u: Vector3f64, flip_z_axis := true) -> (m: Matrix4f64) {
@@ -2069,10 +2046,10 @@ matrix4_look_at_from_fru_f64 :: proc(eye, f, r, u: Vector3f64, flip_z_axis := tr
fe := dot(f, eye)
return {
- {+s.x, +u.x, -f.x, 0},
- {+s.y, +u.y, -f.y, 0},
- {+s.z, +u.z, -f.z, 0},
- {-dot(s, eye), -dot(u, eye), +fe if flip_z_axis else -fe, 1},
+ +s.x, +s.y, +s.z, -dot(s, eye),
+ +u.x, +u.y, +u.z, -dot(u, eye),
+ -f.x, -f.y, -f.z, +fe if flip_z_axis else -fe,
+ 0, 0, 0, 1,
}
}
matrix4_look_at_from_fru :: proc{
@@ -2084,11 +2061,11 @@ matrix4_look_at_from_fru :: proc{
matrix4_perspective_f16 :: proc(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)
- m[2][2] = +(far + near) / (far - near)
- m[2][3] = +1
- m[3][2] = -2*far*near / (far - near)
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +(far + near) / (far - near)
+ m[3, 2] = +1
+ m[2, 3] = -2*far*near / (far - near)
if flip_z_axis {
m[2] = -m[2]
@@ -2098,11 +2075,11 @@ matrix4_perspective_f16 :: proc(fovy, aspect, near, far: f16, flip_z_axis := tru
}
matrix4_perspective_f32 :: proc(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)
- m[2][2] = +(far + near) / (far - near)
- m[2][3] = +1
- m[3][2] = -2*far*near / (far - near)
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +(far + near) / (far - near)
+ m[3, 2] = +1
+ m[2, 3] = -2*far*near / (far - near)
if flip_z_axis {
m[2] = -m[2]
@@ -2112,11 +2089,11 @@ matrix4_perspective_f32 :: proc(fovy, aspect, near, far: f32, flip_z_axis := tru
}
matrix4_perspective_f64 :: proc(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)
- m[2][2] = +(far + near) / (far - near)
- m[2][3] = +1
- m[3][2] = -2*far*near / (far - near)
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +(far + near) / (far - near)
+ m[3, 2] = +1
+ m[2, 3] = -2*far*near / (far - near)
if flip_z_axis {
m[2] = -m[2]
@@ -2133,13 +2110,13 @@ matrix4_perspective :: proc{
matrix_ortho3d_f16 :: proc(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)
- m[3][0] = -(right + left) / (right - left)
- m[3][1] = -(top + bottom) / (top - bottom)
- m[3][2] = -(far + near) / (far- near)
- m[3][3] = 1
+ m[0, 0] = +2 / (right - left)
+ m[1, 1] = +2 / (top - bottom)
+ m[2, 2] = +2 / (far - near)
+ m[0, 3] = -(right + left) / (right - left)
+ m[1, 3] = -(top + bottom) / (top - bottom)
+ m[2, 3] = -(far + near) / (far- near)
+ m[3, 3] = 1
if flip_z_axis {
m[2] = -m[2]
@@ -2148,13 +2125,13 @@ 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) {
- m[0][0] = +2 / (right - left)
- m[1][1] = +2 / (top - bottom)
- m[2][2] = +2 / (far - near)
- m[3][0] = -(right + left) / (right - left)
- m[3][1] = -(top + bottom) / (top - bottom)
- m[3][2] = -(far + near) / (far- near)
- m[3][3] = 1
+ m[0, 0] = +2 / (right - left)
+ m[1, 1] = +2 / (top - bottom)
+ m[2, 2] = +2 / (far - near)
+ m[0, 3] = -(right + left) / (right - left)
+ m[1, 3] = -(top + bottom) / (top - bottom)
+ m[2, 3] = -(far + near) / (far- near)
+ m[3, 3] = 1
if flip_z_axis {
m[2] = -m[2]
@@ -2163,13 +2140,13 @@ 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) {
- m[0][0] = +2 / (right - left)
- m[1][1] = +2 / (top - bottom)
- m[2][2] = +2 / (far - near)
- m[3][0] = -(right + left) / (right - left)
- m[3][1] = -(top + bottom) / (top - bottom)
- m[3][2] = -(far + near) / (far- near)
- m[3][3] = 1
+ m[0, 0] = +2 / (right - left)
+ m[1, 1] = +2 / (top - bottom)
+ m[2, 2] = +2 / (far - near)
+ m[0, 3] = -(right + left) / (right - left)
+ m[1, 3] = -(top + bottom) / (top - bottom)
+ m[2, 3] = -(far + near) / (far- near)
+ m[3, 3] = 1
if flip_z_axis {
m[2] = -m[2]
@@ -2187,11 +2164,11 @@ matrix_ortho3d :: proc{
matrix4_infinite_perspective_f16 :: proc(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)
- m[2][2] = +1
- m[2][3] = +1
- m[3][2] = -2*near
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +1
+ m[3, 2] = +1
+ m[2, 3] = -2*near
if flip_z_axis {
m[2] = -m[2]
@@ -2201,11 +2178,11 @@ matrix4_infinite_perspective_f16 :: proc(fovy, aspect, near: f16, flip_z_axis :=
}
matrix4_infinite_perspective_f32 :: proc(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)
- m[2][2] = +1
- m[2][3] = +1
- m[3][2] = -2*near
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +1
+ m[3, 2] = +1
+ m[2, 3] = -2*near
if flip_z_axis {
m[2] = -m[2]
@@ -2215,11 +2192,11 @@ matrix4_infinite_perspective_f32 :: proc(fovy, aspect, near: f32, flip_z_axis :=
}
matrix4_infinite_perspective_f64 :: proc(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)
- m[2][2] = +1
- m[2][3] = +1
- m[3][2] = -2*near
+ m[0, 0] = 1 / (aspect*tan_half_fovy)
+ m[1, 1] = 1 / (tan_half_fovy)
+ m[2, 2] = +1
+ m[3, 2] = +1
+ m[2, 3] = -2*near
if flip_z_axis {
m[2] = -m[2]
@@ -2236,18 +2213,18 @@ matrix4_infinite_perspective :: proc{
matrix2_from_scalar_f16 :: proc(f: f16) -> (m: Matrix2f16) {
- m[0][0], m[0][1] = f, 0
- m[1][0], m[1][1] = 0, f
+ 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) {
- m[0][0], m[0][1] = f, 0
- m[1][0], m[1][1] = 0, f
+ 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) {
- m[0][0], m[0][1] = f, 0
- m[1][0], m[1][1] = 0, f
+ m[0, 0], m[1, 0] = f, 0
+ m[0, 1], m[1, 1] = 0, f
return
}
matrix2_from_scalar :: proc{
@@ -2258,21 +2235,21 @@ matrix2_from_scalar :: proc{
matrix3_from_scalar_f16 :: proc(f: f16) -> (m: Matrix3f16) {
- m[0][0], m[0][1], m[0][2] = f, 0, 0
- m[1][0], m[1][1], m[1][2] = 0, f, 0
- m[2][0], m[2][1], m[2][2] = 0, 0, f
+ 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) {
- m[0][0], m[0][1], m[0][2] = f, 0, 0
- m[1][0], m[1][1], m[1][2] = 0, f, 0
- m[2][0], m[2][1], m[2][2] = 0, 0, f
+ 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) {
- m[0][0], m[0][1], m[0][2] = f, 0, 0
- m[1][0], m[1][1], m[1][2] = 0, f, 0
- m[2][0], m[2][1], m[2][2] = 0, 0, f
+ 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 :: proc{
@@ -2283,24 +2260,24 @@ matrix3_from_scalar :: proc{
matrix4_from_scalar_f16 :: proc(f: f16) -> (m: Matrix4f16) {
- m[0][0], m[0][1], m[0][2], m[0][3] = f, 0, 0, 0
- m[1][0], m[1][1], m[1][2], m[1][3] = 0, f, 0, 0
- m[2][0], m[2][1], m[2][2], m[2][3] = 0, 0, f, 0
- m[3][0], m[3][1], m[3][2], m[3][3] = 0, 0, 0, f
+ 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) {
- m[0][0], m[0][1], m[0][2], m[0][3] = f, 0, 0, 0
- m[1][0], m[1][1], m[1][2], m[1][3] = 0, f, 0, 0
- m[2][0], m[2][1], m[2][2], m[2][3] = 0, 0, f, 0
- m[3][0], m[3][1], m[3][2], m[3][3] = 0, 0, 0, f
+ 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) {
- m[0][0], m[0][1], m[0][2], m[0][3] = f, 0, 0, 0
- m[1][0], m[1][1], m[1][2], m[1][3] = 0, f, 0, 0
- m[2][0], m[2][1], m[2][2], m[2][3] = 0, 0, f, 0
- m[3][0], m[3][1], m[3][2], m[3][3] = 0, 0, 0, f
+ 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 :: proc{
@@ -2311,18 +2288,18 @@ matrix4_from_scalar :: proc{
matrix2_from_matrix3_f16 :: proc(m: Matrix3f16) -> (r: Matrix2f16) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ 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) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ 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) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ 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 :: proc{
@@ -2333,18 +2310,18 @@ matrix2_from_matrix3 :: proc{
matrix2_from_matrix4_f16 :: proc(m: Matrix4f16) -> (r: Matrix2f16) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ 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) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ 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) {
- r[0][0], r[0][1] = m[0][0], m[0][1]
- r[1][0], r[1][1] = m[1][0], m[1][1]
+ 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 :: proc{
@@ -2355,21 +2332,21 @@ matrix2_from_matrix4 :: proc{
matrix3_from_matrix2_f16 :: proc(m: Matrix2f16) -> (r: Matrix3f16) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], 0
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], 0
- r[2][0], r[2][1], r[2][2] = 0, 0, 1
+ 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) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], 0
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], 0
- r[2][0], r[2][1], r[2][2] = 0, 0, 1
+ 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) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], 0
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], 0
- r[2][0], r[2][1], r[2][2] = 0, 0, 1
+ 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 :: proc{
@@ -2380,21 +2357,21 @@ matrix3_from_matrix2 :: proc{
matrix3_from_matrix4_f16 :: proc(m: Matrix4f16) -> (r: Matrix3f16) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], m[0][2]
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], m[1][2]
- r[2][0], r[2][1], r[2][2] = m[2][0], m[2][1], m[2][2]
+ 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) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], m[0][2]
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], m[1][2]
- r[2][0], r[2][1], r[2][2] = m[2][0], m[2][1], m[2][2]
+ 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) {
- r[0][0], r[0][1], r[0][2] = m[0][0], m[0][1], m[0][2]
- r[1][0], r[1][1], r[1][2] = m[1][0], m[1][1], m[1][2]
- r[2][0], r[2][1], r[2][2] = m[2][0], m[2][1], m[2][2]
+ 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 :: proc{
@@ -2405,24 +2382,24 @@ matrix3_from_matrix4 :: proc{
matrix4_from_matrix2_f16 :: proc(m: Matrix2f16) -> (r: Matrix4f16) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], 0, 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], 0, 0
- r[2][0], r[2][1], r[2][2], r[2][3] = 0, 0, 1, 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ 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) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], 0, 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], 0, 0
- r[2][0], r[2][1], r[2][2], r[2][3] = 0, 0, 1, 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ 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) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], 0, 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], 0, 0
- r[2][0], r[2][1], r[2][2], r[2][3] = 0, 0, 1, 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ 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 :: proc{
@@ -2433,24 +2410,24 @@ matrix4_from_matrix2 :: proc{
matrix4_from_matrix3_f16 :: proc(m: Matrix3f16) -> (r: Matrix4f16) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], m[0][2], 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], m[1][2], 0
- r[2][0], r[2][1], r[2][2], r[2][3] = m[2][0], m[2][1], m[2][2], 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ 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) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], m[0][2], 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], m[1][2], 0
- r[2][0], r[2][1], r[2][2], r[2][3] = m[2][0], m[2][1], m[2][2], 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ 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) {
- r[0][0], r[0][1], r[0][2], r[0][3] = m[0][0], m[0][1], m[0][2], 0
- r[1][0], r[1][1], r[1][2], r[1][3] = m[1][0], m[1][1], m[1][2], 0
- r[2][0], r[2][1], r[2][2], r[2][3] = m[2][0], m[2][1], m[2][2], 0
- r[3][0], r[3][1], r[3][2], r[3][3] = 0, 0, 0, 1
+ 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 :: proc{
diff --git a/core/math/linalg/specific_euler_angles_f16.odin b/core/math/linalg/specific_euler_angles_f16.odin
index d0fb1beb3..9e21c7f97 100644
--- a/core/math/linalg/specific_euler_angles_f16.odin
+++ b/core/math/linalg/specific_euler_angles_f16.odin
@@ -212,29 +212,29 @@ euler_angles_zxy_from_quaternion_f16 :: proc(q: Quaternionf16) -> (t1, t2, t3: f
matrix3_from_euler_angle_x_f16 :: proc(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
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix3f16) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix3f16) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -242,31 +242,31 @@ 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) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_derived_euler_angle_y_f16 :: proc(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
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_derived_euler_angle_z_f16 :: proc(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
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -274,14 +274,14 @@ 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) {
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
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
return
}
@@ -289,14 +289,14 @@ 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) {
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
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
return
}
@@ -322,15 +322,15 @@ matrix3_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
return
}
@@ -342,15 +342,15 @@ matrix3_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return
}
@@ -362,15 +362,15 @@ matrix3_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -382,15 +382,15 @@ matrix3_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -402,15 +402,15 @@ matrix3_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -422,15 +422,15 @@ matrix3_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -442,15 +442,15 @@ matrix3_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
return
}
@@ -462,15 +462,15 @@ matrix3_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
return
}
@@ -483,15 +483,15 @@ matrix3_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
return
}
@@ -503,15 +503,15 @@ matrix3_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
return
}
@@ -523,15 +523,15 @@ matrix3_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
return
}
@@ -543,15 +543,15 @@ matrix3_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix3f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
return
}
@@ -564,25 +564,25 @@ matrix3_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix3f16
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return m
}
euler_angles_xyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -590,12 +590,12 @@ euler_angles_xyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -603,12 +603,12 @@ euler_angles_yxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -616,12 +616,12 @@ euler_angles_xzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_xyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -629,12 +629,12 @@ euler_angles_xyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -642,24 +642,24 @@ euler_angles_yxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -667,12 +667,12 @@ euler_angles_zyz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -680,12 +680,12 @@ euler_angles_zxz_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -693,12 +693,12 @@ euler_angles_xzy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -706,12 +706,12 @@ euler_angles_yzx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_zyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -719,12 +719,12 @@ euler_angles_zyx_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -737,32 +737,32 @@ euler_angles_zxy_from_matrix3_f16 :: proc(m: Matrix3f16) -> (t1, t2, t3: f16) {
matrix4_from_euler_angle_x_f16 :: proc(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
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_y_f16 :: proc(angle_y: f16) -> (m: Matrix4f16) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_z_f16 :: proc(angle_z: f16) -> (m: Matrix4f16) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -770,34 +770,34 @@ 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) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_y_f16 :: proc(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
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_z_f16 :: proc(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
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -805,15 +805,15 @@ 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) {
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
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
+ m[3, 3] = 1
return
}
@@ -821,15 +821,15 @@ 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) {
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
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
+ m[3, 3] = 1
return
}
@@ -855,22 +855,22 @@ matrix4_from_euler_angles_xyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[0][3] = 0
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[1][3] = 0
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[3, 0] = 0
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[3, 1] = 0
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -882,22 +882,22 @@ matrix4_from_euler_angles_yxz_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -909,22 +909,22 @@ matrix4_from_euler_angles_xzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[1][3] = 0
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[3, 1] = 0
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -936,22 +936,22 @@ matrix4_from_euler_angles_xyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[0][3] = 0
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[1][3] = 0
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[3, 0] = 0
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -963,22 +963,22 @@ matrix4_from_euler_angles_yxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[0][3] = 0
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[3, 0] = 0
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -990,22 +990,22 @@ matrix4_from_euler_angles_yzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[0][3] = 0
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1017,22 +1017,22 @@ matrix4_from_euler_angles_zyz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[1][3] = 0
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1044,22 +1044,22 @@ matrix4_from_euler_angles_zxz_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[0][3] = 0
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[1][3] = 0
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[3, 1] = 0
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1072,22 +1072,22 @@ matrix4_from_euler_angles_xzy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[0][3] = 0
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[1][3] = 0
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[3, 0] = 0
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[3, 1] = 0
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1099,22 +1099,22 @@ matrix4_from_euler_angles_yzx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[0][3] = 0
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[3, 0] = 0
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1126,22 +1126,22 @@ matrix4_from_euler_angles_zyx_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[0][3] = 0
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[1][3] = 0
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[3, 0] = 0
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1153,22 +1153,22 @@ matrix4_from_euler_angles_zxy_f16 :: proc(t1, t2, t3: f16) -> (m: Matrix4f16) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[0][3] = 0
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[3, 0] = 0
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1181,32 +1181,32 @@ matrix4_from_yaw_pitch_roll_f16 :: proc(yaw, pitch, roll: f16) -> (m: Matrix4f16
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return m
}
euler_angles_xyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -1214,12 +1214,12 @@ euler_angles_xyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1227,12 +1227,12 @@ euler_angles_yxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1240,12 +1240,12 @@ euler_angles_xzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_xyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1253,12 +1253,12 @@ euler_angles_xyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1266,24 +1266,24 @@ euler_angles_yxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1291,12 +1291,12 @@ euler_angles_zyz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1304,12 +1304,12 @@ euler_angles_zxz_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_xzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1317,12 +1317,12 @@ euler_angles_xzy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_yzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1330,12 +1330,12 @@ euler_angles_yzx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_zyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1343,12 +1343,12 @@ euler_angles_zyx_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
}
euler_angles_zxy_from_matrix4_f16 :: proc(m: Matrix4f16) -> (t1, t2, t3: f16) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
diff --git a/core/math/linalg/specific_euler_angles_f32.odin b/core/math/linalg/specific_euler_angles_f32.odin
index 6ae1b0fa0..80e19ce85 100644
--- a/core/math/linalg/specific_euler_angles_f32.odin
+++ b/core/math/linalg/specific_euler_angles_f32.odin
@@ -212,29 +212,29 @@ euler_angles_zxy_from_quaternion_f32 :: proc(q: Quaternionf32) -> (t1, t2, t3: f
matrix3_from_euler_angle_x_f32 :: proc(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
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix3f32) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix3f32) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -242,31 +242,31 @@ 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) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_derived_euler_angle_y_f32 :: proc(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
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_derived_euler_angle_z_f32 :: proc(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
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -274,14 +274,14 @@ 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) {
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
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
return
}
@@ -289,14 +289,14 @@ 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) {
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
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
return
}
@@ -322,15 +322,15 @@ matrix3_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
return
}
@@ -342,15 +342,15 @@ matrix3_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return
}
@@ -362,15 +362,15 @@ matrix3_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -382,15 +382,15 @@ matrix3_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -402,15 +402,15 @@ matrix3_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -422,15 +422,15 @@ matrix3_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -442,15 +442,15 @@ matrix3_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
return
}
@@ -462,15 +462,15 @@ matrix3_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
return
}
@@ -483,15 +483,15 @@ matrix3_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
return
}
@@ -503,15 +503,15 @@ matrix3_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
return
}
@@ -523,15 +523,15 @@ matrix3_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
return
}
@@ -543,15 +543,15 @@ matrix3_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix3f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
return
}
@@ -564,25 +564,25 @@ matrix3_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix3f32
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return m
}
euler_angles_xyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -590,12 +590,12 @@ euler_angles_xyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -603,12 +603,12 @@ euler_angles_yxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -616,12 +616,12 @@ euler_angles_xzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_xyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -629,12 +629,12 @@ euler_angles_xyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -642,24 +642,24 @@ euler_angles_yxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -667,12 +667,12 @@ euler_angles_zyz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -680,12 +680,12 @@ euler_angles_zxz_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -693,12 +693,12 @@ euler_angles_xzy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -706,12 +706,12 @@ euler_angles_yzx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_zyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -719,12 +719,12 @@ euler_angles_zyx_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -737,32 +737,32 @@ euler_angles_zxy_from_matrix3_f32 :: proc(m: Matrix3f32) -> (t1, t2, t3: f32) {
matrix4_from_euler_angle_x_f32 :: proc(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
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_y_f32 :: proc(angle_y: f32) -> (m: Matrix4f32) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_z_f32 :: proc(angle_z: f32) -> (m: Matrix4f32) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -770,34 +770,34 @@ 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) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_y_f32 :: proc(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
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_z_f32 :: proc(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
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -805,15 +805,15 @@ 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) {
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
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
+ m[3, 3] = 1
return
}
@@ -821,15 +821,15 @@ 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) {
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
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
+ m[3, 3] = 1
return
}
@@ -855,22 +855,22 @@ matrix4_from_euler_angles_xyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[0][3] = 0
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[1][3] = 0
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[3, 0] = 0
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[3, 1] = 0
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -882,22 +882,22 @@ matrix4_from_euler_angles_yxz_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -909,22 +909,22 @@ matrix4_from_euler_angles_xzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[1][3] = 0
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[3, 1] = 0
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -936,22 +936,22 @@ matrix4_from_euler_angles_xyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[0][3] = 0
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[1][3] = 0
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[3, 0] = 0
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -963,22 +963,22 @@ matrix4_from_euler_angles_yxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[0][3] = 0
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[3, 0] = 0
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -990,22 +990,22 @@ matrix4_from_euler_angles_yzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[0][3] = 0
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1017,22 +1017,22 @@ matrix4_from_euler_angles_zyz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[1][3] = 0
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1044,22 +1044,22 @@ matrix4_from_euler_angles_zxz_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[0][3] = 0
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[1][3] = 0
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[3, 1] = 0
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1072,22 +1072,22 @@ matrix4_from_euler_angles_xzy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[0][3] = 0
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[1][3] = 0
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[3, 0] = 0
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[3, 1] = 0
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1099,22 +1099,22 @@ matrix4_from_euler_angles_yzx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[0][3] = 0
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[3, 0] = 0
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1126,22 +1126,22 @@ matrix4_from_euler_angles_zyx_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[0][3] = 0
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[1][3] = 0
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[3, 0] = 0
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1153,22 +1153,22 @@ matrix4_from_euler_angles_zxy_f32 :: proc(t1, t2, t3: f32) -> (m: Matrix4f32) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[0][3] = 0
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[3, 0] = 0
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1181,32 +1181,32 @@ matrix4_from_yaw_pitch_roll_f32 :: proc(yaw, pitch, roll: f32) -> (m: Matrix4f32
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return m
}
euler_angles_xyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -1214,12 +1214,12 @@ euler_angles_xyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1227,12 +1227,12 @@ euler_angles_yxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1240,12 +1240,12 @@ euler_angles_xzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_xyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1253,12 +1253,12 @@ euler_angles_xyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1266,24 +1266,24 @@ euler_angles_yxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1291,12 +1291,12 @@ euler_angles_zyz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1304,12 +1304,12 @@ euler_angles_zxz_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_xzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1317,12 +1317,12 @@ euler_angles_xzy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_yzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1330,12 +1330,12 @@ euler_angles_yzx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_zyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1343,12 +1343,12 @@ euler_angles_zyx_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
}
euler_angles_zxy_from_matrix4_f32 :: proc(m: Matrix4f32) -> (t1, t2, t3: f32) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
diff --git a/core/math/linalg/specific_euler_angles_f64.odin b/core/math/linalg/specific_euler_angles_f64.odin
index efaddd651..2f8f758b0 100644
--- a/core/math/linalg/specific_euler_angles_f64.odin
+++ b/core/math/linalg/specific_euler_angles_f64.odin
@@ -212,29 +212,29 @@ euler_angles_zxy_from_quaternion_f64 :: proc(q: Quaternionf64) -> (t1, t2, t3: f
matrix3_from_euler_angle_x_f64 :: proc(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
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix3f64) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix3f64) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -242,31 +242,31 @@ 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) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
return
}
matrix3_from_derived_euler_angle_y_f64 :: proc(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
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
return
}
matrix3_from_derived_euler_angle_z_f64 :: proc(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
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
return
}
@@ -274,14 +274,14 @@ 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) {
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
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
return
}
@@ -289,14 +289,14 @@ 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) {
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
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
return
}
@@ -322,15 +322,15 @@ matrix3_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
return
}
@@ -342,15 +342,15 @@ matrix3_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return
}
@@ -362,15 +362,15 @@ matrix3_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -382,15 +382,15 @@ matrix3_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -402,15 +402,15 @@ matrix3_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
return
}
@@ -422,15 +422,15 @@ matrix3_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
return
}
@@ -442,15 +442,15 @@ matrix3_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
return
}
@@ -462,15 +462,15 @@ matrix3_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
return
}
@@ -483,15 +483,15 @@ matrix3_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
return
}
@@ -503,15 +503,15 @@ matrix3_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
return
}
@@ -523,15 +523,15 @@ matrix3_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
return
}
@@ -543,15 +543,15 @@ matrix3_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix3f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
return
}
@@ -564,25 +564,25 @@ matrix3_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix3f64
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
return m
}
euler_angles_xyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -590,12 +590,12 @@ euler_angles_xyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -603,12 +603,12 @@ euler_angles_yxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -616,12 +616,12 @@ euler_angles_xzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_xyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -629,12 +629,12 @@ euler_angles_xyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -642,24 +642,24 @@ euler_angles_yxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -667,12 +667,12 @@ euler_angles_zyz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -680,12 +680,12 @@ euler_angles_zxz_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -693,12 +693,12 @@ euler_angles_xzy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -706,12 +706,12 @@ euler_angles_yzx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_zyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -719,12 +719,12 @@ euler_angles_zyx_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -737,32 +737,32 @@ euler_angles_zxy_from_matrix3_f64 :: proc(m: Matrix3f64) -> (t1, t2, t3: f64) {
matrix4_from_euler_angle_x_f64 :: proc(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
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_y_f64 :: proc(angle_y: f64) -> (m: Matrix4f64) {
cos_y, sin_y := math.cos(angle_y), math.sin(angle_y)
- m[0][0] = +cos_y
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_euler_angle_z_f64 :: proc(angle_z: f64) -> (m: Matrix4f64) {
cos_z, sin_z := math.cos(angle_z), math.sin(angle_z)
- m[0][0] = +cos_z
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -770,34 +770,34 @@ 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) {
cos_x := math.cos(angle_x) * angular_velocity_x
sin_x := math.sin(angle_x) * angular_velocity_x
- m[0][0] = 1
- m[1][1] = +cos_x
- m[2][1] = +sin_x
- m[1][2] = -sin_x
- m[2][2] = +cos_x
- m[3][3] = 1
+ m[0, 0] = 1
+ m[1, 1] = +cos_x
+ m[1, 2] = +sin_x
+ m[2, 1] = -sin_x
+ m[2, 2] = +cos_x
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_y_f64 :: proc(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
- m[2][0] = -sin_y
- m[1][1] = 1
- m[0][2] = +sin_y
- m[2][2] = +cos_y
- m[3][3] = 1
+ m[0, 0] = +cos_y
+ m[0, 2] = -sin_y
+ m[1, 1] = 1
+ m[2, 0] = +sin_y
+ m[2, 2] = +cos_y
+ m[3, 3] = 1
return
}
matrix4_from_derived_euler_angle_z_f64 :: proc(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
- m[1][0] = +sin_z
- m[1][1] = +cos_z
- m[0][1] = -sin_z
- m[2][2] = 1
- m[3][3] = 1
+ m[0, 0] = +cos_z
+ m[0, 1] = +sin_z
+ m[1, 1] = +cos_z
+ m[1, 0] = -sin_z
+ m[2, 2] = 1
+ m[3, 3] = 1
return
}
@@ -805,15 +805,15 @@ 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) {
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
- m[1][0] = -sin_x * - sin_y
- m[2][0] = -cos_x * - sin_y
- m[1][1] = cos_x
- m[2][1] = sin_x
- m[0][2] = sin_y
- m[1][2] = -sin_x * cos_y
- m[2][2] = cos_x * cos_y
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 1] = -sin_x * - sin_y
+ m[0, 2] = -cos_x * - sin_y
+ m[1, 1] = cos_x
+ m[1, 2] = sin_x
+ m[2, 0] = sin_y
+ m[2, 1] = -sin_x * cos_y
+ m[2, 2] = cos_x * cos_y
+ m[3, 3] = 1
return
}
@@ -821,15 +821,15 @@ 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) {
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
- m[2][0] = -sin_y
- m[0][1] = sin_y*sin_x
- m[1][1] = cos_x
- m[2][1] = cos_y*sin_x
- m[0][2] = sin_y*cos_x
- m[1][2] = -sin_x
- m[2][2] = cos_y*cos_x
- m[3][3] = 1
+ m[0, 0] = cos_y
+ m[0, 2] = -sin_y
+ m[1, 0] = sin_y*sin_x
+ m[1, 1] = cos_x
+ m[1, 2] = cos_y*sin_x
+ m[2, 0] = sin_y*cos_x
+ m[2, 1] = -sin_x
+ m[2, 2] = cos_y*cos_x
+ m[3, 3] = 1
return
}
@@ -855,22 +855,22 @@ matrix4_from_euler_angles_xyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
s2 := math.sin(-t2)
s3 := math.sin(-t3)
- m[0][0] = c2 * c3
- m[0][1] =-c1 * s3 + s1 * s2 * c3
- m[0][2] = s1 * s3 + c1 * s2 * c3
- m[0][3] = 0
- m[1][0] = c2 * s3
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] =-s1 * c3 + c1 * s2 * s3
- m[1][3] = 0
- m[2][0] =-s2
- m[2][1] = s1 * c2
- m[2][2] = c1 * c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] =-c1 * s3 + s1 * s2 * c3
+ m[2, 0] = s1 * s3 + c1 * s2 * c3
+ m[3, 0] = 0
+ m[0, 1] = c2 * s3
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] =-s1 * c3 + c1 * s2 * s3
+ m[3, 1] = 0
+ m[0, 2] =-s2
+ m[1, 2] = s1 * c2
+ m[2, 2] = c1 * c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -882,22 +882,22 @@ matrix4_from_euler_angles_yxz_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -909,22 +909,22 @@ matrix4_from_euler_angles_xzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = c1 * s2
- m[0][2] = s1 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s2
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c1 * s3 + c2 * c3 * s1
- m[1][3] = 0
- m[2][0] = s2 * s3
- m[2][1] =-c3 * s1 - c1 * c2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = c1 * s2
+ m[2, 0] = s1 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s2
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c1 * s3 + c2 * c3 * s1
+ m[3, 1] = 0
+ m[0, 2] = s2 * s3
+ m[1, 2] =-c3 * s1 - c1 * c2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -936,22 +936,22 @@ matrix4_from_euler_angles_xyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2
- m[0][1] = s1 * s2
- m[0][2] =-c1 * s2
- m[0][3] = 0
- m[1][0] = s2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = c3 * s1 + c1 * c2 * s3
- m[1][3] = 0
- m[2][0] = c3 * s2
- m[2][1] =-c1 * s3 - c2 * c3 * s1
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2
+ m[1, 0] = s1 * s2
+ m[2, 0] =-c1 * s2
+ m[3, 0] = 0
+ m[0, 1] = s2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = c3 * s1 + c1 * c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c3 * s2
+ m[1, 2] =-c1 * s3 - c2 * c3 * s1
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -963,22 +963,22 @@ matrix4_from_euler_angles_yxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = s2* s3
- m[0][2] =-c3 * s1 - c1 * c2 * s3
- m[0][3] = 0
- m[1][0] = s1 * s2
- m[1][1] = c2
- m[1][2] = c1 * s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c2 * c3 * s1
- m[2][1] =-c3 * s2
- m[2][2] = c1 * c2 * c3 - s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = s2* s3
+ m[2, 0] =-c3 * s1 - c1 * c2 * s3
+ m[3, 0] = 0
+ m[0, 1] = s1 * s2
+ m[1, 1] = c2
+ m[2, 1] = c1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c2 * c3 * s1
+ m[1, 2] =-c3 * s2
+ m[2, 2] = c1 * c2 * c3 - s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -990,22 +990,22 @@ matrix4_from_euler_angles_yzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c3 * s2
- m[0][2] =-c1 * s3 - c2 * c3 * s1
- m[0][3] = 0
- m[1][0] =-c1 * s2
- m[1][1] = c2
- m[1][2] = s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * c2 * s3
- m[2][1] = s2 * s3
- m[2][2] = c1 * c3 - c2 * s1 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c3 * s2
+ m[2, 0] =-c1 * s3 - c2 * c3 * s1
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s2
+ m[1, 1] = c2
+ m[2, 1] = s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * c2 * s3
+ m[1, 2] = s2 * s3
+ m[2, 2] = c1 * c3 - c2 * s1 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1017,22 +1017,22 @@ matrix4_from_euler_angles_zyz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2 * c3 - s1 * s3
- m[0][1] = c1 * s3 + c2 * c3 * s1
- m[0][2] =-c3 * s2
- m[0][3] = 0
- m[1][0] =-c3 * s1 - c1 * c2 * s3
- m[1][1] = c1 * c3 - c2 * s1 * s3
- m[1][2] = s2 * s3
- m[1][3] = 0
- m[2][0] = c1 * s2
- m[2][1] = s1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2 * c3 - s1 * s3
+ m[1, 0] = c1 * s3 + c2 * c3 * s1
+ m[2, 0] =-c3 * s2
+ m[3, 0] = 0
+ m[0, 1] =-c3 * s1 - c1 * c2 * s3
+ m[1, 1] = c1 * c3 - c2 * s1 * s3
+ m[2, 1] = s2 * s3
+ m[3, 1] = 0
+ m[0, 2] = c1 * s2
+ m[1, 2] = s1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1044,22 +1044,22 @@ matrix4_from_euler_angles_zxz_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - c2 * s1 * s3
- m[0][1] = c3 * s1 + c1 * c2 * s3
- m[0][2] = s2 *s3
- m[0][3] = 0
- m[1][0] =-c1 * s3 - c2 * c3 * s1
- m[1][1] = c1 * c2 * c3 - s1 * s3
- m[1][2] = c3 * s2
- m[1][3] = 0
- m[2][0] = s1 * s2
- m[2][1] =-c1 * s2
- m[2][2] = c2
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - c2 * s1 * s3
+ m[1, 0] = c3 * s1 + c1 * c2 * s3
+ m[2, 0] = s2 *s3
+ m[3, 0] = 0
+ m[0, 1] =-c1 * s3 - c2 * c3 * s1
+ m[1, 1] = c1 * c2 * c3 - s1 * s3
+ m[2, 1] = c3 * s2
+ m[3, 1] = 0
+ m[0, 2] = s1 * s2
+ m[1, 2] =-c1 * s2
+ m[2, 2] = c2
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1072,22 +1072,22 @@ matrix4_from_euler_angles_xzy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c2 * c3
- m[0][1] = s1 * s3 + c1 * c3 * s2
- m[0][2] = c3 * s1 * s2 - c1 * s3
- m[0][3] = 0
- m[1][0] =-s2
- m[1][1] = c1 * c2
- m[1][2] = c2 * s1
- m[1][3] = 0
- m[2][0] = c2 * s3
- m[2][1] = c1 * s2 * s3 - c3 * s1
- m[2][2] = c1 * c3 + s1 * s2 *s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c2 * c3
+ m[1, 0] = s1 * s3 + c1 * c3 * s2
+ m[2, 0] = c3 * s1 * s2 - c1 * s3
+ m[3, 0] = 0
+ m[0, 1] =-s2
+ m[1, 1] = c1 * c2
+ m[2, 1] = c2 * s1
+ m[3, 1] = 0
+ m[0, 2] = c2 * s3
+ m[1, 2] = c1 * s2 * s3 - c3 * s1
+ m[2, 2] = c1 * c3 + s1 * s2 *s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1099,22 +1099,22 @@ matrix4_from_euler_angles_yzx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = s2
- m[0][2] =-c2 * s1
- m[0][3] = 0
- m[1][0] = s1 * s3 - c1 * c3 * s2
- m[1][1] = c2 * c3
- m[1][2] = c1 * s3 + c3 * s1 * s2
- m[1][3] = 0
- m[2][0] = c3 * s1 + c1 * s2 * s3
- m[2][1] =-c2 * s3
- m[2][2] = c1 * c3 - s1 * s2 * s3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = s2
+ m[2, 0] =-c2 * s1
+ m[3, 0] = 0
+ m[0, 1] = s1 * s3 - c1 * c3 * s2
+ m[1, 1] = c2 * c3
+ m[2, 1] = c1 * s3 + c3 * s1 * s2
+ m[3, 1] = 0
+ m[0, 2] = c3 * s1 + c1 * s2 * s3
+ m[1, 2] =-c2 * s3
+ m[2, 2] = c1 * c3 - s1 * s2 * s3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1126,22 +1126,22 @@ matrix4_from_euler_angles_zyx_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c2
- m[0][1] = c2 * s1
- m[0][2] =-s2
- m[0][3] = 0
- m[1][0] = c1 * s2 * s3 - c3 * s1
- m[1][1] = c1 * c3 + s1 * s2 * s3
- m[1][2] = c2 * s3
- m[1][3] = 0
- m[2][0] = s1 * s3 + c1 * c3 * s2
- m[2][1] = c3 * s1 * s2 - c1 * s3
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c2
+ m[1, 0] = c2 * s1
+ m[2, 0] =-s2
+ m[3, 0] = 0
+ m[0, 1] = c1 * s2 * s3 - c3 * s1
+ m[1, 1] = c1 * c3 + s1 * s2 * s3
+ m[2, 1] = c2 * s3
+ m[3, 1] = 0
+ m[0, 2] = s1 * s3 + c1 * c3 * s2
+ m[1, 2] = c3 * s1 * s2 - c1 * s3
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1153,22 +1153,22 @@ matrix4_from_euler_angles_zxy_f64 :: proc(t1, t2, t3: f64) -> (m: Matrix4f64) {
c3 := math.cos(t3)
s3 := math.sin(t3)
- m[0][0] = c1 * c3 - s1 * s2 * s3
- m[0][1] = c3 * s1 + c1 * s2 * s3
- m[0][2] =-c2 * s3
- m[0][3] = 0
- m[1][0] =-c2 * s1
- m[1][1] = c1 * c2
- m[1][2] = s2
- m[1][3] = 0
- m[2][0] = c1 * s3 + c3 * s1 * s2
- m[2][1] = s1 * s3 - c1 * c3 * s2
- m[2][2] = c2 * c3
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = c1 * c3 - s1 * s2 * s3
+ m[1, 0] = c3 * s1 + c1 * s2 * s3
+ m[2, 0] =-c2 * s3
+ m[3, 0] = 0
+ m[0, 1] =-c2 * s1
+ m[1, 1] = c1 * c2
+ m[2, 1] = s2
+ m[3, 1] = 0
+ m[0, 2] = c1 * s3 + c3 * s1 * s2
+ m[1, 2] = s1 * s3 - c1 * c3 * s2
+ m[2, 2] = c2 * c3
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return
}
@@ -1181,32 +1181,32 @@ matrix4_from_yaw_pitch_roll_f64 :: proc(yaw, pitch, roll: f64) -> (m: Matrix4f64
cb := math.cos(roll)
sb := math.sin(roll)
- m[0][0] = ch * cb + sh * sp * sb
- m[0][1] = sb * cp
- m[0][2] = -sh * cb + ch * sp * sb
- m[0][3] = 0
- m[1][0] = -ch * sb + sh * sp * cb
- m[1][1] = cb * cp
- m[1][2] = sb * sh + ch * sp * cb
- m[1][3] = 0
- m[2][0] = sh * cp
- m[2][1] = -sp
- m[2][2] = ch * cp
- m[2][3] = 0
- m[3][0] = 0
- m[3][1] = 0
- m[3][2] = 0
- m[3][3] = 1
+ m[0, 0] = ch * cb + sh * sp * sb
+ m[1, 0] = sb * cp
+ m[2, 0] = -sh * cb + ch * sp * sb
+ m[3, 0] = 0
+ m[0, 1] = -ch * sb + sh * sp * cb
+ m[1, 1] = cb * cp
+ m[2, 1] = sb * sh + ch * sp * cb
+ m[3, 1] = 0
+ m[0, 2] = sh * cp
+ m[1, 2] = -sp
+ m[2, 2] = ch * cp
+ m[3, 2] = 0
+ m[0, 3] = 0
+ m[1, 3] = 0
+ m[2, 3] = 0
+ m[3, 3] = 1
return m
}
euler_angles_xyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][2])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[1][0]*m[1][0])
- T2 := math.atan2(-m[2][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][2] - C1*m[0][1], C1*m[1][1] - S1*m[1][2])
+ T3 := math.atan2(S1*m[2, 0] - C1*m[1, 0], C1*m[1, 1] - S1*m[2, 1])
t1 = -T1
t2 = -T2
t3 = -T3
@@ -1214,12 +1214,12 @@ euler_angles_xyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], m[2][2])
- C2 := math.sqrt(m[0][1]*m[0][1] + m[1][1]*m[1][1])
- T2 := math.atan2(-m[2][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][2] - C1*m[1][0], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(S1*m[2, 1] - C1*m[0, 1], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1227,12 +1227,12 @@ euler_angles_yxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][2], m[0][1])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[1][2] - S1*m[1][1], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(C1*m[2, 1] - S1*m[1, 1], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1240,12 +1240,12 @@ euler_angles_xzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_xyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], -m[0][2])
- S2 := math.sqrt(m[1][0]*m[1][0] + m[2][0]*m[2][0])
- T2 := math.atan2(S2, m[0][0])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[2][1] - S1*m[2][2], C1*m[1][1] + S1*m[1][2])
+ T3 := math.atan2(-C1*m[1, 2] - S1*m[2, 2], C1*m[1, 1] + S1*m[2, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1253,12 +1253,12 @@ euler_angles_xyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][0], m[1][2])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] - S1*m[2][2], C1*m[0][0] - S1*m[0][2])
+ T3 := math.atan2(C1*m[0, 2] - S1*m[2, 2], C1*m[0, 0] - S1*m[2, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1266,24 +1266,24 @@ euler_angles_yxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], -m[1][0])
- S2 := math.sqrt(m[0][1]*m[0][1] + m[2][1]*m[2][1])
- T2 := math.atan2(S2, m[1][1])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-S1*m[0][0] - C1*m[0][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(-S1*m[0, 0] - C1*m[2, 0], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
return
}
euler_angles_zyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][1], m[2][0])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[0][1] - S1*m[0][0], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(C1*m[1, 0] - S1*m[0, 0], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1291,12 +1291,12 @@ euler_angles_zyz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[2][0], -m[2][1])
- S2 := math.sqrt(m[0][2]*m[0][2] + m[1][2]*m[1][2])
- T2 := math.atan2(S2, m[2][2])
+ 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])
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(-C1*m[1][0] - S1*m[1][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(-C1*m[0, 1] - S1*m[1, 1], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
@@ -1304,12 +1304,12 @@ euler_angles_zxz_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_xzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[1][2], m[1][1])
- C2 := math.sqrt(m[0][0]*m[0][0] + m[2][0]*m[2][0])
- T2 := math.atan2(-m[1][0], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[0][1] - C1*m[0][2], C1*m[2][2] - S1*m[2][1])
+ T3 := math.atan2(S1*m[1, 0] - C1*m[2, 0], C1*m[2, 2] - S1*m[1, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1317,12 +1317,12 @@ euler_angles_xzy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_yzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[0][2], m[0][0])
- C2 := math.sqrt(m[1][1]*m[1][1] + m[2][1]*m[2][1])
- T2 := math.atan2(m[0][1], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[1][0] + C1*m[1][2], S1*m[2][0] + C1*m[2][2])
+ T3 := math.atan2(S1*m[0, 1] + C1*m[2, 1], S1*m[0, 2] + C1*m[2, 2])
t1 = T1
t2 = T2
t3 = T3
@@ -1330,12 +1330,12 @@ euler_angles_yzx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_zyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(m[0][1], m[0][0])
- C2 := math.sqrt(m[1][2]*m[1][2] + m[2][2]*m[2][2])
- T2 := math.atan2(-m[0][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(S1*m[2][0] - C1*m[2][1], C1*m[1][1] - S1*m[1][0])
+ T3 := math.atan2(S1*m[0, 2] - C1*m[1, 2], C1*m[1, 1] - S1*m[0, 1])
t1 = T1
t2 = T2
t3 = T3
@@ -1343,12 +1343,12 @@ euler_angles_zyx_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
}
euler_angles_zxy_from_matrix4_f64 :: proc(m: Matrix4f64) -> (t1, t2, t3: f64) {
- T1 := math.atan2(-m[1][0], m[1][1])
- C2 := math.sqrt(m[0][2]*m[0][2] + m[2][2]*m[2][2])
- T2 := math.atan2(m[1][2], C2)
+ 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)
S1 := math.sin(T1)
C1 := math.cos(T1)
- T3 := math.atan2(C1*m[2][0] + S1*m[2][1], C1*m[0][0] + S1*m[0][1])
+ T3 := math.atan2(C1*m[0, 2] + S1*m[1, 2], C1*m[0, 0] + S1*m[1, 0])
t1 = T1
t2 = T2
t3 = T3
diff --git a/core/math/linalg/swizzle.odin b/core/math/linalg/swizzle.odin
index f035a5276..ada4aebcf 100644
--- a/core/math/linalg/swizzle.odin
+++ b/core/math/linalg/swizzle.odin
@@ -1,5 +1,10 @@
package linalg
+/*
+ These procedures are to allow for swizzling with non-compile (runtime) known components
+*/
+
+
Scalar_Components :: enum u8 {
x = 0,
r = 0,
diff --git a/core/math/math.odin b/core/math/math.odin
index caaa6f51b..b711c160f 100644
--- a/core/math/math.odin
+++ b/core/math/math.odin
@@ -396,7 +396,7 @@ trunc_f16 :: proc "contextless" (x: f16) -> f16 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f16)x
}
@@ -428,7 +428,7 @@ trunc_f32 :: proc "contextless" (x: f32) -> f32 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f32)x
}
@@ -460,7 +460,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
e := (x >> shift) & mask - bias
if e < shift {
- x &= ~(1 << (shift-e)) - 1
+ x &~= 1 << (shift-e) - 1
}
return transmute(f64)x
}
@@ -473,6 +473,7 @@ trunc_f64 :: proc "contextless" (x: f64) -> f64 {
}
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))) }
+// Removes the fractional part of the value, i.e. rounds towards zero.
trunc :: proc{
trunc_f16, trunc_f16le, trunc_f16be,
trunc_f32, trunc_f32le, trunc_f32be,
@@ -958,7 +959,7 @@ classify_f16 :: proc "contextless" (x: f16) -> Float_Class {
return .Neg_Zero
}
return .Zero
- case x*0.5 == x:
+ case x*0.25 == x:
if x < 0 {
return .Neg_Inf
}
@@ -1027,6 +1028,8 @@ classify_f64 :: proc "contextless" (x: f64) -> Float_Class {
}
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)) }
+// Returns the `Float_Class` of the value, i.e. whether normal, subnormal, zero, negative zero, NaN, infinity or
+// negative infinity.
classify :: proc{
classify_f16, classify_f16le, classify_f16be,
classify_f32, classify_f32le, classify_f32be,
@@ -1196,13 +1199,14 @@ sum :: proc "contextless" (x: $T/[]$E) -> (res: E)
prod :: proc "contextless" (x: $T/[]$E) -> (res: E)
where intrinsics.type_is_numeric(E) {
+ res = 1
for i in x {
res *= i
}
return
}
-cumsum_inplace :: proc "contextless" (x: $T/[]$E) -> T
+cumsum_inplace :: proc "contextless" (x: $T/[]$E)
where intrinsics.type_is_numeric(E) {
for i in 1.. (value: f32) {
+ // Get base points and offsets.
+ base := [2]i64{fast_floor(coord.x), fast_floor(coord.y)}
+ i := [2]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y))}
+
+ // Prime pre-multiplication for hash.
+ bp := base * [2]i64{PRIME_X, PRIME_Y}
+
+ // Unskew.
+ t := f32(i.x + i.y) * f32(UNSKEW_2D)
+ d0 := i + [2]f32{t, t}
+
+ // First vertex.
+ a0 := RSQUARED_2D - d0.x * d0.x - d0.y * d0.y
+ if a0 > 0 {
+ value = (a0 * a0) * (a0 * a0) * grad(seed, [2]i64{bp.x, bp.y}, d0)
+ }
+
+ // Second vertex.
+ a1 := f32(2 * (1 + 2 * UNSKEW_2D) * (1 / UNSKEW_2D + 2)) * t + f32(-2 * (1 + 2 * UNSKEW_2D) * (1 + 2 * UNSKEW_2D)) + a0
+ if a1 > 0 {
+ d1 := d0 - [2]f32{f32(1 + 2 * UNSKEW_2D), f32(1 + 2 * UNSKEW_2D)}
+ value += (a1 * a1) * (a1 * a1) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y + PRIME_Y}, d1)
+ }
+
+ // Third vertex.
+ if d0.y > d0.x {
+ d2 := d0 - [2]f32{f32(UNSKEW_2D), f32(UNSKEW_2D + 1)}
+ a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+ if(a2 > 0) {
+ value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x, bp.y + PRIME_Y}, d2)
+ }
+ } else {
+ d2 := d0 - [2]f32{f32(UNSKEW_2D + 1), f32(UNSKEW_2D)}
+ a2 := RSQUARED_2D - d2.x * d2.x - d2.y * d2.y
+ if(a2 > 0) {
+ value += (a2 * a2) * (a2 * a2) * grad(seed, [2]i64{bp.x + PRIME_X, bp.y}, d2)
+ }
+ }
+
+ return
+}
+
+
+/*
+ Generate overlapping cubic lattices for 3D OpenSimplex2 noise.
+*/
+_internal_noise_3d_unrotated_base :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+ seed := seed
+ // Get base points and offsets.
+ // xr, yr, zr := coord.x, coord.y, coord.z
+
+ rb := [3]i64{fast_round(coord.x), fast_round(coord.y), fast_round(coord.z)}
+ ri := [3]f32{f32(coord.x - f64(rb.x)), f32(coord.y - f64(rb.y)), f32(coord.z - f64(rb.z))}
+
+ // -1 if positive, 1 if negative.
+ i_sign := [3]i64{i64(-1.0 - ri.x) | 1, i64(-1.0 - ri.y) | 1, i64(-1.0 - ri.z) | 1}
+ f_sign := [3]f32{f32(i_sign.x), f32(i_sign.y), f32(i_sign.z)}
+
+ // Compute absolute values, using the above as a shortcut. This was faster in my tests for some reason.
+ a0 := f_sign * -ri
+
+ // Prime pre-multiplication for hash.
+ rbp := rb * [3]i64{PRIME_X, PRIME_Y, PRIME_Z}
+
+ // Loop: Pick an edge on each lattice copy.
+ a := (RSQUARED_3D - ri.x * ri.x) - (ri.y * ri.y + ri.z * ri.z)
+
+ l := 0
+ for {
+ defer l += 1
+
+ // Closest point on cube.
+ if a > 0 {
+ a2 := a * a; a4 := a2 * a2
+ value += a4 * grad(seed, rbp, ri)
+ }
+
+ // Second-closest point.
+ if a0.x >= a0.y && a0.x >= a0.z {
+ b := a + a0.x + a0.x
+ if b > 1 {
+ b -= 1
+ b2 := b * b; b4 := b2 * b2
+ value += b4 * grad(seed, [3]i64{rbp.x - i_sign.x * PRIME_X, rbp.y, rbp.z}, [3]f32{ri.x + f_sign.x, ri.y, ri.z})
+ }
+ } else if a0.y > a0.x && a0.y >= a0.z {
+ b := a + a0.y + a0.y
+ if b > 1 {
+ b -= 1
+ b2 := b * b; b4 := b2 * b2
+ value += b4 * grad(seed, [3]i64{rbp.x, rbp.y - i_sign.y * PRIME_Y, rbp.z}, [3]f32{ri.x, ri.y + f_sign.y, ri.z})
+ }
+ } else {
+ b := a + a0.z + a0.z
+ if b > 1 {
+ b -= 1
+ b2 := b * b; b4 := b2 * b2
+ value += b4 * grad(seed, [3]i64{rbp.x, rbp.y, rbp.z - i_sign.z * PRIME_Z}, [3]f32{ri.x, ri.y, ri.z + f_sign.z})
+ }
+ }
+
+ // Break from loop if we're done, skipping updates below.
+ if l == 1 {
+ break
+ }
+
+ // Update absolute value.
+ a0 = 0.5 - a0
+
+ // Update relative coordinate.
+ ri = a0 * f_sign
+
+ // Update falloff.
+ a += (0.75 - a0.x) - (a0.y + a0.z)
+
+ // Update prime for hash.
+ rbp += [3]i64{i_sign.x >> 1, i_sign.y >> 1, i_sign.z >> 1} & {PRIME_X, PRIME_Y, PRIME_Z}
+
+ // Update the reverse sign indicators.
+ i_sign = -i_sign
+ f_sign = -f_sign
+
+ // And finally update the seed for the other lattice copy.
+ seed ~= SEED_FLIP_3D
+ }
+
+ return value
+}
+
+/*
+ 4D OpenSimplex2 noise base.
+*/
+_internal_noise_4d_unskewed_base :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ seed := seed
+
+ // Get base points and offsets
+ base := [4]i64{fast_floor(coord.x), fast_floor(coord.y), fast_floor(coord.z), fast_floor(coord.w)}
+ si := [4]f32{f32(coord.x - f64(base.x)), f32(coord.y - f64(base.y)), f32(coord.z - f64(base.z)), f32(coord.w - f64(base.w))}
+
+ // Determine which lattice we can be confident has a contributing point its corresponding cell's base simplex.
+ // We only look at the spaces between the diagonal planes. This proved effective in all of my tests.
+ si_sum := (si.x + si.y) + (si.z + si.w)
+ starting_lattice := i64(si_sum * 1.25)
+
+ // Offset for seed based on first lattice copy.
+ seed += starting_lattice * SEED_OFFSET_4D
+
+ // Offset for lattice point relative positions (skewed)
+ starting_lattice_offset := f32(starting_lattice) * -LATTICE_STEP_4D
+ si += starting_lattice_offset
+
+ // Prep for vertex contributions.
+ ssi := (si_sum + starting_lattice_offset * 4) * UNSKEW_4D
+
+ // Prime pre-multiplication for hash.
+ svp := base * [4]i64{PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+
+ // Five points to add, total, from five copies of the A4 lattice.
+ for i : i64 = 0; ; i += 1 {
+
+ // Next point is the closest vertex on the 4-simplex whose base vertex is the aforementioned vertex.
+ score := 1.0 + ssi * (-1.0 / UNSKEW_4D) // Seems slightly faster than 1.0-xsi-ysi-zsi-wsi
+ if si.x >= si.x && si.x >= si.z && si.x >= si.w && si.x >= score {
+ svp.x += PRIME_X
+ si.x -= 1
+ ssi -= UNSKEW_4D
+ }
+ else if si.y > si.x && si.y >= si.z && si.y >= si.w && si.y >= score {
+ svp.y += PRIME_Y
+ si.y -= 1
+ ssi -= UNSKEW_4D
+ }
+ else if si.z > si.x && si.z > si.y && si.z >= si.w && si.z >= score {
+ svp.z += PRIME_Z
+ si.z -= 1
+ ssi -= UNSKEW_4D
+ }
+ else if si.w > si.x && si.w > si.y && si.w > si.z && si.w >= score {
+ svp.w += PRIME_W
+ si.w -= 1
+ ssi -= UNSKEW_4D
+ }
+
+ // gradient contribution with falloff.
+ d := si + ssi
+ a := (d.x * d.x + d.y * d.y) + (d.z * d.z + d.w * d.w)
+
+ if a < RSQUARED_4D {
+ a -= RSQUARED_4D
+ a *= a; a4 := a * a
+ value += a4 * grad(seed, svp, d)
+ }
+
+ // Break from loop if we're done, skipping updates below.
+ if i == 4 {
+ break
+ }
+
+ // Update for next lattice copy shifted down by <-0.2, -0.2, -0.2, -0.2>.
+ si += LATTICE_STEP_4D
+ ssi += LATTICE_STEP_4D * 4 * UNSKEW_4D
+ seed -= SEED_OFFSET_4D
+
+ // Because we don't always start on the same lattice copy, there's a special reset case.
+ if i == starting_lattice {
+ svp -= {PRIME_X, PRIME_Y, PRIME_Z, PRIME_W}
+ seed += SEED_OFFSET_4D * 5
+ }
+ }
+ return
+}
+
+/*
+ Utility functions
+*/
+@(optimization_mode="speed")
+grad_2d :: proc(seed: i64, svp: [2]i64, delta: [2]f32) -> (value: f32) {
+ hash := seed ~ svp.x ~ svp.y
+ hash *= HASH_MULTIPLIER
+ hash ~= hash >> (64 - N_GRADS_2D_EXPONENT + 1)
+
+ gi := hash & ((N_GRADS_2D - 1) << 1)
+ return GRADIENTS_2D[gi] * delta.x + GRADIENTS_2D[gi | 1] * delta.y
+}
+
+@(optimization_mode="speed")
+grad_3d :: proc(seed: i64, rvp: [3]i64, delta: [3]f32) -> (value: f32) {
+ hash := (seed ~ rvp.x) ~ (rvp.y ~ rvp.z)
+ hash *= HASH_MULTIPLIER
+ hash ~= hash >> (64 - N_GRADS_3D_EXPONENT + 2)
+
+ gi := hash & ((N_GRADS_3D - 1) << 2)
+ return GRADIENTS_3D[gi] * delta.x + GRADIENTS_3D[gi | 1] * delta.y + GRADIENTS_3D[gi | 2] * delta.z
+}
+
+@(optimization_mode="speed")
+grad_4d :: proc(seed: i64, svp: [4]i64, delta: [4]f32) -> (value: f32) {
+ hash := seed ~ (svp.x ~ svp.y) ~ (svp.z ~ svp.w)
+ hash *= HASH_MULTIPLIER
+ hash ~= hash >> (64 - N_GRADS_4D_EXPONENT + 2)
+
+ gi := hash & ((N_GRADS_4D - 1) << 2)
+ return (GRADIENTS_4D[gi] * delta.x + GRADIENTS_4D[gi | 1] * delta.y) + (GRADIENTS_4D[gi | 2] * delta.z + GRADIENTS_4D[gi | 3] * delta.w)
+}
+
+grad :: proc {grad_2d, grad_3d, grad_4d}
+
+@(optimization_mode="speed")
+fast_floor :: proc(x: f64) -> (floored: i64) {
+ xi := i64(x)
+ return x < f64(xi) ? xi - 1 : xi
+}
+
+@(optimization_mode="speed")
+fast_round :: proc(x: f64) -> (rounded: i64) {
+ return x < 0 ? i64(x - 0.5) : i64(x + 0.5)
+}
\ No newline at end of file
diff --git a/core/math/noise/opensimplex2.odin b/core/math/noise/opensimplex2.odin
new file mode 100644
index 000000000..d90dafdf5
--- /dev/null
+++ b/core/math/noise/opensimplex2.odin
@@ -0,0 +1,171 @@
+/*
+ OpenSimplex2 noise implementation.
+
+ Ported from https://github.com/KdotJPG/OpenSimplex2.
+ Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+package math_noise
+
+/*
+ Input coordinate vectors
+*/
+Vec2 :: [2]f64
+Vec3 :: [3]f64
+Vec4 :: [4]f64
+
+/*
+ Noise Evaluators
+*/
+
+/*
+ 2D Simplex noise, standard lattice orientation.
+*/
+noise_2d :: proc(seed: i64, coord: Vec2) -> (value: f32) {
+ // Get points for A2* lattice
+ skew := SKEW_2D * (coord.x + coord.y)
+ skewed := coord + skew
+
+ return _internal_noise_2d_unskewed_base(seed, skewed)
+}
+
+/*
+ 2D Simplex noise, with Y pointing down the main diagonal.
+ Might be better for a 2D sandbox style game, where Y is vertical.
+ Probably slightly less optimal for heightmaps or continent maps,
+ unless your map is centered around an equator. It's a subtle
+ difference, but the option is here to make it an easy choice.
+*/
+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
+ yy := coord.y * (ROOT_2_OVER_2 * (1 + 2 * SKEW_2D))
+ return _internal_noise_2d_unskewed_base(seed, Vec2{yy + xx, yy - xx})
+}
+
+
+/*
+ 3D OpenSimplex2 noise, with better visual isotropy in (X, Y).
+ Recommended for 3D terrain and time-varied animations.
+ The Z coordinate should always be the "different" coordinate in whatever your use case is.
+ If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, z, Y)` or use `noise_3d_xz_before_y`.
+ 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)`.
+*/
+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,
+ and the planes formed by XY are moved far out of alignment with the cube faces.
+ Orthonormal rotation. Not a skew transform.
+ */
+ xy := coord.x + coord.y
+ s2 := xy * ROTATE_3D_ORTHOGONALIZER
+ zz := coord.z * ROOT_3_OVER_3
+
+ r := Vec3{coord.x + s2 + zz, coord.y + s2 + zz, xy * -ROOT_3_OVER_3 + zz}
+
+ // Evaluate both lattices to form a BCC lattice.
+ return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+ 3D OpenSimplex2 noise, with better visual isotropy in (X, Z).
+ Recommended for 3D terrain and time-varied animations.
+ The Y coordinate should always be the "different" coordinate in whatever your use case is.
+ If Y is vertical in world coordinates, call `noise_3d_improve_xz(x, Y, z)`.
+ 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`.
+*/
+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,
+ and the planes formed by XZ are moved far out of alignment with the cube faces.
+ Orthonormal rotation. Not a skew transform.
+ */
+ xz := coord.x + coord.z
+ s2 := xz * ROTATE_3D_ORTHOGONALIZER
+ yy := coord.y * ROOT_3_OVER_3
+
+ r := Vec3{coord.x + s2 + yy, xz * -ROOT_3_OVER_3 + yy, coord.z + s2 + yy}
+
+ // Evaluate both lattices to form a BCC lattice.
+ return _internal_noise_3d_unrotated_base(seed, r)
+}
+
+/*
+ 3D OpenSimplex2 noise, fallback rotation option
+ 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.
+*/
+noise_3d_fallback :: proc(seed: i64, coord: Vec3) -> (value: f32) {
+ /*
+ Re-orient the cubic lattices via rotation, to produce a familiar look.
+ Orthonormal rotation. Not a skew transform.
+ */
+ bias := FALLBACK_ROTATE_3D * (coord.x + coord.y + coord.z)
+ biased := bias - coord
+ // Evaluate both lattices to form a BCC lattice.
+ return _internal_noise_3d_unrotated_base(seed, biased)
+}
+
+
+/*
+ 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xy`
+ and W for an extra degree of freedom. W repeats eventually.
+ Recommended for time-varied animations which texture a 3D object (W=time)
+ in a space where Z is vertical.
+*/
+noise_4d_improve_xyz_improve_xy :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ xy := coord.x + coord.y
+ s2 := xy * -0.21132486540518699998
+ zz := coord.z * 0.28867513459481294226
+ ww := coord.w * 0.2236067977499788
+
+ xr, yr : f64 = coord.x + (zz + ww + s2), coord.y + (zz + ww + s2)
+ zr : f64 = xy * -0.57735026918962599998 + (zz + ww)
+ wr : f64 = coord.z * -0.866025403784439 + ww
+
+ return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+ 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_improve_xz`
+ and W for an extra degree of freedom. W repeats eventually.
+ Recommended for time-varied animations which texture a 3D object (W=time)
+ in a space where Y is vertical.
+*/
+noise_4d_improve_xyz_improve_xz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ xz := coord.x + coord.z
+ s2 := xz * -0.21132486540518699998
+ yy := coord.y * 0.28867513459481294226
+ ww := coord.w * 0.2236067977499788
+
+ xr, zr : f64 = coord.x + (yy + ww + s2), coord.z + (yy + ww + s2)
+ yr := xz * -0.57735026918962599998 + (yy + ww)
+ wr := coord.y * -0.866025403784439 + ww
+
+ return _internal_noise_4d_unskewed_base(seed, Vec4{xr, yr, zr, wr})
+}
+
+/*
+ 4D OpenSimplex2 noise, with XYZ oriented like `noise_3d_fallback`
+ and W for an extra degree of freedom. W repeats eventually.
+ Recommended for time-varied animations which texture a 3D object (W=time)
+ where there isn't a clear distinction between horizontal and vertical
+*/
+noise_4d_improve_xyz :: proc(seed: i64, coord: Vec4) -> (value: f32) {
+ xyz := coord.x + coord.y + coord.z
+ ww := coord.w * 0.2236067977499788
+ s2 := xyz * -0.16666666666666666 + ww
+
+ skewed := Vec4{coord.x + s2, coord.y + s2, coord.z + s2, -0.5 * xyz + ww}
+ return _internal_noise_4d_unskewed_base(seed, skewed)
+}
+
+/*
+ 4D OpenSimplex2 noise, fallback lattice orientation.
+*/
+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)
+ return _internal_noise_4d_unskewed_base(seed, coord + skew)
+}
\ No newline at end of file
diff --git a/core/math/rand/exp.odin b/core/math/rand/exp.odin
new file mode 100644
index 000000000..c0f92e99c
--- /dev/null
+++ b/core/math/rand/exp.odin
@@ -0,0 +1,214 @@
+package rand
+
+import "core:math"
+
+// exp_float64 returns a exponential distribution in the range (0, max(f64)],
+// with an exponential distribution who rate parameter is 1 (lambda) and whose mean
+// is 1 (1/lambda).
+//
+// To produce a distribution with a differetn rate parameter, divide the result by
+// the desired rate parameter
+//
+// "The Ziggurat Method for Generating Random Variables"
+// Authors: George Marsaglia, Wai Wan Tsang
+// Submitted: 2000-04-15. Published: 2000-10-02.
+// https://www.jstatsoft.org/index.php/jss/article/view/v005i08/ziggurat.pdf [pdf]
+// https://www.jstatsoft.org/article/view/v005i08 [web page]
+//
+exp_float64 :: proc(r: ^Rand = nil) -> f64 {
+ re :: 7.69711747013104972
+
+ @(static)
+ ke := [256]u32{
+ 0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,
+ 0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,
+ 0xf0204efd, 0xf19bdb8e, 0xf2d458bb, 0xf3da104b, 0xf4b86d78,
+ 0xf577ad8a, 0xf61de83d, 0xf6afb784, 0xf730a573, 0xf7a37651,
+ 0xf80a5bb6, 0xf867189d, 0xf8bb1b4f, 0xf9079062, 0xf94d70ca,
+ 0xf98d8c7d, 0xf9c8928a, 0xf9ff175b, 0xfa319996, 0xfa6085f8,
+ 0xfa8c3a62, 0xfab5084e, 0xfadb36c8, 0xfaff0410, 0xfb20a6ea,
+ 0xfb404fb4, 0xfb5e2951, 0xfb7a59e9, 0xfb95038c, 0xfbae44ba,
+ 0xfbc638d8, 0xfbdcf892, 0xfbf29a30, 0xfc0731df, 0xfc1ad1ed,
+ 0xfc2d8b02, 0xfc3f6c4d, 0xfc5083ac, 0xfc60ddd1, 0xfc708662,
+ 0xfc7f8810, 0xfc8decb4, 0xfc9bbd62, 0xfca9027c, 0xfcb5c3c3,
+ 0xfcc20864, 0xfccdd70a, 0xfcd935e3, 0xfce42ab0, 0xfceebace,
+ 0xfcf8eb3b, 0xfd02c0a0, 0xfd0c3f59, 0xfd156b7b, 0xfd1e48d6,
+ 0xfd26daff, 0xfd2f2552, 0xfd372af7, 0xfd3eeee5, 0xfd4673e7,
+ 0xfd4dbc9e, 0xfd54cb85, 0xfd5ba2f2, 0xfd62451b, 0xfd68b415,
+ 0xfd6ef1da, 0xfd750047, 0xfd7ae120, 0xfd809612, 0xfd8620b4,
+ 0xfd8b8285, 0xfd90bcf5, 0xfd95d15e, 0xfd9ac10b, 0xfd9f8d36,
+ 0xfda43708, 0xfda8bf9e, 0xfdad2806, 0xfdb17141, 0xfdb59c46,
+ 0xfdb9a9fd, 0xfdbd9b46, 0xfdc170f6, 0xfdc52bd8, 0xfdc8ccac,
+ 0xfdcc542d, 0xfdcfc30b, 0xfdd319ef, 0xfdd6597a, 0xfdd98245,
+ 0xfddc94e5, 0xfddf91e6, 0xfde279ce, 0xfde54d1f, 0xfde80c52,
+ 0xfdeab7de, 0xfded5034, 0xfdefd5be, 0xfdf248e3, 0xfdf4aa06,
+ 0xfdf6f984, 0xfdf937b6, 0xfdfb64f4, 0xfdfd818d, 0xfdff8dd0,
+ 0xfe018a08, 0xfe03767a, 0xfe05536c, 0xfe07211c, 0xfe08dfc9,
+ 0xfe0a8fab, 0xfe0c30fb, 0xfe0dc3ec, 0xfe0f48b1, 0xfe10bf76,
+ 0xfe122869, 0xfe1383b4, 0xfe14d17c, 0xfe1611e7, 0xfe174516,
+ 0xfe186b2a, 0xfe19843e, 0xfe1a9070, 0xfe1b8fd6, 0xfe1c8289,
+ 0xfe1d689b, 0xfe1e4220, 0xfe1f0f26, 0xfe1fcfbc, 0xfe2083ed,
+ 0xfe212bc3, 0xfe21c745, 0xfe225678, 0xfe22d95f, 0xfe234ffb,
+ 0xfe23ba4a, 0xfe241849, 0xfe2469f2, 0xfe24af3c, 0xfe24e81e,
+ 0xfe25148b, 0xfe253474, 0xfe2547c7, 0xfe254e70, 0xfe25485a,
+ 0xfe25356a, 0xfe251586, 0xfe24e88f, 0xfe24ae64, 0xfe2466e1,
+ 0xfe2411df, 0xfe23af34, 0xfe233eb4, 0xfe22c02c, 0xfe22336b,
+ 0xfe219838, 0xfe20ee58, 0xfe20358c, 0xfe1f6d92, 0xfe1e9621,
+ 0xfe1daef0, 0xfe1cb7ac, 0xfe1bb002, 0xfe1a9798, 0xfe196e0d,
+ 0xfe1832fd, 0xfe16e5fe, 0xfe15869d, 0xfe141464, 0xfe128ed3,
+ 0xfe10f565, 0xfe0f478c, 0xfe0d84b1, 0xfe0bac36, 0xfe09bd73,
+ 0xfe07b7b5, 0xfe059a40, 0xfe03644c, 0xfe011504, 0xfdfeab88,
+ 0xfdfc26e9, 0xfdf98629, 0xfdf6c83b, 0xfdf3ec01, 0xfdf0f04a,
+ 0xfdedd3d1, 0xfdea953d, 0xfde7331e, 0xfde3abe9, 0xfddffdfb,
+ 0xfddc2791, 0xfdd826cd, 0xfdd3f9a8, 0xfdcf9dfc, 0xfdcb1176,
+ 0xfdc65198, 0xfdc15bb3, 0xfdbc2ce2, 0xfdb6c206, 0xfdb117be,
+ 0xfdab2a63, 0xfda4f5fd, 0xfd9e7640, 0xfd97a67a, 0xfd908192,
+ 0xfd8901f2, 0xfd812182, 0xfd78d98e, 0xfd7022bb, 0xfd66f4ed,
+ 0xfd5d4732, 0xfd530f9c, 0xfd48432b, 0xfd3cd59a, 0xfd30b936,
+ 0xfd23dea4, 0xfd16349e, 0xfd07a7a3, 0xfcf8219b, 0xfce7895b,
+ 0xfcd5c220, 0xfcc2aadb, 0xfcae1d5e, 0xfc97ed4e, 0xfc7fe6d4,
+ 0xfc65ccf3, 0xfc495762, 0xfc2a2fc8, 0xfc07ee19, 0xfbe213c1,
+ 0xfbb8051a, 0xfb890078, 0xfb5411a5, 0xfb180005, 0xfad33482,
+ 0xfa839276, 0xfa263b32, 0xf9b72d1c, 0xf930a1a2, 0xf889f023,
+ 0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,
+ 0xe6da6ecf,
+ }
+ @(static)
+ we := [256]f32{
+ 2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,
+ 3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,
+ 5.905944e-11, 6.344942e-11, 6.7643814e-11, 7.1672945e-11,
+ 7.556032e-11, 7.932458e-11, 8.298079e-11, 8.654132e-11,
+ 9.0016515e-11, 9.3415074e-11, 9.674443e-11, 1.0001099e-10,
+ 1.03220314e-10, 1.06377254e-10, 1.09486115e-10, 1.1255068e-10,
+ 1.1557435e-10, 1.1856015e-10, 1.2151083e-10, 1.2442886e-10,
+ 1.2731648e-10, 1.3017575e-10, 1.3300853e-10, 1.3581657e-10,
+ 1.3860142e-10, 1.4136457e-10, 1.4410738e-10, 1.4683108e-10,
+ 1.4953687e-10, 1.5222583e-10, 1.54899e-10, 1.5755733e-10,
+ 1.6020171e-10, 1.6283301e-10, 1.6545203e-10, 1.6805951e-10,
+ 1.7065617e-10, 1.732427e-10, 1.7581973e-10, 1.7838787e-10,
+ 1.8094774e-10, 1.8349985e-10, 1.8604476e-10, 1.8858298e-10,
+ 1.9111498e-10, 1.9364126e-10, 1.9616223e-10, 1.9867835e-10,
+ 2.0119004e-10, 2.0369768e-10, 2.0620168e-10, 2.087024e-10,
+ 2.1120022e-10, 2.136955e-10, 2.1618855e-10, 2.1867974e-10,
+ 2.2116936e-10, 2.2365775e-10, 2.261452e-10, 2.2863202e-10,
+ 2.311185e-10, 2.3360494e-10, 2.360916e-10, 2.3857874e-10,
+ 2.4106667e-10, 2.4355562e-10, 2.4604588e-10, 2.485377e-10,
+ 2.5103128e-10, 2.5352695e-10, 2.560249e-10, 2.585254e-10,
+ 2.6102867e-10, 2.6353494e-10, 2.6604446e-10, 2.6855745e-10,
+ 2.7107416e-10, 2.7359479e-10, 2.761196e-10, 2.7864877e-10,
+ 2.8118255e-10, 2.8372119e-10, 2.8626485e-10, 2.888138e-10,
+ 2.9136826e-10, 2.939284e-10, 2.9649452e-10, 2.9906677e-10,
+ 3.016454e-10, 3.0423064e-10, 3.0682268e-10, 3.0942177e-10,
+ 3.1202813e-10, 3.1464195e-10, 3.1726352e-10, 3.19893e-10,
+ 3.2253064e-10, 3.251767e-10, 3.2783135e-10, 3.3049485e-10,
+ 3.3316744e-10, 3.3584938e-10, 3.3854083e-10, 3.4124212e-10,
+ 3.4395342e-10, 3.46675e-10, 3.4940711e-10, 3.5215003e-10,
+ 3.5490397e-10, 3.5766917e-10, 3.6044595e-10, 3.6323455e-10,
+ 3.660352e-10, 3.6884823e-10, 3.7167386e-10, 3.745124e-10,
+ 3.773641e-10, 3.802293e-10, 3.8310827e-10, 3.860013e-10,
+ 3.8890866e-10, 3.918307e-10, 3.9476775e-10, 3.9772008e-10,
+ 4.0068804e-10, 4.0367196e-10, 4.0667217e-10, 4.09689e-10,
+ 4.1272286e-10, 4.1577405e-10, 4.1884296e-10, 4.2192994e-10,
+ 4.250354e-10, 4.281597e-10, 4.313033e-10, 4.3446652e-10,
+ 4.3764986e-10, 4.408537e-10, 4.4407847e-10, 4.4732465e-10,
+ 4.5059267e-10, 4.5388301e-10, 4.571962e-10, 4.6053267e-10,
+ 4.6389292e-10, 4.6727755e-10, 4.70687e-10, 4.741219e-10,
+ 4.7758275e-10, 4.810702e-10, 4.845848e-10, 4.8812715e-10,
+ 4.9169796e-10, 4.9529775e-10, 4.989273e-10, 5.0258725e-10,
+ 5.0627835e-10, 5.100013e-10, 5.1375687e-10, 5.1754584e-10,
+ 5.21369e-10, 5.2522725e-10, 5.2912136e-10, 5.330522e-10,
+ 5.370208e-10, 5.4102806e-10, 5.45075e-10, 5.491625e-10,
+ 5.532918e-10, 5.5746385e-10, 5.616799e-10, 5.6594107e-10,
+ 5.7024857e-10, 5.746037e-10, 5.7900773e-10, 5.834621e-10,
+ 5.8796823e-10, 5.925276e-10, 5.971417e-10, 6.018122e-10,
+ 6.065408e-10, 6.113292e-10, 6.1617933e-10, 6.2109295e-10,
+ 6.260722e-10, 6.3111916e-10, 6.3623595e-10, 6.4142497e-10,
+ 6.4668854e-10, 6.5202926e-10, 6.5744976e-10, 6.6295286e-10,
+ 6.6854156e-10, 6.742188e-10, 6.79988e-10, 6.858526e-10,
+ 6.9181616e-10, 6.978826e-10, 7.04056e-10, 7.103407e-10,
+ 7.167412e-10, 7.2326256e-10, 7.2990985e-10, 7.366886e-10,
+ 7.4360473e-10, 7.5066453e-10, 7.5787476e-10, 7.6524265e-10,
+ 7.7277595e-10, 7.80483e-10, 7.883728e-10, 7.9645507e-10,
+ 8.047402e-10, 8.1323964e-10, 8.219657e-10, 8.309319e-10,
+ 8.401528e-10, 8.496445e-10, 8.594247e-10, 8.6951274e-10,
+ 8.799301e-10, 8.9070046e-10, 9.018503e-10, 9.134092e-10,
+ 9.254101e-10, 9.378904e-10, 9.508923e-10, 9.644638e-10,
+ 9.786603e-10, 9.935448e-10, 1.0091913e-09, 1.025686e-09,
+ 1.0431306e-09, 1.0616465e-09, 1.08138e-09, 1.1025096e-09,
+ 1.1252564e-09, 1.1498986e-09, 1.1767932e-09, 1.206409e-09,
+ 1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,
+ 1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,
+ }
+ @(static)
+ fe := [256]f32{
+ 1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,
+ 0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,
+ 0.7350381, 0.72286767, 0.71127474, 0.70019263, 0.6895665,
+ 0.67935055, 0.6695063, 0.66000086, 0.65080583, 0.6418967,
+ 0.63325197, 0.6248527, 0.6166822, 0.60872537, 0.60096896,
+ 0.5934009, 0.58601034, 0.5787874, 0.57172304, 0.5648092,
+ 0.5580383, 0.5514034, 0.5448982, 0.5385169, 0.53225386,
+ 0.5261042, 0.52006316, 0.5141264, 0.50828975, 0.5025495,
+ 0.496902, 0.49134386, 0.485872, 0.48048335, 0.4751752,
+ 0.46994483, 0.46478975, 0.45970762, 0.45469615, 0.44975325,
+ 0.44487688, 0.44006512, 0.43531612, 0.43062815, 0.42599955,
+ 0.42142874, 0.4169142, 0.41245446, 0.40804818, 0.403694,
+ 0.3993907, 0.39513698, 0.39093173, 0.38677382, 0.38266218,
+ 0.37859577, 0.37457356, 0.37059465, 0.3666581, 0.362763,
+ 0.35890847, 0.35509375, 0.351318, 0.3475805, 0.34388044,
+ 0.34021714, 0.3365899, 0.33299807, 0.32944095, 0.32591796,
+ 0.3224285, 0.3189719, 0.31554767, 0.31215525, 0.30879408,
+ 0.3054636, 0.3021634, 0.29889292, 0.2956517, 0.29243928,
+ 0.28925523, 0.28609908, 0.28297043, 0.27986884, 0.27679393,
+ 0.2737453, 0.2707226, 0.2677254, 0.26475343, 0.26180625,
+ 0.25888354, 0.25598502, 0.2531103, 0.25025907, 0.24743107,
+ 0.24462597, 0.24184346, 0.23908329, 0.23634516, 0.23362878,
+ 0.23093392, 0.2282603, 0.22560766, 0.22297576, 0.22036438,
+ 0.21777324, 0.21520215, 0.21265087, 0.21011916, 0.20760682,
+ 0.20511365, 0.20263945, 0.20018397, 0.19774707, 0.19532852,
+ 0.19292815, 0.19054577, 0.1881812, 0.18583426, 0.18350479,
+ 0.1811926, 0.17889754, 0.17661946, 0.17435817, 0.17211354,
+ 0.1698854, 0.16767362, 0.16547804, 0.16329853, 0.16113494,
+ 0.15898713, 0.15685499, 0.15473837, 0.15263714, 0.15055119,
+ 0.14848037, 0.14642459, 0.14438373, 0.14235765, 0.14034624,
+ 0.13834943, 0.13636707, 0.13439907, 0.13244532, 0.13050574,
+ 0.1285802, 0.12666863, 0.12477092, 0.12288698, 0.12101672,
+ 0.119160056, 0.1173169, 0.115487166, 0.11367077, 0.11186763,
+ 0.11007768, 0.10830083, 0.10653701, 0.10478614, 0.10304816,
+ 0.101323, 0.09961058, 0.09791085, 0.09622374, 0.09454919,
+ 0.09288713, 0.091237515, 0.08960028, 0.087975375, 0.08636274,
+ 0.08476233, 0.083174095, 0.081597984, 0.08003395, 0.07848195,
+ 0.076941945, 0.07541389, 0.07389775, 0.072393484, 0.07090106,
+ 0.069420435, 0.06795159, 0.066494495, 0.06504912, 0.063615434,
+ 0.062193416, 0.060783047, 0.059384305, 0.057997175,
+ 0.05662164, 0.05525769, 0.053905312, 0.052564494, 0.051235236,
+ 0.049917534, 0.048611384, 0.047316793, 0.046033762, 0.0447623,
+ 0.043502413, 0.042254124, 0.041017443, 0.039792392,
+ 0.038578995, 0.037377283, 0.036187284, 0.035009038,
+ 0.033842582, 0.032687962, 0.031545233, 0.030414443, 0.02929566,
+ 0.02818895, 0.027094385, 0.026012046, 0.024942026, 0.023884421,
+ 0.022839336, 0.021806888, 0.020787204, 0.019780423, 0.0187867,
+ 0.0178062, 0.016839107, 0.015885621, 0.014945968, 0.014020392,
+ 0.013109165, 0.012212592, 0.011331013, 0.01046481, 0.009614414,
+ 0.008780315, 0.007963077, 0.0071633533, 0.006381906,
+ 0.0056196423, 0.0048776558, 0.004157295, 0.0034602648,
+ 0.0027887989, 0.0021459677, 0.0015362998, 0.0009672693,
+ 0.00045413437,
+ }
+
+ for {
+ j := uint32(r)
+ i := j & 0xFF
+ x := f64(j) * f64(we[i])
+ if j < ke[i] {
+ return x
+ }
+ if i == 0 {
+ return re - math.ln(float64(r))
+ }
+ if fe[i]+f32(float64(r))*(fe[i-1]-fe[i]) < f32(math.exp(-x)) {
+ return x
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin
index 4a77543ba..a9edd0f19 100644
--- a/core/math/rand/normal.odin
+++ b/core/math/rand/normal.odin
@@ -2,6 +2,12 @@ package rand
import "core:math"
+
+// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
+// with a standard normal distribution with a mean of 0 and standard deviation of 1.
+//
+// sample = norm_float64() * std_dev + mean
+//
//
// Normal distribution
//
@@ -11,12 +17,6 @@ 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]
//
-
-// norm_float64 returns a normally distributed f64 in the range -max(f64) through +max(f64) inclusive,
-// with a standard normal distribution with a mean of 0 and standard deviation of 1.
-//
-// sample = norm_float64() * std_dev + mean
-//
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
rn :: 3.442619855899
@@ -49,7 +49,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
0x7ba90bdc, 0x7a722176, 0x77d664e5,
}
-
@(static)
wn := [128]f32{
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
@@ -85,7 +84,6 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
}
-
@(static)
fn := [128]f32{
1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165,
diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin
index 9bd30c216..19e475835 100644
--- a/core/math/rand/rand.odin
+++ b/core/math/rand/rand.odin
@@ -1,5 +1,7 @@
package rand
+import "core:intrinsics"
+
Rand :: struct {
state: u64,
inc: u64,
@@ -7,9 +9,7 @@ Rand :: struct {
@(private)
-_GLOBAL_SEED_DATA := 1234567890
-@(private)
-global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)))
+global_rand := create(u64(intrinsics.read_cycle_counter()))
set_global_seed :: proc(seed: u64) {
init(&global_rand, seed)
@@ -70,7 +70,7 @@ int31_max :: proc(n: i32, r: ^Rand = nil) -> i32 {
if n&(n-1) == 0 {
return int31(r) & (n-1)
}
- max := i32((1<<31) - 1 - (1<<31)&u32(n))
+ max := i32((1<<31) - 1 - (1<<31)%u32(n))
v := int31(r)
for v > max {
v = int31(r)
@@ -85,7 +85,7 @@ int63_max :: proc(n: i64, r: ^Rand = nil) -> i64 {
if n&(n-1) == 0 {
return int63(r) & (n-1)
}
- max := i64((1<<63) - 1 - (1<<63)&u64(n))
+ max := i64((1<<63) - 1 - (1<<63)%u64(n))
v := int63(r)
for v > max {
v = int63(r)
@@ -100,7 +100,7 @@ int127_max :: proc(n: i128, r: ^Rand = nil) -> i128 {
if n&(n-1) == 0 {
return int127(r) & (n-1)
}
- max := i128((1<<63) - 1 - (1<<63)&u128(n))
+ max := i128((1<<127) - 1 - (1<<127)%u128(n))
v := int127(r)
for v > max {
v = int127(r)
@@ -142,8 +142,8 @@ read :: proc(p: []byte, r: ^Rand = nil) -> (n: int) {
}
// 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) -> []int {
- m := make([]int, n)
+perm :: proc(n: int, r: ^Rand = nil, allocator := context.allocator) -> []int {
+ m := make([]int, n, allocator)
for i := 0; i < n; i += 1 {
j := int_max(i+1, r)
m[i] = m[j]
diff --git a/core/mem/doc.odin b/core/mem/doc.odin
new file mode 100644
index 000000000..fe53dee83
--- /dev/null
+++ b/core/mem/doc.odin
@@ -0,0 +1,34 @@
+/*
+package mem implements various types of allocators.
+
+
+An example of how to use the `Tracking_Allocator` to track subsequent allocations
+in your program and report leaks and bad frees:
+
+```odin
+package foo
+
+import "core:mem"
+import "core:fmt"
+
+_main :: proc() {
+ do stuff
+}
+
+main :: proc() {
+ track: mem.Tracking_Allocator
+ mem.tracking_allocator_init(&track, context.allocator)
+ context.allocator = mem.tracking_allocator(&track)
+
+ _main()
+
+ for _, leak in track.allocation_map {
+ fmt.printf("%v leaked %v bytes\n", leak.location, leak.size)
+ }
+ for bad_free in track.bad_free_array {
+ fmt.printf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory)
+ }
+}
+```
+*/
+package mem
\ No newline at end of file
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index 8eb877e75..b33f8ba13 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -3,6 +3,12 @@ package mem
import "core:runtime"
import "core:intrinsics"
+Byte :: 1
+Kilobyte :: 1024 * Byte
+Megabyte :: 1024 * Kilobyte
+Gigabyte :: 1024 * Megabyte
+Terabyte :: 1024 * Gigabyte
+
set :: proc "contextless" (data: rawptr, value: byte, len: int) -> rawptr {
return runtime.memset(data, i32(value), len)
}
@@ -16,7 +22,7 @@ zero_explicit :: proc "contextless" (data: rawptr, len: int) -> rawptr {
// equivalent semantics to those provided by the C11 Annex K 3.7.4.1
// memset_s call.
intrinsics.mem_zero_volatile(data, len) // Use the volatile mem_zero
- intrinsics.atomic_fence() // Prevent reordering
+ intrinsics.atomic_thread_fence(.Seq_Cst) // Prevent reordering
return data
}
zero_item :: proc "contextless" (item: $P/^$T) {
@@ -166,7 +172,7 @@ slice_data_cast :: proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -> T {
slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) {
s := transmute(Raw_Slice)slice
- return s.data, s.len
+ return (^T)(s.data), s.len
}
buffer_from_slice :: proc "contextless" (backing: $T/[]$E) -> [dynamic]E {
@@ -192,11 +198,6 @@ any_to_bytes :: proc "contextless" (val: any) -> []byte {
}
-kilobytes :: proc "contextless" (x: int) -> int { return (x) * 1024 }
-megabytes :: proc "contextless" (x: int) -> int { return kilobytes(x) * 1024 }
-gigabytes :: proc "contextless" (x: int) -> int { return megabytes(x) * 1024 }
-terabytes :: proc "contextless" (x: int) -> int { return gigabytes(x) * 1024 }
-
is_power_of_two :: proc "contextless" (x: uintptr) -> bool {
if x <= 0 {
return false
diff --git a/core/mem/virtual/virtual.odin b/core/mem/virtual/virtual.odin
index 38c654254..21ab5ef21 100644
--- a/core/mem/virtual/virtual.odin
+++ b/core/mem/virtual/virtual.odin
@@ -6,25 +6,25 @@ DEFAULT_PAGE_SIZE := uint(4096)
Allocator_Error :: mem.Allocator_Error
-reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
return _reserve(size)
}
-commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
return _commit(data, size)
}
-reserve_and_commit :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+reserve_and_commit :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
data = reserve(size) or_return
commit(raw_data(data), size) or_return
return
}
-decommit :: proc(data: rawptr, size: uint) {
+decommit :: proc "contextless" (data: rawptr, size: uint) {
_decommit(data, size)
}
-release :: proc(data: rawptr, size: uint) {
+release :: proc "contextless" (data: rawptr, size: uint) {
_release(data, size)
}
@@ -36,7 +36,7 @@ Protect_Flag :: enum u32 {
Protect_Flags :: distinct bit_set[Protect_Flag; u32]
Protect_No_Access :: Protect_Flags{}
-protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
return _protect(data, size, flags)
}
@@ -82,11 +82,13 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
pmblock := platform_memory_alloc(0, total_size) or_return
pmblock.block.base = ([^]byte)(uintptr(pmblock) + base_offset)
- commit(pmblock.block.base, committed) or_return
+ commit_err := platform_memory_commit(pmblock, uint(base_offset) + committed)
+ assert(commit_err == nil)
+
// Should be zeroed
assert(pmblock.block.used == 0)
assert(pmblock.block.prev == nil)
- if (do_protection) {
+ if do_protection {
protect(rawptr(uintptr(pmblock) + protect_offset), page_size, Protect_No_Access)
}
@@ -105,7 +107,7 @@ memory_block_alloc :: proc(committed, reserved: uint, flags: Memory_Block_Flags)
}
alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int) -> (data: []byte, err: Allocator_Error) {
- calc_alignment_offset :: proc(block: ^Memory_Block, alignment: uintptr) -> uint {
+ calc_alignment_offset :: proc "contextless" (block: ^Memory_Block, alignment: uintptr) -> uint {
alignment_offset := uint(0)
ptr := uintptr(block.base[block.used:])
mask := alignment-1
@@ -115,23 +117,37 @@ alloc_from_memory_block :: proc(block: ^Memory_Block, min_size, alignment: int)
return alignment_offset
}
-
+ do_commit_if_necessary :: proc(block: ^Memory_Block, size: uint) -> (err: Allocator_Error) {
+ if block.committed - block.used < size {
+ pmblock := (^Platform_Memory_Block)(block)
+ base_offset := uint(uintptr(pmblock.block.base) - uintptr(pmblock))
+ platform_total_commit := base_offset + block.used + size
+
+ assert(pmblock.committed <= pmblock.reserved)
+ assert(pmblock.committed < platform_total_commit)
+
+ platform_memory_commit(pmblock, platform_total_commit) or_return
+
+ pmblock.committed = platform_total_commit
+ block.committed = pmblock.committed - base_offset
+ }
+ return nil
+ }
+
+
alignment_offset := calc_alignment_offset(block, uintptr(alignment))
-
size := uint(min_size) + alignment_offset
-
+
if block.used + size > block.reserved {
err = .Out_Of_Memory
return
}
-
- ptr := block.base[block.used:]
- ptr = ptr[alignment_offset:]
-
+ assert(block.committed <= block.reserved)
+ do_commit_if_necessary(block, size) or_return
+
+ data = block.base[block.used+alignment_offset:][:min_size]
block.used += size
- assert(block.used <= block.reserved)
-
- return ptr[:min_size], nil
+ return
}
diff --git a/core/mem/virtual/virtual_linux.odin b/core/mem/virtual/virtual_linux.odin
index 71a56e499..6ae926e47 100644
--- a/core/mem/virtual/virtual_linux.odin
+++ b/core/mem/virtual/virtual_linux.odin
@@ -58,7 +58,7 @@ madvise :: proc "contextless" (addr: rawptr, length: uint, advice: c.int) -> c.i
}
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
MAP_FAILED := rawptr(~uintptr(0))
result := mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
if result == MAP_FAILED {
@@ -67,7 +67,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
return ([^]byte)(result)[:size], nil
}
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := mprotect(data, size, PROT_READ|PROT_WRITE)
if result != 0 {
// TODO(bill): Handle error value correctly
@@ -75,14 +75,14 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
}
return nil
}
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
mprotect(data, size, PROT_NONE)
madvise(data, size, MADV_FREE)
}
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
munmap(data, size)
}
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: c.int
pflags = PROT_NONE
if .Read in flags { pflags |= PROT_READ }
diff --git a/core/mem/virtual/virtual_platform.odin b/core/mem/virtual/virtual_platform.odin
index c4211ba5e..367346f63 100644
--- a/core/mem/virtual/virtual_platform.odin
+++ b/core/mem/virtual/virtual_platform.odin
@@ -1,15 +1,16 @@
//+private
package mem_virtual
-import sync "core:sync/sync2"
+import "core:sync"
Platform_Memory_Block :: struct {
- block: Memory_Block,
- reserved: uint,
+ block: Memory_Block,
+ committed: uint,
+ reserved: uint,
prev, next: ^Platform_Memory_Block,
}
-platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
+platform_memory_alloc :: proc "contextless" (to_commit, to_reserve: uint) -> (block: ^Platform_Memory_Block, err: Allocator_Error) {
to_commit, to_reserve := to_commit, to_reserve
to_reserve = max(to_commit, to_reserve)
@@ -20,12 +21,13 @@ platform_memory_alloc :: proc(to_commit, to_reserve: uint) -> (block: ^Platform_
commit(raw_data(data), to_commit)
block = (^Platform_Memory_Block)(raw_data(data))
- block.reserved = to_reserve
+ block.committed = to_commit
+ block.reserved = to_reserve
return
}
-platform_memory_free :: proc(block: ^Platform_Memory_Block) {
+platform_memory_free :: proc "contextless" (block: ^Platform_Memory_Block) {
if block != nil {
release(block, block.reserved)
}
@@ -52,3 +54,17 @@ platform_memory_init :: proc() {
global_platform_memory_block_sentinel_set = true
}
}
+
+platform_memory_commit :: proc "contextless" (block: ^Platform_Memory_Block, to_commit: uint) -> (err: Allocator_Error) {
+ if to_commit < block.committed {
+ return nil
+ }
+ if to_commit > block.reserved {
+ return .Out_Of_Memory
+ }
+
+
+ commit(block, to_commit) or_return
+ block.committed = to_commit
+ return nil
+}
diff --git a/core/mem/virtual/virtual_windows.odin b/core/mem/virtual/virtual_windows.odin
index 623e8d469..ef0bf6f1a 100644
--- a/core/mem/virtual/virtual_windows.odin
+++ b/core/mem/virtual/virtual_windows.odin
@@ -62,7 +62,7 @@ foreign Kernel32 {
}
-_reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
+_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
result := VirtualAlloc(nil, size, MEM_RESERVE, PAGE_READWRITE)
if result == nil {
err = .Out_Of_Memory
@@ -72,7 +72,7 @@ _reserve :: proc(size: uint) -> (data: []byte, err: Allocator_Error) {
return
}
-_commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
+_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := VirtualAlloc(data, size, MEM_COMMIT, PAGE_READWRITE)
if result == nil {
switch err := GetLastError(); err {
@@ -85,13 +85,13 @@ _commit :: proc(data: rawptr, size: uint) -> Allocator_Error {
}
return nil
}
-_decommit :: proc(data: rawptr, size: uint) {
+_decommit :: proc "contextless" (data: rawptr, size: uint) {
VirtualFree(data, size, MEM_DECOMMIT)
}
-_release :: proc(data: rawptr, size: uint) {
+_release :: proc "contextless" (data: rawptr, size: uint) {
VirtualFree(data, 0, MEM_RELEASE)
}
-_protect :: proc(data: rawptr, size: uint, flags: Protect_Flags) -> bool {
+_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: u32
pflags = PAGE_NOACCESS
switch flags {
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index 260979d89..f4aa67446 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -34,7 +34,7 @@ Node :: struct {
pos: tokenizer.Pos,
end: tokenizer.Pos,
state_flags: Node_State_Flags,
- derived: any,
+ derived: Any_Node,
}
Comment_Group :: struct {
@@ -88,9 +88,11 @@ File :: struct {
Expr :: struct {
using expr_base: Node,
+ derived_expr: Any_Expr,
}
Stmt :: struct {
using stmt_base: Node,
+ derived_stmt: Any_Stmt,
}
Decl :: struct {
using decl_base: Stmt,
@@ -151,6 +153,7 @@ Comp_Lit :: struct {
open: tokenizer.Pos,
elems: []^Expr,
close: tokenizer.Pos,
+ tag: ^Expr,
}
@@ -540,7 +543,7 @@ unparen_expr :: proc(expr: ^Expr) -> (val: ^Expr) {
return
}
for {
- e, ok := val.derived.(Paren_Expr)
+ e, ok := val.derived.(^Paren_Expr)
if !ok || e.expr == nil {
break
}
@@ -705,12 +708,19 @@ Struct_Type :: struct {
name_count: int,
}
+Union_Type_Kind :: enum u8 {
+ Normal,
+ maybe,
+ no_nil,
+ shared_nil,
+}
+
Union_Type :: struct {
using node: Expr,
tok_pos: tokenizer.Pos,
poly_params: ^Field_List,
align: ^Expr,
- is_maybe: bool,
+ kind: Union_Type_Kind,
where_token: tokenizer.Token,
where_clauses: []^Expr,
variants: []^Expr,
@@ -756,4 +766,173 @@ Matrix_Type :: struct {
row_count: ^Expr,
column_count: ^Expr,
elem: ^Expr,
-}
\ No newline at end of file
+}
+
+
+Any_Node :: union {
+ ^Package,
+ ^File,
+ ^Comment_Group,
+
+ ^Bad_Expr,
+ ^Ident,
+ ^Implicit,
+ ^Undef,
+ ^Basic_Lit,
+ ^Basic_Directive,
+ ^Ellipsis,
+ ^Proc_Lit,
+ ^Comp_Lit,
+ ^Tag_Expr,
+ ^Unary_Expr,
+ ^Binary_Expr,
+ ^Paren_Expr,
+ ^Selector_Expr,
+ ^Implicit_Selector_Expr,
+ ^Selector_Call_Expr,
+ ^Index_Expr,
+ ^Deref_Expr,
+ ^Slice_Expr,
+ ^Matrix_Index_Expr,
+ ^Call_Expr,
+ ^Field_Value,
+ ^Ternary_If_Expr,
+ ^Ternary_When_Expr,
+ ^Or_Else_Expr,
+ ^Or_Return_Expr,
+ ^Type_Assertion,
+ ^Type_Cast,
+ ^Auto_Cast,
+ ^Inline_Asm_Expr,
+
+ ^Proc_Group,
+
+ ^Typeid_Type,
+ ^Helper_Type,
+ ^Distinct_Type,
+ ^Poly_Type,
+ ^Proc_Type,
+ ^Pointer_Type,
+ ^Multi_Pointer_Type,
+ ^Array_Type,
+ ^Dynamic_Array_Type,
+ ^Struct_Type,
+ ^Union_Type,
+ ^Enum_Type,
+ ^Bit_Set_Type,
+ ^Map_Type,
+ ^Relative_Type,
+ ^Matrix_Type,
+
+ ^Bad_Stmt,
+ ^Empty_Stmt,
+ ^Expr_Stmt,
+ ^Tag_Stmt,
+ ^Assign_Stmt,
+ ^Block_Stmt,
+ ^If_Stmt,
+ ^When_Stmt,
+ ^Return_Stmt,
+ ^Defer_Stmt,
+ ^For_Stmt,
+ ^Range_Stmt,
+ ^Inline_Range_Stmt,
+ ^Case_Clause,
+ ^Switch_Stmt,
+ ^Type_Switch_Stmt,
+ ^Branch_Stmt,
+ ^Using_Stmt,
+
+ ^Bad_Decl,
+ ^Value_Decl,
+ ^Package_Decl,
+ ^Import_Decl,
+ ^Foreign_Block_Decl,
+ ^Foreign_Import_Decl,
+
+ ^Attribute,
+ ^Field,
+ ^Field_List,
+}
+
+
+Any_Expr :: union {
+ ^Bad_Expr,
+ ^Ident,
+ ^Implicit,
+ ^Undef,
+ ^Basic_Lit,
+ ^Basic_Directive,
+ ^Ellipsis,
+ ^Proc_Lit,
+ ^Comp_Lit,
+ ^Tag_Expr,
+ ^Unary_Expr,
+ ^Binary_Expr,
+ ^Paren_Expr,
+ ^Selector_Expr,
+ ^Implicit_Selector_Expr,
+ ^Selector_Call_Expr,
+ ^Index_Expr,
+ ^Deref_Expr,
+ ^Slice_Expr,
+ ^Matrix_Index_Expr,
+ ^Call_Expr,
+ ^Field_Value,
+ ^Ternary_If_Expr,
+ ^Ternary_When_Expr,
+ ^Or_Else_Expr,
+ ^Or_Return_Expr,
+ ^Type_Assertion,
+ ^Type_Cast,
+ ^Auto_Cast,
+ ^Inline_Asm_Expr,
+
+ ^Proc_Group,
+
+ ^Typeid_Type,
+ ^Helper_Type,
+ ^Distinct_Type,
+ ^Poly_Type,
+ ^Proc_Type,
+ ^Pointer_Type,
+ ^Multi_Pointer_Type,
+ ^Array_Type,
+ ^Dynamic_Array_Type,
+ ^Struct_Type,
+ ^Union_Type,
+ ^Enum_Type,
+ ^Bit_Set_Type,
+ ^Map_Type,
+ ^Relative_Type,
+ ^Matrix_Type,
+}
+
+
+Any_Stmt :: union {
+ ^Bad_Stmt,
+ ^Empty_Stmt,
+ ^Expr_Stmt,
+ ^Tag_Stmt,
+ ^Assign_Stmt,
+ ^Block_Stmt,
+ ^If_Stmt,
+ ^When_Stmt,
+ ^Return_Stmt,
+ ^Defer_Stmt,
+ ^For_Stmt,
+ ^Range_Stmt,
+ ^Inline_Range_Stmt,
+ ^Case_Clause,
+ ^Switch_Stmt,
+ ^Type_Switch_Stmt,
+ ^Branch_Stmt,
+ ^Using_Stmt,
+
+ ^Bad_Decl,
+ ^Value_Decl,
+ ^Package_Decl,
+ ^Import_Decl,
+ ^Foreign_Block_Decl,
+ ^Foreign_Import_Decl,
+}
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index 1e3058678..400c064f5 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -1,16 +1,25 @@
package odin_ast
+import "core:intrinsics"
import "core:mem"
import "core:fmt"
+import "core:reflect"
import "core:odin/tokenizer"
+_ :: intrinsics
new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T {
n, _ := mem.new(T)
n.pos = pos
n.end = end
- n.derived = n^
+ n.derived = n
base: ^Node = n // dummy check
_ = base // "Use" type to make -vet happy
+ when intrinsics.type_has_field(T, "derived_expr") {
+ n.derived_expr = n
+ }
+ when intrinsics.type_has_field(T, "derived_stmt") {
+ n.derived_stmt = n
+ }
return n
}
@@ -59,232 +68,257 @@ clone_node :: proc(node: ^Node) -> ^Node {
return nil
}
- size := size_of(Node)
+ size := size_of(Node)
align := align_of(Node)
- ti := type_info_of(node.derived.id)
+ ti := reflect.union_variant_type_info(node.derived)
if ti != nil {
- size = ti.size
- align = ti.align
+ elem := ti.variant.(reflect.Type_Info_Pointer).elem
+ size = elem.size
+ align = elem.align
}
- switch in node.derived {
- case Package, File:
+ #partial switch in node.derived {
+ case ^Package, ^File:
panic("Cannot clone this node type")
}
res := cast(^Node)mem.alloc(size, align)
src: rawptr = node
if node.derived != nil {
- src = node.derived.data
+ src = (^rawptr)(&node.derived)^
}
mem.copy(res, src, size)
- res.derived.data = rawptr(res)
- res.derived.id = node.derived.id
+ res_ptr_any: any
+ res_ptr_any.data = &res
+ res_ptr_any.id = ti.id
- switch r in &res.derived {
- case Bad_Expr:
- case Ident:
- case Implicit:
- case Undef:
- case Basic_Lit:
+ reflect.set_union_value(res.derived, res_ptr_any)
- case Ellipsis:
+ res_ptr := reflect.deref(res_ptr_any)
+
+ if de := reflect.struct_field_value_by_name(res_ptr, "derived_expr", true); de != nil {
+ reflect.set_union_value(de, res_ptr_any)
+ }
+ if ds := reflect.struct_field_value_by_name(res_ptr, "derived_stmt", true); ds != nil {
+ reflect.set_union_value(ds, res_ptr_any)
+ }
+
+ if res.derived != nil do switch r in res.derived {
+ case ^Package, ^File:
+ case ^Bad_Expr:
+ case ^Ident:
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Basic_Directive:
+ case ^Comment_Group:
+
+ case ^Ellipsis:
r.expr = clone(r.expr)
- case Proc_Lit:
+ case ^Proc_Lit:
r.type = auto_cast clone(r.type)
r.body = clone(r.body)
- case Comp_Lit:
+ case ^Comp_Lit:
r.type = clone(r.type)
r.elems = clone(r.elems)
- case Tag_Expr:
+ case ^Tag_Expr:
r.expr = clone(r.expr)
- case Unary_Expr:
+ case ^Unary_Expr:
r.expr = clone(r.expr)
- case Binary_Expr:
+ case ^Binary_Expr:
r.left = clone(r.left)
r.right = clone(r.right)
- case Paren_Expr:
+ case ^Paren_Expr:
r.expr = clone(r.expr)
- case Selector_Expr:
+ case ^Selector_Expr:
r.expr = clone(r.expr)
r.field = auto_cast clone(r.field)
- case Implicit_Selector_Expr:
+ case ^Implicit_Selector_Expr:
r.field = auto_cast clone(r.field)
- case Selector_Call_Expr:
+ case ^Selector_Call_Expr:
r.expr = clone(r.expr)
r.call = auto_cast clone(r.call)
- case Index_Expr:
+ case ^Index_Expr:
r.expr = clone(r.expr)
r.index = clone(r.index)
- case Matrix_Index_Expr:
+ case ^Matrix_Index_Expr:
r.expr = clone(r.expr)
r.row_index = clone(r.row_index)
r.column_index = clone(r.column_index)
- case Deref_Expr:
+ case ^Deref_Expr:
r.expr = clone(r.expr)
- case Slice_Expr:
+ case ^Slice_Expr:
r.expr = clone(r.expr)
r.low = clone(r.low)
r.high = clone(r.high)
- case Call_Expr:
+ case ^Call_Expr:
r.expr = clone(r.expr)
r.args = clone(r.args)
- case Field_Value:
+ case ^Field_Value:
r.field = clone(r.field)
r.value = clone(r.value)
- case Ternary_If_Expr:
+ case ^Ternary_If_Expr:
r.x = clone(r.x)
r.cond = clone(r.cond)
r.y = clone(r.y)
- case Ternary_When_Expr:
+ case ^Ternary_When_Expr:
r.x = clone(r.x)
r.cond = clone(r.cond)
r.y = clone(r.y)
- case Or_Else_Expr:
+ case ^Or_Else_Expr:
r.x = clone(r.x)
r.y = clone(r.y)
- case Or_Return_Expr:
+ case ^Or_Return_Expr:
r.expr = clone(r.expr)
- case Type_Assertion:
+ case ^Type_Assertion:
r.expr = clone(r.expr)
r.type = clone(r.type)
- case Type_Cast:
+ case ^Type_Cast:
r.type = clone(r.type)
r.expr = clone(r.expr)
- case Auto_Cast:
+ case ^Auto_Cast:
r.expr = clone(r.expr)
- case Inline_Asm_Expr:
+ case ^Inline_Asm_Expr:
r.param_types = clone(r.param_types)
r.return_type = clone(r.return_type)
r.constraints_string = clone(r.constraints_string)
r.asm_string = clone(r.asm_string)
- case Bad_Stmt:
+ case ^Bad_Stmt:
// empty
- case Empty_Stmt:
+ case ^Empty_Stmt:
// empty
- case Expr_Stmt:
+ case ^Expr_Stmt:
r.expr = clone(r.expr)
- case Tag_Stmt:
+ case ^Tag_Stmt:
r.stmt = clone(r.stmt)
- case Assign_Stmt:
+ case ^Assign_Stmt:
r.lhs = clone(r.lhs)
r.rhs = clone(r.rhs)
- case Block_Stmt:
+ case ^Block_Stmt:
r.label = clone(r.label)
r.stmts = clone(r.stmts)
- case If_Stmt:
+ case ^If_Stmt:
r.label = clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.body = clone(r.body)
r.else_stmt = clone(r.else_stmt)
- case When_Stmt:
+ case ^When_Stmt:
r.cond = clone(r.cond)
r.body = clone(r.body)
r.else_stmt = clone(r.else_stmt)
- case Return_Stmt:
+ case ^Return_Stmt:
r.results = clone(r.results)
- case Defer_Stmt:
+ case ^Defer_Stmt:
r.stmt = clone(r.stmt)
- case For_Stmt:
+ case ^For_Stmt:
r.label = clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.post = clone(r.post)
r.body = clone(r.body)
- case Range_Stmt:
+ case ^Range_Stmt:
r.label = clone(r.label)
r.vals = clone(r.vals)
r.expr = clone(r.expr)
r.body = clone(r.body)
- case Case_Clause:
+ case ^Inline_Range_Stmt:
+ r.label = clone(r.label)
+ r.val0 = clone(r.val0)
+ r.val1 = clone(r.val1)
+ r.expr = clone(r.expr)
+ r.body = clone(r.body)
+ case ^Case_Clause:
r.list = clone(r.list)
r.body = clone(r.body)
- case Switch_Stmt:
+ case ^Switch_Stmt:
r.label = clone(r.label)
r.init = clone(r.init)
r.cond = clone(r.cond)
r.body = clone(r.body)
- case Type_Switch_Stmt:
+ case ^Type_Switch_Stmt:
r.label = clone(r.label)
r.tag = clone(r.tag)
r.expr = clone(r.expr)
r.body = clone(r.body)
- case Branch_Stmt:
+ case ^Branch_Stmt:
r.label = auto_cast clone(r.label)
- case Using_Stmt:
+ case ^Using_Stmt:
r.list = clone(r.list)
- case Bad_Decl:
- case Value_Decl:
+ case ^Bad_Decl:
+ case ^Value_Decl:
r.attributes = clone(r.attributes)
r.names = clone(r.names)
r.type = clone(r.type)
r.values = clone(r.values)
- case Package_Decl:
- case Import_Decl:
- case Foreign_Block_Decl:
+ case ^Package_Decl:
+ case ^Import_Decl:
+ case ^Foreign_Block_Decl:
r.attributes = clone(r.attributes)
r.foreign_library = clone(r.foreign_library)
r.body = clone(r.body)
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
r.name = auto_cast clone(r.name)
- case Proc_Group:
+ case ^Proc_Group:
r.args = clone(r.args)
- case Attribute:
+ case ^Attribute:
r.elems = clone(r.elems)
- case Field:
+ case ^Field:
r.names = clone(r.names)
r.type = clone(r.type)
r.default_value = clone(r.default_value)
- case Field_List:
+ case ^Field_List:
r.list = clone(r.list)
- case Typeid_Type:
+ case ^Typeid_Type:
r.specialization = clone(r.specialization)
- case Helper_Type:
+ case ^Helper_Type:
r.type = clone(r.type)
- case Distinct_Type:
+ case ^Distinct_Type:
r.type = clone(r.type)
- case Poly_Type:
+ case ^Poly_Type:
r.type = auto_cast clone(r.type)
r.specialization = clone(r.specialization)
- case Proc_Type:
+ case ^Proc_Type:
r.params = auto_cast clone(r.params)
r.results = auto_cast clone(r.results)
- case Pointer_Type:
+ case ^Pointer_Type:
r.elem = clone(r.elem)
- case Multi_Pointer_Type:
+ case ^Multi_Pointer_Type:
r.elem = clone(r.elem)
- case Array_Type:
+ case ^Array_Type:
r.len = clone(r.len)
r.elem = clone(r.elem)
- case Dynamic_Array_Type:
+ case ^Dynamic_Array_Type:
r.elem = clone(r.elem)
- case Struct_Type:
+ case ^Struct_Type:
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.fields = auto_cast clone(r.fields)
- case Union_Type:
+ case ^Union_Type:
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
r.variants = clone(r.variants)
- case Enum_Type:
+ case ^Enum_Type:
r.base_type = clone(r.base_type)
r.fields = clone(r.fields)
- case Bit_Set_Type:
+ case ^Bit_Set_Type:
r.elem = clone(r.elem)
r.underlying = clone(r.underlying)
- case Map_Type:
+ case ^Map_Type:
r.key = clone(r.key)
r.value = clone(r.value)
- case Matrix_Type:
+ case ^Matrix_Type:
r.row_count = clone(r.row_count)
r.column_count = clone(r.column_count)
r.elem = clone(r.elem)
+ case ^Relative_Type:
+ r.tag = clone(r.tag)
+ r.type = clone(r.type)
case:
- fmt.panicf("Unhandled node kind: %T", r)
+ fmt.panicf("Unhandled node kind: %v", r)
}
return res
diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin
index d0d17cc9e..b4eaf8140 100644
--- a/core/odin/ast/walk.odin
+++ b/core/odin/ast/walk.odin
@@ -52,71 +52,74 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
}
-
v := v
+ if v == nil || node == nil {
+ return
+ }
+
if v = v->visit(node); v == nil {
return
}
switch n in &node.derived {
- case File:
+ case ^File:
if n.docs != nil {
walk(v, n.docs)
}
walk_stmt_list(v, n.decls[:])
- case Package:
+ case ^Package:
for _, f in n.files {
walk(v, f)
}
- case Comment_Group:
+ case ^Comment_Group:
// empty
- case Bad_Expr:
- case Ident:
- case Implicit:
- case Undef:
- case Basic_Lit:
- case Basic_Directive:
- case Ellipsis:
+ case ^Bad_Expr:
+ case ^Ident:
+ case ^Implicit:
+ case ^Undef:
+ case ^Basic_Lit:
+ case ^Basic_Directive:
+ case ^Ellipsis:
if n.expr != nil {
walk(v, n.expr)
}
- case Proc_Lit:
+ case ^Proc_Lit:
walk(v, n.type)
walk(v, n.body)
walk_expr_list(v, n.where_clauses)
- case Comp_Lit:
+ case ^Comp_Lit:
if n.type != nil {
walk(v, n.type)
}
walk_expr_list(v, n.elems)
- case Tag_Expr:
+ case ^Tag_Expr:
walk(v, n.expr)
- case Unary_Expr:
+ case ^Unary_Expr:
walk(v, n.expr)
- case Binary_Expr:
+ case ^Binary_Expr:
walk(v, n.left)
walk(v, n.right)
- case Paren_Expr:
+ case ^Paren_Expr:
walk(v, n.expr)
- case Selector_Expr:
+ case ^Selector_Expr:
walk(v, n.expr)
walk(v, n.field)
- case Implicit_Selector_Expr:
+ case ^Implicit_Selector_Expr:
walk(v, n.field)
- case Selector_Call_Expr:
+ case ^Selector_Call_Expr:
walk(v, n.expr)
walk(v, n.call)
- case Index_Expr:
+ case ^Index_Expr:
walk(v, n.expr)
walk(v, n.index)
- case Matrix_Index_Expr:
+ case ^Matrix_Index_Expr:
walk(v, n.expr)
walk(v, n.row_index)
walk(v, n.column_index)
- case Deref_Expr:
+ case ^Deref_Expr:
walk(v, n.expr)
- case Slice_Expr:
+ case ^Slice_Expr:
walk(v, n.expr)
if n.low != nil {
walk(v, n.low)
@@ -124,57 +127,57 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.high != nil {
walk(v, n.high)
}
- case Call_Expr:
+ case ^Call_Expr:
walk(v, n.expr)
walk_expr_list(v, n.args)
- case Field_Value:
+ case ^Field_Value:
walk(v, n.field)
walk(v, n.value)
- case Ternary_If_Expr:
+ case ^Ternary_If_Expr:
walk(v, n.x)
walk(v, n.cond)
walk(v, n.y)
- case Ternary_When_Expr:
+ case ^Ternary_When_Expr:
walk(v, n.x)
walk(v, n.cond)
walk(v, n.y)
- case Or_Else_Expr:
+ case ^Or_Else_Expr:
walk(v, n.x)
walk(v, n.y)
- case Or_Return_Expr:
+ case ^Or_Return_Expr:
walk(v, n.expr)
- case Type_Assertion:
+ case ^Type_Assertion:
walk(v, n.expr)
if n.type != nil {
walk(v, n.type)
}
- case Type_Cast:
+ case ^Type_Cast:
walk(v, n.type)
walk(v, n.expr)
- case Auto_Cast:
+ case ^Auto_Cast:
walk(v, n.expr)
- case Inline_Asm_Expr:
+ case ^Inline_Asm_Expr:
walk_expr_list(v, n.param_types)
walk(v, n.return_type)
walk(v, n.constraints_string)
walk(v, n.asm_string)
- case Bad_Stmt:
- case Empty_Stmt:
- case Expr_Stmt:
+ case ^Bad_Stmt:
+ case ^Empty_Stmt:
+ case ^Expr_Stmt:
walk(v, n.expr)
- case Tag_Stmt:
+ case ^Tag_Stmt:
walk(v, n.stmt)
- case Assign_Stmt:
+ case ^Assign_Stmt:
walk_expr_list(v, n.lhs)
walk_expr_list(v, n.rhs)
- case Block_Stmt:
+ case ^Block_Stmt:
if n.label != nil {
walk(v, n.label)
}
walk_stmt_list(v, n.stmts)
- case If_Stmt:
+ case ^If_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -186,17 +189,17 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.else_stmt != nil {
walk(v, n.else_stmt)
}
- case When_Stmt:
+ case ^When_Stmt:
walk(v, n.cond)
walk(v, n.body)
if n.else_stmt != nil {
walk(v, n.else_stmt)
}
- case Return_Stmt:
+ case ^Return_Stmt:
walk_expr_list(v, n.results)
- case Defer_Stmt:
+ case ^Defer_Stmt:
walk(v, n.stmt)
- case For_Stmt:
+ case ^For_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -210,7 +213,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.post)
}
walk(v, n.body)
- case Range_Stmt:
+ case ^Range_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -221,7 +224,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk(v, n.expr)
walk(v, n.body)
- case Inline_Range_Stmt:
+ case ^Inline_Range_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -233,10 +236,10 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk(v, n.expr)
walk(v, n.body)
- case Case_Clause:
+ case ^Case_Clause:
walk_expr_list(v, n.list)
walk_stmt_list(v, n.body)
- case Switch_Stmt:
+ case ^Switch_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -247,7 +250,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.cond)
}
walk(v, n.body)
- case Type_Switch_Stmt:
+ case ^Type_Switch_Stmt:
if n.label != nil {
walk(v, n.label)
}
@@ -258,16 +261,16 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.expr)
}
walk(v, n.body)
- case Branch_Stmt:
+ case ^Branch_Stmt:
if n.label != nil {
walk(v, n.label)
}
- case Using_Stmt:
+ case ^Using_Stmt:
walk_expr_list(v, n.list)
- case Bad_Decl:
- case Value_Decl:
+ case ^Bad_Decl:
+ case ^Value_Decl:
if n.docs != nil {
walk(v, n.docs)
}
@@ -280,21 +283,21 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.comment != nil {
walk(v, n.comment)
}
- case Package_Decl:
+ case ^Package_Decl:
if n.docs != nil {
walk(v, n.docs)
}
if n.comment != nil {
walk(v, n.comment)
}
- case Import_Decl:
+ case ^Import_Decl:
if n.docs != nil {
walk(v, n.docs)
}
if n.comment != nil {
walk(v, n.comment)
}
- case Foreign_Block_Decl:
+ case ^Foreign_Block_Decl:
if n.docs != nil {
walk(v, n.docs)
}
@@ -303,7 +306,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.foreign_library)
}
walk(v, n.body)
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
if n.docs != nil {
walk(v, n.docs)
}
@@ -313,11 +316,11 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.comment)
}
- case Proc_Group:
+ case ^Proc_Group:
walk_expr_list(v, n.args)
- case Attribute:
+ case ^Attribute:
walk_expr_list(v, n.elems)
- case Field:
+ case ^Field:
if n.docs != nil {
walk(v, n.docs)
}
@@ -331,31 +334,31 @@ walk :: proc(v: ^Visitor, node: ^Node) {
if n.comment != nil {
walk(v, n.comment)
}
- case Field_List:
+ case ^Field_List:
for x in n.list {
walk(v, x)
}
- case Typeid_Type:
+ case ^Typeid_Type:
if n.specialization != nil {
walk(v, n.specialization)
}
- case Helper_Type:
+ case ^Helper_Type:
walk(v, n.type)
- case Distinct_Type:
+ case ^Distinct_Type:
walk(v, n.type)
- case Poly_Type:
+ case ^Poly_Type:
walk(v, n.type)
if n.specialization != nil {
walk(v, n.specialization)
}
- case Proc_Type:
+ case ^Proc_Type:
walk(v, n.params)
walk(v, n.results)
- case Pointer_Type:
+ case ^Pointer_Type:
walk(v, n.elem)
- case Multi_Pointer_Type:
+ case ^Multi_Pointer_Type:
walk(v, n.elem)
- case Array_Type:
+ case ^Array_Type:
if n.tag != nil {
walk(v, n.tag)
}
@@ -363,12 +366,12 @@ walk :: proc(v: ^Visitor, node: ^Node) {
walk(v, n.len)
}
walk(v, n.elem)
- case Dynamic_Array_Type:
+ case ^Dynamic_Array_Type:
if n.tag != nil {
walk(v, n.tag)
}
walk(v, n.elem)
- case Struct_Type:
+ case ^Struct_Type:
if n.poly_params != nil {
walk(v, n.poly_params)
}
@@ -377,7 +380,7 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk_expr_list(v, n.where_clauses)
walk(v, n.fields)
- case Union_Type:
+ case ^Union_Type:
if n.poly_params != nil {
walk(v, n.poly_params)
}
@@ -386,23 +389,23 @@ walk :: proc(v: ^Visitor, node: ^Node) {
}
walk_expr_list(v, n.where_clauses)
walk_expr_list(v, n.variants)
- case Enum_Type:
+ case ^Enum_Type:
if n.base_type != nil {
walk(v, n.base_type)
}
walk_expr_list(v, n.fields)
- case Bit_Set_Type:
+ case ^Bit_Set_Type:
walk(v, n.elem)
if n.underlying != nil {
walk(v, n.underlying)
}
- case Map_Type:
+ case ^Map_Type:
walk(v, n.key)
walk(v, n.value)
- case Relative_Type:
+ case ^Relative_Type:
walk(v, n.tag)
walk(v, n.type)
- case Matrix_Type:
+ case ^Matrix_Type:
walk(v, n.row_count)
walk(v, n.column_count)
walk(v, n.elem)
diff --git a/core/odin/doc-format/doc_format.odin b/core/odin/doc-format/doc_format.odin
index c80be2489..62682004d 100644
--- a/core/odin/doc-format/doc_format.odin
+++ b/core/odin/doc-format/doc_format.odin
@@ -11,7 +11,7 @@ String :: distinct Array(byte)
Version_Type_Major :: 0
Version_Type_Minor :: 2
-Version_Type_Patch :: 1
+Version_Type_Patch :: 4
Version_Type :: struct {
major, minor, patch: u8,
@@ -77,9 +77,15 @@ Pkg :: struct {
flags: Pkg_Flags,
docs: String,
files: Array(File_Index),
- entities: Array(Entity_Index),
+ entries: Array(Scope_Entry),
}
+Scope_Entry :: struct {
+ name: String,
+ entity: Entity_Index,
+}
+
+
Entity_Kind :: enum u32le {
Invalid = 0,
Constant = 1,
@@ -89,6 +95,7 @@ Entity_Kind :: enum u32le {
Proc_Group = 5,
Import_Name = 6,
Library_Name = 7,
+ Builtin = 8,
}
Entity_Flag :: enum u32le {
@@ -105,8 +112,13 @@ Entity_Flag :: enum u32le {
Type_Alias = 20,
+ Builtin_Pkg_Builtin = 30,
+ Builtin_Pkg_Intrinsics = 31,
+
Var_Thread_Local = 40,
Var_Static = 41,
+
+ Private = 50,
}
Entity_Flags :: distinct bit_set[Entity_Flag; u64le]
@@ -122,6 +134,10 @@ Entity :: struct {
_: u32le, // reserved for init
comment: String,
docs: String,
+ // May be used by (Struct fields and procedure fields):
+ // .Variable
+ // .Constant
+ field_group_index: i32le,
// May used by:
// .Variable
@@ -242,6 +258,8 @@ Type :: struct {
polymorphic_params: Type_Index,
// Used By: .Struct, .Union
where_clauses: Array(String),
+ // Used By: .Struct
+ tags: Array(String),
}
Type_Flags_Basic :: distinct bit_set[Type_Flag_Basic; u32le]
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index 52d4b5e5a..25eda6bed 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -195,10 +195,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
for p.curr_tok.kind != .EOF {
stmt := parse_stmt(p)
if stmt != nil {
- if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+ if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
append(&p.file.decls, stmt)
- if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
- if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
+ if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
+ if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
error(p, stmt.pos, "procedure literal evaluated but not used")
}
}
@@ -428,9 +428,21 @@ expect_closing_brace_of_field_list :: proc(p: ^Parser) -> tokenizer.Token {
str := tokenizer.token_to_string(token)
error(p, end_of_line_pos(p, p.prev_tok), "expected a comma, got %s", str)
}
- return expect_token(p, .Close_Brace)
+ expect_brace := expect_token(p, .Close_Brace)
+
+ if expect_brace.kind != .Close_Brace {
+ for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF && !is_non_inserted_semicolon(p.curr_tok) {
+ advance_token(p)
+ }
+ return p.curr_tok
+ }
+
+ return expect_brace
}
+is_non_inserted_semicolon :: proc(tok: tokenizer.Token) -> bool {
+ return tok.kind == .Semicolon && tok.text != "\n"
+}
is_blank_ident :: proc{
is_blank_ident_string,
@@ -447,7 +459,7 @@ is_blank_ident_token :: proc(tok: tokenizer.Token) -> bool {
return false
}
is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
- if ident, ok := node.derived.(ast.Ident); ok {
+ if ident, ok := node.derived.(^ast.Ident); ok {
return is_blank_ident(ident.name)
}
return true
@@ -490,34 +502,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return true
}
- switch n in node.derived {
- case ast.Empty_Stmt, ast.Block_Stmt:
+ #partial switch n in node.derived {
+ case ^ast.Empty_Stmt, ^ast.Block_Stmt:
return true
- case ast.If_Stmt, ast.When_Stmt,
- ast.For_Stmt, ast.Range_Stmt, ast.Inline_Range_Stmt,
- ast.Switch_Stmt, ast.Type_Switch_Stmt:
+ case ^ast.If_Stmt, ^ast.When_Stmt,
+ ^ast.For_Stmt, ^ast.Range_Stmt, ^ast.Inline_Range_Stmt,
+ ^ast.Switch_Stmt, ^ast.Type_Switch_Stmt:
return true
- case ast.Helper_Type:
+ case ^ast.Helper_Type:
return is_semicolon_optional_for_node(p, n.type)
- case ast.Distinct_Type:
+ case ^ast.Distinct_Type:
return is_semicolon_optional_for_node(p, n.type)
- case ast.Pointer_Type:
+ case ^ast.Pointer_Type:
return is_semicolon_optional_for_node(p, n.elem)
- case ast.Struct_Type, ast.Union_Type, ast.Enum_Type:
+ case ^ast.Struct_Type, ^ast.Union_Type, ^ast.Enum_Type:
// Require semicolon within a procedure body
return p.curr_proc == nil
- case ast.Proc_Lit:
+ case ^ast.Proc_Lit:
return true
- case ast.Package_Decl, ast.Import_Decl, ast.Foreign_Import_Decl:
+ case ^ast.Package_Decl, ^ast.Import_Decl, ^ast.Foreign_Import_Decl:
return true
- case ast.Foreign_Block_Decl:
+ case ^ast.Foreign_Block_Decl:
return is_semicolon_optional_for_node(p, n.body)
- case ast.Value_Decl:
+ case ^ast.Value_Decl:
if n.is_mutable {
return false
}
@@ -629,10 +641,10 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt {
p.curr_tok.kind != .EOF {
stmt := parse_stmt(p)
if stmt != nil {
- if _, ok := stmt.derived.(ast.Empty_Stmt); !ok {
+ if _, ok := stmt.derived.(^ast.Empty_Stmt); !ok {
append(&list, stmt)
- if es, es_ok := stmt.derived.(ast.Expr_Stmt); es_ok && es.expr != nil {
- if _, pl_ok := es.expr.derived.(ast.Proc_Lit); pl_ok {
+ if es, es_ok := stmt.derived.(^ast.Expr_Stmt); es_ok && es.expr != nil {
+ if _, pl_ok := es.expr.derived.(^ast.Proc_Lit); pl_ok {
error(p, stmt.pos, "procedure literal evaluated but not used")
}
}
@@ -710,7 +722,7 @@ convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast.
if stmt == nil {
return nil
}
- if es, ok := stmt.derived.(ast.Expr_Stmt); ok {
+ if es, ok := stmt.derived.(^ast.Expr_Stmt); ok {
return es.expr
}
error(p, stmt.pos, "expected %s, found a simple statement", kind)
@@ -852,7 +864,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if p.curr_tok.kind != .Semicolon {
cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
- if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
+ if as, ok := cond.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
is_range = true
}
}
@@ -888,12 +900,13 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token")
}
} else {
+ allow_token(p, .Semicolon)
body = parse_body(p)
}
if is_range {
- assign_stmt := cond.derived.(ast.Assign_Stmt)
+ assign_stmt := cond.derived.(^ast.Assign_Stmt)
vals := assign_stmt.lhs[:]
rhs: ^ast.Expr
@@ -974,7 +987,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
tag = as
} else {
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In})
- if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
+ if as, ok := tag.derived.(^ast.Assign_Stmt); ok && as.op.kind == .In {
is_type_switch = true
} else if parse_control_statement_semicolon_separator(p) {
init = tag
@@ -1061,14 +1074,14 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
skip_possible_newline(p)
decl := parse_stmt(p)
- switch d in &decl.derived {
- case ast.Value_Decl:
+ #partial switch d in decl.derived_stmt {
+ case ^ast.Value_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
- case ast.Foreign_Block_Decl:
+ case ^ast.Foreign_Block_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
- case ast.Foreign_Import_Decl:
+ case ^ast.Foreign_Import_Decl:
if d.docs == nil { d.docs = docs }
append(&d.attributes, attribute)
case:
@@ -1082,11 +1095,11 @@ parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind:
parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt {
decl := parse_stmt(p)
- switch in decl.derived {
- case ast.Empty_Stmt, ast.Bad_Stmt, ast.Bad_Decl:
+ #partial switch in decl.derived_stmt {
+ case ^ast.Empty_Stmt, ^ast.Bad_Stmt, ^ast.Bad_Decl:
// Ignore
return nil
- case ast.When_Stmt, ast.Value_Decl:
+ case ^ast.When_Stmt, ^ast.Value_Decl:
return decl
}
@@ -1290,13 +1303,13 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
case .Defer:
tok := advance_token(p)
stmt := parse_stmt(p)
- switch s in stmt.derived {
- case ast.Empty_Stmt:
+ #partial switch s in stmt.derived_stmt {
+ case ^ast.Empty_Stmt:
error(p, s.pos, "empty statement after defer (e.g. ';')")
- case ast.Defer_Stmt:
+ case ^ast.Defer_Stmt:
error(p, s.pos, "you cannot defer a defer statement")
stmt = s.stmt
- case ast.Return_Stmt:
+ case ^ast.Return_Stmt:
error(p, s.pos, "you cannot defer a return statement")
}
ds := ast.new(ast.Defer_Stmt, tok.pos, stmt.end)
@@ -1311,7 +1324,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
results: [dynamic]^ast.Expr
- for p.curr_tok.kind != .Semicolon {
+ for p.curr_tok.kind != .Semicolon && p.curr_tok.kind != .Close_Brace {
result := parse_expr(p, false)
append(&results, result)
if p.curr_tok.kind != .Comma ||
@@ -1368,8 +1381,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
expect_token_after(p, .Colon, "identifier list")
decl := parse_value_decl(p, list, docs)
if decl != nil {
- switch d in &decl.derived {
- case ast.Value_Decl:
+ #partial switch d in decl.derived_stmt {
+ case ^ast.Value_Decl:
d.is_using = true
return decl
}
@@ -1400,9 +1413,9 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
return stmt
case "partial":
stmt := parse_stmt(p)
- switch s in &stmt.derived {
- case ast.Switch_Stmt: s.partial = true
- case ast.Type_Switch_Stmt: s.partial = true
+ #partial switch s in stmt.derived_stmt {
+ case ^ast.Switch_Stmt: s.partial = true
+ case ^ast.Type_Switch_Stmt: s.partial = true
case: error(p, stmt.pos, "#partial can only be applied to a switch statement")
}
return stmt
@@ -1547,11 +1560,11 @@ parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt {
}
convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt {
- switch s in stmt.derived {
- case ast.Block_Stmt:
+ #partial switch s in stmt.derived_stmt {
+ case ^ast.Block_Stmt:
error(p, stmt.pos, "expected a normal statement rather than a block statement")
return stmt
- case ast.Empty_Stmt:
+ case ^ast.Empty_Stmt:
error(p, stmt.pos, "expected a non-empty statement")
}
@@ -1628,10 +1641,10 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
id: ^ast.Expr = ident.expr
- switch n in ident.expr.derived {
- case ast.Ident:
- case ast.Bad_Expr:
- case ast.Poly_Type:
+ #partial switch n in ident.expr.derived_expr {
+ case ^ast.Ident:
+ case ^ast.Bad_Expr:
+ case ^ast.Poly_Type:
if allow_poly_names {
if n.specialization == nil {
break
@@ -1793,21 +1806,21 @@ check_procedure_name_list :: proc(p: ^Parser, names: []^ast.Expr) -> bool {
return false
}
- _, first_is_polymorphic := names[0].derived.(ast.Poly_Type)
+ _, first_is_polymorphic := names[0].derived.(^ast.Poly_Type)
any_polymorphic_names := first_is_polymorphic
for i := 1; i < len(names); i += 1 {
name := names[i]
if first_is_polymorphic {
- if _, ok := name.derived.(ast.Poly_Type); ok {
+ if _, ok := name.derived.(^ast.Poly_Type); ok {
any_polymorphic_names = true
} else {
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
return any_polymorphic_names
}
} else {
- if _, ok := name.derived.(ast.Poly_Type); ok {
+ if _, ok := name.derived.(^ast.Poly_Type); ok {
any_polymorphic_names = true
error(p, name.pos, "mixture of polymorphic and non-polymorphic identifiers")
return any_polymorphic_names
@@ -1872,7 +1885,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
if type == nil {
return false
}
- _, ok := type.derived.(ast.Ellipsis)
+ _, ok := type.derived.(^ast.Ellipsis)
return ok
}
@@ -1890,7 +1903,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
type = parse_var_type(p, allowed_flags)
tt := ast.unparen_expr(type)
if is_signature && !any_polymorphic_names {
- if ti, ok := tt.derived.(ast.Typeid_Type); ok && ti.specialization != nil {
+ if ti, ok := tt.derived.(^ast.Typeid_Type); ok && ti.specialization != nil {
error(p, tt.pos, "specialization of typeid is not allowed without polymorphic names")
}
}
@@ -1966,7 +1979,7 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
p.curr_tok.kind != .EOF {
prefix_flags := parse_field_prefixes(p)
param := parse_var_type(p, allowed_flags & {.Typeid_Token, .Ellipsis})
- if _, ok := param.derived.(ast.Ellipsis); ok {
+ if _, ok := param.derived.(^ast.Ellipsis); ok {
if seen_ellipsis {
error(p, param.pos, "extra variadic parameter after ellipsis")
}
@@ -1993,8 +2006,8 @@ parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags
names := make([]^ast.Expr, 1)
names[0] = ast.new(ast.Ident, tok.pos, end_pos(tok))
- switch ident in &names[0].derived {
- case ast.Ident:
+ #partial switch ident in names[0].derived_expr {
+ case ^ast.Ident:
ident.name = tok.text
case:
unreachable()
@@ -2124,12 +2137,12 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
loop: for param in params.list {
if param.type != nil {
- if _, ok := param.type.derived.(ast.Poly_Type); ok {
+ if _, ok := param.type.derived.(^ast.Poly_Type); ok {
is_generic = true
break loop
}
for name in param.names {
- if _, ok := name.derived.(ast.Poly_Type); ok {
+ if _, ok := name.derived.(^ast.Poly_Type); ok {
is_generic = true
break loop
}
@@ -2166,13 +2179,13 @@ parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^
}
}
- switch e in &ast.unparen_expr(expr).derived {
- case ast.Proc_Lit:
+ #partial switch e in ast.unparen_expr(expr).derived_expr {
+ case ^ast.Proc_Lit:
if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal")
}
e.inlining = pi
- case ast.Call_Expr:
+ case ^ast.Call_Expr:
if e.inlining != .None && e.inlining != pi {
error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call")
}
@@ -2263,22 +2276,40 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
bd.name = name.text
original_type := parse_type(p)
type := ast.unparen_expr(original_type)
- switch t in &type.derived {
- case ast.Array_Type: t.tag = bd
- case ast.Dynamic_Array_Type: t.tag = bd
+ #partial switch t in type.derived_expr {
+ case ^ast.Array_Type: t.tag = bd
+ case ^ast.Dynamic_Array_Type: t.tag = bd
case:
error(p, original_type.pos, "expected an array type after #%s", name.text)
}
return original_type
case "partial":
+ tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
+ tag.tok = tok
+ tag.name = name.text
+ original_expr := parse_expr(p, lhs)
+ expr := ast.unparen_expr(original_expr)
+ #partial switch t in expr.derived_expr {
+ case ^ast.Comp_Lit:
+ t.tag = tag
+ case ^ast.Array_Type:
+ t.tag = tag
+ error(p, tok.pos, "#%s has been replaced with #sparse for non-contiguous enumerated array types", name.text)
+ case:
+ error(p, tok.pos, "expected a compound literal after #%s", name.text)
+
+ }
+ return original_expr
+
+ case "sparse":
tag := ast.new(ast.Basic_Directive, tok.pos, end_pos(name))
tag.tok = tok
tag.name = name.text
original_type := parse_type(p)
type := ast.unparen_expr(original_type)
- switch t in &type.derived {
- case ast.Array_Type:
+ #partial switch t in type.derived_expr {
+ case ^ast.Array_Type:
t.tag = tag
case:
error(p, tok.pos, "expected an enumerated array type after #%s", name.text)
@@ -2318,7 +2349,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
return rt
case "force_inline", "force_no_inline":
- return parse_inlining_operand(p, lhs, tok)
+ return parse_inlining_operand(p, lhs, name)
case:
expr := parse_expr(p, lhs)
te := ast.new(ast.Tag_Expr, tok.pos, expr.pos)
@@ -2599,7 +2630,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
tok := expect_token(p, .Union)
poly_params: ^ast.Field_List
align: ^ast.Expr
- is_maybe: bool
+ is_maybe: bool
+ is_no_nil: bool
+ is_shared_nil: bool
if allow_token(p, .Open_Paren) {
param_count: int
@@ -2626,12 +2659,39 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
}
is_maybe = true
+ case "no_nil":
+ if is_no_nil {
+ error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
+ }
+ is_no_nil = true
+ case "shared_nil":
+ if is_shared_nil {
+ error(p, tag.pos, "duplicate union tag '#%s'", tag.text)
+ }
+ is_shared_nil = true
case:
error(p, tag.pos, "invalid union tag '#%s", tag.text)
}
}
p.expr_level = prev_level
+ if is_no_nil && is_maybe {
+ error(p, p.curr_tok.pos, "#maybe and #no_nil cannot be applied together")
+ }
+ if is_no_nil && is_shared_nil {
+ error(p, p.curr_tok.pos, "#shared_nil and #no_nil cannot be applied together")
+ }
+ if is_shared_nil && is_maybe {
+ error(p, p.curr_tok.pos, "#maybe and #shared_nil cannot be applied together")
+ }
+
+ union_kind := ast.Union_Type_Kind.Normal
+ switch {
+ case is_maybe: union_kind = .maybe
+ case is_no_nil: union_kind = .no_nil
+ case is_shared_nil: union_kind = .shared_nil
+ }
+
where_token: tokenizer.Token
where_clauses: []^ast.Expr
@@ -2652,7 +2712,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
variants: [dynamic]^ast.Expr
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
type := parse_type(p)
- if _, ok := type.derived.(ast.Bad_Expr); !ok {
+ if _, ok := type.derived.(^ast.Bad_Expr); !ok {
append(&variants, type)
}
if !allow_token(p, .Comma) {
@@ -2662,13 +2722,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
+
+
ut := ast.new(ast.Union_Type, tok.pos, end_pos(close))
ut.poly_params = poly_params
ut.variants = variants[:]
ut.align = align
ut.where_token = where_token
ut.where_clauses = where_clauses
- ut.is_maybe = is_maybe
+ ut.kind = union_kind
return ut
@@ -2826,19 +2888,19 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool {
if val == nil {
return false
}
- switch _ in val.derived {
- case ast.Bad_Expr,
- ast.Ident,
- ast.Selector_Expr,
- ast.Array_Type,
- ast.Struct_Type,
- ast.Union_Type,
- ast.Enum_Type,
- ast.Dynamic_Array_Type,
- ast.Map_Type,
- ast.Bit_Set_Type,
- ast.Matrix_Type,
- ast.Call_Expr:
+ #partial switch _ in val.derived_expr {
+ case ^ast.Bad_Expr,
+ ^ast.Ident,
+ ^ast.Selector_Expr,
+ ^ast.Array_Type,
+ ^ast.Struct_Type,
+ ^ast.Union_Type,
+ ^ast.Enum_Type,
+ ^ast.Dynamic_Array_Type,
+ ^ast.Map_Type,
+ ^ast.Bit_Set_Type,
+ ^ast.Matrix_Type,
+ ^ast.Call_Expr:
return true
}
return false
@@ -2960,7 +3022,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
ce.close = close.pos
o := ast.unparen_expr(operand)
- if se, ok := o.derived.(ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
+ if se, ok := o.derived.(^ast.Selector_Expr); ok && se.op.kind == .Arrow_Right {
sce := ast.new(ast.Selector_Call_Expr, ce.pos, ce.end)
sce.expr = o
sce.call = ce
@@ -3390,13 +3452,13 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
stmt := parse_stmt(p)
if stmt != nil {
- switch n in &stmt.derived {
- case ast.Block_Stmt: n.label = label
- case ast.If_Stmt: n.label = label
- case ast.For_Stmt: n.label = label
- case ast.Switch_Stmt: n.label = label
- case ast.Type_Switch_Stmt: n.label = label
- case ast.Range_Stmt: n.label = label
+ #partial switch n in stmt.derived_stmt {
+ case ^ast.Block_Stmt: n.label = label
+ case ^ast.If_Stmt: n.label = label
+ case ^ast.For_Stmt: n.label = label
+ case ^ast.Switch_Stmt: n.label = label
+ case ^ast.Type_Switch_Stmt: n.label = label
+ case ^ast.Range_Stmt: n.label = label
}
}
diff --git a/core/odin/printer/printer.odin b/core/odin/printer/printer.odin
index abda44fa2..807cc99bd 100644
--- a/core/odin/printer/printer.odin
+++ b/core/odin/printer/printer.odin
@@ -151,7 +151,7 @@ print :: proc(p: ^Printer, file: ^ast.File) -> string {
fix_lines(p)
- builder := strings.make_builder(0, mem.megabytes(5), p.allocator)
+ builder := strings.make_builder(0, 5 * mem.Megabyte, p.allocator)
last_line := 0
diff --git a/core/odin/printer/visit.odin b/core/odin/printer/visit.odin
index 023583bde..70140f180 100644
--- a/core/odin/printer/visit.odin
+++ b/core/odin/printer/visit.odin
@@ -342,16 +342,16 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
return
}
- switch v in &decl.derived {
- case Expr_Stmt:
+ #partial switch v in decl.derived_stmt {
+ case ^Expr_Stmt:
move_line(p, decl.pos)
visit_expr(p, v.expr)
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case When_Stmt:
+ case ^When_Stmt:
visit_stmt(p, cast(^Stmt)decl)
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -370,7 +370,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for path in v.fullpaths {
push_ident_token(p, path, 0)
}
- case Foreign_Block_Decl:
+ case ^Foreign_Block_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -383,7 +383,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
visit_expr(p, v.foreign_library)
visit_stmt(p, v.body)
- case Import_Decl:
+ case ^Import_Decl:
move_line(p, decl.pos)
if v.name.text != "" {
@@ -395,7 +395,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
push_ident_token(p, v.fullpath, 1)
}
- case Value_Decl:
+ case ^Value_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -446,10 +446,10 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
add_semicolon := true
for value in v.values {
- switch a in value.derived {
- case Union_Type, Enum_Type, Struct_Type:
+ #partial switch a in value.derived {
+ case ^Union_Type, ^Enum_Type, ^Struct_Type:
add_semicolon = false || called_in_stmt
- case Proc_Lit:
+ case ^Proc_Lit:
add_semicolon = false
}
}
@@ -516,23 +516,34 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
return
}
- switch v in stmt.derived {
- case Import_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- case Value_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- case Foreign_Import_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- case Foreign_Block_Decl:
- visit_decl(p, cast(^Decl)stmt, true)
- return
- }
- switch v in stmt.derived {
- case Using_Stmt:
+ switch v in stmt.derived_stmt {
+ case ^Bad_Stmt:
+ case ^Bad_Decl:
+ case ^Package_Decl:
+
+ case ^Empty_Stmt:
+ push_generic_token(p, .Semicolon, 0)
+ case ^Tag_Stmt:
+ push_generic_token(p, .Hash, 1)
+ push_generic_token(p, v.op.kind, 1, v.op.text)
+ visit_stmt(p, v.stmt)
+
+
+ case ^Import_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+ case ^Value_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+ case ^Foreign_Import_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+ case ^Foreign_Block_Decl:
+ visit_decl(p, cast(^Decl)stmt, true)
+ return
+
+ case ^Using_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Using, 1)
@@ -542,7 +553,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case Block_Stmt:
+ case ^Block_Stmt:
move_line(p, v.pos)
if v.pos.line == v.end.line {
@@ -572,7 +583,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_end_brace(p, v.end)
}
}
- case If_Stmt:
+ case ^If_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -595,7 +606,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
uses_do := false
- if check_stmt, ok := v.body.derived.(Block_Stmt); ok && check_stmt.uses_do {
+ if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do {
uses_do = true
}
@@ -626,7 +637,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt)
}
- case Switch_Stmt:
+ case ^Switch_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -654,7 +665,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.cond)
visit_stmt(p, v.body)
- case Case_Clause:
+ case ^Case_Clause:
move_line(p, v.pos)
if !p.config.indent_cases {
@@ -678,7 +689,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if !p.config.indent_cases {
indent(p)
}
- case Type_Switch_Stmt:
+ case ^Type_Switch_Stmt:
move_line(p, v.pos)
hint_current_line(p, {.Switch_Stmt})
@@ -696,7 +707,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.tag)
visit_stmt(p, v.body)
- case Assign_Stmt:
+ case ^Assign_Stmt:
move_line(p, v.pos)
hint_current_line(p, {.Assign})
@@ -710,13 +721,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case Expr_Stmt:
+ case ^Expr_Stmt:
move_line(p, v.pos)
visit_expr(p, v.expr)
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case For_Stmt:
+ case ^For_Stmt:
// this should be simplified
move_line(p, v.pos)
@@ -753,7 +764,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.body)
- case Inline_Range_Stmt:
+ case ^Inline_Range_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -779,7 +790,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr)
visit_stmt(p, v.body)
- case Range_Stmt:
+ case ^Range_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -805,7 +816,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr)
visit_stmt(p, v.body)
- case Return_Stmt:
+ case ^Return_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Return, 1)
@@ -817,7 +828,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case Defer_Stmt:
+ case ^Defer_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Defer, 0)
@@ -826,7 +837,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
- case When_Stmt:
+ case ^When_Stmt:
move_line(p, v.pos)
push_generic_token(p, .When, 1)
visit_expr(p, v.cond)
@@ -846,7 +857,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt)
}
- case Branch_Stmt:
+ case ^Branch_Stmt:
move_line(p, v.pos)
push_generic_token(p, v.tok.kind, 0)
@@ -918,8 +929,15 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
set_source_position(p, expr.pos)
- switch v in expr.derived {
- case Inline_Asm_Expr:
+ switch v in expr.derived_expr {
+ case ^Bad_Expr:
+
+ case ^Tag_Expr:
+ push_generic_token(p, .Hash, 1)
+ push_generic_token(p, v.op.kind, 1, v.op.text)
+ visit_expr(p, v.expr)
+
+ case ^Inline_Asm_Expr:
push_generic_token(p, v.tok.kind, 1, v.tok.text)
push_generic_token(p, .Open_Paren, 1)
@@ -936,42 +954,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Comma, 0)
visit_expr(p, v.constraints_string)
push_generic_token(p, .Close_Brace, 0)
- case Undef:
+ case ^Undef:
push_generic_token(p, .Undef, 1)
- case Auto_Cast:
+ case ^Auto_Cast:
push_generic_token(p, v.op.kind, 1)
visit_expr(p, v.expr)
- case Ternary_If_Expr:
+ case ^Ternary_If_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y)
- case Ternary_When_Expr:
+ case ^Ternary_When_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y)
- case Or_Else_Expr:
+ case ^Or_Else_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.token.kind, 1)
visit_expr(p, v.y)
- case Or_Return_Expr:
+ case ^Or_Return_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.token.kind, 1)
- case Selector_Call_Expr:
+ case ^Selector_Call_Expr:
visit_expr(p, v.call.expr)
push_generic_token(p, .Open_Paren, 1)
visit_exprs(p, v.call.args, {.Add_Comma})
push_generic_token(p, .Close_Paren, 0)
- case Ellipsis:
+ case ^Ellipsis:
push_generic_token(p, .Ellipsis, 1)
visit_expr(p, v.expr)
- case Relative_Type:
+ case ^Relative_Type:
visit_expr(p, v.tag)
visit_expr(p, v.type)
- case Slice_Expr:
+ case ^Slice_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.low)
@@ -981,37 +999,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_expr(p, v.high)
}
push_generic_token(p, .Close_Bracket, 0)
- case Ident:
+ case ^Ident:
if .Enforce_Poly_Names in options {
push_generic_token(p, .Dollar, 1)
push_ident_token(p, v.name, 0)
} else {
push_ident_token(p, v.name, 1)
}
- case Deref_Expr:
+ case ^Deref_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0)
- case Type_Cast:
+ case ^Type_Cast:
push_generic_token(p, v.tok.kind, 1)
push_generic_token(p, .Open_Paren, 0)
visit_expr(p, v.type)
push_generic_token(p, .Close_Paren, 0)
merge_next_token(p)
visit_expr(p, v.expr)
- case Basic_Directive:
+ case ^Basic_Directive:
push_generic_token(p, v.tok.kind, 1)
push_ident_token(p, v.name, 0)
- case Distinct_Type:
+ case ^Distinct_Type:
push_generic_token(p, .Distinct, 1)
visit_expr(p, v.type)
- case Dynamic_Array_Type:
+ case ^Dynamic_Array_Type:
visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1)
push_generic_token(p, .Dynamic, 0)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.elem)
- case Bit_Set_Type:
+ case ^Bit_Set_Type:
push_generic_token(p, .Bit_Set, 1)
push_generic_token(p, .Open_Bracket, 0)
@@ -1023,13 +1041,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
push_generic_token(p, .Close_Bracket, 0)
- case Union_Type:
+ case ^Union_Type:
push_generic_token(p, .Union, 1)
push_poly_params(p, v.poly_params)
- if v.is_maybe {
- push_ident_token(p, "#maybe", 1)
+ switch v.kind {
+ case .Normal:
+ case .maybe: push_ident_token(p, "#maybe", 1)
+ case .no_nil: push_ident_token(p, "#no_nil", 1)
+ case .shared_nil: push_ident_token(p, "#shared_nil", 1)
}
push_where_clauses(p, v.where_clauses)
@@ -1045,7 +1066,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
visit_end_brace(p, v.end)
}
- case Enum_Type:
+ case ^Enum_Type:
push_generic_token(p, .Enum, 1)
hint_current_line(p, {.Enum})
@@ -1068,7 +1089,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
set_source_position(p, v.end)
- case Struct_Type:
+ case ^Struct_Type:
push_generic_token(p, .Struct, 1)
hint_current_line(p, {.Struct})
@@ -1103,7 +1124,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
set_source_position(p, v.end)
- case Proc_Lit:
+ case ^Proc_Lit:
switch v.inlining {
case .None:
case .Inline:
@@ -1112,7 +1133,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_ident_token(p, "#force_no_inline", 0)
}
- visit_proc_type(p, v.type^, true)
+ visit_proc_type(p, v.type, true)
push_where_clauses(p, v.where_clauses)
@@ -1122,16 +1143,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} else {
push_generic_token(p, .Undef, 1)
}
- case Proc_Type:
+ case ^Proc_Type:
visit_proc_type(p, v)
- case Basic_Lit:
+ case ^Basic_Lit:
push_generic_token(p, v.tok.kind, 1, v.tok.text)
- case Binary_Expr:
+ case ^Binary_Expr:
visit_binary_expr(p, v)
- case Implicit_Selector_Expr:
+ case ^Implicit_Selector_Expr:
push_generic_token(p, .Period, 1)
push_ident_token(p, v.field.name, 0)
- case Call_Expr:
+ case ^Call_Expr:
visit_expr(p, v.expr)
push_format_token(p,
@@ -1146,27 +1167,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
push_generic_token(p, .Close_Paren, 0)
- case Typeid_Type:
+ case ^Typeid_Type:
push_generic_token(p, .Typeid, 1)
if v.specialization != nil {
push_generic_token(p, .Quo, 0)
visit_expr(p, v.specialization)
}
- case Selector_Expr:
+ case ^Selector_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0)
visit_expr(p, v.field)
- case Paren_Expr:
+ case ^Paren_Expr:
push_generic_token(p, .Open_Paren, 1)
visit_expr(p, v.expr)
push_generic_token(p, .Close_Paren, 0)
- case Index_Expr:
+ case ^Index_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.index)
push_generic_token(p, .Close_Bracket, 0)
- case Proc_Group:
+ case ^Matrix_Index_Expr:
+ visit_expr(p, v.expr)
+ push_generic_token(p, .Open_Bracket, 0)
+ visit_expr(p, v.row_index)
+ push_generic_token(p, .Comma, 0)
+ visit_expr(p, v.column_index)
+ push_generic_token(p, .Close_Bracket, 0)
+ case ^Proc_Group:
push_generic_token(p, v.tok.kind, 1)
if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
@@ -1181,7 +1209,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0)
}
- case Comp_Lit:
+ case ^Comp_Lit:
if v.type != nil {
visit_expr(p, v.type)
}
@@ -1198,18 +1226,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0)
}
- case Unary_Expr:
+ case ^Unary_Expr:
push_generic_token(p, v.op.kind, 1)
merge_next_token(p)
visit_expr(p, v.expr)
- case Field_Value:
+ case ^Field_Value:
visit_expr(p, v.field)
push_generic_token(p, .Eq, 1)
visit_expr(p, v.value)
- case Type_Assertion:
+ case ^Type_Assertion:
visit_expr(p, v.expr)
- if unary, ok := v.type.derived.(Unary_Expr); ok && unary.op.text == "?" {
+ if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" {
push_generic_token(p, .Period, 0)
visit_expr(p, v.type)
} else {
@@ -1219,13 +1247,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Paren, 0)
}
- case Pointer_Type:
+ case ^Pointer_Type:
push_generic_token(p, .Pointer, 1)
merge_next_token(p)
visit_expr(p, v.elem)
- case Implicit:
+ case ^Implicit:
push_generic_token(p, v.tok.kind, 1)
- case Poly_Type:
+ case ^Poly_Type:
push_generic_token(p, .Dollar, 1)
merge_next_token(p)
visit_expr(p, v.type)
@@ -1235,22 +1263,35 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
merge_next_token(p)
visit_expr(p, v.specialization)
}
- case Array_Type:
+ case ^Array_Type:
visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1)
visit_expr(p, v.len)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.elem)
- case Map_Type:
+ case ^Map_Type:
push_generic_token(p, .Map, 1)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.key)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.value)
- case Helper_Type:
+ case ^Helper_Type:
visit_expr(p, v.type)
+ case ^Multi_Pointer_Type:
+ push_generic_token(p, .Open_Bracket, 1)
+ push_generic_token(p, .Pointer, 0)
+ push_generic_token(p, .Close_Bracket, 0)
+ visit_expr(p, v.elem)
+ case ^Matrix_Type:
+ push_generic_token(p, .Matrix, 1)
+ push_generic_token(p, .Open_Bracket, 0)
+ visit_expr(p, v.row_count)
+ push_generic_token(p, .Comma, 0)
+ visit_expr(p, v.column_count)
+ push_generic_token(p, .Close_Bracket, 0)
+ visit_expr(p, v.elem)
case:
panic(fmt.aprint(expr.derived))
}
@@ -1348,7 +1389,7 @@ visit_field_list :: proc(p: ^Printer, list: ^ast.Field_List, options := List_Opt
}
}
-visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := false) {
+visit_proc_type :: proc(p: ^Printer, proc_type: ^ast.Proc_Type, is_proc_lit := false) {
if is_proc_lit {
push_format_token(p, Format_Token {
kind = .Proc,
@@ -1392,7 +1433,7 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
} else if len(proc_type.results.list) == 1 {
for name in proc_type.results.list[0].names {
- if ident, ok := name.derived.(ast.Ident); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
if ident.name != "_" {
use_parens = true
}
@@ -1410,19 +1451,19 @@ visit_proc_type :: proc(p: ^Printer, proc_type: ast.Proc_Type, is_proc_lit := fa
}
}
-visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
+visit_binary_expr :: proc(p: ^Printer, binary: ^ast.Binary_Expr) {
move_line(p, binary.left.pos)
- if v, ok := binary.left.derived.(ast.Binary_Expr); ok {
+ if v, ok := binary.left.derived.(^ast.Binary_Expr); ok {
visit_binary_expr(p, v)
} else {
visit_expr(p, binary.left)
}
either_implicit_selector := false
- if _, ok := binary.left.derived.(ast.Implicit_Selector_Expr); ok {
+ if _, ok := binary.left.derived.(^ast.Implicit_Selector_Expr); ok {
either_implicit_selector = true
- } else if _, ok := binary.right.derived.(ast.Implicit_Selector_Expr); ok {
+ } else if _, ok := binary.right.derived.(^ast.Implicit_Selector_Expr); ok {
either_implicit_selector = true
}
@@ -1439,7 +1480,7 @@ visit_binary_expr :: proc(p: ^Printer, binary: ast.Binary_Expr) {
move_line(p, binary.right.pos)
- if v, ok := binary.right.derived.(ast.Binary_Expr); ok {
+ if v, ok := binary.right.derived.(^ast.Binary_Expr); ok {
visit_binary_expr(p, v)
} else {
visit_expr(p, binary.right)
@@ -1499,7 +1540,7 @@ visit_signature_list :: proc(p: ^Printer, list: ^ast.Field_List, remove_blank :=
named := false
for name in field.names {
- if ident, ok := name.derived.(ast.Ident); ok {
+ if ident, ok := name.derived.(^ast.Ident); ok {
//for some reason the parser uses _ to mean empty
if ident.name != "_" || !remove_blank {
named = true
diff --git a/core/os/dir_freebsd.odin b/core/os/dir_freebsd.odin
new file mode 100644
index 000000000..74c410a51
--- /dev/null
+++ b/core/os/dir_freebsd.odin
@@ -0,0 +1,70 @@
+package os
+
+import "core:strings"
+import "core:mem"
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+ dirp: Dir
+ dirp, err = _fdopendir(fd)
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer _closedir(dirp)
+
+ dirpath: string
+ dirpath, err = absolute_path_from_handle(fd)
+
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer delete(dirpath)
+
+ n := n
+ size := n
+ if n <= 0 {
+ n = -1
+ size = 100
+ }
+
+ dfi := make([dynamic]File_Info, 0, size, allocator)
+
+ for {
+ entry: Dirent
+ end_of_stream: bool
+ entry, err, end_of_stream = _readdir(dirp)
+ if err != ERROR_NONE {
+ for fi_ in dfi {
+ file_info_delete(fi_, allocator)
+ }
+ delete(dfi)
+ return
+ } else if end_of_stream {
+ break
+ }
+
+ fi_: File_Info
+ filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
+
+ if filename == "." || filename == ".." {
+ continue
+ }
+
+ fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
+ defer delete(fullpath, context.temp_allocator)
+
+ fi_, err = stat(fullpath, allocator)
+ if err != ERROR_NONE {
+ for fi__ in dfi {
+ file_info_delete(fi__, allocator)
+ }
+ delete(dfi)
+ return
+ }
+
+ append(&dfi, fi_)
+ }
+
+ return dfi[:], ERROR_NONE
+}
diff --git a/core/os/dir_openbsd.odin b/core/os/dir_openbsd.odin
new file mode 100644
index 000000000..465fd35ae
--- /dev/null
+++ b/core/os/dir_openbsd.odin
@@ -0,0 +1,71 @@
+package os
+
+import "core:strings"
+import "core:mem"
+
+read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
+ dirp: Dir
+ dirp, err = _fdopendir(fd)
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer _closedir(dirp)
+
+ // XXX OpenBSD
+ dirpath: string
+ dirpath, err = absolute_path_from_handle(fd)
+
+ if err != ERROR_NONE {
+ return
+ }
+
+ defer delete(dirpath)
+
+ n := n
+ size := n
+ if n <= 0 {
+ n = -1
+ size = 100
+ }
+
+ dfi := make([dynamic]File_Info, 0, size, allocator)
+
+ for {
+ entry: Dirent
+ end_of_stream: bool
+ entry, err, end_of_stream = _readdir(dirp)
+ if err != ERROR_NONE {
+ for fi_ in dfi {
+ file_info_delete(fi_, allocator)
+ }
+ delete(dfi)
+ return
+ } else if end_of_stream {
+ break
+ }
+
+ fi_: File_Info
+ filename := cast(string)(transmute(cstring)mem.Raw_Cstring{ data = &entry.name[0] })
+
+ if filename == "." || filename == ".." {
+ continue
+ }
+
+ fullpath := strings.join( []string{ dirpath, filename }, "/", context.temp_allocator)
+ defer delete(fullpath, context.temp_allocator)
+
+ fi_, err = stat(fullpath, allocator)
+ if err != ERROR_NONE {
+ for fi__ in dfi {
+ file_info_delete(fi__, allocator)
+ }
+ delete(dfi)
+ return
+ }
+
+ append(&dfi, fi_)
+ }
+
+ return dfi[:], ERROR_NONE
+}
diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin
index ff7e53293..3261b8cb3 100644
--- a/core/os/dir_windows.odin
+++ b/core/os/dir_windows.odin
@@ -82,6 +82,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
wpath_search[len(wpath)+2] = 0
path := cleanpath_from_buf(wpath)
+ defer delete(path)
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin
index 419f8bbc2..a9f78070f 100644
--- a/core/os/file_windows.odin
+++ b/core/os/file_windows.odin
@@ -20,13 +20,13 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
}
+ if mode&O_CREATE != 0 {
+ access |= win32.FILE_GENERIC_WRITE
+ }
if mode&O_APPEND != 0 {
access &~= win32.FILE_GENERIC_WRITE
access |= win32.FILE_APPEND_DATA
}
- if mode&O_CREATE != 0 {
- access |= win32.FILE_GENERIC_WRITE
- }
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
sa: ^win32.SECURITY_ATTRIBUTES = nil
@@ -106,19 +106,23 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
BUF_SIZE :: 386
buf16: [BUF_SIZE]u16
buf8: [4*BUF_SIZE]u8
-
+
for n < len(b) && err == 0 {
- max_read := u32(min(BUF_SIZE, len(b)/4))
+ min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
+ max_read := u32(min(BUF_SIZE, min_read))
+ if max_read == 0 {
+ break
+ }
single_read_length: u32
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
if !ok {
err = Errno(win32.GetLastError())
}
-
+
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
src := buf8[:buf8_len]
-
+
ctrl_z := false
for i := 0; i < len(src) && n+i < len(b); i += 1 {
x := src[i]
@@ -129,9 +133,16 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
b[n] = x
n += 1
}
- if ctrl_z || single_read_length < len(buf16) {
+ if ctrl_z || single_read_length < max_read {
break
}
+
+ // NOTE(bill): if the last two values were a newline, then it is expected that
+ // this is the end of the input
+ if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
+ break
+ }
+
}
return
@@ -309,9 +320,6 @@ stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h))
- when size_of(uintptr) == 8 {
- win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0)
- }
return Handle(fd)
}
@@ -399,7 +407,7 @@ is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' {
return true
}
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
if len(path) > 2 {
switch path[0] {
case 'A'..='Z', 'a'..='z':
diff --git a/core/os/os.odin b/core/os/os.odin
index 83158be80..e880ec21e 100644
--- a/core/os/os.odin
+++ b/core/os/os.odin
@@ -139,7 +139,7 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
}
mode: int = 0
- when OS == "linux" || OS == "darwin" {
+ when OS == .Linux || OS == .Darwin {
// NOTE(justasd): 644 (owner read, write; group read; others read)
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
}
@@ -206,11 +206,19 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
}
}
- aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, mem.Allocator_Error) {
+ aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
if p == nil {
return nil, nil
}
- return aligned_alloc(new_size, new_alignment, p)
+
+ 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:])
+ mem.zero(new_region, new_size - old_size)
+ }
+ return
}
switch mode {
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index 028951fe3..7fc7a4ac0 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -1,6 +1,6 @@
package os2
-import sync "core:sync/sync2"
+import "core:sync"
import "core:time"
import "core:runtime"
diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin
index 6dd99c621..976e61bb1 100644
--- a/core/os/os2/user.odin
+++ b/core/os/os2/user.odin
@@ -3,13 +3,13 @@ package os2
import "core:strings"
user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
- switch ODIN_OS {
- case "windows":
+ #partial switch ODIN_OS {
+ case .Windows:
dir = get_env("LocalAppData")
if dir != "" {
dir = strings.clone(dir, allocator)
}
- case "darwin":
+ case .Darwin:
dir = get_env("HOME")
if dir != "" {
dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
@@ -29,13 +29,13 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin
}
user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) {
- switch ODIN_OS {
- case "windows":
+ #partial switch ODIN_OS {
+ case .Windows:
dir = get_env("AppData")
if dir != "" {
dir = strings.clone(dir, allocator)
}
- case "darwin":
+ case .Darwin:
dir = get_env("HOME")
if dir != "" {
dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
@@ -56,8 +56,8 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi
user_home_dir :: proc() -> (dir: string, is_defined: bool) {
env := "HOME"
- switch ODIN_OS {
- case "windows":
+ #partial switch ODIN_OS {
+ case .Windows:
env = "USERPROFILE"
}
if v := get_env(env); v != "" {
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index d40c80aeb..ae5336849 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -260,13 +260,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete
-S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
-S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
-S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
-S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
-S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
-S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
-S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+S_ISLNK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFCHR }
+S_ISBLK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFBLK }
+S_ISFIFO :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFIFO }
+S_ISSOCK :: #force_inline proc(m: u16) -> bool { return (m & S_IFMT) == S_IFSOCK }
R_OK :: 4 // Test for read permission
W_OK :: 2 // Test for write permission
@@ -290,12 +290,22 @@ foreign libc {
@(link_name="fstat64") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
@(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---
- @(link_name="fdopendir$INODE64") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
+
+ @(link_name="fdopendir$INODE64") _unix_fdopendir_amd64 :: proc(fd: Handle) -> Dir ---
+ @(link_name="readdir_r$INODE64") _unix_readdir_r_amd64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+ @(link_name="fdopendir") _unix_fdopendir_arm64 :: proc(fd: Handle) -> Dir ---
+ @(link_name="readdir_r") _unix_readdir_r_arm64 :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
- @(link_name="readdir_r$INODE64") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
+ @(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
+
+ @(link_name="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> c.int ---
+
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
@@ -303,11 +313,22 @@ foreign libc {
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int ---
@(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="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
+when ODIN_ARCH != .arm64 {
+ _unix_fdopendir :: proc {_unix_fdopendir_amd64}
+ _unix_readdir_r :: proc {_unix_readdir_r_amd64}
+} else {
+ _unix_fdopendir :: proc {_unix_fdopendir_arm64}
+ _unix_readdir_r :: proc {_unix_readdir_r_arm64}
+}
+
foreign dl {
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
@@ -319,16 +340,34 @@ get_last_error :: proc() -> int {
return __error()^
}
-open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path)
+get_last_error_string :: proc() -> string {
+ return cast(string)_darwin_string_error(cast(c.int)get_last_error())
+}
+
+open :: proc(path: string, flags: int = O_RDWR, mode: int = 0) -> (Handle, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, i32(flags), u16(mode))
- delete(cstr)
if handle == -1 {
return INVALID_HANDLE, 1
}
+
+when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
+ if mode != 0 {
+ err := fchmod(handle, cast(u16)mode)
+ if err != 0 {
+ _unix_close(handle)
+ return INVALID_HANDLE, 1
+ }
+ }
+}
+
return handle, 0
}
+fchmod :: proc(fildes: Handle, mode: u16) -> Errno {
+ return cast(Errno)_unix_fchmod(fildes, mode)
+}
+
close :: proc(fd: Handle) {
_unix_close(fd)
}
@@ -389,6 +428,65 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+
+rename :: proc(old: string, new: string) -> bool {
+ 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 {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _unix_remove(path_cstr) != -1
+}
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
@@ -530,6 +628,8 @@ heap_alloc :: proc(size: int) -> rawptr {
return _unix_calloc(1, size)
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
return _unix_realloc(ptr, new_size)
}
heap_free :: proc(ptr: rawptr) {
@@ -570,7 +670,17 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
return ERROR_NONE
}
+make_directory :: proc(path: string, mode: u16 = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_mkdir(path_cstr, mode)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
_unix_exit(i32(code))
}
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index e9314b468..4a95028c1 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -7,465 +7,698 @@ import "core:runtime"
import "core:strings"
import "core:c"
-Handle :: distinct i32;
-File_Time :: distinct u64;
-Errno :: distinct i32;
-Syscall :: distinct i32;
+Handle :: distinct i32
+File_Time :: distinct u64
+Errno :: distinct i32
-INVALID_HANDLE :: ~Handle(0);
+INVALID_HANDLE :: ~Handle(0)
-ERROR_NONE: Errno : 0;
-EPERM: Errno : 1;
-ENOENT: Errno : 2;
-ESRCH: Errno : 3;
-EINTR: Errno : 4;
-EIO: Errno : 5;
-ENXIO: Errno : 6;
-E2BIG: Errno : 7;
-ENOEXEC: Errno : 8;
-EBADF: Errno : 9;
-ECHILD: Errno : 10;
-EBEADLK: Errno : 11;
-ENOMEM: Errno : 12;
-EACCESS: Errno : 13;
-EFAULT: Errno : 14;
-ENOTBLK: Errno : 15;
-EBUSY: Errno : 16;
-EEXIST: Errno : 17;
-EXDEV: Errno : 18;
-ENODEV: Errno : 19;
-ENOTDIR: Errno : 20;
-EISDIR: Errno : 21;
-EINVAL: Errno : 22;
-ENFILE: Errno : 23;
-EMFILE: Errno : 24;
-ENOTTY: Errno : 25;
-ETXTBSY: Errno : 26;
-EFBIG: Errno : 27;
-ENOSPC: Errno : 28;
-ESPIPE: Errno : 29;
-EROFS: Errno : 30;
-EMLINK: Errno : 31;
-EPIPE: Errno : 32;
-EDOM: Errno : 33;
-ERANGE: Errno : 34; /* Result too large */
-EAGAIN: Errno : 35;
-EINPROGRESS: Errno : 36;
-EALREADY: Errno : 37;
-ENOTSOCK: Errno : 38;
-EDESTADDRREQ: Errno : 39;
-EMSGSIZE: Errno : 40;
-EPROTOTYPE: Errno : 41;
-ENOPROTOOPT: Errno : 42;
-EPROTONOSUPPORT: Errno : 43;
-ESOCKTNOSUPPORT: Errno : 44;
-EOPNOTSUPP: Errno : 45;
-EPFNOSUPPORT: Errno : 46;
-EAFNOSUPPORT: Errno : 47;
-EADDRINUSE: Errno : 48;
-EADDRNOTAVAIL: Errno : 49;
-ENETDOWN: Errno : 50;
-ENETUNREACH: Errno : 51;
-ENETRESET: Errno : 52;
-ECONNABORTED: Errno : 53;
-ECONNRESET: Errno : 54;
-ENOBUFS: Errno : 55;
-EISCONN: Errno : 56;
-ENOTCONN: Errno : 57;
-ESHUTDOWN: Errno : 58;
-ETIMEDOUT: Errno : 60;
-ECONNREFUSED: Errno : 61;
-ELOOP: Errno : 62;
-ENAMETOOLING: Errno : 63;
-EHOSTDOWN: Errno : 64;
-EHOSTUNREACH: Errno : 65;
-ENOTEMPTY: Errno : 66;
-EPROCLIM: Errno : 67;
-EUSERS: Errno : 68;
-EDQUOT: Errno : 69;
-ESTALE: Errno : 70;
-EBADRPC: Errno : 72;
-ERPCMISMATCH: Errno : 73;
-EPROGUNAVAIL: Errno : 74;
-EPROGMISMATCH: Errno : 75;
-EPROCUNAVAIL: Errno : 76;
-ENOLCK: Errno : 77;
-ENOSYS: Errno : 78;
-EFTYPE: Errno : 79;
-EAUTH: Errno : 80;
-ENEEDAUTH: Errno : 81;
-EIDRM: Errno : 82;
-ENOMSG: Errno : 83;
-EOVERFLOW: Errno : 84;
-ECANCELED: Errno : 85;
-EILSEQ: Errno : 86;
-ENOATTR: Errno : 87;
-EDOOFUS: Errno : 88;
-EBADMSG: Errno : 89;
-EMULTIHOP: Errno : 90;
-ENOLINK: Errno : 91;
-EPROTO: Errno : 92;
-ENOTCAPABLE: Errno : 93;
-ECAPMODE: Errno : 94;
-ENOTRECOVERABLE: Errno : 95;
-EOWNERDEAD: Errno : 96;
+ERROR_NONE: Errno : 0
+EPERM: Errno : 1
+ENOENT: Errno : 2
+ESRCH: Errno : 3
+EINTR: Errno : 4
+EIO: Errno : 5
+ENXIO: Errno : 6
+E2BIG: Errno : 7
+ENOEXEC: Errno : 8
+EBADF: Errno : 9
+ECHILD: Errno : 10
+EBEADLK: Errno : 11
+ENOMEM: Errno : 12
+EACCESS: Errno : 13
+EFAULT: Errno : 14
+ENOTBLK: Errno : 15
+EBUSY: Errno : 16
+EEXIST: Errno : 17
+EXDEV: Errno : 18
+ENODEV: Errno : 19
+ENOTDIR: Errno : 20
+EISDIR: Errno : 21
+EINVAL: Errno : 22
+ENFILE: Errno : 23
+EMFILE: Errno : 24
+ENOTTY: Errno : 25
+ETXTBSY: Errno : 26
+EFBIG: Errno : 27
+ENOSPC: Errno : 28
+ESPIPE: Errno : 29
+EROFS: Errno : 30
+EMLINK: Errno : 31
+EPIPE: Errno : 32
+EDOM: Errno : 33
+ERANGE: Errno : 34 /* Result too large */
+EAGAIN: Errno : 35
+EINPROGRESS: Errno : 36
+EALREADY: Errno : 37
+ENOTSOCK: Errno : 38
+EDESTADDRREQ: Errno : 39
+EMSGSIZE: Errno : 40
+EPROTOTYPE: Errno : 41
+ENOPROTOOPT: Errno : 42
+EPROTONOSUPPORT: Errno : 43
+ESOCKTNOSUPPORT: Errno : 44
+EOPNOTSUPP: Errno : 45
+EPFNOSUPPORT: Errno : 46
+EAFNOSUPPORT: Errno : 47
+EADDRINUSE: Errno : 48
+EADDRNOTAVAIL: Errno : 49
+ENETDOWN: Errno : 50
+ENETUNREACH: Errno : 51
+ENETRESET: Errno : 52
+ECONNABORTED: Errno : 53
+ECONNRESET: Errno : 54
+ENOBUFS: Errno : 55
+EISCONN: Errno : 56
+ENOTCONN: Errno : 57
+ESHUTDOWN: Errno : 58
+ETIMEDOUT: Errno : 60
+ECONNREFUSED: Errno : 61
+ELOOP: Errno : 62
+ENAMETOOLING: Errno : 63
+EHOSTDOWN: Errno : 64
+EHOSTUNREACH: Errno : 65
+ENOTEMPTY: Errno : 66
+EPROCLIM: Errno : 67
+EUSERS: Errno : 68
+EDQUOT: Errno : 69
+ESTALE: Errno : 70
+EBADRPC: Errno : 72
+ERPCMISMATCH: Errno : 73
+EPROGUNAVAIL: Errno : 74
+EPROGMISMATCH: Errno : 75
+EPROCUNAVAIL: Errno : 76
+ENOLCK: Errno : 77
+ENOSYS: Errno : 78
+EFTYPE: Errno : 79
+EAUTH: Errno : 80
+ENEEDAUTH: Errno : 81
+EIDRM: Errno : 82
+ENOMSG: Errno : 83
+EOVERFLOW: Errno : 84
+ECANCELED: Errno : 85
+EILSEQ: Errno : 86
+ENOATTR: Errno : 87
+EDOOFUS: Errno : 88
+EBADMSG: Errno : 89
+EMULTIHOP: Errno : 90
+ENOLINK: Errno : 91
+EPROTO: Errno : 92
+ENOTCAPABLE: Errno : 93
+ECAPMODE: Errno : 94
+ENOTRECOVERABLE: Errno : 95
+EOWNERDEAD: Errno : 96
-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;
+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
-SEEK_SET :: 0;
-SEEK_CUR :: 1;
-SEEK_END :: 2;
-SEEK_DATA :: 3;
-SEEK_HOLE :: 4;
-SEEK_MAX :: SEEK_HOLE;
+SEEK_SET :: 0
+SEEK_CUR :: 1
+SEEK_END :: 2
+SEEK_DATA :: 3
+SEEK_HOLE :: 4
+SEEK_MAX :: SEEK_HOLE
// NOTE: These are OS specific!
// Do not mix these up!
-RTLD_LAZY :: 0x001;
-RTLD_NOW :: 0x002;
-//RTLD_BINDING_MASK :: 0x3; // Called MODEMASK in dlfcn.h
-RTLD_GLOBAL :: 0x100;
-RTLD_LOCAL :: 0x000;
-RTLD_TRACE :: 0x200;
-RTLD_NODELETE :: 0x01000;
-RTLD_NOLOAD :: 0x02000;
+RTLD_LAZY :: 0x001
+RTLD_NOW :: 0x002
+//RTLD_BINDING_MASK :: 0x3 // Called MODEMASK in dlfcn.h
+RTLD_GLOBAL :: 0x100
+RTLD_LOCAL :: 0x000
+RTLD_TRACE :: 0x200
+RTLD_NODELETE :: 0x01000
+RTLD_NOLOAD :: 0x02000
-args := _alloc_command_line_arguments();
+MAX_PATH :: 1024
+
+args := _alloc_command_line_arguments()
Unix_File_Time :: struct {
- seconds: i64,
+ seconds: time_t,
nanoseconds: c.long,
}
-pid_t :: u32;
+dev_t :: u64
+ino_t :: u64
+nlink_t :: u64
+off_t :: i64
+mode_t :: u16
+pid_t :: u32
+uid_t :: u32
+gid_t :: u32
+blkcnt_t :: i64
+blksize_t :: i32
+fflags_t :: u32
+
+when ODIN_ARCH == .amd64 /* LP64 */ {
+ time_t :: i64
+} else {
+ time_t :: i32
+}
+
OS_Stat :: struct {
- device_id: u64,
- serial: u64,
- nlink: u64,
- mode: u32,
+ device_id: dev_t,
+ serial: ino_t,
+ nlink: nlink_t,
+ mode: mode_t,
_padding0: i16,
- uid: u32,
- gid: u32,
+ uid: uid_t,
+ gid: gid_t,
_padding1: i32,
- rdev: u64,
+ rdev: dev_t,
last_access: Unix_File_Time,
modified: Unix_File_Time,
status_change: Unix_File_Time,
birthtime: Unix_File_Time,
- size: i64,
- blocks: i64,
- block_size: i32,
+ size: off_t,
+ blocks: blkcnt_t,
+ block_size: blksize_t,
- flags: u32,
+ flags: fflags_t,
gen: u64,
- lspare: i64,
+ lspare: [10]u64,
}
+
+// since FreeBSD v12
+Dirent :: struct {
+ ino: ino_t,
+ off: off_t,
+ reclen: u16,
+ type: u8,
+ _pad0: u8,
+ namlen: u16,
+ _pad1: u16,
+ name: [256]byte,
+}
+
+Dir :: distinct rawptr // DIR*
+
// File type
-S_IFMT :: 0o170000; // Type of file mask
-S_IFIFO :: 0o010000; // Named pipe (fifo)
-S_IFCHR :: 0o020000; // Character special
-S_IFDIR :: 0o040000; // Directory
-S_IFBLK :: 0o060000; // Block special
-S_IFREG :: 0o100000; // Regular
-S_IFLNK :: 0o120000; // Symbolic link
-S_IFSOCK :: 0o140000; // Socket
-//S_ISVTX :: 0o001000; // Save swapped text even after use
+S_IFMT :: 0o170000 // Type of file mask
+S_IFIFO :: 0o010000 // Named pipe (fifo)
+S_IFCHR :: 0o020000 // Character special
+S_IFDIR :: 0o040000 // Directory
+S_IFBLK :: 0o060000 // Block special
+S_IFREG :: 0o100000 // Regular
+S_IFLNK :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+//S_ISVTX :: 0o001000 // Save swapped text even after use
// File mode
// Read, write, execute/search by owner
-S_IRWXU :: 0o0700; // RWX mask for owner
-S_IRUSR :: 0o0400; // R for owner
-S_IWUSR :: 0o0200; // W for owner
-S_IXUSR :: 0o0100; // X for owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
// 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
+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
-S_IRWXO :: 0o0007; // RWX mask for other
-S_IROTH :: 0o0004; // R for other
-S_IWOTH :: 0o0002; // W for other
-S_IXOTH :: 0o0001; // X for other
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
-S_ISUID :: 0o4000; // Set user id on execution
-S_ISGID :: 0o2000; // Set group id on execution
-S_ISVTX :: 0o1000; // Directory restrcted delete
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISVTX :: 0o1000 // Directory restrcted delete
-S_ISLNK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFLNK;
-S_ISREG :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFREG;
-S_ISDIR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFDIR;
-S_ISCHR :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFCHR;
-S_ISBLK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFBLK;
-S_ISFIFO :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFIFO;
-S_ISSOCK :: #force_inline proc(m: u32) -> bool do return (m & S_IFMT) == S_IFSOCK;
+S_ISLNK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFLNK
+S_ISREG :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFREG
+S_ISDIR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFDIR
+S_ISCHR :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFCHR
+S_ISBLK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFBLK
+S_ISFIFO :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFIFO
+S_ISSOCK :: #force_inline proc(m: mode_t) -> bool do return (m & S_IFMT) == S_IFSOCK
-F_OK :: 0; // Test for file existance
-X_OK :: 1; // Test for execute permission
-W_OK :: 2; // Test for write permission
-R_OK :: 4; // Test for read permission
+F_OK :: 0 // Test for file existance
+X_OK :: 1 // Test for execute permission
+W_OK :: 2 // Test for write permission
+R_OK :: 4 // Test for read permission
foreign libc {
- @(link_name="__error") __errno_location :: proc() -> ^int ---;
- @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
+ @(link_name="__error") __errno_location :: proc() -> ^int ---
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
- @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
- @(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
- @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---;
- @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---;
- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---;
- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---;
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
+ @(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> 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) ---
+ @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
- @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---;
- @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---;
- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
+ @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
- @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
foreign dl {
- @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
- @(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="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(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="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---;
+ @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---
}
is_path_separator :: proc(r: rune) -> bool {
- return r == '/';
+ return r == '/'
}
get_last_error :: proc() -> int {
- return __errno_location()^;
+ return __errno_location()^
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path);
- handle := _unix_open(cstr, c.int(flags), c.int(mode));
- delete(cstr);
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 {
- return INVALID_HANDLE, Errno(get_last_error());
+ return INVALID_HANDLE, Errno(get_last_error())
}
- return handle, ERROR_NONE;
+ return handle, ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
- result := _unix_close(fd);
+ result := _unix_close(fd)
if result == -1 {
- return Errno(get_last_error());
+ return Errno(get_last_error())
}
- return ERROR_NONE;
+ return ERROR_NONE
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
- bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
+ bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
if bytes_read == -1 {
- return -1, Errno(get_last_error());
+ return -1, Errno(get_last_error())
}
- return int(bytes_read), ERROR_NONE;
+ return int(bytes_read), ERROR_NONE
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
- return 0, ERROR_NONE;
+ return 0, ERROR_NONE
}
- bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
+ bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
if bytes_written == -1 {
- return -1, Errno(get_last_error());
+ return -1, Errno(get_last_error())
}
- return int(bytes_written), ERROR_NONE;
+ return int(bytes_written), ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, c.int(whence));
+ res := _unix_seek(fd, offset, c.int(whence))
if res == -1 {
- return -1, Errno(get_last_error());
+ return -1, Errno(get_last_error())
}
- return res, ERROR_NONE;
+ return res, ERROR_NONE
}
file_size :: proc(fd: Handle) -> (i64, Errno) {
- s, err := fstat(fd);
+ s, err := fstat(fd)
if err != ERROR_NONE {
- return -1, err;
+ return -1, err
}
- return s.size, ERROR_NONE;
+ return s.size, ERROR_NONE
}
-stdin: Handle = 0;
-stdout: Handle = 1;
-stderr: Handle = 2;
-
-last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
- s, err := fstat(fd);
- if err != ERROR_NONE {
- return 0, err;
+rename :: proc(old_path, new_path: string) -> Errno {
+ 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)
+ if res == -1 {
+ return Errno(get_last_error())
}
- modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
- return File_Time(modified), ERROR_NONE;
+ return ERROR_NONE
+}
+
+remove :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_unlink(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_mkdir(path_cstr, mode)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+remove_directory :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_rmdir(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+// NOTE(bill): Uses startup to initialize it
+
+stdin: Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+/* TODO(zangent): Implement these!
+last_write_time :: proc(fd: Handle) -> File_Time {}
+last_write_time_by_name :: proc(name: string) -> File_Time {}
+*/
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return 0, err
+ }
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
}
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
- s, err := stat(name);
+ s, err := _stat(name)
if err != ERROR_NONE {
- return 0, err;
+ return 0, err
}
- modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds;
- return File_Time(modified), ERROR_NONE;
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
}
-stat :: proc(path: string) -> (OS_Stat, Errno) {
- cstr := strings.clone_to_cstring(path);
- defer delete(cstr);
-
- s: OS_Stat;
- result := _unix_stat(cstr, &s);
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ s: OS_Stat = ---
+ result := _unix_lstat(cstr, &s)
if result == -1 {
- return s, Errno(get_last_error());
+ return s, Errno(get_last_error())
}
- return s, ERROR_NONE;
+ return s, ERROR_NONE
}
-fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
- s: OS_Stat;
- result := _unix_fstat(fd, &s);
- if result == -1 {
- return s, Errno(get_last_error());
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_lstat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
}
- return s, ERROR_NONE;
+ return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+ s: OS_Stat = ---
+ result := _unix_fstat(fd, &s)
+ if result == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+ dirp := _unix_fdopendir(fd)
+ if dirp == cast(Dir)nil {
+ return nil, Errno(get_last_error())
+ }
+ return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+ rc := _unix_closedir(dirp)
+ if rc != 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+ _unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+ result: ^Dirent
+ rc := _unix_readdir_r(dirp, &entry, &result)
+
+ if rc != 0 {
+ err = Errno(get_last_error())
+ return
+ }
+ err = ERROR_NONE
+
+ if result == nil {
+ end_of_stream = true
+ return
+ }
+
+ return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ bufsz : uint = MAX_PATH
+ buf := make([]byte, MAX_PATH)
+ for {
+ rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ if rc == -1 {
+ delete(buf)
+ return "", Errno(get_last_error())
+ } else if rc == int(bufsz) {
+ bufsz += MAX_PATH
+ delete(buf)
+ buf = make([]byte, bufsz)
+ } else {
+ return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+ }
+ }
+ unreachable()
+}
+
+// XXX FreeBSD
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+ return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+ rel := rel
+ if rel == "" {
+ rel = "."
+ }
+
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+ path_ptr := _unix_realpath(rel_cstr, nil)
+ if path_ptr == nil {
+ return "", Errno(get_last_error())
+ }
+ defer _unix_free(path_ptr)
+
+ path_cstr := transmute(cstring)path_ptr
+ path = strings.clone( string(path_cstr) )
+
+ return path, ERROR_NONE
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
- cstr := strings.clone_to_cstring(path);
- defer delete(cstr);
- result := _unix_access(cstr, c.int(mask));
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ result := _unix_access(cstr, c.int(mask))
if result == -1 {
- return false, Errno(get_last_error());
+ return false, Errno(get_last_error())
}
- return true, ERROR_NONE;
+ return true, ERROR_NONE
}
heap_alloc :: proc(size: int) -> rawptr {
- assert(size >= 0);
- return _unix_calloc(1, c.size_t(size));
+ assert(size >= 0)
+ return _unix_calloc(1, c.size_t(size))
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
- return _unix_realloc(ptr, c.size_t(new_size));
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
+ return _unix_realloc(ptr, c.size_t(new_size))
}
heap_free :: proc(ptr: rawptr) {
- _unix_free(ptr);
+ _unix_free(ptr)
}
getenv :: proc(name: string) -> (string, bool) {
- path_str := strings.clone_to_cstring(name);
- defer delete(path_str);
- cstr := _unix_getenv(path_str);
+ path_str := strings.clone_to_cstring(name, context.temp_allocator)
+ cstr := _unix_getenv(path_str)
if cstr == nil {
- return "", false;
+ return "", false
}
- return string(cstr), true;
+ return string(cstr), true
}
get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems.
// The largest value I could find was 4096, so might as well use the page size.
- page_size := get_page_size();
- buf := make([dynamic]u8, page_size);
+ page_size := get_page_size()
+ buf := make([dynamic]u8, page_size)
#no_bounds_check for {
- cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)));
+ cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)))
if cwd != nil {
- return string(cwd);
+ return string(cwd)
}
if Errno(get_last_error()) != ERANGE {
- return "";
+ return ""
}
- resize(&buf, len(buf)+page_size);
+ resize(&buf, len(buf)+page_size)
}
- unreachable();
+ unreachable()
}
set_current_directory :: proc(path: string) -> (err: Errno) {
- cstr := strings.clone_to_cstring(path, context.temp_allocator);
- res := _unix_chdir(cstr);
- if res == -1 do return Errno(get_last_error());
- return ERROR_NONE;
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_chdir(cstr)
+ if res == -1 do return Errno(get_last_error())
+ return ERROR_NONE
}
exit :: proc "contextless" (code: int) -> ! {
- _unix_exit(c.int(code));
+ runtime._cleanup_runtime_contextless()
+ _unix_exit(c.int(code))
}
current_thread_id :: proc "contextless" () -> int {
- return cast(int) pthread_getthreadid_np();
+ return cast(int) pthread_getthreadid_np()
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
- cstr := strings.clone_to_cstring(filename);
- defer delete(cstr);
- handle := _unix_dlopen(cstr, c.int(flags));
- return handle;
+ 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);
- cstr := strings.clone_to_cstring(symbol);
- defer delete(cstr);
- proc_handle := _unix_dlsym(handle, cstr);
- return proc_handle;
+ assert(handle != nil)
+ cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+ proc_handle := _unix_dlsym(handle, cstr)
+ return proc_handle
}
dlclose :: proc(handle: rawptr) -> bool {
- assert(handle != nil);
- return _unix_dlclose(handle) == 0;
+ assert(handle != nil)
+ return _unix_dlclose(handle) == 0
}
dlerror :: proc() -> string {
- return string(_unix_dlerror());
+ return string(_unix_dlerror())
}
get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to.
- @static page_size := -1;
- if page_size != -1 do return page_size;
+ @static page_size := -1
+ if page_size != -1 do return page_size
- page_size = int(_unix_getpagesize());
- return page_size;
+ page_size = int(_unix_getpagesize())
+ return page_size
}
_alloc_command_line_arguments :: proc() -> []string {
- res := make([]string, len(runtime.args__));
+ res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ {
- res[i] = string(arg);
+ res[i] = string(arg)
}
- return res;
+ return res
}
-
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index 260a051ce..ed73341c0 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -11,6 +11,7 @@ import "core:intrinsics"
import "core:sys/unix"
Handle :: distinct i32
+Pid :: distinct i32
File_Time :: distinct u64
Errno :: distinct i32
@@ -150,6 +151,8 @@ ERFKILL: Errno : 132 /* Operation not possible due to RF-kill */
EHWPOISON: Errno : 133 /* Memory page has hardware error */
+ADDR_NO_RANDOMIZE :: 0x40000
+
O_RDONLY :: 0x00000
O_WRONLY :: 0x00001
O_RDWR :: 0x00002
@@ -266,33 +269,155 @@ X_OK :: 1 // Test for execute permission
W_OK :: 2 // Test for write permission
R_OK :: 4 // Test for read permission
+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)))
+}
+
+_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, &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)))
+ }
+}
+
foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---
- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
- @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
- @(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---
- @(link_name="gettid") _unix_gettid :: proc() -> u64 ---
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
- @(link_name="stat64") _unix_stat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
- @(link_name="lstat") _unix_lstat :: proc(path: cstring, stat: ^OS_Stat) -> c.int ---
- @(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^OS_Stat) -> 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) ---
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
- @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
- @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
- @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
- @(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -308,63 +433,155 @@ is_path_separator :: proc(r: rune) -> bool {
return r == '/'
}
+// determine errno from syscall return value
+@private
+_get_errno :: proc(res: int) -> Errno {
+ if res < 0 && res > -4096 {
+ return Errno(-res)
+ }
+ return 0
+}
+
+// get errno from libc
get_last_error :: proc() -> int {
return __errno_location()^
}
+personality :: proc(persona: u64) -> (Errno) {
+ res := _unix_personality(persona)
+ if res == -1 {
+ return _get_errno(res)
+ }
+ return ERROR_NONE
+}
+
+fork :: proc() -> (Pid, Errno) {
+ pid := _unix_fork()
+ if pid == -1 {
+ return -1, _get_errno(int(pid))
+ }
+ return pid, ERROR_NONE
+}
+
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
- cstr := strings.clone_to_cstring(path)
- handle := _unix_open(cstr, c.int(flags), c.int(mode))
- delete(cstr)
- if handle == -1 {
- return INVALID_HANDLE, Errno(get_last_error())
+ 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))
}
return handle, ERROR_NONE
}
close :: proc(fd: Handle) -> Errno {
- result := _unix_close(fd)
- if result == -1 {
- return Errno(get_last_error())
- }
- return ERROR_NONE
+ return _get_errno(_unix_close(fd))
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
- if bytes_read == -1 {
- return -1, Errno(get_last_error())
+ if bytes_read < 0 {
+ return -1, _get_errno(bytes_read)
}
- return int(bytes_read), ERROR_NONE
+ return bytes_read, ERROR_NONE
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE
}
- bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
- if bytes_written == -1 {
- return -1, Errno(get_last_error())
+ bytes_written := _unix_write(fd, &data[0], uint(len(data)))
+ if bytes_written < 0 {
+ return -1, _get_errno(bytes_written)
}
return int(bytes_written), ERROR_NONE
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
- res := _unix_seek(fd, offset, c.int(whence))
- if res == -1 {
- return -1, Errno(get_last_error())
+ res := _unix_seek(fd, offset, whence)
+ if res < 0 {
+ return -1, _get_errno(int(res))
}
return 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
+}
+
+rename :: proc(old_path, new_path: string) -> Errno {
+ 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))
+}
+
+remove :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _get_errno(_unix_unlink(path_cstr))
+}
+
+make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _get_errno(_unix_mkdir(path_cstr, mode))
+}
+
+remove_directory :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ return _get_errno(_unix_rmdir(path_cstr))
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd)
if err != ERROR_NONE {
- return 0, err
+ return false
}
- return max(s.size, 0), ERROR_NONE
+ return S_ISREG(s.mode)
}
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
// NOTE(bill): Uses startup to initialize it
@@ -396,36 +613,37 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
@private
_stat :: proc(path: string) -> (OS_Stat, Errno) {
- cstr := strings.clone_to_cstring(path)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
- s: OS_Stat
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
result := _unix_stat(cstr, &s)
- if result == -1 {
- return s, Errno(get_last_error())
+ if result < 0 {
+ return s, _get_errno(result)
}
return s, ERROR_NONE
}
@private
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
- cstr := strings.clone_to_cstring(path)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
- s: OS_Stat
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
result := _unix_lstat(cstr, &s)
- if result == -1 {
- return s, Errno(get_last_error())
+ if result < 0 {
+ return s, _get_errno(result)
}
return s, ERROR_NONE
}
@private
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
- s: OS_Stat
+ // deliberately uninitialized; the syscall fills this buffer for us
+ s: OS_Stat = ---
result := _unix_fstat(fd, &s)
- if result == -1 {
- return s, Errno(get_last_error())
+ if result < 0 {
+ return s, _get_errno(result)
}
return s, ERROR_NONE
}
@@ -475,16 +693,15 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
@private
_readlink :: proc(path: string) -> (string, Errno) {
- path_cstr := strings.clone_to_cstring(path)
- defer delete(path_cstr)
+ 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)
- if rc == -1 {
+ if rc < 0 {
delete(buf)
- return "", Errno(get_last_error())
+ return "", _get_errno(rc)
} else if rc == int(bufsz) {
// NOTE(laleksic, 2021-01-21): Any cleaner way to resize the slice?
bufsz *= 2
@@ -512,8 +729,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
rel = "."
}
- rel_cstr := strings.clone_to_cstring(rel)
- defer delete(rel_cstr)
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil {
@@ -528,11 +744,10 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
}
access :: proc(path: string, mask: int) -> (bool, Errno) {
- cstr := strings.clone_to_cstring(path)
- defer delete(cstr)
- result := _unix_access(cstr, c.int(mask))
- if result == -1 {
- return false, Errno(get_last_error())
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ result := _unix_access(cstr, mask)
+ if result < 0 {
+ return false, _get_errno(result)
}
return true, ERROR_NONE
}
@@ -543,6 +758,8 @@ heap_alloc :: proc(size: int) -> rawptr {
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
return _unix_realloc(ptr, c.size_t(new_size))
}
@@ -551,8 +768,7 @@ heap_free :: proc(ptr: rawptr) {
}
getenv :: proc(name: string) -> (string, bool) {
- path_str := strings.clone_to_cstring(name)
- defer delete(path_str)
+ path_str := strings.clone_to_cstring(name, context.temp_allocator)
cstr := _unix_getenv(path_str)
if cstr == nil {
return "", false
@@ -567,11 +783,12 @@ get_current_directory :: proc() -> string {
page_size := get_page_size()
buf := make([dynamic]u8, page_size)
for {
- #no_bounds_check cwd := _unix_getcwd(cstring(&buf[0]), c.size_t(len(buf)))
- if cwd != nil {
- return string(cwd)
+ #no_bounds_check res := _unix_getcwd(&buf[0], uint(len(buf)))
+
+ if res >= 0 {
+ return strings.string_from_nul_terminated_ptr(&buf[0], len(buf))
}
- if Errno(get_last_error()) != ERANGE {
+ if _get_errno(res) != ERANGE {
return ""
}
resize(&buf, len(buf)+page_size)
@@ -582,13 +799,14 @@ get_current_directory :: proc() -> string {
set_current_directory :: proc(path: string) -> (err: Errno) {
cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr)
- if res == -1 {
- return Errno(get_last_error())
+ if res < 0 {
+ return _get_errno(res)
}
return ERROR_NONE
}
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
_unix_exit(c.int(code))
}
@@ -597,15 +815,13 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
- cstr := strings.clone_to_cstring(filename)
- defer delete(cstr)
+ 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)
- cstr := strings.clone_to_cstring(symbol)
- defer delete(cstr)
+ cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
proc_handle := _unix_dlsym(handle, cstr)
return proc_handle
}
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
new file mode 100644
index 000000000..a99c8fef0
--- /dev/null
+++ b/core/os/os_openbsd.odin
@@ -0,0 +1,707 @@
+package os
+
+foreign import libc "system:c"
+
+import "core:strings"
+import "core:c"
+import "core:runtime"
+
+Handle :: distinct i32
+Pid :: distinct i32
+File_Time :: distinct u64
+Errno :: distinct i32
+
+INVALID_HANDLE :: ~Handle(0)
+
+ERROR_NONE: Errno: 0
+
+EPERM: Errno: 1
+ENOENT: Errno: 2
+ESRCH: Errno: 3
+EINTR: Errno: 4
+EIO: Errno: 5
+ENXIO: Errno: 6
+E2BIG: Errno: 7
+ENOEXEC: Errno: 8
+EBADF: Errno: 9
+ECHILD: Errno: 10
+EDEADLK: Errno: 11
+ENOMEM: Errno: 12
+EACCES: Errno: 13
+EFAULT: Errno: 14
+ENOTBLK: Errno: 15
+EBUSY: Errno: 16
+EEXIST: Errno: 17
+EXDEV: Errno: 18
+ENODEV: Errno: 19
+ENOTDIR: Errno: 20
+EISDIR: Errno: 21
+EINVAL: Errno: 22
+ENFILE: Errno: 23
+EMFILE: Errno: 24
+ENOTTY: Errno: 25
+ETXTBSY: Errno: 26
+EFBIG: Errno: 27
+ENOSPC: Errno: 28
+ESPIPE: Errno: 29
+EROFS: Errno: 30
+EMLINK: Errno: 31
+EPIPE: Errno: 32
+EDOM: Errno: 33
+ERANGE: Errno: 34
+EAGAIN: Errno: 35
+EWOULDBLOCK: Errno: EAGAIN
+EINPROGRESS: Errno: 36
+EALREADY: Errno: 37
+ENOTSOCK: Errno: 38
+EDESTADDRREQ: Errno: 39
+EMSGSIZE: Errno: 40
+EPROTOTYPE: Errno: 41
+ENOPROTOOPT: Errno: 42
+EPROTONOSUPPORT: Errno: 43
+ESOCKTNOSUPPORT: Errno: 44
+EOPNOTSUPP: Errno: 45
+EPFNOSUPPORT: Errno: 46
+EAFNOSUPPORT: Errno: 47
+EADDRINUSE: Errno: 48
+EADDRNOTAVAIL: Errno: 49
+ENETDOWN: Errno: 50
+ENETUNREACH: Errno: 51
+ENETRESET: Errno: 52
+ECONNABORTED: Errno: 53
+ECONNRESET: Errno: 54
+ENOBUFS: Errno: 55
+EISCONN: Errno: 56
+ENOTCONN: Errno: 57
+ESHUTDOWN: Errno: 58
+ETOOMANYREFS: Errno: 59
+ETIMEDOUT: Errno: 60
+ECONNREFUSED: Errno: 61
+ELOOP: Errno: 62
+ENAMETOOLONG: Errno: 63
+EHOSTDOWN: Errno: 64
+EHOSTUNREACH: Errno: 65
+ENOTEMPTY: Errno: 66
+EPROCLIM: Errno: 67
+EUSERS: Errno: 68
+EDQUOT: Errno: 69
+ESTALE: Errno: 70
+EREMOTE: Errno: 71
+EBADRPC: Errno: 72
+ERPCMISMATCH: Errno: 73
+EPROGUNAVAIL: Errno: 74
+EPROGMISMATCH: Errno: 75
+EPROCUNAVAIL: Errno: 76
+ENOLCK: Errno: 77
+ENOSYS: Errno: 78
+EFTYPE: Errno: 79
+EAUTH: Errno: 80
+ENEEDAUTH: Errno: 81
+EIPSEC: Errno: 82
+ENOATTR: Errno: 83
+EILSEQ: Errno: 84
+ENOMEDIUM: Errno: 85
+EMEDIUMTYPE: Errno: 86
+EOVERFLOW: Errno: 87
+ECANCELED: Errno: 88
+EIDRM: Errno: 89
+ENOMSG: Errno: 90
+ENOTSUP: Errno: 91
+EBADMSG: Errno: 92
+ENOTRECOVERABLE: Errno: 93
+EOWNERDEAD: Errno: 94
+EPROTO: Errno: 95
+
+O_RDONLY :: 0x00000
+O_WRONLY :: 0x00001
+O_RDWR :: 0x00002
+O_NONBLOCK :: 0x00004
+O_APPEND :: 0x00008
+O_ASYNC :: 0x00040
+O_SYNC :: 0x00080
+O_CREATE :: 0x00200
+O_TRUNC :: 0x00400
+O_EXCL :: 0x00800
+O_NOCTTY :: 0x08000
+O_CLOEXEC :: 0x10000
+
+SEEK_SET :: 0
+SEEK_CUR :: 1
+SEEK_END :: 2
+
+RTLD_LAZY :: 0x001
+RTLD_NOW :: 0x002
+RTLD_LOCAL :: 0x000
+RTLD_GLOBAL :: 0x100
+RTLD_TRACE :: 0x200
+RTLD_NODELETE :: 0x400
+
+MAX_PATH :: 1024
+
+// "Argv" arguments converted to Odin strings
+args := _alloc_command_line_arguments()
+
+pid_t :: i32
+time_t :: i64
+mode_t :: u32
+dev_t :: i32
+ino_t :: u64
+nlink_t :: u32
+uid_t :: u32
+gid_t :: u32
+off_t :: i64
+blkcnt_t :: u64
+blksize_t :: i32
+
+Unix_File_Time :: struct {
+ seconds: time_t,
+ nanoseconds: c.long,
+}
+
+OS_Stat :: struct {
+ mode: mode_t, // inode protection mode
+ device_id: dev_t, // inode's device
+ serial: ino_t, // inode's number
+ nlink: nlink_t, // number of hard links
+ uid: uid_t, // user ID of the file's owner
+ gid: gid_t, // group ID of the file's group
+ rdev: dev_t, // device type
+
+ last_access: Unix_File_Time, // time of last access
+ modified: Unix_File_Time, // time of last data modification
+ status_change: Unix_File_Time, // time of last file status change
+
+ size: off_t, // file size, in bytes
+ blocks: blkcnt_t, // blocks allocated for file
+ block_size: blksize_t, // optimal blocksize for I/O
+
+ flags: u32, // user defined flags for file
+ gen: u32, // file generation number
+ birthtime: Unix_File_Time, // time of file creation
+}
+
+MAXNAMLEN :: 255
+
+// NOTE(laleksic, 2021-01-21): Comment and rename these to match OS_Stat above
+Dirent :: struct {
+ ino: ino_t, // file number of entry
+ off: off_t, // offset after this entry
+ reclen: u16, // length of this record
+ type: u8, // file type
+ namlen: u8, // length of string in name
+ _padding: [4]u8,
+ name: [MAXNAMLEN + 1]byte, // name
+}
+
+Dir :: distinct rawptr // DIR*
+
+// File type
+S_IFMT :: 0o170000 // Type of file mask
+S_IFIFO :: 0o010000 // Named pipe (fifo)
+S_IFCHR :: 0o020000 // Character special
+S_IFDIR :: 0o040000 // Directory
+S_IFBLK :: 0o060000 // Block special
+S_IFREG :: 0o100000 // Regular
+S_IFLNK :: 0o120000 // Symbolic link
+S_IFSOCK :: 0o140000 // Socket
+S_ISVTX :: 0o001000 // Save swapped text even after use
+
+// File mode
+ // Read, write, execute/search by owner
+S_IRWXU :: 0o0700 // RWX mask for owner
+S_IRUSR :: 0o0400 // R for owner
+S_IWUSR :: 0o0200 // W for owner
+S_IXUSR :: 0o0100 // X for owner
+
+ // 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
+S_IRWXO :: 0o0007 // RWX mask for other
+S_IROTH :: 0o0004 // R for other
+S_IWOTH :: 0o0002 // W for other
+S_IXOTH :: 0o0001 // X for other
+
+S_ISUID :: 0o4000 // Set user id on execution
+S_ISGID :: 0o2000 // Set group id on execution
+S_ISTXT :: 0o1000 // Sticky bit
+
+S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
+S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
+S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
+S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
+S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
+S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
+S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
+
+F_OK :: 0x00 // Test for file existance
+X_OK :: 0x01 // Test for execute permission
+W_OK :: 0x02 // Test for write permission
+R_OK :: 0x04 // Test for read permission
+
+AT_FDCWD :: -100
+AT_EACCESS :: 0x01
+AT_SYMLINK_NOFOLLOW :: 0x02
+AT_SYMLINK_FOLLOW :: 0x04
+AT_REMOVEDIR :: 0x08
+
+@(default_calling_convention="c")
+foreign libc {
+ @(link_name="__errno") __errno :: proc() -> ^int ---
+
+ @(link_name="fork") _unix_fork :: proc() -> pid_t ---
+ @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
+
+ @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
+ @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
+ @(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
+ @(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
+ @(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
+ @(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
+ @(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
+ @(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
+ @(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
+ @(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
+ @(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
+ @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
+ @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
+ @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
+
+ @(link_name="getpagesize") _unix_getpagesize :: 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) ---
+ @(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
+
+ @(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
+ @(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
+ @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
+ @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
+
+ @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
+ @(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+
+ @(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
+
+ @(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
+ @(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 ---
+}
+
+is_path_separator :: proc(r: rune) -> bool {
+ return r == '/'
+}
+
+get_last_error :: proc() -> int {
+ return __errno()^
+}
+
+fork :: proc() -> (Pid, Errno) {
+ pid := _unix_fork()
+ if pid == -1 {
+ return Pid(-1), Errno(get_last_error())
+ }
+ 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, c.int(flags), c.int(mode))
+ if handle == -1 {
+ return INVALID_HANDLE, Errno(get_last_error())
+ }
+ return handle, ERROR_NONE
+}
+
+close :: proc(fd: Handle) -> Errno {
+ result := _unix_close(fd)
+ if result == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)))
+ if bytes_read == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return int(bytes_read), ERROR_NONE
+}
+
+write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
+ if len(data) == 0 {
+ return 0, ERROR_NONE
+ }
+ bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)))
+ if bytes_written == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return int(bytes_written), ERROR_NONE
+}
+
+seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
+ res := _unix_seek(fd, offset, c.int(whence))
+ if res == -1 {
+ return -1, Errno(get_last_error())
+ }
+ return res, ERROR_NONE
+}
+
+file_size :: proc(fd: Handle) -> (i64, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return -1, err
+ }
+ return s.size, ERROR_NONE
+}
+
+rename :: proc(old_path, new_path: string) -> Errno {
+ 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)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+remove :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_unlink(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_mkdir(path_cstr, mode)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+remove_directory :: proc(path: string) -> Errno {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_rmdir(path_cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+is_file_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISREG(s.mode)
+}
+
+is_dir_handle :: proc(fd: Handle) -> bool {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
+ s: OS_Stat
+ err: Errno
+ if follow_links {
+ s, err = _stat(path)
+ } else {
+ s, err = _lstat(path)
+ }
+ if err != ERROR_NONE {
+ return false
+ }
+ return S_ISDIR(s.mode)
+}
+
+is_file :: proc {is_file_path, is_file_handle}
+is_dir :: proc {is_dir_path, is_dir_handle}
+
+// NOTE(bill): Uses startup to initialize it
+
+stdin: Handle = 0
+stdout: Handle = 1
+stderr: Handle = 2
+
+/* TODO(zangent): Implement these!
+last_write_time :: proc(fd: Handle) -> File_Time {}
+last_write_time_by_name :: proc(name: string) -> File_Time {}
+*/
+last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
+ s, err := _fstat(fd)
+ if err != ERROR_NONE {
+ return 0, err
+ }
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
+}
+
+last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
+ s, err := _stat(name)
+ if err != ERROR_NONE {
+ return 0, err
+ }
+ modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
+ return File_Time(modified), ERROR_NONE
+}
+
+@private
+_stat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_stat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_lstat :: proc(path: string) -> (OS_Stat, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_lstat(cstr, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
+ // deliberately uninitialized
+ s: OS_Stat = ---
+ res := _unix_fstat(fd, &s)
+ if res == -1 {
+ return s, Errno(get_last_error())
+ }
+ return s, ERROR_NONE
+}
+
+@private
+_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
+ dirp := _unix_fdopendir(fd)
+ if dirp == cast(Dir)nil {
+ return nil, Errno(get_last_error())
+ }
+ return dirp, ERROR_NONE
+}
+
+@private
+_closedir :: proc(dirp: Dir) -> Errno {
+ rc := _unix_closedir(dirp)
+ if rc != 0 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+@private
+_rewinddir :: proc(dirp: Dir) {
+ _unix_rewinddir(dirp)
+}
+
+@private
+_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
+ result: ^Dirent
+ rc := _unix_readdir_r(dirp, &entry, &result)
+
+ if rc != 0 {
+ err = Errno(get_last_error())
+ return
+ }
+ err = ERROR_NONE
+
+ if result == nil {
+ end_of_stream = true
+ return
+ }
+
+ return
+}
+
+@private
+_readlink :: proc(path: string) -> (string, Errno) {
+ path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
+
+ bufsz : uint = MAX_PATH
+ buf := make([]byte, MAX_PATH)
+ for {
+ rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
+ if rc == -1 {
+ delete(buf)
+ return "", Errno(get_last_error())
+ } else if rc == int(bufsz) {
+ bufsz += MAX_PATH
+ delete(buf)
+ buf = make([]byte, bufsz)
+ } else {
+ return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
+ }
+ }
+ unreachable()
+}
+
+// XXX OpenBSD
+absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
+ return "", Errno(ENOSYS)
+}
+
+absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+ rel := rel
+ if rel == "" {
+ rel = "."
+ }
+
+ rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
+
+ path_ptr := _unix_realpath(rel_cstr, nil)
+ if path_ptr == nil {
+ return "", Errno(get_last_error())
+ }
+ defer _unix_free(path_ptr)
+
+ path_cstr := transmute(cstring)path_ptr
+ path = strings.clone( string(path_cstr) )
+
+ return path, ERROR_NONE
+}
+
+access :: proc(path: string, mask: int) -> (bool, Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_access(cstr, c.int(mask))
+ if res == -1 {
+ return false, Errno(get_last_error())
+ }
+ return true, ERROR_NONE
+}
+
+heap_alloc :: proc(size: int) -> rawptr {
+ assert(size >= 0)
+ return _unix_calloc(1, c.size_t(size))
+}
+
+heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
+ // NOTE: _unix_realloc doesn't guarantee new memory will be zeroed on
+ // POSIX platforms. Ensure your caller takes this into account.
+ return _unix_realloc(ptr, c.size_t(new_size))
+}
+
+heap_free :: proc(ptr: rawptr) {
+ _unix_free(ptr)
+}
+
+getenv :: proc(name: string) -> (string, bool) {
+ path_str := strings.clone_to_cstring(name, context.temp_allocator)
+ cstr := _unix_getenv(path_str)
+ if cstr == nil {
+ return "", false
+ }
+ return string(cstr), true
+}
+
+get_current_directory :: proc() -> string {
+ buf := make([dynamic]u8, MAX_PATH)
+ for {
+ cwd := _unix_getcwd(cstring(raw_data(buf)), c.size_t(len(buf)))
+ if cwd != nil {
+ return string(cwd)
+ }
+ if Errno(get_last_error()) != ERANGE {
+ return ""
+ }
+ resize(&buf, len(buf) + MAX_PATH)
+ }
+ unreachable()
+}
+
+set_current_directory :: proc(path: string) -> (err: Errno) {
+ cstr := strings.clone_to_cstring(path, context.temp_allocator)
+ res := _unix_chdir(cstr)
+ if res == -1 {
+ return Errno(get_last_error())
+ }
+ return ERROR_NONE
+}
+
+exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
+ _unix_exit(c.int(code))
+}
+
+current_thread_id :: proc "contextless" () -> int {
+ return _unix_getthrid()
+}
+
+dlopen :: proc(filename: string, flags: int) -> rawptr {
+ 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)
+ cstr := strings.clone_to_cstring(symbol, context.temp_allocator)
+ proc_handle := _unix_dlsym(handle, cstr)
+ return proc_handle
+}
+dlclose :: proc(handle: rawptr) -> bool {
+ assert(handle != nil)
+ return _unix_dlclose(handle) == 0
+}
+dlerror :: proc() -> string {
+ return string(_unix_dlerror())
+}
+
+get_page_size :: proc() -> int {
+ // NOTE(tetra): The page size never changes, so why do anything complicated
+ // if we don't have to.
+ @static page_size := -1
+ if page_size != -1 {
+ return page_size
+ }
+
+ page_size = int(_unix_getpagesize())
+ return page_size
+}
+
+
+_alloc_command_line_arguments :: proc() -> []string {
+ res := make([]string, len(runtime.args__))
+ for arg, i in runtime.args__ {
+ res[i] = string(arg)
+ }
+ return res
+}
diff --git a/core/os/os_wasi.odin b/core/os/os_wasi.odin
index d2ba166bd..7bab1b949 100644
--- a/core/os/os_wasi.odin
+++ b/core/os/os_wasi.odin
@@ -1,6 +1,7 @@
package os
import "core:sys/wasm/wasi"
+import "core:runtime"
Handle :: distinct i32
Errno :: distinct i32
@@ -93,5 +94,6 @@ heap_free :: proc(ptr: rawptr) {
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
wasi.proc_exit(wasi.exitcode_t(code))
}
\ No newline at end of file
diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin
index e6efb89df..fe9496e4c 100644
--- a/core/os/os_windows.odin
+++ b/core/os/os_windows.odin
@@ -2,6 +2,7 @@
package os
import win32 "core:sys/windows"
+import "core:runtime"
Handle :: distinct uintptr
File_Time :: distinct u64
@@ -128,6 +129,7 @@ get_page_size :: proc() -> int {
exit :: proc "contextless" (code: int) -> ! {
+ runtime._cleanup_runtime_contextless()
win32.ExitProcess(win32.DWORD(code))
}
diff --git a/core/os/stat.odin b/core/os/stat.odin
index 6f4c8b0ef..1b64ad33b 100644
--- a/core/os/stat.odin
+++ b/core/os/stat.odin
@@ -2,7 +2,6 @@ package os
import "core:time"
-
File_Info :: struct {
fullpath: string,
name: string,
diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin
index 08c6f53c4..395d2e73e 100644
--- a/core/os/stat_unix.odin
+++ b/core/os/stat_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package os
import "core:time"
@@ -61,7 +61,7 @@ _make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
fi.size = s.size
fi.mode = cast(File_Mode)s.mode
- fi.is_dir = S_ISDIR(u32(s.mode))
+ fi.is_dir = S_ISDIR(s.mode)
// NOTE(laleksic, 2021-01-21): Not really creation time, but closest we can get (maybe better to leave it 0?)
fi.creation_time = _make_time_from_unix_file_time(s.status_change)
diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin
index 2d9f98fd4..5da925560 100644
--- a/core/os/stat_windows.odin
+++ b/core/os/stat_windows.odin
@@ -80,7 +80,7 @@ stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno)
return _stat(name, attrs, allocator)
}
-fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno) {
+fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) {
if fd == 0 {
return {}, ERROR_INVALID_HANDLE
}
@@ -94,14 +94,14 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Errno)
h := win32.HANDLE(fd)
switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
- fi: File_Info
- fi.fullpath = path
fi.name = basename(path)
fi.mode |= file_type_mode(h)
- return fi, ERROR_NONE
+ errno = ERROR_NONE
+ case:
+ fi, errno = file_info_from_get_file_information_by_handle(path, h)
}
-
- return file_info_from_get_file_information_by_handle(path, h)
+ fi.fullpath = path
+ return
}
@@ -132,26 +132,11 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
@(private)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
- if fd == 0 {
- return "", ERROR_INVALID_HANDLE
+ buf, err := cleanpath_from_handle_u16(fd)
+ if err != 0 {
+ return "", err
}
- h := win32.HANDLE(fd)
-
- MAX_PATH := win32.DWORD(260) + 1
- buf: []u16
- for {
- buf = make([]u16, MAX_PATH, context.temp_allocator)
- err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
- switch Errno(err) {
- case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
- return "", Errno(err)
- case ERROR_NOT_ENOUGH_MEMORY:
- MAX_PATH = MAX_PATH*2 + 1
- continue
- }
- break
- }
- return cleanpath_from_buf(buf), ERROR_NONE
+ return win32.utf16_to_utf8(buf, context.allocator), err
}
@(private)
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
@@ -160,21 +145,13 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
}
h := win32.HANDLE(fd)
- MAX_PATH := win32.DWORD(260) + 1
- buf: []u16
- for {
- buf = make([]u16, MAX_PATH, context.temp_allocator)
- err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0)
- switch Errno(err) {
- case ERROR_PATH_NOT_FOUND, ERROR_INVALID_PARAMETER:
- return nil, Errno(err)
- case ERROR_NOT_ENOUGH_MEMORY:
- MAX_PATH = MAX_PATH*2 + 1
- continue
- }
- break
+ n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
+ if n == 0 {
+ return nil, Errno(win32.GetLastError())
}
- return cleanpath_strip_prefix(buf), ERROR_NONE
+ buf := make([]u16, max(n, win32.DWORD(260))+1, context.temp_allocator)
+ buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
+ return buf[:buf_len], ERROR_NONE
}
@(private)
cleanpath_from_buf :: proc(buf: []u16) -> string {
diff --git a/core/os/stream.odin b/core/os/stream.odin
index 5cf5c8405..2c6e1d47f 100644
--- a/core/os/stream.odin
+++ b/core/os/stream.odin
@@ -19,7 +19,7 @@ _file_stream_vtable := &io.Stream_VTable{
return
},
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- when ODIN_OS == "windows" || ODIN_OS == "wasi" {
+ when ODIN_OS == .Windows || ODIN_OS == .WASI {
fd := Handle(uintptr(s.stream_data))
os_err: Errno
n, os_err = read_at(fd, p, offset)
@@ -33,7 +33,7 @@ _file_stream_vtable := &io.Stream_VTable{
return
},
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
- when ODIN_OS == "windows" || ODIN_OS == "wasi" {
+ when ODIN_OS == .Windows || ODIN_OS == .WASI {
fd := Handle(uintptr(s.stream_data))
os_err: Errno
n, os_err = write_at(fd, p, offset)
@@ -53,7 +53,7 @@ _file_stream_vtable := &io.Stream_VTable{
return sz
},
impl_flush = proc(s: io.Stream) -> io.Error {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
fd := Handle(uintptr(s.stream_data))
flush(fd)
} else {
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index cba44953d..252912710 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -89,7 +89,7 @@ scan_chunk :: proc(pattern: string) -> (star: bool, chunk, rest: string) {
scan_loop: for i = 0; i < len(pattern); i += 1 {
switch pattern[i] {
case '\\':
- when ODIN_OS != "windows" {
+ when ODIN_OS != .Windows {
if i+1 < len(pattern) {
i += 1
}
@@ -161,7 +161,7 @@ match_chunk :: proc(chunk, s: string) -> (rest: string, ok: bool, err: Match_Err
chunk = chunk[1:]
case '\\':
- when ODIN_OS != "windows" {
+ when ODIN_OS != .Windows {
chunk = chunk[1:]
if len(chunk) == 0 {
err = .Syntax_Error
@@ -188,7 +188,7 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
return
}
chunk := chunk
- if chunk[0] == '\\' && ODIN_OS != "windows" {
+ if chunk[0] == '\\' && ODIN_OS != .Windows {
chunk = chunk[1:]
if len(chunk) == 0 {
err = .Syntax_Error
@@ -220,19 +220,21 @@ get_escape :: proc(chunk: string) -> (r: rune, next_chunk: string, err: Match_Er
//
glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []string, err: Match_Error) {
+ context.allocator = allocator
+
if !has_meta(pattern) {
// TODO(bill): os.lstat on here to check for error
- m := make([]string, 1, allocator)
+ m := make([]string, 1)
m[0] = pattern
return m[:], .None
}
- temp_buf: [8]byte
-
dir, file := split(pattern)
volume_len := 0
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
+ temp_buf: [8]byte
volume_len, dir = clean_glob_path_windows(dir, temp_buf[:])
+
} else {
dir = clean_glob_path(dir)
}
@@ -247,7 +249,7 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
if err != .None {
return
}
- dmatches := make([dynamic]string, 0, 0, allocator)
+ dmatches := make([dynamic]string, 0, 0)
for d in m {
dmatches, err = _glob(d, file, &dmatches)
if err != .None {
@@ -259,11 +261,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
}
return
}
-_glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]string, e: Match_Error) {
+_glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := context.allocator) -> (m: [dynamic]string, e: Match_Error) {
+ context.allocator = allocator
+
if matches != nil {
m = matches^
} else {
- m = make([dynamic]string, 0, 0, context.allocator)
+ m = make([dynamic]string, 0, 0)
}
@@ -276,6 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
{
file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info)
+
if ferr != 0 {
return
}
@@ -308,7 +313,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s
@(private)
has_meta :: proc(path: string) -> bool {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
CHARS :: `*?[`
} else {
CHARS :: `*?[\`
diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin
index 39cd80a47..32e4a8a37 100644
--- a/core/path/filepath/path.odin
+++ b/core/path/filepath/path.odin
@@ -1,14 +1,16 @@
// The path/filepath package uses either forward slashes or backslashes depending on the operating system
-// To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package
+// To process paths such as URLs that depend on forward slashes regardless of the OS, use the path package
package filepath
import "core:strings"
+SEPARATOR_CHARS :: `/\`
+
// is_separator checks whether the byte is a valid separator character
is_separator :: proc(c: byte) -> bool {
switch c {
case '/': return true
- case '\\': return ODIN_OS == "windows"
+ case '\\': return ODIN_OS == .Windows
}
return false
}
@@ -32,7 +34,7 @@ volume_name :: proc(path: string) -> string {
}
volume_name_len :: proc(path: string) -> int {
- if ODIN_OS == "windows" {
+ if ODIN_OS == .Windows {
if len(path) < 2 {
return 0
}
@@ -69,6 +71,16 @@ volume_name_len :: proc(path: string) -> int {
return 0
}
+/*
+ Gets the file name and extension from a path.
+
+ i.e:
+ 'path/to/name.tar.gz' -> 'name.tar.gz'
+ 'path/to/name.txt' -> 'name.txt'
+ 'path/to/name' -> 'name'
+
+ Returns "." if the path is an empty string.
+*/
base :: proc(path: string) -> string {
if path == "" {
return "."
@@ -94,6 +106,118 @@ base :: proc(path: string) -> string {
return path
}
+/*
+ Gets the name of a file from a path.
+
+ The stem of a file is such that stem(path) + ext(path) = base(path).
+
+ Only the last dot is considered when splitting the file extension.
+ See `short_stem`.
+
+ i.e:
+ 'name.tar.gz' -> 'name.tar'
+ 'name.txt' -> 'name'
+
+ Returns an empty string if there is no stem. e.g: '.gitignore'.
+ Returns an empty string if there's a trailing path separator.
+*/
+stem :: proc(path: string) -> string {
+ if len(path) > 0 && is_separator(path[len(path) - 1]) {
+ // NOTE(tetra): Trailing separator
+ return ""
+ }
+
+ // NOTE(tetra): Get the basename
+ path := path
+ if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 {
+ path = path[i+1:]
+ }
+
+ if i := strings.last_index_byte(path, '.'); i != -1 {
+ return path[:i]
+ }
+
+ return path
+}
+
+/*
+ Gets the name of a file from a path.
+
+ The short stem is such that short_stem(path) + long_ext(path) = base(path).
+
+ The first dot is used to split off the file extension, unlike `stem` which uses the last dot.
+
+ i.e:
+ 'name.tar.gz' -> 'name'
+ 'name.txt' -> 'name'
+
+ Returns an empty string if there is no stem. e.g: '.gitignore'.
+ Returns an empty string if there's a trailing path separator.
+*/
+short_stem :: proc(path: string) -> string {
+ s := stem(path)
+ if i := strings.index_byte(s, '.'); i != -1 {
+ return s[:i]
+ }
+ return s
+}
+
+/*
+ Gets the file extension from a path, including the dot.
+
+ The file extension is such that stem(path) + ext(path) = base(path).
+
+ Only the last dot is considered when splitting the file extension.
+ See `long_ext`.
+
+ i.e:
+ 'name.tar.gz' -> '.gz'
+ 'name.txt' -> '.txt'
+
+ Returns an empty string if there is no dot.
+ Returns an empty string if there is a trailing path separator.
+*/
+ext :: proc(path: string) -> string {
+ for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 {
+ if path[i] == '.' {
+ return path[i:]
+ }
+ }
+ return ""
+}
+
+/*
+ Gets the file extension from a path, including the dot.
+
+ The long file extension is such that short_stem(path) + long_ext(path) = base(path).
+
+ The first dot is used to split off the file extension, unlike `ext` which uses the last dot.
+
+ i.e:
+ 'name.tar.gz' -> '.tar.gz'
+ 'name.txt' -> '.txt'
+
+ Returns an empty string if there is no dot.
+ Returns an empty string if there is a trailing path separator.
+*/
+long_ext :: proc(path: string) -> string {
+ if len(path) > 0 && is_separator(path[len(path) - 1]) {
+ // NOTE(tetra): Trailing separator
+ return ""
+ }
+
+ // NOTE(tetra): Get the basename
+ path := path
+ if i := strings.last_index_any(path, SEPARATOR_CHARS); i != -1 {
+ path = path[i+1:]
+ }
+
+ if i := strings.index_byte(path, '.'); i != -1 {
+ return path[i:]
+ }
+
+ return ""
+}
clean :: proc(path: string, allocator := context.allocator) -> string {
context.allocator = allocator
@@ -122,6 +246,7 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
vol_and_path = original_path,
vol_len = vol_len,
}
+ defer lazy_buffer_destroy(out)
r, dot_dot := 0, 0
if rooted {
@@ -170,7 +295,6 @@ clean :: proc(path: string, allocator := context.allocator) -> string {
cleaned, new_allocation := from_slash(s)
if new_allocation {
delete(s)
- lazy_buffer_destroy(out)
}
return cleaned
}
@@ -189,15 +313,6 @@ to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: str
return strings.replace_all(path, SEPARATOR_STRING, "/", allocator)
}
-ext :: proc(path: string) -> string {
- for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 {
- if path[i] == '.' {
- return path[i:]
- }
- }
- return ""
-}
-
Relative_Error :: enum {
None,
@@ -284,13 +399,14 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (
}
dir :: proc(path: string, allocator := context.allocator) -> string {
+ context.allocator = allocator
vol := volume_name(path)
i := len(path) - 1
for i >= len(vol) && !is_separator(path[i]) {
i -= 1
}
- dir := clean(path[len(vol) : i+1], allocator)
- defer delete(dir, allocator)
+ dir := clean(path[len(vol) : i+1])
+ defer delete(dir)
if dir == "." && len(vol) > 2 {
return strings.clone(vol)
}
@@ -299,6 +415,11 @@ dir :: proc(path: string, allocator := context.allocator) -> string {
+// Splits the PATH-like `path` string, returning an array of its separated components (delete after use).
+// For Windows the separator is `;`, for Unix it's `:`.
+// An empty string returns nil. A non-empty string with no separators returns a 1-element array.
+// Any empty components will be included, e.g. `a::b` will return a 3-element array, as will `::`.
+// Separators within pairs of double-quotes will be ignored and stripped, e.g. `"a:b"c:d` will return []{`a:bc`, `d`}.
split_list :: proc(path: string, allocator := context.allocator) -> []string {
if path == "" {
return nil
@@ -321,7 +442,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
start, quote = 0, false
- list := make([]string, count, allocator)
+ list := make([]string, count + 1, allocator)
index := 0
for i := 0; i < len(path); i += 1 {
c := path[i]
@@ -335,6 +456,7 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string {
}
}
assert(index == count)
+ list[index] = path[start:]
for s0, i in list {
s, new := strings.replace_all(s0, `"`, ``, allocator)
diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin
index 1db528a2f..d0eaa3635 100644
--- a/core/path/filepath/path_unix.odin
+++ b/core/path/filepath/path_unix.odin
@@ -1,7 +1,7 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package filepath
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
foreign import libc "System.framework"
} else {
foreign import libc "system:c"
@@ -54,11 +54,16 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
}
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
@(private)
foreign libc {
@(link_name="__error") __error :: proc() -> ^i32 ---
}
+} else when ODIN_OS == .OpenBSD {
+ @(private)
+ foreign libc {
+ @(link_name="__errno") __error :: proc() -> ^i32 ---
+ }
} else {
@(private)
foreign libc {
diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin
index 29d4fd5b1..dad63cc09 100644
--- a/core/path/filepath/walk.odin
+++ b/core/path/filepath/walk.odin
@@ -71,7 +71,7 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc) -> (err: os.Errno, skip_
@(private)
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) {
- f, err := os.open(dir_name)
+ f, err := os.open(dir_name, os.O_RDONLY)
if err != 0 {
return nil, err
}
diff --git a/core/path/path_error.odin b/core/path/path_error.odin
new file mode 100644
index 000000000..2be0b4cf4
--- /dev/null
+++ b/core/path/path_error.odin
@@ -0,0 +1,5 @@
+package path
+
+#panic(
+`core:path/slashpath - for paths separated by forward slashes, e.g. paths in URLs, this does not deal with OS-specific paths
+core:path/filepath - uses either forward slashes or backslashes depending on the operating system, deals with Windows/NT paths with volume letters or backslashes (on the related platforms)`)
diff --git a/core/path/match.odin b/core/path/slashpath/match.odin
similarity index 99%
rename from core/path/match.odin
rename to core/path/slashpath/match.odin
index 0bea4f6e7..09e774275 100644
--- a/core/path/match.odin
+++ b/core/path/slashpath/match.odin
@@ -1,4 +1,4 @@
-package path
+package slashpath
import "core:strings"
import "core:unicode/utf8"
diff --git a/core/path/path.odin b/core/path/slashpath/path.odin
similarity index 94%
rename from core/path/path.odin
rename to core/path/slashpath/path.odin
index 186176b42..8ac10e655 100644
--- a/core/path/path.odin
+++ b/core/path/slashpath/path.odin
@@ -1,9 +1,9 @@
-// The path package is only to be used for paths separated by forward slashes,
+// The slashpath package is only to be used for paths separated by forward slashes,
// e.g. paths in URLs
//
// This package does not deal with Windows/NT paths with volume letters or backslashes
// To manipulate operating system specific paths, use the path/filepath package
-package path
+package slashpath
import "core:strings"
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index 7f64d0974..49d7ef9b5 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -234,7 +234,7 @@ is_nil :: proc(v: any) -> bool {
return true
}
data := as_bytes(v)
- if data != nil {
+ if data == nil {
return true
}
for v in data {
@@ -365,6 +365,19 @@ index :: proc(val: any, i: int, loc := #caller_location) -> any {
return nil
}
+deref :: proc(val: any) -> any {
+ if val != nil {
+ ti := type_info_base(type_info_of(val.id))
+ if info, ok := ti.variant.(Type_Info_Pointer); ok {
+ return any{
+ (^rawptr)(val.data)^,
+ info.elem.id,
+ }
+ }
+ }
+ return val
+}
+
// Struct_Tag represents the type of the string of a struct field
@@ -680,7 +693,6 @@ union_variant_typeid :: proc(a: any) -> typeid {
return nil
}
panic("expected a union to reflect.union_variant_typeid")
-
}
get_union_variant_raw_tag :: proc(a: any) -> i64 {
@@ -1042,6 +1054,7 @@ as_u64 :: proc(a: any) -> (value: u64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
+ case f16: value = u64(v)
case f32: value = u64(v)
case f64: value = u64(v)
case f32le: value = u64(v)
@@ -1147,6 +1160,7 @@ as_f64 :: proc(a: any) -> (value: f64, valid: bool) {
case Type_Info_Float:
valid = true
switch v in a {
+ case f16: value = f64(v)
case f32: value = f64(v)
case f64: value = (v)
case f32le: value = f64(v)
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index 74778013a..2e2149820 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -334,11 +334,11 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
-write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid) {
- write_type(buf, type_info_of(id))
+write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
+ return write_type_writer(strings.to_writer(buf), type_info_of(id))
}
-write_typeid_writer :: proc(writer: io.Writer, id: typeid) {
- write_type(writer, type_info_of(id))
+write_typeid_writer :: proc(writer: io.Writer, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
+ return write_type_writer(writer, type_info_of(id), n_written)
}
write_typeid :: proc{
@@ -472,6 +472,9 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
write_type(w, info.elem, &n) or_return
case Type_Info_Enumerated_Array:
+ if info.is_sparse {
+ io.write_string(w, "#sparse", &n) or_return
+ }
io.write_string(w, "[", &n) or_return
write_type(w, info.index, &n) or_return
io.write_string(w, "]", &n) or_return
diff --git a/core/runtime/core.odin b/core/runtime/core.odin
index be30eef02..4269450de 100644
--- a/core/runtime/core.odin
+++ b/core/runtime/core.odin
@@ -33,6 +33,11 @@ Calling_Convention :: enum u8 {
None = 6,
Naked = 7,
+
+ _ = 8, // reserved
+
+ Win64 = 9,
+ SysV = 10,
}
Type_Info_Enum_Value :: distinct i64
@@ -95,6 +100,7 @@ Type_Info_Enumerated_Array :: struct {
count: int,
min_value: Type_Info_Enum_Value,
max_value: Type_Info_Enum_Value,
+ is_sparse: bool,
}
Type_Info_Dynamic_Array :: struct {elem: ^Type_Info, elem_size: int}
Type_Info_Slice :: struct {elem: ^Type_Info, elem_size: int}
@@ -130,6 +136,7 @@ Type_Info_Union :: struct {
custom_align: bool,
no_nil: bool,
maybe: bool,
+ shared_nil: bool,
}
Type_Info_Enum :: struct {
base: ^Type_Info,
@@ -345,7 +352,6 @@ Context :: struct {
assertion_failure_proc: Assertion_Failure_Proc,
logger: Logger,
- user_data: any,
user_ptr: rawptr,
user_index: int,
@@ -386,6 +392,59 @@ Raw_Cstring :: struct {
}
+/*
+ // Defined internally by the compiler
+ Odin_OS_Type :: enum int {
+ Unknown,
+ Windows,
+ Darwin,
+ Linux,
+ Essence,
+ FreeBSD,
+ OpenBSD,
+ WASI,
+ JS,
+ Freestanding,
+ }
+*/
+Odin_OS_Type :: type_of(ODIN_OS)
+
+/*
+ // Defined internally by the compiler
+ Odin_Arch_Type :: enum int {
+ Unknown,
+ amd64,
+ i386,
+ arm64,
+ wasm32,
+ wasm64,
+ }
+*/
+Odin_Arch_Type :: type_of(ODIN_ARCH)
+
+/*
+ // Defined internally by the compiler
+ Odin_Build_Mode_Type :: enum int {
+ Executable,
+ Dynamic,
+ Object,
+ Assembly,
+ LLVM_IR,
+ }
+*/
+Odin_Build_Mode_Type :: type_of(ODIN_BUILD_MODE)
+
+/*
+ // Defined internally by the compiler
+ Odin_Endian_Type :: enum int {
+ Unknown,
+ Little,
+ Big,
+ }
+*/
+Odin_Endian_Type :: type_of(ODIN_ENDIAN)
+
+
/////////////////////////////
// Init Startup Procedures //
/////////////////////////////
@@ -394,7 +453,7 @@ Raw_Cstring :: struct {
// This is probably only useful for freestanding targets
foreign {
@(link_name="__$startup_runtime")
- _startup_runtime :: proc() ---
+ _startup_runtime :: proc "odin" () ---
}
@(link_name="__$cleanup_runtime")
@@ -402,6 +461,11 @@ _cleanup_runtime :: proc() {
default_temp_allocator_destroy(&global_default_temp_allocator_data)
}
+_cleanup_runtime_contextless :: proc "contextless" () {
+ context = default_context()
+ _cleanup_runtime()
+}
+
/////////////////////////////
/////////////////////////////
@@ -451,16 +515,18 @@ __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info #no_bounds_check
return &type_table[n]
}
-typeid_base :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_of(id)
- ti = type_info_base(ti)
- return ti.id
+when !ODIN_DISALLOW_RTTI {
+ typeid_base :: proc "contextless" (id: typeid) -> typeid {
+ ti := type_info_of(id)
+ ti = type_info_base(ti)
+ return ti.id
+ }
+ typeid_core :: proc "contextless" (id: typeid) -> typeid {
+ ti := type_info_core(type_info_of(id))
+ return ti.id
+ }
+ typeid_base_without_enum :: typeid_core
}
-typeid_core :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_core(type_info_of(id))
- return ti.id
-}
-typeid_base_without_enum :: typeid_core
@@ -500,7 +566,7 @@ __init_context :: proc "contextless" (c: ^Context) {
return
}
- // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contexless" calling convention
+ // NOTE(bill): Do not initialize these procedures with a call as they are not defined with the "contextless" calling convention
c.allocator.procedure = default_allocator_proc
c.allocator.data = nil
@@ -516,7 +582,7 @@ __init_context :: proc "contextless" (c: ^Context) {
}
default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code_Location) -> ! {
- when ODIN_OS == "freestanding" {
+ when ODIN_OS == .Freestanding {
// Do nothing
} else {
print_caller_location(loc)
diff --git a/core/runtime/core_builtin.odin b/core/runtime/core_builtin.odin
index 44da894c1..13e464a76 100644
--- a/core/runtime/core_builtin.odin
+++ b/core/runtime/core_builtin.odin
@@ -5,6 +5,16 @@ import "core:intrinsics"
@builtin
Maybe :: union($T: typeid) #maybe {T}
+
+@builtin
+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 {
+ offset :: offset_of_by_string(T, field_name)
+ return (^T)(uintptr(ptr) - offset) if ptr != nil else nil
+}
+
+
@thread_local global_default_temp_allocator_data: Default_Temp_Allocator
@builtin
@@ -386,12 +396,13 @@ insert_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
if array == nil {
return
}
- n := len(array)
+ n := max(len(array), index)
m :: 1
- resize(array, n+m, loc)
- if n+m <= len(array) {
+ new_size := n + m
+
+ if resize(array, new_size, loc) {
when size_of(E) != 0 {
- copy(array[index+m:], array[index:])
+ copy(array[index + m:], array[index:])
array[index] = arg
}
ok = true
@@ -409,12 +420,13 @@ insert_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #c
return
}
- n := len(array)
+ n := max(len(array), index)
m := len(args)
- resize(array, n+m, loc)
- if n+m <= len(array) {
+ new_size := n + m
+
+ if resize(array, new_size, loc) {
when size_of(E) != 0 {
- copy(array[index+m:], array[index:])
+ copy(array[index + m:], array[index:])
copy(array[index:], args)
}
ok = true
@@ -427,17 +439,18 @@ insert_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, index: int, arg: string
if array == nil {
return
}
- if len(args) == 0 {
+ if len(arg) == 0 {
ok = true
return
}
- n := len(array)
- m := len(args)
- resize(array, n+m, loc)
- if n+m <= len(array) {
+ n := max(len(array), index)
+ m := len(arg)
+ new_size := n + m
+
+ if resize(array, new_size, loc) {
copy(array[index+m:], array[index:])
- copy(array[index:], args)
+ copy(array[index:], arg)
ok = true
}
return
@@ -614,6 +627,10 @@ raw_data :: proc{raw_array_data, raw_slice_data, raw_dynamic_array_data, raw_str
@(disabled=ODIN_DISABLE_ASSERT)
assert :: proc(condition: bool, message := "", loc := #caller_location) {
if !condition {
+ // NOTE(bill): This is wrapped in a procedure call
+ // to improve performance to make the CPU not
+ // execute speculatively, making it about an order of
+ // magnitude faster
proc(message: string, loc: Source_Code_Location) {
p := context.assertion_failure_proc
if p == nil {
diff --git a/core/runtime/core_builtin_matrix.odin b/core/runtime/core_builtin_matrix.odin
index 08dca288e..53589587c 100644
--- a/core/runtime/core_builtin_matrix.odin
+++ b/core/runtime/core_builtin_matrix.odin
@@ -146,14 +146,14 @@ matrix2x2_inverse_transpose :: proc "contextless" (x: $M/matrix[2, 2]$T) -> (y:
d := x[0, 0]*x[1, 1] - x[0, 1]*x[1, 0]
when intrinsics.type_is_integer(T) {
y[0, 0] = +x[1, 1] / d
- y[1, 0] = -x[1, 0] / d
- y[0, 1] = -x[0, 1] / d
+ y[1, 0] = -x[0, 1] / d
+ y[0, 1] = -x[1, 0] / d
y[1, 1] = +x[0, 0] / d
} else {
id := 1 / d
y[0, 0] = +x[1, 1] * id
- y[1, 0] = -x[1, 0] * id
- y[0, 1] = -x[0, 1] * id
+ y[1, 0] = -x[0, 1] * id
+ y[0, 1] = -x[1, 0] * id
y[1, 1] = +x[0, 0] * id
}
return
@@ -214,16 +214,16 @@ matrix1x1_inverse :: proc "contextless" (x: $M/matrix[1, 1]$T) -> (y: M) {
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) {
- y[0, 0] = x[1, 1] / d
- y[0, 1] = x[1, 0] / d
- y[1, 0] = x[0, 1] / d
- y[1, 1] = x[0, 0] / d
+ y[0, 0] = +x[1, 1] / d
+ y[0, 1] = -x[0, 1] / d
+ y[1, 0] = -x[1, 0] / d
+ y[1, 1] = +x[0, 0] / d
} else {
id := 1 / d
- y[0, 0] = x[1, 1] * id
- y[0, 1] = x[1, 0] * id
- y[1, 0] = x[0, 1] * id
- y[1, 1] = x[0, 0] * id
+ y[0, 0] = +x[1, 1] * id
+ y[0, 1] = -x[0, 1] * id
+ y[1, 0] = -x[1, 0] * id
+ y[1, 1] = +x[0, 0] * id
}
return
}
diff --git a/core/runtime/default_allocators_nil.odin b/core/runtime/default_allocators_nil.odin
index ccb4a3381..04dea0e19 100644
--- a/core/runtime/default_allocators_nil.odin
+++ b/core/runtime/default_allocators_nil.odin
@@ -32,7 +32,7 @@ nil_allocator :: proc() -> Allocator {
-when ODIN_OS == "freestanding" {
+when ODIN_OS == .Freestanding {
default_allocator_proc :: nil_allocator_proc
default_allocator :: nil_allocator
}
\ No newline at end of file
diff --git a/core/runtime/default_temporary_allocator.odin b/core/runtime/default_temporary_allocator.odin
index 01143e222..4337e555b 100644
--- a/core/runtime/default_temporary_allocator.odin
+++ b/core/runtime/default_temporary_allocator.odin
@@ -3,7 +3,7 @@ package runtime
DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE: int : #config(DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE, 1<<22)
-when ODIN_OS == "freestanding" || ODIN_OS == "js" || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
+when ODIN_OS == .Freestanding || ODIN_OS == .JS || ODIN_DEFAULT_TO_NIL_ALLOCATOR {
Default_Temp_Allocator :: struct {}
default_temp_allocator_init :: proc(s: ^Default_Temp_Allocator, size: int, backup_allocator := context.allocator) {}
diff --git a/core/runtime/entry_unix.odin b/core/runtime/entry_unix.odin
new file mode 100644
index 000000000..9f7d219c3
--- /dev/null
+++ b/core/runtime/entry_unix.odin
@@ -0,0 +1,33 @@
+//+private
+//+build linux, darwin, freebsd, openbsd
+package runtime
+
+import "core:intrinsics"
+
+when ODIN_BUILD_MODE == .Dynamic {
+ @(link_name="_odin_entry_point", linkage="strong", require/*, link_section=".init"*/)
+ _odin_entry_point :: proc "c" () {
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ }
+ @(link_name="_odin_exit_point", linkage="strong", require/*, link_section=".fini"*/)
+ _odin_exit_point :: proc "c" () {
+ context = default_context()
+ #force_no_inline _cleanup_runtime()
+ }
+ @(link_name="main", linkage="strong", require)
+ main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
+ return 0
+ }
+} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
+ @(link_name="main", linkage="strong", require)
+ main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
+ args__ = argv[:argc]
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ #force_no_inline _cleanup_runtime()
+ return 0
+ }
+}
diff --git a/core/runtime/entry_wasm.odin b/core/runtime/entry_wasm.odin
new file mode 100644
index 000000000..125abc756
--- /dev/null
+++ b/core/runtime/entry_wasm.odin
@@ -0,0 +1,19 @@
+//+private
+//+build wasm32, wasm64
+package runtime
+
+import "core:intrinsics"
+
+when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
+ @(link_name="_start", linkage="strong", require, export)
+ _start :: proc "c" () {
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ }
+ @(link_name="_end", linkage="strong", require, export)
+ _end :: proc "c" () {
+ context = default_context()
+ #force_no_inline _cleanup_runtime()
+ }
+}
\ No newline at end of file
diff --git a/core/runtime/entry_windows.odin b/core/runtime/entry_windows.odin
new file mode 100644
index 000000000..2f323cb41
--- /dev/null
+++ b/core/runtime/entry_windows.odin
@@ -0,0 +1,45 @@
+//+private
+//+build windows
+package runtime
+
+import "core:intrinsics"
+
+when ODIN_BUILD_MODE == .Dynamic {
+ @(link_name="DllMain", linkage="strong", require)
+ DllMain :: proc "stdcall" (hinstDLL: rawptr, fdwReason: u32, lpReserved: rawptr) -> b32 {
+ context = default_context()
+ switch fdwReason {
+ case 1: // DLL_PROCESS_ATTACH
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ case 0: // DLL_PROCESS_DETACH
+ #force_no_inline _cleanup_runtime()
+ case 2: // DLL_THREAD_ATTACH
+ break
+ case 3: // DLL_THREAD_DETACH
+ break
+ }
+ return true
+ }
+} else when !ODIN_TEST && !ODIN_NO_ENTRY_POINT {
+ when ODIN_ARCH == .i386 || ODIN_NO_CRT {
+ @(link_name="mainCRTStartup", linkage="strong", require)
+ mainCRTStartup :: proc "stdcall" () -> i32 {
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ #force_no_inline _cleanup_runtime()
+ return 0
+ }
+ } else {
+ @(link_name="main", linkage="strong", require)
+ main :: proc "c" (argc: i32, argv: [^]cstring) -> i32 {
+ args__ = argv[:argc]
+ context = default_context()
+ #force_no_inline _startup_runtime()
+ intrinsics.__entry_point()
+ #force_no_inline _cleanup_runtime()
+ return 0
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/runtime/error_checks.odin b/core/runtime/error_checks.odin
index 7f1aeb2d7..0d0b39072 100644
--- a/core/runtime/error_checks.odin
+++ b/core/runtime/error_checks.odin
@@ -1,7 +1,7 @@
package runtime
bounds_trap :: proc "contextless" () -> ! {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
windows_trap_array_bounds()
} else {
trap()
@@ -9,7 +9,7 @@ bounds_trap :: proc "contextless" () -> ! {
}
type_assertion_trap :: proc "contextless" () -> ! {
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
windows_trap_type_assertion()
} else {
trap()
@@ -21,11 +21,12 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
if 0 <= index && index < count {
return
}
+ @(cold)
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))
- print_string(" is out of bounds range 0:")
+ print_string(" is out of range 0..<")
print_i64(i64(count))
print_byte('\n')
bounds_trap()
@@ -35,11 +36,11 @@ bounds_check_error :: proc "contextless" (file: string, line, column: i32, index
slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int, len: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid slice indices: ")
+ print_string(" Invalid slice indices ")
print_i64(i64(lo))
print_string(":")
print_i64(i64(hi))
- print_string(":")
+ print_string(" is out of range 0..<")
print_i64(i64(len))
print_byte('\n')
bounds_trap()
@@ -47,7 +48,7 @@ slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, h
multi_pointer_slice_handle_error :: proc "contextless" (file: string, line, column: i32, lo, hi: int) -> ! {
print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid slice indices: ")
+ print_string(" Invalid slice indices ")
print_i64(i64(lo))
print_string(":")
print_i64(i64(hi))
@@ -81,13 +82,14 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
if 0 <= low && low <= high && high <= max {
return
}
+ @(cold)
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 values: ")
+ print_string(" Invalid dynamic array indices ")
print_i64(i64(low))
print_string(":")
print_i64(i64(high))
- print_string(":")
+ print_string(" is out of range 0..<")
print_i64(i64(max))
print_byte('\n')
bounds_trap()
@@ -97,17 +99,18 @@ dynamic_array_expr_error :: proc "contextless" (file: string, line, column: i32,
matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32, row_index, column_index, row_count, column_count: int) {
- if 0 <= row_index && row_index < row_count &&
+ if 0 <= row_index && row_index < row_count &&
0 <= column_index && column_index < column_count {
return
}
+ @(cold)
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))
print_string(", ")
print_i64(i64(column_index))
- print_string(" is out of bounds range [0..<")
+ print_string(" is out of range [0..<")
print_i64(i64(row_count))
print_string(", 0..<")
print_i64(i64(column_count))
@@ -119,71 +122,101 @@ matrix_bounds_check_error :: proc "contextless" (file: string, line, column: i32
}
-type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
- if ok {
- return
- }
- 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)
- print_string(" to ")
- print_typeid(to)
- print_byte('\n')
- type_assertion_trap()
- }
- handle_error(file, line, column, from, to)
-}
-
-type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
- if ok {
- return
+when ODIN_DISALLOW_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) {
+ print_caller_location(Source_Code_Location{file, line, column, ""})
+ print_string(" Invalid type assertion\n")
+ type_assertion_trap()
+ }
+ handle_error(file, line, column)
}
- variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
- if id == nil || data == nil {
+ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32) {
+ if ok {
+ return
+ }
+ @(cold)
+ 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()
+ }
+ handle_error(file, line, column)
+ }
+} else {
+ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid) {
+ if ok {
+ return
+ }
+ @(cold)
+ 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)
+ print_string(" to ")
+ print_typeid(to)
+ print_byte('\n')
+ type_assertion_trap()
+ }
+ handle_error(file, line, column, from, to)
+ }
+
+ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ if ok {
+ return
+ }
+
+ variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
+ if id == nil || data == nil {
+ return id
+ }
+ ti := type_info_base(type_info_of(id))
+ #partial switch v in ti.variant {
+ case Type_Info_Any:
+ return (^any)(data).id
+ case Type_Info_Union:
+ tag_ptr := uintptr(data) + v.tag_offset
+ idx := 0
+ switch v.tag_type.size {
+ case 1: idx = int((^u8)(tag_ptr)^) - 1
+ case 2: idx = int((^u16)(tag_ptr)^) - 1
+ case 4: idx = int((^u32)(tag_ptr)^) - 1
+ case 8: idx = int((^u64)(tag_ptr)^) - 1
+ case 16: idx = int((^u128)(tag_ptr)^) - 1
+ }
+ if idx < 0 {
+ return nil
+ } else if idx < len(v.variants) {
+ return v.variants[idx].id
+ }
+ }
return id
}
- ti := type_info_base(type_info_of(id))
- #partial switch v in ti.variant {
- case Type_Info_Any:
- return (^any)(data).id
- case Type_Info_Union:
- tag_ptr := uintptr(data) + v.tag_offset
- idx := 0
- switch v.tag_type.size {
- case 1: idx = int((^u8)(tag_ptr)^) - 1
- case 2: idx = int((^u16)(tag_ptr)^) - 1
- case 4: idx = int((^u32)(tag_ptr)^) - 1
- case 8: idx = int((^u64)(tag_ptr)^) - 1
- case 16: idx = int((^u128)(tag_ptr)^) - 1
- }
- if idx < 0 {
- return nil
- } else if idx < len(v.variants) {
- return v.variants[idx].id
+
+ @(cold)
+ handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+
+ actual := variant_type(from, from_data)
+
+ print_caller_location(Source_Code_Location{file, line, column, ""})
+ print_string(" Invalid type assertion from ")
+ print_typeid(from)
+ print_string(" to ")
+ print_typeid(to)
+ if actual != from {
+ print_string(", actual type: ")
+ print_typeid(actual)
}
+ print_byte('\n')
+ type_assertion_trap()
}
- return id
+ handle_error(file, line, column, from, to, from_data)
}
-
- handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
-
- actual := variant_type(from, from_data)
-
- print_caller_location(Source_Code_Location{file, line, column, ""})
- print_string(" Invalid type assertion from ")
- print_typeid(from)
- print_string(" to ")
- print_typeid(to)
- if actual != from {
- print_string(", actual type: ")
- print_typeid(actual)
- }
- print_byte('\n')
- type_assertion_trap()
- }
- handle_error(file, line, column, from, to, from_data)
}
@@ -191,6 +224,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
if 0 <= len {
return
}
+ @(cold)
handle_error :: proc "contextless" (loc: Source_Code_Location, len: int) {
print_caller_location(loc)
print_string(" Invalid slice length for make: ")
@@ -205,6 +239,7 @@ make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #
if 0 <= len && len <= cap {
return
}
+ @(cold)
handle_error :: proc "contextless" (loc: Source_Code_Location, len, cap: int) {
print_caller_location(loc)
print_string(" Invalid dynamic array parameters for make: ")
@@ -221,6 +256,7 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
if 0 <= cap {
return
}
+ @(cold)
handle_error :: proc "contextless" (loc: Source_Code_Location, cap: int) {
print_caller_location(loc)
print_string(" Invalid map capacity for make: ")
diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin
index ed58f4318..30798f623 100644
--- a/core/runtime/internal.odin
+++ b/core/runtime/internal.odin
@@ -2,13 +2,15 @@ package runtime
import "core:intrinsics"
+@(private="file")
+IS_WASM :: ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64
+
@(private)
RUNTIME_LINKAGE :: "strong" when (
(ODIN_USE_SEPARATE_MODULES ||
- ODIN_BUILD_MODE == "dynamic" ||
+ ODIN_BUILD_MODE == .Dynamic ||
!ODIN_NO_CRT) &&
- !(ODIN_ARCH == "wasm32" ||
- ODIN_ARCH == "wasm64")) else "internal"
+ !IS_WASM) else "internal"
RUNTIME_REQUIRE :: true
@@ -35,10 +37,8 @@ bswap_64 :: proc "contextless" (x: u64) -> u64 {
bswap_128 :: proc "contextless" (x: u128) -> u128 {
z := transmute([4]u32)x
- z[0] = bswap_32(z[3])
- z[1] = bswap_32(z[2])
- z[2] = bswap_32(z[1])
- z[3] = bswap_32(z[0])
+ z[0], z[3] = bswap_32(z[3]), bswap_32(z[0])
+ z[1], z[2] = bswap_32(z[2]), bswap_32(z[1])
return transmute(u128)z
}
@@ -752,6 +752,9 @@ extendhfsf2 :: proc "c" (value: u16) -> f32 {
@(link_name="__floattidf", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
floattidf :: proc "c" (a: i128) -> f64 {
+when IS_WASM {
+ return 0
+} else {
DBL_MANT_DIG :: 53
if a == 0 {
return 0.0
@@ -791,10 +794,14 @@ floattidf :: proc "c" (a: i128) -> f64 {
fb[0] = u32(a) // mantissa-low
return transmute(f64)fb
}
+}
@(link_name="__floattidf_unsigned", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
floattidf_unsigned :: proc "c" (a: u128) -> f64 {
+when IS_WASM {
+ return 0
+} else {
DBL_MANT_DIG :: 53
if a == 0 {
return 0.0
@@ -832,6 +839,7 @@ floattidf_unsigned :: proc "c" (a: u128) -> f64 {
fb[0] = u32(a) // mantissa-low
return transmute(f64)fb
}
+}
diff --git a/core/runtime/print.odin b/core/runtime/print.odin
index 8a14eba08..89c196fc2 100644
--- a/core/runtime/print.odin
+++ b/core/runtime/print.odin
@@ -143,18 +143,36 @@ print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
print_string(file_path)
- print_byte('(')
- print_u64(u64(line))
- print_byte(':')
- print_u64(u64(column))
- print_byte(')')
+ when ODIN_ERROR_POS_STYLE == .Default {
+ print_byte('(')
+ print_u64(u64(line))
+ print_byte(':')
+ print_u64(u64(column))
+ print_byte(')')
+ } else when ODIN_ERROR_POS_STYLE == .Unix {
+ print_byte(':')
+ print_u64(u64(line))
+ print_byte(':')
+ print_u64(u64(column))
+ print_byte(':')
+ } else {
+ #panic("unhandled ODIN_ERROR_POS_STYLE")
+ }
}
print_typeid :: proc "contextless" (id: typeid) {
- if id == nil {
- print_string("nil")
+ when ODIN_DISALLOW_RTTI {
+ if id == nil {
+ print_string("nil")
+ } else {
+ print_string("")
+ }
} else {
- ti := type_info_of(id)
- print_type(ti)
+ if id == nil {
+ print_string("nil")
+ } else {
+ ti := type_info_of(id)
+ print_type(ti)
+ }
}
}
print_type :: proc "contextless" (ti: ^Type_Info) {
@@ -250,6 +268,9 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_type(info.elem)
case Type_Info_Enumerated_Array:
+ if info.is_sparse {
+ print_string("#sparse")
+ }
print_byte('[')
print_type(info.index)
print_byte(']')
diff --git a/core/runtime/procs.odin b/core/runtime/procs.odin
index 961f6376f..5a1d11fe0 100644
--- a/core/runtime/procs.odin
+++ b/core/runtime/procs.odin
@@ -1,6 +1,6 @@
package runtime
-when ODIN_NO_CRT && ODIN_OS == "windows" {
+when ODIN_NO_CRT && ODIN_OS == .Windows {
foreign import lib "system:NtDll.lib"
@(private="file")
@@ -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 == .wasm64) {
@(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_darwin.odin b/core/runtime/procs_darwin.odin
new file mode 100644
index 000000000..b54a28dcc
--- /dev/null
+++ b/core/runtime/procs_darwin.odin
@@ -0,0 +1,21 @@
+//+private
+package runtime
+
+foreign import "system:Foundation.framework"
+
+import "core:intrinsics"
+
+objc_id :: ^intrinsics.objc_object
+objc_Class :: ^intrinsics.objc_class
+objc_SEL :: ^intrinsics.objc_selector
+
+foreign Foundation {
+ objc_lookUpClass :: proc "c" (name: cstring) -> objc_Class ---
+ sel_registerName :: proc "c" (name: cstring) -> objc_SEL ---
+ objc_allocateClassPair :: proc "c" (superclass: objc_Class, name: cstring, extraBytes: uint) ---
+
+ objc_msgSend :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
+ objc_msgSend_fpret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> f64 ---
+ objc_msgSend_fp2ret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) -> complex128 ---
+ objc_msgSend_stret :: proc "c" (self: objc_id, op: objc_SEL, #c_vararg args: ..any) ---
+}
diff --git a/core/runtime/procs_wasm32.odin b/core/runtime/procs_wasm32.odin
index 5caf6d2f8..2a4210c1e 100644
--- a/core/runtime/procs_wasm32.odin
+++ b/core/runtime/procs_wasm32.odin
@@ -1,23 +1,40 @@
//+build wasm32
package runtime
+@(private="file")
+ti_int :: struct #raw_union {
+ using s: struct { lo, hi: u64 },
+ all: i128,
+}
+
@(link_name="__ashlti3", linkage="strong")
-__ashlti3 :: proc "c" (a: i64, b_: i32) -> i64 {
- /*
+__ashlti3 :: proc "c" (a: i128, b_: u32) -> i128 {
+ bits_in_dword :: size_of(u32)*8
b := u32(b_)
- input := transmute([2]i32)a
- result: [2]i32
- if b & 32 != 0 {
- result[0] = 0
- result[1] = input[0] << (b - 32)
+
+ input, result: ti_int
+ input.all = a
+ if b & bits_in_dword != 0 {
+ result.lo = 0
+ result.hi = input.lo << (b-bits_in_dword)
} else {
if b == 0 {
return a
}
- result[0] = input[0]<>(32-b))
+ result.lo = input.lo<>(bits_in_dword-b))
}
- return transmute(i64)result
- */
- return 0
+ return result.all
+}
+
+
+@(link_name="__multi3", linkage="strong")
+__multi3 :: proc "c" (a, b: i128) -> i128 {
+ x, y, r: ti_int
+
+ x.all = a
+ y.all = b
+ r.all = i128(x.lo * y.lo) // TODO this is incorrect
+ r.hi += x.hi*y.lo + x.lo*y.hi
+ return r.all
}
\ No newline at end of file
diff --git a/core/runtime/procs_windows_amd64.odin b/core/runtime/procs_windows_amd64.odin
index 273bb57b2..e430357be 100644
--- a/core/runtime/procs_windows_amd64.odin
+++ b/core/runtime/procs_windows_amd64.odin
@@ -22,4 +22,4 @@ windows_trap_type_assertion :: proc "contextless" () -> ! {
when ODIN_NO_CRT {
@(require)
foreign import crt_lib "procs_windows_amd64.asm"
-}
\ No newline at end of file
+}
diff --git a/core/runtime/procs_windows_386.odin b/core/runtime/procs_windows_i386.odin
similarity index 100%
rename from core/runtime/procs_windows_386.odin
rename to core/runtime/procs_windows_i386.odin
diff --git a/core/runtime/udivmod128.odin b/core/runtime/udivmod128.odin
index 1fd1b5f84..87ef73c2c 100644
--- a/core/runtime/udivmod128.odin
+++ b/core/runtime/udivmod128.odin
@@ -11,7 +11,7 @@ udivmod128 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
q, r: [2]u64
sr: u32 = 0
- low :: 1 when ODIN_ENDIAN == "big" else 0
+ low :: 1 when ODIN_ENDIAN == .Big else 0
high :: 1 - low
U64_BITS :: 8*size_of(u64)
U128_BITS :: 8*size_of(u128)
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 487dd46c2..b8fb29ab3 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -10,6 +10,53 @@ _ :: builtin
_ :: bits
_ :: mem
+/*
+ Turn a pointer and a length into a slice.
+*/
+from_ptr :: proc "contextless" (ptr: ^$T, count: int) -> []T {
+ return ([^]T)(ptr)[:count]
+}
+
+/*
+ Turn a pointer and a length into a byte slice.
+*/
+bytes_from_ptr :: proc "contextless" (ptr: rawptr, byte_count: int) -> []byte {
+ return ([^]byte)(ptr)[:byte_count]
+}
+
+/*
+ Turn a slice into a byte slice.
+
+ See `slice.reinterpret` to go the other way.
+*/
+to_bytes :: proc "contextless" (s: []$T) -> []byte {
+ return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
+}
+
+/*
+ Turn a slice of one type, into a slice of another type.
+
+ Only converts the type and length of the slice itself.
+ The length is rounded down to the nearest whole number of items.
+
+ ```
+ large_items := []i64{1, 2, 3, 4}
+ small_items := slice.reinterpret([]i32, large_items)
+ assert(len(small_items) == 8)
+ ```
+ ```
+ small_items := []byte{1, 0, 0, 0, 0, 0, 0, 0,
+ 2, 0, 0, 0}
+ large_items := slice.reinterpret([]i64, small_items)
+ assert(len(large_items) == 1) // only enough bytes to make 1 x i64; two would need at least 8 bytes.
+ ```
+*/
+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]
+}
+
swap :: proc(array: $T/[]$E, a, b: int) {
when size_of(E) > 8 {
@@ -185,7 +232,7 @@ concatenate :: proc(a: []$T/[]$E, allocator := context.allocator) -> (res: T) {
return
}
-// copies slice into a new dynamic array
+// copies a slice into a new slice
clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
d := make([]E, len(a), allocator)
copy(d[:], a)
@@ -194,11 +241,12 @@ clone :: proc(a: $T/[]$E, allocator := context.allocator) -> []E {
// copies slice into a new dynamic array
-to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
+clone_to_dynamic :: proc(a: $T/[]$E, allocator := context.allocator) -> [dynamic]E {
d := make([dynamic]E, len(a), allocator)
copy(d[:], a)
return d
}
+to_dynamic :: clone_to_dynamic
// Converts slice into a dynamic array without cloning or allocating memory
into_dynamic :: proc(a: $T/[]$E) -> [dynamic]E {
@@ -272,7 +320,7 @@ get_ptr :: proc(array: $T/[]$E, index: int) -> (value: ^E, ok: bool) {
return
}
-as_ptr :: proc(array: $T/[]$E) -> ^E {
+as_ptr :: proc(array: $T/[]$E) -> [^]E {
return raw_data(array)
}
@@ -303,6 +351,23 @@ 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 {} }
+
+ res := make([]V, len(s), allocator)
+ p := as_ptr(s)
+ q := as_ptr(res)
+ r := initializer
+
+ for l := len(s); l > 0; l -= 1 {
+ r = f(r, p[0])
+ q[0] = r
+ p = p[1:]
+ q = q[1:]
+ }
+
+ return res
+}
min :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T) #optional_ok {
@@ -326,15 +391,106 @@ max :: proc(s: $S/[]$T) -> (res: T, ok: bool) where intrinsics.type_is_ordered(T
return
}
+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]
+ ok = true
+ for v in s[1:] {
+ min = builtin.min(min, v)
+ max = builtin.max(max, v)
+ }
+ }
+ return
+}
-dot_product :: proc(a, b: $S/[]$T) -> T
+any_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+ for v in s {
+ if v == value {
+ return true
+ }
+ }
+ return false
+}
+
+none_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+ for v in s {
+ if v == value {
+ return false
+ }
+ }
+ return true
+}
+
+all_of :: proc(s: $S/[]$T, value: T) -> bool where intrinsics.type_is_comparable(T) {
+ if len(s) == 0 {
+ return false
+ }
+ for v in s {
+ if v != value {
+ return false
+ }
+ }
+ return true
+}
+
+
+any_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+ for v in s {
+ if f(v) {
+ return true
+ }
+ }
+ return false
+}
+
+none_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+ for v in s {
+ if f(v) {
+ return false
+ }
+ }
+ return true
+}
+
+all_of_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> bool {
+ if len(s) == 0 {
+ return false
+ }
+ for v in s {
+ if !f(v) {
+ return false
+ }
+ }
+ return true
+}
+
+
+count :: proc(s: $S/[]$T, value: T) -> (n: int) where intrinsics.type_is_comparable(T) {
+ for v in s {
+ if v == value {
+ n += 1
+ }
+ }
+ return
+}
+
+count_proc :: proc(s: $S/[]$T, f: proc(T) -> bool) -> (n: int) {
+ for v in s {
+ if f(v) {
+ n += 1
+ }
+ }
+ return
+}
+
+
+dot_product :: proc(a, b: $S/[]$T) -> (r: T, ok: bool)
where intrinsics.type_is_numeric(T) {
if len(a) != len(b) {
- panic("slice.dot_product: slices of unequal length")
+ return
}
- r: T
#no_bounds_check for _, i in a {
r += a[i] * b[i]
}
- return r
+ return r, true
}
diff --git a/core/slice/sort.odin b/core/slice/sort.odin
index d9755ad0e..8a2dec039 100644
--- a/core/slice/sort.odin
+++ b/core/slice/sort.odin
@@ -1,10 +1,5 @@
package slice
-import "core:intrinsics"
-_ :: intrinsics
-
-ORD :: intrinsics.type_is_ordered
-
Ordering :: enum {
Less = -1,
Equal = 0,
@@ -38,7 +33,7 @@ cmp_proc :: proc($E: typeid) -> (proc(E, E) -> Ordering) where ORD(E) {
sort :: proc(data: $T/[]$E) where ORD(E) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
- _quick_sort(data, 0, n, _max_depth(n))
+ _quick_sort_general(data, 0, n, _max_depth(n), struct{}{}, .Ordered)
}
}
}
@@ -48,7 +43,7 @@ sort :: proc(data: $T/[]$E) where ORD(E) {
sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
- _quick_sort_less(data, 0, n, _max_depth(n), less)
+ _quick_sort_general(data, 0, n, _max_depth(n), less, .Less)
}
}
}
@@ -56,7 +51,33 @@ sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
when size_of(E) != 0 {
if n := len(data); n > 1 {
- _quick_sort_cmp(data, 0, n, _max_depth(n), cmp)
+ _quick_sort_general(data, 0, n, _max_depth(n), cmp, .Cmp)
+ }
+ }
+}
+
+// stable_sort sorts a slice
+stable_sort :: proc(data: $T/[]$E) where ORD(E) {
+ when size_of(E) != 0 {
+ if n := len(data); n > 1 {
+ _stable_sort_general(data, struct{}{}, .Ordered)
+ }
+ }
+}
+
+// stable_sort_by sorts a slice with a given procedure to test whether two values are ordered "i < j"
+stable_sort_by :: proc(data: $T/[]$E, less: proc(i, j: E) -> bool) {
+ when size_of(E) != 0 {
+ if n := len(data); n > 1 {
+ _stable_sort_general(data, less, .Less)
+ }
+ }
+}
+
+stable_sort_by_cmp :: proc(data: $T/[]$E, cmp: proc(i, j: E) -> Ordering) {
+ when size_of(E) != 0 {
+ if n := len(data); n > 1 {
+ _stable_sort_general(data, cmp, .Cmp)
}
}
}
@@ -79,6 +100,7 @@ is_sorted_by :: proc(array: $T/[]$E, less: proc(i, j: E) -> bool) -> bool {
return true
}
+is_sorted_by_cmp :: is_sorted_cmp
is_sorted_cmp :: proc(array: $T/[]$E, cmp: proc(i, j: E) -> Ordering) -> bool {
for i := len(array)-1; i > 0; i -= 1 {
if cmp(array[i], array[i-1]) == .Equal {
@@ -140,489 +162,10 @@ is_sorted_by_key :: proc(array: $T/[]$E, key: proc(E) -> $K) -> bool where ORD(K
return true
}
-
-
@(private)
-_max_depth :: proc(n: int) -> int { // 2*ceil(log2(n+1))
- depth: int
+_max_depth :: proc(n: int) -> (depth: int) { // 2*ceil(log2(n+1))
for i := n; i > 0; i >>= 1 {
depth += 1
}
return depth * 2
}
-
-@(private)
-_quick_sort :: proc(data: $T/[]$E, a, b, max_depth: int) where ORD(E) #no_bounds_check {
- median3 :: proc(data: T, m1, m0, m2: int) #no_bounds_check {
- if data[m1] < data[m0] {
- swap(data, m1, m0)
- }
- if data[m2] < data[m1] {
- swap(data, m2, m1)
- if data[m1] < data[m0] {
- swap(data, m1, m0)
- }
- }
- }
-
- do_pivot :: proc(data: T, lo, hi: int) -> (midlo, midhi: int) #no_bounds_check {
- m := int(uint(lo+hi)>>1)
- if hi-lo > 40 {
- s := (hi-lo)/8
- median3(data, lo, lo+s, lo+s*2)
- median3(data, m, m-s, m+s)
- median3(data, hi-1, hi-1-s, hi-1-s*2)
- }
- median3(data, lo, m, hi-1)
-
-
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && data[a] < data[pivot]; a += 1 {
- }
- b := a
-
- for {
- for ; b < c && !(data[pivot] < data[b]); b += 1 { // data[b] <= pivot
- }
- for ; b < c && data[pivot] < data[c-1]; c -=1 { // data[c-1] > pivot
- }
- if b >= c {
- break
- }
-
- swap(data, b, c-1)
- b += 1
- c -= 1
- }
-
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- dups := 0
- if !(data[pivot] < data[hi-1]) {
- swap(data, c, hi-1)
- c += 1
- dups += 1
- }
- if !(data[b-1] < data[pivot]) {
- b -= 1
- dups += 1
- }
-
- if !(data[m] < data[pivot]) {
- swap(data, m, b-1)
- b -= 1
- dups += 1
- }
- protect = dups > 1
- }
- if protect {
- for {
- for ; a < b && !(data[b-1] < data[pivot]); b -= 1 {
- }
- for ; a < b && data[a] < data[pivot]; a += 1 {
- }
- if a >= b {
- break
- }
- swap(data, a, b-1)
- a += 1
- b -= 1
- }
- }
- swap(data, pivot, b-1)
- return b-1, c
- }
-
-
- a, b, max_depth := a, b, max_depth
-
- if b-a > 12 { // only use shell sort for lengths <= 12
- if max_depth == 0 {
- _heap_sort(data, a, b)
- return
- }
- max_depth -= 1
- mlo, mhi := do_pivot(data, a, b)
- if mlo-a < b-mhi {
- _quick_sort(data, a, mlo, max_depth)
- a = mhi
- } else {
- _quick_sort(data, mhi, b, max_depth)
- b = mlo
- }
- }
- if b-a > 1 {
- // Shell short with gap 6
- for i in a+6.. a && data[j] < data[j-1]; j -= 1 {
- swap(data, j, j-1)
- }
- }
-}
-
-@(private)
-_heap_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
- sift_down :: proc(data: T, lo, hi, first: int) #no_bounds_check {
- root := lo
- for {
- child := 2*root + 1
- if child >= hi {
- break
- }
- if child+1 < hi && data[first+child] < data[first+child+1] {
- child += 1
- }
- if !(data[first+root] < data[first+child]) {
- return
- }
- swap(data, first+root, first+child)
- root = child
- }
- }
-
-
- first, lo, hi := a, 0, b-a
-
- for i := (hi-1)/2; i >= 0; i -= 1 {
- sift_down(data, i, hi, first)
- }
-
- for i := hi-1; i >= 0; i -= 1 {
- swap(data, first, first+i)
- sift_down(data, lo, i, first)
- }
-}
-
-
-
-
-
-
-@(private)
-_quick_sort_less :: proc(data: $T/[]$E, a, b, max_depth: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- median3 :: proc(data: T, m1, m0, m2: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- if less(data[m1], data[m0]) {
- swap(data, m1, m0)
- }
- if less(data[m2], data[m1]) {
- swap(data, m2, m1)
- if less(data[m1], data[m0]) {
- swap(data, m1, m0)
- }
- }
- }
-
- do_pivot :: proc(data: T, lo, hi: int, less: proc(i, j: E) -> bool) -> (midlo, midhi: int) #no_bounds_check {
- m := int(uint(lo+hi)>>1)
- if hi-lo > 40 {
- s := (hi-lo)/8
- median3(data, lo, lo+s, lo+s*2, less)
- median3(data, m, m-s, m+s, less)
- median3(data, hi-1, hi-1-s, hi-1-s*2, less)
- }
- median3(data, lo, m, hi-1, less)
-
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && less(data[a], data[pivot]); a += 1 {
- }
- b := a
-
- for {
- for ; b < c && !less(data[pivot], data[b]); b += 1 { // data[b] <= pivot
- }
- for ; b < c && less(data[pivot], data[c-1]); c -=1 { // data[c-1] > pivot
- }
- if b >= c {
- break
- }
-
- swap(data, b, c-1)
- b += 1
- c -= 1
- }
-
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- dups := 0
- if !less(data[pivot], data[hi-1]) {
- swap(data, c, hi-1)
- c += 1
- dups += 1
- }
- if !less(data[b-1], data[pivot]) {
- b -= 1
- dups += 1
- }
-
- if !less(data[m], data[pivot]) {
- swap(data, m, b-1)
- b -= 1
- dups += 1
- }
- protect = dups > 1
- }
- if protect {
- for {
- for ; a < b && !less(data[b-1], data[pivot]); b -= 1 {
- }
- for ; a < b && less(data[a], data[pivot]); a += 1 {
- }
- if a >= b {
- break
- }
- swap(data, a, b-1)
- a += 1
- b -= 1
- }
- }
- swap(data, pivot, b-1)
- return b-1, c
- }
-
-
- a, b, max_depth := a, b, max_depth
-
- if b-a > 12 { // only use shell sort for lengths <= 12
- if max_depth == 0 {
- _heap_sort_less(data, a, b, less)
- return
- }
- max_depth -= 1
- mlo, mhi := do_pivot(data, a, b, less)
- if mlo-a < b-mhi {
- _quick_sort_less(data, a, mlo, max_depth, less)
- a = mhi
- } else {
- _quick_sort_less(data, mhi, b, max_depth, less)
- b = mlo
- }
- }
- if b-a > 1 {
- // Shell short with gap 6
- for i in a+6.. bool) #no_bounds_check {
- for i in a+1.. a && less(data[j], data[j-1]); j -= 1 {
- swap(data, j, j-1)
- }
- }
-}
-
-@(private)
-_heap_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- sift_down :: proc(data: T, lo, hi, first: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- root := lo
- for {
- child := 2*root + 1
- if child >= hi {
- break
- }
- if child+1 < hi && less(data[first+child], data[first+child+1]) {
- child += 1
- }
- if !less(data[first+root], data[first+child]) {
- return
- }
- swap(data, first+root, first+child)
- root = child
- }
- }
-
-
- first, lo, hi := a, 0, b-a
-
- for i := (hi-1)/2; i >= 0; i -= 1 {
- sift_down(data, i, hi, first, less)
- }
-
- for i := hi-1; i >= 0; i -= 1 {
- swap(data, first, first+i)
- sift_down(data, lo, i, first, less)
- }
-}
-
-
-
-
-
-
-@(private)
-_quick_sort_cmp :: proc(data: $T/[]$E, a, b, max_depth: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- median3 :: proc(data: T, m1, m0, m2: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- if cmp(data[m1], data[m0]) == .Less {
- swap(data, m1, m0)
- }
- if cmp(data[m2], data[m1]) == .Less {
- swap(data, m2, m1)
- if cmp(data[m1], data[m0]) == .Less {
- swap(data, m1, m0)
- }
- }
- }
-
- do_pivot :: proc(data: T, lo, hi: int, cmp: proc(i, j: E) -> Ordering) -> (midlo, midhi: int) #no_bounds_check {
- m := int(uint(lo+hi)>>1)
- if hi-lo > 40 {
- s := (hi-lo)/8
- median3(data, lo, lo+s, lo+s*2, cmp)
- median3(data, m, m-s, m+s, cmp)
- median3(data, hi-1, hi-1-s, hi-1-s*2, cmp)
- }
- median3(data, lo, m, hi-1, cmp)
-
- pivot := lo
- a, c := lo+1, hi-1
-
- for ; a < c && cmp(data[a], data[pivot]) == .Less; a += 1 {
- }
- b := a
-
- for {
- for ; b < c && cmp(data[pivot], data[b]) >= .Equal; b += 1 { // data[b] <= pivot
- }
- for ; b < c && cmp(data[pivot], data[c-1]) == .Less; c -=1 { // data[c-1] > pivot
- }
- if b >= c {
- break
- }
-
- swap(data, b, c-1)
- b += 1
- c -= 1
- }
-
- protect := hi-c < 5
- if !protect && hi-c < (hi-lo)/4 {
- dups := 0
- if cmp(data[pivot], data[hi-1]) != .Less {
- swap(data, c, hi-1)
- c += 1
- dups += 1
- }
- if cmp(data[b-1], data[pivot]) != .Less {
- b -= 1
- dups += 1
- }
-
- if cmp(data[m], data[pivot]) != .Less {
- swap(data, m, b-1)
- b -= 1
- dups += 1
- }
- protect = dups > 1
- }
- if protect {
- for {
- for ; a < b && cmp(data[b-1], data[pivot]) >= .Equal; b -= 1 {
- }
- for ; a < b && cmp(data[a], data[pivot]) == .Less; a += 1 {
- }
- if a >= b {
- break
- }
- swap(data, a, b-1)
- a += 1
- b -= 1
- }
- }
- swap(data, pivot, b-1)
- return b-1, c
- }
-
-
- a, b, max_depth := a, b, max_depth
-
- if b-a > 12 { // only use shell sort for lengths <= 12
- if max_depth == 0 {
- _heap_sort_cmp(data, a, b, cmp)
- return
- }
- max_depth -= 1
- mlo, mhi := do_pivot(data, a, b, cmp)
- if mlo-a < b-mhi {
- _quick_sort_cmp(data, a, mlo, max_depth, cmp)
- a = mhi
- } else {
- _quick_sort_cmp(data, mhi, b, max_depth, cmp)
- b = mlo
- }
- }
- if b-a > 1 {
- // Shell short with gap 6
- for i in a+6.. Ordering) #no_bounds_check {
- for i in a+1.. a && cmp(data[j], data[j-1]) == .Less; j -= 1 {
- swap(data, j, j-1)
- }
- }
-}
-
-@(private)
-_heap_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- sift_down :: proc(data: T, lo, hi, first: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- root := lo
- for {
- child := 2*root + 1
- if child >= hi {
- break
- }
- if child+1 < hi && cmp(data[first+child], data[first+child+1]) == .Less {
- child += 1
- }
- if cmp(data[first+root], data[first+child]) >= .Equal {
- return
- }
- swap(data, first+root, first+child)
- root = child
- }
- }
-
-
- first, lo, hi := a, 0, b-a
-
- for i := (hi-1)/2; i >= 0; i -= 1 {
- sift_down(data, i, hi, first, cmp)
- }
-
- for i := hi-1; i >= 0; i -= 1 {
- swap(data, first, first+i)
- sift_down(data, lo, i, first, cmp)
- }
-}
-
-
-
diff --git a/core/slice/sort_private.odin b/core/slice/sort_private.odin
new file mode 100644
index 000000000..d93d74bf9
--- /dev/null
+++ b/core/slice/sort_private.odin
@@ -0,0 +1,200 @@
+//+private
+package slice
+
+import "core:intrinsics"
+_ :: intrinsics
+
+ORD :: intrinsics.type_is_ordered
+
+Sort_Kind :: enum {
+ Ordered,
+ Less,
+ Cmp,
+}
+
+_quick_sort_general :: proc(data: $T/[]$E, a, b, max_depth: int, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
+ less :: #force_inline proc(a, b: E, call: P) -> bool {
+ when KIND == .Ordered {
+ return a < b
+ } else when KIND == .Less {
+ return call(a, b)
+ } else when KIND == .Cmp {
+ return call(a, b) == .Less
+ } else {
+ #panic("unhandled Sort_Kind")
+ }
+ }
+
+ insertion_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check {
+ for i in a+1.. a && less(data[j], data[j-1], call); j -= 1 {
+ swap(data, j, j-1)
+ }
+ }
+ }
+
+ heap_sort :: proc(data: $T/[]$E, a, b: int, call: P) #no_bounds_check {
+ sift_down :: proc(data: T, lo, hi, first: int, call: P) #no_bounds_check {
+ root := lo
+ for {
+ child := 2*root + 1
+ if child >= hi {
+ break
+ }
+ if child+1 < hi && less(data[first+child], data[first+child+1], call) {
+ child += 1
+ }
+ if !less(data[first+root], data[first+child], call) {
+ return
+ }
+ swap(data, first+root, first+child)
+ root = child
+ }
+ }
+
+
+ first, lo, hi := a, 0, b-a
+
+ for i := (hi-1)/2; i >= 0; i -= 1 {
+ sift_down(data, i, hi, first, call)
+ }
+
+ for i := hi-1; i >= 0; i -= 1 {
+ swap(data, first, first+i)
+ sift_down(data, lo, i, first, call)
+ }
+ }
+
+ median3 :: proc(data: T, m1, m0, m2: int, call: P) #no_bounds_check {
+ if less(data[m1], data[m0], call) {
+ swap(data, m1, m0)
+ }
+ if less(data[m2], data[m1], call) {
+ swap(data, m2, m1)
+ if less(data[m1], data[m0], call) {
+ swap(data, m1, m0)
+ }
+ }
+ }
+
+ do_pivot :: proc(data: T, lo, hi: int, call: P) -> (midlo, midhi: int) #no_bounds_check {
+ m := int(uint(lo+hi)>>1)
+ if hi-lo > 40 {
+ s := (hi-lo)/8
+ median3(data, lo, lo+s, lo+s*2, call)
+ median3(data, m, m-s, m+s, call)
+ median3(data, hi-1, hi-1-s, hi-1-s*2, call)
+ }
+ median3(data, lo, m, hi-1, call)
+
+ pivot := lo
+ a, c := lo+1, hi-1
+
+
+ for ; a < c && less(data[a], data[pivot], call); a += 1 {
+ }
+ b := a
+
+ for {
+ for ; b < c && !less(data[pivot], data[b], call); b += 1 { // data[b] <= pivot
+ }
+ for ; b < c && less(data[pivot], data[c-1], call); c -=1 { // data[c-1] > pivot
+ }
+ if b >= c {
+ break
+ }
+
+ swap(data, b, c-1)
+ b += 1
+ c -= 1
+ }
+
+ protect := hi-c < 5
+ if !protect && hi-c < (hi-lo)/4 {
+ dups := 0
+ if !less(data[pivot], data[hi-1], call) {
+ swap(data, c, hi-1)
+ c += 1
+ dups += 1
+ }
+ if !less(data[b-1], data[pivot], call) {
+ b -= 1
+ dups += 1
+ }
+
+ if !less(data[m], data[pivot], call) {
+ swap(data, m, b-1)
+ b -= 1
+ dups += 1
+ }
+ protect = dups > 1
+ }
+ if protect {
+ for {
+ for ; a < b && !less(data[b-1], data[pivot], call); b -= 1 {
+ }
+ for ; a < b && less(data[a], data[pivot], call); a += 1 {
+ }
+ if a >= b {
+ break
+ }
+ swap(data, a, b-1)
+ a += 1
+ b -= 1
+ }
+ }
+ swap(data, pivot, b-1)
+ return b-1, c
+ }
+
+
+ a, b, max_depth := a, b, max_depth
+
+ for b-a > 12 { // only use shell sort for lengths <= 12
+ if max_depth == 0 {
+ heap_sort(data, a, b, call)
+ return
+ }
+ max_depth -= 1
+ mlo, mhi := do_pivot(data, a, b, call)
+ if mlo-a < b-mhi {
+ _quick_sort_general(data, a, mlo, max_depth, call, KIND)
+ a = mhi
+ } else {
+ _quick_sort_general(data, mhi, b, max_depth, call, KIND)
+ b = mlo
+ }
+ }
+ if b-a > 1 {
+ // Shell short with gap 6
+ for i in a+6.. bool {
+ when KIND == .Ordered {
+ return a < b
+ } else when KIND == .Less {
+ return call(a, b)
+ } else when KIND == .Cmp {
+ return call(a, b) == .Less
+ } else {
+ #panic("unhandled Sort_Kind")
+ }
+ }
+
+ n := len(data)
+ for i in 1.. 0 && less(data[j], data[j-1], call); j -= 1 {
+ swap(data, j, j-1)
+ }
+ }
+}
diff --git a/core/sort/map.odin b/core/sort/map.odin
new file mode 100644
index 000000000..32f5e09a2
--- /dev/null
+++ b/core/sort/map.odin
@@ -0,0 +1,36 @@
+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/strconv.odin b/core/strconv/strconv.odin
index 6b3a91b4c..65161a820 100644
--- a/core/strconv/strconv.odin
+++ b/core/strconv/strconv.odin
@@ -882,7 +882,9 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
return -1
}
- assert(len(lit) >= 2)
+ if len(lit) < 2 {
+ return
+ }
if lit[0] == '`' {
return lit[1:len(lit)-1], false, true
}
@@ -893,6 +895,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str
if s == `""` {
return "", false, true
}
+ s = s[1:len(s)-1]
if contains_rune(s, '\n') >= 0 {
return s, false, false
diff --git a/core/strings/ascii_set.odin b/core/strings/ascii_set.odin
index 582049eee..9b59666f3 100644
--- a/core/strings/ascii_set.odin
+++ b/core/strings/ascii_set.odin
@@ -5,6 +5,7 @@ import "core:unicode/utf8"
Ascii_Set :: distinct [8]u32
+// create an ascii set of all unique characters in the string
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
return
}
+// 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 {
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 6952ba088..d6065cf70 100644
--- a/core/strings/builder.odin
+++ b/core/strings/builder.odin
@@ -7,40 +7,56 @@ 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
+*/
Builder :: struct {
buf: [dynamic]byte,
}
+// return a builder, default length 0 / cap 16 are done through make
make_builder_none :: proc(allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, allocator)}
}
+// return a builder, with a set length `len` and cap 16 byte buffer
make_builder_len :: proc(len: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, allocator)}
}
+// return a builder, with a set length `len` byte buffer and a custom `cap`
make_builder_len_cap :: proc(len, cap: int, allocator := context.allocator) -> Builder {
return Builder{buf=make([dynamic]byte, len, cap, allocator)}
}
+// overload simple `make_builder_*` with or without len / cap parameters
make_builder :: proc{
make_builder_none,
make_builder_len,
make_builder_len_cap,
}
+// initialize a builder, default length 0 / cap 16 are done through make
+// replaces the existing `buf`
init_builder_none :: proc(b: ^Builder, allocator := context.allocator) {
b.buf = make([dynamic]byte, allocator)
}
+// initialize a builder, with a set length `len` and cap 16 byte buffer
+// replaces the existing `buf`
init_builder_len :: proc(b: ^Builder, len: int, allocator := context.allocator) {
b.buf = make([dynamic]byte, len, allocator)
}
+// initialize a builder, with a set length `len` byte buffer and a custom `cap`
+// replaces the existing `buf`
init_builder_len_cap :: proc(b: ^Builder, len, cap: int, allocator := context.allocator) {
b.buf = make([dynamic]byte, len, cap, allocator)
}
+// overload simple `init_builder_*` with or without len / ap parameters
init_builder :: proc{
init_builder_none,
init_builder_len,
@@ -76,30 +92,42 @@ _builder_stream_vtable := &io.Stream_VTable{
},
}
+// 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}
}
+
+// return an `io.Writer` from a builder
to_writer :: proc(b: ^Builder) -> io.Writer {
return io.to_writer(to_stream(b))
}
-
-
-
+// delete and clear the builder byte buffer content
destroy_builder :: proc(b: ^Builder) {
delete(b.buf)
clear(&b.buf)
}
+// reserve the builfer byte buffer to a specific cap, when it's higher than before
grow_builder :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap)
}
+// clear the builder byte buffer content
reset_builder :: 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_slice(bytes[:])
+ strings.write_byte(&builder, 'a') -> "a"
+ strings.write_byte(&builder, 'b') -> "ab"
+*/
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing
d := mem.Raw_Dynamic_Array{
@@ -112,20 +140,36 @@ builder_from_slice :: proc(backing: []byte) -> Builder {
buf = transmute([dynamic]byte)d,
}
}
+
+// cast the builder byte buffer to a string and return it
to_string :: proc(b: Builder) -> string {
return string(b.buf[:])
}
+// return the length of the builder byte buffer
builder_len :: proc(b: Builder) -> int {
return len(b.buf)
}
+
+// return the cap of the builder byte buffer
builder_cap :: proc(b: Builder) -> int {
return cap(b.buf)
}
+
+// returns the space left in the builder byte buffer to use up
builder_space :: proc(b: Builder) -> int {
- return max(cap(b.buf), len(b.buf), 0)
+ return cap(b.buf) - len(b.buf)
}
+/*
+ appends a byte to the builder, returns the append diff
+
+ builder := strings.make_builder()
+ 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)
append(&b.buf, x)
@@ -133,6 +177,14 @@ write_byte :: proc(b: ^Builder, x: byte) -> (n: int) {
return n1-n0
}
+/*
+ appends a slice of bytes to the builder, returns the append diff
+
+ builder := strings.make_builder()
+ bytes := [?]byte { 'a', 'b', 'c' }
+ strings.write_bytes(&builder, bytes[:]) // 3
+ fmt.println(strings.to_string(builder)) // -> abc
+*/
write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
n0 := len(b.buf)
append(&b.buf, ..x)
@@ -140,11 +192,28 @@ write_bytes :: proc(b: ^Builder, x: []byte) -> (n: int) {
return n1-n0
}
+/*
+ appends a single rune into the builder, returns written rune size and an `io.Error`
+
+ builder := strings.make_builder()
+ strings.write_rune_builder(&builder, 'ä') // 2 None
+ strings.write_rune_builder(&builder, 'b') // 1 None
+ strings.write_rune_builder(&builder, 'c') // 1 None
+ fmt.println(strings.to_string(builder)) // -> äbc
+*/
write_rune_builder :: proc(b: ^Builder, r: rune) -> (int, io.Error) {
return io.write_rune(to_writer(b), r)
}
+/*
+ appends a quoted rune into the builder, returns written size
+ builder := strings.make_builder()
+ strings.write_string(&builder, "abc") // 3
+ strings.write_quoted_rune_builder(&builder, 'ä') // 4
+ strings.write_string(&builder, "abc") // 3
+ fmt.println(strings.to_string(builder)) // -> abc'ä'abc
+*/
write_quoted_rune_builder :: proc(b: ^Builder, r: rune) -> (n: int) {
return write_quoted_rune(to_writer(b), r)
}
@@ -155,7 +224,7 @@ _write_byte :: proc(w: io.Writer, c: byte) -> int {
return 1 if err == nil else 0
}
-
+// writer append a quoted rune into the byte buffer, return the written size
write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
quote := byte('\'')
n += _write_byte(w, quote)
@@ -173,50 +242,75 @@ write_quoted_rune :: proc(w: io.Writer, r: rune) -> (n: int) {
return
}
-
+// overload for `write_string_*` variants
write_string :: proc{
write_string_builder,
write_string_writer,
}
+/*
+ appends a string to the builder, return the written byte size
+
+ builder := strings.make_builder()
+ strings.write_string(&builder, "a") // 1
+ strings.write_string(&builder, "bc") // 2
+ strings.write_string(&builder, "xyz") // 3
+ fmt.println(strings.to_string(builder)) // -> abcxyz
+*/
write_string_builder :: proc(b: ^Builder, s: string) -> (n: int) {
return write_string_writer(to_writer(b), s)
}
+// appends a string to the writer
write_string_writer :: proc(w: io.Writer, s: string) -> (n: int) {
n, _ = io.write(w, transmute([]byte)s)
return
}
-
-
-
+// pops and returns the last byte in the builder
+// returns 0 when the builder is empty
pop_byte :: proc(b: ^Builder) -> (r: byte) {
if len(b.buf) == 0 {
return 0
}
+
r = b.buf[len(b.buf)-1]
d := cast(^mem.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-1, 0)
return
}
+// pops the last rune in the builder and returns the popped rune and its rune width
+// returns 0, 0 when the builder is empty
pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
+ if len(b.buf) == 0 {
+ return 0, 0
+ }
+
r, width = utf8.decode_last_rune(b.buf[:])
d := cast(^mem.Raw_Dynamic_Array)&b.buf
d.len = max(d.len-width, 0)
return
}
-
@(private)
DIGITS_LOWER := "0123456789abcdefx"
+// overload for `write_quoted_string_*` variants
write_quoted_string :: proc{
write_quoted_string_builder,
write_quoted_string_writer,
}
+/*
+ append a quoted string into the builder, return the written byte size
+
+ builder := strings.make_builder()
+ strings.write_quoted_string(&builder, "a") // 3
+ strings.write_quoted_string(&builder, "bc", '\'') // 4
+ strings.write_quoted_string(&builder, "xyz") // 5
+ fmt.println(strings.to_string(builder)) // -> "a"'bc'xyz"
+*/
write_quoted_string_builder :: proc(b: ^Builder, str: string, quote: byte = '"') -> (n: int) {
n, _ = io.write_quoted_string(to_writer(b), str, quote)
return
@@ -228,11 +322,13 @@ write_quoted_string_writer :: proc(w: io.Writer, str: string, quote: byte = '"')
return
}
+// overload for `write_encoded_rune_*`
write_encoded_rune :: proc{
write_encoded_rune_builder,
write_encoded_rune_writer,
}
+// appends a rune to the builder, optional `write_quote` boolean tag, returns the written rune size
write_encoded_rune_builder :: proc(b: ^Builder, r: rune, write_quote := true) -> (n: int) {
n, _ = io.write_encoded_rune(to_writer(b), r, write_quote)
return
@@ -244,12 +340,15 @@ write_encoded_rune_writer :: proc(w: io.Writer, r: rune, write_quote := true) ->
return
}
-
+// overload for `write_escaped_rune_*`
write_escaped_rune :: proc{
write_escaped_rune_builder,
write_escaped_rune_writer,
}
+// appends a rune to the builder, fully written out in case of escaped runes e.g. '\a' will be written as such
+// when `r` and `quote` match and `quote` is `\\` - they will be written as two slashes
+// `html_safe` flag in case the runes '<', '>', '&' should be encoded as digits e.g. `\u0026`
write_escaped_rune_builder :: proc(b: ^Builder, r: rune, quote: byte, html_safe := false) -> (n: int) {
n, _ = io.write_escaped_rune(to_writer(b), r, quote, html_safe)
return
@@ -261,21 +360,26 @@ write_escaped_rune_writer :: proc(w: io.Writer, r: rune, quote: byte, html_safe
return
}
-
+// writes a u64 value `i` in `base` = 10 into the builder, returns the written amount of characters
write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) {
buf: [32]byte
s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil)
return write_string(b, s)
}
+
+// writes a i64 value `i` in `base` = 10 into the builder, returns the written amount of characters
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 `i` in `base` = 10 into the builder, returns the written amount of characters
write_uint :: proc(b: ^Builder, i: uint, base: int = 10) -> (n: int) {
return write_u64(b, u64(i), base)
}
+
+// writes a int value `i` in `base` = 10 into the builder, returns the written amount of characters
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 b0d42b2eb..5e7110281 100644
--- a/core/strings/conversion.odin
+++ b/core/strings/conversion.odin
@@ -58,6 +58,13 @@ to_valid_utf8 :: proc(s, replacement: string, allocator := context.allocator) ->
return to_string(b)
}
+/*
+ returns the input string `s` with all runes set to lowered case
+ always allocates using the `allocator`
+
+ strings.to_lower("test") -> test
+ strings.to_lower("Test") -> test
+*/
to_lower :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
init_builder(&b, 0, len(s), allocator)
@@ -66,6 +73,14 @@ to_lower :: proc(s: string, allocator := context.allocator) -> string {
}
return to_string(b)
}
+
+/*
+ returns the input string `s` with all runes set to upper case
+ always allocates using the `allocator`
+
+ strings.to_lower("test") -> TEST
+ strings.to_lower("Test") -> TEST
+*/
to_upper :: proc(s: string, allocator := context.allocator) -> string {
b: Builder
init_builder(&b, 0, len(s), allocator)
@@ -75,13 +90,13 @@ to_upper :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
-
-
-
+// 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)
}
+// returns true when the `r` rune is a non alpha or `unicode.is_space` rune
is_separator :: proc(r: rune) -> bool {
if r <= 0x7f {
switch r {
@@ -101,7 +116,10 @@ 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
+*/
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 {
@@ -122,8 +140,9 @@ string_case_iterator :: proc(w: io.Writer, s: string, callback: proc(w: io.Write
}
}
-
to_lower_camel_case :: to_camel_case
+
+// converts the `s` string to "lowerCamelCase"
to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -147,6 +166,8 @@ to_camel_case :: proc(s: string, allocator := context.allocator) -> string {
}
to_upper_camel_case :: to_pascal_case
+
+// converts the `s` string to "PascalCase"
to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
s := s
s = trim_space(s)
@@ -169,6 +190,15 @@ to_pascal_case :: proc(s: string, allocator := context.allocator) -> string {
return to_string(b)
}
+/*
+ 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
+
+ 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 {
s := s
s = trim_space(s)
@@ -208,24 +238,34 @@ to_delimiter_case :: proc(s: string, delimiter: rune, all_upper_case: bool, allo
return to_string(b)
}
-
+/*
+ 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 {
return to_delimiter_case(s, '_', false, allocator)
}
to_screaming_snake_case :: to_upper_snake_case
+
+// converts the `s` string to "SNAKE_CASE" with all runes uppercased
to_upper_snake_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '_', true, allocator)
}
+// converts the `s` string to "kebab-case" with all runes lowercased
to_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', false, allocator)
}
-to_upper_case :: proc(s: string, allocator := context.allocator) -> string {
+// converts the `s` string to "KEBAB-CASE" with all runes uppercased
+to_upper_kebab_case :: proc(s: string, allocator := context.allocator) -> string {
return to_delimiter_case(s, '-', true, allocator)
}
+// converts the `s` string to "Ada_case"
to_ada_case :: proc(s: string, allocator := context.allocator) -> string {
delimiter :: '_'
diff --git a/core/strings/intern.odin b/core/strings/intern.odin
index ff26d7dbb..27c3db084 100644
--- a/core/strings/intern.odin
+++ b/core/strings/intern.odin
@@ -2,21 +2,26 @@ package strings
import "core:mem"
+// 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
+// `allocator` is used to allocate the actual `Intern_Entry` strings
Intern :: struct {
allocator: mem.Allocator,
entries: map[string]^Intern_Entry,
}
+// initialize the entries map and set the allocator for the string entries
intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) {
m.allocator = allocator
m.entries = make(map[string]^Intern_Entry, 16, map_allocator)
}
+// free the map and all its content allocated using the `.allocator`
intern_destroy :: proc(m: ^Intern) {
for _, value in m.entries {
free(value, m.allocator)
@@ -24,15 +29,22 @@ intern_destroy :: proc(m: ^Intern) {
delete(m.entries)
}
+// returns the `text` string from the intern map - gets set if it didnt exist yet
+// the returned string lives as long as the map entry lives
intern_get :: proc(m: ^Intern, text: string) -> string {
entry := _intern_get_entry(m, text)
#no_bounds_check return string(entry.str[:entry.len])
}
+
+// returns the `text` cstring from the intern map - gets set if it didnt exist yet
+// the returned cstring lives as long as the map entry lives
intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring {
entry := _intern_get_entry(m, text)
return cstring(&entry.str[0])
}
+// looks up wether the `text` string exists in the map, returns the entry
+// sets & allocates the entry if it wasnt set yet
_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check {
if prev, ok := m.entries[text]; ok {
return prev
diff --git a/core/strings/reader.odin b/core/strings/reader.odin
index ba266c0b5..9b2e10b68 100644
--- a/core/strings/reader.odin
+++ b/core/strings/reader.odin
@@ -3,46 +3,60 @@ package strings
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`
+*/
Reader :: struct {
s: string, // read-only buffer
i: i64, // current reading index
prev_rune: int, // previous reading index of rune or < 0
}
+// init the reader to the string `s`
reader_init :: proc(r: ^Reader, s: string) {
r.s = s
r.i = 0
r.prev_rune = -1
}
+// returns a stream from the reader data
reader_to_stream :: proc(r: ^Reader) -> (s: io.Stream) {
s.stream_data = r
s.stream_vtable = _reader_vtable
return
}
+// init a reader to the string `s` and return an io.Reader
to_reader :: proc(r: ^Reader, s: string) -> io.Reader {
reader_init(r, s)
rr, _ := io.to_reader(reader_to_stream(r))
return rr
}
+
+// init a reader to the string `s` and return an io.Reader_At
to_reader_at :: proc(r: ^Reader, s: string) -> io.Reader_At {
reader_init(r, s)
rr, _ := io.to_reader_at(reader_to_stream(r))
return rr
}
+
+// init a reader to the string `s` and return an io.Byte_Reader
to_byte_reader :: proc(r: ^Reader, s: string) -> io.Byte_Reader {
reader_init(r, s)
rr, _ := io.to_byte_reader(reader_to_stream(r))
return rr
}
+
+// init a reader to the string `s` and return an io.Rune_Reader
to_rune_reader :: proc(r: ^Reader, s: string) -> io.Rune_Reader {
reader_init(r, s)
rr, _ := io.to_rune_reader(reader_to_stream(r))
return rr
}
-
+// remaining length of the reader
reader_length :: proc(r: ^Reader) -> int {
if r.i >= i64(len(r.s)) {
return 0
@@ -50,10 +64,13 @@ reader_length :: proc(r: ^Reader) -> int {
return int(i64(len(r.s)) - r.i)
}
+// returns the string length stored by the reader
reader_size :: proc(r: ^Reader) -> i64 {
return i64(len(r.s))
}
+// reads len(p) bytes into the slice from the string in the reader
+// returns `n` amount of read bytes and an io.Error
reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
if r.i >= i64(len(r.s)) {
return 0, .EOF
@@ -63,6 +80,9 @@ reader_read :: proc(r: ^Reader, p: []byte) -> (n: int, err: io.Error) {
r.i += i64(n)
return
}
+
+// 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
reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Error) {
if off < 0 {
return 0, .Invalid_Offset
@@ -76,6 +96,8 @@ reader_read_at :: proc(r: ^Reader, p: []byte, off: i64) -> (n: int, err: io.Erro
}
return
}
+
+// reads and returns a single byte - error when out of bounds
reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.prev_rune = -1
if r.i >= i64(len(r.s)) {
@@ -85,6 +107,8 @@ reader_read_byte :: proc(r: ^Reader) -> (byte, io.Error) {
r.i += 1
return b, nil
}
+
+// decreases the reader offset - error when below 0
reader_unread_byte :: proc(r: ^Reader) -> io.Error {
if r.i <= 0 {
return .Invalid_Unread
@@ -93,6 +117,8 @@ reader_unread_byte :: proc(r: ^Reader) -> io.Error {
r.i -= 1
return nil
}
+
+// 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) {
if r.i >= i64(len(r.s)) {
r.prev_rune = -1
@@ -107,6 +133,9 @@ reader_read_rune :: proc(r: ^Reader) -> (ch: rune, size: int, err: io.Error) {
r.i += i64(size)
return
}
+
+// 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 {
if r.i <= 0 {
return .Invalid_Unread
@@ -118,6 +147,8 @@ reader_unread_rune :: proc(r: ^Reader) -> io.Error {
r.prev_rune = -1
return nil
}
+
+// seeks the reader offset to a wanted offset
reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
r.prev_rune = -1
abs: i64
@@ -138,6 +169,8 @@ reader_seek :: proc(r: ^Reader, offset: i64, whence: io.Seek_From) -> (i64, io.E
r.i = abs
return abs, nil
}
+
+// writes the string content left to read into the io.Writer `w`
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)) {
@@ -157,7 +190,6 @@ reader_write_to :: proc(r: ^Reader, w: io.Writer) -> (n: i64, err: io.Error) {
return
}
-
@(private)
_reader_vtable := &io.Stream_VTable{
impl_size = proc(s: io.Stream) -> i64 {
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index a8199e0cf..a3d9fa93e 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -1,17 +1,21 @@
+// simple 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)+1, allocator, loc)
+ c := make([]byte, len(s), allocator, loc)
copy(c, s)
- c[len(s)] = 0
return string(c[:len(s)])
}
+// 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)
copy(c, s)
@@ -19,27 +23,35 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call
return cstring(&c[0])
}
+// 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 {
return transmute(string)mem.Raw_String{ptr, len}
}
+// returns a string from a byte pointer `ptr and byte length `len`
+// searches for a nul byte from 0.. string {
s := transmute(string)mem.Raw_String{ptr, len}
s = truncate_to_byte(s, 0)
return s
}
-
+// returns the raw ^byte start of the string `str`
ptr_from_string :: proc(str: string) -> ^byte {
d := transmute(mem.Raw_String)str
return d.data
}
+// 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 {
d := transmute(mem.Raw_String)str
return cstring(d.data)
}
+// 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 {
n := index_byte(str, b)
if n < 0 {
@@ -47,6 +59,9 @@ truncate_to_byte :: proc(str: string, b: byte) -> string {
}
return str[:n]
}
+
+// 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 {
n := index_rune(str, r)
if n < 0 {
@@ -55,20 +70,28 @@ truncate_to_rune :: proc(str: string, r: rune) -> string {
return str[:n]
}
+// 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)
copy(c, s)
c[len(s)] = 0
return string(c[:len(s)])
}
+
+// 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 {
return clone(string(s), allocator, loc)
}
+
+// 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 {
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
clone_from :: proc{
clone,
clone_from_bytes,
@@ -76,6 +99,8 @@ clone_from :: proc{
clone_from_ptr,
}
+// 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 {
s := string_from_ptr((^u8)(ptr), len)
s = truncate_to_byte(s, 0)
@@ -83,11 +108,12 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context.
}
// Compares two strings, returning a value representing which one comes first lexiographically.
-// -1 for `a`; 1 for `b`, or 0 if they are equal.
+// -1 for `lhs`; 1 for `rhs`, or 0 if they are equal.
compare :: proc(lhs, rhs: string) -> int {
return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs)
}
+// 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 {
if c == r {
@@ -97,20 +123,48 @@ contains_rune :: proc(s: string, r: rune) -> int {
return -1
}
+/*
+ returns true when the string `substr` is contained inside the string `s`
+
+ strings.contains("testing", "test") -> true
+ strings.contains("testing", "ing") -> true
+ strings.contains("testing", "text") -> false
+*/
contains :: proc(s, substr: string) -> 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
+*/
contains_any :: proc(s, chars: string) -> bool {
return index_any(s, chars) >= 0
}
+/*
+ returns the utf8 rune count of the string `s`
+ strings.rune_count("test") -> 4
+ strings.rune_count("testö") -> 5, where len("testö") -> 6
+*/
rune_count :: proc(s: string) -> 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
+ 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 {
s, t := u, v
loop: for s != "" && t != "" {
@@ -154,15 +208,39 @@ equal_fold :: proc(u, v: string) -> bool {
return s == t
}
+/*
+ return true when the string `prefix` is contained at the start of the string `s`
+
+ 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 {
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
+
+ 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 {
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`
+ 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 ""
@@ -182,6 +260,14 @@ join :: proc(a: []string, sep: string, allocator := context.allocator) -> string
return string(b)
}
+/*
+ returns a combined string from the slice of strings `a` without a seperator
+ allocates the string using the `allocator`
+
+
+ a := [?]string { "a", "b", "c" }
+ b := strings.concatenate(a[:]) -> "abc"
+*/
concatenate :: proc(a: []string, allocator := context.allocator) -> string {
if len(a) == 0 {
return ""
@@ -201,30 +287,56 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string {
/*
`rune_offset` and `rune_length` are in runes, not bytes.
- If `rune_length` <= 0, then it'll return the remainder of the string starting with `rune_offset`.
+ If `rune_length` <= 0, then it'll return the remainder of the string starting at `rune_offset`.
+
+ 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) {
s := s; rune_length := rune_length
- l := utf8.rune_count_in_string(s)
+ context.allocator = allocator
- if rune_offset >= l { return "" }
+ // If we signal that we want the entire remainder (length <= 0) *and*
+ // the offset is zero, then we can early out by cloning the input
if rune_offset == 0 && rune_length <= 0 {
- return clone(s, allocator)
+ return clone(s)
}
- if rune_length == 0 { rune_length = l }
+ // We need to know if we have enough runes to cover offset + length.
+ rune_count := utf8.rune_count_in_string(s)
+
+ // 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 ""
+ }
+
+ // If we don't specify the length of the substring, use the remainder.
+ if rune_length <= 0 {
+ rune_length = rune_count - rune_offset
+ }
+
+ // We don't yet know how many bytes we need exactly.
+ // 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, allocator)
+ buf := make([]u8, bytes_needed)
byte_offset := 0
- for i := 0; i < l; i += 1 {
+ for i := 0; i < rune_count; i += 1 {
_, w := utf8.decode_rune_in_string(s)
+
+ // If the rune is part of the substring, copy it to the output buffer.
if i >= rune_offset {
for j := 0; j < w; j += 1 {
buf[byte_offset+j] = s[j]
}
byte_offset += w
}
+
+ // We're done if we reach the end of the input string, *or*
+ // if we've reached a specified length in runes.
if rune_length > 0 {
if i == rune_offset + rune_length - 1 { break }
}
@@ -281,28 +393,61 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato
return res[:i+1]
}
+/*
+ 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]
+ ```
+*/
split :: proc(s, sep: string, allocator := context.allocator) -> []string {
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]
+ ```
+*/
split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> []string {
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`
+
+ 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 {
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`
+
+ 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 {
return _split(s, sep, len(sep), n, allocator)
}
-
@private
-_split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: string, ok: bool) {
- s, n := s, n
-
- if n == 0 {
+_split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, ok: bool) {
+ // stop once the string is empty or nil
+ if s == nil || len(s^) == 0 {
return
}
@@ -313,51 +458,190 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save, n: int) -> (res: stri
return
}
- if n < 0 {
- n = count(s^, sep) + 1
- }
-
- n -= 1
-
- i := 0
- for ; i < n; i += 1 {
- m := index(s^, sep)
- if m < 0 {
- break
- }
+ m := index(s^, sep)
+ if m < 0 {
+ // not found
+ res = s[:]
+ ok = res != ""
+ s^ = s[len(s):]
+ } else {
res = s[:m+sep_save]
ok = true
s^ = s[m+len(sep):]
- return
}
- res = s[:]
- ok = res != ""
- s^ = s[len(s):]
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
+ 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
+ }
+*/
+split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) {
+ m := index_byte(s^, sep)
+ if m < 0 {
+ // not found
+ res = s[:]
+ ok = res != ""
+ s^ = {}
+ } else {
+ res = s[:m]
+ ok = true
+ s^ = s[m+1:]
+ }
+ return
+}
+
+/*
+ split the ^string `s` by the seperator string `sep` in an iterator fashion
+ consumes the original string till the end
+
+ text := "a.b.c.d.e"
+ for str in strings.split_iterator(&text, ".") {
+ fmt.eprintln(str) // every loop -> a b c d e
+ }
+*/
split_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
- return _split_iterator(s, sep, 0, -1)
+ return _split_iterator(s, sep, 0)
}
-split_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
- return _split_iterator(s, sep, 0, n)
-}
+/*
+ split the ^string `s` after every seperator string `sep` in an iterator fashion
+ consumes the original string till the end
+ text := "a.b.c.d.e"
+ for str in strings.split_after_iterator(&text, ".") {
+ fmt.eprintln(str) // every loop -> a. b. c. d. e
+ }
+*/
split_after_iterator :: proc(s: ^string, sep: string) -> (string, bool) {
- return _split_iterator(s, sep, len(sep), -1)
-}
-
-split_after_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
- return _split_iterator(s, sep, len(sep), n)
+ return _split_iterator(s, sep, len(sep))
}
+@(private)
+_trim_cr :: proc(s: string) -> string {
+ n := len(s)
+ if n > 0 {
+ if s[n-1] == '\r' {
+ return s[:n-1]
+ }
+ }
+ return s
+}
+/*
+ split the string `s` at every line break '\n'
+ return an allocated slice of strings
+ 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 {
+ sep :: "\n"
+ lines := _split(s, sep, 0, -1, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+/*
+ split the string `s` at every line break '\n' for `n` parts
+ return an allocated slice of strings
+ 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 {
+ sep :: "\n"
+ lines := _split(s, sep, 0, n, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+
+/*
+ split the string `s` at every line break '\n' leaving the '\n' in the resulting strings
+ return an allocated slice of strings
+
+ 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 {
+ sep :: "\n"
+ lines := _split(s, sep, len(sep), -1, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+
+/*
+ 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
+
+ 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 {
+ sep :: "\n"
+ lines := _split(s, sep, len(sep), n, allocator)
+ for line in &lines {
+ line = _trim_cr(line)
+ }
+ return lines
+}
+
+/*
+ split the string `s` at every line break '\n'
+ returns the current split string every iteration till 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
+ }
+*/
+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
+
+ 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
+ }
+*/
+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
+
+ 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 {
for i := 0; i < len(s); i += 1 {
if s[i] == c {
@@ -367,7 +651,15 @@ index_byte :: proc(s: string, c: byte) -> int {
return -1
}
-// Returns -1 if c is not present
+/*
+ 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
+
+ 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 {
for i := len(s)-1; i >= 0; i -= 1 {
if s[i] == c {
@@ -378,9 +670,50 @@ last_index_byte :: proc(s: string, c: byte) -> int {
}
+/*
+ returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found
+ avoids invalid runes
+
+ 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 {
+ switch {
+ case 0 <= r && r < utf8.RUNE_SELF:
+ return index_byte(s, byte(r))
+
+ case r == utf8.RUNE_ERROR:
+ for c, i in s {
+ if c == utf8.RUNE_ERROR {
+ return i
+ }
+ }
+ return -1
+
+ case !utf8.valid_rune(r):
+ return -1
+ }
+
+ b, w := utf8.encode_rune(r)
+ return index(s, string(b[:w]))
+}
@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
+*/
index :: proc(s, substr: string) -> int {
hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := 0; i < len(s); i += 1 {
@@ -431,6 +764,14 @@ 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
+*/
last_index :: proc(s, substr: string) -> int {
hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) {
for i := len(s) - 1; i >= 0; i -= 1 {
@@ -479,7 +820,15 @@ last_index :: proc(s, substr: string) -> int {
return -1
}
-// index_any returns the index of the first char of `chars` found in `s`. -1 if not found.
+/*
+ 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
+*/
index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1
@@ -504,14 +853,24 @@ index_any :: proc(s, chars: string) -> int {
}
}
- for c in chars {
- if i := index_rune(s, c); i >= 0 {
+ for c, i in s {
+ if index_rune(chars, c) >= 0 {
return i
}
}
return -1
}
+/*
+ returns the index of any first char of `chars` found in `s`, -1 if not found
+ iterates the string in reverse
+
+ strings.index_any("test", "s") -> 2
+ strings.index_any("test", "se") -> 2
+ strings.index_any("test", "et") -> 1
+ strings.index_any("test", "set") -> 3
+ strings.index_any("test", "x") -> -1
+*/
last_index_any :: proc(s, chars: string) -> int {
if chars == "" {
return -1
@@ -561,6 +920,16 @@ 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`
+
+ strings.count("abbccc", "a") -> 1
+ strings.count("abbccc", "b") -> 2
+ strings.count("abbccc", "c") -> 3
+ strings.count("abbccc", "ab") -> 1
+ strings.count("abbccc", " ") -> 0
+*/
count :: proc(s, substr: string) -> int {
if len(substr) == 0 { // special case
return rune_count(s) + 1
@@ -596,7 +965,12 @@ 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
+ strings.repeat("abc", 2) -> "abcabc"
+*/
repeat :: proc(s: string, count: int, allocator := context.allocator) -> string {
if count < 0 {
panic("strings: negative repeat count")
@@ -613,11 +987,28 @@ repeat :: proc(s: string, count: int, allocator := context.allocator) -> string
return string(b)
}
+/*
+ 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
+
+ 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)
}
-// if n < 0, no limit on the number of replacements
+/*
+ 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
+
+ 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 {
was_allocation = false
@@ -658,17 +1049,35 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
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
+
+ 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
+
+ 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)
}
@(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 {
if r < utf8.RUNE_SELF {
return _ascii_space[u8(r)]
@@ -676,6 +1085,7 @@ is_ascii_space :: proc(r: rune) -> bool {
return false
}
+// returns true when the `r` rune is any asci or utf8 based whitespace
is_space :: proc(r: rune) -> bool {
if r < 0x2000 {
switch r {
@@ -694,10 +1104,24 @@ is_space :: proc(r: rune) -> bool {
return false
}
+// returns true when the `r` rune is a nul byte
is_null :: proc(r: rune) -> 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
+
+ call :: proc(r: rune) -> bool {
+ return r == 'a'
+ }
+ 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
+*/
index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> int {
for r, i in s {
if p(r) == truth {
@@ -707,6 +1131,7 @@ 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 {
for r, i in s {
if p(state, r) == truth {
@@ -716,6 +1141,7 @@ 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 {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
@@ -728,6 +1154,7 @@ 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 {
// TODO(bill): Probably use Rabin-Karp Search
for i := len(s); i > 0; {
@@ -739,7 +1166,17 @@ 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
+ find :: proc(r: rune) -> bool {
+ return r != 'i'
+ }
+ strings.trim_left_proc("testing", find) -> "ing"
+*/
trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := index_proc(s, p, false)
if i == -1 {
@@ -748,29 +1185,10 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
return s[i:]
}
-
-index_rune :: proc(s: string, r: rune) -> int {
- switch {
- case 0 <= r && r < utf8.RUNE_SELF:
- return index_byte(s, byte(r))
-
- case r == utf8.RUNE_ERROR:
- for c, i in s {
- if c == utf8.RUNE_ERROR {
- return i
- }
- }
- return -1
-
- case !utf8.valid_rune(r):
- return -1
- }
-
- b, w := utf8.encode_rune(r)
- return index(s, string(b[:w]))
-}
-
-
+/*
+ trims the input string `s` until the procedure `p` with state returns false
+ returns an empty string when no match was found at all
+*/
trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
i := index_proc_with_state(s, p, state, false)
if i == -1 {
@@ -779,6 +1197,16 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat
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
+
+ find :: proc(r: rune) -> bool {
+ return r != 't'
+ }
+ strings.trim_left_proc("testing", find) -> "test"
+*/
trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> string {
i := last_index_proc(s, p, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
@@ -790,6 +1218,10 @@ 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
+*/
trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr) -> string {
i := last_index_proc_with_state(s, p, state, false)
if i >= 0 && s[i] >= utf8.RUNE_SELF {
@@ -801,7 +1233,7 @@ 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 {
if state == nil {
return false
@@ -815,7 +1247,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> bool {
return false
}
-
+// trims the `cutset` string from the `s` string
trim_left :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s
@@ -824,6 +1256,7 @@ trim_left :: proc(s: string, cutset: string) -> string {
return trim_left_proc_with_state(s, is_in_cutset, &state)
}
+// trims the `cutset` string from the `s` string from the right
trim_right :: proc(s: string, cutset: string) -> string {
if s == "" || cutset == "" {
return s
@@ -832,35 +1265,48 @@ trim_right :: proc(s: string, cutset: string) -> string {
return trim_right_proc_with_state(s, is_in_cutset, &state)
}
+// trims the `cutset` string from the `s` string, both from left and right
trim :: proc(s: string, cutset: string) -> string {
return trim_right(trim_left(s, cutset), cutset)
}
+// trims until a valid non space rune: "\t\txyz\t\t" -> "xyz\t\t"
trim_left_space :: proc(s: string) -> string {
return trim_left_proc(s, is_space)
}
+// trims from the right until a valid non space rune: "\t\txyz\t\t" -> "\t\txyz"
trim_right_space :: proc(s: string) -> string {
return trim_right_proc(s, is_space)
}
+// trims from both sides until a valid non space rune: "\t\txyz\t\t" -> "xyz"
trim_space :: proc(s: string) -> string {
return trim_right_space(trim_left_space(s))
}
-
+// trims nul runes from the left: "\x00\x00testing\x00\x00" -> "testing\x00\x00"
trim_left_null :: proc(s: string) -> string {
return trim_left_proc(s, is_null)
}
+// trims nul runes from the right: "\x00\x00testing\x00\x00" -> "\x00\x00testing"
trim_right_null :: proc(s: string) -> string {
return trim_right_proc(s, is_null)
}
+// trims nul runes from both sides: "\x00\x00testing\x00\x00" -> "testing"
trim_null :: proc(s: string) -> 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
+
+ strings.trim_prefix("testing", "test") -> "ing"
+ strings.trim_prefix("testing", "abc") -> "testing"
+*/
trim_prefix :: proc(s, prefix: string) -> string {
if has_prefix(s, prefix) {
return s[len(prefix):]
@@ -868,6 +1314,13 @@ trim_prefix :: proc(s, prefix: string) -> string {
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
+
+ strings.trim_suffix("todo.txt", ".txt") -> "todo"
+ strings.trim_suffix("todo.doc", ".txt") -> "todo.doc"
+*/
trim_suffix :: proc(s, suffix: string) -> string {
if has_suffix(s, suffix) {
return s[:len(s)-len(suffix)]
@@ -875,142 +1328,151 @@ trim_suffix :: proc(s, suffix: string) -> string {
return s
}
-split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator := context.allocator) -> []string #no_bounds_check {
+/*
+ splits the input string `s` by all possible `substrs` []string
+ returns the allocated []string, nil on any empty substring or no matches
+
+ 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 {
if s == "" || len(substrs) <= 0 {
- return nil
+ return
}
- sublen := len(substrs[0])
-
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ // disallow "" substr
+ for substr in substrs {
+ if len(substr) == 0 {
+ return
+ }
}
- shared := len(s) - sublen
+ // 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)
+ })
- if shared <= 0 {
- return nil
- }
+ substrings_found: int
+ temp := s
- // number, index, last
- n, i, l := 0, 0, 0
-
- // count results
- first_pass: for i <= shared {
- for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- n += 1
- }
-
- i += sublen
- l = i
+ // 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
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- n += 1
+ // skip when no results
+ if substrings_found < 1 {
+ return
}
- if n < 1 {
- // no results
- return nil
- }
+ buf = make([]string, substrings_found + 1, allocator)
+ buf_index: int
+ temp = s
+ temp_old := temp
- buf := make([]string, n, allocator)
-
- n, i, l = 0, 0, 0
-
- // slice results
- second_pass: for i <= shared {
- for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- buf[n] = s[l:i]
- n += 1
- }
-
- i += sublen
- l = i
+ // 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
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- buf[n] = s[l:]
- }
+ 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)
+ })
-
-split_multi_iterator :: proc(s: ^string, substrs: []string, skip_empty := false) -> (string, bool) #no_bounds_check {
- if s == nil || s^ == "" || len(substrs) <= 0 {
- return "", false
+ return {
+ temp = s,
+ temp_old = s,
+ substrs = temp_substrs,
}
+}
- sublen := len(substrs[0])
+/*
+ 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
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ 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]
}
-
- shared := len(s) - sublen
-
- if shared <= 0 {
- return "", false
- }
-
- // index, last
- i, l := 0, 0
-
- loop: for i <= shared {
+*/
+split_multi_iterate :: proc(using sm: ^Split_Multi) -> (res: string, ok: bool) #no_bounds_check {
+ pass: for len(temp) > 0 {
for substr in substrs {
- if s[i:i+sublen] == substr {
- if !skip_empty || i - l > 0 {
- res := s[l:i]
- s^ = s[i:]
- return res, true
- }
+ size := len(substr)
- i += sublen
- l = i
-
- continue loop
+ // 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
}
}
- _, skip := utf8.decode_rune_in_string(s[i:])
- i += skip
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if !skip_empty || len(s) - l > 0 {
- res := s[l:]
- s^ = s[len(s):]
- return res, true
+ // allow last iteration
+ if temp_old != "" {
+ res = temp_old[:]
+ ok = true
+ temp_old = ""
}
- return "", false
+ return
}
-
-
-
-
-
// 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 {
@@ -1045,7 +1507,13 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) ->
return to_string(b)
}
+/*
+ returns a reversed version of the `s` string
+ a := "abcxyz"
+ b := strings.reverse(a)
+ fmt.eprintln(a, b) // abcxyz zyxcba
+*/
reverse :: proc(s: string, allocator := context.allocator) -> string {
str := s
n := len(str)
@@ -1061,12 +1529,19 @@ reverse :: proc(s: string, allocator := context.allocator) -> string {
return string(buf)
}
+/*
+ expands the string to a grid spaced by `tab_size` whenever a `\t` character appears
+ returns the tabbed string, panics on tab_size <= 0
+
+ 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 {
if tab_size <= 0 {
panic("tab size must be positive")
}
-
if s == "" {
return ""
}
@@ -1104,7 +1579,16 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) ->
return to_string(b)
}
+/*
+ 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
+ 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)
if i == -1 {
@@ -1288,8 +1772,102 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc
}
if start >= 0 {
- append(&substrings, s[start : end])
+ append(&substrings, s[start : len(s)])
}
return substrings[:]
}
+
+
+// `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
+fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) {
+ start, end := -1, -1
+ for r, offset in s {
+ end = offset
+ if unicode.is_space(r) {
+ if start >= 0 {
+ field = s[start : end]
+ ok = true
+ s^ = s[end:]
+ return
+ }
+ } else {
+ if start < 0 {
+ start = end
+ }
+ }
+ }
+
+ // if either of these are true, the string did not contain any characters
+ if end < 0 || start < 0 {
+ return "", false
+ }
+
+ field = s[start:]
+ ok = true
+ s^ = s[len(s):]
+ return
+}
+
+// `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 {
+ LEVENSHTEIN_DEFAULT_COSTS: []int : {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
+ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
+ 60, 61, 62, 63,
+ }
+
+ m, n := utf8.rune_count_in_string(a), utf8.rune_count_in_string(b)
+
+ if m == 0 {
+ return n
+ }
+ if n == 0 {
+ return m
+ }
+
+ costs: []int
+
+ if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
+ costs = make([]int, n + 1, allocator)
+ for k in 0..=n {
+ costs[k] = k
+ }
+ } else {
+ costs = LEVENSHTEIN_DEFAULT_COSTS
+ }
+
+ defer if n + 1 > len(LEVENSHTEIN_DEFAULT_COSTS) {
+ delete(costs, allocator)
+ }
+
+ i: int
+ for c1 in a {
+ costs[0] = i + 1
+ corner := i
+ j: int
+ for c2 in b {
+ upper := costs[j + 1]
+ if c1 == c2 {
+ costs[j + 1] = corner
+ } else {
+ t := upper if upper < corner else corner
+ costs[j + 1] = (costs[j] if costs[j] < t else t) + 1
+ }
+
+ corner = upper
+ j += 1
+ }
+
+ i += 1
+ }
+
+ return costs[n]
+}
diff --git a/core/sync/atomic.odin b/core/sync/atomic.odin
index 21dcea178..f537764c4 100644
--- a/core/sync/atomic.odin
+++ b/core/sync/atomic.odin
@@ -2,167 +2,44 @@ package sync
import "core:intrinsics"
-Ordering :: enum {
- Relaxed, // Monotonic
- Release,
- Acquire,
- Acquire_Release,
- Sequentially_Consistent,
-}
-
-strongest_failure_ordering_table := [Ordering]Ordering{
- .Relaxed = .Relaxed,
- .Release = .Relaxed,
- .Acquire = .Acquire,
- .Acquire_Release = .Acquire,
- .Sequentially_Consistent = .Sequentially_Consistent,
-}
-
-strongest_failure_ordering :: #force_inline proc(order: Ordering) -> Ordering {
- return strongest_failure_ordering_table[order]
-}
-
-fence :: #force_inline proc($order: Ordering) {
- when order == .Relaxed { #panic("there is no such thing as a relaxed fence") }
- else when order == .Release { intrinsics.atomic_fence_rel() }
- else when order == .Acquire { intrinsics.atomic_fence_acq() }
- else when order == .Acquire_Release { intrinsics.atomic_fence_acqrel() }
- else when order == .Sequentially_Consistent { intrinsics.atomic_fence() }
- else { #panic("unknown order") }
+cpu_relax :: intrinsics.cpu_relax
+
+/*
+Atomic_Memory_Order :: enum {
+ Relaxed = 0,
+ Consume = 1,
+ Acquire = 2,
+ Release = 3,
+ Acq_Rel = 4,
+ Seq_Cst = 5,
}
+*/
+Atomic_Memory_Order :: intrinsics.Atomic_Memory_Order
-atomic_store :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) {
- when order == .Relaxed { intrinsics.atomic_store_relaxed(dst, val) }
- else when order == .Release { intrinsics.atomic_store_rel(dst, val) }
- else when order == .Sequentially_Consistent { intrinsics.atomic_store(dst, val) }
- else when order == .Acquire { #panic("there is not such thing as an acquire store") }
- else when order == .Acquire_Release { #panic("there is not such thing as an acquire/release store") }
- else { #panic("unknown order") }
-}
-
-atomic_load :: #force_inline proc(dst: ^$T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_load_relaxed(dst) }
- else when order == .Acquire { return intrinsics.atomic_load_acq(dst) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_load(dst) }
- else when order == .Release { #panic("there is no such thing as a release load") }
- else when order == .Acquire_Release { #panic("there is no such thing as an acquire/release load") }
- else { #panic("unknown order") }
-}
-
-atomic_swap :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_xchg_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_xchg_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_xchg_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_xchg_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_xchg(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_compare_exchange :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
- when failure == .Relaxed {
- when success == .Relaxed { return intrinsics.atomic_cxchg_relaxed(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new) }
- else when success == .Acquire_Release { return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new) }
- else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg_failrelaxed(dst, old, new) }
- else when success == .Release { return intrinsics.atomic_cxchg_rel(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire {
- when success == .Release { return intrinsics.atomic_cxchg_acqrel(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchg_acq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Sequentially_Consistent {
- when success == .Sequentially_Consistent { return intrinsics.atomic_cxchg(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire_Release {
- #panic("there is not such thing as an acquire/release failure ordering")
- } else when failure == .Release {
- when success == .Acquire { return instrinsics.atomic_cxchg_failacq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else {
- return T{}, false
- }
-
-}
-
-atomic_compare_exchange_weak :: #force_inline proc(dst: ^$T, old, new: T, $success, $failure: Ordering) -> (val: T, ok: bool) {
- when failure == .Relaxed {
- when success == .Relaxed { return intrinsics.atomic_cxchgweak_relaxed(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq_failrelaxed(dst, old, new) }
- else when success == .Acquire_Release { return intrinsics.atomic_cxchgweak_acqrel_failrelaxed(dst, old, new) }
- else when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak_failrelaxed(dst, old, new) }
- else when success == .Release { return intrinsics.atomic_cxchgweak_rel(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire {
- when success == .Release { return intrinsics.atomic_cxchgweak_acqrel(dst, old, new) }
- else when success == .Acquire { return intrinsics.atomic_cxchgweak_acq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Sequentially_Consistent {
- when success == .Sequentially_Consistent { return intrinsics.atomic_cxchgweak(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else when failure == .Acquire_Release {
- #panic("there is not such thing as an acquire/release failure ordering")
- } else when failure == .Release {
- when success == .Acquire { return intrinsics.atomic_cxchgweak_failacq(dst, old, new) }
- else { #panic("an unknown ordering combination") }
- } else {
- return T{}, false
- }
-
-}
-
-
-atomic_add :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_add_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_add_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_add_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_add_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_add(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_sub :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_sub_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_sub_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_sub_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_sub_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_sub(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_and :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_and_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_and_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_and_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_and_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_and(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_nand :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_nand_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_nand_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_nand_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_nand_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_nand(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_or :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_or_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_or_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_or_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_or_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_or(dst, val) }
- else { #panic("unknown order") }
-}
-
-atomic_xor :: #force_inline proc(dst: ^$T, val: T, $order: Ordering) -> T {
- when order == .Relaxed { return intrinsics.atomic_xor_relaxed(dst, val) }
- else when order == .Release { return intrinsics.atomic_xor_rel(dst, val) }
- else when order == .Acquire { return intrinsics.atomic_xor_acq(dst, val) }
- else when order == .Acquire_Release { return intrinsics.atomic_xor_acqrel(dst, val) }
- else when order == .Sequentially_Consistent { return intrinsics.atomic_xor(dst, val) }
- else { #panic("unknown order") }
-}
+atomic_thread_fence :: intrinsics.atomic_thread_fence
+atomic_signal_fence :: intrinsics.atomic_signal_fence
+atomic_store :: intrinsics.atomic_store
+atomic_store_explicit :: intrinsics.atomic_store_explicit
+atomic_load :: intrinsics.atomic_load
+atomic_load_explicit :: intrinsics.atomic_load_explicit
+atomic_add :: intrinsics.atomic_add
+atomic_add_explicit :: intrinsics.atomic_add_explicit
+atomic_sub :: intrinsics.atomic_sub
+atomic_sub_explicit :: intrinsics.atomic_sub_explicit
+atomic_and :: intrinsics.atomic_and
+atomic_and_explicit :: intrinsics.atomic_and_explicit
+atomic_nand :: intrinsics.atomic_nand
+atomic_nand_explicit :: intrinsics.atomic_nand_explicit
+atomic_or :: intrinsics.atomic_or
+atomic_or_explicit :: intrinsics.atomic_or_explicit
+atomic_xor :: intrinsics.atomic_xor
+atomic_xor_explicit :: intrinsics.atomic_xor_explicit
+atomic_exchange :: intrinsics.atomic_exchange
+atomic_exchange_explicit :: intrinsics.atomic_exchange_explicit
+// Returns value and optional ok boolean
+atomic_compare_exchange_strong :: intrinsics.atomic_compare_exchange_strong
+atomic_compare_exchange_strong_explicit :: intrinsics.atomic_compare_exchange_strong_explicit
+atomic_compare_exchange_weak :: intrinsics.atomic_compare_exchange_weak
+atomic_compare_exchange_weak_explicit :: intrinsics.atomic_compare_exchange_weak_explicit
\ No newline at end of file
diff --git a/core/sync/barrier.odin b/core/sync/barrier.odin
deleted file mode 100644
index 997fde82d..000000000
--- a/core/sync/barrier.odin
+++ /dev/null
@@ -1,81 +0,0 @@
-package sync
-
-
-// A barrier enabling multiple threads to synchronize the beginning of some computation
-/*
- * Example:
- *
- * package example
- *
- * import "core:fmt"
- * import "core:sync"
- * import "core:thread"
- *
- * barrier := &sync.Barrier{};
- *
- * main :: proc() {
- * fmt.println("Start");
- *
- * THREAD_COUNT :: 4;
- * threads: [THREAD_COUNT]^thread.Thread;
- *
- * sync.barrier_init(barrier, THREAD_COUNT);
- * defer sync.barrier_destroy(barrier);
- *
- *
- * for _, i in threads {
- * threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
- * // Same messages will be printed together but without any interleaving
- * fmt.println("Getting ready!");
- * sync.barrier_wait(barrier);
- * fmt.println("Off their marks they go!");
- * });
- * }
- *
- * for t in threads {
- * thread.destroy(t); // join and free thread
- * }
- * fmt.println("Finished");
- * }
- *
- */
-Barrier :: struct {
- mutex: Blocking_Mutex,
- cond: Condition,
- index: int,
- generation_id: int,
- thread_count: int,
-}
-
-barrier_init :: proc(b: ^Barrier, thread_count: int) {
- blocking_mutex_init(&b.mutex)
- condition_init(&b.cond, &b.mutex)
- b.index = 0
- b.generation_id = 0
- b.thread_count = thread_count
-}
-
-barrier_destroy :: proc(b: ^Barrier) {
- blocking_mutex_destroy(&b.mutex)
- condition_destroy(&b.cond)
-}
-
-// Block the current thread until all threads have rendezvoused
-// Barrier can be reused after all threads rendezvoused once, and can be used continuously
-barrier_wait :: proc(b: ^Barrier) -> (is_leader: bool) {
- blocking_mutex_lock(&b.mutex)
- defer blocking_mutex_unlock(&b.mutex)
- local_gen := b.generation_id
- b.index += 1
- if b.index < b.thread_count {
- for local_gen == b.generation_id && b.index < b.thread_count {
- condition_wait_for(&b.cond)
- }
- return false
- }
-
- b.index = 0
- b.generation_id += 1
- condition_broadcast(&b.cond)
- return true
-}
diff --git a/core/sync/channel.odin b/core/sync/channel.odin
deleted file mode 100644
index 82b9504f4..000000000
--- a/core/sync/channel.odin
+++ /dev/null
@@ -1,889 +0,0 @@
-package sync
-
-import "core:mem"
-import "core:time"
-import "core:intrinsics"
-import "core:math/rand"
-
-_, _ :: time, rand
-
-Channel_Direction :: enum i8 {
- Both = 0,
- Send = +1,
- Recv = -1,
-}
-
-Channel :: struct($T: typeid, $Direction := Channel_Direction.Both) {
- using _internal: ^Raw_Channel,
-}
-
-channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
- context.allocator = allocator
- ch._internal = raw_channel_create(size_of(T), align_of(T), cap)
- return
-}
-
-channel_destroy :: proc(ch: $C/Channel($T, $D)) {
- raw_channel_destroy(ch._internal)
-}
-
-channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
- res._internal = ch._internal
- return
-}
-
-channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
- res._internal = ch._internal
- return
-}
-
-
-channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
- return ch._internal.len if ch._internal != nil else 0
-}
-channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
- return ch._internal.cap if ch._internal != nil else 0
-}
-
-
-channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
- msg := msg
- _ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc)
-}
-channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
- msg := msg
- return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc)
-}
-
-channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
- c := ch._internal
- if c == nil {
- panic(message="cannot recv message; channel is nil", loc=loc)
- }
- mutex_lock(&c.mutex)
- raw_channel_recv_impl(c, &msg, loc)
- mutex_unlock(&c.mutex)
- return
-}
-channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
- c := ch._internal
- if c != nil && mutex_try_lock(&c.mutex) {
- if c.len > 0 {
- raw_channel_recv_impl(c, &msg, loc)
- ok = true
- }
- mutex_unlock(&c.mutex)
- }
- return
-}
-channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
- res: T
- res, ok = channel_try_recv(ch, loc)
- if ok && msg != nil {
- msg^ = res
- }
- return
-}
-
-
-channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
- return ch._internal == nil
-}
-channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
- c := ch._internal
- return c != nil && !c.closed
-}
-
-
-channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
- return a._internal == b._internal
-}
-channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
- return a._internal != b._internal
-}
-
-
-channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
- return raw_channel_can_send(ch._internal)
-}
-channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
- return raw_channel_can_recv(ch._internal)
-}
-
-
-channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
- c := ch._internal
- if c == nil {
- return -1
- }
- if intrinsics.atomic_load(&c.closed) {
- return -1
- }
- return intrinsics.atomic_load(&c.len)
-}
-
-
-channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
- raw_channel_close(ch._internal, loc)
-}
-
-
-channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
- c := ch._internal
- if c == nil {
- return
- }
-
- if !c.closed || c.len > 0 {
- msg, ok = channel_recv(ch), true
- }
- return
-}
-channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
- raw_channel_drain(ch._internal)
-}
-
-
-channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
- for msg in channel_iterator(src) {
- channel_send(dst, msg)
- }
-}
-
-
-Raw_Channel_Wait_Queue :: struct {
- next: ^Raw_Channel_Wait_Queue,
- state: ^uintptr,
-}
-
-
-Raw_Channel :: struct {
- closed: bool,
- ready: bool, // ready to recv
- data_offset: u16, // data is stored at the end of this data structure
- elem_size: u32,
- len, cap: int,
- read, write: int,
- mutex: Mutex,
- cond: Condition,
- allocator: mem.Allocator,
-
- sendq: ^Raw_Channel_Wait_Queue,
- recvq: ^Raw_Channel_Wait_Queue,
-}
-
-raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
- val.next = head^
- head^ = val
-}
-raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
- p := head
- for p^ != nil && p^ != val {
- p = &p^.next
- }
- if p != nil {
- p^ = p^.next
- }
-}
-
-
-raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel {
- assert(int(u32(elem_size)) == elem_size)
-
- s := size_of(Raw_Channel)
- s = mem.align_forward_int(s, elem_align)
- data_offset := uintptr(s)
- s += elem_size * max(cap, 1)
-
- a := max(elem_align, align_of(Raw_Channel))
-
- c := (^Raw_Channel)(mem.alloc(s, a))
- if c == nil {
- return nil
- }
-
- c.data_offset = u16(data_offset)
- c.elem_size = u32(elem_size)
- c.len, c.cap = 0, max(cap, 0)
- c.read, c.write = 0, 0
- mutex_init(&c.mutex)
- condition_init(&c.cond, &c.mutex)
- c.allocator = context.allocator
- c.closed = false
-
- return c
-}
-
-
-raw_channel_destroy :: proc(c: ^Raw_Channel) {
- if c == nil {
- return
- }
- context.allocator = c.allocator
- intrinsics.atomic_store(&c.closed, true)
-
- condition_destroy(&c.cond)
- mutex_destroy(&c.mutex)
- free(c)
-}
-
-raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) {
- if c == nil {
- panic(message="cannot close nil channel", loc=loc)
- }
- mutex_lock(&c.mutex)
- defer mutex_unlock(&c.mutex)
- intrinsics.atomic_store(&c.closed, true)
-
- // Release readers and writers
- raw_channel_wait_queue_broadcast(c.recvq)
- raw_channel_wait_queue_broadcast(c.sendq)
- condition_broadcast(&c.cond)
-}
-
-
-
-raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool {
- send :: proc(c: ^Raw_Channel, src: rawptr) {
- data := uintptr(c) + uintptr(c.data_offset)
- dst := data + uintptr(c.write * int(c.elem_size))
- mem.copy(rawptr(dst), src, int(c.elem_size))
- c.len += 1
- c.write = (c.write + 1) % max(c.cap, 1)
- }
-
- switch {
- case c == nil:
- panic(message="cannot send message; channel is nil", loc=loc)
- case c.closed:
- panic(message="cannot send message; channel is closed", loc=loc)
- }
-
- mutex_lock(&c.mutex)
- defer mutex_unlock(&c.mutex)
-
- if c.cap > 0 {
- if !block && c.len >= c.cap {
- return false
- }
-
- for c.len >= c.cap {
- condition_wait_for(&c.cond)
- }
- } else if c.len > 0 { // TODO(bill): determine correct behaviour
- if !block {
- return false
- }
- condition_wait_for(&c.cond)
- } else if c.len == 0 && !block {
- return false
- }
-
- send(c, msg)
- condition_signal(&c.cond)
- raw_channel_wait_queue_signal(c.recvq)
-
- return true
-}
-
-raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) {
- recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) {
- if c.len < 1 {
- panic(message="cannot recv message; channel is empty", loc=loc)
- }
- c.len -= 1
-
- data := uintptr(c) + uintptr(c.data_offset)
- src := data + uintptr(c.read * int(c.elem_size))
- mem.copy(dst, rawptr(src), int(c.elem_size))
- c.read = (c.read + 1) % max(c.cap, 1)
- }
-
- if c == nil {
- panic(message="cannot recv message; channel is nil", loc=loc)
- }
- intrinsics.atomic_store(&c.ready, true)
- for c.len < 1 {
- raw_channel_wait_queue_signal(c.sendq)
- condition_wait_for(&c.cond)
- }
- intrinsics.atomic_store(&c.ready, false)
- recv(c, res, loc)
- if c.cap > 0 {
- if c.len == c.cap - 1 {
- // NOTE(bill): Only signal on the last one
- condition_signal(&c.cond)
- }
- } else {
- condition_signal(&c.cond)
- }
-}
-
-
-raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) {
- if c == nil {
- return false
- }
- mutex_lock(&c.mutex)
- switch {
- case c.closed:
- ok = false
- case c.cap > 0:
- ok = c.ready && c.len < c.cap
- case:
- ok = c.ready && c.len == 0
- }
- mutex_unlock(&c.mutex)
- return
-}
-raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) {
- if c == nil {
- return false
- }
- mutex_lock(&c.mutex)
- ok = c.len > 0
- mutex_unlock(&c.mutex)
- return
-}
-
-
-raw_channel_drain :: proc(c: ^Raw_Channel) {
- if c == nil {
- return
- }
- mutex_lock(&c.mutex)
- c.len = 0
- c.read = 0
- c.write = 0
- mutex_unlock(&c.mutex)
-}
-
-
-
-MAX_SELECT_CHANNELS :: 64
-SELECT_MAX_TIMEOUT :: max(time.Duration)
-
-Select_Command :: enum {
- Recv,
- Send,
-}
-
-Select_Channel :: struct {
- channel: ^Raw_Channel,
- command: Select_Command,
-}
-
-
-
-select :: proc(channels: ..Select_Channel) -> (index: int) {
- return select_timeout(SELECT_MAX_TIMEOUT, ..channels)
-}
-select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
-
- if count == 0 {
- wait_state: uintptr = 0
- for _, i in channels {
- q := &queues[i]
- q.state = &wait_state
- }
-
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- q := &queues[i]
- switch c.command {
- case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q)
- case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q)
- }
- }
- raw_channel_wait_queue_wait_on(&wait_state, timeout)
- for c, i in channels {
- if c.channel == nil {
- continue
- }
- q := &queues[i]
- switch c.command {
- case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q)
- case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q)
- }
- }
-
- for c, i in channels {
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
- if count == 0 && timeout == SELECT_MAX_TIMEOUT {
- index = -1
- return
- }
-
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- msg = channel_recv(channels[index])
-
- return
-}
-
-select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.recvq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.recvq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
-
- if msg != nil {
- channel_send(channels[index], msg)
- }
-
- return
-}
-
-select_send :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
- queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- state: uintptr
- for c, i in channels {
- q := &queues[i]
- q.state = &state
- raw_channel_wait_queue_insert(&c.sendq, q)
- }
- raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT)
- for c, i in channels {
- q := &queues[i]
- raw_channel_wait_queue_remove(&c.sendq, q)
- }
-
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
- assert(count != 0)
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_try :: proc(channels: ..Select_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- panic("sync: select with no channels")
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
-
- backing: [MAX_SELECT_CHANNELS]int
- candidates := backing[:]
- cap := len(channels)
- candidates = candidates[:cap]
-
- count := u32(0)
- for c, i in channels {
- switch c.command {
- case .Recv:
- if raw_channel_can_recv(c.channel) {
- candidates[count] = i
- count += 1
- }
- case .Send:
- if raw_channel_can_send(c.channel) {
- candidates[count] = i
- count += 1
- }
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-
-select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
- switch len(channels) {
- case 0:
- index = -1
- return
- case 1:
- index = -1
- if raw_channel_can_recv(channels[0]) {
- index = 0
- }
- return
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-
-select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check {
- switch len(channels) {
- case 0:
- return -1
- case 1:
- if raw_channel_can_send(channels[0]) {
- return 0
- }
- return -1
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- return
-}
-
-select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
- switch len(channels) {
- case 0:
- index = -1
- return
- case 1:
- ok: bool
- if msg, ok = channel_try_recv(channels[0]); ok {
- index = 0
- }
- return
- }
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if channel_can_recv(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- msg = channel_recv(channels[index])
- return
-}
-
-select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
- index = -1
- switch len(channels) {
- case 0:
- return
- case 1:
- if channel_try_send(channels[0], msg) {
- index = 0
- }
- return
- }
-
-
- assert(len(channels) <= MAX_SELECT_CHANNELS)
- candidates: [MAX_SELECT_CHANNELS]int
-
- count := u32(0)
- for c, i in channels {
- if raw_channel_can_send(c) {
- candidates[count] = i
- count += 1
- }
- }
-
- if count == 0 {
- index = -1
- return
- }
-
- t := time.now()
- r := rand.create(transmute(u64)t)
- i := rand.uint32(&r)
-
- index = candidates[i % count]
- channel_send(channels[index], msg)
- return
-}
-
diff --git a/core/sync/channel_unix.odin b/core/sync/channel_unix.odin
deleted file mode 100644
index d6bac2d71..000000000
--- a/core/sync/channel_unix.odin
+++ /dev/null
@@ -1,16 +0,0 @@
-// +build linux, darwin, freebsd
-package sync
-
-import "core:time"
-
-raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
- // stub
-}
-
-raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
- // stub
-}
-
-raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
- // stub
-}
diff --git a/core/sync/channel_windows.odin b/core/sync/channel_windows.odin
deleted file mode 100644
index 5d469ffff..000000000
--- a/core/sync/channel_windows.odin
+++ /dev/null
@@ -1,33 +0,0 @@
-package sync
-
-import "core:intrinsics"
-import win32 "core:sys/windows"
-import "core:time"
-
-raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
- ms: win32.DWORD = win32.INFINITE
- if max(time.Duration) != SELECT_MAX_TIMEOUT {
- ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000)
- }
-
- v := intrinsics.atomic_load(state)
- for v == 0 {
- win32.WaitOnAddress(state, &v, size_of(state^), ms)
- v = intrinsics.atomic_load(state)
- }
- intrinsics.atomic_store(state, 0)
-}
-
-raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
- for x := q; x != nil; x = x.next {
- intrinsics.atomic_add(x.state, 1)
- win32.WakeByAddressSingle(x.state)
- }
-}
-
-raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
- for x := q; x != nil; x = x.next {
- intrinsics.atomic_add(x.state, 1)
- win32.WakeByAddressAll(x.state)
- }
-}
diff --git a/core/sync/sync2/extended.odin b/core/sync/extended.odin
similarity index 73%
rename from core/sync/sync2/extended.odin
rename to core/sync/extended.odin
index d6a99fe04..2cca6f961 100644
--- a/core/sync/sync2/extended.odin
+++ b/core/sync/extended.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -67,44 +67,41 @@ wait_group_wait_with_timeout :: proc(wg: ^Wait_Group, duration: time.Duration) -
-// A barrier enabling multiple threads to synchronize the beginning of some computation
/*
- * Example:
- *
- * package example
- *
- * import "core:fmt"
- * import "core:sync"
- * import "core:thread"
- *
- * barrier := &sync.Barrier{}
- *
- * main :: proc() {
- * fmt.println("Start")
- *
- * THREAD_COUNT :: 4
- * threads: [THREAD_COUNT]^thread.Thread
- *
- * sync.barrier_init(barrier, THREAD_COUNT)
- * defer sync.barrier_destroy(barrier)
- *
- *
- * for _, i in threads {
- * threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
- * // Same messages will be printed together but without any interleaving
- * fmt.println("Getting ready!")
- * sync.barrier_wait(barrier)
- * fmt.println("Off their marks they go!")
- * })
- * }
- *
- * for t in threads {
- * thread.destroy(t) // join and free thread
- * }
- * fmt.println("Finished")
- * }
- *
- */
+A barrier enabling multiple threads to synchronize the beginning of some computation
+
+Example:
+ package example
+
+ import "core:fmt"
+ import "core:sync"
+ import "core:thread"
+
+ barrier := &sync.Barrier{}
+
+ main :: proc() {
+ fmt.println("Start")
+
+ THREAD_COUNT :: 4
+ threads: [THREAD_COUNT]^thread.Thread
+
+ sync.barrier_init(barrier, THREAD_COUNT)
+
+ for _, i in threads {
+ threads[i] = thread.create_and_start(proc(t: ^thread.Thread) {
+ // Same messages will be printed together but without any interleaving
+ fmt.println("Getting ready!")
+ sync.barrier_wait(barrier)
+ fmt.println("Off their marks they go!")
+ })
+ }
+
+ for t in threads {
+ thread.destroy(t) // join and free thread
+ }
+ fmt.println("Finished")
+ }
+*/
Barrier :: struct {
mutex: Mutex,
cond: Cond,
@@ -149,10 +146,10 @@ Auto_Reset_Event :: struct {
}
auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
- old_status := atomic_load_relaxed(&e.status)
+ old_status := atomic_load_explicit(&e.status, .Relaxed)
for {
new_status := old_status + 1 if old_status < 1 else 1
- if _, ok := atomic_compare_exchange_weak_release(&e.status, old_status, new_status); ok {
+ if _, ok := atomic_compare_exchange_weak_explicit(&e.status, old_status, new_status, .Release, .Relaxed); ok {
break
}
@@ -163,7 +160,7 @@ auto_reset_event_signal :: proc(e: ^Auto_Reset_Event) {
}
auto_reset_event_wait :: proc(e: ^Auto_Reset_Event) {
- old_status := atomic_sub_acquire(&e.status, 1)
+ old_status := atomic_sub_explicit(&e.status, 1, .Acquire)
if old_status < 1 {
sema_wait(&e.sema)
}
@@ -177,14 +174,14 @@ Ticket_Mutex :: struct {
}
ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
- ticket := atomic_add_relaxed(&m.ticket, 1)
- for ticket != atomic_load_acquire(&m.serving) {
+ ticket := atomic_add_explicit(&m.ticket, 1, .Relaxed)
+ for ticket != atomic_load_explicit(&m.serving, .Acquire) {
cpu_relax()
}
}
ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
- atomic_add_relaxed(&m.serving, 1)
+ atomic_add_explicit(&m.serving, 1, .Relaxed)
}
@(deferred_in=ticket_mutex_unlock)
ticket_mutex_guard :: proc(m: ^Ticket_Mutex) -> bool {
@@ -199,18 +196,18 @@ Benaphore :: struct {
}
benaphore_lock :: proc(b: ^Benaphore) {
- if atomic_add_acquire(&b.counter, 1) > 1 {
+ if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
sema_wait(&b.sema)
}
}
benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
- v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0)
+ v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire)
return v == 0
}
benaphore_unlock :: proc(b: ^Benaphore) {
- if atomic_sub_release(&b.counter, 1) > 0 {
+ if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
sema_post(&b.sema)
}
}
@@ -230,7 +227,7 @@ Recursive_Benaphore :: struct {
recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
tid := current_thread_id()
- if atomic_add_acquire(&b.counter, 1) > 1 {
+ if atomic_add_explicit(&b.counter, 1, .Acquire) > 1 {
if tid != b.owner {
sema_wait(&b.sema)
}
@@ -243,10 +240,10 @@ recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
tid := current_thread_id()
if b.owner == tid {
- atomic_add_acquire(&b.counter, 1)
+ atomic_add_explicit(&b.counter, 1, .Acquire)
}
- if v, _ := atomic_compare_exchange_strong_acquire(&b.counter, 1, 0); v != 0 {
+ if v, _ := atomic_compare_exchange_strong_explicit(&b.counter, 0, 1, .Acquire, .Acquire); v != 0 {
return false
}
// inside the lock
@@ -263,7 +260,7 @@ recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
if recursion == 0 {
b.owner = 0
}
- if atomic_sub_release(&b.counter, 1) > 0 {
+ if atomic_sub_explicit(&b.counter, 1, .Release) > 0 {
if recursion == 0 {
sema_post(&b.sema)
}
@@ -296,12 +293,12 @@ once_do :: proc(o: ^Once, fn: proc()) {
defer mutex_unlock(&o.m)
if !o.done {
fn()
- atomic_store_release(&o.done, true)
+ atomic_store_explicit(&o.done, true, .Release)
}
}
- if atomic_load_acquire(&o.done) == false {
+ if atomic_load_explicit(&o.done, .Acquire) == false {
do_slow(o, fn)
}
}
diff --git a/core/sync/sync2/futex_darwin.odin b/core/sync/futex_darwin.odin
similarity index 99%
rename from core/sync/sync2/futex_darwin.odin
rename to core/sync/futex_darwin.odin
index 9dad8d375..88e354827 100644
--- a/core/sync/sync2/futex_darwin.odin
+++ b/core/sync/futex_darwin.odin
@@ -1,6 +1,6 @@
//+private
//+build darwin
-package sync2
+package sync
import "core:c"
import "core:time"
diff --git a/core/sync/futex_freebsd.odin b/core/sync/futex_freebsd.odin
new file mode 100644
index 000000000..2e1d065bc
--- /dev/null
+++ b/core/sync/futex_freebsd.odin
@@ -0,0 +1,75 @@
+//+private
+//+build freebsd
+package sync
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+UMTX_OP_WAIT :: 2
+UMTX_OP_WAKE :: 3
+
+foreign import libc "system:c"
+
+foreign libc {
+ _umtx_op :: proc "c" (obj: rawptr, op: c.int, val: c.ulong, uaddr: rawptr, uaddr2: rawptr) -> c.int ---
+}
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+ timeout := os.Unix_File_Time{
+ seconds = 5,
+ nanoseconds = 0,
+ }
+
+ for {
+ res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &timeout)
+
+ if res != -1 {
+ return true
+ }
+
+ if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ continue
+ }
+
+ panic("_futex_wait failure")
+ }
+ unreachable()
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+ if duration <= 0 {
+ return false
+ }
+
+ res := _umtx_op(f, UMTX_OP_WAIT, c.ulong(expected), nil, &os.Unix_File_Time{
+ seconds = (os.time_t)(duration/1e9),
+ nanoseconds = (c.long)(duration%1e9),
+ })
+
+ if res != -1 {
+ return true
+ }
+
+ if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ return false
+ }
+
+ panic("_futex_wait_with_timeout failure")
+}
+
+_futex_signal :: proc(f: ^Futex) {
+ res := _umtx_op(f, UMTX_OP_WAKE, 1, nil, nil)
+
+ if res == -1 {
+ panic("_futex_signal failure")
+ }
+}
+
+_futex_broadcast :: proc(f: ^Futex) {
+ res := _umtx_op(f, UMTX_OP_WAKE, c.ulong(max(i32)), nil, nil)
+
+ if res == -1 {
+ panic("_futex_broadcast failure")
+ }
+}
diff --git a/core/sync/sync2/futex_linux.odin b/core/sync/futex_linux.odin
similarity index 94%
rename from core/sync/sync2/futex_linux.odin
rename to core/sync/futex_linux.odin
index fca28cace..c429a9d64 100644
--- a/core/sync/sync2/futex_linux.odin
+++ b/core/sync/futex_linux.odin
@@ -1,6 +1,6 @@
//+private
//+build linux
-package sync2
+package sync
import "core:c"
import "core:time"
@@ -14,12 +14,6 @@ FUTEX_PRIVATE_FLAG :: 128
FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
-foreign import libc "system:c"
-
-foreign libc {
- __errno_location :: proc "c" () -> ^c.int ---
-}
-
ESUCCESS :: 0
EINTR :: -4
EAGAIN :: -11
diff --git a/core/sync/futex_openbsd.odin b/core/sync/futex_openbsd.odin
new file mode 100644
index 000000000..6ac9d3efb
--- /dev/null
+++ b/core/sync/futex_openbsd.odin
@@ -0,0 +1,78 @@
+//+private
+//+build openbsd
+package sync
+
+import "core:c"
+import "core:os"
+import "core:time"
+
+FUTEX_WAIT :: 1
+FUTEX_WAKE :: 2
+
+FUTEX_PRIVATE_FLAG :: 128
+
+FUTEX_WAIT_PRIVATE :: (FUTEX_WAIT | FUTEX_PRIVATE_FLAG)
+FUTEX_WAKE_PRIVATE :: (FUTEX_WAKE | FUTEX_PRIVATE_FLAG)
+
+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 ---
+}
+
+_futex_wait :: proc(f: ^Futex, expected: u32) -> bool {
+ res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, nil)
+
+ if res != -1 {
+ return true
+ }
+
+ if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ return false
+ }
+
+ panic("futex_wait failure")
+}
+
+_futex_wait_with_timeout :: proc(f: ^Futex, expected: u32, duration: time.Duration) -> bool {
+ if duration <= 0 {
+ return false
+ }
+
+ timespec_t :: struct {
+ tv_sec: c.long,
+ tv_nsec: c.long,
+ }
+
+ res := _unix_futex(f, FUTEX_WAIT_PRIVATE, expected, ×pec_t{
+ tv_sec = (c.long)(duration/1e9),
+ tv_nsec = (c.long)(duration%1e9),
+ })
+
+ if res != -1 {
+ return true
+ }
+
+ if os.Errno(os.get_last_error()) == os.ETIMEDOUT {
+ return false
+ }
+
+ panic("futex_wait_with_timeout failure")
+}
+
+_futex_signal :: proc(f: ^Futex) {
+ res := _unix_futex(f, FUTEX_WAKE_PRIVATE, 1, nil)
+
+ if res == -1 {
+ panic("futex_wake_single failure")
+ }
+}
+
+_futex_broadcast :: proc(f: ^Futex) {
+ res := _unix_futex(f, FUTEX_WAKE_PRIVATE, u32(max(i32)), nil)
+
+ if res == -1 {
+ panic("_futex_wake_all failure")
+ }
+}
diff --git a/core/sync/sync2/futex_windows.odin b/core/sync/futex_windows.odin
similarity index 98%
rename from core/sync/sync2/futex_windows.odin
rename to core/sync/futex_windows.odin
index 200a119ff..1c9d8b845 100644
--- a/core/sync/sync2/futex_windows.odin
+++ b/core/sync/futex_windows.odin
@@ -1,6 +1,6 @@
//+private
//+build windows
-package sync2
+package sync
import "core:time"
diff --git a/core/sync/sync2/primitives.odin b/core/sync/primitives.odin
similarity index 94%
rename from core/sync/sync2/primitives.odin
rename to core/sync/primitives.odin
index d9e013664..483f85343 100644
--- a/core/sync/sync2/primitives.odin
+++ b/core/sync/primitives.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -29,12 +29,12 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool {
return _mutex_try_lock(m)
}
-// Example:
-//
-// if mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=mutex_unlock)
mutex_guard :: proc(m: ^Mutex) -> bool {
mutex_lock(m)
@@ -80,25 +80,24 @@ rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
return _rw_mutex_try_shared_lock(rw)
}
-
-// Example:
-//
-// if rw_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if rw_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=rw_mutex_unlock)
rw_mutex_guard :: proc(m: ^RW_Mutex) -> bool {
rw_mutex_lock(m)
return true
}
-// Example:
-//
-// if rw_mutex_shared_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if rw_mutex_shared_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=rw_mutex_shared_unlock)
rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
rw_mutex_shared_lock(m)
@@ -127,13 +126,12 @@ recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
return _recursive_mutex_try_lock(m)
}
-
-// Example:
-//
-// if recursive_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if recursive_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=recursive_mutex_unlock)
recursive_mutex_guard :: proc(m: ^Recursive_Mutex) -> bool {
recursive_mutex_lock(m)
diff --git a/core/sync/sync2/primitives_atomic.odin b/core/sync/primitives_atomic.odin
similarity index 91%
rename from core/sync/sync2/primitives_atomic.odin
rename to core/sync/primitives_atomic.odin
index a13b73a99..11fff4e60 100644
--- a/core/sync/sync2/primitives_atomic.odin
+++ b/core/sync/primitives_atomic.odin
@@ -1,4 +1,4 @@
-package sync2
+package sync
import "core:time"
@@ -24,7 +24,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
new_state := curr_state // Make a copy of it
spin_lock: for spin in 0.. bool {
- _, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked)
+ _, ok := atomic_compare_exchange_strong_explicit(&m.state, .Unlocked, .Locked, .Acquire, .Consume)
return ok
}
-
-// Example:
-//
-// if atomic_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_mutex_unlock)
atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
atomic_mutex_lock(m)
@@ -193,25 +191,24 @@ atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
return false
}
-
-// Example:
-//
-// if atomic_rw_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_rw_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_rw_mutex_unlock)
atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_lock(m)
return true
}
-// Example:
-//
-// if atomic_rw_mutex_shared_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_rw_mutex_shared_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_rw_mutex_shared_unlock)
atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_shared_lock(m)
@@ -270,13 +267,12 @@ atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
return true
}
-
-// Example:
-//
-// if atomic_recursive_mutex_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if atomic_recursive_mutex_guard(&m) {
+ ...
+ }
+*/
@(deferred_in=atomic_recursive_mutex_unlock)
atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_lock(m)
@@ -294,7 +290,7 @@ Queue_Item :: struct {
@(private="file")
queue_item_wait :: proc(item: ^Queue_Item) {
- for atomic_load_acquire(&item.futex) == 0 {
+ for atomic_load_explicit(&item.futex, .Acquire) == 0 {
futex_wait(&item.futex, 0)
cpu_relax()
}
@@ -302,7 +298,7 @@ queue_item_wait :: proc(item: ^Queue_Item) {
@(private="file")
queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration) -> bool {
start := time.tick_now()
- for atomic_load_acquire(&item.futex) == 0 {
+ for atomic_load_explicit(&item.futex, .Acquire) == 0 {
remaining := duration - time.tick_since(start)
if remaining < 0 {
return false
@@ -316,7 +312,7 @@ queue_item_wait_with_timeout :: proc(item: ^Queue_Item, duration: time.Duration)
}
@(private="file")
queue_item_signal :: proc(item: ^Queue_Item) {
- atomic_store_release(&item.futex, 1)
+ atomic_store_explicit(&item.futex, 1, .Release)
futex_signal(&item.futex)
}
diff --git a/core/sync/sync2/primitives_darwin.odin b/core/sync/primitives_darwin.odin
similarity index 98%
rename from core/sync/sync2/primitives_darwin.odin
rename to core/sync/primitives_darwin.odin
index 66995bd01..514f66f3e 100644
--- a/core/sync/sync2/primitives_darwin.odin
+++ b/core/sync/primitives_darwin.odin
@@ -1,6 +1,6 @@
//+build darwin
//+private
-package sync2
+package sync
import "core:c"
import "core:time"
diff --git a/core/sync/primitives_freebsd.odin b/core/sync/primitives_freebsd.odin
new file mode 100644
index 000000000..b88fca181
--- /dev/null
+++ b/core/sync/primitives_freebsd.odin
@@ -0,0 +1,46 @@
+//+build freebsd
+//+private
+package sync
+
+import "core:os"
+import "core:time"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return os.current_thread_id()
+}
+
+_Mutex :: struct {
+ mutex: Atomic_Mutex,
+}
+
+_mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+}
+
+_mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+}
+
+_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+}
+
+_Cond :: struct {
+ cond: Atomic_Cond,
+}
+
+_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+}
+
+_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+}
+
+_cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+}
+
+_cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+}
diff --git a/core/sync/primitives_internal.odin b/core/sync/primitives_internal.odin
new file mode 100644
index 000000000..de9aca991
--- /dev/null
+++ b/core/sync/primitives_internal.odin
@@ -0,0 +1,125 @@
+//+private
+package sync
+
+when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
+ _Recursive_Mutex :: struct {
+ owner: Futex,
+ recursion: i32,
+ }
+
+ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+ tid := Futex(current_thread_id())
+ for {
+ prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
+ switch prev_owner {
+ case 0, tid:
+ m.impl.recursion += 1
+ // inside the lock
+ return
+ }
+
+ futex_wait(&m.impl.owner, u32(prev_owner))
+ }
+ }
+
+ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+ m.impl.recursion -= 1
+ if m.impl.recursion != 0 {
+ return
+ }
+ atomic_exchange_explicit(&m.impl.owner, 0, .Release)
+
+ futex_signal(&m.impl.owner)
+ // outside the lock
+
+ }
+
+ _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+ tid := Futex(current_thread_id())
+ prev_owner := atomic_compare_exchange_strong_explicit(&m.impl.owner, 0, tid, .Acquire, .Acquire)
+ switch prev_owner {
+ case 0, tid:
+ m.impl.recursion += 1
+ // inside the lock
+ return true
+ }
+ return false
+ }
+} else {
+ _Recursive_Mutex :: struct {
+ owner: int,
+ recursion: int,
+ mutex: Mutex,
+ }
+
+ _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
+ tid := current_thread_id()
+ if tid != m.impl.owner {
+ mutex_lock(&m.impl.mutex)
+ }
+ // inside the lock
+ m.impl.owner = tid
+ m.impl.recursion += 1
+ }
+
+ _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
+ tid := current_thread_id()
+ assert(tid == m.impl.owner)
+ m.impl.recursion -= 1
+ recursion := m.impl.recursion
+ if recursion == 0 {
+ m.impl.owner = 0
+ }
+ if recursion == 0 {
+ mutex_unlock(&m.impl.mutex)
+ }
+ // outside the lock
+
+ }
+
+ _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
+ tid := current_thread_id()
+ if m.impl.owner == tid {
+ return mutex_try_lock(&m.impl.mutex)
+ }
+ if !mutex_try_lock(&m.impl.mutex) {
+ return false
+ }
+ // inside the lock
+ m.impl.owner = tid
+ m.impl.recursion += 1
+ return true
+ }
+}
+
+
+when ODIN_OS != .Windows {
+ _RW_Mutex :: struct {
+ mutex: Atomic_RW_Mutex,
+ }
+
+ _rw_mutex_lock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_unlock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
+ return atomic_rw_mutex_try_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_shared_lock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
+ atomic_rw_mutex_shared_unlock(&rw.impl.mutex)
+ }
+
+ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
+ return atomic_rw_mutex_try_shared_lock(&rw.impl.mutex)
+ }
+
+}
\ No newline at end of file
diff --git a/core/sync/primitives_linux.odin b/core/sync/primitives_linux.odin
new file mode 100644
index 000000000..0a9f0cc33
--- /dev/null
+++ b/core/sync/primitives_linux.odin
@@ -0,0 +1,47 @@
+//+build linux
+//+private
+package sync
+
+import "core:sys/unix"
+import "core:time"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return unix.sys_gettid()
+}
+
+
+_Mutex :: struct {
+ mutex: Atomic_Mutex,
+}
+
+_mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+}
+
+_mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+}
+
+_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+}
+
+_Cond :: struct {
+ cond: Atomic_Cond,
+}
+
+_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+}
+
+_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+}
+
+_cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+}
+
+_cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+}
diff --git a/core/sync/primitives_openbsd.odin b/core/sync/primitives_openbsd.odin
new file mode 100644
index 000000000..7794016f8
--- /dev/null
+++ b/core/sync/primitives_openbsd.odin
@@ -0,0 +1,46 @@
+//+build openbsd
+//+private
+package sync
+
+import "core:os"
+import "core:time"
+
+_current_thread_id :: proc "contextless" () -> int {
+ return os.current_thread_id()
+}
+
+_Mutex :: struct {
+ mutex: Atomic_Mutex,
+}
+
+_mutex_lock :: proc(m: ^Mutex) {
+ atomic_mutex_lock(&m.impl.mutex)
+}
+
+_mutex_unlock :: proc(m: ^Mutex) {
+ atomic_mutex_unlock(&m.impl.mutex)
+}
+
+_mutex_try_lock :: proc(m: ^Mutex) -> bool {
+ return atomic_mutex_try_lock(&m.impl.mutex)
+}
+
+_Cond :: struct {
+ cond: Atomic_Cond,
+}
+
+_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
+ atomic_cond_wait(&c.impl.cond, &m.impl.mutex)
+}
+
+_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
+ return atomic_cond_wait_with_timeout(&c.impl.cond, &m.impl.mutex, duration)
+}
+
+_cond_signal :: proc(c: ^Cond) {
+ atomic_cond_signal(&c.impl.cond)
+}
+
+_cond_broadcast :: proc(c: ^Cond) {
+ atomic_cond_broadcast(&c.impl.cond)
+}
diff --git a/core/sync/sync2/primitives_windows.odin b/core/sync/primitives_windows.odin
similarity index 99%
rename from core/sync/sync2/primitives_windows.odin
rename to core/sync/primitives_windows.odin
index ca7a5c9ee..055167892 100644
--- a/core/sync/sync2/primitives_windows.odin
+++ b/core/sync/primitives_windows.odin
@@ -1,6 +1,6 @@
//+build windows
//+private
-package sync2
+package sync
import "core:time"
import win32 "core:sys/windows"
diff --git a/core/sync/sync2/sema_internal.odin b/core/sync/sema_internal.odin
similarity index 71%
rename from core/sync/sync2/sema_internal.odin
rename to core/sync/sema_internal.odin
index 64fc4ed96..e4a3c0bfc 100644
--- a/core/sync/sync2/sema_internal.odin
+++ b/core/sync/sema_internal.odin
@@ -1,4 +1,5 @@
-package sync2
+//+private
+package sync
import "core:time"
@@ -9,7 +10,7 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
}
_sema_post :: proc(s: ^Sema, count := 1) {
- atomic_add(&s.impl.count, Futex(count))
+ atomic_add_explicit(&s.impl.count, Futex(count), .Release)
if count == 1 {
futex_signal(&s.impl.count)
} else {
@@ -19,12 +20,12 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
_sema_wait :: proc(s: ^Sema) {
for {
- original_count := atomic_load(&s.impl.count)
+ original_count := atomic_load_explicit(&s.impl.count, .Relaxed)
for original_count == 0 {
futex_wait(&s.impl.count, u32(original_count))
original_count = s.impl.count
}
- if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
+ if original_count == atomic_compare_exchange_strong_explicit(&s.impl.count, original_count, original_count-1, .Acquire, .Acquire) {
return
}
}
@@ -36,7 +37,7 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
}
for {
- original_count := atomic_load(&s.impl.count)
+ original_count := atomic_load_explicit(&s.impl.count, .Relaxed)
for start := time.tick_now(); original_count == 0; /**/ {
remaining := duration - time.tick_since(start)
if remaining < 0 {
@@ -48,7 +49,7 @@ when #config(ODIN_SYNC_SEMA_USE_FUTEX, true) {
}
original_count = s.impl.count
}
- if original_count == atomic_compare_exchange_strong(&s.impl.count, original_count-1, original_count) {
+ if original_count == atomic_compare_exchange_strong_explicit(&s.impl.count, original_count, original_count-1, .Acquire, .Acquire) {
return true
}
}
diff --git a/core/sync/sync.odin b/core/sync/sync.odin
deleted file mode 100644
index 05c86a868..000000000
--- a/core/sync/sync.odin
+++ /dev/null
@@ -1,123 +0,0 @@
-package sync
-
-import "core:intrinsics"
-
-cpu_relax :: #force_inline proc "contextless" () {
- intrinsics.cpu_relax()
-}
-
-Condition_Mutex_Ptr :: union{^Mutex, ^Blocking_Mutex}
-
-
-Ticket_Mutex :: struct {
- ticket: u64,
- serving: u64,
-}
-
-ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
- atomic_store(&m.ticket, 0, .Relaxed)
- atomic_store(&m.serving, 0, .Relaxed)
-}
-
-ticket_mutex_lock :: #force_inline proc(m: ^Ticket_Mutex) {
- ticket := atomic_add(&m.ticket, 1, .Relaxed)
- for ticket != atomic_load(&m.serving, .Acquire) {
- intrinsics.cpu_relax()
- }
-}
-
-ticket_mutex_unlock :: #force_inline proc(m: ^Ticket_Mutex) {
- atomic_add(&m.serving, 1, .Relaxed)
-}
-
-
-Benaphore :: struct {
- counter: int,
- sema: Semaphore,
-}
-
-benaphore_init :: proc(b: ^Benaphore) {
- intrinsics.atomic_store(&b.counter, 0)
- semaphore_init(&b.sema)
-}
-
-benaphore_destroy :: proc(b: ^Benaphore) {
- semaphore_destroy(&b.sema)
-}
-
-benaphore_lock :: proc(b: ^Benaphore) {
- if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
- semaphore_wait_for(&b.sema)
- }
-}
-
-benaphore_try_lock :: proc(b: ^Benaphore) -> bool {
- v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
- return v == 0
-}
-
-benaphore_unlock :: proc(b: ^Benaphore) {
- if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
- semaphore_post(&b.sema)
- }
-}
-
-Recursive_Benaphore :: struct {
- counter: int,
- owner: int,
- recursion: int,
- sema: Semaphore,
-}
-
-recursive_benaphore_init :: proc(b: ^Recursive_Benaphore) {
- intrinsics.atomic_store(&b.counter, 0)
- semaphore_init(&b.sema)
-}
-
-recursive_benaphore_destroy :: proc(b: ^Recursive_Benaphore) {
- semaphore_destroy(&b.sema)
-}
-
-recursive_benaphore_lock :: proc(b: ^Recursive_Benaphore) {
- tid := current_thread_id()
- if intrinsics.atomic_add_acq(&b.counter, 1) > 1 {
- if tid != b.owner {
- semaphore_wait_for(&b.sema)
- }
- }
- // inside the lock
- b.owner = tid
- b.recursion += 1
-}
-
-recursive_benaphore_try_lock :: proc(b: ^Recursive_Benaphore) -> bool {
- tid := current_thread_id()
- if b.owner == tid {
- intrinsics.atomic_add_acq(&b.counter, 1)
- } else {
- v, _ := intrinsics.atomic_cxchg_acq(&b.counter, 1, 0)
- if v != 0 {
- return false
- }
- // inside the lock
- b.owner = tid
- }
- b.recursion += 1
- return true
-}
-
-recursive_benaphore_unlock :: proc(b: ^Recursive_Benaphore) {
- tid := current_thread_id()
- assert(tid == b.owner)
- b.recursion -= 1
- recursion := b.recursion
- if recursion == 0 {
- b.owner = 0
- }
- if intrinsics.atomic_sub_rel(&b.counter, 1) > 0 {
- if recursion == 0 {
- semaphore_post(&b.sema)
- }
- }
- // outside the lock
-}
diff --git a/core/sync/sync2/atomic.odin b/core/sync/sync2/atomic.odin
deleted file mode 100644
index fe19f17c8..000000000
--- a/core/sync/sync2/atomic.odin
+++ /dev/null
@@ -1,79 +0,0 @@
-package sync2
-
-import "core:intrinsics"
-
-cpu_relax :: intrinsics.cpu_relax
-
-atomic_fence :: intrinsics.atomic_fence
-atomic_fence_acquire :: intrinsics.atomic_fence_acq
-atomic_fence_release :: intrinsics.atomic_fence_rel
-atomic_fence_acqrel :: intrinsics.atomic_fence_acqrel
-
-atomic_store :: intrinsics.atomic_store
-atomic_store_release :: intrinsics.atomic_store_rel
-atomic_store_relaxed :: intrinsics.atomic_store_relaxed
-atomic_store_unordered :: intrinsics.atomic_store_unordered
-
-atomic_load :: intrinsics.atomic_load
-atomic_load_acquire :: intrinsics.atomic_load_acq
-atomic_load_relaxed :: intrinsics.atomic_load_relaxed
-atomic_load_unordered :: intrinsics.atomic_load_unordered
-
-atomic_add :: intrinsics.atomic_add
-atomic_add_acquire :: intrinsics.atomic_add_acq
-atomic_add_release :: intrinsics.atomic_add_rel
-atomic_add_acqrel :: intrinsics.atomic_add_acqrel
-atomic_add_relaxed :: intrinsics.atomic_add_relaxed
-atomic_sub :: intrinsics.atomic_sub
-atomic_sub_acquire :: intrinsics.atomic_sub_acq
-atomic_sub_release :: intrinsics.atomic_sub_rel
-atomic_sub_acqrel :: intrinsics.atomic_sub_acqrel
-atomic_sub_relaxed :: intrinsics.atomic_sub_relaxed
-atomic_and :: intrinsics.atomic_and
-atomic_and_acquire :: intrinsics.atomic_and_acq
-atomic_and_release :: intrinsics.atomic_and_rel
-atomic_and_acqrel :: intrinsics.atomic_and_acqrel
-atomic_and_relaxed :: intrinsics.atomic_and_relaxed
-atomic_nand :: intrinsics.atomic_nand
-atomic_nand_acquire :: intrinsics.atomic_nand_acq
-atomic_nand_release :: intrinsics.atomic_nand_rel
-atomic_nand_acqrel :: intrinsics.atomic_nand_acqrel
-atomic_nand_relaxed :: intrinsics.atomic_nand_relaxed
-atomic_or :: intrinsics.atomic_or
-atomic_or_acquire :: intrinsics.atomic_or_acq
-atomic_or_release :: intrinsics.atomic_or_rel
-atomic_or_acqrel :: intrinsics.atomic_or_acqrel
-atomic_or_relaxed :: intrinsics.atomic_or_relaxed
-atomic_xor :: intrinsics.atomic_xor
-atomic_xor_acquire :: intrinsics.atomic_xor_acq
-atomic_xor_release :: intrinsics.atomic_xor_rel
-atomic_xor_acqrel :: intrinsics.atomic_xor_acqrel
-atomic_xor_relaxed :: intrinsics.atomic_xor_relaxed
-
-atomic_exchange :: intrinsics.atomic_xchg
-atomic_exchange_acquire :: intrinsics.atomic_xchg_acq
-atomic_exchange_release :: intrinsics.atomic_xchg_rel
-atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel
-atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed
-
-// Returns value and optional ok boolean
-atomic_compare_exchange_strong :: intrinsics.atomic_cxchg
-atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq
-atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel
-atomic_compare_exchange_strong_acqrel :: intrinsics.atomic_cxchg_acqrel
-atomic_compare_exchange_strong_relaxed :: intrinsics.atomic_cxchg_relaxed
-atomic_compare_exchange_strong_failrelaxed :: intrinsics.atomic_cxchg_failrelaxed
-atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_failacq
-atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed
-atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed
-
-// Returns value and optional ok boolean
-atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak
-atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq
-atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel
-atomic_compare_exchange_weak_acqrel :: intrinsics.atomic_cxchgweak_acqrel
-atomic_compare_exchange_weak_relaxed :: intrinsics.atomic_cxchgweak_relaxed
-atomic_compare_exchange_weak_failrelaxed :: intrinsics.atomic_cxchgweak_failrelaxed
-atomic_compare_exchange_weak_failacquire :: intrinsics.atomic_cxchgweak_failacq
-atomic_compare_exchange_weak_acquire_failrelaxed :: intrinsics.atomic_cxchgweak_acq_failrelaxed
-atomic_compare_exchange_weak_acqrel_failrelaxed :: intrinsics.atomic_cxchgweak_acqrel_failrelaxed
diff --git a/core/sync/sync2/primitives_internal.odin b/core/sync/sync2/primitives_internal.odin
deleted file mode 100644
index ae7e2599c..000000000
--- a/core/sync/sync2/primitives_internal.odin
+++ /dev/null
@@ -1,184 +0,0 @@
-//+private
-package sync2
-
-when #config(ODIN_SYNC_RECURSIVE_MUTEX_USE_FUTEX, true) {
- _Recursive_Mutex :: struct {
- owner: Futex,
- recursion: i32,
- }
-
- _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
- tid := Futex(current_thread_id())
- for {
- prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
- switch prev_owner {
- case 0, tid:
- m.impl.recursion += 1
- // inside the lock
- return
- }
-
- futex_wait(&m.impl.owner, u32(prev_owner))
- }
- }
-
- _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
- m.impl.recursion -= 1
- if m.impl.recursion != 0 {
- return
- }
- atomic_exchange_release(&m.impl.owner, 0)
-
- futex_signal(&m.impl.owner)
- // outside the lock
-
- }
-
- _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
- tid := Futex(current_thread_id())
- prev_owner := atomic_compare_exchange_strong_acquire(&m.impl.owner, tid, 0)
- switch prev_owner {
- case 0, tid:
- m.impl.recursion += 1
- // inside the lock
- return true
- }
- return false
- }
-} else {
- _Recursive_Mutex :: struct {
- owner: int,
- recursion: int,
- mutex: Mutex,
- }
-
- _recursive_mutex_lock :: proc(m: ^Recursive_Mutex) {
- tid := current_thread_id()
- if tid != m.impl.owner {
- mutex_lock(&m.impl.mutex)
- }
- // inside the lock
- m.impl.owner = tid
- m.impl.recursion += 1
- }
-
- _recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
- tid := current_thread_id()
- assert(tid == m.impl.owner)
- m.impl.recursion -= 1
- recursion := m.impl.recursion
- if recursion == 0 {
- m.impl.owner = 0
- }
- if recursion == 0 {
- mutex_unlock(&m.impl.mutex)
- }
- // outside the lock
-
- }
-
- _recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
- tid := current_thread_id()
- if m.impl.owner == tid {
- return mutex_try_lock(&m.impl.mutex)
- }
- if !mutex_try_lock(&m.impl.mutex) {
- return false
- }
- // inside the lock
- m.impl.owner = tid
- m.impl.recursion += 1
- return true
- }
-}
-
-
-when ODIN_OS != "windows" {
- RW_Mutex_State :: distinct uint
- RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2
- RW_Mutex_State_Is_Writing :: RW_Mutex_State(1)
- RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1
- RW_Mutex_State_Reader :: RW_Mutex_State(1)< bool {
- if mutex_try_lock(&rw.impl.mutex) {
- state := atomic_load(&rw.impl.state)
- if state & RW_Mutex_State_Reader_Mask == 0 {
- _ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing)
- return true
- }
-
- mutex_unlock(&rw.impl.mutex)
- }
- return false
- }
-
- _rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) {
- state := atomic_load(&rw.impl.state)
- for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
- ok: bool
- state, ok = atomic_compare_exchange_weak(&rw.impl.state, state, state + RW_Mutex_State_Reader)
- if ok {
- return
- }
- }
-
- mutex_lock(&rw.impl.mutex)
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
- mutex_unlock(&rw.impl.mutex)
- }
-
- _rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) {
- state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader)
-
- if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) &&
- (state & RW_Mutex_State_Is_Writing != 0) {
- sema_post(&rw.impl.sema)
- }
- }
-
- _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
- state := atomic_load(&rw.impl.state)
- if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 {
- _, ok := atomic_compare_exchange_strong(&rw.impl.state, state, state + RW_Mutex_State_Reader)
- if ok {
- return true
- }
- }
- if mutex_try_lock(&rw.impl.mutex) {
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader)
- mutex_unlock(&rw.impl.mutex)
- return true
- }
-
- return false
- }
-
-}
\ No newline at end of file
diff --git a/core/sync/sync2/primitives_linux.odin b/core/sync/sync2/primitives_linux.odin
deleted file mode 100644
index 89ed97985..000000000
--- a/core/sync/sync2/primitives_linux.odin
+++ /dev/null
@@ -1,9 +0,0 @@
-//+build linux
-//+private
-package sync2
-
-import "core:sys/unix"
-
-_current_thread_id :: proc "contextless" () -> int {
- return unix.sys_gettid()
-}
diff --git a/core/sync/sync2/primitives_pthreads.odin b/core/sync/sync2/primitives_pthreads.odin
deleted file mode 100644
index 8d2c3986d..000000000
--- a/core/sync/sync2/primitives_pthreads.odin
+++ /dev/null
@@ -1,58 +0,0 @@
-//+build linux, freebsd
-//+private
-package sync2
-
-import "core:time"
-import "core:sys/unix"
-
-_Mutex_State :: enum i32 {
- Unlocked = 0,
- Locked = 1,
- Waiting = 2,
-}
-_Mutex :: struct {
- pthread_mutex: unix.pthread_mutex_t,
-}
-
-_mutex_lock :: proc(m: ^Mutex) {
- err := unix.pthread_mutex_lock(&m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-_mutex_unlock :: proc(m: ^Mutex) {
- err := unix.pthread_mutex_unlock(&m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-_mutex_try_lock :: proc(m: ^Mutex) -> bool {
- err := unix.pthread_mutex_trylock(&m.impl.pthread_mutex)
- return err == 0
-}
-
-_Cond :: struct {
- pthread_cond: unix.pthread_cond_t,
-}
-
-_cond_wait :: proc(c: ^Cond, m: ^Mutex) {
- err := unix.pthread_cond_wait(&c.impl.pthread_cond, &m.impl.pthread_mutex)
- assert(err == 0)
-}
-
-
-_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, duration: time.Duration) -> bool {
- tv_sec := i64(duration/1e9)
- tv_nsec := i64(duration%1e9)
- err := unix.pthread_cond_timedwait(&c.impl.pthread_cond, &m.impl.pthread_mutex, &{tv_sec, tv_nsec})
- return err == 0
-}
-
-
-_cond_signal :: proc(c: ^Cond) {
- err := unix.pthread_cond_signal(&c.impl.pthread_cond)
- assert(err == 0)
-}
-
-_cond_broadcast :: proc(c: ^Cond) {
- err := unix.pthread_cond_broadcast(&c.impl.pthread_cond)
- assert(err == 0)
-}
diff --git a/core/sync/sync_darwin.odin b/core/sync/sync_darwin.odin
deleted file mode 100644
index f3bb4d5a3..000000000
--- a/core/sync/sync_darwin.odin
+++ /dev/null
@@ -1,54 +0,0 @@
-package sync
-
-import "core:sys/darwin"
-
-import "core:c"
-
-foreign import pthread "System.framework"
-
-current_thread_id :: proc "contextless" () -> int {
- tid: u64
- // NOTE(Oskar): available from OSX 10.6 and iOS 3.2.
- // For older versions there is `syscall(SYS_thread_selfid)`, but not really
- // the same thing apparently.
- foreign pthread { pthread_threadid_np :: proc "c" (rawptr, ^u64) -> c.int --- }
- pthread_threadid_np(nil, &tid)
- return int(tid)
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: darwin.semaphore_t,
-}
-// TODO(tetra): Only marked with alignment because we cannot mark distinct integers with alignments.
-// See core/sys/unix/pthread_linux.odin/pthread_t.
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- ct := darwin.mach_task_self()
- res := darwin.semaphore_create(ct, &s.handle, 0, c.int(initial_count))
- assert(res == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- ct := darwin.mach_task_self()
- res := darwin.semaphore_destroy(ct, s.handle)
- assert(res == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- SYS_GETTID :: 186;
- return int(intrinsics.syscall(SYS_GETTID));
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0);
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0);
- s.handle = {};
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. int {
- return unix.sys_gettid()
-}
-
-
-// The Darwin docs say it best:
-// A semaphore is much like a lock, except that a finite number of threads can hold it simultaneously.
-// Semaphores can be thought of as being much like piles of tokens; multiple threads can take these tokens,
-// but when there are none left, a thread must wait until another thread returns one.
-Semaphore :: struct #align 16 {
- handle: unix.sem_t,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- assert(unix.sem_init(&s.handle, 0, u32(initial_count)) == 0)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- assert(unix.sem_destroy(&s.handle) == 0)
- s.handle = {}
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- // NOTE: SPEED: If there's one syscall to do this, we should use it instead of the loop.
- for in 0.. bool {
- return unix.pthread_mutex_trylock(&m.handle) == 0
-}
-
-mutex_unlock :: proc(m: ^Mutex) {
- assert(unix.pthread_mutex_unlock(&m.handle) == 0)
-}
-
-
-Blocking_Mutex :: struct {
- handle: unix.pthread_mutex_t,
-}
-
-
-blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
- // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the mutex.
- attrs: unix.pthread_mutexattr_t
- assert(unix.pthread_mutexattr_init(&attrs) == 0)
- defer unix.pthread_mutexattr_destroy(&attrs) // ignores destruction error
-
- assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
-}
-
-blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_destroy(&m.handle) == 0)
- m.handle = {}
-}
-
-blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_lock(&m.handle) == 0)
-}
-
-// Returns false if someone else holds the lock.
-blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
- return unix.pthread_mutex_trylock(&m.handle) == 0
-}
-
-blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
- assert(unix.pthread_mutex_unlock(&m.handle) == 0)
-}
-
-
-
-// Blocks until signalled, and then lets past exactly
-// one thread.
-Condition :: struct {
- handle: unix.pthread_cond_t,
- mutex: Condition_Mutex_Ptr,
-
- // NOTE(tetra, 2019-11-11): Used to mimic the more sane behavior of Windows' AutoResetEvent.
- // This means that you may signal the condition before anyone is waiting to cause the
- // next thread that tries to wait to just pass by uninterrupted, without sleeping.
- // Without this, signalling a condition will only wake up a thread which is already waiting,
- // but not one that is about to wait, which can cause your program to become out of sync in
- // ways that are hard to debug or fix.
- flag: bool, // atomically mutated
-}
-
-condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
- // NOTE(tetra, 2019-11-01): POSIX OOM if we cannot init the attrs or the condition.
- attrs: unix.pthread_condattr_t
- if unix.pthread_condattr_init(&attrs) != 0 {
- return false
- }
- defer unix.pthread_condattr_destroy(&attrs) // ignores destruction error
-
- c.flag = false
- c.mutex = mutex
- return unix.pthread_cond_init(&c.handle, &attrs) == 0
-}
-
-condition_destroy :: proc(c: ^Condition) {
- assert(unix.pthread_cond_destroy(&c.handle) == 0)
- c.handle = {}
-}
-
-// Awaken exactly one thread who is waiting on the condition
-condition_signal :: proc(c: ^Condition) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- atomic_swap(&c.flag, true, .Sequentially_Consistent)
- return unix.pthread_cond_signal(&c.handle) == 0
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- atomic_swap(&c.flag, true, .Sequentially_Consistent)
- return unix.pthread_cond_signal(&c.handle) == 0
- }
- return false
-}
-
-// Awaken all threads who are waiting on the condition
-condition_broadcast :: proc(c: ^Condition) -> bool {
- return unix.pthread_cond_broadcast(&c.handle) == 0
-}
-
-// Wait for the condition to be signalled.
-// Does not block if the condition has been signalled and no one
-// has waited on it yet.
-condition_wait_for :: proc(c: ^Condition) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- for {
- if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
-
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- for {
- if unix.pthread_cond_wait(&c.handle, &m.handle) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
- }
- return false
-}
-
-// Wait for the condition to be signalled.
-// Does not block if the condition has been signalled and no one
-// has waited on it yet.
-condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
- switch m in c.mutex {
- case ^Mutex:
- mutex_lock(m)
- defer mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
-
- ns := time.duration_nanoseconds(duration)
- timeout: time.TimeSpec
- timeout.tv_sec = ns / 1e9
- timeout.tv_nsec = ns % 1e9
-
- for {
- if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
-
- case ^Blocking_Mutex:
- blocking_mutex_lock(m)
- defer blocking_mutex_unlock(m)
- // NOTE(tetra): If a thread comes by and steals the flag immediately after the signal occurs,
- // the thread that gets signalled and wakes up, discovers that the flag was taken and goes
- // back to sleep.
- // Though this overall behavior is the most sane, there may be a better way to do this that means that
- // the first thread to wait, gets the flag first.
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
-
- ns := time.duration_nanoseconds(duration)
-
- timeout: time.TimeSpec
- timeout.tv_sec = ns / 1e9
- timeout.tv_nsec = ns % 1e9
-
- for {
- if unix.pthread_cond_timedwait(&c.handle, &m.handle, &timeout) != 0 {
- return false
- }
- if atomic_swap(&c.flag, false, .Sequentially_Consistent) {
- return true
- }
- }
- return false
- }
- return false
-}
-
-
-
-thread_yield :: proc() {
- unix.sched_yield()
-}
diff --git a/core/sync/sync2/sync_util.odin b/core/sync/sync_util.odin
similarity index 93%
rename from core/sync/sync2/sync_util.odin
rename to core/sync/sync_util.odin
index 853c3c685..4add9064d 100644
--- a/core/sync/sync2/sync_util.odin
+++ b/core/sync/sync_util.odin
@@ -1,12 +1,11 @@
-package sync2
+package sync
-
-// Example:
-//
-// if guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if guard(&m) {
+ ...
+ }
+*/
guard :: proc{
mutex_guard,
rw_mutex_guard,
@@ -17,13 +16,12 @@ guard :: proc{
atomic_recursive_mutex_guard,
atomic_rw_mutex_guard,
}
-
-// Example:
-//
-// if shared_guard(&m) {
-// ...
-// }
-//
+/*
+Example:
+ if shared_guard(&m) {
+ ...
+ }
+*/
shared_guard :: proc{
rw_mutex_shared_guard,
atomic_rw_mutex_shared_guard,
diff --git a/core/sync/sync_windows.odin b/core/sync/sync_windows.odin
deleted file mode 100644
index 0a7cf71b2..000000000
--- a/core/sync/sync_windows.odin
+++ /dev/null
@@ -1,180 +0,0 @@
-// +build windows
-package sync
-
-import win32 "core:sys/windows"
-import "core:time"
-
-current_thread_id :: proc "contextless" () -> int {
- return int(win32.GetCurrentThreadId())
-}
-
-
-// When waited upon, blocks until the internal count is greater than zero, then subtracts one.
-// Posting to the semaphore increases the count by one, or the provided amount.
-Semaphore :: struct {
- _handle: win32.HANDLE,
-}
-
-semaphore_init :: proc(s: ^Semaphore, initial_count := 0) {
- s._handle = win32.CreateSemaphoreW(nil, i32(initial_count), 1<<31-1, nil)
-}
-
-semaphore_destroy :: proc(s: ^Semaphore) {
- win32.CloseHandle(s._handle)
-}
-
-semaphore_post :: proc(s: ^Semaphore, count := 1) {
- win32.ReleaseSemaphore(s._handle, i32(count), nil)
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
- // NOTE(tetra, 2019-10-30): wait_for_single_object decrements the count before it returns.
- result := win32.WaitForSingleObject(s._handle, win32.INFINITE)
- assert(result != win32.WAIT_FAILED)
-}
-
-
-Mutex :: struct {
- _critical_section: win32.CRITICAL_SECTION,
-}
-
-
-mutex_init :: proc(m: ^Mutex, spin_count := 0) {
- win32.InitializeCriticalSectionAndSpinCount(&m._critical_section, u32(spin_count))
-}
-
-mutex_destroy :: proc(m: ^Mutex) {
- win32.DeleteCriticalSection(&m._critical_section)
-}
-
-mutex_lock :: proc(m: ^Mutex) {
- win32.EnterCriticalSection(&m._critical_section)
-}
-
-mutex_try_lock :: proc(m: ^Mutex) -> bool {
- return bool(win32.TryEnterCriticalSection(&m._critical_section))
-}
-
-mutex_unlock :: proc(m: ^Mutex) {
- win32.LeaveCriticalSection(&m._critical_section)
-}
-
-Blocking_Mutex :: struct {
- _handle: win32.SRWLOCK,
-}
-
-
-blocking_mutex_init :: proc(m: ^Blocking_Mutex) {
- win32.InitializeSRWLock(&m._handle)
-}
-
-blocking_mutex_destroy :: proc(m: ^Blocking_Mutex) {
- //
-}
-
-blocking_mutex_lock :: proc(m: ^Blocking_Mutex) {
- win32.AcquireSRWLockExclusive(&m._handle)
-}
-
-blocking_mutex_try_lock :: proc(m: ^Blocking_Mutex) -> bool {
- return bool(win32.TryAcquireSRWLockExclusive(&m._handle))
-}
-
-blocking_mutex_unlock :: proc(m: ^Blocking_Mutex) {
- win32.ReleaseSRWLockExclusive(&m._handle)
-}
-
-
-// Blocks until signalled.
-// When signalled, awakens exactly one waiting thread.
-Condition :: struct {
- _handle: win32.CONDITION_VARIABLE,
-
- mutex: Condition_Mutex_Ptr,
-}
-
-
-condition_init :: proc(c: ^Condition, mutex: Condition_Mutex_Ptr) -> bool {
- assert(mutex != nil)
- win32.InitializeConditionVariable(&c._handle)
- c.mutex = mutex
- return true
-}
-
-condition_destroy :: proc(c: ^Condition) {
- //
-}
-
-condition_signal :: proc(c: ^Condition) -> bool {
- if c._handle.ptr == nil {
- return false
- }
- win32.WakeConditionVariable(&c._handle)
- return true
-}
-
-condition_broadcast :: proc(c: ^Condition) -> bool {
- if c._handle.ptr == nil {
- return false
- }
- win32.WakeAllConditionVariable(&c._handle)
- return true
-}
-
-condition_wait_for :: proc(c: ^Condition) -> bool {
- switch m in &c.mutex {
- case ^Mutex:
- return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, win32.INFINITE)
- case ^Blocking_Mutex:
- return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, win32.INFINITE, 0)
- }
- return false
-}
-condition_wait_for_timeout :: proc(c: ^Condition, duration: time.Duration) -> bool {
- ms := win32.DWORD((max(time.duration_nanoseconds(duration), 0) + 999999)/1000000)
- switch m in &c.mutex {
- case ^Mutex:
- return cast(bool)win32.SleepConditionVariableCS(&c._handle, &m._critical_section, ms)
- case ^Blocking_Mutex:
- return cast(bool)win32.SleepConditionVariableSRW(&c._handle, &m._handle, ms, 0)
- }
- return false
-}
-
-
-
-
-RW_Lock :: struct {
- _handle: win32.SRWLOCK,
-}
-
-rw_lock_init :: proc(l: ^RW_Lock) {
- l._handle = win32.SRWLOCK_INIT
-}
-rw_lock_destroy :: proc(l: ^RW_Lock) {
- //
-}
-rw_lock_read :: proc(l: ^RW_Lock) {
- win32.AcquireSRWLockShared(&l._handle)
-}
-rw_lock_try_read :: proc(l: ^RW_Lock) -> bool {
- return bool(win32.TryAcquireSRWLockShared(&l._handle))
-}
-rw_lock_write :: proc(l: ^RW_Lock) {
- win32.AcquireSRWLockExclusive(&l._handle)
-}
-rw_lock_try_write :: proc(l: ^RW_Lock) -> bool {
- return bool(win32.TryAcquireSRWLockExclusive(&l._handle))
-}
-rw_lock_read_unlock :: proc(l: ^RW_Lock) {
- win32.ReleaseSRWLockShared(&l._handle)
-}
-rw_lock_write_unlock :: proc(l: ^RW_Lock) {
- win32.ReleaseSRWLockExclusive(&l._handle)
-}
-
-
-thread_yield :: proc() {
- win32.SwitchToThread()
-}
-
diff --git a/core/sync/wait_group.odin b/core/sync/wait_group.odin
deleted file mode 100644
index 63d882ed1..000000000
--- a/core/sync/wait_group.odin
+++ /dev/null
@@ -1,58 +0,0 @@
-package sync
-
-import "core:intrinsics"
-
-Wait_Group :: struct {
- counter: int,
- mutex: Blocking_Mutex,
- cond: Condition,
-}
-
-wait_group_init :: proc(wg: ^Wait_Group) {
- wg.counter = 0
- blocking_mutex_init(&wg.mutex)
- condition_init(&wg.cond, &wg.mutex)
-}
-
-
-wait_group_destroy :: proc(wg: ^Wait_Group) {
- condition_destroy(&wg.cond)
- blocking_mutex_destroy(&wg.mutex)
-}
-
-wait_group_add :: proc(wg: ^Wait_Group, delta: int) {
- if delta == 0 {
- return
- }
-
- blocking_mutex_lock(&wg.mutex)
- defer blocking_mutex_unlock(&wg.mutex)
-
- intrinsics.atomic_add(&wg.counter, delta)
- if wg.counter < 0 {
- panic("sync.Wait_Group negative counter")
- }
- if wg.counter == 0 {
- condition_broadcast(&wg.cond)
- if wg.counter != 0 {
- panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
- }
- }
-}
-
-wait_group_done :: proc(wg: ^Wait_Group) {
- wait_group_add(wg, -1)
-}
-
-wait_group_wait :: proc(wg: ^Wait_Group) {
- blocking_mutex_lock(&wg.mutex)
- defer blocking_mutex_unlock(&wg.mutex)
-
- if wg.counter != 0 {
- condition_wait_for(&wg.cond)
- if wg.counter != 0 {
- panic("sync.Wait_Group misuse: sync.wait_group_add called concurrently with sync.wait_group_wait")
- }
- }
-}
-
diff --git a/core/sys/cpu/cpu_x86.odin b/core/sys/cpu/cpu_x86.odin
index 8f3560a87..146822e61 100644
--- a/core/sys/cpu/cpu_x86.odin
+++ b/core/sys/cpu/cpu_x86.odin
@@ -1,4 +1,4 @@
-//+build 386, amd64
+//+build i386, amd64
package sys_cpu
_cache_line_size :: 64;
diff --git a/core/sys/darwin/xnu_system_call_helpers.odin b/core/sys/darwin/xnu_system_call_helpers.odin
new file mode 100644
index 000000000..94fe0bf47
--- /dev/null
+++ b/core/sys/darwin/xnu_system_call_helpers.odin
@@ -0,0 +1,168 @@
+package darwin
+
+import "core:strings"
+import "core:c"
+
+// this package uses the sys prefix for the proc names to indicate that these aren't native syscalls but directly call such
+sys_write_string :: proc (fd: c.int, message: string) -> bool {
+ return syscall_write(fd, strings.ptr_from_string(message), cast(u64)len(message))
+}
+
+Offset_From :: enum c.int {
+ SEEK_SET = 0, // the offset is set to offset bytes.
+ SEEK_CUR = 1, // the offset is set to its current location plus offset bytes.
+ SEEK_END = 2, // the offset is set to the size of the file plus offset bytes.
+ SEEK_HOLE = 3, // the offset is set to the start of the next hole greater than or equal to the supplied offset.
+ SEEK_DATA = 4, // the offset is set to the start of the next non-hole file region greater than or equal to the supplied offset.
+}
+
+Open_Flags_Enum :: enum u8 {
+ RDONLY, /* open for reading only */
+ WRONLY, /* open for writing only */
+ RDWR, /* open for reading and writing */
+
+ NONBLOCK, /* no delay */
+ APPEND, /* set append mode */
+ CREAT, /* create if nonexistant */
+ TRUNC, /* truncate to zero length */
+ EXCL, /* error if already exists */
+ SHLOCK, /* open with shared file lock */
+ EXLOCK, /* open with exclusive file lock */
+ DIRECTORY, /* restrict open to only directories */
+ NOFOLLOW, /* don't follow symlinks */
+ SYMLINK, /* allow open of a symlink */
+ EVTONLY, /* descriptor requested for event notifications only */
+ CLOEXEC, /* causes the descriptor to be closed if you use any of the exec like functions */
+ NOFOLLOW_ANY, /* no symlinks allowed in path */
+}
+Open_Flags :: bit_set[Open_Flags_Enum; u16]
+
+Permission_Enum :: enum u8 {
+ /* For owner */
+ PERMISSION_OWNER_READ, /* R for owner */
+ PERMISSION_OWNER_WRITE, /* W for owner */
+ PERMISSION_OWNER_EXECUTE, /* X for owner */
+ //IRWXU, /* RWX mask for owner */
+
+ /* For group */
+ PERMISSION_GROUP_READ, /* R for group */
+ PERMISSION_GROUP_WRITE, /* W for group */
+ PERMISSION_GROUP_EXECUTE, /* X for group */
+ //IRWXG, /* RWX mask for group */
+
+ /* For other */
+ PERMISSION_OTHER_READ, /* R for other */
+ PERMISSION_OTHER_WRITE, /* W for other */
+ PERMISSION_OTHER_EXECUTE, /* X for other */
+ //IRWXO, /* RWX mask for other */
+
+ /* Special */
+ PERMISSION_SET_USER_ON_EXECUTION, /* set user id on execution */
+ PERMISSION_SET_GROUP_ON_EXECUTION, /* set group id on execution */
+
+ /* ?? */
+ PERMISSION_ISVTX, /* save swapped text even after use */
+}
+Permission :: bit_set[Permission_Enum; u16]
+
+PERMISSION_NONE_NONE :: Permission{}
+PERMISSION_OWNER_ALL :: Permission{.PERMISSION_OWNER_READ, .PERMISSION_OWNER_WRITE, .PERMISSION_OWNER_EXECUTE}
+PERMISSION_GROUP_ALL :: Permission{.PERMISSION_GROUP_READ, .PERMISSION_GROUP_WRITE, .PERMISSION_GROUP_EXECUTE}
+PERMISSION_OTHER_ALL :: Permission{.PERMISSION_OTHER_READ, .PERMISSION_OTHER_WRITE, .PERMISSION_OTHER_EXECUTE}
+PERMISSION_ALL_ALL :: PERMISSION_OWNER_ALL | PERMISSION_GROUP_ALL | PERMISSION_OTHER_ALL
+
+_sys_permission_mode :: #force_inline proc (mode: Permission) -> u32 {
+ cflags: u32 = 0
+
+ cflags |= PERMISSION_MASK_IRUSR * u32(Permission.PERMISSION_OWNER_READ in mode)
+ cflags |= PERMISSION_MASK_IWUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
+ cflags |= PERMISSION_MASK_IXUSR * u32(Permission.PERMISSION_OWNER_WRITE in mode)
+ cflags |= PERMISSION_MASK_IRGRP * u32(Permission.PERMISSION_GROUP_READ in mode)
+ cflags |= PERMISSION_MASK_IWGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
+ cflags |= PERMISSION_MASK_IXGRP * u32(Permission.PERMISSION_GROUP_WRITE in mode)
+ cflags |= PERMISSION_MASK_IROTH * u32(Permission.PERMISSION_OTHER_READ in mode)
+ cflags |= PERMISSION_MASK_IWOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
+ cflags |= PERMISSION_MASK_IXOTH * u32(Permission.PERMISSION_OTHER_WRITE in mode)
+
+ return cflags
+}
+
+sys_open :: proc(path: string, oflag: Open_Flags, mode: Permission) -> (c.int, bool) {
+
+ cmode: u32 = 0
+ cflags: u32 = 0
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+
+ cflags = _sys_permission_mode(mode)
+
+ cmode |= OPEN_FLAG_RDONLY * u32(Open_Flags.RDONLY in oflag)
+ cmode |= OPEN_FLAG_WRONLY * u32(Open_Flags.WRONLY in oflag)
+ cmode |= OPEN_FLAG_RDWR * u32(Open_Flags.RDWR in oflag)
+ cmode |= OPEN_FLAG_NONBLOCK * u32(Open_Flags.NONBLOCK in oflag)
+ cmode |= OPEN_FLAG_CREAT * u32(Open_Flags.CREAT in oflag)
+ cmode |= OPEN_FLAG_APPEND * u32(Open_Flags.APPEND in oflag)
+ cmode |= OPEN_FLAG_TRUNC * u32(Open_Flags.TRUNC in oflag)
+ cmode |= OPEN_FLAG_EXCL * u32(Open_Flags.EXCL in oflag)
+ cmode |= OPEN_FLAG_SHLOCK * u32(Open_Flags.SHLOCK in oflag)
+ cmode |= OPEN_FLAG_EXLOCK * u32(Open_Flags.EXLOCK in oflag)
+ cmode |= OPEN_FLAG_DIRECTORY * u32(Open_Flags.DIRECTORY in oflag)
+ cmode |= OPEN_FLAG_NOFOLLOW * u32(Open_Flags.NOFOLLOW in oflag)
+ cmode |= OPEN_FLAG_SYMLINK * u32(Open_Flags.SYMLINK in oflag)
+ cmode |= OPEN_FLAG_EVTONLY * u32(Open_Flags.EVTONLY in oflag)
+ cmode |= OPEN_FLAG_CLOEXEC * u32(Open_Flags.CLOEXEC in oflag)
+ cmode |= OPEN_FLAG_NOFOLLOW_ANY * u32(Open_Flags.NOFOLLOW_ANY in oflag)
+
+ result := syscall_open(cpath, cmode, cflags)
+ state := result != -1
+
+ if state && cflags != 0 {
+ state = (syscall_fchmod(result, cflags) != -1)
+ }
+
+ return result * cast(c.int)state, state
+}
+
+sys_mkdir :: proc(path: string, mode: Permission) -> bool {
+ cpath: cstring = strings.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 {
+ cpath: cstring = strings.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 {
+ cpath: cstring = strings.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 {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cnpath: cstring = strings.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 {
+ cpath: cstring = strings.clone_to_cstring(path, context.temp_allocator)
+ cnpath: cstring = strings.clone_to_cstring(new_path, context.temp_allocator)
+ return syscall_rename_at(fd, cpath, to_fd, cnpath) != -1
+}
+
+sys_lseek :: proc(fd: c.int, offset: i64, whence: Offset_From) -> i64 {
+ return syscall_lseek(fd, offset, cast(c.int)whence)
+}
+
+sys_chmod :: proc(path: string, mode: Permission) -> bool {
+ cpath: cstring = strings.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 {
+ cpath: cstring = strings.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
new file mode 100644
index 000000000..b90373fdc
--- /dev/null
+++ b/core/sys/darwin/xnu_system_call_numbers.odin
@@ -0,0 +1,558 @@
+package darwin
+
+unix_offset_syscall :: proc(number: System_Call_Number) -> uintptr {
+ return uintptr(number) + uintptr(0x2000000)
+}
+
+System_Call_Number :: enum uintptr {
+ /* 0 syscall */
+ exit = 1,
+ fork = 2,
+ read = 3,
+ write = 4,
+ open = 5,
+ close = 6,
+ wait4 = 7,
+ /* 8 old creat */
+ link = 9,
+ unlink = 10,
+ /* 11 old execv */
+ chdir = 12,
+ fchdir = 13,
+ mknod = 14,
+ chmod = 15,
+ chown = 16,
+ /* 17 old break */
+ getfsstat = 18,
+ /* 19 old lseek */
+ getpid = 20,
+ /* 21 old mount */
+ /* 22 old umount */
+ setuid = 23,
+ getuid = 24,
+ geteuid = 25,
+ ptrace = 26,
+ recvmsg = 27,
+ sendmsg = 28,
+ recvfrom = 29,
+ accept = 30,
+ getpeername = 31,
+ getsockname = 32,
+ access = 33,
+ chflags = 34,
+ fchflags = 35,
+ sync = 36,
+ kill = 37,
+ /* 38 old stat */
+ getppid = 39,
+ /* 40 old lstat */
+ dup = 41,
+ pipe = 42,
+ getegid = 43,
+ /* 44 old profil */
+ /* 45 old ktrace */
+ sigaction = 46,
+ getgid = 47,
+ sigprocmask = 48,
+ getlogin = 49,
+ setlogin = 50,
+ acct = 51,
+ sigpending = 52,
+ sigaltstack = 53,
+ ioctl = 54,
+ reboot = 55,
+ revoke = 56,
+ symlink = 57,
+ readlink = 58,
+ execve = 59,
+ umask = 60,
+ chroot = 61,
+ /* 62 old fstat */
+ /* 63 used internally and reserved */
+ /* getpagesize = 64, invalid */
+ msync = 65,
+ vfork = 66,
+ /* 67 old vread */
+ /* 68 old vwrite */
+ /* 69 old sbrk */
+ /* 70 old sstk */
+ /* 71 old mmap */
+ /* 72 old vadvise */
+ munmap = 73,
+ mprotect = 74,
+ madvise = 75,
+ /* 76 old vhangup */
+ /* 77 old vlimit */
+ mincore = 78,
+ getgroups = 79,
+ setgroups = 80,
+ getpgrp = 81,
+ setpgid = 82,
+ setitimer = 83,
+ /* 84 old wait */
+ swapon = 85,
+ getitimer = 86,
+ /* 87 old gethostname */
+ /* 88 old sethostname */
+ getdtablesize = 89,
+ dup2 = 90,
+ /* 91 old getdopt */
+ fcntl = 92,
+ select = 93,
+ /* 94 old setdopt */
+ fsync = 95,
+ setpriority = 96,
+ socket = 97,
+ connect = 98,
+ /* 99 old accept */
+ getpriority = 100,
+ /* 101 old send */
+ /* 102 old recv */
+ /* 103 old sigreturn */
+ bind = 104,
+ setsockopt = 105,
+ listen = 106,
+ /* 107 old vtimes */
+ /* 108 old sigvec */
+ /* 109 old sigblock */
+ /* 110 old sigsetmask */
+ sigsuspend = 111,
+ /* 112 old sigstack */
+ /* 113 old recvmsg */
+ /* 114 old sendmsg */
+ /* 115 old vtrace */
+ gettimeofday = 116,
+ getrusage = 117,
+ getsockopt = 118,
+ /* 119 old resuba */
+ readv = 120,
+ writev = 121,
+ settimeofday = 122,
+ fchown = 123,
+ fchmod = 124,
+ /* 125 old recvfrom */
+ setreuid = 126,
+ setregid = 127,
+ rename = 128,
+ /* 129 old truncate */
+ /* 130 old ftruncate */
+ flock = 131,
+ mkfifo = 132,
+ sendto = 133,
+ shutdown = 134,
+ socketpair = 135,
+ mkdir = 136,
+ rmdir = 137,
+ utimes = 138,
+ futimes = 139,
+ adjtime = 140,
+ /* 141 old getpeername */
+ gethostuuid = 142,
+ /* 143 old sethostid */
+ /* 144 old getrlimit */
+ /* 145 old setrlimit */
+ /* 146 old killpg */
+ setsid = 147,
+ /* 148 old setquota */
+ /* 149 old qquota */
+ /* 150 old getsockname */
+ getpgid = 151,
+ setprivexec = 152,
+ pread = 153,
+ pwrite = 154,
+ nfssvc = 155,
+ /* 156 old getdirentries */
+ statfs = 157,
+ fstatfs = 158,
+ unmount = 159,
+ /* 160 old async_daemon */
+ getfh = 161,
+ /* 162 old getdomainname */
+ /* 163 old setdomainname */
+ /* 164 */
+ quotactl = 165,
+ /* 166 old exportfs */
+ mount = 167,
+ /* 168 old ustat */
+ csops = 169,
+ csops_audittoken = 170,
+ /* 171 old wait3 */
+ /* 172 old rpause */
+ waitid = 173,
+ /* 174 old getdents */
+ /* 175 old gc_control */
+ /* 176 old add_profil */
+ kdebug_typefilter = 177,
+ kdebug_trace_string = 178,
+ kdebug_trace64 = 179,
+ kdebug_trace = 180,
+ setgid = 181,
+ setegid = 182,
+ seteuid = 183,
+ sigreturn = 184,
+ /* 185 old chud */
+ thread_selfcounts = 186,
+ fdatasync = 187,
+ stat = 188,
+ fstat = 189,
+ lstat = 190,
+ pathconf = 191,
+ fpathconf = 192,
+ /* 193 old getfsstat */
+ getrlimit = 194,
+ setrlimit = 195,
+ getdirentries = 196,
+ mmap = 197,
+ /* 198 old __syscall */
+ lseek = 199,
+ truncate = 200,
+ ftruncate = 201,
+ sysctl = 202,
+ mlock = 203,
+ munlock = 204,
+ undelete = 205,
+ /* 206 old ATsocket */
+ /* 207 old ATgetmsg */
+ /* 208 old ATputmsg */
+ /* 209 old ATsndreq */
+ /* 210 old ATsndrsp */
+ /* 211 old ATgetreq */
+ /* 212 old ATgetrsp */
+ /* 213 Reserved for AppleTalk */
+ /* 214 */
+ /* 215 */
+ open_dprotected_np = 216,
+ fsgetpath_ext = 217,
+ /* 218 old lstatv */
+ /* 219 old fstatv */
+ getattrlist = 220,
+ setattrlist = 221,
+ getdirentriesattr = 222,
+ exchangedata = 223,
+ /* 224 old checkuseraccess or fsgetpath */
+ searchfs = 225,
+ delete = 226,
+ copyfile = 227,
+ fgetattrlist = 228,
+ fsetattrlist = 229,
+ poll = 230,
+ /* 231 old watchevent */
+ /* 232 old waitevent */
+ /* 233 old modwatch */
+ getxattr = 234,
+ fgetxattr = 235,
+ setxattr = 236,
+ fsetxattr = 237,
+ removexattr = 238,
+ fremovexattr = 239,
+ listxattr = 240,
+ flistxattr = 241,
+ fsctl = 242,
+ initgroups = 243,
+ posix_spawn = 244,
+ ffsctl = 245,
+ /* 246 */
+ nfsclnt = 247,
+ fhopen = 248,
+ /* 249 */
+ minherit = 250,
+ semsys = 251,
+ msgsys = 252,
+ shmsys = 253,
+ semctl = 254,
+ semget = 255,
+ semop = 256,
+ /* 257 old semconfig */
+ msgctl = 258,
+ msgget = 259,
+ msgsnd = 260,
+ msgrcv = 261,
+ shmat = 262,
+ shmctl = 263,
+ shmdt = 264,
+ shmget = 265,
+ shm_open = 266,
+ shm_unlink = 267,
+ sem_open = 268,
+ sem_close = 269,
+ sem_unlink = 270,
+ sem_wait = 271,
+ sem_trywait = 272,
+ sem_post = 273,
+ sysctlbyname = 274,
+ /* 275 old sem_init */
+ /* 276 old sem_destroy */
+ open_extended = 277,
+ umask_extended = 278,
+ stat_extended = 279,
+ lstat_extended = 280,
+ fstat_extended = 281,
+ chmod_extended = 282,
+ fchmod_extended = 283,
+ access_extended = 284,
+ settid = 285,
+ gettid = 286,
+ setsgroups = 287,
+ getsgroups = 288,
+ setwgroups = 289,
+ getwgroups = 290,
+ mkfifo_extended = 291,
+ mkdir_extended = 292,
+ identitysvc = 293,
+ shared_region_check_np = 294,
+ /* 295 old shared_region_map_np */
+ vm_pressure_monitor = 296,
+ psynch_rw_longrdlock = 297,
+ psynch_rw_yieldwrlock = 298,
+ psynch_rw_downgrade = 299,
+ psynch_rw_upgrade = 300,
+ psynch_mutexwait = 301,
+ psynch_mutexdrop = 302,
+ psynch_cvbroad = 303,
+ psynch_cvsignal = 304,
+ psynch_cvwait = 305,
+ psynch_rw_rdlock = 306,
+ psynch_rw_wrlock = 307,
+ psynch_rw_unlock = 308,
+ psynch_rw_unlock2 = 309,
+ getsid = 310,
+ settid_with_pid = 311,
+ psynch_cvclrprepost = 312,
+ aio_fsync = 313,
+ aio_return = 314,
+ aio_suspend = 315,
+ aio_cancel = 316,
+ aio_error = 317,
+ aio_read = 318,
+ aio_write = 319,
+ lio_listio = 320,
+ /* 321 old __pthread_cond_wait */
+ iopolicysys = 322,
+ process_policy = 323,
+ mlockall = 324,
+ munlockall = 325,
+ /* 326 */
+ issetugid = 327,
+ __pthread_kill = 328,
+ __pthread_sigmask = 329,
+ __sigwait = 330,
+ __disable_threadsignal = 331,
+ __pthread_markcancel = 332,
+ __pthread_canceled = 333,
+ __semwait_signal = 334,
+ /* 335 old utrace */
+ proc_info = 336,
+ sendfile = 337,
+ stat64 = 338,
+ fstat64 = 339,
+ lstat64 = 340,
+ stat64_extended = 341,
+ lstat64_extended = 342,
+ fstat64_extended = 343,
+ getdirentries64 = 344,
+ statfs64 = 345,
+ fstatfs64 = 346,
+ getfsstat64 = 347,
+ __pthread_chdir = 348,
+ __pthread_fchdir = 349,
+ audit = 350,
+ auditon = 351,
+ /* 352 */
+ getauid = 353,
+ setauid = 354,
+ /* 355 old getaudit */
+ /* 356 old setaudit */
+ getaudit_addr = 357,
+ setaudit_addr = 358,
+ auditctl = 359,
+ bsdthread_create = 360,
+ bsdthread_terminate = 361,
+ kqueue = 362,
+ kevent = 363,
+ lchown = 364,
+ /* 365 old stack_snapshot */
+ bsdthread_register = 366,
+ workq_open = 367,
+ workq_kernreturn = 368,
+ kevent64 = 369,
+ __old_semwait_signal = 370,
+ __old_semwait_signal_nocancel = 371,
+ thread_selfid = 372,
+ ledger = 373,
+ kevent_qos = 374,
+ kevent_id = 375,
+ /* 376 */
+ /* 377 */
+ /* 378 */
+ /* 379 */
+ __mac_execve = 380,
+ __mac_syscall = 381,
+ __mac_get_file = 382,
+ __mac_set_file = 383,
+ __mac_get_link = 384,
+ __mac_set_link = 385,
+ __mac_get_proc = 386,
+ __mac_set_proc = 387,
+ __mac_get_fd = 388,
+ __mac_set_fd = 389,
+ __mac_get_pid = 390,
+ /* 391 */
+ /* 392 */
+ /* 393 */
+ pselect = 394,
+ pselect_nocancel = 395,
+ read_nocancel = 396,
+ write_nocancel = 397,
+ open_nocancel = 398,
+ close_nocancel = 399,
+ wait4_nocancel = 400,
+ recvmsg_nocancel = 401,
+ sendmsg_nocancel = 402,
+ recvfrom_nocancel = 403,
+ accept_nocancel = 404,
+ msync_nocancel = 405,
+ fcntl_nocancel = 406,
+ select_nocancel = 407,
+ fsync_nocancel = 408,
+ connect_nocancel = 409,
+ sigsuspend_nocancel = 410,
+ readv_nocancel = 411,
+ writev_nocancel = 412,
+ sendto_nocancel = 413,
+ pread_nocancel = 414,
+ pwrite_nocancel = 415,
+ waitid_nocancel = 416,
+ poll_nocancel = 417,
+ msgsnd_nocancel = 418,
+ msgrcv_nocancel = 419,
+ sem_wait_nocancel = 420,
+ aio_suspend_nocancel = 421,
+ __sigwait_nocancel = 422,
+ __semwait_signal_nocancel = 423,
+ __mac_mount = 424,
+ __mac_get_mount = 425,
+ __mac_getfsstat = 426,
+ fsgetpath = 427,
+ audit_session_self = 428,
+ audit_session_join = 429,
+ fileport_makeport = 430,
+ fileport_makefd = 431,
+ audit_session_port = 432,
+ pid_suspend = 433,
+ pid_resume = 434,
+ pid_hibernate = 435,
+ pid_shutdown_sockets = 436,
+ /* 437 old shared_region_slide_np */
+ shared_region_map_and_slide_np = 438,
+ kas_info = 439,
+ memorystatus_control = 440,
+ guarded_open_np = 441,
+ guarded_close_np = 442,
+ guarded_kqueue_np = 443,
+ change_fdguard_np = 444,
+ usrctl = 445,
+ proc_rlimit_control = 446,
+ connectx = 447,
+ disconnectx = 448,
+ peeloff = 449,
+ socket_delegate = 450,
+ telemetry = 451,
+ proc_uuid_policy = 452,
+ memorystatus_get_level = 453,
+ system_override = 454,
+ vfs_purge = 455,
+ sfi_ctl = 456,
+ sfi_pidctl = 457,
+ coalition = 458,
+ coalition_info = 459,
+ necp_match_policy = 460,
+ getattrlistbulk = 461,
+ clonefileat = 462,
+ openat = 463,
+ openat_nocancel = 464,
+ renameat = 465,
+ faccessat = 466,
+ fchmodat = 467,
+ fchownat = 468,
+ fstatat = 469,
+ fstatat64 = 470,
+ linkat = 471,
+ unlinkat = 472,
+ readlinkat = 473,
+ symlinkat = 474,
+ mkdirat = 475,
+ getattrlistat = 476,
+ proc_trace_log = 477,
+ bsdthread_ctl = 478,
+ openbyid_np = 479,
+ recvmsg_x = 480,
+ sendmsg_x = 481,
+ thread_selfusage = 482,
+ csrctl = 483,
+ guarded_open_dprotected_np = 484,
+ guarded_write_np = 485,
+ guarded_pwrite_np = 486,
+ guarded_writev_np = 487,
+ renameatx_np = 488,
+ mremap_encrypted = 489,
+ netagent_trigger = 490,
+ stack_snapshot_with_config = 491,
+ microstackshot = 492,
+ grab_pgo_data = 493,
+ persona = 494,
+ /* 495 */
+ mach_eventlink_signal = 496,
+ mach_eventlink_wait_until = 497,
+ mach_eventlink_signal_wait_until = 498,
+ work_interval_ctl = 499,
+ getentropy = 500,
+ necp_open = 501,
+ necp_client_action = 502,
+ nexus_open = 503, // for those who are intressted http://newosxbook.com/bonus/vol1ch16.html
+ nexus_register = 504,
+ nexus_deregister = 505,
+ nexus_create = 506,
+ nexus_destroy = 507,
+ nexus_get_opt = 508,
+ nexus_set_opt = 509,
+ channel_open = 510,
+ channel_get_info = 511,
+ channel_sync = 512,
+ channel_get_opt = 513,
+ channel_set_opt = 514,
+ ulock_wait = 515,
+ ulock_wake = 516,
+ fclonefileat = 517,
+ fs_snapshot = 518,
+ register_uexc_handler = 519,
+ terminate_with_payload = 520,
+ abort_with_payload = 521,
+ necp_session_open = 522,
+ necp_session_action = 523,
+ setattrlistat = 524,
+ net_qos_guideline = 525,
+ fmount = 526,
+ ntp_adjtime = 527,
+ ntp_gettime = 528,
+ os_fault_with_payload = 529,
+ kqueue_workloop_ctl = 530,
+ mach_bridge_remote_time = 531,
+ coalition_ledger = 532,
+ log_data = 533,
+ memorystatus_available_memory = 534,
+ objc_bp_assist_cfg_np = 535,
+ shared_region_map_and_slide_2_np = 536,
+ pivot_root = 537,
+ task_inspect_for_pid = 538,
+ task_read_for_pid = 539,
+ preadv = 540,
+ pwritev = 541,
+ preadv_nocancel = 542,
+ pwritev_nocancel = 543,
+ ulock_wait2 = 544,
+ proc_info_extended_id = 545,
+ tracker_action = 546,
+ debug_syscall_reject = 547,
+ MAXSYSCALL = 548,
+ /* invalid = 63, */
+}
diff --git a/core/sys/darwin/xnu_system_call_wrappers.odin b/core/sys/darwin/xnu_system_call_wrappers.odin
new file mode 100644
index 000000000..4e4227f1f
--- /dev/null
+++ b/core/sys/darwin/xnu_system_call_wrappers.odin
@@ -0,0 +1,419 @@
+package darwin
+
+import "core:c"
+import "core:intrinsics"
+
+/* flock */
+LOCK_SH :: 1 /* shared lock */
+LOCK_EX :: 2 /* exclusive lock */
+LOCK_NB :: 4 /* don't block when locking */
+LOCK_UN :: 8 /* unlock */
+
+/* sys/unistd.h for access */
+F_OK :: c.int(0) /* test for existence of file */
+X_OK :: c.int((1 << 0)) /* test for execute or search permission */
+W_OK :: c.int((1 << 1)) /* test for write permission */
+R_OK :: c.int((1 << 2)) /* test for read permission */
+
+/* copyfile flags */
+COPYFILE_ACL :: (1 << 0)
+COPYFILE_STAT :: (1 << 1)
+COPYFILE_XATTR :: (1 << 2)
+COPYFILE_DATA :: (1 << 3)
+
+COPYFILE_SECURITY :: (COPYFILE_STAT | COPYFILE_ACL)
+COPYFILE_METADATA :: (COPYFILE_SECURITY | COPYFILE_XATTR)
+COPYFILE_ALL :: (COPYFILE_METADATA | COPYFILE_DATA)
+
+/* syslimits.h */
+PATH_MAX :: 1024 /* max bytes in pathname */
+
+/* param.h */
+MAXPATHLEN :: PATH_MAX
+
+/* proc_info.h */
+DARWIN_PROC_PIDPATHINFO_SIZE :: MAXPATHLEN
+DARWIN_PROC_PIDPATHINFO :: 11
+
+DARWIN_PROC_ALL_PIDS :: c.int(1)
+DARWIN_PROC_PGRP_ONLY :: c.int(2)
+DARWIN_PROC_TTY_ONLY :: c.int(3)
+DARWIN_PROC_UID_ONLY :: c.int(4)
+DARWIN_PROC_RUID_ONLY :: c.int(5)
+DARWIN_PROC_PPID_ONLY :: c.int(6)
+DARWIN_PROC_KDBG_ONLY :: c.int(7)
+
+DARWIN_PROC_INFO_CALL_LISTPIDS :: c.int(0x1)
+DARWIN_PROC_INFO_CALL_PIDINFO :: c.int(0x2)
+DARWIN_PROC_INFO_CALL_PIDFDINFO :: c.int(0x3)
+DARWIN_PROC_INFO_CALL_KERNMSGBUF :: c.int(0x4)
+DARWIN_PROC_INFO_CALL_SETCONTROL :: c.int(0x5)
+DARWIN_PROC_INFO_CALL_PIDFILEPORTINFO :: c.int(0x6)
+DARWIN_PROC_INFO_CALL_TERMINATE :: c.int(0x7)
+DARWIN_PROC_INFO_CALL_DIRTYCONTROL :: c.int(0x8)
+DARWIN_PROC_INFO_CALL_PIDRUSAGE :: c.int(0x9)
+DARWIN_PROC_INFO_CALL_PIDORIGINATORINFO :: c.int(0xa)
+DARWIN_PROC_INFO_CALL_LISTCOALITIONS :: c.int(0xb)
+DARWIN_PROC_INFO_CALL_CANUSEFGHW :: c.int(0xc)
+DARWIN_PROC_INFO_CALL_PIDDYNKQUEUEINFO :: c.int(0xd)
+DARWIN_PROC_INFO_CALL_UDATA_INFO :: c.int(0xe)
+
+/* mmap flags */
+MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
+MAP_FILE :: 0x0000 /* map from file (default) */
+MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
+MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
+MAP_PRIVATE :: 0x0002 /* [MF|SHM] changes are private */
+MAP_SHARED :: 0x0001 /* [MF|SHM] share changes */
+MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
+MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
+MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
+
+/* mmap prot flags */
+PROT_NONE :: 0x00 /* [MC2] no permissions */
+PROT_READ :: 0x01 /* [MC2] pages can be read */
+PROT_WRITE :: 0x02 /* [MC2] pages can be written */
+PROT_EXEC :: 0x04 /* [MC2] pages can be executed */
+
+/* For owner Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXU :: 0o000700 /* RWX mask for owner */
+PERMISSION_MASK_IRUSR :: 0o000400 /* R for owner */
+PERMISSION_MASK_IWUSR :: 0o000200 /* W for owner */
+PERMISSION_MASK_IXUSR :: 0o000100 /* X for owner */
+
+/* For group Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXG :: 0o000070 /* RWX mask for group */
+PERMISSION_MASK_IRGRP :: 0o000040 /* R for group */
+PERMISSION_MASK_IWGRP :: 0o000020 /* W for group */
+PERMISSION_MASK_IXGRP :: 0o000010 /* X for group */
+
+/* For other Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_IRWXO :: 0o000007 /* RWX mask for other */
+PERMISSION_MASK_IROTH :: 0o000004 /* R for other */
+PERMISSION_MASK_IWOTH :: 0o000002 /* W for other */
+PERMISSION_MASK_IXOTH :: 0o000001 /* X for other */
+
+/* Special Mode/Permission Flags for Open etc. */
+PERMISSION_MASK_ISUID :: 0o004000 /* set user id on execution */
+PERMISSION_MASK_ISGID :: 0o002000 /* set group id on execution */
+PERMISSION_MASK_ISVTX :: 0o001000 /* save swapped text even after use */
+
+OPEN_FLAG_RDONLY :: 0x0000 /* open for reading only */
+OPEN_FLAG_WRONLY :: 0x0001 /* open for writing only */
+OPEN_FLAG_RDWR :: 0x0002 /* open for reading and writing */
+
+/* mask for above rd/wr/rdwr flags */
+MASK_ACCMODE :: 0x0003
+
+OPEN_FLAG_NONBLOCK :: 0x00000004 /* no delay */
+OPEN_FLAG_APPEND :: 0x00000008 /* set append mode */
+OPEN_FLAG_CREAT :: 0x00000200 /* create if nonexistant */
+OPEN_FLAG_TRUNC :: 0x00000400 /* truncate to zero length */
+OPEN_FLAG_EXCL :: 0x00000800 /* error if already exists */
+OPEN_FLAG_SHLOCK :: 0x00000010 /* open with shared file lock */
+OPEN_FLAG_EXLOCK :: 0x00000020 /* open with exclusive file lock */
+OPEN_FLAG_DIRECTORY :: 0x00100000 /* restrict open to only directories */
+OPEN_FLAG_NOFOLLOW :: 0x00000100 /* don't follow symlinks */
+OPEN_FLAG_SYMLINK :: 0x00200000 /* allow open of a symlink */
+OPEN_FLAG_EVTONLY :: 0x00008000 /* descriptor requested for event notifications only */
+OPEN_FLAG_CLOEXEC :: 0x01000000 /* causes the descriptor to be closed if you use any of the exec like functions */
+OPEN_FLAG_NOFOLLOW_ANY :: 0x20000000 /* no symlinks allowed in path */
+
+/* bsd/sys/param.h */
+DARWIN_MAXCOMLEN :: 16
+
+/*--==========================================================================--*/
+
+__darwin_ino64_t :: u64
+__darwin_time_t :: u32
+__darwin_dev_t :: i32
+__darwin_mode_t :: u16
+__darwin_off_t :: i64
+__darwin_blkcnt_t :: i64
+__darwin_blksize_t :: i32
+__darwin_pid_t :: i32
+__darwin_suseconds_t :: i32
+
+time_t :: __darwin_time_t
+dev_t :: __darwin_dev_t
+mode_t :: u16
+nlink_t :: u16
+uid_t :: u16
+gid_t :: u16
+off_t :: __darwin_off_t
+blkcnt_t :: __darwin_blkcnt_t
+blksize_t :: __darwin_blksize_t
+pid_t :: __darwin_pid_t
+
+stat :: __DARWIN_STRUCT_STAT64
+timeval :: _STRUCT_TIMEVAL
+
+/*--==========================================================================--*/
+
+/* sys/stat.h */
+__DARWIN_STRUCT_STAT64 :: struct {
+ st_dev: dev_t, /* [XSI] ID of device containing file */
+ st_mode: mode_t, /* [XSI] Mode of file (see below) */
+ st_nlink: nlink_t, /* [XSI] Number of hard links */
+ st_ino: __darwin_ino64_t, /* [XSI] File serial number */
+ st_uid_t: uid_t, /* [XSI] User ID of the file */
+ st_gid_t: gid_t, /* [XSI] Group ID of the file */
+ st_rdev: dev_t, /* [XSI] Device ID */
+
+ // __DARWIN_STRUCT_STAT64_TIMES
+ st_atime: time_t, /* [XSI] Time of last access */
+ st_atimensec: i32, /* nsec of last access */
+ st_mtime: time_t, /* [XSI] Last data modification time */
+ st_mtimensec: i32, /* last data modification nsec */
+ st_ctime: time_t, /* [XSI] Time of last status change */
+ st_ctimensec: u32, /* nsec of last status change */
+ st_birthtime: time_t, /* File creation time(birth) */
+ st_birthtimensec: i32, /* nsec of File creation time */
+ // end __DARWIN_STRUCT_STAT64_TIMES
+
+ st_size: off_t, /* [XSI] file size, in bytes */
+ st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
+ st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
+ st_flags: u32, /* user defined flags for file */
+ st_gen: u32, /* file generation number */
+ st_lspare: i32, /* RESERVED: DO NOT USE! */
+ st_qspare: [2]i64, /* RESERVED: DO NOT USE! */
+}
+
+/* sys/_types/_timeval.h */
+_STRUCT_TIMEVAL :: struct {
+ tv_sec: __darwin_time_t, /* seconds */
+ tv_usec: __darwin_suseconds_t, /* microseconds */
+}
+
+/* pwd.h */
+_Password_Entry :: struct {
+ pw_name: cstring, /* username */
+ pw_passwd: cstring, /* user password */
+ pw_uid: i32, /* user ID */
+ pw_gid: i32, /* group ID */
+ pw_change: u64, /* password change time */
+ pw_class: cstring, /* user access class */
+ pw_gecos: cstring, /* full user name */
+ pw_dir: cstring, /* home directory */
+ pw_shell: cstring, /* shell program */
+ pw_expire: u64, /* account expiration */
+ pw_fields: i32, /* filled fields */
+}
+
+/* processinfo.h */
+_Proc_Bsdinfo :: struct {
+ pbi_flags: u32, /* if is 64bit; emulated etc */
+ pbi_status: u32,
+ pbi_xstatus: u32,
+ pbi_pid: u32,
+ pbi_ppid: u32,
+ pbi_uid: u32,
+ pbi_gid: u32,
+ pbi_ruid: u32,
+ pbi_rgid: u32,
+ pbi_svuid: u32,
+ pbi_svgid: u32,
+ res: u32,
+ pbi_comm: [DARWIN_MAXCOMLEN]u8,
+ pbi_name: [2 * DARWIN_MAXCOMLEN]u8, /* empty if no name is registered */
+ pbi_nfiles: u32,
+ pbi_pgid: u32,
+ pbi_pjobc: u32,
+ e_tdev: u32, /* controlling tty dev */
+ e_tpgid: u32, /* tty process group id */
+ pbi_nice: i32,
+ pbi_start_tvsec: u64,
+ pbi_start_tvusec: u64,
+}
+
+/*--==========================================================================--*/
+
+syscall_fsync :: #force_inline proc(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 {
+ 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 {
+ 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 {
+ 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 {
+ return !(cast(bool)intrinsics.syscall(unix_offset_syscall(.close), uintptr(fd)))
+}
+
+syscall_fchmod :: #force_inline proc(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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ return cast(i64)intrinsics.syscall(unix_offset_syscall(.lseek), uintptr(fd), uintptr(offset), uintptr(whence))
+}
+
+syscall_gettid :: #force_inline proc() -> u64 {
+ return cast(u64)intrinsics.syscall(unix_offset_syscall(.gettid))
+}
+
+syscall_fstat :: #force_inline proc(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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.fsctl), uintptr(fd), uintptr(cmd), uintptr(other))
+}
+
+syscall_exit :: #force_inline proc(code: c.int) {
+ intrinsics.syscall(unix_offset_syscall(.exit), uintptr(code))
+}
+
+syscall_kill :: #force_inline proc(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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ return cast(c.int)intrinsics.syscall(unix_offset_syscall(.getentropy), uintptr(buf), uintptr(buflen))
+}
+
+syscall_pipe :: #force_inline proc(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 {
+ 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 {
+ 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/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin
index e5b0087b1..dd5306417 100644
--- a/core/sys/unix/pthread_freebsd.odin
+++ b/core/sys/unix/pthread_freebsd.odin
@@ -1,76 +1,76 @@
//+build freebsd
package unix
-import "core:c";
+import "core:c"
-pthread_t :: distinct u64;
-// pthread_t :: struct #align 16 { x: u64 };
+pthread_t :: distinct u64
+// pthread_t :: struct #align 16 { x: u64 }
-PTHREAD_COND_T_SIZE :: 8;
+PTHREAD_COND_T_SIZE :: 8
-PTHREAD_MUTEXATTR_T_SIZE :: 8;
-PTHREAD_CONDATTR_T_SIZE :: 8;
-PTHREAD_RWLOCKATTR_T_SIZE :: 8;
-PTHREAD_BARRIERATTR_T_SIZE :: 8;
+PTHREAD_MUTEXATTR_T_SIZE :: 8
+PTHREAD_CONDATTR_T_SIZE :: 8
+PTHREAD_RWLOCKATTR_T_SIZE :: 8
+PTHREAD_BARRIERATTR_T_SIZE :: 8
// WARNING: The sizes of these things are different yet again
// on non-X86!
when size_of(int) == 8 {
- PTHREAD_ATTR_T_SIZE :: 8;
- PTHREAD_MUTEX_T_SIZE :: 8;
- PTHREAD_RWLOCK_T_SIZE :: 8;
- PTHREAD_BARRIER_T_SIZE :: 8;
+ PTHREAD_ATTR_T_SIZE :: 8
+ PTHREAD_MUTEX_T_SIZE :: 8
+ PTHREAD_RWLOCK_T_SIZE :: 8
+ PTHREAD_BARRIER_T_SIZE :: 8
} else when size_of(int) == 4 { // TODO
- PTHREAD_ATTR_T_SIZE :: 32;
- PTHREAD_MUTEX_T_SIZE :: 32;
- PTHREAD_RWLOCK_T_SIZE :: 44;
- PTHREAD_BARRIER_T_SIZE :: 20;
+ PTHREAD_ATTR_T_SIZE :: 32
+ PTHREAD_MUTEX_T_SIZE :: 32
+ PTHREAD_RWLOCK_T_SIZE :: 44
+ PTHREAD_BARRIER_T_SIZE :: 20
}
pthread_cond_t :: struct #align 16 {
_: [PTHREAD_COND_T_SIZE] c.char,
-};
+}
pthread_mutex_t :: struct #align 16 {
_: [PTHREAD_MUTEX_T_SIZE] c.char,
-};
+}
pthread_rwlock_t :: struct #align 16 {
_: [PTHREAD_RWLOCK_T_SIZE] c.char,
-};
+}
pthread_barrier_t :: struct #align 16 {
_: [PTHREAD_BARRIER_T_SIZE] c.char,
-};
+}
pthread_attr_t :: struct #align 16 {
_: [PTHREAD_ATTR_T_SIZE] c.char,
-};
+}
pthread_condattr_t :: struct #align 16 {
_: [PTHREAD_CONDATTR_T_SIZE] c.char,
-};
+}
pthread_mutexattr_t :: struct #align 16 {
_: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
-};
+}
pthread_rwlockattr_t :: struct #align 16 {
_: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
-};
+}
pthread_barrierattr_t :: struct #align 16 {
_: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
-};
+}
-PTHREAD_MUTEX_ERRORCHECK :: 1;
-PTHREAD_MUTEX_RECURSIVE :: 2;
-PTHREAD_MUTEX_NORMAL :: 3;
+PTHREAD_MUTEX_ERRORCHECK :: 1
+PTHREAD_MUTEX_RECURSIVE :: 2
+PTHREAD_MUTEX_NORMAL :: 3
-PTHREAD_CREATE_JOINABLE :: 0;
-PTHREAD_CREATE_DETACHED :: 1;
-PTHREAD_INHERIT_SCHED :: 4;
-PTHREAD_EXPLICIT_SCHED :: 0;
-PTHREAD_PROCESS_PRIVATE :: 0;
-PTHREAD_PROCESS_SHARED :: 1;
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_CREATE_DETACHED :: 1
+PTHREAD_INHERIT_SCHED :: 4
+PTHREAD_EXPLICIT_SCHED :: 0
+PTHREAD_PROCESS_PRIVATE :: 0
+PTHREAD_PROCESS_SHARED :: 1
-SCHED_FIFO :: 1;
-SCHED_OTHER :: 2;
-SCHED_RR :: 3; // Round robin.
+SCHED_FIFO :: 1
+SCHED_OTHER :: 2
+SCHED_RR :: 3 // Round robin.
sched_param :: struct {
@@ -98,17 +98,17 @@ foreign import "system:pthread"
foreign pthread {
// create named semaphore.
// used in process-shared semaphores.
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---;
+ sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---;
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---;
- sem_post :: proc(sem: ^sem_t) -> c.int ---;
- sem_wait :: proc(sem: ^sem_t) -> c.int ---;
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---;
- // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---;
+ sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+ sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+ sem_post :: proc(sem: ^sem_t) -> c.int ---
+ sem_wait :: proc(sem: ^sem_t) -> c.int ---
+ sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+ // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
// NOTE: unclear whether pthread_yield is well-supported on Linux systems,
// see https://linux.die.net/man/3/pthread_yield
- pthread_yield :: proc() ---;
+ pthread_yield :: proc() ---
}
diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin
new file mode 100644
index 000000000..c855f95c0
--- /dev/null
+++ b/core/sys/unix/pthread_openbsd.odin
@@ -0,0 +1,65 @@
+//+build openbsd
+package unix
+
+import "core:c"
+
+pthread_t :: distinct rawptr
+pthread_attr_t :: distinct rawptr
+pthread_mutex_t :: distinct rawptr
+pthread_mutexattr_t :: distinct rawptr
+pthread_cond_t :: distinct rawptr
+pthread_condattr_t :: distinct rawptr
+pthread_rwlock_t :: distinct rawptr
+pthread_rwlockattr_t :: distinct rawptr
+pthread_barrier_t :: distinct rawptr
+pthread_barrierattr_t :: distinct rawptr
+pthread_spinlock_t :: distinct rawptr
+
+pthread_key_t :: distinct c.int
+pthread_once_t :: struct {
+ state: c.int,
+ mutex: pthread_mutex_t,
+}
+
+PTHREAD_MUTEX_ERRORCHECK :: 1
+PTHREAD_MUTEX_RECURSIVE :: 2
+PTHREAD_MUTEX_NORMAL :: 3
+PTHREAD_MUTEX_STRICT_NP :: 4
+
+PTHREAD_DETACHED :: 0x1
+PTHREAD_SCOPE_SYSTEM :: 0x2
+PTHREAD_INHERIT_SCHED :: 0x4
+PTHREAD_NOFLOAT :: 0x8
+
+PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
+PTHREAD_CREATE_JOINABLE :: 0
+PTHREAD_SCOPE_PROCESS :: 0
+PTHREAD_EXPLICIT_SCHED :: 0
+
+SCHED_FIFO :: 1
+SCHED_OTHER :: 2
+SCHED_RR :: 3
+
+sched_param :: struct {
+ sched_priority: c.int,
+}
+
+sem_t :: distinct rawptr
+
+foreign import libc "system:c"
+
+@(default_calling_convention="c")
+foreign libc {
+ sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
+
+ sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
+ sem_destroy :: proc(sem: ^sem_t) -> c.int ---
+ sem_post :: proc(sem: ^sem_t) -> c.int ---
+ sem_wait :: proc(sem: ^sem_t) -> c.int ---
+ sem_trywait :: proc(sem: ^sem_t) -> c.int ---
+ //sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
+
+ // NOTE: unclear whether pthread_yield is well-supported on Linux systems,
+ // see https://linux.die.net/man/3/pthread_yield
+ pthread_yield :: proc() ---
+}
diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin
index ccd8f7844..62e3701ab 100644
--- a/core/sys/unix/pthread_unix.odin
+++ b/core/sys/unix/pthread_unix.odin
@@ -1,4 +1,4 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package unix
foreign import "system:pthread"
diff --git a/core/sys/unix/syscalls_linux.odin b/core/sys/unix/syscalls_linux.odin
index 659eedfbb..3d06d42d4 100644
--- a/core/sys/unix/syscalls_linux.odin
+++ b/core/sys/unix/syscalls_linux.odin
@@ -15,38 +15,1505 @@ import "core:intrinsics"
// 386: arch/x86/entry/syscalls/sycall_32.tbl
// arm: arch/arm/tools/syscall.tbl
-when ODIN_ARCH == "amd64" {
+when ODIN_ARCH == .amd64 {
+ SYS_read : uintptr : 0
+ SYS_write : uintptr : 1
+ SYS_open : uintptr : 2
+ SYS_close : uintptr : 3
+ SYS_stat : uintptr : 4
+ SYS_fstat : uintptr : 5
+ SYS_lstat : uintptr : 6
+ SYS_poll : uintptr : 7
+ SYS_lseek : uintptr : 8
SYS_mmap : uintptr : 9
SYS_mprotect : uintptr : 10
SYS_munmap : uintptr : 11
+ SYS_brk : uintptr : 12
+ SYS_rt_sigaction : uintptr : 13
+ SYS_rt_sigprocmask : uintptr : 14
+ SYS_rt_sigreturn : uintptr : 15
+ SYS_ioctl : uintptr : 16
+ SYS_pread : uintptr : 17
+ SYS_pwrite : uintptr : 18
+ SYS_readv : uintptr : 19
+ SYS_writev : uintptr : 20
+ SYS_access : uintptr : 21
+ SYS_pipe : uintptr : 22
+ SYS_select : uintptr : 23
+ SYS_sched_yield : uintptr : 24
+ SYS_mremap : uintptr : 25
+ SYS_msync : uintptr : 26
+ SYS_mincore : uintptr : 27
SYS_madvise : uintptr : 28
- SYS_futex : uintptr : 202
+ SYS_shmget : uintptr : 29
+ SYS_shmat : uintptr : 30
+ SYS_shmctl : uintptr : 31
+ SYS_dup : uintptr : 32
+ SYS_dup2 : uintptr : 33
+ SYS_pause : uintptr : 34
+ SYS_nanosleep : uintptr : 35
+ SYS_getitimer : uintptr : 36
+ SYS_alarm : uintptr : 37
+ SYS_setitimer : uintptr : 38
+ SYS_getpid : uintptr : 39
+ SYS_sendfile : uintptr : 40
+ SYS_socket : uintptr : 41
+ SYS_connect : uintptr : 42
+ SYS_accept : uintptr : 43
+ SYS_sendto : uintptr : 44
+ SYS_recvfrom : uintptr : 45
+ SYS_sendmsg : uintptr : 46
+ SYS_recvmsg : uintptr : 47
+ SYS_shutdown : uintptr : 48
+ SYS_bind : uintptr : 49
+ SYS_listen : uintptr : 50
+ SYS_getsockname : uintptr : 51
+ SYS_getpeername : uintptr : 52
+ SYS_socketpair : uintptr : 53
+ SYS_setsockopt : uintptr : 54
+ SYS_getsockopt : uintptr : 55
+ SYS_clone : uintptr : 56
+ SYS_fork : uintptr : 57
+ SYS_vfork : uintptr : 58
+ SYS_execve : uintptr : 59
+ SYS_exit : uintptr : 60
+ SYS_wait4 : uintptr : 61
+ SYS_kill : uintptr : 62
+ SYS_uname : uintptr : 63
+ SYS_semget : uintptr : 64
+ SYS_semop : uintptr : 65
+ SYS_semctl : uintptr : 66
+ SYS_shmdt : uintptr : 67
+ SYS_msgget : uintptr : 68
+ SYS_msgsnd : uintptr : 69
+ SYS_msgrcv : uintptr : 70
+ SYS_msgctl : uintptr : 71
+ SYS_fcntl : uintptr : 72
+ SYS_flock : uintptr : 73
+ SYS_fsync : uintptr : 74
+ SYS_fdatasync : uintptr : 75
+ SYS_truncate : uintptr : 76
+ SYS_ftruncate : uintptr : 77
+ SYS_getdents : uintptr : 78
+ SYS_getcwd : uintptr : 79
+ SYS_chdir : uintptr : 80
+ SYS_fchdir : uintptr : 81
+ SYS_rename : uintptr : 82
+ SYS_mkdir : uintptr : 83
+ SYS_rmdir : uintptr : 84
+ SYS_creat : uintptr : 85
+ SYS_link : uintptr : 86
+ SYS_unlink : uintptr : 87
+ SYS_symlink : uintptr : 88
+ SYS_readlink : uintptr : 89
+ SYS_chmod : uintptr : 90
+ SYS_fchmod : uintptr : 91
+ SYS_chown : uintptr : 92
+ SYS_fchown : uintptr : 93
+ SYS_lchown : uintptr : 94
+ SYS_umask : uintptr : 95
+ SYS_gettimeofday : uintptr : 96
+ SYS_getrlimit : uintptr : 97
+ SYS_getrusage : uintptr : 98
+ SYS_sysinfo : uintptr : 99
+ SYS_times : uintptr : 100
+ SYS_ptrace : uintptr : 101
+ SYS_getuid : uintptr : 102
+ SYS_syslog : uintptr : 103
+ SYS_getgid : uintptr : 104
+ SYS_setuid : uintptr : 105
+ SYS_setgid : uintptr : 106
+ SYS_geteuid : uintptr : 107
+ SYS_getegid : uintptr : 108
+ SYS_setpgid : uintptr : 109
+ SYS_getppid : uintptr : 110
+ SYS_getpgrp : uintptr : 111
+ SYS_setsid : uintptr : 112
+ SYS_setreuid : uintptr : 113
+ SYS_setregid : uintptr : 114
+ SYS_getgroups : uintptr : 115
+ SYS_setgroups : uintptr : 116
+ SYS_setresuid : uintptr : 117
+ SYS_getresuid : uintptr : 118
+ SYS_setresgid : uintptr : 119
+ SYS_getresgid : uintptr : 120
+ SYS_getpgid : uintptr : 121
+ SYS_setfsuid : uintptr : 122
+ SYS_setfsgid : uintptr : 123
+ SYS_getsid : uintptr : 124
+ SYS_capget : uintptr : 125
+ SYS_capset : uintptr : 126
+ SYS_rt_sigpending : uintptr : 127
+ SYS_rt_sigtimedwait : uintptr : 128
+ SYS_rt_sigqueueinfo : uintptr : 129
+ SYS_rt_sigsuspend : uintptr : 130
+ SYS_sigaltstack : uintptr : 131
+ SYS_utime : uintptr : 132
+ SYS_mknod : uintptr : 133
+ SYS_uselib : uintptr : 134
+ SYS_personality : uintptr : 135
+ SYS_ustat : uintptr : 136
+ SYS_statfs : uintptr : 137
+ SYS_fstatfs : uintptr : 138
+ SYS_sysfs : uintptr : 139
+ SYS_getpriority : uintptr : 140
+ SYS_setpriority : uintptr : 141
+ SYS_sched_setparam : uintptr : 142
+ SYS_sched_getparam : uintptr : 143
+ SYS_sched_setscheduler : uintptr : 144
+ SYS_sched_getscheduler : uintptr : 145
+ SYS_sched_get_priority_max : uintptr : 146
+ SYS_sched_get_priority_min : uintptr : 147
+ SYS_sched_rr_get_interval : uintptr : 148
+ SYS_mlock : uintptr : 149
+ SYS_munlock : uintptr : 150
+ SYS_mlockall : uintptr : 151
+ SYS_munlockall : uintptr : 152
+ SYS_vhangup : uintptr : 153
+ SYS_modify_ldt : uintptr : 154
+ SYS_pivot_root : uintptr : 155
+ SYS__sysctl : uintptr : 156
+ SYS_prctl : uintptr : 157
+ SYS_arch_prctl : uintptr : 158
+ SYS_adjtimex : uintptr : 159
+ SYS_setrlimit : uintptr : 160
+ SYS_chroot : uintptr : 161
+ SYS_sync : uintptr : 162
+ SYS_acct : uintptr : 163
+ SYS_settimeofday : uintptr : 164
+ SYS_mount : uintptr : 165
+ SYS_umount2 : uintptr : 166
+ SYS_swapon : uintptr : 167
+ SYS_swapoff : uintptr : 168
+ SYS_reboot : uintptr : 169
+ SYS_sethostname : uintptr : 170
+ SYS_setdomainname : uintptr : 171
+ SYS_iopl : uintptr : 172
+ SYS_ioperm : uintptr : 173
+ SYS_create_module : uintptr : 174
+ SYS_init_module : uintptr : 175
+ SYS_delete_module : uintptr : 176
+ SYS_get_kernel_syms : uintptr : 177
+ SYS_query_module : uintptr : 178
+ SYS_quotactl : uintptr : 179
+ SYS_nfsservctl : uintptr : 180
+ SYS_getpmsg : uintptr : 181
+ SYS_putpmsg : uintptr : 182
+ SYS_afs_syscall : uintptr : 183
+ SYS_tuxcall : uintptr : 184
+ SYS_security : uintptr : 185
SYS_gettid : uintptr : 186
+ SYS_readahead : uintptr : 187
+ SYS_setxattr : uintptr : 188
+ SYS_lsetxattr : uintptr : 189
+ SYS_fsetxattr : uintptr : 190
+ SYS_getxattr : uintptr : 191
+ SYS_lgetxattr : uintptr : 192
+ SYS_fgetxattr : uintptr : 193
+ SYS_listxattr : uintptr : 194
+ SYS_llistxattr : uintptr : 195
+ SYS_flistxattr : uintptr : 196
+ SYS_removexattr : uintptr : 197
+ SYS_lremovexattr : uintptr : 198
+ SYS_fremovexattr : uintptr : 199
+ SYS_tkill : uintptr : 200
+ SYS_time : uintptr : 201
+ SYS_futex : uintptr : 202
+ SYS_sched_setaffinity : uintptr : 203
+ SYS_sched_getaffinity : uintptr : 204
+ SYS_set_thread_area : uintptr : 205
+ SYS_io_setup : uintptr : 206
+ SYS_io_destroy : uintptr : 207
+ SYS_io_getevents : uintptr : 208
+ SYS_io_submit : uintptr : 209
+ SYS_io_cancel : uintptr : 210
+ SYS_get_thread_area : uintptr : 211
+ SYS_lookup_dcookie : uintptr : 212
+ SYS_epoll_create : uintptr : 213
+ SYS_epoll_ctl_old : uintptr : 214
+ SYS_epoll_wait_old : uintptr : 215
+ SYS_remap_file_pages : uintptr : 216
+ SYS_getdents64 : uintptr : 217
+ SYS_set_tid_address : uintptr : 218
+ SYS_restart_syscall : uintptr : 219
+ SYS_semtimedop : uintptr : 220
+ SYS_fadvise64 : uintptr : 221
+ SYS_timer_create : uintptr : 222
+ SYS_timer_settime : uintptr : 223
+ SYS_timer_gettime : uintptr : 224
+ SYS_timer_getoverrun : uintptr : 225
+ SYS_timer_delete : uintptr : 226
+ SYS_clock_settime : uintptr : 227
+ SYS_clock_gettime : uintptr : 228
+ SYS_clock_getres : uintptr : 229
+ SYS_clock_nanosleep : uintptr : 230
+ SYS_exit_group : uintptr : 231
+ SYS_epoll_wait : uintptr : 232
+ SYS_epoll_ctl : uintptr : 233
+ SYS_tgkill : uintptr : 234
+ SYS_utimes : uintptr : 235
+ SYS_vserver : uintptr : 236
+ SYS_mbind : uintptr : 237
+ SYS_set_mempolicy : uintptr : 238
+ SYS_get_mempolicy : uintptr : 239
+ SYS_mq_open : uintptr : 240
+ SYS_mq_unlink : uintptr : 241
+ SYS_mq_timedsend : uintptr : 242
+ SYS_mq_timedreceive : uintptr : 243
+ SYS_mq_notify : uintptr : 244
+ SYS_mq_getsetattr : uintptr : 245
+ SYS_kexec_load : uintptr : 246
+ SYS_waitid : uintptr : 247
+ SYS_add_key : uintptr : 248
+ SYS_request_key : uintptr : 249
+ SYS_keyctl : uintptr : 250
+ SYS_ioprio_set : uintptr : 251
+ SYS_ioprio_get : uintptr : 252
+ SYS_inotify_init : uintptr : 253
+ SYS_inotify_add_watch : uintptr : 254
+ SYS_inotify_rm_watch : uintptr : 255
+ SYS_migrate_pages : uintptr : 256
+ SYS_openat : uintptr : 257
+ SYS_mkdirat : uintptr : 258
+ SYS_mknodat : uintptr : 259
+ SYS_fchownat : uintptr : 260
+ SYS_futimesat : uintptr : 261
+ SYS_fstatat : uintptr : 262
+ SYS_unlinkat : uintptr : 263
+ SYS_renameat : uintptr : 264
+ SYS_linkat : uintptr : 265
+ SYS_symlinkat : uintptr : 266
+ SYS_readlinkat : uintptr : 267
+ SYS_fchmodat : uintptr : 268
+ SYS_faccessat : uintptr : 269
+ SYS_pselect6 : uintptr : 270
+ SYS_ppoll : uintptr : 271
+ SYS_unshare : uintptr : 272
+ SYS_set_robust_list : uintptr : 273
+ SYS_get_robust_list : uintptr : 274
+ SYS_splice : uintptr : 275
+ SYS_tee : uintptr : 276
+ SYS_sync_file_range : uintptr : 277
+ SYS_vmsplice : uintptr : 278
+ SYS_move_pages : uintptr : 279
+ SYS_utimensat : uintptr : 280
+ SYS_epoll_pwait : uintptr : 281
+ SYS_signalfd : uintptr : 282
+ SYS_timerfd_create : uintptr : 283
+ SYS_eventfd : uintptr : 284
+ SYS_fallocate : uintptr : 285
+ SYS_timerfd_settime : uintptr : 286
+ SYS_timerfd_gettime : uintptr : 287
+ SYS_accept4 : uintptr : 288
+ SYS_signalfd4 : uintptr : 289
+ SYS_eventfd2 : uintptr : 290
+ SYS_epoll_create1 : uintptr : 291
+ SYS_dup3 : uintptr : 292
+ SYS_pipe2 : uintptr : 293
+ SYS_inotify_init1 : uintptr : 294
+ SYS_preadv : uintptr : 295
+ SYS_pwritev : uintptr : 296
+ SYS_rt_tgsigqueueinfo : uintptr : 297
+ SYS_perf_event_open : uintptr : 298
+ SYS_recvmmsg : uintptr : 299
+ SYS_fanotify_init : uintptr : 300
+ SYS_fanotify_mark : uintptr : 301
+ SYS_prlimit64 : uintptr : 302
+ SYS_name_to_handle_at : uintptr : 303
+ SYS_open_by_handle_at : uintptr : 304
+ SYS_clock_adjtime : uintptr : 305
+ SYS_syncfs : uintptr : 306
+ SYS_sendmmsg : uintptr : 307
+ SYS_setns : uintptr : 308
+ SYS_getcpu : uintptr : 309
+ SYS_process_vm_readv : uintptr : 310
+ SYS_process_vm_writev : uintptr : 311
+ SYS_kcmp : uintptr : 312
+ SYS_finit_module : uintptr : 313
+ SYS_sched_setattr : uintptr : 314
+ SYS_sched_getattr : uintptr : 315
+ SYS_renameat2 : uintptr : 316
+ SYS_seccomp : uintptr : 317
SYS_getrandom : uintptr : 318
-} else when ODIN_ARCH == "arm64" {
- SYS_mmap : uintptr : 222
- SYS_mprotect : uintptr : 226
- SYS_munmap : uintptr : 215
- SYS_madvise : uintptr : 233
+ SYS_memfd_create : uintptr : 319
+ SYS_kexec_file_load : uintptr : 320
+ SYS_bpf : uintptr : 321
+ SYS_execveat : uintptr : 322
+ SYS_userfaultfd : uintptr : 323
+ SYS_membarrier : uintptr : 324
+ SYS_mlock2 : uintptr : 325
+ SYS_copy_file_range : uintptr : 326
+ SYS_preadv2 : uintptr : 327
+ SYS_pwritev2 : uintptr : 328
+ SYS_pkey_mprotect : uintptr : 329
+ SYS_pkey_alloc : uintptr : 330
+ SYS_pkey_free : uintptr : 331
+ SYS_statx : uintptr : 332
+ SYS_io_pgetevents : uintptr : 333
+ SYS_rseq : uintptr : 334
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+ SYS_memfd_secret : uintptr : 447
+} else when ODIN_ARCH == .arm64 {
+ SYS_io_setup : uintptr : 0
+ SYS_io_destroy : uintptr : 1
+ SYS_io_submit : uintptr : 2
+ SYS_io_cancel : uintptr : 3
+ SYS_io_getevents : uintptr : 4
+ SYS_setxattr : uintptr : 5
+ SYS_lsetxattr : uintptr : 6
+ SYS_fsetxattr : uintptr : 7
+ SYS_getxattr : uintptr : 8
+ SYS_lgetxattr : uintptr : 9
+ SYS_fgetxattr : uintptr : 10
+ SYS_listxattr : uintptr : 11
+ SYS_llistxattr : uintptr : 12
+ SYS_flistxattr : uintptr : 13
+ SYS_removexattr : uintptr : 14
+ SYS_lremovexattr : uintptr : 15
+ SYS_fremovexattr : uintptr : 16
+ SYS_getcwd : uintptr : 17
+ SYS_lookup_dcookie : uintptr : 18
+ SYS_eventfd2 : uintptr : 19
+ SYS_epoll_create1 : uintptr : 20
+ SYS_epoll_ctl : uintptr : 21
+ SYS_epoll_pwait : uintptr : 22
+ SYS_dup : uintptr : 23
+ SYS_dup3 : uintptr : 24
+ SYS_fcntl : uintptr : 25
+ SYS_inotify_init1 : uintptr : 26
+ SYS_inotify_add_watch : uintptr : 27
+ SYS_inotify_rm_watch : uintptr : 28
+ SYS_ioctl : uintptr : 29
+ SYS_ioprio_set : uintptr : 30
+ SYS_ioprio_get : uintptr : 31
+ SYS_flock : uintptr : 32
+ SYS_mknodat : uintptr : 33
+ SYS_mkdirat : uintptr : 34
+ SYS_unlinkat : uintptr : 35
+ SYS_symlinkat : uintptr : 36
+ SYS_linkat : uintptr : 37
+ SYS_renameat : uintptr : 38
+ SYS_umount2 : uintptr : 39
+ SYS_mount : uintptr : 40
+ SYS_pivot_root : uintptr : 41
+ SYS_nfsservctl : uintptr : 42
+ SYS_statfs : uintptr : 43
+ SYS_fstatfs : uintptr : 44
+ SYS_truncate : uintptr : 45
+ SYS_ftruncate : uintptr : 46
+ SYS_fallocate : uintptr : 47
+ SYS_faccessat : uintptr : 48
+ SYS_chdir : uintptr : 49
+ SYS_fchdir : uintptr : 50
+ SYS_chroot : uintptr : 51
+ SYS_fchmod : uintptr : 52
+ SYS_fchmodat : uintptr : 53
+ SYS_fchownat : uintptr : 54
+ SYS_fchown : uintptr : 55
+ SYS_openat : uintptr : 56
+ SYS_close : uintptr : 57
+ SYS_vhangup : uintptr : 58
+ SYS_pipe2 : uintptr : 59
+ SYS_quotactl : uintptr : 60
+ SYS_getdents64 : uintptr : 61
+ SYS_lseek : uintptr : 62
+ SYS_read : uintptr : 63
+ SYS_write : uintptr : 64
+ SYS_readv : uintptr : 65
+ SYS_writev : uintptr : 66
+ SYS_pread64 : uintptr : 67
+ SYS_pwrite64 : uintptr : 68
+ SYS_preadv : uintptr : 69
+ SYS_pwritev : uintptr : 70
+ SYS_sendfile : uintptr : 71
+ SYS_pselect6 : uintptr : 72
+ SYS_ppoll : uintptr : 73
+ SYS_signalfd4 : uintptr : 74
+ SYS_vmsplice : uintptr : 75
+ SYS_splice : uintptr : 76
+ SYS_tee : uintptr : 77
+ SYS_readlinkat : uintptr : 78
+ SYS_fstatat : uintptr : 79
+ SYS_fstat : uintptr : 80
+ SYS_sync : uintptr : 81
+ SYS_fsync : uintptr : 82
+ SYS_fdatasync : uintptr : 83
+ SYS_sync_file_range : uintptr : 84
+ SYS_timerfd_create : uintptr : 85
+ SYS_timerfd_settime : uintptr : 86
+ SYS_timerfd_gettime : uintptr : 87
+ SYS_utimensat : uintptr : 88
+ SYS_acct : uintptr : 89
+ SYS_capget : uintptr : 90
+ SYS_capset : uintptr : 91
+ SYS_personality : uintptr : 92
+ SYS_exit : uintptr : 93
+ SYS_exit_group : uintptr : 94
+ SYS_waitid : uintptr : 95
+ SYS_set_tid_address : uintptr : 96
+ SYS_unshare : uintptr : 97
SYS_futex : uintptr : 98
+ SYS_set_robust_list : uintptr : 99
+ SYS_get_robust_list : uintptr : 100
+ SYS_nanosleep : uintptr : 101
+ SYS_getitimer : uintptr : 102
+ SYS_setitimer : uintptr : 103
+ SYS_kexec_load : uintptr : 104
+ SYS_init_module : uintptr : 105
+ SYS_delete_module : uintptr : 106
+ SYS_timer_create : uintptr : 107
+ SYS_timer_gettime : uintptr : 108
+ SYS_timer_getoverrun : uintptr : 109
+ SYS_timer_settime : uintptr : 110
+ SYS_timer_delete : uintptr : 111
+ SYS_clock_settime : uintptr : 112
+ SYS_clock_gettime : uintptr : 113
+ SYS_clock_getres : uintptr : 114
+ SYS_clock_nanosleep : uintptr : 115
+ SYS_syslog : uintptr : 116
+ SYS_ptrace : uintptr : 117
+ SYS_sched_setparam : uintptr : 118
+ SYS_sched_setscheduler : uintptr : 119
+ SYS_sched_getscheduler : uintptr : 120
+ SYS_sched_getparam : uintptr : 121
+ SYS_sched_setaffinity : uintptr : 122
+ SYS_sched_getaffinity : uintptr : 123
+ SYS_sched_yield : uintptr : 124
+ SYS_sched_get_priority_max : uintptr : 125
+ SYS_sched_get_priority_min : uintptr : 126
+ SYS_sched_rr_get_interval : uintptr : 127
+ SYS_restart_syscall : uintptr : 128
+ SYS_kill : uintptr : 129
+ SYS_tkill : uintptr : 130
+ SYS_tgkill : uintptr : 131
+ SYS_sigaltstack : uintptr : 132
+ SYS_rt_sigsuspend : uintptr : 133
+ SYS_rt_sigaction : uintptr : 134
+ SYS_rt_sigprocmask : uintptr : 135
+ SYS_rt_sigpending : uintptr : 136
+ SYS_rt_sigtimedwait : uintptr : 137
+ SYS_rt_sigqueueinfo : uintptr : 138
+ SYS_rt_sigreturn : uintptr : 139
+ SYS_setpriority : uintptr : 140
+ SYS_getpriority : uintptr : 141
+ SYS_reboot : uintptr : 142
+ SYS_setregid : uintptr : 143
+ SYS_setgid : uintptr : 144
+ SYS_setreuid : uintptr : 145
+ SYS_setuid : uintptr : 146
+ SYS_setresuid : uintptr : 147
+ SYS_getresuid : uintptr : 148
+ SYS_setresgid : uintptr : 149
+ SYS_getresgid : uintptr : 150
+ SYS_setfsuid : uintptr : 151
+ SYS_setfsgid : uintptr : 152
+ SYS_times : uintptr : 153
+ SYS_setpgid : uintptr : 154
+ SYS_getpgid : uintptr : 155
+ SYS_getsid : uintptr : 156
+ SYS_setsid : uintptr : 157
+ SYS_getgroups : uintptr : 158
+ SYS_setgroups : uintptr : 159
+ SYS_uname : uintptr : 160
+ SYS_sethostname : uintptr : 161
+ SYS_setdomainname : uintptr : 162
+ SYS_getrlimit : uintptr : 163
+ SYS_setrlimit : uintptr : 164
+ SYS_getrusage : uintptr : 165
+ SYS_umask : uintptr : 166
+ SYS_prctl : uintptr : 167
+ SYS_getcpu : uintptr : 168
+ SYS_gettimeofday : uintptr : 169
+ SYS_settimeofday : uintptr : 170
+ SYS_adjtimex : uintptr : 171
+ SYS_getpid : uintptr : 172
+ SYS_getppid : uintptr : 173
+ SYS_getuid : uintptr : 174
+ SYS_geteuid : uintptr : 175
+ SYS_getgid : uintptr : 176
+ SYS_getegid : uintptr : 177
SYS_gettid : uintptr : 178
+ SYS_sysinfo : uintptr : 179
+ SYS_mq_open : uintptr : 180
+ SYS_mq_unlink : uintptr : 181
+ SYS_mq_timedsend : uintptr : 182
+ SYS_mq_timedreceive : uintptr : 183
+ SYS_mq_notify : uintptr : 184
+ SYS_mq_getsetattr : uintptr : 185
+ SYS_msgget : uintptr : 186
+ SYS_msgctl : uintptr : 187
+ SYS_msgrcv : uintptr : 188
+ SYS_msgsnd : uintptr : 189
+ SYS_semget : uintptr : 190
+ SYS_semctl : uintptr : 191
+ SYS_semtimedop : uintptr : 192
+ SYS_semop : uintptr : 193
+ SYS_shmget : uintptr : 194
+ SYS_shmctl : uintptr : 195
+ SYS_shmat : uintptr : 196
+ SYS_shmdt : uintptr : 197
+ SYS_socket : uintptr : 198
+ SYS_socketpair : uintptr : 199
+ SYS_bind : uintptr : 200
+ SYS_listen : uintptr : 201
+ SYS_accept : uintptr : 202
+ SYS_connect : uintptr : 203
+ SYS_getsockname : uintptr : 204
+ SYS_getpeername : uintptr : 205
+ SYS_sendto : uintptr : 206
+ SYS_recvfrom : uintptr : 207
+ SYS_setsockopt : uintptr : 208
+ SYS_getsockopt : uintptr : 209
+ SYS_shutdown : uintptr : 210
+ SYS_sendmsg : uintptr : 211
+ SYS_recvmsg : uintptr : 212
+ SYS_readahead : uintptr : 213
+ SYS_brk : uintptr : 214
+ SYS_munmap : uintptr : 215
+ SYS_mremap : uintptr : 216
+ SYS_add_key : uintptr : 217
+ SYS_request_key : uintptr : 218
+ SYS_keyctl : uintptr : 219
+ SYS_clone : uintptr : 220
+ SYS_execve : uintptr : 221
+ SYS_mmap : uintptr : 222
+ SYS_fadvise64 : uintptr : 223
+ SYS_swapon : uintptr : 224
+ SYS_swapoff : uintptr : 225
+ SYS_mprotect : uintptr : 226
+ SYS_msync : uintptr : 227
+ SYS_mlock : uintptr : 228
+ SYS_munlock : uintptr : 229
+ SYS_mlockall : uintptr : 230
+ SYS_munlockall : uintptr : 231
+ SYS_mincore : uintptr : 232
+ SYS_madvise : uintptr : 233
+ SYS_remap_file_pages : uintptr : 234
+ SYS_mbind : uintptr : 235
+ SYS_get_mempolicy : uintptr : 236
+ SYS_set_mempolicy : uintptr : 237
+ SYS_migrate_pages : uintptr : 238
+ SYS_move_pages : uintptr : 239
+ SYS_rt_tgsigqueueinfo : uintptr : 240
+ SYS_perf_event_open : uintptr : 241
+ SYS_accept4 : uintptr : 242
+ SYS_recvmmsg : uintptr : 243
+ SYS_arch_specific_syscall : uintptr : 244
+ SYS_wait4 : uintptr : 260
+ SYS_prlimit64 : uintptr : 261
+ SYS_fanotify_init : uintptr : 262
+ SYS_fanotify_mark : uintptr : 263
+ SYS_clock_adjtime : uintptr : 266
+ SYS_syncfs : uintptr : 267
+ SYS_setns : uintptr : 268
+ SYS_sendmmsg : uintptr : 269
+ SYS_process_vm_readv : uintptr : 270
+ SYS_process_vm_writev : uintptr : 271
+ SYS_kcmp : uintptr : 272
+ SYS_finit_module : uintptr : 273
+ SYS_sched_setattr : uintptr : 274
+ SYS_sched_getattr : uintptr : 275
+ SYS_renameat2 : uintptr : 276
+ SYS_seccomp : uintptr : 277
SYS_getrandom : uintptr : 278
-} else when ODIN_ARCH == "386" {
- SYS_mmap : uintptr : 192 // 90 is "sys_old_mmap", we want mmap2
- SYS_mprotect : uintptr : 125
+ SYS_memfd_create : uintptr : 279
+ SYS_bpf : uintptr : 280
+ SYS_execveat : uintptr : 281
+ SYS_userfaultfd : uintptr : 282
+ SYS_membarrier : uintptr : 283
+ SYS_mlock2 : uintptr : 284
+ SYS_copy_file_range : uintptr : 285
+ SYS_preadv2 : uintptr : 286
+ SYS_pwritev2 : uintptr : 287
+ SYS_pkey_mprotect : uintptr : 288
+ SYS_pkey_alloc : uintptr : 289
+ SYS_pkey_free : uintptr : 290
+ SYS_statx : uintptr : 291
+ SYS_io_pgetevents : uintptr : 292
+ SYS_rseq : uintptr : 293
+ SYS_kexec_file_load : uintptr : 294
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+
+ SIGCHLD :: 17
+} else when ODIN_ARCH == .i386 {
+ SYS_restart_syscall : uintptr : 0
+ SYS_exit : uintptr : 1
+ SYS_fork : uintptr : 2
+ SYS_read : uintptr : 3
+ SYS_write : uintptr : 4
+ SYS_open : uintptr : 5
+ SYS_close : uintptr : 6
+ SYS_waitpid : uintptr : 7
+ SYS_creat : uintptr : 8
+ SYS_link : uintptr : 9
+ SYS_unlink : uintptr : 10
+ SYS_execve : uintptr : 11
+ SYS_chdir : uintptr : 12
+ SYS_time : uintptr : 13
+ SYS_mknod : uintptr : 14
+ SYS_chmod : uintptr : 15
+ SYS_lchown : uintptr : 16
+ SYS_break : uintptr : 17
+ SYS_oldstat : uintptr : 18
+ SYS_lseek : uintptr : 19
+ SYS_getpid : uintptr : 20
+ SYS_mount : uintptr : 21
+ SYS_umount : uintptr : 22
+ SYS_setuid : uintptr : 23
+ SYS_getuid : uintptr : 24
+ SYS_stime : uintptr : 25
+ SYS_ptrace : uintptr : 26
+ SYS_alarm : uintptr : 27
+ SYS_oldfstat : uintptr : 28
+ SYS_pause : uintptr : 29
+ SYS_utime : uintptr : 30
+ SYS_stty : uintptr : 31
+ SYS_gtty : uintptr : 32
+ SYS_access : uintptr : 33
+ SYS_nice : uintptr : 34
+ SYS_ftime : uintptr : 35
+ SYS_sync : uintptr : 36
+ SYS_kill : uintptr : 37
+ SYS_rename : uintptr : 38
+ SYS_mkdir : uintptr : 39
+ SYS_rmdir : uintptr : 40
+ SYS_dup : uintptr : 41
+ SYS_pipe : uintptr : 42
+ SYS_times : uintptr : 43
+ SYS_prof : uintptr : 44
+ SYS_brk : uintptr : 45
+ SYS_setgid : uintptr : 46
+ SYS_getgid : uintptr : 47
+ SYS_signal : uintptr : 48
+ SYS_geteuid : uintptr : 49
+ SYS_getegid : uintptr : 50
+ SYS_acct : uintptr : 51
+ SYS_umount2 : uintptr : 52
+ SYS_lock : uintptr : 53
+ SYS_ioctl : uintptr : 54
+ SYS_fcntl : uintptr : 55
+ SYS_mpx : uintptr : 56
+ SYS_setpgid : uintptr : 57
+ SYS_ulimit : uintptr : 58
+ SYS_oldolduname : uintptr : 59
+ SYS_umask : uintptr : 60
+ SYS_chroot : uintptr : 61
+ SYS_ustat : uintptr : 62
+ SYS_dup2 : uintptr : 63
+ SYS_getppid : uintptr : 64
+ SYS_getpgrp : uintptr : 65
+ SYS_setsid : uintptr : 66
+ SYS_sigaction : uintptr : 67
+ SYS_sgetmask : uintptr : 68
+ SYS_ssetmask : uintptr : 69
+ SYS_setreuid : uintptr : 70
+ SYS_setregid : uintptr : 71
+ SYS_sigsuspend : uintptr : 72
+ SYS_sigpending : uintptr : 73
+ SYS_sethostname : uintptr : 74
+ SYS_setrlimit : uintptr : 75
+ SYS_getrlimit : uintptr : 76
+ SYS_getrusage : uintptr : 77
+ SYS_gettimeofday : uintptr : 78
+ SYS_settimeofday : uintptr : 79
+ SYS_getgroups : uintptr : 80
+ SYS_setgroups : uintptr : 81
+ SYS_select : uintptr : 82
+ SYS_symlink : uintptr : 83
+ SYS_oldlstat : uintptr : 84
+ SYS_readlink : uintptr : 85
+ SYS_uselib : uintptr : 86
+ SYS_swapon : uintptr : 87
+ SYS_reboot : uintptr : 88
+ SYS_readdir : uintptr : 89
+ SYS_old_mmap : uintptr : 90 // 90 is "sys_old_mmap", we want mmap2
SYS_munmap : uintptr : 91
- SYS_madvise : uintptr : 219
- SYS_futex : uintptr : 240
- SYS_gettid : uintptr : 224
- SYS_getrandom : uintptr : 355
-} else when ODIN_ARCH == "arm" {
- SYS_mmap : uintptr : 192 // 90 is "sys_old_mmap", we want mmap2
+ SYS_truncate : uintptr : 92
+ SYS_ftruncate : uintptr : 93
+ SYS_fchmod : uintptr : 94
+ SYS_fchown : uintptr : 95
+ SYS_getpriority : uintptr : 96
+ SYS_setpriority : uintptr : 97
+ SYS_profil : uintptr : 98
+ SYS_statfs : uintptr : 99
+ SYS_fstatfs : uintptr : 100
+ SYS_ioperm : uintptr : 101
+ SYS_socketcall : uintptr : 102
+ SYS_syslog : uintptr : 103
+ SYS_setitimer : uintptr : 104
+ SYS_getitimer : uintptr : 105
+ SYS_stat : uintptr : 106
+ SYS_lstat : uintptr : 107
+ SYS_fstat : uintptr : 108
+ SYS_olduname : uintptr : 109
+ SYS_iopl : uintptr : 110
+ SYS_vhangup : uintptr : 111
+ SYS_idle : uintptr : 112
+ SYS_vm86old : uintptr : 113
+ SYS_wait4 : uintptr : 114
+ SYS_swapoff : uintptr : 115
+ SYS_sysinfo : uintptr : 116
+ SYS_ipc : uintptr : 117
+ SYS_fsync : uintptr : 118
+ SYS_sigreturn : uintptr : 119
+ SYS_clone : uintptr : 120
+ SYS_setdomainname : uintptr : 121
+ SYS_uname : uintptr : 122
+ SYS_modify_ldt : uintptr : 123
+ SYS_adjtimex : uintptr : 124
SYS_mprotect : uintptr : 125
- SYS_munmap: uintptr : 91
- SYS_madvise: uintptr : 220
+ SYS_sigprocmask : uintptr : 126
+ SYS_create_module : uintptr : 127
+ SYS_init_module : uintptr : 128
+ SYS_delete_module : uintptr : 129
+ SYS_get_kernel_syms : uintptr : 130
+ SYS_quotactl : uintptr : 131
+ SYS_getpgid : uintptr : 132
+ SYS_fchdir : uintptr : 133
+ SYS_bdflush : uintptr : 134
+ SYS_sysfs : uintptr : 135
+ SYS_personality : uintptr : 136
+ SYS_afs_syscall : uintptr : 137
+ SYS_setfsuid : uintptr : 138
+ SYS_setfsgid : uintptr : 139
+ SYS__llseek : uintptr : 140
+ SYS_getdents : uintptr : 141
+ SYS__newselect : uintptr : 142
+ SYS_flock : uintptr : 143
+ SYS_msync : uintptr : 144
+ SYS_readv : uintptr : 145
+ SYS_writev : uintptr : 146
+ SYS_getsid : uintptr : 147
+ SYS_fdatasync : uintptr : 148
+ SYS__sysctl : uintptr : 149
+ SYS_mlock : uintptr : 150
+ SYS_munlock : uintptr : 151
+ SYS_mlockall : uintptr : 152
+ SYS_munlockall : uintptr : 153
+ SYS_sched_setparam : uintptr : 154
+ SYS_sched_getparam : uintptr : 155
+ SYS_sched_setscheduler : uintptr : 156
+ SYS_sched_getscheduler : uintptr : 157
+ SYS_sched_yield : uintptr : 158
+ SYS_sched_get_priority_max : uintptr : 159
+ SYS_sched_get_priority_min : uintptr : 160
+ SYS_sched_rr_get_interval : uintptr : 161
+ SYS_nanosleep : uintptr : 162
+ SYS_mremap : uintptr : 163
+ SYS_setresuid : uintptr : 164
+ SYS_getresuid : uintptr : 165
+ SYS_vm86 : uintptr : 166
+ SYS_query_module : uintptr : 167
+ SYS_poll : uintptr : 168
+ SYS_nfsservctl : uintptr : 169
+ SYS_setresgid : uintptr : 170
+ SYS_getresgid : uintptr : 171
+ SYS_prctl : uintptr : 172
+ SYS_rt_sigreturn : uintptr : 173
+ SYS_rt_sigaction : uintptr : 174
+ SYS_rt_sigprocmask : uintptr : 175
+ SYS_rt_sigpending : uintptr : 176
+ SYS_rt_sigtimedwait : uintptr : 177
+ SYS_rt_sigqueueinfo : uintptr : 178
+ SYS_rt_sigsuspend : uintptr : 179
+ SYS_pread64 : uintptr : 180
+ SYS_pwrite64 : uintptr : 181
+ SYS_chown : uintptr : 182
+ SYS_getcwd : uintptr : 183
+ SYS_capget : uintptr : 184
+ SYS_capset : uintptr : 185
+ SYS_sigaltstack : uintptr : 186
+ SYS_sendfile : uintptr : 187
+ SYS_getpmsg : uintptr : 188
+ SYS_putpmsg : uintptr : 189
+ SYS_vfork : uintptr : 190
+ SYS_ugetrlimit : uintptr : 191
+ SYS_mmap : uintptr : 192 // actually mmap2
+ SYS_truncate64 : uintptr : 193
+ SYS_ftruncate64 : uintptr : 194
+ SYS_stat64 : uintptr : 195
+ SYS_lstat64 : uintptr : 196
+ SYS_fstat64 : uintptr : 197
+ SYS_lchown32 : uintptr : 198
+ SYS_getuid32 : uintptr : 199
+ SYS_getgid32 : uintptr : 200
+ SYS_geteuid32 : uintptr : 201
+ SYS_getegid32 : uintptr : 202
+ SYS_setreuid32 : uintptr : 203
+ SYS_setregid32 : uintptr : 204
+ SYS_getgroups32 : uintptr : 205
+ SYS_setgroups32 : uintptr : 206
+ SYS_fchown32 : uintptr : 207
+ SYS_setresuid32 : uintptr : 208
+ SYS_getresuid32 : uintptr : 209
+ SYS_setresgid32 : uintptr : 210
+ SYS_getresgid32 : uintptr : 211
+ SYS_chown32 : uintptr : 212
+ SYS_setuid32 : uintptr : 213
+ SYS_setgid32 : uintptr : 214
+ SYS_setfsuid32 : uintptr : 215
+ SYS_setfsgid32 : uintptr : 216
+ SYS_pivot_root : uintptr : 217
+ SYS_mincore : uintptr : 218
+ SYS_madvise : uintptr : 219
+ SYS_getdents64 : uintptr : 220
+ SYS_fcntl64 : uintptr : 221
+ SYS_gettid : uintptr : 224
+ SYS_readahead : uintptr : 225
+ SYS_setxattr : uintptr : 226
+ SYS_lsetxattr : uintptr : 227
+ SYS_fsetxattr : uintptr : 228
+ SYS_getxattr : uintptr : 229
+ SYS_lgetxattr : uintptr : 230
+ SYS_fgetxattr : uintptr : 231
+ SYS_listxattr : uintptr : 232
+ SYS_llistxattr : uintptr : 233
+ SYS_flistxattr : uintptr : 234
+ SYS_removexattr : uintptr : 235
+ SYS_lremovexattr : uintptr : 236
+ SYS_fremovexattr : uintptr : 237
+ SYS_tkill : uintptr : 238
+ SYS_sendfile64 : uintptr : 239
SYS_futex : uintptr : 240
- SYS_gettid : uintptr: 224
+ SYS_sched_setaffinity : uintptr : 241
+ SYS_sched_getaffinity : uintptr : 242
+ SYS_set_thread_area : uintptr : 243
+ SYS_get_thread_area : uintptr : 244
+ SYS_io_setup : uintptr : 245
+ SYS_io_destroy : uintptr : 246
+ SYS_io_getevents : uintptr : 247
+ SYS_io_submit : uintptr : 248
+ SYS_io_cancel : uintptr : 249
+ SYS_fadvise64 : uintptr : 250
+ SYS_exit_group : uintptr : 252
+ SYS_lookup_dcookie : uintptr : 253
+ SYS_epoll_create : uintptr : 254
+ SYS_epoll_ctl : uintptr : 255
+ SYS_epoll_wait : uintptr : 256
+ SYS_remap_file_pages : uintptr : 257
+ SYS_set_tid_address : uintptr : 258
+ SYS_timer_create : uintptr : 259
+ SYS_timer_settime : uintptr : 260
+ SYS_timer_gettime : uintptr : 261
+ SYS_timer_getoverrun : uintptr : 262
+ SYS_timer_delete : uintptr : 263
+ SYS_clock_settime : uintptr : 264
+ SYS_clock_gettime : uintptr : 265
+ SYS_clock_getres : uintptr : 266
+ SYS_clock_nanosleep : uintptr : 267
+ SYS_statfs64 : uintptr : 268
+ SYS_fstatfs64 : uintptr : 269
+ SYS_tgkill : uintptr : 270
+ SYS_utimes : uintptr : 271
+ SYS_fadvise64_64 : uintptr : 272
+ SYS_vserver : uintptr : 273
+ SYS_mbind : uintptr : 274
+ SYS_get_mempolicy : uintptr : 275
+ SYS_set_mempolicy : uintptr : 276
+ SYS_mq_open : uintptr : 277
+ SYS_mq_unlink : uintptr : 278
+ SYS_mq_timedsend : uintptr : 279
+ SYS_mq_timedreceive : uintptr : 280
+ SYS_mq_notify : uintptr : 281
+ SYS_mq_getsetattr : uintptr : 282
+ SYS_kexec_load : uintptr : 283
+ SYS_waitid : uintptr : 284
+ SYS_add_key : uintptr : 286
+ SYS_request_key : uintptr : 287
+ SYS_keyctl : uintptr : 288
+ SYS_ioprio_set : uintptr : 289
+ SYS_ioprio_get : uintptr : 290
+ SYS_inotify_init : uintptr : 291
+ SYS_inotify_add_watch : uintptr : 292
+ SYS_inotify_rm_watch : uintptr : 293
+ SYS_migrate_pages : uintptr : 294
+ SYS_openat : uintptr : 295
+ SYS_mkdirat : uintptr : 296
+ SYS_mknodat : uintptr : 297
+ SYS_fchownat : uintptr : 298
+ SYS_futimesat : uintptr : 299
+ SYS_fstatat64 : uintptr : 300
+ SYS_unlinkat : uintptr : 301
+ SYS_renameat : uintptr : 302
+ SYS_linkat : uintptr : 303
+ SYS_symlinkat : uintptr : 304
+ SYS_readlinkat : uintptr : 305
+ SYS_fchmodat : uintptr : 306
+ SYS_faccessat : uintptr : 307
+ SYS_pselect6 : uintptr : 308
+ SYS_ppoll : uintptr : 309
+ SYS_unshare : uintptr : 310
+ SYS_set_robust_list : uintptr : 311
+ SYS_get_robust_list : uintptr : 312
+ SYS_splice : uintptr : 313
+ SYS_sync_file_range : uintptr : 314
+ SYS_tee : uintptr : 315
+ SYS_vmsplice : uintptr : 316
+ SYS_move_pages : uintptr : 317
+ SYS_getcpu : uintptr : 318
+ SYS_epoll_pwait : uintptr : 319
+ SYS_utimensat : uintptr : 320
+ SYS_signalfd : uintptr : 321
+ SYS_timerfd_create : uintptr : 322
+ SYS_eventfd : uintptr : 323
+ SYS_fallocate : uintptr : 324
+ SYS_timerfd_settime : uintptr : 325
+ SYS_timerfd_gettime : uintptr : 326
+ SYS_signalfd4 : uintptr : 327
+ SYS_eventfd2 : uintptr : 328
+ SYS_epoll_create1 : uintptr : 329
+ SYS_dup3 : uintptr : 330
+ SYS_pipe2 : uintptr : 331
+ SYS_inotify_init1 : uintptr : 332
+ SYS_preadv : uintptr : 333
+ SYS_pwritev : uintptr : 334
+ SYS_rt_tgsigqueueinfo : uintptr : 335
+ SYS_perf_event_open : uintptr : 336
+ SYS_recvmmsg : uintptr : 337
+ SYS_fanotify_init : uintptr : 338
+ SYS_fanotify_mark : uintptr : 339
+ SYS_prlimit64 : uintptr : 340
+ SYS_name_to_handle_at : uintptr : 341
+ SYS_open_by_handle_at : uintptr : 342
+ SYS_clock_adjtime : uintptr : 343
+ SYS_syncfs : uintptr : 344
+ SYS_sendmmsg : uintptr : 345
+ SYS_setns : uintptr : 346
+ SYS_process_vm_readv : uintptr : 347
+ SYS_process_vm_writev : uintptr : 348
+ SYS_kcmp : uintptr : 349
+ SYS_finit_module : uintptr : 350
+ SYS_sched_setattr : uintptr : 351
+ SYS_sched_getattr : uintptr : 352
+ SYS_renameat2 : uintptr : 353
+ SYS_seccomp : uintptr : 354
+ SYS_getrandom : uintptr : 355
+ SYS_memfd_create : uintptr : 356
+ SYS_bpf : uintptr : 357
+ SYS_execveat : uintptr : 358
+ SYS_socket : uintptr : 359
+ SYS_socketpair : uintptr : 360
+ SYS_bind : uintptr : 361
+ SYS_connect : uintptr : 362
+ SYS_listen : uintptr : 363
+ SYS_accept4 : uintptr : 364
+ SYS_getsockopt : uintptr : 365
+ SYS_setsockopt : uintptr : 366
+ SYS_getsockname : uintptr : 367
+ SYS_getpeername : uintptr : 368
+ SYS_sendto : uintptr : 369
+ SYS_sendmsg : uintptr : 370
+ SYS_recvfrom : uintptr : 371
+ SYS_recvmsg : uintptr : 372
+ SYS_shutdown : uintptr : 373
+ SYS_userfaultfd : uintptr : 374
+ SYS_membarrier : uintptr : 375
+ SYS_mlock2 : uintptr : 376
+ SYS_copy_file_range : uintptr : 377
+ SYS_preadv2 : uintptr : 378
+ SYS_pwritev2 : uintptr : 379
+ SYS_pkey_mprotect : uintptr : 380
+ SYS_pkey_alloc : uintptr : 381
+ SYS_pkey_free : uintptr : 382
+ SYS_statx : uintptr : 383
+ SYS_arch_prctl : uintptr : 384
+ SYS_io_pgetevents : uintptr : 385
+ SYS_rseq : uintptr : 386
+ SYS_semget : uintptr : 393
+ SYS_semctl : uintptr : 394
+ SYS_shmget : uintptr : 395
+ SYS_shmctl : uintptr : 396
+ SYS_shmat : uintptr : 397
+ SYS_shmdt : uintptr : 398
+ SYS_msgget : uintptr : 399
+ SYS_msgsnd : uintptr : 400
+ SYS_msgrcv : uintptr : 401
+ SYS_msgctl : uintptr : 402
+ SYS_clock_gettime64 : uintptr : 403
+ SYS_clock_settime64 : uintptr : 404
+ SYS_clock_adjtime64 : uintptr : 405
+ SYS_clock_getres_time64 : uintptr : 406
+ SYS_clock_nanosleep_time64 : uintptr : 407
+ SYS_timer_gettime64 : uintptr : 408
+ SYS_timer_settime64 : uintptr : 409
+ SYS_timerfd_gettime64 : uintptr : 410
+ SYS_timerfd_settime64 : uintptr : 411
+ SYS_utimensat_time64 : uintptr : 412
+ SYS_pselect6_time64 : uintptr : 413
+ SYS_ppoll_time64 : uintptr : 414
+ SYS_io_pgetevents_time64 : uintptr : 416
+ SYS_recvmmsg_time64 : uintptr : 417
+ SYS_mq_timedsend_time64 : uintptr : 418
+ SYS_mq_timedreceive_time64 : uintptr : 419
+ SYS_semtimedop_time64 : uintptr : 420
+ SYS_rt_sigtimedwait_time64 : uintptr : 421
+ SYS_futex_time64 : uintptr : 422
+ SYS_sched_rr_get_interval_time64 : uintptr : 423
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
+ SYS_memfd_secret : uintptr : 447
+} else when false /*ODIN_ARCH == .arm*/ { // TODO
+ SYS_restart_syscall : uintptr : 0
+ SYS_exit : uintptr : 1
+ SYS_fork : uintptr : 2
+ SYS_read : uintptr : 3
+ SYS_write : uintptr : 4
+ SYS_open : uintptr : 5
+ SYS_close : uintptr : 6
+ SYS_creat : uintptr : 8
+ SYS_link : uintptr : 9
+ SYS_unlink : uintptr : 10
+ SYS_execve : uintptr : 11
+ SYS_chdir : uintptr : 12
+ SYS_mknod : uintptr : 14
+ SYS_chmod : uintptr : 15
+ SYS_lchown : uintptr : 16
+ SYS_lseek : uintptr : 19
+ SYS_getpid : uintptr : 20
+ SYS_mount : uintptr : 21
+ SYS_setuid : uintptr : 23
+ SYS_getuid : uintptr : 24
+ SYS_ptrace : uintptr : 26
+ SYS_pause : uintptr : 29
+ SYS_access : uintptr : 33
+ SYS_nice : uintptr : 34
+ SYS_sync : uintptr : 36
+ SYS_kill : uintptr : 37
+ SYS_rename : uintptr : 38
+ SYS_mkdir : uintptr : 39
+ SYS_rmdir : uintptr : 40
+ SYS_dup : uintptr : 41
+ SYS_pipe : uintptr : 42
+ SYS_times : uintptr : 43
+ SYS_brk : uintptr : 45
+ SYS_setgid : uintptr : 46
+ SYS_getgid : uintptr : 47
+ SYS_geteuid : uintptr : 49
+ SYS_getegid : uintptr : 50
+ SYS_acct : uintptr : 51
+ SYS_umount2 : uintptr : 52
+ SYS_ioctl : uintptr : 54
+ SYS_fcntl : uintptr : 55
+ SYS_setpgid : uintptr : 57
+ SYS_umask : uintptr : 60
+ SYS_chroot : uintptr : 61
+ SYS_ustat : uintptr : 62
+ SYS_dup2 : uintptr : 63
+ SYS_getppid : uintptr : 64
+ SYS_getpgrp : uintptr : 65
+ SYS_setsid : uintptr : 66
+ SYS_sigaction : uintptr : 67
+ SYS_setreuid : uintptr : 70
+ SYS_setregid : uintptr : 71
+ SYS_sigsuspend : uintptr : 72
+ SYS_sigpending : uintptr : 73
+ SYS_sethostname : uintptr : 74
+ SYS_setrlimit : uintptr : 75
+ SYS_getrusage : uintptr : 77
+ SYS_gettimeofday : uintptr : 78
+ SYS_settimeofday : uintptr : 79
+ SYS_getgroups : uintptr : 80
+ SYS_setgroups : uintptr : 81
+ SYS_symlink : uintptr : 83
+ SYS_readlink : uintptr : 85
+ SYS_uselib : uintptr : 86
+ SYS_swapon : uintptr : 87
+ SYS_reboot : uintptr : 88
+ SYS_munmap : uintptr : 91
+ SYS_truncate : uintptr : 92
+ SYS_ftruncate : uintptr : 93
+ SYS_fchmod : uintptr : 94
+ SYS_fchown : uintptr : 95
+ SYS_getpriority : uintptr : 96
+ SYS_setpriority : uintptr : 97
+ SYS_statfs : uintptr : 99
+ SYS_fstatfs : uintptr : 100
+ SYS_syslog : uintptr : 103
+ SYS_setitimer : uintptr : 104
+ SYS_getitimer : uintptr : 105
+ SYS_stat : uintptr : 106
+ SYS_lstat : uintptr : 107
+ SYS_fstat : uintptr : 108
+ SYS_vhangup : uintptr : 111
+ SYS_wait4 : uintptr : 114
+ SYS_swapoff : uintptr : 115
+ SYS_sysinfo : uintptr : 116
+ SYS_fsync : uintptr : 118
+ SYS_sigreturn : uintptr : 119
+ SYS_clone : uintptr : 120
+ SYS_setdomainname : uintptr : 121
+ SYS_uname : uintptr : 122
+ SYS_adjtimex : uintptr : 124
+ SYS_mprotect : uintptr : 125
+ SYS_sigprocmask : uintptr : 126
+ SYS_init_module : uintptr : 128
+ SYS_delete_module : uintptr : 129
+ SYS_quotactl : uintptr : 131
+ SYS_getpgid : uintptr : 132
+ SYS_fchdir : uintptr : 133
+ SYS_bdflush : uintptr : 134
+ SYS_sysfs : uintptr : 135
+ SYS_personality : uintptr : 136
+ SYS_setfsuid : uintptr : 138
+ SYS_setfsgid : uintptr : 139
+ SYS__llseek : uintptr : 140
+ SYS_getdents : uintptr : 141
+ SYS__newselect : uintptr : 142
+ SYS_flock : uintptr : 143
+ SYS_msync : uintptr : 144
+ SYS_readv : uintptr : 145
+ SYS_writev : uintptr : 146
+ SYS_getsid : uintptr : 147
+ SYS_fdatasync : uintptr : 148
+ SYS__sysctl : uintptr : 149
+ SYS_mlock : uintptr : 150
+ SYS_munlock : uintptr : 151
+ SYS_mlockall : uintptr : 152
+ SYS_munlockall : uintptr : 153
+ SYS_sched_setparam : uintptr : 154
+ SYS_sched_getparam : uintptr : 155
+ SYS_sched_setscheduler : uintptr : 156
+ SYS_sched_getscheduler : uintptr : 157
+ SYS_sched_yield : uintptr : 158
+ SYS_sched_get_priority_max : uintptr : 159
+ SYS_sched_get_priority_min : uintptr : 160
+ SYS_sched_rr_get_interval : uintptr : 161
+ SYS_nanosleep : uintptr : 162
+ SYS_mremap : uintptr : 163
+ SYS_setresuid : uintptr : 164
+ SYS_getresuid : uintptr : 165
+ SYS_poll : uintptr : 168
+ SYS_nfsservctl : uintptr : 169
+ SYS_setresgid : uintptr : 170
+ SYS_getresgid : uintptr : 171
+ SYS_prctl : uintptr : 172
+ SYS_rt_sigreturn : uintptr : 173
+ SYS_rt_sigaction : uintptr : 174
+ SYS_rt_sigprocmask : uintptr : 175
+ SYS_rt_sigpending : uintptr : 176
+ SYS_rt_sigtimedwait : uintptr : 177
+ SYS_rt_sigqueueinfo : uintptr : 178
+ SYS_rt_sigsuspend : uintptr : 179
+ SYS_pread64 : uintptr : 180
+ SYS_pwrite64 : uintptr : 181
+ SYS_chown : uintptr : 182
+ SYS_getcwd : uintptr : 183
+ SYS_capget : uintptr : 184
+ SYS_capset : uintptr : 185
+ SYS_sigaltstack : uintptr : 186
+ SYS_sendfile : uintptr : 187
+ SYS_vfork : uintptr : 190
+ SYS_ugetrlimit : uintptr : 191
+ SYS_mmap : uintptr : 192 // actually mmap2
+ SYS_truncate64 : uintptr : 193
+ SYS_ftruncate64 : uintptr : 194
+ SYS_stat64 : uintptr : 195
+ SYS_lstat64 : uintptr : 196
+ SYS_fstat64 : uintptr : 197
+ SYS_lchown32 : uintptr : 198
+ SYS_getuid32 : uintptr : 199
+ SYS_getgid32 : uintptr : 200
+ SYS_geteuid32 : uintptr : 201
+ SYS_getegid32 : uintptr : 202
+ SYS_setreuid32 : uintptr : 203
+ SYS_setregid32 : uintptr : 204
+ SYS_getgroups32 : uintptr : 205
+ SYS_setgroups32 : uintptr : 206
+ SYS_fchown32 : uintptr : 207
+ SYS_setresuid32 : uintptr : 208
+ SYS_getresuid32 : uintptr : 209
+ SYS_setresgid32 : uintptr : 210
+ SYS_getresgid32 : uintptr : 211
+ SYS_chown32 : uintptr : 212
+ SYS_setuid32 : uintptr : 213
+ SYS_setgid32 : uintptr : 214
+ SYS_setfsuid32 : uintptr : 215
+ SYS_setfsgid32 : uintptr : 216
+ SYS_getdents64 : uintptr : 217
+ SYS_pivot_root : uintptr : 218
+ SYS_mincore : uintptr : 219
+ SYS_madvise : uintptr : 220
+ SYS_fcntl64 : uintptr : 221
+ SYS_gettid : uintptr : 224
+ SYS_readahead : uintptr : 225
+ SYS_setxattr : uintptr : 226
+ SYS_lsetxattr : uintptr : 227
+ SYS_fsetxattr : uintptr : 228
+ SYS_getxattr : uintptr : 229
+ SYS_lgetxattr : uintptr : 230
+ SYS_fgetxattr : uintptr : 231
+ SYS_listxattr : uintptr : 232
+ SYS_llistxattr : uintptr : 233
+ SYS_flistxattr : uintptr : 234
+ SYS_removexattr : uintptr : 235
+ SYS_lremovexattr : uintptr : 236
+ SYS_fremovexattr : uintptr : 237
+ SYS_tkill : uintptr : 238
+ SYS_sendfile64 : uintptr : 239
+ SYS_futex : uintptr : 240
+ SYS_sched_setaffinity : uintptr : 241
+ SYS_sched_getaffinity : uintptr : 242
+ SYS_io_setup : uintptr : 243
+ SYS_io_destroy : uintptr : 244
+ SYS_io_getevents : uintptr : 245
+ SYS_io_submit : uintptr : 246
+ SYS_io_cancel : uintptr : 247
+ SYS_exit_group : uintptr : 248
+ SYS_lookup_dcookie : uintptr : 249
+ SYS_epoll_create : uintptr : 250
+ SYS_epoll_ctl : uintptr : 251
+ SYS_epoll_wait : uintptr : 252
+ SYS_remap_file_pages : uintptr : 253
+ SYS_set_tid_address : uintptr : 256
+ SYS_timer_create : uintptr : 257
+ SYS_timer_settime : uintptr : 258
+ SYS_timer_gettime : uintptr : 259
+ SYS_timer_getoverrun : uintptr : 260
+ SYS_timer_delete : uintptr : 261
+ SYS_clock_settime : uintptr : 262
+ SYS_clock_gettime : uintptr : 263
+ SYS_clock_getres : uintptr : 264
+ SYS_clock_nanosleep : uintptr : 265
+ SYS_statfs64 : uintptr : 266
+ SYS_fstatfs64 : uintptr : 267
+ SYS_tgkill : uintptr : 268
+ SYS_utimes : uintptr : 269
+ SYS_fadvise64_64 : uintptr : 270
+ SYS_pciconfig_iobase : uintptr : 271
+ SYS_pciconfig_read : uintptr : 272
+ SYS_pciconfig_write : uintptr : 273
+ SYS_mq_open : uintptr : 274
+ SYS_mq_unlink : uintptr : 275
+ SYS_mq_timedsend : uintptr : 276
+ SYS_mq_timedreceive : uintptr : 277
+ SYS_mq_notify : uintptr : 278
+ SYS_mq_getsetattr : uintptr : 279
+ SYS_waitid : uintptr : 280
+ SYS_socket : uintptr : 281
+ SYS_bind : uintptr : 282
+ SYS_connect : uintptr : 283
+ SYS_listen : uintptr : 284
+ SYS_accept : uintptr : 285
+ SYS_getsockname : uintptr : 286
+ SYS_getpeername : uintptr : 287
+ SYS_socketpair : uintptr : 288
+ SYS_send : uintptr : 289
+ SYS_sendto : uintptr : 290
+ SYS_recv : uintptr : 291
+ SYS_recvfrom : uintptr : 292
+ SYS_shutdown : uintptr : 293
+ SYS_setsockopt : uintptr : 294
+ SYS_getsockopt : uintptr : 295
+ SYS_sendmsg : uintptr : 296
+ SYS_recvmsg : uintptr : 297
+ SYS_semop : uintptr : 298
+ SYS_semget : uintptr : 299
+ SYS_semctl : uintptr : 300
+ SYS_msgsnd : uintptr : 301
+ SYS_msgrcv : uintptr : 302
+ SYS_msgget : uintptr : 303
+ SYS_msgctl : uintptr : 304
+ SYS_shmat : uintptr : 305
+ SYS_shmdt : uintptr : 306
+ SYS_shmget : uintptr : 307
+ SYS_shmctl : uintptr : 308
+ SYS_add_key : uintptr : 309
+ SYS_request_key : uintptr : 310
+ SYS_keyctl : uintptr : 311
+ SYS_semtimedop : uintptr : 312
+ SYS_vserver : uintptr : 313
+ SYS_ioprio_set : uintptr : 314
+ SYS_ioprio_get : uintptr : 315
+ SYS_inotify_init : uintptr : 316
+ SYS_inotify_add_watch : uintptr : 317
+ SYS_inotify_rm_watch : uintptr : 318
+ SYS_mbind : uintptr : 319
+ SYS_get_mempolicy : uintptr : 320
+ SYS_set_mempolicy : uintptr : 321
+ SYS_openat : uintptr : 322
+ SYS_mkdirat : uintptr : 323
+ SYS_mknodat : uintptr : 324
+ SYS_fchownat : uintptr : 325
+ SYS_futimesat : uintptr : 326
+ SYS_fstatat64 : uintptr : 327
+ SYS_unlinkat : uintptr : 328
+ SYS_renameat : uintptr : 329
+ SYS_linkat : uintptr : 330
+ SYS_symlinkat : uintptr : 331
+ SYS_readlinkat : uintptr : 332
+ SYS_fchmodat : uintptr : 333
+ SYS_faccessat : uintptr : 334
+ SYS_pselect6 : uintptr : 335
+ SYS_ppoll : uintptr : 336
+ SYS_unshare : uintptr : 337
+ SYS_set_robust_list : uintptr : 338
+ SYS_get_robust_list : uintptr : 339
+ SYS_splice : uintptr : 340
+ SYS_sync_file_range : uintptr : 341
+ SYS_tee : uintptr : 342
+ SYS_vmsplice : uintptr : 343
+ SYS_move_pages : uintptr : 344
+ SYS_getcpu : uintptr : 345
+ SYS_epoll_pwait : uintptr : 346
+ SYS_kexec_load : uintptr : 347
+ SYS_utimensat : uintptr : 348
+ SYS_signalfd : uintptr : 349
+ SYS_timerfd_create : uintptr : 350
+ SYS_eventfd : uintptr : 351
+ SYS_fallocate : uintptr : 352
+ SYS_timerfd_settime : uintptr : 353
+ SYS_timerfd_gettime : uintptr : 354
+ SYS_signalfd4 : uintptr : 355
+ SYS_eventfd2 : uintptr : 356
+ SYS_epoll_create1 : uintptr : 357
+ SYS_dup3 : uintptr : 358
+ SYS_pipe2 : uintptr : 359
+ SYS_inotify_init1 : uintptr : 360
+ SYS_preadv : uintptr : 361
+ SYS_pwritev : uintptr : 362
+ SYS_rt_tgsigqueueinfo : uintptr : 363
+ SYS_perf_event_open : uintptr : 364
+ SYS_recvmmsg : uintptr : 365
+ SYS_accept4 : uintptr : 366
+ SYS_fanotify_init : uintptr : 367
+ SYS_fanotify_mark : uintptr : 368
+ SYS_prlimit64 : uintptr : 369
+ SYS_name_to_handle_at : uintptr : 370
+ SYS_open_by_handle_at : uintptr : 371
+ SYS_clock_adjtime : uintptr : 372
+ SYS_syncfs : uintptr : 373
+ SYS_sendmmsg : uintptr : 374
+ SYS_setns : uintptr : 375
+ SYS_process_vm_readv : uintptr : 376
+ SYS_process_vm_writev : uintptr : 377
+ SYS_kcmp : uintptr : 378
+ SYS_finit_module : uintptr : 379
+ SYS_sched_setattr : uintptr : 380
+ SYS_sched_getattr : uintptr : 381
+ SYS_renameat2 : uintptr : 382
+ SYS_seccomp : uintptr : 383
SYS_getrandom : uintptr : 384
+ SYS_memfd_create : uintptr : 385
+ SYS_bpf : uintptr : 386
+ SYS_execveat : uintptr : 387
+ SYS_userfaultfd : uintptr : 388
+ SYS_membarrier : uintptr : 389
+ SYS_mlock2 : uintptr : 390
+ SYS_copy_file_range : uintptr : 391
+ SYS_preadv2 : uintptr : 392
+ SYS_pwritev2 : uintptr : 393
+ SYS_pkey_mprotect : uintptr : 394
+ SYS_pkey_alloc : uintptr : 395
+ SYS_pkey_free : uintptr : 396
+ SYS_statx : uintptr : 397
+ SYS_rseq : uintptr : 398
+ SYS_io_pgetevents : uintptr : 399
+ SYS_migrate_pages : uintptr : 400
+ SYS_kexec_file_load : uintptr : 401
+ SYS_clock_gettime64 : uintptr : 403
+ SYS_clock_settime64 : uintptr : 404
+ SYS_clock_adjtime64 : uintptr : 405
+ SYS_clock_getres_time64 : uintptr : 406
+ SYS_clock_nanosleep_time64 : uintptr : 407
+ SYS_timer_gettime64 : uintptr : 408
+ SYS_timer_settime64 : uintptr : 409
+ SYS_timerfd_gettime64 : uintptr : 410
+ SYS_timerfd_settime64 : uintptr : 411
+ SYS_utimensat_time64 : uintptr : 412
+ SYS_pselect6_time64 : uintptr : 413
+ SYS_ppoll_time64 : uintptr : 414
+ SYS_io_pgetevents_time64 : uintptr : 416
+ SYS_recvmmsg_time64 : uintptr : 417
+ SYS_mq_timedsend_time64 : uintptr : 418
+ SYS_mq_timedreceive_time64 : uintptr : 419
+ SYS_semtimedop_time64 : uintptr : 420
+ SYS_rt_sigtimedwait_time64 : uintptr : 421
+ SYS_futex_time64 : uintptr : 422
+ SYS_sched_rr_get_interval_time64 : uintptr : 423
+ SYS_pidfd_send_signal : uintptr : 424
+ SYS_io_uring_setup : uintptr : 425
+ SYS_io_uring_enter : uintptr : 426
+ SYS_io_uring_register : uintptr : 427
+ SYS_open_tree : uintptr : 428
+ SYS_move_mount : uintptr : 429
+ SYS_fsopen : uintptr : 430
+ SYS_fsconfig : uintptr : 431
+ SYS_fsmount : uintptr : 432
+ SYS_fspick : uintptr : 433
+ SYS_pidfd_open : uintptr : 434
+ SYS_clone3 : uintptr : 435
+ SYS_close_range : uintptr : 436
+ SYS_openat2 : uintptr : 437
+ SYS_pidfd_getfd : uintptr : 438
+ SYS_faccessat2 : uintptr : 439
+ SYS_process_madvise : uintptr : 440
+ SYS_epoll_pwait2 : uintptr : 441
+ SYS_mount_setattr : uintptr : 442
+ SYS_landlock_create_ruleset : uintptr : 444
+ SYS_landlock_add_rule : uintptr : 445
+ SYS_landlock_restrict_self : uintptr : 446
} else {
#panic("Unsupported architecture")
}
diff --git a/core/sys/win32/user32.odin b/core/sys/win32/user32.odin
index 593fdbb8e..44d1f7004 100644
--- a/core/sys/win32/user32.odin
+++ b/core/sys/win32/user32.odin
@@ -101,6 +101,9 @@ foreign user32 {
}
@(link_name="GetCursorPos") get_cursor_pos :: proc(p: ^Point) -> Bool ---
@(link_name="SetCursorPos") set_cursor_pos :: proc(x, y: i32) -> Bool ---
+ @(link_name="GetCapure") get_capture :: proc(hwnd: Hwnd) -> Hwnd ---
+ @(link_name="SetCapture") set_capture :: proc(hwnd: Hwnd) -> Hwnd ---
+ @(link_name="ReleaseCapture") release_capture :: proc() -> Bool ---
@(link_name="ScreenToClient") screen_to_client :: proc(h: Hwnd, p: ^Point) -> Bool ---
@(link_name="ClientToScreen") client_to_screen :: proc(h: Hwnd, p: ^Point) -> Bool ---
@(link_name="PostQuitMessage") post_quit_message :: proc(exit_code: i32) ---
@@ -108,6 +111,8 @@ foreign user32 {
@(link_name="SetWindowTextW") set_window_text_w :: proc(hwnd: Hwnd, c_string: Wstring) -> Bool ---
@(link_name="RegisterClassA") register_class_a :: proc(wc: ^Wnd_Class_A) -> i16 ---
@(link_name="RegisterClassW") register_class_w :: proc(wc: ^Wnd_Class_W) -> i16 ---
+ @(link_name="UnregisterClassA") unregister_class_a :: proc(class_name: cstring, instance: Hinstance) -> Bool ---
+ @(link_name="UnregisterClassW") unregister_class_w :: proc(class_name: Wstring, instance: Hinstance) -> Bool ---
@(link_name="RegisterClassExA") register_class_ex_a :: proc(wc: ^Wnd_Class_Ex_A) -> i16 ---
@(link_name="RegisterClassExW") register_class_ex_w :: proc(wc: ^Wnd_Class_Ex_W) -> i16 ---
diff --git a/core/sys/windows/dwmapi.odin b/core/sys/windows/dwmapi.odin
new file mode 100644
index 000000000..468b5bb87
--- /dev/null
+++ b/core/sys/windows/dwmapi.odin
@@ -0,0 +1,9 @@
+// +build windows
+package sys_windows
+
+foreign import dwmapi "system:Dwmapi.lib"
+
+@(default_calling_convention="stdcall")
+foreign dwmapi {
+ DwmFlush :: proc() -> HRESULT ---
+}
diff --git a/core/sys/windows/gdi32.odin b/core/sys/windows/gdi32.odin
new file mode 100644
index 000000000..15d5567c7
--- /dev/null
+++ b/core/sys/windows/gdi32.odin
@@ -0,0 +1,66 @@
+// +build windows
+package sys_windows
+
+foreign import gdi32 "system:Gdi32.lib"
+
+@(default_calling_convention="stdcall")
+foreign gdi32 {
+ GetStockObject :: proc(i: c_int) -> HGDIOBJ ---
+ SelectObject :: proc(hdc: HDC, h: HGDIOBJ) -> HGDIOBJ ---
+
+ CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
+
+ CreateDIBitmap :: proc(
+ hdc: HDC,
+ pbmih: ^BITMAPINFOHEADER,
+ flInit: DWORD,
+ pjBits: VOID,
+ pbmi: ^BITMAPINFO,
+ iUsage: UINT,
+ ) -> HBITMAP ---
+
+ CreateDIBSection :: proc(
+ hdc: HDC,
+ pbmi: ^BITMAPINFO,
+ usage: UINT,
+ ppvBits: VOID,
+ hSection: HANDLE,
+ offset: DWORD,
+ ) -> HBITMAP ---
+
+ StretchDIBits :: proc(
+ hdc: HDC,
+ xDest: c_int,
+ yDest: c_int,
+ DestWidth: c_int,
+ DestHeight: c_int,
+ xSrc: c_int,
+ ySrc: c_int,
+ SrcWidth: c_int,
+ SrcHeight: c_int,
+ lpBits: VOID,
+ lpbmi: ^BITMAPINFO,
+ iUsage: UINT,
+ rop: DWORD,
+ ) -> c_int ---
+
+ StretchBlt :: proc(
+ hdcDest: HDC,
+ xDest: c_int,
+ yDest: c_int,
+ wDest: c_int,
+ hDest: c_int,
+ hdcSrc: HDC,
+ xSrc: c_int,
+ ySrc: c_int,
+ wSrc: c_int,
+ hSrc: c_int,
+ rop: DWORD,
+ ) -> BOOL ---
+
+ SetPixelFormat :: proc(hdc: HDC, format: c_int, ppfd: ^PIXELFORMATDESCRIPTOR) -> BOOL ---
+ ChoosePixelFormat :: proc(hdc: HDC, ppfd: ^PIXELFORMATDESCRIPTOR) -> c_int ---
+ SwapBuffers :: proc(HDC) -> BOOL ---
+
+ PatBlt :: proc(hdc: HDC, x, y, w, h: c_int, rop: DWORD) -> BOOL ---
+}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 8c58fbd52..cb90f71da 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -62,6 +62,13 @@ foreign kernel32 {
GetCurrentProcessId :: proc() -> DWORD ---
GetCurrentThread :: proc() -> HANDLE ---
GetCurrentThreadId :: proc() -> DWORD ---
+ GetProcessTimes :: proc(
+ hProcess: HANDLE,
+ lpCreationTime: LPFILETIME,
+ lpExitTime: LPFILETIME,
+ lpKernelTime: LPFILETIME,
+ lpUserTime: LPFILETIME,
+ ) -> BOOL ---
GetStdHandle :: proc(which: DWORD) -> HANDLE ---
ExitProcess :: proc(uExitCode: c_uint) -> ! ---
DeviceIoControl :: proc(
@@ -92,6 +99,20 @@ foreign kernel32 {
CreateSemaphoreW :: proc(attributes: LPSECURITY_ATTRIBUTES, initial_count, maximum_count: LONG, name: LPCSTR) -> HANDLE ---
ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: LONG, previous_count: ^LONG) -> BOOL ---
+ CreateWaitableTimerW :: proc(
+ lpTimerAttributes: LPSECURITY_ATTRIBUTES,
+ bManualReset: BOOL,
+ lpTimerName: LPCWSTR,
+ ) -> HANDLE ---
+ SetWaitableTimerEx :: proc(
+ hTimer: HANDLE,
+ lpDueTime: ^LARGE_INTEGER,
+ lPeriod: LONG,
+ pfnCompletionRoutine: PTIMERAPCROUTINE,
+ lpArgToCompletionRoutine: LPVOID,
+ WakeContext: PREASON_CONTEXT,
+ TolerableDelay: ULONG,
+ ) -> BOOL ---
WaitForSingleObject :: proc(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD ---
Sleep :: proc(dwMilliseconds: DWORD) ---
GetProcessId :: proc(handle: HANDLE) -> DWORD ---
@@ -341,6 +362,7 @@ MEM_TOP_DOWN :: 0x100000
MEM_LARGE_PAGES :: 0x20000000
MEM_4MB_PAGES :: 0x80000000
+@(default_calling_convention="stdcall")
foreign kernel32 {
VirtualAlloc :: proc(
lpAddress: LPVOID,
@@ -483,6 +505,7 @@ LowMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.LowMemoryRes
HighMemoryResourceNotification :: MEMORY_RESOURCE_NOTIFICATION_TYPE.HighMemoryResourceNotification
+@(default_calling_convention="stdcall")
foreign kernel32 {
CreateMemoryResourceNotification :: proc(
NotificationType: MEMORY_RESOURCE_NOTIFICATION_TYPE,
@@ -498,6 +521,7 @@ FILE_CACHE_MAX_HARD_DISABLE :: DWORD(0x00000002)
FILE_CACHE_MIN_HARD_ENABLE :: DWORD(0x00000004)
FILE_CACHE_MIN_HARD_DISABLE :: DWORD(0x00000008)
+@(default_calling_convention="stdcall")
foreign kernel32 {
GetSystemFileCacheSize :: proc(
lpMinimumFileCacheSize: PSIZE_T,
@@ -527,6 +551,7 @@ WIN32_MEMORY_RANGE_ENTRY :: struct {
PWIN32_MEMORY_RANGE_ENTRY :: ^WIN32_MEMORY_RANGE_ENTRY
+@(default_calling_convention="stdcall")
foreign kernel32 {
PrefetchVirtualMemory :: proc(
hProcess: HANDLE,
@@ -584,6 +609,7 @@ foreign kernel32 {
MEHC_PATROL_SCRUBBER_PRESENT :: ULONG(0x1)
+@(default_calling_convention="stdcall")
foreign kernel32 {
GetMemoryErrorHandlingCapabilities :: proc(
Capabilities: PULONG,
@@ -592,6 +618,7 @@ foreign kernel32 {
PBAD_MEMORY_CALLBACK_ROUTINE :: #type proc "stdcall" ()
+@(default_calling_convention="stdcall")
foreign kernel32 {
RegisterBadMemoryNotification :: proc(
Callback: PBAD_MEMORY_CALLBACK_ROUTINE,
@@ -612,6 +639,7 @@ VmOfferPriorityLow :: OFFER_PRIORITY.VmOfferPriorityLow
VmOfferPriorityBelowNormal :: OFFER_PRIORITY.VmOfferPriorityBelowNormal
VmOfferPriorityNormal :: OFFER_PRIORITY.VmOfferPriorityNormal
+@(default_calling_convention="stdcall")
foreign kernel32 {
OfferVirtualMemory :: proc(
VirtualAddress: PVOID,
@@ -676,6 +704,7 @@ WIN32_MEMORY_REGION_INFORMATION_u_s_Bitfield :: distinct ULONG
Reserved : 32-6,
}*/
+@(default_calling_convention="stdcall")
foreign kernel32 {
QueryVirtualMemoryInformation :: proc(
Process: HANDLE,
@@ -700,7 +729,7 @@ foreign kernel32 {
NUMA_NO_PREFERRED_NODE :: 0xffffffff
-MapViewOfFile2 :: #force_inline proc(
+MapViewOfFile2 :: #force_inline proc "stdcall" (
FileMappingHandle: HANDLE,
ProcessHandle: HANDLE,
Offset: ULONG64,
@@ -721,6 +750,7 @@ MapViewOfFile2 :: #force_inline proc(
)
}
+@(default_calling_convention="stdcall")
foreign kernel32 {
UnmapViewOfFile2 :: proc(
ProcessHandle: HANDLE,
diff --git a/core/sys/windows/key_codes.odin b/core/sys/windows/key_codes.odin
new file mode 100644
index 000000000..2f6e01116
--- /dev/null
+++ b/core/sys/windows/key_codes.odin
@@ -0,0 +1,252 @@
+// +build windows
+package sys_windows
+
+// https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
+// Virtual Keys, Standard Set
+VK_LBUTTON :: 0x01
+VK_RBUTTON :: 0x02
+VK_CANCEL :: 0x03
+VK_MBUTTON :: 0x04 // NOT contiguous with L & RBUTTON
+VK_XBUTTON1 :: 0x05 // NOT contiguous with L & RBUTTON
+VK_XBUTTON2 :: 0x06 // NOT contiguous with L & RBUTTON
+
+// 0x07 : reserved
+
+VK_BACK :: 0x08
+VK_TAB :: 0x09
+
+// 0x0A - 0x0B : reserved
+
+VK_CLEAR :: 0x0C
+VK_RETURN :: 0x0D
+
+// 0x0E - 0x0F : unassigned
+
+VK_SHIFT :: 0x10
+VK_CONTROL :: 0x11
+VK_MENU :: 0x12
+VK_PAUSE :: 0x13
+VK_CAPITAL :: 0x14
+VK_KANA :: 0x15
+VK_HANGEUL :: 0x15 // old name - should be here for compatibility
+VK_HANGUL :: 0x15
+VK_IME_ON :: 0x16
+VK_JUNJA :: 0x17
+VK_FINAL :: 0x18
+VK_HANJA :: 0x19
+VK_KANJI :: 0x19
+VK_IME_OFF :: 0x1A
+VK_ESCAPE :: 0x1B
+VK_CONVERT :: 0x1C
+VK_NONCONVERT :: 0x1D
+VK_ACCEPT :: 0x1E
+VK_MODECHANGE :: 0x1F
+VK_SPACE :: 0x20
+VK_PRIOR :: 0x21
+VK_NEXT :: 0x22
+VK_END :: 0x23
+VK_HOME :: 0x24
+VK_LEFT :: 0x25
+VK_UP :: 0x26
+VK_RIGHT :: 0x27
+VK_DOWN :: 0x28
+VK_SELECT :: 0x29
+VK_PRINT :: 0x2A
+VK_EXECUTE :: 0x2B
+VK_SNAPSHOT :: 0x2C
+VK_INSERT :: 0x2D
+VK_DELETE :: 0x2E
+VK_HELP :: 0x2F
+
+VK_0 :: '0'
+VK_1 :: '1'
+VK_2 :: '2'
+VK_3 :: '3'
+VK_4 :: '4'
+VK_5 :: '5'
+VK_6 :: '6'
+VK_7 :: '7'
+VK_8 :: '8'
+VK_9 :: '9'
+
+// 0x3A - 0x40 : unassigned
+
+VK_A :: 'A'
+VK_B :: 'B'
+VK_C :: 'C'
+VK_D :: 'D'
+VK_E :: 'E'
+VK_F :: 'F'
+VK_G :: 'G'
+VK_H :: 'H'
+VK_I :: 'I'
+VK_J :: 'J'
+VK_K :: 'K'
+VK_L :: 'L'
+VK_M :: 'M'
+VK_N :: 'N'
+VK_O :: 'O'
+VK_P :: 'P'
+VK_Q :: 'Q'
+VK_R :: 'R'
+VK_S :: 'S'
+VK_T :: 'T'
+VK_U :: 'U'
+VK_V :: 'V'
+VK_W :: 'W'
+VK_X :: 'X'
+VK_Y :: 'Y'
+VK_Z :: 'Z'
+
+VK_LWIN :: 0x5B
+VK_RWIN :: 0x5C
+VK_APPS :: 0x5D
+
+// 0x5E : reserved
+
+VK_SLEEP :: 0x5F
+VK_NUMPAD0 :: 0x60
+VK_NUMPAD1 :: 0x61
+VK_NUMPAD2 :: 0x62
+VK_NUMPAD3 :: 0x63
+VK_NUMPAD4 :: 0x64
+VK_NUMPAD5 :: 0x65
+VK_NUMPAD6 :: 0x66
+VK_NUMPAD7 :: 0x67
+VK_NUMPAD8 :: 0x68
+VK_NUMPAD9 :: 0x69
+VK_MULTIPLY :: 0x6A
+VK_ADD :: 0x6B
+VK_SEPARATOR :: 0x6C
+VK_SUBTRACT :: 0x6D
+VK_DECIMAL :: 0x6E
+VK_DIVIDE :: 0x6F
+VK_F1 :: 0x70
+VK_F2 :: 0x71
+VK_F3 :: 0x72
+VK_F4 :: 0x73
+VK_F5 :: 0x74
+VK_F6 :: 0x75
+VK_F7 :: 0x76
+VK_F8 :: 0x77
+VK_F9 :: 0x78
+VK_F10 :: 0x79
+VK_F11 :: 0x7A
+VK_F12 :: 0x7B
+VK_F13 :: 0x7C
+VK_F14 :: 0x7D
+VK_F15 :: 0x7E
+VK_F16 :: 0x7F
+VK_F17 :: 0x80
+VK_F18 :: 0x81
+VK_F19 :: 0x82
+VK_F20 :: 0x83
+VK_F21 :: 0x84
+VK_F22 :: 0x85
+VK_F23 :: 0x86
+VK_F24 :: 0x87
+
+// 0x88 - 0x8F : reserved
+
+VK_NUMLOCK :: 0x90
+VK_SCROLL :: 0x91
+
+// NEC PC-9800 kbd definitions
+VK_OEM_NEC_EQUAL :: 0x92 // '=' key on numpad
+
+// Fujitsu/OASYS kbd definitions
+VK_OEM_FJ_JISHO :: 0x92 // 'Dictionary' key
+VK_OEM_FJ_MASSHOU :: 0x93 // 'Unregister word' key
+VK_OEM_FJ_TOUROKU :: 0x94 // 'Register word' key
+VK_OEM_FJ_LOYA :: 0x95 // 'Left OYAYUBI' key
+VK_OEM_FJ_ROYA :: 0x96 // 'Right OYAYUBI' key
+
+// 0x97 - 0x9F : unassigned
+
+// VK_L* & VK_R* - left and right Alt, Ctrl and Shift virtual keys.
+// Used only as parameters to GetAsyncKeyState() and GetKeyState().
+// No other API or message will distinguish left and right keys in this way.
+VK_LSHIFT :: 0xA0
+VK_RSHIFT :: 0xA1
+VK_LCONTROL :: 0xA2
+VK_RCONTROL :: 0xA3
+VK_LMENU :: 0xA4
+VK_RMENU :: 0xA5
+
+VK_BROWSER_BACK :: 0xA6
+VK_BROWSER_FORWARD :: 0xA7
+VK_BROWSER_REFRESH :: 0xA8
+VK_BROWSER_STOP :: 0xA9
+VK_BROWSER_SEARCH :: 0xAA
+VK_BROWSER_FAVORITES :: 0xAB
+VK_BROWSER_HOME :: 0xAC
+VK_VOLUME_MUTE :: 0xAD
+VK_VOLUME_DOWN :: 0xAE
+VK_VOLUME_UP :: 0xAF
+VK_MEDIA_NEXT_TRACK :: 0xB0
+VK_MEDIA_PREV_TRACK :: 0xB1
+VK_MEDIA_STOP :: 0xB2
+VK_MEDIA_PLAY_PAUSE :: 0xB3
+VK_LAUNCH_MAIL :: 0xB4
+VK_LAUNCH_MEDIA_SELECT :: 0xB5
+VK_LAUNCH_APP1 :: 0xB6
+VK_LAUNCH_APP2 :: 0xB7
+
+// 0xB8 - 0xB9 : reserved
+
+VK_OEM_1 :: 0xBA // ';:' for US
+VK_OEM_PLUS :: 0xBB // '+' any country
+VK_OEM_COMMA :: 0xBC // ',' any country
+VK_OEM_MINUS :: 0xBD // '-' any country
+VK_OEM_PERIOD :: 0xBE // '.' any country
+VK_OEM_2 :: 0xBF // '/?' for US
+VK_OEM_3 :: 0xC0 // '`~' for US
+
+// 0xC1 - 0xDA : reserved
+
+VK_OEM_4 :: 0xDB // '[{' for US
+VK_OEM_5 :: 0xDC // '\|' for US
+VK_OEM_6 :: 0xDD // ']}' for US
+VK_OEM_7 :: 0xDE // ''"' for US
+VK_OEM_8 :: 0xDF
+
+// 0xE0 : reserved
+
+// Various extended or enhanced keyboards
+VK_OEM_AX :: 0xE1 // 'AX' key on Japanese AX kbd
+VK_OEM_102 :: 0xE2 // "<>" or "\|" on RT 102-key kbd.
+VK_ICO_HELP :: 0xE3 // Help key on ICO
+VK_ICO_00 :: 0xE4 // 00 key on ICO
+
+VK_PROCESSKEY :: 0xE5
+VK_ICO_CLEAR :: 0xE6
+VK_PACKET :: 0xE7
+
+// 0xE8 : unassigned
+
+// Nokia/Ericsson definitions
+VK_OEM_RESET :: 0xE9
+VK_OEM_JUMP :: 0xEA
+VK_OEM_PA1 :: 0xEB
+VK_OEM_PA2 :: 0xEC
+VK_OEM_PA3 :: 0xED
+VK_OEM_WSCTRL :: 0xEE
+VK_OEM_CUSEL :: 0xEF
+VK_OEM_ATTN :: 0xF0
+VK_OEM_FINISH :: 0xF1
+VK_OEM_COPY :: 0xF2
+VK_OEM_AUTO :: 0xF3
+VK_OEM_ENLW :: 0xF4
+VK_OEM_BACKTAB :: 0xF5
+
+VK_ATTN :: 0xF6
+VK_CRSEL :: 0xF7
+VK_EXSEL :: 0xF8
+VK_EREOF :: 0xF9
+VK_PLAY :: 0xFA
+VK_ZOOM :: 0xFB
+VK_NONAME :: 0xFC
+VK_PA1 :: 0xFD
+VK_OEM_CLEAR :: 0xFE
+
+// 0xFF : reserved
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 3e25a4c18..6bded29e1 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -21,7 +21,16 @@ HINSTANCE :: HANDLE
HMODULE :: distinct HINSTANCE
HRESULT :: distinct LONG
HWND :: distinct HANDLE
+HDC :: distinct HANDLE
HMONITOR :: distinct HANDLE
+HICON :: distinct HANDLE
+HCURSOR :: distinct HANDLE
+HMENU :: distinct HANDLE
+HBRUSH :: distinct HANDLE
+HGDIOBJ :: distinct HANDLE
+HBITMAP :: distinct HANDLE
+HGLOBAL :: distinct HANDLE
+HHOOK :: distinct HANDLE
BOOL :: distinct b32
BYTE :: distinct u8
BOOLEAN :: distinct b8
@@ -42,9 +51,15 @@ PULONG_PTR :: ^ULONG_PTR
LPULONG_PTR :: ^ULONG_PTR
DWORD_PTR :: ULONG_PTR
LONG_PTR :: int
+UINT_PTR :: uintptr
ULONG :: c_ulong
UCHAR :: BYTE
NTSTATUS :: c.long
+LPARAM :: LONG_PTR
+WPARAM :: UINT_PTR
+LRESULT :: LONG_PTR
+LPRECT :: ^RECT
+LPPOINT :: ^POINT
UINT8 :: u8
UINT16 :: u16
@@ -71,6 +86,7 @@ PBOOL :: ^BOOL
LPBOOL :: ^BOOL
LPCSTR :: cstring
LPCWSTR :: wstring
+LPCTSTR :: wstring
LPDWORD :: ^DWORD
PCSTR :: cstring
PCWSTR :: wstring
@@ -81,7 +97,9 @@ LPPROCESS_INFORMATION :: ^PROCESS_INFORMATION
PSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSECURITY_ATTRIBUTES :: ^SECURITY_ATTRIBUTES
LPSTARTUPINFO :: ^STARTUPINFO
-PVOID :: rawptr
+LPTRACKMOUSEEVENT :: ^TRACKMOUSEEVENT
+VOID :: rawptr
+PVOID :: rawptr
LPVOID :: rawptr
PINT :: ^INT
LPINT :: ^INT
@@ -105,6 +123,8 @@ PCONDITION_VARIABLE :: ^CONDITION_VARIABLE
PLARGE_INTEGER :: ^LARGE_INTEGER
PSRWLOCK :: ^SRWLOCK
+MMRESULT :: UINT
+
SOCKET :: distinct uintptr // TODO
socklen_t :: c_int
ADDRESS_FAMILY :: USHORT
@@ -177,6 +197,159 @@ 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
+
+DIAGNOSTIC_REASON_SIMPLE_STRING :: 0x00000001
+DIAGNOSTIC_REASON_DETAILED_STRING :: 0x00000002
+DIAGNOSTIC_REASON_NOT_SPECIFIED :: 0x80000000
+
+// Defines for power request APIs
+
+POWER_REQUEST_CONTEXT_VERSION :: DIAGNOSTIC_REASON_VERSION
+
+POWER_REQUEST_CONTEXT_SIMPLE_STRING :: DIAGNOSTIC_REASON_SIMPLE_STRING
+POWER_REQUEST_CONTEXT_DETAILED_STRING :: DIAGNOSTIC_REASON_DETAILED_STRING
+
+REASON_CONTEXT :: struct {
+ Version: ULONG,
+ Flags: DWORD,
+ Reason: struct #raw_union {
+ Detailed: struct {
+ LocalizedReasonModule: HMODULE,
+ LocalizedReasonId: ULONG,
+ ReasonStringCount: ULONG,
+ ReasonStrings: ^LPWSTR,
+ },
+ SimpleReasonString: LPWSTR,
+ },
+}
+PREASON_CONTEXT :: ^REASON_CONTEXT
+
+PTIMERAPCROUTINE :: #type proc "stdcall" (lpArgToCompletionRoutine: LPVOID, dwTimerLowValue, dwTimerHighValue: DWORD)
+
+TIMERPROC :: #type proc "stdcall" (HWND, UINT, UINT_PTR, DWORD)
+
+WNDPROC :: #type proc "stdcall" (HWND, UINT, WPARAM, LPARAM) -> LRESULT
+
+HOOKPROC :: #type proc "stdcall" (code: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT
+
+CWPRETSTRUCT :: struct {
+ lResult: LRESULT,
+ lParam: LPARAM,
+ wParam: WPARAM,
+ message: UINT,
+ hwnd: HWND,
+}
+
+KBDLLHOOKSTRUCT :: struct {
+ vkCode: DWORD,
+ scanCode: DWORD,
+ flags: DWORD,
+ time: DWORD,
+ dwExtraInfo: ULONG_PTR,
+}
+
+WNDCLASSA :: struct {
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCSTR,
+ lpszClassName: LPCSTR,
+}
+
+WNDCLASSW :: struct {
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCWSTR,
+ lpszClassName: LPCWSTR,
+}
+
+WNDCLASSEXA :: struct {
+ cbSize: UINT,
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCSTR,
+ lpszClassName: LPCSTR,
+ hIconSm: HICON,
+}
+
+WNDCLASSEXW :: struct {
+ cbSize: UINT,
+ style: UINT,
+ lpfnWndProc: WNDPROC,
+ cbClsExtra: c_int,
+ cbWndExtra: c_int,
+ hInstance: HINSTANCE,
+ hIcon: HICON,
+ hCursor: HCURSOR,
+ hbrBackground: HBRUSH,
+ lpszMenuName: LPCWSTR,
+ lpszClassName: LPCWSTR,
+ hIconSm: HICON,
+}
+
+MSG :: struct {
+ hwnd: HWND,
+ message: UINT,
+ wParam: WPARAM,
+ lParam: LPARAM,
+ time: DWORD,
+ pt: POINT,
+}
+
+PAINTSTRUCT :: struct {
+ hdc: HDC,
+ fErase: BOOL,
+ rcPaint: RECT,
+ fRestore: BOOL,
+ fIncUpdate: BOOL,
+ rgbReserved: [32]BYTE,
+}
+
+TRACKMOUSEEVENT :: struct {
+ cbSize: DWORD,
+ dwFlags: DWORD,
+ hwndTrack: HWND,
+ dwHoverTime: DWORD,
+}
WIN32_FIND_DATAW :: struct {
dwFileAttributes: DWORD,
@@ -191,6 +364,709 @@ WIN32_FIND_DATAW :: struct {
cAlternateFileName: [14]wchar_t,
}
+CREATESTRUCTA :: struct {
+ lpCreateParams: LPVOID,
+ hInstance: HINSTANCE,
+ hMenu: HMENU,
+ hwndParent: HWND,
+ cy: c_int,
+ cx: c_int,
+ y: c_int,
+ x: c_int,
+ style: LONG,
+ lpszName: LPCSTR,
+ lpszClass: LPCSTR,
+ dwExStyle: DWORD,
+}
+
+CREATESTRUCTW:: struct {
+ lpCreateParams: LPVOID,
+ hInstance: HINSTANCE,
+ hMenu: HMENU,
+ hwndParent: HWND,
+ cy: c_int,
+ cx: c_int,
+ y: c_int,
+ x: c_int,
+ style: LONG,
+ lpszName: LPCWSTR,
+ lpszClass: LPCWSTR,
+ dwExStyle: DWORD,
+}
+
+// MessageBox() Flags
+MB_OK :: 0x00000000
+MB_OKCANCEL :: 0x00000001
+MB_ABORTRETRYIGNORE :: 0x00000002
+MB_YESNOCANCEL :: 0x00000003
+MB_YESNO :: 0x00000004
+MB_RETRYCANCEL :: 0x00000005
+MB_CANCELTRYCONTINUE :: 0x00000006
+
+MB_ICONHAND :: 0x00000010
+MB_ICONQUESTION :: 0x00000020
+MB_ICONEXCLAMATION :: 0x00000030
+MB_ICONASTERISK :: 0x00000040
+MB_USERICON :: 0x00000080
+MB_ICONWARNING :: MB_ICONEXCLAMATION
+MB_ICONERROR :: MB_ICONHAND
+MB_ICONINFORMATION :: MB_ICONASTERISK
+MB_ICONSTOP :: MB_ICONHAND
+
+MB_DEFBUTTON1 :: 0x00000000
+MB_DEFBUTTON2 :: 0x00000100
+MB_DEFBUTTON3 :: 0x00000200
+MB_DEFBUTTON4 :: 0x00000300
+
+MB_APPLMODAL :: 0x00000000
+MB_SYSTEMMODAL :: 0x00001000
+MB_TASKMODAL :: 0x00002000
+MB_HELP :: 0x00004000 // Help Button
+
+MB_NOFOCUS :: 0x00008000
+MB_SETFOREGROUND :: 0x00010000
+MB_DEFAULT_DESKTOP_ONLY :: 0x00020000
+MB_TOPMOST :: 0x00040000
+MB_RIGHT :: 0x00080000
+MB_RTLREADING :: 0x00100000
+
+MB_SERVICE_NOTIFICATION :: 0x00200000
+MB_SERVICE_NOTIFICATION_NT3X :: 0x00040000
+
+MB_TYPEMASK :: 0x0000000F
+MB_ICONMASK :: 0x000000F0
+MB_DEFMASK :: 0x00000F00
+MB_MODEMASK :: 0x00003000
+MB_MISCMASK :: 0x0000C000
+
+// Dialog Box Command IDs
+IDOK :: 1
+IDCANCEL :: 2
+IDABORT :: 3
+IDRETRY :: 4
+IDIGNORE :: 5
+IDYES :: 6
+IDNO :: 7
+IDCLOSE :: 8
+IDHELP :: 9
+IDTRYAGAIN :: 10
+IDCONTINUE :: 11
+IDTIMEOUT :: 32000
+
+CS_VREDRAW : UINT : 0x0001
+CS_HREDRAW : UINT : 0x0002
+CS_DBLCLKS : UINT : 0x0008
+CS_OWNDC : UINT : 0x0020
+CS_CLASSDC : UINT : 0x0040
+CS_PARENTDC : UINT : 0x0080
+CS_NOCLOSE : UINT : 0x0200
+CS_SAVEBITS : UINT : 0x0800
+CS_BYTEALIGNCLIENT : UINT : 0x1000
+CS_BYTEALIGNWINDOW : UINT : 0x2000
+CS_GLOBALCLASS : UINT : 0x4000
+CS_DROPSHADOW : UINT : 0x0002_0000
+
+WS_BORDER : UINT : 0x0080_0000
+WS_CAPTION : UINT : 0x00C0_0000
+WS_CHILD : UINT : 0x4000_0000
+WS_CHILDWINDOW : UINT : WS_CHILD
+WS_CLIPCHILDREN : UINT : 0x0200_0000
+WS_CLIPSIBLINGS : UINT : 0x0400_0000
+WS_DISABLED : UINT : 0x0800_0000
+WS_DLGFRAME : UINT : 0x0040_0000
+WS_GROUP : UINT : 0x0002_0000
+WS_HSCROLL : UINT : 0x0010_0000
+WS_ICONIC : UINT : 0x2000_0000
+WS_MAXIMIZE : UINT : 0x0100_0000
+WS_MAXIMIZEBOX : UINT : 0x0001_0000
+WS_MINIMIZE : UINT : 0x2000_0000
+WS_MINIMIZEBOX : UINT : 0x0002_0000
+WS_OVERLAPPED : UINT : 0x0000_0000
+WS_OVERLAPPEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
+WS_POPUP : UINT : 0x8000_0000
+WS_POPUPWINDOW : UINT : WS_POPUP | WS_BORDER | WS_SYSMENU
+WS_SIZEBOX : UINT : 0x0004_0000
+WS_SYSMENU : UINT : 0x0008_0000
+WS_TABSTOP : UINT : 0x0001_0000
+WS_THICKFRAME : UINT : 0x0004_0000
+WS_TILED : UINT : 0x0000_0000
+WS_TILEDWINDOW : UINT : WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZE | WS_MAXIMIZE
+WS_VISIBLE : UINT : 0x1000_0000
+WS_VSCROLL : UINT : 0x0020_0000
+
+QS_ALLEVENTS : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY
+QS_ALLINPUT : UINT : QS_INPUT | QS_POSTMESSAGE | QS_TIMER | QS_PAINT | QS_HOTKEY | QS_SENDMESSAGE
+QS_ALLPOSTMESSAGE : UINT : 0x0100
+QS_HOTKEY : UINT : 0x0080
+QS_INPUT : UINT : QS_MOUSE | QS_KEY | QS_RAWINPUT
+QS_KEY : UINT : 0x0001
+QS_MOUSE : UINT : QS_MOUSEMOVE | QS_MOUSEBUTTON
+QS_MOUSEBUTTON : UINT : 0x0004
+QS_MOUSEMOVE : UINT : 0x0002
+QS_PAINT : UINT : 0x0020
+QS_POSTMESSAGE : UINT : 0x0008
+QS_RAWINPUT : UINT : 0x0400
+QS_SENDMESSAGE : UINT : 0x0040
+QS_TIMER : UINT : 0x0010
+
+PM_NOREMOVE : UINT : 0x0000
+PM_REMOVE : UINT : 0x0001
+PM_NOYIELD : UINT : 0x0002
+
+PM_QS_INPUT : UINT : QS_INPUT << 16
+PM_QS_PAINT : UINT : QS_PAINT << 16
+PM_QS_POSTMESSAGE : UINT : (QS_POSTMESSAGE | QS_HOTKEY | QS_TIMER) << 16
+PM_QS_SENDMESSAGE : UINT : QS_SENDMESSAGE << 16
+
+SW_HIDE : c_int : 0
+SW_SHOWNORMAL : c_int : SW_NORMAL
+SW_NORMAL : c_int : 1
+SW_SHOWMINIMIZED : c_int : 2
+SW_SHOWMAXIMIZED : c_int : SW_MAXIMIZE
+SW_MAXIMIZE : c_int : 3
+SW_SHOWNOACTIVATE : c_int : 4
+SW_SHOW : c_int : 5
+SW_MINIMIZE : c_int : 6
+SW_SHOWMINNOACTIVE : c_int : 7
+SW_SHOWNA : c_int : 8
+SW_RESTORE : c_int : 9
+SW_SHOWDEFAULT : c_int : 10
+SW_FORCEMINIMIZE : c_int : 11
+
+// SetWindowPos Flags
+SWP_NOSIZE :: 0x0001
+SWP_NOMOVE :: 0x0002
+SWP_NOZORDER :: 0x0004
+SWP_NOREDRAW :: 0x0008
+SWP_NOACTIVATE :: 0x0010
+SWP_FRAMECHANGED :: 0x0020 // The frame changed: send WM_NCCALCSIZE
+SWP_SHOWWINDOW :: 0x0040
+SWP_HIDEWINDOW :: 0x0080
+SWP_NOCOPYBITS :: 0x0100
+SWP_NOOWNERZORDER :: 0x0200 // Don't do owner Z ordering
+SWP_NOSENDCHANGING :: 0x0400 // Don't send WM_WINDOWPOSCHANGING
+
+SWP_DRAWFRAME :: SWP_FRAMECHANGED
+SWP_NOREPOSITION :: SWP_NOOWNERZORDER
+
+SWP_DEFERERASE :: 0x2000 // same as SWP_DEFERDRAWING
+SWP_ASYNCWINDOWPOS :: 0x4000 // same as SWP_CREATESPB
+
+HWND_TOP :: HWND( uintptr(0)) // 0
+HWND_BOTTOM :: HWND( uintptr(1)) // 1
+HWND_TOPMOST :: HWND(~uintptr(0)) // -1
+HWND_NOTOPMOST :: HWND(~uintptr(0) - 1) // -2
+
+// Window field offsets for GetWindowLong()
+GWL_STYLE :: -16
+GWL_EXSTYLE :: -20
+GWL_ID :: -12
+
+when ODIN_ARCH == .i386 {
+ GWL_WNDPROC :: -4
+ GWL_HINSTANCE :: -6
+ GWL_HWNDPARENT :: -8
+ GWL_USERDATA :: -21
+}
+
+GWLP_WNDPROC :: -4
+GWLP_HINSTANCE :: -6
+GWLP_HWNDPARENT :: -8
+GWLP_USERDATA :: -21
+GWLP_ID :: -12
+
+// Class field offsets for GetClassLong()
+GCL_CBWNDEXTRA :: -18
+GCL_CBCLSEXTRA :: -20
+GCL_STYLE :: -26
+GCW_ATOM :: -32
+
+when ODIN_ARCH == .i386 {
+ GCL_MENUNAME :: -8
+ GCL_HBRBACKGROUND :: -10
+ GCL_HCURSOR :: -12
+ GCL_HICON :: -14
+ GCL_HMODULE :: -16
+ GCL_WNDPROC :: -24
+ GCL_HICONSM :: -34
+}
+
+GCLP_MENUNAME :: -8
+GCLP_HBRBACKGROUND :: -10
+GCLP_HCURSOR :: -12
+GCLP_HICON :: -14
+GCLP_HMODULE :: -16
+GCLP_WNDPROC :: -24
+GCLP_HICONSM :: -34
+
+// GetSystemMetrics() codes
+SM_CXSCREEN :: 0
+SM_CYSCREEN :: 1
+SM_CXVSCROLL :: 2
+SM_CYHSCROLL :: 3
+SM_CYCAPTION :: 4
+SM_CXBORDER :: 5
+SM_CYBORDER :: 6
+SM_CXDLGFRAME :: 7
+SM_CYDLGFRAME :: 8
+SM_CYVTHUMB :: 9
+SM_CXHTHUMB :: 10
+SM_CXICON :: 11
+SM_CYICON :: 12
+SM_CXCURSOR :: 13
+SM_CYCURSOR :: 14
+SM_CYMENU :: 15
+SM_CXFULLSCREEN :: 16
+SM_CYFULLSCREEN :: 17
+SM_CYKANJIWINDOW :: 18
+SM_MOUSEPRESENT :: 19
+SM_CYVSCROLL :: 20
+SM_CXHSCROLL :: 21
+SM_DEBUG :: 22
+SM_SWAPBUTTON :: 23
+SM_RESERVED1 :: 24
+SM_RESERVED2 :: 25
+SM_RESERVED3 :: 26
+SM_RESERVED4 :: 27
+SM_CXMIN :: 28
+SM_CYMIN :: 29
+SM_CXSIZE :: 30
+SM_CYSIZE :: 31
+SM_CXFRAME :: 32
+SM_CYFRAME :: 33
+SM_CXMINTRACK :: 34
+SM_CYMINTRACK :: 35
+SM_CXDOUBLECLK :: 36
+SM_CYDOUBLECLK :: 37
+SM_CXICONSPACING :: 38
+SM_CYICONSPACING :: 39
+SM_MENUDROPALIGNMENT :: 40
+SM_PENWINDOWS :: 41
+SM_DBCSENABLED :: 42
+SM_CMOUSEBUTTONS :: 43
+
+SM_CXFIXEDFRAME :: SM_CXDLGFRAME // ;win40 name change
+SM_CYFIXEDFRAME :: SM_CYDLGFRAME // ;win40 name change
+SM_CXSIZEFRAME :: SM_CXFRAME // ;win40 name change
+SM_CYSIZEFRAME :: SM_CYFRAME // ;win40 name change
+
+SM_SECURE :: 44
+SM_CXEDGE :: 45
+SM_CYEDGE :: 46
+SM_CXMINSPACING :: 47
+SM_CYMINSPACING :: 48
+SM_CXSMICON :: 49
+SM_CYSMICON :: 50
+SM_CYSMCAPTION :: 51
+SM_CXSMSIZE :: 52
+SM_CYSMSIZE :: 53
+SM_CXMENUSIZE :: 54
+SM_CYMENUSIZE :: 55
+SM_ARRANGE :: 56
+SM_CXMINIMIZED :: 57
+SM_CYMINIMIZED :: 58
+SM_CXMAXTRACK :: 59
+SM_CYMAXTRACK :: 60
+SM_CXMAXIMIZED :: 61
+SM_CYMAXIMIZED :: 62
+SM_NETWORK :: 63
+SM_CLEANBOOT :: 67
+SM_CXDRAG :: 68
+SM_CYDRAG :: 69
+
+SM_SHOWSOUNDS :: 70
+SM_CXMENUCHECK :: 71 // Use instead of GetMenuCheckMarkDimensions()!
+SM_CYMENUCHECK :: 72
+SM_SLOWMACHINE :: 73
+SM_MIDEASTENABLED :: 74
+SM_MOUSEWHEELPRESENT :: 75
+SM_XVIRTUALSCREEN :: 76
+SM_YVIRTUALSCREEN :: 77
+SM_CXVIRTUALSCREEN :: 78
+SM_CYVIRTUALSCREEN :: 79
+SM_CMONITORS :: 80
+SM_SAMEDISPLAYFORMAT :: 81
+SM_IMMENABLED :: 82
+SM_CXFOCUSBORDER :: 83
+SM_CYFOCUSBORDER :: 84
+SM_TABLETPC :: 86
+SM_MEDIACENTER :: 87
+SM_STARTER :: 88
+SM_SERVERR2 :: 89
+
+SM_MOUSEHORIZONTALWHEELPRESENT :: 91
+
+SM_CXPADDEDBORDER :: 92
+SM_DIGITIZER :: 94
+SM_MAXIMUMTOUCHES :: 95
+SM_CMETRICS :: 97
+
+SM_REMOTESESSION :: 0x1000
+SM_SHUTTINGDOWN :: 0x2000
+SM_REMOTECONTROL :: 0x2001
+SM_CARETBLINKINGENABLED :: 0x2002
+SM_CONVERTIBLESLATEMODE :: 0x2003
+SM_SYSTEMDOCKED :: 0x2004
+
+// System Menu Command Values
+SC_SIZE :: 0xF000
+SC_MOVE :: 0xF010
+SC_MINIMIZE :: 0xF020
+SC_MAXIMIZE :: 0xF030
+SC_NEXTWINDOW :: 0xF040
+SC_PREVWINDOW :: 0xF050
+SC_CLOSE :: 0xF060
+SC_VSCROLL :: 0xF070
+SC_HSCROLL :: 0xF080
+SC_MOUSEMENU :: 0xF090
+SC_KEYMENU :: 0xF100
+SC_ARRANGE :: 0xF110
+SC_RESTORE :: 0xF120
+SC_TASKLIST :: 0xF130
+SC_SCREENSAVE :: 0xF140
+SC_HOTKEY :: 0xF150
+SC_DEFAULT :: 0xF160
+SC_MONITORPOWER :: 0xF170
+SC_CONTEXTHELP :: 0xF180
+SC_SEPARATOR :: 0xF00F
+SCF_ISSECURE :: 0x00000001
+SC_ICON :: SC_MINIMIZE
+SC_ZOOM :: SC_MAXIMIZE
+
+CW_USEDEFAULT : c_int : -2147483648
+
+SIZE_RESTORED :: 0
+SIZE_MINIMIZED :: 1
+SIZE_MAXIMIZED :: 2
+SIZE_MAXSHOW :: 3
+SIZE_MAXHIDE :: 4
+
+WMSZ_LEFT :: 1
+WMSZ_RIGHT :: 2
+WMSZ_TOP :: 3
+WMSZ_TOPLEFT :: 4
+WMSZ_TOPRIGHT :: 5
+WMSZ_BOTTOM :: 6
+WMSZ_BOTTOMLEFT :: 7
+WMSZ_BOTTOMRIGHT :: 8
+
+// Key State Masks for Mouse Messages
+MK_LBUTTON :: 0x0001
+MK_RBUTTON :: 0x0002
+MK_SHIFT :: 0x0004
+MK_CONTROL :: 0x0008
+MK_MBUTTON :: 0x0010
+MK_XBUTTON1 :: 0x0020
+MK_XBUTTON2 :: 0x0040
+
+// Value for rolling one detent
+WHEEL_DELTA :: 120
+
+// Setting to scroll one page for SPI_GET/SETWHEELSCROLLLINES
+WHEEL_PAGESCROLL :: max(UINT)
+
+// XButton values are WORD flags
+XBUTTON1 :: 0x0001
+XBUTTON2 :: 0x0002
+// Were there to be an XBUTTON3, its value would be 0x0004
+
+MAPVK_VK_TO_VSC :: 0
+MAPVK_VSC_TO_VK :: 1
+MAPVK_VK_TO_CHAR :: 2
+MAPVK_VSC_TO_VK_EX :: 3
+MAPVK_VK_TO_VSC_EX :: 4
+
+TME_HOVER :: 0x00000001
+TME_LEAVE :: 0x00000002
+TME_NONCLIENT :: 0x00000010
+TME_QUERY :: 0x40000000
+TME_CANCEL :: 0x80000000
+HOVER_DEFAULT :: 0xFFFFFFFF
+
+USER_TIMER_MAXIMUM :: 0x7FFFFFFF
+USER_TIMER_MINIMUM :: 0x0000000A
+
+// WM_ACTIVATE state values
+WA_INACTIVE :: 0
+WA_ACTIVE :: 1
+WA_CLICKACTIVE :: 2
+
+// SetWindowsHook() codes
+WH_MIN :: -1
+WH_MSGFILTER :: -1
+WH_JOURNALRECORD :: 0
+WH_JOURNALPLAYBACK :: 1
+WH_KEYBOARD :: 2
+WH_GETMESSAGE :: 3
+WH_CALLWNDPROC :: 4
+WH_CBT :: 5
+WH_SYSMSGFILTER :: 6
+WH_MOUSE :: 7
+WH_HARDWARE :: 8
+WH_DEBUG :: 9
+WH_SHELL :: 10
+WH_FOREGROUNDIDLE :: 11
+WH_CALLWNDPROCRET :: 12
+WH_KEYBOARD_LL :: 13
+WH_MOUSE_LL :: 14
+WH_MAX :: 14
+WH_MINHOOK :: WH_MIN
+WH_MAXHOOK :: WH_MAX
+
+// Hook Codes
+HC_ACTION :: 0
+HC_GETNEXT :: 1
+HC_SKIP :: 2
+HC_NOREMOVE :: 3
+HC_NOREM :: HC_NOREMOVE
+HC_SYSMODALON :: 4
+HC_SYSMODALOFF :: 5
+
+// CBT Hook Codes
+HCBT_MOVESIZE :: 0
+HCBT_MINMAX :: 1
+HCBT_QS :: 2
+HCBT_CREATEWND :: 3
+HCBT_DESTROYWND :: 4
+HCBT_ACTIVATE :: 5
+HCBT_CLICKSKIPPED :: 6
+HCBT_KEYSKIPPED :: 7
+HCBT_SYSCOMMAND :: 8
+HCBT_SETFOCUS :: 9
+
+_IDC_APPSTARTING := rawptr(uintptr(32650))
+_IDC_ARROW := rawptr(uintptr(32512))
+_IDC_CROSS := rawptr(uintptr(32515))
+_IDC_HAND := rawptr(uintptr(32649))
+_IDC_HELP := rawptr(uintptr(32651))
+_IDC_IBEAM := rawptr(uintptr(32513))
+_IDC_ICON := rawptr(uintptr(32641))
+_IDC_NO := rawptr(uintptr(32648))
+_IDC_SIZE := rawptr(uintptr(32640))
+_IDC_SIZEALL := rawptr(uintptr(32646))
+_IDC_SIZENESW := rawptr(uintptr(32643))
+_IDC_SIZENS := rawptr(uintptr(32645))
+_IDC_SIZENWSE := rawptr(uintptr(32642))
+_IDC_SIZEWE := rawptr(uintptr(32644))
+_IDC_UPARROW := rawptr(uintptr(32516))
+_IDC_WAIT := rawptr(uintptr(32514))
+
+IDC_APPSTARTING := cstring(_IDC_APPSTARTING)
+IDC_ARROW := cstring(_IDC_ARROW)
+IDC_CROSS := cstring(_IDC_CROSS)
+IDC_HAND := cstring(_IDC_HAND)
+IDC_HELP := cstring(_IDC_HELP)
+IDC_IBEAM := cstring(_IDC_IBEAM)
+IDC_ICON := cstring(_IDC_ICON)
+IDC_NO := cstring(_IDC_NO)
+IDC_SIZE := cstring(_IDC_SIZE)
+IDC_SIZEALL := cstring(_IDC_SIZEALL)
+IDC_SIZENESW := cstring(_IDC_SIZENESW)
+IDC_SIZENS := cstring(_IDC_SIZENS)
+IDC_SIZENWSE := cstring(_IDC_SIZENWSE)
+IDC_SIZEWE := cstring(_IDC_SIZEWE)
+IDC_UPARROW := cstring(_IDC_UPARROW)
+IDC_WAIT := cstring(_IDC_WAIT)
+
+
+_IDI_APPLICATION := rawptr(uintptr(32512))
+_IDI_HAND := rawptr(uintptr(32513))
+_IDI_QUESTION := rawptr(uintptr(32514))
+_IDI_EXCLAMATION := rawptr(uintptr(32515))
+_IDI_ASTERISK := rawptr(uintptr(32516))
+_IDI_WINLOGO := rawptr(uintptr(32517))
+_IDI_SHIELD := rawptr(uintptr(32518))
+IDI_APPLICATION := cstring(_IDI_APPLICATION)
+IDI_HAND := cstring(_IDI_HAND)
+IDI_QUESTION := cstring(_IDI_QUESTION)
+IDI_EXCLAMATION := cstring(_IDI_EXCLAMATION)
+IDI_ASTERISK := cstring(_IDI_ASTERISK)
+IDI_WINLOGO := cstring(_IDI_WINLOGO)
+IDI_SHIELD := cstring(_IDI_SHIELD)
+IDI_WARNING := IDI_EXCLAMATION
+IDI_ERROR := IDI_HAND
+IDI_INFORMATION := IDI_ASTERISK
+
+
+// DIB color table identifiers
+DIB_RGB_COLORS :: 0
+DIB_PAL_COLORS :: 1
+
+// constants for CreateDIBitmap
+CBM_INIT :: 0x04 // initialize bitmap
+
+// Region Flags
+ERROR :: 0
+NULLREGION :: 1
+SIMPLEREGION :: 2
+COMPLEXREGION :: 3
+RGN_ERROR :: ERROR
+
+// StretchBlt() Modes
+BLACKONWHITE :: 1
+WHITEONBLACK :: 2
+COLORONCOLOR :: 3
+HALFTONE :: 4
+MAXSTRETCHBLTMODE :: 4
+
+// Binary raster ops
+R2_BLACK :: 1 // 0
+R2_NOTMERGEPEN :: 2 // DPon
+R2_MASKNOTPEN :: 3 // DPna
+R2_NOTCOPYPEN :: 4 // PN
+R2_MASKPENNOT :: 5 // PDna
+R2_NOT :: 6 // Dn
+R2_XORPEN :: 7 // DPx
+R2_NOTMASKPEN :: 8 // DPan
+R2_MASKPEN :: 9 // DPa
+R2_NOTXORPEN :: 10 // DPxn
+R2_NOP :: 11 // D
+R2_MERGENOTPEN :: 12 // DPno
+R2_COPYPEN :: 13 // P
+R2_MERGEPENNOT :: 14 // PDno
+R2_MERGEPEN :: 15 // DPo
+R2_WHITE :: 16 // 1
+R2_LAST :: 16
+
+// Ternary raster operations
+SRCCOPY : DWORD : 0x00CC0020 // dest = source
+SRCPAINT : DWORD : 0x00EE0086 // dest = source OR dest
+SRCAND : DWORD : 0x008800C6 // dest = source AND dest
+SRCINVERT : DWORD : 0x00660046 // dest = source XOR dest
+SRCERASE : DWORD : 0x00440328 // dest = source AND (NOT dest)
+NOTSRCCOPY : DWORD : 0x00330008 // dest = (NOT source)
+NOTSRCERASE : DWORD : 0x001100A6 // dest = (NOT src) AND (NOT dest)
+MERGECOPY : DWORD : 0x00C000CA // dest = (source AND pattern
+MERGEPAINT : DWORD : 0x00BB0226 // dest = (NOT source) OR dest
+PATCOPY : DWORD : 0x00F00021 // dest = pattern
+PATPAINT : DWORD : 0x00FB0A09 // dest = DPSnoo
+PATINVERT : DWORD : 0x005A0049 // dest = pattern XOR dest
+DSTINVERT : DWORD : 0x00550009 // dest = (NOT dest)
+BLACKNESS : DWORD : 0x00000042 // dest = BLACK
+WHITENESS : DWORD : 0x00FF0062 // dest = WHITE
+NOMIRRORBITMAP : DWORD : 0x80000000 // Do not Mirror the bitmap in this call
+CAPTUREBLT : DWORD : 0x40000000 // Include layered windows
+
+// Stock Logical Objects
+WHITE_BRUSH :: 0
+LTGRAY_BRUSH :: 1
+GRAY_BRUSH :: 2
+DKGRAY_BRUSH :: 3
+BLACK_BRUSH :: 4
+NULL_BRUSH :: 5
+HOLLOW_BRUSH :: NULL_BRUSH
+WHITE_PEN :: 6
+BLACK_PEN :: 7
+NULL_PEN :: 8
+OEM_FIXED_FONT :: 10
+ANSI_FIXED_FONT :: 11
+ANSI_VAR_FONT :: 12
+SYSTEM_FONT :: 13
+DEVICE_DEFAULT_FONT :: 14
+DEFAULT_PALETTE :: 15
+SYSTEM_FIXED_FONT :: 16
+DEFAULT_GUI_FONT :: 17
+DC_BRUSH :: 18
+DC_PEN :: 19
+STOCK_LAST :: 19
+
+CLR_INVALID :: 0xFFFFFFFF
+
+RGBQUAD :: struct {
+ rgbBlue: BYTE,
+ rgbGreen: BYTE,
+ rgbRed: BYTE,
+ rgbReserved: BYTE,
+}
+
+PIXELFORMATDESCRIPTOR :: struct {
+ nSize: WORD,
+ nVersion: WORD,
+ dwFlags: DWORD,
+ iPixelType: BYTE,
+ cColorBits: BYTE,
+ cRedBits: BYTE,
+ cRedShift: BYTE,
+ cGreenBits: BYTE,
+ cGreenShift: BYTE,
+ cBlueBits: BYTE,
+ cBlueShift: BYTE,
+ cAlphaBits: BYTE,
+ cAlphaShift: BYTE,
+ cAccumBits: BYTE,
+ cAccumRedBits: BYTE,
+ cAccumGreenBits: BYTE,
+ cAccumBlueBits: BYTE,
+ cAccumAlphaBits: BYTE,
+ cDepthBits: BYTE,
+ cStencilBits: BYTE,
+ cAuxBuffers: BYTE,
+ iLayerType: BYTE,
+ bReserved: BYTE,
+ dwLayerMask: DWORD,
+ dwVisibleMask: DWORD,
+ dwDamageMask: DWORD,
+}
+
+BITMAPINFOHEADER :: struct {
+ biSize: DWORD,
+ biWidth: LONG,
+ biHeight: LONG,
+ biPlanes: WORD,
+ biBitCount: WORD,
+ biCompression: DWORD,
+ biSizeImage: DWORD,
+ biXPelsPerMeter: LONG,
+ biYPelsPerMeter: LONG,
+ biClrUsed: DWORD,
+ biClrImportant: DWORD,
+}
+
+BITMAPINFO :: struct {
+ bmiHeader: BITMAPINFOHEADER,
+ bmiColors: [1]RGBQUAD,
+}
+
+// pixel types
+PFD_TYPE_RGBA :: 0
+PFD_TYPE_COLORINDEX :: 1
+
+// layer types
+PFD_MAIN_PLANE :: 0
+PFD_OVERLAY_PLANE :: 1
+PFD_UNDERLAY_PLANE :: -1
+
+// PIXELFORMATDESCRIPTOR flags
+PFD_DOUBLEBUFFER :: 0x00000001
+PFD_STEREO :: 0x00000002
+PFD_DRAW_TO_WINDOW :: 0x00000004
+PFD_DRAW_TO_BITMAP :: 0x00000008
+PFD_SUPPORT_GDI :: 0x00000010
+PFD_SUPPORT_OPENGL :: 0x00000020
+PFD_GENERIC_FORMAT :: 0x00000040
+PFD_NEED_PALETTE :: 0x00000080
+PFD_NEED_SYSTEM_PALETTE :: 0x00000100
+PFD_SWAP_EXCHANGE :: 0x00000200
+PFD_SWAP_COPY :: 0x00000400
+PFD_SWAP_LAYER_BUFFERS :: 0x00000800
+PFD_GENERIC_ACCELERATED :: 0x00001000
+PFD_SUPPORT_DIRECTDRAW :: 0x00002000
+PFD_DIRECT3D_ACCELERATED :: 0x00004000
+PFD_SUPPORT_COMPOSITION :: 0x00008000
+
+// PIXELFORMATDESCRIPTOR flags for use in ChoosePixelFormat only
+PFD_DEPTH_DONTCARE :: 0x20000000
+PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
+PFD_STEREO_DONTCARE :: 0x80000000
+
+// constants for the biCompression field
+BI_RGB :: 0
+BI_RLE8 :: 1
+BI_RLE4 :: 2
+BI_BITFIELDS :: 3
+BI_JPEG :: 4
+BI_PNG :: 5
+
WSA_FLAG_OVERLAPPED: DWORD : 0x01
WSA_FLAG_NO_HANDLE_INHERIT: DWORD : 0x80
@@ -376,6 +1252,18 @@ FILE_TYPE_DISK :: 0x0001
FILE_TYPE_CHAR :: 0x0002
FILE_TYPE_PIPE :: 0x0003
+RECT :: struct {left, top, right, bottom: LONG}
+POINT :: struct {x, y: LONG}
+
+WINDOWPOS :: struct {
+ hwnd: HWND,
+ hwndInsertAfter: HWND,
+ x: c_int,
+ y: c_int,
+ cx: c_int,
+ cy: c_int,
+ flags: UINT,
+}
when size_of(uintptr) == 4 {
WSADATA :: struct {
@@ -575,7 +1463,7 @@ PROCESS_INFORMATION :: struct {
}
// FYI: This is STARTUPINFOW, not STARTUPINFOA
-STARTUPINFO :: struct #packed {
+STARTUPINFO :: struct {
cb: DWORD,
lpReserved: LPWSTR,
lpDesktop: LPWSTR,
@@ -781,17 +1669,17 @@ SYSTEM_INFO :: struct {
// https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
OSVERSIONINFOEXW :: struct {
- dwOSVersionInfoSize: ULONG,
- dwMajorVersion: ULONG,
- dwMinorVersion: ULONG,
- dwBuildNumber: ULONG,
- dwPlatformId: ULONG,
- szCSDVersion: [128]WCHAR,
- wServicePackMajor: USHORT,
- wServicePackMinor: USHORT,
- wSuiteMask: USHORT,
- wProductType: UCHAR,
- wReserved: UCHAR,
+ dwOSVersionInfoSize: ULONG,
+ dwMajorVersion: ULONG,
+ dwMinorVersion: ULONG,
+ dwBuildNumber: ULONG,
+ dwPlatformId: ULONG,
+ szCSDVersion: [128]WCHAR,
+ wServicePackMajor: USHORT,
+ wServicePackMinor: USHORT,
+ wSuiteMask: USHORT,
+ wProductType: UCHAR,
+ wReserved: UCHAR,
}
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-quota_limits
@@ -834,24 +1722,24 @@ PROFILEINFOW :: struct {
lpDefaultPath: LPWSTR,
lpServerName: LPWSTR,
lpPolicyPath: LPWSTR,
- hProfile: HANDLE,
+ hProfile: HANDLE,
}
// Used in LookupAccountNameW
SID_NAME_USE :: distinct DWORD
SID_TYPE :: enum SID_NAME_USE {
- User = 1,
- Group,
- Domain,
- Alias,
- WellKnownGroup,
- DeletedAccount,
- Invalid,
- Unknown,
- Computer,
- Label,
- LogonSession,
+ User = 1,
+ Group,
+ Domain,
+ Alias,
+ WellKnownGroup,
+ DeletedAccount,
+ Invalid,
+ Unknown,
+ Computer,
+ Label,
+ LogonSession,
}
SECURITY_MAX_SID_SIZE :: 68
@@ -866,7 +1754,7 @@ SID :: struct #packed {
#assert(size_of(SID) == SECURITY_MAX_SID_SIZE)
SID_IDENTIFIER_AUTHORITY :: struct #packed {
- Value: [6]u8,
+ Value: [6]u8,
}
// For NetAPI32
@@ -898,11 +1786,11 @@ USER_INFO_FLAG :: enum DWORD {
Passwd_Cant_Change = 6, // 1 << 6: 0x0040,
Encrypted_Text_Password_Allowed = 7, // 1 << 7: 0x0080,
- Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
- Normal_Account = 9, // 1 << 9: 0x0200,
- InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
- Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
- Server_Trust_Account = 13, // 1 << 13: 0x2000,
+ Temp_Duplicate_Account = 8, // 1 << 8: 0x0100,
+ Normal_Account = 9, // 1 << 9: 0x0200,
+ InterDomain_Trust_Account = 11, // 1 << 11: 0x0800,
+ Workstation_Trust_Account = 12, // 1 << 12: 0x1000,
+ Server_Trust_Account = 13, // 1 << 13: 0x2000,
}
USER_INFO_FLAGS :: distinct bit_set[USER_INFO_FLAG]
@@ -1249,4 +2137,4 @@ SYSTEMTIME :: struct {
minute: WORD,
second: WORD,
milliseconds: WORD,
-}
\ No newline at end of file
+}
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
new file mode 100644
index 000000000..dd45df42a
--- /dev/null
+++ b/core/sys/windows/user32.odin
@@ -0,0 +1,258 @@
+// +build windows
+package sys_windows
+
+foreign import user32 "system:User32.lib"
+
+@(default_calling_convention="stdcall")
+foreign user32 {
+ GetClassInfoA :: proc(hInstance: HINSTANCE, lpClassNAme: LPCSTR, lpWndClass: ^WNDCLASSA) -> BOOL ---
+ GetClassInfoW :: proc(hInstance: HINSTANCE, lpClassNAme: LPCWSTR, lpWndClass: ^WNDCLASSW) -> BOOL ---
+ GetClassInfoExA :: proc(hInsatnce: HINSTANCE, lpszClass: LPCSTR, lpwcx: ^WNDCLASSEXA) -> BOOL ---
+ GetClassInfoExW :: proc(hInsatnce: HINSTANCE, lpszClass: LPCWSTR, lpwcx: ^WNDCLASSEXW) -> BOOL ---
+
+ GetClassLongA :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+ GetClassLongW :: proc(hWnd: HWND, nIndex: c_int) -> DWORD ---
+ SetClassLongA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
+ SetClassLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> DWORD ---
+
+ GetWindowLongA :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
+ GetWindowLongW :: proc(hWnd: HWND, nIndex: c_int) -> LONG ---
+ SetWindowLongA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
+ SetWindowLongW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG) -> LONG ---
+
+ GetClassNameA :: proc(hWnd: HWND, lpClassName: LPSTR, nMaxCount: c_int) -> c_int ---
+ GetClassNameW :: proc(hWnd: HWND, lpClassName: LPWSTR, nMaxCount: c_int) -> c_int ---
+
+ RegisterClassA :: proc(lpWndClass: ^WNDCLASSA) -> ATOM ---
+ RegisterClassW :: proc(lpWndClass: ^WNDCLASSW) -> ATOM ---
+ RegisterClassExA :: proc(^WNDCLASSEXA) -> ATOM ---
+ RegisterClassExW :: proc(^WNDCLASSEXW) -> ATOM ---
+
+ CreateWindowExA :: proc(
+ dwExStyle: DWORD,
+ lpClassName: LPCSTR,
+ lpWindowName: LPCSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+ ) -> HWND ---
+ CreateWindowExW :: proc(
+ dwExStyle: DWORD,
+ lpClassName: LPCWSTR,
+ lpWindowName: LPCWSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+ ) -> HWND ---
+
+ DestroyWindow :: proc(hWnd: HWND) -> BOOL ---
+
+ ShowWindow :: proc(hWnd: HWND, nCmdShow: c_int) -> BOOL ---
+ BringWindowToTop :: proc(hWnd: HWND) -> BOOL ---
+ GetTopWindow :: proc(hWnd: HWND) -> HWND ---
+ SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
+ GetForegroundWindow :: proc() -> HWND ---
+ SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
+ GetActiveWindow :: proc() -> HWND ---
+
+ GetMessageA :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
+ GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
+
+ TranslateMessage :: proc(lpMsg: ^MSG) -> BOOL ---
+ DispatchMessageA :: proc(lpMsg: ^MSG) -> LRESULT ---
+ DispatchMessageW :: proc(lpMsg: ^MSG) -> LRESULT ---
+
+ 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 ---
+
+ PostMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ PostMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ SendMessageA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ SendMessageW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ PostThreadMessageA :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+ PostThreadMessageW :: proc(idThread: DWORD, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> BOOL ---
+
+ PostQuitMessage :: proc(nExitCode: c_int) ---
+
+ GetQueueStatus :: proc(flags: UINT) -> DWORD ---
+
+ DefWindowProcA :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+ DefWindowProcW :: proc(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ FindWindowA :: proc(lpClassName: LPCSTR, lpWindowName: LPCSTR) -> HWND ---
+ FindWindowW :: proc(lpClassName: LPCWSTR, lpWindowName: LPCWSTR) -> HWND ---
+ FindWindowExA :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCSTR, lpszWindow: LPCSTR) -> HWND ---
+ FindWindowExW :: proc(hWndParent: HWND, hWndChildAfter: HWND, lpszClass: LPCWSTR, lpszWindow: LPCWSTR) -> HWND ---
+
+ LoadIconA :: proc(hInstance: HINSTANCE, lpIconName: LPCSTR) -> HICON ---
+ LoadIconW :: proc(hInstance: HINSTANCE, lpIconName: LPCWSTR) -> HICON ---
+ LoadCursorA :: proc(hInstance: HINSTANCE, lpCursorName: LPCSTR) -> HCURSOR ---
+ LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
+
+ GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+ GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
+ ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
+ SetWindowPos :: proc(
+ hWnd: HWND,
+ hWndInsertAfter: HWND,
+ X: c_int,
+ Y: c_int,
+ cx: c_int,
+ cy: c_int,
+ uFlags: UINT,
+ ) -> BOOL ---
+ GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
+ AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
+ AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
+
+ GetWindowDC :: proc(hWnd: HWND) -> HDC ---
+ GetDC :: proc(hWnd: HWND) -> HDC ---
+ ReleaseDC :: proc(hWnd: HWND, hDC: HDC) -> c_int ---
+
+ 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 ---
+
+ BeginPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> HDC ---
+ EndPaint :: proc(hWnd: HWND, lpPaint: ^PAINTSTRUCT) -> BOOL ---
+
+ GetCapture :: proc() -> HWND ---
+ SetCapture :: proc(hWnd: HWND) -> HWND ---
+ ReleaseCapture :: proc() -> BOOL ---
+ TrackMouseEvent :: proc(lpEventTrack: LPTRACKMOUSEEVENT) -> BOOL ---
+
+ GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
+ GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
+
+ MapVirtualKeyA :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
+ MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
+
+ SetWindowsHookExA :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
+ SetWindowsHookExW :: proc(idHook: c_int, lpfn: HOOKPROC, hmod: HINSTANCE, dwThreadId: DWORD) -> HHOOK ---
+ UnhookWindowsHookEx :: proc(hhk: HHOOK) -> BOOL ---
+ CallNextHookEx :: proc(hhk: HHOOK, nCode: c_int, wParam: WPARAM, lParam: LPARAM) -> LRESULT ---
+
+ SetTimer :: proc(hWnd: HWND, nIDEvent: UINT_PTR, uElapse: UINT, lpTimerFunc: TIMERPROC) -> UINT_PTR ---
+ KillTimer :: proc(hWnd: HWND, uIDEvent: UINT_PTR) -> BOOL ---
+
+ MessageBoxA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT) -> c_int ---
+ MessageBoxW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT) -> c_int ---
+ MessageBoxExA :: proc(hWnd: HWND, lpText: LPCSTR, lpCaption: LPCSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
+ MessageBoxExW :: proc(hWnd: HWND, lpText: LPCWSTR, lpCaption: LPCWSTR, uType: UINT, wLanguageId: WORD) -> c_int ---
+}
+
+CreateWindowA :: #force_inline proc "stdcall" (
+ lpClassName: LPCSTR,
+ lpWindowName: LPCSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+) -> HWND {
+ return CreateWindowExA(
+ 0,
+ lpClassName,
+ lpWindowName,
+ dwStyle,
+ X,
+ Y,
+ nWidth,
+ nHeight,
+ hWndParent,
+ hMenu,
+ hInstance,
+ lpParam,
+ )
+}
+
+CreateWindowW :: #force_inline proc "stdcall" (
+ lpClassName: LPCTSTR,
+ lpWindowName: LPCTSTR,
+ dwStyle: DWORD,
+ X: c_int,
+ Y: c_int,
+ nWidth: c_int,
+ nHeight: c_int,
+ hWndParent: HWND,
+ hMenu: HMENU,
+ hInstance: HINSTANCE,
+ lpParam: LPVOID,
+) -> HWND {
+ return CreateWindowExW(
+ 0,
+ lpClassName,
+ lpWindowName,
+ dwStyle,
+ X,
+ Y,
+ nWidth,
+ nHeight,
+ hWndParent,
+ hMenu,
+ hInstance,
+ lpParam,
+ )
+}
+
+when ODIN_ARCH == .amd64 {
+ @(default_calling_convention="stdcall")
+ foreign user32 {
+ GetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
+ GetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> ULONG_PTR ---
+ SetClassLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+ SetClassLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> ULONG_PTR ---
+
+ GetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
+ GetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int) -> LONG_PTR ---
+ SetWindowLongPtrA :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
+ SetWindowLongPtrW :: proc(hWnd: HWND, nIndex: c_int, dwNewLong: LONG_PTR) -> LONG_PTR ---
+ }
+} else when ODIN_ARCH == .i386 {
+ GetClassLongPtrA :: GetClassLongA
+ GetClassLongPtrW :: GetClassLongW
+ SetClassLongPtrA :: SetClassLongA
+ SetClassLongPtrW :: SetClassLongW
+
+ GetWindowLongPtrA :: GetWindowLongA
+ GetWindowLongPtrW :: GetWindowLongW
+ SetWindowLongPtrA :: GetWindowLongA
+ SetWindowLongPtrW :: GetWindowLongW
+}
+
+GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
+ return c_int(wParam) & 0xFFF0
+}
+
+GET_WHEEL_DELTA_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
+ return cast(c_short)HIWORD(cast(DWORD)wParam)
+}
+
+GET_KEYSTATE_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
+ return LOWORD(cast(DWORD)wParam)
+}
+
+GET_NCHITTEST_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_short {
+ return cast(c_short)LOWORD(cast(DWORD)wParam)
+}
+
+GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD {
+ return HIWORD(cast(DWORD)wParam)
+}
diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin
index efb37dbc0..f448f6bc5 100644
--- a/core/sys/windows/util.odin
+++ b/core/sys/windows/util.odin
@@ -3,6 +3,9 @@ package sys_windows
import "core:strings"
import "core:sys/win32"
+import "core:intrinsics"
+
+L :: intrinsics.constant_utf16_cstring
LOWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
return WORD(x & 0xffff)
@@ -12,6 +15,14 @@ HIWORD :: #force_inline proc "contextless" (x: DWORD) -> WORD {
return WORD(x >> 16)
}
+GET_X_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int {
+ return cast(c_int)cast(c_short)LOWORD(cast(DWORD)lp)
+}
+
+GET_Y_LPARAM :: #force_inline proc "contextless" (lp: LPARAM) -> c_int {
+ return cast(c_int)cast(c_short)HIWORD(cast(DWORD)lp)
+}
+
utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 {
if len(s) < 1 {
return nil
@@ -45,7 +56,9 @@ utf8_to_wstring :: proc(s: string, allocator := context.temp_allocator) -> wstri
return nil
}
-wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> string {
+wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator) -> (res: string) {
+ context.allocator = allocator
+
if N <= 0 {
return ""
}
@@ -60,7 +73,7 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
// also null terminated.
// If N != -1 it assumes the wide string is not null terminated and the resulting string
// will not be null terminated, we therefore have to force it to be null terminated manually.
- text := make([]byte, n+1 if N != -1 else n, allocator)
+ text := make([]byte, n+1 if N != -1 else n)
n1 := WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, s, i32(N), raw_data(text), n, nil, nil)
if n1 == 0 {
@@ -74,7 +87,6 @@ wstring_to_utf8 :: proc(s: wstring, N: int, allocator := context.temp_allocator)
break
}
}
-
return string(text[:n])
}
@@ -455,4 +467,4 @@ run_as_user :: proc(username, password, application, commandline: string, pi: ^P
} else {
return false
}
-}
\ No newline at end of file
+}
diff --git a/core/sys/windows/window_messages.odin b/core/sys/windows/window_messages.odin
new file mode 100644
index 000000000..9c3a30088
--- /dev/null
+++ b/core/sys/windows/window_messages.odin
@@ -0,0 +1,1048 @@
+// +build windows
+package sys_windows
+
+WM_NULL :: 0x0000
+WM_CREATE :: 0x0001
+WM_DESTROY :: 0x0002
+WM_MOVE :: 0x0003
+WM_SIZE :: 0x0005
+WM_ACTIVATE :: 0x0006
+WM_SETFOCUS :: 0x0007
+WM_KILLFOCUS :: 0x0008
+WM_ENABLE :: 0x000a
+WM_SETREDRAW :: 0x000b
+WM_SETTEXT :: 0x000c
+WM_GETTEXT :: 0x000d
+WM_GETTEXTLENGTH :: 0x000e
+WM_PAINT :: 0x000f
+WM_CLOSE :: 0x0010
+WM_QUERYENDSESSION :: 0x0011
+WM_QUIT :: 0x0012
+WM_QUERYOPEN :: 0x0013
+WM_ERASEBKGND :: 0x0014
+WM_SYSCOLORCHANGE :: 0x0015
+WM_ENDSESSION :: 0x0016
+WM_SHOWWINDOW :: 0x0018
+WM_CTLCOLOR :: 0x0019
+WM_WININICHANGE :: 0x001a
+WM_DEVMODECHANGE :: 0x001b
+WM_ACTIVATEAPP :: 0x001c
+WM_FONTCHANGE :: 0x001d
+WM_TIMECHANGE :: 0x001e
+WM_CANCELMODE :: 0x001f
+WM_SETCURSOR :: 0x0020
+WM_MOUSEACTIVATE :: 0x0021
+WM_CHILDACTIVATE :: 0x0022
+WM_QUEUESYNC :: 0x0023
+WM_GETMINMAXINFO :: 0x0024
+WM_PAINTICON :: 0x0026
+WM_ICONERASEBKGND :: 0x0027
+WM_NEXTDLGCTL :: 0x0028
+WM_SPOOLERSTATUS :: 0x002a
+WM_DRAWITEM :: 0x002b
+WM_MEASUREITEM :: 0x002c
+WM_DELETEITEM :: 0x002d
+WM_VKEYTOITEM :: 0x002e
+WM_CHARTOITEM :: 0x002f
+WM_SETFONT :: 0x0030
+WM_GETFONT :: 0x0031
+WM_SETHOTKEY :: 0x0032
+WM_GETHOTKEY :: 0x0033
+WM_QUERYDRAGICON :: 0x0037
+WM_COMPAREITEM :: 0x0039
+WM_GETOBJECT :: 0x003d
+WM_COMPACTING :: 0x0041
+WM_COMMNOTIFY :: 0x0044
+WM_WINDOWPOSCHANGING :: 0x0046
+WM_WINDOWPOSCHANGED :: 0x0047
+WM_POWER :: 0x0048
+WM_COPYGLOBALDATA :: 0x0049
+WM_COPYDATA :: 0x004a
+WM_CANCELJOURNAL :: 0x004b
+WM_NOTIFY :: 0x004e
+WM_INPUTLANGCHANGEREQUEST :: 0x0050
+WM_INPUTLANGCHANGE :: 0x0051
+WM_TCARD :: 0x0052
+WM_HELP :: 0x0053
+WM_USERCHANGED :: 0x0054
+WM_NOTIFYFORMAT :: 0x0055
+WM_CONTEXTMENU :: 0x007b
+WM_STYLECHANGING :: 0x007c
+WM_STYLECHANGED :: 0x007d
+WM_DISPLAYCHANGE :: 0x007e
+WM_GETICON :: 0x007f
+WM_SETICON :: 0x0080
+WM_NCCREATE :: 0x0081
+WM_NCDESTROY :: 0x0082
+WM_NCCALCSIZE :: 0x0083
+WM_NCHITTEST :: 0x0084
+WM_NCPAINT :: 0x0085
+WM_NCACTIVATE :: 0x0086
+WM_GETDLGCODE :: 0x0087
+WM_SYNCPAINT :: 0x0088
+WM_NCMOUSEMOVE :: 0x00a0
+WM_NCLBUTTONDOWN :: 0x00a1
+WM_NCLBUTTONUP :: 0x00a2
+WM_NCLBUTTONDBLCLK :: 0x00a3
+WM_NCRBUTTONDOWN :: 0x00a4
+WM_NCRBUTTONUP :: 0x00a5
+WM_NCRBUTTONDBLCLK :: 0x00a6
+WM_NCMBUTTONDOWN :: 0x00a7
+WM_NCMBUTTONUP :: 0x00a8
+WM_NCMBUTTONDBLCLK :: 0x00a9
+WM_NCXBUTTONDOWN :: 0x00ab
+WM_NCXBUTTONUP :: 0x00ac
+WM_NCXBUTTONDBLCLK :: 0x00ad
+EM_GETSEL :: 0x00b0
+EM_SETSEL :: 0x00b1
+EM_GETRECT :: 0x00b2
+EM_SETRECT :: 0x00b3
+EM_SETRECTNP :: 0x00b4
+EM_SCROLL :: 0x00b5
+EM_LINESCROLL :: 0x00b6
+EM_SCROLLCARET :: 0x00b7
+EM_GETMODIFY :: 0x00b8
+EM_SETMODIFY :: 0x00b9
+EM_GETLINECOUNT :: 0x00ba
+EM_LINEINDEX :: 0x00bb
+EM_SETHANDLE :: 0x00bc
+EM_GETHANDLE :: 0x00bd
+EM_GETTHUMB :: 0x00be
+EM_LINELENGTH :: 0x00c1
+EM_REPLACESEL :: 0x00c2
+EM_SETFONT :: 0x00c3
+EM_GETLINE :: 0x00c4
+EM_LIMITTEXT :: 0x00c5
+EM_SETLIMITTEXT :: 0x00c5
+EM_CANUNDO :: 0x00c6
+EM_UNDO :: 0x00c7
+EM_FMTLINES :: 0x00c8
+EM_LINEFROMCHAR :: 0x00c9
+EM_SETWORDBREAK :: 0x00ca
+EM_SETTABSTOPS :: 0x00cb
+EM_SETPASSWORDCHAR :: 0x00cc
+EM_EMPTYUNDOBUFFER :: 0x00cd
+EM_GETFIRSTVISIBLELINE :: 0x00ce
+EM_SETREADONLY :: 0x00cf
+EM_SETWORDBREAKPROC :: 0x00d0
+EM_GETWORDBREAKPROC :: 0x00d1
+EM_GETPASSWORDCHAR :: 0x00d2
+EM_SETMARGINS :: 0x00d3
+EM_GETMARGINS :: 0x00d4
+EM_GETLIMITTEXT :: 0x00d5
+EM_POSFROMCHAR :: 0x00d6
+EM_CHARFROMPOS :: 0x00d7
+EM_SETIMESTATUS :: 0x00d8
+EM_GETIMESTATUS :: 0x00d9
+SBM_SETPOS :: 0x00e0
+SBM_GETPOS :: 0x00e1
+SBM_SETRANGE :: 0x00e2
+SBM_GETRANGE :: 0x00e3
+SBM_ENABLE_ARROWS :: 0x00e4
+SBM_SETRANGEREDRAW :: 0x00e6
+SBM_SETSCROLLINFO :: 0x00e9
+SBM_GETSCROLLINFO :: 0x00ea
+SBM_GETSCROLLBARINFO :: 0x00eb
+BM_GETCHECK :: 0x00f0
+BM_SETCHECK :: 0x00f1
+BM_GETSTATE :: 0x00f2
+BM_SETSTATE :: 0x00f3
+BM_SETSTYLE :: 0x00f4
+BM_CLICK :: 0x00f5
+BM_GETIMAGE :: 0x00f6
+BM_SETIMAGE :: 0x00f7
+BM_SETDONTCLICK :: 0x00f8
+WM_INPUT :: 0x00ff
+WM_KEYDOWN :: 0x0100
+WM_KEYFIRST :: 0x0100
+WM_KEYUP :: 0x0101
+WM_CHAR :: 0x0102
+WM_DEADCHAR :: 0x0103
+WM_SYSKEYDOWN :: 0x0104
+WM_SYSKEYUP :: 0x0105
+WM_SYSCHAR :: 0x0106
+WM_SYSDEADCHAR :: 0x0107
+WM_UNICHAR :: 0x0109
+WM_KEYLAST :: 0x0109
+WM_WNT_CONVERTREQUESTEX :: 0x0109
+WM_CONVERTREQUEST :: 0x010a
+WM_CONVERTRESULT :: 0x010b
+WM_INTERIM :: 0x010c
+WM_IME_STARTCOMPOSITION :: 0x010d
+WM_IME_ENDCOMPOSITION :: 0x010e
+WM_IME_COMPOSITION :: 0x010f
+WM_IME_KEYLAST :: 0x010f
+WM_INITDIALOG :: 0x0110
+WM_COMMAND :: 0x0111
+WM_SYSCOMMAND :: 0x0112
+WM_TIMER :: 0x0113
+WM_HSCROLL :: 0x0114
+WM_VSCROLL :: 0x0115
+WM_INITMENU :: 0x0116
+WM_INITMENUPOPUP :: 0x0117
+WM_SYSTIMER :: 0x0118
+WM_MENUSELECT :: 0x011f
+WM_MENUCHAR :: 0x0120
+WM_ENTERIDLE :: 0x0121
+WM_MENURBUTTONUP :: 0x0122
+WM_MENUDRAG :: 0x0123
+WM_MENUGETOBJECT :: 0x0124
+WM_UNINITMENUPOPUP :: 0x0125
+WM_MENUCOMMAND :: 0x0126
+WM_CHANGEUISTATE :: 0x0127
+WM_UPDATEUISTATE :: 0x0128
+WM_QUERYUISTATE :: 0x0129
+WM_LBTRACKPOINT :: 0x0131
+WM_CTLCOLORMSGBOX :: 0x0132
+WM_CTLCOLOREDIT :: 0x0133
+WM_CTLCOLORLISTBOX :: 0x0134
+WM_CTLCOLORBTN :: 0x0135
+WM_CTLCOLORDLG :: 0x0136
+WM_CTLCOLORSCROLLBAR :: 0x0137
+WM_CTLCOLORSTATIC :: 0x0138
+CB_GETEDITSEL :: 0x0140
+CB_LIMITTEXT :: 0x0141
+CB_SETEDITSEL :: 0x0142
+CB_ADDSTRING :: 0x0143
+CB_DELETESTRING :: 0x0144
+CB_DIR :: 0x0145
+CB_GETCOUNT :: 0x0146
+CB_GETCURSEL :: 0x0147
+CB_GETLBTEXT :: 0x0148
+CB_GETLBTEXTLEN :: 0x0149
+CB_INSERTSTRING :: 0x014a
+CB_RESETCONTENT :: 0x014b
+CB_FINDSTRING :: 0x014c
+CB_SELECTSTRING :: 0x014d
+CB_SETCURSEL :: 0x014e
+CB_SHOWDROPDOWN :: 0x014f
+CB_GETITEMDATA :: 0x0150
+CB_SETITEMDATA :: 0x0151
+CB_GETDROPPEDCONTROLRECT :: 0x0152
+CB_SETITEMHEIGHT :: 0x0153
+CB_GETITEMHEIGHT :: 0x0154
+CB_SETEXTENDEDUI :: 0x0155
+CB_GETEXTENDEDUI :: 0x0156
+CB_GETDROPPEDSTATE :: 0x0157
+CB_FINDSTRINGEXACT :: 0x0158
+CB_SETLOCALE :: 0x0159
+CB_GETLOCALE :: 0x015a
+CB_GETTOPINDEX :: 0x015b
+CB_SETTOPINDEX :: 0x015c
+CB_GETHORIZONTALEXTENT :: 0x015d
+CB_SETHORIZONTALEXTENT :: 0x015e
+CB_GETDROPPEDWIDTH :: 0x015f
+CB_SETDROPPEDWIDTH :: 0x0160
+CB_INITSTORAGE :: 0x0161
+CB_MULTIPLEADDSTRING :: 0x0163
+CB_GETCOMBOBOXINFO :: 0x0164
+CB_MSGMAX :: 0x0165
+WM_MOUSEFIRST :: 0x0200
+WM_MOUSEMOVE :: 0x0200
+WM_LBUTTONDOWN :: 0x0201
+WM_LBUTTONUP :: 0x0202
+WM_LBUTTONDBLCLK :: 0x0203
+WM_RBUTTONDOWN :: 0x0204
+WM_RBUTTONUP :: 0x0205
+WM_RBUTTONDBLCLK :: 0x0206
+WM_MBUTTONDOWN :: 0x0207
+WM_MBUTTONUP :: 0x0208
+WM_MBUTTONDBLCLK :: 0x0209
+WM_MOUSELAST :: 0x0209
+WM_MOUSEWHEEL :: 0x020a
+WM_XBUTTONDOWN :: 0x020b
+WM_XBUTTONUP :: 0x020c
+WM_XBUTTONDBLCLK :: 0x020d
+WM_MOUSEHWHEEL :: 0x020e
+WM_PARENTNOTIFY :: 0x0210
+WM_ENTERMENULOOP :: 0x0211
+WM_EXITMENULOOP :: 0x0212
+WM_NEXTMENU :: 0x0213
+WM_SIZING :: 0x0214
+WM_CAPTURECHANGED :: 0x0215
+WM_MOVING :: 0x0216
+WM_POWERBROADCAST :: 0x0218
+WM_DEVICECHANGE :: 0x0219
+WM_MDICREATE :: 0x0220
+WM_MDIDESTROY :: 0x0221
+WM_MDIACTIVATE :: 0x0222
+WM_MDIRESTORE :: 0x0223
+WM_MDINEXT :: 0x0224
+WM_MDIMAXIMIZE :: 0x0225
+WM_MDITILE :: 0x0226
+WM_MDICASCADE :: 0x0227
+WM_MDIICONARRANGE :: 0x0228
+WM_MDIGETACTIVE :: 0x0229
+WM_MDISETMENU :: 0x0230
+WM_ENTERSIZEMOVE :: 0x0231
+WM_EXITSIZEMOVE :: 0x0232
+WM_DROPFILES :: 0x0233
+WM_MDIREFRESHMENU :: 0x0234
+WM_IME_REPORT :: 0x0280
+WM_IME_SETCONTEXT :: 0x0281
+WM_IME_NOTIFY :: 0x0282
+WM_IME_CONTROL :: 0x0283
+WM_IME_COMPOSITIONFULL :: 0x0284
+WM_IME_SELECT :: 0x0285
+WM_IME_CHAR :: 0x0286
+WM_IME_REQUEST :: 0x0288
+WM_IMEKEYDOWN :: 0x0290
+WM_IME_KEYDOWN :: 0x0290
+WM_IMEKEYUP :: 0x0291
+WM_IME_KEYUP :: 0x0291
+WM_NCMOUSEHOVER :: 0x02a0
+WM_MOUSEHOVER :: 0x02a1
+WM_NCMOUSELEAVE :: 0x02a2
+WM_MOUSELEAVE :: 0x02a3
+WM_CUT :: 0x0300
+WM_COPY :: 0x0301
+WM_PASTE :: 0x0302
+WM_CLEAR :: 0x0303
+WM_UNDO :: 0x0304
+WM_RENDERFORMAT :: 0x0305
+WM_RENDERALLFORMATS :: 0x0306
+WM_DESTROYCLIPBOARD :: 0x0307
+WM_DRAWCLIPBOARD :: 0x0308
+WM_PAINTCLIPBOARD :: 0x0309
+WM_VSCROLLCLIPBOARD :: 0x030a
+WM_SIZECLIPBOARD :: 0x030b
+WM_ASKCBFORMATNAME :: 0x030c
+WM_CHANGECBCHAIN :: 0x030d
+WM_HSCROLLCLIPBOARD :: 0x030e
+WM_QUERYNEWPALETTE :: 0x030f
+WM_PALETTEISCHANGING :: 0x0310
+WM_PALETTECHANGED :: 0x0311
+WM_HOTKEY :: 0x0312
+WM_PRINT :: 0x0317
+WM_PRINTCLIENT :: 0x0318
+WM_APPCOMMAND :: 0x0319
+WM_HANDHELDFIRST :: 0x0358
+WM_HANDHELDLAST :: 0x035f
+WM_AFXFIRST :: 0x0360
+WM_AFXLAST :: 0x037f
+WM_PENWINFIRST :: 0x0380
+WM_RCRESULT :: 0x0381
+WM_HOOKRCRESULT :: 0x0382
+WM_GLOBALRCCHANGE :: 0x0383
+WM_PENMISCINFO :: 0x0383
+WM_SKB :: 0x0384
+WM_HEDITCTL :: 0x0385
+WM_PENCTL :: 0x0385
+WM_PENMISC :: 0x0386
+WM_CTLINIT :: 0x0387
+WM_PENEVENT :: 0x0388
+WM_PENWINLAST :: 0x038f
+DDM_SETFMT :: 0x0400
+DM_GETDEFID :: 0x0400
+NIN_SELECT :: 0x0400
+TBM_GETPOS :: 0x0400
+WM_PSD_PAGESETUPDLG :: 0x0400
+WM_USER :: 0x0400
+CBEM_INSERTITEMA :: 0x0401
+DDM_DRAW :: 0x0401
+DM_SETDEFID :: 0x0401
+HKM_SETHOTKEY :: 0x0401
+PBM_SETRANGE :: 0x0401
+RB_INSERTBANDA :: 0x0401
+SB_SETTEXTA :: 0x0401
+TB_ENABLEBUTTON :: 0x0401
+TBM_GETRANGEMIN :: 0x0401
+TTM_ACTIVATE :: 0x0401
+WM_CHOOSEFONT_GETLOGFONT :: 0x0401
+WM_PSD_FULLPAGERECT :: 0x0401
+CBEM_SETIMAGELIST :: 0x0402
+DDM_CLOSE :: 0x0402
+DM_REPOSITION :: 0x0402
+HKM_GETHOTKEY :: 0x0402
+PBM_SETPOS :: 0x0402
+RB_DELETEBAND :: 0x0402
+SB_GETTEXTA :: 0x0402
+TB_CHECKBUTTON :: 0x0402
+TBM_GETRANGEMAX :: 0x0402
+WM_PSD_MINMARGINRECT :: 0x0402
+CBEM_GETIMAGELIST :: 0x0403
+DDM_BEGIN :: 0x0403
+HKM_SETRULES :: 0x0403
+PBM_DELTAPOS :: 0x0403
+RB_GETBARINFO :: 0x0403
+SB_GETTEXTLENGTHA :: 0x0403
+TBM_GETTIC :: 0x0403
+TB_PRESSBUTTON :: 0x0403
+TTM_SETDELAYTIME :: 0x0403
+WM_PSD_MARGINRECT :: 0x0403
+CBEM_GETITEMA :: 0x0404
+DDM_END :: 0x0404
+PBM_SETSTEP :: 0x0404
+RB_SETBARINFO :: 0x0404
+SB_SETPARTS :: 0x0404
+TB_HIDEBUTTON :: 0x0404
+TBM_SETTIC :: 0x0404
+TTM_ADDTOOLA :: 0x0404
+WM_PSD_GREEKTEXTRECT :: 0x0404
+CBEM_SETITEMA :: 0x0405
+PBM_STEPIT :: 0x0405
+TB_INDETERMINATE :: 0x0405
+TBM_SETPOS :: 0x0405
+TTM_DELTOOLA :: 0x0405
+WM_PSD_ENVSTAMPRECT :: 0x0405
+CBEM_GETCOMBOCONTROL :: 0x0406
+PBM_SETRANGE32 :: 0x0406
+RB_SETBANDINFOA :: 0x0406
+SB_GETPARTS :: 0x0406
+TB_MARKBUTTON :: 0x0406
+TBM_SETRANGE :: 0x0406
+TTM_NEWTOOLRECTA :: 0x0406
+WM_PSD_YAFULLPAGERECT :: 0x0406
+CBEM_GETEDITCONTROL :: 0x0407
+PBM_GETRANGE :: 0x0407
+RB_SETPARENT :: 0x0407
+SB_GETBORDERS :: 0x0407
+TBM_SETRANGEMIN :: 0x0407
+TTM_RELAYEVENT :: 0x0407
+CBEM_SETEXSTYLE :: 0x0408
+PBM_GETPOS :: 0x0408
+RB_HITTEST :: 0x0408
+SB_SETMINHEIGHT :: 0x0408
+TBM_SETRANGEMAX :: 0x0408
+TTM_GETTOOLINFOA :: 0x0408
+CBEM_GETEXSTYLE :: 0x0409
+CBEM_GETEXTENDEDSTYLE :: 0x0409
+PBM_SETBARCOLOR :: 0x0409
+RB_GETRECT :: 0x0409
+SB_SIMPLE :: 0x0409
+TB_ISBUTTONENABLED :: 0x0409
+TBM_CLEARTICS :: 0x0409
+TTM_SETTOOLINFOA :: 0x0409
+CBEM_HASEDITCHANGED :: 0x040a
+RB_INSERTBANDW :: 0x040a
+SB_GETRECT :: 0x040a
+TB_ISBUTTONCHECKED :: 0x040a
+TBM_SETSEL :: 0x040a
+TTM_HITTESTA :: 0x040a
+WIZ_QUERYNUMPAGES :: 0x040a
+CBEM_INSERTITEMW :: 0x040b
+RB_SETBANDINFOW :: 0x040b
+SB_SETTEXTW :: 0x040b
+TB_ISBUTTONPRESSED :: 0x040b
+TBM_SETSELSTART :: 0x040b
+TTM_GETTEXTA :: 0x040b
+WIZ_NEXT :: 0x040b
+CBEM_SETITEMW :: 0x040c
+RB_GETBANDCOUNT :: 0x040c
+SB_GETTEXTLENGTHW :: 0x040c
+TB_ISBUTTONHIDDEN :: 0x040c
+TBM_SETSELEND :: 0x040c
+TTM_UPDATETIPTEXTA :: 0x040c
+WIZ_PREV :: 0x040c
+CBEM_GETITEMW :: 0x040d
+RB_GETROWCOUNT :: 0x040d
+SB_GETTEXTW :: 0x040d
+TB_ISBUTTONINDETERMINATE :: 0x040d
+TTM_GETTOOLCOUNT :: 0x040d
+CBEM_SETEXTENDEDSTYLE :: 0x040e
+RB_GETROWHEIGHT :: 0x040e
+SB_ISSIMPLE :: 0x040e
+TB_ISBUTTONHIGHLIGHTED :: 0x040e
+TBM_GETPTICS :: 0x040e
+TTM_ENUMTOOLSA :: 0x040e
+SB_SETICON :: 0x040f
+TBM_GETTICPOS :: 0x040f
+TTM_GETCURRENTTOOLA :: 0x040f
+RB_IDTOINDEX :: 0x0410
+SB_SETTIPTEXTA :: 0x0410
+TBM_GETNUMTICS :: 0x0410
+TTM_WINDOWFROMPOINT :: 0x0410
+RB_GETTOOLTIPS :: 0x0411
+SB_SETTIPTEXTW :: 0x0411
+TBM_GETSELSTART :: 0x0411
+TB_SETSTATE :: 0x0411
+TTM_TRACKACTIVATE :: 0x0411
+RB_SETTOOLTIPS :: 0x0412
+SB_GETTIPTEXTA :: 0x0412
+TB_GETSTATE :: 0x0412
+TBM_GETSELEND :: 0x0412
+TTM_TRACKPOSITION :: 0x0412
+RB_SETBKCOLOR :: 0x0413
+SB_GETTIPTEXTW :: 0x0413
+TB_ADDBITMAP :: 0x0413
+TBM_CLEARSEL :: 0x0413
+TTM_SETTIPBKCOLOR :: 0x0413
+RB_GETBKCOLOR :: 0x0414
+SB_GETICON :: 0x0414
+TB_ADDBUTTONSA :: 0x0414
+TBM_SETTICFREQ :: 0x0414
+TTM_SETTIPTEXTCOLOR :: 0x0414
+RB_SETTEXTCOLOR :: 0x0415
+TB_INSERTBUTTONA :: 0x0415
+TBM_SETPAGESIZE :: 0x0415
+TTM_GETDELAYTIME :: 0x0415
+RB_GETTEXTCOLOR :: 0x0416
+TB_DELETEBUTTON :: 0x0416
+TBM_GETPAGESIZE :: 0x0416
+TTM_GETTIPBKCOLOR :: 0x0416
+RB_SIZETORECT :: 0x0417
+TB_GETBUTTON :: 0x0417
+TBM_SETLINESIZE :: 0x0417
+TTM_GETTIPTEXTCOLOR :: 0x0417
+RB_BEGINDRAG :: 0x0418
+TB_BUTTONCOUNT :: 0x0418
+TBM_GETLINESIZE :: 0x0418
+TTM_SETMAXTIPWIDTH :: 0x0418
+RB_ENDDRAG :: 0x0419
+TB_COMMANDTOINDEX :: 0x0419
+TBM_GETTHUMBRECT :: 0x0419
+TTM_GETMAXTIPWIDTH :: 0x0419
+RB_DRAGMOVE :: 0x041a
+TBM_GETCHANNELRECT :: 0x041a
+TB_SAVERESTOREA :: 0x041a
+TTM_SETMARGIN :: 0x041a
+RB_GETBARHEIGHT :: 0x041b
+TB_CUSTOMIZE :: 0x041b
+TBM_SETTHUMBLENGTH :: 0x041b
+TTM_GETMARGIN :: 0x041b
+RB_GETBANDINFOW :: 0x041c
+TB_ADDSTRINGA :: 0x041c
+TBM_GETTHUMBLENGTH :: 0x041c
+TTM_POP :: 0x041c
+RB_GETBANDINFOA :: 0x041d
+TB_GETITEMRECT :: 0x041d
+TBM_SETTOOLTIPS :: 0x041d
+TTM_UPDATE :: 0x041d
+RB_MINIMIZEBAND :: 0x041e
+TB_BUTTONSTRUCTSIZE :: 0x041e
+TBM_GETTOOLTIPS :: 0x041e
+TTM_GETBUBBLESIZE :: 0x041e
+RB_MAXIMIZEBAND :: 0x041f
+TBM_SETTIPSIDE :: 0x041f
+TB_SETBUTTONSIZE :: 0x041f
+TTM_ADJUSTRECT :: 0x041f
+TBM_SETBUDDY :: 0x0420
+TB_SETBITMAPSIZE :: 0x0420
+TTM_SETTITLEA :: 0x0420
+MSG_FTS_JUMP_VA :: 0x0421
+TB_AUTOSIZE :: 0x0421
+TBM_GETBUDDY :: 0x0421
+TTM_SETTITLEW :: 0x0421
+RB_GETBANDBORDERS :: 0x0422
+MSG_FTS_JUMP_QWORD :: 0x0423
+RB_SHOWBAND :: 0x0423
+TB_GETTOOLTIPS :: 0x0423
+MSG_REINDEX_REQUEST :: 0x0424
+TB_SETTOOLTIPS :: 0x0424
+MSG_FTS_WHERE_IS_IT :: 0x0425
+RB_SETPALETTE :: 0x0425
+TB_SETPARENT :: 0x0425
+RB_GETPALETTE :: 0x0426
+RB_MOVEBAND :: 0x0427
+TB_SETROWS :: 0x0427
+TB_GETROWS :: 0x0428
+TB_GETBITMAPFLAGS :: 0x0429
+TB_SETCMDID :: 0x042a
+RB_PUSHCHEVRON :: 0x042b
+TB_CHANGEBITMAP :: 0x042b
+TB_GETBITMAP :: 0x042c
+MSG_GET_DEFFONT :: 0x042d
+TB_GETBUTTONTEXTA :: 0x042d
+TB_REPLACEBITMAP :: 0x042e
+TB_SETINDENT :: 0x042f
+TB_SETIMAGELIST :: 0x0430
+TB_GETIMAGELIST :: 0x0431
+TB_LOADIMAGES :: 0x0432
+EM_CANPASTE :: 0x0432
+TTM_ADDTOOLW :: 0x0432
+EM_DISPLAYBAND :: 0x0433
+TB_GETRECT :: 0x0433
+TTM_DELTOOLW :: 0x0433
+EM_EXGETSEL :: 0x0434
+TB_SETHOTIMAGELIST :: 0x0434
+TTM_NEWTOOLRECTW :: 0x0434
+EM_EXLIMITTEXT :: 0x0435
+TB_GETHOTIMAGELIST :: 0x0435
+TTM_GETTOOLINFOW :: 0x0435
+EM_EXLINEFROMCHAR :: 0x0436
+TB_SETDISABLEDIMAGELIST :: 0x0436
+TTM_SETTOOLINFOW :: 0x0436
+EM_EXSETSEL :: 0x0437
+TB_GETDISABLEDIMAGELIST :: 0x0437
+TTM_HITTESTW :: 0x0437
+EM_FINDTEXT :: 0x0438
+TB_SETSTYLE :: 0x0438
+TTM_GETTEXTW :: 0x0438
+EM_FORMATRANGE :: 0x0439
+TB_GETSTYLE :: 0x0439
+TTM_UPDATETIPTEXTW :: 0x0439
+EM_GETCHARFORMAT :: 0x043a
+TB_GETBUTTONSIZE :: 0x043a
+TTM_ENUMTOOLSW :: 0x043a
+EM_GETEVENTMASK :: 0x043b
+TB_SETBUTTONWIDTH :: 0x043b
+TTM_GETCURRENTTOOLW :: 0x043b
+EM_GETOLEINTERFACE :: 0x043c
+TB_SETMAXTEXTROWS :: 0x043c
+EM_GETPARAFORMAT :: 0x043d
+TB_GETTEXTROWS :: 0x043d
+EM_GETSELTEXT :: 0x043e
+TB_GETOBJECT :: 0x043e
+EM_HIDESELECTION :: 0x043f
+TB_GETBUTTONINFOW :: 0x043f
+EM_PASTESPECIAL :: 0x0440
+TB_SETBUTTONINFOW :: 0x0440
+EM_REQUESTRESIZE :: 0x0441
+TB_GETBUTTONINFOA :: 0x0441
+EM_SELECTIONTYPE :: 0x0442
+TB_SETBUTTONINFOA :: 0x0442
+EM_SETBKGNDCOLOR :: 0x0443
+TB_INSERTBUTTONW :: 0x0443
+EM_SETCHARFORMAT :: 0x0444
+TB_ADDBUTTONSW :: 0x0444
+EM_SETEVENTMASK :: 0x0445
+TB_HITTEST :: 0x0445
+EM_SETOLECALLBACK :: 0x0446
+TB_SETDRAWTEXTFLAGS :: 0x0446
+EM_SETPARAFORMAT :: 0x0447
+TB_GETHOTITEM :: 0x0447
+EM_SETTARGETDEVICE :: 0x0448
+TB_SETHOTITEM :: 0x0448
+EM_STREAMIN :: 0x0449
+TB_SETANCHORHIGHLIGHT :: 0x0449
+EM_STREAMOUT :: 0x044a
+TB_GETANCHORHIGHLIGHT :: 0x044a
+EM_GETTEXTRANGE :: 0x044b
+TB_GETBUTTONTEXTW :: 0x044b
+EM_FINDWORDBREAK :: 0x044c
+TB_SAVERESTOREW :: 0x044c
+EM_SETOPTIONS :: 0x044d
+TB_ADDSTRINGW :: 0x044d
+EM_GETOPTIONS :: 0x044e
+TB_MAPACCELERATORA :: 0x044e
+EM_FINDTEXTEX :: 0x044f
+TB_GETINSERTMARK :: 0x044f
+EM_GETWORDBREAKPROCEX :: 0x0450
+TB_SETINSERTMARK :: 0x0450
+EM_SETWORDBREAKPROCEX :: 0x0451
+TB_INSERTMARKHITTEST :: 0x0451
+EM_SETUNDOLIMIT :: 0x0452
+TB_MOVEBUTTON :: 0x0452
+TB_GETMAXSIZE :: 0x0453
+EM_REDO :: 0x0454
+TB_SETEXTENDEDSTYLE :: 0x0454
+EM_CANREDO :: 0x0455
+TB_GETEXTENDEDSTYLE :: 0x0455
+EM_GETUNDONAME :: 0x0456
+TB_GETPADDING :: 0x0456
+EM_GETREDONAME :: 0x0457
+TB_SETPADDING :: 0x0457
+EM_STOPGROUPTYPING :: 0x0458
+TB_SETINSERTMARKCOLOR :: 0x0458
+EM_SETTEXTMODE :: 0x0459
+TB_GETINSERTMARKCOLOR :: 0x0459
+EM_GETTEXTMODE :: 0x045a
+TB_MAPACCELERATORW :: 0x045a
+EM_AUTOURLDETECT :: 0x045b
+TB_GETSTRINGW :: 0x045b
+EM_GETAUTOURLDETECT :: 0x045c
+TB_GETSTRINGA :: 0x045c
+EM_SETPALETTE :: 0x045d
+EM_GETTEXTEX :: 0x045e
+EM_GETTEXTLENGTHEX :: 0x045f
+EM_SHOWSCROLLBAR :: 0x0460
+EM_SETTEXTEX :: 0x0461
+TAPI_REPLY :: 0x0463
+ACM_OPENA :: 0x0464
+BFFM_SETSTATUSTEXTA :: 0x0464
+CDM_FIRST :: 0x0464
+CDM_GETSPEC :: 0x0464
+EM_SETPUNCTUATION :: 0x0464
+IPM_CLEARADDRESS :: 0x0464
+WM_CAP_UNICODE_START :: 0x0464
+ACM_PLAY :: 0x0465
+BFFM_ENABLEOK :: 0x0465
+CDM_GETFILEPATH :: 0x0465
+EM_GETPUNCTUATION :: 0x0465
+IPM_SETADDRESS :: 0x0465
+PSM_SETCURSEL :: 0x0465
+UDM_SETRANGE :: 0x0465
+WM_CHOOSEFONT_SETLOGFONT :: 0x0465
+ACM_STOP :: 0x0466
+BFFM_SETSELECTIONA :: 0x0466
+CDM_GETFOLDERPATH :: 0x0466
+EM_SETWORDWRAPMODE :: 0x0466
+IPM_GETADDRESS :: 0x0466
+PSM_REMOVEPAGE :: 0x0466
+UDM_GETRANGE :: 0x0466
+WM_CAP_SET_CALLBACK_ERRORW :: 0x0466
+WM_CHOOSEFONT_SETFLAGS :: 0x0466
+ACM_OPENW :: 0x0467
+BFFM_SETSELECTIONW :: 0x0467
+CDM_GETFOLDERIDLIST :: 0x0467
+EM_GETWORDWRAPMODE :: 0x0467
+IPM_SETRANGE :: 0x0467
+PSM_ADDPAGE :: 0x0467
+UDM_SETPOS :: 0x0467
+WM_CAP_SET_CALLBACK_STATUSW :: 0x0467
+BFFM_SETSTATUSTEXTW :: 0x0468
+CDM_SETCONTROLTEXT :: 0x0468
+EM_SETIMECOLOR :: 0x0468
+IPM_SETFOCUS :: 0x0468
+PSM_CHANGED :: 0x0468
+UDM_GETPOS :: 0x0468
+CDM_HIDECONTROL :: 0x0469
+EM_GETIMECOLOR :: 0x0469
+IPM_ISBLANK :: 0x0469
+PSM_RESTARTWINDOWS :: 0x0469
+UDM_SETBUDDY :: 0x0469
+CDM_SETDEFEXT :: 0x046a
+EM_SETIMEOPTIONS :: 0x046a
+PSM_REBOOTSYSTEM :: 0x046a
+UDM_GETBUDDY :: 0x046a
+EM_GETIMEOPTIONS :: 0x046b
+PSM_CANCELTOCLOSE :: 0x046b
+UDM_SETACCEL :: 0x046b
+EM_CONVPOSITION :: 0x046c
+PSM_QUERYSIBLINGS :: 0x046c
+UDM_GETACCEL :: 0x046c
+MCIWNDM_GETZOOM :: 0x046d
+PSM_UNCHANGED :: 0x046d
+UDM_SETBASE :: 0x046d
+PSM_APPLY :: 0x046e
+UDM_GETBASE :: 0x046e
+PSM_SETTITLEA :: 0x046f
+UDM_SETRANGE32 :: 0x046f
+PSM_SETWIZBUTTONS :: 0x0470
+UDM_GETRANGE32 :: 0x0470
+WM_CAP_DRIVER_GET_NAMEW :: 0x0470
+PSM_PRESSBUTTON :: 0x0471
+UDM_SETPOS32 :: 0x0471
+WM_CAP_DRIVER_GET_VERSIONW :: 0x0471
+PSM_SETCURSELID :: 0x0472
+UDM_GETPOS32 :: 0x0472
+PSM_SETFINISHTEXTA :: 0x0473
+PSM_GETTABCONTROL :: 0x0474
+PSM_ISDIALOGMESSAGE :: 0x0475
+MCIWNDM_REALIZE :: 0x0476
+PSM_GETCURRENTPAGEHWND :: 0x0476
+MCIWNDM_SETTIMEFORMATA :: 0x0477
+PSM_INSERTPAGE :: 0x0477
+EM_SETLANGOPTIONS :: 0x0478
+MCIWNDM_GETTIMEFORMATA :: 0x0478
+PSM_SETTITLEW :: 0x0478
+WM_CAP_FILE_SET_CAPTURE_FILEW :: 0x0478
+EM_GETLANGOPTIONS :: 0x0479
+MCIWNDM_VALIDATEMEDIA :: 0x0479
+PSM_SETFINISHTEXTW :: 0x0479
+WM_CAP_FILE_GET_CAPTURE_FILEW :: 0x0479
+EM_GETIMECOMPMODE :: 0x047a
+EM_FINDTEXTW :: 0x047b
+MCIWNDM_PLAYTO :: 0x047b
+WM_CAP_FILE_SAVEASW :: 0x047b
+EM_FINDTEXTEXW :: 0x047c
+MCIWNDM_GETFILENAMEA :: 0x047c
+EM_RECONVERSION :: 0x047d
+MCIWNDM_GETDEVICEA :: 0x047d
+PSM_SETHEADERTITLEA :: 0x047d
+WM_CAP_FILE_SAVEDIBW :: 0x047d
+EM_SETIMEMODEBIAS :: 0x047e
+MCIWNDM_GETPALETTE :: 0x047e
+PSM_SETHEADERTITLEW :: 0x047e
+EM_GETIMEMODEBIAS :: 0x047f
+MCIWNDM_SETPALETTE :: 0x047f
+PSM_SETHEADERSUBTITLEA :: 0x047f
+MCIWNDM_GETERRORA :: 0x0480
+PSM_SETHEADERSUBTITLEW :: 0x0480
+PSM_HWNDTOINDEX :: 0x0481
+PSM_INDEXTOHWND :: 0x0482
+MCIWNDM_SETINACTIVETIMER :: 0x0483
+PSM_PAGETOINDEX :: 0x0483
+PSM_INDEXTOPAGE :: 0x0484
+DL_BEGINDRAG :: 0x0485
+MCIWNDM_GETINACTIVETIMER :: 0x0485
+PSM_IDTOINDEX :: 0x0485
+DL_DRAGGING :: 0x0486
+PSM_INDEXTOID :: 0x0486
+DL_DROPPED :: 0x0487
+PSM_GETRESULT :: 0x0487
+DL_CANCELDRAG :: 0x0488
+PSM_RECALCPAGESIZES :: 0x0488
+MCIWNDM_GET_SOURCE :: 0x048c
+MCIWNDM_PUT_SOURCE :: 0x048d
+MCIWNDM_GET_DEST :: 0x048e
+MCIWNDM_PUT_DEST :: 0x048f
+MCIWNDM_CAN_PLAY :: 0x0490
+MCIWNDM_CAN_WINDOW :: 0x0491
+MCIWNDM_CAN_RECORD :: 0x0492
+MCIWNDM_CAN_SAVE :: 0x0493
+MCIWNDM_CAN_EJECT :: 0x0494
+MCIWNDM_CAN_CONFIG :: 0x0495
+IE_GETINK :: 0x0496
+IE_MSGFIRST :: 0x0496
+MCIWNDM_PALETTEKICK :: 0x0496
+IE_SETINK :: 0x0497
+IE_GETPENTIP :: 0x0498
+IE_SETPENTIP :: 0x0499
+IE_GETERASERTIP :: 0x049a
+IE_SETERASERTIP :: 0x049b
+IE_GETBKGND :: 0x049c
+IE_SETBKGND :: 0x049d
+IE_GETGRIDORIGIN :: 0x049e
+IE_SETGRIDORIGIN :: 0x049f
+IE_GETGRIDPEN :: 0x04a0
+IE_SETGRIDPEN :: 0x04a1
+IE_GETGRIDSIZE :: 0x04a2
+IE_SETGRIDSIZE :: 0x04a3
+IE_GETMODE :: 0x04a4
+IE_SETMODE :: 0x04a5
+IE_GETINKRECT :: 0x04a6
+WM_CAP_SET_MCI_DEVICEW :: 0x04a6
+WM_CAP_GET_MCI_DEVICEW :: 0x04a7
+WM_CAP_PAL_OPENW :: 0x04b4
+WM_CAP_PAL_SAVEW :: 0x04b5
+IE_GETAPPDATA :: 0x04b8
+IE_SETAPPDATA :: 0x04b9
+IE_GETDRAWOPTS :: 0x04ba
+IE_SETDRAWOPTS :: 0x04bb
+IE_GETFORMAT :: 0x04bc
+IE_SETFORMAT :: 0x04bd
+IE_GETINKINPUT :: 0x04be
+IE_SETINKINPUT :: 0x04bf
+IE_GETNOTIFY :: 0x04c0
+IE_SETNOTIFY :: 0x04c1
+IE_GETRECOG :: 0x04c2
+IE_SETRECOG :: 0x04c3
+IE_GETSECURITY :: 0x04c4
+IE_SETSECURITY :: 0x04c5
+IE_GETSEL :: 0x04c6
+IE_SETSEL :: 0x04c7
+CDM_LAST :: 0x04c8
+EM_SETBIDIOPTIONS :: 0x04c8
+IE_DOCOMMAND :: 0x04c8
+MCIWNDM_NOTIFYMODE :: 0x04c8
+EM_GETBIDIOPTIONS :: 0x04c9
+IE_GETCOMMAND :: 0x04c9
+EM_SETTYPOGRAPHYOPTIONS :: 0x04ca
+IE_GETCOUNT :: 0x04ca
+EM_GETTYPOGRAPHYOPTIONS :: 0x04cb
+IE_GETGESTURE :: 0x04cb
+MCIWNDM_NOTIFYMEDIA :: 0x04cb
+EM_SETEDITSTYLE :: 0x04cc
+IE_GETMENU :: 0x04cc
+EM_GETEDITSTYLE :: 0x04cd
+IE_GETPAINTDC :: 0x04cd
+MCIWNDM_NOTIFYERROR :: 0x04cd
+IE_GETPDEVENT :: 0x04ce
+IE_GETSELCOUNT :: 0x04cf
+IE_GETSELITEMS :: 0x04d0
+IE_GETSTYLE :: 0x04d1
+MCIWNDM_SETTIMEFORMATW :: 0x04db
+EM_OUTLINE :: 0x04dc
+MCIWNDM_GETTIMEFORMATW :: 0x04dc
+EM_GETSCROLLPOS :: 0x04dd
+EM_SETSCROLLPOS :: 0x04de
+EM_SETFONTSIZE :: 0x04df
+EM_GETZOOM :: 0x04e0
+MCIWNDM_GETFILENAMEW :: 0x04e0
+EM_SETZOOM :: 0x04e1
+MCIWNDM_GETDEVICEW :: 0x04e1
+EM_GETVIEWKIND :: 0x04e2
+EM_SETVIEWKIND :: 0x04e3
+EM_GETPAGE :: 0x04e4
+MCIWNDM_GETERRORW :: 0x04e4
+EM_SETPAGE :: 0x04e5
+EM_GETHYPHENATEINFO :: 0x04e6
+EM_SETHYPHENATEINFO :: 0x04e7
+EM_GETPAGEROTATE :: 0x04eb
+EM_SETPAGEROTATE :: 0x04ec
+EM_GETCTFMODEBIAS :: 0x04ed
+EM_SETCTFMODEBIAS :: 0x04ee
+EM_GETCTFOPENSTATUS :: 0x04f0
+EM_SETCTFOPENSTATUS :: 0x04f1
+EM_GETIMECOMPTEXT :: 0x04f2
+EM_ISIME :: 0x04f3
+EM_GETIMEPROPERTY :: 0x04f4
+EM_GETQUERYRTFOBJ :: 0x050d
+EM_SETQUERYRTFOBJ :: 0x050e
+FM_GETFOCUS :: 0x0600
+FM_GETDRIVEINFOA :: 0x0601
+FM_GETSELCOUNT :: 0x0602
+FM_GETSELCOUNTLFN :: 0x0603
+FM_GETFILESELA :: 0x0604
+FM_GETFILESELLFNA :: 0x0605
+FM_REFRESH_WINDOWS :: 0x0606
+FM_RELOAD_EXTENSIONS :: 0x0607
+FM_GETDRIVEINFOW :: 0x0611
+FM_GETFILESELW :: 0x0614
+FM_GETFILESELLFNW :: 0x0615
+WLX_WM_SAS :: 0x0659
+SM_GETSELCOUNT :: 0x07e8
+UM_GETSELCOUNT :: 0x07e8
+WM_CPL_LAUNCH :: 0x07e8
+SM_GETSERVERSELA :: 0x07e9
+UM_GETUSERSELA :: 0x07e9
+WM_CPL_LAUNCHED :: 0x07e9
+SM_GETSERVERSELW :: 0x07ea
+UM_GETUSERSELW :: 0x07ea
+SM_GETCURFOCUSA :: 0x07eb
+UM_GETGROUPSELA :: 0x07eb
+SM_GETCURFOCUSW :: 0x07ec
+UM_GETGROUPSELW :: 0x07ec
+SM_GETOPTIONS :: 0x07ed
+UM_GETCURFOCUSA :: 0x07ed
+UM_GETCURFOCUSW :: 0x07ee
+UM_GETOPTIONS :: 0x07ef
+UM_GETOPTIONS2 :: 0x07f0
+LVM_FIRST :: 0x1000
+LVM_GETBKCOLOR :: 0x1000
+LVM_SETBKCOLOR :: 0x1001
+LVM_GETIMAGELIST :: 0x1002
+LVM_SETIMAGELIST :: 0x1003
+LVM_GETITEMCOUNT :: 0x1004
+LVM_GETITEMA :: 0x1005
+LVM_SETITEMA :: 0x1006
+LVM_INSERTITEMA :: 0x1007
+LVM_DELETEITEM :: 0x1008
+LVM_DELETEALLITEMS :: 0x1009
+LVM_GETCALLBACKMASK :: 0x100a
+LVM_SETCALLBACKMASK :: 0x100b
+LVM_GETNEXTITEM :: 0x100c
+LVM_FINDITEMA :: 0x100d
+LVM_GETITEMRECT :: 0x100e
+LVM_SETITEMPOSITION :: 0x100f
+LVM_GETITEMPOSITION :: 0x1010
+LVM_GETSTRINGWIDTHA :: 0x1011
+LVM_HITTEST :: 0x1012
+LVM_ENSUREVISIBLE :: 0x1013
+LVM_SCROLL :: 0x1014
+LVM_REDRAWITEMS :: 0x1015
+LVM_ARRANGE :: 0x1016
+LVM_EDITLABELA :: 0x1017
+LVM_GETEDITCONTROL :: 0x1018
+LVM_GETCOLUMNA :: 0x1019
+LVM_SETCOLUMNA :: 0x101a
+LVM_INSERTCOLUMNA :: 0x101b
+LVM_DELETECOLUMN :: 0x101c
+LVM_GETCOLUMNWIDTH :: 0x101d
+LVM_SETCOLUMNWIDTH :: 0x101e
+LVM_GETHEADER :: 0x101f
+LVM_CREATEDRAGIMAGE :: 0x1021
+LVM_GETVIEWRECT :: 0x1022
+LVM_GETTEXTCOLOR :: 0x1023
+LVM_SETTEXTCOLOR :: 0x1024
+LVM_GETTEXTBKCOLOR :: 0x1025
+LVM_SETTEXTBKCOLOR :: 0x1026
+LVM_GETTOPINDEX :: 0x1027
+LVM_GETCOUNTPERPAGE :: 0x1028
+LVM_GETORIGIN :: 0x1029
+LVM_UPDATE :: 0x102a
+LVM_SETITEMSTATE :: 0x102b
+LVM_GETITEMSTATE :: 0x102c
+LVM_GETITEMTEXTA :: 0x102d
+LVM_SETITEMTEXTA :: 0x102e
+LVM_SETITEMCOUNT :: 0x102f
+LVM_SORTITEMS :: 0x1030
+LVM_SETITEMPOSITION32 :: 0x1031
+LVM_GETSELECTEDCOUNT :: 0x1032
+LVM_GETITEMSPACING :: 0x1033
+LVM_GETISEARCHSTRINGA :: 0x1034
+LVM_SETICONSPACING :: 0x1035
+LVM_SETEXTENDEDLISTVIEWSTYLE :: 0x1036
+LVM_GETEXTENDEDLISTVIEWSTYLE :: 0x1037
+LVM_GETSUBITEMRECT :: 0x1038
+LVM_SUBITEMHITTEST :: 0x1039
+LVM_SETCOLUMNORDERARRAY :: 0x103a
+LVM_GETCOLUMNORDERARRAY :: 0x103b
+LVM_SETHOTITEM :: 0x103c
+LVM_GETHOTITEM :: 0x103d
+LVM_SETHOTCURSOR :: 0x103e
+LVM_GETHOTCURSOR :: 0x103f
+LVM_APPROXIMATEVIEWRECT :: 0x1040
+LVM_SETWORKAREAS :: 0x1041
+LVM_GETSELECTIONMARK :: 0x1042
+LVM_SETSELECTIONMARK :: 0x1043
+LVM_SETBKIMAGEA :: 0x1044
+LVM_GETBKIMAGEA :: 0x1045
+LVM_GETWORKAREAS :: 0x1046
+LVM_SETHOVERTIME :: 0x1047
+LVM_GETHOVERTIME :: 0x1048
+LVM_GETNUMBEROFWORKAREAS :: 0x1049
+LVM_SETTOOLTIPS :: 0x104a
+LVM_GETITEMW :: 0x104b
+LVM_SETITEMW :: 0x104c
+LVM_INSERTITEMW :: 0x104d
+LVM_GETTOOLTIPS :: 0x104e
+LVM_FINDITEMW :: 0x1053
+LVM_GETSTRINGWIDTHW :: 0x1057
+LVM_GETCOLUMNW :: 0x105f
+LVM_SETCOLUMNW :: 0x1060
+LVM_INSERTCOLUMNW :: 0x1061
+LVM_GETITEMTEXTW :: 0x1073
+LVM_SETITEMTEXTW :: 0x1074
+LVM_GETISEARCHSTRINGW :: 0x1075
+LVM_EDITLABELW :: 0x1076
+LVM_GETBKIMAGEW :: 0x108b
+LVM_SETSELECTEDCOLUMN :: 0x108c
+LVM_SETTILEWIDTH :: 0x108d
+LVM_SETVIEW :: 0x108e
+LVM_GETVIEW :: 0x108f
+LVM_INSERTGROUP :: 0x1091
+LVM_SETGROUPINFO :: 0x1093
+LVM_GETGROUPINFO :: 0x1095
+LVM_REMOVEGROUP :: 0x1096
+LVM_MOVEGROUP :: 0x1097
+LVM_MOVEITEMTOGROUP :: 0x109a
+LVM_SETGROUPMETRICS :: 0x109b
+LVM_GETGROUPMETRICS :: 0x109c
+LVM_ENABLEGROUPVIEW :: 0x109d
+LVM_SORTGROUPS :: 0x109e
+LVM_INSERTGROUPSORTED :: 0x109f
+LVM_REMOVEALLGROUPS :: 0x10a0
+LVM_HASGROUP :: 0x10a1
+LVM_SETTILEVIEWINFO :: 0x10a2
+LVM_GETTILEVIEWINFO :: 0x10a3
+LVM_SETTILEINFO :: 0x10a4
+LVM_GETTILEINFO :: 0x10a5
+LVM_SETINSERTMARK :: 0x10a6
+LVM_GETINSERTMARK :: 0x10a7
+LVM_INSERTMARKHITTEST :: 0x10a8
+LVM_GETINSERTMARKRECT :: 0x10a9
+LVM_SETINSERTMARKCOLOR :: 0x10aa
+LVM_GETINSERTMARKCOLOR :: 0x10ab
+LVM_SETINFOTIP :: 0x10ad
+LVM_GETSELECTEDCOLUMN :: 0x10ae
+LVM_ISGROUPVIEWENABLED :: 0x10af
+LVM_GETOUTLINECOLOR :: 0x10b0
+LVM_SETOUTLINECOLOR :: 0x10b1
+LVM_CANCELEDITLABEL :: 0x10b3
+LVM_MAPINDEXTOID :: 0x10b4
+LVM_MAPIDTOINDEX :: 0x10b5
+LVM_ISITEMVISIBLE :: 0x10b6
+LVM_GETEMPTYTEXT :: 0x10cc
+LVM_GETFOOTERRECT :: 0x10cd
+LVM_GETFOOTERINFO :: 0x10ce
+LVM_GETFOOTERITEMRECT :: 0x10cf
+LVM_GETFOOTERITEM :: 0x10d0
+LVM_GETITEMINDEXRECT :: 0x10d1
+LVM_SETITEMINDEXSTATE :: 0x10d2
+LVM_GETNEXTITEMINDEX :: 0x10d3
+OCM__BASE :: 0x2000
+LVM_SETUNICODEFORMAT :: 0x2005
+LVM_GETUNICODEFORMAT :: 0x2006
+OCM_CTLCOLOR :: 0x2019
+OCM_DRAWITEM :: 0x202b
+OCM_MEASUREITEM :: 0x202c
+OCM_DELETEITEM :: 0x202d
+OCM_VKEYTOITEM :: 0x202e
+OCM_CHARTOITEM :: 0x202f
+OCM_COMPAREITEM :: 0x2039
+OCM_NOTIFY :: 0x204e
+OCM_COMMAND :: 0x2111
+OCM_HSCROLL :: 0x2114
+OCM_VSCROLL :: 0x2115
+OCM_CTLCOLORMSGBOX :: 0x2132
+OCM_CTLCOLOREDIT :: 0x2133
+OCM_CTLCOLORLISTBOX :: 0x2134
+OCM_CTLCOLORBTN :: 0x2135
+OCM_CTLCOLORDLG :: 0x2136
+OCM_CTLCOLORSCROLLBAR :: 0x2137
+OCM_CTLCOLORSTATIC :: 0x2138
+OCM_PARENTNOTIFY :: 0x2210
+WM_APP :: 0x8000
+WM_RASDIALEVENT :: 0xcccd
diff --git a/core/sys/windows/winmm.odin b/core/sys/windows/winmm.odin
new file mode 100644
index 000000000..9edd56acc
--- /dev/null
+++ b/core/sys/windows/winmm.odin
@@ -0,0 +1,10 @@
+// +build windows
+package sys_windows
+
+foreign import winmm "system:Winmm.lib"
+
+@(default_calling_convention="stdcall")
+foreign winmm {
+ timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
+ timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
+}
diff --git a/core/testing/runner_other.odin b/core/testing/runner_other.odin
index 3978a3c83..f3271d209 100644
--- a/core/testing/runner_other.odin
+++ b/core/testing/runner_other.odin
@@ -6,7 +6,7 @@ import "core:time"
run_internal_test :: proc(t: ^T, it: Internal_Test) {
// TODO(bill): Catch panics on other platforms
- it.p(t);
+ it.p(t)
}
_fail_timeout :: proc(t: ^T, duration: time.Duration, loc := #caller_location) {
diff --git a/core/testing/runner_windows.odin b/core/testing/runner_windows.odin
index 560f23956..e812c410a 100644
--- a/core/testing/runner_windows.odin
+++ b/core/testing/runner_windows.odin
@@ -21,7 +21,7 @@ sema_wait :: proc "contextless" (s: ^Sema) {
win32.WaitOnAddress(&s.count, &original_count, size_of(original_count), win32.INFINITE)
original_count = s.count
}
- if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
+ if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return
}
}
@@ -46,7 +46,7 @@ sema_wait_with_timeout :: proc "contextless" (s: ^Sema, duration: time.Duration)
}
original_count = s.count
}
- if original_count == intrinsics.atomic_cxchg(&s.count, original_count-1, original_count) {
+ if original_count == intrinsics.atomic_compare_exchange_strong(&s.count, original_count-1, original_count) {
return true
}
}
diff --git a/core/thread/thread_pool.odin b/core/thread/thread_pool.odin
index 37ee4fa98..af80da9aa 100644
--- a/core/thread/thread_pool.odin
+++ b/core/thread/thread_pool.odin
@@ -1,67 +1,75 @@
package thread
+/*
+ thread.Pool
+ Copyright 2022 eisbehr
+ Made available under Odin's BSD-3 license.
+*/
+
import "core:intrinsics"
import "core:sync"
import "core:mem"
-Task_Status :: enum i32 {
- Ready,
- Busy,
- Waiting,
- Term,
-}
-
-Task_Proc :: #type proc(task: ^Task)
+Task_Proc :: #type proc(task: Task)
Task :: struct {
- procedure: Task_Proc,
- data: rawptr,
+ procedure: Task_Proc,
+ data: rawptr,
user_index: int,
+ allocator: mem.Allocator,
}
-Task_Id :: distinct i32
-INVALID_TASK_ID :: Task_Id(-1)
-
-
+// Do not access the pool's members directly while the pool threads are running,
+// since they use different kinds of locking and mutual exclusion devices.
+// Careless access can and will lead to nasty bugs. Once initialized, the
+// pool's memory address is not allowed to change until it is destroyed.
Pool :: struct {
- allocator: mem.Allocator,
- mutex: sync.Mutex,
- sem_available: sync.Semaphore,
- processing_task_count: int, // atomic
- is_running: bool,
+ allocator: mem.Allocator,
+ mutex: sync.Mutex,
+ sem_available: sync.Sema,
+
+ // the following values are atomic
+ num_waiting: int,
+ num_in_processing: int,
+ num_outstanding: int, // num_waiting + num_in_processing
+ num_done: int,
+ // end of atomics
+
+ is_running: bool,
threads: []^Thread,
- tasks: [dynamic]Task,
+ tasks: [dynamic]Task,
+ tasks_done: [dynamic]Task,
}
-pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator) {
- worker_thread_internal :: proc(t: ^Thread) {
- pool := (^Pool)(t.data)
-
- for pool.is_running {
- sync.semaphore_wait_for(&pool.sem_available)
-
- if task, ok := pool_try_and_pop_task(pool); ok {
- pool_do_work(pool, &task)
- }
- }
-
- sync.semaphore_post(&pool.sem_available, 1)
- }
-
-
+// Once initialized, the pool's memory address is not allowed to change until
+// it is destroyed.
+//
+// The thread pool requires an allocator which it either owns, or which is thread safe.
+pool_init :: proc(pool: ^Pool, allocator: mem.Allocator, thread_count: int) {
context.allocator = allocator
pool.allocator = allocator
- pool.tasks = make([dynamic]Task)
- pool.threads = make([]^Thread, thread_count)
+ pool.tasks = make([dynamic]Task)
+ pool.tasks_done = make([dynamic]Task)
+ pool.threads = make([]^Thread, max(thread_count, 1))
- sync.mutex_init(&pool.mutex)
- sync.semaphore_init(&pool.sem_available)
pool.is_running = true
for _, i in pool.threads {
- t := create(worker_thread_internal)
+ t := create(proc(t: ^Thread) {
+ pool := (^Pool)(t.data)
+
+ for intrinsics.atomic_load(&pool.is_running) {
+ sync.wait(&pool.sem_available)
+
+ if task, ok := pool_pop_waiting(pool); ok {
+ pool_do_work(pool, task)
+ }
+ }
+
+ sync.post(&pool.sem_available, 1)
+ })
t.user_index = i
t.data = pool
pool.threads[i] = t
@@ -70,15 +78,13 @@ pool_init :: proc(pool: ^Pool, thread_count: int, allocator := context.allocator
pool_destroy :: proc(pool: ^Pool) {
delete(pool.tasks)
+ delete(pool.tasks_done)
- for thread in &pool.threads {
- destroy(thread)
+ for t in &pool.threads {
+ destroy(t)
}
delete(pool.threads, pool.allocator)
-
- sync.mutex_destroy(&pool.mutex)
- sync.semaphore_destroy(&pool.sem_available)
}
pool_start :: proc(pool: ^Pool) {
@@ -87,10 +93,12 @@ pool_start :: proc(pool: ^Pool) {
}
}
+// Finish tasks that have already started processing, then shut down all pool
+// threads. Might leave over waiting tasks, any memory allocated for the
+// user data of those tasks will not be freed.
pool_join :: proc(pool: ^Pool) {
- pool.is_running = false
-
- sync.semaphore_post(&pool.sem_available, len(pool.threads))
+ intrinsics.atomic_store(&pool.is_running, false)
+ sync.post(&pool.sem_available, len(pool.threads))
yield()
@@ -99,53 +107,111 @@ pool_join :: proc(pool: ^Pool) {
}
}
-pool_add_task :: proc(pool: ^Pool, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
- sync.mutex_lock(&pool.mutex)
- defer sync.mutex_unlock(&pool.mutex)
+// Add a task to the thread pool.
+//
+// Tasks can be added from any thread, not just the thread that created
+// the thread pool. You can even add tasks from inside other tasks.
+//
+// Each task also needs an allocator which it either owns, or which is thread
+// safe.
+pool_add_task :: proc(pool: ^Pool, allocator: mem.Allocator, procedure: Task_Proc, data: rawptr, user_index: int = 0) {
+ sync.guard(&pool.mutex)
- task: Task
- task.procedure = procedure
- task.data = data
- task.user_index = user_index
-
- append(&pool.tasks, task)
- sync.semaphore_post(&pool.sem_available, 1)
+ append(&pool.tasks, Task{
+ procedure = procedure,
+ data = data,
+ user_index = user_index,
+ allocator = allocator,
+ })
+ intrinsics.atomic_add(&pool.num_waiting, 1)
+ intrinsics.atomic_add(&pool.num_outstanding, 1)
+ sync.post(&pool.sem_available, 1)
}
-pool_try_and_pop_task :: proc(pool: ^Pool) -> (task: Task, got_task: bool = false) {
- if sync.mutex_try_lock(&pool.mutex) {
- if len(pool.tasks) != 0 {
- intrinsics.atomic_add(&pool.processing_task_count, 1)
- task = pop_front(&pool.tasks)
- got_task = true
- }
- sync.mutex_unlock(&pool.mutex)
+// Number of tasks waiting to be processed. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_waiting :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_waiting)
+}
+
+// Number of tasks currently being processed. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_in_processing :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_in_processing)
+}
+
+// Outstanding tasks are all tasks that are not done, that is, tasks that are
+// waiting, as well as tasks that are currently being processed. Only
+// informational, mostly for debugging. Don't rely on this value being
+// consistent with other num_* values.
+pool_num_outstanding :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_outstanding)
+}
+
+// Number of tasks which are done processing. Only informational, mostly for
+// debugging. Don't rely on this value being consistent with other num_*
+// values.
+pool_num_done :: #force_inline proc(pool: ^Pool) -> int {
+ return intrinsics.atomic_load(&pool.num_done)
+}
+
+// If tasks are only being added from one thread, and this procedure is being
+// called from that same thread, it will reliably tell if the thread pool is
+// empty or not. Empty in this case means there are no tasks waiting, being
+// processed, or _done_.
+pool_is_empty :: #force_inline proc(pool: ^Pool) -> bool {
+ return pool_num_outstanding(pool) == 0 && pool_num_done(pool) == 0
+}
+
+// Mostly for internal use.
+pool_pop_waiting :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
+ sync.guard(&pool.mutex)
+
+ if len(pool.tasks) != 0 {
+ intrinsics.atomic_sub(&pool.num_waiting, 1)
+ intrinsics.atomic_add(&pool.num_in_processing, 1)
+ task = pop_front(&pool.tasks)
+ got_task = true
}
+
return
}
+// Use this to take out finished tasks.
+pool_pop_done :: proc(pool: ^Pool) -> (task: Task, got_task: bool) {
+ sync.guard(&pool.mutex)
-pool_do_work :: proc(pool: ^Pool, task: ^Task) {
- task.procedure(task)
- intrinsics.atomic_sub(&pool.processing_task_count, 1)
-}
-
-
-pool_wait_and_process :: proc(pool: ^Pool) {
- for len(pool.tasks) != 0 || intrinsics.atomic_load(&pool.processing_task_count) != 0 {
- if task, ok := pool_try_and_pop_task(pool); ok {
- pool_do_work(pool, &task)
- }
-
- // Safety kick
- if len(pool.tasks) != 0 && intrinsics.atomic_load(&pool.processing_task_count) == 0 {
- sync.mutex_lock(&pool.mutex)
- sync.semaphore_post(&pool.sem_available, len(pool.tasks))
- sync.mutex_unlock(&pool.mutex)
- }
-
- yield()
+ if len(pool.tasks_done) != 0 {
+ task = pop_front(&pool.tasks_done)
+ got_task = true
+ intrinsics.atomic_sub(&pool.num_done, 1)
}
+ return
+}
+
+// Mostly for internal use.
+pool_do_work :: proc(pool: ^Pool, task: Task) {
+ {
+ context.allocator = task.allocator
+ task.procedure(task)
+ }
+
+ sync.guard(&pool.mutex)
+
+ append(&pool.tasks_done, task)
+ intrinsics.atomic_add(&pool.num_done, 1)
+ intrinsics.atomic_sub(&pool.num_outstanding, 1)
+ intrinsics.atomic_sub(&pool.num_in_processing, 1)
+}
+
+// Process the rest of the tasks, also use this thread for processing, then join
+// all the pool threads.
+pool_finish :: proc(pool: ^Pool) {
+ for task in pool_pop_waiting(pool) {
+ pool_do_work(pool, task)
+ }
pool_join(pool)
}
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index cee278c7a..8452df112 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -1,4 +1,4 @@
-// +build linux, darwin, freebsd
+// +build linux, darwin, freebsd, openbsd
// +private
package thread
@@ -7,30 +7,19 @@ import "core:intrinsics"
import "core:sync"
import "core:sys/unix"
+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.
-
- // NOTE: pthread has a proc to query this, but it is marked
- // as non-portable ("np") so we do this instead.
- done: bool,
-
- // since libpthread doesn't seem to have a way to create a thread
- // in a suspended state, we have it wait on this gate, which we
- // signal to start it.
- // destroyed after thread is started.
- start_gate: sync.Condition,
- start_mutex: sync.Mutex,
-
- // if true, the thread has been started and the start_gate has been destroyed.
- started: bool,
-
- // NOTE: with pthreads, it is undefined behavior for multiple threads
- // to call join on the same thread at the same time.
- // this value is atomically updated to detect this.
- // See the comment in `join`.
- already_joined: bool,
+ cond: sync.Cond,
+ mutex: sync.Mutex,
+ flags: bit_set[Thread_State; u8],
}
//
// Creates a thread which will run the given procedure.
@@ -38,26 +27,31 @@ Thread_Os_Specific :: struct #align 16 {
//
_create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
- context = runtime.default_context()
t := (^Thread)(t)
- sync.condition_wait_for(&t.start_gate)
- sync.condition_destroy(&t.start_gate)
- sync.mutex_destroy(&t.start_mutex)
- t.start_gate = {}
- t.start_mutex = {}
- context = t.init_context.? or_else runtime.default_context()
-
+ context = runtime.default_context()
+
+ sync.lock(&t.mutex)
+
t.id = sync.current_thread_id()
- t.procedure(t)
- 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)
- }
+ for (.Started not_in t.flags) {
+ sync.wait(&t.cond, &t.mutex)
+ }
+
+ init_context := t.init_context
+ context = init_context.? or_else runtime.default_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)
}
- intrinsics.atomic_store(&t.done, true)
return nil
}
@@ -76,9 +70,6 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return nil
}
thread.creation_allocator = context.allocator
-
- sync.mutex_init(&thread.start_mutex)
- sync.condition_init(&thread.start_gate, &thread.start_mutex)
// Set thread priority.
policy: i32
@@ -97,65 +88,35 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
res = unix.pthread_attr_setschedparam(&attrs, ¶ms)
assert(res == 0)
+ thread.procedure = procedure
if unix.pthread_create(&thread.unix_thread, &attrs, __linux_thread_entry_proc, thread) != 0 {
free(thread, thread.creation_allocator)
-
- sync.condition_destroy(&thread.start_gate)
- sync.mutex_destroy(&thread.start_mutex)
return nil
}
- thread.procedure = procedure
-
return thread
}
_start :: proc(t: ^Thread) {
- if intrinsics.atomic_xchg(&t.started, true) {
- return
- }
- sync.condition_signal(&t.start_gate)
+ sync.guard(&t.mutex)
+ t.flags += { .Started }
+ sync.signal(&t.cond)
}
_is_done :: proc(t: ^Thread) -> bool {
- return intrinsics.atomic_load(&t.done)
+ return .Done in intrinsics.atomic_load(&t.flags)
}
_join :: proc(t: ^Thread) {
- if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
- return
- }
- // if unix.pthread_self().x == t.unix_thread.x do return;
+ sync.guard(&t.mutex)
- // NOTE(tetra): It's apparently UB for multiple threads to join the same thread
- // at the same time.
- // If someone else already did, spin until the thread dies.
- // See note on `already_joined` field.
- // TODO(tetra): I'm not sure if we should do this, or panic, since I'm not
- // sure it makes sense to need to join from multiple threads?
- if intrinsics.atomic_xchg(&t.already_joined, true) {
- for {
- if intrinsics.atomic_load(&t.done) {
- return
- }
- intrinsics.cpu_relax()
- }
- }
-
- // NOTE(tetra): If we're already dead, don't bother calling to pthread_join as that
- // will just return 3 (ESRCH).
- // We do this instead because I don't know if there is a danger
- // that you may join a different thread from the one you called join on,
- // if the thread handle is reused.
- if intrinsics.atomic_load(&t.done) {
+ if .Joined in t.flags || unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
return
}
- ret_val: rawptr
- _ = unix.pthread_join(t.unix_thread, &ret_val)
- if !intrinsics.atomic_load(&t.done) {
- panic("thread not done after join")
- }
+ unix.pthread_join(t.unix_thread, nil)
+
+ t.flags += { .Joined }
}
_join_multiple :: proc(threads: ..^Thread) {
@@ -164,16 +125,12 @@ _join_multiple :: proc(threads: ..^Thread) {
}
}
-
_destroy :: proc(t: ^Thread) {
_join(t)
- sync.condition_destroy(&t.start_gate)
- sync.mutex_destroy(&t.start_mutex)
t.unix_thread = {}
free(t, t.creation_allocator)
}
-
_terminate :: proc(t: ^Thread, exit_code: int) {
// TODO(bill)
}
diff --git a/core/thread/thread_windows.odin b/core/thread/thread_windows.odin
index 428e241d0..68382444c 100644
--- a/core/thread/thread_windows.odin
+++ b/core/thread/thread_windows.odin
@@ -3,13 +3,21 @@
package thread
import "core:runtime"
-import sync "core:sync/sync2"
+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,
- done: bool, // see note in `is_done`
+ mutex: sync.Mutex,
+ flags: bit_set[Thread_State; u8],
}
_thread_priority_map := [Thread_Priority]i32{
@@ -26,15 +34,16 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
context = t.init_context.? or_else runtime.default_context()
t.id = sync.current_thread_id()
+
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)
}
}
-
- sync.atomic_store(&t.done, true)
return 0
}
@@ -61,23 +70,31 @@ _create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^
return thread
}
-_start :: proc(thread: ^Thread) {
- win32.ResumeThread(thread.win32_thread)
+_start :: proc(t: ^Thread) {
+ sync.guard(&t.mutex)
+ t.flags += {.Started}
+ win32.ResumeThread(t.win32_thread)
}
-_is_done :: proc(using thread: ^Thread) -> bool {
+_is_done :: proc(t: ^Thread) -> bool {
// NOTE(tetra, 2019-10-31): Apparently using wait_for_single_object and
// checking if it didn't time out immediately, is not good enough,
// so we do it this way instead.
- return sync.atomic_load(&done)
+ return .Done in sync.atomic_load(&t.flags)
}
-_join :: proc(using thread: ^Thread) {
- if win32_thread != win32.INVALID_HANDLE {
- win32.WaitForSingleObject(win32_thread, win32.INFINITE)
- win32.CloseHandle(win32_thread)
- win32_thread = win32.INVALID_HANDLE
+_join :: proc(t: ^Thread) {
+ sync.guard(&t.mutex)
+
+ if .Joined in t.flags || t.win32_thread == win32.INVALID_HANDLE {
+ return
}
+
+ win32.WaitForSingleObject(t.win32_thread, win32.INFINITE)
+ win32.CloseHandle(t.win32_thread)
+ t.win32_thread = win32.INVALID_HANDLE
+
+ t.flags += {.Joined}
}
_join_multiple :: proc(threads: ..^Thread) {
diff --git a/core/time/time.odin b/core/time/time.odin
index fddb09d85..1f778a8de 100644
--- a/core/time/time.odin
+++ b/core/time/time.odin
@@ -213,6 +213,44 @@ time_add :: proc(t: Time, d: Duration) -> Time {
return Time{t._nsec + i64(d)}
}
+// Accurate sleep borrowed from: https://blat-blatnik.github.io/computerBear/making-accurate-sleep-function/
+//
+// Accuracy seems to be pretty good out of the box on Linux, to within around 4µs worst case.
+// On Windows it depends but is comparable with regular sleep in the worst case.
+// To get the same kind of accuracy as on Linux, have your program call `win32.time_begin_period(1)` to
+// tell Windows to use a more accurate timer for your process.
+accurate_sleep :: proc(d: Duration) {
+ to_sleep, estimate, mean, m2, count: Duration
+
+ to_sleep = d
+ estimate = 5 * Millisecond
+ mean = 5 * Millisecond
+ count = 1
+
+ for to_sleep > estimate {
+ start := tick_now()
+ sleep(1 * Millisecond)
+
+ observed := tick_since(start)
+ to_sleep -= observed
+
+ count += 1
+
+ delta := observed - mean
+ mean += delta / count
+ m2 += delta * (observed - mean)
+ stddev := intrinsics.sqrt(f64(m2) / f64(count - 1))
+ estimate = mean + Duration(stddev)
+ }
+
+ start := tick_now()
+ for to_sleep > tick_since(start) {
+ // prevent the spinlock from taking the thread hostage, still accurate enough
+ _yield()
+ // NOTE: it might be possible that it yields for too long, in that case it should spinlock freely for a while
+ // TODO: needs actual testing done to check if that's the case
+ }
+}
ABSOLUTE_ZERO_YEAR :: i64(-292277022399) // Day is chosen so that 2001-01-01 is Monday in the calculations
ABSOLUTE_TO_INTERNAL :: i64(-9223371966579724800) // i64((ABSOLUTE_ZERO_YEAR - 1) * 365.2425 * SECONDS_PER_DAY);
diff --git a/core/time/time_unix.odin b/core/time/time_unix.odin
index 0d765b72d..0cfa196a2 100644
--- a/core/time/time_unix.odin
+++ b/core/time/time_unix.odin
@@ -1,9 +1,9 @@
-//+build linux, darwin, freebsd
+//+build linux, darwin, freebsd, openbsd
package time
IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
-when ODIN_OS == "darwin" {
+when ODIN_OS == .Darwin {
foreign import libc "System.framework"
} else {
foreign import libc "system:c"
@@ -17,21 +17,47 @@ foreign libc {
@(link_name="nanosleep") _unix_nanosleep :: proc(requested: ^TimeSpec, remaining: ^TimeSpec) -> i32 ---
}
+foreign import "system:pthread"
+
+import "core:c"
+
+@(private="file")
+@(default_calling_convention="c")
+foreign pthread {
+ sched_yield :: proc() -> c.int ---
+}
+
+_yield :: proc "contextless" () {
+ sched_yield()
+}
+
TimeSpec :: struct {
tv_sec : i64, /* seconds */
tv_nsec : i64, /* nanoseconds */
}
-CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
-CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
-CLOCK_PROCESS_CPUTIME_ID :: 2
-CLOCK_THREAD_CPUTIME_ID :: 3
-CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
-CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
-CLOCK_MONOTONIC_COARSE :: 6
-CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
-CLOCK_REALTIME_ALARM :: 8
-CLOCK_BOOTTIME_ALARM :: 9
+when ODIN_OS == .OpenBSD {
+ CLOCK_REALTIME :: 0
+ CLOCK_PROCESS_CPUTIME_ID :: 2
+ CLOCK_MONOTONIC :: 3
+ CLOCK_THREAD_CPUTIME_ID :: 4
+ CLOCK_UPTIME :: 5
+ CLOCK_BOOTTIME :: 6
+
+ // CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
+ CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
+} else {
+ CLOCK_REALTIME :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
+ CLOCK_MONOTONIC :: 1 // NOTE(tetra): May stand still while system is asleep.
+ CLOCK_PROCESS_CPUTIME_ID :: 2
+ CLOCK_THREAD_CPUTIME_ID :: 3
+ CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+ CLOCK_REALTIME_COARSE :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
+ CLOCK_MONOTONIC_COARSE :: 6
+ CLOCK_BOOTTIME :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
+ CLOCK_REALTIME_ALARM :: 8
+ CLOCK_BOOTTIME_ALARM :: 9
+}
// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
// I do not know if Darwin programmers are used to the existance of these constants or not, so
diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin
index 0fb9eaa0f..397741126 100644
--- a/core/time/time_windows.odin
+++ b/core/time/time_windows.odin
@@ -35,3 +35,7 @@ _tick_now :: proc "contextless" () -> Tick {
_nsec := mul_div_u64(i64(now), 1e9, i64(qpc_frequency))
return Tick{_nsec = _nsec}
}
+
+_yield :: proc "contextless" () {
+ win32.SwitchToThread()
+}
diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin
index 2e349640e..6bdd6558a 100644
--- a/core/unicode/utf16/utf16.odin
+++ b/core/unicode/utf16/utf16.odin
@@ -117,9 +117,9 @@ decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) {
switch c := s[i]; {
case c < _surr1, _surr3 <= c:
r = rune(c)
- case _surr1 <= r && r < _surr2 && i+1 < len(s) &&
+ case _surr1 <= c && c < _surr2 && i+1 < len(s) &&
_surr2 <= s[i+1] && s[i+1] < _surr3:
- r = decode_surrogate_pair(rune(r), rune(s[i+1]))
+ r = decode_surrogate_pair(rune(c), rune(s[i+1]))
i += 1
}
diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin
index 23e2eefc6..86267defb 100644
--- a/core/unicode/utf8/utf8string/string.odin
+++ b/core/unicode/utf8/utf8string/string.odin
@@ -16,140 +16,140 @@ String :: struct {
}
@(private)
-_len :: builtin.len; // helper procedure
+_len :: builtin.len // helper procedure
init :: proc(s: ^String, contents: string) -> ^String {
- s.contents = contents;
- s.byte_pos = 0;
- s.rune_pos = 0;
+ s.contents = contents
+ s.byte_pos = 0
+ s.rune_pos = 0
for i in 0..<_len(contents) {
if contents[i] >= utf8.RUNE_SELF {
- s.rune_count = utf8.rune_count_in_string(contents);
- _, s.width = utf8.decode_rune_in_string(contents);
- s.non_ascii = i;
- return s;
+ s.rune_count = utf8.rune_count_in_string(contents)
+ _, s.width = utf8.decode_rune_in_string(contents)
+ s.non_ascii = i
+ return s
}
}
- s.rune_count = _len(contents);
- s.width = 0;
- s.non_ascii = _len(contents);
- return s;
+ s.rune_count = _len(contents)
+ s.width = 0
+ s.non_ascii = _len(contents)
+ return s
}
to_string :: proc(s: ^String) -> string {
- return s.contents;
+ return s.contents
}
len :: proc(s: ^String) -> int {
- return s.rune_count;
+ return s.rune_count
}
is_ascii :: proc(s: ^String) -> bool {
- return s.width == 0;
+ return s.width == 0
}
at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
- runtime.bounds_check_error_loc(loc, i, s.rune_count);
+ runtime.bounds_check_error_loc(loc, i, s.rune_count)
if i < s.non_ascii {
- return rune(s.contents[i]);
+ return rune(s.contents[i])
}
switch i {
case 0:
- r, s.width = utf8.decode_rune_in_string(s.contents);
- s.rune_pos = 0;
- s.byte_pos = 0;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents)
+ s.rune_pos = 0
+ s.byte_pos = 0
+ return
case s.rune_count-1:
- r, s.width = utf8.decode_rune_in_string(s.contents);
- s.rune_pos = i;
- s.byte_pos = _len(s.contents) - s.width;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents)
+ s.rune_pos = i
+ s.byte_pos = _len(s.contents) - s.width
+ return
case s.rune_pos-1:
- r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos]);
- s.rune_pos = i;
- s.byte_pos -= s.width;
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents[0:s.byte_pos])
+ s.rune_pos = i
+ s.byte_pos -= s.width
+ return
case s.rune_pos+1:
- s.rune_pos = i;
- s.byte_pos += s.width;
- fallthrough;
+ s.rune_pos = i
+ s.byte_pos += s.width
+ fallthrough
case s.rune_pos:
- r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
- return;
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
+ return
}
// Linear scan
- scan_forward := true;
+ scan_forward := true
if i < s.rune_pos {
if i < (s.rune_pos-s.non_ascii)/2 {
- s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii;
+ s.byte_pos, s.rune_pos = s.non_ascii, s.non_ascii
} else {
- scan_forward = false;
+ scan_forward = false
}
} else if i-s.rune_pos < (s.rune_count-s.rune_pos)/2 {
- // scan_forward = true;
+ // scan_forward = true
} else {
- s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count;
- scan_forward = false;
+ s.byte_pos, s.rune_pos = _len(s.contents), s.rune_count
+ scan_forward = false
}
if scan_forward {
for {
- r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:]);
+ r, s.width = utf8.decode_rune_in_string(s.contents[s.byte_pos:])
if s.rune_pos == i {
- return;
+ return
}
- s.rune_pos += 1;
- s.byte_pos += s.width;
+ s.rune_pos += 1
+ s.byte_pos += s.width
}
} else {
for {
- r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos]);
- s.rune_pos -= 1;
- s.byte_pos -= s.width;
+ r, s.width = utf8.decode_last_rune_in_string(s.contents[:s.byte_pos])
+ s.rune_pos -= 1
+ s.byte_pos -= s.width
if s.rune_pos == i {
- return;
+ return
}
}
}
}
slice :: proc(s: ^String, i, j: int, loc := #caller_location) -> string {
- runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count);
+ runtime.slice_expr_error_lo_hi_loc(loc, i, j, s.rune_count)
if j < s.non_ascii {
- return s.contents[i:j];
+ return s.contents[i:j]
}
if i == j {
- return "";
+ return ""
}
- lo, hi: int;
+ lo, hi: int
if i < s.non_ascii {
- lo = i;
+ lo = i
} else if i == s.rune_count {
- lo = _len(s.contents);
+ lo = _len(s.contents)
} else {
- at(s, i, loc);
- lo = s.byte_pos;
+ at(s, i, loc)
+ lo = s.byte_pos
}
if j == s.rune_count {
- hi = _len(s.contents);
+ hi = _len(s.contents)
} else {
- at(s, j, loc);
- hi = s.byte_pos;
+ at(s, j, loc)
+ hi = s.byte_pos
}
- return s.contents[lo:hi];
+ return s.contents[lo:hi]
}
diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin
index a88cc273e..27f199062 100644
--- a/examples/all/all_main.odin
+++ b/examples/all/all_main.odin
@@ -5,25 +5,68 @@ package all
import bufio "core:bufio"
import bytes "core:bytes"
+
import c "core:c"
import libc "core:c/libc"
+
import compress "core:compress"
+import shoco "core:compress/shoco"
import gzip "core:compress/gzip"
import zlib "core:compress/zlib"
-import container "core:container"
+
+import bit_array "core:container/bit_array"
+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 crypto "core:crypto"
+import blake "core:crypto/blake"
+import blake2b "core:crypto/blake2b"
+import blake2s "core:crypto/blake2s"
+import chacha20 "core:crypto/chacha20"
+import chacha20poly1305 "core:crypto/chacha20poly1305"
+import gost "core:crypto/gost"
+import groestl "core:crypto/groestl"
+import haval "core:crypto/haval"
+import jh "core:crypto/jh"
+import keccak "core:crypto/keccak"
+import md2 "core:crypto/md2"
+import md4 "core:crypto/md4"
+import md5 "core:crypto/md5"
+import poly1305 "core:crypto/poly1305"
+import ripemd "core:crypto/ripemd"
+import sha1 "core:crypto/sha1"
+import sha2 "core:crypto/sha2"
+import sha3 "core:crypto/sha3"
+import shake "core:crypto/shake"
+import sm3 "core:crypto/sm3"
+import streebog "core:crypto/streebog"
+import tiger "core:crypto/tiger"
+import tiger2 "core:crypto/tiger2"
+import crypto_util "core:crypto/util"
+import whirlpool "core:crypto/whirlpool"
+import x25519 "core:crypto/x25519"
+
import dynlib "core:dynlib"
-import encoding "core:encoding"
+
import base32 "core:encoding/base32"
import base64 "core:encoding/base64"
import csv "core:encoding/csv"
import hxa "core:encoding/hxa"
import json "core:encoding/json"
+import varint "core:encoding/varint"
+
import fmt "core:fmt"
import hash "core:hash"
+
import image "core:image"
import png "core:image/png"
+import qoi "core:image/qoi"
+
import io "core:io"
import log "core:log"
+
import math "core:math"
import big "core:math/big"
import bits "core:math/bits"
@@ -32,16 +75,22 @@ import linalg "core:math/linalg"
import glm "core:math/linalg/glsl"
import hlm "core:math/linalg/hlsl"
import rand "core:math/rand"
+
import mem "core:mem"
+// import virtual "core:mem/virtual"
+
import ast "core:odin/ast"
import doc_format "core:odin/doc-format"
import odin_format "core:odin/format"
import odin_parser "core:odin/parser"
import odin_printer "core:odin/printer"
import odin_tokenizer "core:odin/tokenizer"
+
import os "core:os"
-import path "core:path"
+
+import slashpath "core:path/slashpath"
import filepath "core:path/filepath"
+
import reflect "core:reflect"
import runtime "core:runtime"
import slice "core:slice"
@@ -49,12 +98,14 @@ import sort "core:sort"
import strconv "core:strconv"
import strings "core:strings"
import sync "core:sync"
-import sync2 "core:sync/sync2"
+import testing "core:testing"
import scanner "core:text/scanner"
import thread "core:thread"
import time "core:time"
+
import unicode "core:unicode"
import utf8 "core:unicode/utf8"
+import utf8string "core:unicode/utf8/utf8string"
import utf16 "core:unicode/utf16"
main :: proc(){}
@@ -65,20 +116,53 @@ _ :: bytes
_ :: c
_ :: libc
_ :: compress
+_ :: shoco
_ :: gzip
_ :: zlib
-_ :: container
+_ :: bit_array
+_ :: priority_queue
+_ :: queue
+_ :: small_array
+_ :: lru
+_ :: crypto
+_ :: blake
+_ :: blake2b
+_ :: blake2s
+_ :: chacha20
+_ :: chacha20poly1305
+_ :: gost
+_ :: groestl
+_ :: haval
+_ :: jh
+_ :: keccak
+_ :: md2
+_ :: md4
+_ :: md5
+_ :: poly1305
+_ :: ripemd
+_ :: sha1
+_ :: sha2
+_ :: sha3
+_ :: shake
+_ :: sm3
+_ :: streebog
+_ :: tiger
+_ :: tiger2
+_ :: crypto_util
+_ :: whirlpool
+_ :: x25519
_ :: dynlib
-_ :: encoding
_ :: base32
_ :: base64
_ :: csv
_ :: hxa
_ :: json
+_ :: varint
_ :: fmt
_ :: hash
_ :: image
_ :: png
+_ :: qoi
_ :: io
_ :: log
_ :: math
@@ -97,7 +181,7 @@ _ :: odin_parser
_ :: odin_printer
_ :: odin_tokenizer
_ :: os
-_ :: path
+_ :: slashpath
_ :: filepath
_ :: reflect
_ :: runtime
@@ -106,10 +190,11 @@ _ :: sort
_ :: strconv
_ :: strings
_ :: sync
-_ :: sync2
+_ :: testing
_ :: scanner
_ :: thread
_ :: time
_ :: unicode
_ :: utf8
+_ :: utf8string
_ :: utf16
\ No newline at end of file
diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin
index 777c184f9..f83e906a2 100644
--- a/examples/all/all_vendor.odin
+++ b/examples/all/all_vendor.odin
@@ -1,26 +1,40 @@
-//+build windows
package all
-import glfw "vendor:glfw"
-import gl "vendor:OpenGL"
-import rl "vendor:raylib"
-import PM "vendor:portmidi"
+import botan "vendor:botan"
+import ENet "vendor:ENet"
+import gl "vendor:OpenGL"
+import glfw "vendor:glfw"
+import microui "vendor:microui"
+import miniaudio "vendor:miniaudio"
+import PM "vendor:portmidi"
+import rl "vendor:raylib"
+
import SDL "vendor:sdl2"
-import IMG "vendor:sdl2/image"
import SDLNet "vendor:sdl2/net"
+import IMG "vendor:sdl2/image"
import MIX "vendor:sdl2/mixer"
import TTF "vendor:sdl2/ttf"
-import vk "vendor:vulkan"
-import ENet "vendor:ENet"
-_ :: glfw
+import vk "vendor:vulkan"
+
+import NS "vendor:darwin/Foundation"
+import MTL "vendor:darwin/Metal"
+import CA "vendor:darwin/QuartzCore"
+
+_ :: botan
+_ :: ENet
_ :: gl
-_ :: rl
+_ :: glfw
+_ :: microui
+_ :: miniaudio
_ :: PM
+_ :: rl
_ :: SDL
-_ :: IMG
_ :: SDLNet
+_ :: IMG
_ :: MIX
_ :: TTF
_ :: vk
-_ :: ENet
\ No newline at end of file
+_ :: NS
+_ :: MTL
+_ :: CA
diff --git a/examples/all/all_vendor_directx.odin b/examples/all/all_vendor_directx.odin
new file mode 100644
index 000000000..2f10d92f8
--- /dev/null
+++ b/examples/all/all_vendor_directx.odin
@@ -0,0 +1,10 @@
+//+build windows
+package all
+
+import D3D11 "vendor:directx/d3d11"
+import D3D12 "vendor:directx/d3d12"
+import DXGI "vendor:directx/dxgi"
+
+_ :: D3D11
+_ :: D3D12
+_ :: DXGI
diff --git a/examples/all/all_vendor_stl.odin b/examples/all/all_vendor_stl.odin
new file mode 100644
index 000000000..9faf53c63
--- /dev/null
+++ b/examples/all/all_vendor_stl.odin
@@ -0,0 +1,15 @@
+//+build windows, linux
+package all
+
+import stb_easy_font "vendor:stb/easy_font"
+import stbi "vendor:stb/image"
+import stbrp "vendor:stb/rect_pack"
+import stbtt "vendor:stb/truetype"
+import stb_vorbis "vendor:stb/vorbis"
+
+_ :: stb_easy_font
+_ :: stbi
+_ :: stbrp
+_ :: stbtt
+_ :: stb_vorbis
+
diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin
index 3e34e3d49..a36acdf18 100644
--- a/examples/demo/demo.odin
+++ b/examples/demo/demo.odin
@@ -11,22 +11,32 @@ import "core:intrinsics"
import "core:math/big"
/*
- The Odin programming language is fast, concise, readable, pragmatic and open sourced.
- It is designed with the intent of replacing C with the following goals:
- * simplicity
- * high performance
- * built for modern systems
- * joy of programming
+ Odin is a general-purpose programming language with distinct typing built
+ for high performance, modern systems and data-oriented programming.
+
+ Odin is the C alternative for the Joy of Programming.
# Installing Odin
Getting Started - https://odin-lang.org/docs/install/
Instructions for downloading and install the Odin compiler and libraries.
# Learning Odin
+ Getting Started - https://odin-lang.org/docs/install/
+ Getting Started with Odin. Downloading, installing, and getting your
+ first program to compile and run.
Overview of Odin - https://odin-lang.org/docs/overview/
- An overview of the Odin programming language.
+ An overview of the Odin programming language and its features.
Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
Answers to common questions about Odin.
+ Packages - https://pkg.odin-lang.org/
+ Documentation for all the official packages part of the
+ core and vendor library collections.
+ Nightly Builds - https://odin-lang.org/docs/nightly/
+ Get the latest nightly builds of Odin.
+ More Odin Examples - https://github.com/odin-lang/examples
+ This repository contains examples of how certain things can be accomplished
+ in idiomatic Odin, allowing you learn its semantics, as well as how to use
+ parts of the core and vendor package collections.
*/
the_basics :: proc() {
@@ -88,6 +98,7 @@ the_basics :: proc() {
z: f64 // `z` is typed of type `f64` (64-bit floating point number)
z = 1 // `1` is an untyped integer literal which can be implicitly converted to `f64`
// No need for any suffixes or decimal places like in other languages
+ // (with the exception of negative zero, which must be given as `-0.0`)
// CONSTANTS JUST WORK!!!
@@ -244,10 +255,10 @@ control_flow :: proc() {
// A switch statement is another way to write a sequence of if-else statements.
// In Odin, the default case is denoted as a case without any expression.
- switch arch := ODIN_ARCH; arch {
- case "386":
+ #partial switch arch := ODIN_ARCH; arch {
+ case .i386:
fmt.println("32-bit")
- case "amd64":
+ case .amd64:
fmt.println("64-bit")
case: // default
fmt.println("Unsupported architecture")
@@ -363,12 +374,12 @@ control_flow :: proc() {
*/
// Example
- when ODIN_ARCH == "386" {
+ when ODIN_ARCH == .i386 {
fmt.println("32 bit")
- } else when ODIN_ARCH == "amd64" {
+ } else when ODIN_ARCH == .amd64 {
fmt.println("64 bit")
} else {
- fmt.println("Unsupported architecture")
+ fmt.println("Unknown architecture")
}
// The when statement is very useful for writing platform specific code.
// This is akin to the #if construct in C’s preprocessor however, in Odin,
@@ -1100,11 +1111,6 @@ prefix_table := [?]string{
}
threading_example :: proc() {
- if ODIN_OS == "darwin" {
- // TODO: Fix threads on darwin/macOS
- return
- }
-
fmt.println("\n# threading_example")
{ // Basic Threads
@@ -1145,7 +1151,7 @@ threading_example :: proc() {
{ // Thread Pool
fmt.println("\n## Thread Pool")
- task_proc :: proc(t: ^thread.Task) {
+ task_proc :: proc(t: thread.Task) {
index := t.user_index % len(prefix_table)
for iteration in 1..=5 {
fmt.printf("Worker Task %d is on iteration %d\n", t.user_index, iteration)
@@ -1155,16 +1161,17 @@ threading_example :: proc() {
}
pool: thread.Pool
- thread.pool_init(pool=&pool, thread_count=3)
+ thread.pool_init(pool=&pool, thread_count=3, allocator=context.allocator)
defer thread.pool_destroy(&pool)
for i in 0..<30 {
- thread.pool_add_task(pool=&pool, procedure=task_proc, data=nil, user_index=i)
+ // 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_start(&pool)
- thread.pool_wait_and_process(&pool)
+ thread.pool_finish(&pool)
}
}
@@ -1606,13 +1613,13 @@ where_clauses :: proc() {
}
-when ODIN_OS == "windows" {
+when ODIN_OS == .Windows {
foreign import kernel32 "system:kernel32.lib"
}
foreign_system :: proc() {
fmt.println("\n#foreign system")
- when ODIN_OS == "windows" {
+ when ODIN_OS == .Windows {
// It is sometimes necessarily to interface with foreign code,
// such as a C library. In Odin, this is achieved through the
// foreign system. You can “import” a library into the code
@@ -1708,7 +1715,6 @@ deprecated_attribute :: proc() {
}
range_statements_with_multiple_return_values :: proc() {
- // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
fmt.println("\n#range statements with multiple return values")
My_Iterator :: struct {
index: int,
@@ -1921,14 +1927,14 @@ constant_literal_expressions :: proc() {
fmt.println("-------")
- Partial_Baz :: enum{A=5, B, C, D=16}
- #assert(len(Partial_Baz) < len(#partial [Partial_Baz]int))
- PARTIAL_ENUM_ARRAY_CONST :: #partial [Partial_Baz]int{.A ..= .C = 1, .D = 16}
+ Sparse_Baz :: enum{A=5, B, C, D=16}
+ #assert(len(Sparse_Baz) < len(#sparse[Sparse_Baz]int))
+ SPARSE_ENUM_ARRAY_CONST :: #sparse[Sparse_Baz]int{.A ..= .C = 1, .D = 16}
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.A])
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.B])
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.C])
- fmt.println(PARTIAL_ENUM_ARRAY_CONST[.D])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.A])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.B])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.C])
+ fmt.println(SPARSE_ENUM_ARRAY_CONST[.D])
fmt.println("-------")
@@ -1998,7 +2004,6 @@ relative_data_types :: proc() {
or_else_operator :: proc() {
fmt.println("\n#'or_else'")
- // IMPORTANT NOTE: 'or_else' is an experimental feature and subject to change/removal
{
m: map[string]int
i: int
@@ -2029,8 +2034,6 @@ or_else_operator :: proc() {
or_return_operator :: proc() {
fmt.println("\n#'or_return'")
- // IMPORTANT NOTE: 'or_return' is an experimental feature and subject to change/removal
- //
// The concept of 'or_return' will work by popping off the end value in a multiple
// valued expression and checking whether it was not 'nil' or 'false', and if so,
// set the end return value to value if possible. If the procedure only has one
@@ -2421,6 +2424,13 @@ matrix_type :: proc() {
}
main :: proc() {
+ /*
+ For More Odin Examples - https://github.com/odin-lang/examples
+ This repository contains examples of how certain things can be accomplished
+ in idiomatic Odin, allowing you learn its semantics, as well as how to use
+ parts of the core and vendor package collections.
+ */
+
when true {
the_basics()
control_flow()
diff --git a/examples/hms2019/basic.odin b/examples/hms2019/basic.odin
deleted file mode 100644
index 4c4a4eff0..000000000
--- a/examples/hms2019/basic.odin
+++ /dev/null
@@ -1,7 +0,0 @@
-package basic
-
-import "core:fmt"
-
-main :: proc() {
- fmt.println("Hellope!");
-}
\ No newline at end of file
diff --git a/examples/hms2019/eca.odin b/examples/hms2019/eca.odin
deleted file mode 100644
index eaefeeb9b..000000000
--- a/examples/hms2019/eca.odin
+++ /dev/null
@@ -1,67 +0,0 @@
-package eca
-
-import "core:fmt"
-import "core:math/rand"
-import "core:time"
-import "intrinsics"
-
-elementary_cellular_automata :: proc(state: $T, rule: u8, generations: int, pause: time.Duration = 0)
- where intrinsics.type_is_integer(T),
- intrinsics.type_is_unsigned(T) {
- N :: 8*size_of(state);
-
- output :: proc(state: T) {
- buf: [N]byte;
- for i in 0.. T {
- return (x >> i) & 0x1;
- }
- set :: proc(x: ^T, cell, k: T, rule: u8) {
- x^ &~= 1<>k&1 != 0 {
- x^ |= 1<| 0 do time.sleep(pause);
-
-
- k := bit(a, last) | bit(a, 0)<<1 | bit(a, 1)<<2;
- set(&a1, 0, k, rule);
- a1 |= (1<<0) * T(rule>>k&1);
- for c in 1..>1 | bit(a, c+1)<<2;
- set(&a1, c, k, rule);
- }
- set(&a1, last, k>>1|bit(a, 0)<<2, rule);
- a, a1 = a1, a;
- output(a);
- if a == a1 {
- return;
- }
- }
-}
-
-main :: proc() {
- elementary_cellular_automata(
- state=rand.uint128(),
- rule=30,
- generations=5000,
- pause=100*time.Millisecond,
- );
-}
\ No newline at end of file
diff --git a/examples/hms2019/hms2019.odin b/examples/hms2019/hms2019.odin
deleted file mode 100644
index 5c2e836f8..000000000
--- a/examples/hms2019/hms2019.odin
+++ /dev/null
@@ -1,1754 +0,0 @@
-package hms2019
-
-import "core:fmt"
-import "core:mem"
-import "core:os"
-import "core:reflect"
-import "intrinsics"
-
-/*
- Welcome to Handmade Seattle 2019!
-
- The Odin programming language is fast, concise, readable, pragmatic and open sourced.
- It is designed with the intent of replacing C with the following goals:
- * simplicity
- * high performance
- * built for modern systems
- * joy of programming
-
- # Installing Odin
- Getting Started - https://odin-lang.org/docs/install/
- Instructions for downloading and install the Odin compiler and libraries.
-
- # Learning Odin
- Overview of Odin - https://odin-lang.org/docs/overview/
- An overview of the Odin programming language.
- Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/
- Answers to common questions about Odin.
-*/
-
-the_basics :: proc() {
- fmt.println("\n# the basics");
-
- { // The Basics
- fmt.println("Hellope");
-
- // Lexical elements and literals
- // A comment
-
- my_integer_variable: int; // A comment for documentaton
-
- // Multi-line comments begin with /* and end with */. Multi-line comments can
- // also be nested (unlike in C):
- /*
- You can have any text or code here and
- have it be commented.
- /*
- NOTE: comments can be nested!
- */
- */
-
- // String literals are enclosed in double quotes and character literals in single quotes.
- // Special characters are escaped with a backslash \
-
- some_string := "This is a string";
- _ = 'A'; // unicode codepoint literal
- _ = '\n';
- _ = "C:\\Windows\\notepad.exe";
- // Raw string literals are enclosed with single back ticks
- _ = `C:\Windows\notepad.exe`;
-
- // The length of a string in bytes can be found using the built-in `len` procedure:
- _ = len("Foo");
- _ = len(some_string);
-
-
- // Numbers
-
- // Numerical literals are written similar to most other programming languages.
- // A useful feature in Odin is that underscores are allowed for better
- // readability: 1_000_000_000 (one billion). A number that contains a dot is a
- // floating point literal: 1.0e9 (one billion). If a number literal is suffixed
- // with i, is an imaginary number literal: 2i (2 multiply the square root of -1).
-
- // Binary literals are prefixed with 0b, octal literals with 0o, and hexadecimal
- // literals 0x. A leading zero does not produce an octal constant (unlike C).
-
- // In Odin, if a number constant is possible to be represented by a type without
- // precision loss, it will automatically convert to that type.
-
- x: int = 1.0; // A float literal but it can be represented by an integer without precision loss
- // Constant literals are “untyped” which means that they can implicitly convert to a type.
-
- y: int; // `y` is typed of type `int`
- y = 1; // `1` is an untyped integer literal which can implicitly convert to `int`
-
- z: f64; // `z` is typed of type `f64` (64-bit floating point number)
- z = 1; // `1` is an untyped integer literals which can be implicity conver to `f64`
- // No need for any suffixes or decimal places like in other languages
- // CONSTANTS JUST WORK!!!
-
-
- // Assignment statements
- h: int = 123; // declares a new variable `h` with type `int` and assigns a value to it
- h = 637; // assigns a new value to `h`
-
- // `=` is the assignment operator
-
- // You can assign multiple variables with it:
- a, b := 1, "hello"; // declares `a` and `b` and infers the types from the assignments
- b, a = "byte", 0;
-
- // Note: `:=` is two tokens, `:` and `=`. The follow are equivalent
- /*
- i: int = 123;
- i: = 123;
- i := 123
- */
-
- // Constant declarations
- // Constants are entities (symbols) which have an assigned value.
- // The constant’s value cannot be changed.
- // The constant’s value must be able to be evaluated at compile time:
- X :: "what"; // constant `X` has the untyped string value "what"
-
- // Constants can be explicitly typed like a variable declaration:
- Y : int : 123;
- Z :: Y + 7; // constant computations are possible
- }
-}
-
-control_flow :: proc() {
- fmt.println("\n# control flow");
- { // Control flow
- // For loop
- // Odin has only one loop statement, the `for` loop
-
- // Basic for loop
- for i := 0; i < 10; i += 1 {
- fmt.println(i);
- }
-
- // NOTE: Unlike other languages like C, there are no parentheses `( )` surrounding the three components.
- // Braces `{ }` or a `do` are always required>
- for i := 0; i < 10; i += 1 { }
- for i := 0; i < 10; i += 1 do fmt.print();
-
- // The initial and post statements are optional
- i := 0;
- for ; i < 10; {
- i += 1;
- }
-
- // These semicolons can be dropped. This `for` loop is equivalent to C's `while` loop
- i = 0;
- for i < 10 {
- i += 1;
- }
-
- // If the condition is omitted, this produces an infinite loop:
- for {
- break;
- }
-
- // Range-based for loop
- // The basic for loop
- for i := 0; i < 10; i += 1 {
- fmt.println(i);
- }
- // can also be written
- for i in 0..<10 {
- fmt.println(i);
- }
- for i in 0..9 {
- fmt.println(i);
- }
-
- // Certain built-in types can be iterated over
- some_string := "Hello, 世界";
- for character in some_string { // Strings are assumed to be UTF-8
- fmt.println(character);
- }
-
- some_array := [3]int{1, 4, 9};
- for value in some_array {
- fmt.println(value);
- }
-
- some_slice := []int{1, 4, 9};
- for value in some_slice {
- fmt.println(value);
- }
-
- some_dynamic_array := [dynamic]int{1, 4, 9};
- defer delete(some_dynamic_array);
- for value in some_dynamic_array {
- fmt.println(value);
- }
-
-
- some_map := map[string]int{"A" = 1, "C" = 9, "B" = 4};
- defer delete(some_map);
- for key in some_map {
- fmt.println(key);
- }
-
- // Alternatively a second index value can be added
- for character, index in some_string {
- fmt.println(index, character);
- }
- for value, index in some_array {
- fmt.println(index, value);
- }
- for value, index in some_slice {
- fmt.println(index, value);
- }
- for value, index in some_dynamic_array {
- fmt.println(index, value);
- }
- for key, value in some_map {
- fmt.println(key, value);
- }
-
- // The iterated values are copies and cannot be written to.
- // The following idiom is useful for iterating over a container in a by-reference manner:
- for _, i in some_slice {
- some_slice[i] = (i+1)*(i+1);
- }
-
-
- // If statements
- x := 123;
- if x >= 0 {
- fmt.println("x is positive");
- }
-
- if y := -34; y < 0 {
- fmt.println("y is negative");
- }
-
- if y := 123; y < 0 {
- fmt.println("y is negative");
- } else if y == 0 {
- fmt.println("y is zero");
- } else {
- fmt.println("y is positive");
- }
-
- // Switch statement
- // A switch statement is another way to write a sequence of if-else statements.
- // In Odin, the default case is denoted as a case without any expression.
-
- switch arch := ODIN_ARCH; arch {
- case "386":
- fmt.println("32-bit");
- case "amd64":
- fmt.println("64-bit");
- case: // default
- fmt.println("Unsupported architecture");
- }
-
- // Odin’s `switch` is like one in C or C++, except that Odin only runs the selected case.
- // This means that a `break` statement is not needed at the end of each case.
- // Another important difference is that the case values need not be integers nor constants.
-
- // To achieve a C-like fall through into the next case block, the keyword `fallthrough` can be used.
- one_angry_dwarf :: proc() -> int {
- fmt.println("one_angry_dwarf was called");
- return 1;
- }
-
- switch i := 0; i {
- case 0:
- case one_angry_dwarf():
- }
-
- // A switch statement without a condition is the same as `switch true`.
- // This can be used to write a clean and long if-else chain and have the
- // ability to break if needed
-
- switch {
- case x < 0:
- fmt.println("x is negative");
- case x == 0:
- fmt.println("x is zero");
- case:
- fmt.println("x is positive");
- }
-
- // A `switch` statement can also use ranges like a range-based loop:
- switch c := 'j'; c {
- case 'A'..'Z', 'a'..'z', '0'..'9':
- fmt.println("c is alphanumeric");
- }
-
- switch x {
- case 0..<10:
- fmt.println("units");
- case 10..<13:
- fmt.println("pre-teens");
- case 13..<20:
- fmt.println("teens");
- case 20..<30:
- fmt.println("twenties");
- }
- }
-
- { // Defer statement
- // A defer statement defers the execution of a statement until the end of
- // the scope it is in.
-
- // The following will print 4 then 234:
- {
- x := 123;
- defer fmt.println(x);
- {
- defer x = 4;
- x = 2;
- }
- fmt.println(x);
-
- x = 234;
- }
-
- // You can defer an entire block too:
- {
- bar :: proc() {}
-
- defer {
- fmt.println("1");
- fmt.println("2");
- }
-
- cond := false;
- defer if cond {
- bar();
- }
- }
-
- // Defer statements are executed in the reverse order that they were declared:
- {
- defer fmt.println("1");
- defer fmt.println("2");
- defer fmt.println("3");
- }
- // Will print 3, 2, and then 1.
-
- if false {
- f, err := os.open("my_file.txt");
- if err != 0 {
- // handle error
- }
- defer os.close(f);
- // rest of code
- }
- }
-
- { // When statement
- /*
- The when statement is almost identical to the if statement but with some differences:
-
- * Each condition must be a constant expression as a when
- statement is evaluated at compile time.
- * The statements within a branch do not create a new scope
- * The compiler checks the semantics and code only for statements
- that belong to the first condition that is true
- * An initial statement is not allowed in a when statement
- * when statements are allowed at file scope
- */
-
- // Example
- when ODIN_ARCH == "386" {
- fmt.println("32 bit");
- } else when ODIN_ARCH == "amd64" {
- fmt.println("64 bit");
- } else {
- fmt.println("Unsupported architecture");
- }
- // The when statement is very useful for writing platform specific code.
- // This is akin to the #if construct in C’s preprocessor however, in Odin,
- // it is type checked.
- }
-
- { // Branch statements
- cond, cond1, cond2 := false, false, false;
- one_step :: proc() { fmt.println("one_step"); }
- beyond :: proc() { fmt.println("beyond"); }
-
- // Break statement
- for cond {
- switch {
- case:
- if cond {
- break; // break out of the `switch` statement
- }
- }
-
- break; // break out of the `for` statement
- }
-
- loop: for cond1 {
- for cond2 {
- break loop; // leaves both loops
- }
- }
-
- // Continue statement
- for cond {
- if cond2 {
- continue;
- }
- fmt.println("Hellope");
- }
-
- // Fallthrough statement
-
- // Odin’s switch is like one in C or C++, except that Odin only runs the selected
- // case. This means that a break statement is not needed at the end of each case.
- // Another important difference is that the case values need not be integers nor
- // constants.
-
- // fallthrough can be used to explicitly fall through into the next case block:
-
- switch i := 0; i {
- case 0:
- one_step();
- fallthrough;
- case 1:
- beyond();
- }
- }
-}
-
-
-named_proc_return_parameters :: proc() {
- fmt.println("\n# 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
-}
-
-
-explicit_procedure_overloading :: proc() {
- fmt.println("\n# 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);
-}
-
-struct_type :: proc() {
- fmt.println("\n# struct type");
- // A struct is a record type in Odin. It is a collection of fields.
- // Struct fields are accessed by using a dot:
- {
- Vector2 :: struct {
- x: f32,
- y: f32,
- };
- v := Vector2{1, 2};
- v.x = 4;
- fmt.println(v.x);
-
- // Struct fields can be accessed through a struct pointer:
-
- v = Vector2{1, 2};
- p := &v;
- p.x = 1335;
- fmt.println(v);
-
- // We could write p^.x, however, it is to nice abstract the ability
- // to not explicitly dereference the pointer. This is very useful when
- // refactoring code to use a pointer rather than a value, and vice versa.
- }
- {
- // A struct literal can be denoted by providing the struct’s type
- // followed by {}. A struct literal must either provide all the
- // arguments or none:
- Vector3 :: struct {
- x, y, z: f32,
- };
- v: Vector3;
- v = Vector3{}; // Zero value
- v = Vector3{1, 4, 9};
-
- // You can list just a subset of the fields if you specify the
- // field by name (the order of the named fields does not matter):
- v = Vector3{z=1, y=2};
- assert(v.x == 0);
- assert(v.y == 2);
- assert(v.z == 1);
- }
- {
- // Structs can tagged with different memory layout and alignment requirements:
-
- a :: struct #align 4 {}; // align to 4 bytes
- b :: struct #packed {}; // remove padding between fields
- c :: struct #raw_union {}; // all fields share the same offset (0). This is the same as C's union
- }
-
-}
-
-
-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 :: distinct [3]f32;
- Quaternion :: distinct quaternion128;
-
- // 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: typeid) -> ^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!");
- fmt.println("I'm a monster");
- }
- }
-
- {
- // 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: typeid) -> ^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;
- }
- */
- }
-}
-
-using_statement :: proc() {
- fmt.println("\n# using statement");
- // using can used to bring entities declared in a scope/namespace
- // into the current scope. This can be applied to import declarations,
- // import names, struct fields, procedure fields, and struct values.
-
- Vector3 :: struct{x, y, z: f32};
- {
- Entity :: struct {
- position: Vector3,
- orientation: quaternion128,
- };
-
- // It can used like this:
- foo0 :: proc(entity: ^Entity) {
- fmt.println(entity.position.x, entity.position.y, entity.position.z);
- }
-
- // The entity members can be brought into the procedure scope by using it:
- foo1 :: proc(entity: ^Entity) {
- using entity;
- fmt.println(position.x, position.y, position.z);
- }
-
- // The using can be applied to the parameter directly:
- foo2 :: proc(using entity: ^Entity) {
- fmt.println(position.x, position.y, position.z);
- }
-
- // It can also be applied to sub-fields:
- foo3 :: proc(entity: ^Entity) {
- using entity.position;
- fmt.println(x, y, z);
- }
- }
- {
- // We can also apply the using statement to the struct fields directly,
- // making all the fields of position appear as if they on Entity itself:
- Entity :: struct {
- using position: Vector3,
- orientation: quaternion128,
- };
- foo :: proc(entity: ^Entity) {
- fmt.println(entity.x, entity.y, entity.z);
- }
-
-
- // Subtype polymorphism
- // It is possible to get subtype polymorphism, similar to inheritance-like
- // functionality in C++, but without the requirement of vtables or unknown
- // struct layout:
-
- Colour :: struct {r, g, b, a: u8};
- Frog :: struct {
- ribbit_volume: f32,
- using entity: Entity,
- colour: Colour,
- };
-
- frog: Frog;
- // Both work
- foo(&frog.entity);
- foo(&frog);
- frog.x = 123;
-
- // Note: using can be applied to arbitrarily many things, which allows
- // the ability to have multiple subtype polymorphism (but also its issues).
-
- // Note: using’d fields can still be referred by name.
- }
- { // using on an enum declaration
-
- using Foo :: enum {A, B, C};
-
- f0 := A;
- f1 := B;
- f2 := C;
- fmt.println(f0, f1, f2);
- fmt.println(len(Foo));
- }
-}
-
-
-implicit_context_system :: proc() {
- fmt.println("\n# implicit context system");
- // In each scope, there is an implicit value named context. This
- // context variable is local to each scope and is implicitly passed
- // by pointer to any procedure call in that scope (if the procedure
- // has the Odin calling convention).
-
- // The main purpose of the implicit context system is for the ability
- // to intercept third-party code and libraries and modify their
- // functionality. One such case is modifying how a library allocates
- // something or logs something. In C, this was usually achieved with
- // the library defining macros which could be overridden so that the
- // user could define what he wanted. However, not many libraries
- // supported this in many languages by default which meant intercepting
- // third-party code to see what it does and to change how it does it is
- // not possible.
-
- c := context; // copy the current scope's context
-
- context.user_index = 456;
- {
- context.allocator = my_custom_allocator();
- context.user_index = 123;
- what_a_fool_believes(); // the `context` for this scope is implicitly passed to `what_a_fool_believes`
- }
-
- // `context` value is local to the scope it is in
- assert(context.user_index == 456);
-
- what_a_fool_believes :: proc() {
- c := context; // this `context` is the same as the parent procedure that it was called from
- // From this example, context.user_index == 123
- // An context.allocator is assigned to the return value of `my_custom_allocator()`
- assert(context.user_index == 123);
-
- // The memory management procedure use the `context.allocator` by
- // default unless explicitly specified otherwise
- china_grove := new(int);
- free(china_grove);
- }
-
- my_custom_allocator :: mem.nil_allocator;
-
- // By default, the context value has default values for its parameters which is
- // decided in the package runtime. What the defaults are are compiler specific.
-
- // To see what the implicit context value contains, please see the following
- // definition in package runtime.
-}
-
-parametric_polymorphism :: proc() {
- fmt.println("\n# 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: typeid) -> ^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: typeid) {
- occupied: bool,
- hash: u32,
- key: Key,
- value: Value,
- };
- TABLE_SIZE_MIN :: 32;
- Table :: struct(Key, Value: typeid) {
- count: int,
- allocator: mem.Allocator,
- slots: []Table_Slot(Key, Value),
- };
-
- // Only allow types that are specializations of a (polymorphic) slice
- make_slice :: proc($T: typeid/[]$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;
- defer delete(old_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);
- }
- }
-
- // 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));
-
- 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.. (res: [N]T) {
- // `N` is the constant value passed
- // `I` is the type of N
- // `T` is the type passed
- fmt.printf("Generating an array of type %v from the value %v of type %v\n",
- typeid_of(type_of(res)), N, typeid_of(I));
- for i in 0.. (c: [M][P]T) {
- for i in 0.. 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));
- }
-}
-
-map_type :: proc() {
- fmt.println("\n# map type");
-
- m := make(map[string]int);
- defer delete(m);
-
- m["Bob"] = 2;
- m["Ted"] = 5;
- fmt.println(m["Bob"]);
-
- delete_key(&m, "Ted");
-
- // If an element of a key does not exist, the zero value of the
- // element will be returned. To check to see if an element exists
- // can be done in two ways:
- elem, ok := m["Bob"];
- exists := "Bob" in m;
-
-}
-
-implicit_selector_expression :: proc() {
- fmt.println("\n# implicit selector expression");
-
- Foo :: enum {A, B, C};
-
- f: Foo;
- f = Foo.A;
- f = .A;
-
- BAR :: bit_set[Foo]{.B, .C};
-
- switch f {
- case .A:
- fmt.println("HERE");
- case .B:
- fmt.println("NEVER");
- case .C:
- fmt.println("FOREVER");
- }
-
- my_map := make(map[Foo]int);
- defer delete(my_map);
-
- my_map[.A] = 123;
- my_map[Foo.B] = 345;
-
- fmt.println(my_map[.A] + my_map[Foo.B] + my_map[.C]);
-}
-
-
-complete_switch :: proc() {
- fmt.println("\n# complete_switch");
- { // enum
- Foo :: enum {
- A,
- B,
- C,
- D,
- };
-
- 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:
- }
- }
-}
-
-cstring_example :: proc() {
- fmt.println("\n# cstring_example");
-
- W :: "Hellope";
- X :: cstring(W);
- Y :: string(X);
-
- w := W;
- _ = w;
- x: cstring = X;
- y: string = Y;
- z := string(x);
- fmt.println(x, y, z);
- fmt.println(len(x), len(y), len(z));
- fmt.println(len(W), len(X), len(Y));
- // IMPORTANT NOTE for cstring variables
- // len(cstring) is O(N)
- // cast(string)cstring is O(N)
-}
-
-bit_set_type :: proc() {
- fmt.println("\n# bit_set type");
-
- {
- using Day :: enum {
- Sunday,
- Monday,
- Tuesday,
- Wednesday,
- Thursday,
- Friday,
- Saturday,
- };
-
- Days :: distinct bit_set[Day];
- WEEKEND :: Days{Sunday, Saturday};
-
- d: Days;
- d = {Sunday, Monday};
- e := d | WEEKEND;
- e |= {Monday};
- fmt.println(d, e);
-
- ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types
- fmt.println(ok);
- if Saturday in e {
- fmt.println("Saturday in", e);
- }
- X :: Saturday in WEEKEND; // Constant evaluation
- fmt.println(X);
- fmt.println("Cardinality:", card(e));
- }
- {
- x: bit_set['A'..'Z'];
- #assert(size_of(x) == size_of(u32));
- y: bit_set[0..8; u16];
- fmt.println(typeid_of(type_of(x))); // bit_set[A..Z]
- fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16]
-
- incl(&x, 'F');
- assert('F' in x);
- excl(&x, 'F');
- assert('F' not_in x);
-
- y |= {1, 4, 2};
- assert(2 in y);
- }
- {
- Letters :: bit_set['A'..'Z'];
- a := Letters{'A', 'B'};
- b := Letters{'A', 'B', 'C', 'D', 'F'};
- c := Letters{'A', 'B'};
-
- assert(a <= b); // 'a' is a subset of 'b'
- assert(b >= a); // 'b' is a superset of 'a'
- assert(a < b); // 'a' is a strict subset of 'b'
- assert(b > a); // 'b' is a strict superset of 'a'
-
- assert(!(a < c)); // 'a' is a not strict subset of 'c'
- assert(!(c > a)); // 'c' is a not strict superset of 'a'
- }
-}
-
-deferred_procedure_associations :: proc() {
- fmt.println("\n# deferred procedure associations");
-
- @(deferred_out=closure)
- open :: proc(s: string) -> bool {
- fmt.println(s);
- return true;
- }
-
- closure :: proc(ok: bool) {
- fmt.println("Goodbye?", ok);
- }
-
- if open("Welcome") {
- fmt.println("Something in the middle, mate.");
- }
-}
-
-reflection :: proc() {
- fmt.println("\n# reflection");
-
- Foo :: struct {
- x: int `tag1`,
- y: string `json:"y_field"`,
- z: bool, // no tag
- };
-
- id := typeid_of(Foo);
- names := reflect.struct_field_names(id);
- types := reflect.struct_field_types(id);
- tags := reflect.struct_field_tags(id);
-
- assert(len(names) == len(types) && len(names) == len(tags));
-
- fmt.println("Foo :: struct {");
- for tag, i in tags {
- name, type := names[i], types[i];
- if tag != "" {
- fmt.printf("\t%s: %T `%s`,\n", name, type, tag);
- } else {
- fmt.printf("\t%s: %T,\n", name, type);
- }
- }
- fmt.println("}");
-
-
- for tag, i in tags {
- if val, ok := reflect.struct_tag_lookup(tag, "json"); ok {
- fmt.printf("json: %s -> %s\n", names[i], val);
- }
- }
-}
-
-quaternions :: proc() {
- // Not just an April Fool's Joke any more, but a fully working thing!
- fmt.println("\n# quaternions");
-
- { // Quaternion operations
- q := 1 + 2i + 3j + 4k;
- r := quaternion(5, 6, 7, 8);
- t := q * r;
- fmt.printf("(%v) * (%v) = %v\n", q, r, t);
- v := q / r;
- fmt.printf("(%v) / (%v) = %v\n", q, r, v);
- u := q + r;
- fmt.printf("(%v) + (%v) = %v\n", q, r, u);
- s := q - r;
- fmt.printf("(%v) - (%v) = %v\n", q, r, s);
- }
- { // The quaternion types
- q128: quaternion128; // 4xf32
- q256: quaternion256; // 4xf64
- q128 = quaternion(1, 0, 0, 0);
- q256 = 1; // quaternion(1, 0, 0, 0);
- }
- { // Built-in procedures
- q := 1 + 2i + 3j + 4k;
- fmt.println("q =", q);
- fmt.println("real(q) =", real(q));
- fmt.println("imag(q) =", imag(q));
- fmt.println("jmag(q) =", jmag(q));
- fmt.println("kmag(q) =", kmag(q));
- fmt.println("conj(q) =", conj(q));
- fmt.println("abs(q) =", abs(q));
- }
- { // Conversion of a complex type to a quaternion type
- c := 1 + 2i;
- q := quaternion256(c);
- fmt.println(c);
- fmt.println(q);
- }
- { // Memory layout of Quaternions
- q := 1 + 2i + 3j + 4k;
- a := transmute([4]f64)q;
- fmt.println("Quaternion memory layout: xyzw/(ijkr)");
- fmt.println(q); // 1.000+2.000i+3.000j+4.000k
- fmt.println(a); // [2.000, 3.000, 4.000, 1.000]
- }
-}
-
-inline_for_statement :: proc() {
- fmt.println("\n#inline for statements");
-
- // 'inline for' works the same as if the 'inline' prefix did not
- // exist but these ranged loops are explicitly unrolled which can
- // be very very useful for certain optimizations
-
- fmt.println("Ranges");
- inline for x, i in 1..<4 {
- fmt.println(x, i);
- }
-
- fmt.println("Strings");
- inline for r, i in "Hello, 世界" {
- fmt.println(r, i);
- }
-
- fmt.println("Arrays");
- inline for elem, idx in ([4]int{1, 4, 9, 16}) {
- fmt.println(elem, idx);
- }
-
-
- Foo_Enum :: enum {
- A = 1,
- B,
- C = 6,
- D,
- };
- fmt.println("Enum types");
- inline for elem, idx in Foo_Enum {
- fmt.println(elem, idx);
- }
-}
-
-where_clauses :: proc() {
- fmt.println("\n#procedure 'where' clauses");
-
- { // Sanity checks
- simple_sanity_check :: proc(x: [2]int)
- where len(x) > 1,
- type_of(x) == [2]int {
- fmt.println(x);
- }
- }
- { // Parametric polymorphism checks
- cross_2d :: proc(a, b: $T/[2]$E) -> E
- where intrinsics.type_is_numeric(E) {
- return a.x*b.y - a.y*b.x;
- }
- cross_3d :: proc(a, b: $T/[3]$E) -> T
- where intrinsics.type_is_numeric(E) {
- x := a.y*b.z - a.z*b.y;
- y := a.z*b.x - a.x*b.z;
- z := a.x*b.y - a.y*b.z;
- return T{x, y, z};
- }
-
- a := [2]int{1, 2};
- b := [2]int{5, -3};
- fmt.println(cross_2d(a, b));
-
- x := [3]f32{1, 4, 9};
- y := [3]f32{-5, 0, 3};
- fmt.println(cross_3d(x, y));
-
- // Failure case
- // i := [2]bool{true, false};
- // j := [2]bool{false, true};
- // fmt.println(cross_2d(i, j));
-
- }
-
- { // Procedure groups usage
- foo :: proc(x: [$N]int) -> bool
- where N > 2 {
- fmt.println(#procedure, "was called with the parameter", x);
- return true;
- }
-
- bar :: proc(x: [$N]int) -> bool
- where 0 < N,
- N <= 2 {
- fmt.println(#procedure, "was called with the parameter", x);
- return false;
- }
-
- baz :: proc{foo, bar};
-
- x := [3]int{1, 2, 3};
- y := [2]int{4, 9};
- ok_x := baz(x);
- ok_y := baz(y);
- assert(ok_x == true);
- assert(ok_y == false);
- }
-
- { // Record types
- Foo :: struct(T: typeid, N: int)
- where intrinsics.type_is_integer(T),
- N > 2 {
- x: [N]T,
- y: [N-2]T,
- };
-
- T :: i32;
- N :: 5;
- f: Foo(T, N);
- #assert(size_of(f) == (N+N-2)*size_of(T));
- }
-}
-
-
-when ODIN_OS == "windows" do foreign import kernel32 "system:kernel32.lib"
-
-foreign_system :: proc() {
- fmt.println("\n#foreign system");
- when ODIN_OS == "windows" {
- // It is sometimes necessarily to interface with foreign code,
- // such as a C library. In Odin, this is achieved through the
- // foreign system. You can “import” a library into the code
- // using the same semantics as a normal import declaration.
-
- // This foreign import declaration will create a
- // “foreign import name” which can then be used to associate
- // entities within a foreign block.
-
- foreign kernel32 {
- ExitProcess :: proc "stdcall" (exit_code: u32) ---
- }
-
- // Foreign procedure declarations have the cdecl/c calling
- // convention by default unless specified otherwise. Due to
- // foreign procedures do not have a body declared within this
- // code, you need append the --- symbol to the end to distinguish
- // it as a procedure literal without a body and not a procedure type.
-
- // The attributes system can be used to change specific properties
- // of entities declared within a block:
-
- @(default_calling_convention = "std")
- foreign kernel32 {
- @(link_name="GetLastError") get_last_error :: proc() -> i32 ---
- }
-
- // Example using the link_prefix attribute
- @(default_calling_convention = "std")
- @(link_prefix = "Get")
- foreign kernel32 {
- LastError :: proc() -> i32 ---
- }
- }
-}
-
-ranged_fields_for_array_compound_literals :: proc() {
- fmt.println("\n#ranged fields for array compound literals");
- { // Normal Array Literal
- foo := [?]int{1, 4, 9, 16};
- fmt.println(foo);
- }
- { // Indexed
- foo := [?]int{
- 3 = 16,
- 1 = 4,
- 2 = 9,
- 0 = 1,
- };
- fmt.println(foo);
- }
- { // Ranges
- i := 2;
- foo := [?]int {
- 0 = 123,
- 5..9 = 54,
- 10..<16 = i*3 + (i-1)*2,
- };
- #assert(len(foo) == 16);
- fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
- }
- { // Slice and Dynamic Array support
- i := 2;
- foo_slice := []int {
- 0 = 123,
- 5..9 = 54,
- 10..<16 = i*3 + (i-1)*2,
- };
- assert(len(foo_slice) == 16);
- fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
-
- foo_dynamic_array := [dynamic]int {
- 0 = 123,
- 5..9 = 54,
- 10..<16 = i*3 + (i-1)*2,
- };
- assert(len(foo_dynamic_array) == 16);
- fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8]
- }
-}
-
-deprecated_attribute :: proc() {
- @(deprecated="Use foo_v2 instead")
- foo_v1 :: proc(x: int) {
- fmt.println("foo_v1");
- }
- foo_v2 :: proc(x: int) {
- fmt.println("foo_v2");
- }
-
- // NOTE: Uncomment to see the warning messages
- // foo_v1(1);
-}
-
-range_statements_with_multiple_return_values :: proc() {
- // IMPORTANT NOTE(bill, 2019-11-02): This feature is subject to be changed/removed
- fmt.println("\n#range statements with multiple return values");
- My_Iterator :: struct {
- index: int,
- data: []i32,
- };
- make_my_iterator :: proc(data: []i32) -> My_Iterator {
- return My_Iterator{data = data};
- }
- my_iterator :: proc(it: ^My_Iterator) -> (val: i32, idx: int, cond: bool) {
- if cond = it.index < len(it.data); cond {
- val = it.data[it.index];
- idx = it.index;
- it.index += 1;
- }
- return;
- }
-
- data := make([]i32, 6);
- for _, i in data {
- data[i] = i32(i*i);
- }
-
- {
- it := make_my_iterator(data);
- for val in my_iterator(&it) {
- fmt.println(val);
- }
- }
- {
- it := make_my_iterator(data);
- for val, idx in my_iterator(&it) {
- fmt.println(val, idx);
- }
- }
- {
- it := make_my_iterator(data);
- for {
- val, _, cond := my_iterator(&it);
- if !cond do break;
- fmt.println(val);
- }
- }
-}
-
-soa_struct_layout :: proc() {
- // IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
- // NOTE(bill): Most likely #soa [N]T
- fmt.println("\n#SOA Struct Layout");
-
- {
- Vector3 :: struct {x, y, z: f32};
-
- N :: 2;
- v_aos: [N]Vector3;
- v_aos[0].x = 1;
- v_aos[0].y = 4;
- v_aos[0].z = 9;
-
- fmt.println(len(v_aos));
- fmt.println(v_aos[0]);
- fmt.println(v_aos[0].x);
- fmt.println(&v_aos[0].x);
-
- v_aos[1] = {0, 3, 4};
- v_aos[1].x = 2;
- fmt.println(v_aos[1]);
- fmt.println(v_aos);
-
- v_soa: #soa[N]Vector3;
-
- v_soa[0].x = 1;
- v_soa[0].y = 4;
- v_soa[0].z = 9;
-
-
- // Same syntax as AOS and treat as if it was an array
- fmt.println(len(v_soa));
- fmt.println(v_soa[0]);
- fmt.println(v_soa[0].x);
- v_soa[1] = {0, 3, 4};
- v_soa[1].x = 2;
- fmt.println(v_soa[1]);
-
- // Can use SOA syntax if necessary
- v_soa.x[0] = 1;
- v_soa.y[0] = 4;
- v_soa.z[0] = 9;
- fmt.println(v_soa.x[0]);
-
- // Same pointer addresses with both syntaxes
- assert(&v_soa[0].x == &v_soa.x[0]);
-
-
- // Same fmt printing
- fmt.println(v_aos);
- fmt.println(v_soa);
- }
- {
- // Works with arrays of length <= 4 which have the implicit fields xyzw/rgba
- Vector3 :: distinct [3]f32;
-
- N :: 2;
- v_aos: [N]Vector3;
- v_aos[0].x = 1;
- v_aos[0].y = 4;
- v_aos[0].z = 9;
-
- v_soa: #soa[N]Vector3;
-
- v_soa[0].x = 1;
- v_soa[0].y = 4;
- v_soa[0].z = 9;
- }
-}
-
-
-main :: proc() {
- when true {
- the_basics();
- control_flow();
- named_proc_return_parameters();
- explicit_procedure_overloading();
- struct_type();
- union_type();
- using_statement();
- implicit_context_system();
- parametric_polymorphism();
- array_programming();
- map_type();
- implicit_selector_expression();
- complete_switch();
- cstring_example();
- bit_set_type();
- deferred_procedure_associations();
- reflection();
- quaternions();
- inline_for_statement();
- where_clauses();
- foreign_system();
- ranged_fields_for_array_compound_literals();
- deprecated_attribute();
- range_statements_with_multiple_return_values();
- soa_struct_layout();
- }
-}
-
diff --git a/src/array.cpp b/src/array.cpp
index c41125c6d..d08bd647f 100644
--- a/src/array.cpp
+++ b/src/array.cpp
@@ -77,15 +77,21 @@ template Slice slice_from_array(Array const &a);
template
Slice slice_make(gbAllocator const &allocator, isize count) {
+ GB_ASSERT(count >= 0);
Slice s = {};
s.data = gb_alloc_array(allocator, T, count);
+ GB_ASSERT(s.data != nullptr);
s.count = count;
return s;
}
template
void slice_init(Slice *s, gbAllocator const &allocator, isize count) {
+ GB_ASSERT(count >= 0);
s->data = gb_alloc_array(allocator, T, count);
+ if (count > 0) {
+ GB_ASSERT(s->data != nullptr);
+ }
s->count = count;
}
diff --git a/src/bug_report.cpp b/src/bug_report.cpp
index 27e7fcf9a..02a2b1ba2 100644
--- a/src/bug_report.cpp
+++ b/src/bug_report.cpp
@@ -17,6 +17,11 @@
#include
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+ #include
+ #include
+#endif
+
/*
NOTE(Jeroen): This prints the Windows product edition only, to be called from `print_platform_details`.
*/
@@ -140,7 +145,7 @@ void report_windows_product_type(DWORD ProductType) {
break;
default:
- gb_printf("Unknown Edition (%08x)", ProductType);
+ gb_printf("Unknown Edition (%08x)", cast(unsigned)ProductType);
}
}
#endif
@@ -242,6 +247,14 @@ void report_ram_info() {
if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
}
+ #elif defined(GB_SYSTEM_OPENBSD)
+ uint64_t ram_amount;
+ size_t val_size = sizeof(ram_amount);
+
+ int sysctls[] = { CTL_HW, HW_PHYSMEM64 };
+ if (sysctl(sysctls, 2, &ram_amount, &val_size, NULL, 0) != -1) {
+ gb_printf("%lld MiB\n", ram_amount / gb_megabytes(1));
+ }
#else
gb_printf("Unknown.\n");
#endif
@@ -316,14 +329,14 @@ void print_bug_report_help() {
}
if (false) {
- gb_printf("dwMajorVersion: %d\n", osvi.dwMajorVersion);
- gb_printf("dwMinorVersion: %d\n", osvi.dwMinorVersion);
- gb_printf("dwBuildNumber: %d\n", osvi.dwBuildNumber);
- gb_printf("dwPlatformId: %d\n", osvi.dwPlatformId);
- gb_printf("wServicePackMajor: %d\n", osvi.wServicePackMajor);
- gb_printf("wServicePackMinor: %d\n", osvi.wServicePackMinor);
- gb_printf("wSuiteMask: %d\n", osvi.wSuiteMask);
- gb_printf("wProductType: %d\n", osvi.wProductType);
+ gb_printf("dwMajorVersion: %u\n", cast(unsigned)osvi.dwMajorVersion);
+ gb_printf("dwMinorVersion: %u\n", cast(unsigned)osvi.dwMinorVersion);
+ gb_printf("dwBuildNumber: %u\n", cast(unsigned)osvi.dwBuildNumber);
+ gb_printf("dwPlatformId: %u\n", cast(unsigned)osvi.dwPlatformId);
+ gb_printf("wServicePackMajor: %u\n", cast(unsigned)osvi.wServicePackMajor);
+ gb_printf("wServicePackMinor: %u\n", cast(unsigned)osvi.wServicePackMinor);
+ gb_printf("wSuiteMask: %u\n", cast(unsigned)osvi.wSuiteMask);
+ gb_printf("wProductType: %u\n", cast(unsigned)osvi.wProductType);
}
gb_printf("Windows ");
@@ -441,18 +454,18 @@ void print_bug_report_help() {
TEXT("DisplayVersion"),
RRF_RT_REG_SZ,
ValueType,
- &DisplayVersion,
+ DisplayVersion,
&ValueSize
);
if (status == 0x0) {
- gb_printf(" (version: %s)", &DisplayVersion);
+ gb_printf(" (version: %s)", DisplayVersion);
}
/*
Now print build number.
*/
- gb_printf(", build %d", osvi.dwBuildNumber);
+ gb_printf(", build %u", cast(unsigned)osvi.dwBuildNumber);
ValueSize = sizeof(UBR);
status = RegGetValue(
@@ -466,18 +479,18 @@ void print_bug_report_help() {
);
if (status == 0x0) {
- gb_printf(".%d", UBR);
+ gb_printf(".%u", cast(unsigned)UBR);
}
gb_printf("\n");
}
#elif defined(GB_SYSTEM_LINUX)
/*
- Try to parse `/usr/lib/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
+ Try to parse `/etc/os-release` for `PRETTY_NAME="Ubuntu 20.04.3 LTS`
*/
gbAllocator a = heap_allocator();
- gbFileContents release = gb_file_read_contents(a, 1, "/usr/lib/os-release");
+ gbFileContents release = gb_file_read_contents(a, 1, "/etc/os-release");
defer (gb_file_free_contents(&release));
b32 found = 0;
@@ -643,6 +656,14 @@ void print_bug_report_help() {
} else {
gb_printf("macOS: Unknown\n");
}
+ #elif defined(GB_SYSTEM_OPENBSD)
+ struct utsname un;
+
+ if (uname(&un) != -1) {
+ gb_printf("%s %s %s %s\n", un.sysname, un.release, un.version, un.machine);
+ } else {
+ gb_printf("OpenBSD: Unknown\n");
+ }
#else
gb_printf("Unknown\n");
@@ -657,4 +678,4 @@ void print_bug_report_help() {
And RAM info.
*/
report_ram_info();
-}
\ No newline at end of file
+}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 29abd441c..89d370144 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -1,14 +1,13 @@
-#if defined(GB_SYSTEM_FREEBSD)
+#if defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
#include
#include
#endif
-
// #if defined(GB_SYSTEM_WINDOWS)
-#define DEFAULT_TO_THREADED_CHECKER
+// #define DEFAULT_TO_THREADED_CHECKER
// #endif
-enum TargetOsKind {
+enum TargetOsKind : u16 {
TargetOs_Invalid,
TargetOs_windows,
@@ -16,6 +15,7 @@ enum TargetOsKind {
TargetOs_linux,
TargetOs_essence,
TargetOs_freebsd,
+ TargetOs_openbsd,
TargetOs_wasi,
TargetOs_js,
@@ -25,11 +25,11 @@ enum TargetOsKind {
TargetOs_COUNT,
};
-enum TargetArchKind {
+enum TargetArchKind : u16 {
TargetArch_Invalid,
TargetArch_amd64,
- TargetArch_386,
+ TargetArch_i386,
TargetArch_arm64,
TargetArch_wasm32,
TargetArch_wasm64,
@@ -37,7 +37,7 @@ enum TargetArchKind {
TargetArch_COUNT,
};
-enum TargetEndianKind {
+enum TargetEndianKind : u8 {
TargetEndian_Invalid,
TargetEndian_Little,
@@ -46,6 +46,16 @@ enum TargetEndianKind {
TargetEndian_COUNT,
};
+enum TargetABIKind : u16 {
+ TargetABI_Default,
+
+ TargetABI_Win64,
+ TargetABI_SysV,
+
+ TargetABI_COUNT,
+};
+
+
String target_os_names[TargetOs_COUNT] = {
str_lit(""),
str_lit("windows"),
@@ -53,6 +63,7 @@ String target_os_names[TargetOs_COUNT] = {
str_lit("linux"),
str_lit("essence"),
str_lit("freebsd"),
+ str_lit("openbsd"),
str_lit("wasi"),
str_lit("js"),
@@ -63,7 +74,7 @@ String target_os_names[TargetOs_COUNT] = {
String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
- str_lit("386"),
+ str_lit("i386"),
str_lit("arm64"),
str_lit("wasm32"),
str_lit("wasm64"),
@@ -75,6 +86,12 @@ String target_endian_names[TargetEndian_COUNT] = {
str_lit("big"),
};
+String target_abi_names[TargetABI_COUNT] = {
+ str_lit(""),
+ str_lit("win64"),
+ str_lit("sysv"),
+};
+
TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Invalid,
TargetEndian_Little,
@@ -98,6 +115,7 @@ struct TargetMetrics {
isize max_align;
String target_triplet;
String target_data_layout;
+ TargetABIKind abi;
};
@@ -119,6 +137,8 @@ enum BuildModeKind {
BuildMode_Object,
BuildMode_Assembly,
BuildMode_LLVM_IR,
+
+ BuildMode_COUNT,
};
enum CommandKind : u32 {
@@ -163,19 +183,50 @@ enum TimingsExportFormat : i32 {
TimingsExportCSV = 2,
};
+enum ErrorPosStyle {
+ ErrorPosStyle_Default, // path(line:column) msg
+ ErrorPosStyle_Unix, // path:line:column: msg
+
+ ErrorPosStyle_COUNT
+};
+
+enum RelocMode : u8 {
+ RelocMode_Default,
+ RelocMode_Static,
+ RelocMode_PIC,
+ RelocMode_DynamicNoPIC,
+};
+
+enum BuildPath : u8 {
+ BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
+ BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
+ BuildPath_RES, // Output Path for .res file, generated from previous.
+ BuildPath_Win_SDK_Root, // windows_sdk_root
+ BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
+ BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
+ BuildPath_VS_EXE, // vs_exe_path
+ BuildPath_VS_LIB, // vs_library_path
+
+ BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
+ BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
+
+ BuildPathCOUNT,
+};
+
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
- String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
- String ODIN_BUILD_MODE;
bool ODIN_DEBUG; // Odin in debug mode
bool ODIN_DISABLE_ASSERT; // Whether the default 'assert' et al is disabled in code or not
bool ODIN_DEFAULT_TO_NIL_ALLOCATOR; // Whether the default allocator is a "nil" allocator or not (i.e. it does nothing)
+ bool ODIN_FOREIGN_ERROR_PROCEDURES;
+
+ ErrorPosStyle ODIN_ERROR_POS_STYLE;
TargetEndianKind endian_kind;
@@ -190,14 +241,19 @@ struct BuildContext {
bool show_help;
+ Array build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
+ // BuildPath enum contains the indices of paths we know *before* the work starts.
+
String out_filepath;
String resource_filepath;
String pdb_filepath;
+
bool has_resource;
String link_flags;
String extra_linker_flags;
String extra_assembler_flags;
String microarch;
+ String target_features;
BuildModeKind build_mode;
bool generate_docs;
i32 optimization_level;
@@ -243,6 +299,12 @@ struct BuildContext {
bool copy_file_contents;
+ bool disallow_rtti;
+
+ RelocMode reloc_mode;
+ bool disable_red_zone;
+
+
u32 cmd_doc_flags;
Array extra_packages;
@@ -254,10 +316,9 @@ struct BuildContext {
isize thread_count;
PtrMap defined_values;
+
};
-
-
gb_global BuildContext build_context = {0};
bool global_warnings_as_errors(void) {
@@ -268,9 +329,9 @@ bool global_ignore_warnings(void) {
}
-gb_global TargetMetrics target_windows_386 = {
+gb_global TargetMetrics target_windows_i386 = {
TargetOs_windows,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-pc-windows-msvc"),
@@ -284,9 +345,9 @@ gb_global TargetMetrics target_windows_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
-gb_global TargetMetrics target_linux_386 = {
+gb_global TargetMetrics target_linux_i386 = {
TargetOs_linux,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-pc-linux-gnu"),
@@ -300,6 +361,14 @@ gb_global TargetMetrics target_linux_amd64 = {
str_lit("x86_64-pc-linux-gnu"),
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_linux_arm64 = {
+ TargetOs_linux,
+ TargetArch_arm64,
+ 8,
+ 16,
+ str_lit("aarch64-linux-elf"),
+ str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"),
+};
gb_global TargetMetrics target_darwin_amd64 = {
TargetOs_darwin,
@@ -319,9 +388,9 @@ gb_global TargetMetrics target_darwin_arm64 = {
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"), // TODO(bill): Is this correct?
};
-gb_global TargetMetrics target_freebsd_386 = {
+gb_global TargetMetrics target_freebsd_i386 = {
TargetOs_freebsd,
- TargetArch_386,
+ TargetArch_i386,
4,
8,
str_lit("i386-unknown-freebsd-elf"),
@@ -336,6 +405,15 @@ gb_global TargetMetrics target_freebsd_amd64 = {
str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
};
+gb_global TargetMetrics target_openbsd_amd64 = {
+ TargetOs_openbsd,
+ TargetArch_amd64,
+ 8,
+ 16,
+ str_lit("x86_64-unknown-openbsd-elf"),
+ str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
+};
+
gb_global TargetMetrics target_essence_amd64 = {
TargetOs_essence,
TargetArch_amd64,
@@ -381,6 +459,16 @@ gb_global TargetMetrics target_wasi_wasm32 = {
// str_lit(""),
// };
+gb_global TargetMetrics target_freestanding_amd64_sysv = {
+ TargetOs_freestanding,
+ TargetArch_amd64,
+ 8,
+ 16,
+ str_lit("x86_64-pc-none-gnu"),
+ str_lit("e-m:w-i64:64-f80:128-n8:16:32:64-S128"),
+ TargetABI_SysV,
+};
+
struct NamedTargetMetrics {
@@ -392,16 +480,19 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("darwin_amd64"), &target_darwin_amd64 },
{ str_lit("darwin_arm64"), &target_darwin_arm64 },
{ str_lit("essence_amd64"), &target_essence_amd64 },
- { str_lit("linux_386"), &target_linux_386 },
+ { str_lit("linux_i386"), &target_linux_i386 },
{ str_lit("linux_amd64"), &target_linux_amd64 },
- { str_lit("windows_386"), &target_windows_386 },
+ { str_lit("linux_arm64"), &target_linux_arm64 },
+ { str_lit("windows_i386"), &target_windows_i386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
- { str_lit("freebsd_386"), &target_freebsd_386 },
+ { str_lit("freebsd_i386"), &target_freebsd_i386 },
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
+ { str_lit("openbsd_amd64"), &target_openbsd_amd64 },
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
{ str_lit("js_wasm32"), &target_js_wasm32 },
- // { str_lit("freestanding_wasm64"), &target_freestanding_wasm64 },
+
+ { str_lit("freestanding_amd64_sysv"), &target_freestanding_amd64_sysv },
};
NamedTargetMetrics *selected_target_metrics;
@@ -524,7 +615,6 @@ bool allow_check_foreign_filepath(void) {
return true;
}
-
// TODO(bill): OS dependent versions for the BuildContext
// join_path
// is_dir
@@ -703,10 +793,38 @@ String internal_odin_root_dir(void) {
len = readlink("/proc/curproc/exe", &path_buf[0], path_buf.count);
#elif defined(GB_SYSTEM_DRAGONFLYBSD)
len = readlink("/proc/curproc/file", &path_buf[0], path_buf.count);
-#else
+#elif defined(GB_SYSTEM_LINUX)
len = readlink("/proc/self/exe", &path_buf[0], path_buf.count);
+#elif defined(GB_SYSTEM_OPENBSD)
+ int error;
+ int mib[] = {
+ CTL_KERN,
+ KERN_PROC_ARGS,
+ getpid(),
+ KERN_PROC_ARGV,
+ };
+ // get argv size
+ error = sysctl(mib, 4, NULL, (size_t *) &len, NULL, 0);
+ if (error == -1) {
+ // sysctl error
+ return make_string(nullptr, 0);
+ }
+ // get argv
+ char **argv = (char **)gb_malloc(len);
+ error = sysctl(mib, 4, argv, (size_t *) &len, NULL, 0);
+ if (error == -1) {
+ // sysctl error
+ gb_mfree(argv);
+ return make_string(nullptr, 0);
+ }
+ // copy argv[0] to path_buf
+ len = gb_strlen(argv[0]);
+ if(len < path_buf.count) {
+ gb_memmove(&path_buf[0], argv[0], len);
+ }
+ gb_mfree(argv);
#endif
- if(len == 0) {
+ if(len == 0 || len == -1) {
return make_string(nullptr, 0);
}
if (len < path_buf.count) {
@@ -834,6 +952,21 @@ bool has_asm_extension(String const &path) {
return false;
}
+// temporary
+char *token_pos_to_string(TokenPos const &pos) {
+ gbString s = gb_string_make_reserve(temporary_allocator(), 128);
+ String file = get_file_path_string(pos.file_id);
+ switch (build_context.ODIN_ERROR_POS_STYLE) {
+ default: /*fallthrough*/
+ case ErrorPosStyle_Default:
+ s = gb_string_append_fmt(s, "%.*s(%d:%d)", LIT(file), pos.line, pos.column);
+ break;
+ case ErrorPosStyle_Unix:
+ s = gb_string_append_fmt(s, "%.*s:%d:%d:", LIT(file), pos.line, pos.column);
+ break;
+ }
+ return s;
+}
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -846,25 +979,31 @@ void init_build_context(TargetMetrics *cross_target) {
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = ODIN_VERSION;
bc->ODIN_ROOT = odin_root_dir();
- switch (bc->build_mode) {
- default:
- case BuildMode_Executable:
- bc->ODIN_BUILD_MODE = str_lit("executable");
- break;
- case BuildMode_DynamicLibrary:
- bc->ODIN_BUILD_MODE = str_lit("dynamic");
- break;
- case BuildMode_Object:
- bc->ODIN_BUILD_MODE = str_lit("object");
- break;
- case BuildMode_Assembly:
- bc->ODIN_BUILD_MODE = str_lit("assembly");
- break;
- case BuildMode_LLVM_IR:
- bc->ODIN_BUILD_MODE = str_lit("llvm-ir");
- break;
+
+ {
+ char const *found = gb_get_env("ODIN_ERROR_POS_STYLE", permanent_allocator());
+ if (found) {
+ ErrorPosStyle kind = ErrorPosStyle_Default;
+ String style = make_string_c(found);
+ style = string_trim_whitespace(style);
+ if (style == "" || style == "default" || style == "odin") {
+ kind = ErrorPosStyle_Default;
+ } else if (style == "unix" || style == "gcc" || style == "clang" || style == "llvm") {
+ kind = ErrorPosStyle_Unix;
+ } else {
+ gb_printf_err("Invalid ODIN_ERROR_POS_STYLE: got %.*s\n", LIT(style));
+ gb_printf_err("Valid formats:\n");
+ gb_printf_err("\t\"default\" or \"odin\"\n");
+ gb_printf_err("\t\tpath(line:column) message\n");
+ gb_printf_err("\t\"unix\"\n");
+ gb_printf_err("\t\tpath:line:column: message\n");
+ gb_exit(1);
+ }
+
+ build_context.ODIN_ERROR_POS_STYLE = kind;
+ }
}
-
+
bc->copy_file_contents = true;
TargetMetrics *metrics = nullptr;
@@ -880,18 +1019,22 @@ void init_build_context(TargetMetrics *cross_target) {
#endif
#elif defined(GB_SYSTEM_FREEBSD)
metrics = &target_freebsd_amd64;
+ #elif defined(GB_SYSTEM_OPENBSD)
+ metrics = &target_openbsd_amd64;
+ #elif defined(GB_CPU_ARM)
+ metrics = &target_linux_arm64;
#else
metrics = &target_linux_amd64;
#endif
#else
#if defined(GB_SYSTEM_WINDOWS)
- metrics = &target_windows_386;
+ metrics = &target_windows_i386;
#elif defined(GB_SYSTEM_OSX)
#error "Build Error: Unsupported architecture"
#elif defined(GB_SYSTEM_FREEBSD)
- metrics = &target_freebsd_386;
+ metrics = &target_freebsd_i386;
#else
- metrics = &target_linux_386;
+ metrics = &target_linux_i386;
#endif
#endif
@@ -910,7 +1053,6 @@ void init_build_context(TargetMetrics *cross_target) {
bc->metrics = *metrics;
bc->ODIN_OS = target_os_names[metrics->os];
bc->ODIN_ARCH = target_arch_names[metrics->arch];
- bc->ODIN_ENDIAN = target_endian_names[target_endians[metrics->arch]];
bc->endian_kind = target_endians[metrics->arch];
bc->word_size = metrics->word_size;
bc->max_align = metrics->max_align;
@@ -920,6 +1062,21 @@ void init_build_context(TargetMetrics *cross_target) {
bc->threaded_checker = true;
#endif
+ if (bc->disable_red_zone) {
+ if (!!is_arch_wasm() && bc->metrics.os == TargetOs_freestanding) {
+ gb_printf_err("-disable-red-zone is not support for this target");
+ gb_exit(1);
+ }
+ }
+
+ if (bc->metrics.os == TargetOs_freestanding) {
+ bc->no_entry_point = true;
+ } else {
+ if (bc->disallow_rtti) {
+ gb_printf_err("-disallow-rtti is only allowed on freestanding targets\n");
+ gb_exit(1);
+ }
+ }
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
@@ -937,8 +1094,11 @@ void init_build_context(TargetMetrics *cross_target) {
case TargetOs_freebsd:
bc->link_flags = str_lit("-arch x86-64 ");
break;
+ case TargetOs_openbsd:
+ bc->link_flags = str_lit("-arch x86-64 ");
+ break;
}
- } else if (bc->metrics.arch == TargetArch_386) {
+ } else if (bc->metrics.arch == TargetArch_i386) {
switch (bc->metrics.os) {
case TargetOs_windows:
bc->link_flags = str_lit("/machine:x86 ");
@@ -959,6 +1119,9 @@ void init_build_context(TargetMetrics *cross_target) {
case TargetOs_darwin:
bc->link_flags = str_lit("-arch arm64 ");
break;
+ case TargetOs_linux:
+ bc->link_flags = str_lit("-arch aarch64 ");
+ break;
}
} else if (is_arch_wasm()) {
gbString link_flags = gb_string_make(heap_allocator(), " ");
@@ -968,14 +1131,14 @@ void init_build_context(TargetMetrics *cross_target) {
if (bc->metrics.arch == TargetArch_wasm64) {
link_flags = gb_string_appendc(link_flags, "-mwas64 ");
}
- if (bc->metrics.os == TargetOs_freestanding) {
+ if (bc->no_entry_point) {
link_flags = gb_string_appendc(link_flags, "--no-entry ");
}
bc->link_flags = make_string_c(link_flags);
// Disallow on wasm
- build_context.use_separate_modules = false;
+ bc->use_separate_modules = false;
} else {
gb_printf_err("Compiler Error: Unsupported architecture\n");
gb_exit(1);
@@ -986,3 +1149,191 @@ void init_build_context(TargetMetrics *cross_target) {
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}
+
+#if defined(GB_SYSTEM_WINDOWS)
+// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
+// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
+#include "microsoft_craziness.h"
+#endif
+
+// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
+// We've previously called `parse_build_flags`, so `out_filepath` should be set.
+bool init_build_paths(String init_filename) {
+ gbAllocator ha = heap_allocator();
+ BuildContext *bc = &build_context;
+
+ // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
+ array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
+
+ // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
+ bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
+
+ bool produces_output_file = false;
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ produces_output_file = true;
+ } else if (bc->command_kind & Command__does_build) {
+ produces_output_file = true;
+ }
+
+ if (!produces_output_file) {
+ // Command doesn't produce output files. We're done.
+ return true;
+ }
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (bc->resource_filepath.len > 0) {
+ bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
+ bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
+ }
+
+ if (bc->pdb_filepath.len > 0) {
+ bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
+ }
+
+ if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
+ // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
+ Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
+
+ if (find_result.windows_sdk_version == 0) {
+ gb_printf_err("Windows SDK not found.\n");
+ return false;
+ }
+
+ GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
+ GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
+
+ if (find_result.windows_sdk_root.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
+ }
+
+ if (find_result.windows_sdk_um_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
+ }
+
+ if (find_result.windows_sdk_ucrt_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
+ }
+
+ if (find_result.vs_exe_path.len > 0) {
+ bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
+ }
+
+ if (find_result.vs_library_path.len > 0) {
+ bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
+ }
+
+ gb_free(ha, find_result.windows_sdk_root.text);
+ gb_free(ha, find_result.windows_sdk_um_library_path.text);
+ gb_free(ha, find_result.windows_sdk_ucrt_library_path.text);
+ gb_free(ha, find_result.vs_exe_path.text);
+ gb_free(ha, find_result.vs_library_path.text);
+
+ }
+ #endif
+
+ // All the build targets and OSes.
+ String output_extension;
+
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ output_extension = STR_LIT("odin-doc");
+ } else if (is_arch_wasm()) {
+ output_extension = STR_LIT("wasm");
+ } else if (build_context.build_mode == BuildMode_Executable) {
+ // By default use a .bin executable extension.
+ output_extension = STR_LIT("bin");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("exe");
+ } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+ output_extension = make_string(nullptr, 0);
+ }
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // By default use a .so shared library extension.
+ output_extension = STR_LIT("so");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("dll");
+ } else if (build_context.metrics.os == TargetOs_darwin) {
+ output_extension = STR_LIT("dylib");
+ }
+ } else if (build_context.build_mode == BuildMode_Object) {
+ // By default use a .o object extension.
+ output_extension = STR_LIT("o");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("obj");
+ }
+ } else if (build_context.build_mode == BuildMode_Assembly) {
+ // By default use a .S asm extension.
+ output_extension = STR_LIT("S");
+ } else if (build_context.build_mode == BuildMode_LLVM_IR) {
+ output_extension = STR_LIT("ll");
+ } else {
+ GB_PANIC("Unhandled build mode/target combination.\n");
+ }
+
+ if (bc->out_filepath.len > 0) {
+ bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
+ if (build_context.metrics.os == TargetOs_windows) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ } else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
+ return false;
+ }
+ }
+ } else {
+ Path output_path;
+
+ if (str_eq(init_filename, str_lit("."))) {
+ // We must name the output file after the current directory.
+ debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
+
+ if (last_element.len == 0) {
+ gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ return false;
+ }
+ output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
+ output_path.name = copy_string(ha, last_element);
+
+ } else {
+ // Init filename was not 'current path'.
+ // Contruct the output name from the path elements as usual.
+ String output_name = remove_directory_from_path(init_filename);
+ output_name = remove_extension_from_path(output_name);
+ output_name = copy_string(ha, string_trim_whitespace(output_name));
+ output_path = path_from_string(ha, output_name);
+
+ // Replace extension.
+ if (output_path.ext.len > 0) {
+ gb_free(ha, output_path.ext.text);
+ }
+ }
+ output_path.ext = copy_string(ha, output_extension);
+
+ bc->build_paths[BuildPath_Output] = output_path;
+ }
+
+ // Do we have an extension? We might not if the output filename was supplied.
+ if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
+ bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
+ }
+ }
+
+ // Check if output path is a directory.
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ }
+
+ return true;
+}
\ No newline at end of file
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index f93cf9886..6c7972d45 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -143,6 +143,270 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
}
+bool does_require_msgSend_stret(Type *return_type) {
+ if (return_type == nullptr) {
+ return false;
+ }
+ if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+ i64 struct_limit = type_size_of(t_uintptr) << 1;
+ return type_size_of(return_type) > struct_limit;
+ }
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ return false;
+ }
+
+ // if (build_context.metrics.arch == TargetArch_arm32) {
+ // i64 struct_limit = type_size_of(t_uintptr);
+ // // NOTE(bill): This is technically wrong
+ // return is_type_struct(return_type) && !is_type_raw_union(return_type) && type_size_of(return_type) > struct_limit;
+ // }
+ GB_PANIC("unsupported architecture");
+ return false;
+}
+
+ObjcMsgKind get_objc_proc_kind(Type *return_type) {
+ if (return_type == nullptr) {
+ return ObjcMsg_normal;
+ }
+
+ if (build_context.metrics.arch == TargetArch_i386 || build_context.metrics.arch == TargetArch_amd64) {
+ if (is_type_float(return_type)) {
+ return ObjcMsg_fpret;
+ }
+ if (build_context.metrics.arch == TargetArch_amd64) {
+ if (is_type_complex(return_type)) {
+ // URL: https://github.com/opensource-apple/objc4/blob/cd5e62a5597ea7a31dccef089317abb3a661c154/runtime/message.h#L143-L159
+ return ObjcMsg_fpret;
+ }
+ }
+ }
+ if (build_context.metrics.arch != TargetArch_arm64) {
+ if (does_require_msgSend_stret(return_type)) {
+ return ObjcMsg_stret;
+ }
+ }
+ return ObjcMsg_normal;
+}
+
+void add_objc_proc_type(CheckerContext *c, Ast *call, Type *return_type, Slice param_types) {
+ ObjcMsgKind kind = get_objc_proc_kind(return_type);
+
+ Scope *scope = create_scope(c->info, nullptr);
+
+ // NOTE(bill, 2022-02-08): the backend's ABI handling should handle this correctly, I hope
+ Type *params = alloc_type_tuple();
+ {
+ auto variables = array_make(permanent_allocator(), 0, param_types.count);
+
+ for_array(i, param_types) {
+ Type *type = param_types[i];
+ Entity *param = alloc_entity_param(scope, blank_token, type, false, true);
+ array_add(&variables, param);
+ }
+ params->Tuple.variables = slice_from_array(variables);
+ }
+
+ Type *results = alloc_type_tuple();
+ if (return_type) {
+ auto variables = array_make(permanent_allocator(), 1);
+ results->Tuple.variables = slice_from_array(variables);
+ Entity *param = alloc_entity_param(scope, blank_token, return_type, false, true);
+ results->Tuple.variables[0] = param;
+ }
+
+
+ ObjcMsgData data = {};
+ data.kind = kind;
+ data.proc_type = alloc_type_proc(scope, params, param_types.count, results, results->Tuple.variables.count, false, ProcCC_CDecl);
+
+ mutex_lock(&c->info->objc_types_mutex);
+ map_set(&c->info->objc_msgSend_types, call, data);
+ mutex_unlock(&c->info->objc_types_mutex);
+
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_fpret");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_fp2ret");
+ try_to_add_package_dependency(c, "runtime", "objc_msgSend_stret");
+}
+
+bool is_constant_string(CheckerContext *c, String const &builtin_name, Ast *expr, String *name_) {
+ Operand op = {};
+ check_expr(c, &op, expr);
+ if (op.mode == Addressing_Constant && op.value.kind == ExactValue_String) {
+ if (name_) *name_ = op.value.value_string;
+ return true;
+ }
+ gbString e = expr_to_string(op.expr);
+ gbString t = type_to_string(op.type);
+ error(op.expr, "'%.*s' expected a constant string value, got %s of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+}
+
+bool check_builtin_objc_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
+ String builtin_name = builtin_procs[id].name;
+
+ if (build_context.metrics.os != TargetOs_darwin) {
+ // allow on doc generation (e.g. Metal stuff)
+ if (build_context.command_kind != Command_doc && build_context.command_kind != Command_check) {
+ error(call, "'%.*s' only works on darwin", LIT(builtin_name));
+ }
+ }
+
+
+ ast_node(ce, CallExpr, call);
+ switch (id) {
+ default:
+ GB_PANIC("Implement objective built-in procedure: %.*s", LIT(builtin_name));
+ return false;
+
+ case BuiltinProc_objc_send: {
+ Type *return_type = nullptr;
+
+ Operand rt = {};
+ check_expr_or_type(c, &rt, ce->args[0]);
+ if (rt.mode == Addressing_Type) {
+ return_type = rt.type;
+ } else if (is_operand_nil(rt)) {
+ return_type = nullptr;
+ } else {
+ gbString e = expr_to_string(rt.expr);
+ error(rt.expr, "'%.*s' expected a type or nil to define the return type of the Objective-C call, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->type = return_type;
+ operand->mode = return_type ? Addressing_Value : Addressing_NoValue;
+
+ String class_name = {};
+ String sel_name = {};
+
+ Type *sel_type = t_objc_SEL;
+ Operand self = {};
+ check_expr_or_type(c, &self, ce->args[1]);
+ if (self.mode == Addressing_Type) {
+ if (!is_type_objc_object(self.type)) {
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ if (!has_type_got_objc_class_attribute(self.type)) {
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+
+ sel_type = t_objc_Class;
+ } else if (!is_operand_value(self) || !check_is_assignable_to(c, &self, t_objc_id)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a type or value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else if (!is_type_pointer(self.type)) {
+ gbString e = expr_to_string(self.expr);
+ gbString t = type_to_string(self.type);
+ error(self.expr, "'%.*s' expected a pointer of a value derived from intrinsics.objc_object, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ } else {
+ Type *type = type_deref(self.type);
+ if (!(type->kind == Type_Named &&
+ type->Named.type_name != nullptr &&
+ type->Named.type_name->TypeName.objc_class_name != "")) {
+ gbString t = type_to_string(type);
+ error(self.expr, "'%.*s' expected a named type with the attribute @(obj_class=) , got type %s", LIT(builtin_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ }
+
+
+ if (!is_constant_string(c, builtin_name, ce->args[2], &sel_name)) {
+ return false;
+ }
+
+ isize const arg_offset = 1;
+ auto param_types = slice_make(permanent_allocator(), ce->args.count-arg_offset);
+ param_types[0] = t_objc_id;
+ param_types[1] = sel_type;
+
+ for (isize i = 2+arg_offset; i < ce->args.count; i++) {
+ Operand x = {};
+ check_expr(c, &x, ce->args[i]);
+ param_types[i-arg_offset] = x.type;
+ }
+
+ add_objc_proc_type(c, call, return_type, param_types);
+
+ return true;
+ } break;
+
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ {
+ String sel_name = {};
+ if (!is_constant_string(c, builtin_name, ce->args[0], &sel_name)) {
+ return false;
+ }
+
+ switch (id) {
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_register_selector:
+ operand->type = t_objc_SEL;
+ break;
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_class:
+ operand->type = t_objc_Class;
+ break;
+
+ }
+ operand->mode = Addressing_Value;
+
+ try_to_add_package_dependency(c, "runtime", "objc_lookUpClass");
+ try_to_add_package_dependency(c, "runtime", "sel_registerName");
+ try_to_add_package_dependency(c, "runtime", "objc_allocateClassPair");
+ return true;
+ } break;
+ }
+}
+
+bool check_atomic_memory_order_argument(CheckerContext *c, Ast *expr, String const &builtin_name, OdinAtomicMemoryOrder *memory_order_, char const *extra_message = nullptr) {
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, expr, t_atomic_memory_order);
+ if (x.mode == Addressing_Invalid) {
+ return false;
+ }
+ if (!are_types_identical(x.type, t_atomic_memory_order) || x.mode != Addressing_Constant) {
+ gbString str = type_to_string(x.type);
+ if (extra_message) {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for the %s of '%.*s', got %s", extra_message, LIT(builtin_name), str);
+ } else {
+ error(x.expr, "Expected a constant Atomic_Memory_Order value for '%.*s', got %s", LIT(builtin_name), str);
+ }
+ gb_string_free(str);
+ return false;
+ }
+ i64 value = exact_value_to_i64(x.value);
+ if (value < 0 || value >= OdinAtomicMemoryOrder_COUNT) {
+ error(x.expr, "Illegal Atomic_Memory_Order value, got %lld", cast(long long)value);
+ return false;
+ }
+ if (memory_order_) {
+ *memory_order_ = cast(OdinAtomicMemoryOrder)value;
+ }
+
+ return true;
+
+}
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
ast_node(ce, CallExpr, call);
@@ -179,9 +443,21 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_len:
case BuiltinProc_min:
case BuiltinProc_max:
+ case BuiltinProc_type_is_subtype_of:
+ case BuiltinProc_objc_send:
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ case BuiltinProc_atomic_type_is_lock_free:
// NOTE(bill): The first arg may be a Type, this will be checked case by case
break;
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ // NOTE(bill): first type will require a type hint
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -202,7 +478,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- String builtin_name = builtin_procs[id].name;;
+ String builtin_name = builtin_procs[id].name;
if (ce->args.count > 0) {
@@ -219,6 +495,19 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
GB_PANIC("Implement built-in procedure: %.*s", LIT(builtin_name));
break;
+ case BuiltinProc_objc_send:
+ case BuiltinProc_objc_find_selector:
+ case BuiltinProc_objc_find_class:
+ case BuiltinProc_objc_register_selector:
+ case BuiltinProc_objc_register_class:
+ return check_builtin_objc_procedure(c, operand, call, id, type_hint);
+
+ case BuiltinProc___entry_point:
+ operand->mode = Addressing_NoValue;
+ operand->type = nullptr;
+ mpmc_enqueue(&c->info->intrinsics_entry_point_usage, call);
+ break;
+
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
@@ -542,8 +831,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
} else if (name == "assert") {
- if (ce->args.count != 1) {
- error(call, "'#assert' expects 1 argument, got %td", ce->args.count);
+ if (ce->args.count != 1 && ce->args.count != 2) {
+ error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
return false;
}
if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
@@ -552,15 +841,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
gb_string_free(str);
return false;
}
+ if (ce->args.count == 2) {
+ Ast *arg = unparen_expr(ce->args[1]);
+ if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
+ gbString str = expr_to_string(arg);
+ error(call, "'%s' is not a constant string", str);
+ gb_string_free(str);
+ return false;
+ }
+ }
+
if (!operand->value.value_bool) {
- gbString arg = expr_to_string(ce->args[0]);
- error(call, "Compile time assertion: %s", arg);
+ gbString arg1 = expr_to_string(ce->args[0]);
+ gbString arg2 = {};
+
+ if (ce->args.count == 1) {
+ error(call, "Compile time assertion: %s", arg1);
+ } else {
+ arg2 = expr_to_string(ce->args[1]);
+ error(call, "Compile time assertion: %s (%s)", arg1, arg2);
+ }
+
if (c->proc_name != "") {
gbString str = type_to_string(c->curr_proc_sig);
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
gb_string_free(str);
}
- gb_string_free(arg);
+
+ gb_string_free(arg1);
+ if (ce->args.count == 2) {
+ gb_string_free(arg2);
+ }
}
operand->type = t_untyped_bool;
@@ -698,7 +1009,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
mode = Addressing_Constant;
value = exact_value_i64(at->EnumeratedArray.count);
type = t_untyped_integer;
- } else if (is_type_slice(op_type) && id == BuiltinProc_len) {
+ } else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) {
mode = Addressing_Value;
} else if (is_type_dynamic_array(op_type)) {
mode = Addressing_Value;
@@ -852,7 +1163,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
@@ -864,7 +1175,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (sel.indirect) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
gb_string_free(type_str);
@@ -925,7 +1236,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Selection sel = lookup_field(type, field_name, false);
if (sel.entity == nullptr) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"'%s' has no field named '%.*s'", type_str, LIT(field_name));
gb_string_free(type_str);
@@ -937,7 +1248,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
return false;
}
if (sel.indirect) {
- gbString type_str = type_to_string(type);
+ gbString type_str = type_to_string_shorthand(type);
error(ce->args[0],
"Field '%.*s' is embedded via a pointer in '%s'", LIT(field_name), type_str);
gb_string_free(type_str);
@@ -987,6 +1298,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'type_info_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
+ if (build_context.disallow_rtti) {
+ error(call, "'%.*s' has been disallowed", LIT(builtin_name));
+ return false;
+ }
// NOTE(bill): The type information may not be setup yet
init_core_type_info(c->checker);
@@ -999,9 +1314,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
Type *t = o.type;
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(t)) {
if (is_type_polymorphic(t)) {
- error(ce->args[0], "Invalid argument for 'type_info_of', unspecialized polymorphic type");
+ error(ce->args[0], "Invalid argument for '%.*s', unspecialized polymorphic type", LIT(builtin_name));
} else {
- error(ce->args[0], "Invalid argument for 'type_info_of'");
+ error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name));
}
return false;
}
@@ -1012,7 +1327,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (is_operand_value(o) && is_type_typeid(t)) {
add_package_dependency(c, "runtime", "__type_info_of");
} else if (o.mode != Addressing_Type) {
- error(expr, "Expected a type or typeid for 'type_info_of'");
+ error(expr, "Expected a type or typeid for '%.*s'", LIT(builtin_name));
return false;
}
@@ -1026,6 +1341,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
if (c->scope->flags&ScopeFlag_Global) {
compiler_error("'typeid_of' Cannot be declared within the runtime package due to how the internals of the compiler works");
}
+ if (build_context.disallow_rtti) {
+ error(call, "'%.*s' has been disallowed", LIT(builtin_name));
+ return false;
+ }
// NOTE(bill): The type information may not be setup yet
init_core_type_info(c->checker);
@@ -1037,7 +1356,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
Type *t = o.type;
if (t == nullptr || t == t_invalid || is_type_asm_proc(o.type) || is_type_polymorphic(operand->type)) {
- error(ce->args[0], "Invalid argument for 'typeid_of'");
+ error(ce->args[0], "Invalid argument for '%.*s'", LIT(builtin_name));
return false;
}
t = default_type(t);
@@ -1045,7 +1364,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
add_type_info_type(c, t);
if (o.mode != Addressing_Type) {
- error(expr, "Expected a type for 'typeid_of'");
+ error(expr, "Expected a type for '%.*s'", LIT(builtin_name));
return false;
}
@@ -1858,7 +2177,14 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
f64 r = operand->value.value_complex->real;
f64 i = operand->value.value_complex->imag;
operand->value = exact_value_float(gb_sqrt(r*r + i*i));
-
+ break;
+ }
+ case ExactValue_Quaternion: {
+ f64 r = operand->value.value_quaternion->real;
+ f64 i = operand->value.value_quaternion->imag;
+ f64 j = operand->value.value_quaternion->jmag;
+ f64 k = operand->value.value_quaternion->kmag;
+ operand->value = exact_value_float(gb_sqrt(r*r + i*i + j*j + k*k));
break;
}
default:
@@ -1877,10 +2203,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
}
- if (is_type_complex(operand->type)) {
+ if (is_type_complex_or_quaternion(operand->type)) {
operand->type = base_complex_elem_type(operand->type);
}
- GB_ASSERT(!is_type_complex(operand->type));
+ GB_ASSERT(!is_type_complex_or_quaternion(operand->type));
break;
}
@@ -2170,9 +2496,43 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
}
operand->mode = Addressing_Value;
- if (is_type_array(t)) {
+ if (t->kind == Type_Array) {
+ i32 rank = type_math_rank(t);
// Do nothing
- operand->type = x.type;
+ operand->type = x.type;
+ if (rank > 2) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a rank of 2, got %s of rank %d", LIT(builtin_name), s, rank);
+ gb_string_free(s);
+ return false;
+ } else if (rank == 2) {
+ Type *inner = base_type(t->Array.elem);
+ GB_ASSERT(inner->kind == Type_Array);
+ Type *elem = inner->Array.elem;
+ Type *array_inner = alloc_type_array(elem, t->Array.count);
+ Type *array_outer = alloc_type_array(array_inner, inner->Array.count);
+ operand->type = array_outer;
+
+ i64 elements = t->Array.count*inner->Array.count;
+ i64 size = type_size_of(operand->type);
+ if (!is_type_valid_for_matrix_elems(elem)) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a base element type of an integer, float, or complex number, got %s", LIT(builtin_name), s);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with a maximum of %d elements, got %s with %lld elements", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s, elements);
+ gb_string_free(s);
+ } else if (elements > MATRIX_ELEMENT_COUNT_MAX) {
+ gbString s = type_to_string(x.type);
+ error(call, "'%.*s' expects a matrix or array with non-zero elements, got %s", LIT(builtin_name), MATRIX_ELEMENT_COUNT_MAX, s);
+ gb_string_free(s);
+ } else if (size > MATRIX_ELEMENT_MAX_SIZE) {
+ gbString s = type_to_string(x.type);
+ error(call, "Too large of a type for '%.*s', got %s of size %lld, maximum size %d", LIT(builtin_name), s, cast(long long)size, MATRIX_ELEMENT_MAX_SIZE);
+ gb_string_free(s);
+ }
+ }
} else {
GB_ASSERT(t->kind == Type_Matrix);
operand->type = alloc_type_matrix(t->Matrix.elem, t->Matrix.column_count, t->Matrix.row_count);
@@ -2895,11 +3255,56 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
- case BuiltinProc_atomic_fence:
- case BuiltinProc_atomic_fence_acq:
- case BuiltinProc_atomic_fence_rel:
- case BuiltinProc_atomic_fence_acqrel:
- operand->mode = Addressing_NoValue;
+ case BuiltinProc_atomic_type_is_lock_free:
+ {
+ Ast *expr = ce->args[0];
+ Operand o = {};
+ check_expr_or_type(c, &o, expr);
+
+ if (o.mode == Addressing_Invalid || o.mode == Addressing_Builtin) {
+ return false;
+ }
+ if (o.type == nullptr || o.type == t_invalid || is_type_asm_proc(o.type)) {
+ error(o.expr, "Invalid argument to '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ if (is_type_polymorphic(o.type)) {
+ error(o.expr, "'%.*s' of polymorphic type cannot be determined", LIT(builtin_name));
+ return false;
+ }
+ if (is_type_untyped(o.type)) {
+ error(o.expr, "'%.*s' of untyped type is not allowed", LIT(builtin_name));
+ return false;
+ }
+ Type *t = o.type;
+ bool is_lock_free = is_type_lock_free(t);
+
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ operand->value = exact_value_bool(is_lock_free);
+ break;
+ }
+
+ case BuiltinProc_atomic_thread_fence:
+ case BuiltinProc_atomic_signal_fence:
+ {
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[0], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ error(ce->args[0], "Illegal memory ordering for '%.*s', got .%s", LIT(builtin_name), OdinAtomicMemoryOrder_strings[memory_order]);
+ break;
+ }
+
+ operand->mode = Addressing_NoValue;
+ }
break;
case BuiltinProc_volatile_store:
@@ -2907,9 +3312,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_unaligned_store:
/*fallthrough*/
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2925,60 +3327,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
- case BuiltinProc_volatile_load:
- /*fallthrough*/
- case BuiltinProc_unaligned_load:
- /*fallthrough*/
- case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered:
- {
- Type *elem = nullptr;
- if (!is_type_normal_pointer(operand->type, &elem)) {
- error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
- return false;
- }
- operand->type = elem;
- operand->mode = Addressing_Value;
- break;
- }
-
- case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
- case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
- case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
- case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
- case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
- case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed:
+ case BuiltinProc_atomic_store_explicit:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -2989,30 +3338,147 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
check_expr_with_type_hint(c, &x, ce->args[1], elem);
check_assignment(c, &x, elem, builtin_name);
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, &memory_order)) {
+ return false;
+ }
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[2], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = nullptr;
+ operand->mode = Addressing_NoValue;
+ break;
+ }
+
+
+ case BuiltinProc_volatile_load:
+ /*fallthrough*/
+ case BuiltinProc_unaligned_load:
+ /*fallthrough*/
+ case BuiltinProc_atomic_load:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
operand->type = elem;
operand->mode = Addressing_Value;
break;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
+ case BuiltinProc_atomic_load_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed:
+ OdinAtomicMemoryOrder memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[1], builtin_name, &memory_order)) {
+ return false;
+ }
+
+ switch (memory_order) {
+ case OdinAtomicMemoryOrder_release:
+ case OdinAtomicMemoryOrder_acq_rel:
+ error(ce->args[1], "Illegal memory order .%s for '%.*s'", OdinAtomicMemoryOrder_strings[memory_order], LIT(builtin_name));
+ break;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_add:
+ case BuiltinProc_atomic_sub:
+ case BuiltinProc_atomic_and:
+ case BuiltinProc_atomic_nand:
+ case BuiltinProc_atomic_or:
+ case BuiltinProc_atomic_xor:
+ case BuiltinProc_atomic_exchange:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+ Type *t = type_deref(operand->type);
+ switch (id) {
+ case BuiltinProc_atomic_add:
+ case BuiltinProc_atomic_sub:
+ if (!is_type_numeric(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ } else if (is_type_different_to_arch_endianness(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_assignment(c, &x, elem, builtin_name);
+
+
+ if (!check_atomic_memory_order_argument(c, ce->args[2], builtin_name, nullptr)) {
+ return false;
+ }
+
+ Type *t = type_deref(operand->type);
+ switch (id) {
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ if (!is_type_numeric(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ } else if (is_type_different_to_arch_endianness(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a numeric type of the same platform endianness for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+ break;
+ }
+
+ operand->type = elem;
+ operand->mode = Addressing_Value;
+ break;
+ }
+
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
{
Type *elem = nullptr;
if (!is_type_normal_pointer(operand->type, &elem)) {
@@ -3026,11 +3492,110 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
check_assignment(c, &x, elem, builtin_name);
check_assignment(c, &y, elem, builtin_name);
+ Type *t = type_deref(operand->type);
+ if (!is_type_comparable(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+
+ operand->mode = Addressing_OptionalOk;
+ operand->type = elem;
+ break;
+ }
+
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit:
+ {
+ Type *elem = nullptr;
+ if (!is_type_normal_pointer(operand->type, &elem)) {
+ error(operand->expr, "Expected a pointer for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ Operand y = {};
+ check_expr_with_type_hint(c, &x, ce->args[1], elem);
+ check_expr_with_type_hint(c, &y, ce->args[2], elem);
+ check_assignment(c, &x, elem, builtin_name);
+ check_assignment(c, &y, elem, builtin_name);
+
+ OdinAtomicMemoryOrder success_memory_order = {};
+ OdinAtomicMemoryOrder failure_memory_order = {};
+ if (!check_atomic_memory_order_argument(c, ce->args[3], builtin_name, &success_memory_order, "success ordering")) {
+ return false;
+ }
+ if (!check_atomic_memory_order_argument(c, ce->args[4], builtin_name, &failure_memory_order, "failure ordering")) {
+ return false;
+ }
+
+ Type *t = type_deref(operand->type);
+ if (!is_type_comparable(t)) {
+ gbString str = type_to_string(t);
+ error(operand->expr, "Expected a comparable type for '%.*s', got %s", LIT(builtin_name), str);
+ gb_string_free(str);
+ }
+
+ bool invalid_combination = false;
+
+ switch (success_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_release:
+ if (failure_memory_order != OdinAtomicMemoryOrder_relaxed) {
+ invalid_combination = true;
+ }
+ break;
+ case OdinAtomicMemoryOrder_consume:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_acq_rel:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ case OdinAtomicMemoryOrder_seq_cst:
+ switch (failure_memory_order) {
+ case OdinAtomicMemoryOrder_relaxed:
+ case OdinAtomicMemoryOrder_consume:
+ case OdinAtomicMemoryOrder_acquire:
+ case OdinAtomicMemoryOrder_seq_cst:
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+ break;
+ default:
+ invalid_combination = true;
+ break;
+ }
+
+
+ if (invalid_combination) {
+ error(ce->args[3], "Illegal memory order pairing for '%.*s', success = .%s, failure = .%s",
+ LIT(builtin_name),
+ OdinAtomicMemoryOrder_strings[success_memory_order],
+ OdinAtomicMemoryOrder_strings[failure_memory_order]
+ );
+ }
+
operand->mode = Addressing_OptionalOk;
operand->type = elem;
break;
}
- break;
case BuiltinProc_fixed_point_mul:
case BuiltinProc_fixed_point_div:
@@ -3211,8 +3776,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case TargetOs_linux:
case TargetOs_essence:
case TargetOs_freebsd:
+ case TargetOs_openbsd:
switch (build_context.metrics.arch) {
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_amd64:
case TargetArch_arm64:
max_arg_count = 7;
@@ -3297,9 +3863,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_simple_compare:
case BuiltinProc_type_is_dereferenceable:
case BuiltinProc_type_is_valid_map_key:
+ case BuiltinProc_type_is_valid_matrix_elements:
case BuiltinProc_type_is_named:
case BuiltinProc_type_is_pointer:
case BuiltinProc_type_is_array:
+ case BuiltinProc_type_is_enumerated_array:
case BuiltinProc_type_is_slice:
case BuiltinProc_type_is_dynamic_array:
case BuiltinProc_type_is_map:
@@ -3307,10 +3875,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_union:
case BuiltinProc_type_is_enum:
case BuiltinProc_type_is_proc:
- case BuiltinProc_type_is_bit_field:
- case BuiltinProc_type_is_bit_field_value:
case BuiltinProc_type_is_bit_set:
case BuiltinProc_type_is_simd_vector:
+ case BuiltinProc_type_is_matrix:
case BuiltinProc_type_is_specialized_polymorphic_record:
case BuiltinProc_type_is_unspecialized_polymorphic_record:
case BuiltinProc_type_has_nil:
@@ -3359,6 +3926,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
break;
+ case BuiltinProc_type_field_type:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr(c, &x, ce->args[1]);
+
+ if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
+ error(ce->args[1], "Expected a const string for field argument");
+ return false;
+ }
+
+ String field_name = x.value.value_string;
+
+ Selection sel = lookup_field(type, field_name, false);
+ if (sel.index.count == 0) {
+ gbString t = type_to_string(type);
+ error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ operand->mode = Addressing_Type;
+ operand->type = sel.entity->type;
+ break;
+ }
+ break;
case BuiltinProc_type_is_specialization_of:
{
@@ -3678,6 +4276,31 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
+ case BuiltinProc_type_is_subtype_of:
+ {
+ Operand op_src = {};
+ Operand op_dst = {};
+
+ check_expr_or_type(c, &op_src, ce->args[0]);
+ if (op_src.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_src.expr);
+ error(op_src.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+ check_expr_or_type(c, &op_dst, ce->args[1]);
+ if (op_dst.mode != Addressing_Type) {
+ gbString e = expr_to_string(op_dst.expr);
+ error(op_dst.expr, "'%.*s' expects a type, got %s", LIT(builtin_name), e);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->value = exact_value_bool(is_type_subtype_of(op_src.type, op_dst.type));
+ operand->mode = Addressing_Constant;
+ operand->type = t_untyped_bool;
+ } break;
+
case BuiltinProc_type_field_index_of:
{
Operand op = {};
@@ -3767,6 +4390,87 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
operand->type = t_hasher_proc;
break;
}
+
+ case BuiltinProc_constant_utf16_cstring:
+ {
+ String value = {};
+ if (!is_constant_string(c, builtin_name, ce->args[0], &value)) {
+ return false;
+ }
+ operand->mode = Addressing_Value;
+ operand->type = alloc_type_multi_pointer(t_u16);
+ operand->value = {};
+ break;
+ }
+
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ Operand delta = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+ check_expr(c, &delta, ce->args[1]); if (delta.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+ convert_to_typed(c, &delta, t_uintptr); if (delta.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ if (!is_operand_value(delta) || !check_is_assignable_to(c, &delta, t_uintptr)) {
+ gbString e = expr_to_string(delta.expr);
+ gbString t = type_to_string(delta.type);
+ error(delta.expr, "'%.*s' expected a uintptr for the memory delta, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+ case BuiltinProc_wasm_memory_size:
+ {
+ if (!is_arch_wasm()) {
+ error(call, "'%.*s' is only allowed on wasm targets", LIT(builtin_name));
+ return false;
+ }
+
+ Operand index = {};
+ check_expr(c, &index, ce->args[0]); if (index.mode == Addressing_Invalid) return false;
+
+ convert_to_typed(c, &index, t_uintptr); if (index.mode == Addressing_Invalid) return false;
+
+ if (!is_operand_value(index) || !check_is_assignable_to(c, &index, t_uintptr)) {
+ gbString e = expr_to_string(index.expr);
+ gbString t = type_to_string(index.type);
+ error(index.expr, "'%.*s' expected a uintptr for the memory index, got '%s' of type %s", LIT(builtin_name), e, t);
+ gb_string_free(t);
+ gb_string_free(e);
+ return false;
+ }
+
+ operand->mode = Addressing_Value;
+ operand->type = t_int;
+ operand->value = {};
+ break;
+ }
+ break;
+
}
return true;
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 42f68203c..5acd56097 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -174,6 +174,10 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) {
return;
}
+ if (is_type_proc(e->type)) {
+ error(e->token, "Illegal declaration of a constant procedure value");
+ }
+
e->parent_proc_decl = ctx->curr_proc_decl;
e->Constant.value = operand->value;
@@ -238,6 +242,51 @@ isize total_attribute_count(DeclInfo *decl) {
return attribute_count;
}
+Type *clone_enum_type(CheckerContext *ctx, Type *original_enum_type, Type *named_type) {
+ // NOTE(bill, 2022-02-05): Stupid edge case for `distinct` declarations
+ //
+ // X :: enum {A, B, C}
+ // Y :: distinct X
+ //
+ // To make Y be just like X, it will need to copy the elements of X and change their type
+ // so that they match Y rather than X.
+ GB_ASSERT(original_enum_type != nullptr);
+ GB_ASSERT(named_type != nullptr);
+ GB_ASSERT(original_enum_type->kind == Type_Enum);
+ GB_ASSERT(named_type->kind == Type_Named);
+
+ Scope *parent = original_enum_type->Enum.scope->parent;
+ Scope *scope = create_scope(nullptr, parent);
+
+
+ Type *et = alloc_type_enum();
+ et->Enum.base_type = original_enum_type->Enum.base_type;
+ et->Enum.min_value = original_enum_type->Enum.min_value;
+ et->Enum.max_value = original_enum_type->Enum.max_value;
+ et->Enum.min_value_index = original_enum_type->Enum.min_value_index;
+ et->Enum.max_value_index = original_enum_type->Enum.max_value_index;
+ et->Enum.scope = scope;
+
+ auto fields = array_make(permanent_allocator(), original_enum_type->Enum.fields.count);
+ for_array(i, fields) {
+ Entity *old = original_enum_type->Enum.fields[i];
+
+ Entity *e = alloc_entity_constant(scope, old->token, named_type, old->Constant.value);
+ e->file = old->file;
+ e->identifier = clone_ast(old->identifier);
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ e->Constant.flags = old->Constant.flags;
+ e->Constant.docs = old->Constant.docs;
+ e->Constant.comment = old->Constant.comment;
+
+ fields[i] = e;
+ add_entity(ctx, scope, nullptr, e);
+ add_entity_use(ctx, e->identifier, e);
+ }
+ et->Enum.fields = fields;
+ return et;
+}
void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) {
GB_ASSERT(e->type == nullptr);
@@ -258,7 +307,11 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
Type *bt = check_type_expr(ctx, te, named);
check_type_path_pop(ctx);
- named->Named.base = base_type(bt);
+ Type *base = base_type(bt);
+ if (is_distinct && bt->kind == Type_Named && base->kind == Type_Enum) {
+ base = clone_enum_type(ctx, base, named);
+ }
+ named->Named.base = base;
if (is_distinct && is_type_typeid(e->type)) {
error(init_expr, "'distinct' cannot be applied to 'typeid'");
@@ -289,6 +342,13 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (decl != nullptr) {
AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
+ if (e->kind == Entity_TypeName && ac.objc_class != "") {
+ e->TypeName.objc_class_name = ac.objc_class;
+
+ if (type_size_of(e->type) > 0) {
+ error(e->token, "@(objc_class) marked type must be of zero size");
+ }
+ }
}
@@ -380,12 +440,56 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
if (type_expr) {
e->type = check_type(ctx, type_expr);
+ if (are_types_identical(e->type, t_typeid)) {
+ e->type = nullptr;
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
}
Operand operand = {};
if (init != nullptr) {
- Entity *entity = nullptr;
+ Entity *entity = check_entity_from_ident_or_selector(ctx, init, false);
+ if (entity != nullptr && entity->kind == Entity_TypeName) {
+ // @TypeAliasingProblem
+ // NOTE(bill, 2022-02-03): This is used to solve the problem caused by type aliases
+ // being "confused" as constants
+ //
+ // A :: B
+ // C :: proc "c" (^A)
+ // B :: struct {x: C}
+ //
+ // A gets evaluated first, and then checks B.
+ // B then checks C.
+ // C then tries to check A which is unresolved but thought to be a constant.
+ // Therefore within C's check, A errs as "not a type".
+ //
+ // This is because a const declaration may or may not be a type and this cannot
+ // be determined from a syntactical standpoint.
+ // This check allows the compiler to override the entity to be checked as a type.
+ //
+ // There is no problem if B is prefixed with the `#type` helper enforcing at
+ // both a syntax and semantic level that B must be a type.
+ //
+ // A :: #type B
+ //
+ // This approach is not fool proof and can fail in case such as:
+ //
+ // X :: type_of(x)
+ // X :: Foo(int).Type
+ //
+ // Since even these kind of declarations may cause weird checking cycles.
+ // For the time being, these are going to be treated as an unfortunate error
+ // until there is a proper delaying system to try declaration again if they
+ // have failed.
+
+ e->kind = Entity_TypeName;
+ check_type_decl(ctx, e, init, named_type);
+ return;
+ }
+ entity = nullptr;
if (init->kind == Ast_Ident) {
entity = check_ident(ctx, &operand, init, nullptr, e->type, true);
} else if (init->kind == Ast_SelectorExpr) {
@@ -732,6 +836,63 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
+ if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
+ if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
+ error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
+ } else if (ac.objc_type == nullptr) {
+ error(e->token, "@(objc_name) requires that @(objc_type) to be set");
+ } else if (ac.objc_name.len == 0 && ac.objc_type) {
+ error(e->token, "@(objc_name) is required with @(objc_type)");
+ } else {
+ Type *t = ac.objc_type;
+ if (t->kind == Type_Named) {
+ Entity *tn = t->Named.type_name;
+
+ GB_ASSERT(tn->kind == Entity_TypeName);
+
+ if (tn->scope != e->scope) {
+ error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
+ } else {
+ mutex_lock(&global_type_name_objc_metadata_mutex);
+ defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
+
+ if (!tn->TypeName.objc_metadata) {
+ tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
+ }
+ auto *md = tn->TypeName.objc_metadata;
+ mutex_lock(md->mutex);
+ defer (mutex_unlock(md->mutex));
+
+ if (!ac.objc_is_class_method) {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ } else {
+ bool ok = true;
+ for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
+ if (entry.name == ac.objc_name) {
+ error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
+ ok = false;
+ break;
+ }
+ }
+ if (ok) {
+ array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
+ }
+ }
+ }
+ }
+ }
+ }
+
switch (e->Procedure.optimization_mode) {
case ProcedureOptimizationMode_None:
@@ -777,21 +938,23 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
if (e->pkg != nullptr && e->token.string == "main") {
- if (pt->param_count != 0 ||
- pt->result_count != 0) {
- gbString str = type_to_string(proc_type);
- error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
- gb_string_free(str);
- }
- if (pt->calling_convention != default_calling_convention()) {
- error(e->token, "Procedure 'main' cannot have a custom calling convention");
- }
- pt->calling_convention = default_calling_convention();
- if (e->pkg->kind == Package_Init) {
- if (ctx->info->entry_point != nullptr) {
- error(e->token, "Redeclaration of the entry pointer procedure 'main'");
- } else {
- ctx->info->entry_point = e;
+ if (e->pkg->kind != Package_Runtime) {
+ if (pt->param_count != 0 ||
+ pt->result_count != 0) {
+ gbString str = type_to_string(proc_type);
+ error(e->token, "Procedure type of 'main' was expected to be 'proc()', got %s", str);
+ gb_string_free(str);
+ }
+ if (pt->calling_convention != default_calling_convention()) {
+ error(e->token, "Procedure 'main' cannot have a custom calling convention");
+ }
+ pt->calling_convention = default_calling_convention();
+ if (e->pkg->kind == Package_Init) {
+ if (ctx->info->entry_point != nullptr) {
+ error(e->token, "Redeclaration of the entry pointer procedure 'main'");
+ } else {
+ ctx->info->entry_point = e;
+ }
}
}
}
@@ -924,7 +1087,9 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
"\tother at %s",
LIT(name), token_pos_to_string(pos));
} else if (name == "main") {
- error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ if (d->entity->pkg->kind != Package_Runtime) {
+ error(d->proc_lit, "The link name 'main' is reserved for internal use");
+ }
} else {
string_map_set(fp, key, e);
}
@@ -971,6 +1136,12 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
+ if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
+ e->Variable.thread_local_model.len = 0;
+ // NOTE(bill): ignore this message for the time begin
+ // error(e->token, "@(thread_local) is not supported for this target platform");
+ }
+
String context_name = str_lit("variable declaration");
if (type_expr != nullptr) {
@@ -1046,6 +1217,8 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
Operand o = {};
check_expr_with_type_hint(ctx, &o, init_expr, e->type);
check_init_variable(ctx, e, &o, str_lit("variable declaration"));
+
+ check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
}
void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) {
@@ -1303,7 +1476,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
if (t->kind == Type_Struct) {
Scope *scope = t->Struct.scope;
GB_ASSERT(scope != nullptr);
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *f = scope->elements.entries[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr);
@@ -1321,11 +1494,10 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
}
}
-
- for_array(i, using_entities) {
+ MUTEX_GUARD_BLOCK(ctx->scope->mutex) for_array(i, using_entities) {
Entity *e = using_entities[i].e;
Entity *uvar = using_entities[i].uvar;
- Entity *prev = scope_insert(ctx->scope, uvar);
+ Entity *prev = scope_insert(ctx->scope, uvar, false);
if (prev != nullptr) {
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index 560e5e607..336a711d4 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -119,6 +119,115 @@ void check_or_else_split_types(CheckerContext *c, Operand *x, String const &name
void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint);
void check_or_return_split_types(CheckerContext *c, Operand *x, String const &name, Type **left_type_, Type **right_type_);
+
+void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
+ auto results = did_you_mean_results(d);
+ if (results.count != 0) {
+ error_line("\tSuggestion: Did you mean?\n");
+ for_array(i, results) {
+ String const &target = results[i].target;
+ error_line("\t\t%s%.*s\n", prefix, LIT(target));
+ // error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
+ }
+ }
+}
+
+void populate_check_did_you_mean_objc_entity(StringSet *set, Entity *e, bool is_type) {
+ if (e->kind != Entity_TypeName) {
+ return;
+ }
+ if (e->TypeName.objc_metadata == nullptr) {
+ return;
+ }
+ TypeNameObjCMetadata *objc_metadata = e->TypeName.objc_metadata;
+ Type *t = base_type(e->type);
+ GB_ASSERT(t->kind == Type_Struct);
+
+ if (is_type) {
+ for_array(i, objc_metadata->type_entries) {
+ String name = objc_metadata->type_entries[i].name;
+ string_set_add(set, name);
+ }
+ } else {
+ for_array(i, objc_metadata->value_entries) {
+ String name = objc_metadata->value_entries[i].name;
+ string_set_add(set, name);
+ }
+ }
+
+ for_array(i, t->Struct.fields) {
+ Entity *f = t->Struct.fields[i];
+ if (f->flags & EntityFlag_Using && f->type != nullptr) {
+ if (f->type->kind == Type_Named && f->type->Named.type_name) {
+ populate_check_did_you_mean_objc_entity(set, f->type->Named.type_name, is_type);
+ }
+ }
+ }
+}
+
+
+void check_did_you_mean_objc_entity(String const &name, Entity *e, bool is_type, char const *prefix = "") {
+ ERROR_BLOCK();
+ GB_ASSERT(e->kind == Entity_TypeName);
+ GB_ASSERT(e->TypeName.objc_metadata != nullptr);
+ auto *objc_metadata = e->TypeName.objc_metadata;
+ mutex_lock(objc_metadata->mutex);
+ defer (mutex_unlock(objc_metadata->mutex));
+
+ StringSet set = {};
+ string_set_init(&set, heap_allocator());
+ defer (string_set_destroy(&set));
+ populate_check_did_you_mean_objc_entity(&set, e, is_type);
+
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), set.entries.count, name);
+ defer (did_you_mean_destroy(&d));
+ for_array(i, set.entries) {
+ did_you_mean_append(&d, set.entries[i].value);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+void check_did_you_mean_type(String const &name, Array const &fields, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ for_array(i, fields) {
+ did_you_mean_append(&d, fields[i]->token.string);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+
+void check_did_you_mean_type(String const &name, Slice const &fields, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ for_array(i, fields) {
+ did_you_mean_append(&d, fields[i]->token.string);
+ }
+ check_did_you_mean_print(&d, prefix);
+}
+
+void check_did_you_mean_scope(String const &name, Scope *scope, char const *prefix = "") {
+ ERROR_BLOCK();
+
+ DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
+ defer (did_you_mean_destroy(&d));
+
+ mutex_lock(&scope->mutex);
+ for_array(i, scope->elements.entries) {
+ Entity *e = scope->elements.entries[i].value;
+ did_you_mean_append(&d, e->token.string);
+ }
+ mutex_unlock(&scope->mutex);
+ check_did_you_mean_print(&d, prefix);
+}
+
Entity *entity_from_expr(Ast *expr) {
expr = unparen_expr(expr);
switch (expr->kind) {
@@ -176,42 +285,6 @@ void check_scope_decls(CheckerContext *c, Slice const &nodes, isize reser
}
}
-
-isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0, bool src_is_ptr = false) {
- Type *prev_src = src;
- src = type_deref(src);
- if (!src_is_ptr) {
- src_is_ptr = src != prev_src;
- }
- src = base_type(src);
-
- if (!is_type_struct(src)) {
- return 0;
- }
-
- for_array(i, src->Struct.fields) {
- Entity *f = src->Struct.fields[i];
- if (f->kind != Entity_Variable || (f->flags&EntityFlag_Using) == 0) {
- continue;
- }
-
- if (are_types_identical(f->type, dst)) {
- return level+1;
- }
- if (src_is_ptr && is_type_pointer(dst)) {
- if (are_types_identical(f->type, type_deref(dst))) {
- return level+1;
- }
- }
- isize nested_level = check_is_assignable_to_using_subtype(f->type, dst, level+1, src_is_ptr);
- if (nested_level > 0) {
- return nested_level;
- }
- }
-
- return 0;
-}
-
bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, Entity *base_entity, Type *type,
Array *param_operands, Ast *poly_def_node, PolyProcData *poly_proc_data) {
///////////////////////////////////////////////////////////////////////////////
@@ -456,6 +529,10 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
#define MAXIMUM_TYPE_DISTANCE 10
i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
+ if (c == nullptr) {
+ GB_ASSERT(operand->mode == Addressing_Value);
+ GB_ASSERT(is_type_typed(operand->type));
+ }
if (operand->mode == Addressing_Invalid ||
type == t_invalid) {
return -1;
@@ -621,6 +698,42 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 1;
}
}
+
+ // TODO(bill): Determine which rule is a better on in practice
+ #if 1
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ return score+2;
+ }
+ }
+ #else
+ // NOTE(bill): check to see you can assign to it with one of the variants?
+ i64 prev_lowest_score = -1;
+ i64 lowest_score = -1;
+ for_array(i, dst->Union.variants) {
+ Type *vt = dst->Union.variants[i];
+ i64 score = check_distance_between_types(c, operand, vt);
+ if (score >= 0) {
+ if (lowest_score < 0) {
+ lowest_score = score;
+ } else {
+ if (prev_lowest_score < 0) {
+ prev_lowest_score = lowest_score;
+ } else {
+ prev_lowest_score = gb_min(prev_lowest_score, lowest_score);
+ }
+ lowest_score = gb_min(lowest_score, score);
+ }
+ }
+ }
+ if (lowest_score >= 0) {
+ if (prev_lowest_score != lowest_score) { // remove possible ambiguities
+ return lowest_score+2;
+ }
+ }
+ #endif
}
if (is_type_relative_pointer(dst)) {
@@ -649,6 +762,13 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
return 4;
}
}
+
+ if (is_type_complex_or_quaternion(dst)) {
+ Type *elem = base_complex_elem_type(dst);
+ if (are_types_identical(elem, base_type(src))) {
+ return 5;
+ }
+ }
if (is_type_array(dst)) {
Type *elem = base_array_type(dst);
@@ -723,6 +843,13 @@ bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
return check_is_assignable_to_with_score(c, operand, type, &score);
}
+bool internal_check_is_assignable_to(Type *src, Type *dst) {
+ Operand x = {};
+ x.type = src;
+ x.mode = Addressing_Value;
+ return check_is_assignable_to(nullptr, &x, dst);
+}
+
AstPackage *get_package_of_type(Type *type) {
for (;;) {
if (type == nullptr) {
@@ -1227,7 +1354,6 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
return false;
}
-
Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name) {
GB_ASSERT(n->kind == Ast_Ident);
o->mode = Addressing_Invalid;
@@ -1363,8 +1489,12 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
case Entity_TypeName:
o->mode = Addressing_Type;
if (check_cycle(c, e, true)) {
- type = t_invalid;
+ o->type = t_invalid;
}
+ if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
+ o->type = base_type(o->type);
+ }
+
break;
case Entity_ImportName:
@@ -3354,7 +3484,16 @@ void convert_untyped_error(CheckerContext *c, Operand *operand, Type *target_typ
}
}
}
+ ERROR_BLOCK();
+
error(operand->expr, "Cannot convert untyped value '%s' to '%s' from '%s'%s", expr_str, type_str, from_type_str, extra_text);
+ if (operand->value.kind == ExactValue_String) {
+ String key = operand->value.value_string;
+ if (is_type_string(operand->type) && is_type_enum(target_type)) {
+ Type *et = base_type(target_type);
+ check_did_you_mean_type(key, et->Enum.fields, ".");
+ }
+ }
gb_string_free(from_type_str);
gb_string_free(type_str);
@@ -3972,54 +4111,6 @@ ExactValue get_constant_field(CheckerContext *c, Operand const *operand, Selecti
if (success_) *success_ = true;
return empty_exact_value;
}
-void check_did_you_mean_print(DidYouMeanAnswers *d) {
- auto results = did_you_mean_results(d);
- if (results.count != 0) {
- error_line("\tSuggestion: Did you mean?\n");
- for_array(i, results) {
- String const &target = results[i].target;
- error_line("\t\t%.*s\n", LIT(target));
- // error_line("\t\t%.*s %td\n", LIT(target), results[i].distance);
- }
- }
-}
-
-void check_did_you_mean_type(String const &name, Array const &fields) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
- }
- check_did_you_mean_print(&d);
-}
-
-void check_did_you_mean_type(String const &name, Slice const &fields) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), fields.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, fields) {
- did_you_mean_append(&d, fields[i]->token.string);
- }
- check_did_you_mean_print(&d);
-}
-
-void check_did_you_mean_scope(String const &name, Scope *scope) {
- ERROR_BLOCK();
-
- DidYouMeanAnswers d = did_you_mean_make(heap_allocator(), scope->elements.entries.count, name);
- defer (did_you_mean_destroy(&d));
-
- for_array(i, scope->elements.entries) {
- Entity *e = scope->elements.entries[i].value;
- did_you_mean_append(&d, e->token.string);
- }
- check_did_you_mean_print(&d);
-}
Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize new_count) {
Type *array_type = base_type(type_deref(original_type));
@@ -4044,6 +4135,101 @@ Type *determine_swizzle_array_type(Type *original_type, Type *type_hint, isize n
}
+bool is_entity_declared_for_selector(Entity *entity, Scope *import_scope, bool *allow_builtin) {
+ bool is_declared = entity != nullptr;
+ if (is_declared) {
+ if (entity->kind == Entity_Builtin) {
+ // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
+ // This means that we should just ignore the found result through it
+ *allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
+ } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
+ is_declared = false;
+ }
+ }
+ return is_declared;
+}
+
+// NOTE(bill, 2022-02-03): see `check_const_decl` for why it exists reasoning
+Entity *check_entity_from_ident_or_selector(CheckerContext *c, Ast *node, bool ident_only) {
+ if (node->kind == Ast_Ident) {
+ String name = node->Ident.token.string;
+ return scope_lookup(c->scope, name);
+ } else if (!ident_only) if (node->kind == Ast_SelectorExpr) {
+ ast_node(se, SelectorExpr, node);
+ if (se->token.kind == Token_ArrowRight) {
+ return nullptr;
+ }
+
+ Ast *op_expr = se->expr;
+ Ast *selector = unparen_expr(se->selector);
+ if (selector == nullptr) {
+ return nullptr;
+ }
+ if (selector->kind != Ast_Ident) {
+ return nullptr;
+ }
+
+ Entity *entity = nullptr;
+ Entity *expr_entity = nullptr;
+ bool check_op_expr = true;
+
+ if (op_expr->kind == Ast_Ident) {
+ String op_name = op_expr->Ident.token.string;
+ Entity *e = scope_lookup(c->scope, op_name);
+ if (e == nullptr) {
+ return nullptr;
+ }
+ add_entity_use(c, op_expr, e);
+ expr_entity = e;
+
+ if (e != nullptr && e->kind == Entity_ImportName && selector->kind == Ast_Ident) {
+ // IMPORTANT NOTE(bill): This is very sloppy code but it's also very fragile
+ // It pretty much needs to be in this order and this way
+ // If you can clean this up, please do but be really careful
+ String import_name = op_name;
+ Scope *import_scope = e->ImportName.scope;
+ String entity_name = selector->Ident.token.string;
+
+ check_op_expr = false;
+ entity = scope_lookup_current(import_scope, entity_name);
+ bool allow_builtin = false;
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
+ return nullptr;
+ }
+
+ check_entity_decl(c, entity, nullptr, nullptr);
+ if (entity->kind == Entity_ProcGroup) {
+ return entity;
+ }
+ GB_ASSERT_MSG(entity->type != nullptr, "%.*s (%.*s)", LIT(entity->token.string), LIT(entity_strings[entity->kind]));
+ }
+ }
+
+ Operand operand = {};
+ if (check_op_expr) {
+ check_expr_base(c, &operand, op_expr, nullptr);
+ if (operand.mode == Addressing_Invalid) {
+ return nullptr;
+ }
+ }
+
+ if (entity == nullptr && selector->kind == Ast_Ident) {
+ String field_name = selector->Ident.token.string;
+ if (is_type_dynamic_array(type_deref(operand.type))) {
+ init_mem_allocator(c->checker);
+ }
+ auto sel = lookup_field(operand.type, field_name, operand.mode == Addressing_Type);
+ entity = sel.entity;
+ }
+
+ if (entity != nullptr) {
+ return entity;
+ }
+ }
+ return nullptr;
+}
+
+
Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *type_hint) {
ast_node(se, SelectorExpr, node);
@@ -4092,18 +4278,8 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
check_op_expr = false;
entity = scope_lookup_current(import_scope, entity_name);
- bool is_declared = entity != nullptr;
bool allow_builtin = false;
- if (is_declared) {
- if (entity->kind == Entity_Builtin) {
- // NOTE(bill): Builtin's are in the universal scope which is part of every scopes hierarchy
- // This means that we should just ignore the found result through it
- allow_builtin = entity->scope == import_scope || entity->scope != builtin_pkg->scope;
- } else if ((entity->scope->flags&ScopeFlag_Global) == ScopeFlag_Global && (import_scope->flags&ScopeFlag_Global) == 0) {
- is_declared = false;
- }
- }
- if (!is_declared) {
+ if (!is_entity_declared_for_selector(entity, import_scope, &allow_builtin)) {
error(op_expr, "'%.*s' is not declared by '%.*s'", LIT(entity_name), LIT(import_name));
operand->mode = Addressing_Invalid;
operand->expr = node;
@@ -4193,7 +4369,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
}
- if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
+ if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
// TODO(bill): Simd_Vector swizzling
String field_name = selector->Ident.token.string;
@@ -4294,14 +4470,19 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (entity == nullptr) {
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "'%s' of type '%s' has no field '%s'", op_str, type_str, sel_str);
if (operand->type != nullptr && selector->kind == Ast_Ident) {
String const &name = selector->Ident.token.string;
Type *bt = base_type(operand->type);
- if (bt->kind == Type_Struct) {
+ if (operand->type->kind == Type_Named &&
+ operand->type->Named.type_name &&
+ operand->type->Named.type_name->kind == Entity_TypeName &&
+ operand->type->Named.type_name->TypeName.objc_metadata) {
+ check_did_you_mean_objc_entity(name, operand->type->Named.type_name, operand->mode == Addressing_Type);
+ } else if (bt->kind == Type_Struct) {
check_did_you_mean_type(name, bt->Struct.fields);
} else if (bt->kind == Type_Enum) {
check_did_you_mean_type(name, bt->Enum.fields);
@@ -4330,7 +4511,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4355,7 +4536,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
}
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access non-constant field '%s' from '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4368,7 +4549,7 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ
if (expr_entity != nullptr && is_type_polymorphic(expr_entity->type)) {
gbString op_str = expr_to_string(op_expr);
- gbString type_str = type_to_string(operand->type);
+ gbString type_str = type_to_string_shorthand(operand->type);
gbString sel_str = expr_to_string(selector);
error(op_expr, "Cannot access field '%s' from non-specialized polymorphic type '%s'", sel_str, op_str);
gb_string_free(sel_str);
@@ -4691,25 +4872,16 @@ bool is_expr_constant_zero(Ast *expr) {
return false;
}
-
-CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
- ast_node(ce, CallExpr, call);
- GB_ASSERT(is_type_proc(proc_type));
- proc_type = base_type(proc_type);
- TypeProc *pt = &proc_type->Proc;
-
+isize get_procedure_param_count_excluding_defaults(Type *pt, isize *param_count_) {
+ GB_ASSERT(pt != nullptr);
+ GB_ASSERT(pt->kind == Type_Proc);
isize param_count = 0;
isize param_count_excluding_defaults = 0;
- bool variadic = pt->variadic;
- bool vari_expand = (ce->ellipsis.pos.line != 0);
- i64 score = 0;
- bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
-
-
+ bool variadic = pt->Proc.variadic;
TypeTuple *param_tuple = nullptr;
- if (pt->params != nullptr) {
- param_tuple = &pt->params->Tuple;
+ if (pt->Proc.params != nullptr) {
+ param_tuple = &pt->Proc.params->Tuple;
param_count = param_tuple->variables.count;
if (variadic) {
@@ -4749,6 +4921,31 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
}
}
+ if (param_count_) *param_count_ = param_count;
+ return param_count_excluding_defaults;
+}
+
+
+CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
+ ast_node(ce, CallExpr, call);
+ GB_ASSERT(is_type_proc(proc_type));
+ proc_type = base_type(proc_type);
+ TypeProc *pt = &proc_type->Proc;
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(proc_type, ¶m_count);
+ bool variadic = pt->variadic;
+ bool vari_expand = (ce->ellipsis.pos.line != 0);
+ i64 score = 0;
+ bool show_error = show_error_mode == CallArgumentMode_ShowErrors;
+
+
+ TypeTuple *param_tuple = nullptr;
+ if (pt->params != nullptr) {
+ param_tuple = &pt->params->Tuple;
+ }
+
+
CallArgumentError err = CallArgumentError_None;
Type *final_proc_type = proc_type;
Entity *gen_entity = nullptr;
@@ -5421,7 +5618,37 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (operand->mode == Addressing_ProcGroup) {
check_entity_decl(c, operand->proc_group, nullptr, nullptr);
- Array procs = proc_group_entities(c, *operand);
+ auto procs = proc_group_entities_cloned(c, *operand);
+
+ if (procs.count > 1) {
+ isize max_arg_count = args.count;
+ for_array(i, args) {
+ // NOTE(bill): The only thing that may have multiple values
+ // will be a call expression (assuming `or_return` and `()` will be stripped)
+ Ast *arg = strip_or_return_expr(args[i]);
+ if (arg && arg->kind == Ast_CallExpr) {
+ max_arg_count = ISIZE_MAX;
+ break;
+ }
+ }
+
+ for (isize proc_index = 0; proc_index < procs.count; /**/) {
+ Entity *proc = procs[proc_index];
+ Type *pt = base_type(proc->type);
+ if (!(pt != nullptr && is_type_proc(pt))) {
+ continue;
+ }
+
+ isize param_count = 0;
+ isize param_count_excluding_defaults = get_procedure_param_count_excluding_defaults(pt, ¶m_count);
+
+ if (param_count_excluding_defaults > max_arg_count) {
+ array_unordered_remove(&procs, proc_index);
+ } else {
+ proc_index++;
+ }
+ }
+ }
if (procs.count == 1) {
Ast *ident = operand->expr;
@@ -5451,6 +5678,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
return data;
}
+
Entity **lhs = nullptr;
isize lhs_count = -1;
@@ -5735,8 +5963,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -5749,6 +5981,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
Entity *e = entity_of_node(ident);
+
CallArgumentData data = {};
CallArgumentError err = call_checker(c, call, proc_type, e, operands, CallArgumentMode_ShowErrors, &data);
gb_unused(err);
@@ -5757,7 +5990,6 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
if (entity_to_use != nullptr) {
update_untyped_expr_type(c, operand->expr, entity_to_use->type, true);
}
-
if (data.gen_entity != nullptr) {
Entity *e = data.gen_entity;
DeclInfo *decl = data.gen_entity->decl_info;
@@ -5769,8 +6001,12 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
ctx.curr_proc_sig = e->type;
GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit);
- evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
+ bool ok = evaluate_where_clauses(&ctx, call, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true);
decl->where_clauses_evaluated = true;
+
+ if (ok && (data.gen_entity->flags & EntityFlag_ProcBodyChecked) == 0) {
+ check_procedure_later(c, e->file, e->token, decl, e->type, decl->proc_lit->ProcLit.body, decl->proc_lit->ProcLit.tags);
+ }
}
return data;
}
@@ -6064,7 +6300,8 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper
}
// NOTE(bill): Add type info the parameters
- add_type_info_type(c, o->type);
+ // TODO(bill, 2022-01-23): why was this line added in the first place? I'm commenting it out for the time being
+ // add_type_info_type(c, o->type);
}
{
@@ -6268,10 +6505,10 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
return builtin_procs[id].kind;
}
- Entity *e = entity_of_node(operand->expr);
+ Entity *initial_entity = entity_of_node(operand->expr);
- if (e != nullptr && e->kind == Entity_Procedure) {
- if (e->Procedure.deferred_procedure.entity != nullptr) {
+ if (initial_entity != nullptr && initial_entity->kind == Entity_Procedure) {
+ if (initial_entity->Procedure.deferred_procedure.entity != nullptr) {
call->viral_state_flags |= ViralStateFlag_ContainsDeferredProcedure;
}
}
@@ -6839,6 +7076,1903 @@ void check_matrix_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *typ
}
+struct TypeAndToken {
+ Type *type;
+ Token token;
+};
+
+typedef PtrMap SeenMap;
+
+void add_constant_switch_case(CheckerContext *ctx, SeenMap *seen, Operand operand, bool use_expr = true) {
+ if (operand.mode != Addressing_Constant) {
+ return;
+ }
+ if (operand.value.kind == ExactValue_Invalid) {
+ return;
+ }
+
+ uintptr key = hash_exact_value(operand.value);
+ TypeAndToken *found = map_get(seen, key);
+ if (found != nullptr) {
+ isize count = multi_map_count(seen, key);
+ TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
+
+ multi_map_get_all(seen, key, taps);
+ for (isize i = 0; i < count; i++) {
+ TypeAndToken tap = taps[i];
+ if (!are_types_identical(operand.type, tap.type)) {
+ continue;
+ }
+
+ TokenPos pos = tap.token.pos;
+ if (use_expr) {
+ gbString expr_str = expr_to_string(operand.expr);
+ error(operand.expr,
+ "Duplicate case '%s'\n"
+ "\tprevious case at %s",
+ expr_str,
+ token_pos_to_string(pos));
+ gb_string_free(expr_str);
+ } else {
+ error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
+ }
+ return;
+ }
+ }
+
+ TypeAndToken tap = {operand.type, ast_token(operand.expr)};
+ multi_map_insert(seen, key, tap);
+}
+
+
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, TokenKind upper_op, Operand const &x, Operand const &lhs, Operand const &rhs) {
+ if (is_type_enum(x.type)) {
+ // TODO(bill): Fix this logic so it's fast!!!
+
+ i64 v0 = exact_value_to_i64(lhs.value);
+ i64 v1 = exact_value_to_i64(rhs.value);
+ Operand v = {};
+ v.mode = Addressing_Constant;
+ v.type = x.type;
+ v.expr = x.expr;
+
+ Type *bt = base_type(x.type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for (i64 vi = v0; vi <= v1; vi++) {
+ if (upper_op != Token_LtEq && vi == v1) {
+ break;
+ }
+
+ bool found = false;
+ for_array(j, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[j];
+ GB_ASSERT(f->kind == Entity_Constant);
+
+ i64 fv = exact_value_to_i64(f->Constant.value);
+ if (fv == vi) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ v.value = exact_value_i64(vi);
+ add_constant_switch_case(ctx, seen, v);
+ }
+ }
+ } else {
+ add_constant_switch_case(ctx, seen, lhs);
+ if (upper_op == Token_LtEq) {
+ add_constant_switch_case(ctx, seen, rhs);
+ }
+ }
+}
+void add_to_seen_map(CheckerContext *ctx, SeenMap *seen, Operand const &x) {
+ add_constant_switch_case(ctx, seen, x);
+}
+
+ExprKind check_basic_directive_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(bd, BasicDirective, node);
+
+ ExprKind kind = Expr_Expr;
+
+ o->mode = Addressing_Constant;
+ String name = bd->name.string;
+ if (name == "file") {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
+ } else if (name == "line") {
+ o->type = t_untyped_integer;
+ o->value = exact_value_i64(bd->token.pos.line);
+ } else if (name == "procedure") {
+ if (c->curr_proc_decl == nullptr) {
+ error(node, "#procedure may only be used within procedures");
+ o->type = t_untyped_string;
+ o->value = exact_value_string(str_lit(""));
+ } else {
+ o->type = t_untyped_string;
+ o->value = exact_value_string(c->proc_name);
+ }
+ } else if (name == "caller_location") {
+ init_core_source_code_location(c->checker);
+ error(node, "#caller_location may only be used as a default argument parameter");
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
+ } else {
+ if (name == "location") {
+ init_core_source_code_location(c->checker);
+ error(node, "'#%.*s' must be used in a call expression", LIT(name));
+ o->type = t_source_code_location;
+ o->mode = Addressing_Value;
+ } else if (
+ name == "assert" ||
+ name == "defined" ||
+ name == "config" ||
+ name == "load" ||
+ name == "load_hash" ||
+ name == "load_or"
+ ) {
+ error(node, "'#%.*s' must be used as a call", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
+ } else {
+ error(node, "Unknown directive: #%.*s", LIT(name));
+ o->type = t_invalid;
+ o->mode = Addressing_Invalid;
+ }
+
+ }
+ return kind;
+}
+
+ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {Addressing_Invalid};
+ ast_node(te, TernaryIfExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
+
+ if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
+ error(te->cond, "Non-boolean condition in ternary if expression");
+ }
+
+ Operand x = {Addressing_Invalid};
+ Operand y = {Addressing_Invalid};
+ check_expr_or_type(c, &x, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
+
+ if (te->y != nullptr) {
+ check_expr_or_type(c, &y, te->y, type_hint);
+ node->viral_state_flags |= te->y->viral_state_flags;
+ } else {
+ error(node, "A ternary expression must have an else clause");
+ return kind;
+ }
+
+ if (x.type == nullptr || x.type == t_invalid ||
+ y.type == nullptr || y.type == t_invalid) {
+ return kind;
+ }
+
+ convert_to_typed(c, &x, y.type);
+ if (x.mode == Addressing_Invalid) {
+ return kind;
+ }
+ convert_to_typed(c, &y, x.type);
+ if (y.mode == Addressing_Invalid) {
+ x.mode = Addressing_Invalid;
+ return kind;
+ }
+
+ if (!ternary_compare_types(x.type, y.type)) {
+ gbString its = type_to_string(x.type);
+ gbString ets = type_to_string(y.type);
+ error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
+ gb_string_free(ets);
+ gb_string_free(its);
+ return kind;
+ }
+
+ o->type = x.type;
+ if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
+ o->type = y.type;
+ }
+
+ o->mode = Addressing_Value;
+ o->expr = node;
+ if (type_hint != nullptr && is_type_untyped(o->type)) {
+ if (check_cast_internal(c, &x, type_hint) &&
+ check_cast_internal(c, &y, type_hint)) {
+ convert_to_typed(c, o, type_hint);
+ update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
+ }
+ }
+ return kind;
+}
+
+ExprKind check_ternary_when_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ Operand cond = {};
+ ast_node(te, TernaryWhenExpr, node);
+ check_expr(c, &cond, te->cond);
+ node->viral_state_flags |= te->cond->viral_state_flags;
+
+ if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
+ error(te->cond, "Expected a constant boolean condition in ternary when expression");
+ return kind;
+ }
+
+ if (cond.value.value_bool) {
+ check_expr_or_type(c, o, te->x, type_hint);
+ node->viral_state_flags |= te->x->viral_state_flags;
+ } else {
+ if (te->y != nullptr) {
+ check_expr_or_type(c, o, te->y, type_hint);
+ node->viral_state_flags |= te->y->viral_state_flags;
+ } else {
+ error(node, "A ternary when expression must have an else clause");
+ return kind;
+ }
+ }
+ return kind;
+}
+
+ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(oe, OrElseExpr, node);
+
+ String name = oe->token.string;
+ Ast *arg = oe->x;
+ Ast *default_value = oe->y;
+
+ Operand x = {};
+ Operand y = {};
+ check_multi_expr_with_type_hint(c, &x, arg, type_hint);
+ if (x.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ check_multi_expr_with_type_hint(c, &y, default_value, x.type);
+ error_operand_no_value(&y);
+ if (y.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_else_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
+
+ if (left_type != nullptr) {
+ check_assignment(c, &y, left_type, name);
+ } else {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ }
+
+ if (left_type == nullptr) {
+ left_type = t_invalid;
+ }
+ o->mode = Addressing_Value;
+ o->type = left_type;
+ o->expr = node;
+ return Expr_Expr;
+}
+
+ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(re, OrReturnExpr, node);
+
+ String name = re->token.string;
+ Operand x = {};
+ check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
+ if (x.mode == Addressing_Invalid) {
+ o->mode = Addressing_Value;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Expr;
+ }
+
+ Type *left_type = nullptr;
+ Type *right_type = nullptr;
+ check_or_return_split_types(c, &x, name, &left_type, &right_type);
+ add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
+
+ if (right_type == nullptr) {
+ check_or_else_expr_no_value_error(c, name, x, type_hint);
+ } else {
+ Type *proc_type = base_type(c->curr_proc_sig);
+ GB_ASSERT(proc_type->kind == Type_Proc);
+ Type *result_type = proc_type->Proc.results;
+ if (result_type == nullptr) {
+ error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
+ } else {
+ GB_ASSERT(result_type->kind == Type_Tuple);
+
+ auto const &vars = result_type->Tuple.variables;
+ Type *end_type = vars[vars.count-1]->type;
+
+ if (vars.count > 1) {
+ if (!proc_type->Proc.has_named_results) {
+ error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
+ }
+ }
+
+ Operand rhs = {};
+ rhs.type = right_type;
+ rhs.mode = Addressing_Value;
+
+ // TODO(bill): better error message
+ if (!check_is_assignable_to(c, &rhs, end_type)) {
+ gbString a = type_to_string(right_type);
+ gbString b = type_to_string(end_type);
+ gbString ret_type = type_to_string(result_type);
+ error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
+ if (vars.count == 1) {
+ error_line("\tProcedure return value type: %s\n", ret_type);
+ } else {
+ error_line("\tProcedure return value types: (%s)\n", ret_type);
+ }
+ gb_string_free(ret_type);
+ gb_string_free(b);
+ gb_string_free(a);
+ }
+ }
+ }
+
+ o->expr = node;
+ o->type = left_type;
+ if (left_type != nullptr) {
+ o->mode = Addressing_Value;
+ } else {
+ o->mode = Addressing_NoValue;
+ }
+
+ if (c->curr_proc_sig == nullptr) {
+ error(node, "'%.*s' can only be used within a procedure", LIT(name));
+ }
+
+ if (c->in_defer) {
+ error(node, "'or_return' cannot be used within a defer statement");
+ }
+
+ return Expr_Expr;
+}
+
+ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(cl, CompoundLit, node);
+
+ Type *type = type_hint;
+ if (type != nullptr && is_type_untyped(type)) {
+ type = nullptr;
+ }
+ bool is_to_be_determined_array_count = false;
+ bool is_constant = true;
+ if (cl->type != nullptr) {
+ type = nullptr;
+
+ // [?]Type
+ if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
+ Ast *count = cl->type->ArrayType.count;
+ if (count->kind == Ast_UnaryExpr &&
+ count->UnaryExpr.op.kind == Token_Question) {
+ type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
+ is_to_be_determined_array_count = true;
+ }
+ if (cl->elems.count > 0) {
+ if (cl->type->ArrayType.tag != nullptr) {
+ Ast *tag = cl->type->ArrayType.tag;
+ GB_ASSERT(tag->kind == Ast_BasicDirective);
+ String name = tag->BasicDirective.name.string;
+ if (name == "soa") {
+ error(node, "#soa arrays are not supported for compound literals");
+ return kind;
+ }
+ }
+ }
+ }
+ if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
+ if (cl->elems.count > 0) {
+ Ast *tag = cl->type->DynamicArrayType.tag;
+ GB_ASSERT(tag->kind == Ast_BasicDirective);
+ String name = tag->BasicDirective.name.string;
+ if (name == "soa") {
+ error(node, "#soa arrays are not supported for compound literals");
+ return kind;
+ }
+ }
+ }
+
+ if (type == nullptr) {
+ type = check_type(c, cl->type);
+ }
+ }
+
+ if (type == nullptr) {
+ error(node, "Missing type in compound literal");
+ return kind;
+ }
+
+
+ Type *t = base_type(type);
+ if (is_type_polymorphic(t)) {
+ gbString str = type_to_string(type);
+ error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
+ o->expr = node;
+ o->type = type;
+ gb_string_free(str);
+ return kind;
+ }
+
+
+ switch (t->kind) {
+ case Type_Struct: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ if (t->Struct.is_raw_union) {
+ if (cl->elems.count > 0) {
+ // NOTE: unions cannot be constant
+ is_constant = false;
+
+ if (cl->elems[0]->kind != Ast_FieldValue) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
+ gb_string_free(type_str);
+ } else {
+ if (cl->elems.count != 1) {
+ gbString type_str = type_to_string(type);
+ error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
+ gb_string_free(type_str);
+ } else {
+ Ast *elem = cl->elems[0];
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ break;
+ }
+
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ break;
+ }
+
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ break;
+ }
+
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
+
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
+
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+
+ }
+ }
+ break;
+ }
+
+
+ isize field_count = t->Struct.fields.count;
+ isize min_field_count = t->Struct.fields.count;
+ for (isize i = min_field_count-1; i >= 0; i--) {
+ Entity *e = t->Struct.fields[i];
+ GB_ASSERT(e->kind == Entity_Variable);
+ if (e->Variable.param_value.kind != ParameterValue_Invalid) {
+ min_field_count--;
+ } else {
+ break;
+ }
+ }
+
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in structure literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ bool is_unknown = sel.entity == nullptr;
+ if (is_unknown) {
+ error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
+
+ if (sel.index.count > 1) {
+ error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
+ continue;
+ }
+
+ Entity *field = t->Struct.fields[sel.index[0]];
+ add_entity_use(c, fv->field, field);
+
+ if (fields_visited[sel.index[0]]) {
+ error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
+ continue;
+ }
+
+ fields_visited[sel.index[0]] = true;
+
+ Operand o = {};
+ check_expr_or_type(c, &o, fv->value, field->type);
+
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ } else {
+ bool seen_field_value = false;
+
+ for_array(index, cl->elems) {
+ Entity *field = nullptr;
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ seen_field_value = true;
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ } else if (seen_field_value) {
+ error(elem, "Value elements cannot be used after a 'field = value'");
+ continue;
+ }
+ if (index >= field_count) {
+ error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ break;
+ }
+
+ if (field == nullptr) {
+ field = t->Struct.fields[index];
+ }
+
+ Operand o = {};
+ check_expr_or_type(c, &o, elem, field->type);
+
+ if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
+ is_constant = false;
+ }
+ if (is_constant) {
+ is_constant = check_is_operand_compound_lit_constant(c, &o);
+ }
+
+ check_assignment(c, &o, field->type, str_lit("structure literal"));
+ }
+ if (cl->elems.count < field_count) {
+ if (min_field_count < field_count) {
+ if (cl->elems.count < min_field_count) {
+ error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
+ }
+ } else {
+ error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_Slice:
+ case Type_Array:
+ case Type_DynamicArray:
+ case Type_SimdVector:
+ case Type_Matrix:
+ {
+ Type *elem_type = nullptr;
+ String context_name = {};
+ i64 max_type_count = -1;
+ if (t->kind == Type_Slice) {
+ elem_type = t->Slice.elem;
+ context_name = str_lit("slice literal");
+ } else if (t->kind == Type_Array) {
+ elem_type = t->Array.elem;
+ context_name = str_lit("array literal");
+ if (!is_to_be_determined_array_count) {
+ max_type_count = t->Array.count;
+ }
+ } else if (t->kind == Type_DynamicArray) {
+ elem_type = t->DynamicArray.elem;
+ context_name = str_lit("dynamic array literal");
+ is_constant = false;
+
+ if (!build_context.no_dynamic_literals) {
+ add_package_dependency(c, "runtime", "__dynamic_array_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_array_append");
+ }
+ } else if (t->kind == Type_SimdVector) {
+ elem_type = t->SimdVector.elem;
+ context_name = str_lit("simd vector literal");
+ max_type_count = t->SimdVector.count;
+ } else if (t->kind == Type_Matrix) {
+ elem_type = t->Matrix.elem;
+ context_name = str_lit("matrix literal");
+ max_type_count = t->Matrix.row_count*t->Matrix.column_count;
+ } else {
+ GB_PANIC("unreachable");
+ }
+
+
+ i64 max = 0;
+
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
+
+ if (bet == t_invalid) {
+ break;
+ }
+
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ if (is_type_simd_vector(t)) {
+ error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
+ } else {
+ RangeCache rc = range_cache_make(heap_allocator());
+ defer (range_cache_destroy(&rc));
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (is_ast_range(fv->field)) {
+ Token op = fv->field->BinaryExpr.op;
+
+ Operand x = {};
+ Operand y = {};
+ bool ok = check_range(c, fv->field, &x, &y, nullptr);
+ if (!ok) {
+ continue;
+ }
+ if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
+ error(x.expr, "Expected a constant integer as an array field");
+ continue;
+ }
+
+ if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
+ error(y.expr, "Expected a constant integer as an array field");
+ continue;
+ }
+
+ i64 lo = exact_value_to_i64(x.value);
+ i64 hi = exact_value_to_i64(y.value);
+ i64 max_index = hi;
+ if (op.kind == Token_RangeHalf) { // ..< (exclusive)
+ hi -= 1;
+ } else { // .. (inclusive)
+ max_index += 1;
+ }
+
+ bool new_range = range_cache_add_range(&rc, lo, hi);
+ if (!new_range) {
+ error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
+ continue;
+ }
+
+
+ if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
+ continue;
+ }
+ if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
+ continue;
+ }
+
+ if (max < hi) {
+ max = max_index;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ } else {
+ Operand op_index = {};
+ check_expr(c, &op_index, fv->field);
+
+ if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
+ error(elem, "Expected a constant integer as an array field");
+ continue;
+ }
+ // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
+
+ i64 index = exact_value_to_i64(op_index.value);
+
+ if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
+ error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
+ continue;
+ }
+
+ bool new_index = range_cache_add_index(&rc, index);
+ if (!new_index) {
+ error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
+ continue;
+ }
+
+ if (max < index+1) {
+ max = index+1;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+ }
+
+ cl->max_count = max;
+ }
+
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
+
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
+ }
+
+
+ if (t->kind == Type_Array) {
+ if (is_to_be_determined_array_count) {
+ t->Array.count = max;
+ } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->Array.count) {
+ error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
+ }
+ }
+ }
+
+
+ if (t->kind == Type_SimdVector) {
+ if (!is_constant) {
+ error(node, "Expected all constant elements for a simd vector");
+ }
+ }
+
+
+ if (t->kind == Type_DynamicArray) {
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ }
+ }
+
+ if (t->kind == Type_Matrix) {
+ if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < max_type_count) {
+ error(node, "Expected %lld values for this matrix literal, got %lld", cast(long long)max_type_count, cast(long long)max);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_EnumeratedArray:
+ {
+ Type *elem_type = t->EnumeratedArray.elem;
+ Type *index_type = t->EnumeratedArray.index;
+ String context_name = str_lit("enumerated array literal");
+ i64 max_type_count = t->EnumeratedArray.count;
+
+ gbString index_type_str = type_to_string(index_type);
+ defer (gb_string_free(index_type_str));
+
+ i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
+ i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
+
+ String total_lo_string = {};
+ String total_hi_string = {};
+ GB_ASSERT(is_type_enum(index_type));
+ {
+ Type *bt = base_type(index_type);
+ GB_ASSERT(bt->kind == Type_Enum);
+ for_array(i, bt->Enum.fields) {
+ Entity *f = bt->Enum.fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
+ total_lo_string = f->token.string;
+ }
+ if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
+ total_hi_string = f->token.string;
+ }
+ if (total_lo_string.len != 0 && total_hi_string.len != 0) {
+ break;
+ }
+ }
+ }
+
+ i64 max = 0;
+
+ Type *bet = base_type(elem_type);
+ if (!elem_type_can_be_constant(bet)) {
+ is_constant = false;
+ }
+
+ if (bet == t_invalid) {
+ break;
+ }
+ bool is_partial = cl->tag && (cl->tag->BasicDirective.name.string == "partial");
+
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ map_init(&seen, heap_allocator());
+ defer (map_destroy(&seen));
+
+ if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
+ RangeCache rc = range_cache_make(heap_allocator());
+ defer (range_cache_destroy(&rc));
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (is_ast_range(fv->field)) {
+ Token op = fv->field->BinaryExpr.op;
+
+ Operand x = {};
+ Operand y = {};
+ bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
+ if (!ok) {
+ continue;
+ }
+ if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
+
+ if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
+ error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
+
+ i64 lo = exact_value_to_i64(x.value);
+ i64 hi = exact_value_to_i64(y.value);
+ i64 max_index = hi;
+ if (op.kind == Token_RangeHalf) {
+ hi -= 1;
+ }
+
+ bool new_range = range_cache_add_range(&rc, lo, hi);
+ if (!new_range) {
+ gbString lo_str = expr_to_string(x.expr);
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
+ gb_string_free(hi_str);
+ gb_string_free(lo_str);
+ continue;
+ }
+
+
+ // NOTE(bill): These are sanity checks for invalid enum values
+ if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
+ gbString lo_str = expr_to_string(x.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(lo_str);
+ continue;
+ }
+ if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
+ gbString hi_str = expr_to_string(y.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(hi_str);
+ continue;
+ }
+
+ if (max < hi) {
+ max = max_index;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ TokenKind upper_op = Token_LtEq;
+ if (op.kind == Token_RangeHalf) {
+ upper_op = Token_Lt;
+ }
+ add_to_seen_map(c, &seen, upper_op, x, x, y);
+ } else {
+ Operand op_index = {};
+ check_expr_with_type_hint(c, &op_index, fv->field, index_type);
+
+ if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
+ error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
+ continue;
+ }
+
+ i64 index = exact_value_to_i64(op_index.value);
+
+ if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
+ }
+
+ bool new_index = range_cache_add_index(&rc, index);
+ if (!new_index) {
+ gbString idx_str = expr_to_string(op_index.expr);
+ error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
+ gb_string_free(idx_str);
+ continue;
+ }
+
+ if (max < index+1) {
+ max = index+1;
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, fv->value, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+
+ add_to_seen_map(c, &seen, op_index);
+ }
+ }
+
+ cl->max_count = max;
+
+ } else {
+ isize index = 0;
+ for (; index < cl->elems.count; index++) {
+ Ast *e = cl->elems[index];
+ if (e == nullptr) {
+ error(node, "Invalid literal element");
+ continue;
+ }
+
+ if (e->kind == Ast_FieldValue) {
+ error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
+ continue;
+ }
+
+ if (0 <= max_type_count && max_type_count <= index) {
+ error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
+ }
+
+ Operand operand = {};
+ check_expr_with_type_hint(c, &operand, e, elem_type);
+ check_assignment(c, &operand, elem_type, context_name);
+
+ is_constant = is_constant && operand.mode == Addressing_Constant;
+ }
+
+ if (max < index) {
+ max = index;
+ }
+ }
+
+ bool was_error = false;
+ if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
+ if (0 < max && max < t->EnumeratedArray.count) {
+ error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
+ was_error = true;
+ } else {
+ error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
+ was_error = true;
+ }
+ }
+
+ // NOTE(bill): Check for missing cases when `#partial literal` is not present
+ if (cl->elems.count > 0 && !was_error && !is_partial) {
+ Type *et = base_type(index_type);
+ GB_ASSERT(et->kind == Type_Enum);
+ auto fields = et->Enum.fields;
+
+ auto unhandled = array_make(temporary_allocator(), 0, fields.count);
+
+ for_array(i, fields) {
+ Entity *f = fields[i];
+ if (f->kind != Entity_Constant) {
+ continue;
+ }
+ ExactValue v = f->Constant.value;
+ auto found = map_get(&seen, hash_exact_value(v));
+ if (!found) {
+ array_add(&unhandled, f);
+ }
+ }
+
+ if (unhandled.count > 0) {
+ begin_error_block();
+ defer (end_error_block());
+
+ if (unhandled.count == 1) {
+ error_no_newline(node, "Unhandled enumerated array case: %.*s", LIT(unhandled[0]->token.string));
+ } else {
+ error(node, "Unhandled enumerated array cases:");
+ for_array(i, unhandled) {
+ Entity *f = unhandled[i];
+ error_line("\t%.*s\n", LIT(f->token.string));
+ }
+ }
+ error_line("\n");
+
+ error_line("\tSuggestion: Was '#partial %s{...}' wanted?\n", type_to_string(type));
+ }
+ }
+
+ break;
+ }
+
+ case Type_Basic: {
+ if (!is_type_any(t)) {
+ if (cl->elems.count != 0) {
+ gbString s = type_to_string(t);
+ error(node, "Illegal compound literal, %s cannot be used as a compound literal with fields", s);
+ gb_string_free(s);
+ is_constant = false;
+ }
+ break;
+ }
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ { // Checker values
+ Type *field_types[2] = {t_rawptr, t_typeid};
+ isize field_count = 2;
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ bool fields_visited[2] = {};
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+ if (fv->field->kind != Ast_Ident) {
+ gbString expr_str = expr_to_string(fv->field);
+ error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
+ gb_string_free(expr_str);
+ continue;
+ }
+ String name = fv->field->Ident.token.string;
+
+ Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
+ if (sel.entity == nullptr) {
+ error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
+ continue;
+ }
+
+ isize index = sel.index[0];
+
+ if (fields_visited[index]) {
+ error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
+ continue;
+ }
+
+ fields_visited[index] = true;
+ check_expr(c, o, fv->value);
+
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
+
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
+ } else {
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
+ continue;
+ }
+
+
+ check_expr(c, o, elem);
+ if (index >= field_count) {
+ error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
+ break;
+ }
+
+ // NOTE(bill): 'any' literals can never be constant
+ is_constant = false;
+
+ check_assignment(c, o, field_types[index], str_lit("'any' literal"));
+ }
+ if (cl->elems.count < field_count) {
+ error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case Type_Map: {
+ if (cl->elems.count == 0) {
+ break;
+ }
+ is_constant = false;
+ { // Checker values
+ bool key_is_typeid = is_type_typeid(t->Map.key);
+ bool value_is_typeid = is_type_typeid(t->Map.value);
+
+ for_array(i, cl->elems) {
+ Ast *elem = cl->elems[i];
+ if (elem->kind != Ast_FieldValue) {
+ error(elem, "Only 'field = value' elements are allowed in a map literal");
+ continue;
+ }
+ ast_node(fv, FieldValue, elem);
+
+ if (key_is_typeid) {
+ check_expr_or_type(c, o, fv->field, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, o, fv->field, t->Map.key);
+ }
+ check_assignment(c, o, t->Map.key, str_lit("map literal"));
+ if (o->mode == Addressing_Invalid) {
+ continue;
+ }
+
+ if (value_is_typeid) {
+ check_expr_or_type(c, o, fv->value, t->Map.value);
+ } else {
+ check_expr_with_type_hint(c, o, fv->value, t->Map.value);
+ }
+ check_assignment(c, o, t->Map.value, str_lit("map literal"));
+ }
+ }
+
+ if (build_context.no_dynamic_literals && cl->elems.count) {
+ error(node, "Compound literals of dynamic types have been disabled");
+ } else {
+ add_package_dependency(c, "runtime", "__dynamic_map_reserve");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
+ }
+ break;
+ }
+
+ case Type_BitSet: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+ Type *et = base_type(t->BitSet.elem);
+ isize field_count = 0;
+ if (et->kind == Type_Enum) {
+ field_count = et->Enum.fields.count;
+ }
+
+ if (cl->elems[0]->kind == Ast_FieldValue) {
+ error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
+ is_constant = false;
+ } else {
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ if (elem->kind == Ast_FieldValue) {
+ error(elem, "'field = value' in a bit_set a literal is not allowed");
+ continue;
+ }
+
+ check_expr_with_type_hint(c, o, elem, et);
+
+ if (is_constant) {
+ is_constant = o->mode == Addressing_Constant;
+ }
+
+ check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+ if (o->mode == Addressing_Constant) {
+ i64 lower = t->BitSet.lower;
+ i64 upper = t->BitSet.upper;
+ i64 v = exact_value_to_i64(o->value);
+ if (lower <= v && v <= upper) {
+ // okay
+ } else {
+ error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
+ continue;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ default: {
+ if (cl->elems.count == 0) {
+ break; // NOTE(bill): No need to init
+ }
+
+ gbString str = type_to_string(type);
+ error(node, "Invalid compound literal type '%s'", str);
+ gb_string_free(str);
+ return kind;
+ }
+ }
+
+ if (is_constant) {
+ o->mode = Addressing_Constant;
+
+ if (is_type_bit_set(type)) {
+ // NOTE(bill): Encode as an integer
+
+ i64 lower = base_type(type)->BitSet.lower;
+
+ u64 bits = 0;
+ for_array(index, cl->elems) {
+ Ast *elem = cl->elems[index];
+ GB_ASSERT(elem->kind != Ast_FieldValue);
+ TypeAndValue tav = elem->tav;
+ ExactValue i = exact_value_to_integer(tav.value);
+ if (i.kind != ExactValue_Integer) {
+ continue;
+ }
+ i64 val = big_int_to_i64(&i.value_integer);
+ val -= lower;
+ u64 bit = u64(1ll<value = exact_value_u64(bits);
+ } else if (is_type_constant_type(type) && cl->elems.count == 0) {
+ ExactValue value = exact_value_compound(node);
+ Type *bt = core_type(type);
+ if (bt->kind == Type_Basic) {
+ if (bt->Basic.flags & BasicFlag_Boolean) {
+ value = exact_value_bool(false);
+ } else if (bt->Basic.flags & BasicFlag_Integer) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Unsigned) {
+ value = exact_value_i64(0);
+ } else if (bt->Basic.flags & BasicFlag_Float) {
+ value = exact_value_float(0);
+ } else if (bt->Basic.flags & BasicFlag_Complex) {
+ value = exact_value_complex(0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Quaternion) {
+ value = exact_value_quaternion(0, 0, 0, 0);
+ } else if (bt->Basic.flags & BasicFlag_Pointer) {
+ value = exact_value_pointer(0);
+ } else if (bt->Basic.flags & BasicFlag_String) {
+ String empty_string = {};
+ value = exact_value_string(empty_string);
+ } else if (bt->Basic.flags & BasicFlag_Rune) {
+ value = exact_value_i64(0);
+ }
+ }
+
+ o->value = value;
+ } else {
+ o->value = exact_value_compound(node);
+ }
+ } else {
+ o->mode = Addressing_Value;
+ }
+ o->type = type;
+ return kind;
+}
+
+ExprKind check_type_assertion(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ta, TypeAssertion, node);
+ check_expr(c, o, ta->expr);
+ node->viral_state_flags |= ta->expr->viral_state_flags;
+
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+ if (o->mode == Addressing_Constant) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (is_type_untyped(o->type)) {
+ gbString expr_str = expr_to_string(o->expr);
+ error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
+ gb_string_free(expr_str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ Type *src = type_deref(o->type);
+ Type *bsrc = base_type(src);
+
+
+ if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
+ if (!is_type_union(src)) {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
+ bool allowed = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, type_hint)) {
+ allowed = true;
+ add_type_info_type(c, vt);
+ break;
+ }
+ }
+ if (allowed) {
+ add_type_info_type(c, o->type);
+ o->type = type_hint;
+ o->mode = Addressing_OptionalOk;
+ return kind;
+ }
+ }
+
+ if (bsrc->Union.variants.count != 1) {
+ error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, bsrc->Union.variants[0]);
+
+ o->type = bsrc->Union.variants[0];
+ o->mode = Addressing_OptionalOk;
+ } else {
+ Type *t = check_type(c, ta->type);
+ Type *dst = t;
+
+ if (is_type_union(src)) {
+ bool ok = false;
+ for_array(i, bsrc->Union.variants) {
+ Type *vt = bsrc->Union.variants[i];
+ if (are_types_identical(vt, dst)) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (!ok) {
+ gbString expr_str = expr_to_string(o->expr);
+ gbString dst_type_str = type_to_string(t);
+ defer (gb_string_free(expr_str));
+ defer (gb_string_free(dst_type_str));
+ if (bsrc->Union.variants.count == 0) {
+ error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
+ } else {
+ error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
+ }
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+ } else if (is_type_any(src)) {
+ o->type = t;
+ o->mode = Addressing_OptionalOk;
+
+ add_type_info_type(c, o->type);
+ add_type_info_type(c, t);
+ } else {
+ gbString str = type_to_string(o->type);
+ error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
+
+ if ((c->state_flags & StateFlag_no_type_assert) == 0) {
+ add_package_dependency(c, "runtime", "type_assertion_check");
+ add_package_dependency(c, "runtime", "type_assertion_check2");
+ }
+ return kind;
+}
+
+ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ast_node(se, SelectorCallExpr, node);
+ // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
+ // COM APIs is a great example of where this kind of thing is extremely useful
+ // General idea:
+ //
+ // x->y(123) == x.y(x, 123)
+ //
+ // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
+ // Just regenerating a new AST aids things
+ //
+ // TODO(bill): Is this a good hack or not?
+ //
+ // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
+
+
+ if (se->modified_call) {
+ // Prevent double evaluation
+ o->expr = node;
+ o->type = node->tav.type;
+ o->value = node->tav.value;
+ o->mode = node->tav.mode;
+ return Expr_Expr;
+ }
+
+ bool allow_arrow_right_selector_expr;
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ Operand x = {};
+ ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
+ if (x.mode == Addressing_Invalid || x.type == t_invalid) {
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return kind;
+ }
+ if (!is_type_proc(x.type)) {
+ gbString type_str = type_to_string(x.type);
+ error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
+ gb_string_free(type_str);
+
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
+
+ ast_node(ce, CallExpr, se->call);
+
+ GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
+
+ Ast *first_arg = x.expr->SelectorExpr.expr;
+ GB_ASSERT(first_arg != nullptr);
+
+ Type *pt = base_type(x.type);
+ GB_ASSERT(pt->kind == Type_Proc);
+ Type *first_type = nullptr;
+ String first_arg_name = {};
+ if (pt->Proc.param_count > 0) {
+ Entity *f = pt->Proc.params->Tuple.variables[0];
+ first_type = f->type;
+ first_arg_name = f->token.string;
+ }
+ if (first_arg_name.len == 0) {
+ first_arg_name = str_lit("_");
+ }
+
+ if (first_type == nullptr) {
+ error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
+ o->mode = Addressing_Invalid;
+ o->type = t_invalid;
+ o->expr = node;
+ return Expr_Stmt;
+ }
+
+ Operand y = {};
+ y.mode = first_arg->tav.mode;
+ y.type = first_arg->tav.type;
+ y.value = first_arg->tav.value;
+ if (check_is_assignable_to(c, &y, first_type)) {
+ // Do nothing, it's valid
+ } else {
+ Operand z = y;
+ z.type = type_deref(y.type);
+ if (check_is_assignable_to(c, &z, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_Pointer};
+ first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
+ } else if (y.mode == Addressing_Variable) {
+ Operand w = y;
+ w.type = alloc_type_pointer(y.type);
+ if (check_is_assignable_to(c, &w, first_type)) {
+ // NOTE(bill): AST GENERATION HACK!
+ Token op = {Token_And};
+ first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
+ }
+ }
+ }
+
+ if (ce->args.count > 0) {
+ bool fail = false;
+ bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+ for_array(i, ce->args) {
+ Ast *arg = ce->args[i];
+ bool mix = false;
+ if (first_is_field_value) {
+ mix = arg->kind != Ast_FieldValue;
+ } else {
+ mix = arg->kind == Ast_FieldValue;
+ }
+ if (mix) {
+ fail = true;
+ break;
+ }
+ }
+ if (!fail && first_is_field_value) {
+ Token op = {Token_Eq};
+ AstFile *f = first_arg->file();
+ first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
+ }
+ }
+
+
+
+ auto modified_args = slice_make(heap_allocator(), ce->args.count+1);
+ modified_args[0] = first_arg;
+ slice_copy(&modified_args, ce->args, 1);
+ ce->args = modified_args;
+ se->modified_call = true;
+
+ allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
+ c->allow_arrow_right_selector_expr = true;
+ check_expr_base(c, o, se->call, type_hint);
+ c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
+
+ o->expr = node;
+ return Expr_Expr;
+}
+
+
+ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Expr;
+ ast_node(ie, IndexExpr, node);
+ check_expr(c, o, ie->expr);
+ node->viral_state_flags |= ie->expr->viral_state_flags;
+ if (o->mode == Addressing_Invalid) {
+ o->expr = node;
+ return kind;
+ }
+
+ Type *t = base_type(type_deref(o->type));
+ bool is_ptr = is_type_pointer(o->type);
+ bool is_const = o->mode == Addressing_Constant;
+
+ if (is_type_map(t)) {
+ Operand key = {};
+ if (is_type_typeid(t->Map.key)) {
+ check_expr_or_type(c, &key, ie->index, t->Map.key);
+ } else {
+ check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
+ }
+ check_assignment(c, &key, t->Map.key, str_lit("map index"));
+ if (key.mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ o->mode = Addressing_MapIndex;
+ o->type = t->Map.value;
+ o->expr = node;
+
+ add_package_dependency(c, "runtime", "__dynamic_map_get");
+ add_package_dependency(c, "runtime", "__dynamic_map_set");
+ return Expr_Expr;
+ }
+
+ i64 max_count = -1;
+ bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
+
+ if (is_const) {
+ if (is_type_array(t)) {
+ // OKay
+ } else if (is_type_slice(t)) {
+ // Okay
+ } else if (is_type_enumerated_array(t)) {
+ // Okay
+ } else if (is_type_string(t)) {
+ // Okay
+ } else if (is_type_relative_slice(t)) {
+ // Okay
+ } else if (is_type_matrix(t)) {
+ // Okay
+ } else {
+ valid = false;
+ }
+ }
+
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ defer (gb_string_free(str));
+ defer (gb_string_free(type_str));
+ if (is_const) {
+ error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
+ } else {
+ error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
+ }
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (ie->index == nullptr) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Missing index for '%s'", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ Type *index_type_hint = nullptr;
+ if (is_type_enumerated_array(t)) {
+ Type *bt = base_type(t);
+ GB_ASSERT(bt->kind == Type_EnumeratedArray);
+ index_type_hint = bt->EnumeratedArray.index;
+ }
+
+ i64 index = 0;
+ bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
+ if (is_const) {
+ if (index < 0) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot index a constant '%s'", str);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ } else if (ok) {
+ ExactValue value = type_and_value_of_expr(ie->expr).value;
+ o->mode = Addressing_Constant;
+ bool success = false;
+ bool finish = false;
+ o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
+ if (!success) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ }
+ }
+
+ if (type_hint != nullptr && is_type_matrix(t)) {
+ // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
+ // if a type hint exists
+ }
+ return kind;
+}
+
+ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
+ ExprKind kind = Expr_Stmt;
+ ast_node(se, SliceExpr, node);
+ check_expr(c, o, se->expr);
+ node->viral_state_flags |= se->expr->viral_state_flags;
+
+ if (o->mode == Addressing_Invalid) {
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ bool valid = false;
+ i64 max_count = -1;
+ Type *t = base_type(type_deref(o->type));
+ switch (t->kind) {
+ case Type_Basic:
+ if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
+ valid = true;
+ if (o->mode == Addressing_Constant) {
+ max_count = o->value.value_string.len;
+ }
+ o->type = type_deref(o->type);
+ }
+ break;
+
+ case Type_Array:
+ valid = true;
+ max_count = t->Array.count;
+ if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot slice array '%s', value is not addressable", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ o->type = alloc_type_slice(t->Array.elem);
+ break;
+
+ case Type_MultiPointer:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
+
+ case Type_Slice:
+ valid = true;
+ o->type = type_deref(o->type);
+ break;
+
+ case Type_DynamicArray:
+ valid = true;
+ o->type = alloc_type_slice(t->DynamicArray.elem);
+ break;
+
+ case Type_Struct:
+ if (is_type_soa_struct(t)) {
+ valid = true;
+ o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
+ }
+ break;
+
+ case Type_RelativeSlice:
+ valid = true;
+ o->type = t->RelativeSlice.slice_type;
+ if (o->mode != Addressing_Variable) {
+ gbString str = expr_to_string(node);
+ error(node, "Cannot relative slice '%s', value is not addressable", str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+ break;
+ }
+
+ if (!valid) {
+ gbString str = expr_to_string(o->expr);
+ gbString type_str = type_to_string(o->type);
+ error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
+ gb_string_free(type_str);
+ gb_string_free(str);
+ o->mode = Addressing_Invalid;
+ o->expr = node;
+ return kind;
+ }
+
+ if (se->low == nullptr && se->high != nullptr) {
+ // It is okay to continue as it will assume the 1st index is zero
+ }
+
+ i64 indices[2] = {};
+ Ast *nodes[2] = {se->low, se->high};
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ i64 index = max_count;
+ if (nodes[i] != nullptr) {
+ i64 capacity = -1;
+ if (max_count >= 0) {
+ capacity = max_count;
+ }
+ i64 j = 0;
+ if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
+ index = j;
+ }
+
+ node->viral_state_flags |= nodes[i]->viral_state_flags;
+ } else if (i == 0) {
+ index = 0;
+ }
+ indices[i] = index;
+ }
+
+ for (isize i = 0; i < gb_count_of(indices); i++) {
+ i64 a = indices[i];
+ for (isize j = i+1; j < gb_count_of(indices); j++) {
+ i64 b = indices[j];
+ if (a > b && b >= 0) {
+ error(se->close, "Invalid slice indices: [%td > %td]", a, b);
+ }
+ }
+ }
+
+ if (max_count < 0) {
+ if (o->mode == Addressing_Constant) {
+ gbString s = expr_to_string(se->expr);
+ error(se->expr, "Cannot slice constant value '%s'", s);
+ gb_string_free(s);
+ }
+ }
+
+ if (t->kind == Type_MultiPointer && se->high != nullptr) {
+ /*
+ x[:] -> [^]T
+ x[i:] -> [^]T
+ x[:n] -> []T
+ x[i:n] -> []T
+ */
+ o->type = alloc_type_slice(t->MultiPointer.elem);
+ }
+
+ o->mode = Addressing_Value;
+
+ if (is_type_string(t) && max_count >= 0) {
+ bool all_constant = true;
+ for (isize i = 0; i < gb_count_of(nodes); i++) {
+ if (nodes[i] != nullptr) {
+ TypeAndValue tav = type_and_value_of_expr(nodes[i]);
+ if (tav.mode != Addressing_Constant) {
+ all_constant = false;
+ break;
+ }
+ }
+ }
+ if (!all_constant) {
+ gbString str = expr_to_string(o->expr);
+ error(o->expr, "Cannot slice '%s' with non-constant indices", str);
+ error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
+ gb_string_free(str);
+ o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
+ o->expr = node;
+ return kind;
+ }
+
+ String s = {};
+ if (o->value.kind == ExactValue_String) {
+ s = o->value.value_string;
+ }
+
+ o->mode = Addressing_Constant;
+ o->type = t;
+ o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
+ }
+ return kind;
+}
+
ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
u32 prev_state_flags = c->state_flags;
defer (c->state_flags = prev_state_flags);
@@ -6854,6 +8988,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
c->state_flags = out;
}
@@ -6861,6 +9003,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->mode = Addressing_Invalid;
o->type = t_invalid;
+ o->value = {ExactValue_Invalid};
switch (node->kind) {
default:
@@ -6933,52 +9076,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(bd, BasicDirective, node);
- o->mode = Addressing_Constant;
- String name = bd->name.string;
- if (name == "file") {
- o->type = t_untyped_string;
- o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
- } else if (name == "line") {
- o->type = t_untyped_integer;
- o->value = exact_value_i64(bd->token.pos.line);
- } else if (name == "procedure") {
- if (c->curr_proc_decl == nullptr) {
- error(node, "#procedure may only be used within procedures");
- o->type = t_untyped_string;
- o->value = exact_value_string(str_lit(""));
- } else {
- o->type = t_untyped_string;
- o->value = exact_value_string(c->proc_name);
- }
- } else if (name == "caller_location") {
- init_core_source_code_location(c->checker);
- error(node, "#caller_location may only be used as a default argument parameter");
- o->type = t_source_code_location;
- o->mode = Addressing_Value;
- } else {
- if (name == "location") {
- init_core_source_code_location(c->checker);
- error(node, "'#%.*s' must be used in a call expression", LIT(name));
- o->type = t_source_code_location;
- o->mode = Addressing_Value;
- } else if (
- name == "assert" ||
- name == "defined" ||
- name == "config" ||
- name == "load" ||
- name == "load_hash" ||
- name == "load_or"
- ) {
- error(node, "'#%.*s' must be used as a call", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- } else {
- error(node, "Unknown directive: #%.*s", LIT(name));
- o->type = t_invalid;
- o->mode = Addressing_Invalid;
- }
-
- }
+ kind = check_basic_directive_expr(c, o, node, type_hint);
case_end;
case_ast_node(pg, ProcGroup, node);
@@ -7027,1102 +9125,23 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(te, TernaryIfExpr, node);
- Operand cond = {Addressing_Invalid};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
-
- if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) {
- error(te->cond, "Non-boolean condition in ternary if expression");
- }
-
- Operand x = {Addressing_Invalid};
- Operand y = {Addressing_Invalid};
- check_expr_or_type(c, &x, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
-
- if (te->y != nullptr) {
- check_expr_or_type(c, &y, te->y, type_hint);
- node->viral_state_flags |= te->y->viral_state_flags;
- } else {
- error(node, "A ternary expression must have an else clause");
- return kind;
- }
-
- if (x.type == nullptr || x.type == t_invalid ||
- y.type == nullptr || y.type == t_invalid) {
- return kind;
- }
-
- convert_to_typed(c, &x, y.type);
- if (x.mode == Addressing_Invalid) {
- return kind;
- }
- convert_to_typed(c, &y, x.type);
- if (y.mode == Addressing_Invalid) {
- x.mode = Addressing_Invalid;
- return kind;
- }
-
- if (!ternary_compare_types(x.type, y.type)) {
- gbString its = type_to_string(x.type);
- gbString ets = type_to_string(y.type);
- error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets);
- gb_string_free(ets);
- gb_string_free(its);
- return kind;
- }
-
- o->type = x.type;
- if (is_type_untyped_nil(o->type) || is_type_untyped_undef(o->type)) {
- o->type = y.type;
- }
-
- o->mode = Addressing_Value;
- o->expr = node;
- if (type_hint != nullptr && is_type_untyped(o->type)) {
- if (check_cast_internal(c, &x, type_hint) &&
- check_cast_internal(c, &y, type_hint)) {
- convert_to_typed(c, o, type_hint);
- update_untyped_expr_type(c, node, type_hint, !is_type_untyped(type_hint));
- }
- }
+ kind = check_ternary_if_expr(c, o, node, type_hint);
case_end;
case_ast_node(te, TernaryWhenExpr, node);
- Operand cond = {};
- check_expr(c, &cond, te->cond);
- node->viral_state_flags |= te->cond->viral_state_flags;
-
- if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) {
- error(te->cond, "Expected a constant boolean condition in ternary when expression");
- return kind;
- }
-
- if (cond.value.value_bool) {
- check_expr_or_type(c, o, te->x, type_hint);
- node->viral_state_flags |= te->x->viral_state_flags;
- } else {
- if (te->y != nullptr) {
- check_expr_or_type(c, o, te->y, type_hint);
- node->viral_state_flags |= te->y->viral_state_flags;
- } else {
- error(node, "A ternary when expression must have an else clause");
- return kind;
- }
- }
+ kind = check_ternary_when_expr(c, o, node, type_hint);
case_end;
case_ast_node(oe, OrElseExpr, node);
- String name = oe->token.string;
- Ast *arg = oe->x;
- Ast *default_value = oe->y;
-
- Operand x = {};
- Operand y = {};
- check_multi_expr_with_type_hint(c, &x, arg, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
-
- check_multi_expr_with_type_hint(c, &y, default_value, x.type);
- error_operand_no_value(&y);
- if (y.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
-
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_else_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, arg, x.mode, x.type, x.value);
-
- if (left_type != nullptr) {
- check_assignment(c, &y, left_type, name);
- } else {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
- }
-
- if (left_type == nullptr) {
- left_type = t_invalid;
- }
- o->mode = Addressing_Value;
- o->type = left_type;
- o->expr = node;
- return Expr_Expr;
+ return check_or_else_expr(c, o, node, type_hint);
case_end;
case_ast_node(re, OrReturnExpr, node);
- String name = re->token.string;
- Operand x = {};
- check_multi_expr_with_type_hint(c, &x, re->expr, type_hint);
- if (x.mode == Addressing_Invalid) {
- o->mode = Addressing_Value;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Expr;
- }
-
- Type *left_type = nullptr;
- Type *right_type = nullptr;
- check_or_return_split_types(c, &x, name, &left_type, &right_type);
- add_type_and_value(&c->checker->info, re->expr, x.mode, x.type, x.value);
-
- if (right_type == nullptr) {
- check_or_else_expr_no_value_error(c, name, x, type_hint);
- } else {
- Type *proc_type = base_type(c->curr_proc_sig);
- GB_ASSERT(proc_type->kind == Type_Proc);
- Type *result_type = proc_type->Proc.results;
- if (result_type == nullptr) {
- error(node, "'%.*s' requires the current procedure to have at least one return value", LIT(name));
- } else {
- GB_ASSERT(result_type->kind == Type_Tuple);
-
- auto const &vars = result_type->Tuple.variables;
- Type *end_type = vars[vars.count-1]->type;
-
- if (vars.count > 1) {
- if (!proc_type->Proc.has_named_results) {
- error(node, "'%.*s' within a procedure with more than 1 return value requires that the return values are named, allowing for early return", LIT(name));
- }
- }
-
- Operand rhs = {};
- rhs.type = right_type;
- rhs.mode = Addressing_Value;
-
- // TODO(bill): better error message
- if (!check_is_assignable_to(c, &rhs, end_type)) {
- gbString a = type_to_string(right_type);
- gbString b = type_to_string(end_type);
- gbString ret_type = type_to_string(result_type);
- error(node, "Cannot assign end value of type '%s' to '%s' in '%.*s'", a, b, LIT(name));
- if (vars.count == 1) {
- error_line("\tProcedure return value type: %s\n", ret_type);
- } else {
- error_line("\tProcedure return value types: (%s)\n", ret_type);
- }
- gb_string_free(ret_type);
- gb_string_free(b);
- gb_string_free(a);
- }
- }
- }
-
- o->expr = node;
- o->type = left_type;
- if (left_type != nullptr) {
- o->mode = Addressing_Value;
- } else {
- o->mode = Addressing_NoValue;
- }
-
- if (c->curr_proc_sig == nullptr) {
- error(node, "'%.*s' can only be used within a procedure", LIT(name));
- }
-
- if (c->in_defer) {
- error(node, "'or_return' cannot be used within a defer statement");
- }
-
- return Expr_Expr;
+ return check_or_return_expr(c, o, node, type_hint);
case_end;
case_ast_node(cl, CompoundLit, node);
- Type *type = type_hint;
- if (type != nullptr && is_type_untyped(type)) {
- type = nullptr;
- }
- bool is_to_be_determined_array_count = false;
- bool is_constant = true;
- if (cl->type != nullptr) {
- type = nullptr;
-
- // [?]Type
- if (cl->type->kind == Ast_ArrayType && cl->type->ArrayType.count != nullptr) {
- Ast *count = cl->type->ArrayType.count;
- if (count->kind == Ast_UnaryExpr &&
- count->UnaryExpr.op.kind == Token_Question) {
- type = alloc_type_array(check_type(c, cl->type->ArrayType.elem), -1);
- is_to_be_determined_array_count = true;
- }
- if (cl->elems.count > 0) {
- if (cl->type->ArrayType.tag != nullptr) {
- Ast *tag = cl->type->ArrayType.tag;
- GB_ASSERT(tag->kind == Ast_BasicDirective);
- String name = tag->BasicDirective.name.string;
- if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
- }
- }
- }
- }
- if (cl->type->kind == Ast_DynamicArrayType && cl->type->DynamicArrayType.tag != nullptr) {
- if (cl->elems.count > 0) {
- Ast *tag = cl->type->DynamicArrayType.tag;
- GB_ASSERT(tag->kind == Ast_BasicDirective);
- String name = tag->BasicDirective.name.string;
- if (name == "soa") {
- error(node, "#soa arrays are not supported for compound literals");
- return kind;
- }
- }
- }
-
- if (type == nullptr) {
- type = check_type(c, cl->type);
- }
- }
-
- if (type == nullptr) {
- error(node, "Missing type in compound literal");
- return kind;
- }
-
-
- Type *t = base_type(type);
- if (is_type_polymorphic(t)) {
- gbString str = type_to_string(type);
- error(node, "Cannot use a polymorphic type for a compound literal, got '%s'", str);
- o->expr = node;
- o->type = type;
- gb_string_free(str);
- return kind;
- }
-
-
- switch (t->kind) {
- case Type_Struct: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- if (t->Struct.is_raw_union) {
- if (cl->elems.count > 0) {
- // NOTE: unions cannot be constant
- is_constant = false;
-
- if (cl->elems[0]->kind != Ast_FieldValue) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain 'field = value' elements", type_str);
- gb_string_free(type_str);
- } else {
- if (cl->elems.count != 1) {
- gbString type_str = type_to_string(type);
- error(node, "%s ('struct #raw_union') compound literals are only allowed to contain up to 1 'field = value' element, got %td", type_str, cl->elems.count);
- gb_string_free(type_str);
- } else {
- Ast *elem = cl->elems[0];
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- break;
- }
-
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- break;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- break;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
-
- }
- }
- break;
- }
-
-
- isize field_count = t->Struct.fields.count;
- isize min_field_count = t->Struct.fields.count;
- for (isize i = min_field_count-1; i >= 0; i--) {
- Entity *e = t->Struct.fields[i];
- GB_ASSERT(e->kind == Entity_Variable);
- if (e->Variable.param_value.kind != ParameterValue_Invalid) {
- min_field_count--;
- } else {
- break;
- }
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool *fields_visited = gb_alloc_array(temporary_allocator(), bool, field_count);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in structure literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- bool is_unknown = sel.entity == nullptr;
- if (is_unknown) {
- error(elem, "Unknown field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- if (sel.index.count > 1) {
- error(elem, "Cannot assign to an anonymous field '%.*s' in a structure literal (at the moment)", LIT(name));
- continue;
- }
-
- Entity *field = t->Struct.fields[sel.index[0]];
- add_entity_use(c, fv->field, field);
-
- if (fields_visited[sel.index[0]]) {
- error(elem, "Duplicate field '%.*s' in structure literal", LIT(name));
- continue;
- }
-
- fields_visited[sel.index[0]] = true;
-
- Operand o = {};
- check_expr_or_type(c, &o, fv->value, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- } else {
- bool seen_field_value = false;
-
- for_array(index, cl->elems) {
- Entity *field = nullptr;
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- seen_field_value = true;
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- } else if (seen_field_value) {
- error(elem, "Value elements cannot be used after a 'field = value'");
- continue;
- }
- if (index >= field_count) {
- error(elem, "Too many values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- break;
- }
-
- if (field == nullptr) {
- field = t->Struct.fields[index];
- }
-
- Operand o = {};
- check_expr_or_type(c, &o, elem, field->type);
-
- if (is_type_any(field->type) || is_type_union(field->type) || is_type_raw_union(field->type) || is_type_typeid(field->type)) {
- is_constant = false;
- }
- if (is_constant) {
- is_constant = check_is_operand_compound_lit_constant(c, &o);
- }
-
- check_assignment(c, &o, field->type, str_lit("structure literal"));
- }
- if (cl->elems.count < field_count) {
- if (min_field_count < field_count) {
- if (cl->elems.count < min_field_count) {
- error(cl->close, "Too few values in structure literal, expected at least %td, got %td", min_field_count, cl->elems.count);
- }
- } else {
- error(cl->close, "Too few values in structure literal, expected %td, got %td", field_count, cl->elems.count);
- }
- }
- }
-
- break;
- }
-
- case Type_Slice:
- case Type_Array:
- case Type_DynamicArray:
- case Type_SimdVector:
- case Type_Matrix:
- {
- Type *elem_type = nullptr;
- String context_name = {};
- i64 max_type_count = -1;
- if (t->kind == Type_Slice) {
- elem_type = t->Slice.elem;
- context_name = str_lit("slice literal");
- } else if (t->kind == Type_Array) {
- elem_type = t->Array.elem;
- context_name = str_lit("array literal");
- if (!is_to_be_determined_array_count) {
- max_type_count = t->Array.count;
- }
- } else if (t->kind == Type_DynamicArray) {
- elem_type = t->DynamicArray.elem;
- context_name = str_lit("dynamic array literal");
- is_constant = false;
-
- if (!build_context.no_dynamic_literals) {
- add_package_dependency(c, "runtime", "__dynamic_array_reserve");
- add_package_dependency(c, "runtime", "__dynamic_array_append");
- }
- } else if (t->kind == Type_SimdVector) {
- elem_type = t->SimdVector.elem;
- context_name = str_lit("simd vector literal");
- max_type_count = t->SimdVector.count;
- } else if (t->kind == Type_Matrix) {
- elem_type = t->Matrix.elem;
- context_name = str_lit("matrix literal");
- max_type_count = t->Matrix.row_count*t->Matrix.column_count;
- } else {
- GB_PANIC("unreachable");
- }
-
-
- i64 max = 0;
-
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
-
- if (bet == t_invalid) {
- break;
- }
-
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
- if (is_type_simd_vector(t)) {
- error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals");
- } else {
- RangeCache rc = range_cache_make(heap_allocator());
- defer (range_cache_destroy(&rc));
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
-
- if (is_ast_range(fv->field)) {
- Token op = fv->field->BinaryExpr.op;
-
- Operand x = {};
- Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr);
- if (!ok) {
- continue;
- }
- if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) {
- error(x.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) {
- error(y.expr, "Expected a constant integer as an array field");
- continue;
- }
-
- i64 lo = exact_value_to_i64(x.value);
- i64 hi = exact_value_to_i64(y.value);
- i64 max_index = hi;
- if (op.kind == Token_RangeHalf) { // ..< (exclusive)
- hi -= 1;
- } else { // .. (inclusive)
- max_index += 1;
- }
-
- bool new_range = range_cache_add_range(&rc, lo, hi);
- if (!new_range) {
- error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name));
- continue;
- }
-
-
- if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name));
- continue;
- }
- if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name));
- continue;
- }
-
- if (max < hi) {
- max = max_index;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- } else {
- Operand op_index = {};
- check_expr(c, &op_index, fv->field);
-
- if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) {
- error(elem, "Expected a constant integer as an array field");
- continue;
- }
- // add_type_and_value(c->info, op_index.expr, op_index.mode, op_index.type, op_index.value);
-
- i64 index = exact_value_to_i64(op_index.value);
-
- if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) {
- error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name));
- continue;
- }
-
- bool new_index = range_cache_add_index(&rc, index);
- if (!new_index) {
- error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name));
- continue;
- }
-
- if (max < index+1) {
- max = index+1;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- }
-
- cl->max_count = max;
- }
-
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
-
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
-
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
-
- if (max < index) {
- max = index;
- }
- }
-
-
- if (t->kind == Type_Array) {
- if (is_to_be_determined_array_count) {
- t->Array.count = max;
- } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->Array.count) {
- error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max);
- }
- }
- }
-
-
- if (t->kind == Type_SimdVector) {
- if (!is_constant) {
- error(node, "Expected all constant elements for a simd vector");
- }
- }
-
-
- if (t->kind == Type_DynamicArray) {
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- }
- }
-
- break;
- }
-
- case Type_EnumeratedArray:
- {
- Type *elem_type = t->EnumeratedArray.elem;
- Type *index_type = t->EnumeratedArray.index;
- String context_name = str_lit("enumerated array literal");
- i64 max_type_count = t->EnumeratedArray.count;
-
- gbString index_type_str = type_to_string(index_type);
- defer (gb_string_free(index_type_str));
-
- i64 total_lo = exact_value_to_i64(*t->EnumeratedArray.min_value);
- i64 total_hi = exact_value_to_i64(*t->EnumeratedArray.max_value);
-
- String total_lo_string = {};
- String total_hi_string = {};
- GB_ASSERT(is_type_enum(index_type));
- {
- Type *bt = base_type(index_type);
- GB_ASSERT(bt->kind == Type_Enum);
- for_array(i, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[i];
- if (f->kind != Entity_Constant) {
- continue;
- }
- if (total_lo_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.min_value)) {
- total_lo_string = f->token.string;
- }
- if (total_hi_string.len == 0 && compare_exact_values(Token_CmpEq, f->Constant.value, *t->EnumeratedArray.max_value)) {
- total_hi_string = f->token.string;
- }
- if (total_lo_string.len != 0 && total_hi_string.len != 0) {
- break;
- }
- }
- }
-
- i64 max = 0;
-
- Type *bet = base_type(elem_type);
- if (!elem_type_can_be_constant(bet)) {
- is_constant = false;
- }
-
- if (bet == t_invalid) {
- break;
- }
-
- if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) {
- RangeCache rc = range_cache_make(heap_allocator());
- defer (range_cache_destroy(&rc));
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
-
- if (is_ast_range(fv->field)) {
- Token op = fv->field->BinaryExpr.op;
-
- Operand x = {};
- Operand y = {};
- bool ok = check_range(c, fv->field, &x, &y, nullptr, index_type);
- if (!ok) {
- continue;
- }
- if (x.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(x.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
- continue;
- }
-
- if (y.mode != Addressing_Constant || !are_types_identical(x.type, index_type)) {
- error(y.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
- continue;
- }
-
- i64 lo = exact_value_to_i64(x.value);
- i64 hi = exact_value_to_i64(y.value);
- i64 max_index = hi;
- if (op.kind == Token_RangeHalf) {
- hi -= 1;
- }
-
- bool new_range = range_cache_add_range(&rc, lo, hi);
- if (!new_range) {
- gbString lo_str = expr_to_string(x.expr);
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Overlapping field range index %s %.*s %s for %.*s", lo_str, LIT(op.string), hi_str, LIT(context_name));
- gb_string_free(hi_str);
- gb_string_free(lo_str);
- continue;
- }
-
-
- // NOTE(bill): These are sanity checks for invalid enum values
- if (max_type_count >= 0 && (lo < total_lo || lo > total_hi)) {
- gbString lo_str = expr_to_string(x.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", lo_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(lo_str);
- continue;
- }
- if (max_type_count >= 0 && (hi < 0 || hi > total_hi)) {
- gbString hi_str = expr_to_string(y.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", hi_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(hi_str);
- continue;
- }
-
- if (max < hi) {
- max = max_index;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- } else {
- Operand op_index = {};
- check_expr_with_type_hint(c, &op_index, fv->field, index_type);
-
- if (op_index.mode != Addressing_Constant || !are_types_identical(op_index.type, index_type)) {
- error(op_index.expr, "Expected a constant enum of type '%s' as an array field", index_type_str);
- continue;
- }
-
- i64 index = exact_value_to_i64(op_index.value);
-
- if (max_type_count >= 0 && (index < total_lo || index > total_hi)) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Index %s is out of bounds (%.*s .. %.*s) for %.*s", idx_str, LIT(total_lo_string), LIT(total_hi_string), LIT(context_name));
- gb_string_free(idx_str);
- continue;
- }
-
- bool new_index = range_cache_add_index(&rc, index);
- if (!new_index) {
- gbString idx_str = expr_to_string(op_index.expr);
- error(elem, "Duplicate field index %s for %.*s", idx_str, LIT(context_name));
- gb_string_free(idx_str);
- continue;
- }
-
- if (max < index+1) {
- max = index+1;
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, fv->value, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
- }
-
- cl->max_count = max;
-
- } else {
- isize index = 0;
- for (; index < cl->elems.count; index++) {
- Ast *e = cl->elems[index];
- if (e == nullptr) {
- error(node, "Invalid literal element");
- continue;
- }
-
- if (e->kind == Ast_FieldValue) {
- error(e, "Mixture of 'field = value' and value elements in a literal is not allowed");
- continue;
- }
-
- if (0 <= max_type_count && max_type_count <= index) {
- error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name));
- }
-
- Operand operand = {};
- check_expr_with_type_hint(c, &operand, e, elem_type);
- check_assignment(c, &operand, elem_type, context_name);
-
- is_constant = is_constant && operand.mode == Addressing_Constant;
- }
-
- if (max < index) {
- max = index;
- }
- }
-
- if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) {
- if (0 < max && max < t->EnumeratedArray.count) {
- error(node, "Expected %lld values for this enumerated array literal, got %lld", cast(long long)t->EnumeratedArray.count, cast(long long)max);
- } else {
- error(node, "Enumerated array literals must only have 'field = value' elements, bare elements are not allowed");
- }
- }
-
- break;
- }
-
- case Type_Basic: {
- if (!is_type_any(t)) {
- if (cl->elems.count != 0) {
- error(node, "Illegal compound literal");
- }
- break;
- }
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- { // Checker values
- Type *field_types[2] = {t_rawptr, t_typeid};
- isize field_count = 2;
- if (cl->elems[0]->kind == Ast_FieldValue) {
- bool fields_visited[2] = {};
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
- ast_node(fv, FieldValue, elem);
- if (fv->field->kind != Ast_Ident) {
- gbString expr_str = expr_to_string(fv->field);
- error(elem, "Invalid field name '%s' in 'any' literal", expr_str);
- gb_string_free(expr_str);
- continue;
- }
- String name = fv->field->Ident.token.string;
-
- Selection sel = lookup_field(type, name, o->mode == Addressing_Type);
- if (sel.entity == nullptr) {
- error(elem, "Unknown field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
-
- isize index = sel.index[0];
-
- if (fields_visited[index]) {
- error(elem, "Duplicate field '%.*s' in 'any' literal", LIT(name));
- continue;
- }
-
- fields_visited[index] = true;
- check_expr(c, o, fv->value);
-
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
-
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
- }
- } else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- error(elem, "Mixture of 'field = value' and value elements in a 'any' literal is not allowed");
- continue;
- }
-
-
- check_expr(c, o, elem);
- if (index >= field_count) {
- error(o->expr, "Too many values in 'any' literal, expected %td", field_count);
- break;
- }
-
- // NOTE(bill): 'any' literals can never be constant
- is_constant = false;
-
- check_assignment(c, o, field_types[index], str_lit("'any' literal"));
- }
- if (cl->elems.count < field_count) {
- error(cl->close, "Too few values in 'any' literal, expected %td, got %td", field_count, cl->elems.count);
- }
- }
- }
-
- break;
- }
-
- case Type_Map: {
- if (cl->elems.count == 0) {
- break;
- }
- is_constant = false;
- { // Checker values
- bool key_is_typeid = is_type_typeid(t->Map.key);
- bool value_is_typeid = is_type_typeid(t->Map.value);
-
- for_array(i, cl->elems) {
- Ast *elem = cl->elems[i];
- if (elem->kind != Ast_FieldValue) {
- error(elem, "Only 'field = value' elements are allowed in a map literal");
- continue;
- }
- ast_node(fv, FieldValue, elem);
-
- if (key_is_typeid) {
- check_expr_or_type(c, o, fv->field, t->Map.key);
- } else {
- check_expr_with_type_hint(c, o, fv->field, t->Map.key);
- }
- check_assignment(c, o, t->Map.key, str_lit("map literal"));
- if (o->mode == Addressing_Invalid) {
- continue;
- }
-
- if (value_is_typeid) {
- check_expr_or_type(c, o, fv->value, t->Map.value);
- } else {
- check_expr_with_type_hint(c, o, fv->value, t->Map.value);
- }
- check_assignment(c, o, t->Map.value, str_lit("map literal"));
- }
- }
-
- if (build_context.no_dynamic_literals && cl->elems.count) {
- error(node, "Compound literals of dynamic types have been disabled");
- } else {
- add_package_dependency(c, "runtime", "__dynamic_map_reserve");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- }
- break;
- }
-
- case Type_BitSet: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
- Type *et = base_type(t->BitSet.elem);
- isize field_count = 0;
- if (et->kind == Type_Enum) {
- field_count = et->Enum.fields.count;
- }
-
- if (cl->elems[0]->kind == Ast_FieldValue) {
- error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
- is_constant = false;
- } else {
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- if (elem->kind == Ast_FieldValue) {
- error(elem, "'field = value' in a bit_set a literal is not allowed");
- continue;
- }
-
- check_expr_with_type_hint(c, o, elem, et);
-
- if (is_constant) {
- is_constant = o->mode == Addressing_Constant;
- }
-
- check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
- if (o->mode == Addressing_Constant) {
- i64 lower = t->BitSet.lower;
- i64 upper = t->BitSet.upper;
- i64 v = exact_value_to_i64(o->value);
- if (lower <= v && v <= upper) {
- // okay
- } else {
- error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
- continue;
- }
- }
- }
- }
- break;
- }
-
- default: {
- if (cl->elems.count == 0) {
- break; // NOTE(bill): No need to init
- }
-
- gbString str = type_to_string(type);
- error(node, "Invalid compound literal type '%s'", str);
- gb_string_free(str);
- return kind;
- }
- }
-
- if (is_constant) {
- o->mode = Addressing_Constant;
-
- if (is_type_bit_set(type)) {
- // NOTE(bill): Encode as an integer
-
- i64 lower = base_type(type)->BitSet.lower;
-
- u64 bits = 0;
- for_array(index, cl->elems) {
- Ast *elem = cl->elems[index];
- GB_ASSERT(elem->kind != Ast_FieldValue);
- TypeAndValue tav = elem->tav;
- ExactValue i = exact_value_to_integer(tav.value);
- if (i.kind != ExactValue_Integer) {
- continue;
- }
- i64 val = big_int_to_i64(&i.value_integer);
- val -= lower;
- u64 bit = u64(1ll<value = exact_value_u64(bits);
- } else if (is_type_constant_type(type) && cl->elems.count == 0) {
- ExactValue value = exact_value_compound(node);
- Type *bt = core_type(type);
- if (bt->kind == Type_Basic) {
- if (bt->Basic.flags & BasicFlag_Boolean) {
- value = exact_value_bool(false);
- } else if (bt->Basic.flags & BasicFlag_Integer) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Unsigned) {
- value = exact_value_i64(0);
- } else if (bt->Basic.flags & BasicFlag_Float) {
- value = exact_value_float(0);
- } else if (bt->Basic.flags & BasicFlag_Complex) {
- value = exact_value_complex(0, 0);
- } else if (bt->Basic.flags & BasicFlag_Quaternion) {
- value = exact_value_quaternion(0, 0, 0, 0);
- } else if (bt->Basic.flags & BasicFlag_Pointer) {
- value = exact_value_pointer(0);
- } else if (bt->Basic.flags & BasicFlag_String) {
- String empty_string = {};
- value = exact_value_string(empty_string);
- } else if (bt->Basic.flags & BasicFlag_Rune) {
- value = exact_value_i64(0);
- }
- }
-
- o->value = value;
- } else {
- o->value = exact_value_compound(node);
- }
- } else {
- o->mode = Addressing_Value;
- }
- o->type = type;
+ kind = check_compound_literal(c, o, node, type_hint);
case_end;
case_ast_node(pe, ParenExpr, node);
@@ -8142,127 +9161,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(ta, TypeAssertion, node);
- check_expr(c, o, ta->expr);
- node->viral_state_flags |= ta->expr->viral_state_flags;
-
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
- if (o->mode == Addressing_Constant) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to a constant expression: '%s'", expr_str);
- gb_string_free(expr_str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (is_type_untyped(o->type)) {
- gbString expr_str = expr_to_string(o->expr);
- error(o->expr, "A type assertion cannot be applied to an untyped expression: '%s'", expr_str);
- gb_string_free(expr_str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- Type *src = type_deref(o->type);
- Type *bsrc = base_type(src);
-
-
- if (ta->type != nullptr && ta->type->kind == Ast_UnaryExpr && ta->type->UnaryExpr.op.kind == Token_Question) {
- if (!is_type_union(src)) {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions with .? can only operate on unions, got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (bsrc->Union.variants.count != 1 && type_hint != nullptr) {
- bool allowed = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, type_hint)) {
- allowed = true;
- add_type_info_type(c, vt);
- break;
- }
- }
- if (allowed) {
- add_type_info_type(c, o->type);
- o->type = type_hint;
- o->mode = Addressing_OptionalOk;
- return kind;
- }
- }
-
- if (bsrc->Union.variants.count != 1) {
- error(o->expr, "Type assertions with .? can only operate on unions with 1 variant, got %lld", cast(long long)bsrc->Union.variants.count);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, bsrc->Union.variants[0]);
-
- o->type = bsrc->Union.variants[0];
- o->mode = Addressing_OptionalOk;
- } else {
- Type *t = check_type(c, ta->type);
- Type *dst = t;
-
- if (is_type_union(src)) {
- bool ok = false;
- for_array(i, bsrc->Union.variants) {
- Type *vt = bsrc->Union.variants[i];
- if (are_types_identical(vt, dst)) {
- ok = true;
- break;
- }
- }
-
- if (!ok) {
- gbString expr_str = expr_to_string(o->expr);
- gbString dst_type_str = type_to_string(t);
- defer (gb_string_free(expr_str));
- defer (gb_string_free(dst_type_str));
- if (bsrc->Union.variants.count == 0) {
- error(o->expr, "Cannot type assert '%s' to '%s' as this is an empty union", expr_str, dst_type_str);
- } else {
- error(o->expr, "Cannot type assert '%s' to '%s' as it is not a variant of that union", expr_str, dst_type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
-
- o->type = t;
- o->mode = Addressing_OptionalOk;
- } else if (is_type_any(src)) {
- o->type = t;
- o->mode = Addressing_OptionalOk;
-
- add_type_info_type(c, o->type);
- add_type_info_type(c, t);
- } else {
- gbString str = type_to_string(o->type);
- error(o->expr, "Type assertions can only operate on unions and 'any', got %s", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- }
-
- add_package_dependency(c, "runtime", "type_assertion_check");
- add_package_dependency(c, "runtime", "type_assertion_check2");
+ kind = check_type_assertion(c, o, node, type_hint);
case_end;
case_ast_node(tc, TypeCast, node);
@@ -8350,443 +9249,19 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_end;
case_ast_node(se, SelectorCallExpr, node);
- // IMPORTANT NOTE(bill, 2020-05-22): This is a complete hack to get a shorthand which is extremely useful for vtables
- // COM APIs is a great example of where this kind of thing is extremely useful
- // General idea:
- //
- // x->y(123) == x.y(x, 123)
- //
- // How this has been implemented at the moment is quite hacky but it's done so to reduce need for huge backend changes
- // Just regenerating a new AST aids things
- //
- // TODO(bill): Is this a good hack or not?
- //
- // NOTE(bill, 2020-05-22): I'm going to regret this decision, ain't I?
-
-
- if (se->modified_call) {
- // Prevent double evaluation
- o->expr = node;
- o->type = node->tav.type;
- o->value = node->tav.value;
- o->mode = node->tav.mode;
- return Expr_Expr;
- }
-
- bool allow_arrow_right_selector_expr;
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- Operand x = {};
- ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
-
- if (x.mode == Addressing_Invalid || x.type == t_invalid) {
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return kind;
- }
- if (!is_type_proc(x.type)) {
- gbString type_str = type_to_string(x.type);
- error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
- gb_string_free(type_str);
-
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Stmt;
- }
-
- ast_node(ce, CallExpr, se->call);
-
- GB_ASSERT(x.expr->kind == Ast_SelectorExpr);
-
- Ast *first_arg = x.expr->SelectorExpr.expr;
- GB_ASSERT(first_arg != nullptr);
-
- Type *pt = base_type(x.type);
- GB_ASSERT(pt->kind == Type_Proc);
- Type *first_type = nullptr;
- String first_arg_name = {};
- if (pt->Proc.param_count > 0) {
- Entity *f = pt->Proc.params->Tuple.variables[0];
- first_type = f->type;
- first_arg_name = f->token.string;
- }
- if (first_arg_name.len == 0) {
- first_arg_name = str_lit("_");
- }
-
- if (first_type == nullptr) {
- error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
- o->mode = Addressing_Invalid;
- o->type = t_invalid;
- o->expr = node;
- return Expr_Stmt;
- }
-
- Operand y = {};
- y.mode = first_arg->tav.mode;
- y.type = first_arg->tav.type;
- y.value = first_arg->tav.value;
- if (check_is_assignable_to(c, &y, first_type)) {
- // Do nothing, it's valid
- } else {
- Operand z = y;
- z.type = type_deref(y.type);
- if (check_is_assignable_to(c, &z, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_Pointer};
- first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
- } else if (y.mode == Addressing_Variable) {
- Operand w = y;
- w.type = alloc_type_pointer(y.type);
- if (check_is_assignable_to(c, &w, first_type)) {
- // NOTE(bill): AST GENERATION HACK!
- Token op = {Token_And};
- first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
- }
- }
- }
-
- if (ce->args.count > 0) {
- bool fail = false;
- bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
- for_array(i, ce->args) {
- Ast *arg = ce->args[i];
- bool mix = false;
- if (first_is_field_value) {
- mix = arg->kind != Ast_FieldValue;
- } else {
- mix = arg->kind == Ast_FieldValue;
- }
- if (mix) {
- fail = true;
- break;
- }
- }
- if (!fail && first_is_field_value) {
- Token op = {Token_Eq};
- AstFile *f = first_arg->file();
- first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
- }
- }
-
-
-
- auto modified_args = slice_make(heap_allocator(), ce->args.count+1);
- modified_args[0] = first_arg;
- slice_copy(&modified_args, ce->args, 1);
- ce->args = modified_args;
- se->modified_call = true;
-
- allow_arrow_right_selector_expr = c->allow_arrow_right_selector_expr;
- c->allow_arrow_right_selector_expr = true;
- check_expr_base(c, o, se->call, type_hint);
- c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
-
- o->expr = node;
- return Expr_Expr;
+ return check_selector_call_expr(c, o, node, type_hint);
case_end;
-
case_ast_node(ise, ImplicitSelectorExpr, node);
return check_implicit_selector_expr(c, o, node, type_hint);
case_end;
case_ast_node(ie, IndexExpr, node);
- check_expr(c, o, ie->expr);
- node->viral_state_flags |= ie->expr->viral_state_flags;
- if (o->mode == Addressing_Invalid) {
- o->expr = node;
- return kind;
- }
-
- Type *t = base_type(type_deref(o->type));
- bool is_ptr = is_type_pointer(o->type);
- bool is_const = o->mode == Addressing_Constant;
-
- if (is_type_map(t)) {
- Operand key = {};
- if (is_type_typeid(t->Map.key)) {
- check_expr_or_type(c, &key, ie->index, t->Map.key);
- } else {
- check_expr_with_type_hint(c, &key, ie->index, t->Map.key);
- }
- check_assignment(c, &key, t->Map.key, str_lit("map index"));
- if (key.mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- o->mode = Addressing_MapIndex;
- o->type = t->Map.value;
- o->expr = node;
-
- add_package_dependency(c, "runtime", "__dynamic_map_get");
- add_package_dependency(c, "runtime", "__dynamic_map_set");
- return Expr_Expr;
- }
-
- i64 max_count = -1;
- bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
-
- if (is_const) {
- if (is_type_array(t)) {
- // OKay
- } else if (is_type_slice(t)) {
- // Okay
- } else if (is_type_enumerated_array(t)) {
- // Okay
- } else if (is_type_string(t)) {
- // Okay
- } else if (is_type_relative_slice(t)) {
- // Okay
- } else if (is_type_matrix(t)) {
- // Okay
- } else {
- valid = false;
- }
- }
-
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- defer (gb_string_free(str));
- defer (gb_string_free(type_str));
- if (is_const) {
- error(o->expr, "Cannot index constant '%s' of type '%s'", str, type_str);
- } else {
- error(o->expr, "Cannot index '%s' of type '%s'", str, type_str);
- }
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (ie->index == nullptr) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Missing index for '%s'", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- Type *index_type_hint = nullptr;
- if (is_type_enumerated_array(t)) {
- Type *bt = base_type(t);
- GB_ASSERT(bt->kind == Type_EnumeratedArray);
- index_type_hint = bt->EnumeratedArray.index;
- }
-
- i64 index = 0;
- bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
- if (is_const) {
- if (index < 0) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot index a constant '%s'", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- } else if (ok) {
- ExactValue value = type_and_value_of_expr(ie->expr).value;
- o->mode = Addressing_Constant;
- bool success = false;
- bool finish = false;
- o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
- if (!success) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- }
- }
-
- if (type_hint != nullptr && is_type_matrix(t)) {
- // TODO(bill): allow matrix columns to be assignable to other types which are the same internally
- // if a type hint exists
- }
-
+ kind = check_index_expr(c, o, node, type_hint);
case_end;
case_ast_node(se, SliceExpr, node);
- check_expr(c, o, se->expr);
- node->viral_state_flags |= se->expr->viral_state_flags;
-
- if (o->mode == Addressing_Invalid) {
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- bool valid = false;
- i64 max_count = -1;
- Type *t = base_type(type_deref(o->type));
- switch (t->kind) {
- case Type_Basic:
- if (t->Basic.kind == Basic_string || t->Basic.kind == Basic_UntypedString) {
- valid = true;
- if (o->mode == Addressing_Constant) {
- max_count = o->value.value_string.len;
- }
- o->type = type_deref(o->type);
- }
- break;
-
- case Type_Array:
- valid = true;
- max_count = t->Array.count;
- if (o->mode != Addressing_Variable && !is_type_pointer(o->type)) {
- gbString str = expr_to_string(node);
- error(node, "Cannot slice array '%s', value is not addressable", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- o->type = alloc_type_slice(t->Array.elem);
- break;
-
- case Type_MultiPointer:
- valid = true;
- o->type = type_deref(o->type);
- break;
-
- case Type_Slice:
- valid = true;
- o->type = type_deref(o->type);
- break;
-
- case Type_DynamicArray:
- valid = true;
- o->type = alloc_type_slice(t->DynamicArray.elem);
- break;
-
- case Type_Struct:
- if (is_type_soa_struct(t)) {
- valid = true;
- o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
- }
- break;
-
- case Type_RelativeSlice:
- valid = true;
- o->type = t->RelativeSlice.slice_type;
- if (o->mode != Addressing_Variable) {
- gbString str = expr_to_string(node);
- error(node, "Cannot relative slice '%s', value is not addressable", str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
- break;
- }
-
- if (!valid) {
- gbString str = expr_to_string(o->expr);
- gbString type_str = type_to_string(o->type);
- error(o->expr, "Cannot slice '%s' of type '%s'", str, type_str);
- gb_string_free(type_str);
- gb_string_free(str);
- o->mode = Addressing_Invalid;
- o->expr = node;
- return kind;
- }
-
- if (se->low == nullptr && se->high != nullptr) {
- // It is okay to continue as it will assume the 1st index is zero
- }
-
- i64 indices[2] = {};
- Ast *nodes[2] = {se->low, se->high};
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- i64 index = max_count;
- if (nodes[i] != nullptr) {
- i64 capacity = -1;
- if (max_count >= 0) {
- capacity = max_count;
- }
- i64 j = 0;
- if (check_index_value(c, t, true, nodes[i], capacity, &j)) {
- index = j;
- }
-
- node->viral_state_flags |= nodes[i]->viral_state_flags;
- } else if (i == 0) {
- index = 0;
- }
- indices[i] = index;
- }
-
- for (isize i = 0; i < gb_count_of(indices); i++) {
- i64 a = indices[i];
- for (isize j = i+1; j < gb_count_of(indices); j++) {
- i64 b = indices[j];
- if (a > b && b >= 0) {
- error(se->close, "Invalid slice indices: [%td > %td]", a, b);
- }
- }
- }
-
- if (max_count < 0) {
- if (o->mode == Addressing_Constant) {
- gbString s = expr_to_string(se->expr);
- error(se->expr, "Cannot slice constant value '%s'", s);
- gb_string_free(s);
- }
- }
-
- if (t->kind == Type_MultiPointer && se->high != nullptr) {
- /*
- x[:] -> [^]T
- x[i:] -> [^]T
- x[:n] -> []T
- x[i:n] -> []T
- */
- o->type = alloc_type_slice(t->MultiPointer.elem);
- }
-
- o->mode = Addressing_Value;
-
- if (is_type_string(t) && max_count >= 0) {
- bool all_constant = true;
- for (isize i = 0; i < gb_count_of(nodes); i++) {
- if (nodes[i] != nullptr) {
- TypeAndValue tav = type_and_value_of_expr(nodes[i]);
- if (tav.mode != Addressing_Constant) {
- all_constant = false;
- break;
- }
- }
- }
- if (!all_constant) {
- gbString str = expr_to_string(o->expr);
- error(o->expr, "Cannot slice '%s' with non-constant indices", str);
- error_line("\tSuggestion: store the constant into a variable in order to index it with a variable index\n");
- gb_string_free(str);
- o->mode = Addressing_Value; // NOTE(bill): Keep subsequent values going without erring
- o->expr = node;
- return kind;
- }
-
- String s = {};
- if (o->value.kind == ExactValue_String) {
- s = o->value.value_string;
- }
-
- o->mode = Addressing_Constant;
- o->type = t;
- o->value = exact_value_string(substring(s, cast(isize)indices[0], cast(isize)indices[1]));
- }
-
+ kind = check_slice_expr(c, o, node, type_hint);
case_end;
case_ast_node(mie, MatrixIndexExpr, node);
@@ -8911,6 +9386,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind;
}
+
+
ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) {
ExprKind kind = check_expr_base_internal(c, o, node, type_hint);
if (o->type != nullptr && core_type(o->type) == nullptr) {
@@ -8926,6 +9403,8 @@ ExprKind check_expr_base(CheckerContext *c, Operand *o, Ast *node, Type *type_hi
if (o->type != nullptr && is_type_untyped(o->type)) {
add_untyped(c, node, o->mode, o->type, o->value);
}
+ check_rtti_type_disallowed(node, o->type, "An expression is using a type, %s, which has been disallowed");
+
add_type_and_value(c->info, node, o->mode, o->type, o->value);
return kind;
}
@@ -9085,18 +9564,7 @@ gbString string_append_string(gbString str, String string) {
gbString string_append_token(gbString str, Token token) {
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
str = string_append_string(str, token.string);
- if (token.kind == Token_String) {
- str = gb_string_append_rune(str, '"');
- } else if (token.kind == Token_Rune) {
- str = gb_string_append_rune(str, '\'');
- }
-
return str;
}
@@ -9323,6 +9791,13 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = gb_string_appendc(str, " = ");
str = write_expr_to_string(str, fv->value, shorthand);
case_end;
+ case_ast_node(fv, EnumFieldValue, node);
+ str = write_expr_to_string(str, fv->name, shorthand);
+ if (fv->value) {
+ str = gb_string_appendc(str, " = ");
+ str = write_expr_to_string(str, fv->value, shorthand);
+ }
+ case_end;
case_ast_node(ht, HelperType, node);
str = gb_string_appendc(str, "#type ");
@@ -9414,6 +9889,9 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
if (f->flags&FieldFlag_const) {
str = gb_string_appendc(str, "#const ");
}
+ if (f->flags&FieldFlag_subtype) {
+ str = gb_string_appendc(str, "#subtype ");
+ }
for_array(i, f->names) {
Ast *name = f->names[i];
@@ -9572,8 +10050,11 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
str = write_expr_to_string(str, st->polymorphic_params, shorthand);
str = gb_string_appendc(str, ") ");
}
- if (st->no_nil) str = gb_string_appendc(str, "#no_nil ");
- if (st->maybe) str = gb_string_appendc(str, "#maybe ");
+ switch (st->kind) {
+ case UnionType_maybe: str = gb_string_appendc(str, "#maybe "); break;
+ case UnionType_no_nil: str = gb_string_appendc(str, "#no_nil "); break;
+ case UnionType_shared_nil: str = gb_string_appendc(str, "#shared_nil "); break;
+ }
if (st->align) {
str = gb_string_appendc(str, "#align ");
str = write_expr_to_string(str, st->align, shorthand);
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 1a424240c..f2c830c1b 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -490,6 +490,14 @@ void check_stmt(CheckerContext *ctx, Ast *node, u32 flags) {
out &= ~StateFlag_no_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
+
ctx->state_flags = out;
}
@@ -607,7 +615,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
String name = scope->elements.entries[i].key.string;
Entity *decl = scope->elements.entries[i].value;
if (!is_entity_exported(decl)) continue;
@@ -689,54 +697,6 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
return true;
}
-
-struct TypeAndToken {
- Type *type;
- Token token;
-};
-
-
-void add_constant_switch_case(CheckerContext *ctx, PtrMap *seen, Operand operand, bool use_expr = true) {
- if (operand.mode != Addressing_Constant) {
- return;
- }
- if (operand.value.kind == ExactValue_Invalid) {
- return;
- }
-
- uintptr key = hash_exact_value(operand.value);
- TypeAndToken *found = map_get(seen, key);
- if (found != nullptr) {
- isize count = multi_map_count(seen, key);
- TypeAndToken *taps = gb_alloc_array(temporary_allocator(), TypeAndToken, count);
-
- multi_map_get_all(seen, key, taps);
- for (isize i = 0; i < count; i++) {
- TypeAndToken tap = taps[i];
- if (!are_types_identical(operand.type, tap.type)) {
- continue;
- }
-
- TokenPos pos = tap.token.pos;
- if (use_expr) {
- gbString expr_str = expr_to_string(operand.expr);
- error(operand.expr,
- "Duplicate case '%s'\n"
- "\tprevious case at %s",
- expr_str,
- token_pos_to_string(pos));
- gb_string_free(expr_str);
- } else {
- error(operand.expr, "Duplicate case found with previous case at %s", token_pos_to_string(pos));
- }
- return;
- }
- }
-
- TypeAndToken tap = {operand.type, ast_token(operand.expr)};
- multi_map_insert(seen, key, tap);
-}
-
void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
ast_node(irs, UnrollRangeStmt, node);
check_open_scope(ctx, node);
@@ -961,7 +921,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
- PtrMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
+ SeenMap seen = {}; // NOTE(bill): Multimap, Key: ExactValue
map_init(&seen, heap_allocator());
defer (map_destroy(&seen));
@@ -1001,9 +961,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
- case Token_Ellipsis: upper_op = Token_GtEq; break;
- case Token_RangeFull: upper_op = Token_GtEq; break;
- case Token_RangeHalf: upper_op = Token_Gt; break;
+ case Token_Ellipsis: upper_op = Token_LtEq; break;
+ case Token_RangeFull: upper_op = Token_LtEq; break;
+ case Token_RangeHalf: upper_op = Token_Lt; break;
default: GB_PANIC("Invalid range operator"); break;
}
@@ -1024,45 +984,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
- if (is_type_enum(x.type)) {
- // TODO(bill): Fix this logic so it's fast!!!
-
- i64 v0 = exact_value_to_i64(lhs.value);
- i64 v1 = exact_value_to_i64(rhs.value);
- Operand v = {};
- v.mode = Addressing_Constant;
- v.type = x.type;
- v.expr = x.expr;
-
- Type *bt = base_type(x.type);
- GB_ASSERT(bt->kind == Type_Enum);
- for (i64 vi = v0; vi <= v1; vi++) {
- if (upper_op != Token_GtEq && vi == v1) {
- break;
- }
-
- bool found = false;
- for_array(j, bt->Enum.fields) {
- Entity *f = bt->Enum.fields[j];
- GB_ASSERT(f->kind == Entity_Constant);
-
- i64 fv = exact_value_to_i64(f->Constant.value);
- if (fv == vi) {
- found = true;
- break;
- }
- }
- if (found) {
- v.value = exact_value_i64(vi);
- add_constant_switch_case(ctx, &seen, v);
- }
- }
- } else {
- add_constant_switch_case(ctx, &seen, lhs);
- if (upper_op == Token_GtEq) {
- add_constant_switch_case(ctx, &seen, rhs);
- }
- }
+ add_to_seen_map(ctx, &seen, upper_op, x, lhs, rhs);
if (is_type_string(x.type)) {
// NOTE(bill): Force dependency for strings here
@@ -1107,7 +1029,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
update_untyped_expr_type(ctx, z.expr, x.type, !is_type_untyped(x.type));
- add_constant_switch_case(ctx, &seen, y);
+ add_to_seen_map(ctx, &seen, y);
}
}
}
@@ -1143,7 +1065,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
- error_no_newline(node, "Unhandled switch cases: ");
+ error(node, "Unhandled switch cases:");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
@@ -2230,7 +2152,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
e->state = EntityState_Resolved;
}
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
- e->Variable.thread_local_model = ac.thread_local_model;
if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name;
@@ -2243,6 +2164,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'static' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'static' variables cannot be declared within a defer statement");
+ }
}
}
if (ac.thread_local_model != "") {
@@ -2251,10 +2175,18 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
error(e->token, "The 'thread_local' attribute is not allowed to be applied to '_'");
} else {
e->flags |= EntityFlag_Static;
+ if (ctx->in_defer) {
+ error(e->token, "'thread_local' variables cannot be declared within a defer statement");
+ }
}
e->Variable.thread_local_model = ac.thread_local_model;
}
+ if (is_arch_wasm() && e->Variable.thread_local_model.len != 0) {
+ error(e->token, "@(thread_local) is not supported for this target platform");
+ }
+
+
if (ac.is_static && ac.thread_local_model != "") {
error(e->token, "The 'static' attribute is not needed if 'thread_local' is applied");
}
diff --git a/src/check_type.cpp b/src/check_type.cpp
index b4f30d2f0..51f472961 100644
--- a/src/check_type.cpp
+++ b/src/check_type.cpp
@@ -109,14 +109,19 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
}
i32 field_src_index = 0;
+ i32 field_group_index = -1;
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
continue;
}
+ field_group_index += 1;
+
ast_node(p, Field, param);
Ast *type_expr = p->type;
Type *type = nullptr;
+ CommentGroup *docs = p->docs;
+ CommentGroup *comment = p->comment;
if (type_expr != nullptr) {
type = check_type_expr(ctx, type_expr, nullptr);
@@ -139,6 +144,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
}
bool is_using = (p->flags&FieldFlag_using) != 0;
+ bool is_subtype = (p->flags&FieldFlag_subtype) != 0;
for_array(j, p->names) {
Ast *name = p->names[j];
@@ -152,6 +158,18 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index);
add_entity(ctx, ctx->scope, name, field);
+ field->Variable.field_group_index = field_group_index;
+ if (is_subtype) {
+ field->flags |= EntityFlag_Subtype;
+ }
+
+ if (j == 0) {
+ field->Variable.docs = docs;
+ }
+ if (j+1 == p->names.count) {
+ field->Variable.comment = comment;
+ }
+
array_add(&fields_array, field);
String tag = p->tag.string;
if (tag.len != 0 && !unquote_string(permanent_allocator(), &tag, 0, tag.text[0] == '`')) {
@@ -180,6 +198,20 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Slice *fields
populate_using_entity_scope(ctx, node, p, type);
}
+
+ if (is_subtype && p->names.count > 0) {
+ Type *first_type = fields_array[fields_array.count-1]->type;
+ Type *t = base_type(type_deref(first_type));
+
+ if (!does_field_type_allow_using(t) &&
+ p->names.count >= 1 &&
+ p->names[0]->kind == Ast_Ident) {
+ Token name_token = p->names[0]->Ident.token;
+ gbString type_str = type_to_string(first_type);
+ error(name_token, "'subtype' cannot be applied to the field '%.*s' of type '%s'", LIT(name_token.string), type_str);
+ gb_string_free(type_str);
+ }
+ }
}
*fields = slice_from_array(fields_array);
@@ -309,6 +341,10 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
}
named_type->Named.type_name = e;
+ GB_ASSERT(original_type->kind == Type_Named);
+ e->TypeName.objc_class_name = original_type->Named.type_name->TypeName.objc_class_name;
+ // TODO(bill): Is this even correct? Or should the metadata be copied?
+ e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
mutex_lock(&ctx->info->gen_types_mutex);
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
@@ -639,22 +675,31 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Arraykind == UnionType_shared_nil) {
+ if (!type_has_nil(t)) {
+ gbString s = type_to_string(t);
+ error(node, "Each variant of a union with #shared_nil must have a 'nil' value, got %s", s);
+ gb_string_free(s);
+ }
+ }
}
}
}
union_type->Union.variants = slice_from_array(variants);
- union_type->Union.no_nil = ut->no_nil;
- union_type->Union.maybe = ut->maybe;
- if (union_type->Union.no_nil) {
+ union_type->Union.kind = ut->kind;
+ switch (ut->kind) {
+ case UnionType_no_nil:
if (variants.count < 2) {
error(ut->align, "A union with #no_nil must have at least 2 variants");
}
- }
- if (union_type->Union.maybe) {
+ break;
+ case UnionType_maybe:
if (variants.count != 1) {
error(ut->align, "A union with #maybe must have at 1 variant, got %lld", cast(long long)variants.count);
}
+ break;
}
if (ut->align != nullptr) {
@@ -718,20 +763,19 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
Ast *ident = nullptr;
Ast *init = nullptr;
u32 entity_flags = 0;
- if (field->kind == Ast_FieldValue) {
- ast_node(fv, FieldValue, field);
- if (fv->field == nullptr || fv->field->kind != Ast_Ident) {
- error(field, "An enum field's name must be an identifier");
- continue;
- }
- ident = fv->field;
- init = fv->value;
- } else if (field->kind == Ast_Ident) {
- ident = field;
- } else {
+ if (field->kind != Ast_EnumFieldValue) {
error(field, "An enum field's name must be an identifier");
continue;
}
+ ident = field->EnumFieldValue.name;
+ init = field->EnumFieldValue.value;
+ if (ident == nullptr || ident->kind != Ast_Ident) {
+ error(field, "An enum field's name must be an identifier");
+ continue;
+ }
+ CommentGroup *docs = field->EnumFieldValue.docs;
+ CommentGroup *comment = field->EnumFieldValue.comment;
+
String name = ident->Ident.token.string;
if (init != nullptr) {
@@ -789,6 +833,8 @@ void check_enum_type(CheckerContext *ctx, Type *enum_type, Type *named_type, Ast
e->flags |= EntityFlag_Visited;
e->state = EntityState_Resolved;
e->Constant.flags |= entity_flags;
+ e->Constant.docs = docs;
+ e->Constant.comment = comment;
if (scope_lookup_current(ctx->scope, name) != nullptr) {
error(ident, "'%.*s' is already declared in this enumeration", LIT(name));
@@ -922,20 +968,19 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
i64 lower = big_int_to_i64(&i);
i64 upper = big_int_to_i64(&j);
- bool lower_changed = false;
+ i64 actual_lower = lower;
i64 bits = MAX_BITS;
if (type->BitSet.underlying != nullptr) {
bits = 8*type_size_of(type->BitSet.underlying);
if (lower > 0) {
- lower = 0;
- lower_changed = true;
+ actual_lower = 0;
} else if (lower < 0) {
error(bs->elem, "bit_set does not allow a negative lower bound (%lld) when an underlying type is set", lower);
}
}
- i64 bits_required = upper-lower;
+ i64 bits_required = upper-actual_lower;
switch (be->op.kind) {
case Token_Ellipsis:
case Token_RangeFull:
@@ -959,7 +1004,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
break;
}
if (!is_valid) {
- if (lower_changed) {
+ if (actual_lower != lower) {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required (internal the lower changed was changed 0 as an underlying type was set)", bits, bits_required);
} else {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, bits_required);
@@ -1367,11 +1412,13 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
isize variadic_index = -1;
bool is_c_vararg = false;
auto variables = array_make(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, params) {
Ast *param = params[i];
if (param->kind != Ast_Field) {
continue;
}
+ field_group_index += 1;
ast_node(p, Field, param);
Ast *type_expr = unparen_expr(p->type);
Type *type = nullptr;
@@ -1672,9 +1719,11 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
}
param = alloc_entity_const_param(scope, name->Ident.token, type, poly_const, is_type_polymorphic(type));
+ param->Constant.field_group_index = field_group_index;
} else {
param = alloc_entity_param(scope, name->Ident.token, type, is_using, true);
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = field_group_index;
}
}
if (p->flags&FieldFlag_no_alias) {
@@ -1768,7 +1817,10 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
}
auto variables = array_make(permanent_allocator(), 0, variable_count);
+ i32 field_group_index = -1;
for_array(i, results) {
+ field_group_index += 1;
+
ast_node(field, Field, results[i]);
Ast *default_value = unparen_expr(field->default_value);
ParameterValue param_value = {};
@@ -1799,6 +1851,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
token.string = str_lit("");
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = -1;
array_add(&variables, param);
} else {
for_array(j, field->names) {
@@ -1822,6 +1875,7 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) {
Entity *param = alloc_entity_param(scope, token, type, false, false);
param->flags |= EntityFlag_Result;
param->Variable.param_value = param_value;
+ param->Variable.field_group_index = field_group_index;
array_add(&variables, param);
add_entity(ctx, scope, name, param);
// NOTE(bill): Removes `declared but not used` when using -vet
@@ -1883,6 +1937,25 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
c->scope->flags &= ~ScopeFlag_ContextDefined;
}
+ TargetArchKind arch = build_context.metrics.arch;
+ switch (cc) {
+ case ProcCC_StdCall:
+ case ProcCC_FastCall:
+ if (arch != TargetArch_i386 && arch != TargetArch_amd64) {
+ error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected either i386 or amd64, got %.*s",
+ proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+ }
+ break;
+ case ProcCC_Win64:
+ case ProcCC_SysV:
+ if (arch != TargetArch_amd64) {
+ error(proc_type_node, "Invalid procedure calling convention \"%s\" for target architecture, expected amd64, got %.*s",
+ proc_calling_convention_strings[cc], LIT(target_arch_names[arch]));
+ }
+ break;
+ }
+
+
bool variadic = false;
isize variadic_index = -1;
bool success = true;
@@ -1896,20 +1969,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (params) param_count = params ->Tuple.variables.count;
if (results) result_count = results->Tuple.variables.count;
- if (param_count > 0) {
- for_array(i, params->Tuple.variables) {
- Entity *param = params->Tuple.variables[i];
- if (param->kind == Entity_Variable) {
- ParameterValue pv = param->Variable.param_value;
- if (pv.kind == ParameterValue_Constant &&
- pv.value.kind == ExactValue_Procedure) {
- type->Proc.has_proc_default_values = true;
- break;
- }
- }
- }
- }
-
if (result_count > 0) {
Entity *first = results->Tuple.variables[0];
type->Proc.has_named_results = first->token.string != "";
@@ -1967,10 +2026,14 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
if (param_count > 0) {
Entity *end = params->Tuple.variables[param_count-1];
if (end->flags&EntityFlag_CVarArg) {
- if (cc == ProcCC_StdCall || cc == ProcCC_CDecl) {
+ switch (cc) {
+ default:
type->Proc.c_vararg = true;
- } else {
+ break;
+ case ProcCC_Odin:
+ case ProcCC_Contextless:
error(end->token, "Calling convention does not support #c_vararg");
+ break;
}
}
}
@@ -2106,7 +2169,7 @@ void init_map_entry_type(Type *type) {
/*
struct {
- hash: runtime.Map_Hash,
+ hash: uintptr,
next: int,
key: Key,
value: Value,
@@ -2285,10 +2348,21 @@ void check_matrix_type(CheckerContext *ctx, Type **type, Ast *node) {
}
if (!is_type_valid_for_matrix_elems(elem)) {
+ if (elem == t_typeid) {
+ Entity *e = entity_of_node(mt->elem);
+ if (e && e->kind == Entity_TypeName && e->TypeName.is_type_alias) {
+ // HACK TODO(bill): This is to allow polymorphic parameters for matrix elements
+ // proc($T: typeid) -> matrix[2, 2]T
+ //
+ // THIS IS NEEDS TO BE FIXED AND NOT USE THIS HACK
+ goto type_assign;
+ }
+ }
gbString s = type_to_string(elem);
error(column.expr, "Matrix elements types are limited to integers, floats, and complex, got %s", s);
gb_string_free(s);
}
+type_assign:;
*type = alloc_type_matrix(elem, row_count, column_count, generic_row, generic_column);
@@ -2679,29 +2753,30 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *t = alloc_type_enumerated_array(elem, index, bt->Enum.min_value, bt->Enum.max_value, Token_Invalid);
- bool is_partial = false;
+ bool is_sparse = false;
if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name.string;
- if (name == "partial") {
- is_partial = true;
+ if (name == "sparse") {
+ is_sparse = true;
} else {
error(at->tag, "Invalid tag applied to an enumerated array, got #%.*s", LIT(name));
}
}
- if (!is_partial && t->EnumeratedArray.count > bt->Enum.fields.count) {
+ if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
error(e, "Non-contiguous enumeration used as an index in an enumerated array");
long long ea_count = cast(long long)t->EnumeratedArray.count;
long long enum_count = cast(long long)bt->Enum.fields.count;
error_line("\tenumerated array length: %lld\n", ea_count);
error_line("\tenum field count: %lld\n", enum_count);
- error_line("\tSuggestion: prepend #partial to the enumerated array to allow for non-named elements\n");
+ error_line("\tSuggestion: prepend #sparse to the enumerated array to allow for non-contiguous elements\n");
if (2*enum_count < ea_count) {
error_line("\tWarning: the number of named elements is much smaller than the length of the array, are you sure this is what you want?\n");
- error_line("\t this warning will be removed if #partial is applied\n");
+ error_line("\t this warning will be removed if #sparse is applied\n");
}
}
+ t->EnumeratedArray.is_sparse = is_sparse;
*type = t;
@@ -2951,5 +3026,7 @@ Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type) {
}
set_base_type(named_type, type);
+ check_rtti_type_disallowed(e, type, "Use of a type, %s, which has been disallowed");
+
return type;
}
diff --git a/src/checker.cpp b/src/checker.cpp
index 667146eda..1bb786ea1 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -4,7 +4,7 @@
void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
-
+Type *check_type(CheckerContext *ctx, Ast *e);
bool is_operand_value(Operand o) {
switch (o.mode) {
@@ -29,6 +29,23 @@ bool is_operand_undef(Operand o) {
return o.mode == Addressing_Value && o.type == t_untyped_undef;
}
+bool check_rtti_type_disallowed(Token const &token, Type *type, char const *format) {
+ if (build_context.disallow_rtti && type) {
+ if (is_type_any(type)) {
+ gbString t = type_to_string(type);
+ error(token, format, t);
+ gb_string_free(t);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool check_rtti_type_disallowed(Ast *expr, Type *type, char const *format) {
+ GB_ASSERT(expr != nullptr);
+ return check_rtti_type_disallowed(ast_token(expr), type, format);
+}
+
void scope_reset(Scope *scope) {
if (scope == nullptr) return;
@@ -225,8 +242,8 @@ bool decl_info_has_init(DeclInfo *d) {
Scope *create_scope(CheckerInfo *info, Scope *parent, isize init_elements_capacity=DEFAULT_SCOPE_CAPACITY) {
Scope *s = gb_alloc_item(permanent_allocator(), Scope);
s->parent = parent;
- string_map_init(&s->elements, permanent_allocator(), init_elements_capacity);
- ptr_set_init(&s->imported, permanent_allocator(), 0);
+ string_map_init(&s->elements, heap_allocator(), init_elements_capacity);
+ ptr_set_init(&s->imported, heap_allocator(), 0);
mutex_init(&s->mutex);
if (parent != nullptr && parent != builtin_pkg->scope) {
@@ -446,7 +463,7 @@ Entity *scope_lookup(Scope *s, String const &name) {
-Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) {
+Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity, bool use_mutex=true) {
if (name == "") {
return nullptr;
}
@@ -454,8 +471,8 @@ Entity *scope_insert_with_name(Scope *s, String const &name, Entity *entity) {
Entity **found = nullptr;
Entity *result = nullptr;
- mutex_lock(&s->mutex);
- defer (mutex_unlock(&s->mutex));
+ if (use_mutex) mutex_lock(&s->mutex);
+ defer (if (use_mutex) mutex_unlock(&s->mutex));
found = string_map_get(&s->elements, key);
@@ -485,9 +502,9 @@ end:;
return result;
}
-Entity *scope_insert(Scope *s, Entity *entity) {
+Entity *scope_insert(Scope *s, Entity *entity, bool use_mutex) {
String name = entity->token.string;
- return scope_insert_with_name(s, name, entity);
+ return scope_insert_with_name(s, name, entity, use_mutex);
}
@@ -504,6 +521,7 @@ enum VettedEntityKind {
VettedEntity_Unused,
VettedEntity_Shadowed,
+ VettedEntity_Shadowed_And_Unused,
};
struct VettedEntity {
VettedEntityKind kind;
@@ -622,15 +640,21 @@ void check_scope_usage(Checker *c, Scope *scope) {
Array vetted_entities = {};
array_init(&vetted_entities, heap_allocator());
- for_array(i, scope->elements.entries) {
+ MUTEX_GUARD_BLOCK(scope->mutex) for_array(i, scope->elements.entries) {
Entity *e = scope->elements.entries[i].value;
if (e == nullptr) continue;
- VettedEntity ve = {};
- if (vet_unused && check_vet_unused(c, e, &ve)) {
- array_add(&vetted_entities, ve);
- }
- if (vet_shadowing && check_vet_shadowing(c, e, &ve)) {
- array_add(&vetted_entities, ve);
+ VettedEntity ve_unused = {};
+ VettedEntity ve_shadowed = {};
+ bool is_unused = vet_unused && check_vet_unused(c, e, &ve_unused);
+ bool is_shadowed = vet_shadowing && check_vet_shadowing(c, e, &ve_shadowed);
+ if (is_unused && is_shadowed) {
+ VettedEntity ve_both = ve_shadowed;
+ ve_both.kind = VettedEntity_Shadowed_And_Unused;
+ array_add(&vetted_entities, ve_both);
+ } else if (is_unused) {
+ array_add(&vetted_entities, ve_unused);
+ } else if (is_shadowed) {
+ array_add(&vetted_entities, ve_shadowed);
}
}
@@ -642,16 +666,18 @@ void check_scope_usage(Checker *c, Scope *scope) {
Entity *other = ve.other;
String name = e->token.string;
- if (build_context.vet) {
+ if (ve.kind == VettedEntity_Shadowed_And_Unused) {
+ error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
+ } else if (build_context.vet) {
switch (ve.kind) {
case VettedEntity_Unused:
error(e->token, "'%.*s' declared but not used", LIT(name));
break;
case VettedEntity_Shadowed:
if (e->flags&EntityFlag_Using) {
- error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
} else {
- error(e->token, "Declaration of '%.*s' shadows declaration at line %lld", LIT(name), cast(long long)other->token.pos.line);
+ error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
}
break;
default:
@@ -688,12 +714,17 @@ void add_dependency(CheckerInfo *info, DeclInfo *d, Entity *e) {
ptr_set_add(&d->deps, e);
mutex_unlock(&info->deps_mutex);
}
-void add_type_info_dependency(DeclInfo *d, Type *type) {
+void add_type_info_dependency(CheckerInfo *info, DeclInfo *d, Type *type, bool require_mutex) {
if (d == nullptr) {
return;
}
- // NOTE(bill): no mutex is required here because the only procedure calling it is wrapped in a mutex already
+ if (require_mutex) {
+ mutex_lock(&info->deps_mutex);
+ }
ptr_set_add(&d->type_info_deps, type);
+ if (require_mutex) {
+ mutex_unlock(&info->deps_mutex);
+ }
}
AstPackage *get_core_package(CheckerInfo *info, String name) {
@@ -719,12 +750,25 @@ void add_package_dependency(CheckerContext *c, char const *package_name, char co
String n = make_string_c(name);
AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
Entity *e = scope_lookup(p->scope, n);
- e->flags |= EntityFlag_Used;
GB_ASSERT_MSG(e != nullptr, "%s", name);
GB_ASSERT(c->decl != nullptr);
+ e->flags |= EntityFlag_Used;
add_dependency(c->info, c->decl, e);
}
+void try_to_add_package_dependency(CheckerContext *c, char const *package_name, char const *name) {
+ String n = make_string_c(name);
+ AstPackage *p = get_core_package(&c->checker->info, make_string_c(package_name));
+ Entity *e = scope_lookup(p->scope, n);
+ if (e == nullptr) {
+ return;
+ }
+ GB_ASSERT(c->decl != nullptr);
+ e->flags |= EntityFlag_Used;
+ add_dependency(c->info, c->decl, e);
+}
+
+
void add_declaration_dependency(CheckerContext *c, Entity *e) {
if (e == nullptr) {
return;
@@ -780,6 +824,69 @@ AstPackage *create_builtin_package(char const *name) {
return pkg;
}
+struct GlobalEnumValue {
+ char const *name;
+ i64 value;
+};
+
+Slice add_global_enum_type(String const &type_name, GlobalEnumValue *values, isize value_count, Type **enum_type_ = nullptr) {
+ Scope *scope = create_scope(nullptr, builtin_pkg->scope);
+ Entity *entity = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+
+ Type *enum_type = alloc_type_enum();
+ Type *named_type = alloc_type_named(type_name, enum_type, entity);
+ set_base_type(named_type, enum_type);
+ enum_type->Enum.base_type = t_int;
+ enum_type->Enum.scope = scope;
+ entity->type = named_type;
+
+ auto fields = array_make(permanent_allocator(), value_count);
+ for (isize i = 0; i < value_count; i++) {
+ i64 value = values[i].value;
+ Entity *e = alloc_entity_constant(scope, make_token_ident(values[i].name), named_type, exact_value_i64(value));
+ e->flags |= EntityFlag_Visited;
+ e->state = EntityState_Resolved;
+ fields[i] = e;
+
+ Entity *ie = scope_insert(scope, e);
+ GB_ASSERT(ie == nullptr);
+ }
+
+
+ enum_type->Enum.fields = fields;
+ enum_type->Enum.min_value_index = 0;
+ enum_type->Enum.max_value_index = value_count-1;
+ enum_type->Enum.min_value = &enum_type->Enum.fields[enum_type->Enum.min_value_index]->Constant.value;
+ enum_type->Enum.max_value = &enum_type->Enum.fields[enum_type->Enum.max_value_index]->Constant.value;
+
+
+ if (enum_type_) *enum_type_ = named_type;
+
+ return slice_from_array(fields);
+}
+void add_global_enum_constant(Slice const &fields, char const *name, i64 value) {
+ for (Entity *field : fields) {
+ GB_ASSERT(field->kind == Entity_Constant);
+ if (value == exact_value_to_i64(field->Constant.value)) {
+ add_global_constant(name, field->type, field->Constant.value);
+ return;
+ }
+ }
+ GB_PANIC("Unfound enum value for global constant: %s %lld", name, cast(long long)value);
+}
+
+Type *add_global_type_name(Scope *scope, String const &type_name, Type *backing_type) {
+ Entity *e = alloc_entity_type_name(scope, make_token_ident(type_name), nullptr, EntityState_Resolved);
+ Type *named_type = alloc_type_named(type_name, backing_type, e);
+ e->type = named_type;
+ set_base_type(named_type, backing_type);
+ if (scope_insert(scope, e)) {
+ compiler_error("double declaration of %.*s", LIT(e->token.string));
+ }
+ return named_type;
+}
+
+
void init_universal(void) {
BuildContext *bc = &build_context;
@@ -789,7 +896,8 @@ void init_universal(void) {
// Types
for (isize i = 0; i < gb_count_of(basic_types); i++) {
- add_global_type_entity(basic_types[i].Basic.name, &basic_types[i]);
+ String const &name = basic_types[i].Basic.name;
+ add_global_type_entity(name, &basic_types[i]);
}
add_global_type_entity(str_lit("byte"), &basic_types[Basic_u8]);
@@ -808,14 +916,96 @@ void init_universal(void) {
add_global_bool_constant("false", false);
// TODO(bill): Set through flags in the compiler
- add_global_string_constant("ODIN_OS", bc->ODIN_OS);
- add_global_string_constant("ODIN_ARCH", bc->ODIN_ARCH);
- add_global_string_constant("ODIN_ENDIAN", bc->ODIN_ENDIAN);
add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
+
+ {
+ GlobalEnumValue values[TargetOs_COUNT] = {
+ {"Unknown", TargetOs_Invalid},
+ {"Windows", TargetOs_windows},
+ {"Darwin", TargetOs_darwin},
+ {"Linux", TargetOs_linux},
+ {"Essence", TargetOs_essence},
+ {"FreeBSD", TargetOs_freebsd},
+ {"OpenBSD", TargetOs_openbsd},
+ {"WASI", TargetOs_wasi},
+ {"JS", TargetOs_js},
+ {"Freestanding", TargetOs_freestanding},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_OS_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_OS", bc->metrics.os);
+ add_global_string_constant("ODIN_OS_STRING", target_os_names[bc->metrics.os]);
+ }
+
+ {
+ GlobalEnumValue values[TargetArch_COUNT] = {
+ {"Unknown", TargetArch_Invalid},
+ {"amd64", TargetArch_amd64},
+ {"i386", TargetArch_i386},
+ {"arm64", TargetArch_arm64},
+ {"wasm32", TargetArch_wasm32},
+ {"wasm64", TargetArch_wasm64},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Arch_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
+ add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
+ }
- add_global_string_constant("ODIN_BUILD_MODE", bc->ODIN_BUILD_MODE);
+ {
+ GlobalEnumValue values[BuildMode_COUNT] = {
+ {"Executable", BuildMode_Executable},
+ {"Dynamic", BuildMode_DynamicLibrary},
+ {"Object", BuildMode_Object},
+ {"Assembly", BuildMode_Assembly},
+ {"LLVM_IR", BuildMode_LLVM_IR},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Build_Mode_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_BUILD_MODE", bc->build_mode);
+ }
+
+ {
+ GlobalEnumValue values[TargetEndian_COUNT] = {
+ {"Unknown", TargetEndian_Invalid},
+
+ {"Little", TargetEndian_Little},
+ {"Big", TargetEndian_Big},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Endian_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ENDIAN", target_endians[bc->metrics.arch]);
+ add_global_string_constant("ODIN_ENDIAN_STRING", target_endian_names[target_endians[bc->metrics.arch]]);
+ }
+
+ {
+ GlobalEnumValue values[ErrorPosStyle_COUNT] = {
+ {"Default", ErrorPosStyle_Default},
+ {"Unix", ErrorPosStyle_Unix},
+ };
+
+ auto fields = add_global_enum_type(str_lit("Odin_Error_Pos_Style_Type"), values, gb_count_of(values));
+ add_global_enum_constant(fields, "ODIN_ERROR_POS_STYLE", build_context.ODIN_ERROR_POS_STYLE);
+ }
+
+ {
+ GlobalEnumValue values[OdinAtomicMemoryOrder_COUNT] = {
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_relaxed], OdinAtomicMemoryOrder_relaxed},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_consume], OdinAtomicMemoryOrder_consume},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acquire], OdinAtomicMemoryOrder_acquire},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_release], OdinAtomicMemoryOrder_release},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_acq_rel], OdinAtomicMemoryOrder_acq_rel},
+ {OdinAtomicMemoryOrder_strings[OdinAtomicMemoryOrder_seq_cst], OdinAtomicMemoryOrder_seq_cst},
+ };
+
+ add_global_enum_type(str_lit("Atomic_Memory_Order"), values, gb_count_of(values), &t_atomic_memory_order);
+ GB_ASSERT(t_atomic_memory_order->kind == Type_Named);
+ scope_insert(intrinsics_pkg->scope, t_atomic_memory_order->Named.type_name);
+ }
+
+
add_global_bool_constant("ODIN_DEBUG", bc->ODIN_DEBUG);
add_global_bool_constant("ODIN_DISABLE_ASSERT", bc->ODIN_DISABLE_ASSERT);
add_global_bool_constant("ODIN_DEFAULT_TO_NIL_ALLOCATOR", bc->ODIN_DEFAULT_TO_NIL_ALLOCATOR);
@@ -823,6 +1013,9 @@ void init_universal(void) {
add_global_bool_constant("ODIN_NO_CRT", bc->no_crt);
add_global_bool_constant("ODIN_USE_SEPARATE_MODULES", bc->use_separate_modules);
add_global_bool_constant("ODIN_TEST", bc->command_kind == Command_test);
+ add_global_bool_constant("ODIN_NO_ENTRY_POINT", bc->no_entry_point);
+ add_global_bool_constant("ODIN_FOREIGN_ERROR_PROCEDURES", bc->ODIN_FOREIGN_ERROR_PROCEDURES);
+ add_global_bool_constant("ODIN_DISALLOW_RTTI", bc->disallow_rtti);
// Builtin Procedures
@@ -887,6 +1080,17 @@ void init_universal(void) {
t_f64_ptr = alloc_type_pointer(t_f64);
t_u8_slice = alloc_type_slice(t_u8);
t_string_slice = alloc_type_slice(t_string);
+
+ // intrinsics types for objective-c stuff
+ {
+ t_objc_object = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_object"), alloc_type_struct());
+ t_objc_selector = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_selector"), alloc_type_struct());
+ t_objc_class = add_global_type_name(intrinsics_pkg->scope, str_lit("objc_class"), alloc_type_struct());
+
+ t_objc_id = alloc_type_pointer(t_objc_object);
+ t_objc_SEL = alloc_type_pointer(t_objc_selector);
+ t_objc_Class = alloc_type_pointer(t_objc_class);
+ }
}
@@ -941,6 +1145,11 @@ void init_checker_info(CheckerInfo *i) {
mutex_init(&i->foreign_mutex);
semaphore_init(&i->collect_semaphore);
+
+ mpmc_init(&i->intrinsics_entry_point_usage, a, 1<<10); // just waste some memory here, even if it probably never used
+
+ mutex_init(&i->objc_types_mutex);
+ map_init(&i->objc_msgSend_types, a);
}
void destroy_checker_info(CheckerInfo *i) {
@@ -973,6 +1182,9 @@ void destroy_checker_info(CheckerInfo *i) {
mutex_destroy(&i->type_and_value_mutex);
mutex_destroy(&i->identifier_uses_mutex);
mutex_destroy(&i->foreign_mutex);
+
+ mutex_destroy(&i->objc_types_mutex);
+ map_destroy(&i->objc_msgSend_types);
}
CheckerContext make_checker_context(Checker *c) {
@@ -1177,7 +1389,7 @@ isize type_info_index(CheckerInfo *info, Type *type, bool error_on_failure) {
// TODO(bill): This is O(n) and can be very slow
for_array(i, info->type_info_map.entries){
auto *e = &info->type_info_map.entries[i];
- if (are_types_identical(e->key, type)) {
+ if (are_types_identical_unique_tuples(e->key, type)) {
entry_index = e->value;
// NOTE(bill): Add it to the search map
map_set(&info->type_info_map, type, entry_index);
@@ -1228,7 +1440,7 @@ void add_type_and_value(CheckerInfo *i, Ast *expr, AddressingMode mode, Type *ty
while (prev_expr != expr) {
prev_expr = expr;
expr->tav.mode = mode;
- if (type != nullptr && expr->tav.type != nullptr &&
+ if (type != nullptr && expr->tav.type != nullptr &&
is_type_any(type) && is_type_untyped(expr->tav.type)) {
// ignore
} else {
@@ -1424,7 +1636,7 @@ bool could_entity_be_lazy(Entity *e, DeclInfo *d) {
return false;
} else if (name == "linkage") {
return false;
- }
+ }
}
}
}
@@ -1496,6 +1708,10 @@ void add_implicit_entity(CheckerContext *c, Ast *clause, Entity *e) {
void add_type_info_type(CheckerContext *c, Type *t) {
void add_type_info_type_internal(CheckerContext *c, Type *t);
+ if (build_context.disallow_rtti) {
+ return;
+ }
+
mutex_lock(&c->info->type_info_mutex);
add_type_info_type_internal(c, t);
mutex_unlock(&c->info->type_info_mutex);
@@ -1513,7 +1729,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
return;
}
- add_type_info_dependency(c->decl, t);
+ add_type_info_dependency(c->info, c->decl, t, false);
auto found = map_get(&c->info->type_info_map, t);
if (found != nullptr) {
@@ -1525,7 +1741,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
isize ti_index = -1;
for_array(i, c->info->type_info_map.entries) {
auto *e = &c->info->type_info_map.entries[i];
- if (are_types_identical(t, e->key)) {
+ if (are_types_identical_unique_tuples(t, e->key)) {
// Duplicate entry
ti_index = e->value;
prev = true;
@@ -1642,6 +1858,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
} else {
add_type_info_type_internal(c, t_type_info_ptr);
}
+ add_type_info_type_internal(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_type_info_type_internal(c, bt->Union.variants[i]);
}
@@ -1665,6 +1882,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
}
}
}
+ add_type_info_type_internal(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_type_info_type_internal(c, f->type);
@@ -1704,7 +1922,7 @@ void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->RelativeSlice.slice_type);
add_type_info_type_internal(c, bt->RelativeSlice.base_integer);
break;
-
+
case Type_Matrix:
add_type_info_type_internal(c, bt->Matrix.elem);
break;
@@ -1858,6 +2076,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
} else {
add_min_dep_type_info(c, t_type_info_ptr);
}
+ add_min_dep_type_info(c, bt->Union.polymorphic_params);
for_array(i, bt->Union.variants) {
add_min_dep_type_info(c, bt->Union.variants[i]);
}
@@ -1881,6 +2100,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
}
}
}
+ add_min_dep_type_info(c, bt->Struct.polymorphic_params);
for_array(i, bt->Struct.fields) {
Entity *f = bt->Struct.fields[i];
add_min_dep_type_info(c, f->type);
@@ -1919,7 +2139,7 @@ void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->RelativeSlice.slice_type);
add_min_dep_type_info(c, bt->RelativeSlice.base_integer);
break;
-
+
case Type_Matrix:
add_min_dep_type_info(c, bt->Matrix.elem);
break;
@@ -2004,23 +2224,27 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
ptr_set_init(&c->info.minimum_dependency_set, heap_allocator(), min_dep_set_cap);
ptr_set_init(&c->info.minimum_dependency_type_info_set, heap_allocator());
- String required_runtime_entities[] = {
+#define FORCE_ADD_RUNTIME_ENTITIES(condition, ...) do { \
+ if (condition) { \
+ String entities[] = {__VA_ARGS__}; \
+ for (isize i = 0; i < gb_count_of(entities); i++) { \
+ force_add_dependency_entity(c, c->info.runtime_package->scope, entities[i]); \
+ } \
+ } \
+} while (0)
+
+ // required runtime entities
+ FORCE_ADD_RUNTIME_ENTITIES(true,
// Odin types
- str_lit("Type_Info"),
str_lit("Source_Code_Location"),
str_lit("Context"),
str_lit("Allocator"),
str_lit("Logger"),
- // Global variables
- str_lit("args__"),
- str_lit("type_table"),
-
// Odin internal procedures
str_lit("__init_context"),
- str_lit("__type_info_of"),
str_lit("cstring_to_string"),
- str_lit("_cleanup_runtime"),
+ str_lit("_cleanup_runtime"),
// Pseudo-CRT required procedures
str_lit("memset"),
@@ -2047,39 +2271,40 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("gnu_h2f_ieee"),
str_lit("gnu_f2h_ieee"),
str_lit("extendhfsf2"),
-
+
// WASM Specific
str_lit("__ashlti3"),
str_lit("__multi3"),
- };
- for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, required_runtime_entities[i]);
- }
+ );
- if (build_context.no_crt) {
- String required_no_crt_entities[] = {
- // NOTE(bill): Only if these exist
- str_lit("_tls_index"),
- str_lit("_fltused"),
- };
- for (isize i = 0; i < gb_count_of(required_no_crt_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, required_no_crt_entities[i]);
- }
- }
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.disallow_rtti,
+ // Odin types
+ str_lit("Type_Info"),
- if (!build_context.no_bounds_check) {
- String bounds_check_entities[] = {
- // Bounds checking related procedures
- str_lit("bounds_check_error"),
- str_lit("matrix_bounds_check_error"),
- str_lit("slice_expr_error_hi"),
- str_lit("slice_expr_error_lo_hi"),
- str_lit("multi_pointer_slice_expr_error"),
- };
- for (isize i = 0; i < gb_count_of(bounds_check_entities); i++) {
- force_add_dependency_entity(c, c->info.runtime_package->scope, bounds_check_entities[i]);
- }
- }
+ // Global variables
+ str_lit("type_table"),
+ str_lit("__type_info_of"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_entry_point,
+ // Global variables
+ str_lit("args__"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES((build_context.no_crt && !is_arch_wasm()),
+ // NOTE(bill): Only if these exist
+ str_lit("_tls_index"),
+ str_lit("_fltused"),
+ );
+
+ FORCE_ADD_RUNTIME_ENTITIES(!build_context.no_bounds_check,
+ // Bounds checking related procedures
+ str_lit("bounds_check_error"),
+ str_lit("matrix_bounds_check_error"),
+ str_lit("slice_expr_error_hi"),
+ str_lit("slice_expr_error_lo_hi"),
+ str_lit("multi_pointer_slice_expr_error"),
+ );
for_array(i, c->info.definitions) {
Entity *e = c->info.definitions[i];
@@ -2110,34 +2335,38 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
case Entity_Variable:
if (e->Variable.is_export) {
add_dependency_to_set(c, e);
+ } else if (e->flags & EntityFlag_Require) {
+ add_dependency_to_set(c, e);
}
break;
case Entity_Procedure:
if (e->Procedure.is_export) {
add_dependency_to_set(c, e);
+ } else if (e->flags & EntityFlag_Require) {
+ add_dependency_to_set(c, e);
}
if (e->flags & EntityFlag_Init) {
Type *t = base_type(e->type);
GB_ASSERT(t->kind == Type_Proc);
-
+
bool is_init = true;
-
+
if (t->Proc.param_count != 0 || t->Proc.result_count != 0) {
gbString str = type_to_string(t);
error(e->token, "@(init) procedures must have a signature type with no parameters nor results, got %s", str);
gb_string_free(str);
is_init = false;
}
-
+
if ((e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) == 0) {
error(e->token, "@(init) procedures must be declared at the file scope");
is_init = false;
}
-
+
if (is_init) {
add_dependency_to_set(c, e);
array_add(&c->info.init_procedures, e);
- }
+ }
}
break;
}
@@ -2197,6 +2426,8 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) {
start->flags |= EntityFlag_Used;
add_dependency_to_set(c, start);
}
+
+#undef FORCE_ADD_RUNTIME_ENTITIES
}
bool is_entity_a_dependency(Entity *e) {
@@ -2445,6 +2676,15 @@ Array proc_group_entities(CheckerContext *c, Operand o) {
return procs;
}
+Array proc_group_entities_cloned(CheckerContext *c, Operand o) {
+ auto entities = proc_group_entities(c, o);
+ if (entities.count == 0) {
+ return {};
+ }
+ return array_clone(permanent_allocator(), entities);
+}
+
+
void init_core_type_info(Checker *c) {
@@ -2604,6 +2844,14 @@ ExactValue check_decl_attribute_value(CheckerContext *c, Ast *value) {
return ev;
}
+Type *check_decl_attribute_type(CheckerContext *c, Ast *value) {
+ if (value != nullptr) {
+ return check_type(c, value);
+ }
+ return nullptr;
+}
+
+
#define ATTRIBUTE_USER_TAG_NAME "tag"
@@ -2903,6 +3151,42 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
error(elem, "Expected a string for '%.*s'", LIT(name));
}
return true;
+ } else if (name == "objc_name") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_String) {
+ if (string_is_valid_identifier(ev.value_string)) {
+ ac->objc_name = ev.value_string;
+ } else {
+ error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
+ }
+ } else {
+ error(elem, "Expected a string value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_is_class_method") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind == ExactValue_Bool) {
+ ac->objc_is_class_method = ev.value_bool;
+ } else {
+ error(elem, "Expected a boolean value for '%.*s'", LIT(name));
+ }
+ return true;
+ } else if (name == "objc_type") {
+ if (value == nullptr) {
+ error(elem, "Expected a type for '%.*s'", LIT(name));
+ } else {
+ Type *objc_type = check_type(c, value);
+ if (objc_type != nullptr) {
+ if (!has_type_got_objc_class_attribute(objc_type)) {
+ gbString t = type_to_string(objc_type);
+ error(value, "'%.*s' expected a named type with the attribute @(obj_class=), got type %s", LIT(name), t);
+ gb_string_free(t);
+ } else {
+ ac->objc_type = objc_type;
+ }
+ }
+ }
+ return true;
}
return false;
}
@@ -3053,6 +3337,14 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
} else if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true;
+ } else if (name == "objc_class") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_String || ev.value_string == "") {
+ error(elem, "Expected a non-empty string value for '%.*s'", LIT(name));
+ } else {
+ ac->objc_class = ev.value_string;
+ }
+ return true;
}
return false;
}
@@ -3366,6 +3658,16 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
}
}
+ if (entity_visibility_kind == EntityVisiblity_Public &&
+ (c->scope->flags&ScopeFlag_File) &&
+ c->scope->file) {
+ if (c->scope->file->flags & AstFile_IsPrivateFile) {
+ entity_visibility_kind = EntityVisiblity_PrivateToFile;
+ } else if (c->scope->file->flags & AstFile_IsPrivatePkg) {
+ entity_visibility_kind = EntityVisiblity_PrivateToPackage;
+ }
+ }
+
if (entity_visibility_kind != EntityVisiblity_Public && !(c->scope->flags&ScopeFlag_File)) {
error(decl, "Attribute 'private' is not allowed on a non file scope entity");
}
@@ -3454,9 +3756,6 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) {
if (is_ast_type(init)) {
e = alloc_entity_type_name(d->scope, token, nullptr);
- // if (vd->type != nullptr) {
- // error(name, "A type declaration cannot have an type parameter");
- // }
} else if (init->kind == Ast_ProcLit) {
if (c->scope->flags&ScopeFlag_Type) {
error(name, "Procedure declarations are not allowed within a struct");
@@ -3559,6 +3858,59 @@ void check_add_foreign_block_decl(CheckerContext *ctx, Ast *decl) {
check_collect_entities(&c, block->stmts);
}
+bool correct_single_type_alias(CheckerContext *c, Entity *e) {
+ if (e->kind == Entity_Constant) {
+ DeclInfo *d = e->decl_info;
+ if (d != nullptr && d->init_expr != nullptr) {
+ Ast *init = d->init_expr;
+ Entity *alias_of = check_entity_from_ident_or_selector(c, init, true);
+ if (alias_of != nullptr && alias_of->kind == Entity_TypeName) {
+ e->kind = Entity_TypeName;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool correct_type_alias_in_scope_backwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = n-1; i >= 0; i--) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+bool correct_type_alias_in_scope_forwards(CheckerContext *c, Scope *s) {
+ isize n = s->elements.entries.count;
+ bool correction = false;
+ for (isize i = 0; i < n; i++) {
+ correction |= correct_single_type_alias(c, s->elements.entries[i].value);
+ }
+ return correction;
+}
+
+
+void correct_type_aliases_in_scope(CheckerContext *c, Scope *s) {
+ // NOTE(bill, 2022-02-04): This is used to solve the problem caused by type aliases
+ // of type aliases being "confused" as constants
+ //
+ // A :: C
+ // B :: A
+ // C :: struct {b: ^B}
+ //
+ // See @TypeAliasingProblem for more information
+ for (;;) {
+ bool corrections = false;
+ corrections |= correct_type_alias_in_scope_backwards(c, s);
+ corrections |= correct_type_alias_in_scope_forwards(c, s);
+ if (!corrections) {
+ return;
+ }
+ }
+}
+
+
// NOTE(bill): If file_scopes == nullptr, this will act like a local scope
void check_collect_entities(CheckerContext *c, Slice const &nodes) {
AstFile *curr_file = nullptr;
@@ -3630,6 +3982,7 @@ void check_collect_entities(CheckerContext *c, Slice const &nodes) {
}
}
+ // correct_type_aliases(c);
// NOTE(bill): 'when' stmts need to be handled after the other as the condition may refer to something
// declared after this stmt in source
@@ -3677,11 +4030,6 @@ void check_single_global_entity(Checker *c, Entity *e, DeclInfo *d) {
error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
return;
}
- } else if (pkg->kind == Package_Runtime) {
- if (e->token.string == "main") {
- error(e->token, "'main' is reserved as the entry point procedure in the initial scope");
- return;
- }
}
check_entity_decl(ctx, e, d, nullptr);
@@ -3841,7 +4189,7 @@ void add_import_dependency_node(Checker *c, Ast *decl, PtrMap generate_import_dependency_graph(Checker *c) {
- PtrMap M = {};
+ PtrMap M = {};
map_init(&M, heap_allocator(), 2*c->parser->packages.count);
defer (map_destroy(&M));
@@ -4121,11 +4469,11 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
mpmc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e);
add_entity_use(ctx, nullptr, e);
}
-
+
if (has_asm_extension(fullpath)) {
if (build_context.metrics.arch != TargetArch_amd64 ||
build_context.metrics.os != TargetOs_windows) {
- error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
+ error(decl, "Assembly files are not yet supported on this platform: %.*s_%.*s",
LIT(target_os_names[build_context.metrics.os]), LIT(target_arch_names[build_context.metrics.arch]));
}
}
@@ -4280,10 +4628,11 @@ bool collect_file_decls(CheckerContext *ctx, Slice const &decls) {
for_array(i, decls) {
if (collect_file_decl(ctx, decls[i])) {
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return true;
}
}
-
+ correct_type_aliases_in_scope(ctx, ctx->scope);
return false;
}
@@ -4327,7 +4676,7 @@ void check_with_workers(Checker *c, WorkerTaskProc *proc, isize total_count) {
if (!build_context.threaded_checker) {
worker_count = 0;
}
-
+
semaphore_post(&c->info.collect_semaphore, cast(i32)thread_count);
if (worker_count == 0) {
ThreadProcCheckerSection section_all = {};
@@ -4351,7 +4700,7 @@ void check_with_workers(Checker *c, WorkerTaskProc *proc, isize total_count) {
}
GB_ASSERT(remaining_count <= 0);
-
+
for (isize i = 0; i < thread_count; i++) {
global_thread_pool_add_task(proc, thread_data+i);
}
@@ -4553,6 +4902,15 @@ void check_import_entities(Checker *c) {
}
add_untyped_expressions(ctx.info, &untyped);
}
+
+ for_array(i, pkg->files) {
+ AstFile *f = pkg->files[i];
+ reset_checker_context(&ctx, f, &untyped);
+ ctx.collect_delayed_decls = false;
+
+ correct_type_aliases_in_scope(&ctx, pkg->scope);
+ }
+
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
reset_checker_context(&ctx, f, &untyped);
@@ -4750,11 +5108,11 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.decl = pi->decl;
ctx.procs_to_check_queue = procs_to_check_queue;
GB_ASSERT(procs_to_check_queue != nullptr);
-
+
GB_ASSERT(pi->type->kind == Type_Proc);
TypeProc *pt = &pi->type->Proc;
String name = pi->token.string;
-
+
if (pt->is_polymorphic && !pt->is_poly_specialized) {
Token token = pi->token;
if (pi->poly_def_node != nullptr) {
@@ -4774,6 +5132,9 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
bool bounds_check = (pi->tags & ProcTag_bounds_check) != 0;
bool no_bounds_check = (pi->tags & ProcTag_no_bounds_check) != 0;
+ bool type_assert = (pi->tags & ProcTag_type_assert) != 0;
+ bool no_type_assert = (pi->tags & ProcTag_no_type_assert) != 0;
+
if (bounds_check) {
ctx.state_flags |= StateFlag_bounds_check;
ctx.state_flags &= ~StateFlag_no_bounds_check;
@@ -4781,6 +5142,15 @@ bool check_proc_info(Checker *c, ProcInfo *pi, UntypedExprInfoMap *untyped, Proc
ctx.state_flags |= StateFlag_no_bounds_check;
ctx.state_flags &= ~StateFlag_bounds_check;
}
+
+ if (type_assert) {
+ ctx.state_flags |= StateFlag_type_assert;
+ ctx.state_flags &= ~StateFlag_no_type_assert;
+ } else if (no_type_assert) {
+ ctx.state_flags |= StateFlag_no_type_assert;
+ ctx.state_flags &= ~StateFlag_type_assert;
+ }
+
if (pi->body != nullptr && e != nullptr) {
GB_ASSERT((e->flags & EntityFlag_ProcBodyChecked) == 0);
}
@@ -4832,7 +5202,7 @@ void check_unchecked_bodies(Checker *c) {
if (pi->body == nullptr) {
continue;
}
-
+
debugf("unchecked: %.*s\n", LIT(e->token.string));
mpmc_enqueue(&c->procs_to_check_queue, pi);
}
@@ -4941,17 +5311,16 @@ void check_procedure_bodies(Checker *c) {
if (!build_context.threaded_checker) {
worker_count = 0;
}
- worker_count = 0;
if (worker_count == 0) {
auto *this_queue = &c->procs_to_check_queue;
-
+
UntypedExprInfoMap untyped = {};
map_init(&untyped, heap_allocator());
-
+
for (ProcInfo *pi = nullptr; mpmc_dequeue(this_queue, &pi); /**/) {
consume_proc_info_queue(c, pi, this_queue, &untyped);
}
-
+
map_destroy(&untyped);
debugf("Total Procedure Bodies Checked: %td\n", total_bodies_checked.load(std::memory_order_relaxed));
@@ -4995,7 +5364,7 @@ void check_procedure_bodies(Checker *c) {
GB_ASSERT(total_queued == original_queue_count);
semaphore_post(&c->procs_to_check_semaphore, cast(i32)thread_count);
-
+
for (isize i = 0; i < thread_count; i++) {
global_thread_pool_add_task(thread_proc_body, thread_data+i);
}
@@ -5032,7 +5401,7 @@ void check_deferred_procedures(Checker *c) {
Entity *dst = src->Procedure.deferred_procedure.entity;
GB_ASSERT(dst != nullptr);
GB_ASSERT(dst->kind == Entity_Procedure);
-
+
char const *attribute = "deferred_none";
switch (dst_kind) {
case DeferredProcedure_none:
@@ -5195,12 +5564,18 @@ void check_unique_package_names(Checker *c) {
string_map_set(&pkgs, key, pkg);
continue;
}
+ auto *curr = pkg->files[0]->pkg_decl;
+ auto *prev = (*found)->files[0]->pkg_decl;
+ if (curr == prev) {
+ // NOTE(bill): A false positive was found, ignore it
+ continue;
+ }
- error(pkg->files[0]->pkg_decl, "Duplicate declaration of 'package %.*s'", LIT(name));
+ error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
error_line("\tA package name must be unique\n"
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
"\tA package name is required for link name prefixing to have a consistent ABI\n");
- error((*found)->files[0]->pkg_decl, "found at previous location");
+ error(prev, "found at previous location");
}
}
@@ -5233,7 +5608,7 @@ GB_COMPARE_PROC(init_procedures_cmp) {
cmp = 0;
return cmp;
}
-
+
if (x->pkg != y->pkg) {
isize order_x = x->pkg ? x->pkg->order : 0;
isize order_y = y->pkg ? y->pkg->order : 0;
@@ -5247,14 +5622,14 @@ GB_COMPARE_PROC(init_procedures_cmp) {
String fullpath_y = y->file ? y->file->fullpath : (String{});
String file_x = filename_from_path(fullpath_x);
String file_y = filename_from_path(fullpath_y);
-
+
cmp = string_compare(file_x, file_y);
if (cmp) {
return cmp;
}
}
-
+
cmp = u64_cmp(x->order_in_src, y->order_in_src);
if (cmp) {
return cmp;
@@ -5392,9 +5767,6 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("calculate global init order");
calculate_global_init_order(c);
- TIME_SECTION("generate minimum dependency set");
- generate_minimum_dependency_set(c, c->info.entry_point);
-
TIME_SECTION("check test procedures");
check_test_procedures(c);
@@ -5405,6 +5777,9 @@ void check_parsed_files(Checker *c) {
add_type_info_for_type_definitions(c);
check_merge_queues_into_arrays(c);
+ TIME_SECTION("generate minimum dependency set");
+ generate_minimum_dependency_set(c, c->info.entry_point);
+
TIME_SECTION("check entry point");
if (build_context.build_mode == BuildMode_Executable && !build_context.no_entry_point && build_context.command_kind != Command_test) {
Scope *s = c->info.init_scope;
@@ -5434,9 +5809,21 @@ void check_parsed_files(Checker *c) {
TIME_SECTION("sanity checks");
GB_ASSERT(c->info.entity_queue.count.load(std::memory_order_relaxed) == 0);
GB_ASSERT(c->info.definition_queue.count.load(std::memory_order_relaxed) == 0);
-
+
TIME_SECTION("sort init procedures");
check_sort_init_procedures(c);
+ if (c->info.intrinsics_entry_point_usage.count > 0) {
+ TIME_SECTION("check intrinsics.__entry_point usage");
+ Ast *node = nullptr;
+ while (mpmc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
+ if (c->info.entry_point == nullptr && node != nullptr) {
+ if (node->file()->pkg->kind != Package_Runtime) {
+ warning(node, "usage of intrinsics.__entry_point will be a no-op");
+ }
+ }
+ }
+ }
+
TIME_SECTION("type check finish");
}
diff --git a/src/checker.hpp b/src/checker.hpp
index 74435c1d4..552e6aca7 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -118,6 +118,11 @@ struct AttributeContext {
bool init : 1;
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
+
+ String objc_class;
+ String objc_name;
+ bool objc_is_class_method;
+ Type * objc_type;
};
AttributeContext make_attribute_context(String link_prefix) {
@@ -267,6 +272,17 @@ struct UntypedExprInfo {
typedef PtrMap UntypedExprInfoMap;
typedef MPMCQueue ProcBodyQueue;
+enum ObjcMsgKind : u32 {
+ ObjcMsg_normal,
+ ObjcMsg_fpret,
+ ObjcMsg_fp2ret,
+ ObjcMsg_stret,
+};
+struct ObjcMsgData {
+ ObjcMsgKind kind;
+ Type *proc_type;
+};
+
// CheckerInfo stores all the symbol information for a type-checked program
struct CheckerInfo {
Checker *checker;
@@ -338,6 +354,10 @@ struct CheckerInfo {
MPMCQueue required_global_variable_queue;
MPMCQueue required_foreign_imports_through_force_queue;
+ MPMCQueue intrinsics_entry_point_usage;
+
+ BlockingMutex objc_types_mutex;
+ PtrMap objc_msgSend_types;
};
struct CheckerContext {
@@ -423,7 +443,7 @@ Entity *entity_of_node(Ast *expr);
Entity *scope_lookup_current(Scope *s, String const &name);
Entity *scope_lookup (Scope *s, String const &name);
void scope_lookup_parent (Scope *s, String const &name, Scope **scope_, Entity **entity_);
-Entity *scope_insert (Scope *s, Entity *entity);
+Entity *scope_insert (Scope *s, Entity *entity, bool use_mutex=true);
void add_type_and_value (CheckerInfo *i, Ast *expression, AddressingMode mode, Type *type, ExactValue value);
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index abd9fc6ca..0f72f01f7 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -86,77 +86,31 @@ enum BuiltinProcId {
BuiltinProc_prefetch_write_instruction,
BuiltinProc_prefetch_write_data,
- BuiltinProc_atomic_fence,
- BuiltinProc_atomic_fence_acq,
- BuiltinProc_atomic_fence_rel,
- BuiltinProc_atomic_fence_acqrel,
-
+ BuiltinProc_atomic_type_is_lock_free,
+ BuiltinProc_atomic_thread_fence,
+ BuiltinProc_atomic_signal_fence,
BuiltinProc_atomic_store,
- BuiltinProc_atomic_store_rel,
- BuiltinProc_atomic_store_relaxed,
- BuiltinProc_atomic_store_unordered,
-
+ BuiltinProc_atomic_store_explicit,
BuiltinProc_atomic_load,
- BuiltinProc_atomic_load_acq,
- BuiltinProc_atomic_load_relaxed,
- BuiltinProc_atomic_load_unordered,
-
+ BuiltinProc_atomic_load_explicit,
BuiltinProc_atomic_add,
- BuiltinProc_atomic_add_acq,
- BuiltinProc_atomic_add_rel,
- BuiltinProc_atomic_add_acqrel,
- BuiltinProc_atomic_add_relaxed,
+ BuiltinProc_atomic_add_explicit,
BuiltinProc_atomic_sub,
- BuiltinProc_atomic_sub_acq,
- BuiltinProc_atomic_sub_rel,
- BuiltinProc_atomic_sub_acqrel,
- BuiltinProc_atomic_sub_relaxed,
+ BuiltinProc_atomic_sub_explicit,
BuiltinProc_atomic_and,
- BuiltinProc_atomic_and_acq,
- BuiltinProc_atomic_and_rel,
- BuiltinProc_atomic_and_acqrel,
- BuiltinProc_atomic_and_relaxed,
+ BuiltinProc_atomic_and_explicit,
BuiltinProc_atomic_nand,
- BuiltinProc_atomic_nand_acq,
- BuiltinProc_atomic_nand_rel,
- BuiltinProc_atomic_nand_acqrel,
- BuiltinProc_atomic_nand_relaxed,
+ BuiltinProc_atomic_nand_explicit,
BuiltinProc_atomic_or,
- BuiltinProc_atomic_or_acq,
- BuiltinProc_atomic_or_rel,
- BuiltinProc_atomic_or_acqrel,
- BuiltinProc_atomic_or_relaxed,
+ BuiltinProc_atomic_or_explicit,
BuiltinProc_atomic_xor,
- BuiltinProc_atomic_xor_acq,
- BuiltinProc_atomic_xor_rel,
- BuiltinProc_atomic_xor_acqrel,
- BuiltinProc_atomic_xor_relaxed,
-
- BuiltinProc_atomic_xchg,
- BuiltinProc_atomic_xchg_acq,
- BuiltinProc_atomic_xchg_rel,
- BuiltinProc_atomic_xchg_acqrel,
- BuiltinProc_atomic_xchg_relaxed,
-
- BuiltinProc_atomic_cxchg,
- BuiltinProc_atomic_cxchg_acq,
- BuiltinProc_atomic_cxchg_rel,
- BuiltinProc_atomic_cxchg_acqrel,
- BuiltinProc_atomic_cxchg_relaxed,
- BuiltinProc_atomic_cxchg_failrelaxed,
- BuiltinProc_atomic_cxchg_failacq,
- BuiltinProc_atomic_cxchg_acq_failrelaxed,
- BuiltinProc_atomic_cxchg_acqrel_failrelaxed,
-
- BuiltinProc_atomic_cxchgweak,
- BuiltinProc_atomic_cxchgweak_acq,
- BuiltinProc_atomic_cxchgweak_rel,
- BuiltinProc_atomic_cxchgweak_acqrel,
- BuiltinProc_atomic_cxchgweak_relaxed,
- BuiltinProc_atomic_cxchgweak_failrelaxed,
- BuiltinProc_atomic_cxchgweak_failacq,
- BuiltinProc_atomic_cxchgweak_acq_failrelaxed,
- BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed,
+ BuiltinProc_atomic_xor_explicit,
+ BuiltinProc_atomic_exchange,
+ BuiltinProc_atomic_exchange_explicit,
+ BuiltinProc_atomic_compare_exchange_strong,
+ BuiltinProc_atomic_compare_exchange_strong_explicit,
+ BuiltinProc_atomic_compare_exchange_weak,
+ BuiltinProc_atomic_compare_exchange_weak_explicit,
BuiltinProc_fixed_point_mul,
BuiltinProc_fixed_point_div,
@@ -213,8 +167,6 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_union,
BuiltinProc_type_is_enum,
BuiltinProc_type_is_proc,
- BuiltinProc_type_is_bit_field,
- BuiltinProc_type_is_bit_field_value,
BuiltinProc_type_is_bit_set,
BuiltinProc_type_is_simd_vector,
BuiltinProc_type_is_matrix,
@@ -227,6 +179,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_has_field,
+ BuiltinProc_type_field_type,
BuiltinProc_type_is_specialization_of,
@@ -243,6 +196,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_polymorphic_record_parameter_count,
BuiltinProc_type_polymorphic_record_parameter_value,
+ BuiltinProc_type_is_subtype_of,
+
BuiltinProc_type_field_index_of,
BuiltinProc_type_equal_proc,
@@ -250,6 +205,19 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end,
+ BuiltinProc___entry_point,
+
+ BuiltinProc_objc_send,
+ BuiltinProc_objc_find_selector,
+ BuiltinProc_objc_find_class,
+ BuiltinProc_objc_register_selector,
+ BuiltinProc_objc_register_class,
+
+ BuiltinProc_constant_utf16_cstring,
+
+ BuiltinProc_wasm_memory_grow,
+ BuiltinProc_wasm_memory_size,
+
BuiltinProc_COUNT,
};
gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
@@ -339,78 +307,31 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("prefetch_write_instruction"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("prefetch_write_data"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
- {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
+ {STR_LIT("atomic_type_is_lock_free"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_thread_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_signal_fence"), 1, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_store_explicit"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_load_explicit"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_add_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_sub_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_and_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_nand_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_or_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_xor_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_exchange"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_exchange_explicit"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_strong"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_strong_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_weak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("atomic_compare_exchange_weak_explicit"), 5, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_mul"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("fixed_point_div"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -464,8 +385,6 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
- {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -477,6 +396,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -493,10 +413,27 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_polymorphic_record_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_polymorphic_record_parameter_value"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_subtype_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
{STR_LIT("type_field_index_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_equal_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_hasher_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
-
+
+
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("objc_find_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_find_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_register_selector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("objc_register_class"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("constant_utf16_cstring"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
+ {STR_LIT("wasm_memory_grow"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("wasm_memory_size"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
};
diff --git a/src/common.cpp b/src/common.cpp
index ab2a46118..94248fb62 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -675,262 +675,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
#endif
-
-#if defined(GB_SYSTEM_WINDOWS)
- bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return res;
-}
-
-
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-ReadDirectoryError read_directory(String path, Array *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(a, path);
- defer (gb_free(a, c_str));
-
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- wchar_t *filename_w = file_data.cFileName;
- i64 size = cast(i64)file_data.nFileSizeLow;
- size |= (cast(i64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD)
-
-#include
-
-ReadDirectoryError read_directory(String path, Array *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#else
-#error Implement read_directory
-#endif
-
-
+#include "path.cpp"
struct LoadedFile {
void *handle;
@@ -1021,7 +766,7 @@ LoadedFileError load_file_32(char const *fullpath, LoadedFile *memory_mapped_fil
#endif
}
- gbFileContents fc = gb_file_read_contents(heap_allocator(), true, fullpath);
+ gbFileContents fc = gb_file_read_contents(permanent_allocator(), true, fullpath);
if (fc.size > I32_MAX) {
err = LoadedFile_FileTooLarge;
diff --git a/src/common_memory.cpp b/src/common_memory.cpp
index 2d7a7a246..953462077 100644
--- a/src/common_memory.cpp
+++ b/src/common_memory.cpp
@@ -139,6 +139,7 @@ struct PlatformMemoryBlock {
};
+gb_global std::atomic global_platform_memory_total_usage;
gb_global PlatformMemoryBlock global_platform_memory_block_sentinel;
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size);
@@ -158,10 +159,17 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)VirtualAlloc(0, total_size, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
- GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ if (pmblock == nullptr) {
+ gb_printf_err("Out of Virtual memory, oh no...\n");
+ gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
+ gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
+ GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ }
+ global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
+ global_platform_memory_total_usage -= block->total_size;
GB_ASSERT(VirtualFree(block, 0, MEM_RELEASE));
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -180,11 +188,18 @@ void platform_virtual_memory_protect(void *memory, isize size);
PlatformMemoryBlock *platform_virtual_memory_alloc(isize total_size) {
PlatformMemoryBlock *pmblock = (PlatformMemoryBlock *)mmap(nullptr, total_size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ if (pmblock == nullptr) {
+ gb_printf_err("Out of Virtual memory, oh no...\n");
+ gb_printf_err("Requested: %lld bytes\n", cast(long long)total_size);
+ gb_printf_err("Total Usage: %lld bytes\n", cast(long long)global_platform_memory_total_usage);
+ GB_ASSERT_MSG(pmblock != nullptr, "Out of Virtual Memory, oh no...");
+ }
+ global_platform_memory_total_usage += total_size;
return pmblock;
}
void platform_virtual_memory_free(PlatformMemoryBlock *block) {
isize size = block->total_size;
+ global_platform_memory_total_usage -= size;
munmap(block, size);
}
void platform_virtual_memory_protect(void *memory, isize size) {
@@ -325,18 +340,32 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) {
// TODO(bill): Throughly test!
switch (type) {
#if defined(GB_COMPILER_MSVC)
- case gbAllocation_Alloc: {
- isize aligned_size = align_formula_isize(size, alignment);
- // TODO(bill): Make sure this is aligned correctly
- ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
- } break;
- case gbAllocation_Free:
- HeapFree(GetProcessHeap(), 0, old_memory);
+ case gbAllocation_Alloc:
+ if (size == 0) {
+ return NULL;
+ } else {
+ isize aligned_size = align_formula_isize(size, alignment);
+ // TODO(bill): Make sure this is aligned correctly
+ ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
+ }
+ break;
+ case gbAllocation_Free:
+ if (old_memory != nullptr) {
+ HeapFree(GetProcessHeap(), 0, old_memory);
+ }
+ break;
+ case gbAllocation_Resize:
+ if (old_memory != nullptr && size > 0) {
+ isize aligned_size = align_formula_isize(size, alignment);
+ ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
+ } else if (old_memory != nullptr) {
+ HeapFree(GetProcessHeap(), 0, old_memory);
+ } else if (size != 0) {
+ isize aligned_size = align_formula_isize(size, alignment);
+ // TODO(bill): Make sure this is aligned correctly
+ ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, aligned_size);
+ }
break;
- case gbAllocation_Resize: {
- isize aligned_size = align_formula_isize(size, alignment);
- ptr = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, old_memory, aligned_size);
- } break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
diff --git a/src/docs.cpp b/src/docs.cpp
index 8d65cb83a..3ea3cce1b 100644
--- a/src/docs.cpp
+++ b/src/docs.cpp
@@ -67,6 +67,14 @@ GB_COMPARE_PROC(cmp_ast_package_by_name) {
#include "docs_format.cpp"
#include "docs_writer.cpp"
+void print_doc_line(i32 indent, String const &data) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
+ gb_printf("\n");
+}
+
void print_doc_line(i32 indent, char const *fmt, ...) {
while (indent --> 0) {
gb_printf("\t");
@@ -86,6 +94,13 @@ void print_doc_line_no_newline(i32 indent, char const *fmt, ...) {
gb_printf_va(fmt, va);
va_end(va);
}
+void print_doc_line_no_newline(i32 indent, String const &data) {
+ while (indent --> 0) {
+ gb_printf("\t");
+ }
+ gb_file_write(gb_file_get_standard(gbFileStandard_Output), data.text, data.len);
+}
+
bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
if (g == nullptr) {
@@ -106,8 +121,9 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
- bool slash_slash = comment[1] == '/';
+ bool slash_slash = false;
if (comment[1] == '/') {
+ slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -131,7 +147,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
if (slash_slash) {
- print_doc_line(indent, "%.*s", LIT(comment));
+ print_doc_line(indent, comment);
count += 1;
} else {
isize pos = 0;
@@ -143,7 +159,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
- pos = end+1;
+ pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -159,7 +175,7 @@ bool print_doc_comment_group_string(i32 indent, CommentGroup *g) {
line = substring(line, 2, line.len);
}
- print_doc_line(indent, "%.*s", LIT(line));
+ print_doc_line(indent, line);
count += 1;
}
}
@@ -263,7 +279,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
}
GB_ASSERT(type_expr != nullptr || init_expr != nullptr);
- print_doc_line_no_newline(2, "%.*s", LIT(e->token.string));
+ print_doc_line_no_newline(2, e->token.string);
if (type_expr != nullptr) {
gbString t = expr_to_string(type_expr);
gb_printf(": %s ", t);
@@ -298,7 +314,7 @@ void print_doc_package(CheckerInfo *info, AstPackage *pkg) {
for_array(i, pkg->files) {
AstFile *f = pkg->files[i];
String filename = remove_directory_from_path(f->fullpath);
- print_doc_line(2, "%.*s", LIT(filename));
+ print_doc_line(2, filename);
}
}
diff --git a/src/docs_format.cpp b/src/docs_format.cpp
index 1c3af6257..ee32d0e05 100644
--- a/src/docs_format.cpp
+++ b/src/docs_format.cpp
@@ -15,7 +15,7 @@ struct OdinDocVersionType {
#define OdinDocVersionType_Major 0
#define OdinDocVersionType_Minor 2
-#define OdinDocVersionType_Patch 1
+#define OdinDocVersionType_Patch 4
struct OdinDocHeaderBase {
u8 magic[8];
@@ -99,6 +99,7 @@ enum OdinDocTypeFlag_Union : u32 {
OdinDocTypeFlag_Union_polymorphic = 1<<0,
OdinDocTypeFlag_Union_no_nil = 1<<1,
OdinDocTypeFlag_Union_maybe = 1<<2,
+ OdinDocTypeFlag_Union_shared_nil = 1<<3,
};
enum OdinDocTypeFlag_Proc : u32 {
@@ -137,6 +138,7 @@ struct OdinDocType {
OdinDocArray entities;
OdinDocTypeIndex polmorphic_params;
OdinDocArray where_clauses;
+ OdinDocArray tags; // struct field tags
};
struct OdinDocAttribute {
@@ -153,6 +155,7 @@ enum OdinDocEntityKind : u32 {
OdinDocEntity_ProcGroup = 5,
OdinDocEntity_ImportName = 6,
OdinDocEntity_LibraryName = 7,
+ OdinDocEntity_Builtin = 8,
};
enum OdinDocEntityFlag : u64 {
@@ -169,8 +172,13 @@ enum OdinDocEntityFlag : u64 {
OdinDocEntityFlag_Type_Alias = 1ull<<20,
+ OdinDocEntityFlag_Builtin_Pkg_Builtin = 1ull<<30,
+ OdinDocEntityFlag_Builtin_Pkg_Intrinsics = 1ull<<31,
+
OdinDocEntityFlag_Var_Thread_Local = 1ull<<40,
OdinDocEntityFlag_Var_Static = 1ull<<41,
+
+ OdinDocEntityFlag_Private = 1ull<<50,
};
struct OdinDocEntity {
@@ -182,8 +190,9 @@ struct OdinDocEntity {
OdinDocTypeIndex type;
OdinDocString init_string;
u32 reserved_for_init;
- OdinDocString comment;
- OdinDocString docs;
+ OdinDocString comment; // line comment
+ OdinDocString docs; // preceding comment
+ i32 field_group_index;
OdinDocEntityIndex foreign_library;
OdinDocString link_name;
OdinDocArray attributes;
@@ -197,15 +206,21 @@ enum OdinDocPkgFlags : u32 {
OdinDocPkgFlag_Init = 1<<2,
};
+struct OdinDocScopeEntry {
+ OdinDocString name;
+ OdinDocEntityIndex entity;
+};
+
struct OdinDocPkg {
OdinDocString fullpath;
OdinDocString name;
u32 flags;
OdinDocString docs;
- OdinDocArray files;
- OdinDocArray entities;
+ OdinDocArray files;
+ OdinDocArray entries;
};
+
struct OdinDocHeader {
OdinDocHeaderBase base;
diff --git a/src/docs_writer.cpp b/src/docs_writer.cpp
index e8e8892ec..0ad10ac49 100644
--- a/src/docs_writer.cpp
+++ b/src/docs_writer.cpp
@@ -292,8 +292,9 @@ bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) {
String comment = g->list[i].string;
String original_comment = comment;
- bool slash_slash = comment[1] == '/';
+ bool slash_slash = false;
if (comment[1] == '/') {
+ slash_slash = true;
comment.text += 2;
comment.len -= 2;
} else if (comment[1] == '*') {
@@ -330,7 +331,7 @@ bool odin_doc_append_comment_group_string(Array *buf, CommentGroup *g) {
}
}
String line = substring(comment, pos, end);
- pos = end+1;
+ pos = end;
String trimmed_line = string_trim_whitespace(line);
if (trimmed_line.len == 0) {
if (count == 0) {
@@ -482,7 +483,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
for_array(i, w->type_cache.entries) {
// NOTE(bill): THIS IS SLOW
Type *other = w->type_cache.entries[i].key;
- if (are_types_identical(type, other)) {
+ if (are_types_identical_unique_tuples(type, other)) {
OdinDocTypeIndex index = w->type_cache.entries[i].value;
map_set(&w->type_cache, type, index);
return index;
@@ -511,10 +512,16 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.entities = odin_doc_add_entity_as_slice(w, type->Named.type_name);
break;
case Type_Generic:
- doc_type.kind = OdinDocType_Generic;
- doc_type.name = odin_doc_write_string(w, type->Generic.name);
- if (type->Generic.specialized) {
- doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ {
+ String name = type->Generic.name;
+ if (type->Generic.entity) {
+ name = type->Generic.entity->token.string;
+ }
+ doc_type.kind = OdinDocType_Generic;
+ doc_type.name = odin_doc_write_string(w, name);
+ if (type->Generic.specialized) {
+ doc_type.types = odin_doc_type_as_slice(w, type->Generic.specialized);
+ }
}
break;
case Type_Pointer:
@@ -598,14 +605,25 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
}
doc_type.where_clauses = odin_doc_where_clauses(w, st->where_clauses);
}
+
+ auto tags = array_make(heap_allocator(), type->Struct.fields.count);
+ defer (array_free(&tags));
+
+ for_array(i, type->Struct.fields) {
+ tags[i] = odin_doc_write_string(w, type->Struct.tags[i]);
+ }
+
+ doc_type.tags = odin_write_slice(w, tags.data, tags.count);
}
break;
case Type_Union:
doc_type.kind = OdinDocType_Union;
if (type->Union.is_polymorphic) { doc_type.flags |= OdinDocTypeFlag_Union_polymorphic; }
- if (type->Union.no_nil) { doc_type.flags |= OdinDocTypeFlag_Union_no_nil; }
- if (type->Union.maybe) { doc_type.flags |= OdinDocTypeFlag_Union_maybe; }
-
+ switch (type->Union.kind) {
+ case UnionType_maybe: doc_type.flags |= OdinDocTypeFlag_Union_maybe; break;
+ case UnionType_no_nil: doc_type.flags |= OdinDocTypeFlag_Union_no_nil; break;
+ case UnionType_shared_nil: doc_type.flags |= OdinDocTypeFlag_Union_shared_nil; break;
+ }
{
auto variants = array_make(heap_allocator(), type->Union.variants.count);
defer (array_free(&variants));
@@ -667,40 +685,7 @@ OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
types[1] = odin_doc_type(w, type->Proc.results);
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
- String calling_convention = {};
- switch (type->Proc.calling_convention) {
- case ProcCC_Invalid:
- // no need
- break;
- case ProcCC_Odin:
- if (default_calling_convention() != ProcCC_Odin) {
- calling_convention = str_lit("odin");
- }
- break;
- case ProcCC_Contextless:
- if (default_calling_convention() != ProcCC_Contextless) {
- calling_convention = str_lit("contextless");
- }
- break;
- case ProcCC_CDecl:
- calling_convention = str_lit("cdecl");
- break;
- case ProcCC_StdCall:
- calling_convention = str_lit("stdcall");
- break;
- case ProcCC_FastCall:
- calling_convention = str_lit("fastcall");
- break;
- case ProcCC_None:
- calling_convention = str_lit("none");
- break;
- case ProcCC_Naked:
- calling_convention = str_lit("naked");
- break;
- case ProcCC_InlineAsm:
- calling_convention = str_lit("inline-assembly");
- break;
- }
+ String calling_convention = make_string_c(proc_calling_convention_strings[type->Proc.calling_convention]);
doc_type.calling_convention = odin_doc_write_string(w, calling_convention);
}
break;
@@ -795,11 +780,21 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
comment = e->decl_info->comment;
docs = e->decl_info->docs;
}
+ if (e->kind == Entity_Variable) {
+ if (!comment) { comment = e->Variable.comment; }
+ if (!docs) { docs = e->Variable.docs; }
+ } else if (e->kind == Entity_Constant) {
+ if (!comment) { comment = e->Constant.comment; }
+ if (!docs) { docs = e->Constant.docs; }
+ }
+ String name = e->token.string;
String link_name = {};
+ TokenPos pos = e->token.pos;
OdinDocEntityKind kind = OdinDocEntity_Invalid;
- u32 flags = 0;
+ u64 flags = 0;
+ i32 field_group_index = -1;
switch (e->kind) {
case Entity_Invalid: kind = OdinDocEntity_Invalid; break;
@@ -810,6 +805,7 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
case Entity_ProcGroup: kind = OdinDocEntity_ProcGroup; break;
case Entity_ImportName: kind = OdinDocEntity_ImportName; break;
case Entity_LibraryName: kind = OdinDocEntity_LibraryName; break;
+ case Entity_Builtin: kind = OdinDocEntity_Builtin; break;
}
switch (e->kind) {
@@ -829,12 +825,33 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
if (init_expr == nullptr) {
init_expr = e->Variable.init_expr;
}
+ field_group_index = e->Variable.field_group_index;
+ break;
+ case Entity_Constant:
+ field_group_index = e->Constant.field_group_index;
break;
case Entity_Procedure:
if (e->Procedure.is_foreign) { flags |= OdinDocEntityFlag_Foreign; }
if (e->Procedure.is_export) { flags |= OdinDocEntityFlag_Export; }
link_name = e->Procedure.link_name;
break;
+ case Entity_Builtin:
+ {
+ auto bp = builtin_procs[e->Builtin.id];
+ pos = {};
+ name = bp.name;
+ switch (bp.pkg) {
+ case BuiltinProcPkg_builtin:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Builtin;
+ break;
+ case BuiltinProcPkg_intrinsics:
+ flags |= OdinDocEntityFlag_Builtin_Pkg_Intrinsics;
+ break;
+ default:
+ GB_PANIC("Unhandled BuiltinProcPkg");
+ }
+ }
+ break;
}
if (e->flags & EntityFlag_Param) {
@@ -845,6 +862,9 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
}
+ if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
+ flags |= OdinDocEntityFlag_Private;
+ }
OdinDocString init_string = {};
if (init_expr) {
@@ -867,12 +887,13 @@ OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e) {
doc_entity.kind = kind;
doc_entity.flags = flags;
- doc_entity.pos = odin_doc_token_pos_cast(w, e->token.pos);
- doc_entity.name = odin_doc_write_string(w, e->token.string);
+ doc_entity.pos = odin_doc_token_pos_cast(w, pos);
+ doc_entity.name = odin_doc_write_string(w, name);
doc_entity.type = 0; // Set later
doc_entity.init_string = init_string;
doc_entity.comment = odin_doc_comment_group_string(w, comment);
doc_entity.docs = odin_doc_comment_group_string(w, docs);
+ doc_entity.field_group_index = field_group_index;
doc_entity.foreign_library = 0; // Set later
doc_entity.link_name = odin_doc_write_string(w, link_name);
if (e->decl_info != nullptr) {
@@ -944,7 +965,7 @@ void odin_doc_update_entities(OdinDocWriter *w) {
-OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, AstPackage *pkg) {
+OdinDocArray odin_doc_add_pkg_entries(OdinDocWriter *w, AstPackage *pkg) {
if (pkg->scope == nullptr) {
return {};
}
@@ -952,14 +973,14 @@ OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
return {};
}
- auto entities = array_make(heap_allocator(), 0, pkg->scope->elements.entries.count);
- defer (array_free(&entities));
+ auto entries = array_make(heap_allocator(), 0, w->entity_cache.entries.count);
+ defer (array_free(&entries));
for_array(i, pkg->scope->elements.entries) {
+ String name = pkg->scope->elements.entries[i].key.string;
Entity *e = pkg->scope->elements.entries[i].value;
switch (e->kind) {
case Entity_Invalid:
- case Entity_Builtin:
case Entity_Nil:
case Entity_Label:
continue;
@@ -970,34 +991,27 @@ OdinDocArray odin_doc_add_pkg_entities(OdinDocWriter *w, Ast
case Entity_ProcGroup:
case Entity_ImportName:
case Entity_LibraryName:
+ case Entity_Builtin:
// Fine
break;
}
- array_add(&entities, e);
- }
- gb_sort_array(entities.data, entities.count, cmp_entities_for_printing);
-
- auto entity_indices = array_make(heap_allocator(), 0, w->entity_cache.entries.count);
- defer (array_free(&entity_indices));
-
- for_array(i, entities) {
- Entity *e = entities[i];
if (e->pkg != pkg) {
continue;
}
- if (!is_entity_exported(e)) {
+ if (!is_entity_exported(e, true)) {
continue;
}
if (e->token.string.len == 0) {
continue;
}
- OdinDocEntityIndex doc_entity_index = 0;
- doc_entity_index = odin_doc_add_entity(w, e);
- array_add(&entity_indices, doc_entity_index);
+ OdinDocScopeEntry entry = {};
+ entry.name = odin_doc_write_string(w, name);
+ entry.entity = odin_doc_add_entity(w, e);
+ array_add(&entries, entry);
}
- return odin_write_slice(w, entity_indices.data, entity_indices.count);
+ return odin_write_slice(w, entries.data, entries.count);
}
@@ -1065,7 +1079,7 @@ void odin_doc_write_docs(OdinDocWriter *w) {
}
doc_pkg.files = odin_write_slice(w, file_indices.data, file_indices.count);
- doc_pkg.entities = odin_doc_add_pkg_entities(w, pkg);
+ doc_pkg.entries = odin_doc_add_pkg_entries(w, pkg);
if (dst) {
*dst = doc_pkg;
diff --git a/src/entity.cpp b/src/entity.cpp
index b39ffc63a..1f87f7af6 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -45,9 +45,9 @@ enum EntityFlag : u64 {
EntityFlag_NoAlias = 1ull<<9,
EntityFlag_TypeField = 1ull<<10,
EntityFlag_Value = 1ull<<11,
- EntityFlag_Sret = 1ull<<12,
- EntityFlag_ByVal = 1ull<<13,
- EntityFlag_BitFieldValue = 1ull<<14,
+
+
+
EntityFlag_PolyConst = 1ull<<15,
EntityFlag_NotExported = 1ull<<16,
EntityFlag_ConstInput = 1ull<<17,
@@ -74,6 +74,7 @@ enum EntityFlag : u64 {
EntityFlag_Test = 1ull<<30,
EntityFlag_Init = 1ull<<31,
+ EntityFlag_Subtype = 1ull<<32,
EntityFlag_CustomLinkName = 1ull<<40,
EntityFlag_CustomLinkage_Internal = 1ull<<41,
@@ -86,6 +87,10 @@ enum EntityFlag : u64 {
EntityFlag_Overridden = 1ull<<63,
};
+enum : u64 {
+ EntityFlags_IsSubtype = EntityFlag_Using|EntityFlag_Subtype,
+};
+
enum EntityState : u32 {
EntityState_Unresolved = 0,
EntityState_InProgress = 1,
@@ -110,6 +115,16 @@ struct ParameterValue {
};
};
+bool has_parameter_value(ParameterValue const ¶m_value) {
+ if (param_value.kind != ParameterValue_Invalid) {
+ return true;
+ }
+ if (param_value.original_ast_expr != nullptr) {
+ return true;
+ }
+ return false;
+}
+
enum EntityConstantFlags : u32 {
EntityConstantFlag_ImplicitEnumValue = 1<<0,
};
@@ -122,6 +137,28 @@ enum ProcedureOptimizationMode : u32 {
ProcedureOptimizationMode_Speed,
};
+
+BlockingMutex global_type_name_objc_metadata_mutex;
+
+struct TypeNameObjCMetadataEntry {
+ String name;
+ Entity *entity;
+};
+struct TypeNameObjCMetadata {
+ BlockingMutex *mutex;
+ Array type_entries;
+ Array value_entries;
+};
+
+TypeNameObjCMetadata *create_type_name_obj_c_metadata() {
+ TypeNameObjCMetadata *md = gb_alloc_item(permanent_allocator(), TypeNameObjCMetadata);
+ md->mutex = gb_alloc_item(permanent_allocator(), BlockingMutex);
+ mutex_init(md->mutex);
+ array_init(&md->type_entries, heap_allocator());
+ array_init(&md->value_entries, heap_allocator());
+ return md;
+}
+
// An Entity is a named "thing" in the language
struct Entity {
EntityKind kind;
@@ -160,10 +197,14 @@ struct Entity {
ExactValue value;
ParameterValue param_value;
u32 flags;
+ i32 field_group_index;
+ CommentGroup *docs;
+ CommentGroup *comment;
} Constant;
struct {
Ast *init_expr; // only used for some variables within procedure bodies
i32 field_index;
+ i32 field_group_index;
ParameterValue param_value;
@@ -173,6 +214,8 @@ struct Entity {
String link_name;
String link_prefix;
String link_section;
+ CommentGroup *docs;
+ CommentGroup *comment;
bool is_foreign;
bool is_export;
} Variable;
@@ -180,6 +223,8 @@ struct Entity {
Type * type_parameter_specialization;
String ir_mangled_name;
bool is_type_alias;
+ String objc_class_name;
+ TypeNameObjCMetadata *objc_metadata;
} TypeName;
struct {
u64 tags;
@@ -239,7 +284,7 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) {
if (e->flags & EntityFlag_NotExported) {
return false;
}
- if (e->file != nullptr && (e->file->flags & AstFile_IsPrivate) != 0) {
+ if (e->file != nullptr && (e->file->flags & (AstFile_IsPrivatePkg|AstFile_IsPrivateFile)) != 0) {
return false;
}
diff --git a/src/error.cpp b/src/error.cpp
new file mode 100644
index 000000000..faf4d11fb
--- /dev/null
+++ b/src/error.cpp
@@ -0,0 +1,416 @@
+struct ErrorCollector {
+ TokenPos prev;
+ std::atomic count;
+ std::atomic warning_count;
+ std::atomic in_block;
+ BlockingMutex mutex;
+ BlockingMutex error_out_mutex;
+ BlockingMutex string_mutex;
+ RecursiveMutex block_mutex;
+
+ Array error_buffer;
+ Array errors;
+};
+
+gb_global ErrorCollector global_error_collector;
+
+#define MAX_ERROR_COLLECTOR_COUNT (36)
+
+
+bool any_errors(void) {
+ return global_error_collector.count.load() != 0;
+}
+
+void init_global_error_collector(void) {
+ mutex_init(&global_error_collector.mutex);
+ mutex_init(&global_error_collector.block_mutex);
+ mutex_init(&global_error_collector.error_out_mutex);
+ mutex_init(&global_error_collector.string_mutex);
+ array_init(&global_error_collector.errors, heap_allocator());
+ array_init(&global_error_collector.error_buffer, heap_allocator());
+ array_init(&global_file_path_strings, heap_allocator(), 1, 4096);
+ array_init(&global_files, heap_allocator(), 1, 4096);
+}
+
+
+// temporary
+// defined in build_settings.cpp
+char *token_pos_to_string(TokenPos const &pos);
+
+bool set_file_path_string(i32 index, String const &path) {
+ bool ok = false;
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ if (index >= global_file_path_strings.count) {
+ array_resize(&global_file_path_strings, index+1);
+ }
+ String prev = global_file_path_strings[index];
+ if (prev.len == 0) {
+ global_file_path_strings[index] = path;
+ ok = true;
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return ok;
+}
+
+bool thread_safe_set_ast_file_from_id(i32 index, AstFile *file) {
+ bool ok = false;
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ if (index >= global_files.count) {
+ array_resize(&global_files, index+1);
+ }
+ AstFile *prev = global_files[index];
+ if (prev == nullptr) {
+ global_files[index] = file;
+ ok = true;
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return ok;
+}
+
+String get_file_path_string(i32 index) {
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ String path = {};
+ if (index < global_file_path_strings.count) {
+ path = global_file_path_strings[index];
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return path;
+}
+
+AstFile *thread_safe_get_ast_file_from_id(i32 index) {
+ GB_ASSERT(index >= 0);
+ mutex_lock(&global_error_collector.string_mutex);
+
+ AstFile *file = nullptr;
+ if (index < global_files.count) {
+ file = global_files[index];
+ }
+
+ mutex_unlock(&global_error_collector.string_mutex);
+ return file;
+}
+
+
+
+void begin_error_block(void) {
+ mutex_lock(&global_error_collector.block_mutex);
+ global_error_collector.in_block.store(true);
+}
+
+void end_error_block(void) {
+ if (global_error_collector.error_buffer.count > 0) {
+ isize n = global_error_collector.error_buffer.count;
+ u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
+ gb_memmove(text, global_error_collector.error_buffer.data, n);
+ text[n] = 0;
+ String s = {text, n};
+ array_add(&global_error_collector.errors, s);
+ global_error_collector.error_buffer.count = 0;
+ }
+
+ global_error_collector.in_block.store(false);
+ mutex_unlock(&global_error_collector.block_mutex);
+}
+
+#define ERROR_BLOCK() begin_error_block(); defer (end_error_block())
+
+
+#define ERROR_OUT_PROC(name) void name(char const *fmt, va_list va)
+typedef ERROR_OUT_PROC(ErrorOutProc);
+
+ERROR_OUT_PROC(default_error_out_va) {
+ gbFile *f = gb_file_get_standard(gbFileStandard_Error);
+
+ char buf[4096] = {};
+ isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va);
+ isize n = len-1;
+ if (global_error_collector.in_block) {
+ isize cap = global_error_collector.error_buffer.count + n;
+ array_reserve(&global_error_collector.error_buffer, cap);
+ u8 *data = global_error_collector.error_buffer.data + global_error_collector.error_buffer.count;
+ gb_memmove(data, buf, n);
+ global_error_collector.error_buffer.count += n;
+ } else {
+ mutex_lock(&global_error_collector.error_out_mutex);
+ {
+ u8 *text = gb_alloc_array(permanent_allocator(), u8, n+1);
+ gb_memmove(text, buf, n);
+ text[n] = 0;
+ array_add(&global_error_collector.errors, make_string(text, n));
+ }
+ mutex_unlock(&global_error_collector.error_out_mutex);
+
+ }
+ gb_file_write(f, buf, n);
+}
+
+
+ErrorOutProc *error_out_va = default_error_out_va;
+
+// NOTE: defined in build_settings.cpp
+bool global_warnings_as_errors(void);
+bool global_ignore_warnings(void);
+bool show_error_line(void);
+gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
+
+void error_out(char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_out_va(fmt, va);
+ va_end(va);
+}
+
+
+bool show_error_on_line(TokenPos const &pos, TokenPos end) {
+ if (!show_error_line()) {
+ return false;
+ }
+
+ i32 offset = 0;
+ gbString the_line = get_file_line_as_string(pos, &offset);
+ defer (gb_string_free(the_line));
+
+ if (the_line != nullptr) {
+ String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
+
+ // TODO(bill): This assumes ASCII
+
+ enum {
+ MAX_LINE_LENGTH = 76,
+ MAX_TAB_WIDTH = 8,
+ ELLIPSIS_PADDING = 8
+ };
+
+ error_out("\n\t");
+ if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
+ i32 const half_width = MAX_LINE_LENGTH/2;
+ i32 left = cast(i32)(offset);
+ i32 right = cast(i32)(line.len - offset);
+ left = gb_min(left, half_width);
+ right = gb_min(right, half_width);
+
+ line.text += offset-left;
+ line.len -= offset+right-left;
+
+ line = string_trim_whitespace(line);
+
+ offset = left + ELLIPSIS_PADDING/2;
+
+ error_out("... %.*s ...", LIT(line));
+ } else {
+ error_out("%.*s", LIT(line));
+ }
+ error_out("\n\t");
+
+ for (i32 i = 0; i < offset; i++) {
+ error_out(" ");
+ }
+ error_out("^");
+ if (end.file_id == pos.file_id) {
+ if (end.line > pos.line) {
+ for (i32 i = offset; i < line.len; i++) {
+ error_out("~");
+ }
+ } else if (end.line == pos.line && end.column > pos.column) {
+ i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
+ for (i32 i = 1; i < length-1; i++) {
+ error_out("~");
+ }
+ if (length > 1) {
+ error_out("^");
+ }
+ }
+ }
+
+ error_out("\n\n");
+ return true;
+ }
+ return false;
+}
+
+void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ global_error_collector.count.fetch_add(1);
+
+ mutex_lock(&global_error_collector.mutex);
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Error: %s\n", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ }
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ if (global_warnings_as_errors()) {
+ error_va(pos, end, fmt, va);
+ return;
+ }
+ global_error_collector.warning_count.fetch_add(1);
+ mutex_lock(&global_error_collector.mutex);
+ if (!global_ignore_warnings()) {
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Warning: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ }
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+void error_line_va(char const *fmt, va_list va) {
+ error_out_va(fmt, va);
+}
+
+void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.count++;
+ // NOTE(bill): Duplicate error, skip it
+ if (pos.line == 0) {
+ error_out("Error: %s", gb_bprintf_va(fmt, va));
+ } else if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s %s",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ }
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+
+void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.count++;
+ // NOTE(bill): Duplicate error, skip it
+ if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Syntax Error: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ } else if (pos.line == 0) {
+ error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
+ }
+
+ mutex_unlock(&global_error_collector.mutex);
+ if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
+ gb_exit(1);
+ }
+}
+
+void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
+ if (global_warnings_as_errors()) {
+ syntax_error_va(pos, end, fmt, va);
+ return;
+ }
+ mutex_lock(&global_error_collector.mutex);
+ global_error_collector.warning_count++;
+ if (!global_ignore_warnings()) {
+ // NOTE(bill): Duplicate error, skip it
+ if (global_error_collector.prev != pos) {
+ global_error_collector.prev = pos;
+ error_out("%s Syntax Warning: %s\n",
+ token_pos_to_string(pos),
+ gb_bprintf_va(fmt, va));
+ show_error_on_line(pos, end);
+ } else if (pos.line == 0) {
+ error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
+ }
+ }
+ mutex_unlock(&global_error_collector.mutex);
+}
+
+
+
+void warning(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ warning_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error(TokenPos pos, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ Token token = {};
+ token.pos = pos;
+ error_va(pos, {}, fmt, va);
+ va_end(va);
+}
+
+void error_line(char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ error_line_va(fmt, va);
+ va_end(va);
+}
+
+
+void syntax_error(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_error_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+void syntax_error(TokenPos pos, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_error_va(pos, {}, fmt, va);
+ va_end(va);
+}
+
+void syntax_warning(Token const &token, char const *fmt, ...) {
+ va_list va;
+ va_start(va, fmt);
+ syntax_warning_va(token.pos, {}, fmt, va);
+ va_end(va);
+}
+
+
+void compiler_error(char const *fmt, ...) {
+ va_list va;
+
+ va_start(va, fmt);
+ gb_printf_err("Internal Compiler Error: %s\n",
+ gb_bprintf_va(fmt, va));
+ va_end(va);
+ GB_DEBUG_TRAP();
+ gb_exit(1);
+}
+
+
+
+
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index fd90278e5..cedef48c4 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -50,9 +50,9 @@ struct ExactValue {
union {
bool value_bool;
String value_string;
- BigInt value_integer; // NOTE(bill): This must be an integer and not a pointer
+ BigInt value_integer;
f64 value_float;
- i64 value_pointer;
+ i64 value_pointer; // NOTE(bill): This must be an integer and not a pointer
Complex128 *value_complex;
Quaternion256 *value_quaternion;
Ast * value_compound;
@@ -591,6 +591,7 @@ failure:
i32 exact_value_order(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
+ case ExactValue_Compound:
return 0;
case ExactValue_Bool:
case ExactValue_String:
@@ -607,8 +608,6 @@ i32 exact_value_order(ExactValue const &v) {
return 6;
case ExactValue_Procedure:
return 7;
- // case ExactValue_Compound:
- // return 8;
default:
GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind);
@@ -630,6 +629,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool:
case ExactValue_String:
case ExactValue_Quaternion:
+ case ExactValue_Pointer:
+ case ExactValue_Procedure:
+ case ExactValue_Typeid:
return;
case ExactValue_Integer:
@@ -671,9 +673,6 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
return;
}
break;
-
- case ExactValue_Procedure:
- return;
}
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
@@ -932,6 +931,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
break;
}
+ case ExactValue_Pointer: {
+ switch (op) {
+ case Token_CmpEq: return x.value_pointer == y.value_pointer;
+ case Token_NotEq: return x.value_pointer != y.value_pointer;
+ case Token_Lt: return x.value_pointer < y.value_pointer;
+ case Token_LtEq: return x.value_pointer <= y.value_pointer;
+ case Token_Gt: return x.value_pointer > y.value_pointer;
+ case Token_GtEq: return x.value_pointer >= y.value_pointer;
+ }
+ }
+
case ExactValue_Typeid:
switch (op) {
case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid);
diff --git a/src/gb/gb.h b/src/gb/gb.h
index f716b0840..3b2d6434c 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -79,6 +79,10 @@ extern "C" {
#ifndef GB_SYSTEM_FREEBSD
#define GB_SYSTEM_FREEBSD 1
#endif
+ #elif defined(__OpenBSD__)
+ #ifndef GB_SYSTEM_OPENBSD
+ #define GB_SYSTEM_OPENBSD 1
+ #endif
#else
#error This UNIX operating system is not supported
#endif
@@ -199,7 +203,7 @@ extern "C" {
#endif
#include // NOTE(bill): malloc on linux
#include
- #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__)
+ #if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
#include
#endif
#include
@@ -235,6 +239,12 @@ extern "C" {
#define sendfile(out, in, offset, count) sendfile(out, in, offset, count, NULL, NULL, 0)
#endif
+#if defined(GB_SYSTEM_OPENBSD)
+ #include
+ #include
+ #define lseek64 lseek
+#endif
+
#if defined(GB_SYSTEM_UNIX)
#include
#endif
@@ -783,6 +793,13 @@ typedef struct gbAffinity {
isize thread_count;
isize threads_per_core;
} gbAffinity;
+#elif defined(GB_SYSTEM_OPENBSD)
+typedef struct gbAffinity {
+ b32 is_accurate;
+ isize core_count;
+ isize thread_count;
+ isize threads_per_core;
+} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -3355,6 +3372,8 @@ gb_inline u32 gb_thread_current_id(void) {
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
+#elif defined(GB_SYSTEM_LINUX)
+ thread_id = gettid();
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -3676,6 +3695,30 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
return true;
}
+isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
+ GB_ASSERT(0 <= core && core < a->core_count);
+ return a->threads_per_core;
+}
+
+#elif defined(GB_SYSTEM_OPENBSD)
+#include
+
+void gb_affinity_init(gbAffinity *a) {
+ a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
+ a->threads_per_core = 1;
+ a->is_accurate = a->core_count > 0;
+ a->core_count = a->is_accurate ? a->core_count : 1;
+ a->thread_count = a->core_count;
+}
+
+void gb_affinity_destroy(gbAffinity *a) {
+ gb_unused(a);
+}
+
+b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
+ return true;
+}
+
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
@@ -6023,7 +6066,7 @@ gbFileTime gb_file_last_write_time(char const *filepath) {
gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filename, b32 fail_if_exists) {
#if defined(GB_SYSTEM_OSX)
return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0;
-#else
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_FREEBSD)
isize size;
int existing_fd = open(existing_filename, O_RDONLY, 0);
int new_fd = open(new_filename, O_WRONLY|O_CREAT, 0666);
@@ -6039,6 +6082,49 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
close(new_fd);
close(existing_fd);
+ return size == stat_existing.st_size;
+#else
+ int new_flags = O_WRONLY | O_CREAT;
+ if (fail_if_exists) {
+ new_flags |= O_EXCL;
+ }
+ int existing_fd = open(existing_filename, O_RDONLY, 0);
+ int new_fd = open(new_filename, new_flags, 0666);
+
+ struct stat stat_existing;
+ if (fstat(existing_fd, &stat_existing) == -1) {
+ return 0;
+ }
+
+ size_t bsize = stat_existing.st_blksize > BUFSIZ ? stat_existing.st_blksize : BUFSIZ;
+ char *buf = (char *)malloc(bsize);
+ if (buf == NULL) {
+ close(new_fd);
+ close(existing_fd);
+ return 0;
+ }
+
+ isize size = 0;
+ ssize_t nread, nwrite, offset;
+ while ((nread = read(existing_fd, buf, bsize)) != -1 && nread != 0) {
+ for (offset = 0; nread; nread -= nwrite, offset += nwrite) {
+ if ((nwrite = write(new_fd, buf + offset, nread)) == -1 || nwrite == 0) {
+ free(buf);
+ close(new_fd);
+ close(existing_fd);
+ return 0;
+ }
+ size += nwrite;
+ }
+ }
+
+ free(buf);
+ close(new_fd);
+ close(existing_fd);
+
+ if (nread == -1) {
+ return 0;
+ }
return size == stat_existing.st_size;
#endif
}
@@ -6091,6 +6177,7 @@ gbFileContents gb_file_read_contents(gbAllocator a, b32 zero_terminate, char con
}
void gb_file_free_contents(gbFileContents *fc) {
+ if (fc == NULL || fc->size == 0) return;
GB_ASSERT_NOT_NULL(fc->data);
gb_free(fc->allocator, fc->data);
fc->data = NULL;
@@ -6186,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
#else
char *p, *result, *fullpath = NULL;
isize len;
- p = realpath(path, NULL);
- fullpath = p;
- if (p == NULL) {
- // NOTE(bill): File does not exist
- fullpath = cast(char *)path;
+ fullpath = realpath(path, NULL);
+
+ if (fullpath == NULL) {
+ // NOTE(Jeroen): Path doesn't exist.
+ if (gb_strlen(path) > 0 && path[0] == '/') {
+ // But it is an absolute path, so return as-is.
+
+ fullpath = (char *)path;
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+
+ } else {
+ // Appears to be a relative path, so construct an absolute one relative to .
+ char cwd[4096];
+ getcwd(&cwd[0], 4096);
+
+ isize path_len = gb_strlen(path);
+ isize cwd_len = gb_strlen(cwd);
+ len = cwd_len + 1 + path_len + 1;
+ result = gb_alloc_array(a, char, len);
+
+ gb_memmove(result, (void *)cwd, cwd_len);
+ result[cwd_len] = '/';
+
+ gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
+ result[len] = 0;
+
+ }
+ } else {
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+ free(fullpath);
}
-
- len = gb_strlen(fullpath);
-
- result = gb_alloc_array(a, char, len + 1);
- gb_memmove(result, fullpath, len);
- result[len] = 0;
- free(p);
-
return result;
#endif
}
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index e18dc344b..07d2dd6e3 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -965,6 +965,10 @@ namespace lbAbiArm64 {
}
return false;
}
+
+ unsigned is_homogenous_aggregate_small_enough(LLVMTypeRef *base_type_, unsigned member_count_) {
+ return (member_count_ <= 4);
+ }
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef type, bool return_is_defined) {
LLVMTypeRef homo_base_type = {};
@@ -975,22 +979,31 @@ namespace lbAbiArm64 {
} else if (is_register(type)) {
return non_struct(c, type);
} else if (is_homogenous_aggregate(c, type, &homo_base_type, &homo_member_count)) {
- return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ if(is_homogenous_aggregate_small_enough(&homo_base_type, homo_member_count)) {
+ return lb_arg_type_direct(type, LLVMArrayType(homo_base_type, homo_member_count), nullptr, nullptr);
+ } else {
+ //TODO(Platin): do i need to create stuff that can handle the diffrent return type?
+ // else this needs a fix in llvm_backend_proc as we would need to cast it to the correct array type
+
+ //LLVMTypeRef array_type = LLVMArrayType(homo_base_type, homo_member_count);
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
+ return lb_arg_type_indirect(type, attr);
+ }
} else {
i64 size = lb_sizeof(type);
if (size <= 16) {
LLVMTypeRef cast_type = nullptr;
if (size <= 1) {
- cast_type = LLVMIntTypeInContext(c, 8);
+ cast_type = LLVMInt8TypeInContext(c);
} else if (size <= 2) {
- cast_type = LLVMIntTypeInContext(c, 16);
+ cast_type = LLVMInt16TypeInContext(c);
} else if (size <= 4) {
- cast_type = LLVMIntTypeInContext(c, 32);
+ cast_type = LLVMInt32TypeInContext(c);
} else if (size <= 8) {
- cast_type = LLVMIntTypeInContext(c, 64);
+ cast_type = LLVMInt64TypeInContext(c);
} else {
unsigned count = cast(unsigned)((size+7)/8);
- cast_type = LLVMArrayType(LLVMIntTypeInContext(c, 64), count);
+ cast_type = LLVMArrayType(LLVMInt64TypeInContext(c), count);
}
return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else {
@@ -999,7 +1012,7 @@ namespace lbAbiArm64 {
}
}
}
-
+
Array compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make(heap_allocator(), arg_count);
@@ -1171,16 +1184,24 @@ LB_ABI_INFO(lb_get_abi_info) {
ft->calling_convention = calling_convention;
return ft;
}
+ case ProcCC_Win64:
+ GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
+ return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ case ProcCC_SysV:
+ GB_ASSERT(build_context.metrics.arch == TargetArch_amd64);
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- if (build_context.metrics.os == TargetOs_windows) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.metrics.abi == TargetABI_Win64) {
return lbAbiAmd64Win64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ } else if (build_context.metrics.abi == TargetABI_SysV) {
+ return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
} else {
return lbAbiAmd64SysV::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
}
- case TargetArch_386:
+ case TargetArch_i386:
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_arm64:
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index 5acd2a80f..7781997f7 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -624,6 +624,9 @@ struct lbGlobalVariable {
};
lbProcedure *lb_create_startup_type_info(lbModule *m) {
+ if (build_context.disallow_rtti) {
+ return nullptr;
+ }
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -652,7 +655,54 @@ lbProcedure *lb_create_startup_type_info(lbModule *m) {
return p;
}
-lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, Array &global_variables) { // Startup Runtime
+lbProcedure *lb_create_objc_names(lbModule *main_module) {
+ if (build_context.metrics.os != TargetOs_darwin) {
+ return nullptr;
+ }
+ Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_CDecl);
+ lbProcedure *p = lb_create_dummy_procedure(main_module, str_lit("__$init_objc_names"), proc_type);
+ p->is_startup = true;
+ return p;
+}
+
+void lb_finalize_objc_names(lbProcedure *p) {
+ if (p == nullptr) {
+ return;
+ }
+ lbModule *m = p->module;
+
+ LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(m->mod);
+ lb_populate_function_pass_manager(m, default_function_pass_manager, false, build_context.optimization_level);
+ LLVMFinalizeFunctionPassManager(default_function_pass_manager);
+
+
+ auto args = array_make(permanent_allocator(), 1);
+
+ LLVMSetLinkage(p->value, LLVMInternalLinkage);
+ lb_begin_procedure_body(p);
+ for_array(i, m->objc_classes.entries) {
+ auto const &entry = m->objc_classes.entries[i];
+ String name = entry.key.string;
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "objc_lookUpClass", args);
+ lb_addr_store(p, entry.value, ptr);
+ }
+
+ for_array(i, m->objc_selectors.entries) {
+ auto const &entry = m->objc_selectors.entries[i];
+ String name = entry.key.string;
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
+ lb_addr_store(p, entry.value, ptr);
+ }
+
+ lb_end_procedure_body(p);
+
+ lb_run_function_pass_manager(default_function_pass_manager, p);
+
+}
+
+lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *startup_type_info, lbProcedure *objc_names, Array &global_variables) { // Startup Runtime
LLVMPassManagerRef default_function_pass_manager = LLVMCreateFunctionPassManagerForModule(main_module->mod);
lb_populate_function_pass_manager(main_module, default_function_pass_manager, false, build_context.optimization_level);
LLVMFinalizeFunctionPassManager(default_function_pass_manager);
@@ -664,7 +714,13 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
lb_begin_procedure_body(p);
- LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ if (startup_type_info) {
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, startup_type_info->type)), startup_type_info->value, nullptr, 0, "");
+ }
+
+ if (objc_names) {
+ LLVMBuildCall2(p->builder, LLVMGetElementType(lb_type(main_module, objc_names->type)), objc_names->value, nullptr, 0, "");
+ }
for_array(i, global_variables) {
auto *var = &global_variables[i];
@@ -783,7 +839,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
params->Tuple.variables[1] = alloc_entity_param(nullptr, make_token_ident("fdwReason"), t_u32, false, true);
params->Tuple.variables[2] = alloc_entity_param(nullptr, make_token_ident("lpReserved"), t_rawptr, false, true);
call_cleanup = false;
- } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_386 || build_context.no_crt)) {
+ } else if (build_context.metrics.os == TargetOs_windows && (build_context.metrics.arch == TargetArch_i386 || build_context.no_crt)) {
name = str_lit("mainCRTStartup");
} else if (is_arch_wasm()) {
name = str_lit("_start");
@@ -873,7 +929,7 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
} else {
if (m->info->entry_point != nullptr) {
lbValue entry_point = lb_find_procedure_value_from_entity(m, m->info->entry_point);
- lb_emit_call(p, entry_point, {});
+ lb_emit_call(p, entry_point, {}, ProcInlining_no_inline);
}
}
@@ -911,7 +967,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
}
String lb_filepath_ll_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
} else if (USE_SEPARATE_MODULES) {
@@ -922,7 +983,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
return path;
}
String lb_filepath_obj_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
}
@@ -945,6 +1011,19 @@ String lb_filepath_obj_for_module(lbModule *m) {
case TargetOs_essence:
ext = STR_LIT(".o");
break;
+
+ case TargetOs_freestanding:
+ switch (build_context.metrics.abi) {
+ default:
+ case TargetABI_Default:
+ case TargetABI_SysV:
+ ext = STR_LIT(".o");
+ break;
+ case TargetABI_Win64:
+ ext = STR_LIT(".obj");
+ break;
+ }
+ break;
}
}
}
@@ -1140,7 +1219,7 @@ void lb_generate_code(lbGenerator *gen) {
switch (build_context.metrics.arch) {
case TargetArch_amd64:
- case TargetArch_386:
+ case TargetArch_i386:
LLVMInitializeX86TargetInfo();
LLVMInitializeX86Target();
LLVMInitializeX86TargetMC();
@@ -1197,6 +1276,8 @@ void lb_generate_code(lbGenerator *gen) {
LLVMCodeModel code_mode = LLVMCodeModelDefault;
if (is_arch_wasm()) {
code_mode = LLVMCodeModelJITDefault;
+ } else if (build_context.metrics.os == TargetOs_freestanding) {
+ code_mode = LLVMCodeModelKernel;
}
char const *host_cpu_name = LLVMGetHostCPUName();
@@ -1219,10 +1300,18 @@ void lb_generate_code(lbGenerator *gen) {
// x86-64-v3: (close to Haswell) AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
// x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ, AVX512VL
if (ODIN_LLVM_MINIMUM_VERSION_12) {
- llvm_cpu = "x86-64-v2";
+ if (build_context.metrics.os == TargetOs_freestanding) {
+ llvm_cpu = "x86-64";
+ } else {
+ llvm_cpu = "x86-64-v2";
+ }
}
}
+ if (build_context.target_features.len != 0) {
+ llvm_features = alloc_cstring(permanent_allocator(), build_context.target_features);
+ }
+
// GB_ASSERT_MSG(LLVMTargetHasAsmBackend(target));
LLVMCodeGenOptLevel code_gen_level = LLVMCodeGenLevelNone;
@@ -1238,12 +1327,36 @@ void lb_generate_code(lbGenerator *gen) {
// NOTE(bill, 2021-05-04): Target machines must be unique to each module because they are not thread safe
auto target_machines = array_make(permanent_allocator(), gen->modules.entries.count);
+ // NOTE(dweiler): Dynamic libraries require position-independent code.
+ LLVMRelocMode reloc_mode = LLVMRelocDefault;
+ if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ reloc_mode = LLVMRelocPIC;
+ }
+
+ switch (build_context.reloc_mode) {
+ case RelocMode_Default:
+ if (build_context.metrics.os == TargetOs_openbsd) {
+ // Always use PIC for OpenBSD: it defaults to PIE
+ reloc_mode = LLVMRelocPIC;
+ }
+ break;
+ case RelocMode_Static:
+ reloc_mode = LLVMRelocStatic;
+ break;
+ case RelocMode_PIC:
+ reloc_mode = LLVMRelocPIC;
+ break;
+ case RelocMode_DynamicNoPIC:
+ reloc_mode = LLVMRelocDynamicNoPic;
+ break;
+ }
+
for_array(i, gen->modules.entries) {
target_machines[i] = LLVMCreateTargetMachine(
target, target_triple, llvm_cpu,
llvm_features,
code_gen_level,
- LLVMRelocDefault,
+ reloc_mode,
code_mode);
LLVMSetModuleDataLayout(gen->modules.entries[i].value->mod, LLVMCreateTargetDataLayout(target_machines[i]));
}
@@ -1304,7 +1417,7 @@ void lb_generate_code(lbGenerator *gen) {
TIME_SECTION("LLVM Global Variables");
- {
+ if (!build_context.disallow_rtti) {
lbModule *m = default_module;
{ // Add type info data
@@ -1399,30 +1512,30 @@ void lb_generate_code(lbGenerator *gen) {
isize global_variable_max_count = 0;
- Entity *entry_point = info->entry_point;
- bool has_dll_main = false;
- bool has_win_main = false;
+ bool already_has_entry_point = false;
for_array(i, info->entities) {
Entity *e = info->entities[i];
String name = e->token.string;
- bool is_global = e->pkg != nullptr;
-
if (e->kind == Entity_Variable) {
global_variable_max_count++;
- } else if (e->kind == Entity_Procedure && !is_global) {
+ } else if (e->kind == Entity_Procedure) {
if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
- GB_ASSERT(e == entry_point);
- // entry_point = e;
+ GB_ASSERT(e == info->entry_point);
}
- if (e->Procedure.is_export ||
- (e->Procedure.link_name.len > 0) ||
- ((e->scope->flags&ScopeFlag_File) && e->Procedure.link_name.len > 0)) {
- if (!has_dll_main && name == "DllMain") {
- has_dll_main = true;
- } else if (!has_win_main && name == "WinMain") {
- has_win_main = true;
+ if (build_context.command_kind == Command_test &&
+ (e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
+ String link_name = e->Procedure.link_name;
+ if (e->pkg->kind == Package_Runtime) {
+ if (link_name == "main" ||
+ link_name == "DllMain" ||
+ link_name == "WinMain" ||
+ link_name == "wWinMain" ||
+ link_name == "mainCRTStartup" ||
+ link_name == "_start") {
+ already_has_entry_point = true;
+ }
}
}
}
@@ -1560,6 +1673,14 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ TIME_SECTION("LLVM Runtime Type Information Creation");
+ lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
+
+ lbProcedure *objc_names = lb_create_objc_names(default_module);
+
+ TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
+ lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, objc_names, global_variables);
+ gb_unused(startup_runtime);
TIME_SECTION("LLVM Global Procedures and Types");
for_array(i, info->entities) {
@@ -1619,14 +1740,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
-
- TIME_SECTION("LLVM Runtime Type Information Creation");
- lbProcedure *startup_type_info = lb_create_startup_type_info(default_module);
-
- TIME_SECTION("LLVM Runtime Startup Creation (Global Variables)");
- lbProcedure *startup_runtime = lb_create_startup_runtime(default_module, startup_type_info, global_variables);
-
-
TIME_SECTION("LLVM Procedure Generation");
for_array(j, gen->modules.entries) {
lbModule *m = gen->modules.entries[j].value;
@@ -1636,8 +1749,7 @@ void lb_generate_code(lbGenerator *gen) {
}
}
-
- if (!(build_context.build_mode == BuildMode_DynamicLibrary && !has_dll_main)) {
+ if (build_context.command_kind == Command_test && !already_has_entry_point) {
TIME_SECTION("LLVM main");
lb_create_main_procedure(default_module, startup_runtime);
}
@@ -1651,6 +1763,8 @@ void lb_generate_code(lbGenerator *gen) {
}
}
+ lb_finalize_objc_names(objc_names);
+
if (build_context.ODIN_DEBUG) {
TIME_SECTION("LLVM Debug Info Complete Types and Finalize");
for_array(j, gen->modules.entries) {
@@ -1663,6 +1777,7 @@ void lb_generate_code(lbGenerator *gen) {
}
+
TIME_SECTION("LLVM Function Pass");
for_array(i, gen->modules.entries) {
lbModule *m = gen->modules.entries[i].value;
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index e70b1f84c..f2bcfaff6 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -144,6 +144,9 @@ struct lbModule {
PtrMap debug_values;
Array debug_incomplete_types;
+
+ StringMap objc_classes;
+ StringMap objc_selectors;
};
struct lbGenerator {
@@ -204,7 +207,6 @@ enum lbDeferExitKind {
enum lbDeferKind {
lbDefer_Node,
- lbDefer_Instr,
lbDefer_Proc,
};
@@ -215,8 +217,6 @@ struct lbDefer {
lbBlock * block;
union {
Ast *stmt;
- // NOTE(bill): 'instr' will be copied every time to create a new one
- lbValue instr;
struct {
lbValue deferred;
Array result_as_args;
@@ -235,6 +235,7 @@ struct lbTargetList {
enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0,
+ lbProcedureFlag_DebugAllocaCopy = 1<<1,
};
struct lbCopyElisionHint {
@@ -270,7 +271,6 @@ struct lbProcedure {
bool is_done;
lbAddr return_ptr;
- Array params;
Array defer_stmts;
Array blocks;
Array branch_blocks;
@@ -296,7 +296,6 @@ struct lbProcedure {
bool lb_init_generator(lbGenerator *gen, Checker *c);
-void lb_generate_module(lbGenerator *gen);
String lb_mangle_name(lbModule *m, Entity *e);
String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
@@ -369,7 +368,7 @@ lbContextData *lb_push_context_onto_stack(lbProcedure *p, lbAddr ctx);
lbContextData *lb_push_context_onto_stack_from_implicit_parameter(lbProcedure *p);
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={});
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value={}, Entity **entity_=nullptr);
lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e=nullptr, bool zero_init=true, i32 param_index=0, bool force_no_init=false);
void lb_add_foreign_library_path(lbModule *m, Entity *e);
@@ -549,6 +548,10 @@ lbCallingConventionKind const lb_calling_convention_map[ProcCC_MAX] = {
lbCallingConvention_C, // ProcCC_None,
lbCallingConvention_C, // ProcCC_Naked,
lbCallingConvention_C, // ProcCC_InlineAsm,
+
+ lbCallingConvention_Win64, // ProcCC_Win64,
+ lbCallingConvention_X86_64_SysV, // ProcCC_SysV,
+
};
enum : LLVMDWARFTypeEncoding {
diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp
index 5862a7add..8f17a1cfb 100644
--- a/src/llvm_backend_const.cpp
+++ b/src/llvm_backend_const.cpp
@@ -115,8 +115,8 @@ LLVMValueRef llvm_const_cast(LLVMValueRef val, LLVMTypeRef dst) {
lbValue lb_const_ptr_cast(lbModule *m, lbValue value, Type *t) {
- GB_ASSERT(is_type_pointer(value.type));
- GB_ASSERT(is_type_pointer(t));
+ GB_ASSERT(is_type_internally_pointer_like(value.type));
+ GB_ASSERT(is_type_internally_pointer_like(t));
GB_ASSERT(lb_is_const(value));
lbValue res = {};
@@ -175,7 +175,7 @@ LLVMValueRef llvm_const_array(LLVMTypeRef elem_type, LLVMValueRef *values, isize
}
LLVMValueRef llvm_const_slice(lbModule *m, lbValue data, lbValue len) {
- GB_ASSERT(is_type_pointer(data.type));
+ GB_ASSERT(is_type_pointer(data.type) || is_type_multi_pointer(data.type));
GB_ASSERT(are_types_identical(len.type, t_int));
LLVMValueRef vals[2] = {
data.value,
@@ -568,7 +568,7 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
}
case ExactValue_Integer:
- if (is_type_pointer(type)) {
+ if (is_type_pointer(type) || is_type_multi_pointer(type)) {
LLVMTypeRef t = lb_type(m, original_type);
LLVMValueRef i = lb_big_int_to_llvm(m, t_uintptr, &value.value_integer);
res.value = LLVMConstIntToPtr(i, t);
diff --git a/src/llvm_backend_debug.cpp b/src/llvm_backend_debug.cpp
index 7a2b00fe9..b91f32bfc 100644
--- a/src/llvm_backend_debug.cpp
+++ b/src/llvm_backend_debug.cpp
@@ -958,13 +958,79 @@ void lb_add_debug_local_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, T
);
LLVMValueRef storage = ptr;
- LLVMValueRef instr = ptr;
+ LLVMBasicBlockRef block = p->decl_block->block;
LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
lb_set_llvm_metadata(m, ptr, llvm_expr);
- LLVMDIBuilderInsertDeclareBefore(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, instr);
+ LLVMDIBuilderInsertDeclareAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block);
}
+
+void lb_add_debug_param_variable(lbProcedure *p, LLVMValueRef ptr, Type *type, Token const &token, unsigned arg_number, lbBlock *block) {
+ if (p->debug_info == nullptr) {
+ return;
+ }
+ if (type == nullptr) {
+ return;
+ }
+ if (type == t_invalid) {
+ return;
+ }
+ if (p->body == nullptr) {
+ return;
+ }
+
+ lbModule *m = p->module;
+ String const &name = token.string;
+ if (name == "" || name == "_") {
+ return;
+ }
+
+ if (lb_get_llvm_metadata(m, ptr) != nullptr) {
+ // Already been set
+ return;
+ }
+
+
+ AstFile *file = p->body->file();
+
+ LLVMMetadataRef llvm_scope = lb_get_current_debug_scope(p);
+ LLVMMetadataRef llvm_file = lb_get_llvm_metadata(m, file);
+ GB_ASSERT(llvm_scope != nullptr);
+ if (llvm_file == nullptr) {
+ llvm_file = LLVMDIScopeGetFile(llvm_scope);
+ }
+
+ if (llvm_file == nullptr) {
+ return;
+ }
+
+ LLVMDIFlags flags = LLVMDIFlagZero;
+ LLVMBool always_preserve = build_context.optimization_level == 0;
+
+ LLVMMetadataRef debug_type = lb_debug_type(m, type);
+
+ LLVMMetadataRef var_info = LLVMDIBuilderCreateParameterVariable(
+ m->debug_builder, llvm_scope,
+ cast(char const *)name.text, cast(size_t)name.len,
+ arg_number,
+ llvm_file, token.pos.line,
+ debug_type,
+ always_preserve, flags
+ );
+
+ LLVMValueRef storage = ptr;
+ LLVMMetadataRef llvm_debug_loc = lb_debug_location_from_token_pos(p, token.pos);
+ LLVMMetadataRef llvm_expr = LLVMDIBuilderCreateExpression(m->debug_builder, nullptr, 0);
+ lb_set_llvm_metadata(m, ptr, llvm_expr);
+
+ // NOTE(bill, 2022-02-01): For parameter values, you must insert them at the end of the decl block
+ // The reason is that if the parameter is at index 0 and a pointer, there is not such things as an
+ // instruction "before" it.
+ LLVMDIBuilderInsertDbgValueAtEnd(m->debug_builder, storage, var_info, llvm_expr, llvm_debug_loc, block->block);
+}
+
+
void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
if (!p->debug_info || !p->body) {
return;
@@ -984,5 +1050,10 @@ void lb_add_debug_context_variable(lbProcedure *p, lbAddr const &ctx) {
token.string = str_lit("context");
token.pos = pos;
- lb_add_debug_local_variable(p, ctx.addr.value, t_context, token);
+ LLVMValueRef ptr = ctx.addr.value;
+ while (LLVMIsABitCastInst(ptr)) {
+ ptr = LLVMGetOperand(ptr, 0);
+ }
+
+ lb_add_debug_local_variable(p, ptr, t_context, token);
}
diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp
index edda3a267..133df4d41 100644
--- a/src/llvm_backend_expr.cpp
+++ b/src/llvm_backend_expr.cpp
@@ -508,7 +508,7 @@ bool lb_is_matrix_simdable(Type *t) {
case TargetArch_arm64:
// TODO(bill): determine when this is fine
return true;
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_wasm32:
case TargetArch_wasm64:
return false;
@@ -580,6 +580,27 @@ LLVMValueRef lb_matrix_to_trimmed_vector(lbProcedure *p, lbValue m) {
lbValue lb_emit_matrix_tranpose(lbProcedure *p, lbValue m, Type *type) {
if (is_type_array(m.type)) {
+ i32 rank = type_math_rank(m.type);
+ if (rank == 2) {
+ lbAddr addr = lb_add_local_generated(p, type, false);
+ lbValue dst = addr.addr;
+ lbValue src = m;
+ i32 n = cast(i32)get_array_type_count(m.type);
+ i32 m = cast(i32)get_array_type_count(type);
+ // m.type == [n][m]T
+ // type == [m][n]T
+
+ for (i32 j = 0; j < m; j++) {
+ lbValue dst_col = lb_emit_struct_ep(p, dst, j);
+ for (i32 i = 0; i < n; i++) {
+ lbValue dst_row = lb_emit_struct_ep(p, dst_col, i);
+ lbValue src_col = lb_emit_struct_ev(p, src, i);
+ lbValue src_row = lb_emit_struct_ev(p, src_col, j);
+ lb_emit_store(p, dst_row, src_row);
+ }
+ }
+ return lb_addr_load(p, addr);
+ }
// no-op
m.type = type;
return m;
@@ -1655,26 +1676,10 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return res;
}
- if (is_type_float(src) && is_type_complex(dst)) {
- Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
- lbValue gp = lb_addr_get_ptr(p, gen);
- lbValue real = lb_emit_conv(p, value, ft);
- lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
- return lb_addr_load(p, gen);
- }
- if (is_type_float(src) && is_type_quaternion(dst)) {
- Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
- lbValue gp = lb_addr_get_ptr(p, gen);
- lbValue real = lb_emit_conv(p, value, ft);
- lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
- return lb_addr_load(p, gen);
- }
if (is_type_complex(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
+ lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1686,7 +1691,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_quaternion(src) && is_type_quaternion(dst)) {
// @QuaternionLayout
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, false);
+ lbAddr gen = lb_add_local_generated(p, t, false);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue q0 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue q1 = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1701,7 +1706,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_integer(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1709,7 +1714,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_float(src) && is_type_complex(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
lb_emit_store(p, lb_emit_struct_ep(p, gp, 0), real);
@@ -1719,7 +1724,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
if (is_type_integer(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
// @QuaternionLayout
@@ -1728,7 +1733,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_float(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, value, ft);
// @QuaternionLayout
@@ -1737,7 +1742,7 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
}
if (is_type_complex(src) && is_type_quaternion(dst)) {
Type *ft = base_complex_elem_type(dst);
- lbAddr gen = lb_add_local_generated(p, dst, true);
+ lbAddr gen = lb_add_local_generated(p, t, true);
lbValue gp = lb_addr_get_ptr(p, gen);
lbValue real = lb_emit_conv(p, lb_emit_struct_ev(p, value, 0), ft);
lbValue imag = lb_emit_conv(p, lb_emit_struct_ev(p, value, 1), ft);
@@ -1850,6 +1855,15 @@ lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) {
return lb_addr_load(p, parent);
}
}
+ if (dst->Union.variants.count == 1) {
+ Type *vt = dst->Union.variants[0];
+ if (internal_check_is_assignable_to(src, vt)) {
+ value = lb_emit_conv(p, value, vt);
+ lbAddr parent = lb_add_local_generated(p, t, true);
+ lb_emit_store_union_variant(p, parent.addr, value, vt);
+ return lb_addr_load(p, parent);
+ }
+ }
}
// NOTE(bill): This has to be done before 'Pointer <-> Pointer' as it's
@@ -2188,6 +2202,21 @@ lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left, lbValue ri
}
}
+ if (is_type_matrix(a) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
+ Type *tl = base_type(a);
+ lbValue lhs = lb_address_from_load_or_generate_local(p, left);
+ lbValue rhs = lb_address_from_load_or_generate_local(p, right);
+
+
+ // TODO(bill): Test to see if this is actually faster!!!!
+ auto args = array_make(permanent_allocator(), 3);
+ args[0] = lb_emit_conv(p, lhs, t_rawptr);
+ args[1] = lb_emit_conv(p, rhs, t_rawptr);
+ args[2] = lb_const_int(p->module, t_int, type_size_of(tl));
+ lbValue val = lb_emit_runtime_call(p, "memory_compare", args);
+ lbValue res = lb_emit_comp(p, op_kind, val, lb_const_nil(p->module, val.type));
+ return lb_emit_conv(p, res, t_bool);
+ }
if (is_type_array(a) || is_type_enumerated_array(a)) {
Type *tl = base_type(a);
lbValue lhs = lb_address_from_load_or_generate_local(p, left);
@@ -2784,28 +2813,39 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
Type *src_type = type_deref(v.type);
Type *dst_type = type;
- lbValue src_tag = {};
- lbValue dst_tag = {};
- if (is_type_union_maybe_pointer(src_type)) {
- src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
- dst_tag = lb_const_bool(p->module, t_bool, true);
- } else {
- src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
- dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ lbValue src_tag = {};
+ lbValue dst_tag = {};
+ if (is_type_union_maybe_pointer(src_type)) {
+ src_tag = lb_emit_comp_against_nil(p, Token_NotEq, v);
+ dst_tag = lb_const_bool(p->module, t_bool, true);
+ } else {
+ src_tag = lb_emit_load(p, lb_emit_union_tag_ptr(p, v));
+ dst_tag = lb_const_union_tag(p->module, src_type, dst_type);
+ }
+
+
+ isize arg_count = 6;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
+
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
+ auto args = array_make(permanent_allocator(), arg_count);
+ args[0] = ok;
+
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
+
+ if (!build_context.disallow_rtti) {
+ args[4] = lb_typeid(p->module, src_type);
+ args[5] = lb_typeid(p->module, dst_type);
+ }
+ lb_emit_runtime_call(p, "type_assertion_check", args);
}
- lbValue ok = lb_emit_comp(p, Token_CmpEq, src_tag, dst_tag);
- auto args = array_make(permanent_allocator(), 6);
- args[0] = ok;
-
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
-
- args[4] = lb_typeid(p->module, src_type);
- args[5] = lb_typeid(p->module, dst_type);
- lb_emit_runtime_call(p, "type_assertion_check", args);
-
lbValue data_ptr = v;
return lb_emit_conv(p, data_ptr, tv.type);
} else if (is_type_any(t)) {
@@ -2813,23 +2853,25 @@ lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
if (is_type_pointer(v.type)) {
v = lb_emit_load(p, v);
}
-
lbValue data_ptr = lb_emit_struct_ev(p, v, 0);
- lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue id = lb_typeid(p->module, type);
+ if ((p->state_flags & StateFlag_no_type_assert) == 0) {
+ GB_ASSERT(!build_context.disallow_rtti);
+ lbValue any_id = lb_emit_struct_ev(p, v, 1);
- lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
- auto args = array_make(permanent_allocator(), 6);
- args[0] = ok;
+ lbValue id = lb_typeid(p->module, type);
+ lbValue ok = lb_emit_comp(p, Token_CmpEq, any_id, id);
+ auto args = array_make(permanent_allocator(), 6);
+ args[0] = ok;
- args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(p->module, t_i32, pos.line);
- args[3] = lb_const_int(p->module, t_i32, pos.column);
+ args[1] = lb_find_or_add_entity_string(p->module, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(p->module, t_i32, pos.line);
+ args[3] = lb_const_int(p->module, t_i32, pos.column);
- args[4] = any_id;
- args[5] = id;
- lb_emit_runtime_call(p, "type_assertion_check", args);
+ args[4] = any_id;
+ args[5] = id;
+ lb_emit_runtime_call(p, "type_assertion_check", args);
+ }
return lb_emit_conv(p, data_ptr, tv.type);
} else {
@@ -2859,6 +2901,14 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ } else if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ }
+
p->state_flags = out;
}
@@ -2978,7 +3028,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *type = default_type(type_of_expr(expr));
@@ -3279,9 +3329,9 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(se, SelectorExpr, expr);
- Ast *sel = unparen_expr(se->selector);
- if (sel->kind == Ast_Ident) {
- String selector = sel->Ident.token.string;
+ Ast *sel_node = unparen_expr(se->selector);
+ if (sel_node->kind == Ast_Ident) {
+ String selector = sel_node->Ident.token.string;
TypeAndValue tav = type_and_value_of_expr(se->expr);
if (tav.mode == Addressing_Invalid) {
@@ -3296,7 +3346,12 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Type *type = base_type(tav.type);
if (tav.mode == Addressing_Type) { // Addressing_Type
- GB_PANIC("Unreachable");
+ Selection sel = lookup_field(tav.type, selector, true);
+ if (sel.pseudo_field) {
+ GB_ASSERT(sel.entity->kind == Entity_Procedure);
+ return lb_addr(lb_find_value_from_entity(p->module, sel.entity));
+ }
+ GB_PANIC("Unreachable %.*s", LIT(selector));
}
if (se->swizzle_count > 0) {
@@ -3323,6 +3378,11 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
Selection sel = lookup_field(type, selector, false);
GB_ASSERT(sel.entity != nullptr);
+ if (sel.pseudo_field) {
+ GB_ASSERT(sel.entity->kind == Entity_Procedure);
+ Entity *e = entity_of_node(sel_node);
+ return lb_addr(lb_find_value_from_entity(p->module, e));
+ }
{
lbAddr addr = lb_build_addr(p, se->expr);
@@ -3476,7 +3536,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
GB_ASSERT_MSG(is_type_indexable(t), "%s %s", type_to_string(t), expr_to_string(expr));
if (is_type_map(t)) {
- lbValue map_val = lb_build_addr_ptr(p, ie->expr);
+ lbAddr map_addr = lb_build_addr(p, ie->expr);
+ lbValue map_val = lb_addr_load(p, map_addr);
if (deref) {
map_val = lb_emit_load(p, map_val);
}
@@ -3485,7 +3546,8 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
key = lb_emit_conv(p, key, t->Map.key);
Type *result_type = type_of_expr(expr);
- return lb_addr_map(map_val, key, t, result_type);
+ lbValue map_ptr = lb_address_from_load_or_generate_local(p, map_val);
+ return lb_addr_map(map_ptr, key, t, result_type);
}
switch (t->kind) {
@@ -4584,7 +4646,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
lbBlock *done = lb_create_block(p, "if.done"); // NOTE(bill): Append later
lbBlock *else_ = lb_create_block(p, "if.else");
- lbValue cond = lb_build_cond(p, te->cond, then, else_);
+ lb_build_cond(p, te->cond, then, else_);
lb_start_block(p, then);
Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 17eeb0bea..1a431a4ac 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -72,6 +72,9 @@ void lb_init_module(lbModule *m, Checker *c) {
map_init(&m->debug_values, a);
array_init(&m->debug_incomplete_types, a, 0, 1024);
+
+ string_map_init(&m->objc_classes, a);
+ string_map_init(&m->objc_selectors, a);
}
bool lb_init_generator(lbGenerator *gen, Checker *c) {
@@ -84,7 +87,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false;
}
-
String init_fullpath = c->parser->init_fullpath;
if (build_context.out_filepath.len == 0) {
@@ -271,6 +273,10 @@ lbAddr lb_addr(lbValue addr) {
lbAddr lb_addr_map(lbValue addr, lbValue map_key, Type *map_type, Type *map_result) {
+ GB_ASSERT(is_type_pointer(addr.type));
+ Type *mt = type_deref(addr.type);
+ GB_ASSERT(is_type_map(mt));
+
lbAddr v = {lbAddr_Map, addr};
v.map.key = map_key;
v.map.type = map_type;
@@ -1169,10 +1175,35 @@ void lb_emit_store_union_variant_tag(lbProcedure *p, lbValue parent, Type *varia
}
void lb_emit_store_union_variant(lbProcedure *p, lbValue parent, lbValue variant, Type *variant_type) {
- lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ Type *pt = base_type(type_deref(parent.type));
+ GB_ASSERT(pt->kind == Type_Union);
+ if (pt->Union.kind == UnionType_shared_nil) {
+ lbBlock *if_nil = lb_create_block(p, "shared_nil.if_nil");
+ lbBlock *if_not_nil = lb_create_block(p, "shared_nil.if_not_nil");
+ lbBlock *done = lb_create_block(p, "shared_nil.done");
- lb_emit_store(p, underlying, variant);
- lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lbValue cond_is_nil = lb_emit_comp_against_nil(p, Token_CmpEq, variant);
+ lb_emit_if(p, cond_is_nil, if_nil, if_not_nil);
+
+ lb_start_block(p, if_nil);
+ lb_emit_store(p, parent, lb_const_nil(p->module, type_deref(parent.type)));
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, if_not_nil);
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ lb_emit_jump(p, done);
+
+ lb_start_block(p, done);
+
+
+ } else {
+ lbValue underlying = lb_emit_conv(p, parent, alloc_type_pointer(variant_type));
+
+ lb_emit_store(p, underlying, variant);
+ lb_emit_store_union_variant_tag(p, parent, variant_type);
+ }
}
@@ -1598,8 +1629,9 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
return llvm_type;
}
llvm_type = LLVMStructCreateNamed(ctx, name);
+ LLVMTypeRef found_val = *found;
map_set(&m->types, type, llvm_type);
- lb_clone_struct_type(llvm_type, *found);
+ lb_clone_struct_type(llvm_type, found_val);
return llvm_type;
}
}
@@ -2304,7 +2336,7 @@ LLVMValueRef lb_find_or_add_entity_string_ptr(lbModule *m, String const &str) {
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = LLVMConstInBoundsGEP(global_data, indices, 2);
string_map_set(&m->const_strings, key, ptr);
@@ -2346,7 +2378,7 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
}
LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(data), name);
LLVMSetInitializer(global_data, data);
- LLVMSetLinkage(global_data, LLVMInternalLinkage);
+ LLVMSetLinkage(global_data, LLVMPrivateLinkage);
LLVMValueRef ptr = nullptr;
if (str.len != 0) {
@@ -2445,7 +2477,7 @@ lbValue lb_find_procedure_value_from_entity(lbModule *m, Entity *e) {
return {};
}
-lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
+lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value, Entity **entity_) {
GB_ASSERT(type != nullptr);
type = default_type(type);
@@ -2471,6 +2503,9 @@ lbAddr lb_add_global_generated(lbModule *m, Type *type, lbValue value) {
lb_add_entity(m, e, g);
lb_add_member(m, name, g);
+
+ if (entity_) *entity_ = e;
+
return lb_addr(g);
}
@@ -2579,7 +2614,7 @@ lbValue lb_generate_global_array(lbModule *m, Type *elem_type, i64 count, String
g.value = LLVMAddGlobal(m->mod, lb_type(m, t), text);
g.type = alloc_type_pointer(t);
LLVMSetInitializer(g.value, LLVMConstNull(lb_type(m, t)));
- LLVMSetLinkage(g.value, LLVMInternalLinkage);
+ LLVMSetLinkage(g.value, LLVMPrivateLinkage);
string_map_set(&m->members, s, g);
return g;
}
@@ -2591,6 +2626,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
GB_ASSERT(true_block != nullptr);
GB_ASSERT(false_block != nullptr);
+ // Use to signal not to do compile time short circuit for consts
+ lbValue no_comptime_short_circuit = {};
+
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2598,7 +2636,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
case_ast_node(ue, UnaryExpr, cond);
if (ue->op.kind == Token_Not) {
- return lb_build_cond(p, ue->expr, false_block, true_block);
+ lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
+ if (cond_val.value && LLVMIsConstant(cond_val.value)) {
+ return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
+ }
+ return no_comptime_short_circuit;
}
case_end;
@@ -2607,12 +2649,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
lbBlock *block = lb_create_block(p, "cmp.and");
lb_build_cond(p, be->left, block, false_block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
} else if (be->op.kind == Token_CmpOr) {
lbBlock *block = lb_create_block(p, "cmp.or");
lb_build_cond(p, be->left, true_block, block);
lb_start_block(p, block);
- return lb_build_cond(p, be->right, true_block, false_block);
+ lb_build_cond(p, be->right, true_block, false_block);
+ return no_comptime_short_circuit;
}
case_end;
}
diff --git a/src/llvm_backend_opt.cpp b/src/llvm_backend_opt.cpp
index de925655f..d36bdec0b 100644
--- a/src/llvm_backend_opt.cpp
+++ b/src/llvm_backend_opt.cpp
@@ -48,12 +48,6 @@ LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data
return LLVMIsAAllocaInst(value) != nullptr;
}
-void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
- if (false && optimization_level == 0 && m->debug_builder) {
- // LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback);
- }
-}
-
#if LLVM_VERSION_MAJOR < 12
#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm) LLVMAddConstantPropagationPass(fpm)
@@ -61,16 +55,15 @@ void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i3
#define LLVM_ADD_CONSTANT_VALUE_PASS(fpm)
#endif
-void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
- LLVMAddPromoteMemoryToRegisterPass(fpm);
- LLVMAddMergedLoadStoreMotionPass(fpm);
- LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
- LLVMAddEarlyCSEPass(fpm);
-
- // LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
- // LLVMAddMergedLoadStoreMotionPass(fpm);
- // LLVMAddPromoteMemoryToRegisterPass(fpm);
- // LLVMAddCFGSimplificationPass(fpm);
+void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm, i32 optimization_level) {
+ if (false && optimization_level == 0 && build_context.ODIN_DEBUG) {
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ } else {
+ LLVMAddPromoteMemoryToRegisterPass(fpm);
+ LLVMAddMergedLoadStoreMotionPass(fpm);
+ LLVM_ADD_CONSTANT_VALUE_PASS(fpm);
+ LLVMAddEarlyCSEPass(fpm);
+ }
}
void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
@@ -78,14 +71,12 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
- lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
-
if (ignore_memcpy_pass) {
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
} else if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
@@ -96,7 +87,7 @@ void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool
LLVMPassManagerBuilderPopulateFunctionPassManager(pmb, fpm);
#else
LLVMAddMemCpyOptPass(fpm);
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
LLVMAddSCCPPass(fpm);
@@ -114,11 +105,9 @@ void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
- lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
-
if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm);
- lb_basic_populate_function_pass_manager(fpm);
+ lb_basic_populate_function_pass_manager(fpm, optimization_level);
return;
}
@@ -191,6 +180,9 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
// NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2);
+ if (optimization_level == 0 && build_context.ODIN_DEBUG) {
+ return;
+ }
LLVMAddAlwaysInlinerPass(mpm);
LLVMAddStripDeadPrototypesPass(mpm);
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 25b27ee47..96ff19d10 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -57,11 +57,12 @@ void lb_mem_copy_non_overlapping(lbProcedure *p, lbValue dst, lbValue src, lbVal
LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
}
+
lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body) {
GB_ASSERT(entity != nullptr);
GB_ASSERT(entity->kind == Entity_Procedure);
if (!entity->Procedure.is_foreign) {
- GB_ASSERT(entity->flags & EntityFlag_ProcBodyChecked);
+ GB_ASSERT_MSG(entity->flags & EntityFlag_ProcBodyChecked, "%.*s :: %s", LIT(entity->token.string), type_to_string(entity->type));
}
String link_name = {};
@@ -107,7 +108,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
gbAllocator a = heap_allocator();
p->children.allocator = a;
- p->params.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
@@ -135,6 +135,10 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
lb_add_attribute_to_proc(m, p->value, "naked");
}
+ if (!entity->Procedure.is_foreign && build_context.disable_red_zone) {
+ lb_add_attribute_to_proc(m, p->value, "noredzone");
+ }
+
switch (p->inlining) {
case ProcInlining_inline:
lb_add_attribute_to_proc(m, p->value, "alwaysinline");
@@ -164,14 +168,6 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
break;
}
-
-
- // lbCallingConventionKind cc_kind = lbCallingConvention_C;
- // // TODO(bill): Clean up this logic
- // if (build_context.metrics.os != TargetOs_js) {
- // cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
- // }
- // LLVMSetFunctionCallConv(p->value, cc_kind);
lbValue proc_value = {p->value, p->type};
lb_add_entity(m, entity, proc_value);
lb_add_member(m, p->name, proc_value);
@@ -304,7 +300,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type) {
{
lbValue *found = string_map_get(&m->members, link_name);
- GB_ASSERT(found == nullptr);
+ GB_ASSERT_MSG(found == nullptr, "failed to create dummy procedure for: %.*s", LIT(link_name));
}
lbProcedure *p = gb_alloc_item(permanent_allocator(), lbProcedure);
@@ -323,7 +319,6 @@ lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name, Type *type
gbAllocator a = permanent_allocator();
p->children.allocator = a;
- p->params.allocator = a;
p->defer_stmts.allocator = a;
p->blocks.allocator = a;
p->branch_blocks.allocator = a;
@@ -452,7 +447,7 @@ void lb_begin_procedure_body(lbProcedure *p) {
Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(p->type->Proc.results));
Entity *e = alloc_entity_param(nullptr, make_token_ident(name), ptr_type, false, false);
- e->flags |= EntityFlag_Sret | EntityFlag_NoAlias;
+ e->flags |= EntityFlag_NoAlias;
return_ptr_value.value = LLVMGetParam(p->value, 0);
LLVMSetValueName2(return_ptr_value.value, cast(char const *)name.text, name.len);
@@ -475,44 +470,42 @@ void lb_begin_procedure_body(lbProcedure *p) {
}
lbArgType *arg_type = &ft->args[param_index];
+ defer (param_index += 1);
+
if (arg_type->kind == lbArg_Ignore) {
continue;
} else if (arg_type->kind == lbArg_Direct) {
- lbParamPasskind kind = lbParamPass_Value;
- LLVMTypeRef param_type = lb_type(p->module, e->type);
- if (param_type != arg_type->type) {
- kind = lbParamPass_BitCast;
+ if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+ LLVMTypeRef param_type = lb_type(p->module, e->type);
+ LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+param_index);
+ LLVMValueRef value = OdinLLVMBuildTransmute(p, original_value, param_type);
+
+ lbValue param = {};
+ param.value = value;
+ param.type = e->type;
+
+ lbValue ptr = lb_address_from_load_or_generate_local(p, param);
+ GB_ASSERT(LLVMIsAAllocaInst(ptr.value));
+ lb_add_entity(p->module, e, ptr);
+
+ lbBlock *block = p->decl_block;
+ if (original_value != value) {
+ block = p->curr_block;
+ }
+ LLVMValueRef debug_storage_value = value;
+ if (original_value != value && LLVMIsALoadInst(value)) {
+ debug_storage_value = LLVMGetOperand(value, 0);
+ }
+ lb_add_debug_param_variable(p, debug_storage_value, e->type, e->token, param_index+1, block);
}
- LLVMValueRef value = LLVMGetParam(p->value, param_offset+param_index);
-
- value = OdinLLVMBuildTransmute(p, value, param_type);
-
- lbValue param = {};
- param.value = value;
- param.type = e->type;
- array_add(&p->params, param);
-
- if (e->token.string.len != 0) {
- lbAddr l = lb_add_local(p, e->type, e, false, param_index);
- lb_addr_store(p, l, param);
- }
-
- param_index += 1;
} else if (arg_type->kind == lbArg_Indirect) {
- LLVMValueRef value_ptr = LLVMGetParam(p->value, param_offset+param_index);
- LLVMValueRef value = LLVMBuildLoad(p->builder, value_ptr, "");
-
- lbValue param = {};
- param.value = value;
- param.type = e->type;
- array_add(&p->params, param);
-
- lbValue ptr = {};
- ptr.value = value_ptr;
- ptr.type = alloc_type_pointer(e->type);
-
- lb_add_entity(p->module, e, ptr);
- param_index += 1;
+ if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) {
+ lbValue ptr = {};
+ ptr.value = LLVMGetParam(p->value, param_offset+param_index);
+ ptr.type = alloc_type_pointer(e->type);
+ lb_add_entity(p->module, e, ptr);
+ lb_add_debug_param_variable(p, ptr.value, e->type, e->token, param_index+1, p->decl_block);
+ }
}
}
}
@@ -736,6 +729,10 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, "");
+ if (return_ptr.value != nullptr) {
+ LLVMAddCallSiteAttribute(ret, 1, lb_create_enum_attribute_with_type(p->module->ctx, "sret", LLVMTypeOf(args[0])));
+ }
+
switch (inlining) {
case ProcInlining_none:
break;
@@ -1049,7 +1046,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return lb_string_len(p, v);
} else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant");
- } else if (is_type_slice(t)) {
+ } else if (is_type_slice(t) || is_type_relative_slice(t)) {
return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_len(p, v);
@@ -1075,7 +1072,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_PANIC("Unreachable");
} else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant");
- } else if (is_type_slice(t)) {
+ } else if (is_type_slice(t) || is_type_relative_slice(t)) {
return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_cap(p, v);
@@ -1372,15 +1369,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_cpu_relax:
- if (build_context.metrics.arch == TargetArch_386 ||
+ if (build_context.metrics.arch == TargetArch_i386 ||
build_context.metrics.arch == TargetArch_amd64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {});
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
} else if (build_context.metrics.arch == TargetArch_arm64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
- LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("yield"), {});
+ // NOTE(bill, 2022-03-30): `isb` appears to a better option that `yield`
+ // See: https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8258604
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("isb"), {}, true);
+ GB_ASSERT(the_asm != nullptr);
+ LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ // NOTE: default to something to prevent optimization
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit(""), {}, true);
GB_ASSERT(the_asm != nullptr);
LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
}
@@ -1409,14 +1414,23 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_read_cycle_counter:
{
- char const *name = "llvm.readcyclecounter";
- unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
- GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
- LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
-
lbValue res = {};
- res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
res.type = tv.type;
+
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ LLVMTypeRef func_type = LLVMFunctionType(LLVMInt64TypeInContext(p->module->ctx), nullptr, 0, false);
+ bool has_side_effects = false;
+ LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("mrs x9, cntvct_el0"), str_lit("=r"), has_side_effects);
+ GB_ASSERT(the_asm != nullptr);
+ res.value = LLVMBuildCall2(p->builder, func_type, the_asm, nullptr, 0, "");
+ } else {
+ char const *name = "llvm.readcyclecounter";
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, nullptr, 0);
+
+ res.value = LLVMBuildCall(p->builder, ip, nullptr, 0, "");
+ }
return res;
}
@@ -1592,36 +1606,26 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
-
- case BuiltinProc_atomic_fence:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingSequentiallyConsistent, false, "");
+ // TODO(bill): Which is correct?
+ case BuiltinProc_atomic_thread_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), false, "");
return {};
- case BuiltinProc_atomic_fence_acq:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquire, false, "");
- return {};
- case BuiltinProc_atomic_fence_rel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingRelease, false, "");
- return {};
- case BuiltinProc_atomic_fence_acqrel:
- LLVMBuildFence(p->builder, LLVMAtomicOrderingAcquireRelease, false, "");
+ case BuiltinProc_atomic_signal_fence:
+ LLVMBuildFence(p->builder, llvm_atomic_ordering_from_odin(ce->args[0]), true, "");
return {};
case BuiltinProc_volatile_store:
case BuiltinProc_atomic_store:
- case BuiltinProc_atomic_store_rel:
- case BuiltinProc_atomic_store_relaxed:
- case BuiltinProc_atomic_store_unordered: {
+ case BuiltinProc_atomic_store_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
LLVMValueRef instr = LLVMBuildStore(p->builder, val.value, dst.value);
switch (id) {
- case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_store_rel: LLVMSetOrdering(instr, LLVMAtomicOrderingRelease); break;
- case BuiltinProc_atomic_store_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_store_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_volatile_store: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_store: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_store_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[2])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1631,18 +1635,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_volatile_load:
case BuiltinProc_atomic_load:
- case BuiltinProc_atomic_load_acq:
- case BuiltinProc_atomic_load_relaxed:
- case BuiltinProc_atomic_load_unordered: {
+ case BuiltinProc_atomic_load_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
LLVMValueRef instr = LLVMBuildLoad(p->builder, dst.value, "");
switch (id) {
- case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
- case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
- case BuiltinProc_atomic_load_acq: LLVMSetOrdering(instr, LLVMAtomicOrderingAcquire); break;
- case BuiltinProc_atomic_load_relaxed: LLVMSetOrdering(instr, LLVMAtomicOrderingMonotonic); break;
- case BuiltinProc_atomic_load_unordered: LLVMSetOrdering(instr, LLVMAtomicOrderingUnordered); break;
+ case BuiltinProc_volatile_load: LLVMSetVolatile(instr, true); break;
+ case BuiltinProc_atomic_load: LLVMSetOrdering(instr, LLVMAtomicOrderingSequentiallyConsistent); break;
+ case BuiltinProc_atomic_load_explicit: LLVMSetOrdering(instr, llvm_atomic_ordering_from_odin(ce->args[1])); break;
}
LLVMSetAlignment(instr, cast(unsigned)type_align_of(type_deref(dst.type)));
@@ -1672,40 +1672,19 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
}
case BuiltinProc_atomic_add:
- case BuiltinProc_atomic_add_acq:
- case BuiltinProc_atomic_add_rel:
- case BuiltinProc_atomic_add_acqrel:
- case BuiltinProc_atomic_add_relaxed:
case BuiltinProc_atomic_sub:
- case BuiltinProc_atomic_sub_acq:
- case BuiltinProc_atomic_sub_rel:
- case BuiltinProc_atomic_sub_acqrel:
- case BuiltinProc_atomic_sub_relaxed:
case BuiltinProc_atomic_and:
- case BuiltinProc_atomic_and_acq:
- case BuiltinProc_atomic_and_rel:
- case BuiltinProc_atomic_and_acqrel:
- case BuiltinProc_atomic_and_relaxed:
case BuiltinProc_atomic_nand:
- case BuiltinProc_atomic_nand_acq:
- case BuiltinProc_atomic_nand_rel:
- case BuiltinProc_atomic_nand_acqrel:
- case BuiltinProc_atomic_nand_relaxed:
case BuiltinProc_atomic_or:
- case BuiltinProc_atomic_or_acq:
- case BuiltinProc_atomic_or_rel:
- case BuiltinProc_atomic_or_acqrel:
- case BuiltinProc_atomic_or_relaxed:
case BuiltinProc_atomic_xor:
- case BuiltinProc_atomic_xor_acq:
- case BuiltinProc_atomic_xor_rel:
- case BuiltinProc_atomic_xor_acqrel:
- case BuiltinProc_atomic_xor_relaxed:
- case BuiltinProc_atomic_xchg:
- case BuiltinProc_atomic_xchg_acq:
- case BuiltinProc_atomic_xchg_rel:
- case BuiltinProc_atomic_xchg_acqrel:
- case BuiltinProc_atomic_xchg_relaxed: {
+ case BuiltinProc_atomic_exchange:
+ case BuiltinProc_atomic_add_explicit:
+ case BuiltinProc_atomic_sub_explicit:
+ case BuiltinProc_atomic_and_explicit:
+ case BuiltinProc_atomic_nand_explicit:
+ case BuiltinProc_atomic_or_explicit:
+ case BuiltinProc_atomic_xor_explicit:
+ case BuiltinProc_atomic_exchange_explicit: {
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue val = lb_build_expr(p, ce->args[1]);
val = lb_emit_conv(p, val, type_deref(dst.type));
@@ -1714,41 +1693,20 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMAtomicOrdering ordering = {};
switch (id) {
- case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_add_acq: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_add_rel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_add_acqrel: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_add_relaxed: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_sub_acq: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_sub_rel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_sub_acqrel: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_sub_relaxed: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_and_acq: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_and_rel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_and_acqrel: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_and_relaxed: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_nand_acq: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_nand_rel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_nand_acqrel: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_nand_relaxed: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_or_acq: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_or_rel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_or_acqrel: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_or_relaxed: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xor_acq: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xor_rel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xor_acqrel: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xor_relaxed: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingMonotonic; break;
- case BuiltinProc_atomic_xchg: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
- case BuiltinProc_atomic_xchg_acq: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquire; break;
- case BuiltinProc_atomic_xchg_rel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingRelease; break;
- case BuiltinProc_atomic_xchg_acqrel: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingAcquireRelease; break;
- case BuiltinProc_atomic_xchg_relaxed: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingMonotonic; break;
+ case BuiltinProc_atomic_add: op = LLVMAtomicRMWBinOpAdd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_sub: op = LLVMAtomicRMWBinOpSub; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_and: op = LLVMAtomicRMWBinOpAnd; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_nand: op = LLVMAtomicRMWBinOpNand; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_or: op = LLVMAtomicRMWBinOpOr; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_xor: op = LLVMAtomicRMWBinOpXor; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_exchange: op = LLVMAtomicRMWBinOpXchg; ordering = LLVMAtomicOrderingSequentiallyConsistent; break;
+ case BuiltinProc_atomic_add_explicit: op = LLVMAtomicRMWBinOpAdd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_sub_explicit: op = LLVMAtomicRMWBinOpSub; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_and_explicit: op = LLVMAtomicRMWBinOpAnd; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_nand_explicit: op = LLVMAtomicRMWBinOpNand; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_or_explicit: op = LLVMAtomicRMWBinOpOr; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_xor_explicit: op = LLVMAtomicRMWBinOpXor; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
+ case BuiltinProc_atomic_exchange_explicit: op = LLVMAtomicRMWBinOpXchg; ordering = llvm_atomic_ordering_from_odin(ce->args[2]); break;
}
lbValue res = {};
@@ -1757,24 +1715,10 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
- case BuiltinProc_atomic_cxchg:
- case BuiltinProc_atomic_cxchg_acq:
- case BuiltinProc_atomic_cxchg_rel:
- case BuiltinProc_atomic_cxchg_acqrel:
- case BuiltinProc_atomic_cxchg_relaxed:
- case BuiltinProc_atomic_cxchg_failrelaxed:
- case BuiltinProc_atomic_cxchg_failacq:
- case BuiltinProc_atomic_cxchg_acq_failrelaxed:
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed:
- case BuiltinProc_atomic_cxchgweak:
- case BuiltinProc_atomic_cxchgweak_acq:
- case BuiltinProc_atomic_cxchgweak_rel:
- case BuiltinProc_atomic_cxchgweak_acqrel:
- case BuiltinProc_atomic_cxchgweak_relaxed:
- case BuiltinProc_atomic_cxchgweak_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_failacq:
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed:
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: {
+ case BuiltinProc_atomic_compare_exchange_strong:
+ case BuiltinProc_atomic_compare_exchange_weak:
+ case BuiltinProc_atomic_compare_exchange_strong_explicit:
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: {
lbValue address = lb_build_expr(p, ce->args[0]);
Type *elem = type_deref(address.type);
lbValue old_value = lb_build_expr(p, ce->args[1]);
@@ -1787,28 +1731,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
LLVMBool weak = false;
switch (id) {
- case BuiltinProc_atomic_cxchg: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchg_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = false; break;
- case BuiltinProc_atomic_cxchg_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchg_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = false; break;
- case BuiltinProc_atomic_cxchgweak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
- case BuiltinProc_atomic_cxchgweak_acq: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_rel: success_ordering = LLVMAtomicOrderingRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_relaxed: success_ordering = LLVMAtomicOrderingMonotonic; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failrelaxed: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_failacq: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingAcquire; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acq_failrelaxed: success_ordering = LLVMAtomicOrderingAcquire; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
- case BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed: success_ordering = LLVMAtomicOrderingAcquireRelease; failure_ordering = LLVMAtomicOrderingMonotonic; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak: success_ordering = LLVMAtomicOrderingSequentiallyConsistent; failure_ordering = LLVMAtomicOrderingSequentiallyConsistent; weak = true; break;
+ case BuiltinProc_atomic_compare_exchange_strong_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = false; break;
+ case BuiltinProc_atomic_compare_exchange_weak_explicit: success_ordering = llvm_atomic_ordering_from_odin(ce->args[3]); failure_ordering = llvm_atomic_ordering_from_odin(ce->args[4]); weak = true; break;
}
// TODO(bill): Figure out how to make it weak
- LLVMBool single_threaded = weak;
+ LLVMBool single_threaded = false;
LLVMValueRef value = LLVMBuildAtomicCmpXchg(
p->builder, address.value,
@@ -1817,6 +1747,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
failure_ordering,
single_threaded
);
+ LLVMSetWeak(value, weak);
if (tv.type->kind == Type_Tuple) {
Type *fix_typed = alloc_type_tuple();
@@ -1961,6 +1892,14 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
return res;
}
+ case BuiltinProc___entry_point:
+ if (p->module->info->entry_point) {
+ lbValue entry_point = lb_find_procedure_value_from_entity(p->module, p->module->info->entry_point);
+ GB_ASSERT(entry_point.value != nullptr);
+ lb_emit_call(p, entry_point, {});
+ }
+ return {};
+
case BuiltinProc_syscall:
{
unsigned arg_count = cast(unsigned)ce->args.count;
@@ -2021,7 +1960,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
- case TargetArch_386:
+ case TargetArch_i386:
{
GB_ASSERT(arg_count <= 7);
@@ -2054,26 +1993,47 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
break;
case TargetArch_arm64:
{
- GB_ASSERT(arg_count <= 7);
-
- char asm_string[] = "svc #0";
- gbString constraints = gb_string_make(heap_allocator(), "={x0}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
- "x8",
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- };
- constraints = gb_string_appendc(constraints, regs[i]);
- constraints = gb_string_appendc(constraints, "}");
- }
+ GB_ASSERT(arg_count <= 7);
+
+ if(build_context.metrics.os == TargetOs_darwin) {
+ char asm_string[] = "svc #0x80";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x16",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
- inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ } else {
+ char asm_string[] = "svc #0";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x8",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
}
break;
default:
@@ -2085,6 +2045,124 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = t_uintptr;
return res;
}
+
+ case BuiltinProc_objc_send:
+ return lb_handle_objc_send(p, expr);
+
+ case BuiltinProc_objc_find_selector: return lb_handle_objc_find_selector(p, expr);
+ case BuiltinProc_objc_find_class: return lb_handle_objc_find_class(p, expr);
+ case BuiltinProc_objc_register_selector: return lb_handle_objc_register_selector(p, expr);
+ case BuiltinProc_objc_register_class: return lb_handle_objc_register_class(p, expr);
+
+
+ case BuiltinProc_constant_utf16_cstring:
+ {
+ auto const encode_surrogate_pair = [](Rune r, u16 *r1, u16 *r2) {
+ if (r < 0x10000 || r > 0x10ffff) {
+ *r1 = 0xfffd;
+ *r2 = 0xfffd;
+ } else {
+ r -= 0x10000;
+ *r1 = 0xd800 + ((r>>10)&0x3ff);
+ *r2 = 0xdc00 + (r&0x3ff);
+ }
+ };
+
+ lbModule *m = p->module;
+
+ auto tav = type_and_value_of_expr(ce->args[0]);
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String value = tav.value.value_string;
+
+ LLVMTypeRef llvm_u16 = lb_type(m, t_u16);
+
+ isize max_len = value.len*2 + 1;
+ LLVMValueRef *buffer = gb_alloc_array(temporary_allocator(), LLVMValueRef, max_len);
+ isize n = 0;
+ while (value.len > 0) {
+ Rune r = 0;
+ isize w = gb_utf8_decode(value.text, value.len, &r);
+ value.text += w;
+ value.len -= w;
+ if ((0 <= r && r < 0xd800) || (0xe000 <= r && r < 0x10000)) {
+ buffer[n++] = LLVMConstInt(llvm_u16, cast(u16)r, false);
+ } else if (0x10000 <= r && r <= 0x10ffff) {
+ u16 r1, r2;
+ encode_surrogate_pair(r, &r1, &r2);
+ buffer[n++] = LLVMConstInt(llvm_u16, r1, false);
+ buffer[n++] = LLVMConstInt(llvm_u16, r2, false);
+ } else {
+ buffer[n++] = LLVMConstInt(llvm_u16, 0xfffd, false);
+ }
+ }
+
+ buffer[n++] = LLVMConstInt(llvm_u16, 0, false);
+
+ LLVMValueRef array = LLVMConstArray(llvm_u16, buffer, cast(unsigned int)n);
+
+ char *name = nullptr;
+ {
+ isize max_len = 7+8+1;
+ name = gb_alloc_array(permanent_allocator(), char, max_len);
+ u32 id = m->gen->global_array_index.fetch_add(1);
+ isize len = gb_snprintf(name, max_len, "csbs$%x", id);
+ len -= 1;
+ }
+ LLVMValueRef global_data = LLVMAddGlobal(m->mod, LLVMTypeOf(array), name);
+ LLVMSetInitializer(global_data, array);
+ LLVMSetLinkage(global_data, LLVMInternalLinkage);
+
+
+
+ LLVMValueRef indices[] = {
+ LLVMConstInt(lb_type(m, t_u32), 0, false),
+ LLVMConstInt(lb_type(m, t_u32), 0, false),
+ };
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildInBoundsGEP(p->builder, global_data, indices, gb_count_of(indices), "");
+ return res;
+
+ }
+
+ case BuiltinProc_wasm_memory_grow:
+ {
+ char const *name = "llvm.wasm.memory.grow";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[2] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+ args[1] = lb_emit_conv(p, lb_build_expr(p, ce->args[1]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+ case BuiltinProc_wasm_memory_size:
+ {
+ char const *name = "llvm.wasm.memory.size";
+ LLVMTypeRef types[1] = {
+ lb_type(p->module, t_uintptr),
+ };
+ unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
+ GB_ASSERT_MSG(id != 0, "Unable to find %s", name, LLVMPrintTypeToString(types[0]));
+ LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
+
+ LLVMValueRef args[1] = {};
+ args[0] = lb_emit_conv(p, lb_build_expr(p, ce->args[0]), t_uintptr).value;
+
+ lbValue res = {};
+ res.type = tv.type;
+ res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
+ return res;
+ }
+
}
GB_PANIC("Unhandled built-in procedure %.*s", LIT(builtin_procs[id].name));
diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp
index 016e464b8..2afb5300b 100644
--- a/src/llvm_backend_stmt.cpp
+++ b/src/llvm_backend_stmt.cpp
@@ -1652,13 +1652,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
}
lbValue cond = lb_build_cond(p, is->cond, then, else_);
+ // Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
+ // and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
+ // will target the correct (& only) branch statement
if (is->label != nullptr) {
lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
tl->is_block = true;
}
- if (LLVMIsConstant(cond.value)) {
+ if (cond.value && LLVMIsConstant(cond.value)) {
// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
// This done manually rather than relying on the SSA passes because sometimes the SSA passes
// miss some even if they are constantly known, especially with few optimization passes.
@@ -1766,6 +1769,8 @@ void lb_build_for_stmt(lbProcedure *p, Ast *node) {
}
void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs, lbValue const &value) {
+ GB_ASSERT(op != Token_Eq);
+
Type *lhs_type = lb_addr_type(lhs);
Type *array_type = base_type(lhs_type);
GB_ASSERT(is_type_array_like(array_type));
@@ -1795,7 +1800,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
}
indices[index_count++] = index;
}
- gb_sort_array(indices, index_count, gb_i32_cmp(0));
lbValue lhs_ptrs[4] = {};
lbValue x_loads[4] = {};
@@ -1840,7 +1844,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
}
indices[index_count++] = index;
}
- gb_sort_array(indices.data, index_count, gb_i32_cmp(0));
lbValue lhs_ptrs[4] = {};
lbValue x_loads[4] = {};
@@ -1868,11 +1871,7 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
lbValue x = lb_addr_get_ptr(p, lhs);
-
-
if (inline_array_arith) {
- #if 1
- #if 1
unsigned n = cast(unsigned)count;
auto lhs_ptrs = slice_make(temporary_allocator(), n);
@@ -1896,50 +1895,6 @@ void lb_build_assign_stmt_array(lbProcedure *p, TokenKind op, lbAddr const &lhs,
for (unsigned i = 0; i < n; i++) {
lb_emit_store(p, lhs_ptrs[i], ops[i]);
}
-
- #else
- lbValue y = lb_address_from_load_or_generate_local(p, rhs);
-
- unsigned n = cast(unsigned)count;
-
- auto lhs_ptrs = slice_make(temporary_allocator(), n);
- auto rhs_ptrs = slice_make(temporary_allocator(), n);
- auto x_loads = slice_make(temporary_allocator(), n);
- auto y_loads = slice_make(temporary_allocator(), n);
- auto ops = slice_make(temporary_allocator(), n);
-
- for (unsigned i = 0; i < n; i++) {
- lhs_ptrs[i] = lb_emit_array_epi(p, x, i);
- }
- for (unsigned i = 0; i < n; i++) {
- rhs_ptrs[i] = lb_emit_array_epi(p, y, i);
- }
- for (unsigned i = 0; i < n; i++) {
- x_loads[i] = lb_emit_load(p, lhs_ptrs[i]);
- }
- for (unsigned i = 0; i < n; i++) {
- y_loads[i] = lb_emit_load(p, rhs_ptrs[i]);
- }
- for (unsigned i = 0; i < n; i++) {
- ops[i] = lb_emit_arith(p, op, x_loads[i], y_loads[i], elem_type);
- }
- for (unsigned i = 0; i < n; i++) {
- lb_emit_store(p, lhs_ptrs[i], ops[i]);
- }
- #endif
- #else
- lbValue y = lb_address_from_load_or_generate_local(p, rhs);
-
- for (i64 i = 0; i < count; i++) {
- lbValue a_ptr = lb_emit_array_epi(p, x, i);
- lbValue b_ptr = lb_emit_array_epi(p, y, i);
-
- lbValue a = lb_emit_load(p, a_ptr);
- lbValue b = lb_emit_load(p, b_ptr);
- lbValue c = lb_emit_arith(p, op, a, b, elem_type);
- lb_emit_store(p, a_ptr, c);
- }
- #endif
} else {
lbValue y = lb_address_from_load_or_generate_local(p, rhs);
@@ -2039,6 +1994,13 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
out |= StateFlag_no_bounds_check;
out &= ~StateFlag_bounds_check;
}
+ if (in & StateFlag_no_type_assert) {
+ out |= StateFlag_no_type_assert;
+ out &= ~StateFlag_type_assert;
+ } else if (in & StateFlag_type_assert) {
+ out |= StateFlag_type_assert;
+ out &= ~StateFlag_no_type_assert;
+ }
p->state_flags = out;
}
@@ -2188,6 +2150,7 @@ void lb_build_stmt(lbProcedure *p, Ast *node) {
lb_emit_defer_stmts(p, lbDeferExit_Branch, block);
}
lb_emit_jump(p, block);
+ lb_start_block(p, lb_create_block(p, "unreachable"));
case_end;
}
}
@@ -2219,10 +2182,6 @@ void lb_build_defer_stmt(lbProcedure *p, lbDefer const &d) {
lb_start_block(p, b);
if (d.kind == lbDefer_Node) {
lb_build_stmt(p, d.stmt);
- } else if (d.kind == lbDefer_Instr) {
- // NOTE(bill): Need to make a new copy
- LLVMValueRef instr = LLVMInstructionClone(d.instr.value);
- LLVMInsertIntoBuilder(p->builder, instr);
} else if (d.kind == lbDefer_Proc) {
lb_emit_call(p, d.proc.deferred, d.proc.result_as_args);
}
diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp
index decb57702..7d73956e8 100644
--- a/src/llvm_backend_type.cpp
+++ b/src/llvm_backend_type.cpp
@@ -1,11 +1,10 @@
isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true) {
- isize index = type_info_index(info, type, false);
+ auto *set = &info->minimum_dependency_type_info_set;
+ isize index = type_info_index(info, type, err_on_not_found);
if (index >= 0) {
- auto *set = &info->minimum_dependency_type_info_set;
- for_array(i, set->entries) {
- if (set->entries[i].ptr == index) {
- return i+1;
- }
+ isize i = ptr_entry_index(set, index);
+ if (i >= 0) {
+ return i+1;
}
}
if (err_on_not_found) {
@@ -15,6 +14,8 @@ isize lb_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=tr
}
lbValue lb_typeid(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
type = default_type(type);
u64 id = cast(u64)lb_type_info_index(m->info, type);
@@ -89,6 +90,8 @@ lbValue lb_typeid(lbModule *m, Type *type) {
}
lbValue lb_type_info(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
type = default_type(type);
isize index = lb_type_info_index(m->info, type);
@@ -107,6 +110,8 @@ lbValue lb_type_info(lbModule *m, Type *type) {
}
lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
+ GB_ASSERT(!build_context.disallow_rtti);
+
i32 index = cast(i32)lb_type_info_index(m->info, type);
GB_ASSERT(index >= 0);
// gb_printf_err("%d %s\n", index, type_to_string(type));
@@ -156,6 +161,10 @@ lbValue lb_type_info_member_tags_offset(lbProcedure *p, isize count) {
void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info data
+ if (build_context.disallow_rtti) {
+ return;
+ }
+
lbModule *m = p->module;
CheckerInfo *info = m->info;
@@ -455,7 +464,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
case Type_EnumeratedArray: {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
- LLVMValueRef vals[6] = {
+ LLVMValueRef vals[7] = {
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
@@ -464,6 +473,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
// Unions
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
LLVMConstNull(lb_type(m, t_type_info_enum_value)),
+
+ lb_const_bool(m, t_bool, t->EnumeratedArray.is_sparse).value,
};
lbValue res = {};
@@ -630,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_union_ptr);
{
- LLVMValueRef vals[7] = {};
+ LLVMValueRef vals[8] = {};
isize variant_count = gb_max(0, t->Union.variants.count);
lbValue memory_types = lb_type_info_member_types_offset(p, variant_count);
@@ -664,8 +675,9 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
}
vals[4] = lb_const_bool(m, t_bool, t->Union.custom_align != 0).value;
- vals[5] = lb_const_bool(m, t_bool, t->Union.no_nil).value;
- vals[6] = lb_const_bool(m, t_bool, t->Union.maybe).value;
+ vals[5] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_no_nil).value;
+ vals[6] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_maybe).value;
+ vals[7] = lb_const_bool(m, t_bool, t->Union.kind == UnionType_shared_nil).value;
for (isize i = 0; i < gb_count_of(vals); i++) {
if (vals[i] == nullptr) {
diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp
index 0350f7287..037171637 100644
--- a/src/llvm_backend_utility.cpp
+++ b/src/llvm_backend_utility.cpp
@@ -626,6 +626,12 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lbValue value_ = lb_address_from_load_or_generate_local(p, value);
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_conv(p, value_, alloc_type_pointer(type));
+ return lb_emit_load(p, ptr);
+ }
+
lbValue tag = {};
lbValue dst_tag = {};
lbValue cond = {};
@@ -666,23 +672,29 @@ lbValue lb_emit_union_cast(lbProcedure *p, lbValue value, Type *type, TokenPos p
lb_start_block(p, end_block);
if (!is_tuple) {
- {
- // NOTE(bill): Panic on invalid conversion
- Type *dst_type = tuple->Tuple.variables[0]->type;
+ GB_ASSERT((p->state_flags & StateFlag_no_type_assert) == 0);
+ // NOTE(bill): Panic on invalid conversion
+ Type *dst_type = tuple->Tuple.variables[0]->type;
- lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make(permanent_allocator(), 7);
- args[0] = ok;
+ isize arg_count = 7;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
- args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
- args[2] = lb_const_int(m, t_i32, pos.line);
- args[3] = lb_const_int(m, t_i32, pos.column);
+ lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
+ auto args = array_make(permanent_allocator(), arg_count);
+ args[0] = ok;
+ args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
+ args[2] = lb_const_int(m, t_i32, pos.line);
+ args[3] = lb_const_int(m, t_i32, pos.column);
+
+ if (!build_context.disallow_rtti) {
args[4] = lb_typeid(m, src_type);
args[5] = lb_typeid(m, dst_type);
args[6] = lb_emit_conv(p, value_, t_rawptr);
- lb_emit_runtime_call(p, "type_assertion_check2", args);
}
+ lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 0));
}
@@ -706,6 +718,13 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
}
Type *dst_type = tuple->Tuple.variables[0]->type;
+ if ((p->state_flags & StateFlag_no_type_assert) != 0 && !is_tuple) {
+ // just do a bit cast of the data at the front
+ lbValue ptr = lb_emit_struct_ev(p, value, 0);
+ ptr = lb_emit_conv(p, ptr, alloc_type_pointer(type));
+ return lb_addr(ptr);
+ }
+
lbAddr v = lb_add_local_generated(p, tuple, true);
lbValue dst_typeid = lb_typeid(m, dst_type);
@@ -731,18 +750,24 @@ lbAddr lb_emit_any_cast_addr(lbProcedure *p, lbValue value, Type *type, TokenPos
if (!is_tuple) {
// NOTE(bill): Panic on invalid conversion
-
lbValue ok = lb_emit_load(p, lb_emit_struct_ep(p, v.addr, 1));
- auto args = array_make(permanent_allocator(), 7);
+
+ isize arg_count = 7;
+ if (build_context.disallow_rtti) {
+ arg_count = 4;
+ }
+ auto args = array_make(permanent_allocator(), arg_count);
args[0] = ok;
args[1] = lb_const_string(m, get_file_path_string(pos.file_id));
args[2] = lb_const_int(m, t_i32, pos.line);
args[3] = lb_const_int(m, t_i32, pos.column);
- args[4] = any_typeid;
- args[5] = dst_typeid;
- args[6] = lb_emit_struct_ev(p, value, 0);;
+ if (!build_context.disallow_rtti) {
+ args[4] = any_typeid;
+ args[5] = dst_typeid;
+ args[6] = lb_emit_struct_ev(p, value, 0);
+ }
lb_emit_runtime_call(p, "type_assertion_check2", args);
return lb_addr(lb_emit_struct_ep(p, v.addr, 0));
@@ -1200,7 +1225,7 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
}
lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
- index = lb_correct_endianness(p, index);
+ index = lb_emit_conv(p, index, t_int);
LLVMValueRef indices[1] = {index.value};
lbValue res = {};
res.type = ptr.type;
@@ -1362,7 +1387,7 @@ lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
return lb_emit_struct_ev(p, slice, 0);
}
lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
- GB_ASSERT(is_type_slice(slice.type));
+ GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type));
return lb_emit_struct_ev(p, slice, 1);
}
lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
@@ -1494,7 +1519,7 @@ lbValue lb_emit_mul_add(lbProcedure *p, lbValue a, lbValue b, lbValue c, Type *t
case TargetArch_arm64:
// possible
break;
- case TargetArch_386:
+ case TargetArch_i386:
case TargetArch_wasm32:
case TargetArch_wasm64:
is_possible = false;
@@ -1768,7 +1793,7 @@ LLVMValueRef llvm_get_inline_asm(LLVMTypeRef func_type, String const &str, Strin
return LLVMGetInlineAsm(func_type,
cast(char *)str.text, cast(size_t)str.len,
cast(char *)clobbers.text, cast(size_t)clobbers.len,
- /*HasSideEffects*/true, /*IsAlignStack*/false,
+ has_side_effects, is_align_stack,
dialect
#if LLVM_VERSION_MAJOR >= 13
, /*CanThrow*/false
@@ -1808,3 +1833,197 @@ void lb_set_wasm_export_attributes(LLVMValueRef value, String export_name) {
LLVMSetVisibility(value, LLVMDefaultVisibility);
LLVMAddTargetDependentFunctionAttr(value, "wasm-export-name", alloc_cstring(permanent_allocator(), export_name));
}
+
+
+lbValue lb_lookup_runtime_procedure(lbModule *m, String const &name);
+
+
+lbAddr lb_handle_objc_find_or_register_selector(lbProcedure *p, String const &name) {
+ lbAddr *found = string_map_get(&p->module->objc_selectors, name);
+ if (found) {
+ return *found;
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_selectors, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_selectors, name, local_addr);
+ }
+ return local_addr;
+ }
+}
+
+lbValue lb_handle_objc_find_selector(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ return lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, name));
+}
+
+lbValue lb_handle_objc_register_selector(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ lbModule *m = p->module;
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ lbAddr dst = lb_handle_objc_find_or_register_selector(p, name);
+
+ auto args = array_make(permanent_allocator(), 1);
+ args[0] = lb_const_value(m, t_cstring, exact_value_string(name));
+ lbValue ptr = lb_emit_runtime_call(p, "sel_registerName", args);
+ lb_addr_store(p, dst, ptr);
+
+ return lb_addr_load(p, dst);
+}
+
+lbAddr lb_handle_objc_find_or_register_class(lbProcedure *p, String const &name) {
+ lbAddr *found = string_map_get(&p->module->objc_classes, name);
+ if (found) {
+ return *found;
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_SEL, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_classes, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_classes, name, local_addr);
+ }
+ return local_addr;
+ }
+}
+
+lbValue lb_handle_objc_find_class(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ return lb_addr_load(p, lb_handle_objc_find_or_register_class(p, name));
+}
+
+lbValue lb_handle_objc_register_class(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+ lbModule *m = p->module;
+
+ auto tav = ce->args[0]->tav;
+ GB_ASSERT(tav.value.kind == ExactValue_String);
+ String name = tav.value.value_string;
+ lbAddr dst = lb_handle_objc_find_or_register_class(p, name);
+
+ auto args = array_make(permanent_allocator(), 3);
+ args[0] = lb_const_nil(m, t_objc_Class);
+ args[1] = lb_const_nil(m, t_objc_Class);
+ args[2] = lb_const_int(m, t_uint, 0);
+ lbValue ptr = lb_emit_runtime_call(p, "objc_allocateClassPair", args);
+ lb_addr_store(p, dst, ptr);
+
+ return lb_addr_load(p, dst);
+}
+
+
+lbValue lb_handle_objc_id(lbProcedure *p, Ast *expr) {
+ TypeAndValue const &tav = type_and_value_of_expr(expr);
+ if (tav.mode == Addressing_Type) {
+ Type *type = tav.type;
+ GB_ASSERT_MSG(type->kind == Type_Named, "%s", type_to_string(type));
+ Entity *e = type->Named.type_name;
+ GB_ASSERT(e->kind == Entity_TypeName);
+ String name = e->TypeName.objc_class_name;
+
+ lbAddr *found = string_map_get(&p->module->objc_classes, name);
+ if (found) {
+ return lb_addr_load(p, *found);
+ } else {
+ lbModule *default_module = &p->module->gen->default_module;
+ Entity *e = nullptr;
+ lbAddr default_addr = lb_add_global_generated(default_module, t_objc_Class, {}, &e);
+
+ lbValue ptr = lb_find_value_from_entity(p->module, e);
+ lbAddr local_addr = lb_addr(ptr);
+
+ string_map_set(&default_module->objc_classes, name, default_addr);
+ if (default_module != p->module) {
+ string_map_set(&p->module->objc_classes, name, local_addr);
+ }
+ return lb_addr_load(p, local_addr);
+ }
+ }
+
+ return lb_build_expr(p, expr);
+}
+
+lbValue lb_handle_objc_send(lbProcedure *p, Ast *expr) {
+ ast_node(ce, CallExpr, expr);
+
+ lbModule *m = p->module;
+ CheckerInfo *info = m->info;
+ ObjcMsgData data = map_must_get(&info->objc_msgSend_types, expr);
+ GB_ASSERT(data.proc_type != nullptr);
+
+ GB_ASSERT(ce->args.count >= 3);
+ auto args = array_make(permanent_allocator(), 0, ce->args.count-1);
+
+ lbValue id = lb_handle_objc_id(p, ce->args[1]);
+ Ast *sel_expr = ce->args[2];
+ GB_ASSERT(sel_expr->tav.value.kind == ExactValue_String);
+ lbValue sel = lb_addr_load(p, lb_handle_objc_find_or_register_selector(p, sel_expr->tav.value.value_string));
+
+ array_add(&args, id);
+ array_add(&args, sel);
+ for (isize i = 3; i < ce->args.count; i++) {
+ lbValue arg = lb_build_expr(p, ce->args[i]);
+ array_add(&args, arg);
+ }
+
+
+ lbValue the_proc = {};
+ switch (data.kind) {
+ default:
+ GB_PANIC("unhandled ObjcMsgKind %u", data.kind);
+ break;
+ case ObjcMsg_normal: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend")); break;
+ case ObjcMsg_fpret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fpret")); break;
+ case ObjcMsg_fp2ret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_fp2ret")); break;
+ case ObjcMsg_stret: the_proc = lb_lookup_runtime_procedure(m, str_lit("objc_msgSend_stret")); break;
+ }
+
+ the_proc = lb_emit_conv(p, the_proc, data.proc_type);
+
+ return lb_emit_call(p, the_proc, args);
+}
+
+
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(ExactValue const &value) {
+ GB_ASSERT(value.kind == ExactValue_Integer);
+ i64 v = exact_value_to_i64(value);
+ switch (v) {
+ case OdinAtomicMemoryOrder_relaxed: return LLVMAtomicOrderingUnordered;
+ case OdinAtomicMemoryOrder_consume: return LLVMAtomicOrderingMonotonic;
+ case OdinAtomicMemoryOrder_acquire: return LLVMAtomicOrderingAcquire;
+ case OdinAtomicMemoryOrder_release: return LLVMAtomicOrderingRelease;
+ case OdinAtomicMemoryOrder_acq_rel: return LLVMAtomicOrderingAcquireRelease;
+ case OdinAtomicMemoryOrder_seq_cst: return LLVMAtomicOrderingSequentiallyConsistent;
+ }
+ GB_PANIC("Unknown atomic ordering");
+ return LLVMAtomicOrderingSequentiallyConsistent;
+}
+
+
+LLVMAtomicOrdering llvm_atomic_ordering_from_odin(Ast *expr) {
+ ExactValue value = type_and_value_of_expr(expr).value;
+ return llvm_atomic_ordering_from_odin(value);
+}
diff --git a/src/main.cpp b/src/main.cpp
index 7b4bc92ee..7b0364149 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -46,7 +46,6 @@ gb_global Timings global_timings = {0};
#include "checker.cpp"
#include "docs.cpp"
-
#include "llvm_backend.cpp"
#if defined(GB_SYSTEM_OSX)
@@ -57,16 +56,8 @@ gb_global Timings global_timings = {0};
#endif
#include "query_data.cpp"
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
-#include "microsoft_craziness.h"
-#endif
-
#include "bug_report.cpp"
-
// NOTE(bill): 'name' is used in debugging and profiling modes
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
@@ -74,16 +65,16 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
isize cmd_len = 0;
va_list va;
i32 exit_code = 0;
-
+
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, cmd_cap-1, fmt, va);
va_end(va);
-
+
#if defined(GB_SYSTEM_WINDOWS)
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
PROCESS_INFORMATION pi = {0};
String16 wcmd = {};
-
+
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
start_info.wShowWindow = SW_SHOW;
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
@@ -117,44 +108,48 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
gb_printf_err("%s\n\n", cmd_line);
}
exit_code = system(cmd_line);
+ if (WIFEXITED(exit_code)) {
+ exit_code = WEXITSTATUS(exit_code);
+ }
#endif
-
+
if (exit_code) {
exit(exit_code);
}
-
+
return exit_code;
}
-
-
i32 linker_stage(lbGenerator *gen) {
i32 result = 0;
Timings *timings = &global_timings;
- String output_base = gen->output_base;
+ String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ debugf("Linking %.*s\n", LIT(output_filename));
+
+ // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
-
+
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
- "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
+ "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
- "wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
-#ifdef GB_SYSTEM_UNIX
+#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
@@ -178,28 +173,11 @@ i32 linker_stage(lbGenerator *gen) {
gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
- char const *output_ext = "exe";
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
defer (gb_string_free(link_settings));
-
- // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
- Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
-
- if (find_result.windows_sdk_version == 0) {
- gb_printf_err("Windows SDK not found.\n");
- exit(1);
- }
-
- if (build_context.ignore_microsoft_magic) {
- find_result = {};
- }
-
// Add library search paths.
- if (find_result.vs_library_path.len > 0) {
- GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
- GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
-
+ if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
String path = {};
auto add_path = [&](String path) {
if (path[path.len-1] == '\\') {
@@ -207,16 +185,16 @@ i32 linker_stage(lbGenerator *gen) {
}
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
};
- add_path(find_result.windows_sdk_um_library_path);
- add_path(find_result.windows_sdk_ucrt_library_path);
- add_path(find_result.vs_library_path);
+ add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
+ add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
+ add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
}
-
-
+
+
StringSet libs = {};
string_set_init(&libs, heap_allocator(), 64);
defer (string_set_destroy(&libs));
-
+
StringSet asm_files = {};
string_set_init(&asm_files, heap_allocator(), 64);
defer (string_set_destroy(&asm_files));
@@ -241,22 +219,22 @@ i32 linker_stage(lbGenerator *gen) {
string_set_add(&libs, lib);
}
}
-
+
for_array(i, libs.entries) {
String lib = libs.entries[i].value;
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
}
-
-
+
+
if (build_context.build_mode == BuildMode_DynamicLibrary) {
- output_ext = "dll";
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
}
if (build_context.pdb_filepath != "") {
- link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
+ String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
+ link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
}
if (build_context.no_crt) {
@@ -268,7 +246,7 @@ i32 linker_stage(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
}
-
+
for_array(i, asm_files.entries) {
String asm_file = asm_files.entries[i].value;
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
@@ -283,7 +261,7 @@ i32 linker_stage(lbGenerator *gen) {
LIT(obj_file),
LIT(build_context.extra_assembler_flags)
);
-
+
if (result) {
return result;
}
@@ -297,27 +275,35 @@ i32 linker_stage(lbGenerator *gen) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
+ String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
+ defer (gb_free(heap_allocator(), vs_exe_path.text));
+
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
if (build_context.has_resource) {
+ String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
+ String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
+ defer (gb_free(heap_allocator(), rc_path.text));
+ defer (gb_free(heap_allocator(), res_path.text));
+
result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
+ "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+ LIT(res_path),
+ LIT(rc_path)
);
-
+
if (result) {
return result;
}
result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
+ "\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
+ LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -326,13 +312,13 @@ i32 linker_stage(lbGenerator *gen) {
);
} else {
result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
+ "\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
+ LIT(vs_exe_path), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -340,27 +326,27 @@ i32 linker_stage(lbGenerator *gen) {
lib_str
);
}
-
+
if (result) {
return result;
}
-
+
} else { // lld
result = system_exec_command_line_app("msvc-lld-link",
- "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
+ "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
+ LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
-
+
if (result) {
return result;
}
@@ -384,7 +370,7 @@ i32 linker_stage(lbGenerator *gen) {
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
// This allows you to specify '-f' in a #foreign_system_library,
// without having to implement any new syntax specifically for MacOS.
- #if defined(GB_SYSTEM_OSX)
+ if (build_context.metrics.os == TargetOs_darwin) {
if (string_ends_with(lib, str_lit(".framework"))) {
// framework thingie
String lib_name = lib;
@@ -400,25 +386,25 @@ i32 linker_stage(lbGenerator *gen) {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
- #else
+ } else {
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
// since those are statically linked to at link time. shared libraries (.so) has to be
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
- if (string_ends_with(lib, str_lit(".a"))) {
- // static libs, absolute full path relative to the file in which the lib was imported from
+ if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
+ // static libs and object files, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
} else if (string_ends_with(lib, str_lit(".so"))) {
// dynamic lib, relative path to executable
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
- // at runtimeto the executable
+ // at runtime to the executable
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
- #endif
+ }
}
gbString object_files = gb_string_make(heap_allocator(), "");
@@ -428,80 +414,74 @@ i32 linker_stage(lbGenerator *gen) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
- // Unlike the Win32 linker code, the output_ext includes the dot, because
- // typically executable files on *NIX systems don't have extensions.
- String output_ext = {};
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
- char const *linker;
- if (build_context.build_mode == BuildMode_DynamicLibrary) {
- // NOTE(tetra, 2020-11-06): __$startup_runtime must be called at DLL load time.
- // Clang, for some reason, won't let us pass the '-init' flag that lets us do this,
- // so use ld instead.
- // :UseLDForShared
- linker = "ld";
- link_settings = gb_string_appendc(link_settings, "-init '__$startup_runtime' ");
- // Shared libraries are .dylib on MacOS and .so on Linux.
- #if defined(GB_SYSTEM_OSX)
- output_ext = STR_LIT(".dylib");
- link_settings = gb_string_appendc(link_settings, "-dylib -dynamic ");
- #else
- output_ext = STR_LIT(".so");
- link_settings = gb_string_appendc(link_settings, "-shared ");
- #endif
- } else {
- #if defined(GB_SYSTEM_OSX)
- linker = "ld";
- #else
- // TODO(zangent): Figure out how to make ld work on Linux.
- // It probably has to do with including the entire CRT, but
- // that's quite a complicated issue to solve while remaining distro-agnostic.
- // Clang can figure out linker flags for us, and that's good enough _for now_.
- linker = "clang -Wno-unused-command-line-argument";
- #endif
+
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
}
- if (build_context.metrics.os == TargetOs_linux) {
+ // NOTE(dweiler): We use clang as a frontend for the linker as there are
+ // other runtime and compiler support libraries that need to be linked in
+ // very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
+ // These are not always typically inside /lib, /lib64, or /usr versions
+ // of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
+ // the distribution of Linux even. The gcc or clang specs is the only
+ // reliable way to query this information to call ld directly.
+ if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // NOTE(dweiler): Let the frontend know we're building a shared library
+ // so it doesn't generate symbols which cannot be relocated.
+ link_settings = gb_string_appendc(link_settings, "-shared ");
+
+ // NOTE(dweiler): _odin_entry_point must be called at initialization
+ // time of the shared object, similarly, _odin_exit_point must be called
+ // at deinitialization. We can pass both -init and -fini to the linker by
+ // using a comma separated list of arguments to -Wl.
+ //
+ // This previously used ld but ld cannot actually build a shared library
+ // correctly this way since all the other dependencies provided implicitly
+ // by the compiler frontend are still needed and most of the command
+ // line arguments prepared previously are incompatible with ld.
+ link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
+ link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
+ } else if (build_context.metrics.os != TargetOs_openbsd) {
+ // OpenBSD defaults to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
}
-
- if (build_context.out_filepath.len > 0) {
- //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
- isize pos = string_extension_position(build_context.out_filepath);
- if (pos > 0) {
- output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
- }
+ gbString platform_lib_str = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(platform_lib_str));
+ if (build_context.metrics.os == TargetOs_darwin) {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
+ } else {
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
}
- result = system_exec_command_line_app("ld-link",
- "%s %s -o \"%.*s%.*s\" %s "
- " %s "
- " %.*s "
- " %.*s "
- " %s "
- #if defined(GB_SYSTEM_OSX)
- // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
- // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
- // make sure to also change the 'mtriple' param passed to 'opt'
- #if defined(GB_CPU_ARM)
- " -macosx_version_min 11.0.0 "
- #else
- " -macosx_version_min 10.8.0 "
- #endif
- // This points the linker to where the entry point is
- " -e _main "
- #endif
- , linker, object_files, LIT(output_base), LIT(output_ext),
- #if defined(GB_SYSTEM_OSX)
- "-lSystem -lm -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib",
- #else
- "-lc -lm",
- #endif
- lib_str,
- LIT(build_context.link_flags),
- LIT(build_context.extra_linker_flags),
- link_settings);
-
+ if (build_context.metrics.os == TargetOs_darwin) {
+ // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
+ // NOTE: If you change this (although this minimum is as low as you can go with Odin working)
+ // make sure to also change the 'mtriple' param passed to 'opt'
+ if (build_context.metrics.arch == TargetArch_arm64) {
+ link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
+ } else {
+ link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
+ }
+ // This points the linker to where the entry point is
+ link_settings = gb_string_appendc(link_settings, " -e _main ");
+ }
+
+ gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
+ defer (gb_string_free(link_command_line));
+
+ link_command_line = gb_string_appendc(link_command_line, object_files);
+ link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
+ link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
+ link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
+ link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
+
+ result = system_exec_command_line_app("ld-link", link_command_line);
+
if (result) {
return result;
}
@@ -510,10 +490,8 @@ i32 linker_stage(lbGenerator *gen) {
if (build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
- result = system_exec_command_line_app("dsymutil",
- "dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
- );
-
+ result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
+
if (result) {
return result;
}
@@ -571,54 +549,26 @@ void usage(String argv0) {
print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
print_usage_line(0, "Commands:");
- print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
- print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
- print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
- print_usage_line(1, "check parse and type check .odin file");
- print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
- print_usage_line(1, "doc generate documentation .odin file, or directory of .odin files");
- print_usage_line(1, "version print version");
- print_usage_line(1, "report print information useful to reporting a bug");
+ print_usage_line(1, "build compile directory of .odin files, as an executable.");
+ print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
+ print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(1, "check parse, and type check a directory of .odin files");
+ print_usage_line(1, "query parse, type check, and output a .json file containing information about the program");
+ print_usage_line(1, "strip-semicolon parse, type check, and remove unneeded semicolons from the entire program");
+ print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
+ print_usage_line(1, "doc generate documentation on a directory of .odin files");
+ print_usage_line(1, "version print version");
+ print_usage_line(1, "report print information useful to reporting a bug");
print_usage_line(0, "");
print_usage_line(0, "For further details on a command, use -help after the command name");
print_usage_line(1, "e.g. odin build -help");
}
-
-bool string_is_valid_identifier(String str) {
- if (str.len <= 0) return false;
-
- isize rune_count = 0;
-
- isize w = 0;
- isize offset = 0;
- while (offset < str.len) {
- Rune r = 0;
- w = utf8_decode(str.text, str.len, &r);
- if (r == GB_RUNE_INVALID) {
- return false;
- }
-
- if (rune_count == 0) {
- if (!rune_is_letter(r)) {
- return false;
- }
- } else {
- if (!rune_is_letter(r) && !rune_is_digit(r)) {
- return false;
- }
- }
- rune_count += 1;
- offset += w;
- }
-
- return true;
-}
-
enum BuildFlagKind {
BuildFlag_Invalid,
BuildFlag_Help,
+ BuildFlag_SingleFile,
BuildFlag_OutFile,
BuildFlag_OptimizationLevel,
@@ -654,6 +604,10 @@ enum BuildFlagKind {
BuildFlag_ExtraLinkerFlags,
BuildFlag_ExtraAssemblerFlags,
BuildFlag_Microarch,
+ BuildFlag_TargetFeatures,
+
+ BuildFlag_RelocMode,
+ BuildFlag_DisableRedZone,
BuildFlag_TestName,
@@ -662,6 +616,8 @@ enum BuildFlagKind {
BuildFlag_InsertSemicolon,
BuildFlag_StrictStyle,
BuildFlag_StrictStyleInitOnly,
+ BuildFlag_ForeignErrorProcedures,
+ BuildFlag_DisallowRTTI,
BuildFlag_Compact,
BuildFlag_GlobalDefinitions,
@@ -674,9 +630,9 @@ enum BuildFlagKind {
BuildFlag_IgnoreWarnings,
BuildFlag_WarningsAsErrors,
BuildFlag_VerboseErrors,
-
+
// internal use only
- BuildFlag_InternalIgnoreLazy,
+ BuildFlag_InternalIgnoreLazy,
#if defined(GB_SYSTEM_WINDOWS)
BuildFlag_IgnoreVsSearch,
@@ -771,69 +727,80 @@ ExactValue build_param_to_exact_value(String name, String param) {
bool parse_build_flags(Array args) {
auto build_flags = array_make(heap_allocator(), 0, BuildFlag_COUNT);
- add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
- add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
- add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
- add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
- add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
- add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build|Command_strip_semicolon);
- add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
- add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
- add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
- add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
- add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_UseSeparateModules,str_lit("use-separate-modules"),BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Help, str_lit("help"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_SingleFile, str_lit("file"), BuildFlagParam_None, Command__does_build | Command__does_check);
+ add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String, Command__does_build &~ Command_test);
+ add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("o"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_OptimizationMode, str_lit("O"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowMoreTimings, str_lit("show-more-timings"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportTimings, str_lit("export-timings"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExportTimingsFile, str_lit("export-timings-file"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowUnused, str_lit("show-unused"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowUnusedWithLocation, str_lit("show-unused-with-location"), BuildFlagParam_None, Command_check);
+ add_flag(&build_flags, BuildFlag_ShowSystemCalls, str_lit("show-system-calls"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer, Command_all);
+ add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None, Command__does_build | Command_strip_semicolon);
+ add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String, Command__does_check, true);
+ add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String, Command__does_build); // Commands_build is not used to allow for a better error message
+ add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String, Command__does_check);
+ add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DisableAssert, str_lit("disable-assert"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoDynamicLiterals, str_lit("no-dynamic-literals"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_NoEntryPoint, str_lit("no-entry-point"), BuildFlagParam_None, Command__does_check &~ Command_test);
+ add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ThreadedChecker, str_lit("threaded-checker"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_UseLLVMApi, str_lit("llvm-api"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Microarch, str_lit("microarch"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_TargetFeatures, str_lit("target-features"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
+ add_flag(&build_flags, BuildFlag_RelocMode, str_lit("reloc-mode"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_DisableRedZone, str_lit("disable-red-zone"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
- add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
- add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_TestName, str_lit("test-name"), BuildFlagParam_String, Command_test);
- add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc);
- add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_InsertSemicolon, str_lit("insert-semicolon"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
+ add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
- add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
- add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
-
- add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_DisallowRTTI, str_lit("disallow-rtti"), BuildFlagParam_None, Command__does_check);
+
+
+ add_flag(&build_flags, BuildFlag_Compact, str_lit("compact"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
+ add_flag(&build_flags, BuildFlag_GoToDefinitions, str_lit("go-to-definitions"), BuildFlagParam_None, Command_query);
+
+
+ add_flag(&build_flags, BuildFlag_Short, str_lit("short"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_AllPackages, str_lit("all-packages"), BuildFlagParam_None, Command_doc);
+ add_flag(&build_flags, BuildFlag_DocFormat, str_lit("doc-format"), BuildFlagParam_None, Command_doc);
+
+ add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+
+ add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
#if defined(GB_SYSTEM_WINDOWS)
- add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
- add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
- add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
+ add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String, Command__does_build);
+ add_flag(&build_flags, BuildFlag_Subsystem, str_lit("subsystem"), BuildFlagParam_String, Command__does_build);
#endif
@@ -1280,7 +1247,7 @@ bool parse_build_flags(Array args) {
} else {
gb_printf_err("Unknown build mode '%.*s'\n", LIT(str));
gb_printf_err("Valid build modes:\n");
- gb_printf_err("\tdll, shared\n");
+ gb_printf_err("\tdll, shared, dynamic\n");
gb_printf_err("\tobj, object\n");
gb_printf_err("\texe\n");
gb_printf_err("\tasm, assembly, assembler\n");
@@ -1355,7 +1322,7 @@ bool parse_build_flags(Array args) {
GB_ASSERT(value.kind == ExactValue_String);
build_context.extra_linker_flags = value.value_string;
break;
- case BuildFlag_ExtraAssemblerFlags:
+ case BuildFlag_ExtraAssemblerFlags:
GB_ASSERT(value.kind == ExactValue_String);
build_context.extra_assembler_flags = value.value_string;
break;
@@ -1365,6 +1332,37 @@ bool parse_build_flags(Array args) {
string_to_lower(&build_context.microarch);
break;
}
+ case BuildFlag_TargetFeatures: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ build_context.target_features = value.value_string;
+ string_to_lower(&build_context.target_features);
+ break;
+ }
+ case BuildFlag_RelocMode: {
+ GB_ASSERT(value.kind == ExactValue_String);
+ String v = value.value_string;
+ if (v == "default") {
+ build_context.reloc_mode = RelocMode_Default;
+ } else if (v == "static") {
+ build_context.reloc_mode = RelocMode_Static;
+ } else if (v == "pic") {
+ build_context.reloc_mode = RelocMode_PIC;
+ } else if (v == "dynamic-no-pic") {
+ build_context.reloc_mode = RelocMode_DynamicNoPIC;
+ } else {
+ gb_printf_err("-reloc-mode flag expected one of the following\n");
+ gb_printf_err("\tdefault\n");
+ gb_printf_err("\tstatic\n");
+ gb_printf_err("\tpic\n");
+ gb_printf_err("\tdynamic-no-pic\n");
+ bad_flags = true;
+ }
+
+ break;
+ }
+ case BuildFlag_DisableRedZone:
+ build_context.disable_red_zone = true;
+ break;
case BuildFlag_TestName: {
GB_ASSERT(value.kind == ExactValue_String);
{
@@ -1383,9 +1381,15 @@ bool parse_build_flags(Array args) {
case BuildFlag_DisallowDo:
build_context.disallow_do = true;
break;
+ case BuildFlag_DisallowRTTI:
+ build_context.disallow_rtti = true;
+ break;
case BuildFlag_DefaultToNilAllocator:
build_context.ODIN_DEFAULT_TO_NIL_ALLOCATOR = true;
break;
+ case BuildFlag_ForeignErrorProcedures:
+ build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
+ break;
case BuildFlag_InsertSemicolon: {
gb_printf_err("-insert-semicolon flag is not required any more\n");
bad_flags = true;
@@ -1486,6 +1490,10 @@ bool parse_build_flags(Array args) {
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
bad_flags = true;
break;
+ } else if (!gb_file_exists((const char *)path.text)) {
+ gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path));
+ bad_flags = true;
+ break;
}
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
build_context.has_resource = true;
@@ -1500,6 +1508,11 @@ bool parse_build_flags(Array args) {
String path = value.value_string;
path = string_trim_whitespace(path);
if (is_build_flag_path_valid(path)) {
+ if (path_is_directory(path)) {
+ gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
+ bad_flags = true;
+ break;
+ }
// #if defined(GB_SYSTEM_WINDOWS)
// String ext = path_extension(path);
// if (ext != ".pdb") {
@@ -1817,7 +1830,7 @@ void show_timings(Checker *c, Timings *t) {
void remove_temp_files(lbGenerator *gen) {
if (build_context.keep_temp_files) return;
-
+
TIME_SECTION("remove keep temp files");
for_array(i, gen->output_temp_paths) {
@@ -1841,32 +1854,47 @@ void remove_temp_files(lbGenerator *gen) {
void print_show_help(String const arg0, String const &command) {
print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(arg0));
- print_usage_line(0, "Usage");
+ print_usage_line(0, "Usage:");
print_usage_line(1, "%.*s %.*s [arguments]", LIT(arg0), LIT(command));
print_usage_line(0, "");
if (command == "build") {
- print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable.");
- print_usage_line(1, " one must contain the program's entry point, all must be in the same package.");
- } else if (command == "run") {
- print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable.");
- } else if (command == "check") {
- print_usage_line(1, "check parse and type check .odin file(s)");
- } else if (command == "test") {
- print_usage_line(1, "test build ands runs procedures with the attribute @(test) in the initial package");
- } else if (command == "query") {
- print_usage_line(1, "query [experimental] parse, type check, and output a .json file containing information about the program");
- } else if (command == "doc") {
- print_usage_line(1, "doc generate documentation from a .odin file, or directory of .odin files");
+ print_usage_line(1, "build Compile directory of .odin files as an executable.");
+ print_usage_line(2, "One must contain the program's entry point, all must be in the same package.");
+ print_usage_line(2, "Use `-file` to build a single file instead.");
print_usage_line(2, "Examples:");
- print_usage_line(3, "odin doc core/path");
- print_usage_line(3, "odin doc core/path core/path/filepath");
+ print_usage_line(3, "odin build . # Build package in current directory");
+ print_usage_line(3, "odin build # Build package in ");
+ print_usage_line(3, "odin build filename.odin -file # Build single-file package, must contain entry point.");
+ } else if (command == "run") {
+ print_usage_line(1, "run Same as 'build', but also then runs the newly compiled executable.");
+ print_usage_line(2, "Append an empty flag and then the args, '-- ', to specify args for the output.");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin run . # Build and run package in current directory");
+ print_usage_line(3, "odin run # Build and run package in ");
+ print_usage_line(3, "odin run filename.odin -file # Build and run single-file package, must contain entry point.");
+ } else if (command == "check") {
+ print_usage_line(1, "check Parse and type check directory of .odin files");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin check . # Type check package in current directory");
+ print_usage_line(3, "odin check # Type check package in ");
+ print_usage_line(3, "odin check filename.odin -file # Type check single-file package, must contain entry point.");
+ } else if (command == "test") {
+ print_usage_line(1, "test Build ands runs procedures with the attribute @(test) in the initial package");
+ } else if (command == "query") {
+ print_usage_line(1, "query [experimental] Parse, type check, and output a .json file containing information about the program");
+ } else if (command == "doc") {
+ print_usage_line(1, "doc generate documentation from a directory of .odin files");
+ print_usage_line(2, "Examples:");
+ print_usage_line(3, "odin doc . # Generate documentation on package in current directory");
+ print_usage_line(3, "odin doc # Generate documentation on package in ");
+ print_usage_line(3, "odin doc filename.odin -file # Generate documentation on single-file package.");
} else if (command == "version") {
print_usage_line(1, "version print version");
} else if (command == "strip-semicolon") {
print_usage_line(1, "strip-semicolon");
- print_usage_line(2, "parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
- }
+ print_usage_line(2, "Parse and type check .odin file(s) and then remove unneeded semicolons from the entire project");
+ }
bool doc = command == "doc";
bool build = command == "build";
@@ -1880,6 +1908,13 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "Flags");
print_usage_line(0, "");
+ if (check) {
+ print_usage_line(1, "-file");
+ print_usage_line(2, "Tells `%.*s %.*s` to treat the given file as a self-contained package.", LIT(arg0), LIT(command));
+ print_usage_line(2, "This means that `/a.odin` won't have access to `/b.odin`'s contents.");
+ print_usage_line(0, "");
+ }
+
if (doc) {
print_usage_line(1, "-short");
print_usage_line(2, "Show shortened documentation for the packages");
@@ -1970,6 +2005,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-define:=");
print_usage_line(2, "Defines a global constant with a value");
print_usage_line(2, "Example: -define:SPAM=123");
+ print_usage_line(2, "To use: #config(SPAM, default_value)");
print_usage_line(0, "");
}
@@ -2071,7 +2107,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-extra-linker-flags:");
print_usage_line(2, "Adds extra linker specific flags in a string");
print_usage_line(0, "");
-
+
print_usage_line(1, "-extra-assembler-flags:");
print_usage_line(2, "Adds extra assembler specific flags in a string");
print_usage_line(0, "");
@@ -2083,6 +2119,18 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(3, "-microarch:sandybridge");
print_usage_line(3, "-microarch:native");
print_usage_line(0, "");
+
+ print_usage_line(1, "-reloc-mode:");
+ print_usage_line(2, "Specifies the reloc mode");
+ print_usage_line(2, "Options:");
+ print_usage_line(3, "default");
+ print_usage_line(3, "static");
+ print_usage_line(3, "pic");
+ print_usage_line(3, "dynamic-no-pic");
+ print_usage_line(0, "");
+
+ print_usage_line(1, "-disable-red-zone");
+ print_usage_line(2, "Disable red zone on a supported freestanding target");
}
if (check) {
@@ -2097,7 +2145,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-strict-style");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons");
print_usage_line(0, "");
-
+
print_usage_line(1, "-strict-style-init-only");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons, only on the initial project");
print_usage_line(0, "");
@@ -2113,6 +2161,11 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-verbose-errors");
print_usage_line(2, "Prints verbose error messages showing the code on that line and the location in that line");
print_usage_line(0, "");
+
+ print_usage_line(1, "-foreign-error-procedures");
+ print_usage_line(2, "States that the error procedues used in the runtime are defined in a separate translation unit");
+ print_usage_line(0, "");
+
}
if (run_or_build) {
@@ -2262,7 +2315,7 @@ gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *writt
}
written += to_write;
prev_offset = token_pos_end(*token).offset;
- }
+ }
if (token->flags & TokenFlag_Replace) {
if (token->kind == Token_Ellipsis) {
if (!gb_file_write(f, "..=", 3)) {
@@ -2281,7 +2334,7 @@ gbFileError write_file_with_stripped_tokens(gbFile *f, AstFile *file, i64 *writt
}
written += to_write;
}
-
+
if (written_) *written_ = written;
return err;
}
@@ -2292,14 +2345,14 @@ int strip_semicolons(Parser *parser) {
AstPackage *pkg = parser->packages[i];
file_count += pkg->files.count;
}
-
+
auto generated_files = array_make(permanent_allocator(), 0, file_count);
-
+
for_array(i, parser->packages) {
AstPackage *pkg = parser->packages[i];
for_array(j, pkg->files) {
AstFile *file = pkg->files[j];
-
+
bool nothing_to_change = true;
for_array(i, file->tokens) {
Token *token = &file->tokens[i];
@@ -2308,29 +2361,29 @@ int strip_semicolons(Parser *parser) {
break;
}
}
-
+
if (nothing_to_change) {
continue;
}
-
+
String old_fullpath = copy_string(permanent_allocator(), file->fullpath);
-
+
// assumes .odin extension
String fullpath_base = substring(old_fullpath, 0, old_fullpath.len-5);
-
+
String old_fullpath_backup = concatenate_strings(permanent_allocator(), fullpath_base, str_lit("~backup.odin-temp"));
String new_fullpath = concatenate_strings(permanent_allocator(), fullpath_base, str_lit("~temp.odin-temp"));
-
+
array_add(&generated_files, StripSemicolonFile{old_fullpath, old_fullpath_backup, new_fullpath, file});
}
}
-
+
gb_printf_err("File count to be stripped of unneeded tokens: %td\n", generated_files.count);
-
-
+
+
isize generated_count = 0;
bool failed = false;
-
+
for_array(i, generated_files) {
auto *file = &generated_files[i];
char const *filename = cast(char const *)file->new_fullpath.text;
@@ -2338,15 +2391,15 @@ int strip_semicolons(Parser *parser) {
defer (if (err != gbFileError_None) {
failed = true;
});
-
- gbFile f = {};
+
+ gbFile f = {};
err = gb_file_create(&f, filename);
if (err) {
break;
}
defer (err = gb_file_close(&f));
generated_count += 1;
-
+
i64 written = 0;
defer (err = gb_file_truncate(&f, written));
@@ -2367,23 +2420,23 @@ int strip_semicolons(Parser *parser) {
}
return 1;
}
-
+
isize overwritten_files = 0;
-
+
for_array(i, generated_files) {
auto *file = &generated_files[i];
-
+
char const *old_fullpath = cast(char const *)file->old_fullpath.text;
char const *old_fullpath_backup = cast(char const *)file->old_fullpath_backup.text;
char const *new_fullpath = cast(char const *)file->new_fullpath.text;
-
+
debugf("Copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup);
if (!gb_file_copy(old_fullpath, old_fullpath_backup, false)) {
gb_printf_err("failed to copy '%s' to '%s'\n", old_fullpath, old_fullpath_backup);
failed = true;
break;
}
-
+
debugf("Copy '%s' to '%s'\n", new_fullpath, old_fullpath);
if (!gb_file_copy(new_fullpath, old_fullpath, false)) {
@@ -2400,19 +2453,19 @@ int strip_semicolons(Parser *parser) {
if (!gb_file_remove(old_fullpath_backup)) {
gb_printf_err("failed to remove '%s'\n", old_fullpath_backup);
}
-
+
overwritten_files++;
}
-
+
if (!build_context.keep_temp_files) {
for_array(i, generated_files) {
auto *file = &generated_files[i];
char const *filename = nullptr;
filename = cast(char const *)file->new_fullpath.text;
-
+
debugf("Remove '%s'\n", filename);
GB_ASSERT_MSG(gb_file_remove(filename), "unable to delete file %s", filename);
-
+
filename = cast(char const *)file->old_fullpath_backup.text;
debugf("Remove '%s'\n", filename);
if (gb_file_exists(filename) && !gb_file_remove(filename)) {
@@ -2423,15 +2476,13 @@ int strip_semicolons(Parser *parser) {
}
}
}
-
+
gb_printf_err("Files stripped of unneeded token: %td\n", generated_files.count);
-
-
+
+
return cast(int)failed;
}
-
-
int main(int arg_count, char const **arg_ptr) {
if (arg_count < 2) {
usage(make_string_c(arg_ptr[0]));
@@ -2442,10 +2493,11 @@ int main(int arg_count, char const **arg_ptr) {
defer (timings_destroy(&global_timings));
MAIN_TIME_SECTION("initialization");
-
+
virtual_memory_init();
mutex_init(&fullpath_mutex);
mutex_init(&hash_exact_value_mutex);
+ mutex_init(&global_type_name_objc_metadata_mutex);
init_string_buffer_memory();
init_string_interner();
@@ -2471,6 +2523,7 @@ int main(int arg_count, char const **arg_ptr) {
String command = args[1];
String init_filename = {};
String run_args_string = {};
+ isize last_non_run_arg = args.count;
bool run_output = false;
if (command == "run" || command == "test") {
@@ -2486,7 +2539,6 @@ int main(int arg_count, char const **arg_ptr) {
Array run_args = array_make(heap_allocator(), 0, arg_count);
defer (array_free(&run_args));
- isize last_non_run_arg = args.count;
for_array(i, args) {
if (args[i] == "--") {
last_non_run_arg = i;
@@ -2499,6 +2551,7 @@ int main(int arg_count, char const **arg_ptr) {
args = array_slice(args, 0, last_non_run_arg);
run_args_string = string_join_and_quote(heap_allocator(), run_args);
+
init_filename = args[2];
run_output = true;
@@ -2586,11 +2639,40 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
+ init_filename = copy_string(permanent_allocator(), init_filename);
+
if (init_filename == "-help" ||
init_filename == "--help") {
build_context.show_help = true;
}
+ if (init_filename.len > 0 && !build_context.show_help) {
+ // The command must be build, run, test, check, or another that takes a directory or filename.
+ if (!path_is_directory(init_filename)) {
+ // Input package is a filename. We allow this only if `-file` was given, otherwise we exit with an error message.
+ bool single_file_package = false;
+ for_array(i, args) {
+ if (i >= 3 && i <= last_non_run_arg && args[i] == "-file") {
+ single_file_package = true;
+ break;
+ }
+ }
+
+ if (!single_file_package) {
+ gb_printf_err("ERROR: `%.*s %.*s` takes a package as its first argument.\n", LIT(args[0]), LIT(command));
+ gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
+ gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
+ return 1;
+ } else {
+ String const ext = str_lit(".odin");
+ if (!string_ends_with(init_filename, ext)) {
+ gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+ return 1;
+ }
+ }
+ }
+ }
+
build_context.command = command;
if (!parse_build_flags(args)) {
@@ -2605,19 +2687,30 @@ int main(int arg_count, char const **arg_ptr) {
// NOTE(bill): add 'shared' directory if it is not already set
if (!find_library_collection_path(str_lit("shared"), nullptr)) {
add_library_collection(str_lit("shared"),
- get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
+ get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
}
-
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
// return 1;
// }
+ // Set and check build paths...
+ if (!init_build_paths(init_filename)) {
+ return 1;
+ }
+
+ if (build_context.show_debug_messages) {
+ for_array(i, build_context.build_paths) {
+ String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
+ debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
+ }
+ }
+
init_global_thread_pool();
defer (thread_pool_destroy(&global_thread_pool));
-
+
init_universal();
// TODO(bill): prevent compiling without a linker
@@ -2631,6 +2724,8 @@ int main(int arg_count, char const **arg_ptr) {
}
defer (destroy_parser(parser));
+ // TODO(jeroen): Remove the `init_filename` param.
+ // Let's put that on `build_context.build_paths[0]` instead.
if (parse_packages(parser, init_filename) != ParseFile_None) {
return 1;
}
@@ -2649,7 +2744,7 @@ int main(int arg_count, char const **arg_ptr) {
if (any_errors()) {
return 1;
}
-
+
if (build_context.command_kind == Command_strip_semicolon) {
return strip_semicolons(parser);
}
@@ -2703,22 +2798,20 @@ int main(int arg_count, char const **arg_ptr) {
}
remove_temp_files(gen);
-
+
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
if (run_output) {
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+
#if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
+ return system_exec_command_line_app("odin run", "%.*s %.*s", LIT(exe_name), LIT(run_args_string));
#else
- //NOTE(thebirk): This whole thing is a little leaky
- String output_ext = {};
- String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext);
- complete_path = path_to_full_path(permanent_allocator(), complete_path);
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
+ return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
#endif
}
-
return 0;
}
diff --git a/src/microsoft_craziness.h b/src/microsoft_craziness.h
index 02f14dda3..b4f815284 100644
--- a/src/microsoft_craziness.h
+++ b/src/microsoft_craziness.h
@@ -460,7 +460,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
wchar_t *library_path = nullptr;
if (build_context.metrics.arch == TargetArch_amd64) {
library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
library_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\lib\\x86\\");
} else {
continue;
@@ -472,7 +472,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
wchar_t *link_exe_path = nullptr;
if (build_context.metrics.arch == TargetArch_amd64) {
link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx64\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
link_exe_path = concat(bstr_inst_path, L"\\VC\\Tools\\MSVC\\", version, L"\\bin\\Hostx86\\x86\\");
} else {
continue;
@@ -529,7 +529,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
if (build_context.metrics.arch == TargetArch_amd64) {
lib_path = concat(buffer, L"VC\\Lib\\amd64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
lib_path = concat(buffer, L"VC\\Lib\\");
} else {
continue;
@@ -542,7 +542,7 @@ bool find_visual_studio_by_fighting_through_microsoft_craziness(Find_Result *res
if (os_file_exists(vcruntime_filename)) {
if (build_context.metrics.arch == TargetArch_amd64) {
result->vs_exe_path = concat(buffer, L"VC\\bin\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
// result->vs_exe_path = concat(buffer, L"VC\\bin\\amd64_x86\\");
result->vs_exe_path = concat(buffer, L"VC\\bin\\x86_amd64\\");
} else {
@@ -573,7 +573,7 @@ Find_Result find_visual_studio_and_windows_sdk() {
if (build_context.metrics.arch == TargetArch_amd64) {
result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x64\\");
result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x64\\");
- } else if (build_context.metrics.arch == TargetArch_386) {
+ } else if (build_context.metrics.arch == TargetArch_i386) {
result.windows_sdk_um_library_path = concat(result.windows_sdk_root, L"um\\x86\\");
result.windows_sdk_ucrt_library_path = concat(result.windows_sdk_root, L"ucrt\\x86\\");
}
diff --git a/src/parser.cpp b/src/parser.cpp
index cbd4d61d5..df7f908a6 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -57,6 +57,9 @@ isize ast_node_size(AstKind kind) {
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
}
+
+gb_global std::atomic global_total_node_memory_allocated;
+
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
Ast *alloc_ast_node(AstFile *f, AstKind kind) {
gbAllocator a = ast_allocator(f);
@@ -66,6 +69,9 @@ Ast *alloc_ast_node(AstFile *f, AstKind kind) {
Ast *node = cast(Ast *)gb_alloc(a, size);
node->kind = kind;
node->file_id = f ? f->id : 0;
+
+ global_total_node_memory_allocated += size;
+
return node;
}
@@ -183,6 +189,11 @@ Ast *clone_ast(Ast *node) {
n->FieldValue.value = clone_ast(n->FieldValue.value);
break;
+ case Ast_EnumFieldValue:
+ n->EnumFieldValue.name = clone_ast(n->EnumFieldValue.name);
+ n->EnumFieldValue.value = clone_ast(n->EnumFieldValue.value);
+ break;
+
case Ast_TernaryIfExpr:
n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x);
n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond);
@@ -693,6 +704,16 @@ Ast *ast_field_value(AstFile *f, Ast *field, Ast *value, Token eq) {
return result;
}
+
+Ast *ast_enum_field_value(AstFile *f, Ast *name, Ast *value, CommentGroup *docs, CommentGroup *comment) {
+ Ast *result = alloc_ast_node(f, Ast_EnumFieldValue);
+ result->EnumFieldValue.name = name;
+ result->EnumFieldValue.value = value;
+ result->EnumFieldValue.docs = docs;
+ result->EnumFieldValue.comment = comment;
+ return result;
+}
+
Ast *ast_compound_lit(AstFile *f, Ast *type, Array const &elems, Token open, Token close) {
Ast *result = alloc_ast_node(f, Ast_CompoundLit);
result->CompoundLit.type = type;
@@ -944,7 +965,7 @@ Ast *ast_field(AstFile *f, Array const &names, Ast *type, Ast *default_va
result->Field.default_value = default_value;
result->Field.flags = flags;
result->Field.tag = tag;
- result->Field.docs = docs;
+ result->Field.docs = docs;
result->Field.comment = comment;
return result;
}
@@ -1050,15 +1071,14 @@ Ast *ast_struct_type(AstFile *f, Token token, Slice fields, isize field_c
}
-Ast *ast_union_type(AstFile *f, Token token, Array const &variants, Ast *polymorphic_params, Ast *align, bool no_nil, bool maybe,
+Ast *ast_union_type(AstFile *f, Token token, Array | |