aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-27 14:37:15 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-27 14:37:15 +0200
commitc4e0d1efa1ec655bae9134b95a0fcd060cc7bbea (patch)
treec29bd0b78138e8d67aebe34ac689d13e32d9d15f /core
parent6e61abc7d06f22129f93110a9f652c3eec21f0c6 (diff)
parent9349dfba8fec53f52f77a0c8928e115ec93ff447 (diff)
Merge branch 'master' into xml
Diffstat (limited to 'core')
-rw-r--r--core/bufio/scanner.odin9
-rw-r--r--core/builtin/builtin.odin2
-rw-r--r--core/bytes/bytes.odin51
-rw-r--r--core/c/c.odin10
-rw-r--r--core/c/frontend/preprocessor/preprocess.odin2
-rw-r--r--core/c/libc/complex.odin4
-rw-r--r--core/c/libc/ctype.odin4
-rw-r--r--core/c/libc/errno.odin23
-rw-r--r--core/c/libc/math.odin4
-rw-r--r--core/c/libc/setjmp.odin6
-rw-r--r--core/c/libc/signal.odin10
-rw-r--r--core/c/libc/stdatomic.odin257
-rw-r--r--core/c/libc/stdio.odin62
-rw-r--r--core/c/libc/stdlib.odin10
-rw-r--r--core/c/libc/string.odin4
-rw-r--r--core/c/libc/threads.odin6
-rw-r--r--core/c/libc/time.odin15
-rw-r--r--core/c/libc/types.odin2
-rw-r--r--core/c/libc/uchar.odin4
-rw-r--r--core/c/libc/wchar.odin4
-rw-r--r--core/c/libc/wctype.odin21
-rw-r--r--core/compress/common.odin22
-rw-r--r--core/compress/gzip/gzip.odin7
-rw-r--r--core/compress/shoco/model.odin148
-rw-r--r--core/compress/shoco/shoco.odin318
-rw-r--r--core/compress/zlib/zlib.odin83
-rw-r--r--core/container/array.odin216
-rw-r--r--core/container/bit_array/bit_array.odin239
-rw-r--r--core/container/bit_array/doc.odin53
-rw-r--r--core/container/bloom_filter.odin80
-rw-r--r--core/container/lru/lru_cache.odin201
-rw-r--r--core/container/map.odin377
-rw-r--r--core/container/priority_queue.odin121
-rw-r--r--core/container/priority_queue/priority_queue.odin143
-rw-r--r--core/container/queue.odin175
-rw-r--r--core/container/queue/queue.odin209
-rw-r--r--core/container/ring.odin74
-rw-r--r--core/container/set.odin240
-rw-r--r--core/container/small_array.odin95
-rw-r--r--core/container/small_array/small_array.odin117
-rw-r--r--core/container/topological_sort/topological_sort.odin98
-rw-r--r--core/crypto/README.md8
-rw-r--r--core/crypto/_fiat/field_poly1305/field.odin2
-rw-r--r--core/crypto/_sha3/_sha3.odin4
-rw-r--r--core/crypto/blake/blake.odin145
-rw-r--r--core/crypto/blake2b/blake2b.odin40
-rw-r--r--core/crypto/blake2s/blake2s.odin40
-rw-r--r--core/crypto/chacha20/chacha20.odin2
-rw-r--r--core/crypto/gost/gost.odin36
-rw-r--r--core/crypto/groestl/groestl.odin145
-rw-r--r--core/crypto/haval/haval.odin566
-rw-r--r--core/crypto/jh/jh.odin145
-rw-r--r--core/crypto/keccak/keccak.odin167
-rw-r--r--core/crypto/md2/md2.odin64
-rw-r--r--core/crypto/md4/md4.odin40
-rw-r--r--core/crypto/md5/md5.odin40
-rw-r--r--core/crypto/rand_generic.odin2
-rw-r--r--core/crypto/rand_openbsd.odin12
-rw-r--r--core/crypto/rand_windows.odin23
-rw-r--r--core/crypto/ripemd/ripemd.odin141
-rw-r--r--core/crypto/sha1/sha1.odin37
-rw-r--r--core/crypto/sha2/sha2.odin145
-rw-r--r--core/crypto/sha3/sha3.odin161
-rw-r--r--core/crypto/shake/shake.odin85
-rw-r--r--core/crypto/siphash/siphash.odin335
-rw-r--r--core/crypto/sm3/sm3.odin43
-rw-r--r--core/crypto/streebog/streebog.odin72
-rw-r--r--core/crypto/tiger/tiger.odin109
-rw-r--r--core/crypto/tiger2/tiger2.odin109
-rw-r--r--core/crypto/whirlpool/whirlpool.odin36
-rw-r--r--core/dynlib/lib_unix.odin2
-rw-r--r--core/encoding/hxa/doc.odin12
-rw-r--r--core/encoding/hxa/read.odin29
-rw-r--r--core/encoding/hxa/write.odin6
-rw-r--r--core/encoding/json/marshal.odin11
-rw-r--r--core/encoding/json/parser.odin6
-rw-r--r--core/encoding/json/unmarshal.odin50
-rw-r--r--core/encoding/varint/doc.odin28
-rw-r--r--core/encoding/varint/leb128.odin165
-rw-r--r--core/fmt/doc.odin2
-rw-r--r--core/fmt/fmt.odin475
-rw-r--r--core/fmt/fmt_js.odin7
-rw-r--r--core/fmt/fmt_os.odin10
-rw-r--r--core/hash/hash.odin19
-rw-r--r--core/hash/xxhash/streaming.odin2
-rw-r--r--core/image/common.odin849
-rw-r--r--core/image/png/example.odin4
-rw-r--r--core/image/png/helpers.odin21
-rw-r--r--core/image/png/png.odin76
-rw-r--r--core/image/qoi/qoi.odin408
-rw-r--r--core/image/tga/tga.odin103
-rw-r--r--core/intrinsics/intrinsics.odin117
-rw-r--r--core/io/io.odin41
-rw-r--r--core/math/big/api.odin6
-rw-r--r--core/math/big/common.odin32
-rw-r--r--core/math/big/doc.odin6
-rw-r--r--core/math/big/helpers.odin6
-rw-r--r--core/math/big/internal.odin176
-rw-r--r--core/math/big/logical.odin35
-rw-r--r--core/math/big/prime.odin2
-rw-r--r--core/math/big/private.odin120
-rw-r--r--core/math/big/public.odin2
-rw-r--r--core/math/big/radix.odin2
-rw-r--r--core/math/big/rat.odin16
-rw-r--r--core/math/big/tune.odin3
-rw-r--r--core/math/bits/bits.odin32
-rw-r--r--core/math/ease/ease.odin483
-rw-r--r--core/math/linalg/extended.odin4
-rw-r--r--core/math/linalg/general.odin81
-rw-r--r--core/math/linalg/glsl/linalg_glsl.odin56
-rw-r--r--core/math/linalg/glsl/linalg_glsl_math.odin2
-rw-r--r--core/math/linalg/hlsl/linalg_hlsl.odin112
-rw-r--r--core/math/linalg/hlsl/linalg_hlsl_math.odin5
-rw-r--r--core/math/linalg/specific.odin1069
-rw-r--r--core/math/linalg/specific_euler_angles_f16.odin1042
-rw-r--r--core/math/linalg/specific_euler_angles_f32.odin1042
-rw-r--r--core/math/linalg/specific_euler_angles_f64.odin1042
-rw-r--r--core/math/linalg/swizzle.odin5
-rw-r--r--core/math/math.odin34
-rw-r--r--core/math/noise/internal.odin734
-rw-r--r--core/math/noise/opensimplex2.odin171
-rw-r--r--core/math/rand/exp.odin214
-rw-r--r--core/math/rand/normal.odin14
-rw-r--r--core/math/rand/rand.odin16
-rw-r--r--core/mem/doc.odin34
-rw-r--r--core/mem/mem.odin15
-rw-r--r--core/mem/virtual/virtual.odin54
-rw-r--r--core/mem/virtual/virtual_linux.odin10
-rw-r--r--core/mem/virtual/virtual_platform.odin28
-rw-r--r--core/mem/virtual/virtual_windows.odin10
-rw-r--r--core/odin/ast/ast.odin187
-rw-r--r--core/odin/ast/clone.odin200
-rw-r--r--core/odin/ast/walk.odin159
-rw-r--r--core/odin/doc-format/doc_format.odin22
-rw-r--r--core/odin/parser/parser.odin246
-rw-r--r--core/odin/printer/printer.odin2
-rw-r--r--core/odin/printer/visit.odin217
-rw-r--r--core/os/dir_freebsd.odin70
-rw-r--r--core/os/dir_openbsd.odin71
-rw-r--r--core/os/dir_windows.odin1
-rw-r--r--core/os/file_windows.odin32
-rw-r--r--core/os/os.odin14
-rw-r--r--core/os/os2/process.odin2
-rw-r--r--core/os/os2/user.odin16
-rw-r--r--core/os/os_darwin.odin134
-rw-r--r--core/os/os_freebsd.odin839
-rw-r--r--core/os/os_linux.odin356
-rw-r--r--core/os/os_openbsd.odin707
-rw-r--r--core/os/os_wasi.odin2
-rw-r--r--core/os/os_windows.odin2
-rw-r--r--core/os/stat.odin1
-rw-r--r--core/os/stat_unix.odin4
-rw-r--r--core/os/stat_windows.odin55
-rw-r--r--core/os/stream.odin6
-rw-r--r--core/path/filepath/match.odin27
-rw-r--r--core/path/filepath/path.odin154
-rw-r--r--core/path/filepath/path_unix.odin11
-rw-r--r--core/path/filepath/walk.odin2
-rw-r--r--core/path/path_error.odin5
-rw-r--r--core/path/slashpath/match.odin (renamed from core/path/match.odin)2
-rw-r--r--core/path/slashpath/path.odin (renamed from core/path/path.odin)4
-rw-r--r--core/reflect/reflect.odin18
-rw-r--r--core/reflect/types.odin11
-rw-r--r--core/runtime/core.odin92
-rw-r--r--core/runtime/core_builtin.odin45
-rw-r--r--core/runtime/core_builtin_matrix.odin24
-rw-r--r--core/runtime/default_allocators_nil.odin2
-rw-r--r--core/runtime/default_temporary_allocator.odin2
-rw-r--r--core/runtime/entry_unix.odin33
-rw-r--r--core/runtime/entry_wasm.odin19
-rw-r--r--core/runtime/entry_windows.odin45
-rw-r--r--core/runtime/error_checks.odin160
-rw-r--r--core/runtime/internal.odin22
-rw-r--r--core/runtime/print.odin39
-rw-r--r--core/runtime/procs.odin4
-rw-r--r--core/runtime/procs_darwin.odin21
-rw-r--r--core/runtime/procs_wasm32.odin41
-rw-r--r--core/runtime/procs_windows_amd64.odin2
-rw-r--r--core/runtime/procs_windows_i386.odin (renamed from core/runtime/procs_windows_386.odin)0
-rw-r--r--core/runtime/udivmod128.odin2
-rw-r--r--core/slice/slice.odin170
-rw-r--r--core/slice/sort.odin519
-rw-r--r--core/slice/sort_private.odin200
-rw-r--r--core/sort/map.odin36
-rw-r--r--core/strconv/strconv.odin5
-rw-r--r--core/strings/ascii_set.odin2
-rw-r--r--core/strings/builder.odin130
-rw-r--r--core/strings/conversion.odin54
-rw-r--r--core/strings/intern.odin12
-rw-r--r--core/strings/reader.odin36
-rw-r--r--core/strings/strings.odin908
-rw-r--r--core/sync/atomic.odin205
-rw-r--r--core/sync/barrier.odin81
-rw-r--r--core/sync/channel.odin889
-rw-r--r--core/sync/channel_unix.odin16
-rw-r--r--core/sync/channel_windows.odin33
-rw-r--r--core/sync/extended.odin (renamed from core/sync/sync2/extended.odin)103
-rw-r--r--core/sync/futex_darwin.odin (renamed from core/sync/sync2/futex_darwin.odin)2
-rw-r--r--core/sync/futex_freebsd.odin75
-rw-r--r--core/sync/futex_linux.odin (renamed from core/sync/sync2/futex_linux.odin)8
-rw-r--r--core/sync/futex_openbsd.odin78
-rw-r--r--core/sync/futex_windows.odin (renamed from core/sync/sync2/futex_windows.odin)2
-rw-r--r--core/sync/primitives.odin (renamed from core/sync/sync2/primitives.odin)52
-rw-r--r--core/sync/primitives_atomic.odin (renamed from core/sync/sync2/primitives_atomic.odin)76
-rw-r--r--core/sync/primitives_darwin.odin (renamed from core/sync/sync2/primitives_darwin.odin)2
-rw-r--r--core/sync/primitives_freebsd.odin46
-rw-r--r--core/sync/primitives_internal.odin125
-rw-r--r--core/sync/primitives_linux.odin47
-rw-r--r--core/sync/primitives_openbsd.odin46
-rw-r--r--core/sync/primitives_windows.odin (renamed from core/sync/sync2/primitives_windows.odin)2
-rw-r--r--core/sync/sema_internal.odin (renamed from core/sync/sync2/sema_internal.odin)13
-rw-r--r--core/sync/sync.odin123
-rw-r--r--core/sync/sync2/atomic.odin79
-rw-r--r--core/sync/sync2/primitives_internal.odin184
-rw-r--r--core/sync/sync2/primitives_linux.odin9
-rw-r--r--core/sync/sync2/primitives_pthreads.odin58
-rw-r--r--core/sync/sync_darwin.odin54
-rw-r--r--core/sync/sync_freebsd.odin40
-rw-r--r--core/sync/sync_linux.odin36
-rw-r--r--core/sync/sync_unix.odin248
-rw-r--r--core/sync/sync_util.odin (renamed from core/sync/sync2/sync_util.odin)30
-rw-r--r--core/sync/sync_windows.odin180
-rw-r--r--core/sync/wait_group.odin58
-rw-r--r--core/sys/cpu/cpu_x86.odin2
-rw-r--r--core/sys/darwin/xnu_system_call_helpers.odin168
-rw-r--r--core/sys/darwin/xnu_system_call_numbers.odin558
-rw-r--r--core/sys/darwin/xnu_system_call_wrappers.odin419
-rw-r--r--core/sys/unix/pthread_freebsd.odin90
-rw-r--r--core/sys/unix/pthread_openbsd.odin65
-rw-r--r--core/sys/unix/pthread_unix.odin2
-rw-r--r--core/sys/unix/syscalls_linux.odin1497
-rw-r--r--core/sys/win32/user32.odin5
-rw-r--r--core/sys/windows/dwmapi.odin9
-rw-r--r--core/sys/windows/gdi32.odin66
-rw-r--r--core/sys/windows/kernel32.odin32
-rw-r--r--core/sys/windows/key_codes.odin252
-rw-r--r--core/sys/windows/types.odin952
-rw-r--r--core/sys/windows/user32.odin258
-rw-r--r--core/sys/windows/util.odin20
-rw-r--r--core/sys/windows/window_messages.odin1048
-rw-r--r--core/sys/windows/winmm.odin10
-rw-r--r--core/testing/runner_other.odin2
-rw-r--r--core/testing/runner_windows.odin4
-rw-r--r--core/thread/thread_pool.odin228
-rw-r--r--core/thread/thread_unix.odin121
-rw-r--r--core/thread/thread_windows.odin43
-rw-r--r--core/time/time.odin38
-rw-r--r--core/time/time_unix.odin50
-rw-r--r--core/time/time_windows.odin4
-rw-r--r--core/unicode/utf16/utf16.odin4
-rw-r--r--core/unicode/utf8/utf8string/string.odin120
251 files changed, 21253 insertions, 9456 deletions
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 <nom@duclavier.com>.
+ 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..<HUFFMAN_MAX_BITS {
+ for i in 1 ..= HUFFMAN_MAX_BITS {
next_code[i] = code
z.firstcode[i] = u16(code)
z.firstsymbol[i] = u16(k)
code = code + sizes[i]
if sizes[i] != 0 {
if code - 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..<new_size {
- array_set(&nm.hash, i, -1)
- }
- for i in 0..<array_len(m.entries) {
- e := array_get(m.entries, i)
- multi_map_insert(&nm, e.key, e.value)
- }
-
- map_delete(m^)
- m^ = nm
-}
-
-map_clear :: proc(m: ^$M/Map($Key, $Value)) {
- array_clear(&m.hash)
- array_clear(&m.entries)
-}
-
-
-
-multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^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..<n {
- r = r.next
- }
- }
- return r
-}
-
-ring_link :: proc(r, s: ^$R/Ring) -> ^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..<new_size {
- array_set(&nm.hash, i, -1)
- }
- for i in 0..<array_len(m.entries) {
- e := array_get(m.entries, i)
- set_add(&nm, e.key)
- }
-
- set_delete(m^)
- m^ = nm
-}
-
-set_clear :: proc(m: ^Set) {
- array_clear(&m.hash)
- array_clear(&m.entries)
-}
-
-
-set_equal :: proc(a, b: Set) -> 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_<size>`\*.
-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 <https://github.com/veorq/SipHash> and <https://www.aumasson.jp/siphash/siphash.pdf>
+
+ 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.c_rounds {
+ _compress(ctx)
+ }
+
+ ctx.v0 ~= m
+ }
+}
+
+final :: proc(ctx: ^Context, dst: ^u64) {
+ m: u64
+ for i := len(ctx.buf) - 1; i >= 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..<ctx.c_rounds {
+ _compress(ctx)
+ }
+
+ ctx.v0 ~= m
+ ctx.v2 ~= 0xff
+
+ for _ in 0..<ctx.d_rounds {
+ _compress(ctx)
+ }
+
+ dst^ = ctx.v0 ~ ctx.v1 ~ ctx.v2 ~ ctx.v3
+
+ reset(ctx)
+}
+
+reset :: proc(ctx: ^Context) {
+ ctx.k0, ctx.k1 = 0, 0
+ ctx.v0, ctx.v1 = 0, 0
+ ctx.v2, ctx.v3 = 0, 0
+ ctx.last_block = 0
+ ctx.c_rounds = 0
+ ctx.d_rounds = 0
+ ctx.is_initialized = false
+}
+
+Context :: struct {
+ v0, v1, v2, v3: u64, // State values
+ k0, k1: u64, // Split key
+ c_rounds: int, // Number of message rounds
+ d_rounds: int, // Number of finalization rounds
+ buf: []byte, // Provided data
+ last_block: int, // Offset from the last block
+ is_initialized: bool,
+}
+
+_get_byte :: #force_inline proc "contextless" (byte_num: byte, into: u64) -> 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 := "<input>", 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 <nom@duclavier.com>.
+ 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, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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, "<nil>")
+ io.write_string(writer, "<nil>", &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..<fi.width - len(s) {
- io.write_byte(fi.writer, ' ')
+ if fi.width_set {
+ if fi.width > len(s) {
+ if fi.minus {
+ io.write_string(fi.writer, s, &fi.n)
+ }
+
+ for _ in 0..<fi.width - len(s) {
+ io.write_byte(fi.writer, ' ', &fi.n)
+ }
+
+ if !fi.minus {
+ io.write_string(fi.writer, s, &fi.n)
+ }
}
+ else {
+ io.write_string(fi.writer, s[:fi.width], &fi.n)
+ }
+ }
+ else
+ {
+ io.write_string(fi.writer, s, &fi.n)
}
case 'q': // quoted string
- io.write_quoted_string(fi.writer, s, '"')
+ io.write_quoted_string(fi.writer, s, '"', &fi.n)
case 'x', 'X':
space := fi.space
@@ -947,7 +976,7 @@ fmt_string :: proc(fi: ^Info, s: string, verb: rune) {
for i in 0..<len(s) {
if i > 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, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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..<fi.indent {
- io.write_byte(fi.writer, '\t')
+ io.write_byte(fi.writer, '\t', &fi.n)
}
}
fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: int, elem_id: typeid, verb: rune) {
- 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)
if count <= 0 {
return
}
if fi.hash {
- io.write_byte(fi.writer, '\n')
+ io.write_byte(fi.writer, '\n', &fi.n)
defer fmt_write_indent(fi)
indent := fi.indent
@@ -1204,11 +1235,11 @@ fmt_write_array :: proc(fi: ^Info, array_data: rawptr, count: int, elem_size: in
data := uintptr(array_data) + uintptr(i*elem_size)
fmt_arg(fi, any{rawptr(data), elem_id}, verb)
- io.write_string(fi.writer, ",\n")
+ io.write_string(fi.writer, ",\n", &fi.n)
}
} else {
for i in 0..<count {
- if i > 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..<n {
- io.write_byte(fi.writer, '0')
+ io.write_byte(fi.writer, '0', &fi.n)
}
- io.write_i64(fi.writer, i, 10)
+ io.write_i64(fi.writer, i, 10, &fi.n)
}
if v.data == nil || v.id == nil {
- io.write_string(fi.writer, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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..<indent { io.write_byte(fi.writer, '\t') }
+ for in 0..<indent { io.write_byte(fi.writer, '\t', &fi.n) }
}
- io.write_byte(fi.writer, ']' if is_soa else '}')
+ io.write_byte(fi.writer, ']' if is_soa else '}', &fi.n)
}
if is_soa {
@@ -1423,37 +1454,37 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
for index in 0..<uintptr(b.soa_len) {
- if !hash && index > 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, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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, "<nil>")
+ io.write_string(fi.writer, "<nil>", &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..<info.count {
- if i > 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..<info.count {
- if i > 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..<entries.len {
- if i > 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..<n {
- if !hash && index > 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..<actual_field_count {
name := info.names[i]
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 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..<len {
- if i > 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..<info.row_count {
fmt_write_indent(fi)
for col in 0..<info.column_count {
- if col > 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..<info.row_count {
- if row > 0 { io.write_string(fi.writer, "; ") }
+ if row > 0 { io.write_string(fi.writer, "; ", &fi.n) }
for col in 0..<info.column_count {
- if col > 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 <nom@duclavier.com>.
+ 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..<length {
+ copy(pixels, pix[:img.channels])
+ pixels = pixels[img.channels:]
+ }
+ }
+
+ continue decode
+
+ case:
+ unreachable()
+ }
+ }
+
+ #no_bounds_check {
+ copy(pixels, pix[:img.channels])
+ pixels = pixels[img.channels:]
+ }
+ }
+
+ // The byte stream's end is marked with 7 0x00 bytes followed by a single 0x01 byte.
+ trailer, trailer_err := compress.read_data(ctx, u64be)
+ if trailer_err != nil || trailer != 0x1 {
+ return img, .Missing_Or_Corrupt_Trailer
+ }
+
+ if .alpha_premultiply in options && !image.alpha_drop_if_present(img, options) {
+ return img, .Post_Processing_Error
+ }
+
+ return
+}
+
+load :: proc{load_from_file, load_from_slice, load_from_context}
+
+/*
+ Cleanup of image-specific data.
+*/
+destroy :: proc(img: ^Image) {
+ if img == nil {
+ /*
+ Nothing to do.
+ Load must've returned with an error.
+ */
+ return
+ }
+
+ bytes.buffer_destroy(&img.pixels)
+
+ if v, ok := img.metadata.(^image.QOI_Info); ok {
+ free(v)
+ }
+ free(img)
+}
+
+QOI_Opcode_Tag :: enum u8 {
+ // 2-bit tags
+ INDEX = 0b0000_0000, // 6-bit index into color array follows
+ DIFF = 0b0100_0000, // 3x (RGB) 2-bit difference follows (-2..1), bias of 2.
+ LUMA = 0b1000_0000, // Luma difference
+ RUN = 0b1100_0000, // Run length encoding, bias -1
+
+ // 8-bit tags
+ RGB = 0b1111_1110, // Raw RGB pixel follows
+ RGBA = 0b1111_1111, // Raw RGBA pixel follows
+}
+
+QOI_Opcode_Mask :: 0b1100_0000
+QOI_Data_Mask :: 0b0011_1111
+
+qoi_hash :: #force_inline proc(pixel: RGBA_Pixel) -> (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 <nom@duclavier.com>.
+ 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_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_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_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 ---
+atomic_load_explicit :: proc(dst: ^$T, order: Atomic_Memory_Order) -> 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_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 ---
+
// 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 <nom@duclavier.com>.
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 <nom@duclavier.com>.
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 <nom@duclavier.com>.
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 <nom@duclavier.com>.
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
+
+ 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 src != 0 {
- dest.digit[dest.used] = DIGIT(src) & _MASK
+ #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,29 +2318,70 @@ 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.
*/
if a.sign == .Negative { res = -res }
@@ -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
}
/*
@@ -2634,37 +2686,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.
*/
internal_int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
@@ -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
/*
@@ -87,21 +89,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.
*/
int_shr_signed :: proc(dest, src: ^Int, bits: int, allocator := context.allocator) -> (err: Error) {
@@ -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))<<B */
- internal_shl_digit(x1y1, B * 2) or_return /* x1y1 = x1y1 << 2*B */
+ _private_int_shl_leg(t1, B) or_return /* t1 = (x0y0 + x1y1 - (x1-x0)*(y1-y0))<<B */
+ _private_int_shl_leg(x1y1, B * 2) or_return /* x1y1 = x1y1 << 2*B */
internal_add(t1, x0y0, t1) or_return /* t1 = x0y0 + t1 */
internal_add(dest, t1, x1y1) or_return /* t1 = x0y0 + t1 + x1y1 */
@@ -588,7 +590,7 @@ _private_int_mul_balance :: proc(dest, a, b: ^Int, allocator := context.allocato
/*
Shift `tmp` to the correct position.
*/
- internal_shl_digit(tmp, b_size * i) or_return
+ _private_int_shl_leg(tmp, b_size * i) or_return
/*
Add to output. No carry needed.
@@ -606,7 +608,7 @@ _private_int_mul_balance :: proc(dest, a, b: ^Int, allocator := context.allocato
internal_clamp(a0)
internal_mul(tmp, a0, b) or_return
- internal_shl_digit(tmp, b_size * i) or_return
+ _private_int_shl_leg(tmp, b_size * i) or_return
internal_add(r, r, tmp) or_return
}
@@ -840,8 +842,8 @@ _private_int_sqr_karatsuba :: proc(dest, src: ^Int, allocator := context.allocat
/*
Shift by B.
*/
- internal_shl_digit(t1, B) or_return
- internal_shl_digit(x1x1, B * 2) or_return
+ _private_int_shl_leg(t1, B) or_return
+ _private_int_shl_leg(x1x1, B * 2) or_return
internal_add(t1, t1, x0x0) or_return
internal_add(dest, t1, x1x1) or_return
@@ -942,10 +944,10 @@ _private_int_sqr_toom :: proc(dest, src: ^Int, allocator := context.allocator) -
internal_sub(dest, dest, S0) or_return
/** \\P = S4*x^4 + S3*x^3 + S2*x^2 + S1*x + S0; */
/** P = a2*x^4 + a1*x^3 + b*x^2 + a0*x + S0; */
- internal_shl_digit( a2, 4 * B) or_return
- internal_shl_digit( a1, 3 * B) or_return
- internal_shl_digit(dest, 2 * B) or_return
- internal_shl_digit( a0, 1 * B) or_return
+ _private_int_shl_leg( a2, 4 * B) or_return
+ _private_int_shl_leg( a1, 3 * B) or_return
+ _private_int_shl_leg(dest, 2 * B) or_return
+ _private_int_shl_leg( a0, 1 * B) or_return
internal_add(a2, a2, a1) or_return
internal_add(dest, dest, a2) or_return
@@ -1069,7 +1071,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
y = y*b**{n-t}
*/
- internal_shl_digit(y, n - t) or_return
+ _private_int_shl_leg(y, n - t) or_return
gte := internal_gte(x, y)
for gte {
@@ -1081,7 +1083,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
/*
Reset y by shifting it back down.
*/
- internal_shr_digit(y, n - t)
+ _private_int_shr_leg(y, n - t)
/*
Step 3. for i from n down to (t + 1).
@@ -1146,7 +1148,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
Step 3.3 x = x - q{i-t-1} * y * b**{i-t-1}
*/
int_mul_digit(t1, y, q.digit[(i - t) - 1]) or_return
- internal_shl_digit(t1, (i - t) - 1) or_return
+ _private_int_shl_leg(t1, (i - t) - 1) or_return
internal_sub(x, x, t1) or_return
/*
@@ -1154,7 +1156,7 @@ _private_int_div_school :: proc(quotient, remainder, numerator, denominator: ^In
*/
if x.sign == .Negative {
internal_copy(t1, y) or_return
- internal_shl_digit(t1, (i - t) - 1) or_return
+ _private_int_shl_leg(t1, (i - t) - 1) or_return
internal_add(x, x, t1) or_return
q.digit[(i - t) - 1] = (q.digit[(i - t) - 1] - 1) & _MASK
@@ -1220,7 +1222,7 @@ _private_div_recursion :: proc(quotient, remainder, a, b: ^Int, allocator := con
/*
A1 = (R1 * beta^(2k)) + (A % beta^(2k)) - (Q1 * B0 * beta^k)
*/
- internal_shl_digit(R1, 2 * k) or_return
+ _private_int_shl_leg(R1, 2 * k) or_return
internal_add(A1, R1, t) or_return
internal_mul(t, Q1, B0) or_return
@@ -1246,7 +1248,7 @@ _private_div_recursion :: proc(quotient, remainder, a, b: ^Int, allocator := con
/*
A2 = (R0*beta^k) + (A1 % beta^k) - (Q0*B0)
*/
- internal_shl_digit(R0, k) or_return
+ _private_int_shl_leg(R0, k) or_return
internal_add(A2, R0, t) or_return
internal_mul(t, Q0, B0) or_return
internal_sub(A2, A2, t) or_return
@@ -1262,7 +1264,7 @@ _private_div_recursion :: proc(quotient, remainder, a, b: ^Int, allocator := con
/*
Return q = (Q1*beta^k) + Q0, r = A2.
*/
- internal_shl_digit(Q1, k) or_return
+ _private_int_shl_leg(Q1, k) or_return
internal_add(quotient, Q1, Q0) or_return
return internal_copy(remainder, A2)
@@ -1923,7 +1925,7 @@ _private_int_montgomery_reduce :: proc(x, n: ^Int, rho: DIGIT, allocator := cont
x = x/b**n.used.
*/
internal_clamp(x)
- internal_shr_digit(x, n.used)
+ _private_int_shr_leg(x, n.used)
/*
if x >= 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 <nom@duclavier.com>.
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..<len(T) {
- out[i] = builtin.abs(a[i])
+ out[i] = auto_cast builtin.abs(a[i])
}
} else {
- out = builtin.abs(a)
+ out = auto_cast builtin.abs(a)
}
return
}
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index 6c594945f..9f22fa45e 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -1,6 +1,7 @@
package linalg
import "core:math"
+import "core:builtin"
import "core:intrinsics"
// Generic
@@ -60,14 +61,7 @@ quaternion256_dot :: proc(a, b: $T/quaternion256) -> (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..<M {
- for j in 0..<N {
- out[i][j] = a[i]*b[j]
- }
- }
- return
-}
+outer_product :: builtin.outer_product
quaternion_inverse :: proc(q: $Q) -> 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..<N {
- tr += m[i][i]
- }
- return
-}
+trace :: builtin.matrix_trace
+transpose :: builtin.transpose
-transpose :: proc(a: $T/[$N][$M]$E) -> (m: (T when N == M else [M][N]E)) #no_bounds_check {
- for j in 0..<M {
- for i in 0..<N {
- m[j][i] = a[i][j]
- }
- }
- return
-}
-
-matrix_mul :: proc(a, b: $M/[$N][N]$E) -> (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..<N {
- for k in 0..<N {
- for j in 0..<N {
- c[k][i] += a[j][i] * b[k][j]
- }
- }
- }
- return
+ return a * b
}
-matrix_comp_mul :: proc(a, b: $M/[$J][$I]$E) -> (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..<J {
- for i in 0..<I {
- c[j][i] = a[j][i] * b[j][i]
- }
- }
- return
+ return hadamard_product(a, b)
}
-matrix_mul_differ :: proc(a: $A/[$J][$I]$E, b: $B/[$K][J]E) -> (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..<K {
- for j in 0..<J {
- for i in 0..<I {
- c[k][i] += a[j][i] * b[k][j]
- }
- }
- }
- return
+ return a * b
}
-matrix_mul_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (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..<I {
- for j in 0..<J {
- c[j] += a[i][j] * b[i]
- }
- }
- return
+ return a * b
}
quaternion_mul_quaternion :: proc(q1, q2: $Q) -> 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..<M {
- for j in 0..<N {
- w[i][j] = Elem_Type(v[i][j])
+matrix_cast :: proc(v: $A/matrix[$M, $N]$T, $Elem_Type: typeid) -> (w: matrix[M, N]Elem_Type) #no_bounds_check {
+ for j in 0..<N {
+ for i in 0..<M {
+ w[i, j] = Elem_Type(v[i, j])
}
}
return
@@ -357,6 +314,6 @@ to_uint :: #force_inline proc(v: $A/[$N]$T) -> [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..<len(x) {
x[i] = x[i-1] + x[i]
@@ -1714,4 +1718,22 @@ F32_BIAS :: 0x7f
F64_MASK :: 0x7ff
F64_SHIFT :: 64 - 12
-F64_BIAS :: 0x3ff \ No newline at end of file
+F64_BIAS :: 0x3ff
+
+INF_F16 :f16: 0h7C00
+NEG_INF_F16 :f16: 0hFC00
+
+SNAN_F16 :f16: 0h7C01
+QNAN_F16 :f16: 0h7E01
+
+INF_F32 :f32: 0h7F80_0000
+NEG_INF_F32 :f32: 0hFF80_0000
+
+SNAN_F32 :f32: 0hFF80_0001
+QNAN_F32 :f32: 0hFFC0_0001
+
+INF_F64 :f64: 0h7FF0_0000_0000_0000
+NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
+
+SNAN_F64 :f64: 0h7FF0_0000_0000_0001
+QNAN_F64 :f64: 0h7FF8_0000_0000_0001
diff --git a/core/math/noise/internal.odin b/core/math/noise/internal.odin
new file mode 100644
index 000000000..5837f9235
--- /dev/null
+++ b/core/math/noise/internal.odin
@@ -0,0 +1,734 @@
+/*
+ OpenSimplex2 noise implementation.
+
+ Ported from https://github.com/KdotJPG/OpenSimplex2.
+ Copyright 2022 Yuki2 (https://github.com/NoahR02)
+*/
+//+private
+package math_noise
+
+/*
+ Private implementation details follow.
+*/
+
+PRIME_X :: i64(0x5205402B9270C86F)
+PRIME_Y :: i64(0x598CD327003817B5)
+PRIME_Z :: i64(0x5BCC226E9FA0BACB)
+PRIME_W :: i64(0x56CC5227E58F554B)
+
+HASH_MULTIPLIER :: i64(0x53A3F72DEEC546F5)
+SEED_FLIP_3D :: i64(-0x52D547B2E96ED629)
+SEED_OFFSET_4D :: i64(0xE83DC3E0DA7164D)
+
+ROOT_2_OVER_2 :: f64(0.7071067811865476)
+SKEW_2D :: f64(0.366025403784439)
+UNSKEW_2D :: f64(-0.21132486540518713)
+ROOT_3_OVER_3 :: f64(0.577350269189626)
+
+FALLBACK_ROTATE_3D :: f64(2.0) / f64(3.0)
+ROTATE_3D_ORTHOGONALIZER :: f64(UNSKEW_2D)
+
+SKEW_4D :: f32(0hbe0d8369)
+UNSKEW_4D :: f32(0.309016994374947)
+LATTICE_STEP_4D :: f32(0.2)
+
+N_GRADS_2D_EXPONENT :: 7
+N_GRADS_3D_EXPONENT :: 8
+N_GRADS_4D_EXPONENT :: 9
+N_GRADS_2D :: 1 << N_GRADS_2D_EXPONENT
+N_GRADS_3D :: 1 << N_GRADS_3D_EXPONENT
+N_GRADS_4D :: 1 << N_GRADS_4D_EXPONENT
+
+NORMALIZER_2D :: f64(0.01001634121365712)
+NORMALIZER_3D :: f64(0.07969837668935331)
+NORMALIZER_4D :: f64(0.0220065933241897)
+RSQUARED_2D :: f32(0.5)
+RSQUARED_3D :: f32(0.6)
+RSQUARED_4D :: f32(0.6)
+
+GRADIENTS_2D := [N_GRADS_2D * 2]f32{
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+ 0h4150804d, 0h42c5f72a, 0h42731b78, 0h429e696c, 0h429e696c, 0h42731b78, 0h42c5f72a, 0h4150804d,
+ 0h42c5f72a, 0hc150804d, 0h429e696c, 0hc2731b78, 0h42731b78, 0hc29e696c, 0h4150804d, 0hc2c5f72a,
+ 0hc150804d, 0hc2c5f72a, 0hc2731b78, 0hc29e696c, 0hc29e696c, 0hc2731b78, 0hc2c5f72a, 0hc150804d,
+ 0hc2c5f72a, 0h4150804d, 0hc29e696c, 0h42731b78, 0hc2731b78, 0h429e696c, 0hc150804d, 0h42c5f72a,
+ 0h4218d2da, 0h42b87975, 0h42b87975, 0h4218d2da, 0h42b87975, 0hc218d2da, 0h4218d2da, 0hc2b87975,
+ 0hc218d2da, 0hc2b87975, 0hc2b87975, 0hc218d2da, 0hc2b87975, 0h4218d2da, 0hc218d2da, 0h42b87975,
+}
+
+GRADIENTS_3D := [N_GRADS_3D * 4]f32{
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+ 0hc1df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0hc16b5146, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc21ae5b8, 0h00000000, 0hc16b5146, 0h00000000,
+ 0hc1df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0hc1df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0hc21ae5b8, 0h00000000, 0h416b5146, 0h00000000, 0hc16b5146, 0h00000000, 0h421ae5b8, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000,
+ 0hc148c1c5, 0h41df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0h41df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000,
+ 0h41df5103, 0hc1df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0hc1df5103, 0h4148c1c5, 0h00000000,
+ 0h416b5146, 0hc21ae5b8, 0h00000000, 0h00000000, 0h421ae5b8, 0hc16b5146, 0h00000000, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0hc1df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0hc1df5103, 0h00000000,
+ 0h421ae5b8, 0h00000000, 0hc16b5146, 0h00000000, 0h416b5146, 0h00000000, 0hc21ae5b8, 0h00000000,
+ 0h41df5103, 0hc148c1c5, 0h41df5103, 0h00000000, 0h41df5103, 0h4148c1c5, 0h41df5103, 0h00000000,
+ 0h416b5146, 0h00000000, 0h421ae5b8, 0h00000000, 0h421ae5b8, 0h00000000, 0h416b5146, 0h00000000,
+ 0h41df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0h41df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0h421ae5b8, 0h416b5146, 0h00000000, 0h00000000, 0h416b5146, 0h421ae5b8, 0h00000000, 0h00000000,
+ 0hc1df5103, 0h41df5103, 0hc148c1c5, 0h00000000, 0hc1df5103, 0h41df5103, 0h4148c1c5, 0h00000000,
+ 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0hc1df5103, 0h00000000,
+ 0h00000000, 0hc21ae5b8, 0hc16b5146, 0h00000000, 0h00000000, 0hc16b5146, 0hc21ae5b8, 0h00000000,
+ 0hc148c1c5, 0hc1df5103, 0h41df5103, 0h00000000, 0h4148c1c5, 0hc1df5103, 0h41df5103, 0h00000000,
+ 0h00000000, 0hc16b5146, 0h421ae5b8, 0h00000000, 0h00000000, 0hc21ae5b8, 0h416b5146, 0h00000000,
+}
+
+GRADIENTS_4D := [N_GRADS_4D * 4]f32{
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+ 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+ 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+ 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+ 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+ 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+ 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+ 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+ 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+ 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+ 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+ 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+ 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+ 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+ 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+ 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+ 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+ 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+ 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+ 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+ 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+ 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+ 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+ 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+ 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+ 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+ 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+ 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+ 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+ 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+ 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+ 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+ 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+ 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+ 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+ 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+ 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+ 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+ 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+ 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+ 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+ 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+ 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+ 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+ 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+ 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+ 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+ 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+ 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+ 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+ 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+ 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+ 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+ 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+ 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+ 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+ 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+ 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+ 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+ 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+ 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+ 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+ 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+ 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+ 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+ 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+ 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+ 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+ 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+ 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+ 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+ 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+ 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+ 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+ 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+ 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+ 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+ 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+ 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+ 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+ 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+ 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+ 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+ 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+ 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+ 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+ 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+ 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+ 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+ 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+ 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+ 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+ 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+ 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+ 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+ 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+ 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+ 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+ 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+ 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+ 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+ 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+ 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+ 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+ 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+ 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+ 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+ 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+ 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+ 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+ 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+ 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+ 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+ 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+ 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+ 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+ 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+ 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+ 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+ 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+ 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+ 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+ 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+ 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee, 0h40b05c85,
+ 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee,
+ 0hc21c1252, 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d,
+ 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc235739c, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc18a0670, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85,
+ 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0hc19d18ee,
+ 0hc1b8e69d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0h40024b8d,
+ 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0hbfc4b564, 0hc235739c, 0hbfc4b564, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc18a0670, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85,
+ 0hc19d18ee, 0h40b05c85, 0hc20e2b7a, 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0hc19d18ee,
+ 0hc1b8e69d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0h40024b8d,
+ 0h40024b8d, 0h40024b8d, 0hc21c1252, 0hc1b8e69d, 0hbfc4b564, 0hbfc4b564, 0hc235739c, 0hbfc4b564,
+ 0hc18a0670, 0hc18a0670, 0hc18a0670, 0hc208ee18, 0hc19d18ee, 0hc19d18ee, 0h40b05c85, 0hc20e2b7a,
+ 0hc19d18ee, 0h40b05c85, 0hc19d18ee, 0hc20e2b7a, 0h40b05c85, 0hc19d18ee, 0hc19d18ee, 0hc20e2b7a,
+ 0hc1b8e69d, 0h40024b8d, 0h40024b8d, 0hc21c1252, 0h40024b8d, 0hc1b8e69d, 0h40024b8d, 0hc21c1252,
+ 0h40024b8d, 0h40024b8d, 0hc1b8e69d, 0hc21c1252, 0hbfc4b564, 0hbfc4b564, 0hbfc4b564, 0hc235739c,
+ 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc19194b0, 0hc208695c, 0h40de6d7d, 0h41b6d966,
+ 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h406d72bf, 0hc22076c5, 0h406d72bf, 0h41a58418,
+ 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc0de6d7d, 0hc1b6d966, 0h419194b0, 0h4208695c,
+ 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h416b8e00, 0hc1d2a716, 0h416b8e00, 0h41f50507,
+ 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc19194b0, 0h40de6d7d, 0hc208695c, 0h41b6d966,
+ 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h406d72bf, 0h406d72bf, 0hc22076c5, 0h41a58418,
+ 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc0de6d7d, 0h419194b0, 0hc1b6d966, 0h4208695c,
+ 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h416b8e00, 0h416b8e00, 0hc1d2a716, 0h41f50507,
+ 0hc16b8e00, 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc19194b0, 0hc208695c, 0h41b6d966, 0h40de6d7d,
+ 0h40de6d7d, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h406d72bf, 0hc22076c5, 0h41a58418, 0h406d72bf,
+ 0hc06d72bf, 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc0de6d7d, 0hc1b6d966, 0h4208695c, 0h419194b0,
+ 0h419194b0, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h416b8e00, 0hc1d2a716, 0h41f50507, 0h416b8e00,
+ 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc19194b0, 0h40de6d7d, 0h41b6d966, 0hc208695c,
+ 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h406d72bf, 0h406d72bf, 0h41a58418, 0hc22076c5,
+ 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc0de6d7d, 0h419194b0, 0h4208695c, 0hc1b6d966,
+ 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h416b8e00, 0h416b8e00, 0h41f50507, 0hc1d2a716,
+ 0hc16b8e00, 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc19194b0, 0h41b6d966, 0hc208695c, 0h40de6d7d,
+ 0h40de6d7d, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h406d72bf, 0h41a58418, 0hc22076c5, 0h406d72bf,
+ 0hc06d72bf, 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc0de6d7d, 0h4208695c, 0hc1b6d966, 0h419194b0,
+ 0h419194b0, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h416b8e00, 0h41f50507, 0hc1d2a716, 0h416b8e00,
+ 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc19194b0, 0h41b6d966, 0h40de6d7d, 0hc208695c,
+ 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h406d72bf, 0h41a58418, 0h406d72bf, 0hc22076c5,
+ 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc0de6d7d, 0h4208695c, 0h419194b0, 0hc1b6d966,
+ 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h416b8e00, 0h41f50507, 0h416b8e00, 0hc1d2a716,
+ 0h41d2a716, 0hc16b8e00, 0hc1f50507, 0hc16b8e00, 0h41b6d966, 0hc19194b0, 0hc208695c, 0h40de6d7d,
+ 0h41b6d966, 0h40de6d7d, 0hc208695c, 0hc19194b0, 0h41a58418, 0h406d72bf, 0hc22076c5, 0h406d72bf,
+ 0h422076c5, 0hc06d72bf, 0hc1a58418, 0hc06d72bf, 0h4208695c, 0hc0de6d7d, 0hc1b6d966, 0h419194b0,
+ 0h4208695c, 0h419194b0, 0hc1b6d966, 0hc0de6d7d, 0h41f50507, 0h416b8e00, 0hc1d2a716, 0h416b8e00,
+ 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc1f50507, 0h41b6d966, 0hc19194b0, 0h40de6d7d, 0hc208695c,
+ 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc208695c, 0h41a58418, 0h406d72bf, 0h406d72bf, 0hc22076c5,
+ 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1a58418, 0h4208695c, 0hc0de6d7d, 0h419194b0, 0hc1b6d966,
+ 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1b6d966, 0h41f50507, 0h416b8e00, 0h416b8e00, 0hc1d2a716,
+ 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d, 0h421c1252,
+ 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0b05c85, 0h419d18ee, 0h419d18ee, 0h420e2b7a,
+ 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h421c1252, 0h419d18ee, 0hc0b05c85, 0h419d18ee, 0h420e2b7a,
+ 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h418a0670, 0h418a0670, 0h418a0670, 0h4208ee18,
+ 0h3fc4b564, 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0hc0024b8d, 0h40024b8d, 0h421c1252, 0h41b8e69d,
+ 0hc0024b8d, 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0b05c85, 0h419d18ee, 0h420e2b7a, 0h419d18ee,
+ 0h41b8e69d, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h419d18ee, 0hc0b05c85, 0h420e2b7a, 0h419d18ee,
+ 0h419d18ee, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h418a0670, 0h418a0670, 0h4208ee18, 0h418a0670,
+ 0h3fc4b564, 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0hc0024b8d, 0h421c1252, 0hc0024b8d, 0h41b8e69d,
+ 0hc0024b8d, 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0b05c85, 0h420e2b7a, 0h419d18ee, 0h419d18ee,
+ 0h41b8e69d, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h419d18ee, 0h420e2b7a, 0hc0b05c85, 0h419d18ee,
+ 0h419d18ee, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h418a0670, 0h4208ee18, 0h418a0670, 0h418a0670,
+ 0h4235739c, 0h3fc4b564, 0h3fc4b564, 0h3fc4b564, 0h421c1252, 0hc0024b8d, 0hc0024b8d, 0h41b8e69d,
+ 0h421c1252, 0hc0024b8d, 0h41b8e69d, 0hc0024b8d, 0h420e2b7a, 0hc0b05c85, 0h419d18ee, 0h419d18ee,
+ 0h421c1252, 0h41b8e69d, 0hc0024b8d, 0hc0024b8d, 0h420e2b7a, 0h419d18ee, 0hc0b05c85, 0h419d18ee,
+ 0h420e2b7a, 0h419d18ee, 0h419d18ee, 0hc0b05c85, 0h4208ee18, 0h418a0670, 0h418a0670, 0h418a0670,
+ 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41d2a716, 0hc208695c, 0hc19194b0, 0h40de6d7d, 0h41b6d966,
+ 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41b6d966, 0hc22076c5, 0h406d72bf, 0h406d72bf, 0h41a58418,
+ 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h422076c5, 0hc1b6d966, 0hc0de6d7d, 0h419194b0, 0h4208695c,
+ 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h4208695c, 0hc1d2a716, 0h416b8e00, 0h416b8e00, 0h41f50507,
+ 0hc1f50507, 0hc16b8e00, 0h41d2a716, 0hc16b8e00, 0hc208695c, 0hc19194b0, 0h41b6d966, 0h40de6d7d,
+ 0hc208695c, 0h40de6d7d, 0h41b6d966, 0hc19194b0, 0hc22076c5, 0h406d72bf, 0h41a58418, 0h406d72bf,
+ 0hc1a58418, 0hc06d72bf, 0h422076c5, 0hc06d72bf, 0hc1b6d966, 0hc0de6d7d, 0h4208695c, 0h419194b0,
+ 0hc1b6d966, 0h419194b0, 0h4208695c, 0hc0de6d7d, 0hc1d2a716, 0h416b8e00, 0h41f50507, 0h416b8e00,
+ 0hc1f50507, 0h41d2a716, 0hc16b8e00, 0hc16b8e00, 0hc208695c, 0h41b6d966, 0hc19194b0, 0h40de6d7d,
+ 0hc208695c, 0h41b6d966, 0h40de6d7d, 0hc19194b0, 0hc22076c5, 0h41a58418, 0h406d72bf, 0h406d72bf,
+ 0hc1a58418, 0h422076c5, 0hc06d72bf, 0hc06d72bf, 0hc1b6d966, 0h4208695c, 0hc0de6d7d, 0h419194b0,
+ 0hc1b6d966, 0h4208695c, 0h419194b0, 0hc0de6d7d, 0hc1d2a716, 0h41f50507, 0h416b8e00, 0h416b8e00,
+ 0h41d2a716, 0hc1f50507, 0hc16b8e00, 0hc16b8e00, 0h41b6d966, 0hc208695c, 0hc19194b0, 0h40de6d7d,
+ 0h41b6d966, 0hc208695c, 0h40de6d7d, 0hc19194b0, 0h41a58418, 0hc22076c5, 0h406d72bf, 0h406d72bf,
+ 0h422076c5, 0hc1a58418, 0hc06d72bf, 0hc06d72bf, 0h4208695c, 0hc1b6d966, 0hc0de6d7d, 0h419194b0,
+ 0h4208695c, 0hc1b6d966, 0h419194b0, 0hc0de6d7d, 0h41f50507, 0hc1d2a716, 0h416b8e00, 0h416b8e00,
+}
+
+/*
+ 2D Simplex noise base.
+*/
+_internal_noise_2d_unskewed_base :: proc(seed: i64, coord: Vec2) -> (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
+
+ reflect.set_union_value(res.derived, res_ptr_any)
- switch r in &res.derived {
- case Bad_Expr:
- case Ident:
- case Implicit:
- case Undef:
- case Basic_Lit:
+ 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)
+ }
- case Ellipsis:
+ 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,9 +2276,9 @@ 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)
}
@@ -2275,10 +2288,28 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
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:
+
+ 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:
+ case ^Value_Decl:
visit_decl(p, cast(^Decl)stmt, true)
return
- case Foreign_Import_Decl:
+ case ^Foreign_Import_Decl:
visit_decl(p, cast(^Decl)stmt, true)
return
- case Foreign_Block_Decl:
+ case ^Foreign_Block_Decl:
visit_decl(p, cast(^Decl)stmt, true)
return
- }
- switch v in stmt.derived {
- case Using_Stmt:
+ 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;
-
-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;
-
-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;
+Handle :: 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
+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
+
+
+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="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="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="exit") _unix_exit :: proc(status: c.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="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="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
+
+ @(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
+}
+
+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 s.size, ERROR_NONE;
+ return ERROR_NONE
}
-stdin: Handle = 0;
-stdout: Handle = 1;
-stderr: Handle = 2;
+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);
+ s, err := _fstat(fd)
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
}
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);
+@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) {
+ s: OS_Stat = ---
+ result := _unix_fstat(fd, &s)
if result == -1 {
- return s, Errno(get_last_error());
+ 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
+ }
}
- return s, 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
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
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
-}
-typeid_core :: proc "contextless" (id: typeid) -> typeid {
- ti := type_info_core(type_info_of(id))
- 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_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()
+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)
}
- 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
+ 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)
}
- variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
- if id == nil || data == nil {
- return id
+ type_assertion_check2 :: proc "contextless" (ok: bool, file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ if ok {
+ return
}
- 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
+
+ variant_type :: proc "contextless" (id: typeid, data: rawptr) -> typeid {
+ if id == nil || data == nil {
+ return id
}
- if idx < 0 {
- return nil
- } else if idx < len(v.variants) {
- return v.variants[idx].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
}
- return id
- }
- handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
+ @(cold)
+ handle_error :: proc "contextless" (file: string, line, column: i32, from, to: typeid, from_data: rawptr) {
- actual := variant_type(from, from_data)
+ 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_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()
}
- print_byte('\n')
- type_assertion_trap()
+ handle_error(file, line, column, from, to, from_data)
}
- 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("<unknown type>")
+ }
} 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]<<b
- result[1] = (input[1]<<b) | (input[0]>>(32-b))
+ result.lo = input.lo<<b
+ result.hi = (input.hi<<b) | (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
index f810197f1..f810197f1 100644
--- a/core/runtime/procs_windows_386.odin
+++ b/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
+}
+
+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
+}
-dot_product :: proc(a, b: $S/[]$T) -> T
+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..<b {
- if data[i] < data[i-6] {
- swap(data, i, i-6)
- }
- }
- _insertion_sort(data, a, b)
- }
-}
-
-@(private)
-_insertion_sort :: proc(data: $T/[]$E, a, b: int) where ORD(E) #no_bounds_check {
- for i in a+1..<b {
- for j := i; j > 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..<b {
- if less(data[i], data[i-6]) {
- swap(data, i, i-6)
- }
- }
- _insertion_sort_less(data, a, b, less)
- }
-}
-
-@(private)
-_insertion_sort_less :: proc(data: $T/[]$E, a, b: int, less: proc(i, j: E) -> bool) #no_bounds_check {
- for i in a+1..<b {
- for j := i; j > 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..<b {
- if cmp(data[i], data[i-6]) == .Less {
- swap(data, i, i-6)
- }
- }
- _insertion_sort_cmp(data, a, b, cmp)
- }
-}
-
-@(private)
-_insertion_sort_cmp :: proc(data: $T/[]$E, a, b: int, cmp: proc(i, j: E) -> Ordering) #no_bounds_check {
- for i in a+1..<b {
- for j := i; j > 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..<b {
+ for j := i; j > 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..<b {
+ if less(data[i], data[i-6], call) {
+ swap(data, i, i-6)
+ }
+ }
+ insertion_sort(data, a, b, call)
+ }
+}
+
+
+// merge sort
+_stable_sort_general :: proc(data: $T/[]$E, call: $P, $KIND: Sort_Kind) where (ORD(E) && KIND == .Ordered) || (KIND != .Ordered) #no_bounds_check {
+ less :: #force_inline proc(a, b: E, call: P) -> bool {
+ when KIND == .Ordered {
+ 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..<n {
+ for j := i; j > 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..<len(chars) {
c := chars[i]
@@ -17,6 +18,7 @@ ascii_set_make :: proc(chars: string) -> (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..<len, otherwhise `len` will be the end size
string_from_nul_terminated_ptr :: proc(ptr: ^byte, len: int) -> 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)
+ }
+
+ // 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
}
- if rune_length == 0 { rune_length = l }
+ // 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
+ 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
+}
- n -= 1
+/*
+ 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
- i := 0
- for ; i < n; i += 1 {
- m := index(s^, sep)
- if m < 0 {
- break
- }
- res = s[:m+sep_save]
+ 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+len(sep):]
- return
+ s^ = s[m+1:]
}
- res = s[:]
- ok = res != ""
- s^ = s[len(s):]
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)
+ 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_after_n_iterator :: proc(s: ^string, sep: string, n: int) -> (string, bool) {
- return _split_iterator(s, sep, len(sep), n)
+/*
+ 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 {
- if s == "" || len(substrs) <= 0 {
- return nil
- }
-
- sublen := len(substrs[0])
+/*
+ splits the input string `s` by all possible `substrs` []string
+ returns the allocated []string, nil on any empty substring or no matches
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ 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
}
- shared := len(s) - sublen
-
- if shared <= 0 {
- return nil
+ // disallow "" substr
+ for substr in substrs {
+ if len(substr) == 0 {
+ return
+ }
}
- // number, index, last
- n, i, l := 0, 0, 0
+ // 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)
+ })
- // 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
- }
+ substrings_found: int
+ temp := s
- 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
- }
-
- if !skip_empty || len(s) - l > 0 {
- n += 1
+ // step through string
+ _, skip := utf8.decode_rune_in_string(temp[:])
+ temp = temp[skip:]
}
- if n < 1 {
- // no results
- return nil
+ // skip when no results
+ if substrings_found < 1 {
+ return
}
- buf := make([]string, n, allocator)
-
- n, i, l = 0, 0, 0
+ buf = make([]string, substrings_found + 1, allocator)
+ buf_index: int
+ temp = s
+ temp_old := temp
- // 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
- }
-
- sublen := len(substrs[0])
-
- for substr in substrs[1:] {
- sublen = min(sublen, len(substr))
+ return {
+ temp = s,
+ temp_old = s,
+ substrs = temp_substrs,
}
+}
- shared := len(s) - sublen
+/*
+ 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
- if shared <= 0 {
- return "", false
+ 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]
}
-
- // 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
- }
-
- i += sublen
- l = i
-
- continue loop
+ size := len(substr)
+
+ // check range and compare string to substr
+ if size <= len(temp) && temp[:size] == substr {
+ res = temp_old[:len(temp_old) - len(temp)]
+ temp = temp[size:]
+ temp_old = temp
+ ok = true
+ return
}
}
- _, 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") }
-}
-
-
-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") }
-}
-
+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_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
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
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
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, &timespec_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
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
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
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..<i32(100) {
- state, ok := atomic_compare_exchange_weak_acquire(&m.state, .Unlocked, new_state)
+ state, ok := atomic_compare_exchange_weak_explicit(&m.state, .Unlocked, new_state, .Acquire, .Consume)
if ok {
return
}
@@ -38,8 +38,11 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
}
}
+ // Set just in case 100 iterations did not do it
+ new_state = .Waiting
+
for {
- if atomic_exchange_acquire(&m.state, .Waiting) == .Unlocked {
+ if atomic_exchange_explicit(&m.state, .Waiting, .Acquire) == .Unlocked {
return
}
@@ -49,11 +52,7 @@ atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
}
- switch v := atomic_exchange_acquire(&m.state, .Locked); v {
- case .Unlocked:
- // Okay
- case: fallthrough
- case .Locked, .Waiting:
+ if v := atomic_exchange_explicit(&m.state, .Locked, .Acquire); v != .Unlocked {
lock_slow(m, v)
}
}
@@ -66,7 +65,7 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
}
- switch atomic_exchange_release(&m.state, .Unlocked) {
+ switch atomic_exchange_explicit(&m.state, .Unlocked, .Release) {
case .Unlocked:
unreachable()
case .Locked:
@@ -78,17 +77,16 @@ atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
// atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
- _, 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
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
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
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)<<RW_Mutex_State_Half_Width
-
- RW_Mutex_State_Writer_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << 1
- RW_Mutex_State_Reader_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << RW_Mutex_State_Half_Width
-
-
- _RW_Mutex :: struct {
- // NOTE(bill): pthread_rwlock_t cannot be used since pthread_rwlock_destroy is required on some platforms
- // TODO(bill): Can we determine which platforms exactly?
- state: RW_Mutex_State,
- mutex: Mutex,
- sema: Sema,
- }
-
- _rw_mutex_lock :: proc(rw: ^RW_Mutex) {
- _ = atomic_add(&rw.impl.state, RW_Mutex_State_Writer)
- mutex_lock(&rw.impl.mutex)
-
- state := atomic_or(&rw.impl.state, RW_Mutex_State_Writer)
- if state & RW_Mutex_State_Reader_Mask != 0 {
- sema_wait(&rw.impl.sema)
- }
- }
-
- _rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
- _ = atomic_and(&rw.impl.state, ~RW_Mutex_State_Is_Writing)
- mutex_unlock(&rw.impl.mutex)
- }
-
- _rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> 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..<count {
- res := darwin.semaphore_signal(s.handle)
- assert(res == 0)
- }
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
- res := darwin.semaphore_wait(s.handle)
- assert(res == 0)
-}
diff --git a/core/sync/sync_freebsd.odin b/core/sync/sync_freebsd.odin
deleted file mode 100644
index 240308b7d..000000000
--- a/core/sync/sync_freebsd.odin
+++ /dev/null
@@ -1,40 +0,0 @@
-package sync
-
-import "core:sys/unix"
-import "core:intrinsics"
-
-
-current_thread_id :: proc "contextless" () -> 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..<count {
- assert(unix.sem_post(&s.handle) == 0);
- }
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
- assert(unix.sem_wait(&s.handle) == 0);
-}
-
diff --git a/core/sync/sync_linux.odin b/core/sync/sync_linux.odin
deleted file mode 100644
index 340437c11..000000000
--- a/core/sync/sync_linux.odin
+++ /dev/null
@@ -1,36 +0,0 @@
-package sync
-
-import "core:sys/unix"
-
-current_thread_id :: proc "contextless" () -> 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..<count {
- assert(unix.sem_post(&s.handle) == 0)
- }
-}
-
-semaphore_wait_for :: proc(s: ^Semaphore) {
- assert(unix.sem_wait(&s.handle) == 0)
-}
diff --git a/core/sync/sync_unix.odin b/core/sync/sync_unix.odin
deleted file mode 100644
index 114625483..000000000
--- a/core/sync/sync_unix.odin
+++ /dev/null
@@ -1,248 +0,0 @@
-// +build linux, darwin, freebsd
-package sync
-
-import "core:sys/unix"
-import "core:time"
-
-// A recursive lock that can only be held by one thread at once
-Mutex :: struct {
- handle: unix.pthread_mutex_t,
-}
-
-
-mutex_init :: proc(m: ^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
- unix.pthread_mutexattr_settype(&attrs, unix.PTHREAD_MUTEX_RECURSIVE)
-
- assert(unix.pthread_mutex_init(&m.handle, &attrs) == 0)
-}
-
-mutex_destroy :: proc(m: ^Mutex) {
- assert(unix.pthread_mutex_destroy(&m.handle) == 0)
- m.handle = {}
-}
-
-mutex_lock :: proc(m: ^Mutex) {
- assert(unix.pthread_mutex_lock(&m.handle) == 0)
-}
-
-// Returns false if someone else holds the lock.
-mutex_try_lock :: proc(m: ^Mutex) -> 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
index 853c3c685..4add9064d 100644
--- a/core/sync/sync2/sync_util.odin
+++ b/core/sync/sync_util.odin
@@ -1,12 +1,11 @@
-package sync2
-
-
-// Example:
-//
-// if guard(&m) {
-// ...
-// }
-//
+package sync
+
+/*
+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_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_munmap : uintptr : 215
+ 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_futex : uintptr : 98
- SYS_gettid : uintptr : 178
+ 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_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_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_futex : uintptr : 240
+ 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_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
-} else when ODIN_ARCH == "arm" {
- SYS_mmap : uintptr : 192 // 90 is "sys_old_mmap", we want mmap2
+ 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_munmap: uintptr : 91
- SYS_madvise: uintptr : 220
+ 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_gettid : uintptr: 224
+ 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.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
-Pool :: struct {
- allocator: mem.Allocator,
- mutex: sync.Mutex,
- sem_available: sync.Semaphore,
- processing_task_count: int, // atomic
- is_running: bool,
+ 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)
+
+ 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)
+}
- task: Task
- task.procedure = procedure
- task.data = data
- task.user_index = user_index
+// 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)
+}
- append(&pool.tasks, task)
- sync.semaphore_post(&pool.sem_available, 1)
+// 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
}
-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)
+// 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)
+
+ if len(pool.tasks_done) != 0 {
+ task = pop_front(&pool.tasks_done)
+ got_task = true
+ intrinsics.atomic_sub(&pool.num_done, 1)
+ }
-pool_do_work :: proc(pool: ^Pool, task: ^Task) {
- task.procedure(task)
- intrinsics.atomic_sub(&pool.processing_task_count, 1)
+ return
}
+// Mostly for internal use.
+pool_do_work :: proc(pool: ^Pool, task: Task) {
+ {
+ context.allocator = task.allocator
+ task.procedure(task)
+ }
-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)
- }
+ sync.guard(&pool.mutex)
- // 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)
- }
+ 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)
+}
- yield()
+// 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()
+
+ 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)
- 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)
- }
+ 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, &params)
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;
-
- // 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()
- }
- }
+ sync.guard(&t.mutex)
- // 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]
}