ソースを参照

update to master

Colin Davidson 9 ヶ月 前
コミット
d60fb5a44e
100 ファイル変更2561 行追加912 行削除
  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/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)
+          ./odin check tests/benchmark -vet -strict-style -no-entry-point
+
   build_freebsd:
     name: FreeBSD Build, Check, and Test
     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/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)
+          ./odin check tests/benchmark -vet -strict-style -no-entry-point
   ci:
     strategy:
       fail-fast: false
@@ -128,6 +131,8 @@ jobs:
           cd tests/issues
           ./run.sh
 
+      - name: Check benchmarks
+        run: ./odin check tests/benchmark -vet -strict-style -no-entry-point
       - name: Odin check examples/all for Linux i386
         run: ./odin check examples/all -vet -strict-style -disallow-do -target:linux_i386
         if: matrix.os == 'ubuntu-latest'
@@ -203,6 +208,11 @@ jobs:
         run: |
           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
+      - 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
         shell: cmd
         run: |

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

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

+ 3 - 0
.gitignore

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

+ 0 - 12
base/runtime/core.odin

@@ -171,14 +171,6 @@ Type_Info_Simd_Vector :: struct {
 	elem_size:  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 {
 	elem:         ^Type_Info,
 	elem_size:    int,
@@ -241,8 +233,6 @@ Type_Info :: struct {
 		Type_Info_Map,
 		Type_Info_Bit_Set,
 		Type_Info_Simd_Vector,
-		Type_Info_Relative_Pointer,
-		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Matrix,
 		Type_Info_Soa_Pointer,
 		Type_Info_Bit_Field,
@@ -275,8 +265,6 @@ Typeid_Kind :: enum u8 {
 	Map,
 	Bit_Set,
 	Simd_Vector,
-	Relative_Pointer,
-	Relative_Multi_Pointer,
 	Matrix,
 	Soa_Pointer,
 	Bit_Field,

+ 48 - 15
base/runtime/core_builtin.odin

@@ -6,6 +6,39 @@ import "base:intrinsics"
 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)
 container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
 	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
 }
 
-// `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.
 //
 // Note: Prefer using the procedure group `make`.
 @(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)
 	context.allocator = allocator
 
@@ -392,6 +436,7 @@ make :: proc{
 	make_dynamic_array_len,
 	make_dynamic_array_len_cap,
 	make_map,
+	make_map_cap,
 	make_multi_pointer,
 
 	make_soa_slice,
@@ -894,19 +939,7 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location)
 
 @builtin
 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_byte(']')
 		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:
 		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_month=%CURR_DATE_TIME:~4,2%
+set curr_day=%CURR_DATE_TIME:~6,2%
 
 :: Make sure this is a decent name and not generic
 set exe_name=odin.exe
@@ -45,7 +45,19 @@ if "%2" == "1" (
 	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
 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_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
 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 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
 
 if %nightly% equ 1 set compiler_defines=%compiler_defines% -DNIGHTLY
 
 if %release_mode% EQU 0 ( rem Debug
 	set compiler_flags=%compiler_flags% -Od -MDd -Z7
+	set rc_flags=%rc_flags% -D_DEBUG
 ) else ( rem Release
 	set compiler_flags=%compiler_flags% -O2 -MT -Z7
 	set compiler_defines=%compiler_defines% -DNO_ARRAY_BOUNDS_CHECK
@@ -82,6 +106,8 @@ set libs= ^
 	kernel32.lib ^
 	Synchronization.lib ^
 	bin\llvm\windows\LLVM-C.lib
+set odin_res=misc\odin.res
+set odin_rc=misc\odin.rc
 
 rem DO NOT TOUCH!
 rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
@@ -93,7 +119,7 @@ if %tilde_backend% EQU 1 (
 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
 	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 linker_settings=%libs% %linker_flags%
+set linker_settings=%libs% %odin_res% %linker_flags%
 
 del *.pdb > 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%
+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
 
 call build_vendor.bat
 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
-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.
 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"
 		;;
 	release-native)
-		if [ "$OS_ARCH" = "arm64" ]; then
+		if [ "$OS_ARCH" = "arm64" ] || [ "$OS_ARCH" = "aarch64" ]; then
 			# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
 			EXTRAFLAGS="-O3 -mcpu=native"
 		else
@@ -152,9 +152,7 @@ build_odin() {
 }
 
 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
@@ -166,14 +164,20 @@ if [ $# -eq 0 ]; then
 elif [ $# -eq 1 ]; then
 	case $1 in
 	report)
-		[ ! -f "./odin" ] && build_odin debug
+		if [ ! -f "./odin" ]; then
+			build_odin debug
+			run_demo
+		fi
 		./odin report
 		;;
+	debug)
+		build_odin debug
+		run_demo
+		;;
 	*)
 		build_odin $1
 		;;
 	esac
-	run_demo
 else
 	error "Too many arguments!"
 fi

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

@@ -291,7 +291,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
 		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")
 		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                                    |
 | `<iso646.h>`      | Not applicable, use Odin's operators               |
 | `<limits.h>`      | Not projected                                      |
-| `<locale.h>`      | Not projected                                      |
+| `<locale.h>`      | Fully projected                                    |
 | `<math.h>`        | Mostly projected, see [limitations](#Limitations)  |
 | `<setjmp.h>`      | Fully projected                                    |
 | `<signal.h>`      | Fully projected                                    |
@@ -70,4 +70,4 @@ with the following copyright.
 
 ```
 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
 }
 
-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 != .acq_rel)
 

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

@@ -13,6 +13,7 @@ Example:
 	package main
 
 	import "core:bytes"
+	import "core:compress/zlib"
 	import "core:fmt"
 
 	main :: proc() {
@@ -36,7 +37,7 @@ Example:
 		buf: bytes.Buffer
 
 		// 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)
 
 		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 {
 	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).
 
@@ -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).
 */
 create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
-	context.allocator = allocator
 	size_in_bits := max_index - min_index
 
 	if size_in_bits < 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
-	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.bias         = min_index
 	res.length       = max_index - min_index
-	res.free_pointer = true
+	res.free_pointer = false
 	return
 }
+
 /*
 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 {
 	@(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
 		// are flipped, so the 64-bit addition can't be done with a simple
 		// 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:
 	//    z = z || 0
 
-	// 3. while (len(z)/8) mod w  0:
+	// 3. while (len(z)/8) mod w != 0:
 	//    z = z || 00000000
 	z_len := u128(z_hi) << 64 | u128(z_lo)
 	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) {

+ 5 - 5
core/dynlib/lib.odin

@@ -37,8 +37,8 @@ Example:
 		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.
 last_error :: proc() -> string {
 	return _last_error()
-}
+}

+ 5 - 3
core/dynlib/lib_js.odin

@@ -2,7 +2,9 @@
 #+private
 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
 }
 
@@ -10,10 +12,10 @@ _unload_library :: proc(library: Library) -> bool {
 	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
 }
 
 _last_error :: proc() -> string {
 	return ""
-}
+}

+ 20 - 10
core/dynlib/lib_unix.odin

@@ -2,28 +2,38 @@
 #+private
 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 {
-		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
 }
 
 _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
 	return
 }
 
 _last_error :: proc() -> string {
-	err := os.dlerror()
+	err := string(posix.dlerror())
 	return "unknown" if err == "" else err
-}
+}

+ 5 - 3
core/dynlib/lib_windows.odin

@@ -2,11 +2,13 @@
 #+private
 package dynlib
 
+import "base:runtime"
+
 import win32 "core:sys/windows"
 import "core:strings"
 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
 	wide_path := win32.utf8_to_wstring(path, allocator)
 	defer free(wide_path, allocator)
@@ -19,7 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
 	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)
 	defer delete(c_str, allocator)
 	ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
@@ -31,4 +33,4 @@ _last_error :: proc() -> string {
 	err := win32.System_Error(win32.GetLastError())
 	err_msg := reflect.enum_string(err)
 	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
 					}
 				}
-				return false
+				return true
 			}
 
 			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,
 	) -> (out_of_space: bool, err: Unmarshal_Error) {
 		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
 			
 			// 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 }
 			}
 			
+			// 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)
 			if length == -1 && err == .Break { break }
 			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.data       = raw_data(data) 
 		raw.len        = 0
-		raw.cap        = length
+		raw.cap        = scap
 		raw.allocator  = context.allocator
 
 		_ = 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
 		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.
 			key: string
 			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.
 				if use_field_idx < 0 {
+					val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+					destroy(val, context.temp_allocator)
 					continue
 				}
 			}
@@ -672,6 +676,17 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
 			fany  := any{ptr, field.type.id}
 			_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
 
 	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.components = read_value(r, u8) 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 {
 					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)))

+ 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_string(w, name, &n) or_return
 	io.write_byte  (w, ']',  &n) or_return
+	io.write_byte  (w, '\n',  &n) or_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:
 		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:
 		return .Unsupported_Type

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

@@ -172,20 +172,33 @@ assign_float :: proc(val: any, f: $T) -> bool {
 
 
 @(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
 	switch &dst in val {
 	case string:
 		dst = str
-		return true
+		return true, nil
 	case cstring:  
 		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 {
 			// NOTE: This is valid because 'clone_string' appends a NUL terminator
 			dst = cstring(raw_data(str)) 
 		}
-		return true
+		ok = true
+		return
 	}
 	
 	#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 {
 			if name == str {
 				assign_int(val, variant.values[i])
-				return true
+				return true, nil
 			}
 		}
 		// TODO(bill): should this be an error or not?
-		return true
+		return true, nil
 		
 	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) {
-			return true
+			return true, nil
 		}
 		if assign_float(val, i) {
-			return true
+			return true, nil
 		}
 	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) {
-			return true
+			return true, nil
 		}
 		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:
 		advance_token(p)
 		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
 			}
 		}
@@ -312,13 +331,18 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
 		
 	case .String:
 		advance_token(p)
-		str := unquote_string(token, p.spec, p.allocator) or_return
-		if 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:
 		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 '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 '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:
@@ -3002,18 +3002,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 	case runtime.Type_Info_Bit_Set:
 		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:
 		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) {
 	loader := _internal_loaders[which(data)]
 	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)
 }
@@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
 		return .HDR
 	case s[:4] == "\x38\x42\x50\x53":
 		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
 	case s[:4] == "\x69\x63\x6e\x73":
 		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 {
 				write_byte(w, '\\', &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
 				}
 			}

+ 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)
 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)
 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)
 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}

+ 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
 @(private="file")
 _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

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

@@ -670,20 +670,69 @@ choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
 
 
 @(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 {
-		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 {
 	return Allocator{
 		procedure = nil_allocator_proc,
-		data = nil,
+		data      = nil,
 	}
 }
 
 nil_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
 	size, alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	return nil, nil
@@ -41,7 +41,7 @@ not be allocated, and an attempt to allocate memory is an error.
 panic_allocator :: proc() -> Allocator {
 	return Allocator{
 		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)
 arena_alloc :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := arena_alloc_bytes(a, size, alignment, loc)
 	return raw_data(bytes), err
@@ -175,10 +175,10 @@ This procedure returns a slice of the newly allocated memory region.
 */
 @(require_results)
 arena_alloc_bytes :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
 	if bytes != nil {
@@ -197,10 +197,10 @@ memory region.
 */
 @(require_results)
 arena_alloc_non_zeroed :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
 	return raw_data(bytes), err
@@ -216,10 +216,10 @@ memory region.
 */
 @(require_results)
 arena_alloc_bytes_non_zeroed :: proc(
-	a: ^Arena,
+	a:    ^Arena,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 	if a.data == nil {
 		panic("Arena is not initialized", loc)
@@ -244,11 +244,11 @@ arena_free_all :: proc(a: ^Arena) {
 
 arena_allocator_proc :: proc(
 	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,
 ) -> ([]byte, Allocator_Error)  {
 	arena := cast(^Arena)allocator_data
@@ -398,10 +398,10 @@ returns a pointer to the allocated memory region.
 */
 @(require_results)
 scratch_alloc :: proc(
-	s: ^Scratch,
+	s:    ^Scratch,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_alloc_bytes(s, size, alignment, loc)
 	return raw_data(bytes), err
@@ -416,10 +416,10 @@ returns a slice of the allocated memory region.
 */
 @(require_results)
 scratch_alloc_bytes :: proc(
-	s: ^Scratch,
+	s:    ^Scratch,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
@@ -437,10 +437,10 @@ This procedure returns a pointer to the allocated memory region.
 */
 @(require_results)
 scratch_alloc_non_zeroed :: proc(
-	s: ^Scratch,
+	s:    ^Scratch,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	return raw_data(bytes), err
@@ -455,10 +455,10 @@ This procedure returns a slice of the allocated memory region.
 */
 @(require_results)
 scratch_alloc_bytes_non_zeroed :: proc(
-	s: ^Scratch,
+	s:   ^Scratch,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 		DEFAULT_BACKING_SIZE :: 4 * Megabyte
@@ -477,7 +477,7 @@ scratch_alloc_bytes_non_zeroed :: proc(
 			offset = 0
 		}
 		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.curr_offset = int(offset) + size
 		return byte_slice(rawptr(ptr), size), nil
@@ -574,12 +574,12 @@ This procedure returns the pointer to the resized memory region.
 */
 @(require_results)
 scratch_resize :: proc(
-	s: ^Scratch,
+	s:          ^Scratch,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
@@ -603,11 +603,11 @@ This procedure returns the slice of the resized memory region.
 */
 @(require_results)
 scratch_resize_bytes :: proc(
-	s: ^Scratch,
+	s:        ^Scratch,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
 	if bytes != nil && size > len(old_data) {
@@ -634,12 +634,12 @@ This procedure returns the pointer to the resized memory region.
 */
 @(require_results)
 scratch_resize_non_zeroed :: proc(
-	s: ^Scratch,
+	s:          ^Scratch,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
@@ -663,11 +663,11 @@ This procedure returns the slice of the resized memory region.
 */
 @(require_results)
 scratch_resize_bytes_non_zeroed :: proc(
-	s: ^Scratch,
+	s:        ^Scratch,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 	old_memory := raw_data(old_data)
 	old_size := len(old_data)
@@ -678,8 +678,8 @@ scratch_resize_bytes_non_zeroed :: proc(
 		}
 		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)
 	if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
 		s.curr_offset = int(old_ptr-begin)+size
@@ -695,11 +695,11 @@ scratch_resize_bytes_non_zeroed :: proc(
 }
 
 scratch_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
 	size, alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	s := (^Scratch)(allocator_data)
@@ -735,10 +735,10 @@ scratch_allocator_proc :: proc(
 Stack allocator data.
 */
 Stack :: struct {
-	data: []byte,
+	data:        []byte,
 	prev_offset: int,
 	curr_offset: int,
-	peak_used: int,
+	peak_used:   int,
 }
 
 /*
@@ -769,7 +769,7 @@ previous allocation header.
 stack_allocator :: proc(stack: ^Stack) -> Allocator {
 	return Allocator{
 		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.
 */
 stack_init :: proc(s: ^Stack, data: []byte) {
-	s.data = data
+	s.data        = data
 	s.prev_offset = 0
 	s.curr_offset = 0
-	s.peak_used = 0
+	s.peak_used   = 0
 }
 
 @(deprecated="prefer 'mem.stack_init'")
 init_stack :: proc(s: ^Stack, data: []byte) {
-	s.data = data
+	s.data        = data
 	s.prev_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)
 stack_alloc :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_alloc_bytes(s, size, alignment, loc)
 	return raw_data(bytes), err
@@ -821,10 +821,10 @@ procedure returns the slice of the allocated memory.
 */
 @(require_results)
 stack_alloc_bytes :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
@@ -842,10 +842,10 @@ zero-initialized. This procedure returns the pointer to the allocated memory.
 */
 @(require_results)
 stack_alloc_non_zeroed :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	return raw_data(bytes), err
@@ -860,10 +860,10 @@ zero-initialized. This procedure returns the slice of the allocated memory.
 */
 @(require_results)
 stack_alloc_bytes_non_zeroed :: proc(
-	s: ^Stack,
+	s:    ^Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location
+	loc       := #caller_location
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 		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.
 */
 stack_free :: proc(
-	s: ^Stack,
+	s:          ^Stack,
 	old_memory: rawptr,
 	loc := #caller_location,
 ) -> (Allocator_Error) {
@@ -953,12 +953,12 @@ This procedure returns the pointer to the resized memory region.
 */
 @(require_results)
 stack_resize :: proc(
-	s: ^Stack,
+	s:          ^Stack,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment)
 	return raw_data(bytes), err
@@ -982,11 +982,11 @@ This procedure returns the slice of the resized memory region.
 */
 @(require_results)
 stack_resize_bytes :: proc(
-	s: ^Stack,
+	s:        ^Stack,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
@@ -1017,12 +1017,12 @@ This procedure returns the pointer to the resized memory region.
 */
 @(require_results)
 stack_resize_non_zeroed :: proc(
-	s: ^Stack,
+	s:          ^Stack,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment)
 	return raw_data(bytes), err
@@ -1046,11 +1046,11 @@ This procedure returns the slice of the resized memory region.
 */
 @(require_results)
 stack_resize_bytes_non_zeroed :: proc(
-	s: ^Stack,
+	s:        ^Stack,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	old_memory := raw_data(old_data)
 	old_size := len(old_data)
@@ -1063,8 +1063,8 @@ stack_resize_bytes_non_zeroed :: proc(
 	if size == 0 {
 		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)
 	if !(start <= curr_addr && curr_addr < end) {
 		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(
 	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,
 ) -> ([]byte, Allocator_Error) {
 	s := cast(^Stack)allocator_data
@@ -1200,10 +1200,10 @@ returns a pointer to the allocated memory region.
 */
 @(require_results)
 small_stack_alloc :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_alloc_bytes(s, size, alignment, loc)
 	return raw_data(bytes), err
@@ -1218,10 +1218,10 @@ returns a slice of the allocated memory region.
 */
 @(require_results)
 small_stack_alloc_bytes :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	if bytes != nil {
@@ -1239,10 +1239,10 @@ procedure returns a pointer to the allocated memory region.
 */
 @(require_results)
 small_stack_alloc_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
 	return raw_data(bytes), err
@@ -1257,10 +1257,10 @@ procedure returns a slice of the allocated memory region.
 */
 @(require_results)
 small_stack_alloc_bytes_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:    ^Small_Stack,
 	size: int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 		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.
 */
 small_stack_free :: proc(
-	s: ^Small_Stack,
+	s:          ^Small_Stack,
 	old_memory: rawptr,
 	loc := #caller_location,
 ) -> Allocator_Error {
@@ -1341,12 +1341,12 @@ This procedure returns the pointer to the resized memory region.
 */
 @(require_results)
 small_stack_resize :: proc(
-	s: ^Small_Stack,
+	s:          ^Small_Stack,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
@@ -1370,11 +1370,11 @@ This procedure returns the slice of the resized memory region.
 */
 @(require_results)
 small_stack_resize_bytes :: proc(
-	s: ^Small_Stack,
+	s:        ^Small_Stack,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
 	if bytes != nil {
@@ -1405,12 +1405,12 @@ This procedure returns the pointer to the resized memory region.
 */
 @(require_results)
 small_stack_resize_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:          ^Small_Stack,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
 	return raw_data(bytes), err
@@ -1434,18 +1434,18 @@ This procedure returns the slice of the resized memory region.
 */
 @(require_results)
 small_stack_resize_bytes_non_zeroed :: proc(
-	s: ^Small_Stack,
+	s:        ^Small_Stack,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	alignment := DEFAULT_ALIGNMENT,
-	loc := #caller_location,
+	loc       := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	if s.data == nil {
 		panic("Small stack is not initialized", loc)
 	}
 	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)
 	if old_memory == nil {
 		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 {
 		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)
 	if !(start <= curr_addr && curr_addr < end) {
 		// 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(
-	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,
 ) -> ([]byte, Allocator_Error) {
 	s := cast(^Small_Stack)allocator_data
@@ -1514,17 +1514,17 @@ small_stack_allocator_proc :: proc(
 
 
 /* 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_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.
@@ -1540,16 +1540,16 @@ DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554
 Dynamic arena allocator data.
 */
 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,
-	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,
 	block_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.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)
 dynamic_arena_resize :: proc(
-	a: ^Dynamic_Arena,
+	a:          ^Dynamic_Arena,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	loc := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	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)
 dynamic_arena_resize_bytes :: proc(
-	a: ^Dynamic_Arena,
+	a:        ^Dynamic_Arena,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	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)
 dynamic_arena_resize_non_zeroed :: proc(
-	a: ^Dynamic_Arena,
+	a:          ^Dynamic_Arena,
 	old_memory: rawptr,
-	old_size: int,
-	size: int,
+	old_size:   int,
+	size:       int,
 	loc := #caller_location,
 ) -> (rawptr, Allocator_Error) {
 	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)
 dynamic_arena_resize_bytes_non_zeroed :: proc(
-	a: ^Dynamic_Arena,
+	a:        ^Dynamic_Arena,
 	old_data: []byte,
-	size: int,
+	size:     int,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	old_memory := raw_data(old_data)
@@ -1892,11 +1892,11 @@ dynamic_arena_resize_bytes_non_zeroed :: proc(
 
 dynamic_arena_allocator_proc :: proc(
 	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,
 ) -> ([]byte, Allocator_Error) {
 	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
 			best_block = buddy
 		}
-		if (block.size <= buddy.size) {
+		if block.size <= buddy.size {
 			block = buddy_block_next(buddy)
 			if (block < tail) {
 				// 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.
 */
 Buddy_Allocator :: struct {
-	head: ^Buddy_Block,
-	tail: ^Buddy_Block,
+	head:      ^Buddy_Block,
+	tail:      ^Buddy_Block,
 	alignment: uint,
 }
 
@@ -2234,11 +2234,11 @@ buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) {
 }
 
 buddy_allocator_proc :: proc(
-	allocator_data: rawptr,
-	mode: Allocator_Mode,
+	allocator_data:  rawptr,
+	mode:            Allocator_Mode,
 	size, alignment: int,
-	old_memory: rawptr,
-	old_size: int,
+	old_memory:      rawptr,
+	old_size:        int,
 	loc := #caller_location,
 ) -> ([]byte, Allocator_Error) {
 	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
 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 {
 	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
 
 import "core:sync"

+ 1 - 1
core/mem/tracking_allocator.odin

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

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

@@ -6,17 +6,13 @@ import "core:sys/posix"
 
 // Define non-posix needed flags:
 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 {
-	MAP_ANONYMOUS :: 0x1000
-
-	MADV_FREE     :: 6
+	MADV_FREE :: 6
 }
 
 _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)
 	if result == posix.MAP_FAILED {
 		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)
 	res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
 	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
@@ -120,6 +120,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
 	family := family_from_endpoint(interface_endpoint)
 	sock := create_socket(family, .TCP) or_return
 	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
 	// 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)
 	errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
 	if errno != nil {
-		err = cast(Dial_Error)errno
-		return
+		close(socket)
+		return {}, cast(Dial_Error)errno
 	}
 
 	return
@@ -137,6 +137,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
 	family := family_from_endpoint(interface_endpoint)
 	new_socket := create_socket(family, .TCP) or_return
 	socket = new_socket.(TCP_Socket)
+	defer if err != nil { close(socket) }
 
 	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)
 	errno = linux.connect(linux.Fd(os_sock), &addr)
 	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
 	no_delay: b32 = cast(b32) options.no_delay
@@ -166,40 +167,48 @@ _bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
 }
 
 @(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
 	assert(backlog > 0 && i32(backlog) < max(i32))
+
 	// Figure out the address family and address of the endpoint
 	ep_family := _unwrap_os_family(family_from_endpoint(endpoint))
 	ep_address := _unwrap_os_addr(endpoint)
+
 	// Create TCP socket
 	os_sock: linux.Fd
 	os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
 	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
 	// bypass the cooldown period, and allow the next run of the program to
 	// use the same address immediately.
 	//
 	// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
 	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
-	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
-	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)

+ 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)
 	res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
 	if res < 0 {
-		err = Dial_Error(win.WSAGetLastError())
-		return
+		close(socket)
+		return {}, Dial_Error(win.WSAGetLastError())
 	}
 
 	if options.no_delay {
@@ -107,6 +107,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
 	family := family_from_endpoint(interface_endpoint)
 	sock := create_socket(family, .TCP) or_return
 	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
 	// 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 {
 	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 {

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

@@ -316,7 +316,8 @@ clone_node :: proc(node: ^Node) -> ^Node {
 		case ^Struct_Type:
 			r.poly_params = auto_cast clone(r.poly_params)
 			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)
 		case ^Union_Type:
 			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:
 		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_raw_union: bool
 		is_no_copy:   bool
@@ -2645,10 +2646,21 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 				}
 				align = parse_expr(p, true)
 			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)
 				}
-				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":
 				if is_raw_union {
 					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)
 
 		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
 
 	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 {
 		end: ^ast.Expr
 		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.names = names
 	decl.type = type

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

@@ -331,7 +331,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
 		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")
 		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)
 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
+	tmp := temp_allocator_temp_begin(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)
 _read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
-	return {}, nil
+	return {}, .Unsupported
 }
 
 _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
 
+	handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
+	defer win32.CloseHandle(handle)
 
 	fi.fullpath = path
 	fi.name = basename(path)
 	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.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))
 
-
-	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)) {
 		#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
 		#assert(size_of(fi.inode) == 16)
 		runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
 	}
 
-
 	return
 }
 
@@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
 		return
 	}
 	file_info_delete(it.impl.prev_fi, file_allocator())
+	delete(it.impl.path, file_allocator())
 	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
 	case .ENOMEM:
 		return .Out_Of_Memory
+	case .ENOSYS:
+		return .Unsupported
 	}
 
 	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
 	case .ENOMEM:
 		return .Out_Of_Memory
+	case .ENOSYS:
+		return .Unsupported
 	case:
 		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:
 		return .Invalid_Offset
 
+	case win32.ERROR_BROKEN_PIPE:
+		return .Broken_Pipe
+
 	case
 		win32.ERROR_BAD_ARGUMENTS,
 		win32.ERROR_INVALID_PARAMETER,
 		win32.ERROR_NOT_ENOUGH_MEMORY,
 		win32.ERROR_NO_MORE_FILES,
 		win32.ERROR_LOCK_VIOLATION,
-		win32.ERROR_BROKEN_PIPE,
 		win32.ERROR_CALL_NOT_IMPLEMENTED,
 		win32.ERROR_INSUFFICIENT_BUFFER,
 		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)
-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
 	if truncate {
 		flags |= O_TRUNC

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

@@ -50,9 +50,9 @@ init_std_files :: proc() {
 }
 @(fini)
 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 {
 		dir, file := filepath.split(path)
-		if file != path {
+		if file != path && dir != "/" {
 			if len(dir) > 1 && dir[len(dir) - 1] == '/' {
 				dir = dir[:len(dir) - 1]
 			}

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

@@ -1,6 +1,43 @@
 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)
 pipe :: proc() -> (r, w: ^File, err: Error) {
 	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
 }
+
+@(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
 }
 
+@(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
 }
 
+@(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
 
 import "base:runtime"
+
 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
 
 /*
-	Arguments to the current process.
+Arguments to the current process.
 */
 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(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)
 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)
 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)
 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)
 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)
 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)
 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)
 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_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}
 
 /*
-	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 {
 	// 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)
 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)
 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)
 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_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) {
 	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 {
 	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)
 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)
-process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
+process_start :: proc(desc: Process_Desc) -> (Process, Error) {
 	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.
 */
@@ -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)
 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)
 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)
 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) -> ! {
 			error_byte: [1]u8 = { u8(errno) }
 			linux.write(parent_fd, error_byte[:])
-			intrinsics.trap()
+			linux.exit(126)
 		}
 
 		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))
 			errno := u8(posix.errno())
 			posix.write(parent_fd, &errno, 1)
-			runtime.trap()
+			posix.exit(126)
 		}
 
 		null := posix.open("/dev/null", {.RDWR})
@@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
 			return
 		}
 
-		process.pid = int(pid)
 		process, _ = _process_open(int(pid), {})
 		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.pid = pid
 	err = .Unsupported
 	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)
 	}
 	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
@@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) ->
 			strings.write_byte(&builder, ' ')
 		}
 		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
 			}
-			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)
 }

+ 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 {
 		mode |= 0o666
 	}
+
 	is_sym := false
 	if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
 		is_sym = false
 	} else {
 		is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
 	}
+
 	if is_sym {
 		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
 }

+ 2 - 4
core/os/os_darwin.odin

@@ -1061,7 +1061,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
 }
 
 @(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
 	if rel == "" {
 		rel = "."
@@ -1076,9 +1076,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	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 {

+ 2 - 5
core/os/os_freebsd.odin

@@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 
 @(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
 	if rel == "" {
 		rel = "."
@@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	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) {

+ 2 - 4
core/os/os_haiku.odin

@@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 
 @(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
 	if rel == "" {
 		rel = "."
@@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	defer _unix_free(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) {

+ 10 - 9
core/os/os_linux.odin

@@ -242,10 +242,13 @@ F_SETFL: int : 4 /* Set file flags */
 
 // NOTE(zangent): These are OS specific!
 // 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
 
@@ -487,7 +490,7 @@ foreign libc {
 	@(link_name="free")             _unix_free          :: proc(ptr: rawptr) ---
 	@(link_name="realloc")          _unix_realloc       :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
 
-	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> int ---
+	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> c.int ---
 	@(link_name="getenv")           _unix_getenv        :: proc(cstring) -> cstring ---
 	@(link_name="putenv")           _unix_putenv        :: proc(cstring) -> 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)
-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
 	if rel == "" {
 		rel = "."
@@ -929,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	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) {

+ 2 - 4
core/os/os_netbsd.odin

@@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
 }
 
 @(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
 	if rel == "" {
 		rel = "."
@@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	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) {

+ 2 - 4
core/os/os_openbsd.odin

@@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
 }
 
 @(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
 	if rel == "" {
 		rel = "."
@@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
 	}
 	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) {

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

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

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

@@ -1,14 +1,10 @@
 #+build linux, darwin, freebsd, openbsd, netbsd
 package filepath
 
-when ODIN_OS == .Darwin {
-	foreign import libc "system:System.framework"
-} else {
-	foreign import libc "system:c"
-}
-
 import "base:runtime"
+
 import "core:strings"
+import "core:sys/posix"
 
 SEPARATOR :: '/'
 SEPARATOR_STRING :: `/`
@@ -28,11 +24,11 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
 		rel = "."
 	}
 	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 {
-		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)
 	return path_str, true
@@ -48,26 +44,3 @@ join :: proc(elems: []string, allocator := context.allocator) -> (joined: string
 	}
 	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_Bit_Set                :: runtime.Type_Info_Bit_Set
 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_Soa_Pointer            :: runtime.Type_Info_Soa_Pointer
 Type_Info_Bit_Field              :: runtime.Type_Info_Bit_Field
@@ -67,8 +65,6 @@ Type_Kind :: enum {
 	Map,
 	Bit_Set,
 	Simd_Vector,
-	Relative_Pointer,
-	Relative_Multi_Pointer,
 	Matrix,
 	Soa_Pointer,
 	Bit_Field,
@@ -80,35 +76,33 @@ type_kind :: proc(T: typeid) -> Type_Kind {
 	ti := type_info_of(T)
 	if ti != nil {
 		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
 }
 
+/*
+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
 }
 
-@(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)
 relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
 	_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: valid = false
 		}
-
-	case Type_Info_Relative_Pointer:
-		valid = true
-		value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
 	}
 
 	return
@@ -1656,8 +1652,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
 		Type_Info_Bit_Set,
 		Type_Info_Enum,
 		Type_Info_Simd_Vector,
-		Type_Info_Relative_Pointer,
-		Type_Info_Relative_Multi_Pointer,
 		Type_Info_Soa_Pointer,
 		Type_Info_Matrix:
 		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:
 		y := b.variant.(Type_Info_Simd_Vector) or_return
 		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:
 		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)
 	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)
@@ -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_byte(w, ']',                &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:
 		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")
 _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")
 _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")
 _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")
 _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
 }
 
-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
 	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 {
 		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
 		for bucket_index in 0..<map_cap {
 			if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
-				key   := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
-				value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
+				key   := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
+				value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
 				entries[entry_index].hash  = hash
 				entries[entry_index].key   = (^K)(key)^
 				entries[entry_index].value = (^V)(value)^

+ 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)]
 }
 
+/*
+	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.
 
@@ -96,9 +107,37 @@ contains :: proc(array: $T/[]$E, value: E) -> bool where intrinsics.type_is_comp
 	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)
 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 {
 		if x == key {
 			return i, true
@@ -107,8 +146,18 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
 	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)
-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 {
 		if f(x) {
 			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
 	uniquely determined position; the second and third are not
 	found; the fourth could match any position in `[1, 4]`.
+	*/
 
-	```
 	index: int
 	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)
 	assert(index >= 1 && index <= 4 && found == true)
-	```
-
-	For slices of more complex types see: binary_search_by
 */
 @(require_results)
 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)
@@ -359,6 +471,12 @@ is_empty :: proc(a: $T/[]$E) -> bool {
 	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)

+ 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
 		}
 		f := f64(mantissa)
+		f_abs := f
 		if neg {
 			f = -f
 		}
@@ -1132,7 +1133,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
 				f *= pow10[exp-22]
 				exp = 22
 			}
-			if f > 1e15 || f < 1e-15 {
+			if f_abs > 1e15 || f_abs < 1e-15 {
 				break trunc_block
 			}
 			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
 	}
 
-	if rune_length <= 1 {
+	if rune_length < 1 {
 		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)
 }
 
+@(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")
 Application_windows :: proc "c" (self: ^Application) -> ^Array {
 	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"
 
 import "core:c"
+import "base:intrinsics"
 
 // NOTE(tetra): Unclear whether these should be aligned 16 or not.
 // However all other sync primitives are aligned for robustness.
 // I cannot currently align these though.
 // 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
 
 semaphore_t :: distinct u64
 
-kern_return_t  :: distinct u64
+kern_return_t :: distinct c.int
 thread_act_t   :: distinct u64
 thread_state_t :: distinct ^u32
 thread_list_t  :: [^]thread_act_t
@@ -37,11 +38,6 @@ MACH_MSGH_BITS_COMPLEX :: 0x80000000
 MACH_PORT_RIGHT_SEND    :: 0
 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_COPY         :: 1
 VM_INHERIT_NONE         :: 2
@@ -58,6 +54,27 @@ ARM_THREAD_STATE64 :: 6
 mach_msg_option_t :: distinct i32
 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 {
 	name: mach_port_t,
 	_: 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_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_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_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 {

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

@@ -28,6 +28,23 @@ CPU_Feature :: enum u64 {
 	ssse3,     // Supplemental streaming SIMD extension 3
 	sse41,     // Streaming SIMD extension 4 and 4.1
 	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]
@@ -82,9 +99,11 @@ init_cpu_features :: proc "c" () {
 	//
 	// See: crbug.com/375968
 	os_supports_avx := false
+	os_supports_avx512 := false
 	if .os_xsave in set && is_set(26, ecx1) {
 		eax, _ := xgetbv(0)
 		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 {
 		try_set(&set, .avx, 28, ecx1)
@@ -94,11 +113,37 @@ init_cpu_features :: proc "c" () {
 		return
 	}
 
-	_, ebx7, _, _ := cpuid(7, 0)
+	_, ebx7, ecx7, edx7 := cpuid(7, 0)
 	try_set(&set, .bmi1, 3, ebx7)
 	if os_supports_avx {
 		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, .erms,    9, 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}}},
 	"21G920"     = {{21, 6, 0}, "macOS", {"Monterey",       {12, 7, 1}}},
 	"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 
 	"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}}},
 	"22G313"     = {{22, 6, 0}, "macOS", {"Ventura",        {13, 6, 1}}},
 	"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 
 	"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}}},
 	"23G93"      = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 6, 1}}},
 	"23H124"     = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 7, 0}}},
+	"23H222"     = {{23, 6, 0}, "macOS", {"Sonoma",         {14, 7, 1}}},
 
 	// MacOS Sequoia
 	"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)

+ 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
 	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
@@ -542,6 +519,36 @@ Fd_Poll_Events_Bits :: enum {
 	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
 */

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

@@ -135,6 +135,31 @@ STATX_BASIC_STATS :: Statx_Mask {
 	.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
 */

+ 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): 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
 

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

@@ -30,6 +30,11 @@ Id :: distinct uint
 */
 Fd  :: distinct i32
 
+/*
+	Represents a watch descriptor.
+*/
+Wd  :: distinct i32
+
 /*
 	Type for PID file descriptors.
 */
@@ -343,6 +348,18 @@ Poll_Fd :: struct {
 	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.
 */
@@ -1136,6 +1153,12 @@ when ODIN_ARCH == .arm32 {
 		eflags:           uint,
 		rsp:              uint,
 		ss:               uint,
+		fs_base:          uint,
+		gs_base:          uint,
+		ds:               uint,
+		es:               uint,
+		fs:               uint,
+		gs:               uint,
 	}
 	// All floating point state
 	_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
 
 import "core:c"

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

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 
 import "core:c"
@@ -29,12 +30,12 @@ foreign lib {
 			panic(string(posix.strerror(posix.errno())))
 		}
 		defer posix.free(list)
-	
+
 		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 ]]
 	*/
@@ -53,15 +54,6 @@ foreign lib {
 	*/
 	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
 	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).
 
 	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 ]]
@@ -160,11 +152,36 @@ when ODIN_OS == .NetBSD {
 	@(private) LSCANDIR   :: "__scandir30"
 	@(private) LOPENDIR   :: "__opendir30"
 	@(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 {
 	@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
 	@(private) LSCANDIR   :: "scandir"   + INODE_SUFFIX
 	@(private) LOPENDIR   :: "opendir"   + 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 {
@@ -193,13 +210,21 @@ when ODIN_OS == .Darwin {
 } else when ODIN_OS == .NetBSD {
 
 	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
 
 import "core:c"
@@ -111,7 +112,14 @@ when ODIN_OS == .Darwin {
 
 	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
 
 import "core:c"
@@ -141,7 +142,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
@@ -151,7 +152,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
@@ -220,7 +221,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
@@ -230,7 +231,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
@@ -301,7 +302,7 @@ when ODIN_OS == .Darwin {
 	EMLINK          :: 31
 	EPIPE           :: 32
 	EAGAIN          :: 35
-	EWOULDBLOCK     :: 35
+	EWOULDBLOCK     :: EAGAIN
 	EINPROGRESS     :: 36
 	EALREADY        :: 37
 	ENOTSOCK        :: 38
@@ -311,7 +312,7 @@ when ODIN_OS == .Darwin {
 	ENOPROTOOPT     :: 42
 	EPROTONOSUPPORT :: 43
 	ENOTSUP         :: 45
-	EOPNOTSUPP      :: 45
+	EOPNOTSUPP      :: ENOTSUP
 	EAFNOSUPPORT    :: 47
 	EADDRINUSE      :: 48
 	EADDRNOTAVAIL   :: 49
@@ -367,7 +368,173 @@ when ODIN_OS == .Darwin {
 		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
 
 import "core:c"
@@ -92,9 +93,6 @@ Lock_Type :: enum c.short {
 	WRLCK = F_WRLCK,
 }
 
-// Assertions made to unify this bit set.
-#assert(O_RDONLY == 0)
-
 O_Flag_Bits :: enum c.int {
 	// Sets FD_CLOEXEC on the file descriptor.
 	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.
 	NOCTTY    = log2(O_NOCTTY),
 	// 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.
 	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.
 	// TTY_INIT = O_TTY_INIT,
 
@@ -123,7 +121,8 @@ O_Flag_Bits :: enum c.int {
 	NONBLOCK  = log2(O_NONBLOCK),
 	// Write I/O shall complete as defined by synchronized I/O file integrity completion.
 	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.
 	// RSYNC = O_RSYNC,
 
@@ -135,11 +134,10 @@ O_Flag_Bits :: enum c.int {
 	WRONLY    = log2(O_WRONLY),
 	// Reading only.
 	// RDONLY = 0, // Default
-
 }
+
 O_Flags :: bit_set[O_Flag_Bits; c.int]
 
-// A mask of all the access mode bits.
 O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY }
 
 AT_Flag_Bits :: enum c.int {
@@ -152,8 +150,8 @@ AT_Flags :: bit_set[AT_Flag_Bits; c.int]
 
 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_CLOEXEC :: 67
@@ -178,7 +176,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x00100000
 	O_EXCL      :: 0x00000800
 	O_NOCTTY    :: 0x00020000
-	O_NOFOLOW   :: 0x00000100
+	O_NOFOLLOW  :: 0x00000100
 	O_TRUNC     :: 0x00000400
 
 	_O_TTY_INIT :: 0
@@ -189,16 +187,16 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x00000004
 	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_Flags{ .EXEC, .DIRECTORY }
+	O_SEARCH  :: O_Flags{.EXEC, .DIRECTORY}
 
 	AT_FDCWD: FD: -2
 
@@ -217,8 +215,8 @@ when ODIN_OS == .Darwin {
 
 } 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_CLOEXEC :: 17
@@ -243,7 +241,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x00020000
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 
 	_O_TTY_INIT :: 0x00080000
@@ -256,10 +254,10 @@ when ODIN_OS == .Darwin {
 	_O_RSYNC   :: 0
 	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_Flags{ .EXEC }
@@ -282,8 +280,8 @@ when ODIN_OS == .Darwin {
 
 } 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_CLOEXEC :: 12
@@ -308,7 +306,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x0020000
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 
 	_O_TTY_INIT :: 0
@@ -319,14 +317,14 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x0004
 	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  :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
@@ -347,8 +345,8 @@ when ODIN_OS == .Darwin {
 	}
 } 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_CLOEXEC :: 10
@@ -373,7 +371,7 @@ when ODIN_OS == .Darwin {
 	O_DIRECTORY :: 0x20000
 	O_EXCL      :: 0x0800
 	O_NOCTTY    :: 0x8000
-	O_NOFOLOW   :: 0x0100
+	O_NOFOLLOW  :: 0x0100
 	O_TRUNC     :: 0x0400
 
 	_O_TTY_INIT :: 0
@@ -384,13 +382,13 @@ when ODIN_OS == .Darwin {
 	O_NONBLOCK :: 0x0004
 	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  :: 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 */
 	}
 
-} 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
 
 import "core:c"
@@ -53,6 +54,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	FNM_PERIOD   :: 0x04
 	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
 
 import "core:c"
@@ -112,7 +113,7 @@ when ODIN_OS == .Darwin {
 
 	glob_t :: struct {
 		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_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		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 {
 		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_flags:  Glob_Flags,                    /* copy of flags parameter to glob */
 		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_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
 
 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 ---
 }
 
-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
 
@@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		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
 
 import "core:c"

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

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -238,7 +239,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 	ABDAY_4 :: 16
 	ABDAY_5 :: 17
 	ABDAY_6 :: 18
-	ABDAY_7 :: 19
+	ABDAY_7 :: 19	
 
 	MON_1  :: 20
 	MON_2  :: 21
@@ -278,7 +279,91 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
 	YESEXPR :: 47
 	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 {
 	#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
 
 when ODIN_OS == .Darwin {
@@ -56,6 +57,7 @@ foreign lib {
 
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
 	*/
+	@(link_name=LBASENAME)
 	basename :: proc(path: cstring) -> cstring ---
 
 	/*
@@ -72,3 +74,9 @@ foreign lib {
 	*/
 	dirname :: proc(path: cstring) -> cstring ---
 }
+
+when ODIN_OS == .Linux {
+	@(private) LBASENAME :: "__xpg_basename"
+} else {
+	@(private) LBASENAME :: "basename"
+}

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません