Browse Source

Merge branch 'master' into json-add-int-key-map-support

VladPavliuk 1 year ago
parent
commit
3f8712edb0

+ 4 - 4
base/runtime/core_builtin.odin

@@ -484,7 +484,7 @@ non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc :
 	return _append_elem(array, arg, false, loc=loc)
 }
 
-_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
+_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: []E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
 	if array == nil {
 		return 0, nil
 	}
@@ -525,12 +525,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
 
 @builtin
 append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
-	return _append_elems(array, true, loc, ..args)
+	return _append_elems(array, true, loc, args)
 }
 
 @builtin
 non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
-	return _append_elems(array, false, loc, ..args)
+	return _append_elems(array, false, loc, args)
 }
 
 // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
@@ -679,7 +679,7 @@ assign_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
 
 
 @builtin
-assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
+assign_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
 	new_size := index + len(args)
 	if len(args) == 0 {
 		ok = true

+ 1 - 1
base/runtime/core_builtin_soa.odin

@@ -352,7 +352,7 @@ non_zero_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, #no_broadcast args
 }
 
 
-_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
+_append_soa_elems :: proc(array: ^$T/#soa[dynamic]$E, zero_memory: bool, #no_broadcast args: []E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
 	if array == nil {
 		return
 	}

+ 2 - 2
core/container/queue/queue.odin

@@ -95,11 +95,11 @@ front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
 }
 
 back :: proc(q: ^$Q/Queue($T)) -> T {
-	idx := (q.offset+uint(q.len))%builtin.len(q.data)
+	idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
 	return q.data[idx]
 }
 back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
-	idx := (q.offset+uint(q.len))%builtin.len(q.data)
+	idx := (q.offset+uint(q.len - 1))%builtin.len(q.data)
 	return &q.data[idx]
 }
 

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

@@ -121,7 +121,7 @@ load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options :
 	data := os.read_entire_file(path, allocator) or_return
 	defer delete(data, allocator)
 	m, err = load_map_from_string(string(data), allocator, options)
-	ok = err != nil
+	ok = err == nil
 	defer if !ok {
 		delete_map(m)
 	}

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

@@ -384,6 +384,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
 				omitempty := false
 
 				json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
