Browse Source

update to master

Colin Davidson 9 months ago
parent
commit
d60fb5a44e
100 changed files with 2561 additions and 912 deletions
  1. 10 0
      .github/workflows/ci.yml
  2. 1 0
      .github/workflows/nightly.yml
  3. 3 0
      .gitignore
  4. 0 12
      base/runtime/core.odin
  5. 48 15
      base/runtime/core_builtin.odin
  6. 0 12
      base/runtime/print.odin
  7. 19 0
      bin/RAD-LICENSE
  8. BIN
      bin/radlink.exe
  9. 36 8
      build.bat
  10. 10 6
      build_odin.sh
  11. 1 1
      core/c/frontend/tokenizer/tokenizer.odin
  12. 2 2
      core/c/libc/README.md
  13. 133 0
      core/c/libc/locale.odin
  14. 1 1
      core/c/libc/stdatomic.odin
  15. 2 1
      core/compress/zlib/doc.odin
  16. 35 6
      core/container/bit_array/bit_array.odin
  17. 1 1
      core/crypto/_chacha20/simd128/chacha20_simd128.odin
  18. 8 6
      core/crypto/_sha3/sp800_185.odin
  19. 5 5
      core/dynlib/lib.odin
  20. 5 3
      core/dynlib/lib_js.odin
  21. 20 10
      core/dynlib/lib_unix.odin
  22. 5 3
      core/dynlib/lib_windows.odin
  23. 1 1
      core/encoding/cbor/cbor.odin
  24. 20 5
      core/encoding/cbor/unmarshal.odin
  25. 1 1
      core/encoding/hxa/read.odin
  26. 1 0
      core/encoding/ini/ini.odin
  27. 0 6
      core/encoding/json/marshal.odin
  28. 43 19
      core/encoding/json/unmarshal.odin
  29. 1 13
      core/fmt/fmt.odin
  30. 10 2
      core/image/general.odin
  31. 1 1
      core/io/util.odin
  32. 3 3
      core/math/linalg/general.odin
  33. 6 6
      core/math/math_sincos.odin
  34. 64 15
      core/math/rand/rand.odin
  35. 167 167
      core/mem/allocators.odin
  36. 3 1
      core/mem/mem.odin
  37. 1 1
      core/mem/mutex_allocator.odin
  38. 1 1
      core/mem/tracking_allocator.odin
  39. 3 7
      core/mem/virtual/virtual_posix.odin
  40. 3 2
      core/net/socket_darwin.odin
  41. 3 2
      core/net/socket_freebsd.odin
  42. 23 14
      core/net/socket_linux.odin
  43. 3 2
      core/net/socket_windows.odin
  44. 12 11
      core/odin/ast/ast.odin
  45. 2 1
      core/odin/ast/clone.odin
  46. 31 16
      core/odin/parser/parser.odin
  47. 1 1
      core/odin/tokenizer/tokenizer.odin
  48. 1 1
      core/os/os2/allocators.odin
  49. 1 1
      core/os/os2/dir_linux.odin
  50. 4 6
      core/os/os2/dir_windows.odin
  51. 2 0
      core/os/os2/errors_linux.odin
  52. 2 0
      core/os/os2/errors_posix.odin
  53. 3 1
      core/os/os2/errors_windows.odin
  54. 1 1
      core/os/os2/file_util.odin
  55. 3 3
      core/os/os2/file_windows.odin
  56. 1 1
      core/os/os2/path_posix.odin
  57. 37 0
      core/os/os2/pipe.odin
  58. 26 0
      core/os/os2/pipe_linux.odin
  59. 27 0
      core/os/os2/pipe_posix.odin
  60. 12 0
      core/os/os2/pipe_windows.odin
  61. 232 124
      core/os/os2/process.odin
  62. 1 1
      core/os/os2/process_linux.odin
  63. 1 2
      core/os/os2/process_posix.odin
  64. 1 0
      core/os/os2/process_posix_other.odin
  65. 22 18
      core/os/os2/process_windows.odin
  66. 7 8
      core/os/os2/stat_windows.odin
  67. 2 4
      core/os/os_darwin.odin
  68. 2 5
      core/os/os_freebsd.odin
  69. 2 4
      core/os/os_haiku.odin
  70. 10 9
      core/os/os_linux.odin
  71. 2 4
      core/os/os_netbsd.odin
  72. 2 4
      core/os/os_openbsd.odin
  73. 7 0
      core/path/filepath/match.odin
  74. 5 32
      core/path/filepath/path_unix.odin
  75. 48 54
      core/reflect/reflect.odin
  76. 0 32
      core/reflect/types.odin
  77. 2 2
      core/simd/x86/sse2.odin
  78. 4 4
      core/slice/map.odin
  79. 142 24
      core/slice/slice.odin
  80. 2 1
      core/strconv/strconv.odin
  81. 1 1
      core/strings/strings.odin
  82. 10 0
      core/sys/darwin/Foundation/NSApplication.odin
  83. 500 14
      core/sys/darwin/mach_darwin.odin
  84. 46 1
      core/sys/info/cpu_intel.odin
  85. 16 0
      core/sys/info/platform_darwin.odin
  86. 67 60
      core/sys/linux/bits.odin
  87. 25 0
      core/sys/linux/constants.odin
  88. 22 3
      core/sys/linux/sys.odin
  89. 23 0
      core/sys/linux/types.odin
  90. 1 0
      core/sys/posix/arpa_inet.odin
  91. 56 31
      core/sys/posix/dirent.odin
  92. 10 2
      core/sys/posix/dlfcn.odin
  93. 175 8
      core/sys/posix/errno.odin
  94. 107 45
      core/sys/posix/fcntl.odin
  95. 9 2
      core/sys/posix/fnmatch.odin
  96. 32 4
      core/sys/posix/glob.odin
  97. 2 3
      core/sys/posix/grp.odin
  98. 1 0
      core/sys/posix/iconv.odin
  99. 87 2
      core/sys/posix/langinfo.odin
  100. 8 0
      core/sys/posix/libgen.odin

+ 10 - 0
.github/workflows/ci.yml

@@ -36,6 +36,8 @@ jobs:
           ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           (cd tests/issues; ./run.sh)
           (cd tests/issues; ./run.sh)
+          ./odin check tests/benchmark -vet -strict-style -no-entry-point
+
   build_freebsd:
   build_freebsd:
     name: FreeBSD Build, Check, and Test
     name: FreeBSD Build, Check, and Test
     runs-on: ubuntu-latest
     runs-on: ubuntu-latest
@@ -64,6 +66,7 @@ jobs:
           ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           ./odin test tests/core/speed.odin -file -all-packages -vet -strict-style -disallow-do -o:speed -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           ./odin test tests/vendor -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           (cd tests/issues; ./run.sh)
           (cd tests/issues; ./run.sh)
+          ./odin check tests/benchmark -vet -strict-style -no-entry-point
   ci:
   ci:
     strategy:
     strategy:
       fail-fast: false
       fail-fast: false
@@ -128,6 +131,8 @@ jobs:
           cd tests/issues
           cd tests/issues
           ./run.sh
           ./run.sh
 
 
+      - name: Check benchmarks
+        run: ./odin check tests/benchmark -vet -strict-style -no-entry-point
       - name: Odin check examples/all for Linux i386
       - name: Odin check examples/all for Linux i386
         run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
         run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
         if: matrix.os == 'ubuntu-latest'
         if: matrix.os == 'ubuntu-latest'
@@ -203,6 +208,11 @@ jobs:
         run: |
         run: |
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
           call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
           odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
           odin test tests/internal -all-packages -vet -strict-style -disallow-do -define:ODIN_TEST_FANCY=false -define:ODIN_TEST_FAIL_ON_BAD_MEMORY=true
+      - name: Check benchmarks
+        shell: cmd
+        run: |
+          call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+          odin check tests/benchmark -vet -strict-style -no-entry-point
       - name: Odin documentation tests
       - name: Odin documentation tests
         shell: cmd
         shell: cmd
         run: |
         run: |

+ 1 - 0
.github/workflows/nightly.yml

@@ -38,6 +38,7 @@ jobs:
       - name: Upload artifact
       - name: Upload artifact
         uses: actions/upload-artifact@v4
         uses: actions/upload-artifact@v4
         with:
         with:
+          include-hidden-files: true
           name: windows_artifacts
           name: windows_artifacts
           path: dist
           path: dist
   build_linux:
   build_linux:

+ 3 - 0
.gitignore

@@ -266,6 +266,9 @@ bin/
 *.exe
 *.exe
 *.obj
 *.obj
 *.pdb
 *.pdb
+*.res
+desktop.ini
+Thumbs.db
 
 
 # - Linux/MacOS
 # - Linux/MacOS
 odin
 odin

+ 0 - 12
base/runtime/core.odin

@@ -171,14 +171,6 @@ Type_Info_Simd_Vector :: struct {
 	elem_size:  int,
 	elem_size:  int,
 	count:      int,
 	count:      int,
 }
 }
-Type_Info_Relative_Pointer :: struct {
-	pointer:      ^Type_Info, // ^T
-	base_integer: ^Type_Info,
-}
-Type_Info_Relative_Multi_Pointer :: struct {
-	pointer:      ^Type_Info, // [^]T
-	base_integer: ^Type_Info,
-}
 Type_Info_Matrix :: struct {
 Type_Info_Matrix :: struct {
 	elem:         ^Type_Info,
 	elem:         ^Type_Info,
 	elem_size:    int,
 	elem_size:    int,
@@ -241,8 +233,6 @@ Type_Info :: struct {
 		Type_Info_Map,
 		Type_Info_Map,
 		Type_Info_Bit_Set,
 		Type_Info_Bit_Set,
 		Type_Info_Simd_Vector,
 		Type_Info_Simd_Vector,
-		Type_Info_Relative_Pointer,
-		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Matrix,
 		Type_Info_Matrix,
 		Type_Info_Soa_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Bit_Field,
 		Type_Info_Bit_Field,
@@ -275,8 +265,6 @@ Typeid_Kind :: enum u8 {
 	Map,
 	Map,
 	Bit_Set,
 	Bit_Set,
 	Simd_Vector,
 	Simd_Vector,
-	Relative_Pointer,
-	Relative_Multi_Pointer,
 	Matrix,
 	Matrix,
 	Soa_Pointer,
 	Soa_Pointer,
 	Bit_Field,
 	Bit_Field,

+ 48 - 15
base/runtime/core_builtin.odin

@@ -6,6 +6,39 @@ import "base:intrinsics"
 Maybe :: union($T: typeid) {T}
 Maybe :: union($T: typeid) {T}
 
 
 
 
+/*
+Recovers the containing/parent struct from a pointer to one of its fields.
+Works by "walking back" to the struct's starting address using the offset between the field and the struct.
+
+Inputs:
+- ptr: Pointer to the field of a container struct
+- T: The type of the container struct
+- field_name: The name of the field in the `T` struct
+
+Returns:
+- A pointer to the container struct based on a pointer to a field in it
+
+Example:
+	package container_of
+	import "base:runtime"
+
+	Node :: struct {
+		value: int,
+		prev:  ^Node,
+		next:  ^Node,
+	}
+
+	main :: proc() {
+		node: Node
+		field_ptr := &node.next
+		container_struct_ptr: ^Node = runtime.container_of(field_ptr, Node, "next")
+		assert(container_struct_ptr == &node)
+		assert(uintptr(field_ptr) - uintptr(container_struct_ptr) == size_of(node.value) + size_of(node.prev))
+	}
+
+Output:
+	^Node
+*/
 @(builtin, require_results)
 @(builtin, require_results)
 container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
 container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
 	where intrinsics.type_has_field(T, field_name),
 	where intrinsics.type_has_field(T, field_name),
@@ -350,12 +383,23 @@ _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, ali
 	return
 	return
 }
 }
 
 
-// `make_map` allocates and initializes a map. Like `new`, the first argument is a type, not a value.
+// `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value.
 // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
 // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
 //
 //
 // Note: Prefer using the procedure group `make`.
 // Note: Prefer using the procedure group `make`.
 @(builtin, require_results)
 @(builtin, require_results)
-make_map :: proc($T: typeid/map[$K]$E, #any_int capacity: int = 1<<MAP_MIN_LOG2_CAPACITY, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
+make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) {
+	m.allocator = allocator
+	return m
+}
+
+// `make_map_cap` initializes a map with an allocator and allocates space using `capacity`.
+// Like `new`, the first argument is a type, not a value.
+// Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
+//
+// Note: Prefer using the procedure group `make`.
+@(builtin, require_results)
+make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
 	make_map_expr_error_loc(loc, capacity)
 	make_map_expr_error_loc(loc, capacity)
 	context.allocator = allocator
 	context.allocator = allocator
 
 
@@ -392,6 +436,7 @@ make :: proc{
 	make_dynamic_array_len,
 	make_dynamic_array_len,
 	make_dynamic_array_len_cap,
 	make_dynamic_array_len_cap,
 	make_map,
 	make_map,
+	make_map_cap,
 	make_multi_pointer,
 	make_multi_pointer,
 
 
 	make_soa_slice,
 	make_soa_slice,
@@ -894,19 +939,7 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
 
 
 @builtin
 @builtin
 card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
 card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
-	when size_of(S) == 1 {
-		return int(intrinsics.count_ones(transmute(u8)s))
-	} else when size_of(S) == 2 {
-		return int(intrinsics.count_ones(transmute(u16)s))
-	} else when size_of(S) == 4 {
-		return int(intrinsics.count_ones(transmute(u32)s))
-	} else when size_of(S) == 8 {
-		return int(intrinsics.count_ones(transmute(u64)s))
-	} else when size_of(S) == 16 {
-		return int(intrinsics.count_ones(transmute(u128)s))
-	} else {
-		#panic("Unhandled card bit_set size")
-	}
+	return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
 }
 }
 
 
 
 

+ 0 - 12
base/runtime/print.odin

@@ -486,18 +486,6 @@ print_type :: #force_no_inline proc "contextless" (ti: ^Type_Info) {
 		print_u64(u64(info.count))
 		print_u64(u64(info.count))
 		print_byte(']')
 		print_byte(']')
 		print_type(info.elem)
 		print_type(info.elem)
-
-	case Type_Info_Relative_Pointer:
-		print_string("#relative(")
-		print_type(info.base_integer)
-		print_string(") ")
-		print_type(info.pointer)
-
-	case Type_Info_Relative_Multi_Pointer:
-		print_string("#relative(")
-		print_type(info.base_integer)
-		print_string(") ")
-		print_type(info.pointer)
 		
 		
 	case Type_Info_Matrix:
 	case Type_Info_Matrix:
 		print_string("matrix[")
 		print_string("matrix[")

+ 19 - 0
bin/RAD-LICENSE

@@ -0,0 +1,19 @@
+Copyright (c) 2024 Epic Games Tools
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

BIN
bin/radlink.exe


+ 36 - 8
build.bat

@@ -19,12 +19,12 @@ if "%VSCMD_ARG_TGT_ARCH%" neq "x64" (
 	)
 	)
 )
 )
 
 
-for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
-	set CURR_DATE_TIME=%%j
+for /f %%i in ('powershell get-date -format "{yyyyMMdd}"') do (
+	set CURR_DATE_TIME=%%i
 )
 )
-
 set curr_year=%CURR_DATE_TIME:~0,4%
 set curr_year=%CURR_DATE_TIME:~0,4%
 set curr_month=%CURR_DATE_TIME:~4,2%
 set curr_month=%CURR_DATE_TIME:~4,2%
+set curr_day=%CURR_DATE_TIME:~6,2%
 
 
 :: Make sure this is a decent name and not generic
 :: Make sure this is a decent name and not generic
 set exe_name=odin.exe
 set exe_name=odin.exe
@@ -45,7 +45,19 @@ if "%2" == "1" (
 	set nightly=0
 	set nightly=0
 )
 )
 
 
-set odin_version_raw="dev-%curr_year%-%curr_month%"
+if %release_mode% equ 0 (
+	set V1=%curr_year%
+	set V2=%curr_month%
+	set V3=%curr_day%
+) else (
+	set V1=%curr_year%
+	set V2=%curr_month%
+	set V3=0
+)
+set V4=0
+set odin_version_full="%V1%.%V2%.%V3%.%V4%"
+set odin_version_raw="dev-%V1%-%V2%"
+
 
 
 set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
 set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
 rem Parse source code as utf-8 even on shift-jis and other codepages
 rem Parse source code as utf-8 even on shift-jis and other codepages
@@ -53,18 +65,30 @@ rem See https://learn.microsoft.com/en-us/cpp/build/reference/utf-8-set-source-a
 set compiler_flags= %compiler_flags% /utf-8
 set compiler_flags= %compiler_flags% /utf-8
 set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
 set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
 
 
+rem fileversion is defined as {Major,Minor,Build,Private: u16} so a bit limited
+set rc_flags=-nologo ^
+-DV1=%V1% -DV2=%V2% -DV3=%V3% -DV4=%V4% ^
+-DVF=%odin_version_full% -DNIGHTLY=%nightly%
+
+where /Q git.exe || goto skip_git_hash
 if not exist .git\ goto skip_git_hash
 if not exist .git\ goto skip_git_hash
 for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
 for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
 	set odin_version_raw=dev-%%i
 	set odin_version_raw=dev-%%i
 	set GIT_SHA=%%j
 	set GIT_SHA=%%j
 )
 )
-if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
+if %ERRORLEVEL% equ 0 (
+	set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
+	set rc_flags=%rc_flags% -DGIT_SHA=%GIT_SHA% -DVP=%odin_version_raw%:%GIT_SHA%
+) else (
+	set rc_flags=%rc_flags% -DVP=%odin_version_raw%
+)
 :skip_git_hash
 :skip_git_hash
 
 
 if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
 if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
 
 
 if %release_mode% EQU 0 ( rem Debug
 if %release_mode% EQU 0 ( rem Debug
 	set compiler_flags=%compiler_flags% -Od -MDd -Z7
 	set compiler_flags=%compiler_flags% -Od -MDd -Z7
+	set rc_flags=%rc_flags% -D_DEBUG
 ) else ( rem Release
 ) else ( rem Release
 	set compiler_flags=%compiler_flags% -O2 -MT -Z7
 	set compiler_flags=%compiler_flags% -O2 -MT -Z7
 	set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
 	set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
@@ -82,6 +106,8 @@ set libs= ^
 	kernel32.lib ^
 	kernel32.lib ^
 	Synchronization.lib ^
 	Synchronization.lib ^
 	bin\llvm\windows\LLVM-C.lib
 	bin\llvm\windows\LLVM-C.lib
+set odin_res=misc\odin.res
+set odin_rc=misc\odin.rc
 
 
 rem DO NOT TOUCH!
 rem DO NOT TOUCH!
 rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
 rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
@@ -93,7 +119,7 @@ if %tilde_backend% EQU 1 (
 rem DO NOT TOUCH!
 rem DO NOT TOUCH!
 
 
 
 
-set linker_flags= -incremental:no -opt:ref -subsystem:console
+set linker_flags= -incremental:no -opt:ref -subsystem:console -MANIFEST:EMBED
 
 
 if %release_mode% EQU 0 ( rem Debug
 if %release_mode% EQU 0 ( rem Debug
 	set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
 	set linker_flags=%linker_flags% -debug /NATVIS:src\odin_compiler.natvis
@@ -102,19 +128,21 @@ if %release_mode% EQU 0 ( rem Debug
 )
 )
 
 
 set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
 set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
-set linker_settings=%libs% %linker_flags%
+set linker_settings=%libs% %odin_res% %linker_flags%
 
 
 del *.pdb > NUL 2> NUL
 del *.pdb > NUL 2> NUL
 del *.ilk > NUL 2> NUL
 del *.ilk > NUL 2> NUL
 
 
+rc %rc_flags% %odin_rc%
 cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
 cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
+mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32"
 if %errorlevel% neq 0 goto end_of_build
 if %errorlevel% neq 0 goto end_of_build
 
 
 call build_vendor.bat
 call build_vendor.bat
 if %errorlevel% neq 0 goto end_of_build
 if %errorlevel% neq 0 goto end_of_build
 
 
 rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
 rem If the demo doesn't run for you and your CPU is more than a decade old, try -microarch:native
-if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -- Hellope World
+if %release_mode% EQU 0 odin run examples/demo -vet -strict-style -resource:examples/demo/demo.rc -- Hellope World
 
 
 rem Many non-compiler devs seem to run debug build but don't realize.
 rem Many non-compiler devs seem to run debug build but don't realize.
 if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat release" if you want a faster, release mode compiler.
 if %release_mode% EQU 0 echo: & echo Debug compiler built. Note: run "build.bat release" if you want a faster, release mode compiler.

+ 10 - 6
build_odin.sh

@@ -130,7 +130,7 @@ build_odin() {
 		EXTRAFLAGS="-O3"
 		EXTRAFLAGS="-O3"
 		;;
 		;;
 	release-native)
 	release-native)
-		if [ "$OS_ARCH" = "arm64" ]; then
+		if [ "$OS_ARCH" = "arm64" ] || [ "$OS_ARCH" = "aarch64" ]; then
 			# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
 			# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
 			EXTRAFLAGS="-O3 -mcpu=native"
 			EXTRAFLAGS="-O3 -mcpu=native"
 		else
 		else
@@ -152,9 +152,7 @@ build_odin() {
 }
 }
 
 
 run_demo() {
 run_demo() {
-	if [ $# -eq 0 ] || [ "$1" = "debug" ]; then
-		./odin run examples/demo -vet -strict-style -- Hellope World
-	fi
+	./odin run examples/demo -vet -strict-style -- Hellope World
 }
 }
 
 
 if [ $# -eq 0 ]; then
 if [ $# -eq 0 ]; then
@@ -166,14 +164,20 @@ if [ $# -eq 0 ]; then
 elif [ $# -eq 1 ]; then
 elif [ $# -eq 1 ]; then
 	case $1 in
 	case $1 in
 	report)
 	report)
-		[ ! -f "./odin" ] && build_odin debug
+		if [ ! -f "./odin" ]; then
+			build_odin debug
+			run_demo
+		fi
 		./odin report
 		./odin report
 		;;
 		;;
+	debug)
+		build_odin debug
+		run_demo
+		;;
 	*)
 	*)
 		build_odin $1
 		build_odin $1
 		;;
 		;;
 	esac
 	esac
-	run_demo
 else
 else
 	error "Too many arguments!"
 	error "Too many arguments!"
 fi
 fi

+ 1 - 1
core/c/frontend/tokenizer/tokenizer.odin

@@ -291,7 +291,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
 		n -= 1
 		n -= 1
 	}
 	}
 
 
-	if x > max || 0xd800 <= x && x <= 0xe000 {
+	if x > max || 0xd800 <= x && x <= 0xdfff {
 		error_offset(t, offset, "escape sequence is an invalid Unicode code point")
 		error_offset(t, offset, "escape sequence is an invalid Unicode code point")
 		return false
 		return false
 	}
 	}

+ 2 - 2
core/c/libc/README.md

@@ -14,7 +14,7 @@ The following is a mostly-complete projection of the C11 standard library as def
 | `<inttypes.h>`    | Fully projected                                    |
 | `<inttypes.h>`    | Fully projected                                    |
 | `<iso646.h>`      | Not applicable, use Odin's operators               |
 | `<iso646.h>`      | Not applicable, use Odin's operators               |
 | `<limits.h>`      | Not projected                                      |
 | `<limits.h>`      | Not projected                                      |
-| `<locale.h>`      | Not projected                                      |
+| `<locale.h>`      | Fully projected                                    |
 | `<math.h>`        | Mostly projected, see [limitations](#Limitations)  |
 | `<math.h>`        | Mostly projected, see [limitations](#Limitations)  |
 | `<setjmp.h>`      | Fully projected                                    |
 | `<setjmp.h>`      | Fully projected                                    |
 | `<signal.h>`      | Fully projected                                    |
 | `<signal.h>`      | Fully projected                                    |
@@ -70,4 +70,4 @@ with the following copyright.
 
 
 ```
 ```
 Copyright 2021 Dale Weiler <[email protected]>.
 Copyright 2021 Dale Weiler <[email protected]>.
-```
+```

+ 133 - 0
core/c/libc/locale.odin

@@ -0,0 +1,133 @@
+package libc
+
+import "core:c"
+
+when ODIN_OS == .Windows {
+	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == .Darwin {
+	foreign import libc "system:System.framework"
+} else {
+	foreign import libc "system:c"
+}
+
+// locale.h - category macros
+
+foreign libc {
+	/*
+	Sets the components of an object with the type lconv with the values appropriate for the
+	formatting of numeric quantities (monetary and otherwise) according to the rules of the current
+	locale.
+
+	Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale()
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]]
+	*/
+	localeconv :: proc() -> ^lconv ---
+
+	/*
+	Selects the appropriate piece of the global locale, as specified by the category and locale arguments,
+	and can be used to change or query the entire global locale or portions thereof.
+
+	Returns: the current locale if `locale` is `nil`, the set locale otherwise
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]]
+	*/
+	@(link_name=LSETLOCALE)
+	setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring ---
+}
+
+Locale_Category :: enum c.int {
+	ALL      = LC_ALL,
+	COLLATE  = LC_COLLATE,
+	CTYPE    = LC_CTYPE,
+	MESSAGES = LC_MESSAGES,
+	MONETARY = LC_MONETARY,
+	NUMERIC  = LC_NUMERIC,
+	TIME     = LC_TIME,
+}
+
+when ODIN_OS == .NetBSD {
+	@(private) LSETLOCALE :: "__setlocale50"
+} else {
+	@(private) LSETLOCALE :: "setlocale"
+}
+
+when ODIN_OS == .Windows {
+	lconv :: struct {
+		decimal_point:        cstring,
+		thousand_sep:         cstring,
+		grouping:             cstring,
+		int_curr_symbol:      cstring,
+		currency_symbol:      cstring,
+		mon_decimal_points:   cstring,
+		mon_thousands_sep:    cstring,
+		mon_grouping:         cstring,
+		positive_sign:        cstring,
+		negative_sign:        cstring,
+		int_frac_digits:      c.char,
+		frac_digits:          c.char,
+		p_cs_precedes:        c.char,
+		p_sep_by_space:       c.char,
+		n_cs_precedes:        c.char,
+		n_sep_by_space:       c.char,
+		p_sign_posn:          c.char,
+		n_sign_posn:          c.char,
+		_W_decimal_point:     [^]u16 `fmt:"s,0"`,
+		_W_thousands_sep:     [^]u16 `fmt:"s,0"`,
+		_W_int_curr_symbol:   [^]u16 `fmt:"s,0"`,
+		_W_currency_symbol:   [^]u16 `fmt:"s,0"`,
+		_W_mon_decimal_point: [^]u16 `fmt:"s,0"`,
+		_W_mon_thousands_sep: [^]u16 `fmt:"s,0"`,
+		_W_positive_sign:     [^]u16 `fmt:"s,0"`,
+		_W_negative_sign:     [^]u16 `fmt:"s,0"`,
+	}
+} else {
+	lconv :: struct {
+		decimal_point:       cstring,
+		thousand_sep:        cstring,
+		grouping:            cstring,
+		int_curr_symbol:     cstring,
+		currency_symbol:     cstring,
+		mon_decimal_points:  cstring,
+		mon_thousands_sep:   cstring,
+		mon_grouping:        cstring,
+		positive_sign:       cstring,
+		negative_sign:       cstring,
+		int_frac_digits:     c.char,
+		frac_digits:         c.char,
+		p_cs_precedes:       c.char,
+		p_sep_by_space:      c.char,
+		n_cs_precedes:       c.char,
+		n_sep_by_space:      c.char,
+		p_sign_posn:         c.char,
+		n_sign_posn:         c.char,
+		_int_p_cs_precedes:  c.char,
+		_int_n_cs_precedes:  c.char,
+		_int_p_sep_by_space: c.char,
+		_int_n_sep_by_space: c.char,
+		_int_p_sign_posn:    c.char,
+		_int_n_sign_posn:    c.char,
+	}
+}
+
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD  || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
+
+	LC_ALL      :: 0
+	LC_COLLATE  :: 1
+	LC_CTYPE    :: 2
+	LC_MESSAGES :: 6
+	LC_MONETARY :: 3
+	LC_NUMERIC  :: 4
+	LC_TIME     :: 5
+
+} else when ODIN_OS == .Linux {
+
+	LC_CTYPE    :: 0
+	LC_NUMERIC  :: 1
+	LC_TIME     :: 2
+	LC_COLLATE  :: 3
+	LC_MONETARY :: 4
+	LC_MESSAGES :: 5
+	LC_ALL      :: 6
+
+}

+ 1 - 1
core/c/libc/stdatomic.odin

@@ -235,7 +235,7 @@ atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desire
 	return ok
 	return ok
 }
 }
 
 
-atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
+atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
 	assert(failure != .release)
 	assert(failure != .release)
 	assert(failure != .acq_rel)
 	assert(failure != .acq_rel)
 
 

+ 2 - 1
core/compress/zlib/doc.odin

@@ -13,6 +13,7 @@ Example:
 	package main
 	package main
 
 
 	import "core:bytes"
 	import "core:bytes"
+	import "core:compress/zlib"
 	import "core:fmt"
 	import "core:fmt"
 
 
 	main :: proc() {
 	main :: proc() {
@@ -36,7 +37,7 @@ Example:
 		buf: bytes.Buffer
 		buf: bytes.Buffer
 
 
 		// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
 		// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
-		err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
+		err := zlib.inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
 		defer bytes.buffer_destroy(&buf)
 		defer bytes.buffer_destroy(&buf)
 
 
 		if err != nil {
 		if err != nil {

+ 35 - 6
core/container/bit_array/bit_array.odin

@@ -259,6 +259,7 @@ Inputs:
 unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
 unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
 	b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
 	b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
 }
 }
+
 /*
 /*
 A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
 A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
 
 
@@ -276,22 +277,50 @@ Returns:
 - ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
 - ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
 */
 */
 create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
 create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
-	context.allocator = allocator
 	size_in_bits := max_index - min_index
 	size_in_bits := max_index - min_index
 
 
 	if size_in_bits < 0 { return {}, false }
 	if size_in_bits < 0 { return {}, false }
 
 
+	res = new(Bit_Array, allocator)
+	ok  = init(res, max_index, min_index, allocator)
+	res.free_pointer = true
+
+	if !ok { free(res, allocator) }
+
+	return
+}
+
+/*
+A helper function to initialize a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+
+The range of bits created by this procedure is `min_index..<max_index`, and the
+array will be able to expand beyond `max_index` if needed.
+
+*Allocates (`make(ba.bits)`)*
+
+Inputs:
+- max_index: maximum starting index
+- min_index: minimum starting index (used as a bias)
+- allocator: (default is context.allocator)
+*/
+init :: proc(res: ^Bit_Array, max_index: int, min_index: int = 0, allocator := context.allocator) -> (ok: bool) {
+	size_in_bits := max_index - min_index
+
+	if size_in_bits < 0 { return false }
+
 	legs := size_in_bits >> INDEX_SHIFT
 	legs := size_in_bits >> INDEX_SHIFT
-	if size_in_bits & INDEX_MASK > 0 {legs+=1}
-	bits, err := make([dynamic]u64, legs)
-	ok = err == mem.Allocator_Error.None
-	res = new(Bit_Array)
+	if size_in_bits & INDEX_MASK > 0 { legs += 1 }
+
+	bits, err := make([dynamic]u64, legs, allocator)
+	ok = err == nil
+
 	res.bits         = bits
 	res.bits         = bits
 	res.bias         = min_index
 	res.bias         = min_index
 	res.length       = max_index - min_index
 	res.length       = max_index - min_index
-	res.free_pointer = true
+	res.free_pointer = false
 	return
 	return
 }
 }
+
 /*
 /*
 Sets all values in the Bit_Array to zero.
 Sets all values in the Bit_Array to zero.
 
 

+ 1 - 1
core/crypto/_chacha20/simd128/chacha20_simd128.odin

@@ -51,7 +51,7 @@ _ROT_16: simd.u32x4 : {16, 16, 16, 16}
 
 
 when ODIN_ENDIAN == .Big {
 when ODIN_ENDIAN == .Big {
 	@(private = "file")
 	@(private = "file")
-	_increment_counter :: #force_inline proc "contextless" (ctx: ^Context) -> simd.u32x4 {
+	_increment_counter :: #force_inline proc "contextless" (ctx: ^_chacha20.Context) -> simd.u32x4 {
 		// In the Big Endian case, the low and high portions in the vector
 		// In the Big Endian case, the low and high portions in the vector
 		// are flipped, so the 64-bit addition can't be done with a simple
 		// are flipped, so the 64-bit addition can't be done with a simple
 		// vector add.
 		// vector add.

+ 8 - 6
core/crypto/_sha3/sp800_185.odin

@@ -81,16 +81,18 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
 	// 2. while len(z) mod 8 ≠ 0:
 	// 2. while len(z) mod 8 ≠ 0:
 	//    z = z || 0
 	//    z = z || 0
 
 
-	// 3. while (len(z)/8) mod w  0:
+	// 3. while (len(z)/8) mod w != 0:
 	//    z = z || 00000000
 	//    z = z || 00000000
 	z_len := u128(z_hi) << 64 | u128(z_lo)
 	z_len := u128(z_hi) << 64 | u128(z_lo)
 	z_rem := int(z_len % u128(w))
 	z_rem := int(z_len % u128(w))
-	pad := _PAD[:w - z_rem]
+	if z_rem != 0 {
+		pad := _PAD[:w - z_rem]
 
 
-	// We just add the padding to the state, instead of returning z.
-	//
-	// 4. return z.
-	update(ctx, pad)
+		// We just add the padding to the state, instead of returning z.
+		//
+		// 4. return z.
+		update(ctx, pad)
+	}
 }
 }
 
 
 encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
 encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {

+ 5 - 5
core/dynlib/lib.odin

@@ -37,8 +37,8 @@ Example:
 		fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
 		fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
 	}
 	}
 */
 */
-load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
-	return _load_library(path, global_symbols)
+load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (library: Library, did_load: bool) {
+	return _load_library(path, global_symbols, allocator)
 }
 }
 
 
 /*
 /*
@@ -98,8 +98,8 @@ Example:
 		}
 		}
 	}
 	}
 */
 */
-symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
-	return _symbol_address(library, symbol)
+symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) #optional_ok {
+	return _symbol_address(library, symbol, allocator)
 }
 }
 
 
 /*
 /*
@@ -174,4 +174,4 @@ initialize_symbols :: proc(
 // Returns an error message for the last failed procedure call.
 // Returns an error message for the last failed procedure call.
 last_error :: proc() -> string {
 last_error :: proc() -> string {
 	return _last_error()
 	return _last_error()
-}
+}

+ 5 - 3
core/dynlib/lib_js.odin

@@ -2,7 +2,9 @@
 #+private
 #+private
 package dynlib
 package dynlib
 
 
-_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
+import "base:runtime"
+
+_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 	return nil, false
 	return nil, false
 }
 }
 
 
@@ -10,10 +12,10 @@ _unload_library :: proc(library: Library) -> bool {
 	return false
 	return false
 }
 }
 
 
-_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
+_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
 	return nil, false
 	return nil, false
 }
 }
 
 
 _last_error :: proc() -> string {
 _last_error :: proc() -> string {
 	return ""
 	return ""
-}
+}

+ 20 - 10
core/dynlib/lib_unix.odin

@@ -2,28 +2,38 @@
 #+private
 #+private
 package dynlib
 package dynlib
 
 
-import "core:os"
+import "base:runtime"
 
 
-_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
-	flags := os.RTLD_NOW
+import "core:strings"
+import "core:sys/posix"
+
+_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
+	flags := posix.RTLD_Flags{.NOW}
 	if global_symbols {
 	if global_symbols {
-		flags |= os.RTLD_GLOBAL
+		flags += {.GLOBAL}
 	}
 	}
-	lib := os.dlopen(path, flags)
+
+	cpath := strings.clone_to_cstring(path, allocator)
+	defer delete(cpath, allocator)
+
+	lib := posix.dlopen(cpath, flags)
 	return Library(lib), lib != nil
 	return Library(lib), lib != nil
 }
 }
 
 
 _unload_library :: proc(library: Library) -> bool {
 _unload_library :: proc(library: Library) -> bool {
-	return os.dlclose(rawptr(library))
+	return posix.dlclose(posix.Symbol_Table(library)) == 0
 }
 }
 
 
-_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
-	ptr = os.dlsym(rawptr(library), symbol)
+_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
+	csymbol := strings.clone_to_cstring(symbol, allocator)
+	defer delete(csymbol, allocator)
+
+	ptr   = posix.dlsym(posix.Symbol_Table(library), csymbol)
 	found = ptr != nil
 	found = ptr != nil
 	return
 	return
 }
 }
 
 
 _last_error :: proc() -> string {
 _last_error :: proc() -> string {
-	err := os.dlerror()
+	err := string(posix.dlerror())
 	return "unknown" if err == "" else err
 	return "unknown" if err == "" else err
-}
+}

+ 5 - 3
core/dynlib/lib_windows.odin

@@ -2,11 +2,13 @@
 #+private
 #+private
 package dynlib
 package dynlib
 
 
+import "base:runtime"
+
 import win32 "core:sys/windows"
 import win32 "core:sys/windows"
 import "core:strings"
 import "core:strings"
 import "core:reflect"
 import "core:reflect"
 
 
-_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
+_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
 	// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
 	// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
 	wide_path := win32.utf8_to_wstring(path, allocator)
 	wide_path := win32.utf8_to_wstring(path, allocator)
 	defer free(wide_path, allocator)
 	defer free(wide_path, allocator)
@@ -19,7 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
 	return bool(ok)
 	return bool(ok)
 }
 }
 
 
-_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
+_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
 	c_str := strings.clone_to_cstring(symbol, allocator)
 	c_str := strings.clone_to_cstring(symbol, allocator)
 	defer delete(c_str, allocator)
 	defer delete(c_str, allocator)
 	ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
 	ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
@@ -31,4 +33,4 @@ _last_error :: proc() -> string {
 	err := win32.System_Error(win32.GetLastError())
 	err := win32.System_Error(win32.GetLastError())
 	err_msg := reflect.enum_string(err)
 	err_msg := reflect.enum_string(err)
 	return "unknown" if err_msg == "" else err_msg
 	return "unknown" if err_msg == "" else err_msg
-}
+}

+ 1 - 1
core/encoding/cbor/cbor.odin

@@ -563,7 +563,7 @@ to_json :: proc(val: Value, allocator := context.allocator) -> (json.Value, mem.
 					case: return false
 					case: return false
 					}
 					}
 				}
 				}
-				return false
+				return true
 			}
 			}
 
 
 			if keys_all_strings(v) {
 			if keys_all_strings(v) {

+ 20 - 5
core/encoding/cbor/unmarshal.odin

@@ -442,9 +442,6 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
 		loc := #caller_location,
 		loc := #caller_location,
 	) -> (out_of_space: bool, err: Unmarshal_Error) {
 	) -> (out_of_space: bool, err: Unmarshal_Error) {
 		for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
 		for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
-			elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
-			elem     := any{elem_ptr, elemt.id}
-
 			hdr := _decode_header(d.reader) or_return
 			hdr := _decode_header(d.reader) or_return
 			
 			
 			// Double size if out of capacity.
 			// Double size if out of capacity.
@@ -459,6 +456,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
 				if !ok { return false, .Out_Of_Memory }
 				if !ok { return false, .Out_Of_Memory }
 			}
 			}
 			
 			
+			// Set ptr after potential resizes to avoid invalidation.
+			elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
+			elem     := any{elem_ptr, elemt.id}
+
 			err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
 			err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
 			if length == -1 && err == .Break { break }
 			if length == -1 && err == .Break { break }
 			if err != nil { return }
 			if err != nil { return }
@@ -509,7 +510,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
 		raw           := (^mem.Raw_Dynamic_Array)(v.data)
 		raw           := (^mem.Raw_Dynamic_Array)(v.data)
 		raw.data       = raw_data(data) 
 		raw.data       = raw_data(data) 
 		raw.len        = 0
 		raw.len        = 0
-		raw.cap        = length
+		raw.cap        = scap
 		raw.allocator  = context.allocator
 		raw.allocator  = context.allocator
 
 
 		_ = assign_array(d, raw, t.elem, length) or_return
 		_ = assign_array(d, raw, t.elem, length) or_return
@@ -627,7 +628,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
 		unknown := length == -1
 		unknown := length == -1
 		fields := reflect.struct_fields_zipped(ti.id)
 		fields := reflect.struct_fields_zipped(ti.id)
 
 
-		for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
+		idx := 0
+		for ; idx < len(fields) && (unknown || idx < length); idx += 1 {
 			// Decode key, keys can only be strings.
 			// Decode key, keys can only be strings.
 			key: string
 			key: string
 			if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break {
 			if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break {
@@ -662,6 +664,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
 				
 				
 				// Skips unused map entries.
 				// Skips unused map entries.
 				if use_field_idx < 0 {
 				if use_field_idx < 0 {
+					val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+					destroy(val, context.temp_allocator)
 					continue
 					continue
 				}
 				}
 			}
 			}
@@ -672,6 +676,17 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
 			fany  := any{ptr, field.type.id}
 			fany  := any{ptr, field.type.id}
 			_unmarshal_value(d, fany, _decode_header(r) or_return) or_return
 			_unmarshal_value(d, fany, _decode_header(r) or_return) or_return
 		}
 		}
+
+		// If there are fields left in the map that did not get decoded into the struct, decode and discard them.
+		if !unknown {
+			for _ in idx..<length {
+				key := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+				destroy(key, context.temp_allocator)
+				val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+				destroy(val, context.temp_allocator)
+			}
+		}
+
 		return
 		return
 
 
 	case reflect.Type_Info_Map:
 	case reflect.Type_Info_Map:

+ 1 - 1
core/encoding/hxa/read.odin

@@ -117,7 +117,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
 			layer.name = read_name(r) or_return
 			layer.name = read_name(r) or_return
 			layer.components = read_value(r, u8) or_return
 			layer.components = read_value(r, u8) or_return
 			type := read_value(r, Layer_Data_Type) or_return
 			type := read_value(r, Layer_Data_Type) or_return
-			if type > max(type) {
+			if type > max(Layer_Data_Type) {
 				if r.print_error {
 				if r.print_error {
 					fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
 					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)))
 								r.filename, u8(type), u8(max(Layer_Data_Type)))

+ 1 - 0
core/encoding/ini/ini.odin

@@ -154,6 +154,7 @@ write_section :: proc(w: io.Writer, name: string, n_written: ^int = nil) -> (n:
 	io.write_byte  (w, '[',  &n) or_return
 	io.write_byte  (w, '[',  &n) or_return
 	io.write_string(w, name, &n) or_return
 	io.write_string(w, name, &n) or_return
 	io.write_byte  (w, ']',  &n) or_return
 	io.write_byte  (w, ']',  &n) or_return
+	io.write_byte  (w, '\n',  &n) or_return
 	return
 	return
 }
 }
 
 

+ 0 - 6
core/encoding/json/marshal.odin

@@ -192,12 +192,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 
 
 	case runtime.Type_Info_Simd_Vector:
 	case runtime.Type_Info_Simd_Vector:
 		return .Unsupported_Type
 		return .Unsupported_Type
-
-	case runtime.Type_Info_Relative_Pointer:
-		return .Unsupported_Type
-
-	case runtime.Type_Info_Relative_Multi_Pointer:
-		return .Unsupported_Type
 		
 		
 	case runtime.Type_Info_Matrix:
 	case runtime.Type_Info_Matrix:
 		return .Unsupported_Type
 		return .Unsupported_Type

+ 43 - 19
core/encoding/json/unmarshal.odin

@@ -172,20 +172,33 @@ assign_float :: proc(val: any, f: $T) -> bool {
 
 
 
 
 @(private)
 @(private)
-unmarshal_string_token :: 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) -> (ok: bool, err: Error) {
 	val := val
 	val := val
 	switch &dst in val {
 	switch &dst in val {
 	case string:
 	case string:
 		dst = str
 		dst = str
-		return true
+		return true, nil
 	case cstring:  
 	case cstring:  
 		if str == "" {
 		if str == "" {
-			dst = strings.clone_to_cstring("", p.allocator)
+			a_err: runtime.Allocator_Error
+			dst, a_err = strings.clone_to_cstring("", p.allocator)
+			#partial switch a_err {
+			case nil:
+				// okay
+			case .Out_Of_Memory:
+				err = .Out_Of_Memory
+			case:
+				err = .Invalid_Allocator
+			}
+			if err != nil {
+				return
+			}
 		} else {
 		} else {
 			// NOTE: This is valid because 'clone_string' appends a NUL terminator
 			// NOTE: This is valid because 'clone_string' appends a NUL terminator
 			dst = cstring(raw_data(str)) 
 			dst = cstring(raw_data(str)) 
 		}
 		}
-		return true
+		ok = true
+		return
 	}
 	}
 	
 	
 	#partial switch variant in ti.variant {
 	#partial switch variant in ti.variant {
@@ -193,31 +206,37 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.T
 		for name, i in variant.names {
 		for name, i in variant.names {
 			if name == str {
 			if name == str {
 				assign_int(val, variant.values[i])
 				assign_int(val, variant.values[i])
-				return true
+				return true, nil
 			}
 			}
 		}
 		}
 		// TODO(bill): should this be an error or not?
 		// TODO(bill): should this be an error or not?
-		return true
+		return true, nil
 		
 		
 	case reflect.Type_Info_Integer:
 	case reflect.Type_Info_Integer:
-		i := strconv.parse_i128(str) or_return
+		i, pok := strconv.parse_i128(str)
+		if !pok {
+			return false, nil
+		}
 		if assign_int(val, i) {
 		if assign_int(val, i) {
-			return true
+			return true, nil
 		}
 		}
 		if assign_float(val, i) {
 		if assign_float(val, i) {
-			return true
+			return true, nil
 		}
 		}
 	case reflect.Type_Info_Float:
 	case reflect.Type_Info_Float:
-		f := strconv.parse_f64(str) or_return
+		f, pok := strconv.parse_f64(str)
+		if !pok {
+			return false, nil
+		}
 		if assign_int(val, f) {
 		if assign_int(val, f) {
-			return true
+			return true, nil
 		}
 		}
 		if assign_float(val, f) {
 		if assign_float(val, f) {
-			return true
+			return true, nil
 		}
 		}
 	}
 	}
 	
 	
-	return false
+	return false, nil
 }
 }
 
 
 
 
@@ -304,7 +323,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 	case .Ident:
 	case .Ident:
 		advance_token(p)
 		advance_token(p)
 		if p.spec == .MJSON {
 		if p.spec == .MJSON {
-			if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
+			if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) or_return {
 				return nil
 				return nil
 			}
 			}
 		}
 		}
@@ -312,13 +331,18 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 		
 		
 	case .String:
 	case .String:
 		advance_token(p)
 		advance_token(p)
-		str := unquote_string(token, p.spec, p.allocator) or_return
-		if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
-			return nil
+		str  := unquote_string(token, p.spec, p.allocator) or_return
+		dest := any{v.data, ti.id}
+		if !(unmarshal_string_token(p, dest, str, ti) or_return) {
+			delete(str, p.allocator)
+			return UNSUPPORTED_TYPE
 		}
 		}
-		delete(str, p.allocator)
-		return UNSUPPORTED_TYPE
 
 
+		switch destv in dest {
+		case string, cstring:
+		case: delete(str, p.allocator)
+		}
+		return nil
 
 
 	case .Open_Brace:
 	case .Open_Brace:
 		return unmarshal_object(p, v, .Close_Brace)
 		return unmarshal_object(p, v, .Close_Brace)

+ 1 - 13
core/fmt/fmt.odin

@@ -1531,7 +1531,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
 	case 'o': _fmt_int(fi, u,  8, false, 8*size_of(rawptr), __DIGITS_UPPER)
 	case 'o': _fmt_int(fi, u,  8, false, 8*size_of(rawptr), __DIGITS_UPPER)
 	case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
 	case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
 	case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER)
 	case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER)
-	case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
+	case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_LOWER)
 	case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
 	case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
 
 
 	case:
 	case:
@@ -3002,18 +3002,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 	case runtime.Type_Info_Bit_Set:
 	case runtime.Type_Info_Bit_Set:
 		fmt_bit_set(fi, v, verb = verb)
 		fmt_bit_set(fi, v, verb = verb)
 
 
-	case runtime.Type_Info_Relative_Pointer:
-		ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
-		absolute_ptr := any{ptr, info.pointer.id}
-
-		fmt_value(fi, absolute_ptr, verb)
-
-	case runtime.Type_Info_Relative_Multi_Pointer:
-		ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
-		absolute_ptr := any{ptr, info.pointer.id}
-
-		fmt_value(fi, absolute_ptr, verb)
-
 	case runtime.Type_Info_Matrix:
 	case runtime.Type_Info_Matrix:
 		fmt_matrix(fi, v, verb, info)
 		fmt_matrix(fi, v, verb, info)
 
 

+ 10 - 2
core/image/general.odin

@@ -23,7 +23,15 @@ register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_
 load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
 load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
 	loader := _internal_loaders[which(data)]
 	loader := _internal_loaders[which(data)]
 	if loader == nil {
 	if loader == nil {
-		return nil, .Unsupported_Format
+
+		// Check if there is at least one loader, otherwise panic to let the user know about misuse.
+		for a_loader in _internal_loaders {
+			if a_loader != nil {
+				return nil, .Unsupported_Format
+			}
+		}
+
+		panic("image.load called when no image loaders are registered. Register a loader by first importing a subpackage (eg: `import \"core:image/png\"`), or with image.register")
 	}
 	}
 	return loader(data, options, allocator)
 	return loader(data, options, allocator)
 }
 }
@@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
 		return .HDR
 		return .HDR
 	case s[:4] == "\x38\x42\x50\x53":
 	case s[:4] == "\x38\x42\x50\x53":
 		return .PSD
 		return .PSD
-	case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
+	case s[:4] == "\x53\x80\xF6\x34" && s[88:92] == "PICT":
 		return .PIC
 		return .PIC
 	case s[:4] == "\x69\x63\x6e\x73":
 	case s[:4] == "\x69\x63\x6e\x73":
 		return .ICNS
 		return .ICNS

+ 1 - 1
core/io/util.odin

@@ -225,7 +225,7 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
 			} else {
 			} else {
 				write_byte(w, '\\', &n) or_return
 				write_byte(w, '\\', &n) or_return
 				write_byte(w, 'U', &n)  or_return
 				write_byte(w, 'U', &n)  or_return
-				for s := 24; s >= 0; s -= 4 {
+				for s := 28; s >= 0; s -= 4 {
 					write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
 					write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
 				}
 				}
 			}
 			}

+ 3 - 3
core/math/linalg/general.odin

@@ -53,15 +53,15 @@ vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E)
 }
 }
 @(require_results)
 @(require_results)
 quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
 quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
-	return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
+	return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
 }
 }
 @(require_results)
 @(require_results)
 quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) {
 quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) {
-	return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
+	return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
 }
 }
 @(require_results)
 @(require_results)
 quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) {
 quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) {
-	return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
+	return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
 }
 }
 
 
 dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
 dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}

+ 6 - 6
core/math/math_sincos.odin

@@ -189,12 +189,12 @@ sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check {
 // sin coefficients
 // sin coefficients
 @(private="file")
 @(private="file")
 _sin := [?]f64{
 _sin := [?]f64{
-	 0h3de5d8fd1fd19ccd, //  1.58962301576546568060e-10
-	 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
-	 0h3ec71de3567d48a1, //  2.75573136213857245213e-6
-	 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
-	 0h3f8111111110f7d0, //  8.33333333332211858878e-3
-	 0hbfc5555555555548, // -1.66666666666666307295e-1
+	0h3de5d8fd1fd19ccd, //  1.58962301576546568060e-10
+	0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
+	0h3ec71de3567d48a1, //  2.75573136213857245213e-6
+	0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
+	0h3f8111111110f7d0, //  8.33333333332211858878e-3
+	0hbfc5555555555548, // -1.66666666666666307295e-1
 }
 }
 
 
 // cos coefficients
 // cos coefficients

+ 64 - 15
core/math/rand/rand.odin

@@ -670,20 +670,69 @@ choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
 
 
 
 
 @(require_results)
 @(require_results)
-choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
-	where
-		intrinsics.type_is_enum(T),
-		size_of(T) <= 8,
-		len(T) == cap(T) /* Only allow contiguous enum types */ \
-{
-	when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
-	     u64(max(T)) > u64(max(i64)) {
-		i := uint64(gen) % u64(len(T))
-		i += u64(min(T))
-		return T(i)
+choice_enum :: proc($T: typeid, gen := context.random_generator) -> T where intrinsics.type_is_enum(T) {
+	when size_of(T) <= 8 && len(T) == cap(T) {
+		when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
+			 u64(max(T)) > u64(max(i64)) {
+			i := uint64(gen) % u64(len(T))
+			i += u64(min(T))
+			return T(i)
+		} else {
+			i := int63_max(i64(len(T)), gen)
+			i += i64(min(T))
+			return T(i)
+		}
 	} else {
 	} else {
-		i := int63_max(i64(len(T)), gen)
-		i += i64(min(T))
-		return T(i)
+		values := runtime.type_info_base(type_info_of(T)).variant.(runtime.Type_Info_Enum).values
+		return T(choice(values))
+	}
+}
+
+/*
+Returns a random *set* bit from the provided `bit_set`.
+
+Inputs:
+- set: The `bit_set` to choose a random set bit from
+
+Returns:
+- res: The randomly selected bit, or the zero value if `ok` is `false`
+- ok:  Whether the bit_set was not empty and thus `res` is actually a random set bit
+
+Example:
+	import "core:math/rand"
+	import "core:fmt"
+
+	choice_bit_set_example :: proc() {
+		Flags :: enum {
+			A,
+			B = 10,
+			C,
+		}
+
+		fmt.println(rand.choice_bit_set(bit_set[Flags]{}))
+		fmt.println(rand.choice_bit_set(bit_set[Flags]{.B}))
+		fmt.println(rand.choice_bit_set(bit_set[Flags]{.B, .C}))
+		fmt.println(rand.choice_bit_set(bit_set[0..<15]{5, 1, 4}))
 	}
 	}
-}
+
+Possible Output:
+	A false
+	B true
+	C true
+	5 true
+*/
+@(require_results)
+choice_bit_set :: proc(set: $T/bit_set[$E], gen := context.random_generator) -> (res: E, ok: bool) {
+	total_set := card(set)
+	if total_set == 0 {
+		return {}, false
+	}
+
+	core_set := transmute(intrinsics.type_bit_set_underlying_type(T))set
+
+	for target := int_max(total_set, gen); target > 0; target -= 1 {
+		core_set &= core_set - 1
+	}
+
+	return E(intrinsics.count_trailing_zeros(core_set)), true
+}

+ 167 - 167
core/mem/allocators.odin

@@ -14,16 +14,16 @@ but an attempt to allocate memory is not an error.
 nil_allocator :: proc() -> Allocator {
 nil_allocator :: proc() -> Allocator {
 	return Allocator{
 	return Allocator{
 		procedure = nil_allocator_proc,
 		procedure = nil_allocator_proc,
-		data = nil,
+		data      = nil,
 	}
 	}
 }
 }
 
 
 nil_allocator_proc :: proc(
 nil_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
 	size, alignment: int,
 	size, alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	return nil, nil
 	return nil, nil
@@ -41,7 +41,7 @@ not be allocated, and an attempt to allocate memory is an error.
 panic_allocator :: proc() -> Allocator {
 panic_allocator :: proc() -> Allocator {
 	return Allocator{
 	return Allocator{
 		procedure = panic_allocator_proc,
 		procedure = panic_allocator_proc,
-		data = nil,
+		data      = nil,
 	}
 	}
 }
 }
 
 
@@ -157,10 +157,10 @@ This procedure returns a pointer to the newly allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 arena_alloc :: proc(
 arena_alloc :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := arena_alloc_bytes(a, size, alignment, loc)
 	bytes, err := arena_alloc_bytes(a, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -175,10 +175,10 @@ This procedure returns a slice of the newly allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 arena_alloc_bytes :: proc(
 arena_alloc_bytes :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
 	bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
 	if bytes != nil {
 	if bytes != nil {
@@ -197,10 +197,10 @@ memory region.
 */
 */
 @(require_results)
 @(require_results)
 arena_alloc_non_zeroed :: proc(
 arena_alloc_non_zeroed :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
 	bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -216,10 +216,10 @@ memory region.
 */
 */
 @(require_results)
 @(require_results)
 arena_alloc_bytes_non_zeroed :: proc(
 arena_alloc_bytes_non_zeroed :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	if a.data == nil {
 	if a.data == nil {
 		panic("Arena is not initialized", loc)
 		panic("Arena is not initialized", loc)
@@ -244,11 +244,11 @@ arena_free_all :: proc(a: ^Arena) {
 
 
 arena_allocator_proc :: proc(
 arena_allocator_proc :: proc(
 	allocator_data: rawptr,
 	allocator_data: rawptr,
-	mode: Allocator_Mode,
-	size: int,
-	alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	mode:           Allocator_Mode,
+	size:           int,
+	alignment:      int,
+	old_memory:     rawptr,
+	old_size:       int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error)  {
 ) -> ([]byte, Allocator_Error)  {
 	arena := cast(^Arena)allocator_data
 	arena := cast(^Arena)allocator_data
@@ -398,10 +398,10 @@ returns a pointer to the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_alloc :: proc(
 scratch_alloc :: proc(
-	s: ^Scratch,
+	s:    ^Scratch,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_alloc_bytes(s, size, alignment, loc)
 	bytes, err := scratch_alloc_bytes(s, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -416,10 +416,10 @@ returns a slice of the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_alloc_bytes :: proc(
 scratch_alloc_bytes :: proc(
-	s: ^Scratch,
+	s:    ^Scratch,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
 	if bytes != nil {
@@ -437,10 +437,10 @@ This procedure returns a pointer to the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_alloc_non_zeroed :: proc(
 scratch_alloc_non_zeroed :: proc(
-	s: ^Scratch,
+	s:    ^Scratch,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -455,10 +455,10 @@ This procedure returns a slice of the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_alloc_bytes_non_zeroed :: proc(
 scratch_alloc_bytes_non_zeroed :: proc(
-	s: ^Scratch,
+	s:   ^Scratch,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 	if s.data == nil {
 		DEFAULT_BACKING_SIZE :: 4 * Megabyte
 		DEFAULT_BACKING_SIZE :: 4 * Megabyte
@@ -477,7 +477,7 @@ scratch_alloc_bytes_non_zeroed :: proc(
 			offset = 0
 			offset = 0
 		}
 		}
 		start := uintptr(raw_data(s.data))
 		start := uintptr(raw_data(s.data))
-		ptr := align_forward_uintptr(offset+start, uintptr(alignment))
+		ptr   := align_forward_uintptr(offset+start, uintptr(alignment))
 		s.prev_allocation = rawptr(ptr)
 		s.prev_allocation = rawptr(ptr)
 		s.curr_offset = int(offset) + size
 		s.curr_offset = int(offset) + size
 		return byte_slice(rawptr(ptr), size), nil
 		return byte_slice(rawptr(ptr), size), nil
@@ -574,12 +574,12 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_resize :: proc(
 scratch_resize :: proc(
-	s: ^Scratch,
+	s:          ^Scratch,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -603,11 +603,11 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_resize_bytes :: proc(
 scratch_resize_bytes :: proc(
-	s: ^Scratch,
+	s:        ^Scratch,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
 	bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
 	if bytes != nil && size > len(old_data) {
 	if bytes != nil && size > len(old_data) {
@@ -634,12 +634,12 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_resize_non_zeroed :: proc(
 scratch_resize_non_zeroed :: proc(
-	s: ^Scratch,
+	s:          ^Scratch,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -663,11 +663,11 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 scratch_resize_bytes_non_zeroed :: proc(
 scratch_resize_bytes_non_zeroed :: proc(
-	s: ^Scratch,
+	s:        ^Scratch,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	old_memory := raw_data(old_data)
 	old_memory := raw_data(old_data)
 	old_size := len(old_data)
 	old_size := len(old_data)
@@ -678,8 +678,8 @@ scratch_resize_bytes_non_zeroed :: proc(
 		}
 		}
 		scratch_init(s, DEFAULT_BACKING_SIZE)
 		scratch_init(s, DEFAULT_BACKING_SIZE)
 	}
 	}
-	begin := uintptr(raw_data(s.data))
-	end := begin + uintptr(len(s.data))
+	begin   := uintptr(raw_data(s.data))
+	end     := begin + uintptr(len(s.data))
 	old_ptr := uintptr(old_memory)
 	old_ptr := uintptr(old_memory)
 	if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
 	if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
 		s.curr_offset = int(old_ptr-begin)+size
 		s.curr_offset = int(old_ptr-begin)+size
@@ -695,11 +695,11 @@ scratch_resize_bytes_non_zeroed :: proc(
 }
 }
 
 
 scratch_allocator_proc :: proc(
 scratch_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
 	size, alignment: int,
 	size, alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	s := (^Scratch)(allocator_data)
 	s := (^Scratch)(allocator_data)
@@ -735,10 +735,10 @@ scratch_allocator_proc :: proc(
 Stack allocator data.
 Stack allocator data.
 */
 */
 Stack :: struct {
 Stack :: struct {
-	data: []byte,
+	data:        []byte,
 	prev_offset: int,
 	prev_offset: int,
 	curr_offset: int,
 	curr_offset: int,
-	peak_used: int,
+	peak_used:   int,
 }
 }
 
 
 /*
 /*
@@ -769,7 +769,7 @@ previous allocation header.
 stack_allocator :: proc(stack: ^Stack) -> Allocator {
 stack_allocator :: proc(stack: ^Stack) -> Allocator {
 	return Allocator{
 	return Allocator{
 		procedure = stack_allocator_proc,
 		procedure = stack_allocator_proc,
-		data = stack,
+		data      = stack,
 	}
 	}
 }
 }
 
 
@@ -780,18 +780,18 @@ This procedure initializes the stack allocator with a backing buffer specified
 by `data` parameter.
 by `data` parameter.
 */
 */
 stack_init :: proc(s: ^Stack, data: []byte) {
 stack_init :: proc(s: ^Stack, data: []byte) {
-	s.data = data
+	s.data        = data
 	s.prev_offset = 0
 	s.prev_offset = 0
 	s.curr_offset = 0
 	s.curr_offset = 0
-	s.peak_used = 0
+	s.peak_used   = 0
 }
 }
 
 
 @(deprecated="prefer 'mem.stack_init'")
 @(deprecated="prefer 'mem.stack_init'")
 init_stack :: proc(s: ^Stack, data: []byte) {
 init_stack :: proc(s: ^Stack, data: []byte) {
-	s.data = data
+	s.data        = data
 	s.prev_offset = 0
 	s.prev_offset = 0
 	s.curr_offset = 0
 	s.curr_offset = 0
-	s.peak_used = 0
+	s.peak_used   = 0
 }
 }
 
 
 /*
 /*
@@ -803,10 +803,10 @@ procedure returns the pointer to the allocated memory.
 */
 */
 @(require_results)
 @(require_results)
 stack_alloc :: proc(
 stack_alloc :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_alloc_bytes(s, size, alignment, loc)
 	bytes, err := stack_alloc_bytes(s, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -821,10 +821,10 @@ procedure returns the slice of the allocated memory.
 */
 */
 @(require_results)
 @(require_results)
 stack_alloc_bytes :: proc(
 stack_alloc_bytes :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
 	if bytes != nil {
@@ -842,10 +842,10 @@ zero-initialized. This procedure returns the pointer to the allocated memory.
 */
 */
 @(require_results)
 @(require_results)
 stack_alloc_non_zeroed :: proc(
 stack_alloc_non_zeroed :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -860,10 +860,10 @@ zero-initialized. This procedure returns the slice of the allocated memory.
 */
 */
 @(require_results)
 @(require_results)
 stack_alloc_bytes_non_zeroed :: proc(
 stack_alloc_bytes_non_zeroed :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 	if s.data == nil {
 		panic("Stack allocation on an uninitialized stack allocator", loc)
 		panic("Stack allocation on an uninitialized stack allocator", loc)
@@ -896,7 +896,7 @@ If the freeing does is an out of order freeing, the `.Invalid_Pointer` error
 is returned.
 is returned.
 */
 */
 stack_free :: proc(
 stack_free :: proc(
-	s: ^Stack,
+	s:          ^Stack,
 	old_memory: rawptr,
 	old_memory: rawptr,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> (Allocator_Error) {
 ) -> (Allocator_Error) {
@@ -953,12 +953,12 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 stack_resize :: proc(
 stack_resize :: proc(
-	s: ^Stack,
+	s:          ^Stack,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment)
 	bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -982,11 +982,11 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 stack_resize_bytes :: proc(
 stack_resize_bytes :: proc(
-	s: ^Stack,
+	s:        ^Stack,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
 	if bytes != nil {
@@ -1017,12 +1017,12 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 stack_resize_non_zeroed :: proc(
 stack_resize_non_zeroed :: proc(
-	s: ^Stack,
+	s:          ^Stack,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment)
 	bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -1046,11 +1046,11 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 stack_resize_bytes_non_zeroed :: proc(
 stack_resize_bytes_non_zeroed :: proc(
-	s: ^Stack,
+	s:        ^Stack,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	old_memory := raw_data(old_data)
 	old_memory := raw_data(old_data)
 	old_size := len(old_data)
 	old_size := len(old_data)
@@ -1063,8 +1063,8 @@ stack_resize_bytes_non_zeroed :: proc(
 	if size == 0 {
 	if size == 0 {
 		return nil, nil
 		return nil, nil
 	}
 	}
-	start := uintptr(raw_data(s.data))
-	end := start + uintptr(len(s.data))
+	start     := uintptr(raw_data(s.data))
+	end       := start + uintptr(len(s.data))
 	curr_addr := uintptr(old_memory)
 	curr_addr := uintptr(old_memory)
 	if !(start <= curr_addr && curr_addr < end) {
 	if !(start <= curr_addr && curr_addr < end) {
 		panic("Out of bounds memory address passed to stack allocator (resize)")
 		panic("Out of bounds memory address passed to stack allocator (resize)")
@@ -1097,11 +1097,11 @@ stack_resize_bytes_non_zeroed :: proc(
 
 
 stack_allocator_proc :: proc(
 stack_allocator_proc :: proc(
 	allocator_data: rawptr,
 	allocator_data: rawptr,
-	mode: Allocator_Mode,
-	size: int,
-	alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	mode:           Allocator_Mode,
+	size:           int,
+	alignment:      int,
+	old_memory:     rawptr,
+	old_size:       int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	s := cast(^Stack)allocator_data
 	s := cast(^Stack)allocator_data
@@ -1200,10 +1200,10 @@ returns a pointer to the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_alloc :: proc(
 small_stack_alloc :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_alloc_bytes(s, size, alignment, loc)
 	bytes, err := small_stack_alloc_bytes(s, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -1218,10 +1218,10 @@ returns a slice of the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_alloc_bytes :: proc(
 small_stack_alloc_bytes :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
 	if bytes != nil {
@@ -1239,10 +1239,10 @@ procedure returns a pointer to the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_alloc_non_zeroed :: proc(
 small_stack_alloc_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -1257,10 +1257,10 @@ procedure returns a slice of the allocated memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_alloc_bytes_non_zeroed :: proc(
 small_stack_alloc_bytes_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 	if s.data == nil {
 		panic("Small stack is not initialized", loc)
 		panic("Small stack is not initialized", loc)
@@ -1289,7 +1289,7 @@ by `alignment`. The allocated memory is not explicitly zero-initialized. This
 procedure returns a slice of the allocated memory region.
 procedure returns a slice of the allocated memory region.
 */
 */
 small_stack_free :: proc(
 small_stack_free :: proc(
-	s: ^Small_Stack,
+	s:          ^Small_Stack,
 	old_memory: rawptr,
 	old_memory: rawptr,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> Allocator_Error {
 ) -> Allocator_Error {
@@ -1341,12 +1341,12 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_resize :: proc(
 small_stack_resize :: proc(
-	s: ^Small_Stack,
+	s:          ^Small_Stack,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -1370,11 +1370,11 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_resize_bytes :: proc(
 small_stack_resize_bytes :: proc(
-	s: ^Small_Stack,
+	s:        ^Small_Stack,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
 	bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
 	if bytes != nil {
 	if bytes != nil {
@@ -1405,12 +1405,12 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_resize_non_zeroed :: proc(
 small_stack_resize_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:          ^Small_Stack,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
 	return raw_data(bytes), err
@@ -1434,18 +1434,18 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 small_stack_resize_bytes_non_zeroed :: proc(
 small_stack_resize_bytes_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:        ^Small_Stack,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 	if s.data == nil {
 		panic("Small stack is not initialized", loc)
 		panic("Small stack is not initialized", loc)
 	}
 	}
 	old_memory := raw_data(old_data)
 	old_memory := raw_data(old_data)
-	old_size := len(old_data)
-	alignment := alignment
+	old_size   := len(old_data)
+	alignment  := alignment
 	alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
 	alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
 	if old_memory == nil {
 	if old_memory == nil {
 		return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 		return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
@@ -1453,8 +1453,8 @@ small_stack_resize_bytes_non_zeroed :: proc(
 	if size == 0 {
 	if size == 0 {
 		return nil, nil
 		return nil, nil
 	}
 	}
-	start := uintptr(raw_data(s.data))
-	end := start + uintptr(len(s.data))
+	start     := uintptr(raw_data(s.data))
+	end       := start + uintptr(len(s.data))
 	curr_addr := uintptr(old_memory)
 	curr_addr := uintptr(old_memory)
 	if !(start <= curr_addr && curr_addr < end) {
 	if !(start <= curr_addr && curr_addr < end) {
 		// panic("Out of bounds memory address passed to stack allocator (resize)");
 		// panic("Out of bounds memory address passed to stack allocator (resize)");
@@ -1476,11 +1476,11 @@ small_stack_resize_bytes_non_zeroed :: proc(
 }
 }
 
 
 small_stack_allocator_proc :: proc(
 small_stack_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
-    size, alignment: int,
-    old_memory: rawptr,
-	old_size: int,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
+	size, alignment: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	s := cast(^Small_Stack)allocator_data
 	s := cast(^Small_Stack)allocator_data
@@ -1514,17 +1514,17 @@ small_stack_allocator_proc :: proc(
 
 
 
 
 /* Preserved for compatibility */
 /* Preserved for compatibility */
-Dynamic_Pool :: Dynamic_Arena
-DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT
+Dynamic_Pool                          :: Dynamic_Arena
+DYNAMIC_POOL_BLOCK_SIZE_DEFAULT       :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT
 DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT
 DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT
-dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc
-dynamic_pool_free_all :: dynamic_arena_free_all
-dynamic_pool_reset :: dynamic_arena_reset
-dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes
-dynamic_pool_alloc :: dynamic_arena_alloc
-dynamic_pool_init :: dynamic_arena_init
-dynamic_pool_allocator :: dynamic_arena_allocator
-dynamic_pool_destroy :: dynamic_arena_destroy
+dynamic_pool_allocator_proc           :: dynamic_arena_allocator_proc
+dynamic_pool_free_all                 :: dynamic_arena_free_all
+dynamic_pool_reset                    :: dynamic_arena_reset
+dynamic_pool_alloc_bytes              :: dynamic_arena_alloc_bytes
+dynamic_pool_alloc                    :: dynamic_arena_alloc
+dynamic_pool_init                     :: dynamic_arena_init
+dynamic_pool_allocator                :: dynamic_arena_allocator
+dynamic_pool_destroy                  :: dynamic_arena_destroy
 
 
 /*
 /*
 Default block size for dynamic arena.
 Default block size for dynamic arena.
@@ -1540,16 +1540,16 @@ DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554
 Dynamic arena allocator data.
 Dynamic arena allocator data.
 */
 */
 Dynamic_Arena :: struct {
 Dynamic_Arena :: struct {
-	block_size: int,
-	out_band_size: int,
-	alignment: int,
-	unused_blocks: [dynamic]rawptr,
-	used_blocks: [dynamic]rawptr,
+	block_size:           int,
+	out_band_size:        int,
+	alignment:            int,
+	unused_blocks:        [dynamic]rawptr,
+	used_blocks:          [dynamic]rawptr,
 	out_band_allocations: [dynamic]rawptr,
 	out_band_allocations: [dynamic]rawptr,
-	current_block: rawptr,
-	current_pos: rawptr,
-	bytes_left: int,
-	block_allocator: Allocator,
+	current_block:        rawptr,
+	current_pos:          rawptr,
+	bytes_left:           int,
+	block_allocator:      Allocator,
 }
 }
 
 
 /*
 /*
@@ -1565,17 +1565,17 @@ dynamic_arena_init :: proc(
 	pool: ^Dynamic_Arena,
 	pool: ^Dynamic_Arena,
 	block_allocator := context.allocator,
 	block_allocator := context.allocator,
 	array_allocator := context.allocator,
 	array_allocator := context.allocator,
-	block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT,
-	out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT,
-	alignment := DEFAULT_ALIGNMENT,
+	block_size      := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT,
+	out_band_size   := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT,
+	alignment       := DEFAULT_ALIGNMENT,
 ) {
 ) {
-	pool.block_size = block_size
-	pool.out_band_size = out_band_size
-	pool.alignment = alignment
-	pool.block_allocator = block_allocator
+	pool.block_size                     = block_size
+	pool.out_band_size                  = out_band_size
+	pool.alignment                      = alignment
+	pool.block_allocator                = block_allocator
 	pool.out_band_allocations.allocator = array_allocator
 	pool.out_band_allocations.allocator = array_allocator
-	pool.unused_blocks.allocator = array_allocator
-	pool.used_blocks.allocator = array_allocator
+	pool.unused_blocks.allocator        = array_allocator
+	pool.used_blocks.allocator          = array_allocator
 }
 }
 
 
 /*
 /*
@@ -1783,10 +1783,10 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 dynamic_arena_resize :: proc(
 dynamic_arena_resize :: proc(
-	a: ^Dynamic_Arena,
+	a:          ^Dynamic_Arena,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc)
 	bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc)
@@ -1811,9 +1811,9 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 dynamic_arena_resize_bytes :: proc(
 dynamic_arena_resize_bytes :: proc(
-	a: ^Dynamic_Arena,
+	a:        ^Dynamic_Arena,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc)
 	bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc)
@@ -1845,10 +1845,10 @@ This procedure returns the pointer to the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 dynamic_arena_resize_non_zeroed :: proc(
 dynamic_arena_resize_non_zeroed :: proc(
-	a: ^Dynamic_Arena,
+	a:          ^Dynamic_Arena,
 	old_memory: rawptr,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc)
 	bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc)
@@ -1873,9 +1873,9 @@ This procedure returns the slice of the resized memory region.
 */
 */
 @(require_results)
 @(require_results)
 dynamic_arena_resize_bytes_non_zeroed :: proc(
 dynamic_arena_resize_bytes_non_zeroed :: proc(
-	a: ^Dynamic_Arena,
+	a:        ^Dynamic_Arena,
 	old_data: []byte,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	old_memory := raw_data(old_data)
 	old_memory := raw_data(old_data)
@@ -1892,11 +1892,11 @@ dynamic_arena_resize_bytes_non_zeroed :: proc(
 
 
 dynamic_arena_allocator_proc :: proc(
 dynamic_arena_allocator_proc :: proc(
 	allocator_data: rawptr,
 	allocator_data: rawptr,
-	mode: Allocator_Mode,
-	size: int,
-	alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	mode:           Allocator_Mode,
+	size:           int,
+	alignment:      int,
+	old_memory:     rawptr,
+	old_size:       int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	arena := (^Dynamic_Arena)(allocator_data)
 	arena := (^Dynamic_Arena)(allocator_data)
@@ -2048,7 +2048,7 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl
 			// to pick the buddy as it "bounces around" less
 			// to pick the buddy as it "bounces around" less
 			best_block = buddy
 			best_block = buddy
 		}
 		}
-		if (block.size <= buddy.size) {
+		if block.size <= buddy.size {
 			block = buddy_block_next(buddy)
 			block = buddy_block_next(buddy)
 			if (block < tail) {
 			if (block < tail) {
 				// Delay the buddy block for the next iteration
 				// Delay the buddy block for the next iteration
@@ -2072,8 +2072,8 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl
 The buddy allocator data.
 The buddy allocator data.
 */
 */
 Buddy_Allocator :: struct {
 Buddy_Allocator :: struct {
-	head: ^Buddy_Block,
-	tail: ^Buddy_Block,
+	head:      ^Buddy_Block,
+	tail:      ^Buddy_Block,
 	alignment: uint,
 	alignment: uint,
 }
 }
 
 
@@ -2234,11 +2234,11 @@ buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) {
 }
 }
 
 
 buddy_allocator_proc :: proc(
 buddy_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
 	size, alignment: int,
 	size, alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 ) -> ([]byte, Allocator_Error) {
 	b := (^Buddy_Allocator)(allocator_data)
 	b := (^Buddy_Allocator)(allocator_data)

+ 3 - 1
core/mem/mem.odin

@@ -461,10 +461,12 @@ Check if a pointer is aligned.
 
 
 This procedure checks whether a pointer `x` is aligned to a boundary specified
 This procedure checks whether a pointer `x` is aligned to a boundary specified
 by `align`, and returns `true` if the pointer is aligned, and false otherwise.
 by `align`, and returns `true` if the pointer is aligned, and false otherwise.
+
+The specified alignment must be a power of 2.
 */
 */
 is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool {
 is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool {
 	p := uintptr(x)
 	p := uintptr(x)
-	return (p & (1<<uintptr(align) - 1)) == 0
+	return (p & (uintptr(align) - 1)) == 0
 }
 }
 
 
 /*
 /*

+ 1 - 1
core/mem/mutex_allocator.odin

@@ -1,4 +1,4 @@
-#+build !freestanding
+#+build !freestanding, wasm32, wasm64p32
 package mem
 package mem
 
 
 import "core:sync"
 import "core:sync"

+ 1 - 1
core/mem/tracking_allocator.odin

@@ -1,4 +1,4 @@
-#+build !freestanding
+#+build !freestanding, wasm32, wasm64p32
 package mem
 package mem
 
 
 import "base:runtime"
 import "base:runtime"

+ 3 - 7
core/mem/virtual/virtual_posix.odin

@@ -6,17 +6,13 @@ import "core:sys/posix"
 
 
 // Define non-posix needed flags:
 // Define non-posix needed flags:
 when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
-	MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
-
-	MADV_FREE     :: 5      /* pages unneeded, discard contents */
+	MADV_FREE :: 5      /* pages unneeded, discard contents */
 } else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
 } else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
-	MAP_ANONYMOUS :: 0x1000
-
-	MADV_FREE     :: 6
+	MADV_FREE :: 6
 }
 }
 
 
 _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
 _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
-	flags  := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS)
+	flags  := posix.Map_Flags{ .ANONYMOUS, .PRIVATE }
 	result := posix.mmap(nil, size, {}, flags)
 	result := posix.mmap(nil, size, {}, flags)
 	if result == posix.MAP_FAILED {
 	if result == posix.MAP_FAILED {
 		return nil, .Out_Of_Memory
 		return nil, .Out_Of_Memory

+ 3 - 2
core/net/socket_darwin.odin

@@ -88,8 +88,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
 	sockaddr := _endpoint_to_sockaddr(endpoint)
 	sockaddr := _endpoint_to_sockaddr(endpoint)
 	res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	if res != nil {
 	if res != nil {
-		err = Dial_Error(os.is_platform_error(res) or_else -1)
-		return
+		close(skt)
+		return {}, Dial_Error(os.is_platform_error(res) or_else -1)
 	}
 	}
 
 
 	return
 	return
@@ -120,6 +120,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
 	family := family_from_endpoint(interface_endpoint)
 	family := family_from_endpoint(interface_endpoint)
 	sock := create_socket(family, .TCP) or_return
 	sock := create_socket(family, .TCP) or_return
 	skt = sock.(TCP_Socket)
 	skt = sock.(TCP_Socket)
+	defer if err != nil { close(skt) }
 
 
 	// NOTE(tetra): This is so that if we crash while the socket is open, we can
 	// NOTE(tetra): This is so that if we crash while the socket is open, we can
 	// bypass the cooldown period, and allow the next run of the program to
 	// bypass the cooldown period, and allow the next run of the program to

+ 3 - 2
core/net/socket_freebsd.odin

@@ -114,8 +114,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
 	sockaddr := _endpoint_to_sockaddr(endpoint)
 	sockaddr := _endpoint_to_sockaddr(endpoint)
 	errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
 	errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
 	if errno != nil {
 	if errno != nil {
-		err = cast(Dial_Error)errno
-		return
+		close(socket)
+		return {}, cast(Dial_Error)errno
 	}
 	}
 
 
 	return
 	return
@@ -137,6 +137,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
 	family := family_from_endpoint(interface_endpoint)
 	family := family_from_endpoint(interface_endpoint)
 	new_socket := create_socket(family, .TCP) or_return
 	new_socket := create_socket(family, .TCP) or_return
 	socket = new_socket.(TCP_Socket)
 	socket = new_socket.(TCP_Socket)
+	defer if err != nil { close(socket) }
 
 
 	bind(socket, interface_endpoint) or_return
 	bind(socket, interface_endpoint) or_return
 
 

+ 23 - 14
core/net/socket_linux.odin

@@ -147,7 +147,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
 	addr := _unwrap_os_addr(endpoint)
 	addr := _unwrap_os_addr(endpoint)
 	errno = linux.connect(linux.Fd(os_sock), &addr)
 	errno = linux.connect(linux.Fd(os_sock), &addr)
 	if errno != .NONE {
 	if errno != .NONE {
-		return cast(TCP_Socket) os_sock, Dial_Error(errno)
+		close(cast(TCP_Socket) os_sock)
+		return {}, Dial_Error(errno)
 	}
 	}
 	// NOTE(tetra): Not vital to succeed; error ignored
 	// NOTE(tetra): Not vital to succeed; error ignored
 	no_delay: b32 = cast(b32) options.no_delay
 	no_delay: b32 = cast(b32) options.no_delay
@@ -166,40 +167,48 @@ _bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
 }
 }
 
 
 @(private)
 @(private)
-_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network_Error) {
+_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
 	errno: linux.Errno
 	errno: linux.Errno
 	assert(backlog > 0 && i32(backlog) < max(i32))
 	assert(backlog > 0 && i32(backlog) < max(i32))
+
 	// Figure out the address family and address of the endpoint
 	// Figure out the address family and address of the endpoint
 	ep_family := _unwrap_os_family(family_from_endpoint(endpoint))
 	ep_family := _unwrap_os_family(family_from_endpoint(endpoint))
 	ep_address := _unwrap_os_addr(endpoint)
 	ep_address := _unwrap_os_addr(endpoint)
+
 	// Create TCP socket
 	// Create TCP socket
 	os_sock: linux.Fd
 	os_sock: linux.Fd
 	os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
 	os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
 	if errno != .NONE {
 	if errno != .NONE {
-		// TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
-		return {}, Create_Socket_Error(errno)
+		err = Create_Socket_Error(errno)
+		return
 	}
 	}
+	socket = cast(TCP_Socket)os_sock
+	defer if err != nil { close(socket) }
+
 	// NOTE(tetra): This is so that if we crash while the socket is open, we can
 	// NOTE(tetra): This is so that if we crash while the socket is open, we can
 	// bypass the cooldown period, and allow the next run of the program to
 	// bypass the cooldown period, and allow the next run of the program to
 	// use the same address immediately.
 	// use the same address immediately.
 	//
 	//
 	// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
 	// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
 	do_reuse_addr: b32 = true
 	do_reuse_addr: b32 = true
-	errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr)
-	if errno != .NONE {
-		return cast(TCP_Socket) os_sock, Listen_Error(errno)
+	if errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr); errno != .NONE {
+		err = Listen_Error(errno)
+		return
 	}
 	}
+
 	// Bind the socket to endpoint address
 	// Bind the socket to endpoint address
-	errno = linux.bind(os_sock, &ep_address)
-	if errno != .NONE {
-		return cast(TCP_Socket) os_sock, Bind_Error(errno)
+	if errno = linux.bind(os_sock, &ep_address); errno != .NONE {
+		err = Bind_Error(errno)
+		return
 	}
 	}
+
 	// Listen on bound socket
 	// Listen on bound socket
-	errno = linux.listen(os_sock, cast(i32) backlog)
-	if errno != .NONE {
-		return cast(TCP_Socket) os_sock, Listen_Error(errno)
+	if errno = linux.listen(os_sock, cast(i32) backlog); errno != .NONE {
+		err = Listen_Error(errno)
+		return
 	}
 	}
-	return cast(TCP_Socket) os_sock, nil
+
+	return
 }
 }
 
 
 @(private)
 @(private)

+ 3 - 2
core/net/socket_windows.odin

@@ -80,8 +80,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
 	sockaddr := _endpoint_to_sockaddr(endpoint)
 	sockaddr := _endpoint_to_sockaddr(endpoint)
 	res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
 	res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
 	if res < 0 {
 	if res < 0 {
-		err = Dial_Error(win.WSAGetLastError())
-		return
+		close(socket)
+		return {}, Dial_Error(win.WSAGetLastError())
 	}
 	}
 
 
 	if options.no_delay {
 	if options.no_delay {
@@ -107,6 +107,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
 	family := family_from_endpoint(interface_endpoint)
 	family := family_from_endpoint(interface_endpoint)
 	sock := create_socket(family, .TCP) or_return
 	sock := create_socket(family, .TCP) or_return
 	socket = sock.(TCP_Socket)
 	socket = sock.(TCP_Socket)
+	defer if err != nil { close(socket) }
 
 
 	// NOTE(tetra): While I'm not 100% clear on it, my understanding is that this will
 	// NOTE(tetra): While I'm not 100% clear on it, my understanding is that this will
 	// prevent hijacking of the server's endpoint by other applications.
 	// prevent hijacking of the server's endpoint by other applications.

+ 12 - 11
core/odin/ast/ast.odin

@@ -776,17 +776,18 @@ Dynamic_Array_Type :: struct {
 
 
 Struct_Type :: struct {
 Struct_Type :: struct {
 	using node: Expr,
 	using node: Expr,
-	tok_pos:       tokenizer.Pos,
-	poly_params:   ^Field_List,
-	align:         ^Expr,
-	field_align:   ^Expr,
-	where_token:   tokenizer.Token,
-	where_clauses: []^Expr,
-	is_packed:     bool,
-	is_raw_union:  bool,
-	is_no_copy:    bool,
-	fields:        ^Field_List,
-	name_count:    int,
+	tok_pos:         tokenizer.Pos,
+	poly_params:     ^Field_List,
+	align:           ^Expr,
+	min_field_align: ^Expr,
+	max_field_align: ^Expr,
+	where_token:     tokenizer.Token,
+	where_clauses:   []^Expr,
+	is_packed:       bool,
+	is_raw_union:    bool,
+	is_no_copy:      bool,
+	fields:          ^Field_List,
+	name_count:      int,
 }
 }
 
 
 Union_Type_Kind :: enum u8 {
 Union_Type_Kind :: enum u8 {

+ 2 - 1
core/odin/ast/clone.odin

@@ -316,7 +316,8 @@ clone_node :: proc(node: ^Node) -> ^Node {
 		case ^Struct_Type:
 		case ^Struct_Type:
 			r.poly_params = auto_cast clone(r.poly_params)
 			r.poly_params = auto_cast clone(r.poly_params)
 			r.align = clone(r.align)
 			r.align = clone(r.align)
-			r.field_align = clone(r.field_align)
+			r.min_field_align = clone(r.min_field_align)
+			r.max_field_align = clone(r.max_field_align)
 			r.fields = auto_cast clone(r.fields)
 			r.fields = auto_cast clone(r.fields)
 		case ^Union_Type:
 		case ^Union_Type:
 			r.poly_params = auto_cast clone(r.poly_params)
 			r.poly_params = auto_cast clone(r.poly_params)

+ 31 - 16
core/odin/parser/parser.odin

@@ -2610,9 +2610,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 	case .Struct:
 	case .Struct:
 		tok := expect_token(p, .Struct)
 		tok := expect_token(p, .Struct)
 
 
-		poly_params: ^ast.Field_List
-		align:        ^ast.Expr
-		field_align:  ^ast.Expr
+		poly_params:     ^ast.Field_List
+		align:           ^ast.Expr
+		min_field_align: ^ast.Expr
+		max_field_align: ^ast.Expr
 		is_packed:    bool
 		is_packed:    bool
 		is_raw_union: bool
 		is_raw_union: bool
 		is_no_copy:   bool
 		is_no_copy:   bool
@@ -2645,10 +2646,21 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 				}
 				}
 				align = parse_expr(p, true)
 				align = parse_expr(p, true)
 			case "field_align":
 			case "field_align":
-				if field_align != nil {
+				if min_field_align != nil {
+					error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
+				}
+				warn(p, tag.pos, "#field_align has been deprecated in favour of #min_field_align")
+				min_field_align = parse_expr(p, true)
+			case "min_field_align":
+				if min_field_align != nil {
 					error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
 					error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
 				}
 				}
-				field_align = parse_expr(p, true)
+				min_field_align = parse_expr(p, true)
+			case "max_field_align":
+				if max_field_align != nil {
+					error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
+				}
+				max_field_align = parse_expr(p, true)
 			case "raw_union":
 			case "raw_union":
 				if is_raw_union {
 				if is_raw_union {
 					error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
 					error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
@@ -2689,16 +2701,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		close := expect_closing_brace_of_field_list(p)
 		close := expect_closing_brace_of_field_list(p)
 
 
 		st := ast.new(ast.Struct_Type, tok.pos, end_pos(close))
 		st := ast.new(ast.Struct_Type, tok.pos, end_pos(close))
-		st.poly_params   = poly_params
-		st.align         = align
-		st.field_align   = field_align
-		st.is_packed     = is_packed
-		st.is_raw_union  = is_raw_union
-		st.is_no_copy    = is_no_copy
-		st.fields        = fields
-		st.name_count    = name_count
-		st.where_token   = where_token
-		st.where_clauses = where_clauses
+		st.poly_params     = poly_params
+		st.align           = align
+		st.min_field_align = min_field_align
+		st.max_field_align = max_field_align
+		st.is_packed       = is_packed
+		st.is_raw_union    = is_raw_union
+		st.is_no_copy      = is_no_copy
+		st.fields          = fields
+		st.name_count      = name_count
+		st.where_token     = where_token
+		st.where_clauses   = where_clauses
 		return st
 		return st
 
 
 	case .Union:
 	case .Union:
@@ -3683,6 +3696,8 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
 		}
 		}
 	}
 	}
 
 
+	end := p.prev_tok
+
 	if p.expr_level >= 0 {
 	if p.expr_level >= 0 {
 		end: ^ast.Expr
 		end: ^ast.Expr
 		if !is_mutable && len(values) > 0 {
 		if !is_mutable && len(values) > 0 {
@@ -3702,7 +3717,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
 		}
 		}
 	}
 	}
 
 
-	decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok))
+	decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(end))
 	decl.docs = docs
 	decl.docs = docs
 	decl.names = names
 	decl.names = names
 	decl.type = type
 	decl.type = type

+ 1 - 1
core/odin/tokenizer/tokenizer.odin

@@ -331,7 +331,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
 		n -= 1
 		n -= 1
 	}
 	}
 
 
-	if x > max || 0xd800 <= x && x <= 0xe000 {
+	if x > max || 0xd800 <= x && x <= 0xdfff {
 		error(t, offset, "escape sequence is an invalid Unicode code point")
 		error(t, offset, "escape sequence is an invalid Unicode code point")
 		return false
 		return false
 	}
 	}

+ 1 - 1
core/os/os2/allocators.odin

@@ -62,8 +62,8 @@ TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_locati
 
 
 @(deferred_out=TEMP_ALLOCATOR_GUARD_END)
 @(deferred_out=TEMP_ALLOCATOR_GUARD_END)
 TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
 TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
-	tmp := temp_allocator_temp_begin(loc)
 	global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
 	global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
+	tmp := temp_allocator_temp_begin(loc)
 	return tmp, loc
 	return tmp, loc
 }
 }
 
 

+ 1 - 1
core/os/os2/dir_linux.odin

@@ -13,7 +13,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
 
 
 @(require_results)
 @(require_results)
 _read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
 _read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
-	return {}, nil
+	return {}, .Unsupported
 }
 }
 
 
 _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
 _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {

+ 4 - 6
core/os/os2/dir_windows.odin

@@ -16,28 +16,25 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
 	}
 	}
 	path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
 	path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
 
 
+	handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
+	defer win32.CloseHandle(handle)
 
 
 	fi.fullpath = path
 	fi.fullpath = path
 	fi.name = basename(path)
 	fi.name = basename(path)
 	fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
 	fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
 
 
-	fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
+	fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
 
 
 	fi.creation_time     = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
 	fi.creation_time     = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
 	fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
 	fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
 	fi.access_time       = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
 	fi.access_time       = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
 
 
-
-	handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
-	defer win32.CloseHandle(handle)
-
 	if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
 	if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
 		#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
 		#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
 		#assert(size_of(fi.inode) == 16)
 		#assert(size_of(fi.inode) == 16)
 		runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
 		runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
 	}
 	}
 
 
-
 	return
 	return
 }
 }
 
 
@@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
 		return
 		return
 	}
 	}
 	file_info_delete(it.impl.prev_fi, file_allocator())
 	file_info_delete(it.impl.prev_fi, file_allocator())
+	delete(it.impl.path, file_allocator())
 	win32.FindClose(it.impl.find_handle)
 	win32.FindClose(it.impl.find_handle)
 }
 }

+ 2 - 0
core/os/os2/errors_linux.odin

@@ -162,6 +162,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
 		return .Invalid_File
 		return .Invalid_File
 	case .ENOMEM:
 	case .ENOMEM:
 		return .Out_Of_Memory
 		return .Out_Of_Memory
+	case .ENOSYS:
+		return .Unsupported
 	}
 	}
 
 
 	return Platform_Error(i32(errno))
 	return Platform_Error(i32(errno))

+ 2 - 0
core/os/os2/errors_posix.odin

@@ -26,6 +26,8 @@ _get_platform_error :: proc() -> Error {
 		return .Invalid_File
 		return .Invalid_File
 	case .ENOMEM:
 	case .ENOMEM:
 		return .Out_Of_Memory
 		return .Out_Of_Memory
+	case .ENOSYS:
+		return .Unsupported
 	case:
 	case:
 		return Platform_Error(errno)
 		return Platform_Error(errno)
 	}
 	}

+ 3 - 1
core/os/os2/errors_windows.odin

@@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error {
 	case win32.ERROR_NEGATIVE_SEEK:
 	case win32.ERROR_NEGATIVE_SEEK:
 		return .Invalid_Offset
 		return .Invalid_Offset
 
 
+	case win32.ERROR_BROKEN_PIPE:
+		return .Broken_Pipe
+
 	case
 	case
 		win32.ERROR_BAD_ARGUMENTS,
 		win32.ERROR_BAD_ARGUMENTS,
 		win32.ERROR_INVALID_PARAMETER,
 		win32.ERROR_INVALID_PARAMETER,
 		win32.ERROR_NOT_ENOUGH_MEMORY,
 		win32.ERROR_NOT_ENOUGH_MEMORY,
 		win32.ERROR_NO_MORE_FILES,
 		win32.ERROR_NO_MORE_FILES,
 		win32.ERROR_LOCK_VIOLATION,
 		win32.ERROR_LOCK_VIOLATION,
-		win32.ERROR_BROKEN_PIPE,
 		win32.ERROR_CALL_NOT_IMPLEMENTED,
 		win32.ERROR_CALL_NOT_IMPLEMENTED,
 		win32.ERROR_INSUFFICIENT_BUFFER,
 		win32.ERROR_INSUFFICIENT_BUFFER,
 		win32.ERROR_INVALID_NAME,
 		win32.ERROR_INVALID_NAME,

+ 1 - 1
core/os/os2/file_util.odin

@@ -164,7 +164,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
 }
 }
 
 
 @(require_results)
 @(require_results)
-write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error {
+write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error {
 	flags := O_WRONLY|O_CREATE
 	flags := O_WRONLY|O_CREATE
 	if truncate {
 	if truncate {
 		flags |= O_TRUNC
 		flags |= O_TRUNC

+ 3 - 3
core/os/os2/file_windows.odin

@@ -50,9 +50,9 @@ init_std_files :: proc() {
 }
 }
 @(fini)
 @(fini)
 fini_std_files :: proc() {
 fini_std_files :: proc() {
-	close(stdin)
-	close(stdout)
-	close(stderr)
+	_destroy((^File_Impl)(stdin.impl))
+	_destroy((^File_Impl)(stdout.impl))
+	_destroy((^File_Impl)(stderr.impl))
 }
 }
 
 
 
 

+ 1 - 1
core/os/os2/path_posix.odin

@@ -40,7 +40,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
 
 
 	internal_mkdir_all :: proc(path: string, perm: int) -> Error {
 	internal_mkdir_all :: proc(path: string, perm: int) -> Error {
 		dir, file := filepath.split(path)
 		dir, file := filepath.split(path)
-		if file != path {
+		if file != path && dir != "/" {
 			if len(dir) > 1 && dir[len(dir) - 1] == '/' {
 			if len(dir) > 1 && dir[len(dir) - 1] == '/' {
 				dir = dir[:len(dir) - 1]
 				dir = dir[:len(dir) - 1]
 			}
 			}

+ 37 - 0
core/os/os2/pipe.odin

@@ -1,6 +1,43 @@
 package os2
 package os2
 
 
+/*
+Create an anonymous pipe.
+
+This procedure creates an anonymous pipe, returning two ends of the pipe, `r`
+and `w`. The file `r` is the readable end of the pipe. The file `w` is a
+writeable end of the pipe.
+
+Pipes are used as an inter-process communication mechanism, to communicate
+between a parent and a child process. The child uses one end of the pipe to
+write data, and the parent uses the other end to read from the pipe
+(or vice-versa). When a parent passes one of the ends of the pipe to the child
+process, that end of the pipe needs to be closed by the parent, before any data
+is attempted to be read.
+
+Although pipes look like files and is compatible with most file APIs in package
+os2, the way it's meant to be read is different. Due to asynchronous nature of
+the communication channel, the data may not be present at the time of a read
+request. The other scenario is when a pipe has no data because the other end
+of the pipe was closed by the child process.
+*/
 @(require_results)
 @(require_results)
 pipe :: proc() -> (r, w: ^File, err: Error) {
 pipe :: proc() -> (r, w: ^File, err: Error) {
 	return _pipe()
 	return _pipe()
 }
 }
+
+/*
+Check if the pipe has any data.
+
+This procedure checks whether a read-end of the pipe has data that can be
+read, and returns `true`, if the pipe has readable data, and `false` if the
+pipe is empty. This procedure does not block the execution of the current
+thread.
+
+**Note**: If the other end of the pipe was closed by the child process, the
+`.Broken_Pipe`
+can be returned by this procedure. Handle these errors accordingly.
+*/
+@(require_results)
+pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	return _pipe_has_data(r)
+}

+ 26 - 0
core/os/os2/pipe_linux.odin

@@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 
 
 	return
 	return
 }
 }
+
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	if r == nil || r.impl == nil {
+		return false, nil
+	}
+	fd := linux.Fd((^File_Impl)(r.impl).fd)
+	poll_fds := []linux.Poll_Fd {
+		linux.Poll_Fd {
+			fd = fd,
+			events = {.IN, .HUP},
+		},
+	}
+	n, errno := linux.poll(poll_fds, 0)
+	if n != 1 || errno != nil {
+		return false, _get_platform_error(errno)
+	}
+	pipe_events := poll_fds[0].revents
+	if pipe_events >= {.IN} {
+		return true, nil
+	}
+	if pipe_events >= {.HUP} {
+		return false, .Broken_Pipe
+	}
+	return false, nil
+}

+ 27 - 0
core/os/os2/pipe_posix.odin

@@ -44,3 +44,30 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 	return
 	return
 }
 }
 
 
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	if r == nil || r.impl == nil {
+		return false, nil
+	}
+	fd := __fd(r)
+	poll_fds := []posix.pollfd {
+		posix.pollfd {
+			fd = fd,
+			events = {.IN, .HUP},
+		},
+	}
+	n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0)
+	if n < 0 {
+		return false, _get_platform_error()
+	} else if n != 1 {
+		return false, nil
+	}
+	pipe_events := poll_fds[0].revents
+	if pipe_events >= {.IN} {
+		return true, nil
+	}
+	if pipe_events >= {.HUP} {
+		return false, .Broken_Pipe
+	}
+	return false, nil
+}

+ 12 - 0
core/os/os2/pipe_windows.odin

@@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
 	return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
 	return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
 }
 }
 
 
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+	if r == nil || r.impl == nil {
+		return false, nil
+	}
+	handle := win32.HANDLE((^File_Impl)(r.impl).fd)
+	bytes_available: u32
+	if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) {
+		return false, _get_platform_error()
+	}
+	return bytes_available > 0, nil
+}

+ 232 - 124
core/os/os2/process.odin

@@ -1,16 +1,17 @@
 package os2
 package os2
 
 
 import "base:runtime"
 import "base:runtime"
+
 import "core:time"
 import "core:time"
 
 
 /*
 /*
-	In procedures that explicitly state this as one of the allowed values,
-	specifies an infinite timeout.
+In procedures that explicitly state this as one of the allowed values,
+specifies an infinite timeout.
 */
 */
 TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
 TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
 
 
 /*
 /*
-	Arguments to the current process.
+Arguments to the current process.
 */
 */
 args := get_args()
 args := get_args()
 
 
@@ -24,17 +25,17 @@ get_args :: proc() -> []string {
 }
 }
 
 
 /*
 /*
-	Exit the current process.
+Exit the current process.
 */
 */
 exit :: proc "contextless" (code: int) -> ! {
 exit :: proc "contextless" (code: int) -> ! {
 	_exit(code)
 	_exit(code)
 }
 }
 
 
 /*
 /*
-	Obtain the UID of the current process.
+Obtain the UID of the current process.
 
 
-	**Note(windows)**: Windows doesn't follow the posix permissions model, so
-	the function simply returns -1.
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
 */
 */
 @(require_results)
 @(require_results)
 get_uid :: proc() -> int {
 get_uid :: proc() -> int {
@@ -42,15 +43,15 @@ get_uid :: proc() -> int {
 }
 }
 
 
 /*
 /*
-	Obtain the effective UID of the current process.
-
-	The effective UID is typically the same as the UID of the process. In case
-	the process was run by a user with elevated permissions, the process may
-	lower the privilege to perform some tasks without privilege. In these cases
-	the real UID of the process and the effective UID are different.
-	
-	**Note(windows)**: Windows doesn't follow the posix permissions model, so
-	the function simply returns -1.
+Obtain the effective UID of the current process.
+
+The effective UID is typically the same as the UID of the process. In case
+the process was run by a user with elevated permissions, the process may
+lower the privilege to perform some tasks without privilege. In these cases
+the real UID of the process and the effective UID are different.
+
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
 */
 */
 @(require_results)
 @(require_results)
 get_euid :: proc() -> int {
 get_euid :: proc() -> int {
@@ -58,10 +59,10 @@ get_euid :: proc() -> int {
 }
 }
 
 
 /*
 /*
-	Obtain the GID of the current process.
-	
-	**Note(windows)**: Windows doesn't follow the posix permissions model, so
-	the function simply returns -1.
+Obtain the GID of the current process.
+
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
 */
 */
 @(require_results)
 @(require_results)
 get_gid :: proc() -> int {
 get_gid :: proc() -> int {
@@ -69,15 +70,15 @@ get_gid :: proc() -> int {
 }
 }
 
 
 /*
 /*
-	Obtain the effective GID of the current process.
-	
-	The effective GID is typically the same as the GID of the process. In case
-	the process was run by a user with elevated permissions, the process may
-	lower the privilege to perform some tasks without privilege. In these cases
-	the real GID of the process and the effective GID are different.
-
-	**Note(windows)**: Windows doesn't follow the posix permissions model, so
-	the function simply returns -1.
+Obtain the effective GID of the current process.
+
+The effective GID is typically the same as the GID of the process. In case
+the process was run by a user with elevated permissions, the process may
+lower the privilege to perform some tasks without privilege. In these cases
+the real GID of the process and the effective GID are different.
+
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
 */
 */
 @(require_results)
 @(require_results)
 get_egid :: proc() -> int {
 get_egid :: proc() -> int {
@@ -85,7 +86,7 @@ get_egid :: proc() -> int {
 }
 }
 
 
 /*
 /*
-	Obtain the ID of the current process.
+Obtain the ID of the current process.
 */
 */
 @(require_results)
 @(require_results)
 get_pid :: proc() -> int {
 get_pid :: proc() -> int {
@@ -93,13 +94,13 @@ get_pid :: proc() -> int {
 }
 }
 
 
 /*
 /*
-	Obtain the ID of the parent process.
+Obtain the ID of the parent process.
 
 
-	**Note(windows)**: Windows does not mantain strong relationships between
-	parent and child processes. This function returns the ID of the process
-	that has created the current process. In case the parent has died, the ID
-	returned by this function can identify a non-existent or a different
-	process.
+**Note(windows)**: Windows does not mantain strong relationships between
+parent and child processes. This function returns the ID of the process
+that has created the current process. In case the parent has died, the ID
+returned by this function can identify a non-existent or a different
+process.
 */
 */
 @(require_results)
 @(require_results)
 get_ppid :: proc() -> int {
 get_ppid :: proc() -> int {
@@ -107,7 +108,7 @@ get_ppid :: proc() -> int {
 }
 }
 
 
 /*
 /*
-	Obtain ID's of all processes running in the system.
+Obtain ID's of all processes running in the system.
 */
 */
 @(require_results)
 @(require_results)
 process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
 process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
@@ -115,9 +116,9 @@ process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
 }
 }
 
 
 /*
 /*
-	Bit set specifying which fields of the `Process_Info` struct need to be
-	obtained by the `process_info()` procedure. Each bit corresponds to a
-	field in the `Process_Info` struct.
+Bit set specifying which fields of the `Process_Info` struct need to be
+obtained by the `process_info()` procedure. Each bit corresponds to a
+field in the `Process_Info` struct.
 */
 */
 Process_Info_Fields :: bit_set[Process_Info_Field]
 Process_Info_Fields :: bit_set[Process_Info_Field]
 Process_Info_Field :: enum {
 Process_Info_Field :: enum {
@@ -134,8 +135,8 @@ Process_Info_Field :: enum {
 ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
 ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
 
 
 /*
 /*
-	Contains information about the process as obtained by the `process_info()`
-	procedure.
+Contains information about the process as obtained by the `process_info()`
+procedure.
 */
 */
 Process_Info :: struct {
 Process_Info :: struct {
 	// The information about a process the struct contains. `pid` is always
 	// The information about a process the struct contains. `pid` is always
@@ -162,19 +163,19 @@ Process_Info :: struct {
 }
 }
 
 
 /*
 /*
-	Obtain information about a process.
+Obtain information about a process.
 
 
-	This procedure obtains an information, specified by `selection` parameter of
-	a process given by `pid`.
+This procedure obtains an information, specified by `selection` parameter of
+a process given by `pid`.
 
 
-	Use `free_process_info` to free the memory allocated by this procedure. The
-	`free_process_info` procedure needs to be called, even if this procedure
-	returned an error, as some of the fields may have been allocated.
+Use `free_process_info` to free the memory allocated by this procedure. The
+`free_process_info` procedure needs to be called, even if this procedure
+returned an error, as some of the fields may have been allocated.
 
 
-	**Note**: The resulting information may or may contain the fields specified
-	by the `selection` parameter. Always check whether the returned
-	`Process_Info` struct has the required fields before checking the error code
-	returned by this procedure.
+**Note**: The resulting information may or may contain the fields specified
+by the `selection` parameter. Always check whether the returned
+`Process_Info` struct has the required fields before checking the error code
+returned by this procedure.
 */
 */
 @(require_results)
 @(require_results)
 process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
 process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -182,20 +183,20 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
 }
 }
 
 
 /*
 /*
-	Obtain information about a process.
+Obtain information about a process.
 
 
-	This procedure obtains information, specified by `selection` parameter
-	about a process that has been opened by the application, specified in
-	the `process` parameter.
+This procedure obtains information, specified by `selection` parameter
+about a process that has been opened by the application, specified in
+the `process` parameter.
 
 
-	Use `free_process_info` to free the memory allocated by this procedure. The
-	`free_process_info` procedure needs to be called, even if this procedure
-	returned an error, as some of the fields may have been allocated.
+Use `free_process_info` to free the memory allocated by this procedure. The
+`free_process_info` procedure needs to be called, even if this procedure
+returned an error, as some of the fields may have been allocated.
 
 
-	**Note**: The resulting information may or may contain the fields specified
-	by the `selection` parameter. Always check whether the returned
-	`Process_Info` struct has the required fields before checking the error code
-	returned by this procedure.
+**Note**: The resulting information may or may contain the fields specified
+by the `selection` parameter. Always check whether the returned
+`Process_Info` struct has the required fields before checking the error code
+returned by this procedure.
 */
 */
 @(require_results)
 @(require_results)
 process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
 process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -203,19 +204,19 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
 }
 }
 
 
 /*
 /*
-	Obtain information about the current process.
+Obtain information about the current process.
 
 
-	This procedure obtains the information, specified by `selection` parameter
-	about the currently running process.
+This procedure obtains the information, specified by `selection` parameter
+about the currently running process.
 
 
-	Use `free_process_info` to free the memory allocated by this procedure. The
-	`free_process_info` procedure needs to be called, even if this procedure
-	returned an error, as some of the fields may have been allocated.
+Use `free_process_info` to free the memory allocated by this procedure. The
+`free_process_info` procedure needs to be called, even if this procedure
+returned an error, as some of the fields may have been allocated.
 
 
-	**Note**: The resulting information may or may contain the fields specified
-	by the `selection` parameter. Always check whether the returned
-	`Process_Info` struct has the required fields before checking the error code
-	returned by this procedure.
+**Note**: The resulting information may or may contain the fields specified
+by the `selection` parameter. Always check whether the returned
+`Process_Info` struct has the required fields before checking the error code
+returned by this procedure.
 */
 */
 @(require_results)
 @(require_results)
 current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
 current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -223,7 +224,7 @@ current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.
 }
 }
 
 
 /*
 /*
-	Obtain information about the specified process.
+Obtain information about the specified process.
 */
 */
 process_info :: proc {
 process_info :: proc {
 	process_info_by_pid,
 	process_info_by_pid,
@@ -232,11 +233,11 @@ process_info :: proc {
 }
 }
 
 
 /*
 /*
-	Free the information about the process.
+Free the information about the process.
 
 
-	This procedure frees the memory occupied by process info using the provided
-	allocator. The allocator needs to be the same allocator that was supplied
-	to the `process_info` function.
+This procedure frees the memory occupied by process info using the provided
+allocator. The allocator needs to be the same allocator that was supplied
+to the `process_info` function.
 */
 */
 free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
 free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
 	delete(pi.executable_path, allocator)
 	delete(pi.executable_path, allocator)
@@ -254,13 +255,13 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
 }
 }
 
 
 /*
 /*
-	Represents a process handle.
+Represents a process handle.
 
 
-	When a process dies, the OS is free to re-use the pid of that process. The
-	`Process` struct represents a handle to the process that will refer to a
-	specific process, even after it has died.
+When a process dies, the OS is free to re-use the pid of that process. The
+`Process` struct represents a handle to the process that will refer to a
+specific process, even after it has died.
 
 
-	**Note(linux)**: The `handle` will be referring to pidfd.
+**Note(linux)**: The `handle` will be referring to pidfd.
 */
 */
 Process :: struct {
 Process :: struct {
 	pid: int,
 	pid: int,
@@ -276,13 +277,13 @@ Process_Open_Flag :: enum {
 }
 }
 
 
 /*
 /*
-	Open a process handle using it's pid.
+Open a process handle using it's pid.
 
 
-	This procedure obtains a process handle of a process specified by `pid`.
-	This procedure can be subject to race conditions. See the description of
-	`Process`.
+This procedure obtains a process handle of a process specified by `pid`.
+This procedure can be subject to race conditions. See the description of
+`Process`.
 
 
-	Use `process_close()` function to close the process handle.
+Use `process_close()` function to close the process handle.
 */
 */
 @(require_results)
 @(require_results)
 process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
 process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
@@ -322,31 +323,139 @@ Process_Desc :: struct {
 }
 }
 
 
 /*
 /*
-	Create a new process and obtain its handle.
-
-	This procedure creates a new process, with a given command and environment
-	strings as parameters. Use `environ()` to inherit the environment of the
-	current process.
-
-	The `desc` parameter specifies the description of how the process should
-	be created. It contains information such as the command line, the
-	environment of the process, the starting directory and many other options.
-	Most of the fields in the struct can be set to `nil` or an empty value.
-	
-	Use `process_close` to close the handle to the process. Note, that this
-	is not the same as terminating the process. One can terminate the process
-	and not close the handle, in which case the handle would be leaked. In case
-	the function returns an error, an invalid handle is returned.
-
-	This procedure is not thread-safe. It may alter the inheritance properties
-	of file handles in an unpredictable manner. In case multiple threads change
-	handle inheritance properties, make sure to serialize all those calls.
+Create a new process and obtain its handle.
+
+This procedure creates a new process, with a given command and environment
+strings as parameters. Use `environ()` to inherit the environment of the
+current process.
+
+The `desc` parameter specifies the description of how the process should
+be created. It contains information such as the command line, the
+environment of the process, the starting directory and many other options.
+Most of the fields in the struct can be set to `nil` or an empty value.
+
+Use `process_close` to close the handle to the process. Note, that this
+is not the same as terminating the process. One can terminate the process
+and not close the handle, in which case the handle would be leaked. In case
+the function returns an error, an invalid handle is returned.
+
+This procedure is not thread-safe. It may alter the inheritance properties
+of file handles in an unpredictable manner. In case multiple threads change
+handle inheritance properties, make sure to serialize all those calls.
 */
 */
 @(require_results)
 @(require_results)
-process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
+process_start :: proc(desc: Process_Desc) -> (Process, Error) {
 	return _process_start(desc)
 	return _process_start(desc)
 }
 }
 
 
+/*
+Execute the process and capture stdout and stderr streams.
+
+This procedure creates a new process, with a given command and environment
+strings as parameters, and waits until the process finishes execution. While
+the process is running, this procedure accumulates the output of its stdout
+and stderr streams and returns byte slices containing the captured data from
+the streams.
+
+This procedure expects that `stdout` and `stderr` fields of the `desc` parameter
+are left at default, i.e. a `nil` value. You can not capture stdout/stderr and
+redirect it to a file at the same time.
+
+This procedure does not free `stdout` and `stderr` slices before an error is
+returned. Make sure to call `delete` on these slices.
+*/
+@(require_results)
+process_exec :: proc(
+	desc: Process_Desc,
+	allocator: runtime.Allocator,
+	loc := #caller_location,
+) -> (
+	state: Process_State,
+	stdout: []byte,
+	stderr: []byte,
+	err: Error,
+) {
+	assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
+	assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
+
+	stdout_r, stdout_w := pipe() or_return
+	defer close(stdout_r)
+	stderr_r, stderr_w := pipe() or_return
+	defer close(stderr_r)
+
+	process: Process
+	{
+		// NOTE(flysand): Make sure the write-ends are closed, regardless
+		// of the outcome. This makes read-ends readable on our side.
+		defer close(stdout_w)
+		defer close(stderr_w)
+		desc := desc
+		desc.stdout = stdout_w
+		desc.stderr = stderr_w
+		process = process_start(desc) or_return
+	}
+
+	{
+		stdout_b: [dynamic]byte
+		stdout_b.allocator = allocator
+		defer stdout = stdout_b[:]
+
+		stderr_b: [dynamic]byte
+		stderr_b.allocator = allocator
+		defer stderr = stderr_b[:]
+
+		buf: [1024]u8 = ---
+		
+		stdout_done, stderr_done, has_data: bool
+		for err == nil && (!stdout_done || !stderr_done) {
+			n := 0
+
+			if !stdout_done {
+				has_data, err = pipe_has_data(stdout_r)
+				if has_data {
+					n, err = read(stdout_r, buf[:])
+				}
+
+				switch err {
+				case nil:
+					_, err = append(&stdout_b, ..buf[:n])
+				case .EOF, .Broken_Pipe:
+					stdout_done = true
+					err = nil
+				}
+			}
+
+			if err == nil && !stderr_done {
+				n = 0
+				has_data, err = pipe_has_data(stderr_r)
+				if has_data {
+					n, err = read(stderr_r, buf[:])
+				}
+
+				switch err {
+				case nil:
+					_, err = append(&stderr_b, ..buf[:n])
+				case .EOF, .Broken_Pipe:
+					stderr_done = true
+					err = nil
+				}
+			}
+		}
+	}
+
+	if err != nil {
+		state, _ = process_wait(process, timeout=0)
+		if !state.exited {
+			_ = process_kill(process)
+			state, _ = process_wait(process)
+		}
+		return
+	}
+
+	state, err = process_wait(process)
+	return
+}
+
 /*
 /*
 	The state of the process after it has finished execution.
 	The state of the process after it has finished execution.
 */
 */
@@ -371,17 +480,17 @@ Process_State :: struct {
 }
 }
 
 
 /*
 /*
-	Wait for a process event.
+Wait for a process event.
 
 
-	This procedure blocks the execution until the process has exited or the
-	timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
-	no timeout restriction is imposed and the procedure can block indefinately.
+This procedure blocks the execution until the process has exited or the
+timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
+no timeout restriction is imposed and the procedure can block indefinately.
 
 
-	If the timeout has expired, the `General_Error.Timeout` is returned as
-	the error.
+If the timeout has expired, the `General_Error.Timeout` is returned as
+the error.
 
 
-	If an error is returned for any other reason, other than timeout, the
-	process state is considered undetermined.
+If an error is returned for any other reason, other than timeout, the
+process state is considered undetermined.
 */
 */
 @(require_results)
 @(require_results)
 process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
 process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
@@ -389,12 +498,12 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_
 }
 }
 
 
 /*
 /*
-	Close the handle to a process.
+Close the handle to a process.
 
 
-	This procedure closes the handle associated with a process. It **does not**
-	terminate a process, in case it was running. In case a termination is
-	desired, kill the process first, wait for the process to finish,
-	then close the handle.
+This procedure closes the handle associated with a process. It **does not**
+terminate a process, in case it was running. In case a termination is
+desired, kill the process first, wait for the process to finish,
+then close the handle.
 */
 */
 @(require_results)
 @(require_results)
 process_close :: proc(process: Process) -> (Error) {
 process_close :: proc(process: Process) -> (Error) {
@@ -402,10 +511,9 @@ process_close :: proc(process: Process) -> (Error) {
 }
 }
 
 
 /*
 /*
-	Terminate a process.
-
-	This procedure terminates a process, specified by it's handle, `process`.
+Terminate a process.
 
 
+This procedure terminates a process, specified by it's handle, `process`.
 */
 */
 @(require_results)
 @(require_results)
 process_kill :: proc(process: Process) -> (Error) {
 process_kill :: proc(process: Process) -> (Error) {

+ 1 - 1
core/os/os2/process_linux.odin

@@ -523,7 +523,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 		write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! {
 		write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! {
 			error_byte: [1]u8 = { u8(errno) }
 			error_byte: [1]u8 = { u8(errno) }
 			linux.write(parent_fd, error_byte[:])
 			linux.write(parent_fd, error_byte[:])
-			intrinsics.trap()
+			linux.exit(126)
 		}
 		}
 
 
 		stdin_fd: linux.Fd
 		stdin_fd: linux.Fd

+ 1 - 2
core/os/os2/process_posix.odin

@@ -163,7 +163,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 			#assert(len(posix.Errno) < max(u8))
 			#assert(len(posix.Errno) < max(u8))
 			errno := u8(posix.errno())
 			errno := u8(posix.errno())
 			posix.write(parent_fd, &errno, 1)
 			posix.write(parent_fd, &errno, 1)
-			runtime.trap()
+			posix.exit(126)
 		}
 		}
 
 
 		null := posix.open("/dev/null", {.RDWR})
 		null := posix.open("/dev/null", {.RDWR})
@@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 			return
 			return
 		}
 		}
 
 
-		process.pid = int(pid)
 		process, _ = _process_open(int(pid), {})
 		process, _ = _process_open(int(pid), {})
 		return
 		return
 	}
 	}

+ 1 - 0
core/os/os2/process_posix_other.odin

@@ -15,6 +15,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error)
 }
 }
 
 
 _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
 _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
+	process.pid = pid
 	err = .Unsupported
 	err = .Unsupported
 	return
 	return
 }
 }

+ 22 - 18
core/os/os2/process_windows.odin

@@ -442,7 +442,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 		stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
 		stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
 	}
 	}
 	if desc.stdin != nil {
 	if desc.stdin != nil {
-		stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
+		stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd)
 	}
 	}
 
 
 	working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
 	working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
@@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) ->
 			strings.write_byte(&builder, ' ')
 			strings.write_byte(&builder, ' ')
 		}
 		}
 		j := 0
 		j := 0
-		strings.write_byte(&builder, '"')
-		for j < len(arg) {
-			backslashes := 0
-			for j < len(arg) && arg[j] == '\\' {
-				backslashes += 1
+		if strings.contains_any(arg, "()[]{}^=;!'+,`~\" ") {
+			strings.write_byte(&builder, '"')
+			for j < len(arg) {
+				backslashes := 0
+				for j < len(arg) && arg[j] == '\\' {
+					backslashes += 1
+					j += 1
+				}
+				if j == len(arg) {
+					_write_byte_n_times(&builder, '\\', 2*backslashes)
+					break
+				} else if arg[j] == '"' {
+					_write_byte_n_times(&builder, '\\', 2*backslashes+1)
+					strings.write_byte(&builder, arg[j])
+				} else {
+					_write_byte_n_times(&builder, '\\', backslashes)
+					strings.write_byte(&builder, arg[j])
+				}
 				j += 1
 				j += 1
 			}
 			}
-			if j == len(arg) {
-				_write_byte_n_times(&builder, '\\', 2*backslashes)
-				break
-			} else if arg[j] == '"' {
-				_write_byte_n_times(&builder, '\\', 2*backslashes+1)
-				strings.write_byte(&builder, '"')
-			} else {
-				_write_byte_n_times(&builder, '\\', backslashes)
-				strings.write_byte(&builder, arg[j])
-			}
-			j += 1
+			strings.write_byte(&builder, '"')
+		} else {
+			strings.write_string(&builder, arg)
 		}
 		}
-		strings.write_byte(&builder, '"')
 	}
 	}
 	return strings.to_string(builder)
 	return strings.to_string(builder)
 }
 }

+ 7 - 8
core/os/os2/stat_windows.odin

@@ -200,22 +200,21 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi
 	} else {
 	} else {
 		mode |= 0o666
 		mode |= 0o666
 	}
 	}
+
 	is_sym := false
 	is_sym := false
 	if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
 	if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
 		is_sym = false
 		is_sym = false
 	} else {
 	} else {
 		is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
 		is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
 	}
 	}
+
 	if is_sym {
 	if is_sym {
 		type = .Symlink
 		type = .Symlink
-	} else {
-		if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
-			type = .Directory
-			mode |= 0o111
-		}
-		if h != nil {
-			type = file_type(h)
-		}
+	} else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
+		type = .Directory
+		mode |= 0o111
+	} else if h != nil {
+		type = file_type(h)
 	}
 	}
 	return
 	return
 }
 }

+ 2 - 4
core/os/os_darwin.odin

@@ -1061,7 +1061,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
 }
 }
 
 
 @(require_results)
 @(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
 	rel := rel
 	rel := rel
 	if rel == "" {
 	if rel == "" {
 		rel = "."
 		rel = "."
@@ -1076,9 +1076,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	}
 	defer _unix_free(rawptr(path_ptr))
 	defer _unix_free(rawptr(path_ptr))
 
 
-	path = strings.clone(string(path_ptr))
-
-	return path, nil
+	return strings.clone(string(path_ptr), allocator)
 }
 }
 
 
 access :: proc(path: string, mask: int) -> bool {
 access :: proc(path: string, mask: int) -> bool {

+ 2 - 5
core/os/os_freebsd.odin

@@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 }
 
 
 @(require_results)
 @(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
 	rel := rel
 	rel := rel
 	if rel == "" {
 	if rel == "" {
 		rel = "."
 		rel = "."
@@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	}
 	defer _unix_free(rawptr(path_ptr))
 	defer _unix_free(rawptr(path_ptr))
 
 
-
-	path = strings.clone(string(path_ptr))
-
-	return path, nil
+	return strings.clone(string(path_ptr), allocator)
 }
 }
 
 
 access :: proc(path: string, mask: int) -> (bool, Error) {
 access :: proc(path: string, mask: int) -> (bool, Error) {

+ 2 - 4
core/os/os_haiku.odin

@@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 }
 
 
 @(require_results)
 @(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
 	rel := rel
 	rel := rel
 	if rel == "" {
 	if rel == "" {
 		rel = "."
 		rel = "."
@@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	defer _unix_free(path_ptr)
 	defer _unix_free(path_ptr)
 
 
 	path_cstr := cstring(path_ptr)
 	path_cstr := cstring(path_ptr)
-	path = strings.clone(string(path_cstr))
-
-	return path, nil
+	return strings.clone(string(path_cstr), allocator)
 }
 }
 
 
 access :: proc(path: string, mask: int) -> (bool, Error) {
 access :: proc(path: string, mask: int) -> (bool, Error) {

+ 10 - 9
core/os/os_linux.odin

@@ -242,10 +242,13 @@ F_SETFL: int : 4 /* Set file flags */
 
 
 // NOTE(zangent): These are OS specific!
 // NOTE(zangent): These are OS specific!
 // Do not mix these up!
 // Do not mix these up!
-RTLD_LAZY         :: 0x001
-RTLD_NOW          :: 0x002
-RTLD_BINDING_MASK :: 0x3
-RTLD_GLOBAL       :: 0x100
+RTLD_LAZY         :: 0x0001
+RTLD_NOW          :: 0x0002
+RTLD_BINDING_MASK :: 0x0003
+RTLD_GLOBAL       :: 0x0100
+RTLD_NOLOAD       :: 0x0004
+RTLD_DEEPBIND     :: 0x0008
+RTLD_NODELETE     :: 0x1000
 
 
 socklen_t :: c.int
 socklen_t :: c.int
 
 
@@ -487,7 +490,7 @@ foreign libc {
 	@(link_name="free")             _unix_free          :: proc(ptr: rawptr) ---
 	@(link_name="free")             _unix_free          :: proc(ptr: rawptr) ---
 	@(link_name="realloc")          _unix_realloc       :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
 	@(link_name="realloc")          _unix_realloc       :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
 
 
-	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> int ---
+	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> c.int ---
 	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---
 	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---
 	@(link_name="putenv")           _unix_putenv        :: proc(cstring) -> c.int ---
 	@(link_name="putenv")           _unix_putenv        :: proc(cstring) -> c.int ---
 	@(link_name="setenv")           _unix_setenv        :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
 	@(link_name="setenv")           _unix_setenv        :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
@@ -914,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 }
 
 
 @(require_results)
 @(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
 	rel := rel
 	rel := rel
 	if rel == "" {
 	if rel == "" {
 		rel = "."
 		rel = "."
@@ -929,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	}
 	defer _unix_free(rawptr(path_ptr))
 	defer _unix_free(rawptr(path_ptr))
 
 
-	path = strings.clone(string(path_ptr))
-
-	return path, nil
+	return strings.clone(string(path_ptr), allocator)
 }
 }
 
 
 access :: proc(path: string, mask: int) -> (bool, Error) {
 access :: proc(path: string, mask: int) -> (bool, Error) {

+ 2 - 4
core/os/os_netbsd.odin

@@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
 }
 }
 
 
 @(require_results)
 @(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
 	rel := rel
 	rel := rel
 	if rel == "" {
 	if rel == "" {
 		rel = "."
 		rel = "."
@@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	}
 	defer _unix_free(rawptr(path_ptr))
 	defer _unix_free(rawptr(path_ptr))
 
 
-	path = strings.clone(string(path_ptr))
-
-	return path, nil
+	return strings.clone(string(path_ptr), allocator)
 }
 }
 
 
 access :: proc(path: string, mask: int) -> (bool, Error) {
 access :: proc(path: string, mask: int) -> (bool, Error) {

+ 2 - 4
core/os/os_openbsd.odin

@@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 }
 
 
 @(require_results)
 @(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
 	rel := rel
 	rel := rel
 	if rel == "" {
 	if rel == "" {
 		rel = "."
 		rel = "."
@@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	}
 	defer _unix_free(rawptr(path_ptr))
 	defer _unix_free(rawptr(path_ptr))
 
 
-	path = strings.clone(string(path_ptr))
-
-	return path, nil
+	return strings.clone(string(path_ptr), allocator)
 }
 }
 
 
 access :: proc(path: string, mask: int) -> (bool, Error) {
 access :: proc(path: string, mask: int) -> (bool, Error) {

+ 7 - 0
core/path/filepath/match.odin

@@ -246,6 +246,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
 	if err != .None {
 	if err != .None {
 		return
 		return
 	}
 	}
+	defer {
+		for s in m {
+			delete(s)
+		}
+		delete(m)
+	}
+
 	dmatches := make([dynamic]string, 0, 0)
 	dmatches := make([dynamic]string, 0, 0)
 	for d in m {
 	for d in m {
 		dmatches, err = _glob(d, file, &dmatches)
 		dmatches, err = _glob(d, file, &dmatches)

+ 5 - 32
core/path/filepath/path_unix.odin

@@ -1,14 +1,10 @@
 #+build linux, darwin, freebsd, openbsd, netbsd
 #+build linux, darwin, freebsd, openbsd, netbsd
 package filepath
 package filepath
 
 
-when ODIN_OS == .Darwin {
-	foreign import libc "system:System.framework"
-} else {
-	foreign import libc "system:c"
-}
-
 import "base:runtime"
 import "base:runtime"
+
 import "core:strings"
 import "core:strings"
+import "core:sys/posix"
 
 
 SEPARATOR :: '/'
 SEPARATOR :: '/'
 SEPARATOR_STRING :: `/`
 SEPARATOR_STRING :: `/`
@@ -28,11 +24,11 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
 		rel = "."
 		rel = "."
 	}
 	}
 	rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
 	rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
-	path_ptr := realpath(rel_cstr, nil)
+	path_ptr := posix.realpath(rel_cstr, nil)
 	if path_ptr == nil {
 	if path_ptr == nil {
-		return "", __error()^ == 0
+		return "", posix.errno() == nil
 	}
 	}
-	defer _unix_free(rawptr(path_ptr))
+	defer posix.free(path_ptr)
 
 
 	path_str := strings.clone(string(path_ptr), allocator)
 	path_str := strings.clone(string(path_ptr), allocator)
 	return path_str, true
 	return path_str, true
@@ -48,26 +44,3 @@ join :: proc(elems: []string, allocator := context.allocator) -> (joined: string
 	}
 	}
 	return "", nil
 	return "", nil
 }
 }
-
-@(private)
-foreign libc {
-	realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
-	@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
-
-}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
-	@(private)
-	foreign libc {
-		@(link_name="__error")          __error :: proc() -> ^i32 ---
-	}
-} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
-	@(private)
-	foreign libc {
-		@(link_name="__errno")		__error :: proc() -> ^i32 ---
-	}
-} else {
-	@(private)
-	foreign libc {
-		@(link_name="__errno_location") __error :: proc() -> ^i32 ---
-	}
-}

+ 48 - 54
core/reflect/reflect.odin

@@ -31,8 +31,6 @@ Type_Info_Enum                   :: runtime.Type_Info_Enum
 Type_Info_Map                    :: runtime.Type_Info_Map
 Type_Info_Map                    :: runtime.Type_Info_Map
 Type_Info_Bit_Set                :: runtime.Type_Info_Bit_Set
 Type_Info_Bit_Set                :: runtime.Type_Info_Bit_Set
 Type_Info_Simd_Vector            :: runtime.Type_Info_Simd_Vector
 Type_Info_Simd_Vector            :: runtime.Type_Info_Simd_Vector
-Type_Info_Relative_Pointer       :: runtime.Type_Info_Relative_Pointer
-Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
 Type_Info_Matrix                 :: runtime.Type_Info_Matrix
 Type_Info_Matrix                 :: runtime.Type_Info_Matrix
 Type_Info_Soa_Pointer            :: runtime.Type_Info_Soa_Pointer
 Type_Info_Soa_Pointer            :: runtime.Type_Info_Soa_Pointer
 Type_Info_Bit_Field              :: runtime.Type_Info_Bit_Field
 Type_Info_Bit_Field              :: runtime.Type_Info_Bit_Field
@@ -67,8 +65,6 @@ Type_Kind :: enum {
 	Map,
 	Map,
 	Bit_Set,
 	Bit_Set,
 	Simd_Vector,
 	Simd_Vector,
-	Relative_Pointer,
-	Relative_Multi_Pointer,
 	Matrix,
 	Matrix,
 	Soa_Pointer,
 	Soa_Pointer,
 	Bit_Field,
 	Bit_Field,
@@ -80,35 +76,33 @@ type_kind :: proc(T: typeid) -> Type_Kind {
 	ti := type_info_of(T)
 	ti := type_info_of(T)
 	if ti != nil {
 	if ti != nil {
 		switch _ in ti.variant {
 		switch _ in ti.variant {
-		case Type_Info_Named:                  return .Named
-		case Type_Info_Integer:                return .Integer
-		case Type_Info_Rune:                   return .Rune
-		case Type_Info_Float:                  return .Float
-		case Type_Info_Complex:                return .Complex
-		case Type_Info_Quaternion:             return .Quaternion
-		case Type_Info_String:                 return .String
-		case Type_Info_Boolean:                return .Boolean
-		case Type_Info_Any:                    return .Any
-		case Type_Info_Type_Id:                return .Type_Id
-		case Type_Info_Pointer:                return .Pointer
-		case Type_Info_Multi_Pointer:          return .Multi_Pointer
-		case Type_Info_Procedure:              return .Procedure
-		case Type_Info_Array:                  return .Array
-		case Type_Info_Enumerated_Array:       return .Enumerated_Array
-		case Type_Info_Dynamic_Array:          return .Dynamic_Array
-		case Type_Info_Slice:                  return .Slice
-		case Type_Info_Parameters:             return .Tuple
-		case Type_Info_Struct:                 return .Struct
-		case Type_Info_Union:                  return .Union
-		case Type_Info_Enum:                   return .Enum
-		case Type_Info_Map:                    return .Map
-		case Type_Info_Bit_Set:                return .Bit_Set
-		case Type_Info_Simd_Vector:            return .Simd_Vector
-		case Type_Info_Relative_Pointer:       return .Relative_Pointer
-		case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
-		case Type_Info_Matrix:                 return .Matrix
-		case Type_Info_Soa_Pointer:            return .Soa_Pointer
-		case Type_Info_Bit_Field:              return .Bit_Field
+		case Type_Info_Named:            return .Named
+		case Type_Info_Integer:          return .Integer
+		case Type_Info_Rune:             return .Rune
+		case Type_Info_Float:            return .Float
+		case Type_Info_Complex:          return .Complex
+		case Type_Info_Quaternion:       return .Quaternion
+		case Type_Info_String:           return .String
+		case Type_Info_Boolean:          return .Boolean
+		case Type_Info_Any:              return .Any
+		case Type_Info_Type_Id:          return .Type_Id
+		case Type_Info_Pointer:          return .Pointer
+		case Type_Info_Multi_Pointer:    return .Multi_Pointer
+		case Type_Info_Procedure:        return .Procedure
+		case Type_Info_Array:            return .Array
+		case Type_Info_Enumerated_Array: return .Enumerated_Array
+		case Type_Info_Dynamic_Array:    return .Dynamic_Array
+		case Type_Info_Slice:            return .Slice
+		case Type_Info_Parameters:       return .Tuple
+		case Type_Info_Struct:           return .Struct
+		case Type_Info_Union:            return .Union
+		case Type_Info_Enum:             return .Enum
+		case Type_Info_Map:              return .Map
+		case Type_Info_Bit_Set:          return .Bit_Set
+		case Type_Info_Simd_Vector:      return .Simd_Vector
+		case Type_Info_Matrix:           return .Matrix
+		case Type_Info_Soa_Pointer:      return .Soa_Pointer
+		case Type_Info_Bit_Field:        return .Bit_Field
 		}
 		}
 
 
 	}
 	}
@@ -723,6 +717,27 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
 	return
 	return
 }
 }
 
 
+/*
+Returns whether the value given has a defined name in the enum type.
+*/
+@(require_results)
+enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
+	when len(T) == cap(T) {
+		return value >= min(T) && value <= max(T)
+	} else {
+		if value < min(T) || value > max(T) {
+			return false
+		}
+
+		for valid_value in T {
+			if valid_value == value {
+				return true
+			}
+		}
+
+		return false
+	}
+}
 
 
 
 
 
 
@@ -1467,21 +1482,6 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
 	return
 	return
 }
 }
 
 
-@(require_results)
-relative_pointer_to_absolute :: proc(a: any) -> rawptr {
-	if a == nil { return nil }
-	a := a
-	ti := runtime.type_info_core(type_info_of(a.id))
-	a.id = ti.id
-
-	#partial switch info in ti.variant {
-	case Type_Info_Relative_Pointer:
-		return relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
-	}
-	return nil
-}
-
-
 @(require_results)
 @(require_results)
 relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
 relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
 	_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
 	_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
@@ -1543,10 +1543,6 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
 		case cstring: value = rawptr(v)
 		case cstring: value = rawptr(v)
 		case: valid = false
 		case: valid = false
 		}
 		}
-
-	case Type_Info_Relative_Pointer:
-		valid = true
-		value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
 	}
 	}
 
 
 	return
 	return
@@ -1656,8 +1652,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		Type_Info_Bit_Set,
 		Type_Info_Bit_Set,
 		Type_Info_Enum,
 		Type_Info_Enum,
 		Type_Info_Simd_Vector,
 		Type_Info_Simd_Vector,
-		Type_Info_Relative_Pointer,
-		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Matrix:
 		Type_Info_Matrix:
 		return runtime.memory_compare(a.data, b.data, t.size) == 0
 		return runtime.memory_compare(a.data, b.data, t.size) == 0

+ 0 - 32
core/reflect/types.odin

@@ -158,14 +158,6 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
 	case Type_Info_Simd_Vector:
 	case Type_Info_Simd_Vector:
 		y := b.variant.(Type_Info_Simd_Vector) or_return
 		y := b.variant.(Type_Info_Simd_Vector) or_return
 		return x.count == y.count && x.elem == y.elem
 		return x.count == y.count && x.elem == y.elem
-
-	case Type_Info_Relative_Pointer:
-		y := b.variant.(Type_Info_Relative_Pointer) or_return
-		return x.base_integer == y.base_integer && x.pointer == y.pointer
-
-	case Type_Info_Relative_Multi_Pointer:
-		y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
-		return x.base_integer == y.base_integer && x.pointer == y.pointer
 		
 		
 	case Type_Info_Matrix:
 	case Type_Info_Matrix:
 		y := b.variant.(Type_Info_Matrix) or_return
 		y := b.variant.(Type_Info_Matrix) or_return
@@ -392,18 +384,6 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool {
 	_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
 	_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
 	return ok
 	return ok
 }
 }
-@(require_results)
-is_relative_pointer :: proc(info: ^Type_Info) -> bool {
-	if info == nil { return false }
-	_, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
-	return ok
-}
-@(require_results)
-is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
-	if info == nil { return false }
-	_, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer)
-	return ok
-}
 
 
 
 
 @(require_results)
 @(require_results)
@@ -736,18 +716,6 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
 		io.write_i64(w, i64(info.count), 10, &n) or_return
 		io.write_i64(w, i64(info.count), 10, &n) or_return
 		io.write_byte(w, ']',                &n) or_return
 		io.write_byte(w, ']',                &n) or_return
 		write_type(w, info.elem,             &n) or_return
 		write_type(w, info.elem,             &n) or_return
-
-	case Type_Info_Relative_Pointer:
-		io.write_string(w, "#relative(", &n) or_return
-		write_type(w, info.base_integer, &n) or_return
-		io.write_string(w, ") ",         &n) or_return
-		write_type(w, info.pointer,      &n) or_return
-
-	case Type_Info_Relative_Multi_Pointer:
-		io.write_string(w, "#relative(", &n) or_return
-		write_type(w, info.base_integer, &n) or_return
-		io.write_string(w, ") ",         &n) or_return
-		write_type(w, info.pointer,      &n) or_return
 		
 		
 	case Type_Info_Matrix:
 	case Type_Info_Matrix:
 		if info.layout == .Row_Major {
 		if info.layout == .Row_Major {

+ 2 - 2
core/simd/x86/sse2.odin

@@ -240,7 +240,7 @@ _mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
 }
 }
 @(require_results, enable_target_feature="sse2")
 @(require_results, enable_target_feature="sse2")
 _mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
 _mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
-	return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8)
+	return transmute(__m128i)psraiw(transmute(i16x8)a, IMM8)
 }
 }
 @(require_results, enable_target_feature="sse2")
 @(require_results, enable_target_feature="sse2")
 _mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
 _mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
@@ -262,7 +262,7 @@ _mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
 }
 }
 @(require_results, enable_target_feature="sse2")
 @(require_results, enable_target_feature="sse2")
 _mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
 _mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
-	return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8)
+	return transmute(__m128i)psrliw(transmute(i16x8)a, IMM8)
 }
 }
 @(require_results, enable_target_feature="sse2")
 @(require_results, enable_target_feature="sse2")
 _mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
 _mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {

+ 4 - 4
core/slice/map.odin

@@ -48,11 +48,11 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
 	return
 	return
 }
 }
 
 
-map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
+map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V), err: runtime.Allocator_Error) #no_bounds_check {
 	m := m
 	m := m
 	rm := (^runtime.Raw_Map)(&m)
 	rm := (^runtime.Raw_Map)(&m)
 
 
