mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-03 05:08:14 +00:00
Merge branch 'master' into json-add-int-key-map-support
This commit is contained in:
@@ -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)
|
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 {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -525,12 +525,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
|
|||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
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
|
@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 {
|
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
|
// 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
|
@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)
|
new_size := index + len(args)
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
ok = true
|
ok = true
|
||||||
|
|||||||
@@ -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 {
|
if array == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,11 +95,11 @@ front_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
back :: 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]
|
return q.data[idx]
|
||||||
}
|
}
|
||||||
back_ptr :: proc(q: ^$Q/Queue($T)) -> ^T {
|
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]
|
return &q.data[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options :
|
|||||||
data := os.read_entire_file(path, allocator) or_return
|
data := os.read_entire_file(path, allocator) or_return
|
||||||
defer delete(data, allocator)
|
defer delete(data, allocator)
|
||||||
m, err = load_map_from_string(string(data), allocator, options)
|
m, err = load_map_from_string(string(data), allocator, options)
|
||||||
ok = err != nil
|
ok = err == nil
|
||||||
defer if !ok {
|
defer if !ok {
|
||||||
delete_map(m)
|
delete_map(m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -384,6 +384,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
|||||||
omitempty := false
|
omitempty := false
|
||||||
|
|
||||||
json_name, extra := json_name_from_tag_value(reflect.struct_tag_get(reflect.Struct_Tag(info.tags[i]), "json"))
|
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, ",") {
|
for flag in strings.split_iterator(&extra, ",") {
|
||||||
switch flag {
|
switch flag {
|
||||||
case "omitempty":
|
case "omitempty":
|
||||||
|
|||||||
@@ -599,6 +599,7 @@ Field_Flag :: enum {
|
|||||||
Subtype,
|
Subtype,
|
||||||
By_Ptr,
|
By_Ptr,
|
||||||
No_Broadcast,
|
No_Broadcast,
|
||||||
|
No_Capture,
|
||||||
|
|
||||||
Results,
|
Results,
|
||||||
Tags,
|
Tags,
|
||||||
@@ -619,6 +620,7 @@ field_flag_strings := [Field_Flag]string{
|
|||||||
.Subtype = "#subtype",
|
.Subtype = "#subtype",
|
||||||
.By_Ptr = "#by_ptr",
|
.By_Ptr = "#by_ptr",
|
||||||
.No_Broadcast = "#no_broadcast",
|
.No_Broadcast = "#no_broadcast",
|
||||||
|
.No_Capture = "#no_capture",
|
||||||
|
|
||||||
.Results = "results",
|
.Results = "results",
|
||||||
.Tags = "field tag",
|
.Tags = "field tag",
|
||||||
@@ -634,6 +636,7 @@ field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
|||||||
{"subtype", .Subtype},
|
{"subtype", .Subtype},
|
||||||
{"by_ptr", .By_Ptr},
|
{"by_ptr", .By_Ptr},
|
||||||
{"no_broadcast", .No_Broadcast},
|
{"no_broadcast", .No_Broadcast},
|
||||||
|
{"no_capture", .No_Capture},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
|||||||
src := buf8[:buf8_len]
|
src := buf8[:buf8_len]
|
||||||
|
|
||||||
ctrl_z := false
|
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]
|
x := src[i]
|
||||||
if x == 0x1a { // ctrl-z
|
if x == 0x1a { // ctrl-z
|
||||||
ctrl_z = true
|
ctrl_z = true
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ General_Error :: enum u32 {
|
|||||||
Invalid_File,
|
Invalid_File,
|
||||||
Invalid_Dir,
|
Invalid_Dir,
|
||||||
Invalid_Path,
|
Invalid_Path,
|
||||||
|
Invalid_Callback,
|
||||||
|
|
||||||
Pattern_Has_Separator,
|
Pattern_Has_Separator,
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ error_string :: proc(ferr: Error) -> string {
|
|||||||
case .Invalid_File: return "invalid file"
|
case .Invalid_File: return "invalid file"
|
||||||
case .Invalid_Dir: return "invalid directory"
|
case .Invalid_Dir: return "invalid directory"
|
||||||
case .Invalid_Path: return "invalid path"
|
case .Invalid_Path: return "invalid path"
|
||||||
|
case .Invalid_Callback: return "invalid callback"
|
||||||
case .Unsupported: return "unsupported"
|
case .Unsupported: return "unsupported"
|
||||||
case .Pattern_Has_Separator: return "pattern has separator"
|
case .Pattern_Has_Separator: return "pattern has separator"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import "core:time"
|
|||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
|
|
||||||
File :: struct {
|
File :: struct {
|
||||||
impl: _File,
|
impl: rawptr,
|
||||||
stream: io.Stream,
|
stream: io.Stream,
|
||||||
user_fstat: Fstat_Callback,
|
fstat: Fstat_Callback,
|
||||||
}
|
}
|
||||||
|
|
||||||
File_Mode :: distinct u32
|
File_Mode :: distinct u32
|
||||||
|
|||||||
+59
-55
@@ -6,14 +6,15 @@ import "core:time"
|
|||||||
import "base:runtime"
|
import "base:runtime"
|
||||||
import "core:sys/linux"
|
import "core:sys/linux"
|
||||||
|
|
||||||
_File :: struct {
|
File_Impl :: struct {
|
||||||
|
file: File,
|
||||||
name: string,
|
name: string,
|
||||||
fd: linux.Fd,
|
fd: linux.Fd,
|
||||||
allocator: runtime.Allocator,
|
allocator: runtime.Allocator,
|
||||||
}
|
}
|
||||||
|
|
||||||
_stdin : File = {
|
_stdin := File{
|
||||||
impl = {
|
impl = &File_Impl{
|
||||||
name = "/proc/self/fd/0",
|
name = "/proc/self/fd/0",
|
||||||
fd = 0,
|
fd = 0,
|
||||||
allocator = _file_allocator(),
|
allocator = _file_allocator(),
|
||||||
@@ -21,9 +22,10 @@ _stdin : File = {
|
|||||||
stream = {
|
stream = {
|
||||||
procedure = _file_stream_proc,
|
procedure = _file_stream_proc,
|
||||||
},
|
},
|
||||||
|
fstat = _fstat,
|
||||||
}
|
}
|
||||||
_stdout : File = {
|
_stdout := File{
|
||||||
impl = {
|
impl = &File_Impl{
|
||||||
name = "/proc/self/fd/1",
|
name = "/proc/self/fd/1",
|
||||||
fd = 1,
|
fd = 1,
|
||||||
allocator = _file_allocator(),
|
allocator = _file_allocator(),
|
||||||
@@ -31,9 +33,10 @@ _stdout : File = {
|
|||||||
stream = {
|
stream = {
|
||||||
procedure = _file_stream_proc,
|
procedure = _file_stream_proc,
|
||||||
},
|
},
|
||||||
|
fstat = _fstat,
|
||||||
}
|
}
|
||||||
_stderr : File = {
|
_stderr := File{
|
||||||
impl = {
|
impl = &File_Impl{
|
||||||
name = "/proc/self/fd/2",
|
name = "/proc/self/fd/2",
|
||||||
fd = 2,
|
fd = 2,
|
||||||
allocator = _file_allocator(),
|
allocator = _file_allocator(),
|
||||||
@@ -41,6 +44,7 @@ _stderr : File = {
|
|||||||
stream = {
|
stream = {
|
||||||
procedure = _file_stream_proc,
|
procedure = _file_stream_proc,
|
||||||
},
|
},
|
||||||
|
fstat = _fstat,
|
||||||
}
|
}
|
||||||
|
|
||||||
@init
|
@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 {
|
_new_file :: proc(fd: uintptr, _: string = "") -> ^File {
|
||||||
file := new(File, file_allocator())
|
impl := new(File_Impl, file_allocator())
|
||||||
_construct_file(file, fd, "")
|
impl.file.impl = impl
|
||||||
return file
|
impl.fd = linux.Fd(fd)
|
||||||
}
|
impl.allocator = file_allocator()
|
||||||
|
impl.name = _get_full_path(impl.fd, impl.allocator)
|
||||||
_construct_file :: proc(file: ^File, fd: uintptr, _: string = "") {
|
impl.file.stream = {
|
||||||
file^ = {
|
data = impl,
|
||||||
impl = {
|
procedure = _file_stream_proc,
|
||||||
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.file.fstat = _fstat
|
||||||
|
return &impl.file
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroy :: proc(f: ^File) -> Error {
|
_destroy :: proc(f: ^File_Impl) -> Error {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
delete(f.impl.name, f.impl.allocator)
|
a := f.allocator
|
||||||
free(f, f.impl.allocator)
|
delete(f.name, a)
|
||||||
|
free(f, a)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_close :: proc(f: ^File) -> Error {
|
_close :: proc(f: ^File_Impl) -> Error {
|
||||||
if f == nil {
|
if f == nil{
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
errno := linux.close(f.impl.fd)
|
errno := linux.close(f.fd)
|
||||||
if errno == .EBADF { // avoid possible double free
|
if errno == .EBADF { // avoid possible double free
|
||||||
return _get_platform_error(errno)
|
return _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
@@ -131,41 +130,41 @@ _close :: proc(f: ^File) -> Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fd :: proc(f: ^File) -> uintptr {
|
_fd :: proc(f: ^File) -> uintptr {
|
||||||
if f == nil {
|
if f == nil || f.impl == nil {
|
||||||
return ~uintptr(0)
|
return ~uintptr(0)
|
||||||
}
|
}
|
||||||
return uintptr(f.impl.fd)
|
impl := (^File_Impl)(f.impl)
|
||||||
|
return uintptr(impl.fd)
|
||||||
}
|
}
|
||||||
|
|
||||||
_name :: proc(f: ^File) -> string {
|
_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) {
|
_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||||
n, errno := linux.lseek(f.impl.fd, offset, linux.Seek_Whence(whence))
|
n, errno := linux.lseek(f.fd, offset, linux.Seek_Whence(whence))
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(errno)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_read :: proc(f: ^File, p: []byte) -> (i64, Error) {
|
_read :: proc(f: ^File_Impl, p: []byte) -> (i64, Error) {
|
||||||
if len(p) == 0 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n, errno := linux.read(f.impl.fd, p[:])
|
n, errno := linux.read(f.fd, p[:])
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(errno)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return i64(n), n == 0 ? io.Error.EOF : nil
|
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 {
|
if offset < 0 {
|
||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
|
n, errno := linux.pread(f.fd, p[:], offset)
|
||||||
n, errno := linux.pread(f.impl.fd, p[:], offset)
|
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(errno)
|
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
|
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 {
|
if len(p) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
n, errno := linux.write(f.impl.fd, p[:])
|
n, errno := linux.write(f.fd, p[:])
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(errno)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return i64(n), nil
|
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 {
|
if offset < 0 {
|
||||||
return 0, .Invalid_Offset
|
return 0, .Invalid_Offset
|
||||||
}
|
}
|
||||||
|
n, errno := linux.pwrite(f.fd, p[:], offset)
|
||||||
n, errno := linux.pwrite(f.impl.fd, p[:], offset)
|
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(errno)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
return i64(n), nil
|
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 = ---
|
s: linux.Stat = ---
|
||||||
errno := linux.fstat(f.impl.fd, &s)
|
errno := linux.fstat(f.fd, &s)
|
||||||
if errno != .NONE {
|
if errno != .NONE {
|
||||||
return -1, _get_platform_error(errno)
|
return -1, _get_platform_error(errno)
|
||||||
}
|
}
|
||||||
@@ -208,15 +206,17 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_sync :: proc(f: ^File) -> 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 {
|
_flush :: proc(f: ^File_Impl) -> Error {
|
||||||
return _get_platform_error(linux.fsync(f.impl.fd))
|
return _get_platform_error(linux.fsync(f.fd))
|
||||||
}
|
}
|
||||||
|
|
||||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
_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 {
|
_remove :: proc(name: string) -> Error {
|
||||||
@@ -292,7 +292,8 @@ _chdir :: proc(name: string) -> Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_fchdir :: proc(f: ^File) -> 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 {
|
_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 {
|
_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
|
// 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
|
// NOTE: will throw error without super user priviledges
|
||||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
_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 {
|
_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),
|
uint(mtime._nsec) % uint(time.Second),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return _get_platform_error(linux.utimensat(f.impl.fd, nil, ×[0], nil))
|
impl := (^File_Impl)(f.impl)
|
||||||
|
return _get_platform_error(linux.utimensat(impl.fd, nil, ×[0], nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
_exists :: proc(name: string) -> bool {
|
_exists :: proc(name: string) -> bool {
|
||||||
@@ -443,7 +447,7 @@ _read_entire_pseudo_file_cstring :: proc(name: cstring, allocator: runtime.Alloc
|
|||||||
|
|
||||||
@(private="package")
|
@(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) {
|
_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
|
ferr: Error
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Read:
|
case .Read:
|
||||||
|
|||||||
@@ -17,17 +17,19 @@ _ERROR_BAD_NETPATH :: 53
|
|||||||
MAX_RW :: 1<<30
|
MAX_RW :: 1<<30
|
||||||
|
|
||||||
|
|
||||||
_File_Kind :: enum u8 {
|
File_Impl_Kind :: enum u8 {
|
||||||
File,
|
File,
|
||||||
Console,
|
Console,
|
||||||
Pipe,
|
Pipe,
|
||||||
}
|
}
|
||||||
|
|
||||||
_File :: struct {
|
File_Impl :: struct {
|
||||||
|
file: File,
|
||||||
|
|
||||||
fd: rawptr,
|
fd: rawptr,
|
||||||
name: string,
|
name: string,
|
||||||
wname: win32.wstring,
|
wname: win32.wstring,
|
||||||
kind: _File_Kind,
|
kind: File_Impl_Kind,
|
||||||
|
|
||||||
allocator: runtime.Allocator,
|
allocator: runtime.Allocator,
|
||||||
|
|
||||||
@@ -75,11 +77,9 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
|
|||||||
access |= win32.FILE_APPEND_DATA
|
access |= win32.FILE_APPEND_DATA
|
||||||
}
|
}
|
||||||
share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
|
share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
|
||||||
sa: ^win32.SECURITY_ATTRIBUTES
|
sa := win32.SECURITY_ATTRIBUTES {
|
||||||
if .Close_On_Exec not_in flags {
|
nLength = size_of(win32.SECURITY_ATTRIBUTES),
|
||||||
sa = &win32.SECURITY_ATTRIBUTES{}
|
bInheritHandle = .Close_On_Exec not_in flags,
|
||||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
|
||||||
sa.bInheritHandle = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
create_mode: u32 = win32.OPEN_EXISTING
|
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.
|
// 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,
|
// If the file already exists, to make it akin to a *nix open call,
|
||||||
// the call preserves the existing permissions.
|
// 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 {
|
if h == win32.INVALID_HANDLE {
|
||||||
switch e := win32.GetLastError(); e {
|
switch e := win32.GetLastError(); e {
|
||||||
case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
|
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 {
|
if h == win32.INVALID_HANDLE {
|
||||||
return 0, _get_platform_error()
|
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) {
|
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
||||||
flags := flags if flags != nil else {.Read}
|
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
|
return _new_file(handle, name), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,75 +132,81 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
|||||||
if handle == INVALID_HANDLE {
|
if handle == INVALID_HANDLE {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
f := new(File, file_allocator())
|
impl := new(File_Impl, file_allocator())
|
||||||
|
impl.file.impl = impl
|
||||||
|
|
||||||
f.impl.allocator = file_allocator()
|
impl.allocator = file_allocator()
|
||||||
f.impl.fd = rawptr(handle)
|
impl.fd = rawptr(handle)
|
||||||
f.impl.name, _ = clone_string(name, f.impl.allocator)
|
impl.name, _ = clone_string(name, impl.allocator)
|
||||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
impl.wname = win32.utf8_to_wstring(name, impl.allocator)
|
||||||
|
|
||||||
handle := _handle(f)
|
handle := _handle(&impl.file)
|
||||||
kind := _File_Kind.File
|
kind := File_Impl_Kind.File
|
||||||
if m: u32; win32.GetConsoleMode(handle, &m) {
|
if m: u32; win32.GetConsoleMode(handle, &m) {
|
||||||
kind = .Console
|
kind = .Console
|
||||||
}
|
}
|
||||||
if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
|
if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
|
||||||
kind = .Pipe
|
kind = .Pipe
|
||||||
}
|
}
|
||||||
f.impl.kind = kind
|
impl.kind = kind
|
||||||
|
|
||||||
f.stream = {
|
impl.file.stream = {
|
||||||
data = f,
|
data = impl,
|
||||||
procedure = _file_stream_proc,
|
procedure = _file_stream_proc,
|
||||||
}
|
}
|
||||||
|
impl.file.fstat = _fstat
|
||||||
|
|
||||||
return f
|
return &impl.file
|
||||||
}
|
}
|
||||||
|
|
||||||
_fd :: proc(f: ^File) -> uintptr {
|
_fd :: proc(f: ^File) -> uintptr {
|
||||||
if f == nil {
|
if f == nil || f.impl == nil {
|
||||||
return INVALID_HANDLE
|
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 {
|
if f == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
a := f.impl.allocator
|
a := f.allocator
|
||||||
free(f.impl.wname, a)
|
err0 := free(f.wname, a)
|
||||||
delete(f.impl.name, a)
|
err1 := delete(f.name, a)
|
||||||
free(f, a)
|
err2 := free(f, a)
|
||||||
|
err0 or_return
|
||||||
|
err1 or_return
|
||||||
|
err2 or_return
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
_close :: proc(f: ^File) -> Error {
|
_close :: proc(f: ^File_Impl) -> Error {
|
||||||
if f == nil {
|
if f == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) {
|
if !win32.CloseHandle(win32.HANDLE(f.fd)) {
|
||||||
return .Closed
|
return .Closed
|
||||||
}
|
}
|
||||||
return _destroy(f)
|
return _destroy(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
_name :: proc(f: ^File) -> string {
|
_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) {
|
_seek :: proc(f: ^File_Impl, offset: i64, whence: io.Seek_From) -> (ret: i64, err: Error) {
|
||||||
handle := _handle(f)
|
handle := _handle(&f.file)
|
||||||
if handle == win32.INVALID_HANDLE {
|
if handle == win32.INVALID_HANDLE {
|
||||||
return 0, .Invalid_File
|
return 0, .Invalid_File
|
||||||
}
|
}
|
||||||
if f.impl.kind == .Pipe {
|
|
||||||
|
if f.kind == .Pipe {
|
||||||
return 0, .Invalid_File
|
return 0, .Invalid_File
|
||||||
}
|
}
|
||||||
|
|
||||||
sync.guard(&f.impl.rw_mutex)
|
sync.guard(&f.rw_mutex)
|
||||||
|
|
||||||
w: u32
|
w: u32
|
||||||
switch whence {
|
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
|
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) {
|
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||||
if len(b) == 0 {
|
if len(b) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
@@ -269,18 +275,18 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
handle := _handle(f)
|
handle := _handle(&f.file)
|
||||||
|
|
||||||
single_read_length: win32.DWORD
|
single_read_length: win32.DWORD
|
||||||
total_read: int
|
total_read: int
|
||||||
length := len(p)
|
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)
|
to_read := min(win32.DWORD(length), MAX_RW)
|
||||||
ok: win32.BOOL
|
ok: win32.BOOL
|
||||||
if f.impl.kind == .Console {
|
if f.kind == .Console {
|
||||||
n, cerr := read_console(handle, p[total_read:][:to_read])
|
n, cerr := read_console(handle, p[total_read:][:to_read])
|
||||||
total_read += n
|
total_read += n
|
||||||
if cerr != nil {
|
if cerr != nil {
|
||||||
@@ -300,15 +306,15 @@ _read :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
|
|||||||
return i64(total_read), err
|
return i64(total_read), err
|
||||||
}
|
}
|
||||||
|
|
||||||
_read_at :: proc(f: ^File, p: []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, data: []byte, offset: i64) -> (n: i64, err: Error) {
|
pread :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
|
||||||
buf := data
|
buf := data
|
||||||
if len(buf) > MAX_RW {
|
if len(buf) > MAX_RW {
|
||||||
buf = buf[:MAX_RW]
|
buf = buf[:MAX_RW]
|
||||||
|
|
||||||
}
|
}
|
||||||
curr_offset := seek(f, offset, .Current) or_return
|
curr_offset := _seek(f, offset, .Current) or_return
|
||||||
defer seek(f, curr_offset, .Start)
|
defer _seek(f, curr_offset, .Start)
|
||||||
|
|
||||||
o := win32.OVERLAPPED{
|
o := win32.OVERLAPPED{
|
||||||
OffsetHigh = u32(offset>>32),
|
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
|
// TODO(bill): Determine the correct behaviour for consoles
|
||||||
|
|
||||||
h := _handle(f)
|
h := _handle(&f.file)
|
||||||
done: win32.DWORD
|
done: win32.DWORD
|
||||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||||
err = _get_platform_error()
|
err = _get_platform_error()
|
||||||
@@ -327,7 +333,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sync.guard(&f.impl.p_mutex)
|
sync.guard(&f.p_mutex)
|
||||||
|
|
||||||
p, offset := p, offset
|
p, offset := p, offset
|
||||||
for len(p) > 0 {
|
for len(p) > 0 {
|
||||||
@@ -339,7 +345,7 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
|||||||
return
|
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 {
|
if len(p) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -348,9 +354,9 @@ _write :: proc(f: ^File, p: []byte) -> (n: i64, err: Error) {
|
|||||||
total_write: i64
|
total_write: i64
|
||||||
length := i64(len(p))
|
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 {
|
for total_write < length {
|
||||||
remaining := length - total_write
|
remaining := length - total_write
|
||||||
to_write := win32.DWORD(min(i32(remaining), MAX_RW))
|
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
|
return i64(total_write), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_write_at :: proc(f: ^File, p: []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, data: []byte, offset: i64) -> (n: i64, err: Error) {
|
pwrite :: proc(f: ^File_Impl, data: []byte, offset: i64) -> (n: i64, err: Error) {
|
||||||
buf := data
|
buf := data
|
||||||
if len(buf) > MAX_RW {
|
if len(buf) > MAX_RW {
|
||||||
buf = buf[:MAX_RW]
|
buf = buf[:MAX_RW]
|
||||||
|
|
||||||
}
|
}
|
||||||
curr_offset := seek(f, offset, .Current) or_return
|
curr_offset := _seek(f, offset, .Current) or_return
|
||||||
defer seek(f, curr_offset, .Start)
|
defer _seek(f, curr_offset, .Start)
|
||||||
|
|
||||||
o := win32.OVERLAPPED{
|
o := win32.OVERLAPPED{
|
||||||
OffsetHigh = u32(offset>>32),
|
OffsetHigh = u32(offset>>32),
|
||||||
Offset = u32(offset),
|
Offset = u32(offset),
|
||||||
}
|
}
|
||||||
|
|
||||||
h := _handle(f)
|
h := _handle(&f.file)
|
||||||
done: win32.DWORD
|
done: win32.DWORD
|
||||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||||
err = _get_platform_error()
|
err = _get_platform_error()
|
||||||
@@ -391,7 +397,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: i64, err: Error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sync.guard(&f.impl.p_mutex)
|
sync.guard(&f.p_mutex)
|
||||||
p, offset := p, offset
|
p, offset := p, offset
|
||||||
for len(p) > 0 {
|
for len(p) > 0 {
|
||||||
m := pwrite(f, p, offset) or_return
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
_file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) {
|
||||||
length: win32.LARGE_INTEGER
|
length: win32.LARGE_INTEGER
|
||||||
if f.impl.kind == .Pipe {
|
if f.kind == .Pipe {
|
||||||
return 0, .No_Size
|
return 0, .No_Size
|
||||||
}
|
}
|
||||||
handle := _handle(f)
|
handle := _handle(&f.file)
|
||||||
if !win32.GetFileSizeEx(handle, &length) {
|
if !win32.GetFileSizeEx(handle, &length) {
|
||||||
err = _get_platform_error()
|
err = _get_platform_error()
|
||||||
}
|
}
|
||||||
@@ -417,11 +423,14 @@ _file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
|||||||
|
|
||||||
|
|
||||||
_sync :: proc(f: ^File) -> 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 {
|
_flush :: proc(f: ^File_Impl) -> Error {
|
||||||
handle := _handle(f)
|
handle := _handle(&f.file)
|
||||||
if !win32.FlushFileBuffers(handle) {
|
if !win32.FlushFileBuffers(handle) {
|
||||||
return _get_platform_error()
|
return _get_platform_error()
|
||||||
}
|
}
|
||||||
@@ -429,7 +438,7 @@ _flush :: proc(f: ^File) -> Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||||
if f == nil {
|
if f == nil || f.impl == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
curr_off := seek(f, 0, .Current) or_return
|
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 {
|
_fchdir :: proc(f: ^File) -> Error {
|
||||||
if f == nil {
|
if f == nil || f.impl == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !win32.SetCurrentDirectoryW(f.impl.wname) {
|
impl := (^File_Impl)(f.impl)
|
||||||
|
if !win32.SetCurrentDirectoryW(impl.wname) {
|
||||||
return _get_platform_error()
|
return _get_platform_error()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||||
if f == nil {
|
if f == nil || f.impl == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||||
@@ -681,7 +691,7 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
|||||||
return _fchtimes(f, atime, mtime)
|
return _fchtimes(f, atime, mtime)
|
||||||
}
|
}
|
||||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||||
if f == nil {
|
if f == nil || f.impl == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||||
@@ -737,7 +747,7 @@ _is_dir :: proc(path: string) -> bool {
|
|||||||
|
|
||||||
@(private="package")
|
@(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) {
|
_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
|
ferr: Error
|
||||||
switch mode {
|
switch mode {
|
||||||
case .Read:
|
case .Read:
|
||||||
|
|||||||
@@ -29,10 +29,12 @@ file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) {
|
|||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||||
if f != nil && f.user_fstat != nil {
|
if f == nil {
|
||||||
return f->user_fstat(allocator)
|
return {}, nil
|
||||||
|
} else if f.fstat != nil {
|
||||||
|
return f->fstat(allocator)
|
||||||
}
|
}
|
||||||
return _fstat(f, allocator)
|
return {}, .Invalid_Callback
|
||||||
}
|
}
|
||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import "core:sys/linux"
|
|||||||
import "core:path/filepath"
|
import "core:path/filepath"
|
||||||
|
|
||||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
_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) {
|
_fstat_internal :: proc(fd: linux.Fd, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import "core:strings"
|
|||||||
import win32 "core:sys/windows"
|
import win32 "core:sys/windows"
|
||||||
|
|
||||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
_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
|
return {}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +122,7 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
|||||||
|
|
||||||
|
|
||||||
_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
|
_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
|
||||||
if f == nil || f.impl.fd == nil {
|
if f == nil {
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
h := _handle(f)
|
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) {
|
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
||||||
if f == nil || f.impl.fd == nil {
|
if f == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
h := _handle(f)
|
h := _handle(f)
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ foreign advapi32 {
|
|||||||
OpenAsSelf: BOOL,
|
OpenAsSelf: BOOL,
|
||||||
TokenHandle: ^HANDLE) -> 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 ---
|
CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD ---
|
||||||
CryptGenRandom :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD ---
|
CryptGenRandom :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD ---
|
||||||
CryptReleaseContext :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD ---
|
CryptReleaseContext :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD ---
|
||||||
@@ -44,7 +52,17 @@ foreign advapi32 {
|
|||||||
cbSid: ^DWORD,
|
cbSid: ^DWORD,
|
||||||
ReferencedDomainName: wstring,
|
ReferencedDomainName: wstring,
|
||||||
cchReferencedDomainName: ^DWORD,
|
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 ---
|
) -> BOOL ---
|
||||||
|
|
||||||
CreateProcessWithLogonW :: proc(
|
CreateProcessWithLogonW :: proc(
|
||||||
@@ -164,3 +182,156 @@ foreign advapi32 {
|
|||||||
AccessStatus: LPBOOL,
|
AccessStatus: LPBOOL,
|
||||||
) -> BOOL ---
|
) -> 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,
|
||||||
|
}
|
||||||
|
|||||||
@@ -233,6 +233,12 @@ foreign kernel32 {
|
|||||||
QueryPerformanceCounter :: proc(lpPerformanceCount: ^LARGE_INTEGER) -> BOOL ---
|
QueryPerformanceCounter :: proc(lpPerformanceCount: ^LARGE_INTEGER) -> BOOL ---
|
||||||
GetExitCodeProcess :: proc(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL ---
|
GetExitCodeProcess :: proc(hProcess: HANDLE, lpExitCode: LPDWORD) -> BOOL ---
|
||||||
TerminateProcess :: proc(hProcess: HANDLE, uExitCode: UINT) -> 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(
|
CreateProcessW :: proc(
|
||||||
lpApplicationName: LPCWSTR,
|
lpApplicationName: LPCWSTR,
|
||||||
lpCommandLine: LPWSTR,
|
lpCommandLine: LPWSTR,
|
||||||
@@ -543,6 +549,45 @@ THREAD_PRIORITY_IDLE :: THREAD_BASE_PRIORITY_IDLE
|
|||||||
THREAD_MODE_BACKGROUND_BEGIN :: 0x00010000
|
THREAD_MODE_BACKGROUND_BEGIN :: 0x00010000
|
||||||
THREAD_MODE_BACKGROUND_END :: 0x00020000
|
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_FAIL_IF_EXISTS :: 0x00000001
|
||||||
COPY_FILE_RESTARTABLE :: 0x00000002
|
COPY_FILE_RESTARTABLE :: 0x00000002
|
||||||
COPY_FILE_OPEN_SOURCE_FOR_WRITE :: 0x00000004
|
COPY_FILE_OPEN_SOURCE_FOR_WRITE :: 0x00000004
|
||||||
|
|||||||
+135
-18
@@ -64,6 +64,7 @@ LONG_PTR :: int
|
|||||||
UINT_PTR :: uintptr
|
UINT_PTR :: uintptr
|
||||||
ULONG :: c_ulong
|
ULONG :: c_ulong
|
||||||
ULONGLONG :: c_ulonglong
|
ULONGLONG :: c_ulonglong
|
||||||
|
LONGLONG :: c_longlong
|
||||||
UCHAR :: BYTE
|
UCHAR :: BYTE
|
||||||
NTSTATUS :: c.long
|
NTSTATUS :: c.long
|
||||||
COLORREF :: DWORD
|
COLORREF :: DWORD
|
||||||
@@ -2145,6 +2146,7 @@ SECURITY_IMPERSONATION_LEVEL :: enum {
|
|||||||
SECURITY_INFORMATION :: DWORD
|
SECURITY_INFORMATION :: DWORD
|
||||||
ANYSIZE_ARRAY :: 1
|
ANYSIZE_ARRAY :: 1
|
||||||
|
|
||||||
|
PLUID_AND_ATTRIBUTES :: ^LUID_AND_ATTRIBUTES
|
||||||
LUID_AND_ATTRIBUTES :: struct {
|
LUID_AND_ATTRIBUTES :: struct {
|
||||||
Luid: LUID,
|
Luid: LUID,
|
||||||
Attributes: DWORD,
|
Attributes: DWORD,
|
||||||
@@ -2570,7 +2572,139 @@ EXCEPTION_RECORD :: struct {
|
|||||||
ExceptionInformation: [EXCEPTION_MAXIMUM_PARAMETERS]LPVOID,
|
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 {
|
EXCEPTION_POINTERS :: struct {
|
||||||
ExceptionRecord: ^EXCEPTION_RECORD,
|
ExceptionRecord: ^EXCEPTION_RECORD,
|
||||||
@@ -2733,23 +2867,6 @@ PROFILEINFOW :: struct {
|
|||||||
hProfile: HANDLE,
|
hProfile: HANDLE,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used in LookupAccountNameW
|
|
||||||
SID_NAME_USE :: distinct DWORD
|
|
||||||
|
|
||||||
SID_TYPE :: enum SID_NAME_USE {
|
|
||||||
User = 1,
|
|
||||||
Group,
|
|
||||||
Domain,
|
|
||||||
Alias,
|
|
||||||
WellKnownGroup,
|
|
||||||
DeletedAccount,
|
|
||||||
Invalid,
|
|
||||||
Unknown,
|
|
||||||
Computer,
|
|
||||||
Label,
|
|
||||||
LogonSession,
|
|
||||||
}
|
|
||||||
|
|
||||||
SECURITY_MAX_SID_SIZE :: 68
|
SECURITY_MAX_SID_SIZE :: 68
|
||||||
|
|
||||||
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
|
// https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid
|
||||||
|
|||||||
@@ -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)
|
username_w := utf8_to_utf16(username, context.temp_allocator)
|
||||||
cbsid: DWORD
|
cbsid: DWORD
|
||||||
computer_name_size: DWORD
|
computer_name_size: DWORD
|
||||||
pe_use := SID_TYPE.User
|
pe_use := SID_NAME_USE.SidTypeUser
|
||||||
|
|
||||||
res := LookupAccountNameW(
|
res := LookupAccountNameW(
|
||||||
nil, // Look on this computer first
|
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)
|
username_w := utf8_to_utf16(username, context.temp_allocator)
|
||||||
cbsid: DWORD
|
cbsid: DWORD
|
||||||
computer_name_size: DWORD
|
computer_name_size: DWORD
|
||||||
pe_use := SID_TYPE.User
|
pe_use := SID_NAME_USE.SidTypeUser
|
||||||
|
|
||||||
res := LookupAccountNameW(
|
res := LookupAccountNameW(
|
||||||
nil, // Look on this computer first
|
nil, // Look on this computer first
|
||||||
|
|||||||
@@ -1649,7 +1649,11 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
|||||||
if (!bc->custom_optimization_level) {
|
if (!bc->custom_optimization_level) {
|
||||||
// NOTE(bill): when building with `-debug` but not specifying an 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
|
// 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);
|
bc->optimization_level = gb_clamp(bc->optimization_level, -1, 3);
|
||||||
|
|||||||
@@ -1869,5 +1869,14 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
|||||||
|
|
||||||
add_deps_from_child_to_parent(decl);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+25
-6
@@ -6033,6 +6033,22 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
|||||||
|
|
||||||
Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
|
Entity *vt = pt->params->Tuple.variables[pt->variadic_index];
|
||||||
o.type = vt->type;
|
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 {
|
} else {
|
||||||
dummy_argument_count += 1;
|
dummy_argument_count += 1;
|
||||||
o.type = t_untyped_nil;
|
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.
|
// NOTE: Due to restrictions in LLVM you can not inline calls with a superset of features.
|
||||||
if (is_call_inlined) {
|
if (is_call_inlined) {
|
||||||
GB_ASSERT(c->curr_proc_decl);
|
if (c->curr_proc_decl == nullptr) {
|
||||||
GB_ASSERT(c->curr_proc_decl->entity);
|
error(call, "Calling a '#force_inline' procedure that enables target features is not allowed at file scope");
|
||||||
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
|
} else {
|
||||||
String scope_features = c->curr_proc_decl->entity->type->Proc.enable_target_feature;
|
GB_ASSERT(c->curr_proc_decl->entity);
|
||||||
if (!check_target_feature_is_superset_of(scope_features, pt->Proc.enable_target_feature, &invalid)) {
|
GB_ASSERT(c->curr_proc_decl->entity->type->kind == Type_Proc);
|
||||||
error(call, "Inlined procedure enables target feature '%.*s', this requires the calling procedure to at least enable the same feature", LIT(invalid));
|
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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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");
|
error(name, "'#by_ptr' can only be applied to variable fields");
|
||||||
p->flags &= ~FieldFlag_by_ptr;
|
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 = alloc_entity_type_name(scope, name->Ident.token, type, EntityState_Resolved);
|
||||||
param->TypeName.is_type_alias = true;
|
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
|
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 (is_poly_name) {
|
||||||
if (p->flags&FieldFlag_no_alias) {
|
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");
|
error(name, "'#by_ptr' can only be applied to variable fields");
|
||||||
p->flags &= ~FieldFlag_by_ptr;
|
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])) {
|
if (!is_type_polymorphic(type) && check_constant_parameter_value(type, params[i])) {
|
||||||
// failed
|
// failed
|
||||||
@@ -2091,6 +2122,8 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
|||||||
param->flags |= EntityFlag_Ellipsis;
|
param->flags |= EntityFlag_Ellipsis;
|
||||||
if (is_c_vararg) {
|
if (is_c_vararg) {
|
||||||
param->flags |= EntityFlag_CVarArg;
|
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) {
|
if (p->flags&FieldFlag_by_ptr) {
|
||||||
param->flags |= EntityFlag_ByPtr;
|
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
|
param->state = EntityState_Resolved; // NOTE(bill): This should have be resolved whilst determining it
|
||||||
add_entity(ctx, scope, name, param);
|
add_entity(ctx, scope, name, param);
|
||||||
|
|||||||
@@ -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->deps, 0);
|
||||||
ptr_set_init(&d->type_info_deps, 0);
|
ptr_set_init(&d->type_info_deps, 0);
|
||||||
d->labels.allocator = heap_allocator();
|
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) {
|
gb_internal DeclInfo *make_decl_info(Scope *scope, DeclInfo *parent) {
|
||||||
|
|||||||
@@ -181,6 +181,11 @@ char const *ProcCheckedState_strings[ProcCheckedState_COUNT] {
|
|||||||
"Checked",
|
"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
|
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
|
||||||
struct DeclInfo {
|
struct DeclInfo {
|
||||||
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
||||||
@@ -219,6 +224,10 @@ struct DeclInfo {
|
|||||||
|
|
||||||
Array<BlockLabel> labels;
|
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
|
// 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;
|
struct lbModule *code_gen_module;
|
||||||
};
|
};
|
||||||
|
|||||||
+1
-1
@@ -45,7 +45,7 @@ enum EntityFlag : u64 {
|
|||||||
EntityFlag_Value = 1ull<<11,
|
EntityFlag_Value = 1ull<<11,
|
||||||
EntityFlag_BitFieldField = 1ull<<12,
|
EntityFlag_BitFieldField = 1ull<<12,
|
||||||
|
|
||||||
|
EntityFlag_NoCapture = 1ull<<13, // #no_capture
|
||||||
|
|
||||||
EntityFlag_PolyConst = 1ull<<15,
|
EntityFlag_PolyConst = 1ull<<15,
|
||||||
EntityFlag_NotExported = 1ull<<16,
|
EntityFlag_NotExported = 1ull<<16,
|
||||||
|
|||||||
+10
-4
@@ -15,6 +15,7 @@ struct lbArgType {
|
|||||||
LLVMAttributeRef align_attribute; // Optional
|
LLVMAttributeRef align_attribute; // Optional
|
||||||
i64 byval_alignment;
|
i64 byval_alignment;
|
||||||
bool is_byval;
|
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);
|
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->multiple_return_original_type) {
|
||||||
if (ft->original_arg_count <= i) {
|
if (ft->original_arg_count <= i) {
|
||||||
LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
|
LLVMAddAttributeAtIndex(fn, arg_index+1, noalias_attr);
|
||||||
@@ -645,10 +651,10 @@ namespace lbAbiAmd64SysV {
|
|||||||
if (is_mem_cls(cls, attribute_kind)) {
|
if (is_mem_cls(cls, attribute_kind)) {
|
||||||
LLVMAttributeRef attribute = nullptr;
|
LLVMAttributeRef attribute = nullptr;
|
||||||
if (attribute_kind == Amd64TypeAttribute_ByVal) {
|
if (attribute_kind == Amd64TypeAttribute_ByVal) {
|
||||||
// if (!is_calling_convention_odin(calling_convention)) {
|
if (is_calling_convention_odin(calling_convention)) {
|
||||||
return lb_arg_type_indirect_byval(c, type);
|
return lb_arg_type_indirect(type, attribute);
|
||||||
// }
|
}
|
||||||
// attribute = nullptr;
|
return lb_arg_type_indirect_byval(c, type);
|
||||||
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
|
} else if (attribute_kind == Amd64TypeAttribute_StructRect) {
|
||||||
attribute = lb_create_enum_attribute_with_type(c, "sret", type);
|
attribute = lb_create_enum_attribute_with_type(c, "sret", type);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1570,6 +1570,7 @@ gb_internal WORKER_TASK_PROC(lb_llvm_module_pass_worker_proc) {
|
|||||||
|
|
||||||
switch (build_context.optimization_level) {
|
switch (build_context.optimization_level) {
|
||||||
case -1:
|
case -1:
|
||||||
|
array_add(&passes, "function(annotation-remarks)");
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
array_add(&passes, "always-inline");
|
array_add(&passes, "always-inline");
|
||||||
|
|||||||
@@ -296,6 +296,11 @@ enum lbProcedureFlag : u32 {
|
|||||||
lbProcedureFlag_DebugAllocaCopy = 1<<1,
|
lbProcedureFlag_DebugAllocaCopy = 1<<1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct lbVariadicReuseSlices {
|
||||||
|
Type *slice_type;
|
||||||
|
lbAddr slice_addr;
|
||||||
|
};
|
||||||
|
|
||||||
struct lbProcedure {
|
struct lbProcedure {
|
||||||
u32 flags;
|
u32 flags;
|
||||||
u16 state_flags;
|
u16 state_flags;
|
||||||
@@ -336,8 +341,10 @@ struct lbProcedure {
|
|||||||
bool in_multi_assignment;
|
bool in_multi_assignment;
|
||||||
Array<LLVMValueRef> raw_input_parameters;
|
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;
|
Ast *curr_stmt;
|
||||||
|
|
||||||
Array<Scope *> scope_stack;
|
Array<Scope *> scope_stack;
|
||||||
|
|||||||
@@ -253,6 +253,11 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i
|
|||||||
if (e->flags&EntityFlag_NoAlias) {
|
if (e->flags&EntityFlag_NoAlias) {
|
||||||
lb_add_proc_attribute_at_index(p, offset+parameter_index, "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;
|
parameter_index += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -517,6 +522,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) {
|
|||||||
lb_start_block(p, p->entry_block);
|
lb_start_block(p, p->entry_block);
|
||||||
|
|
||||||
map_init(&p->direct_parameters);
|
map_init(&p->direct_parameters);
|
||||||
|
p->variadic_reuses.allocator = heap_allocator();
|
||||||
|
|
||||||
GB_ASSERT(p->type != nullptr);
|
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;
|
isize slice_len = var_args.count;
|
||||||
if (slice_len > 0) {
|
if (slice_len > 0) {
|
||||||
lbAddr slice = lb_add_local_generated(p, slice_type, true);
|
lbAddr slice = {};
|
||||||
lbAddr base_array = lb_add_local_generated(p, alloc_type_array(elem_type, slice_len), true);
|
|
||||||
|
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++) {
|
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];
|
lbValue var_arg = var_args[i];
|
||||||
var_arg = lb_emit_conv(p, var_arg, elem_type);
|
var_arg = lb_emit_conv(p, var_arg, elem_type);
|
||||||
lb_emit_store(p, addr, var_arg);
|
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);
|
lbValue len = lb_const_int(p->module, t_int, slice_len);
|
||||||
lb_fill_slice(p, slice, base_elem, len);
|
lb_fill_slice(p, slice, base_elem, len);
|
||||||
|
|
||||||
|
|||||||
@@ -399,6 +399,8 @@ enum BuildFlagKind {
|
|||||||
|
|
||||||
BuildFlag_Sanitize,
|
BuildFlag_Sanitize,
|
||||||
|
|
||||||
|
BuildFlag_FastBuild,
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
BuildFlag_IgnoreVsSearch,
|
BuildFlag_IgnoreVsSearch,
|
||||||
BuildFlag_ResourceFile,
|
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_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)
|
#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_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
|
||||||
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
|
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
|
||||||
@@ -1441,6 +1446,13 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
|||||||
}
|
}
|
||||||
break;
|
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)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
case BuildFlag_IgnoreVsSearch: {
|
case BuildFlag_IgnoreVsSearch: {
|
||||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||||
|
|||||||
@@ -4014,6 +4014,7 @@ struct ParseFieldPrefixMapping {
|
|||||||
gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
|
gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
|
||||||
{str_lit("using"), Token_using, FieldFlag_using},
|
{str_lit("using"), Token_using, FieldFlag_using},
|
||||||
{str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
|
{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("c_vararg"), Token_Hash, FieldFlag_c_vararg},
|
||||||
{str_lit("const"), Token_Hash, FieldFlag_const},
|
{str_lit("const"), Token_Hash, FieldFlag_const},
|
||||||
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
||||||
|
|||||||
+7
-2
@@ -331,8 +331,10 @@ enum FieldFlag : u32 {
|
|||||||
FieldFlag_by_ptr = 1<<8,
|
FieldFlag_by_ptr = 1<<8,
|
||||||
FieldFlag_no_broadcast = 1<<9, // disallow array programming
|
FieldFlag_no_broadcast = 1<<9, // disallow array programming
|
||||||
|
|
||||||
|
FieldFlag_no_capture = 1<<11,
|
||||||
|
|
||||||
// Internal use by the parser only
|
// Internal use by the parser only
|
||||||
FieldFlag_Tags = 1<<10,
|
FieldFlag_Tags = 1<<15,
|
||||||
FieldFlag_Results = 1<<16,
|
FieldFlag_Results = 1<<16,
|
||||||
|
|
||||||
|
|
||||||
@@ -340,7 +342,10 @@ enum FieldFlag : u32 {
|
|||||||
FieldFlag_Invalid = 1u<<31,
|
FieldFlag_Invalid = 1u<<31,
|
||||||
|
|
||||||
// Parameter List Restrictions
|
// 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,
|
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+85
-38
@@ -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 bool thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data);
|
||||||
gb_internal void thread_pool_wait(ThreadPool *pool);
|
gb_internal void thread_pool_wait(ThreadPool *pool);
|
||||||
|
|
||||||
|
enum GrabState {
|
||||||
|
Grab_Success = 0,
|
||||||
|
Grab_Empty = 1,
|
||||||
|
Grab_Failed = 2,
|
||||||
|
};
|
||||||
|
|
||||||
struct ThreadPool {
|
struct ThreadPool {
|
||||||
gbAllocator threads_allocator;
|
gbAllocator threads_allocator;
|
||||||
Slice<Thread> threads;
|
Slice<Thread> threads;
|
||||||
std::atomic<bool> running;
|
std::atomic<bool> running;
|
||||||
|
|
||||||
Futex tasks_available;
|
Futex tasks_available;
|
||||||
|
|
||||||
Futex tasks_left;
|
Futex tasks_left;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,7 +51,7 @@ gb_internal void thread_pool_destroy(ThreadPool *pool) {
|
|||||||
|
|
||||||
for_array_off(i, 1, pool->threads) {
|
for_array_off(i, 1, pool->threads) {
|
||||||
Thread *t = &pool->threads[i];
|
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);
|
futex_broadcast(&pool->tasks_available);
|
||||||
thread_join_and_destroy(t);
|
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);
|
gb_free(pool->threads_allocator, pool->threads.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void thread_pool_queue_push(Thread *thread, WorkerTask task) {
|
void thread_pool_queue_push(Thread *thread, WorkerTask task) {
|
||||||
u64 capture;
|
isize bot = thread->queue.bottom.load(std::memory_order_relaxed);
|
||||||
u64 new_capture;
|
isize top = thread->queue.top.load(std::memory_order_acquire);
|
||||||
do {
|
TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
|
||||||
capture = thread->head_and_tail.load();
|
|
||||||
|
|
||||||
u64 mask = thread->capacity - 1;
|
isize size = bot - top;
|
||||||
u64 head = (capture >> 32) & mask;
|
if (size > (cur_ring->size - 1)) {
|
||||||
u64 tail = ((u32)capture) & mask;
|
// Queue is full
|
||||||
|
thread->queue.ring = task_ring_grow(thread->queue.ring, bot, top);
|
||||||
|
cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
u64 new_head = (head + 1) & mask;
|
cur_ring->buffer[bot % cur_ring->size] = task;
|
||||||
GB_ASSERT_MSG(new_head != tail, "Thread Queue Full!");
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
|
thread->queue.bottom.store(bot + 1, 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));
|
|
||||||
|
|
||||||
thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
|
thread->pool->tasks_left.fetch_add(1, std::memory_order_release);
|
||||||
thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
|
thread->pool->tasks_available.fetch_add(1, std::memory_order_relaxed);
|
||||||
futex_broadcast(&thread->pool->tasks_available);
|
futex_broadcast(&thread->pool->tasks_available);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool thread_pool_queue_pop(Thread *thread, WorkerTask *task) {
|
GrabState thread_pool_queue_take(Thread *thread, WorkerTask *task) {
|
||||||
u64 capture;
|
isize bot = thread->queue.bottom.load(std::memory_order_relaxed) - 1;
|
||||||
u64 new_capture;
|
TaskRingBuffer *cur_ring = thread->queue.ring.load(std::memory_order_relaxed);
|
||||||
do {
|
thread->queue.bottom.store(bot, std::memory_order_relaxed);
|
||||||
capture = thread->head_and_tail.load(std::memory_order_acquire);
|
std::atomic_thread_fence(std::memory_order_seq_cst);
|
||||||
|
|
||||||
u64 mask = thread->capacity - 1;
|
isize top = thread->queue.top.load(std::memory_order_relaxed);
|
||||||
u64 head = (capture >> 32) & mask;
|
if (top <= bot) {
|
||||||
u64 tail = ((u32)capture) & mask;
|
|
||||||
|
|
||||||
u64 new_tail = (tail + 1) & mask;
|
// Queue is not empty
|
||||||
if (tail == head) {
|
*task = cur_ring->buffer[bot % cur_ring->size];
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// We got a task without hitting a race
|
||||||
*task = thread->queue[tail];
|
return Grab_Success;
|
||||||
|
} else {
|
||||||
|
// Queue is empty
|
||||||
|
thread->queue.bottom.store(bot + 1, std::memory_order_relaxed);
|
||||||
|
return Grab_Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
new_capture = (head << 32) | new_tail;
|
GrabState thread_pool_queue_steal(Thread *thread, WorkerTask *task) {
|
||||||
} while (!thread->head_and_tail.compare_exchange_weak(capture, new_capture, std::memory_order_release));
|
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);
|
||||||
|
|
||||||
return true;
|
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) {
|
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)) {
|
while (pool->tasks_left.load(std::memory_order_acquire)) {
|
||||||
// if we've got tasks on our queue, run them
|
// 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);
|
task.do_work(task.data);
|
||||||
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// is this mem-barriered enough?
|
// is this mem-barriered enough?
|
||||||
// This *must* be executed in this order, so the futex wakes immediately
|
// This *must* be executed in this order, so the futex wakes immediately
|
||||||
// if rem_tasks has changed since we checked last, otherwise the program
|
// 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;
|
usize finished_tasks = 0;
|
||||||
i32 state;
|
i32 state;
|
||||||
|
|
||||||
while (thread_pool_queue_pop(current_thread, &task)) {
|
while (!thread_pool_queue_take(current_thread, &task)) {
|
||||||
task.do_work(task.data);
|
task.do_work(task.data);
|
||||||
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
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];
|
Thread *thread = &pool->threads.data[idx];
|
||||||
WorkerTask task;
|
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);
|
task.do_work(task.data);
|
||||||
pool->tasks_left.fetch_sub(1, std::memory_order_release);
|
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);
|
futex_signal(&pool->tasks_left);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*fallthrough*/
|
||||||
|
case Grab_Failed:
|
||||||
goto main_loop_continue;
|
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
|
// 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);
|
state = pool->tasks_available.load(std::memory_order_acquire);
|
||||||
|
if (!pool->running) { break; }
|
||||||
futex_wait(&pool->tasks_available, state);
|
futex_wait(&pool->tasks_available, state);
|
||||||
|
|
||||||
main_loop_continue:;
|
main_loop_continue:;
|
||||||
|
|||||||
+29
-10
@@ -46,6 +46,18 @@ typedef struct WorkerTask {
|
|||||||
void *data;
|
void *data;
|
||||||
} WorkerTask;
|
} 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 {
|
struct Thread {
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
void *win32_handle;
|
void *win32_handle;
|
||||||
@@ -54,12 +66,9 @@ struct Thread {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
isize idx;
|
isize idx;
|
||||||
|
isize stack_size;
|
||||||
|
|
||||||
WorkerTask *queue;
|
struct TaskQueue queue;
|
||||||
size_t capacity;
|
|
||||||
std::atomic<uint64_t> head_and_tail;
|
|
||||||
|
|
||||||
isize stack_size;
|
|
||||||
struct ThreadPool *pool;
|
struct ThreadPool *pool;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -551,6 +560,18 @@ gb_internal void *internal_thread_proc(void *arg) {
|
|||||||
}
|
}
|
||||||
#endif
|
#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_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
|
||||||
gb_zero_item(t);
|
gb_zero_item(t);
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
@@ -559,14 +580,12 @@ gb_internal void thread_init(ThreadPool *pool, Thread *t, isize idx) {
|
|||||||
t->posix_handle = 0;
|
t->posix_handle = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
t->capacity = 1 << 14; // must be a power of 2
|
// Size must be a power of 2
|
||||||
t->queue = gb_alloc_array(heap_allocator(), WorkerTask, t->capacity);
|
t->queue.ring = task_ring_init(1 << 14);
|
||||||
t->head_and_tail = 0;
|
|
||||||
t->pool = pool;
|
t->pool = pool;
|
||||||
t->idx = idx;
|
t->idx = idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
|
gb_internal void thread_init_and_start(ThreadPool *pool, Thread *t, isize idx) {
|
||||||
thread_init(pool, t, idx);
|
thread_init(pool, t, idx);
|
||||||
isize stack_size = 0;
|
isize stack_size = 0;
|
||||||
@@ -598,7 +617,7 @@ gb_internal void thread_join_and_destroy(Thread *t) {
|
|||||||
t->posix_handle = 0;
|
t->posix_handle = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
gb_free(heap_allocator(), t->queue);
|
thread_queue_destroy(&t->queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
gb_internal void thread_set_name(Thread *t, char const *name) {
|
gb_internal void thread_set_name(Thread *t, char const *name) {
|
||||||
|
|||||||
@@ -2923,11 +2923,14 @@ gb_internal Type *c_vararg_promote_type(Type *type) {
|
|||||||
|
|
||||||
if (core->kind == Type_Basic) {
|
if (core->kind == Type_Basic) {
|
||||||
switch (core->Basic.kind) {
|
switch (core->Basic.kind) {
|
||||||
|
case Basic_f16:
|
||||||
case Basic_f32:
|
case Basic_f32:
|
||||||
case Basic_UntypedFloat:
|
case Basic_UntypedFloat:
|
||||||
return t_f64;
|
return t_f64;
|
||||||
|
case Basic_f16le:
|
||||||
case Basic_f32le:
|
case Basic_f32le:
|
||||||
return t_f64le;
|
return t_f64le;
|
||||||
|
case Basic_f16be:
|
||||||
case Basic_f32be:
|
case Basic_f32be:
|
||||||
return t_f64be;
|
return t_f64be;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
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
|
@test
|
||||||
map_with_integer_keys :: proc(t: ^testing.T) {
|
map_with_integer_keys :: proc(t: ^testing.T) {
|
||||||
my_map := make(map[i32]string)
|
my_map := make(map[i32]string)
|
||||||
|
|||||||
+1
-1
@@ -568,7 +568,7 @@ foreign stbtt {
|
|||||||
// some of the values for the IDs are below; for more see the truetype spec:
|
// 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://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
|
||||||
// http://www.microsoft.com/typography/otspec/name.htm
|
// 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 ---
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1676,7 +1676,7 @@ when ODIN_OS != .JS {
|
|||||||
|
|
||||||
GetVersion :: proc() -> u32 ---
|
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) ---
|
RenderPassEncoderMultiDrawIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
|
||||||
RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
|
RenderPassEncoderMultiDrawIndexedIndirect :: proc(encoder: RenderPassEncoder, buffer: Buffer, offset: u64, count: u32) ---
|
||||||
|
|||||||
Reference in New Issue
Block a user