+
+				if json_name == "-" {
+					continue
+				}
+
 				for flag in strings.split_iterator(&extra, ",") {
 					switch flag {
 					case "omitempty":

+ 3 - 0
core/odin/ast/ast.odin

@@ -599,6 +599,7 @@ Field_Flag :: enum {
 	Subtype,
 	By_Ptr,
 	No_Broadcast,
+	No_Capture,
 
 	Results,
 	Tags,
@@ -619,6 +620,7 @@ field_flag_strings := [Field_Flag]string{
 	.Subtype            = "#subtype",
 	.By_Ptr             = "#by_ptr",
 	.No_Broadcast       = "#no_broadcast",
+	.No_Capture         = "#no_capture",
 
 	.Results            = "results",
 	.Tags               = "field tag",
@@ -634,6 +636,7 @@ field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
 	{"subtype",      .Subtype},
 	{"by_ptr",       .By_Ptr},
 	{"no_broadcast", .No_Broadcast},
+	{"no_capture",   .No_Capture},
 }
 
 

+ 1 - 1
core/os/file_windows.odin

@@ -125,7 +125,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
 		src := buf8[:buf8_len]
 
 		ctrl_z := false
-		for i := 0; i < len(src) && n+i < len(b); i += 1 {
+		for i := 0; i < len(src) && n < len(b); i += 1 {
 			x := src[i]
 			if x == 0x1a { // ctrl-z
 				ctrl_z = true

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

@@ -22,6 +22,7 @@ General_Error :: enum u32 {
 	Invalid_File,
 	Invalid_Dir,
 	Invalid_Path,
+	Invalid_Callback,
 
 	Pattern_Has_Separator,
 
@@ -64,6 +65,7 @@ error_string :: proc(ferr: Error) -> string {
 		case .Invalid_File:      return "invalid file"
 		case .Invalid_Dir:       return "invalid directory"
 		case .Invalid_Path:      return "invalid path"
+		case .Invalid_Callback:  return "invalid callback"
 		case .Unsupported:       return "unsupported"
 		case .Pattern_Has_Separator: return "pattern has separator"
 		}

+ 2 - 2
core/os/os2/file.odin

@@ -5,9 +5,9 @@ import "core:time"
 import "base:runtime"
 
 File :: struct {
-	impl:   _File,
+	impl:   rawptr,
 	stream: io.Stream,
-	user_fstat: Fstat_Callback,
+	fstat:  Fstat_Callback,
 }
 
 File_Mode :: distinct u32

+ 59 - 55
core/os/os2/file_linux.odin

@@ -6,14 +6,15 @@ import "core:time"
 import "base:runtime"
 import "core:sys/linux"
 
-_File :: struct {
+File_Impl :: struct {
+	file: File,
 	name: string,
 	fd: linux.Fd,
 	allocator: runtime.Allocator,
 }
 
-_stdin : File = {
-	impl = {
+_stdin := File{
+	impl = &File_Impl{
 		name = "/proc/self/fd/0",
 		fd = 0,
 		allocator = _file_allocator(),
@@ -21,9 +22,10 @@ _stdin : File = {
 	stream = {
 		procedure = _file_stream_proc,
 	},
+	fstat = _fstat,
 }
-_stdout : File = {
-	impl = {
+_stdout := File{
+	impl = &File_Impl{
 		name = "/proc/self/fd/1",
 		fd = 1,
 		allocator = _file_allocator(),
@@ -31,9 +33,10 @@ _stdout : File = {
 	stream = {
 		procedure = _file_stream_proc,
 	},
+	fstat = _fstat,
 }
-_stderr : File = {
-	impl = {
+_stderr := File{
+	impl = &File_Impl{
 		name = "/proc/self/fd/2",
 		fd = 2,
 		allocator = _file_allocator(),
@@ -41,6 +44,7 @@ _stderr : File = {
 	stream = {
 		procedure = _file_stream_proc,
 	},
+	fstat = _fstat,
 }
 
 @init
@@ -89,40 +93,35 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, er
 }
 
 _new_file :: proc(fd: uintptr, _: string = "") -> ^File {
-	file := new(File, file_allocator())
-	_construct_file(file, fd, "")
-	return file
-}
-
-_construct_file :: proc(file: ^File, fd: uintptr, _: string = "") {
-	file^ = {
-		impl = {
-			fd = linux.Fd(fd),
-			allocator = file_allocator(),
-			name = _get_full_path(file.impl.fd, file.impl.allocator),
-		},
-		stream = {
-			data = file,
-			procedure = _file_stream_proc,
-		},
+	impl := new(File_Impl, file_allocator())
+	impl.file.impl = impl
+	impl.fd = linux.Fd(fd)
+	impl.allocator = file_allocator()
+	impl.name = _get_full_path(impl.fd, impl.allocator)
+	impl.file.stream = {
+		data = impl,
+		procedure = _file_stream_proc,
 	}
+	impl.file.fstat = _fstat
+	return &impl.file
 }
 
-_destroy :: proc(f: ^File) -> Error {
+_destroy :: proc(f: ^File_Impl) -> Error {
 	if f == nil {
 		return nil
 	}
-	delete(f.impl.name, f.impl.allocator)
-	free(f, f.impl.allocator)
+	a := f.allocator
+	delete(f.name, a)
+	free(f, a)
 	return nil
 }
 
 
-_close :: proc(f: ^File) -> Error {
-	if f == nil {
+_close :: proc(f: ^File_Impl) -> Error {
+	if f == nil{
 		return nil
 	}
-	errno := linux.close(f.impl.fd)
+	errno := linux.close(f.fd)
 	if errno == .EBADF { // avoid possible double free
 		return _get_platform_error(errno)
 	}
@@ -131,41 +130,41 @@ _close :: proc(f: ^File) -> Error {
 }
 
 _fd :: proc(f: ^File) -> uintptr {
-	if f == nil {
+	if f == nil || f.impl == nil {
 		return ~uintptr(0)
 	}
-	return uintptr(f.impl.fd)
+	impl := (^File_Impl)(f.impl)
+	return uintptr(impl.fd)
 }
 
 _name :: proc(f: ^File) -> string {
-	return f.impl.name if f != nil else ""
+	return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
 }
 
-_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
-	n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
+_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
+	n, errno := linux.lseek(f.fd, offset, linux.Seek_Whence(whence))
 	if errno != .NONE {
 		return -1, _get_platform_error(errno)
 	}
 	return n, nil
 }
 
-_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
+_read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
 	if len(p) == 0 {
 		return 0, nil
 	}
-	n, errno := linux.read(f.impl.fd, p[:])
+	n, errno := linux.read(f.fd, p[:])
 	if errno != .NONE {
 		return -1, _get_platform_error(errno)
 	}
 	return i64(n), n == 0 ? io.Error.EOF : nil
 }
 
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
+_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
 	if offset < 0 {
 		return 0, .Invalid_Offset
 	}
-
-	n, errno := linux.pread(f.impl.fd, p[:], offset)
+	n, errno := linux.pread(f.fd, p[:], offset)
 	if errno != .NONE {
 		return -1, _get_platform_error(errno)
 	}
@@ -175,32 +174,31 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
 	return i64(n), nil
 }
 
-_write :: proc(f: ^File, p: []byte) -> (i64, Error) {
+_write :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
 	if len(p) == 0 {
 		return 0, nil
 	}
-	n, errno := linux.write(f.impl.fd, p[:])
+	n, errno := linux.write(f.fd, p[:])
 	if errno != .NONE {
 		return -1, _get_platform_error(errno)
 	}
 	return i64(n), nil
 }
 
-_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (i64, Error) {
+_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (i64, Error) {
 	if offset < 0 {
 		return 0, .Invalid_Offset
 	}
-
-	n, errno := linux.pwrite(f.impl.fd, p[:], offset)
+	n, errno := linux.pwrite(f.fd, p[:], offset)
 	if errno != .NONE {
 		return -1, _get_platform_error(errno)
 	}
 	return i64(n), nil
 }
 
-_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
+_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
 	s: linux.Stat = ---
-	errno := linux.fstat(f.impl.fd, &s)
+	errno := linux.fstat(f.fd, &s)
 	if errno != .NONE {
 		return -1, _get_platform_error(errno)
 	}
@@ -208,15 +206,17 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
 }
 
 _sync :: proc(f: ^File) -> Error {
-	return _get_platform_error(linux.fsync(f.impl.fd))
+	impl := (^File_Impl)(f.impl)
+	return _get_platform_error(linux.fsync(impl.fd))
 }
 
-_flush :: proc(f: ^File) -> Error {
-	return _get_platform_error(linux.fsync(f.impl.fd))
+_flush :: proc(f: ^File_Impl) -> Error {
+	return _get_platform_error(linux.fsync(f.fd))
 }
 
 _truncate :: proc(f: ^File, size: i64) -> Error {
-	return _get_platform_error(linux.ftruncate(f.impl.fd, size))
+	impl := (^File_Impl)(f.impl)
+	return _get_platform_error(linux.ftruncate(impl.fd, size))
 }
 
 _remove :: proc(name: string) -> Error {
@@ -292,7 +292,8 @@ _chdir :: proc(name: string) -> Error {
 }
 
 _fchdir :: proc(f: ^File) -> Error {
-	return _get_platform_error(linux.fchdir(f.impl.fd))
+	impl := (^File_Impl)(f.impl)
+	return _get_platform_error(linux.fchdir(impl.fd))
 }
 
 _chmod :: proc(name: string, mode: File_Mode) -> Error {
@@ -302,7 +303,8 @@ _chmod :: proc(name: string, mode: File_Mode) -> Error {
 }
 
 _fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
-	return _get_platform_error(linux.fchmod(f.impl.fd, transmute(linux.Mode)(u32(mode))))
+	impl := (^File_Impl)(f.impl)
+	return _get_platform_error(linux.fchmod(impl.fd, transmute(linux.Mode)(u32(mode))))
 }
 
 // NOTE: will throw error without super user priviledges
@@ -321,7 +323,8 @@ _lchown :: proc(name: string, uid, gid: int) -> Error {
 
 // NOTE: will throw error without super user priviledges
 _fchown :: proc(f: ^File, uid, gid: int) -> Error {
-	return _get_platform_error(linux.fchown(f.impl.fd, linux.Uid(uid), linux.Gid(gid)))
+	impl := (^File_Impl)(f.impl)
+	return _get_platform_error(linux.fchown(impl.fd, linux.Uid(uid), linux.Gid(gid)))
 }
 
 _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
@@ -351,7 +354,8 @@ _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
 			uint(mtime._nsec) % uint(time.Second),
 		},
 	}
-	return _get_platform_error(linux.utimensat(f.impl.fd, nil, &times[0], nil))
+	impl := (^File_Impl)(f.impl)
+	return _get_platform_error(linux.utimensat(impl.fd, nil, &times[0], nil))
 }
 
 _exists :: proc(name: string) -> bool {
@@ -443,7 +447,7 @@ _read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Alloc
 
 @(private="package")
 _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
-	f := (^File)(stream_data)
+	f := (^File_Impl)(stream_data)
 	ferr: Error
 	switch mode {
 	case .Read:

+ 79 - 69
core/os/os2/file_windows.odin

@@ -17,17 +17,19 @@ _ERROR_BAD_NETPATH :: 53
 MAX_RW :: 1<<30
 
 
-_File_Kind :: enum u8 {
+File_Impl_Kind :: enum u8 {
 	File,
 	Console,
 	Pipe,
 }
 
-_File :: struct {
+File_Impl :: struct {
+	file: File,
+
 	fd:   rawptr,
 	name: string,
 	wname: win32.wstring,
-	kind: _File_Kind,
+	kind: File_Impl_Kind,
 
 	allocator: runtime.Allocator,
 
@@ -75,11 +77,9 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
 		access |= win32.FILE_APPEND_DATA
 	}
 	share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
-	sa: ^win32.SECURITY_ATTRIBUTES
-	if .Close_On_Exec not_in flags {
-		sa = &win32.SECURITY_ATTRIBUTES{}
-		sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
-		sa.bInheritHandle = true
+	sa := win32.SECURITY_ATTRIBUTES {
+		nLength = size_of(win32.SECURITY_ATTRIBUTES),
+		bInheritHandle = .Close_On_Exec not_in flags,
 	}
 
 	create_mode: u32 = win32.OPEN_EXISTING
@@ -101,7 +101,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
 			// NOTE(bill): Open has just asked to create a file in read-only mode.
 			// If the file already exists, to make it akin to a *nix open call,
 			// the call preserves the existing permissions.
-			h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
+			h := win32.CreateFileW(path, access, share_mode, &sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
 			if h == win32.INVALID_HANDLE {
 				switch e := win32.GetLastError(); e {
 				case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
@@ -114,7 +114,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
 			}
 		}
 	}
-	h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil)
+	h := win32.CreateFileW(path, access, share_mode, &sa, create_mode, attrs, nil)
 	if h == win32.INVALID_HANDLE {
 		return 0, _get_platform_error()
 	}
@@ -124,7 +124,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
 
 _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
 	flags := flags if flags != nil else {.Read}
-	handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return
+	handle := _open_internal(name, flags, perm) or_return
 	return _new_file(handle, name), nil
 }
 
@@ -132,75 +132,81 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
 	if handle == INVALID_HANDLE {
 		return nil
 	}
-	f := new(File, file_allocator())
+	impl := new(File_Impl, file_allocator())
+	impl.file.impl = impl
 
-	f.impl.allocator = file_allocator()
-	f.impl.fd = rawptr(handle)
-	f.impl.name, _ = clone_string(name, f.impl.allocator)
-	f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
+	impl.allocator = file_allocator()
+	impl.fd = rawptr(handle)
+	impl.name, _ = clone_string(name, impl.allocator)
+	impl.wname = win32.utf8_to_wstring(name, impl.allocator)
 
-	handle := _handle(f)
-	kind := _File_Kind.File
+	handle := _handle(&impl.file)
+	kind := File_Impl_Kind.File
 	if m: u32; win32.GetConsoleMode(handle, &m) {
 		kind = .Console
 	}
 	if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
 		kind = .Pipe
 	}
-	f.impl.kind = kind
+	impl.kind = kind
 
-	f.stream = {
-		data = f,
+	impl.file.stream = {
+		data = impl,
 		procedure = _file_stream_proc,
 	}
+	impl.file.fstat = _fstat
 
-	return f
+	return &impl.file
 }
 
 _fd :: proc(f: ^File) -> uintptr {
-	if f == nil {
+	if f == nil || f.impl == nil {
 		return INVALID_HANDLE
 	}
-	return uintptr(f.impl.fd)
+	return uintptr((^File_Impl)(f.impl).fd)
 }
 
-_destroy :: proc(f: ^File) -> Error {
+_destroy :: proc(f: ^File_Impl) -> Error {
 	if f == nil {
 		return nil
 	}
 
-	a := f.impl.allocator
-	free(f.impl.wname, a)
-	delete(f.impl.name, a)
-	free(f, a)
+	a := f.allocator
+	err0 := free(f.wname, a)
+	err1 := delete(f.name, a)
+	err2 := free(f, a)
+	err0 or_return
+	err1 or_return
+	err2 or_return
 	return nil
 }
 
 
-_close :: proc(f: ^File) -> Error {
-	if f == nil {
+_close :: proc(f: ^File_Impl) -> Error {
+	if f == nil  {
 		return nil
 	}
-	if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) {
+	if !win32.CloseHandle(win32.HANDLE(f.fd)) {
 		return .Closed
 	}
 	return _destroy(f)
 }
 
 _name :: proc(f: ^File) -> string {
-	return f.impl.name if f != nil else ""
+	return (^File_Impl)(f.impl).name if f != nil && f.impl != nil else ""
 }
 
-_seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
-	handle := _handle(f)
+_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
+	handle := _handle(&f.file)
 	if handle == win32.INVALID_HANDLE {
 		return 0, .Invalid_File
 	}
-	if f.impl.kind == .Pipe {
+
+	if f.kind == .Pipe {
 		return 0, .Invalid_File
 	}
 
-	sync.guard(&f.impl.rw_mutex)
+	sync.guard(&f.rw_mutex)
 
 	w: u32
 	switch whence {
@@ -218,7 +224,7 @@ _seek :: proc(f: ^File, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Er
 	return i64(hi)<<32 + i64(dw_ptr), nil
 }
 
-_read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
+_read :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
 	read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
 		if len(b) == 0 {
 			return 0, nil
@@ -269,18 +275,18 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
 		return
 	}
 
-	handle := _handle(f)
+	handle := _handle(&f.file)
 
 	single_read_length: win32.DWORD
 	total_read: int
 	length := len(p)
 
-	sync.shared_guard(&f.impl.rw_mutex) // multiple readers
+	sync.shared_guard(&f.rw_mutex) // multiple readers
 
-	if sync.guard(&f.impl.p_mutex) {
+	if sync.guard(&f.p_mutex) {
 		to_read := min(win32.DWORD(length), MAX_RW)
 		ok: win32.BOOL
-		if f.impl.kind == .Console {
+		if f.kind == .Console {
 			n, cerr := read_console(handle, p[total_read:][:to_read])
 			total_read += n
 			if cerr != nil {
@@ -300,15 +306,15 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
 	return i64(total_read), err
 }
 
-_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
-	pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) {
+_read_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) {
+	pread :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
 		buf := data
 		if len(buf) > MAX_RW {
 			buf = buf[:MAX_RW]
 
 		}
-		curr_offset := seek(f, offset, .Current) or_return
-		defer seek(f, curr_offset, .Start)
+		curr_offset := _seek(f, offset, .Current) or_return
+		defer _seek(f, curr_offset, .Start)
 
 		o := win32.OVERLAPPED{
 			OffsetHigh = u32(offset>>32),
@@ -317,7 +323,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 
 		// TODO(bill): Determine the correct behaviour for consoles
 
-		h := _handle(f)
+		h := _handle(&f.file)
 		done: win32.DWORD
 		if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
 			err = _get_platform_error()
@@ -327,7 +333,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 		return
 	}
 
-	sync.guard(&f.impl.p_mutex)
+	sync.guard(&f.p_mutex)
 
 	p, offset := p, offset
 	for len(p) > 0 {
@@ -339,7 +345,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 	return
 }
 
-_write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
+_write :: proc(f: ^File_Impl, p: []byte) -> (n: i64, err: Error) {
 	if len(p) == 0 {
 		return
 	}
@@ -348,9 +354,9 @@ _write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
 	total_write: i64
 	length := i64(len(p))
 
-	handle := _handle(f)
+	handle := _handle(&f.file)
 
-	sync.guard(&f.impl.rw_mutex)
+	sync.guard(&f.rw_mutex)
 	for total_write < length {
 		remaining := length - total_write
 		to_write := win32.DWORD(min(i32(remaining), MAX_RW))
@@ -366,22 +372,22 @@ _write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
 	return i64(total_write), nil
 }
 
-_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
-	pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: i64, err: Error) {
+_write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (n: i64, err: Error) {
+	pwrite :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
 		buf := data
 		if len(buf) > MAX_RW {
 			buf = buf[:MAX_RW]
 
 		}
-		curr_offset := seek(f, offset, .Current) or_return
-		defer seek(f, curr_offset, .Start)
+		curr_offset := _seek(f, offset, .Current) or_return
+		defer _seek(f, curr_offset, .Start)
 
 		o := win32.OVERLAPPED{
 			OffsetHigh = u32(offset>>32),
 			Offset = u32(offset),
 		}
 
-		h := _handle(f)
+		h := _handle(&f.file)
 		done: win32.DWORD
 		if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
 			err = _get_platform_error()
@@ -391,7 +397,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 		return
 	}
 
-	sync.guard(&f.impl.p_mutex)
+	sync.guard(&f.p_mutex)
 	p, offset := p, offset
 	for len(p) > 0 {
 		m := pwrite(f, p, offset) or_return
@@ -402,12 +408,12 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
 	return
 }
 
-_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
+_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
 	length: win32.LARGE_INTEGER
-	if f.impl.kind == .Pipe {
+	if f.kind == .Pipe {
 		return 0, .No_Size
 	}
-	handle := _handle(f)
+	handle := _handle(&f.file)
 	if !win32.GetFileSizeEx(handle, &length) {
 		err = _get_platform_error()
 	}
@@ -417,11 +423,14 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
 
 
 _sync :: proc(f: ^File) -> Error {
-	return _flush(f)
+	if f != nil && f.impl != nil {
+		return _flush((^File_Impl)(f.impl))
+	}
+	return nil
 }
 
-_flush :: proc(f: ^File) -> Error {
-	handle := _handle(f)
+_flush :: proc(f: ^File_Impl) -> Error {
+	handle := _handle(&f.file)
 	if !win32.FlushFileBuffers(handle) {
 		return _get_platform_error()
 	}
@@ -429,7 +438,7 @@ _flush :: proc(f: ^File) -> Error {
 }
 
 _truncate :: proc(f: ^File, size: i64) -> Error {
-	if f == nil {
+	if f == nil || f.impl == nil {
 		return nil
 	}
 	curr_off := seek(f, 0, .Current) or_return
@@ -616,17 +625,18 @@ _read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, er
 
 
 _fchdir :: proc(f: ^File) -> Error {
-	if f == nil {
+	if f == nil || f.impl == nil {
 		return nil
 	}
-	if !win32.SetCurrentDirectoryW(f.impl.wname) {
+	impl := (^File_Impl)(f.impl)
+	if !win32.SetCurrentDirectoryW(impl.wname) {
 		return _get_platform_error()
 	}
 	return nil
 }
 
 _fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
-	if f == nil {
+	if f == nil || f.impl == nil {
 		return nil
 	}
 	d: win32.BY_HANDLE_FILE_INFORMATION
@@ -681,7 +691,7 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
 	return _fchtimes(f, atime, mtime)
 }
 _fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
-	if f == nil {
+	if f == nil || f.impl == nil {
 		return nil
 	}
 	d: win32.BY_HANDLE_FILE_INFORMATION
@@ -737,7 +747,7 @@ _is_dir :: proc(path: string) -> bool {
 
 @(private="package")
 _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
-	f := (^File)(stream_data)
+	f := (^File_Impl)(stream_data)
 	ferr: Error
 	switch mode {
 	case .Read:

+ 5 - 3
core/os/os2/stat.odin

@@ -29,10 +29,12 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
 
 @(require_results)
 fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
-	if f != nil && f.user_fstat != nil {
-		return f->user_fstat(allocator)
+	if f == nil {
+		return {}, nil
+	} else if f.fstat != nil {
+		return f->fstat(allocator)
 	}
-	return _fstat(f, allocator)
+	return {}, .Invalid_Callback
 }
 
 @(require_results)

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

@@ -7,7 +7,8 @@ import "core:sys/linux"
 import "core:path/filepath"
 
 _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
-	return _fstat_internal(f.impl.fd, allocator)
+	impl := (^File_Impl)(f.impl)
+	return _fstat_internal(impl.fd, allocator)
 }
 
 _fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {

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

@@ -7,7 +7,7 @@ import "core:strings"
 import win32 "core:sys/windows"
 
 _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
-	if f == nil || f.impl.fd == nil {
+	if f == nil || (^File_Impl)(f.impl).fd == nil {
 		return {}, nil
 	}
 
@@ -122,7 +122,7 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
 
 
 _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
-	if f == nil || f.impl.fd == nil {
+	if f == nil {
 		return "", nil
 	}
 	h := _handle(f)
@@ -138,7 +138,7 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
 }
 
 _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
-	if f == nil || f.impl.fd == nil {
+	if f  == nil {
 		return nil, nil
 	}
 	h := _handle(f)

+ 172 - 1
core/sys/windows/advapi32.odin

@@ -18,6 +18,14 @@ foreign advapi32 {
 	                        OpenAsSelf:    BOOL,
 	                        TokenHandle:   ^HANDLE) -> BOOL ---
 
+	GetTokenInformation :: proc (
+		TokenHandle: HANDLE,
+		TokenInformationClass: TOKEN_INFORMATION_CLASS,
+		TokenInformation: LPVOID,
+		TokenInformationLength: DWORD,
+		ReturnLength: PDWORD,
+	) -> BOOL ---
+
 	CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD ---
 	CryptGenRandom       :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD ---
 	CryptReleaseContext  :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD ---
@@ -44,7 +52,17 @@ foreign advapi32 {
 		cbSid: ^DWORD,
 		ReferencedDomainName: wstring,
 		cchReferencedDomainName: ^DWORD,
-		peUse: ^SID_TYPE,
+		peUse: PSID_NAME_USE,
+	) -> BOOL ---
+
+	LookupAccountSidW :: proc (
+		lpSystemName: LPCWSTR,
+		Sid: PSID,
+		Name: LPWSTR,
+		cchName: LPDWORD,
+		ReferencedDomainName: LPWSTR,
+		cchReferencedDomainName: LPDWORD,
+		peUse: PSID_NAME_USE,
 	) -> BOOL ---
 
 	CreateProcessWithLogonW :: proc(
@@ -164,3 +182,156 @@ foreign advapi32 {
 		AccessStatus: LPBOOL,
 	) -> BOOL ---
 }
+
+PTOKEN_INFORMATION_CLASS :: ^TOKEN_INFORMATION_CLASS
+TOKEN_INFORMATION_CLASS :: enum i32 {
+	TokenUser = 1,
+	TokenGroups,
+	TokenPrivileges,
+	TokenOwner,
+	TokenPrimaryGroup,
+	TokenDefaultDacl,
+	TokenSource,
+	TokenType,
+	TokenImpersonationLevel,
+	TokenStatistics,
+	TokenRestrictedSids,
+	TokenSessionId,
+	TokenGroupsAndPrivileges,
+	TokenSessionReference,
+	TokenSandBoxInert,
+	TokenAuditPolicy,
+	TokenOrigin,
+	TokenElevationType,
+	TokenLinkedToken,
+	TokenElevation,
+	TokenHasRestrictions,
+	TokenAccessInformation,
+	TokenVirtualizationAllowed,
+	TokenVirtualizationEnabled,
+	TokenIntegrityLevel,
+	TokenUIAccess,
+	TokenMandatoryPolicy,
+	TokenLogonSid,
+	TokenIsAppContainer,
+	TokenCapabilities,
+	TokenAppContainerSid,
+	TokenAppContainerNumber,
+	TokenUserClaimAttributes,
+	TokenDeviceClaimAttributes,
+	TokenRestrictedUserClaimAttributes,
+	TokenRestrictedDeviceClaimAttributes,
+	TokenDeviceGroups,
+	TokenRestrictedDeviceGroups,
+	TokenSecurityAttributes,
+	TokenIsRestricted,
+	TokenProcessTrustLevel,
+	TokenPrivateNameSpace,
+	TokenSingletonAttributes,
+	TokenBnoIsolation,
+	TokenChildProcessFlags,
+	TokenIsLessPrivilegedAppContainer,
+	TokenIsSandboxed,
+	TokenIsAppSilo,
+	TokenLoggingInformation,
+	MaxTokenInfoClass,
+}
+
+PSID_NAME_USE :: ^SID_NAME_USE
+SID_NAME_USE :: enum i32 {
+	SidTypeUser = 1,
+	SidTypeGroup,
+	SidTypeDomain,
+	SidTypeAlias,
+	SidTypeWellKnownGroup,
+	SidTypeDeletedAccount,
+	SidTypeInvalid,
+	SidTypeUnknown,
+	SidTypeComputer,
+	SidTypeLabel,
+	SidTypeLogonSession,
+}
+
+PTOKEN_USER :: ^TOKEN_USER
+TOKEN_USER :: struct {
+	User: SID_AND_ATTRIBUTES,
+}
+
+PSID_AND_ATTRIBUTES :: ^SID_AND_ATTRIBUTES
+SID_AND_ATTRIBUTES :: struct {
+	Sid: rawptr,
+	Attributes: ULONG,
+}
+
+PTOKEN_TYPE :: ^TOKEN_TYPE
+TOKEN_TYPE :: enum {
+	TokenPrimary = 1,
+	TokenImpersonation = 2,
+}
+
+PTOKEN_STATISTICS :: ^TOKEN_STATISTICS
+TOKEN_STATISTICS :: struct {
+	TokenId: LUID,
+	AuthenticationId: LUID,
+	ExpirationTime: LARGE_INTEGER,
+	TokenType: TOKEN_TYPE,
+	ImpersonationLevel: SECURITY_IMPERSONATION_LEVEL,
+	DynamicCharged: DWORD,
+	DynamicAvailable: DWORD,
+	GroupCount: DWORD,
+	PrivilegeCount: DWORD,
+	ModifiedId: LUID,
+}
+
+
+TOKEN_SOURCE_LENGTH :: 8
+PTOKEN_SOURCE :: ^TOKEN_SOURCE
+TOKEN_SOURCE :: struct {
+	SourceName: [TOKEN_SOURCE_LENGTH]CHAR,
+	SourceIdentifier: LUID,
+}
+
+
+PTOKEN_PRIVILEGES :: ^TOKEN_PRIVILEGES
+TOKEN_PRIVILEGES :: struct {
+	PrivilegeCount: DWORD,
+	Privileges: [0]LUID_AND_ATTRIBUTES,
+}
+
+PTOKEN_PRIMARY_GROUP :: ^TOKEN_PRIMARY_GROUP
+TOKEN_PRIMARY_GROUP :: struct {
+	PrimaryGroup: PSID,
+}
+
+PTOKEN_OWNER :: ^TOKEN_OWNER
+TOKEN_OWNER :: struct {
+	Owner: PSID,
+}
+
+PTOKEN_GROUPS_AND_PRIVILEGES :: ^TOKEN_GROUPS_AND_PRIVILEGES
+TOKEN_GROUPS_AND_PRIVILEGES :: struct {
+	SidCount: DWORD,
+	SidLength: DWORD,
+	Sids: PSID_AND_ATTRIBUTES,
+	RestrictedSidCount: DWORD,
+	RestrictedSidLength: DWORD,
+	RestrictedSids: PSID_AND_ATTRIBUTES,
+	PrivilegeCount: DWORD,
+	PrivilegeLength: DWORD,
+	Privileges: PLUID_AND_ATTRIBUTES,
+	AuthenticationId: LUID,
+}
+
+PTOKEN_DEFAULT_DACL :: ^TOKEN_DEFAULT_DACL
+TOKEN_DEFAULT_DACL :: struct {
+	DefaultDacl: PACL,
+}
+
+PACL :: ^ACL
+ACL :: struct {
+	AclRevision: BYTE,
+	Sbz1: BYTE,
+	AclSize: WORD,
+	AceCount: WORD,
+	Sbz2: WORD,
+}

+ 45 - 0
core/sys/windows/kernel32.odin

@@ -233,6 +233,12 @@ foreign kernel32 {
 	QueryPerformanceCounter :: proc(lpPerformanceCount: ^LARGE_INTEGER) -> BOOL ---
 	GetExitCodeProcess :: proc(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL ---
 	TerminateProcess :: proc(hProcess: HANDLE, uExitCode: UINT) -> BOOL ---
+	OpenProcess :: proc(dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwProcessId: DWORD) -> HANDLE ---
+	OpenThread :: proc(dwDesiredAccess: DWORD, bInheritHandle: BOOL, dwThreadId: DWORD) -> HANDLE ---
+	GetThreadContext :: proc(
+		hThread: HANDLE,
+		lpContext: LPCONTEXT,
+	) -> BOOL ---
 	CreateProcessW :: proc(
 		lpApplicationName: LPCWSTR,
 		lpCommandLine: LPWSTR,
@@ -543,6 +549,45 @@ THREAD_PRIORITY_IDLE          :: THREAD_BASE_PRIORITY_IDLE
 THREAD_MODE_BACKGROUND_BEGIN  :: 0x00010000
 THREAD_MODE_BACKGROUND_END    :: 0x00020000
 
+PROCESS_ALL_ACCESS :: 0x000F0000 | SYNCHRONIZE | 0xFFFF
+PROCESS_CREATE_PROCESS :: 0x0080
+PROCESS_CREATE_THREAD :: 0x0002
+PROCESS_DUP_HANDLE :: 0x0040
+PROCESS_QUERY_INFORMATION :: 0x0400
+PROCESS_QUERY_LIMITED_INFORMATION :: 0x1000
+PROCESS_SET_INFORMATION :: 0x0200
+PROCESS_SET_QUOTA :: 0x0100
+PROCESS_SUSPEND_RESUME :: 0x0800
+PROCESS_TERMINATE :: 0x0001
+PROCESS_VM_OPERATION :: 0x0008
+PROCESS_VM_READ :: 0x0010
+PROCESS_VM_WRITE :: 0x0020
+
+THREAD_ALL_ACCESS :: \
+	THREAD_DIRECT_IMPERSONATION |
+	THREAD_GET_CONTEXT |
+	THREAD_IMPERSONATE |
+	THREAD_QUERY_INFORMATION |
+	THREAD_QUERY_LIMITED_INFORMATION |
+	THREAD_SET_CONTEXT |
+	THREAD_SET_INFORMATION |
+	THREAD_SET_LIMITED_INFORMATION |
+	THREAD_SET_THREAD_TOKEN |
+	THREAD_SUSPEND_RESUME |
+	THREAD_TERMINATE |
+	SYNCHRONIZE
+THREAD_DIRECT_IMPERSONATION :: 0x0200
+THREAD_GET_CONTEXT :: 0x0008
+THREAD_IMPERSONATE :: 0x0100
+THREAD_QUERY_INFORMATION :: 0x0040
+THREAD_QUERY_LIMITED_INFORMATION :: 0x0800
+THREAD_SET_CONTEXT :: 0x0010
+THREAD_SET_INFORMATION :: 0x0020
+THREAD_SET_LIMITED_INFORMATION :: 0x0400
+THREAD_SET_THREAD_TOKEN :: 0x0080
+THREAD_SUSPEND_RESUME :: 0x0002
+THREAD_TERMINATE :: 0x0001
+
 COPY_FILE_FAIL_IF_EXISTS              :: 0x00000001
 COPY_FILE_RESTARTABLE                 :: 0x00000002
 COPY_FILE_OPEN_SOURCE_FOR_WRITE       :: 0x00000004

+ 135 - 18
core/sys/windows/types.odin

@@ -64,6 +64,7 @@ LONG_PTR :: int
 UINT_PTR :: uintptr
 ULONG :: c_ulong
 ULONGLONG :: c_ulonglong
+LONGLONG :: c_longlong
 UCHAR :: BYTE
 NTSTATUS :: c.long
 COLORREF :: DWORD
@@ -2145,6 +2146,7 @@ SECURITY_IMPERSONATION_LEVEL :: enum {
 SECURITY_INFORMATION :: DWORD
 ANYSIZE_ARRAY :: 1
 
+PLUID_AND_ATTRIBUTES :: ^LUID_AND_ATTRIBUTES
 LUID_AND_ATTRIBUTES :: struct {
 	Luid: LUID,
 	Attributes: DWORD,
@@ -2570,7 +2572,139 @@ EXCEPTION_RECORD :: struct {
 	ExceptionInformation: [EXCEPTION_MAXIMUM_PARAMETERS]LPVOID,
 }
 
-CONTEXT :: struct{} // TODO(bill)
+
+CONTEXT :: struct {
+	P1Home: DWORD64,
+	P2Home: DWORD64,
+	P3Home: DWORD64,
+	P4Home: DWORD64,
+	P5Home: DWORD64,
+	P6Home: DWORD64,
+	ContextFlags: DWORD,
+	MxCsr: DWORD,
+	SegCs: WORD,
+	SegDs: WORD,
+	SegEs: WORD,
+	SegFs: WORD,
+	SegGs: WORD,
+	SegSs: WORD,
+	EFlags: DWORD,
+	Dr0: DWORD64,
+	Dr1: DWORD64,
+	Dr2: DWORD64,
+	Dr3: DWORD64,
+	Dr6: DWORD64,
+	Dr7: DWORD64,
+	Rax: DWORD64,
+	Rcx: DWORD64,
+	Rdx: DWORD64,
+	Rbx: DWORD64,
+	Rsp: DWORD64,
+	Rbp: DWORD64,
+	Rsi: DWORD64,
+	Rdi: DWORD64,
+	R8: DWORD64,
+	R9: DWORD64,
+	R10: DWORD64,
+	R11: DWORD64,
+	R12: DWORD64,
+	R13: DWORD64,
+	R14: DWORD64,
+	R15: DWORD64,
+	Rip: DWORD64,
+	_: struct #raw_union {
+		FltSave: XMM_SAVE_AREA32,
+		Q: [16]NEON128,
+		D: [32]ULONGLONG,
+		_: struct {
+			Header: [2]M128A,
+			Legacy: [8]M128A,
+			Xmm0: M128A,
+			Xmm1: M128A,
+			Xmm2: M128A,
+			Xmm3: M128A,
+			Xmm4: M128A,
+			Xmm5: M128A,
+			Xmm6: M128A,
+			Xmm7: M128A,
+			Xmm8: M128A,
+			Xmm9: M128A,
+			Xmm10: M128A,
+			Xmm11: M128A,
+			Xmm12: M128A,
+			Xmm13: M128A,
+			Xmm14: M128A,
+			Xmm15: M128A,
+		},
+		S: [32]DWORD,
+	},
+	VectorRegister: [26]M128A,
+	VectorControl: DWORD64,
+	DebugControl: DWORD64,
+	LastBranchToRip: DWORD64,
+	LastBranchFromRip: DWORD64,
+	LastExceptionToRip: DWORD64,
+	LastExceptionFromRip: DWORD64,
+}
+
+PCONTEXT :: ^CONTEXT
+LPCONTEXT :: ^CONTEXT
+
+when size_of(uintptr) == 32 { 
+	XSAVE_FORMAT :: struct #align(16) {
+		ControlWord: WORD,
+		StatusWord: WORD,
+		TagWord: BYTE,
+		Reserved1: BYTE,
+		ErrorOpcode: WORD,
+		ErrorOffset: DWORD,
+		ErrorSelector: WORD,
+		Reserved2: WORD,
+		DataOffset: DWORD,
+		DataSelector: WORD,
+		Reserved3: WORD,
+		MxCsr: DWORD,
+		MxCsr_Mask: DWORD,
+		FloatRegisters: [8]M128A,
+		// 32-bit specific
+		XmmRegisters: [8]M128A,
+		Reserved4: [192]BYTE,
+		StackControl: [7]DWORD,
+		Cr0NpxState: DWORD,
+	}
+} else {
+	XSAVE_FORMAT :: struct #align(16) {
+		ControlWord: WORD,
+		StatusWord: WORD,
+		TagWord: BYTE,
+		Reserved1: BYTE,
+		ErrorOpcode: WORD,
+		ErrorOffset: DWORD,
+		ErrorSelector: WORD,
+		Reserved2: WORD,
+		DataOffset: DWORD,
+		DataSelector: WORD,
+		Reserved3: WORD,
+		MxCsr: DWORD,
+		MxCsr_Mask: DWORD,
+		FloatRegisters: [8]M128A,
+		// 64-bit specific
+		XmmRegisters: [16]M128A,
+		Reserved4: [96]BYTE,
+	}
+}
+
+XMM_SAVE_AREA32 :: XSAVE_FORMAT
+
+M128A :: struct {
+	Low: ULONGLONG,
+	High: LONGLONG,
+}
+
+NEON128 :: struct {
+	Low: ULONGLONG,
+	High: LONGLONG,
+}
 
 EXCEPTION_POINTERS :: struct {
 	ExceptionRecord: ^EXCEPTION_RECORD,
@@ -2733,23 +2867,6 @@ PROFILEINFOW :: struct {
 	hProfile: HANDLE,
 }
 
-// Used in LookupAccountNameW
-SID_NAME_USE :: distinct DWORD
-
-SID_TYPE :: enum SID_NAME_USE {
-	User = 1,
-	Group,
-	Domain,
-	Alias,
-	WellKnownGroup,
-	DeletedAccount,
-	Invalid,
-	Unknown,
-	Computer,
-	Label,
-	LogonSession,
-}
-
 SECURITY_MAX_SID_SIZE :: 68
 
 // https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid

+ 2 - 2
core/sys/windows/util.odin

@@ -202,7 +202,7 @@ get_computer_name_and_account_sid :: proc(username: string) -> (computer_name: s
 	username_w := utf8_to_utf16(username, context.temp_allocator)
 	cbsid: DWORD
 	computer_name_size: DWORD
-	pe_use := SID_TYPE.User
+	pe_use := SID_NAME_USE.SidTypeUser
 
 	res := LookupAccountNameW(
 		nil, // Look on this computer first
@@ -244,7 +244,7 @@ get_sid :: proc(username: string, sid: ^SID) -> (ok: bool) {
 	username_w := utf8_to_utf16(username, context.temp_allocator)
 	cbsid: DWORD
 	computer_name_size: DWORD
-	pe_use := SID_TYPE.User
+	pe_use := SID_NAME_USE.SidTypeUser
 
 	res := LookupAccountNameW(
 		nil, // Look on this computer first

+ 5 - 1
src/build_settings.cpp

@@ -1649,7 +1649,11 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
 	if (!bc->custom_optimization_level) {
 		// NOTE(bill): when building with `-debug` but not specifying an optimization level
 		// default to `-o:none` to improve the debug symbol generation by default
-		bc->optimization_level = -1; // -o:none
+		if (bc->ODIN_DEBUG) {
+			bc->optimization_level = -1; // -o:none
+		} else {
+			bc->optimization_level = 0; // -o:minimal
+		}
 	}
 
 	bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3);

+ 9 - 0
src/check_decl.cpp

@@ -1869,5 +1869,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
 
 	add_deps_from_child_to_parent(decl);
 
+	for (VariadicReuseData const &vr : decl->variadic_reuses) {
+		GB_ASSERT(vr.slice_type->kind == Type_Slice);
+		Type *elem = vr.slice_type->Slice.elem;
+		i64 size = type_size_of(elem);
+		i64 align = type_align_of(elem);
+		decl->variadic_reuse_max_bytes = gb_max(decl->variadic_reuse_max_bytes, size*vr.max_count);
+		decl->variadic_reuse_max_align = gb_max(decl->variadic_reuse_max_align, align);
+	}
+
 	return true;
 }

+ 25 - 6
src/check_expr.cpp

@@ -6033,6 +6033,22 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
 
 					Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
 					o.type = vt->type;
+
+					// NOTE(bill, 2024-07-14): minimize the stack usage for variadic parameters with the backing array
+					if (c->decl) {
+						bool found = false;
+						for (auto &vr : c->decl->variadic_reuses) {
+							if (are_types_identical(vt->type, vr.slice_type)) {
+								vr.max_count = gb_max(vr.max_count, variadic_operands.count);
+								found = true;
+								break;
+							}
+						}
+						if (!found) {
+							array_add(&c->decl->variadic_reuses, VariadicReuseData{vt->type, variadic_operands.count});
+						}
+					}
+
 				} else {
 					dummy_argument_count += 1;
 					o.type = t_untyped_nil;
@@ -7888,12 +7904,15 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
 
 			// NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features.
 			if (is_call_inlined) {
-				GB_ASSERT(c->curr_proc_decl);
-				GB_ASSERT(c->curr_proc_decl->entity);
-				GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
-				String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
-				if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
-					error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
+				if (c->curr_proc_decl == nullptr) {
+					error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
+				} else {
+					GB_ASSERT(c->curr_proc_decl->entity);
+					GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
+					String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
+					if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
+						error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
+					}
 				}
 			}
 		}

+ 37 - 0
src/check_type.cpp

@@ -1953,6 +1953,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 					error(name, "'#by_ptr' can only be applied to variable fields");
 					p->flags &= ~FieldFlag_by_ptr;
 				}
+				if (p->flags&FieldFlag_no_capture) {
+					error(name, "'#no_capture' can only be applied to variable fields");
+					p->flags &= ~FieldFlag_no_capture;
+				}
 
 				param = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
 				param->TypeName.is_type_alias = true;
@@ -2054,6 +2058,28 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 						p->flags &= ~FieldFlag_by_ptr; // Remove the flag
 					}
 				}
+				if (p->flags&FieldFlag_no_capture) {
+					if (is_variadic && variadic_index == variables.count) {
+						if (p->flags & FieldFlag_c_vararg) {
+							error(name, "'#no_capture' cannot be applied to a #c_vararg parameter");
+							p->flags &= ~FieldFlag_no_capture;
+						} else {
+							error(name, "'#no_capture' is already implied on all variadic parameter");
+						}
+					} else if (is_type_polymorphic(type)) {
+						// ignore
+					} else {
+						if (is_type_internally_pointer_like(type)) {
+							error(name, "'#no_capture' is currently reserved for future use");
+						} else {
+							ERROR_BLOCK();
+							error(name, "'#no_capture' can only be applied to pointer-like types");
+							error_line("\t'#no_capture' does not currently do anything useful\n");
+							p->flags &= ~FieldFlag_no_capture;
+						}
+					}
+				}
+
 
 				if (is_poly_name) {
 					if (p->flags&FieldFlag_no_alias) {
@@ -2072,6 +2098,11 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 						error(name, "'#by_ptr' can only be applied to variable fields");
 						p->flags &= ~FieldFlag_by_ptr;
 					}
+					if (p->flags&FieldFlag_no_capture) {
+						error(name, "'#no_capture' can only be applied to variable fields");
+						p->flags &= ~FieldFlag_no_capture;
+					}
+
 
 					if (!is_type_polymorphic(type) && check_constant_parameter_value(type, params[i])) {
 						// failed
@@ -2091,6 +2122,8 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 				param->flags |= EntityFlag_Ellipsis;
 				if (is_c_vararg) {
 					param->flags |= EntityFlag_CVarArg;
+				} else {
+					param->flags |= EntityFlag_NoCapture;
 				}
 			}
 
@@ -2115,6 +2148,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
 			if (p->flags&FieldFlag_by_ptr) {
 				param->flags |= EntityFlag_ByPtr;
 			}
+			if (p->flags&FieldFlag_no_capture) {
+				param->flags |= EntityFlag_NoCapture;
+			}
+
 
 			param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
 			add_entity(ctx, scope, name, param);

+ 3 - 0
src/checker.cpp

@@ -184,6 +184,9 @@ gb_internal void init_decl_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
 	ptr_set_init(&d->deps, 0);
 	ptr_set_init(&d->type_info_deps, 0);
 	d->labels.allocator = heap_allocator();
+	d->variadic_reuses.allocator = heap_allocator();
+	d->variadic_reuse_max_bytes = 0;
+	d->variadic_reuse_max_align = 1;
 }
 
 gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {

+ 9 - 0
src/checker.hpp

@@ -181,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
 	"Checked",
 };
 
+struct VariadicReuseData {
+	Type *slice_type; // ..elem_type
+	i64 max_count;
+};
+
 // DeclInfo is used to store information of certain declarations to allow for "any order" usage
 struct DeclInfo {
 	DeclInfo *    parent; // NOTE(bill): only used for procedure literals at the moment
@@ -219,6 +224,10 @@ struct DeclInfo {
 
 	Array<BlockLabel> labels;
 
+	Array<VariadicReuseData> variadic_reuses;
+	i64 variadic_reuse_max_bytes;
+	i64 variadic_reuse_max_align;
+
 	// NOTE(bill): this is to prevent a race condition since these procedure literals can be created anywhere at any time
 	struct lbModule *code_gen_module;
 };

+ 1 - 1
src/entity.cpp

@@ -45,7 +45,7 @@ enum EntityFlag : u64 {
 	EntityFlag_Value         = 1ull<<11,
 	EntityFlag_BitFieldField = 1ull<<12,
 
-
+	EntityFlag_NoCapture = 1ull<<13, // #no_capture
 
 	EntityFlag_PolyConst     = 1ull<<15,
 	EntityFlag_NotExported   = 1ull<<16,

+ 10 - 4
src/llvm_abi.cpp

@@ -15,6 +15,7 @@ struct lbArgType {
 	LLVMAttributeRef align_attribute; // Optional
 	i64 byval_alignment;
 	bool is_byval;
+	bool no_capture;
 };
 
 
@@ -159,6 +160,11 @@ gb_internal void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType
 			LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
 		}
 
+		if (arg->no_capture) {
+			LLVMAddAttributeAtIndex(fn, arg_index+1, nocapture_attr);
+		}
+
+
 		if (ft->multiple_return_original_type) {
 			if (ft->original_arg_count <= i) {
 				LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
@@ -645,10 +651,10 @@ namespace lbAbiAmd64SysV {
 		if (is_mem_cls(cls, attribute_kind)) {
 			LLVMAttributeRef attribute = nullptr;
 			if (attribute_kind == Amd64TypeAttribute_ByVal) {
-				// if (!is_calling_convention_odin(calling_convention)) {
-					return lb_arg_type_indirect_byval(c, type);
-				// }
-				// attribute = nullptr;
+				if (is_calling_convention_odin(calling_convention)) {
+					return lb_arg_type_indirect(type, attribute);
+				}
+				return lb_arg_type_indirect_byval(c, type);
 			} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
 				attribute = lb_create_enum_attribute_with_type(c, "sret", type);
 			}

+ 1 - 0
src/llvm_backend.cpp

@@ -1570,6 +1570,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
 
 	switch (build_context.optimization_level) {
 	case -1:
+		array_add(&passes, "function(annotation-remarks)");
 		break;
 	case 0:
 		array_add(&passes, "always-inline");

+ 8 - 1
src/llvm_backend.hpp

@@ -296,6 +296,11 @@ enum lbProcedureFlag : u32 {
 	lbProcedureFlag_DebugAllocaCopy = 1<<1,
 };
 
+struct lbVariadicReuseSlices {
+	Type *slice_type;
+	lbAddr slice_addr;
+};
+
 struct lbProcedure {
 	u32 flags;
 	u16 state_flags;
@@ -336,8 +341,10 @@ struct lbProcedure {
 	bool             in_multi_assignment;
 	Array<LLVMValueRef> raw_input_parameters;
 
-	LLVMValueRef temp_callee_return_struct_memory;
+	Array<lbVariadicReuseSlices> variadic_reuses;
+	lbAddr variadic_reuse_base_array_ptr;
 
+	LLVMValueRef temp_callee_return_struct_memory;
 	Ast *curr_stmt;
 
 	Array<Scope *>       scope_stack;

+ 52 - 4
src/llvm_backend_proc.cpp

@@ -253,6 +253,11 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
 			if (e->flags&EntityFlag_NoAlias) {
 				lb_add_proc_attribute_at_index(p, offset+parameter_index, "noalias");
 			}
+			if (e->flags&EntityFlag_NoCapture) {
+				if (is_type_internally_pointer_like(e->type)) {
+					lb_add_proc_attribute_at_index(p, offset+parameter_index, "nocapture");
+				}
+			}
 			parameter_index += 1;
 		}
 	}
@@ -517,6 +522,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
 	lb_start_block(p, p->entry_block);
 
 	map_init(&p->direct_parameters);
+	p->variadic_reuses.allocator = heap_allocator();
 
 	GB_ASSERT(p->type != nullptr);
 
@@ -3450,17 +3456,59 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
 					}
 					isize slice_len = var_args.count;
 					if (slice_len > 0) {
-						lbAddr slice = lb_add_local_generated(p, slice_type, true);
-						lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
+						lbAddr slice = {};
+
+						for (auto const &vr : p->variadic_reuses) {
+							if (are_types_identical(vr.slice_type, slice_type)) {
+								slice = vr.slice_addr;
+								break;
+							}
+						}
+
+						DeclInfo *d = decl_info_of_entity(p->entity);
+						if (d != nullptr && slice.addr.value == nullptr) {
+							for (auto const &vr : d->variadic_reuses) {
+								if (are_types_identical(vr.slice_type, slice_type)) {
+								#if LLVM_VERSION_MAJOR >= 13
+									// NOTE(bill): No point wasting even more memory, just reuse this stack variable too
+									if (p->variadic_reuses.count > 0) {
+										slice = p->variadic_reuses[0].slice_addr;
+									} else {
+										slice = lb_add_local_generated(p, slice_type, true);
+									}
+									// NOTE(bill): Change the underlying type to match the specific type
+									slice.addr.type = alloc_type_pointer(slice_type);
+								#else
+									slice = lb_add_local_generated(p, slice_type, true);
+								#endif
+									array_add(&p->variadic_reuses, lbVariadicReuseSlices{slice_type, slice});
+									break;
+								}
+							}
+						}
+
+						lbValue base_array_ptr = p->variadic_reuse_base_array_ptr.addr;
+						if (d != nullptr && base_array_ptr.value == nullptr) {
+							i64 max_bytes = d->variadic_reuse_max_bytes;
+							i64 max_align = gb_max(d->variadic_reuse_max_align, 16);
+							p->variadic_reuse_base_array_ptr = lb_add_local_generated(p, alloc_type_array(t_u8, max_bytes), true);
+							lb_try_update_alignment(p->variadic_reuse_base_array_ptr.addr, cast(unsigned)max_align);
+							base_array_ptr = p->variadic_reuse_base_array_ptr.addr;
+						}
+
+						GB_ASSERT(base_array_ptr.value != nullptr);
+						GB_ASSERT(slice.addr.value != nullptr);
+
+						base_array_ptr = lb_emit_conv(p, base_array_ptr, alloc_type_pointer(alloc_type_array(elem_type, slice_len)));
 
 						for (isize i = 0; i < var_args.count; i++) {
-							lbValue addr = lb_emit_array_epi(p, base_array.addr, cast(i32)i);
+							lbValue addr = lb_emit_array_epi(p, base_array_ptr, cast(i32)i);
 							lbValue var_arg = var_args[i];
 							var_arg = lb_emit_conv(p, var_arg, elem_type);
 							lb_emit_store(p, addr, var_arg);
 						}
 
-						lbValue base_elem = lb_emit_array_epi(p, base_array.addr, 0);
+						lbValue base_elem = lb_emit_array_epi(p, base_array_ptr, 0);
 						lbValue len = lb_const_int(p->module, t_int, slice_len);
 						lb_fill_slice(p, slice, base_elem, len);
 

+ 12 - 0
src/main.cpp

@@ -399,6 +399,8 @@ enum BuildFlagKind {
 
 	BuildFlag_Sanitize,
 
+	BuildFlag_FastBuild,
+
 #if defined(GB_SYSTEM_WINDOWS)
 	BuildFlag_IgnoreVsSearch,
 	BuildFlag_ResourceFile,
@@ -605,6 +607,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
 
 	add_flag(&build_flags, BuildFlag_Sanitize,                str_lit("sanitize"),                  BuildFlagParam_String,  Command__does_build, true);
 
+	add_flag(&build_flags, BuildFlag_FastBuild,               str_lit("fast-build"),                BuildFlagParam_None,    Command__does_build);
+
+
 #if defined(GB_SYSTEM_WINDOWS)
 	add_flag(&build_flags, BuildFlag_IgnoreVsSearch,          str_lit("ignore-vs-search"),          BuildFlagParam_None,    Command__does_build);
 	add_flag(&build_flags, BuildFlag_ResourceFile,            str_lit("resource"),                  BuildFlagParam_String,  Command__does_build);
@@ -1441,6 +1446,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
 							}
 							break;
 
+
+						case BuildFlag_FastBuild:
+							build_context.custom_optimization_level = true;
+							build_context.optimization_level = -1;
+							build_context.use_separate_modules = true;
+							break;
+
 					#if defined(GB_SYSTEM_WINDOWS)
 						case BuildFlag_IgnoreVsSearch: {
 							GB_ASSERT(value.kind == ExactValue_Invalid);

+ 1 - 0
src/parser.cpp

@@ -4014,6 +4014,7 @@ struct ParseFieldPrefixMapping {
 gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
 	{str_lit("using"),        Token_using,     FieldFlag_using},
 	{str_lit("no_alias"),     Token_Hash,      FieldFlag_no_alias},
+	{str_lit("no_capture"),   Token_Hash,      FieldFlag_no_capture},
 	{str_lit("c_vararg"),     Token_Hash,      FieldFlag_c_vararg},
 	{str_lit("const"),        Token_Hash,      FieldFlag_const},
 	{str_lit("any_int"),      Token_Hash,      FieldFlag_any_int},

+ 7 - 2
src/parser.hpp

@@ -331,8 +331,10 @@ enum FieldFlag : u32 {
 	FieldFlag_by_ptr    = 1<<8,
 	FieldFlag_no_broadcast = 1<<9, // disallow array programming
 
+	FieldFlag_no_capture  = 1<<11,
+
 	// Internal use by the parser only
-	FieldFlag_Tags      = 1<<10,
+	FieldFlag_Tags      = 1<<15,
 	FieldFlag_Results   = 1<<16,
 
 
@@ -340,7 +342,10 @@ enum FieldFlag : u32 {
 	FieldFlag_Invalid   = 1u<<31,
 
 	// Parameter List Restrictions
-	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast,
+	FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|
+	                      FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast|
+	                      FieldFlag_no_capture,
+
 	FieldFlag_Struct    = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
 };
 

+ 88 - 41
src/thread_pool.cpp

@@ -10,13 +10,18 @@ gb_internal void thread_pool_destroy(ThreadPool *pool);
 gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data);
 gb_internal void thread_pool_wait(ThreadPool *pool);
 
+enum GrabState {
+	Grab_Success = 0,
+	Grab_Empty   = 1,
+	Grab_Failed  = 2,
+};
+
 struct ThreadPool {
-	gbAllocator threads_allocator;
-	Slice<Thread> threads;
+	gbAllocator       threads_allocator;
+	Slice<Thread>     threads;
 	std::atomic<bool> running;
 
 	Futex tasks_available;
-
 	Futex tasks_left;
 };
 
@@ -46,7 +51,7 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
 
 	for_array_off(i, 1, pool->threads) {
 		Thread *t = &pool->threads[i];
-		pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
+		pool->tasks_available.fetch_add(1, std::memory_order_acquire);
 		futex_broadcast(&pool->tasks_available);
 		thread_join_and_destroy(t);
 	}
@@ -54,51 +59,86 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
 	gb_free(pool->threads_allocator, pool->threads.data);
 }
 
-void thread_pool_queue_push(Thread *thread, WorkerTask task) {
-	u64 capture;
-	u64 new_capture;
-	do {
-		capture = thread->head_and_tail.load();
-
-		u64 mask = thread->capacity - 1;
-		u64 head = (capture >> 32) & mask;
-		u64 tail = ((u32)capture) & mask;
+TaskRingBuffer *task_ring_grow(TaskRingBuffer *ring, isize bottom, isize top) {
+	TaskRingBuffer *new_ring = task_ring_init(ring->size * 2);
+	for (isize i = top; i < bottom; i++) {
+		new_ring->buffer[i % new_ring->size] = ring->buffer[i % ring->size];
+	}
+	return new_ring;
+}
 
-		u64 new_head = (head + 1) & mask;
-		GB_ASSERT_MSG(new_head != tail, "Thread Queue Full!");
+void thread_pool_queue_push(Thread *thread, WorkerTask task) {
+	isize bot                = thread->queue.bottom.load(std::memory_order_relaxed);
+	isize top                = thread->queue.top.load(std::memory_order_acquire);
+	TaskRingBuffer *cur_ring   = thread->queue.ring.load(std::memory_order_relaxed);
+
+	isize size = bot - top;
+	if (size > (cur_ring->size - 1)) {
+		// Queue is full
+		thread->queue.ring = task_ring_grow(thread->queue.ring, bot, top);
+		cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
+	}
 
-		// This *must* be done in here, to avoid a potential race condition where we no longer own the slot by the time we're assigning
-		thread->queue[head] = task;
-		new_capture = (new_head << 32) | tail;
-	} while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture));
+	cur_ring->buffer[bot % cur_ring->size] = task;
+	std::atomic_thread_fence(std::memory_order_release);
+	thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
 
 	thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
 	thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
 	futex_broadcast(&thread->pool->tasks_available);
 }
 
-bool thread_pool_queue_pop(Thread *thread, WorkerTask *task) {
-	u64 capture;
-	u64 new_capture;
-	do {
-		capture = thread->head_and_tail.load(std::memory_order_acquire);
-
-		u64 mask = thread->capacity - 1;
-		u64 head = (capture >> 32) & mask;
-		u64 tail = ((u32)capture) & mask;
+GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
+	isize bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1;
+	TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
+	thread->queue.bottom.store(bot, std::memory_order_relaxed);
+	std::atomic_thread_fence(std::memory_order_seq_cst);
+
+	isize top = thread->queue.top.load(std::memory_order_relaxed);
+	if (top <= bot) {
+
+		// Queue is not empty
+		*task = cur_ring->buffer[bot % cur_ring->size];
+		if (top == bot) {
+			// Only one entry left in queue
+			if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
+				// Race failed
+				thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
+				return Grab_Empty;
+			}
 
-		u64 new_tail = (tail + 1) & mask;
-		if (tail == head) {
-			return false;
+			thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
+			return Grab_Success;
 		}
 
-		// Making a copy of the task before we increment the tail, avoiding the same potential race condition as above
-		*task = thread->queue[tail];
-
-		new_capture = (head << 32) | new_tail;
-	} while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture, std::memory_order_release));
+		// We got a task without hitting a race
+		return Grab_Success;
+	} else {
+		// Queue is empty
+		thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
+		return Grab_Empty;
+	}
+}
 
-	return true;
+GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) {
+	isize top = thread->queue.top.load(std::memory_order_acquire);
+	std::atomic_thread_fence(std::memory_order_seq_cst);
+	isize bot = thread->queue.bottom.load(std::memory_order_acquire);
+
+	GrabState ret = Grab_Empty;
+	if (top < bot) {
+		// Queue is not empty
+		TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_consume);
+		*task = cur_ring->buffer[top % cur_ring->size];
+
+		if (!thread->queue.top.compare_exchange_strong(top, top + 1, std::memory_order_seq_cst, std::memory_order_relaxed)) {
+			// Race failed
+			ret = Grab_Failed;
+		} else {
+			ret = Grab_Success;
+		}
+	}
+	return ret;
 }
 
 gb_internal bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) {
@@ -115,12 +155,11 @@ gb_internal void thread_pool_wait(ThreadPool *pool) {
 
 	while (pool->tasks_left.load(std::memory_order_acquire)) {
 		// if we've got tasks on our queue, run them
-		while (thread_pool_queue_pop(current_thread, &task)) {
+		while (!thread_pool_queue_take(current_thread, &task)) {
 			task.do_work(task.data);
 			pool->tasks_left.fetch_sub(1, std::memory_order_release);
 		}
 
-
 		// is this mem-barriered enough?
 		// This *must* be executed in this order, so the futex wakes immediately
 		// if rem_tasks has changed since we checked last, otherwise the program
@@ -145,7 +184,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
 		usize finished_tasks = 0;
 		i32 state;
 
-		while (thread_pool_queue_pop(current_thread, &task)) {
+		while (!thread_pool_queue_take(current_thread, &task)) {
 			task.do_work(task.data);
 			pool->tasks_left.fetch_sub(1, std::memory_order_release);
 
@@ -167,7 +206,12 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
 
 				Thread *thread = &pool->threads.data[idx];
 				WorkerTask task;
-				if (thread_pool_queue_pop(thread, &task)) {
+
+				GrabState ret = thread_pool_queue_steal(thread, &task);
+				switch (ret) {
+				case Grab_Empty:
+					continue;
+				case Grab_Success:
 					task.do_work(task.data);
 					pool->tasks_left.fetch_sub(1, std::memory_order_release);
 
@@ -175,6 +219,8 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
 						futex_signal(&pool->tasks_left);
 					}
 
+					/*fallthrough*/
+				case Grab_Failed:
 					goto main_loop_continue;
 				}
 			}
@@ -182,6 +228,7 @@ gb_internal THREAD_PROC(thread_pool_thread_proc) {
 
 		// if we've done all our work, and there's nothing to steal, go to sleep
 		state = pool->tasks_available.load(std::memory_order_acquire);
+		if (!pool->running) { break; }
 		futex_wait(&pool->tasks_available, state);
 
 		main_loop_continue:;

+ 29 - 10
src/threading.cpp

@@ -46,6 +46,18 @@ typedef struct WorkerTask {
 	void           *data;
 } WorkerTask;
 
+typedef struct TaskRingBuffer {
+	std::atomic<isize> size;
+	std::atomic<WorkerTask *> buffer;
+} TaskRingBuffer;
+
+typedef struct TaskQueue {
+	std::atomic<isize> top;
+	std::atomic<isize> bottom;
+
+	std::atomic<TaskRingBuffer *> ring;
+} TaskQueue;
+
 struct Thread {
 #if defined(GB_SYSTEM_WINDOWS)
 	void *win32_handle;
@@ -54,12 +66,9 @@ struct Thread {
 #endif
 
 	isize idx;
+	isize stack_size;
 
-	WorkerTask *queue;
-	size_t capacity;
-	std::atomic<uint64_t> head_and_tail;
-
-	isize  stack_size;
+	struct TaskQueue   queue;
 	struct ThreadPool *pool;
 };
 
@@ -551,6 +560,18 @@ gb_internal void *internal_thread_proc(void *arg) {
 }
 #endif
 
+TaskRingBuffer *task_ring_init(isize size) {
+	TaskRingBuffer *ring = gb_alloc_item(heap_allocator(), TaskRingBuffer);
+	ring->size = size;
+	ring->buffer = gb_alloc_array(heap_allocator(), WorkerTask, ring->size);
+	return ring;
+}
+
+void thread_queue_destroy(TaskQueue *q) {
+	gb_free(heap_allocator(), (*q->ring).buffer);
+	gb_free(heap_allocator(), q->ring);
+}
+
 gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
 	gb_zero_item(t);
 #if defined(GB_SYSTEM_WINDOWS)
@@ -559,14 +580,12 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
 	t->posix_handle = 0;
 #endif
 
-	t->capacity = 1 << 14; // must be a power of 2
-	t->queue = gb_alloc_array(heap_allocator(), WorkerTask, t->capacity);
-	t->head_and_tail = 0;
+	// Size must be a power of 2
+	t->queue.ring = task_ring_init(1 << 14);
 	t->pool = pool;
 	t->idx = idx;
 }
 
-
 gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
 	thread_init(pool, t, idx);
 	isize stack_size = 0;
@@ -598,7 +617,7 @@ gb_internal void thread_join_and_destroy(Thread *t) {
 	t->posix_handle = 0;
 #endif
 
-	gb_free(heap_allocator(), t->queue);
+	thread_queue_destroy(&t->queue);
 }
 
 gb_internal void thread_set_name(Thread *t, char const *name) {

+ 3 - 0
src/types.cpp

@@ -2923,11 +2923,14 @@ gb_internal Type *c_vararg_promote_type(Type *type) {
 
 	if (core->kind == Type_Basic) {
 		switch (core->Basic.kind) {
+		case Basic_f16:
 		case Basic_f32:
 		case Basic_UntypedFloat:
 			return t_f64;
+		case Basic_f16le:
 		case Basic_f32le:
 			return t_f64le;
+		case Basic_f16be:
 		case Basic_f32be:
 			return t_f64be;
 

+ 21 - 0
tests/core/encoding/json/test_core_json.odin

@@ -371,6 +371,27 @@ utf8_string_of_multibyte_characters :: proc(t: ^testing.T) {
 	testing.expectf(t, err == nil, "Expected `json.parse` to return nil, got %v", err)
 }
 
+@test
+struct_with_ignore_tags :: proc(t: ^testing.T) {
+	My_Struct :: struct {
+		a: string `json:"-"`,
+	}
+
+	my_struct := My_Struct{
+		a = "test",
+	}
+
+	my_struct_marshaled, marshal_err := json.marshal(my_struct)
+	defer delete(my_struct_marshaled)
+
+	testing.expectf(t, marshal_err == nil, "Expected `json.marshal` to return nil error, got %v", marshal_err)
+
+	my_struct_json := transmute(string)my_struct_marshaled
+	expected_json := `{}`
+
+	testing.expectf(t, expected_json == my_struct_json, "Expected `json.marshal` to return %s, got %s", expected_json, my_struct_json)
+}
+
 @test
 map_with_integer_keys :: proc(t: ^testing.T) {
 	my_map := make(map[i32]string)

+ 2 - 2
vendor/raylib/raylib.odin

@@ -1667,7 +1667,7 @@ IsGestureDetected :: proc "c" (gesture: Gesture) -> bool {
 
 
 // Text formatting with variables (sprintf style)
-TextFormat :: proc(text: cstring, args: ..any) -> cstring { 
+TextFormat :: proc(text: cstring, args: ..any) -> cstring {
 	@static buffers: [MAX_TEXTFORMAT_BUFFERS][MAX_TEXT_BUFFER_LENGTH]byte
 	@static index: u32
 	
@@ -1683,7 +1683,7 @@ TextFormat :: proc(text: cstring, args: ..any) -> cstring {
 }
 
 // Text formatting with variables (sprintf style) and allocates (must be freed with 'MemFree')
-TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring { 
+TextFormatAlloc :: proc(text: cstring, args: ..any) -> cstring {
 	str := fmt.tprintf(string(text), ..args)
 	return strings.clone_to_cstring(str, MemAllocator())
 }

+ 1 - 1
vendor/stb/truetype/stb_truetype.odin

@@ -568,7 +568,7 @@ foreign stbtt {
 	// some of the values for the IDs are below; for more see the truetype spec:
 	//     http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
 	//     http://www.microsoft.com/typography/otspec/name.htm
-	GetFontNameString :: proc(font: ^fontinfo, length: c.int, platformID: PLATFORM_ID, encodingID, languageID, nameID: c.int) -> cstring ---
+	GetFontNameString :: proc(font: ^fontinfo, length: ^c.int, platformID: PLATFORM_ID, encodingID, languageID, nameID: c.int) -> cstring ---
 }
 
 

+ 1 - 1
vendor/wgpu/wgpu.odin

@@ -1676,7 +1676,7 @@ when ODIN_OS != .JS {
 
 		GetVersion :: proc() -> u32 ---
 
-		RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: cstring) ---
+		RenderPassEncoderSetPushConstants :: proc(encoder: RenderPassEncoder, stages: ShaderStageFlags, offset: u32, sizeBytes: u32, data: rawptr) ---
 
 		RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
 		RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---