-	info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
+	info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
 	if info.map_info != nil {
 	if info.map_info != nil {
 		entries = make(type_of(entries), len(m), allocator) or_return
 		entries = make(type_of(entries), len(m), allocator) or_return
 
 
@@ -61,8 +61,8 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
 		entry_index := 0
 		entry_index := 0
 		for bucket_index in 0..<map_cap {
 		for bucket_index in 0..<map_cap {
 			if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
 			if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
-				key   := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
-				value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
+				key   := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
+				value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
 				entries[entry_index].hash  = hash
 				entries[entry_index].hash  = hash
 				entries[entry_index].key   = (^K)(key)^
 				entries[entry_index].key   = (^K)(key)^
 				entries[entry_index].value = (^V)(value)^
 				entries[entry_index].value = (^V)(value)^

+ 142 - 24
core/slice/slice.odin

@@ -36,6 +36,17 @@ to_bytes :: proc "contextless" (s: []$T) -> []byte {
 	return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
 	return ([^]byte)(raw_data(s))[:len(s) * size_of(T)]
 }
 }
 
 
+/*
+	Turns a byte slice into a type.
+*/
+@(require_results)
+to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
+	if len(buf) < size_of(T) {
+		return {}, false
+	}
+	return intrinsics.unaligned_load((^T)(raw_data(buf))), true
+}
+
 /*
 /*
 	Turn a slice of one type, into a slice of another type.
 	Turn a slice of one type, into a slice of another type.
 
 
@@ -96,9 +107,37 @@ contains :: proc(array: $T/[]$E, value: E) -> bool where intrinsics.type_is_comp
 	return found
 	return found
 }
 }
 
 
+/*
+Searches the given slice for the given element in O(n) time.
+
+If you need a custom search condition, see `linear_search_proc`
+
+Inputs:
+- array: The slice to search in.
+- key: The element to search for.
+
+Returns:
+- index: The index `i`, such that `array[i]` is the first occurrence of `key` in `array`, or -1 if `key` is not present in `array`.
+
+Example:
+	index: int
+	found: bool
+
+	a := []i32{10, 10, 10, 20}
+
+	index, found = linear_search_reverse(a, 10)
+	assert(index == 0 && found == true)
+
+	index, found = linear_search_reverse(a, 30)
+	assert(index == -1 && found == false)
+
+	// Note that `index == 1`, since it is relative to `a[2:]`
+	index, found = linear_search_reverse(a[2:], 20)
+	assert(index == 1 && found == true)
+*/
 @(require_results)
 @(require_results)
 linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
 linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
-	where intrinsics.type_is_comparable(T) #no_bounds_check {
+	where intrinsics.type_is_comparable(T) {
 	for x, i in array {
 	for x, i in array {
 		if x == key {
 		if x == key {
 			return i, true
 			return i, true
@@ -107,8 +146,18 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
 	return -1, false
 	return -1, false
 }
 }
 
 
+/*
+Searches the given slice for the first element satisfying predicate `f` in O(n) time.
+
+Inputs:
+- array: The slice to search in.
+- f: The search condition.
+
+Returns:
+- index: The index `i`, such that `array[i]` is the first `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist.
+*/
 @(require_results)
 @(require_results)
-linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) #no_bounds_check {
+linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) {
 	for x, i in array {
 	for x, i in array {
 		if f(x) {
 		if f(x) {
 			return i, true
 			return i, true
@@ -118,22 +167,88 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
 }
 }
 
 
 /*
 /*
-	Binary search searches the given slice for the given element.
-	If the slice is not sorted, the returned index is unspecified and meaningless.
+Searches the given slice for the given element in O(n) time, starting from the
+slice end.
+
+If you need a custom search condition, see `linear_search_reverse_proc`
+
+Inputs:
+- array: The slice to search in.
+- key: The element to search for.
 
 
-	If the value is found then the returned int is the index of the matching element.
-	If there are multiple matches, then any one of the matches could be returned.
+Returns:
+- index: The index `i`, such that `array[i]` is the last occurrence of `key` in `array`, or -1 if `key` is not present in `array`.
 
 
-	If the value is not found then the returned int is the index where a matching
-	element could be inserted while maintaining sorted order.
+Example:
+	index: int
+	found: bool
+
+	a := []i32{10, 10, 10, 20}
+
+	index, found = linear_search_reverse(a, 20)
+	assert(index == 3 && found == true)
 
 
-	# Examples
+	index, found = linear_search_reverse(a, 10)
+	assert(index == 2 && found == true)
 
 
+	index, found = linear_search_reverse(a, 30)
+	assert(index == -1 && found == false)
+
+	// Note that `index == 1`, since it is relative to `a[2:]`
+	index, found = linear_search_reverse(a[2:], 20)
+	assert(index == 1 && found == true)
+*/
+@(require_results)
+linear_search_reverse :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
+	where intrinsics.type_is_comparable(T) {
+	#reverse for x, i in array {
+		if x == key {
+			return i, true
+		}
+	}
+	return -1, false
+}
+
+/*
+Searches the given slice for the last element satisfying predicate `f` in O(n)
+time, starting from the slice end.
+
+Inputs:
+- array: The slice to search in.
+- f: The search condition.
+
+Returns:
+- index: The index `i`, such that `array[i]` is the last `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist.
+*/
+@(require_results)
+linear_search_reverse_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) {
+	#reverse for x, i in array {
+		if f(x) {
+			return i, true
+		}
+	}
+	return -1, false
+}
+
+/*
+Searches the given slice for the given element.
+If the slice is not sorted, the returned index is unspecified and meaningless.
+
+If the value is found then the returned int is the index of the matching element.
+If there are multiple matches, then any one of the matches could be returned.
+
+If the value is not found then the returned int is the index where a matching
+element could be inserted while maintaining sorted order.
+
+For slices of more complex types see: `binary_search_by`
+
+Example:
+	/*
 	Looks up a series of four elements. The first is found, with a
 	Looks up a series of four elements. The first is found, with a
 	uniquely determined position; the second and third are not
 	uniquely determined position; the second and third are not
 	found; the fourth could match any position in `[1, 4]`.
 	found; the fourth could match any position in `[1, 4]`.
+	*/
 
 
-	```
 	index: int
 	index: int
 	found: bool
 	found: bool
 
 
@@ -150,9 +265,6 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
 
 
 	index, found = slice.binary_search(s, 1)
 	index, found = slice.binary_search(s, 1)
 	assert(index >= 1 && index <= 4 && found == true)
 	assert(index >= 1 && index <= 4 && found == true)
-	```
-
-	For slices of more complex types see: binary_search_by
 */
 */
 @(require_results)
 @(require_results)
 binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
 binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
@@ -161,21 +273,21 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
 }
 }
 
 
 /*
 /*
-	Binary search searches the given slice for the given element.
-	If the slice is not sorted, the returned index is unspecified and meaningless.
+Searches the given slice for the given element.
+If the slice is not sorted, the returned index is unspecified and meaningless.
 
 
-	If the value is found then the returned int is the index of the matching element.
-	If there are multiple matches, then any one of the matches could be returned.
+If the value is found then the returned int is the index of the matching element.
+If there are multiple matches, then any one of the matches could be returned.
 
 
-	If the value is not found then the returned int is the index where a matching
-	element could be inserted while maintaining sorted order.
+If the value is not found then the returned int is the index where a matching
+element could be inserted while maintaining sorted order.
 
 
-	The array elements and key may be different types. This allows the filter procedure
-	to compare keys against a slice of structs, one struct value at a time.
+The array elements and key may be different types. This allows the filter procedure
+to compare keys against a slice of structs, one struct value at a time.
 
 
-	Returns:
-	index: int
-	found: bool
+Returns:
+- index: int
+- found: bool
 
 
 */
 */
 @(require_results)
 @(require_results)
@@ -359,6 +471,12 @@ is_empty :: proc(a: $T/[]$E) -> bool {
 	return len(a) == 0
 	return len(a) == 0
 }
 }
 
 
+// Gets the byte size of the backing data
+@(require_results)
+size :: proc "contextless" (a: $T/[]$E) -> int {
+	return len(a) * size_of(E)
+}
+
 
 
 
 
 @(require_results)
 @(require_results)

+ 2 - 1
core/strconv/strconv.odin

@@ -1121,6 +1121,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
 			break trunc_block
 			break trunc_block
 		}
 		}
 		f := f64(mantissa)
 		f := f64(mantissa)
+		f_abs := f
 		if neg {
 		if neg {
 			f = -f
 			f = -f
 		}
 		}
@@ -1132,7 +1133,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
 				f *= pow10[exp-22]
 				f *= pow10[exp-22]
 				exp = 22
 				exp = 22
 			}
 			}
-			if f > 1e15 || f < 1e-15 {
+			if f_abs > 1e15 || f_abs < 1e-15 {
 				break trunc_block
 				break trunc_block
 			}
 			}
 			return f * pow10[exp], nr, true
 			return f * pow10[exp], nr, true

+ 1 - 1
core/strings/strings.odin

@@ -752,7 +752,7 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0)) -> (res: st
 		count += 1
 		count += 1
 	}
 	}
 
 
-	if rune_length <= 1 {
+	if rune_length < 1 {
 		return s
 		return s
 	}
 	}
 
 

+ 10 - 0
core/sys/darwin/Foundation/NSApplication.odin

@@ -108,6 +108,16 @@ Application_setMainMenu :: proc "c" (self: ^Application, menu: ^Menu) {
 	msgSend(nil, self, "setMainMenu:", menu)
 	msgSend(nil, self, "setMainMenu:", menu)
 }
 }
 
 
+@(objc_type=Application, objc_name="mainWindow")
+Application_mainWindow :: proc "c" (self: ^Application) -> ^Window {
+	return msgSend(^Window, self, "mainWindow")
+}
+
+@(objc_type=Application, objc_name="keyWindow")
+Application_keyWindow :: proc "c" (self: ^Application) -> ^Window {
+	return msgSend(^Window, self, "keyWindow")
+}
+
 @(objc_type=Application, objc_name="windows")
 @(objc_type=Application, objc_name="windows")
 Application_windows :: proc "c" (self: ^Application) -> ^Array {
 Application_windows :: proc "c" (self: ^Application) -> ^Array {
 	return msgSend(^Array, self, "windows")
 	return msgSend(^Array, self, "windows")

+ 500 - 14
core/sys/darwin/mach_darwin.odin

@@ -3,17 +3,18 @@ package darwin
 foreign import mach "system:System.framework"
 foreign import mach "system:System.framework"
 
 
 import "core:c"
 import "core:c"
+import "base:intrinsics"
 
 
 // NOTE(tetra): Unclear whether these should be aligned 16 or not.
 // NOTE(tetra): Unclear whether these should be aligned 16 or not.
 // However all other sync primitives are aligned for robustness.
 // However all other sync primitives are aligned for robustness.
 // I cannot currently align these though.
 // I cannot currently align these though.
 // See core/sys/unix/pthread_linux.odin/pthread_t.
 // See core/sys/unix/pthread_linux.odin/pthread_t.
-mach_port_t :: distinct i32
+mach_port_t :: distinct c.uint
 task_t :: mach_port_t
 task_t :: mach_port_t
 
 
 semaphore_t :: distinct u64
 semaphore_t :: distinct u64
 
 
-kern_return_t  :: distinct u64
+kern_return_t :: distinct c.int
 thread_act_t   :: distinct u64
 thread_act_t   :: distinct u64
 thread_state_t :: distinct ^u32
 thread_state_t :: distinct ^u32
 thread_list_t  :: [^]thread_act_t
 thread_list_t  :: [^]thread_act_t
@@ -37,11 +38,6 @@ MACH_MSGH_BITS_COMPLEX :: 0x80000000
 MACH_PORT_RIGHT_SEND    :: 0
 MACH_PORT_RIGHT_SEND    :: 0
 MACH_PORT_RIGHT_RECEIVE :: 1
 MACH_PORT_RIGHT_RECEIVE :: 1
 
 
-VM_PROT_NONE    :: 0
-VM_PROT_READ    :: 1
-VM_PROT_WRITE   :: 2
-VM_PROT_EXECUTE :: 4
-
 VM_INHERIT_SHARE        :: 0
 VM_INHERIT_SHARE        :: 0
 VM_INHERIT_COPY         :: 1
 VM_INHERIT_COPY         :: 1
 VM_INHERIT_NONE         :: 2
 VM_INHERIT_NONE         :: 2
@@ -58,6 +54,27 @@ ARM_THREAD_STATE64 :: 6
 mach_msg_option_t :: distinct i32
 mach_msg_option_t :: distinct i32
 name_t :: distinct cstring
 name_t :: distinct cstring
 
 
+vm_map_t :: mach_port_t
+mem_entry_name_port_t :: mach_port_t
+ipc_space_t :: mach_port_t
+thread_t :: mach_port_t
+
+vm_size_t :: distinct c.uintptr_t
+
+vm_address_t :: vm_offset_t
+vm_offset_t :: distinct c.uintptr_t
+
+// NOTE(beau): typedefed to int in the original headers
+boolean_t :: b32
+
+vm_prot_t :: distinct c.int
+
+vm_inherit_t :: distinct c.uint
+
+mach_port_name_t :: distinct c.uint
+
+sync_policy_t :: distinct c.int
+
 mach_msg_port_descriptor_t :: struct {
 mach_msg_port_descriptor_t :: struct {
 	name: mach_port_t,
 	name: mach_port_t,
 	_: u32,
 	_: u32,
@@ -257,20 +274,489 @@ foreign mach {
 	task_info      :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t ---
 	task_info      :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t ---
 	task_terminate :: proc(task: task_t) -> kern_return_t ---
 	task_terminate :: proc(task: task_t) -> kern_return_t ---
 
 
+	semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return ---
+	semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return ---
+
+	semaphore_signal :: proc(semaphore: semaphore_t) -> Kern_Return ---
+	semaphore_signal_all :: proc(semaphore: semaphore_t) -> Kern_Return ---
+	semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_t) -> Kern_Return ---
+
+	semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return ---
+
 	thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t ---
 	thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t ---
 	thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t ---
 	thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t ---
 
 
 	bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t ---
 	bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t ---
 	bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t ---
 	bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t ---
 
 
-	semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t ---
-	semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t ---
+	vm_map :: proc(
+		target_task:    vm_map_t,
+		address:        ^vm_address_t,
+		size:           vm_size_t,
+		mask:           vm_address_t,
+		flags:          VM_Flags,
+		object:         mem_entry_name_port_t,
+		offset:         vm_offset_t,
+		copy:           boolean_t,
+		cur_protection,
+		max_protection: VM_Prot_Flags,
+		inheritance:    VM_Inherit,
+	) -> Kern_Return ---
+
+	mach_make_memory_entry :: proc(
+		target_task:   vm_map_t,
+		size:          ^vm_size_t,
+		offset:        vm_offset_t,
+		permission:    VM_Prot_Flags,
+		object_handle: ^mem_entry_name_port_t,
+		parent_entry:  mem_entry_name_port_t,
+	) -> Kern_Return ---
+}
+
+
+
+Kern_Return :: enum kern_return_t {
+	Success,
+
+	/* Specified address is not currently valid.
+	 */
+	Invalid_Address,
+
+	/* Specified memory is valid, but does not permit the
+	 * required forms of access.
+	 */
+	Protection_Failure,
+
+	/* The address range specified is already in use, or
+	 * no address range of the size specified could be
+	 * found.
+	 */
+	No_Space,
+
+	/* The function requested was not applicable to this
+	 * type of argument, or an argument is invalid
+	 */
+	Invalid_Argument,
+
+	/* The function could not be performed.  A catch-all.
+	 */
+	Failure,
+
+	/* A system resource could not be allocated to fulfill
+	 * this request.  This failure may not be permanent.
+	 */
+	Resource_Shortage,
+
+	/* The task in question does not hold receive rights
+	 * for the port argument.
+	 */
+	Not_Receiver,
+
+	/* Bogus access restriction.
+	 */
+	No_Access,
+
+	/* During a page fault, the target address refers to a
+	 * memory object that has been destroyed.  This
+	 * failure is permanent.
+	 */
+	Memory_Failure,
+
+	/* During a page fault, the memory object indicated
+	 * that the data could not be returned.  This failure
+	 * may be temporary; future attempts to access this
+	 * same data may succeed, as defined by the memory
+	 * object.
+	 */
+	Memory_Error,
+
+	/* The receive right is already a member of the portset.
+	 */
+	Already_In_Set,
+
+	/* The receive right is not a member of a port set.
+	 */
+	Not_In_Set,
+
+	/* The name already denotes a right in the task.
+	 */
+	Name_Exists,
+
+	/* The operation was aborted.  Ipc code will
+	 * catch this and reflect it as a message error.
+	 */
+	Aborted,
+
+	/* The name doesn't denote a right in the task.
+	 */
+	Invalid_Name,
+
+	/* Target task isn't an active task.
+	 */
+	Invalid_Task,
+
+	/* The name denotes a right, but not an appropriate right.
+	 */
+	Invalid_Right,
+
+	/* A blatant range error.
+	 */
+	Invalid_Value,
+
+	/* Operation would overflow limit on user-references.
+	 */
+	URefs_Overflow,
+
+	/* The supplied (port) capability is improper.
+	 */
+	Invalid_Capability,
+
+	/* The task already has send or receive rights
+	 * for the port under another name.
+	 */
+	Right_Exists,
+
+	/* Target host isn't actually a host.
+	 */
+	Invalid_Host,
+
+	/* An attempt was made to supply "precious" data
+	 * for memory that is already present in a
+	 * memory object.
+	 */
+	Memory_Present,
+
+	/* A page was requested of a memory manager via
+	 * memory_object_data_request for an object using
+	 * a MEMORY_OBJECT_COPY_CALL strategy, with the
+	 * VM_PROT_WANTS_COPY flag being used to specify
+	 * that the page desired is for a copy of the
+	 * object, and the memory manager has detected
+	 * the page was pushed into a copy of the object
+	 * while the kernel was walking the shadow chain
+	 * from the copy to the object. This error code
+	 * is delivered via memory_object_data_error
+	 * and is handled by the kernel (it forces the
+	 * kernel to restart the fault). It will not be
+	 * seen by users.
+	 */
+	Memory_Data_Moved,
+
+	/* A strategic copy was attempted of an object
+	 * upon which a quicker copy is now possible.
+	 * The caller should retry the copy using
+	 * vm_object_copy_quickly. This error code
+	 * is seen only by the kernel.
+	 */
+	Memory_Restart_Copy,
+
+	/* An argument applied to assert processor set privilege
+	 * was not a processor set control port.
+	 */
+	Invalid_Processor_Set,
+
+	/* The specified scheduling attributes exceed the thread's
+	 * limits.
+	 */
+	Policy_Limit,
+
+	/* The specified scheduling policy is not currently
+	 * enabled for the processor set.
+	 */
+	Invalid_Policy,
+
+	/* The external memory manager failed to initialize the
+	 * memory object.
+	 */
+	Invalid_Object,
+
+	/* A thread is attempting to wait for an event for which
+	 * there is already a waiting thread.
+	 */
+	Already_Waiting,
+
+	/* An attempt was made to destroy the default processor
+	 * set.
+	 */
+	Default_Set,
+
+	/* An attempt was made to fetch an exception port that is
+	 * protected, or to abort a thread while processing a
+	 * protected exception.
+	 */
+	Exception_Protected,
+
+	/* A ledger was required but not supplied.
+	 */
+	Invalid_Ledger,
+
+	/* The port was not a memory cache control port.
+	 */
+	Invalid_Memory_Control,
+
+	/* An argument supplied to assert security privilege
+	 * was not a host security port.
+	 */
+	Invalid_Security,
+
+	/* thread_depress_abort was called on a thread which
+	 * was not currently depressed.
+	 */
+	Not_Depressed,
+
+	/* Object has been terminated and is no longer available
+	 */
+	Terminated,
+
+	/* Lock set has been destroyed and is no longer available.
+	 */
+	Lock_Set_Destroyed,
+
+	/* The thread holding the lock terminated before releasing
+	 * the lock
+	 */
+	Lock_Unstable,
+
+	/* The lock is already owned by another thread
+	 */
+	Lock_Owned,
+
+	/* The lock is already owned by the calling thread
+	 */
+	Lock_Owned_Self,
+
+	/* Semaphore has been destroyed and is no longer available.
+	 */
+	Semaphore_Destroyed,
+
+	/* Return from RPC indicating the target server was
+	 * terminated before it successfully replied
+	 */
+	Rpc_Server_Terminated,
+
+	/* Terminate an orphaned activation.
+	 */
+	RPC_Terminate_Orphan,
+
+	/* Allow an orphaned activation to continue executing.
+	 */
+	RPC_Continue_Orphan,
+
+	/* Empty thread activation (No thread linked to it)
+	 */
+	Not_Supported,
+
+	/* Remote node down or inaccessible.
+	 */
+	Node_Down,
+
+	/* A signalled thread was not actually waiting. */
+	Not_Waiting,
+
+	/* Some thread-oriented operation (semaphore_wait) timed out
+	 */
+	Operation_Timed_Out,
+
+	/* During a page fault, indicates that the page was rejected
+	 * as a result of a signature check.
+	 */
+	Codesign_Error,
+
+	/* The requested property cannot be changed at this time.
+	 */
+	Policy_Static,
+
+	/* The provided buffer is of insufficient size for the requested data.
+	 */
+	Insufficient_Buffer_Size,
+
+	/* Denied by security policy
+	 */
+	Denied,
+
+	/* The KC on which the function is operating is missing
+	 */
+	Missing_KC,
+
+	/* The KC on which the function is operating is invalid
+	 */
+	Invalid_KC,
+
+	/* A search or query operation did not return a result
+	 */
+	Not_Found,
+
+	/* Maximum return value allowable
+	 */
+	Return_Max               = 0x100,
+}
+
+/*
+ * VM allocation flags:
+ *
+ * VM_FLAGS_FIXED
+ *      (really the absence of VM_FLAGS_ANYWHERE)
+ *	Allocate new VM region at the specified virtual address, if possible.
+ *
+ * VM_FLAGS_ANYWHERE
+ *	Allocate new VM region anywhere it would fit in the address space.
+ *
+ * VM_FLAGS_PURGABLE
+ *	Create a purgable VM object for that new VM region.
+ *
+ * VM_FLAGS_4GB_CHUNK
+ *	The new VM region will be chunked up into 4GB sized pieces.
+ *
+ * VM_FLAGS_NO_PMAP_CHECK
+ *	(for DEBUG kernel config only, ignored for other configs)
+ *	Do not check that there is no stale pmap mapping for the new VM region.
+ *	This is useful for kernel memory allocations at bootstrap when building
+ *	the initial kernel address space while some memory is already in use.
+ *
+ * VM_FLAGS_OVERWRITE
+ *	The new VM region can replace existing VM regions if necessary
+ *	(to be used in combination with VM_FLAGS_FIXED).
+ *
+ * VM_FLAGS_NO_CACHE
+ *	Pages brought in to this VM region are placed on the speculative
+ *	queue instead of the active queue.  In other words, they are not
+ *	cached so that they will be stolen first if memory runs low.
+ */
+
+@(private="file")
+LOG2 :: intrinsics.constant_log2
+
+VM_Flag :: enum c.int {
+	Anywhere,
+	Purgable,
+	_4GB_Chunk,
+	Random_Addr,
+	No_Cache,
+	Resilient_Codesign,
+	Resilient_Media,
+	Permanent,
+
+	// NOTE(beau): log 2 of the bit we want in the bit set so we get that bit in
+	// the bit set
+
+	TPRO                = LOG2(0x1000),
+	Overwrite           = LOG2(0x4000),/* delete any existing mappings first */
+
+	Superpage_Size_Any  = LOG2(0x10000),
+	Superpage_Size_2MB  = LOG2(0x20000),
+	__Superpage3        = LOG2(0x40000),
+
+	Return_Data_Addr    = LOG2(0x100000),
+	Return_4K_Data_Addr = LOG2(0x800000),
+
+	Alias_Mask1         = 24,
+	Alias_Mask2,
+	Alias_Mask3,
+	Alias_Mask4,
+	Alias_Mask5,
+	Alias_Mask6,
+	Alias_Mask7,
+	Alias_Mask8,
+
+	HW = TPRO,
+}
+
+VM_Flags :: distinct bit_set[VM_Flag; c.int]
+VM_FLAGS_FIXED :: VM_Flags{}
+
+/*
+ * VM_FLAGS_SUPERPAGE_MASK
+ *	3 bits that specify whether large pages should be used instead of
+ *	base pages (!=0), as well as the requested page size.
+ */
+VM_FLAGS_SUPERPAGE_MASK :: VM_Flags {
+	.Superpage_Size_Any,
+	.Superpage_Size_2MB,
+	.__Superpage3,
+}
+
+// 0xFF000000
+VM_FLAGS_ALIAS_MASK :: VM_Flags {
+	.Alias_Mask1,
+	.Alias_Mask2,
+	.Alias_Mask3,
+	.Alias_Mask4,
+	.Alias_Mask5,
+	.Alias_Mask6,
+	.Alias_Mask7,
+	.Alias_Mask8,
+}
+
+VM_GET_FLAGS_ALIAS :: proc(flags: VM_Flags) -> c.int {
+	return transmute(c.int)(flags & VM_FLAGS_ALIAS_MASK) >> 24
+}
+// NOTE(beau): no need for VM_SET_FLAGS_ALIAS, just mask in things from
+// VM_Flag.Alias_Mask*
+
+/* These are the flags that we accept from user-space */
+VM_FLAGS_USER_ALLOCATE :: VM_Flags {
+	 .Anywhere,
+	 .Purgable,
+	 ._4GB_Chunk,
+	 .Random_Addr,
+	 .No_Cache,
+	 .Permanent,
+	 .Overwrite,
+} | VM_FLAGS_FIXED | VM_FLAGS_SUPERPAGE_MASK | VM_FLAGS_ALIAS_MASK
+
+VM_FLAGS_USER_MAP :: VM_Flags {
+	.Return_4K_Data_Addr,
+	.Return_Data_Addr,
+} | VM_FLAGS_USER_ALLOCATE
+
+VM_FLAGS_USER_REMAP :: VM_Flags {
+	.Anywhere,
+	.Random_Addr,
+	.Overwrite,
+	.Return_Data_Addr,
+	.Resilient_Codesign,
+	.Resilient_Media,
+} | VM_FLAGS_FIXED
+
+VM_FLAGS_SUPERPAGE_NONE :: VM_Flags{} /* no superpages, if all bits are 0 */
+
+/*
+ *	Protection values, defined as bits within the vm_prot_t type
+ */
+
+VM_Prot :: enum vm_prot_t {
+	Read,
+	Write,
+	Execute,
+}
+
+VM_Prot_Flags :: distinct bit_set[VM_Prot; vm_prot_t]
+
+VM_PROT_NONE    :: VM_Prot_Flags{}
+VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write}
+VM_PROT_ALL     :: VM_Prot_Flags{.Read, .Write, .Execute}
+
+/*
+ *	Enumeration of valid values for vm_inherit_t.
+ */
+
+VM_Inherit :: enum vm_inherit_t {
+	Share,
+	Copy,
+	None,
+	Donate_Copy,
+
+	Default    = Copy,
+	Last_Valid = None,
+}
+
+Sync_Policy :: enum sync_policy_t {
+	Fifo,
+	Fixed_Priority,
+	Reversed,
+	Order_Mask,
 
 
-	semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t ---
-	semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t ---
-	semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t ---
-	
-	semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t ---
+	Lifo = Fifo | Reversed,
 }
 }
 
 
 mach_vm_trunc_page :: proc(v: u64) -> u64 {
 mach_vm_trunc_page :: proc(v: u64) -> u64 {

+ 46 - 1
core/sys/info/cpu_intel.odin

@@ -28,6 +28,23 @@ CPU_Feature :: enum u64 {
 	ssse3,     // Supplemental streaming SIMD extension 3
 	ssse3,     // Supplemental streaming SIMD extension 3
 	sse41,     // Streaming SIMD extension 4 and 4.1
 	sse41,     // Streaming SIMD extension 4 and 4.1
 	sse42,     // Streaming SIMD extension 4 and 4.2
 	sse42,     // Streaming SIMD extension 4 and 4.2
+
+	avx512bf16,         // Vector Neural Network Instructions supporting bfloat16
+	avx512bitalg,       // Bit Algorithms
+	avx512bw,           // Byte and Word instructions
+	avx512cd,           // Conflict Detection instructions
+	avx512dq,           // Doubleword and Quadword instructions
+	avx512er,           // Exponential and Reciprocal instructions
+	avx512f,            // Foundation
+	avx512fp16,         // Vector 16-bit float instructions
+	avx512ifma,         // Integer Fused Multiply Add
+	avx512pf,           // Prefetch instructions
+	avx512vbmi,         // Vector Byte Manipulation Instructions
+	avx512vbmi2,        // Vector Byte Manipulation Instructions 2
+	avx512vl,           // Vector Length extensions
+	avx512vnni,         // Vector Neural Network Instructions
+	avx512vp2intersect, // Vector Pair Intersection to a Pair of Mask Registers
+	avx512vpopcntdq,    // Vector Population Count for Doubleword and Quadword
 }
 }
 
 
 CPU_Features :: distinct bit_set[CPU_Feature; u64]
 CPU_Features :: distinct bit_set[CPU_Feature; u64]
@@ -82,9 +99,11 @@ init_cpu_features :: proc "c" () {
 	//
 	//
 	// See: crbug.com/375968
 	// See: crbug.com/375968
 	os_supports_avx := false
 	os_supports_avx := false
+	os_supports_avx512 := false
 	if .os_xsave in set && is_set(26, ecx1) {
 	if .os_xsave in set && is_set(26, ecx1) {
 		eax, _ := xgetbv(0)
 		eax, _ := xgetbv(0)
 		os_supports_avx = is_set(1, eax) && is_set(2, eax)
 		os_supports_avx = is_set(1, eax) && is_set(2, eax)
+		os_supports_avx512 = is_set(5, eax) && is_set(6, eax) && is_set(7, eax)
 	}
 	}
 	if os_supports_avx {
 	if os_supports_avx {
 		try_set(&set, .avx, 28, ecx1)
 		try_set(&set, .avx, 28, ecx1)
@@ -94,11 +113,37 @@ init_cpu_features :: proc "c" () {
 		return
 		return
 	}
 	}
 
 
-	_, ebx7, _, _ := cpuid(7, 0)
+	_, ebx7, ecx7, edx7 := cpuid(7, 0)
 	try_set(&set, .bmi1, 3, ebx7)
 	try_set(&set, .bmi1, 3, ebx7)
 	if os_supports_avx {
 	if os_supports_avx {
 		try_set(&set, .avx2, 5, ebx7)
 		try_set(&set, .avx2, 5, ebx7)
 	}
 	}
+	if os_supports_avx512 {
+		try_set(&set, .avx512f,    16, ebx7)
+		try_set(&set, .avx512dq,   17, ebx7)
+		try_set(&set, .avx512ifma, 21, ebx7)
+		try_set(&set, .avx512pf,   26, ebx7)
+		try_set(&set, .avx512er,   27, ebx7)
+		try_set(&set, .avx512cd,   28, ebx7)
+		try_set(&set, .avx512bw,   30, ebx7)
+
+		// XMM/YMM are also required for 128/256-bit instructions
+		if os_supports_avx {
+			try_set(&set, .avx512vl, 31, ebx7)
+		}
+
+		try_set(&set, .avx512vbmi,       1, ecx7)
+		try_set(&set, .avx512vbmi2,      6, ecx7)
+		try_set(&set, .avx512vnni,      11, ecx7)
+		try_set(&set, .avx512bitalg,    12, ecx7)
+		try_set(&set, .avx512vpopcntdq, 14, ecx7)
+
+		try_set(&set, .avx512vp2intersect,  8, edx7)
+		try_set(&set, .avx512fp16,         23, edx7)
+
+		eax7_1, _, _, _ := cpuid(7, 1)
+		try_set(&set, .avx512bf16, 5, eax7_1)
+	}
 	try_set(&set, .bmi2,    8, ebx7)
 	try_set(&set, .bmi2,    8, ebx7)
 	try_set(&set, .erms,    9, ebx7)
 	try_set(&set, .erms,    9, ebx7)
 	try_set(&set, .rdseed, 18, ebx7)
 	try_set(&set, .rdseed, 18, ebx7)

+ 16 - 0
core/sys/info/platform_darwin.odin

@@ -494,6 +494,10 @@ macos_release_map: map[string]Darwin_To_Release = {
 	"21G816"     = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 0}}},
 	"21G816"     = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 0}}},
 	"21G920"     = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 1}}},
 	"21G920"     = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 1}}},
 	"21G1974"    = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 2}}},
 	"21G1974"    = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 2}}},
+	"21H1015"    = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 3}}},
+	"21H1123"    = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 4}}},
+	"21H1222"    = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 5}}},
+	"21H1320"    = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 6}}},
 
 
 	// MacOS Ventura 
 	// MacOS Ventura 
 	"22A380"     = {{22, 1, 0}, "macOS", {"Ventura",        {13, 0, 0}}},
 	"22A380"     = {{22, 1, 0}, "macOS", {"Ventura",        {13, 0, 0}}},
@@ -513,6 +517,15 @@ macos_release_map: map[string]Darwin_To_Release = {
 	"22G120"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 0}}},
 	"22G120"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 0}}},
 	"22G313"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 1}}},
 	"22G313"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 1}}},
 	"22G320"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 2}}},
 	"22G320"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 2}}},
+	"22G436"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 3}}},
+	"22G513"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 4}}},
+	"22G621"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 5}}},
+	"22G630"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 6}}},
+	"22G720"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 7}}},
+	"22G820"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 8}}},
+	"22G830"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 9}}},
+	"22H123"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 7, 0}}},
+	"22H221"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 7, 1}}},
 
 
 	// MacOS Sonoma 
 	// MacOS Sonoma 
 	"23A344"     = {{23, 0, 0}, "macOS", {"Sonoma",         {14, 0, 0}}},
 	"23A344"     = {{23, 0, 0}, "macOS", {"Sonoma",         {14, 0, 0}}},
@@ -531,9 +544,12 @@ macos_release_map: map[string]Darwin_To_Release = {
 	"23G80"      = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 6, 0}}},
 	"23G80"      = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 6, 0}}},
 	"23G93"      = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 6, 1}}},
 	"23G93"      = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 6, 1}}},
 	"23H124"     = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 7, 0}}},
 	"23H124"     = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 7, 0}}},
+	"23H222"     = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 7, 1}}},
 
 
 	// MacOS Sequoia
 	// MacOS Sequoia
 	"24A335"     = {{24, 0, 0}, "macOS", {"Sequoia",        {15, 0, 0}}},
 	"24A335"     = {{24, 0, 0}, "macOS", {"Sequoia",        {15, 0, 0}}},
+	"24A348"     = {{24, 0, 0}, "macOS", {"Sequoia",        {15, 0, 1}}},
+	"24B83"      = {{24, 1, 0}, "macOS", {"Sequoia",        {15, 1, 0}}},
 }
 }
 
 
 @(private)
 @(private)

+ 67 - 60
core/sys/linux/bits.odin

@@ -152,66 +152,43 @@ Errno :: enum i32 {
 	RDONLY flag is not present, because it has the value of 0, i.e. it is the
 	RDONLY flag is not present, because it has the value of 0, i.e. it is the
 	default, unless WRONLY or RDWR is specified.
 	default, unless WRONLY or RDWR is specified.
 */
 */
-when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
-	Open_Flags_Bits :: enum {
-		WRONLY    = 0,
-		RDWR      = 1,
-		CREAT     = 6,
-		EXCL      = 7,
-		NOCTTY    = 8,
-		TRUNC     = 9,
-		APPEND    = 10,
-		NONBLOCK  = 11,
-		DSYNC     = 12,
-		ASYNC     = 13,
-		DIRECT    = 14,
-		LARGEFILE = 15,
-		DIRECTORY = 16,
-		NOFOLLOW  = 17,
-		NOATIME   = 18,
-		CLOEXEC   = 19,
-		PATH      = 21,
-	}
-	// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
-	#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
-	#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
-	#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
-	#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
-	#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
-	#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
-	#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
-	#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
-	#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
-	#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
-	#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
-	#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
-	#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
-	#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
-	#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
-	#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
-	#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
-
-} else {
-	Open_Flags_Bits :: enum {
-		WRONLY    = 0,
-		RDWR      = 1,
-		CREAT     = 6,
-		EXCL      = 7,
-		NOCTTY    = 8,
-		TRUNC     = 9,
-		APPEND    = 10,
-		NONBLOCK  = 11,
-		DSYNC     = 12,
-		ASYNC     = 13,
-		DIRECTORY = 14,
-		NOFOLLOW  = 15,
-		DIRECT    = 16,
-		LARGEFILE = 17,
-		NOATIME   = 18,
-		CLOEXEC   = 19,
-		PATH      = 21,
-	}
-}
+Open_Flags_Bits :: enum {
+	WRONLY    = 0,
+	RDWR      = 1,
+	CREAT     = 6,
+	EXCL      = 7,
+	NOCTTY    = 8,
+	TRUNC     = 9,
+	APPEND    = 10,
+	NONBLOCK  = 11,
+	DSYNC     = 12,
+	ASYNC     = 13,
+	DIRECT    = 14,
+	LARGEFILE = 15,
+	DIRECTORY = 16,
+	NOFOLLOW  = 17,
+	NOATIME   = 18,
+	CLOEXEC   = 19,
+	PATH      = 21,
+}
+// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+#assert(1 << uint(Open_Flags_Bits.WRONLY)    == 0o0000000_1)
+#assert(1 << uint(Open_Flags_Bits.RDWR)      == 0o0000000_2)
+#assert(1 << uint(Open_Flags_Bits.CREAT)     == 0o00000_100)
+#assert(1 << uint(Open_Flags_Bits.EXCL)      == 0o00000_200)
+#assert(1 << uint(Open_Flags_Bits.NOCTTY)    == 0o00000_400)
+#assert(1 << uint(Open_Flags_Bits.TRUNC)     == 0o0000_1000)
+#assert(1 << uint(Open_Flags_Bits.APPEND)    == 0o0000_2000)
+#assert(1 << uint(Open_Flags_Bits.NONBLOCK)  == 0o0000_4000)
+#assert(1 << uint(Open_Flags_Bits.DSYNC)     == 0o000_10000)
+#assert(1 << uint(Open_Flags_Bits.ASYNC)     == 0o000_20000)
+#assert(1 << uint(Open_Flags_Bits.DIRECT)    == 0o000_40000)
+#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+#assert(1 << uint(Open_Flags_Bits.NOFOLLOW)  == 0o00_400000)
+#assert(1 << uint(Open_Flags_Bits.NOATIME)   == 0o0_1000000)
+#assert(1 << uint(Open_Flags_Bits.CLOEXEC)   == 0o0_2000000)
+#assert(1 << uint(Open_Flags_Bits.PATH)      == 0o_10000000)
 
 
 /*
 /*
 	Bits for FD_Flags bitset
 	Bits for FD_Flags bitset
@@ -542,6 +519,36 @@ Fd_Poll_Events_Bits :: enum {
 	RDHUP  = 13,
 	RDHUP  = 13,
 }
 }
 
 
+Inotify_Init_Bits :: enum {
+	NONBLOCK = 11,
+	CLOEXEC  = 19,
+}
+
+Inotify_Event_Bits :: enum u32 {
+	ACCESS        = 0,
+	MODIFY        = 1,
+	ATTRIB        = 2,
+	CLOSE_WRITE   = 3,
+	CLOSE_NOWRITE = 4,
+	OPEN          = 5,
+	MOVED_FROM    = 6,
+	MOVED_TO      = 7,
+	CREATE        = 8,
+	DELETE        = 9,
+	DELETE_SELF   = 10,
+	MOVE_SELF     = 11,
+	UNMOUNT       = 13,
+	Q_OVERFLOW    = 14,
+	IGNORED       = 15,
+	ONLYDIR       = 24,
+	DONT_FOLLOW   = 25,
+	EXCL_UNLINK   = 26,
+	MASK_CREATE   = 28,
+	MASK_ADD      = 29,
+	ISDIR         = 30,
+	ONESHOT       = 31,
+}
+
 /*
 /*
 	Bits for Mem_Protection bitfield
 	Bits for Mem_Protection bitfield
 */
 */

+ 25 - 0
core/sys/linux/constants.odin

@@ -135,6 +135,31 @@ STATX_BASIC_STATS :: Statx_Mask {
 	.BLOCKS,
 	.BLOCKS,
 }
 }
 
 
+IN_ALL_EVENTS	:: Inotify_Event_Mask {
+	.ACCESS,
+	.MODIFY,
+	.ATTRIB,
+	.CLOSE_WRITE,
+	.CLOSE_NOWRITE,
+	.OPEN,
+	.MOVED_FROM,
+	.MOVED_TO,
+	.CREATE,
+	.DELETE,
+	.DELETE_SELF,
+	.MOVE_SELF,
+}
+
+IN_CLOSE :: Inotify_Event_Mask {
+	.CLOSE_WRITE,
+	.CLOSE_NOWRITE,
+}
+
+IN_MOVE :: Inotify_Event_Mask {
+	.MOVED_FROM,
+	.MOVED_TO,
+}
+
 /*
 /*
 	Tell `shmget` to create a new key
 	Tell `shmget` to create a new key
 */
 */

+ 22 - 3
core/sys/linux/sys.odin

@@ -2536,11 +2536,30 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt
 
 
 // TODO(flysand): ioprio_get
 // TODO(flysand): ioprio_get
 
 
-// TODO(flysand): inotify_init
+inotify_init :: proc "contextless" () -> (Fd, Errno) {
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
+		ret := syscall(SYS_inotify_init1, 0)
+		return errno_unwrap(ret, Fd)
+	} else {
+		ret := syscall(SYS_inotify_init)
+		return errno_unwrap(ret, Fd)
+	}
+}
 
 
-// TODO(flysand): inotify_add_watch
+inotify_init1 :: proc "contextless" (flags: Inotify_Init_Flags) -> (Fd, Errno) {
+	ret := syscall(SYS_inotify_init1, transmute(i32)flags)
+	return errno_unwrap(ret, Fd)
+}
+
+inotify_add_watch :: proc "contextless" (fd: Fd, pathname: cstring, mask: Inotify_Event_Mask) -> (Wd, Errno) {
+	ret := syscall(SYS_inotify_add_watch, fd, transmute(uintptr) pathname, transmute(u32) mask)
+	return errno_unwrap(ret, Wd)
+}
 
 
-// TODO(flysand): inotify_rm_watch
+inotify_rm_watch :: proc "contextless" (fd: Fd, wd: Wd) -> (Errno) {
+	ret := syscall(SYS_inotify_rm_watch, fd, wd)
+	return Errno(-ret)
+}
 
 
 // TODO(flysand): migrate_pages
 // TODO(flysand): migrate_pages
 
 

+ 23 - 0
core/sys/linux/types.odin

@@ -30,6 +30,11 @@ Id :: distinct uint
 */
 */
 Fd  :: distinct i32
 Fd  :: distinct i32
 
 
+/*
+	Represents a watch descriptor.
+*/
+Wd  :: distinct i32
+
 /*
 /*
 	Type for PID file descriptors.
 	Type for PID file descriptors.
 */
 */
@@ -343,6 +348,18 @@ Poll_Fd :: struct {
 	revents: Fd_Poll_Events,
 	revents: Fd_Poll_Events,
 }
 }
 
 
+Inotify_Init_Flags :: bit_set[Inotify_Init_Bits; i32]
+
+Inotify_Event :: struct {
+	wd:     Wd,
+	mask:   Inotify_Event_Mask,
+	cookie: u32,
+	len:    u32,
+	name:   [0]u8,
+}
+
+Inotify_Event_Mask :: bit_set[Inotify_Event_Bits; u32]
+
 /*
 /*
 	Specifies protection for memory pages.
 	Specifies protection for memory pages.
 */
 */
@@ -1136,6 +1153,12 @@ when ODIN_ARCH == .arm32 {
 		eflags:           uint,
 		eflags:           uint,
 		rsp:              uint,
 		rsp:              uint,
 		ss:               uint,
 		ss:               uint,
+		fs_base:          uint,
+		gs_base:          uint,
+		ds:               uint,
+		es:               uint,
+		fs:               uint,
+		gs:               uint,
 	}
 	}
 	// All floating point state
 	// All floating point state
 	_Arch_User_FP_Regs :: struct {
 	_Arch_User_FP_Regs :: struct {

+ 1 - 0
core/sys/posix/arpa_inet.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"

+ 56 - 31
core/sys/posix/dirent.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -29,12 +30,12 @@ foreign lib {
 			panic(string(posix.strerror(posix.errno())))
 			panic(string(posix.strerror(posix.errno())))
 		}
 		}
 		defer posix.free(list)
 		defer posix.free(list)
-	
+
 		entries := list[:ret]
 		entries := list[:ret]
-	 	for entry in entries {
-	 		log.info(entry)
-	 		posix.free(entry)
-	 	}
+		for entry in entries {
+			log.info(entry)
+			posix.free(entry)
+		}
 
 
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
 	*/
 	*/
@@ -53,15 +54,6 @@ foreign lib {
 	*/
 	*/
 	closedir :: proc(dirp: DIR) -> result ---
 	closedir :: proc(dirp: DIR) -> result ---
 
 
-	/*
-	Return a file descriptor referring to the same directory as the dirp argument.
-
-	// TODO: this is a macro on NetBSD?
-
-	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
-	*/
-	dirfd :: proc(dirp: DIR) -> FD ---
-
 	/*
 	/*
 	Equivalent to the opendir() function except that the directory is specified by a file descriptor
 	Equivalent to the opendir() function except that the directory is specified by a file descriptor
 	rather than by a name.
 	rather than by a name.
@@ -89,16 +81,16 @@ foreign lib {
 	Returns nil when the end is reached or an error occurred (which sets errno).
 	Returns nil when the end is reached or an error occurred (which sets errno).
 
 
 	Example:
 	Example:
-	 	posix.set_errno(.NONE)
-	 	entry := posix.readdir(dirp)
-	 	if entry == nil {
-	 		if errno := posix.errno(); errno != .NONE {
-	 			panic(string(posix.strerror(errno)))
-	 		} else {
-	 			fmt.println("end of directory stream")
-	 		}
-	 	} else {
-	 		fmt.println(entry)
+		posix.set_errno(.NONE)
+		entry := posix.readdir(dirp)
+		if entry == nil {
+			if errno := posix.errno(); errno != .NONE {
+				panic(string(posix.strerror(errno)))
+			} else {
+				fmt.println("end of directory stream")
+			}
+		} else {
+			fmt.println(entry)
 		}
 		}
 
 
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]]
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]]
@@ -160,11 +152,36 @@ when ODIN_OS == .NetBSD {
 	@(private) LSCANDIR   :: "__scandir30"
 	@(private) LSCANDIR   :: "__scandir30"
 	@(private) LOPENDIR   :: "__opendir30"
 	@(private) LOPENDIR   :: "__opendir30"
 	@(private) LREADDIR   :: "__readdir30"
 	@(private) LREADDIR   :: "__readdir30"
+
+	/*
+	Return a file descriptor referring to the same directory as the dirp argument.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
+	*/
+	dirfd :: proc "c" (dirp: DIR) -> FD {
+		_dirdesc :: struct {
+			dd_fd: FD,
+
+			// more stuff...
+		}
+
+		return (^_dirdesc)(dirp).dd_fd
+	}
+
 } else {
 } else {
 	@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
 	@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
 	@(private) LSCANDIR   :: "scandir"   + INODE_SUFFIX
 	@(private) LSCANDIR   :: "scandir"   + INODE_SUFFIX
 	@(private) LOPENDIR   :: "opendir"   + INODE_SUFFIX
 	@(private) LOPENDIR   :: "opendir"   + INODE_SUFFIX
 	@(private) LREADDIR   :: "readdir"   + INODE_SUFFIX
 	@(private) LREADDIR   :: "readdir"   + INODE_SUFFIX
+
+	foreign lib {
+		/*
+		Return a file descriptor referring to the same directory as the dirp argument.
+
+		[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
+		*/
+		dirfd :: proc(dirp: DIR) -> FD ---
+	}
 }
 }
 
 
 when ODIN_OS == .Darwin {
 when ODIN_OS == .Darwin {
@@ -193,13 +210,21 @@ when ODIN_OS == .Darwin {
 } else when ODIN_OS == .NetBSD {
 } else when ODIN_OS == .NetBSD {
 
 
 	dirent :: struct {
 	dirent :: struct {
-		d_ino:     ino_t,                    /* [PSX] file number of entry */
-		d_reclen:  c.uint16_t,               /* length of this record */
-		d_namelen: c.uint16_t,               /* length of string in d_name */
-		d_type:    D_Type,                   /* file type  */
-		d_name:    [512]c.char `fmt:"s,0"`,  /* [PSX] entry name */
+		d_ino:     ino_t,                   /* [PSX] file number of entry */
+		d_reclen:  c.uint16_t,              /* length of this record */
+		d_namelen: c.uint16_t,              /* length of string in d_name */
+		d_type:    D_Type,                  /* file type  */
+		d_name:    [512]c.char `fmt:"s,0"`, /* [PSX] entry name */
 	}
 	}
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+		dirent :: struct {
+			d_ino:    u64,                     /* [PSX] file number of entry */
+			d_off:    i64,                     /* directory offset of the next entry */
+			d_reclen: u16,                     /* length of this record */
+			d_type:   D_Type,                  /* file type  */
+			d_name:   [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
+		}
+
 }
 }

+ 10 - 2
core/sys/posix/dlfcn.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -111,7 +112,14 @@ when ODIN_OS == .Darwin {
 
 
 	RTLD_LOCAL   :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
 	RTLD_LOCAL   :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	RTLD_LAZY    :: 0x001
+	RTLD_NOW     :: 0x002
+	RTLD_GLOBAL  :: 0x100
+
+	_RTLD_LOCAL  :: 0
+	RTLD_LOCAL   :: RTLD_Flags{}
+
 }
 }
 
 

+ 175 - 8
core/sys/posix/errno.odin

@@ -1,3 +1,4 @@
+#+build windows, darwin, linux, freebsd, openbsd, netbsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -141,7 +142,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EPIPE           :: 32
 	EAGAIN          :: 35
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
 	ENOTSOCK        :: 38
@@ -151,7 +152,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
 	EADDRNOTAVAIL   :: 49
@@ -220,7 +221,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EPIPE           :: 32
 	EAGAIN          :: 35
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
 	ENOTSOCK        :: 38
@@ -230,7 +231,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
 	EADDRNOTAVAIL   :: 49
@@ -301,7 +302,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EPIPE           :: 32
 	EAGAIN          :: 35
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
 	ENOTSOCK        :: 38
@@ -311,7 +312,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
 	EADDRNOTAVAIL   :: 49
@@ -367,7 +368,173 @@ when ODIN_OS == .Darwin {
 		ETIME           :: -1
 		ETIME           :: -1
 	}
 	}
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+	EPERM           :: 1
+	ENOENT          :: 2
+	ESRCH           :: 3
+	EINTR           :: 4
+	EIO             :: 5
+	ENXIO           :: 6
+	E2BIG           :: 7
+	ENOEXEC         :: 8
+	EBADF           :: 9
+	ECHILD          :: 10
+	EAGAIN          :: 11
+	EWOULDBLOCK     :: EAGAIN
+	ENOMEM          :: 12
+	EACCES          :: 13
+	EFAULT          :: 14
+	EBUSY           :: 16
+	EEXIST          :: 17
+	EXDEV           :: 18
+	ENODEV          :: 19
+	ENOTDIR         :: 20
+	EISDIR          :: 21
+	EINVAL          :: 22
+	ENFILE          :: 23
+	EMFILE          :: 24
+	ENOTTY          :: 25
+	ETXTBSY         :: 26
+	EFBIG           :: 27
+	ENOSPC          :: 28
+	ESPIPE          :: 29
+	EROFS           :: 30
+	EMLINK          :: 31
+	EPIPE           :: 32
+
+	EDEADLK         :: 35
+	ENAMETOOLONG    :: 36
+	ENOLCK          :: 37
+	ENOSYS          :: 38
+	ENOTEMPTY       :: 39
+	ELOOP           :: 40
+	ENOMSG          :: 42
+	EIDRM           :: 43
+
+	ENOSTR          :: 60
+	ENODATA         :: 61
+	ETIME           :: 62
+	ENOSR           :: 63
+
+	ENOLINK         :: 67
+
+	EPROTO          :: 71
+	EMULTIHOP       :: 72
+	EBADMSG         :: 74
+	EOVERFLOW       :: 75
+
+	ENOTSOCK        :: 88
+	EDESTADDRREQ    :: 89
+	EMSGSIZE        :: 90
+	EPROTOTYPE      :: 91
+	ENOPROTOOPT     :: 92
+	EPROTONOSUPPORT :: 93
+
+	EOPNOTSUPP      :: 95
+	ENOTSUP         :: EOPNOTSUPP
+	EAFNOSUPPORT    :: 97
+	EADDRINUSE      :: 98
+	EADDRNOTAVAIL   :: 99
+	ENETDOWN        :: 100
+	ENETUNREACH     :: 101
+	ENETRESET       :: 102
+	ECONNABORTED    :: 103
+	ECONNRESET      :: 104
+	ENOBUFS         :: 105
+	EISCONN         :: 106
+	ENOTCONN        :: 107
+
+	ETIMEDOUT       :: 110
+	ECONNREFUSED    :: 111
+
+	EHOSTUNREACH    :: 113
+	EALREADY        :: 114
+	EINPROGRESS     :: 115
+	ESTALE          :: 116
+
+	EDQUOT          :: 122
+	ECANCELED       :: 125
+
+	EOWNERDEAD      :: 130
+	ENOTRECOVERABLE :: 131
+} else when ODIN_OS == .Windows {
+	E2BIG           :: 7
+	EACCES          :: 13
+	EADDRINUSE      :: 100
+	EADDRNOTAVAIL   :: 101
+	EAFNOSUPPORT    :: 102
+	EAGAIN          :: 11
+	EALREADY        :: 103
+	EBADF           :: 9
+	EBADMSG         :: 104
+	EBUSY           :: 16
+	ECANCELED       :: 105
+	ECHILD          :: 10
+	ECONNABORTED    :: 106
+	ECONNREFUSED    :: 107
+	ECONNRESET      :: 108
+	EDEADLK         :: 36
+	EDESTADDRREQ    :: 109
+	EDQUOT          :: -1 // NOTE: not defined
+	EEXIST          :: 17
+	EFAULT          :: 14
+	EFBIG           :: 27
+	EHOSTUNREACH    :: 110
+	EIDRM           :: 111
+	EINPROGRESS     :: 112
+	EINTR           :: 4
+	EINVAL          :: 22
+	EIO             :: 5
+	EISCONN         :: 113
+	EISDIR          :: 21
+	ELOOP           :: 114
+	EMFILE          :: 24
+	EMLINK          :: 31
+	EMSGSIZE        :: 115
+	EMULTIHOP       :: -1 // NOTE: not defined
+	ENAMETOOLONG    :: 38
+	ENETDOWN        :: 116
+	ENETRESET       :: 117
+	ENETUNREACH     :: 118
+	ENFILE          :: 23
+	ENOBUFS         :: 119
+	ENODATA         :: 120
+	ENODEV          :: 19
+	ENOENT          :: 2
+	ENOEXEC         :: 8
+	ENOLCK          :: 39
+	ENOLINK         :: 121
+	ENOMEM          :: 12
+	ENOMSG          :: 122
+	ENOPROTOOPT     :: 123
+	ENOSPC          :: 28
+	ENOSR           :: 124
+	ENOSTR          :: 125
+	ENOSYS          :: 40
+	ENOTCONN        :: 126
+	ENOTDIR         :: 20
+	ENOTEMPTY       :: 41
+	ENOTRECOVERABLE :: 127
+	ENOTSOCK        :: 128
+	ENOTSUP         :: 129
+	ENOTTY          :: 25
+	ENXIO           :: 6
+	EOPNOTSUPP      :: 130
+	EOVERFLOW       :: 132
+	EOWNERDEAD      :: 133
+	EPERM           :: 1
+	EPIPE           :: 32
+	EPROTO          :: 134
+	EPROTONOSUPPORT :: 135
+	EPROTOTYPE      :: 136
+	EROFS           :: 30
+	ESPIPE          :: 29
+	ESRCH           :: 3
+	ESTALE          :: -1 // NOTE: not defined
+	ETIME           :: 137
+	ETIMEDOUT       :: 138
+	ETXTBSY         :: 139
+	EWOULDBLOCK     :: 140
+	EXDEV           :: 18
 }
 }
 
 

+ 107 - 45
core/sys/posix/fcntl.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, openbsd, freebsd, netbsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -92,9 +93,6 @@ Lock_Type :: enum c.short {
 	WRLCK = F_WRLCK,
 	WRLCK = F_WRLCK,
 }
 }
 
 
-// Assertions made to unify this bit set.
-#assert(O_RDONLY == 0)
-
 O_Flag_Bits :: enum c.int {
 O_Flag_Bits :: enum c.int {
 	// Sets FD_CLOEXEC on the file descriptor.
 	// Sets FD_CLOEXEC on the file descriptor.
 	CLOEXEC   = log2(O_CLOEXEC),
 	CLOEXEC   = log2(O_CLOEXEC),
@@ -107,11 +105,11 @@ O_Flag_Bits :: enum c.int {
 	// If terminal device, do not make it the controlling terminal for the process.
 	// If terminal device, do not make it the controlling terminal for the process.
 	NOCTTY    = log2(O_NOCTTY),
 	NOCTTY    = log2(O_NOCTTY),
 	// Don't follow symbolic links, fail with errno ELOOP.
 	// Don't follow symbolic links, fail with errno ELOOP.
-	NOFOLLOW  = log2(O_NOFOLOW),
+	NOFOLLOW  = log2(O_NOFOLLOW),
 	// If exists and regular, truncate the length to 0.
 	// If exists and regular, truncate the length to 0.
 	TRUNC     = log2(O_TRUNC),
 	TRUNC     = log2(O_TRUNC),
 
 
- 	// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
+	// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
 	// this bit set enum because it is 0 on some platforms and a value on others.
 	// this bit set enum because it is 0 on some platforms and a value on others.
 	// TTY_INIT = O_TTY_INIT,
 	// TTY_INIT = O_TTY_INIT,
 
 
@@ -123,7 +121,8 @@ O_Flag_Bits :: enum c.int {
 	NONBLOCK  = log2(O_NONBLOCK),
 	NONBLOCK  = log2(O_NONBLOCK),
 	// Write I/O shall complete as defined by synchronized I/O file integrity completion.
 	// Write I/O shall complete as defined by synchronized I/O file integrity completion.
 	SYNC      = log2(O_SYNC),
 	SYNC      = log2(O_SYNC),
- 	// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
+
+	// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
 	// this bit set enum because it is 0 on some platforms and a value on others.
 	// this bit set enum because it is 0 on some platforms and a value on others.
 	// RSYNC = O_RSYNC,
 	// RSYNC = O_RSYNC,
 
 
@@ -135,11 +134,10 @@ O_Flag_Bits :: enum c.int {
 	WRONLY    = log2(O_WRONLY),
 	WRONLY    = log2(O_WRONLY),
 	// Reading only.
 	// Reading only.
 	// RDONLY = 0, // Default
 	// RDONLY = 0, // Default
-
 }
 }
+
 O_Flags :: bit_set[O_Flag_Bits; c.int]
 O_Flags :: bit_set[O_Flag_Bits; c.int]
 
 
-// A mask of all the access mode bits.
 O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY }
 O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY }
 
 
 AT_Flag_Bits :: enum c.int {
 AT_Flag_Bits :: enum c.int {
@@ -152,8 +150,8 @@ AT_Flags :: bit_set[AT_Flag_Bits; c.int]
 
 
 when ODIN_OS == .Darwin {
 when ODIN_OS == .Darwin {
 
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 
 	F_DUPFD         :: 0
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 67
 	F_DUPFD_CLOEXEC :: 67
@@ -178,7 +176,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x00100000
 	O_DIRECTORY :: 0x00100000
 	O_EXCL      :: 0x00000800
 	O_EXCL      :: 0x00000800
 	O_NOCTTY    :: 0x00020000
 	O_NOCTTY    :: 0x00020000
-	O_NOFOLOW   :: 0x00000100
+	O_NOFOLLOW  :: 0x00000100
 	O_TRUNC     :: 0x00000400
 	O_TRUNC     :: 0x00000400
 
 
 	_O_TTY_INIT :: 0
 	_O_TTY_INIT :: 0
@@ -189,16 +187,16 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x00000004
 	O_NONBLOCK :: 0x00000004
 	O_SYNC     :: 0x0080
 	O_SYNC     :: 0x0080
 
 
-	_O_RSYNC   :: 0
-	O_RSYNC    :: O_Flags{}
+	_O_RSYNC :: 0
+	O_RSYNC  :: O_Flags{}
 
 
-	O_EXEC    :: 0x40000000
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x40000000
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 
 	_O_SEARCH :: O_EXEC | O_DIRECTORY
 	_O_SEARCH :: O_EXEC | O_DIRECTORY
-	O_SEARCH  :: O_Flags{ .EXEC, .DIRECTORY }
+	O_SEARCH  :: O_Flags{.EXEC, .DIRECTORY}
 
 
 	AT_FDCWD: FD: -2
 	AT_FDCWD: FD: -2
 
 
@@ -217,8 +215,8 @@ when ODIN_OS == .Darwin {
 
 
 } else when ODIN_OS == .FreeBSD {
 } else when ODIN_OS == .FreeBSD {
 
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 
 	F_DUPFD         :: 0
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 17
 	F_DUPFD_CLOEXEC :: 17
@@ -243,7 +241,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x00020000
 	O_DIRECTORY :: 0x00020000
 	O_EXCL      :: 0x0800
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 	O_TRUNC     :: 0x0400
 
 
 	_O_TTY_INIT :: 0x00080000
 	_O_TTY_INIT :: 0x00080000
@@ -256,10 +254,10 @@ when ODIN_OS == .Darwin {
 	_O_RSYNC   :: 0
 	_O_RSYNC   :: 0
 	O_RSYNC    :: O_Flags{} // NOTE: not defined in headers
 	O_RSYNC    :: O_Flags{} // NOTE: not defined in headers
 
 
-	O_EXEC    :: 0x00040000
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x00040000
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 
 	_O_SEARCH :: O_EXEC
 	_O_SEARCH :: O_EXEC
 	O_SEARCH  :: O_Flags{ .EXEC }
 	O_SEARCH  :: O_Flags{ .EXEC }
@@ -282,8 +280,8 @@ when ODIN_OS == .Darwin {
 
 
 } else when ODIN_OS == .NetBSD {
 } else when ODIN_OS == .NetBSD {
 
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 
 	F_DUPFD         :: 0
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 12
 	F_DUPFD_CLOEXEC :: 12
@@ -308,7 +306,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x0020000
 	O_DIRECTORY :: 0x0020000
 	O_EXCL      :: 0x0800
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 	O_TRUNC     :: 0x0400
 
 
 	_O_TTY_INIT :: 0
 	_O_TTY_INIT :: 0
@@ -319,14 +317,14 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x0004
 	O_NONBLOCK :: 0x0004
 	O_SYNC     :: 0x0080
 	O_SYNC     :: 0x0080
 
 
-	_O_RSYNC   :: 0x0002
-	O_RSYNC    :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
+	_O_RSYNC :: 0x0002
+	O_RSYNC  :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
 
 
 
 
-	O_EXEC    :: 0x04000000
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x04000000
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 
 	_O_SEARCH :: 0x00800000
 	_O_SEARCH :: 0x00800000
 	O_SEARCH  :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
 	O_SEARCH  :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
@@ -347,8 +345,8 @@ when ODIN_OS == .Darwin {
 	}
 	}
 } else when ODIN_OS == .OpenBSD {
 } else when ODIN_OS == .OpenBSD {
 
 
-	off_t  :: distinct c.int64_t
-	pid_t  :: distinct c.int32_t
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int32_t
 
 
 	F_DUPFD         :: 0
 	F_DUPFD         :: 0
 	F_DUPFD_CLOEXEC :: 10
 	F_DUPFD_CLOEXEC :: 10
@@ -373,7 +371,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x20000
 	O_DIRECTORY :: 0x20000
 	O_EXCL      :: 0x0800
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 	O_TRUNC     :: 0x0400
 
 
 	_O_TTY_INIT :: 0
 	_O_TTY_INIT :: 0
@@ -384,13 +382,13 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x0004
 	O_NONBLOCK :: 0x0004
 	O_SYNC     :: 0x0080
 	O_SYNC     :: 0x0080
 
 
-	_O_RSYNC   :: O_SYNC
-	O_RSYNC    :: O_Flags{ .SYNC }
+	_O_RSYNC :: O_SYNC
+	O_RSYNC  :: O_Flags{.SYNC}
 
 
-	O_EXEC    :: 0x04000000 // NOTE: not defined in the headers
-	O_RDONLY  :: 0
-	O_RDWR    :: 0x0002
-	O_WRONLY  :: 0x0001
+	O_EXEC   :: 0x04000000 // NOTE: not defined in the headers
+	O_RDONLY :: 0
+	O_RDWR   :: 0x0002
+	O_WRONLY :: 0x0001
 
 
 	_O_SEARCH :: 0
 	_O_SEARCH :: 0
 	O_SEARCH  :: O_Flags{} // NOTE: not defined in the headers
 	O_SEARCH  :: O_Flags{} // NOTE: not defined in the headers
@@ -410,6 +408,70 @@ when ODIN_OS == .Darwin {
 		l_whence: c.short,   /* [PSX] flag (Whence) of starting offset */
 		l_whence: c.short,   /* [PSX] flag (Whence) of starting offset */
 	}
 	}
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	off_t :: distinct c.int64_t
+	pid_t :: distinct c.int
+
+	F_DUPFD  :: 0
+	F_GETFD  :: 1
+	F_SETFD  :: 2
+	F_GETFL  :: 3
+	F_SETFL  :: 4
+	F_GETLK  :: 5
+	F_SETLK  :: 6
+	F_SETLKW :: 7
+	F_SETOWN :: 8
+	F_GETOWN :: 9
+	F_RDLCK  :: 0
+	F_UNLCK  :: 2
+	F_WRLCK  :: 1
+
+	F_DUPFD_CLOEXEC :: 1030
+
+	FD_CLOEXEC :: 1
+
+	O_CREAT     :: 0o0_000_100
+	O_EXCL      :: 0o0_000_200
+	O_NOCTTY    :: 0o0_000_400
+	O_TRUNC     :: 0o0_001_000
+	O_DIRECTORY :: 0o0_200_000
+	O_NOFOLLOW  :: 0o0_400_000
+	O_CLOEXEC   :: 0o2_000_000
+
+	_O_TTY_INIT :: 0
+	O_TTY_INIT  :: O_Flags{}
+
+	O_APPEND   :: 0o0_002_000
+	O_NONBLOCK :: 0o0_004_000
+	O_DSYNC    :: 0o0_010_000
+	O_SYNC     :: 0o4_010_000
+
+	_O_RSYNC :: 0
+	O_RSYNC  :: O_Flags{}
+
+	O_EXEC   :: 0x04000000 // NOTE: not defined in the headers
+
+	O_RDONLY :: 0
+	O_WRONLY :: 0o1
+	O_RDWR   :: 0o2
+
+	_O_SEARCH :: 0
+	O_SEARCH  :: O_Flags{}
+
+	AT_FDCWD: FD: -100
+
+	AT_EACCESS          :: 0x200
+	AT_SYMLINK_NOFOLLOW :: 0x100
+	AT_SYMLINK_FOLLOW   :: 0x400
+	AT_REMOVEDIR        :: 0x200
+
+	flock :: struct {
+		l_type:   Lock_Type, /* [PSX] type of lock. */
+		l_whence: c.short,   /* [PSX] flag (Whence) of starting offset. */
+		l_start:  off_t,     /* [PSX] relative offset in bytes. */
+		l_len:    off_t,     /* [PSX] size; if 0 then until EOF. */
+		l_pid:    pid_t,     /* [PSX] process ID of the process holding the lock. */
+	}
+
 }
 }

+ 9 - 2
core/sys/posix/fnmatch.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, openbsd, freebsd, netbsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -53,6 +54,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	FNM_PERIOD   :: 0x04
 	FNM_PERIOD   :: 0x04
 	FNM_NOESCAPE :: 0x01
 	FNM_NOESCAPE :: 0x01
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	FNM_NOMATCH  :: 1
+
+	FNM_PATHNAME :: 0x01
+	FNM_NOESCAPE :: 0x02
+	FNM_PERIOD   :: 0x04
+
 }
 }

+ 32 - 4
core/sys/posix/glob.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -112,7 +113,7 @@ when ODIN_OS == .Darwin {
 
 
 	glob_t :: struct {
 	glob_t :: struct {
 		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
 		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
-		gl_matchc: c.size_t,                         /* count of paths matching pattern */
+		gl_matchc: c.size_t,                      /* count of paths matching pattern */
 		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
 		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
 		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
 		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
@@ -144,7 +145,7 @@ when ODIN_OS == .Darwin {
 
 
 	glob_t :: struct {
 	glob_t :: struct {
 		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
 		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
-		gl_matchc: c.size_t,                         /* count of paths matching pattern */
+		gl_matchc: c.size_t,                      /* count of paths matching pattern */
 		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
 		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
 		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
 		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
@@ -174,6 +175,33 @@ when ODIN_OS == .Darwin {
 	GLOB_NOMATCH :: -3
 	GLOB_NOMATCH :: -3
 	GLOB_NOSPACE :: -1
 	GLOB_NOSPACE :: -1
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	glob_t :: struct {
+		gl_pathc:  c.size_t,                      /* [PSX] count of paths matched by pattern */
+		gl_pathv:  [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
+		gl_offs:   c.size_t,                      /* [PSX] slots to reserve at the beginning of gl_pathv */
+		gl_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
+
+		// Non-standard alternate file system access functions:
+
+		gl_closedir: proc "c" (dirp: DIR),
+		gl_readdir:  proc "c" (dirp: DIR) -> ^dirent,
+		gl_opendir:  proc "c" (path: cstring) -> DIR,
+		gl_lstat:    proc "c" (path: cstring, buf: ^stat_t) -> result,
+		gl_stat:     proc "c" (path: cstring, buf: ^stat_t) -> result,
+	}
+
+	GLOB_ERR      :: 1 << 0
+	GLOB_MARK     :: 1 << 1
+	GLOB_NOSORT   :: 1 << 2
+	GLOB_DOOFFS   :: 1 << 3
+	GLOB_NOCHECK  :: 1 << 4
+	GLOB_APPEND   :: 1 << 5
+	GLOB_NOESCAPE :: 1 << 6
+
+	GLOB_NOSPACE :: 1
+	GLOB_ABORTED :: 2
+	GLOB_NOMATCH :: 3
+
 }
 }

+ 2 - 3
core/sys/posix/grp.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -114,7 +115,7 @@ foreign lib {
 	getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
 	getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
 }
 }
 
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
 
 
 	gid_t :: distinct c.uint32_t
 	gid_t :: distinct c.uint32_t
 
 
@@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		gr_mem:    [^]cstring, /* [PSX] group members */
 		gr_mem:    [^]cstring, /* [PSX] group members */
 	}
 	}
 
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }
 }

+ 1 - 0
core/sys/posix/iconv.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"

+ 87 - 2
core/sys/posix/langinfo.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 package posix
 
 
 import "core:c"
 import "core:c"
@@ -238,7 +239,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 	ABDAY_4 :: 16
 	ABDAY_4 :: 16
 	ABDAY_5 :: 17
 	ABDAY_5 :: 17
 	ABDAY_6 :: 18
 	ABDAY_6 :: 18
-	ABDAY_7 :: 19
+	ABDAY_7 :: 19	
 
 
 	MON_1  :: 20
 	MON_1  :: 20
 	MON_2  :: 21
 	MON_2  :: 21
@@ -278,7 +279,91 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 	YESEXPR :: 47
 	YESEXPR :: 47
 	NOEXPR  :: 49
 	NOEXPR  :: 49
 
 
-	CRNCYSTR :: 50
+	CRNCYSTR :: 50	
+
+} else when ODIN_OS == .Linux {
+
+	// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
+	nl_item_t :: distinct c.int
+
+	// NOTE: All these values are set in an enum on the Linux implementation.
+	// Some depend on locale.h contants (bits/locale.h to be precise).
+
+	// NOTE: ABDAY_1 is set to LC_TIME << 16 (LC_TIME is 2) on the enum group of
+	// the Linux implementation.
+	ABDAY_1 :: 0x20_000
+	ABDAY_2 :: 0x20_001
+	ABDAY_3 :: 0x20_002
+	ABDAY_4 :: 0x20_003
+	ABDAY_5 :: 0x20_004
+	ABDAY_6 :: 0x20_005
+	ABDAY_7 :: 0x20_006
+
+	DAY_1 :: 0x20_007
+	DAY_2 :: 0x20_008
+	DAY_3 :: 0x20_009
+	DAY_4 :: 0x20_00A
+	DAY_5 :: 0x20_00B
+	DAY_6 :: 0x20_00C
+	DAY_7 :: 0x20_00D
+
+	ABMON_1  :: 0x20_00E
+	ABMON_2  :: 0x20_010
+	ABMON_3  :: 0x20_011
+	ABMON_4  :: 0x20_012
+	ABMON_5  :: 0x20_013
+	ABMON_6  :: 0x20_014
+	ABMON_7  :: 0x20_015
+	ABMON_8  :: 0x20_016
+	ABMON_9  :: 0x20_017
+	ABMON_10 :: 0x20_018
+	ABMON_11 :: 0x20_019
+	ABMON_12 :: 0x20_01A
+
+	MON_1  :: 0x20_01B
+	MON_2  :: 0x20_01C
+	MON_3  :: 0x20_01D
+	MON_4  :: 0x20_01E
+	MON_5  :: 0x20_020
+	MON_6  :: 0x20_021
+	MON_7  :: 0x20_022
+	MON_8  :: 0x20_023
+	MON_9  :: 0x20_024
+	MON_10 :: 0x20_025
+	MON_11 :: 0x20_026
+	MON_12 :: 0x20_027
+
+	AM_STR :: 0x20_028
+	PM_STR :: 0x20_029
+
+	D_T_FMT    :: 0x20_02A
+	D_FMT      :: 0x20_02B
+	T_FMT      :: 0x20_02C
+	T_FMT_AMPM :: 0x20_02D
+
+	ERA         :: 0x20_02E
+	ERA_D_FMT   :: 0x20_030
+	ALT_DIGITS  :: 0x20_031
+	ERA_D_T_FMT :: 0x20_032
+	ERA_T_FMT   :: 0x20_033
+
+	// NOTE: CODESET is the 16th member of the enum group starting with value
+	// LC_CTYPE << 16, LC_CTYPE is 0.
+	CODESET :: 0x0F
+
+	// NOTE: CRNCYSTR is the 16th member of the enum group starting with value
+	// LC_MONETARY << 16, LC_MONETARY is 4.
+	CRNCYSTR :: 0x40_00F
+
+	// NOTE: RADIXCHAR is the 1st member of the enum group starting with value
+	// LC_NUMERIC << 16, LC_NUMERIC is 1.
+	RADIXCHAR :: 0x10_000
+	THOUSEP   :: 0x10_001
+
+	// NOTE: YESEXPR is the 1st member of the enum group starting with value
+	// LC_MESSAGES << 16, LC_MESSAGES is 5.
+	YESEXPR :: 0x50_000
+	NOEXPR  :: 0x50_001
 
 
 } else {
 } else {
 	#panic("posix is unimplemented for the current target")
 	#panic("posix is unimplemented for the current target")

+ 8 - 0
core/sys/posix/libgen.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 package posix
 
 
 when ODIN_OS == .Darwin {
 when ODIN_OS == .Darwin {
@@ -56,6 +57,7 @@ foreign lib {
 
 
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
 	*/
 	*/
+	@(link_name=LBASENAME)
 	basename :: proc(path: cstring) -> cstring ---
 	basename :: proc(path: cstring) -> cstring ---
 
 
 	/*
 	/*
@@ -72,3 +74,9 @@ foreign lib {
 	*/
 	*/
 	dirname :: proc(path: cstring) -> cstring ---
 	dirname :: proc(path: cstring) -> cstring ---
 }
 }
+
+when ODIN_OS == .Linux {
+	@(private) LBASENAME :: "__xpg_basename"
+} else {
+	@(private) LBASENAME :: "basename"
+}

Some files were not shown because too many files changed in this diff