diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index 3dad2fdbc..8b7a5004a 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -939,19 +939,7 @@ map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) @builtin card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int { - when size_of(S) == 1 { - return int(intrinsics.count_ones(transmute(u8)s)) - } else when size_of(S) == 2 { - return int(intrinsics.count_ones(transmute(u16)s)) - } else when size_of(S) == 4 { - return int(intrinsics.count_ones(transmute(u32)s)) - } else when size_of(S) == 8 { - return int(intrinsics.count_ones(transmute(u64)s)) - } else when size_of(S) == 16 { - return int(intrinsics.count_ones(transmute(u128)s)) - } else { - #panic("Unhandled card bit_set size") - } + return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s)) } diff --git a/core/c/libc/README.md b/core/c/libc/README.md index 95053b963..08e789757 100644 --- a/core/c/libc/README.md +++ b/core/c/libc/README.md @@ -14,7 +14,7 @@ The following is a mostly-complete projection of the C11 standard library as def | `` | Fully projected | | `` | Not applicable, use Odin's operators | | `` | Not projected | -| `` | Not projected | +| `` | Fully projected | | `` | Mostly projected, see [limitations](#Limitations) | | `` | Fully projected | | `` | Fully projected | @@ -70,4 +70,4 @@ with the following copyright. ``` Copyright 2021 Dale Weiler . -``` \ No newline at end of file +``` diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin new file mode 100644 index 000000000..371d755c5 --- /dev/null +++ b/core/c/libc/locale.odin @@ -0,0 +1,133 @@ +package libc + +import "core:c" + +when ODIN_OS == .Windows { + foreign import libc "system:libucrt.lib" +} else when ODIN_OS == .Darwin { + foreign import libc "system:System.framework" +} else { + foreign import libc "system:c" +} + +// locale.h - category macros + +foreign libc { + /* + Sets the components of an object with the type lconv with the values appropriate for the + formatting of numeric quantities (monetary and otherwise) according to the rules of the current + locale. + + Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale() + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]] + */ + localeconv :: proc() -> ^lconv --- + + /* + Selects the appropriate piece of the global locale, as specified by the category and locale arguments, + and can be used to change or query the entire global locale or portions thereof. + + Returns: the current locale if `locale` is `nil`, the set locale otherwise + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]] + */ + @(link_name=LSETLOCALE) + setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring --- +} + +Locale_Category :: enum c.int { + ALL = LC_ALL, + COLLATE = LC_COLLATE, + CTYPE = LC_CTYPE, + MESSAGES = LC_MESSAGES, + MONETARY = LC_MONETARY, + NUMERIC = LC_NUMERIC, + TIME = LC_TIME, +} + +when ODIN_OS == .NetBSD { + @(private) LSETLOCALE :: "__setlocale50" +} else { + @(private) LSETLOCALE :: "setlocale" +} + +when ODIN_OS == .Windows { + lconv :: struct { + decimal_point: cstring, + thousand_sep: cstring, + grouping: cstring, + int_curr_symbol: cstring, + currency_symbol: cstring, + mon_decimal_points: cstring, + mon_thousands_sep: cstring, + mon_grouping: cstring, + positive_sign: cstring, + negative_sign: cstring, + int_frac_digits: c.char, + frac_digits: c.char, + p_cs_precedes: c.char, + p_sep_by_space: c.char, + n_cs_precedes: c.char, + n_sep_by_space: c.char, + p_sign_posn: c.char, + n_sign_posn: c.char, + _W_decimal_point: [^]u16 `fmt:"s,0"`, + _W_thousands_sep: [^]u16 `fmt:"s,0"`, + _W_int_curr_symbol: [^]u16 `fmt:"s,0"`, + _W_currency_symbol: [^]u16 `fmt:"s,0"`, + _W_mon_decimal_point: [^]u16 `fmt:"s,0"`, + _W_mon_thousands_sep: [^]u16 `fmt:"s,0"`, + _W_positive_sign: [^]u16 `fmt:"s,0"`, + _W_negative_sign: [^]u16 `fmt:"s,0"`, + } +} else { + lconv :: struct { + decimal_point: cstring, + thousand_sep: cstring, + grouping: cstring, + int_curr_symbol: cstring, + currency_symbol: cstring, + mon_decimal_points: cstring, + mon_thousands_sep: cstring, + mon_grouping: cstring, + positive_sign: cstring, + negative_sign: cstring, + int_frac_digits: c.char, + frac_digits: c.char, + p_cs_precedes: c.char, + p_sep_by_space: c.char, + n_cs_precedes: c.char, + n_sep_by_space: c.char, + p_sign_posn: c.char, + n_sign_posn: c.char, + _int_p_cs_precedes: c.char, + _int_n_cs_precedes: c.char, + _int_p_sep_by_space: c.char, + _int_n_sep_by_space: c.char, + _int_p_sign_posn: c.char, + _int_n_sign_posn: c.char, + } +} + +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Windows { + + LC_ALL :: 0 + LC_COLLATE :: 1 + LC_CTYPE :: 2 + LC_MESSAGES :: 6 + LC_MONETARY :: 3 + LC_NUMERIC :: 4 + LC_TIME :: 5 + +} else when ODIN_OS == .Linux { + + LC_CTYPE :: 0 + LC_NUMERIC :: 1 + LC_TIME :: 2 + LC_COLLATE :: 3 + LC_MONETARY :: 4 + LC_MESSAGES :: 5 + LC_ALL :: 6 + +} diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin index 09e16002d..84675a560 100644 --- a/core/dynlib/lib.odin +++ b/core/dynlib/lib.odin @@ -37,8 +37,8 @@ Example: fmt.println("The library %q was successfully loaded", LIBRARY_PATH) } */ -load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) { - return _load_library(path, global_symbols) +load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (library: Library, did_load: bool) { + return _load_library(path, global_symbols, allocator) } /* @@ -98,8 +98,8 @@ Example: } } */ -symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok { - return _symbol_address(library, symbol) +symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) #optional_ok { + return _symbol_address(library, symbol, allocator) } /* @@ -174,4 +174,4 @@ initialize_symbols :: proc( // Returns an error message for the last failed procedure call. last_error :: proc() -> string { return _last_error() -} \ No newline at end of file +} diff --git a/core/dynlib/lib_js.odin b/core/dynlib/lib_js.odin index 698cfee9c..b99143ba0 100644 --- a/core/dynlib/lib_js.odin +++ b/core/dynlib/lib_js.odin @@ -2,7 +2,9 @@ #+private package dynlib -_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { +import "base:runtime" + +_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) { return nil, false } @@ -10,10 +12,10 @@ _unload_library :: proc(library: Library) -> bool { return false } -_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { +_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) { return nil, false } _last_error :: proc() -> string { return "" -} \ No newline at end of file +} diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin index f467d730d..337bf496d 100644 --- a/core/dynlib/lib_unix.odin +++ b/core/dynlib/lib_unix.odin @@ -2,28 +2,38 @@ #+private package dynlib -import "core:os" +import "base:runtime" -_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) { - flags := os.RTLD_NOW +import "core:strings" +import "core:sys/posix" + +_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) { + flags := posix.RTLD_Flags{.NOW} if global_symbols { - flags |= os.RTLD_GLOBAL + flags += {.GLOBAL} } - lib := os.dlopen(path, flags) + + cpath := strings.clone_to_cstring(path, allocator) + defer delete(cpath, allocator) + + lib := posix.dlopen(cpath, flags) return Library(lib), lib != nil } _unload_library :: proc(library: Library) -> bool { - return os.dlclose(rawptr(library)) + return posix.dlclose(posix.Symbol_Table(library)) == 0 } -_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) { - ptr = os.dlsym(rawptr(library), symbol) +_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) { + csymbol := strings.clone_to_cstring(symbol, allocator) + defer delete(csymbol, allocator) + + ptr = posix.dlsym(posix.Symbol_Table(library), csymbol) found = ptr != nil return } _last_error :: proc() -> string { - err := os.dlerror() + err := string(posix.dlerror()) return "unknown" if err == "" else err -} \ No newline at end of file +} diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin index 6c41a1a75..928a1510d 100644 --- a/core/dynlib/lib_windows.odin +++ b/core/dynlib/lib_windows.odin @@ -2,11 +2,13 @@ #+private package dynlib +import "base:runtime" + import win32 "core:sys/windows" import "core:strings" import "core:reflect" -_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) { +_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) { // NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL wide_path := win32.utf8_to_wstring(path, allocator) defer free(wide_path, allocator) @@ -19,7 +21,7 @@ _unload_library :: proc(library: Library) -> bool { return bool(ok) } -_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) { +_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) { c_str := strings.clone_to_cstring(symbol, allocator) defer delete(c_str, allocator) ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str) @@ -31,4 +33,4 @@ _last_error :: proc() -> string { err := win32.System_Error(win32.GetLastError()) err_msg := reflect.enum_string(err) return "unknown" if err_msg == "" else err_msg -} \ No newline at end of file +} diff --git a/core/encoding/cbor/cbor.odin b/core/encoding/cbor/cbor.odin index 692be0020..8eb829ed3 100644 --- a/core/encoding/cbor/cbor.odin +++ b/core/encoding/cbor/cbor.odin @@ -563,7 +563,7 @@ to_json :: proc(val: Value, allocator := context.allocator) -> (json.Value, mem. case: return false } } - return false + return true } if keys_all_strings(v) { diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin index bf27171f4..c39255d9d 100644 --- a/core/encoding/cbor/unmarshal.odin +++ b/core/encoding/cbor/unmarshal.odin @@ -442,9 +442,6 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header loc := #caller_location, ) -> (out_of_space: bool, err: Unmarshal_Error) { for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 { - elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size)) - elem := any{elem_ptr, elemt.id} - hdr := _decode_header(d.reader) or_return // Double size if out of capacity. @@ -459,6 +456,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header if !ok { return false, .Out_Of_Memory } } + // Set ptr after potential resizes to avoid invalidation. + elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size)) + elem := any{elem_ptr, elemt.id} + err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc) if length == -1 && err == .Break { break } if err != nil { return } @@ -509,7 +510,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header raw := (^mem.Raw_Dynamic_Array)(v.data) raw.data = raw_data(data) raw.len = 0 - raw.cap = length + raw.cap = scap raw.allocator = context.allocator _ = assign_array(d, raw, t.elem, length) or_return @@ -627,7 +628,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, unknown := length == -1 fields := reflect.struct_fields_zipped(ti.id) - for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 { + idx := 0 + for ; idx < len(fields) && (unknown || idx < length); idx += 1 { // Decode key, keys can only be strings. key: string if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break { @@ -662,6 +664,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, // Skips unused map entries. if use_field_idx < 0 { + val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return + destroy(val, context.temp_allocator) continue } } @@ -672,6 +676,17 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header, fany := any{ptr, field.type.id} _unmarshal_value(d, fany, _decode_header(r) or_return) or_return } + + // If there are fields left in the map that did not get decoded into the struct, decode and discard them. + if !unknown { + for _ in idx.. (img: ^Image, err: Error) { loader := _internal_loaders[which(data)] if loader == nil { - return nil, .Unsupported_Format + + // Check if there is at least one loader, otherwise panic to let the user know about misuse. + for a_loader in _internal_loaders { + if a_loader != nil { + return nil, .Unsupported_Format + } + } + + panic("image.load called when no image loaders are registered. Register a loader by first importing a subpackage (eg: `import \"core:image/png\"`), or with image.register") } return loader(data, options, allocator) } @@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type { return .HDR case s[:4] == "\x38\x42\x50\x53": return .PSD - case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT": + case s[:4] == "\x53\x80\xF6\x34" && s[88:92] == "PICT": return .PIC case s[:4] == "\x69\x63\x6e\x73": return .ICNS diff --git a/core/io/util.odin b/core/io/util.odin index e65a69fb3..296be7bc0 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -225,7 +225,7 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false, } else { write_byte(w, '\\', &n) or_return write_byte(w, 'U', &n) or_return - for s := 24; s >= 0; s -= 4 { + for s := 28; s >= 0; s -= 4 { write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return } } diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin index 61301cf8a..474277e84 100644 --- a/core/math/rand/rand.odin +++ b/core/math/rand/rand.odin @@ -670,20 +670,69 @@ choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) { @(require_results) -choice_enum :: proc($T: typeid, gen := context.random_generator) -> T - where - intrinsics.type_is_enum(T), - size_of(T) <= 8, - len(T) == cap(T) /* Only allow contiguous enum types */ \ -{ - when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) && - u64(max(T)) > u64(max(i64)) { - i := uint64(gen) % u64(len(T)) - i += u64(min(T)) - return T(i) +choice_enum :: proc($T: typeid, gen := context.random_generator) -> T where intrinsics.type_is_enum(T) { + when size_of(T) <= 8 && len(T) == cap(T) { + when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) && + u64(max(T)) > u64(max(i64)) { + i := uint64(gen) % u64(len(T)) + i += u64(min(T)) + return T(i) + } else { + i := int63_max(i64(len(T)), gen) + i += i64(min(T)) + return T(i) + } } else { - i := int63_max(i64(len(T)), gen) - i += i64(min(T)) - return T(i) + values := runtime.type_info_base(type_info_of(T)).variant.(runtime.Type_Info_Enum).values + return T(choice(values)) } -} \ No newline at end of file +} + +/* +Returns a random *set* bit from the provided `bit_set`. + +Inputs: +- set: The `bit_set` to choose a random set bit from + +Returns: +- res: The randomly selected bit, or the zero value if `ok` is `false` +- ok: Whether the bit_set was not empty and thus `res` is actually a random set bit + +Example: + import "core:math/rand" + import "core:fmt" + + choice_bit_set_example :: proc() { + Flags :: enum { + A, + B = 10, + C, + } + + fmt.println(rand.choice_bit_set(bit_set[Flags]{})) + fmt.println(rand.choice_bit_set(bit_set[Flags]{.B})) + fmt.println(rand.choice_bit_set(bit_set[Flags]{.B, .C})) + fmt.println(rand.choice_bit_set(bit_set[0..<15]{5, 1, 4})) + } + +Possible Output: + A false + B true + C true + 5 true +*/ +@(require_results) +choice_bit_set :: proc(set: $T/bit_set[$E], gen := context.random_generator) -> (res: E, ok: bool) { + total_set := card(set) + if total_set == 0 { + return {}, false + } + + core_set := transmute(intrinsics.type_bit_set_underlying_type(T))set + + for target := int_max(total_set, gen); target > 0; target -= 1 { + core_set &= core_set - 1 + } + + return E(intrinsics.count_trailing_zeros(core_set)), true +} diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 67ed56c39..ccbc77798 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -461,10 +461,12 @@ Check if a pointer is aligned. This procedure checks whether a pointer `x` is aligned to a boundary specified by `align`, and returns `true` if the pointer is aligned, and false otherwise. + +The specified alignment must be a power of 2. */ is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool { p := uintptr(x) - return (p & (1< (process: Process, err: Error) { stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd) } if desc.stdin != nil { - stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd) + stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd) } working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index c7e1750d6..616e167a1 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -1026,7 +1026,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -1041,9 +1041,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> bool { diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index f617cf973..837e79f4d 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> (bool, Error) { diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin index 0d2c334be..4ad370724 100644 --- a/core/os/os_haiku.odin +++ b/core/os/os_haiku.odin @@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { defer _unix_free(path_ptr) path_cstr := cstring(path_ptr) - path = strings.clone(string(path_cstr)) - - return path, nil + return strings.clone(string(path_cstr), allocator) } access :: proc(path: string, mask: int) -> (bool, Error) { diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index e9039ba20..e023ce7cb 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -490,7 +490,7 @@ foreign libc { @(link_name="free") _unix_free :: proc(ptr: rawptr) --- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int --- + @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> c.int --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int --- @(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int --- @@ -917,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -932,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> (bool, Error) { diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index 493527803..e3ba760a4 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> (bool, Error) { diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index 62872d9dc..3c377968c 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> (bool, Error) { diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index 7eb72b9a7..003f8046d 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -246,6 +246,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str if err != .None { return } + defer { + for s in m { + delete(s) + } + delete(m) + } + dmatches := make([dynamic]string, 0, 0) for d in m { dmatches, err = _glob(d, file, &dmatches) diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index a18dc739e..35b98a7ae 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -1,14 +1,10 @@ #+build linux, darwin, freebsd, openbsd, netbsd package filepath -when ODIN_OS == .Darwin { - foreign import libc "system:System.framework" -} else { - foreign import libc "system:c" -} - import "base:runtime" + import "core:strings" +import "core:sys/posix" SEPARATOR :: '/' SEPARATOR_STRING :: `/` @@ -28,11 +24,11 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { rel = "." } rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator) - path_ptr := realpath(rel_cstr, nil) + path_ptr := posix.realpath(rel_cstr, nil) if path_ptr == nil { - return "", __error()^ == 0 + return "", posix.errno() == nil } - defer _unix_free(rawptr(path_ptr)) + defer posix.free(path_ptr) path_str := strings.clone(string(path_ptr), allocator) return path_str, true @@ -48,26 +44,3 @@ join :: proc(elems: []string, allocator := context.allocator) -> (joined: string } return "", nil } - -@(private) -foreign libc { - realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring --- - @(link_name="free") _unix_free :: proc(ptr: rawptr) --- - -} -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { - @(private) - foreign libc { - @(link_name="__error") __error :: proc() -> ^i32 --- - } -} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { - @(private) - foreign libc { - @(link_name="__errno") __error :: proc() -> ^i32 --- - } -} else { - @(private) - foreign libc { - @(link_name="__errno_location") __error :: proc() -> ^i32 --- - } -} diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index b1155c22f..26a737bd1 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1121,6 +1121,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { break trunc_block } f := f64(mantissa) + f_abs := f if neg { f = -f } @@ -1132,7 +1133,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { f *= pow10[exp-22] exp = 22 } - if f > 1e15 || f < 1e-15 { + if f_abs > 1e15 || f_abs < 1e-15 { break trunc_block } return f * pow10[exp], nr, true diff --git a/core/sys/posix/arpa_inet.odin b/core/sys/posix/arpa_inet.odin index 7e950c4be..d3592dd80 100644 --- a/core/sys/posix/arpa_inet.odin +++ b/core/sys/posix/arpa_inet.odin @@ -1,3 +1,4 @@ +#+build darwin, linux, freebsd, openbsd, netbsd package posix import "core:c" diff --git a/core/sys/posix/dirent.odin b/core/sys/posix/dirent.odin index 48830b030..73351b29d 100644 --- a/core/sys/posix/dirent.odin +++ b/core/sys/posix/dirent.odin @@ -1,3 +1,4 @@ +#+build darwin, linux, freebsd, openbsd, netbsd package posix import "core:c" @@ -29,12 +30,12 @@ foreign lib { panic(string(posix.strerror(posix.errno()))) } defer posix.free(list) - + entries := list[:ret] - for entry in entries { - log.info(entry) - posix.free(entry) - } + for entry in entries { + log.info(entry) + posix.free(entry) + } [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]] */ @@ -89,16 +90,16 @@ foreign lib { Returns nil when the end is reached or an error occurred (which sets errno). Example: - posix.set_errno(.NONE) - entry := posix.readdir(dirp) - if entry == nil { - if errno := posix.errno(); errno != .NONE { - panic(string(posix.strerror(errno))) - } else { - fmt.println("end of directory stream") - } - } else { - fmt.println(entry) + posix.set_errno(.NONE) + entry := posix.readdir(dirp) + if entry == nil { + if errno := posix.errno(); errno != .NONE { + panic(string(posix.strerror(errno))) + } else { + fmt.println("end of directory stream") + } + } else { + fmt.println(entry) } [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]] @@ -210,6 +211,4 @@ when ODIN_OS == .Darwin { d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/dlfcn.odin b/core/sys/posix/dlfcn.odin index 6a467a2bd..e84b29d79 100644 --- a/core/sys/posix/dlfcn.odin +++ b/core/sys/posix/dlfcn.odin @@ -1,3 +1,4 @@ +#+build darwin, linux, freebsd, openbsd, netbsd package posix import "core:c" @@ -120,7 +121,5 @@ when ODIN_OS == .Darwin { _RTLD_LOCAL :: 0 RTLD_LOCAL :: RTLD_Flags{} -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/errno.odin b/core/sys/posix/errno.odin index e670ca889..9bc77f12e 100644 --- a/core/sys/posix/errno.odin +++ b/core/sys/posix/errno.odin @@ -1,3 +1,4 @@ +#+build windows, darwin, linux, freebsd, openbsd, netbsd package posix import "core:c" @@ -456,7 +457,84 @@ when ODIN_OS == .Darwin { EOWNERDEAD :: 130 ENOTRECOVERABLE :: 131 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Windows { + E2BIG :: 7 + EACCES :: 13 + EADDRINUSE :: 100 + EADDRNOTAVAIL :: 101 + EAFNOSUPPORT :: 102 + EAGAIN :: 11 + EALREADY :: 103 + EBADF :: 9 + EBADMSG :: 104 + EBUSY :: 16 + ECANCELED :: 105 + ECHILD :: 10 + ECONNABORTED :: 106 + ECONNREFUSED :: 107 + ECONNRESET :: 108 + EDEADLK :: 36 + EDESTADDRREQ :: 109 + EDQUOT :: -1 // NOTE: not defined + EEXIST :: 17 + EFAULT :: 14 + EFBIG :: 27 + EHOSTUNREACH :: 110 + EIDRM :: 111 + EINPROGRESS :: 112 + EINTR :: 4 + EINVAL :: 22 + EIO :: 5 + EISCONN :: 113 + EISDIR :: 21 + ELOOP :: 114 + EMFILE :: 24 + EMLINK :: 31 + EMSGSIZE :: 115 + EMULTIHOP :: -1 // NOTE: not defined + ENAMETOOLONG :: 38 + ENETDOWN :: 116 + ENETRESET :: 117 + ENETUNREACH :: 118 + ENFILE :: 23 + ENOBUFS :: 119 + ENODATA :: 120 + ENODEV :: 19 + ENOENT :: 2 + ENOEXEC :: 8 + ENOLCK :: 39 + ENOLINK :: 121 + ENOMEM :: 12 + ENOMSG :: 122 + ENOPROTOOPT :: 123 + ENOSPC :: 28 + ENOSR :: 124 + ENOSTR :: 125 + ENOSYS :: 40 + ENOTCONN :: 126 + ENOTDIR :: 20 + ENOTEMPTY :: 41 + ENOTRECOVERABLE :: 127 + ENOTSOCK :: 128 + ENOTSUP :: 129 + ENOTTY :: 25 + ENXIO :: 6 + EOPNOTSUPP :: 130 + EOVERFLOW :: 132 + EOWNERDEAD :: 133 + EPERM :: 1 + EPIPE :: 32 + EPROTO :: 134 + EPROTONOSUPPORT :: 135 + EPROTOTYPE :: 136 + EROFS :: 30 + ESPIPE :: 29 + ESRCH :: 3 + ESTALE :: -1 // NOTE: not defined + ETIME :: 137 + ETIMEDOUT :: 138 + ETXTBSY :: 139 + EWOULDBLOCK :: 140 + EXDEV :: 18 } diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin index 1ccc07c54..d948af600 100644 --- a/core/sys/posix/fcntl.odin +++ b/core/sys/posix/fcntl.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, openbsd, freebsd, netbsd package posix import "core:c" @@ -466,13 +467,11 @@ when ODIN_OS == .Darwin { AT_REMOVEDIR :: 0x200 flock :: struct { + l_type: Lock_Type, /* [PSX] type of lock. */ + l_whence: c.short, /* [PSX] flag (Whence) of starting offset. */ l_start: off_t, /* [PSX] relative offset in bytes. */ l_len: off_t, /* [PSX] size; if 0 then until EOF. */ l_pid: pid_t, /* [PSX] process ID of the process holding the lock. */ - l_type: Lock_Type, /* [PSX] type of lock. */ - l_whence: c.short, /* [PSX] flag (Whence) of starting offset. */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin index 1326ce9e4..2d582705c 100644 --- a/core/sys/posix/fnmatch.odin +++ b/core/sys/posix/fnmatch.odin @@ -1,3 +1,4 @@ +#+build darwin, linux, openbsd, freebsd, netbsd package posix import "core:c" @@ -61,6 +62,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS FNM_NOESCAPE :: 0x02 FNM_PERIOD :: 0x04 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin index c17f8b2c3..7c8009a59 100644 --- a/core/sys/posix/glob.odin +++ b/core/sys/posix/glob.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -203,6 +204,4 @@ when ODIN_OS == .Darwin { GLOB_ABORTED :: 2 GLOB_NOMATCH :: 3 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin index 3a78e2c4c..956ed148b 100644 --- a/core/sys/posix/grp.odin +++ b/core/sys/posix/grp.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS gr_mem: [^]cstring, /* [PSX] group members */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/iconv.odin b/core/sys/posix/iconv.odin index 59248890f..f7447be9e 100644 --- a/core/sys/posix/iconv.odin +++ b/core/sys/posix/iconv.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" diff --git a/core/sys/posix/langinfo.odin b/core/sys/posix/langinfo.odin index 04b46921c..3c001aee0 100644 --- a/core/sys/posix/langinfo.odin +++ b/core/sys/posix/langinfo.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" diff --git a/core/sys/posix/libgen.odin b/core/sys/posix/libgen.odin index 99506797e..69176a557 100644 --- a/core/sys/posix/libgen.odin +++ b/core/sys/posix/libgen.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix when ODIN_OS == .Darwin { @@ -56,6 +57,7 @@ foreign lib { [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]] */ + @(link_name=LBASENAME) basename :: proc(path: cstring) -> cstring --- /* @@ -72,3 +74,9 @@ foreign lib { */ dirname :: proc(path: cstring) -> cstring --- } + +when ODIN_OS == .Linux { + @(private) LBASENAME :: "__xpg_basename" +} else { + @(private) LBASENAME :: "basename" +} diff --git a/core/sys/posix/limits.odin b/core/sys/posix/limits.odin index 58adce0a1..6680986ad 100644 --- a/core/sys/posix/limits.odin +++ b/core/sys/posix/limits.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix // limits.h - implementation-defined constants @@ -549,6 +550,4 @@ when ODIN_OS == .Darwin { NL_TEXTMAX :: 2048 // 255 on glibc, 2048 on musl NZERO :: 20 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/locale.odin b/core/sys/posix/locale.odin index fae692bcb..5b8d7c216 100644 --- a/core/sys/posix/locale.odin +++ b/core/sys/posix/locale.odin @@ -1,131 +1,11 @@ +#+build windows, linux, darwin, netbsd, openbsd, freebsd package posix -import "core:c" +import "core:c/libc" -when ODIN_OS == .Darwin { - foreign import lib "system:System.framework" -} else { - foreign import lib "system:c" -} +localeconv :: libc.localeconv +setlocale :: libc.setlocale -// locale.h - category macros +lconv :: libc.lconv -foreign lib { - /* - Sets the components of an object with the type lconv with the values appropriate for the - formatting of numeric quantities (monetary and otherwise) according to the rules of the current - locale. - - Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale() - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]] - */ - localeconv :: proc() -> ^lconv --- - - /* - Selects the appropriate piece of the global locale, as specified by the category and locale arguments, - and can be used to change or query the entire global locale or portions thereof. - - Returns: the current locale if `locale` is `nil`, the set locale otherwise - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]] - */ - @(link_name=LSETLOCALE) - setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring --- -} - -Locale_Category :: enum c.int { - ALL = LC_ALL, - COLLATE = LC_COLLATE, - CTYPE = LC_CTYPE, - MESSAGES = LC_MESSAGES, - MONETARY = LC_MONETARY, - NUMERIC = LC_NUMERIC, - TIME = LC_TIME, -} - -when ODIN_OS == .NetBSD { - @(private) LSETLOCALE :: "__setlocale50" -} else { - @(private) LSETLOCALE :: "setlocale" -} - -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { - - // NOTE: All of these fields are standard ([PSX]). - lconv :: struct { - decimal_point: cstring, - thousand_sep: cstring, - grouping: cstring, - int_curr_symbol: cstring, - currency_symbol: cstring, - mon_decimal_points: cstring, - mon_thousands_sep: cstring, - mon_grouping: cstring, - positive_sign: cstring, - negative_sign: cstring, - int_frac_digits: c.char, - frac_digits: c.char, - p_cs_precedes: c.char, - p_sep_by_space: c.char, - n_cs_precedes: c.char, - n_sep_by_space: c.char, - p_sign_posn: c.char, - n_sign_posn: c.char, - int_p_cs_precedes: c.char, - int_n_cs_precedes: c.char, - int_p_sep_by_space: c.char, - int_n_sep_by_space: c.char, - int_p_sign_posn: c.char, - int_n_sign_posn: c.char, - } - - LC_ALL :: 0 - LC_COLLATE :: 1 - LC_CTYPE :: 2 - LC_MESSAGES :: 6 - LC_MONETARY :: 3 - LC_NUMERIC :: 4 - LC_TIME :: 5 - -} else when ODIN_OS == .Linux { - - // NOTE: All of these fields are standard ([PSX]). - lconv :: struct { - decimal_point: cstring, - thousand_sep: cstring, - grouping: cstring, - int_curr_symbol: cstring, - currency_symbol: cstring, - mon_decimal_points: cstring, - mon_thousands_sep: cstring, - mon_grouping: cstring, - positive_sign: cstring, - negative_sign: cstring, - int_frac_digits: c.char, - frac_digits: c.char, - p_cs_precedes: c.char, - p_sep_by_space: c.char, - n_cs_precedes: c.char, - n_sep_by_space: c.char, - p_sign_posn: c.char, - n_sign_posn: c.char, - int_p_cs_precedes: c.char, - int_n_cs_precedes: c.char, - int_p_sep_by_space: c.char, - int_n_sep_by_space: c.char, - int_p_sign_posn: c.char, - int_n_sign_posn: c.char, - } - - LC_CTYPE :: 0 - LC_NUMERIC :: 1 - LC_TIME :: 2 - LC_COLLATE :: 3 - LC_MONETARY :: 4 - LC_MESSAGES :: 5 - LC_ALL :: 6 - -} else { - #panic("posix is unimplemented for the current target") -} +Locale_Category :: libc.Locale_Category diff --git a/core/sys/posix/monetary.odin b/core/sys/posix/monetary.odin index b4f0c31ee..ee342e211 100644 --- a/core/sys/posix/monetary.odin +++ b/core/sys/posix/monetary.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" diff --git a/core/sys/posix/net_if.odin b/core/sys/posix/net_if.odin index 75c1a863e..774d11b72 100644 --- a/core/sys/posix/net_if.odin +++ b/core/sys/posix/net_if.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -55,6 +56,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS IF_NAMESIZE :: 16 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/netdb.odin b/core/sys/posix/netdb.odin index f31de2c2b..79e13a140 100644 --- a/core/sys/posix/netdb.odin +++ b/core/sys/posix/netdb.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -472,6 +473,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS EAI_OVERFLOW :: 14 } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/netinet_in.odin b/core/sys/posix/netinet_in.odin index 22bfde9bc..a2cf904ce 100644 --- a/core/sys/posix/netinet_in.odin +++ b/core/sys/posix/netinet_in.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -61,6 +62,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS sin6_scope_id: c.uint32_t, /* [PSX] set of interfaces for a scope */ } + ipv6_mreq :: struct { + ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */ + ipv6mr_interface: c.uint, /* [PSX] interface index */ + } + IPV6_MULTICAST_IF :: 17 IPV6_UNICAST_HOPS :: 16 IPV6_MULTICAST_HOPS :: 18 @@ -223,6 +229,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS ) } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/netinet_tcp.odin b/core/sys/posix/netinet_tcp.odin index 284351732..b1da12f5e 100644 --- a/core/sys/posix/netinet_tcp.odin +++ b/core/sys/posix/netinet_tcp.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix // netinet/tcp.h - definitions for the Internet Transmission Control Protocol (TCP) @@ -6,6 +7,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS TCP_NODELAY :: 0x01 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin index 9e38afe12..9c3b8b081 100644 --- a/core/sys/posix/poll.odin +++ b/core/sys/posix/poll.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "base:intrinsics" @@ -92,7 +93,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS POLLHUP :: 0x0010 POLLNVAL :: 0x0020 -} else { - #panic("posix is unimplemented for the current target") } - diff --git a/core/sys/posix/posix.odin b/core/sys/posix/posix.odin index 44486e424..d56217407 100644 --- a/core/sys/posix/posix.odin +++ b/core/sys/posix/posix.odin @@ -11,6 +11,9 @@ The struct fields that are cross-platform are documented with `[PSX]`. Accessing these fields on one target should be the same on others. Other fields are implementation specific. +The parts of POSIX that Windows implements are also supported here, but +other symbols are undefined on Windows targets. + Most macros have been reimplemented in Odin with inlined functions. Unimplemented headers: diff --git a/core/sys/posix/pthread.odin b/core/sys/posix/pthread.odin index 76acb1a3d..490064da6 100644 --- a/core/sys/posix/pthread.odin +++ b/core/sys/posix/pthread.odin @@ -1,10 +1,11 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" when ODIN_OS == .Darwin { foreign import lib "system:System.framework" -} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { +} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Linux { foreign import lib "system:pthread" } else { foreign import lib "system:c" @@ -354,12 +355,16 @@ Thread_Scope :: enum c.int { } Cancel_State :: enum c.int { + // Cancel takes place at next cancellation point. ENABLE = PTHREAD_CANCEL_ENABLE, + // Cancel postponed. DISABLE = PTHREAD_CANCEL_DISABLE, } Cancel_Type :: enum c.int { + // Cancel waits until cancellation point. DEFERRED = PTHREAD_CANCEL_DEFERRED, + // Cancel occurs immediately. ASYNCHRONOUS = PTHREAD_CANCEL_ASYNCHRONOUS, } @@ -371,6 +376,12 @@ when ODIN_OS == .Darwin { PTHREAD_CANCEL_DISABLE :: 0x00 PTHREAD_CANCEL_ENABLE :: 0x01 + // PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + // PTHREAD_CANCEL_DEFERRED :: 0 + // + // PTHREAD_CANCEL_DISABLE :: 1 + // PTHREAD_CANCEL_ENABLE :: 0 + PTHREAD_CANCELED :: rawptr(uintptr(1)) PTHREAD_CREATE_DETACHED :: 2 @@ -398,6 +409,16 @@ when ODIN_OS == .Darwin { pthread_key_t :: distinct c.ulong + pthread_mutex_t :: struct { + __sig: c.long, + __opaque: [56]c.char, + } + + pthread_cond_t :: struct { + __sig: c.long, + __opaque: [40]c.char, + } + sched_param :: struct { sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ _: [4]c.char, @@ -423,18 +444,28 @@ when ODIN_OS == .Darwin { PTHREAD_PRIO_NONE :: 0 PTHREAD_PRIO_PROTECT :: 2 - PTHREAD_PROCESS_SHARED :: 0 - PTHREAD_PROCESS_PRIVATE :: 1 + PTHREAD_PROCESS_SHARED :: 1 + PTHREAD_PROCESS_PRIVATE :: 0 PTHREAD_SCOPE_PROCESS :: 0 PTHREAD_SCOPE_SYSTEM :: 2 pthread_t :: distinct u64 - pthread_attr_t :: distinct rawptr + pthread_attr_t :: struct #align(8) { + _: [8]byte, + } pthread_key_t :: distinct c.int + pthread_mutex_t :: struct #align(8) { + _: [8]byte, + } + + pthread_cond_t :: struct #align(8) { + _: [8]byte, + } + sched_param :: struct { sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ } @@ -475,6 +506,14 @@ when ODIN_OS == .Darwin { pthread_key_t :: distinct c.int + pthread_cond_t :: struct #align(8) { + _: [40]byte, + } + + pthread_mutex_t :: struct #align(8) { + _: [48]byte, + } + sched_param :: struct { sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ } @@ -505,9 +544,11 @@ when ODIN_OS == .Darwin { PTHREAD_SCOPE_PROCESS :: 0 PTHREAD_SCOPE_SYSTEM :: 0x2 - pthread_t :: distinct rawptr - pthread_attr_t :: distinct rawptr - pthread_key_t :: distinct c.int + pthread_t :: distinct rawptr + pthread_attr_t :: distinct rawptr + pthread_key_t :: distinct c.int + pthread_mutex_t :: distinct rawptr + pthread_cond_t :: distinct rawptr sched_param :: struct { sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ @@ -548,6 +589,16 @@ when ODIN_OS == .Darwin { pthread_key_t :: distinct c.uint + pthread_cond_t :: struct { + __size: [40]c.char, // NOTE: may be smaller depending on libc or arch, but never larger. + __align: c.long, + } + + pthread_mutex_t :: struct { + __size: [32]c.char, // NOTE: may be smaller depending on libc or arch, but never larger. + __align: c.long, + } + sched_param :: struct { sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */ @@ -557,6 +608,4 @@ when ODIN_OS == .Darwin { __reserved3: c.int, } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/pwd.odin b/core/sys/posix/pwd.odin index 546d58309..33cbcd7c5 100644 --- a/core/sys/posix/pwd.odin +++ b/core/sys/posix/pwd.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -163,6 +164,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { pw_fields: c.int, } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + passwd :: struct { + pw_name: cstring, /* [PSX] user name */ + pw_passwd: cstring, /* encrypted password */ + pw_uid: uid_t, /* [PSX] user uid */ + pw_gid: gid_t, /* [PSX] user gid */ + pw_gecos: cstring, /* Real name. */ + pw_dir: cstring, /* Home directory. */ + pw_shell: cstring, /* Shell program. */ + } + } diff --git a/core/sys/posix/sched.odin b/core/sys/posix/sched.odin index 3923257aa..e91178b09 100644 --- a/core/sys/posix/sched.odin +++ b/core/sys/posix/sched.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -100,6 +101,4 @@ when ODIN_OS == .Darwin { SCHED_FIFO :: 1 SCHED_RR :: 2 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/setjmp.odin b/core/sys/posix/setjmp.odin index cb1dad184..926dbd3ad 100644 --- a/core/sys/posix/setjmp.odin +++ b/core/sys/posix/setjmp.odin @@ -1,7 +1,7 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" -import "core:c/libc" when ODIN_OS == .Darwin { foreign import lib "system:System.framework" @@ -43,12 +43,8 @@ foreign lib { sigsetjmp :: proc(env: ^sigjmp_buf, savemask: b32) -> c.int --- } -jmp_buf :: libc.jmp_buf sigjmp_buf :: distinct jmp_buf -longjmp :: libc.longjmp -setjmp :: libc.setjmp - when ODIN_OS == .NetBSD { @(private) LSIGSETJMP :: "__sigsetjmp14" @(private) LSIGLONGJMP :: "__siglongjmp14" diff --git a/core/sys/posix/setjmp_libc.odin b/core/sys/posix/setjmp_libc.odin new file mode 100644 index 000000000..a69dff09f --- /dev/null +++ b/core/sys/posix/setjmp_libc.odin @@ -0,0 +1,11 @@ +#+build windows, linux, darwin, netbsd, openbsd, freebsd +package posix + +import "core:c/libc" + +// setjmp.h - stack environment declarations + +jmp_buf :: libc.jmp_buf + +longjmp :: libc.longjmp +setjmp :: libc.setjmp diff --git a/core/sys/posix/signal.odin b/core/sys/posix/signal.odin index 1e3f05104..4ba4e9943 100644 --- a/core/sys/posix/signal.odin +++ b/core/sys/posix/signal.odin @@ -1,9 +1,9 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "base:intrinsics" import "core:c" -import "core:c/libc" when ODIN_OS == .Darwin { foreign import lib "system:System.framework" @@ -14,31 +14,6 @@ when ODIN_OS == .Darwin { // signal.h - signals foreign lib { - // LIBC: - - /* - Set a signal handler. - - func can either be: - - `auto_cast posix.SIG_DFL` setting the default handler for that specific signal - - `auto_cast posix.SIG_IGN` causing the specific signal to be ignored - - a custom signal handler - - Returns: SIG_ERR (setting errno), the last value of func on success - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html ]] - */ - signal :: proc(sig: Signal, func: proc "c" (Signal)) -> proc "c" (Signal) --- - - /* - Raises a signal, calling its handler and then returning. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html ]] - */ - raise :: proc(sig: Signal) -> result --- - - // POSIX: - /* Raise a signal to the process/group specified by pid. @@ -227,72 +202,6 @@ sigval :: struct #raw_union { sigval_ptr: rawptr, /* [PSX] pointer signal value */ } -Signal :: enum c.int { - NONE, - - // LIBC: - - // Process abort signal. - SIGABRT = SIGABRT, - // Erronous arithemtic operation. - SIGFPE = SIGFPE, - // Illegal instruction. - SIGILL = SIGILL, - // Terminal interrupt signal. - SIGINT = SIGINT, - // Invalid memory reference. - SIGSEGV = SIGSEGV, - // Termination signal. - SIGTERM = SIGTERM, - - // POSIX: - - // Process abort signal. - SIGALRM = SIGALRM, - // Access to an undefined portion of a memory object. - SIGBUS = SIGBUS, - // Child process terminated, stopped, or continued. - SIGCHLD = SIGCHLD, - // Continue execution, if stopped. - SIGCONT = SIGCONT, - // Hangup. - SIGHUP = SIGHUP, - // Kill (cannot be caught or ignored). - SIGKILL = SIGKILL, - // Write on a pipe with no one to read it. - SIGPIPE = SIGPIPE, - // Terminal quit signal. - SIGQUIT = SIGQUIT, - // Stop executing (cannot be caught or ignored). - SIGSTOP = SIGSTOP, - // Terminal stop process. - SIGTSTP = SIGTSTP, - // Background process attempting read. - SIGTTIN = SIGTTIN, - // Background process attempting write. - SIGTTOU = SIGTTOU, - // User-defined signal 1. - SIGUSR1 = SIGUSR1, - // User-defined signal 2. - SIGUSR2 = SIGUSR2, - // Pollable event. - SIGPOLL = SIGPOLL, - // Profiling timer expired. - SIGPROF = SIGPROF, - // Bad system call. - SIGSYS = SIGSYS, - // Trace/breakpoint trap. - SIGTRAP = SIGTRAP, - // High bandwidth data is available at a socket. - SIGURG = SIGURG, - // Virtual timer expired. - SIGVTALRM = SIGVTALRM, - // CPU time limit exceeded. - SIGXCPU = SIGXCPU, - // File size limit exceeded. - SIGXFSZ = SIGXFSZ, -} - ILL_Code :: enum c.int { // Illegal opcode. ILLOPC = ILL_ILLOPC, @@ -434,20 +343,6 @@ Sig :: enum c.int { SETMASK = SIG_SETMASK, } -// Request for default signal handling. -SIG_DFL :: libc.SIG_DFL -// Return value from signal() in case of error. -SIG_ERR :: libc.SIG_ERR -// Request that signal be ignored. -SIG_IGN :: libc.SIG_IGN - -SIGABRT :: libc.SIGABRT -SIGFPE :: libc.SIGFPE -SIGILL :: libc.SIGILL -SIGINT :: libc.SIGINT -SIGSEGV :: libc.SIGSEGV -SIGTERM :: libc.SIGTERM - when ODIN_OS == .NetBSD { @(private) LSIGPROCMASK :: "__sigprocmask14" @(private) LSIGACTION :: "__sigaction_siginfo" @@ -1118,7 +1013,7 @@ when ODIN_OS == .Darwin { uid_t :: distinct c.uint32_t sigset_t :: struct { - [1024/(8 * size_of(c.ulong))]val, + __val: [1024/(8 * size_of(c.ulong))]c.ulong, } SIGHUP :: 1 @@ -1285,6 +1180,4 @@ when ODIN_OS == .Darwin { SI_TIMER :: -2 SI_MESGQ :: -3 SI_ASYNCIO :: -4 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/signal_libc.odin b/core/sys/posix/signal_libc.odin new file mode 100644 index 000000000..aef22da29 --- /dev/null +++ b/core/sys/posix/signal_libc.odin @@ -0,0 +1,145 @@ +#+build linux, windows, darwin, netbsd, openbsd, freebsd +package posix + +import "base:intrinsics" + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib "system:libucrt.lib" +} else when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// signal.h - signals + +foreign lib { + /* + Set a signal handler. + + func can either be: + - `auto_cast posix.SIG_DFL` setting the default handler for that specific signal + - `auto_cast posix.SIG_IGN` causing the specific signal to be ignored + - a custom signal handler + + Returns: SIG_ERR (setting errno), the last value of func on success + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html ]] + */ + signal :: proc(sig: Signal, func: proc "c" (Signal)) -> proc "c" (Signal) --- + + /* + Raises a signal, calling its handler and then returning. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html ]] + */ + raise :: proc(sig: Signal) -> result --- +} + +Signal :: enum c.int { + NONE, + + // LIBC: + + // Process abort signal. + SIGABRT = SIGABRT, + // Erronous arithemtic operation. + SIGFPE = SIGFPE, + // Illegal instruction. + SIGILL = SIGILL, + // Terminal interrupt signal. + SIGINT = SIGINT, + // Invalid memory reference. + SIGSEGV = SIGSEGV, + // Termination signal. + SIGTERM = SIGTERM, + + // POSIX: + + // Process abort signal. + SIGALRM = SIGALRM, + // Access to an undefined portion of a memory object. + SIGBUS = SIGBUS, + // Child process terminated, stopped, or continued. + SIGCHLD = SIGCHLD, + // Continue execution, if stopped. + SIGCONT = SIGCONT, + // Hangup. + SIGHUP = SIGHUP, + // Kill (cannot be caught or ignored). + SIGKILL = SIGKILL, + // Write on a pipe with no one to read it. + SIGPIPE = SIGPIPE, + // Terminal quit signal. + SIGQUIT = SIGQUIT, + // Stop executing (cannot be caught or ignored). + SIGSTOP = SIGSTOP, + // Terminal stop process. + SIGTSTP = SIGTSTP, + // Background process attempting read. + SIGTTIN = SIGTTIN, + // Background process attempting write. + SIGTTOU = SIGTTOU, + // User-defined signal 1. + SIGUSR1 = SIGUSR1, + // User-defined signal 2. + SIGUSR2 = SIGUSR2, + // Pollable event. + SIGPOLL = SIGPOLL, + // Profiling timer expired. + SIGPROF = SIGPROF, + // Bad system call. + SIGSYS = SIGSYS, + // Trace/breakpoint trap. + SIGTRAP = SIGTRAP, + // High bandwidth data is available at a socket. + SIGURG = SIGURG, + // Virtual timer expired. + SIGVTALRM = SIGVTALRM, + // CPU time limit exceeded. + SIGXCPU = SIGXCPU, + // File size limit exceeded. + SIGXFSZ = SIGXFSZ, +} + +// Request for default signal handling. +SIG_DFL :: libc.SIG_DFL +// Return value from signal() in case of error. +SIG_ERR :: libc.SIG_ERR +// Request that signal be ignored. +SIG_IGN :: libc.SIG_IGN + +SIGABRT :: libc.SIGABRT +SIGFPE :: libc.SIGFPE +SIGILL :: libc.SIGILL +SIGINT :: libc.SIGINT +SIGSEGV :: libc.SIGSEGV +SIGTERM :: libc.SIGTERM + +when ODIN_OS == .Windows { + SIGALRM :: -1 + SIGBUS :: -1 + SIGCHLD :: -1 + SIGCONT :: -1 + SIGHUP :: -1 + SIGKILL :: -1 + SIGPIPE :: -1 + SIGQUIT :: -1 + SIGSTOP :: -1 + SIGTSTP :: -1 + SIGTTIN :: -1 + SIGTTOU :: -1 + SIGUSR1 :: -1 + SIGUSR2 :: -1 + SIGPOLL :: -1 + SIGPROF :: -1 + SIGSYS :: -1 + SIGTRAP :: -1 + SIGURG :: -1 + SIGVTALRM :: -1 + SIGXCPU :: -1 + SIGXFSZ :: -1 +} diff --git a/core/sys/posix/stdio.odin b/core/sys/posix/stdio.odin index de716f8d7..24464dfd8 100644 --- a/core/sys/posix/stdio.odin +++ b/core/sys/posix/stdio.odin @@ -1,7 +1,7 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" -import "core:c/libc" when ODIN_OS == .Darwin { foreign import lib "system:System.framework" @@ -32,16 +32,6 @@ foreign lib { */ dprintf :: proc(fildse: FD, format: cstring, #c_vararg args: ..any) -> c.int --- - /* - Equivalent to fprintf but output is written to s, it is the user's responsibility to - ensure there is enough space. - - Return: number of bytes written, negative (setting errno) on failure - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html ]] - */ - sprintf :: proc(s: [^]byte, format: cstring, #c_vararg args: ..any) -> c.int --- - /* Associate a stream with a file descriptor. @@ -115,34 +105,6 @@ foreign lib { */ open_memstream :: proc(bufp: ^[^]byte, sizep: ^c.size_t) -> ^FILE --- - /* - Equivalent to getc but unaffected by locks. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] - */ - getc_unlocked :: proc(stream: ^FILE) -> c.int --- - - /* - Equivalent to getchar but unaffected by locks. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] - */ - getchar_unlocked :: proc() -> c.int --- - - /* - Equivalent to putc but unaffected by locks. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] - */ - putc_unlocked :: proc(ch: c.int, stream: ^FILE) -> c.int --- - - /* - Equivalent to putchar but unaffected by locks. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] - */ - putchar_unlocked :: proc(ch: c.int) -> c.int --- - /* Read a delimited record from the stream. @@ -181,60 +143,6 @@ foreign lib { */ getline :: proc(lineptr: ^cstring, n: ^c.size_t, stream: ^FILE) -> c.ssize_t --- - /* - Get a string from the stdin stream. - - It is up to the user to make sure s is big enough. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gets.html ]] - */ - gets :: proc(s: [^]byte) -> cstring --- - - /* - Create a name for a temporary file. - - Returns: an allocated cstring that needs to be freed, nil on failure - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tempnam.html ]] - */ - tempnam :: proc(dir: cstring, pfx: cstring) -> cstring --- - - /* - Executes the command specified, creating a pipe and returning a pointer to a stream that can - read or write from/to the pipe. - - Returns: nil (setting errno) on failure or a pointer to the stream - - Example: - fp := posix.popen("ls *", "r") - if fp == nil { - /* Handle error */ - } - - path: [1024]byte - for posix.fgets(raw_data(path[:]), len(path), fp) != nil { - posix.printf("%s", &path) - } - - status := posix.pclose(fp) - if status == -1 { - /* Error reported by pclose() */ - } else { - /* Use functions described under wait() to inspect `status` in order - to determine success/failure of the command executed by popen() */ - } - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html ]] - */ - popen :: proc(command: cstring, mode: cstring) -> ^FILE --- - - /* - Closes a pipe stream to or from a process. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pclose.html ]] - */ - pclose :: proc(stream: ^FILE) -> c.int --- - /* Equivalent to rename but relative directories are resolved from their respective fds. @@ -243,76 +151,6 @@ foreign lib { renameat :: proc(oldfd: FD, old: cstring, newfd: FD, new: cstring) -> result --- } -clearerr :: libc.clearerr -fclose :: libc.fclose -feof :: libc.feof -ferror :: libc.ferror -fflush :: libc.fflush -fgetc :: libc.fgetc -fgetpos :: libc.fgetpos -fgets :: libc.fgets -fopen :: libc.fopen -fprintf :: libc.fprintf -fputc :: libc.fputc -fread :: libc.fread -freopen :: libc.freopen -fscanf :: libc.fscanf -fseek :: libc.fseek -fsetpos :: libc.fsetpos -ftell :: libc.ftell -fwrite :: libc.fwrite -getc :: libc.getc -getchar :: libc.getchar -perror :: libc.perror -printf :: libc.printf -putc :: libc.puts -putchar :: libc.putchar -puts :: libc.puts -remove :: libc.remove -rename :: libc.rename -rewind :: libc.rewind -scanf :: libc.scanf -setbuf :: libc.setbuf -setvbuf :: libc.setvbuf -snprintf :: libc.snprintf -sscanf :: libc.sscanf -tmpfile :: libc.tmpfile -tmpnam :: libc.tmpnam -vfprintf :: libc.vfprintf -vfscanf :: libc.vfscanf -vprintf :: libc.vprintf -vscanf :: libc.vscanf -vsnprintf :: libc.vsnprintf -vsprintf :: libc.vsprintf -vsscanf :: libc.vsscanf -ungetc :: libc.ungetc - -to_stream :: libc.to_stream - -Whence :: libc.Whence -FILE :: libc.FILE -fpos_t :: libc.fpos_t - -BUFSIZ :: libc.BUFSIZ - -_IOFBF :: libc._IOFBF -_IOLBF :: libc._IOLBF -_IONBF :: libc._IONBF - -SEEK_CUR :: libc.SEEK_CUR -SEEK_END :: libc.SEEK_END -SEEK_SET :: libc.SEEK_SET - -FILENAME_MAX :: libc.FILENAME_MAX -FOPEN_MAX :: libc.FOPEN_MAX -TMP_MAX :: libc.TMP_MAX - -EOF :: libc.EOF - -stderr := libc.stderr -stdin := libc.stdin -stdout := libc.stdout - when ODIN_OS == .Darwin { L_ctermid :: 1024 @@ -327,6 +165,11 @@ when ODIN_OS == .Darwin { P_tmpdir :: "/tmp/" -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + L_ctermid :: 20 // 20 on musl, 9 on glibc + L_tmpnam :: 20 + + P_tmpdir :: "/tmp/" + } diff --git a/core/sys/posix/stdio_libc.odin b/core/sys/posix/stdio_libc.odin new file mode 100644 index 000000000..fbd949b2c --- /dev/null +++ b/core/sys/posix/stdio_libc.odin @@ -0,0 +1,207 @@ +#+build linux, windows, linux, darwin, netbsd, openbsd, freebsd +package posix + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib { + "system:libucrt.lib", + "system:legacy_stdio_definitions.lib", + } +} else when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// stdio.h - standard buffered input/output + +when ODIN_OS == .Windows { + @(private) LGETC_UNLOCKED :: "_getc_nolock" + @(private) LGETCHAR_UNLOCKED :: "_getchar_nolock" + @(private) LPUTC_UNLOCKED :: "_putc_nolock" + @(private) LPUTCHAR_UNLOCKED :: "_putchar_nolock" + @(private) LTEMPNAM :: "_tempnam" + @(private) LPOPEN :: "_popen" + @(private) LPCLOSE :: "_pclose" +} else { + @(private) LGETC_UNLOCKED :: "getc_unlocked" + @(private) LGETCHAR_UNLOCKED :: "getchar_unlocked" + @(private) LPUTC_UNLOCKED :: "putc_unlocked" + @(private) LPUTCHAR_UNLOCKED :: "putchar_unlocked" + @(private) LTEMPNAM :: "tempnam" + @(private) LPOPEN :: "popen" + @(private) LPCLOSE :: "pclose" +} + +foreign lib { + /* + Equivalent to fprintf but output is written to s, it is the user's responsibility to + ensure there is enough space. + + Return: number of bytes written, negative (setting errno) on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html ]] + */ + sprintf :: proc(s: [^]byte, format: cstring, #c_vararg args: ..any) -> c.int --- + + /* + Equivalent to getc but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + @(link_name=LGETC_UNLOCKED) + getc_unlocked :: proc(stream: ^FILE) -> c.int --- + + /* + Equivalent to getchar but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + @(link_name=LGETCHAR_UNLOCKED) + getchar_unlocked :: proc() -> c.int --- + + /* + Equivalent to putc but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + @(link_name=LPUTC_UNLOCKED) + putc_unlocked :: proc(ch: c.int, stream: ^FILE) -> c.int --- + + /* + Equivalent to putchar but unaffected by locks. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]] + */ + @(link_name=LPUTCHAR_UNLOCKED) + putchar_unlocked :: proc(ch: c.int) -> c.int --- + + /* + Get a string from the stdin stream. + + It is up to the user to make sure s is big enough. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gets.html ]] + */ + gets :: proc(s: [^]byte) -> cstring --- + + /* + Create a name for a temporary file. + + Returns: an allocated cstring that needs to be freed, nil on failure + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tempnam.html ]] + */ + @(link_name=LTEMPNAM) + tempnam :: proc(dir: cstring, pfx: cstring) -> cstring --- + + /* + Executes the command specified, creating a pipe and returning a pointer to a stream that can + read or write from/to the pipe. + + Returns: nil (setting errno) on failure or a pointer to the stream + + Example: + fp := posix.popen("ls *", "r") + if fp == nil { + /* Handle error */ + } + + path: [1024]byte + for posix.fgets(raw_data(path[:]), len(path), fp) != nil { + posix.printf("%s", &path) + } + + status := posix.pclose(fp) + if status == -1 { + /* Error reported by pclose() */ + } else { + /* Use functions described under wait() to inspect `status` in order + to determine success/failure of the command executed by popen() */ + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html ]] + */ + @(link_name=LPOPEN) + popen :: proc(command: cstring, mode: cstring) -> ^FILE --- + + /* + Closes a pipe stream to or from a process. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pclose.html ]] + */ + @(link_name=LPCLOSE) + pclose :: proc(stream: ^FILE) -> c.int --- +} + +clearerr :: libc.clearerr +fclose :: libc.fclose +feof :: libc.feof +ferror :: libc.ferror +fflush :: libc.fflush +fgetc :: libc.fgetc +fgetpos :: libc.fgetpos +fgets :: libc.fgets +fopen :: libc.fopen +fprintf :: libc.fprintf +fputc :: libc.fputc +fread :: libc.fread +freopen :: libc.freopen +fscanf :: libc.fscanf +fseek :: libc.fseek +fsetpos :: libc.fsetpos +ftell :: libc.ftell +fwrite :: libc.fwrite +getc :: libc.getc +getchar :: libc.getchar +perror :: libc.perror +printf :: libc.printf +putc :: libc.puts +putchar :: libc.putchar +puts :: libc.puts +remove :: libc.remove +rename :: libc.rename +rewind :: libc.rewind +scanf :: libc.scanf +setbuf :: libc.setbuf +setvbuf :: libc.setvbuf +snprintf :: libc.snprintf +sscanf :: libc.sscanf +tmpfile :: libc.tmpfile +tmpnam :: libc.tmpnam +vfprintf :: libc.vfprintf +vfscanf :: libc.vfscanf +vprintf :: libc.vprintf +vscanf :: libc.vscanf +vsnprintf :: libc.vsnprintf +vsprintf :: libc.vsprintf +vsscanf :: libc.vsscanf +ungetc :: libc.ungetc + +to_stream :: libc.to_stream + +Whence :: libc.Whence +FILE :: libc.FILE +fpos_t :: libc.fpos_t + +BUFSIZ :: libc.BUFSIZ + +_IOFBF :: libc._IOFBF +_IOLBF :: libc._IOLBF +_IONBF :: libc._IONBF + +SEEK_CUR :: libc.SEEK_CUR +SEEK_END :: libc.SEEK_END +SEEK_SET :: libc.SEEK_SET + +FILENAME_MAX :: libc.FILENAME_MAX +FOPEN_MAX :: libc.FOPEN_MAX +TMP_MAX :: libc.TMP_MAX + +EOF :: libc.EOF + +stderr := libc.stderr +stdin := libc.stdin +stdout := libc.stdout diff --git a/core/sys/posix/stdlib.odin b/core/sys/posix/stdlib.odin index a1e2eab50..640c70b5a 100644 --- a/core/sys/posix/stdlib.odin +++ b/core/sys/posix/stdlib.odin @@ -1,9 +1,9 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "base:intrinsics" import "core:c" -import "core:c/libc" when ODIN_OS == .Darwin { foreign import lib "system:System.framework" @@ -11,56 +11,6 @@ when ODIN_OS == .Darwin { foreign import lib "system:c" } -// stdlib.h - standard library definitions - -atof :: libc.atof -atoi :: libc.atoi -atol :: libc.atol -atoll :: libc.atoll -strtod :: libc.strtod -strtof :: libc.strtof -strtol :: libc.strtol -strtoll :: libc.strtoll -strtoul :: libc.strtoul -strtoull :: libc.strtoull - -rand :: libc.rand -srand :: libc.srand - -calloc :: libc.calloc -malloc :: libc.malloc -realloc :: libc.realloc - -abort :: libc.abort -atexit :: libc.atexit -at_quick_exit :: libc.at_quick_exit -exit :: libc.exit -_Exit :: libc._Exit -getenv :: libc.getenv -quick_exit :: libc.quick_exit -system :: libc.system - -bsearch :: libc.bsearch -qsort :: libc.qsort - -abs :: libc.abs -labs :: libc.labs -llabs :: libc.llabs -div :: libc.div -ldiv :: libc.ldiv -lldiv :: libc.lldiv - -mblen :: libc.mblen -mbtowc :: libc.mbtowc -wctomb :: libc.wctomb - -mbstowcs :: libc.mbstowcs -wcstombs :: libc.wcstombs - -free :: #force_inline proc(ptr: $T) where intrinsics.type_is_pointer(T) || intrinsics.type_is_multi_pointer(T) || T == cstring { - libc.free(rawptr(ptr)) -} - foreign lib { /* Takes a pointer to a radix-64 representation, in which the first digit is the least significant, @@ -342,21 +292,6 @@ foreign lib { */ unlockpt :: proc(fildes: FD) -> result --- - /* - Uses the string argument to set environment variable values. - - Returns: 0 on success, non-zero (setting errno) on failure - - Example: - if posix.putenv("HOME=/usr/home") != 0 { - fmt.panicf("putenv failure: %v", posix.strerror(posix.errno())) - } - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html ]] - */ - @(link_name=LPUTENV) - putenv :: proc(string: cstring) -> c.int --- - /* Updates or add a variable in the environment of the calling process. @@ -427,23 +362,11 @@ foreign lib { setkey :: proc(key: [^]byte) --- } -EXIT_FAILURE :: libc.EXIT_FAILURE -EXIT_SUCCESS :: libc.EXIT_SUCCESS - -RAND_MAX :: libc.RAND_MAX -MB_CUR_MAX :: libc.MB_CUR_MAX - -div_t :: libc.div_t -ldiv_t :: libc.ldiv_t -lldiv_t :: libc.lldiv_t - when ODIN_OS == .NetBSD { - @(private) LPUTENV :: "__putenv50" @(private) LINITSTATE :: "__initstate60" @(private) LSRANDOM :: "__srandom60" @(private) LUNSETENV :: "__unsetenv13" } else { - @(private) LPUTENV :: "putenv" @(private) LINITSTATE :: "initstate" @(private) LSRANDOM :: "srandom" @(private) LUNSETENV :: "unsetenv" diff --git a/core/sys/posix/stdlib_libc.odin b/core/sys/posix/stdlib_libc.odin new file mode 100644 index 000000000..fa4d925b2 --- /dev/null +++ b/core/sys/posix/stdlib_libc.odin @@ -0,0 +1,101 @@ +#+build linux, windows, darwin, netbsd, openbsd, freebsd +package posix + +import "base:intrinsics" + +import "core:c" +import "core:c/libc" + +when ODIN_OS == .Windows { + foreign import lib "system:libucrt.lib" +} else when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// stdlib.h - standard library definitions + +atof :: libc.atof +atoi :: libc.atoi +atol :: libc.atol +atoll :: libc.atoll +strtod :: libc.strtod +strtof :: libc.strtof +strtol :: libc.strtol +strtoll :: libc.strtoll +strtoul :: libc.strtoul +strtoull :: libc.strtoull + +rand :: libc.rand +srand :: libc.srand + +calloc :: libc.calloc +malloc :: libc.malloc +realloc :: libc.realloc + +abort :: libc.abort +atexit :: libc.atexit +at_quick_exit :: libc.at_quick_exit +exit :: libc.exit +_Exit :: libc._Exit +getenv :: libc.getenv +quick_exit :: libc.quick_exit +system :: libc.system + +bsearch :: libc.bsearch +qsort :: libc.qsort + +abs :: libc.abs +labs :: libc.labs +llabs :: libc.llabs +div :: libc.div +ldiv :: libc.ldiv +lldiv :: libc.lldiv + +mblen :: libc.mblen +mbtowc :: libc.mbtowc +wctomb :: libc.wctomb + +mbstowcs :: libc.mbstowcs +wcstombs :: libc.wcstombs + +free :: #force_inline proc(ptr: $T) where intrinsics.type_is_pointer(T) || intrinsics.type_is_multi_pointer(T) || T == cstring { + libc.free(rawptr(ptr)) +} + +foreign lib { + + /* + Uses the string argument to set environment variable values. + + Returns: 0 on success, non-zero (setting errno) on failure + + Example: + if posix.putenv("HOME=/usr/home") != 0 { + fmt.panicf("putenv failure: %v", posix.strerror(posix.errno())) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html ]] + */ + @(link_name=LPUTENV) + putenv :: proc(string: cstring) -> c.int --- +} + +EXIT_FAILURE :: libc.EXIT_FAILURE +EXIT_SUCCESS :: libc.EXIT_SUCCESS + +RAND_MAX :: libc.RAND_MAX +MB_CUR_MAX :: libc.MB_CUR_MAX + +div_t :: libc.div_t +ldiv_t :: libc.ldiv_t +lldiv_t :: libc.lldiv_t + +when ODIN_OS == .Windows { + @(private) LPUTENV :: "_putenv" +} else when ODIN_OS == .NetBSD { + @(private) LPUTENV :: "__putenv50" +} else { + @(private) LPUTENV :: "putenv" +} diff --git a/core/sys/posix/string.odin b/core/sys/posix/string.odin index d22f49a96..96b6a9007 100644 --- a/core/sys/posix/string.odin +++ b/core/sys/posix/string.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -13,16 +14,6 @@ when ODIN_OS == .Darwin { // NOTE: most of the symbols in this header are not useful in Odin and have been left out. foreign lib { - /* - Map the error number to a locale-dependent error message string. - - Returns: a string that may be invalidated by subsequent calls - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html ]] - */ - @(link_name="strerror") - _strerror :: proc(errnum: Errno) -> cstring --- - /* Map the error number to a locale-dependent error message string and put it in the buffer. @@ -41,7 +32,3 @@ foreign lib { */ strsignal :: proc(sig: Signal) -> cstring --- } - -strerror :: #force_inline proc "contextless" (errnum: Maybe(Errno) = nil) -> cstring { - return _strerror(errnum.? or_else errno()) -} diff --git a/core/sys/posix/string_libc.odin b/core/sys/posix/string_libc.odin new file mode 100644 index 000000000..336352cbc --- /dev/null +++ b/core/sys/posix/string_libc.odin @@ -0,0 +1,30 @@ +#+build linux, windows, darwin, netbsd, openbsd, freebsd +package posix + +when ODIN_OS == .Windows { + foreign import lib "system:libucrt.lib" +} else when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// string.h - string operations + +// NOTE: most of the symbols in this header are not useful in Odin and have been left out. + +foreign lib { + /* + Map the error number to a locale-dependent error message string. + + Returns: a string that may be invalidated by subsequent calls + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html ]] + */ + @(link_name="strerror") + _strerror :: proc(errnum: Errno) -> cstring --- +} + +strerror :: #force_inline proc "contextless" (errnum: Maybe(Errno) = nil) -> cstring { + return _strerror(errnum.? or_else errno()) +} diff --git a/core/sys/posix/sys_ipc.odin b/core/sys/posix/sys_ipc.odin index 33f8fa259..0f7ec06c5 100644 --- a/core/sys/posix/sys_ipc.odin +++ b/core/sys/posix/sys_ipc.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -110,6 +111,4 @@ when ODIN_OS == .Darwin { IPC_SET :: 1 IPC_STAT :: 2 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_mman.odin b/core/sys/posix/sys_mman.odin index 2f4eb566b..9e2939a05 100644 --- a/core/sys/posix/sys_mman.odin +++ b/core/sys/posix/sys_mman.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -225,6 +226,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS POSIX_MADV_SEQUENTIAL :: 2 POSIX_MADV_WILLNEED :: 3 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_msg.odin b/core/sys/posix/sys_msg.odin index 98b76c3a5..0e78777f9 100644 --- a/core/sys/posix/sys_msg.odin +++ b/core/sys/posix/sys_msg.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -150,6 +151,24 @@ when ODIN_OS == .Darwin { msg_pad4: [4]c.long, } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + msgqnum_t :: distinct c.ulong + msglen_t :: distinct c.ulong + + MSG_NOERROR :: 0o10000 + + msqid_ds :: struct { + msg_perm: ipc_perm, /* [PSX] operation permission structure */ + msg_stime: time_t, /* [PSX] time of last msgsnd() */ + msg_rtime: time_t, /* [PSX] time of last msgrcv() */ + msg_ctime: time_t, /* [PSX] time of last change */ + msg_cbytes: c.ulong, + msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */ + msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */ + msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */ + msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */ + __unused: [2]c.ulong, + } + } diff --git a/core/sys/posix/sys_resource.odin b/core/sys/posix/sys_resource.odin index 55789ee95..9af2a929b 100644 --- a/core/sys/posix/sys_resource.odin +++ b/core/sys/posix/sys_resource.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -154,6 +155,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS RLIMIT_AS :: 10 } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin index c20636b21..2058ee777 100644 --- a/core/sys/posix/sys_select.odin +++ b/core/sys/posix/sys_select.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "base:intrinsics" @@ -72,7 +73,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS // NOTE: this seems correct for FreeBSD but they do use a set backed by the long type themselves (thus the align change). @(private) - ALIGN :: align_of(c.long) when ODIN_OS == .FreeBSD else align_of(c.int32_t) + ALIGN :: align_of(c.long) when ODIN_OS == .FreeBSD || ODIN_OS == .Linux else align_of(c.int32_t) fd_set :: struct #align(ALIGN) { fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t, @@ -115,6 +116,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS intrinsics.mem_zero(_p, size_of(fd_set)) } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_sem.odin b/core/sys/posix/sys_sem.odin index 2d7bd1b1e..6b695e766 100644 --- a/core/sys/posix/sys_sem.odin +++ b/core/sys/posix/sys_sem.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -153,6 +154,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS sem_flg: c.short, /* [PSX] operation flags */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_shm.odin b/core/sys/posix/sys_shm.odin index ff56ba441..8f3c56b9c 100644 --- a/core/sys/posix/sys_shm.odin +++ b/core/sys/posix/sys_shm.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -137,6 +138,24 @@ when ODIN_OS == .Darwin { _shm_internal: rawptr, } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + SHM_RDONLY :: 0o10000 + SHM_RND :: 0o20000 + + SHMLBA :: 4096 + + shmatt_t :: distinct c.ulong + + shmid_ds :: struct { + shm_perm: ipc_perm, /* [PSX] operation permission structure */ + shm_segsz: c.size_t, /* [PSX] size of segment in bytes */ + shm_atime: time_t, /* [PSX] time of last shmat() */ + shm_dtime: time_t, /* [PSX] time of last shmdt() */ + shm_ctime: time_t, /* [PSX] time of last change by shmctl() */ + shm_cpid: pid_t, /* [PSX] process ID of creator */ + shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ + shm_nattch: shmatt_t, /* [PSX] number of current attaches */ + _: [2]c.ulong, + } } diff --git a/core/sys/posix/sys_socket.odin b/core/sys/posix/sys_socket.odin index e613f0a10..3c9db5bfa 100644 --- a/core/sys/posix/sys_socket.odin +++ b/core/sys/posix/sys_socket.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -325,14 +326,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS socklen_t :: distinct c.uint - _sa_family_t :: distinct c.uint8_t - when ODIN_OS == .Linux { + _sa_family_t :: distinct c.ushort + sockaddr :: struct { sa_family: sa_family_t, /* [PSX] address family */ sa_data: [14]c.char, /* [PSX] socket address */ } } else { + _sa_family_t :: distinct c.uint8_t + sockaddr :: struct { sa_len: c.uint8_t, /* total length */ sa_family: sa_family_t, /* [PSX] address family */ @@ -560,7 +563,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS SHUT_RDWR :: 2 SHUT_WR :: 1 -} else { - #panic("posix is unimplemented for the current target") } - diff --git a/core/sys/posix/sys_stat.odin b/core/sys/posix/sys_stat.odin index dd66d7d14..61b98ef35 100644 --- a/core/sys/posix/sys_stat.odin +++ b/core/sys/posix/sys_stat.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -280,20 +281,20 @@ when ODIN_OS == .Darwin { ino_t :: distinct c.uint64_t stat_t :: struct { - st_dev: dev_t, /* [XSI] ID of device containing file */ - st_mode: mode_t, /* [XSI] mode of file */ - st_nlink: nlink_t, /* [XSI] number of hard links */ - st_ino: ino_t, /* [XSI] file serial number */ - st_uid: uid_t, /* [XSI] user ID of the file */ - st_gid: gid_t, /* [XSI] group ID of the file */ - st_rdev: dev_t, /* [XSI] device ID */ - st_atim: timespec, /* [XSI] time of last access */ - st_mtim: timespec, /* [XSI] time of last data modification */ - st_ctim: timespec, /* [XSI] time of last status change */ + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_mode: mode_t, /* [PSX] mode of file */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_ino: ino_t, /* [PSX] file serial number */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ + st_rdev: dev_t, /* [PSX] device ID */ + st_atim: timespec, /* [PSX] time of last access */ + st_mtim: timespec, /* [PSX] time of last data modification */ + st_ctim: timespec, /* [PSX] time of last status change */ st_birthtimespec: timespec, /* time of file creation(birth) */ - st_size: off_t, /* [XSI] file size, in bytes */ - st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ - st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_size: off_t, /* [PSX] file size, in bytes */ + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ st_flags: c.uint32_t, /* user defined flags for file */ st_gen: c.uint32_t, /* file generation number */ st_lspare: c.int32_t, /* RESERVED */ @@ -314,47 +315,47 @@ when ODIN_OS == .Darwin { when ODIN_ARCH == .i386 { stat_t :: struct { - st_dev: dev_t, /* [XSI] ID of device containing file */ - st_ino: ino_t, /* [XSI] file serial number */ - st_nlink: nlink_t, /* [XSI] number of hard links */ - st_mode: mode_t, /* [XSI] mode of file */ + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_ino: ino_t, /* [PSX] file serial number */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_mode: mode_t, /* [PSX] mode of file */ st_padding0: c.int16_t, - st_uid: uid_t, /* [XSI] user ID of the file */ - st_gid: gid_t, /* [XSI] group ID of the file */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ st_padding1: c.int32_t, - st_rdev: dev_t, /* [XSI] device ID */ + st_rdev: dev_t, /* [PSX] device ID */ st_atim_ext: c.int32_t, - st_atim: timespec, /* [XSI] time of last access */ + st_atim: timespec, /* [PSX] time of last access */ st_mtim_ext: c.int32_t, - st_mtim: timespec, /* [XSI] time of last data modification */ + st_mtim: timespec, /* [PSX] time of last data modification */ st_ctim_ext: c.int32_t, - st_ctim: timespec, /* [XSI] time of last status change */ + st_ctim: timespec, /* [PSX] time of last status change */ st_birthtimespec: timespec, /* time of file creation(birth) */ - st_size: off_t, /* [XSI] file size, in bytes */ - st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ - st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_size: off_t, /* [PSX] file size, in bytes */ + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ st_flags: c.uint32_t, /* user defined flags for file */ st_gen: c.uint64_t, st_spare: [10]c.uint64_t, } } else { stat_t :: struct { - st_dev: dev_t, /* [XSI] ID of device containing file */ - st_ino: ino_t, /* [XSI] file serial number */ - st_nlink: nlink_t, /* [XSI] number of hard links */ - st_mode: mode_t, /* [XSI] mode of file */ + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_ino: ino_t, /* [PSX] file serial number */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_mode: mode_t, /* [PSX] mode of file */ st_padding0: c.int16_t, - st_uid: uid_t, /* [XSI] user ID of the file */ - st_gid: gid_t, /* [XSI] group ID of the file */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ st_padding1: c.int32_t, - st_rdev: dev_t, /* [XSI] device ID */ - st_atim: timespec, /* [XSI] time of last access */ - st_mtim: timespec, /* [XSI] time of last data modification */ - st_ctim: timespec, /* [XSI] time of last status change */ + st_rdev: dev_t, /* [PSX] device ID */ + st_atim: timespec, /* [PSX] time of last access */ + st_mtim: timespec, /* [PSX] time of last data modification */ + st_ctim: timespec, /* [PSX] time of last status change */ st_birthtimespec: timespec, /* time of file creation(birth) */ - st_size: off_t, /* [XSI] file size, in bytes */ - st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ - st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_size: off_t, /* [PSX] file size, in bytes */ + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ st_flags: c.uint32_t, /* user defined flags for file */ st_gen: c.uint64_t, st_spare: [10]c.uint64_t, @@ -374,20 +375,20 @@ when ODIN_OS == .Darwin { ino_t :: distinct c.uint64_t stat_t :: struct { - st_dev: dev_t, /* [XSI] ID of device containing file */ - st_mode: mode_t, /* [XSI] mode of file */ - st_ino: ino_t, /* [XSI] file serial number */ - st_nlink: nlink_t, /* [XSI] number of hard links */ - st_uid: uid_t, /* [XSI] user ID of the file */ - st_gid: gid_t, /* [XSI] group ID of the file */ - st_rdev: dev_t, /* [XSI] device ID */ - st_atim: timespec, /* [XSI] time of last access */ - st_mtim: timespec, /* [XSI] time of last data modification */ - st_ctim: timespec, /* [XSI] time of last status change */ + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_mode: mode_t, /* [PSX] mode of file */ + st_ino: ino_t, /* [PSX] file serial number */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ + st_rdev: dev_t, /* [PSX] device ID */ + st_atim: timespec, /* [PSX] time of last access */ + st_mtim: timespec, /* [PSX] time of last data modification */ + st_ctim: timespec, /* [PSX] time of last status change */ st_birthtimespec: timespec, /* time of file creation(birth) */ - st_size: off_t, /* [XSI] file size, in bytes */ - st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ - st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_size: off_t, /* [PSX] file size, in bytes */ + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ st_flags: c.uint32_t, /* user defined flags for file */ st_gen: c.uint64_t, st_spare: [2]c.uint32_t, @@ -406,19 +407,19 @@ when ODIN_OS == .Darwin { ino_t :: distinct c.uint64_t stat_t :: struct { - st_mode: mode_t, /* [XSI] mode of file */ - st_dev: dev_t, /* [XSI] ID of device containing file */ - st_ino: ino_t, /* [XSI] file serial number */ - st_nlink: nlink_t, /* [XSI] number of hard links */ - st_uid: uid_t, /* [XSI] user ID of the file */ - st_gid: gid_t, /* [XSI] group ID of the file */ - st_rdev: dev_t, /* [XSI] device ID */ - st_atim: timespec, /* [XSI] time of last access */ - st_mtim: timespec, /* [XSI] time of last data modification */ - st_ctim: timespec, /* [XSI] time of last status change */ - st_size: off_t, /* [XSI] file size, in bytes */ - st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */ - st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */ + st_mode: mode_t, /* [PSX] mode of file */ + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_ino: ino_t, /* [PSX] file serial number */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ + st_rdev: dev_t, /* [PSX] device ID */ + st_atim: timespec, /* [PSX] time of last access */ + st_mtim: timespec, /* [PSX] time of last data modification */ + st_ctim: timespec, /* [PSX] time of last status change */ + st_size: off_t, /* [PSX] file size, in bytes */ + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ st_flags: c.uint32_t, /* user defined flags for file */ st_gen: c.int32_t, st_birthtimespec: timespec, @@ -427,6 +428,58 @@ when ODIN_OS == .Darwin { UTIME_NOW :: -2 UTIME_OMIT :: -1 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + dev_t :: distinct u64 + _mode_t :: distinct c.uint + blkcnt_t :: distinct i64 + + when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 { + nlink_t :: distinct c.uint + blksize_t :: distinct c.int + } else { + nlink_t :: distinct c.size_t + blksize_t :: distinct c.long + } + + ino_t :: distinct u64 + + when ODIN_ARCH == .amd64 { + stat_t :: struct { + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_ino: ino_t, /* [PSX] file serial number */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_mode: mode_t, /* [PSX] mode of file */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ + _pad0: c.uint, + st_rdev: dev_t, /* [PSX] device ID */ + st_size: off_t, /* [PSX] file size, in bytes */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_atim: timespec, /* [PSX] time of last access */ + st_mtim: timespec, /* [PSX] time of last data modification */ + st_ctim: timespec, /* [PSX] time of last status change */ + __unused: [3]c.long, + } + } else { + stat_t :: struct { + st_dev: dev_t, /* [PSX] ID of device containing file */ + st_ino: ino_t, /* [PSX] file serial number */ + st_mode: mode_t, /* [PSX] mode of file */ + st_nlink: nlink_t, /* [PSX] number of hard links */ + st_uid: uid_t, /* [PSX] user ID of the file */ + st_gid: gid_t, /* [PSX] group ID of the file */ + st_rdev: dev_t, /* [PSX] device ID */ + __pad: c.ulonglong, + st_size: off_t, /* [PSX] file size, in bytes */ + st_blksize: blksize_t, /* [PSX] optimal blocksize for I/O */ + __pad2: c.int, + st_blocks: blkcnt_t, /* [PSX] blocks allocated for file */ + st_atim: timespec, /* [PSX] time of last access */ + st_mtim: timespec, /* [PSX] time of last data modification */ + st_ctim: timespec, /* [PSX] time of last status change */ + __unused: [2]c.uint, + } + } } diff --git a/core/sys/posix/sys_statvfs.odin b/core/sys/posix/sys_statvfs.odin index eb6c16806..47c810135 100644 --- a/core/sys/posix/sys_statvfs.odin +++ b/core/sys/posix/sys_statvfs.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -130,6 +131,27 @@ when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD { ST_RDONLY :: 0x00000001 ST_NOSUID :: 0x00000008 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + fsblkcnt_t :: distinct c.uint64_t + + statvfs_t :: struct { + f_bsize: c.ulong, /* [PSX] file system block size */ + f_frsize: c.ulong, /* [PSX] fundamental file system block size */ + f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */ + f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */ + f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */ + f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */ + f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */ + f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */ + f_fsid: c.ulong, /* [PSX] file system ID */ + _: [2*size_of(c.int)-size_of(c.long)]byte, + f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */ + f_namemax: c.ulong, /* [PSX] maximum filename length */ + f_type: c.uint, + __reserved: [5]c.int, + } + + ST_RDONLY :: 0x00000001 + ST_NOSUID :: 0x00000002 } diff --git a/core/sys/posix/sys_time.odin b/core/sys/posix/sys_time.odin index fd2e58121..3036352aa 100644 --- a/core/sys/posix/sys_time.odin +++ b/core/sys/posix/sys_time.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -77,6 +78,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS ITIMER_VIRTUAL :: 1 ITIMER_PROF :: 2 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_times.odin b/core/sys/posix/sys_times.odin index d38f3efc4..113e3f963 100644 --- a/core/sys/posix/sys_times.odin +++ b/core/sys/posix/sys_times.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix when ODIN_OS == .Darwin { @@ -33,6 +34,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS tms_cstime: clock_t, /* [PSX] terminated children system CPU time */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_uio.odin b/core/sys/posix/sys_uio.odin index 6755fe2ae..a0ad2934e 100644 --- a/core/sys/posix/sys_uio.odin +++ b/core/sys/posix/sys_uio.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -37,6 +38,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS iov_len: c.size_t, /* [PSX] size of the region iov_base points to */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_un.odin b/core/sys/posix/sys_un.odin index 15eb7b5fc..ca5c4ee31 100644 --- a/core/sys/posix/sys_un.odin +++ b/core/sys/posix/sys_un.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -19,6 +20,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS sun_path: [108]c.char, /* [PSX] socket pathname */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin index 4786eb4fd..64930160f 100644 --- a/core/sys/posix/sys_utsname.odin +++ b/core/sys/posix/sys_utsname.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -37,15 +38,10 @@ foreign lib { uname :: proc(uname: ^utsname) -> c.int --- } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { - when ODIN_OS == .Linux { - @(private) - _SYS_NAMELEN :: 65 - } else { - @(private) - _SYS_NAMELEN :: 256 - } + @(private) + _SYS_NAMELEN :: 256 utsname :: struct { sysname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */ @@ -55,6 +51,17 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS machine: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] hardware type */ } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + @(private) + _SYS_NAMELEN :: 65 + + utsname :: struct { + sysname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */ + nodename: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of this network node */ + release: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] release level */ + version: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] version level */ + machine: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] hardware type */ + __domainname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, + } } diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin index e0e2ae21b..812bd8c62 100644 --- a/core/sys/posix/sys_wait.odin +++ b/core/sys/posix/sys_wait.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -391,6 +392,54 @@ when ODIN_OS == .Darwin { return (x & _WCONTINUED) == _WCONTINUED } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + id_t :: distinct c.uint + + WCONTINUED :: 8 + WNOHANG :: 1 + WUNTRACED :: 2 + + WEXITED :: 4 + WNOWAIT :: 0x1000000 + WSTOPPED :: 2 + + _P_ALL :: 0 + _P_PID :: 1 + _P_PGID :: 2 + + @(private) + _WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool { + return _WTERMSIG(x) == nil + } + + @(private) + _WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { + return (x & 0xff00) >> 8 + } + + @(private) + _WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool { + return (x & 0xffff) - 1 < 0xff + } + + @(private) + _WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(x & 0x7f) + } + + @(private) + _WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool { + return ((x & 0xffff) * 0x10001) >> 8 > 0x7f00 + } + + @(private) + _WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal { + return Signal(_WEXITSTATUS(x)) + } + + @(private) + _WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { + return x == 0xffff + } } diff --git a/core/sys/posix/termios.odin b/core/sys/posix/termios.odin index c73936d58..0c07eceb9 100644 --- a/core/sys/posix/termios.odin +++ b/core/sys/posix/termios.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -152,7 +153,7 @@ CControl_Flag_Bits :: enum tcflag_t { CControl_Flags :: bit_set[CControl_Flag_Bits; tcflag_t] // character size mask -CSIZE :: CControl_Flags{ .CS6, .CS7, .CS8 } +CSIZE :: transmute(CControl_Flags)tcflag_t(_CSIZE) COutput_Flag_Bits :: enum tcflag_t { OPOST = log2(OPOST), /* enable following output processing */ @@ -181,17 +182,17 @@ COutput_Flag_Bits :: enum tcflag_t { COutput_Flags :: bit_set[COutput_Flag_Bits; tcflag_t] // \n delay mask -NLDLY :: COutput_Flags{ .NL1, COutput_Flag_Bits(9) } +NLDLY :: transmute(COutput_Flags)tcflag_t(_NLDLY) // \r delay mask -CRDLY :: COutput_Flags{ .CR1, .CR2, .CR3 } +CRDLY :: transmute(COutput_Flags)tcflag_t(_CRDLY) // horizontal tab delay mask -TABDLY :: COutput_Flags{ .TAB1, .TAB3, COutput_Flag_Bits(2) } +TABDLY :: transmute(COutput_Flags)tcflag_t(_TABDLY) // \b delay mask -BSDLY :: COutput_Flags{ .BS1 } +BSDLY :: transmute(COutput_Flags)tcflag_t(_BSDLY) // vertical tab delay mask -VTDLY :: COutput_Flags{ .VT1 } +VTDLY :: transmute(COutput_Flags)tcflag_t(_VTDLY) // form feed delay mask -FFDLY :: COutput_Flags{ .FF1 } +FFDLY :: transmute(COutput_Flags)tcflag_t(_FFDLY) speed_t :: enum _speed_t { B0 = B0, @@ -596,6 +597,4 @@ when ODIN_OS == .Darwin { TCOOFF :: 0 TCOON :: 1 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin index 5c6ebcf2f..f9c51c63c 100644 --- a/core/sys/posix/time.odin +++ b/core/sys/posix/time.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -229,6 +230,16 @@ when ODIN_OS == .Darwin { getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on OpenBSD. -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + clockid_t :: distinct c.int + + CLOCK_MONOTONIC :: 1 + CLOCK_PROCESS_CPUTIME_ID :: 2 + CLOCK_REALTIME :: 0 + CLOCK_THREAD_CPUTIME_ID :: 3 + + foreign lib { + getdate_err: Errno + } } diff --git a/core/sys/posix/ulimit.odin b/core/sys/posix/ulimit.odin index 782756f6e..0f87641fa 100644 --- a/core/sys/posix/ulimit.odin +++ b/core/sys/posix/ulimit.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -38,6 +39,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS // NOTE: I don't think OpenBSD implements this API. -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin index 5b428ef6f..0526b3235 100644 --- a/core/sys/posix/unistd.odin +++ b/core/sys/posix/unistd.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -11,19 +12,6 @@ when ODIN_OS == .Darwin { // unistd.h - standard symbolic constants and types foreign lib { - /* - Checks the file named by the pathname pointed to by the path argument for - accessibility according to the bit pattern contained in amode. - - Example: - if (posix.access("/tmp/myfile", posix.F_OK) != .OK) { - fmt.printfln("/tmp/myfile access check failed: %v", posix.strerror(posix.errno())) - } - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]] - */ - access :: proc(path: cstring, amode: Mode_Flags = F_OK) -> result --- - /* Equivalent to `access` but relative paths are resolved based on `fd`. @@ -42,18 +30,6 @@ foreign lib { */ alarm :: proc(seconds: c.uint) -> c.uint --- - /* - Causes the directory named by path to become the current working directory. - - Example: - if (posix.chdir("/tmp") == .OK) { - fmt.println("changed current directory to /tmp") - } - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html ]] - */ - chdir :: proc(path: cstring) -> result --- - /* Equivalent to chdir but instead of a path the fildes is resolved to a directory. @@ -204,15 +180,6 @@ foreign lib { */ dup2 :: proc(fildes, fildes2: FD) -> FD --- - /* - Exits but, shall not call functions registered with atexit() nor any registered signal handlers. - Open streams shall not be flushed. - Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]] - */ - _exit :: proc(status: c.int) -> ! --- - /* The exec family of functions shall replace the current process image with a new process image. The new image shall be constructed from a regular, executable file called the new process image file. @@ -392,44 +359,6 @@ foreign lib { */ ftruncate :: proc(fildes: FD, length: off_t) -> result --- - /* - Places an absolute pathname of the current working directory into buf. - - Returns: buf as a cstring on success, nil (setting errno) on failure - - Example: - size: int - path_max := posix.pathconf(".", ._PATH_MAX) - if path_max == -1 { - size = 1024 - } else if path_max > 10240 { - size = 10240 - } else { - size = int(path_max) - } - - buf: [dynamic]byte - cwd: cstring - for ; cwd == nil; size *= 2 { - if err := resize(&buf, size); err != nil { - fmt.panicf("allocation failure: %v", err) - } - - cwd = posix.getcwd(raw_data(buf), len(buf)) - if cwd == nil { - errno := posix.errno() - if errno != .ERANGE { - fmt.panicf("getcwd failure: %v", posix.strerror(errno)) - } - } - } - - fmt.println(path_max, cwd) - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html ]] - */ - getcwd :: proc(buf: [^]c.char, size: c.size_t) -> cstring --- - /* Returns the effective group ID of the calling process. @@ -829,13 +758,6 @@ foreign lib { */ readlinkat :: proc(fd: FD, path: cstring, buf: [^]byte, bufsize: c.size_t) -> c.ssize_t --- - /* - Remove an (empty) directory. - - ]] More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html ]] - */ - rmdir :: proc(path: cstring) -> result --- - /* Set the effective group ID. @@ -912,13 +834,6 @@ foreign lib { */ sleep :: proc(seconds: c.uint) -> c.uint --- - /* - Copy nbyte bytes, from src, to dest, exchanging adjecent bytes. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/swab.html ]] - */ - swab :: proc(src: [^]byte, dest: [^]byte, nbytes: c.ssize_t) --- - /* Schedule file system updates. @@ -958,13 +873,6 @@ foreign lib { */ ttyname_r :: proc(fildes: FD, name: [^]byte, namesize: c.size_t) -> Errno --- - /* - Remove a directory entry. - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]] - */ - unlink :: proc(path: cstring) -> result --- - /* Equivalent to unlink or rmdir (if flag is .REMOVEDIR) but relative paths are relative to the dir fd. @@ -973,20 +881,6 @@ foreign lib { unlinkat :: proc(fd: FD, path: cstring, flag: AT_Flags) -> result --- } -STDERR_FILENO :: 2 -STDIN_FILENO :: 0 -STDOUT_FILENO :: 1 - -Mode_Flag_Bits :: enum c.int { - X_OK = log2(X_OK), - W_OK = log2(W_OK), - R_OK = log2(R_OK), -} -Mode_Flags :: bit_set[Mode_Flag_Bits; c.int] - -#assert(_F_OK == 0) -F_OK :: Mode_Flags{} - CS :: enum c.int { _PATH = _CS_PATH, _POSIX_V6_ILP32_OFF32_CFLAGS = _CS_POSIX_V6_ILP32_OFF32_CFLAGS, @@ -2063,6 +1957,7 @@ when ODIN_OS == .Darwin { _SC_TYPED_MEMORY_OBJECTS :: 165 _SC_2_PBS :: 168 _SC_2_PBS_ACCOUNTING :: 169 + _SC_2_PBS_LOCATE :: 170 _SC_2_PBS_MESSAGE :: 171 _SC_2_PBS_TRACK :: 172 _SC_SYMLOOP_MAX :: 173 @@ -2097,7 +1992,4 @@ when ODIN_OS == .Darwin { // NOTE: Not implemented. _POSIX_VDISABLE :: 0 -} else { - #panic("posix is unimplemented for the current target") } - diff --git a/core/sys/posix/unistd_libc.odin b/core/sys/posix/unistd_libc.odin new file mode 100644 index 000000000..bbfe3d59d --- /dev/null +++ b/core/sys/posix/unistd_libc.odin @@ -0,0 +1,153 @@ +#+build linux, windows, darwin, netbsd, openbsd, freebsd +package posix + +import "core:c" + +when ODIN_OS == .Windows { + foreign import lib "system:libucrt.lib" +} else when ODIN_OS == .Darwin { + foreign import lib "system:System.framework" +} else { + foreign import lib "system:c" +} + +// unistd.h - standard symbolic constants and types + +foreign lib { + /* + Checks the file named by the pathname pointed to by the path argument for + accessibility according to the bit pattern contained in amode. + + Example: + if (posix.access("/tmp/myfile", posix.F_OK) != .OK) { + fmt.printfln("/tmp/myfile access check failed: %v", posix.strerror(posix.errno())) + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]] + */ + @(link_name=LACCESS) + access :: proc(path: cstring, amode: Mode_Flags = F_OK) -> result --- + + /* + Causes the directory named by path to become the current working directory. + + Example: + if (posix.chdir("/tmp") == .OK) { + fmt.println("changed current directory to /tmp") + } + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html ]] + */ + @(link_name=LCHDIR) + chdir :: proc(path: cstring) -> result --- + + /* + Exits but, shall not call functions registered with atexit() nor any registered signal handlers. + Open streams shall not be flushed. + Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]] + */ + _exit :: proc(status: c.int) -> ! --- + + /* + Places an absolute pathname of the current working directory into buf. + + Returns: buf as a cstring on success, nil (setting errno) on failure + + Example: + size: int + path_max := posix.pathconf(".", ._PATH_MAX) + if path_max == -1 { + size = 1024 + } else if path_max > 10240 { + size = 10240 + } else { + size = int(path_max) + } + + buf: [dynamic]byte + cwd: cstring + for ; cwd == nil; size *= 2 { + if err := resize(&buf, size); err != nil { + fmt.panicf("allocation failure: %v", err) + } + + cwd = posix.getcwd(raw_data(buf), len(buf)) + if cwd == nil { + errno := posix.errno() + if errno != .ERANGE { + fmt.panicf("getcwd failure: %v", posix.strerror(errno)) + } + } + } + + fmt.println(path_max, cwd) + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html ]] + */ + @(link_name=LGETCWD) + getcwd :: proc(buf: [^]c.char, size: c.size_t) -> cstring --- + + /* + Remove an (empty) directory. + + ]] More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html ]] + */ + @(link_name=LRMDIR) + rmdir :: proc(path: cstring) -> result --- + + /* + Copy nbyte bytes, from src, to dest, exchanging adjecent bytes. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/swab.html ]] + */ + @(link_name=LSWAB) + swab :: proc(src: [^]byte, dest: [^]byte, nbytes: c.ssize_t) --- + + /* + Remove a directory entry. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]] + */ + @(link_name=LUNLINK) + unlink :: proc(path: cstring) -> result --- +} + +when ODIN_OS == .Windows { + @(private) LACCESS :: "_access" + @(private) LCHDIR :: "_chdir" + @(private) LGETCWD :: "_getcwd" + @(private) LRMDIR :: "_rmdir" + @(private) LSWAB :: "_swab" + @(private) LUNLINK :: "_unlink" +} else { + @(private) LACCESS :: "access" + @(private) LCHDIR :: "chdir" + @(private) LGETCWD :: "getcwd" + @(private) LRMDIR :: "rmdir" + @(private) LSWAB :: "swab" + @(private) LUNLINK :: "unlink" +} + +STDERR_FILENO :: 2 +STDIN_FILENO :: 0 +STDOUT_FILENO :: 1 + +Mode_Flag_Bits :: enum c.int { + X_OK = log2(X_OK), + W_OK = log2(W_OK), + R_OK = log2(R_OK), +} +Mode_Flags :: bit_set[Mode_Flag_Bits; c.int] + +#assert(_F_OK == 0) +F_OK :: Mode_Flags{} + +when ODIN_OS == .Windows { + _F_OK :: 0 + X_OK :: 1 + W_OK :: 2 + R_OK :: 4 + #assert(W_OK|R_OK == 6) +} diff --git a/core/sys/posix/utime.odin b/core/sys/posix/utime.odin index 1207cb402..e884eb1a3 100644 --- a/core/sys/posix/utime.odin +++ b/core/sys/posix/utime.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix when ODIN_OS == .Darwin { @@ -31,6 +32,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS modtime: time_t, /* [PSX] modification time (seconds since epoch) */ } -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/posix/wordexp.odin b/core/sys/posix/wordexp.odin index 6bb362625..a9e6f39a7 100644 --- a/core/sys/posix/wordexp.odin +++ b/core/sys/posix/wordexp.odin @@ -1,3 +1,4 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix import "core:c" @@ -123,6 +124,4 @@ when ODIN_OS == .Darwin { WRDE_CMDSUB :: 4 WRDE_SYNTAX :: 5 -} else { - #panic("posix is unimplemented for the current target") } diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin deleted file mode 100644 index eb2cc4c9f..000000000 --- a/core/sys/unix/pthread_darwin.odin +++ /dev/null @@ -1,96 +0,0 @@ -#+build darwin -package unix - -import "core:c" - -// NOTE(tetra): No 32-bit Macs. -// Source: _pthread_types.h on my Mac. -PTHREAD_SIZE :: 8176 -PTHREAD_ATTR_SIZE :: 56 -PTHREAD_MUTEXATTR_SIZE :: 8 -PTHREAD_MUTEX_SIZE :: 56 -PTHREAD_CONDATTR_SIZE :: 8 -PTHREAD_COND_SIZE :: 40 -PTHREAD_ONCE_SIZE :: 8 -PTHREAD_RWLOCK_SIZE :: 192 -PTHREAD_RWLOCKATTR_SIZE :: 16 - -pthread_t :: distinct u64 - -pthread_attr_t :: struct { - sig: c.long, - _: [PTHREAD_ATTR_SIZE] c.char, -} - -pthread_cond_t :: struct { - sig: c.long, - _: [PTHREAD_COND_SIZE] c.char, -} - -pthread_condattr_t :: struct { - sig: c.long, - _: [PTHREAD_CONDATTR_SIZE] c.char, -} - -pthread_mutex_t :: struct { - sig: c.long, - _: [PTHREAD_MUTEX_SIZE] c.char, -} - -pthread_mutexattr_t :: struct { - sig: c.long, - _: [PTHREAD_MUTEXATTR_SIZE] c.char, -} - -pthread_once_t :: struct { - sig: c.long, - _: [PTHREAD_ONCE_SIZE] c.char, -} - -pthread_rwlock_t :: struct { - sig: c.long, - _: [PTHREAD_RWLOCK_SIZE] c.char, -} - -pthread_rwlockattr_t :: struct { - sig: c.long, - _: [PTHREAD_RWLOCKATTR_SIZE] c.char, -} - -SCHED_OTHER :: 1 // Avoid if you are writing portable software. -SCHED_FIFO :: 4 -SCHED_RR :: 2 // Round robin. - -SCHED_PARAM_SIZE :: 4 - -sched_param :: struct { - sched_priority: c.int, - _: [SCHED_PARAM_SIZE] c.char, -} - -// Source: https://github.com/apple/darwin-libpthread/blob/03c4628c8940cca6fd6a82957f683af804f62e7f/pthread/pthread.h#L138 -PTHREAD_CREATE_JOINABLE :: 1 -PTHREAD_CREATE_DETACHED :: 2 -PTHREAD_INHERIT_SCHED :: 1 -PTHREAD_EXPLICIT_SCHED :: 2 -PTHREAD_PROCESS_SHARED :: 1 -PTHREAD_PROCESS_PRIVATE :: 2 - - -PTHREAD_MUTEX_NORMAL :: 0 -PTHREAD_MUTEX_RECURSIVE :: 1 -PTHREAD_MUTEX_ERRORCHECK :: 2 - -PTHREAD_CANCEL_ENABLE :: 0 -PTHREAD_CANCEL_DISABLE :: 1 -PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 1 - -foreign import pthread "system:System.framework" - -@(default_calling_convention="c") -foreign pthread { - pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- - pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- - pthread_cancel :: proc (thread: pthread_t) -> c.int --- -} diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin deleted file mode 100644 index 38fe7db55..000000000 --- a/core/sys/unix/pthread_freebsd.odin +++ /dev/null @@ -1,122 +0,0 @@ -#+build freebsd -package unix - -import "core:c" - -pthread_t :: distinct u64 -// pthread_t :: struct #align(16) { x: u64 } - -PTHREAD_COND_T_SIZE :: 8 - -PTHREAD_MUTEXATTR_T_SIZE :: 8 -PTHREAD_CONDATTR_T_SIZE :: 8 -PTHREAD_RWLOCKATTR_T_SIZE :: 8 -PTHREAD_BARRIERATTR_T_SIZE :: 8 - -// WARNING: The sizes of these things are different yet again -// on non-X86! -when size_of(int) == 8 { - PTHREAD_ATTR_T_SIZE :: 8 - PTHREAD_MUTEX_T_SIZE :: 8 - PTHREAD_RWLOCK_T_SIZE :: 8 - PTHREAD_BARRIER_T_SIZE :: 8 -} else when size_of(int) == 4 { // TODO - PTHREAD_ATTR_T_SIZE :: 32 - PTHREAD_MUTEX_T_SIZE :: 32 - PTHREAD_RWLOCK_T_SIZE :: 44 - PTHREAD_BARRIER_T_SIZE :: 20 -} - -pthread_cond_t :: struct #align(16) { - _: [PTHREAD_COND_T_SIZE] c.char, -} -pthread_mutex_t :: struct #align(16) { - _: [PTHREAD_MUTEX_T_SIZE] c.char, -} -pthread_rwlock_t :: struct #align(16) { - _: [PTHREAD_RWLOCK_T_SIZE] c.char, -} -pthread_barrier_t :: struct #align(16) { - _: [PTHREAD_BARRIER_T_SIZE] c.char, -} - -pthread_attr_t :: struct #align(16) { - _: [PTHREAD_ATTR_T_SIZE] c.char, -} -pthread_condattr_t :: struct #align(16) { - _: [PTHREAD_CONDATTR_T_SIZE] c.char, -} -pthread_mutexattr_t :: struct #align(16) { - _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, -} -pthread_rwlockattr_t :: struct #align(16) { - _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, -} -pthread_barrierattr_t :: struct #align(16) { - _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, -} - -PTHREAD_MUTEX_ERRORCHECK :: 1 -PTHREAD_MUTEX_RECURSIVE :: 2 -PTHREAD_MUTEX_NORMAL :: 3 - - -PTHREAD_CREATE_JOINABLE :: 0 -PTHREAD_CREATE_DETACHED :: 1 -PTHREAD_INHERIT_SCHED :: 4 -PTHREAD_EXPLICIT_SCHED :: 0 -PTHREAD_PROCESS_PRIVATE :: 0 -PTHREAD_PROCESS_SHARED :: 1 - -SCHED_FIFO :: 1 -SCHED_OTHER :: 2 -SCHED_RR :: 3 // Round robin. - - -sched_param :: struct { - sched_priority: c.int, -} - -_usem :: struct { - _has_waiters: u32, - _count: u32, - _flags: u32, -} -_usem2 :: struct { - _count: u32, - _flags: u32, -} -sem_t :: struct { - _magic: u32, - _kern: _usem2, - _padding: u32, -} - -PTHREAD_CANCEL_ENABLE :: 0 -PTHREAD_CANCEL_DISABLE :: 1 -PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 2 - -foreign import "system:pthread" - -@(default_calling_convention="c") -foreign pthread { - // create named semaphore. - // used in process-shared semaphores. - sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- - sem_destroy :: proc(sem: ^sem_t) -> c.int --- - sem_post :: proc(sem: ^sem_t) -> c.int --- - sem_wait :: proc(sem: ^sem_t) -> c.int --- - sem_trywait :: proc(sem: ^sem_t) -> c.int --- - // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int --- - - // NOTE: unclear whether pthread_yield is well-supported on Linux systems, - // see https://linux.die.net/man/3/pthread_yield - pthread_yield :: proc() --- - - pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- - pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- - pthread_cancel :: proc (thread: pthread_t) -> c.int --- -} diff --git a/core/sys/unix/pthread_haiku.odin b/core/sys/unix/pthread_haiku.odin deleted file mode 100644 index 1278f34fe..000000000 --- a/core/sys/unix/pthread_haiku.odin +++ /dev/null @@ -1,71 +0,0 @@ -package unix - -import "core:c" - -pthread_t :: distinct rawptr -pthread_attr_t :: distinct rawptr -pthread_mutex_t :: distinct rawptr -pthread_mutexattr_t :: distinct rawptr -pthread_cond_t :: distinct rawptr -pthread_condattr_t :: distinct rawptr -pthread_rwlock_t :: distinct rawptr -pthread_rwlockattr_t :: distinct rawptr -pthread_barrier_t :: distinct rawptr -pthread_barrierattr_t :: distinct rawptr -pthread_spinlock_t :: distinct rawptr - -pthread_key_t :: distinct c.int -pthread_once_t :: struct { - state: c.int, - mutex: pthread_mutex_t, -} - -PTHREAD_MUTEX_DEFAULT :: 0 -PTHREAD_MUTEX_NORMAL :: 1 -PTHREAD_MUTEX_ERRORCHECK :: 2 -PTHREAD_MUTEX_RECURSIVE :: 3 - -PTHREAD_DETACHED :: 0x1 -PTHREAD_SCOPE_SYSTEM :: 0x2 -PTHREAD_INHERIT_SCHED :: 0x4 -PTHREAD_NOFLOAT :: 0x8 - -PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED -PTHREAD_CREATE_JOINABLE :: 0 -PTHREAD_SCOPE_PROCESS :: 0 -PTHREAD_EXPLICIT_SCHED :: 0 - -SCHED_FIFO :: 1 -SCHED_RR :: 2 -SCHED_SPORADIC :: 3 -SCHED_OTHER :: 4 - -sched_param :: struct { - sched_priority: c.int, -} - -sem_t :: distinct rawptr - -PTHREAD_CANCEL_ENABLE :: 0 -PTHREAD_CANCEL_DISABLE :: 1 -PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 2 - -foreign import libc "system:c" - -@(default_calling_convention="c") -foreign libc { - sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- - sem_destroy :: proc(sem: ^sem_t) -> c.int --- - sem_post :: proc(sem: ^sem_t) -> c.int --- - sem_wait :: proc(sem: ^sem_t) -> c.int --- - sem_trywait :: proc(sem: ^sem_t) -> c.int --- - - pthread_yield :: proc() --- - - pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- - pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- - pthread_cancel :: proc (thread: pthread_t) -> c.int --- -} diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin deleted file mode 100644 index d67add24b..000000000 --- a/core/sys/unix/pthread_linux.odin +++ /dev/null @@ -1,124 +0,0 @@ -#+build linux -package unix - -import "core:c" - -// TODO(tetra): For robustness, I'd like to mark this with align 16. -// I cannot currently do this. -// And at the time of writing there is a bug with putting it -// as the only field in a struct. -pthread_t :: distinct u64 -// pthread_t :: struct #align(16) { x: u64 }; - -// NOTE(tetra): Got all the size constants from pthreadtypes-arch.h on my -// Linux machine. - -PTHREAD_COND_T_SIZE :: 48 - -PTHREAD_MUTEXATTR_T_SIZE :: 4 -PTHREAD_CONDATTR_T_SIZE :: 4 -PTHREAD_RWLOCKATTR_T_SIZE :: 8 -PTHREAD_BARRIERATTR_T_SIZE :: 4 - -// WARNING: The sizes of these things are different yet again -// on non-X86! -when size_of(int) == 8 { - PTHREAD_ATTR_T_SIZE :: 56 - PTHREAD_MUTEX_T_SIZE :: 40 - PTHREAD_RWLOCK_T_SIZE :: 56 - PTHREAD_BARRIER_T_SIZE :: 32 -} else when size_of(int) == 4 { - PTHREAD_ATTR_T_SIZE :: 32 - PTHREAD_MUTEX_T_SIZE :: 32 - PTHREAD_RWLOCK_T_SIZE :: 44 - PTHREAD_BARRIER_T_SIZE :: 20 -} - -pthread_cond_t :: struct #align(16) { - _: [PTHREAD_COND_T_SIZE] c.char, -} -pthread_mutex_t :: struct #align(16) { - _: [PTHREAD_MUTEX_T_SIZE] c.char, -} -pthread_rwlock_t :: struct #align(16) { - _: [PTHREAD_RWLOCK_T_SIZE] c.char, -} -pthread_barrier_t :: struct #align(16) { - _: [PTHREAD_BARRIER_T_SIZE] c.char, -} - -pthread_attr_t :: struct #align(16) { - _: [PTHREAD_ATTR_T_SIZE] c.char, -} -pthread_condattr_t :: struct #align(16) { - _: [PTHREAD_CONDATTR_T_SIZE] c.char, -} -pthread_mutexattr_t :: struct #align(16) { - _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, -} -pthread_rwlockattr_t :: struct #align(16) { - _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, -} -pthread_barrierattr_t :: struct #align(16) { - _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, -} - -PTHREAD_MUTEX_NORMAL :: 0 -PTHREAD_MUTEX_RECURSIVE :: 1 -PTHREAD_MUTEX_ERRORCHECK :: 2 - - -// TODO(tetra, 2019-11-01): Maybe make `enum c.int`s for these? -PTHREAD_CREATE_JOINABLE :: 0 -PTHREAD_CREATE_DETACHED :: 1 -PTHREAD_INHERIT_SCHED :: 0 -PTHREAD_EXPLICIT_SCHED :: 1 -PTHREAD_PROCESS_PRIVATE :: 0 -PTHREAD_PROCESS_SHARED :: 1 - -SCHED_OTHER :: 0 -SCHED_FIFO :: 1 -SCHED_RR :: 2 // Round robin. - -sched_param :: struct { - sched_priority: c.int, -} - -sem_t :: struct #align(16) { - _: [SEM_T_SIZE] c.char, -} - -when size_of(int) == 8 { - SEM_T_SIZE :: 32 -} else when size_of(int) == 4 { - SEM_T_SIZE :: 16 -} - -PTHREAD_CANCEL_ENABLE :: 0 -PTHREAD_CANCEL_DISABLE :: 1 -PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 1 - -foreign import "system:pthread" - -@(default_calling_convention="c") -foreign pthread { - // create named semaphore. - // used in process-shared semaphores. - sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- - sem_destroy :: proc(sem: ^sem_t) -> c.int --- - sem_post :: proc(sem: ^sem_t) -> c.int --- - sem_wait :: proc(sem: ^sem_t) -> c.int --- - sem_trywait :: proc(sem: ^sem_t) -> c.int --- - // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---; - - // NOTE: unclear whether pthread_yield is well-supported on Linux systems, - // see https://linux.die.net/man/3/pthread_yield - pthread_yield :: proc() -> c.int --- - - pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- - pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- - pthread_cancel :: proc (thread: pthread_t) -> c.int --- -} diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin deleted file mode 100644 index 9107f1139..000000000 --- a/core/sys/unix/pthread_netbsd.odin +++ /dev/null @@ -1,102 +0,0 @@ -package unix - -import "core:c" - -pthread_t :: distinct rawptr - -SEM_T_SIZE :: 8 - -PTHREAD_CONDATTR_T_SIZE :: 16 -PTHREAD_MUTEXATTR_T_SIZE :: 16 -PTHREAD_RWLOCKATTR_T_SIZE :: 16 -PTHREAD_BARRIERATTR_T_SIZE :: 16 - -PTHREAD_COND_T_SIZE :: 40 -PTHREAD_MUTEX_T_SIZE :: 48 -PTHREAD_RWLOCK_T_SIZE :: 64 -PTHREAD_BARRIER_T_SIZE :: 48 -PTHREAD_ATTR_T_SIZE :: 16 - -pthread_cond_t :: struct #align(8) { - _: [PTHREAD_COND_T_SIZE] c.char, -} - -pthread_mutex_t :: struct #align(8) { - _: [PTHREAD_MUTEX_T_SIZE] c.char, -} - -pthread_rwlock_t :: struct #align(8) { - _: [PTHREAD_RWLOCK_T_SIZE] c.char, -} - -pthread_barrier_t :: struct #align(8) { - _: [PTHREAD_BARRIER_T_SIZE] c.char, -} - -pthread_attr_t :: struct #align(8) { - _: [PTHREAD_ATTR_T_SIZE] c.char, -} - -pthread_condattr_t :: struct #align(8) { - _: [PTHREAD_CONDATTR_T_SIZE] c.char, -} - -pthread_mutexattr_t :: struct #align(8) { - _: [PTHREAD_MUTEXATTR_T_SIZE] c.char, -} - -pthread_rwlockattr_t :: struct #align(8) { - _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char, -} - -pthread_barrierattr_t :: struct #align(8) { - _: [PTHREAD_BARRIERATTR_T_SIZE] c.char, -} - -PTHREAD_MUTEX_NORMAL :: 0 -PTHREAD_MUTEX_ERRORCHECK :: 1 -PTHREAD_MUTEX_RECURSIVE :: 2 - -PTHREAD_CREATE_JOINABLE :: 0 -PTHREAD_CREATE_DETACHED :: 1 -PTHREAD_INHERIT_SCHED :: 0 -PTHREAD_EXPLICIT_SCHED :: 1 -PTHREAD_PROCESS_PRIVATE :: 0 -PTHREAD_PROCESS_SHARED :: 1 - -SCHED_NONE :: -1 -SCHED_OTHER :: 0 -SCHED_FIFO :: 1 -SCHED_RR :: 3 - -sched_param :: struct { - sched_priority: c.int, -} - -sem_t :: struct #align(16) { - _: [SEM_T_SIZE] c.char, -} - -PTHREAD_CANCEL_ENABLE :: 0 -PTHREAD_CANCEL_DISABLE :: 1 -PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 1 - -foreign import "system:pthread" - -@(default_calling_convention="c") -foreign pthread { - sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- - sem_destroy :: proc(sem: ^sem_t) -> c.int --- - sem_post :: proc(sem: ^sem_t) -> c.int --- - sem_wait :: proc(sem: ^sem_t) -> c.int --- - sem_trywait :: proc(sem: ^sem_t) -> c.int --- - - pthread_yield :: proc() --- - - pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- - pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- - pthread_cancel :: proc (thread: pthread_t) -> c.int --- -} diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin deleted file mode 100644 index 2c6d9e598..000000000 --- a/core/sys/unix/pthread_openbsd.odin +++ /dev/null @@ -1,74 +0,0 @@ -#+build openbsd -package unix - -import "core:c" - -pthread_t :: distinct rawptr -pthread_attr_t :: distinct rawptr -pthread_mutex_t :: distinct rawptr -pthread_mutexattr_t :: distinct rawptr -pthread_cond_t :: distinct rawptr -pthread_condattr_t :: distinct rawptr -pthread_rwlock_t :: distinct rawptr -pthread_rwlockattr_t :: distinct rawptr -pthread_barrier_t :: distinct rawptr -pthread_barrierattr_t :: distinct rawptr -pthread_spinlock_t :: distinct rawptr - -pthread_key_t :: distinct c.int -pthread_once_t :: struct { - state: c.int, - mutex: pthread_mutex_t, -} - -PTHREAD_MUTEX_ERRORCHECK :: 1 -PTHREAD_MUTEX_RECURSIVE :: 2 -PTHREAD_MUTEX_NORMAL :: 3 -PTHREAD_MUTEX_STRICT_NP :: 4 - -PTHREAD_DETACHED :: 0x1 -PTHREAD_SCOPE_SYSTEM :: 0x2 -PTHREAD_INHERIT_SCHED :: 0x4 -PTHREAD_NOFLOAT :: 0x8 - -PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED -PTHREAD_CREATE_JOINABLE :: 0 -PTHREAD_SCOPE_PROCESS :: 0 -PTHREAD_EXPLICIT_SCHED :: 0 - -SCHED_FIFO :: 1 -SCHED_OTHER :: 2 -SCHED_RR :: 3 - -sched_param :: struct { - sched_priority: c.int, -} - -sem_t :: distinct rawptr - -PTHREAD_CANCEL_ENABLE :: 0 -PTHREAD_CANCEL_DISABLE :: 1 -PTHREAD_CANCEL_DEFERRED :: 0 -PTHREAD_CANCEL_ASYNCHRONOUS :: 2 - -foreign import libc "system:c" - -@(default_calling_convention="c") -foreign libc { - sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t --- - - sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int --- - sem_destroy :: proc(sem: ^sem_t) -> c.int --- - sem_post :: proc(sem: ^sem_t) -> c.int --- - sem_wait :: proc(sem: ^sem_t) -> c.int --- - sem_trywait :: proc(sem: ^sem_t) -> c.int --- - //sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int --- - - // NOTE: unclear whether pthread_yield is well-supported on Linux systems, - // see https://linux.die.net/man/3/pthread_yield - pthread_yield :: proc() --- - - pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int --- - pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int --- - pthread_cancel :: proc (thread: pthread_t) -> c.int --- -} diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin deleted file mode 100644 index 43c4866ed..000000000 --- a/core/sys/unix/pthread_unix.odin +++ /dev/null @@ -1,127 +0,0 @@ -#+build linux, darwin, freebsd, openbsd, netbsd, haiku -package unix - -foreign import "system:pthread" - -import "core:c" - -timespec :: struct { - tv_sec: i64, - tv_nsec: i64, -} - -// -// On success, these functions return 0. -// - -@(default_calling_convention="c") -foreign pthread { - pthread_create :: proc(t: ^pthread_t, attrs: ^pthread_attr_t, routine: proc(data: rawptr) -> rawptr, arg: rawptr) -> c.int --- - - // retval is a pointer to a location to put the return value of the thread proc. - pthread_join :: proc(t: pthread_t, retval: ^rawptr) -> c.int --- - - pthread_kill :: proc(t: pthread_t, sig: c.int) -> c.int --- - - pthread_self :: proc() -> pthread_t --- - - pthread_equal :: proc(a, b: pthread_t) -> b32 --- - - pthread_detach :: proc(t: pthread_t) -> c.int --- - - sched_get_priority_min :: proc(policy: c.int) -> c.int --- - sched_get_priority_max :: proc(policy: c.int) -> c.int --- - - // NOTE: POSIX says this can fail with OOM. - pthread_attr_init :: proc(attrs: ^pthread_attr_t) -> c.int --- - - pthread_attr_destroy :: proc(attrs: ^pthread_attr_t) -> c.int --- - - pthread_attr_getschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int --- - pthread_attr_setschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int --- - - // states: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE - pthread_attr_setdetachstate :: proc(attrs: ^pthread_attr_t, detach_state: c.int) -> c.int --- - - // NOTE(tetra, 2019-11-06): WARNING: Different systems have different alignment requirements. - // For maximum usefulness, use the OS's page size. - // ALSO VERY MAJOR WARNING: `stack_ptr` must be the LAST byte of the stack on systems - // where the stack grows downwards, which is the common case, so far as I know. - // On systems where it grows upwards, give the FIRST byte instead. - // ALSO SLIGHTLY LESS MAJOR WARNING: Using this procedure DISABLES automatically-provided - // guard pages. If you are using this procedure, YOU must set them up manually. - // If you forget to do this, you WILL get stack corruption bugs if you do not EXTREMELY - // know what you are doing! - pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int --- - pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int --- - - pthread_sigmask :: proc(how: c.int, set: rawptr, oldset: rawptr) -> c.int --- - - sched_yield :: proc() -> c.int --- -} - -// NOTE: Unimplemented in Haiku. -when ODIN_OS != .Haiku { - foreign pthread { - // scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED - pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int --- - - pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int --- - pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int --- - } -} - -@(default_calling_convention="c") -foreign pthread { - // NOTE: POSIX says this can fail with OOM. - pthread_cond_init :: proc(cond: ^pthread_cond_t, attrs: ^pthread_condattr_t) -> c.int --- - - pthread_cond_destroy :: proc(cond: ^pthread_cond_t) -> c.int --- - - pthread_cond_signal :: proc(cond: ^pthread_cond_t) -> c.int --- - - // same as signal, but wakes up _all_ threads that are waiting - pthread_cond_broadcast :: proc(cond: ^pthread_cond_t) -> c.int --- - - - // assumes the mutex is pre-locked - pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int --- - pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int --- - - pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int --- - pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int --- - - // p-shared = "process-shared" - i.e: is this condition shared among multiple processes? - // values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED - pthread_condattr_setpshared :: proc(attrs: ^pthread_condattr_t, value: c.int) -> c.int --- - pthread_condattr_getpshared :: proc(attrs: ^pthread_condattr_t, result: ^c.int) -> c.int --- - -} - -@(default_calling_convention="c") -foreign pthread { - // NOTE: POSIX says this can fail with OOM. - pthread_mutex_init :: proc(mutex: ^pthread_mutex_t, attrs: ^pthread_mutexattr_t) -> c.int --- - - pthread_mutex_destroy :: proc(mutex: ^pthread_mutex_t) -> c.int --- - - pthread_mutex_trylock :: proc(mutex: ^pthread_mutex_t) -> c.int --- - - pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int --- - - pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int --- - - pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int --- - - - pthread_mutexattr_init :: proc(attrs: ^pthread_mutexattr_t) -> c.int --- - pthread_mutexattr_destroy :: proc(attrs: ^pthread_mutexattr_t) -> c.int --- - pthread_mutexattr_settype :: proc(attrs: ^pthread_mutexattr_t, type: c.int) -> c.int --- - - // p-shared = "process-shared" - i.e: is this mutex shared among multiple processes? - // values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED - pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int --- - pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int --- - - pthread_testcancel :: proc () --- -} diff --git a/core/sys/unix/unix.odin b/core/sys/unix/unix.odin new file mode 100644 index 000000000..e9f58e554 --- /dev/null +++ b/core/sys/unix/unix.odin @@ -0,0 +1,8 @@ +package unix + +import "core:c" + +timespec :: struct { + secs: i64, + nsecs: c.long, +} diff --git a/core/sys/wasm/js/dom.odin b/core/sys/wasm/js/dom.odin index ffc58a9a3..902dfc941 100644 --- a/core/sys/wasm/js/dom.odin +++ b/core/sys/wasm/js/dom.odin @@ -20,6 +20,8 @@ foreign dom_lib { device_pixel_ratio :: proc() -> f64 --- window_set_scroll :: proc(x, y: f64) --- + + set_element_style :: proc(id: string, key: string, value: string) --- } get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string { diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index 6f086e595..4d93bab23 100644 --- a/core/sys/wasm/js/odin.js +++ b/core/sys/wasm/js/odin.js @@ -1259,13 +1259,26 @@ class WebGLInterface { }; -function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, eventQueue, event_temp) { +function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) { const MAX_INFO_CONSOLE_LINES = 512; let infoConsoleLines = new Array(); let currentLine = {}; currentLine[false] = ""; currentLine[true] = ""; let prevIsError = false; + + let event_temp = {}; + + const onEventReceived = (event_data, data, callback) => { + event_temp.data = event_data; + + const exports = wasmMemoryInterface.exports; + const odin_ctx = exports.default_context_ptr(); + + exports.odin_dom_do_event_callback(data, callback, odin_ctx); + + event_temp.data = null; + }; const writeToConsole = (line, isError) => { if (!line) { @@ -1594,7 +1607,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev event_data.event = e; event_data.name_code = name_code; - eventQueue.push({event_data: event_data, data: data, callback: callback}); + onEventReceived(event_data, data, callback); }; wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener; element.addEventListener(name, listener, !!use_capture); @@ -1611,7 +1624,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev event_data.event = e; event_data.name_code = name_code; - eventQueue.push({event_data: event_data, data: data, callback: callback}); + onEventReceived(event_data, data, callback); }; wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener; element.addEventListener(name, listener, !!use_capture); @@ -1802,6 +1815,16 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev } }, + set_element_style: (id_ptr, id_len, key_ptr, key_len, value_ptr, value_len) => { + let id = wasmMemoryInterface.loadString(id_ptr, id_len); + let key = wasmMemoryInterface.loadString(key_ptr, key_len); + let value = wasmMemoryInterface.loadString(value_ptr, value_len); + let element = getElement(id); + if (element) { + element.style[key] = value; + } + }, + get_element_key_f64: (id_ptr, id_len, key_ptr, key_len) => { let id = wasmMemoryInterface.loadString(id_ptr, id_len); let key = wasmMemoryInterface.loadString(key_ptr, key_len); @@ -1905,10 +1928,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory } wasmMemoryInterface.setIntSize(intSize); - let eventQueue = new Array(); - let event_temp = {}; - - let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory, eventQueue, event_temp); + let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory); let exports = {}; if (extraForeignImports !== undefined) { @@ -1948,13 +1968,6 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory const dt = (currTimeStamp - prevTimeStamp)*0.001; prevTimeStamp = currTimeStamp; - while (eventQueue.length > 0) { - let e = eventQueue.shift() - event_temp.data = e.event_data; - exports.odin_dom_do_event_callback(e.data, e.callback, odin_ctx); - } - event_temp.data = null; - if (!exports.step(dt, odin_ctx)) { exports._end(); return; diff --git a/core/sys/windows/icu.odin b/core/sys/windows/icu.odin new file mode 100644 index 000000000..6ed8c9b40 --- /dev/null +++ b/core/sys/windows/icu.odin @@ -0,0 +1,14 @@ +#+build windows +package sys_windows + +foreign import "system:icu.lib" + +UError :: enum i32 { + U_ZERO_ERROR = 0, +} + +@(default_calling_convention="system") +foreign icu { + ucal_getWindowsTimeZoneID :: proc(id: wstring, len: i32, winid: wstring, winidCapacity: i32, status: ^UError) -> i32 --- + ucal_getDefaultTimeZone :: proc(result: wstring, cap: i32, status: ^UError) -> i32 --- +} diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin index b3b470619..61a7d9d86 100644 --- a/core/sys/windows/winerror.odin +++ b/core/sys/windows/winerror.odin @@ -251,26 +251,26 @@ SEVERITY :: enum DWORD { // Generic test for success on any status value (non-negative numbers indicate success). SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= S_OK } // and the inverse -FAILED :: #force_inline proc(#any_int result: int) -> bool { return result < S_OK } +FAILED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result < S_OK } // Generic test for error on any status value. -IS_ERROR :: #force_inline proc(#any_int status: int) -> bool { return u32(status) >> 31 == u32(SEVERITY.ERROR) } +IS_ERROR :: #force_inline proc "contextless" (#any_int status: int) -> bool { return u32(status) >> 31 == u32(SEVERITY.ERROR) } // Return the code -HRESULT_CODE :: #force_inline proc(#any_int hr: int) -> int { return int(u32(hr) & 0xFFFF) } +HRESULT_CODE :: #force_inline proc "contextless" (#any_int hr: int) -> int { return int(u32(hr) & 0xFFFF) } // Return the facility -HRESULT_FACILITY :: #force_inline proc(#any_int hr: int) -> FACILITY { return FACILITY((u32(hr) >> 16) & 0x1FFF) } +HRESULT_FACILITY :: #force_inline proc "contextless" (#any_int hr: int) -> FACILITY { return FACILITY((u32(hr) >> 16) & 0x1FFF) } // Return the severity -HRESULT_SEVERITY :: #force_inline proc(#any_int hr: int) -> SEVERITY { return SEVERITY((u32(hr) >> 31) & 0x1) } +HRESULT_SEVERITY :: #force_inline proc "contextless" (#any_int hr: int) -> SEVERITY { return SEVERITY((u32(hr) >> 31) & 0x1) } // Create an HRESULT value from component pieces -MAKE_HRESULT :: #force_inline proc(#any_int sev: int, #any_int fac: int, #any_int code: int) -> HRESULT { +MAKE_HRESULT :: #force_inline proc "contextless" (#any_int sev: int, #any_int fac: int, #any_int code: int) -> HRESULT { return HRESULT((uint(sev)<<31) | (uint(fac)<<16) | (uint(code))) } -DECODE_HRESULT :: #force_inline proc(#any_int hr: int) -> (SEVERITY, FACILITY, int) { +DECODE_HRESULT :: #force_inline proc "contextless" (#any_int hr: int) -> (SEVERITY, FACILITY, int) { return HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr) } diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin index 7442c100c..281fbde40 100644 --- a/core/testing/signal_handler_libc.odin +++ b/core/testing/signal_handler_libc.odin @@ -15,7 +15,6 @@ import "core:c/libc" import "core:encoding/ansi" import "core:sync" import "core:os" -@require import "core:sys/unix" @(private="file") stop_runner_flag: libc.sig_atomic_t @@ -45,7 +44,7 @@ stop_runner_callback :: proc "c" (sig: libc.int) { } } -@(private="file") +@(private) stop_test_callback :: proc "c" (sig: libc.int) { if !local_test_index_set { // We're a thread created by a test thread. @@ -106,17 +105,7 @@ This is a dire bug and should be reported to the Odin developers. // otherwise we may continue to generate signals. intrinsics.cpu_relax() - when ODIN_OS != .Windows { - // NOTE(Feoramund): Some UNIX-like platforms may require this. - // - // During testing, I found that NetBSD 10.0 refused to - // terminate a task thread, even when its thread had been - // properly set to PTHREAD_CANCEL_ASYNCHRONOUS. - // - // The runner would stall after returning from `pthread_cancel`. - - unix.pthread_testcancel() - } + _test_thread_cancel() } } } @@ -133,14 +122,12 @@ _setup_signal_handler :: proc() { // For tests: // Catch asserts and panics. libc.signal(libc.SIGILL, stop_test_callback) - when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin { - // Catch panics on Darwin and unhandled calls to `debug_trap`. - libc.signal(SIGTRAP, stop_test_callback) - } // Catch arithmetic errors. libc.signal(libc.SIGFPE, stop_test_callback) // Catch segmentation faults (illegal memory access). libc.signal(libc.SIGSEGV, stop_test_callback) + + __setup_signal_handler() } _setup_task_signal_handler :: proc(test_index: int) { diff --git a/core/testing/signal_handler_posix.odin b/core/testing/signal_handler_posix.odin new file mode 100644 index 000000000..1bfcc875b --- /dev/null +++ b/core/testing/signal_handler_posix.odin @@ -0,0 +1,22 @@ +#+build linux, darwin, netbsd, openbsd, freebsd +#+private +package testing + +import "core:c/libc" +import "core:sys/posix" + +__setup_signal_handler :: proc() { + libc.signal(posix.SIGTRAP, stop_test_callback) +} + +_test_thread_cancel :: proc "contextless" () { + // NOTE(Feoramund): Some UNIX-like platforms may require this. + // + // During testing, I found that NetBSD 10.0 refused to + // terminate a task thread, even when its thread had been + // properly set to PTHREAD_CANCEL_ASYNCHRONOUS. + // + // The runner would stall after returning from `pthread_cancel`. + + posix.pthread_testcancel() +} diff --git a/core/testing/signal_handler_windows.odin b/core/testing/signal_handler_windows.odin new file mode 100644 index 000000000..74ebe2998 --- /dev/null +++ b/core/testing/signal_handler_windows.odin @@ -0,0 +1,6 @@ +#+private +package testing + +__setup_signal_handler :: proc() {} + +_test_thread_cancel :: proc "contextless" () {} diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 9576a3040..ff79cfcbc 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -4,14 +4,14 @@ package thread import "base:runtime" import "core:sync" -import "core:sys/unix" +import "core:sys/posix" _IS_SUPPORTED :: true // NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t. // Also see core/sys/darwin/mach_darwin.odin/semaphore_t. Thread_Os_Specific :: struct #align(16) { - unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux. + unix_thread: posix.pthread_t, // NOTE: very large on Darwin, small on Linux. start_ok: sync.Sema, } // @@ -23,7 +23,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { t := (^Thread)(t) // We need to give the thread a moment to start up before we enable cancellation. - can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) == 0 + // NOTE(laytan): setting to .DISABLE on darwin, with .ENABLE pthread_cancel would deadlock + // most of the time, don't ask me why. + can_set_thread_cancel_state := posix.pthread_setcancelstate(.DISABLE when ODIN_OS == .Darwin else .ENABLE, nil) == nil t.id = sync.current_thread_id() @@ -36,9 +38,15 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { } // Enable thread's cancelability. - if can_set_thread_cancel_state { - unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil) - unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) + // NOTE(laytan): Darwin does not correctly/fully support all of this, not doing this does + // actually make pthread_cancel work in the capacity of my tests, while executing this would + // basically always make it deadlock. + if ODIN_OS != .Darwin && can_set_thread_cancel_state { + err := posix.pthread_setcancelstate(.ENABLE, nil) + assert_contextless(err == nil) + + err = posix.pthread_setcanceltype(.ASYNCHRONOUS, nil) + assert_contextless(err == nil) } { @@ -59,8 +67,8 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { sync.atomic_or(&t.flags, { .Done }) if .Self_Cleanup in sync.atomic_load(&t.flags) { - res := unix.pthread_detach(t.unix_thread) - assert_contextless(res == 0) + res := posix.pthread_detach(t.unix_thread) + assert_contextless(res == nil) t.unix_thread = {} // NOTE(ftphikari): It doesn't matter which context 'free' received, right? @@ -71,19 +79,19 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { return nil } - attrs: unix.pthread_attr_t - if unix.pthread_attr_init(&attrs) != 0 { + attrs: posix.pthread_attr_t + if posix.pthread_attr_init(&attrs) != nil { return nil // NOTE(tetra, 2019-11-01): POSIX OOM. } - defer unix.pthread_attr_destroy(&attrs) + defer posix.pthread_attr_destroy(&attrs) // NOTE(tetra, 2019-11-01): These only fail if their argument is invalid. - res: i32 - res = unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) - assert(res == 0) + res: posix.Errno + res = posix.pthread_attr_setdetachstate(&attrs, .CREATE_JOINABLE) + assert(res == nil) when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { - res = unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) - assert(res == 0) + res = posix.pthread_attr_setinheritsched(&attrs, .EXPLICIT_SCHED) + assert(res == nil) } thread := new(Thread) @@ -93,26 +101,26 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread { thread.creation_allocator = context.allocator // Set thread priority. - policy: i32 + policy: posix.Sched_Policy when ODIN_OS != .Haiku && ODIN_OS != .NetBSD { - res = unix.pthread_attr_getschedpolicy(&attrs, &policy) - assert(res == 0) + res = posix.pthread_attr_getschedpolicy(&attrs, &policy) + assert(res == nil) } - params: unix.sched_param - res = unix.pthread_attr_getschedparam(&attrs, ¶ms) - assert(res == 0) - low := unix.sched_get_priority_min(policy) - high := unix.sched_get_priority_max(policy) + params: posix.sched_param + res = posix.pthread_attr_getschedparam(&attrs, ¶ms) + assert(res == nil) + low := posix.sched_get_priority_min(policy) + high := posix.sched_get_priority_max(policy) switch priority { case .Normal: // Okay case .Low: params.sched_priority = low + 1 case .High: params.sched_priority = high } - res = unix.pthread_attr_setschedparam(&attrs, ¶ms) - assert(res == 0) + res = posix.pthread_attr_setschedparam(&attrs, ¶ms) + assert(res == nil) thread.procedure = procedure - if unix.pthread_create(&thread.unix_thread, &attrs, __unix_thread_entry_proc, thread) != 0 { + if posix.pthread_create(&thread.unix_thread, &attrs, __unix_thread_entry_proc, thread) != nil { free(thread, thread.creation_allocator) return nil } @@ -130,7 +138,7 @@ _is_done :: proc(t: ^Thread) -> bool { } _join :: proc(t: ^Thread) { - if unix.pthread_equal(unix.pthread_self(), t.unix_thread) { + if posix.pthread_equal(posix.pthread_self(), t.unix_thread) { return } @@ -144,7 +152,7 @@ _join :: proc(t: ^Thread) { if .Started not_in sync.atomic_load(&t.flags) { _start(t) } - unix.pthread_join(t.unix_thread, nil) + posix.pthread_join(t.unix_thread, nil) } _join_multiple :: proc(threads: ..^Thread) { @@ -170,9 +178,9 @@ _terminate :: proc(t: ^Thread, exit_code: int) { // // This is in contrast to behavior I have seen on Linux where the thread is // just terminated. - unix.pthread_cancel(t.unix_thread) + posix.pthread_cancel(t.unix_thread) } _yield :: proc() { - unix.sched_yield() + posix.sched_yield() } diff --git a/core/time/datetime/constants.odin b/core/time/datetime/constants.odin index 5f336ef4a..e24709e49 100644 --- a/core/time/datetime/constants.odin +++ b/core/time/datetime/constants.odin @@ -77,12 +77,55 @@ Time :: struct { nano: i32, } +TZ_Record :: struct { + time: i64, + utc_offset: i64, + shortname: string, + dst: bool, +} + +TZ_Date_Kind :: enum { + No_Leap, + Leap, + Month_Week_Day, +} + +TZ_Transition_Date :: struct { + type: TZ_Date_Kind, + + month: u8, + week: u8, + day: u16, + + time: i64, +} + +TZ_RRule :: struct { + has_dst: bool, + + std_name: string, + std_offset: i64, + std_date: TZ_Transition_Date, + + dst_name: string, + dst_offset: i64, + dst_date: TZ_Transition_Date, +} + +TZ_Region :: struct { + name: string, + records: []TZ_Record, + shortnames: []string, + rrule: TZ_RRule, +} + /* A type representing datetime. */ DateTime :: struct { using date: Date, using time: Time, + tz: ^TZ_Region, } /* @@ -130,4 +173,4 @@ Weekday :: enum i8 { } @(private) -MONTH_DAYS :: [?]i8{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} \ No newline at end of file +MONTH_DAYS :: [?]i8{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} diff --git a/core/time/datetime/datetime.odin b/core/time/datetime/datetime.odin index fc9780e3b..2cd90b0e7 100644 --- a/core/time/datetime/datetime.odin +++ b/core/time/datetime/datetime.odin @@ -76,7 +76,7 @@ datetime, an error is returned. components_to_datetime :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (datetime: DateTime, err: Error) { date := components_to_date(year, month, day) or_return time := components_to_time(hour, minute, second, nanos) or_return - return {date, time}, .None + return {date, time, nil}, .None } /* @@ -88,7 +88,7 @@ object will always have the time equal to `00:00:00.000`. */ ordinal_to_datetime :: proc "contextless" (ordinal: Ordinal) -> (datetime: DateTime, err: Error) { d := ordinal_to_date(ordinal) or_return - return {Date(d), {}}, .None + return {Date(d), {}, nil}, .None } /* @@ -433,4 +433,4 @@ unsafe_ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date) day := i8(ordinal - unsafe_date_to_ordinal(Date{year, month, 1}) + 1) return {year, month, day} -} \ No newline at end of file +} diff --git a/core/time/time.odin b/core/time/time.odin index 98639b36a..b488f951c 100644 --- a/core/time/time.odin +++ b/core/time/time.odin @@ -930,7 +930,7 @@ If the datetime represents a time outside of a valid range, `false` is returned as the second return value. See `Time` for the representable range. */ compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) { - unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}} + unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}, nil} delta, err := dt.sub(datetime, unix_epoch) if err != .None { return @@ -958,7 +958,7 @@ datetime_to_time :: proc{components_to_time, compound_to_time} Convert time into datetime. */ time_to_datetime :: proc "contextless" (t: Time) -> (dt.DateTime, bool) { - unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}} + unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}, nil} datetime, err := dt.add(unix_epoch, dt.Delta{ nanos = t._nsec }) if err != .None { diff --git a/core/time/timezone/tz_unix.odin b/core/time/timezone/tz_unix.odin new file mode 100644 index 000000000..990e78d41 --- /dev/null +++ b/core/time/timezone/tz_unix.odin @@ -0,0 +1,89 @@ +#+build darwin, linux, freebsd, openbsd, netbsd +#+private +package timezone + +import "core:os" +import "core:strings" +import "core:path/filepath" +import "core:time/datetime" + +local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) { + local_str, ok := os.lookup_env("TZ", allocator) + if !ok { + orig_localtime_path := "/etc/localtime" + path, err := os.absolute_path_from_relative(orig_localtime_path, allocator) + if err != nil { + // If we can't find /etc/localtime, fallback to UTC + if err == .ENOENT { + str, err2 := strings.clone("UTC", allocator) + if err2 != nil { return } + return str, true + } + + return + } + defer delete(path, allocator) + + // FreeBSD makes me sad. + // This is a hackaround, because FreeBSD copies rather than softlinks their local timezone file, + // *sometimes* and then stores the original name of the timezone in /var/db/zoneinfo instead + if path == orig_localtime_path { + data := os.read_entire_file("/var/db/zoneinfo", allocator) or_return + return strings.trim_right_space(string(data)), true + } + + // Looking for tz path (ex fmt: "UTC", "Etc/UTC" or "America/Los_Angeles") + path_dir, path_file := filepath.split(path) + if path_dir == "" { + return + } + upper_path_dir, upper_path_chunk := filepath.split(path_dir[:len(path_dir)-1]) + if upper_path_dir == "" { + return + } + + if strings.contains(upper_path_chunk, "zoneinfo") { + region_str, err := strings.clone(path_file, allocator) + if err != nil { return } + return region_str, true + } else { + region_str, err := filepath.join({upper_path_chunk, path_file}, allocator = allocator) + if err != nil { return } + return region_str, true + } + } + + if local_str == "" { + delete(local_str, allocator) + + str, err := strings.clone("UTC", allocator) + if err != nil { return } + return str, true + } + + return local_str, true +} + +_region_load :: proc(_reg_str: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, success: bool) { + reg_str := _reg_str + if reg_str == "UTC" { + return nil, true + } + + if reg_str == "local" { + local_name := local_tz_name(allocator) or_return + if local_name == "UTC" { + delete(local_name, allocator) + return nil, true + } + + reg_str = local_name + } + defer if _reg_str == "local" { delete(reg_str, allocator) } + + db_path := "/usr/share/zoneinfo" + region_path := filepath.join({db_path, reg_str}, allocator) + defer delete(region_path, allocator) + + return load_tzif_file(region_path, reg_str, allocator) +} diff --git a/core/time/timezone/tz_windows.odin b/core/time/timezone/tz_windows.odin new file mode 100644 index 000000000..f1604b939 --- /dev/null +++ b/core/time/timezone/tz_windows.odin @@ -0,0 +1,295 @@ +#+build windows +#+private +package timezone + +import "core:strings" +import "core:sys/windows" +import "core:time/datetime" + +TZ_Abbrev :: struct { + std: string, + dst: string, +} + +tz_abbrevs := map[string]TZ_Abbrev { + "Egypt Standard Time" = {"EET", "EEST"}, // Africa/Cairo + "Morocco Standard Time" = {"+00", "+01"}, // Africa/Casablanca + "South Africa Standard Time" = {"SAST", "SAST"}, // Africa/Johannesburg + "South Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Juba + "Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Khartoum + "W. Central Africa Standard Time" = {"WAT", "WAT"}, // Africa/Lagos + "E. Africa Standard Time" = {"EAT", "EAT"}, // Africa/Nairobi + "Sao Tome Standard Time" = {"GMT", "GMT"}, // Africa/Sao_Tome + "Libya Standard Time" = {"EET", "EET"}, // Africa/Tripoli + "Namibia Standard Time" = {"CAT", "CAT"}, // Africa/Windhoek + "Aleutian Standard Time" = {"HST", "HDT"}, // America/Adak + "Alaskan Standard Time" = {"AKST", "AKDT"}, // America/Anchorage + "Tocantins Standard Time" = {"-03", "-03"}, // America/Araguaina + "Paraguay Standard Time" = {"-04", "-03"}, // America/Asuncion + "Bahia Standard Time" = {"-03", "-03"}, // America/Bahia + "SA Pacific Standard Time" = {"-05", "-05"}, // America/Bogota + "Argentina Standard Time" = {"-03", "-03"}, // America/Buenos_Aires + "Eastern Standard Time (Mexico)" = {"EST", "EST"}, // America/Cancun + "Venezuela Standard Time" = {"-04", "-04"}, // America/Caracas + "SA Eastern Standard Time" = {"-03", "-03"}, // America/Cayenne + "Central Standard Time" = {"CST", "CDT"}, // America/Chicago + "Central Brazilian Standard Time" = {"-04", "-04"}, // America/Cuiaba + "Mountain Standard Time" = {"MST", "MDT"}, // America/Denver + "Greenland Standard Time" = {"-03", "-02"}, // America/Godthab + "Turks And Caicos Standard Time" = {"EST", "EDT"}, // America/Grand_Turk + "Central America Standard Time" = {"CST", "CST"}, // America/Guatemala + "Atlantic Standard Time" = {"AST", "ADT"}, // America/Halifax + "Cuba Standard Time" = {"CST", "CDT"}, // America/Havana + "US Eastern Standard Time" = {"EST", "EDT"}, // America/Indianapolis + "SA Western Standard Time" = {"-04", "-04"}, // America/La_Paz + "Pacific Standard Time" = {"PST", "PDT"}, // America/Los_Angeles + "Mountain Standard Time (Mexico)" = {"MST", "MST"}, // America/Mazatlan + "Central Standard Time (Mexico)" = {"CST", "CST"}, // America/Mexico_City + "Saint Pierre Standard Time" = {"-03", "-02"}, // America/Miquelon + "Montevideo Standard Time" = {"-03", "-03"}, // America/Montevideo + "Eastern Standard Time" = {"EST", "EDT"}, // America/New_York + "US Mountain Standard Time" = {"MST", "MST"}, // America/Phoenix + "Haiti Standard Time" = {"EST", "EDT"}, // America/Port-au-Prince + "Magallanes Standard Time" = {"-03", "-03"}, // America/Punta_Arenas + "Canada Central Standard Time" = {"CST", "CST"}, // America/Regina + "Pacific SA Standard Time" = {"-04", "-03"}, // America/Santiago + "E. South America Standard Time" = {"-03", "-03"}, // America/Sao_Paulo + "Newfoundland Standard Time" = {"NST", "NDT"}, // America/St_Johns + "Pacific Standard Time (Mexico)" = {"PST", "PDT"}, // America/Tijuana + "Yukon Standard Time" = {"MST", "MST"}, // America/Whitehorse + "Central Asia Standard Time" = {"+06", "+06"}, // Asia/Almaty + "Jordan Standard Time" = {"+03", "+03"}, // Asia/Amman + "Arabic Standard Time" = {"+03", "+03"}, // Asia/Baghdad + "Azerbaijan Standard Time" = {"+04", "+04"}, // Asia/Baku + "SE Asia Standard Time" = {"+07", "+07"}, // Asia/Bangkok + "Altai Standard Time" = {"+07", "+07"}, // Asia/Barnaul + "Middle East Standard Time" = {"EET", "EEST"}, // Asia/Beirut + "India Standard Time" = {"IST", "IST"}, // Asia/Calcutta + "Transbaikal Standard Time" = {"+09", "+09"}, // Asia/Chita + "Sri Lanka Standard Time" = {"+0530", "+0530"}, // Asia/Colombo + "Syria Standard Time" = {"+03", "+03"}, // Asia/Damascus + "Bangladesh Standard Time" = {"+06", "+06"}, // Asia/Dhaka + "Arabian Standard Time" = {"+04", "+04"}, // Asia/Dubai + "West Bank Standard Time" = {"EET", "EEST"}, // Asia/Hebron + "W. Mongolia Standard Time" = {"+07", "+07"}, // Asia/Hovd + "North Asia East Standard Time" = {"+08", "+08"}, // Asia/Irkutsk + "Israel Standard Time" = {"IST", "IDT"}, // Asia/Jerusalem + "Afghanistan Standard Time" = {"+0430", "+0430"}, // Asia/Kabul + "Russia Time Zone 11" = {"+12", "+12"}, // Asia/Kamchatka + "Pakistan Standard Time" = {"PKT", "PKT"}, // Asia/Karachi + "Nepal Standard Time" = {"+0545", "+0545"}, // Asia/Katmandu + "North Asia Standard Time" = {"+07", "+07"}, // Asia/Krasnoyarsk + "Magadan Standard Time" = {"+11", "+11"}, // Asia/Magadan + "N. Central Asia Standard Time" = {"+07", "+07"}, // Asia/Novosibirsk + "Omsk Standard Time" = {"+06", "+06"}, // Asia/Omsk + "North Korea Standard Time" = {"KST", "KST"}, // Asia/Pyongyang + "Qyzylorda Standard Time" = {"+05", "+05"}, // Asia/Qyzylorda + "Myanmar Standard Time" = {"+0630", "+0630"}, // Asia/Rangoon + "Arab Standard Time" = {"+03", "+03"}, // Asia/Riyadh + "Sakhalin Standard Time" = {"+11", "+11"}, // Asia/Sakhalin + "Korea Standard Time" = {"KST", "KST"}, // Asia/Seoul + "China Standard Time" = {"CST", "CST"}, // Asia/Shanghai + "Singapore Standard Time" = {"+08", "+08"}, // Asia/Singapore + "Russia Time Zone 10" = {"+11", "+11"}, // Asia/Srednekolymsk + "Taipei Standard Time" = {"CST", "CST"}, // Asia/Taipei + "West Asia Standard Time" = {"+05", "+05"}, // Asia/Tashkent + "Georgian Standard Time" = {"+04", "+04"}, // Asia/Tbilisi + "Iran Standard Time" = {"+0330", "+0330"}, // Asia/Tehran + "Tokyo Standard Time" = {"JST", "JST"}, // Asia/Tokyo + "Tomsk Standard Time" = {"+07", "+07"}, // Asia/Tomsk + "Ulaanbaatar Standard Time" = {"+08", "+08"}, // Asia/Ulaanbaatar + "Vladivostok Standard Time" = {"+10", "+10"}, // Asia/Vladivostok + "Yakutsk Standard Time" = {"+09", "+09"}, // Asia/Yakutsk + "Ekaterinburg Standard Time" = {"+05", "+05"}, // Asia/Yekaterinburg + "Caucasus Standard Time" = {"+04", "+04"}, // Asia/Yerevan + "Azores Standard Time" = {"-01", "+00"}, // Atlantic/Azores + "Cape Verde Standard Time" = {"-01", "-01"}, // Atlantic/Cape_Verde + "Greenwich Standard Time" = {"GMT", "GMT"}, // Atlantic/Reykjavik + "Cen. Australia Standard Time" = {"ACST", "ACDT"}, // Australia/Adelaide + "E. Australia Standard Time" = {"AEST", "AEST"}, // Australia/Brisbane + "AUS Central Standard Time" = {"ACST", "ACST"}, // Australia/Darwin + "Aus Central W. Standard Time" = {"+0845", "+0845"}, // Australia/Eucla + "Tasmania Standard Time" = {"AEST", "AEDT"}, // Australia/Hobart + "Lord Howe Standard Time" = {"+1030", "+11"}, // Australia/Lord_Howe + "W. Australia Standard Time" = {"AWST", "AWST"}, // Australia/Perth + "AUS Eastern Standard Time" = {"AEST", "AEDT"}, // Australia/Sydney + "UTC-11" = {"-11", "-11"}, // Etc/GMT+11 + "Dateline Standard Time" = {"-12", "-12"}, // Etc/GMT+12 + "UTC-02" = {"-02", "-02"}, // Etc/GMT+2 + "UTC-08" = {"-08", "-08"}, // Etc/GMT+8 + "UTC-09" = {"-09", "-09"}, // Etc/GMT+9 + "UTC+12" = {"+12", "+12"}, // Etc/GMT-12 + "UTC+13" = {"+13", "+13"}, // Etc/GMT-13 + "UTC" = {"UTC", "UTC"}, // Etc/UTC + "Astrakhan Standard Time" = {"+04", "+04"}, // Europe/Astrakhan + "W. Europe Standard Time" = {"CET", "CEST"}, // Europe/Berlin + "GTB Standard Time" = {"EET", "EEST"}, // Europe/Bucharest + "Central Europe Standard Time" = {"CET", "CEST"}, // Europe/Budapest + "E. Europe Standard Time" = {"EET", "EEST"}, // Europe/Chisinau + "Turkey Standard Time" = {"+03", "+03"}, // Europe/Istanbul + "Kaliningrad Standard Time" = {"EET", "EET"}, // Europe/Kaliningrad + "FLE Standard Time" = {"EET", "EEST"}, // Europe/Kiev + "GMT Standard Time" = {"GMT", "BST"}, // Europe/London + "Belarus Standard Time" = {"+03", "+03"}, // Europe/Minsk + "Russian Standard Time" = {"MSK", "MSK"}, // Europe/Moscow + "Romance Standard Time" = {"CET", "CEST"}, // Europe/Paris + "Russia Time Zone 3" = {"+04", "+04"}, // Europe/Samara + "Saratov Standard Time" = {"+04", "+04"}, // Europe/Saratov + "Volgograd Standard Time" = {"MSK", "MSK"}, // Europe/Volgograd + "Central European Standard Time" = {"CET", "CEST"}, // Europe/Warsaw + "Mauritius Standard Time" = {"+04", "+04"}, // Indian/Mauritius + "Samoa Standard Time" = {"+13", "+13"}, // Pacific/Apia + "New Zealand Standard Time" = {"NZST", "NZDT"}, // Pacific/Auckland + "Bougainville Standard Time" = {"+11", "+11"}, // Pacific/Bougainville + "Chatham Islands Standard Time" = {"+1245", "+1345"}, // Pacific/Chatham + "Easter Island Standard Time" = {"-06", "-05"}, // Pacific/Easter + "Fiji Standard Time" = {"+12", "+12"}, // Pacific/Fiji + "Central Pacific Standard Time" = {"+11", "+11"}, // Pacific/Guadalcanal + "Hawaiian Standard Time" = {"HST", "HST"}, // Pacific/Honolulu + "Line Islands Standard Time" = {"+14", "+14"}, // Pacific/Kiritimati + "Marquesas Standard Time" = {"-0930", "-0930"}, // Pacific/Marquesas + "Norfolk Standard Time" = {"+11", "+12"}, // Pacific/Norfolk + "West Pacific Standard Time" = {"+10", "+10"}, // Pacific/Port_Moresby + "Tonga Standard Time" = {"+13", "+13"}, // Pacific/Tongatapu +} + +iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) { + wintz_name_buffer: [128]u16 + status: windows.UError + + iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator) + defer free(iana_name_wstr, allocator) + + wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, raw_data(wintz_name_buffer[:]), len(wintz_name_buffer), &status) + if status != .U_ZERO_ERROR { + return + } + + wintz_name, err := windows.utf16_to_utf8(wintz_name_buffer[:wintz_name_len], allocator) + if err != nil { + return + } + + return wintz_name, true +} + +local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) { + iana_name_buffer: [128]u16 + status: windows.UError + + zone_str_len := windows.ucal_getDefaultTimeZone(raw_data(iana_name_buffer[:]), len(iana_name_buffer), &status) + if status != .U_ZERO_ERROR { + return + } + + iana_name, err := windows.utf16_to_utf8(iana_name_buffer[:zone_str_len], allocator) + if err != nil { + return + } + + return iana_name, true +} + +REG_TZI_FORMAT :: struct #packed { + bias: windows.LONG, + std_bias: windows.LONG, + dst_bias: windows.LONG, + std_date: windows.SYSTEMTIME, + dst_date: windows.SYSTEMTIME, +} + +generate_rrule_from_tzi :: proc(tzi: ^REG_TZI_FORMAT, abbrevs: TZ_Abbrev, allocator := context.allocator) -> (rrule: datetime.TZ_RRule, ok: bool) { + std_name, err := strings.clone(abbrevs.std, allocator) + if err != nil { return } + defer if err != nil { delete(std_name, allocator) } + + dst_name: string + dst_name, err = strings.clone(abbrevs.dst, allocator) + if err != nil { return } + defer if err != nil { delete(dst_name, allocator) } + + return datetime.TZ_RRule{ + has_dst = true, + + std_name = std_name, + std_offset = -(i64(tzi.bias) + i64(tzi.std_bias)) * 60, + dst_date = datetime.TZ_Transition_Date{ + type = .Month_Week_Day, + month = u8(tzi.std_date.month), + week = u8(tzi.std_date.day), + day = tzi.std_date.day_of_week, + time = (i64(tzi.std_date.hour) * 60 * 60) + (i64(tzi.std_date.minute) * 60) + i64(tzi.std_date.second), + }, + + dst_name = dst_name, + dst_offset = -(i64(tzi.bias) + i64(tzi.dst_bias)) * 60, + std_date = datetime.TZ_Transition_Date{ + type = .Month_Week_Day, + month = u8(tzi.dst_date.month), + week = u8(tzi.dst_date.day), + day = tzi.dst_date.day_of_week, + time = (i64(tzi.dst_date.hour) * 60 * 60) + (i64(tzi.dst_date.minute) * 60) + i64(tzi.dst_date.second), + }, + }, true +} + +_region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, success: bool) { + wintz_name: string + iana_name: string + + if reg_str == "local" { + ok := false + + iana_name = local_tz_name(allocator) or_return + wintz_name, ok = iana_to_windows_tz(iana_name, allocator) + if !ok { + delete(iana_name, allocator) + return + } + } else { + wintz_name = iana_to_windows_tz(reg_str, allocator) or_return + iana_name = strings.clone(reg_str, allocator) + } + defer delete(wintz_name, allocator) + defer delete(iana_name, allocator) + + abbrevs := tz_abbrevs[wintz_name] or_return + if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std { + return nil, true + } + + key_base := `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones` + tz_key := strings.join({key_base, wintz_name}, "\\", allocator = allocator) + defer delete(tz_key, allocator) + + tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator) + defer free(tz_key_wstr, allocator) + + key: windows.HKEY + res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key) + if res != 0 { return } + defer windows.RegCloseKey(key) + + tzi: REG_TZI_FORMAT + size := u32(size_of(REG_TZI_FORMAT)) + + res = windows.RegGetValueW(key, nil, windows.L("TZI"), windows.RRF_RT_ANY, nil, &tzi, &size) + if res != 0 { + return + } + + rrule := generate_rrule_from_tzi(&tzi, abbrevs, allocator) or_return + + region_name, err := strings.clone(iana_name, allocator) + if err != nil { return } + defer if err != nil { delete(region_name, allocator) } + + region: ^datetime.TZ_Region + region, err = new_clone(datetime.TZ_Region{ + name = region_name, + rrule = rrule, + }, allocator) + if err != nil { return } + + return region, true +} diff --git a/core/time/timezone/tzdate.odin b/core/time/timezone/tzdate.odin new file mode 100644 index 000000000..8f83d1bf4 --- /dev/null +++ b/core/time/timezone/tzdate.odin @@ -0,0 +1,339 @@ +package timezone + +import "core:fmt" +import "core:slice" +import "core:time" +import "core:time/datetime" + +region_load :: proc(reg: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, ok: bool) { + return _region_load(reg, allocator) +} + +region_load_from_file :: proc(file_path, reg: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, ok: bool) { + return load_tzif_file(file_path, reg, allocator) +} + +region_load_from_buffer :: proc(buffer: []u8, reg: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, ok: bool) { + return parse_tzif(buffer, reg, allocator) +} + +rrule_destroy :: proc(rrule: datetime.TZ_RRule, allocator := context.allocator) { + delete(rrule.std_name, allocator) + delete(rrule.dst_name, allocator) +} + +region_destroy :: proc(region: ^datetime.TZ_Region, allocator := context.allocator) { + if region == nil { + return + } + + for name in region.shortnames { + delete(name, allocator) + } + delete(region.shortnames, allocator) + delete(region.records, allocator) + delete(region.name, allocator) + rrule_destroy(region.rrule, allocator) + free(region, allocator) +} + + +@private +region_get_nearest :: proc(region: ^datetime.TZ_Region, tm: time.Time) -> (out: datetime.TZ_Record, success: bool) { + if len(region.records) == 0 { + return process_rrule(region.rrule, tm) + } + + n := len(region.records) + left, right := 0, n + + tm_sec := time.to_unix_seconds(tm) + last_time := region.records[len(region.records)-1].time + if tm_sec > last_time { + return process_rrule(region.rrule, tm) + } + + for left < right { + mid := int(uint(left+right) >> 1) + if region.records[mid].time < tm_sec { + left = mid + 1 + } else { + right = mid + } + } + + idx := max(0, left-1) + return region.records[idx], true +} + +@private +month_to_seconds :: proc(month: int, is_leap: bool) -> i64 { + month_seconds := []i64{ + 0, 31 * 86_400, 59 * 86_400, 90 * 86_400, + 120 * 86_400, 151 * 86_400, 181 * 86_400, 212 * 86_400, + 243 * 86_400, 273 * 86_400, 304 * 86_400, 334 * 86_400, + } + + t := month_seconds[month] + if is_leap && month >= 2 { + t += 86_400 + } + return t +} + +@private +trans_date_to_seconds :: proc(year: i64, td: datetime.TZ_Transition_Date) -> (secs: i64, ok: bool) { + is_leap := datetime.is_leap_year(year) + DAY_SEC :: 86_400 + + year_start := datetime.DateTime{{year, 1, 1}, {0, 0, 0, 0}, nil} + year_start_time := time.datetime_to_time(year_start) or_return + + t := i64(time.to_unix_seconds(year_start_time)) + + switch td.type { + case .Month_Week_Day: + t += month_to_seconds(int(td.month) - 1, is_leap) + + weekday := ((t + (4 * DAY_SEC)) %% (7 * DAY_SEC)) / DAY_SEC + days := i64(td.day) - weekday + if days < 0 { days += 7 } + + month_daycount, err := datetime.last_day_of_month(year, td.month) + if err != nil { return } + + week := td.week + if week == 5 && days + 28 >= i64(month_daycount) { + week = 4 + } + + t += DAY_SEC * (days + (7 * i64(week - 1))) + t += td.time + + return t, true + + // Both of these should result in 0 -> 365 days (in seconds) + case .No_Leap: + day := i64(td.day) + + // if before Feb 29th || not a leap year + if day < 60 || !is_leap { + day -= 1 + } + t += DAY_SEC * day + + return t, true + + case .Leap: + t += DAY_SEC * i64(td.day) + + return t, true + + case: + return + } + + return +} + +@private +process_rrule :: proc(rrule: datetime.TZ_RRule, tm: time.Time) -> (out: datetime.TZ_Record, success: bool) { + if !rrule.has_dst { + return datetime.TZ_Record{ + time = time.to_unix_seconds(tm), + utc_offset = rrule.std_offset, + shortname = rrule.std_name, + dst = false, + }, true + } + + y, _, _ := time.date(tm) + std_secs := trans_date_to_seconds(i64(y), rrule.std_date) or_return + dst_secs := trans_date_to_seconds(i64(y), rrule.dst_date) or_return + + records := []datetime.TZ_Record{ + { + time = std_secs, + utc_offset = rrule.std_offset, + shortname = rrule.std_name, + dst = false, + }, + { + time = dst_secs, + utc_offset = rrule.dst_offset, + shortname = rrule.dst_name, + dst = true, + }, + } + record_sort_proc :: proc(i, j: datetime.TZ_Record) -> bool { + return i.time > j.time + } + slice.sort_by(records, record_sort_proc) + + tm_sec := time.to_unix_seconds(tm) + for record in records { + if tm_sec < record.time { + return record, true + } + } + + return records[len(records)-1], true +} + +datetime_to_utc :: proc(dt: datetime.DateTime) -> (out: datetime.DateTime, success: bool) #optional_ok { + if dt.tz == nil { + return dt, true + } + + tm := time.datetime_to_time(dt) or_return + record := region_get_nearest(dt.tz, tm) or_return + + secs := time.time_to_unix(tm) + adj_time := time.unix(secs - record.utc_offset, 0) + adj_dt := time.time_to_datetime(adj_time) or_return + return adj_dt, true +} + +/* +Converts a datetime on one timezone to another timezone + +Inputs: +- dt: The input datetime +- tz: The timezone to convert to + +NOTE: tz will be referenced in the result datetime, so it must stay alive/allocated as long as it is used +Returns: +- out: The converted datetime +- success: `false` if the datetime was invalid +*/ +datetime_to_tz :: proc(dt: datetime.DateTime, tz: ^datetime.TZ_Region) -> (out: datetime.DateTime, success: bool) #optional_ok { + dt := dt + if dt.tz == tz { + return dt, true + } + if dt.tz != nil { + dt = datetime_to_utc(dt) + } + if tz == nil { + return dt, true + } + + tm := time.datetime_to_time(dt) or_return + record := region_get_nearest(tz, tm) or_return + + secs := time.time_to_unix(tm) + adj_time := time.unix(secs + record.utc_offset, 0) + adj_dt := time.time_to_datetime(adj_time) or_return + adj_dt.tz = tz + + return adj_dt, true +} + +/* +Gets the timezone abbreviation/shortname for a given date. +(ex: "PDT") + +Inputs: +- dt: The datetime containing the date, time, and timezone pointer for the lookup + +NOTE: The lifetime of name matches the timezone it was pulled from. +Returns: +- name: The timezone abbreviation +- success: returns `false` if the passed datetime is invalid +*/ +shortname :: proc(dt: datetime.DateTime) -> (name: string, success: bool) #optional_ok { + tm := time.datetime_to_time(dt) or_return + if dt.tz == nil { return "UTC", true } + + record := region_get_nearest(dt.tz, tm) or_return + return record.shortname, true +} + +/* +Gets the timezone abbreviation/shortname for a given date. +(ex: "PDT") + +WARNING: This is unsafe because it doesn't check if your datetime is valid or if your region contains a valid record. + +Inputs: +- dt: The input datetime + +NOTE: The lifetime of name matches the timezone it was pulled from. +Returns: +- name: The timezone abbreviation +*/ +shortname_unsafe :: proc(dt: datetime.DateTime) -> string { + if dt.tz == nil { return "UTC" } + + tm, _ := time.datetime_to_time(dt) + record, _ := region_get_nearest(dt.tz, tm) + return record.shortname +} + +/* +Checks DST for a given date. + +Inputs: +- dt: The input datetime + +Returns: +- is_dst: returns `true` if dt is in daylight savings time, `false` if not +- success: returns `false` if the passed datetime is invalid +*/ +dst :: proc(dt: datetime.DateTime) -> (is_dst: bool, success: bool) #optional_ok { + tm := time.datetime_to_time(dt) or_return + if dt.tz == nil { return false, true } + + record := region_get_nearest(dt.tz, tm) or_return + return record.dst, true +} + +/* +Checks DST for a given date. + +WARNING: This is unsafe because it doesn't check if your datetime is valid or if your region contains a valid record. + +Inputs: +- dt: The input datetime + +Returns: +- is_dst: returns `true` if dt is in daylight savings time, `false` if not +*/ +dst_unsafe :: proc(dt: datetime.DateTime) -> bool { + if dt.tz == nil { return false } + + tm, _ := time.datetime_to_time(dt) + record, _ := region_get_nearest(dt.tz, tm) + return record.dst +} + +datetime_to_str :: proc(dt: datetime.DateTime, allocator := context.allocator) -> string { + if dt.tz == nil { + _, ok := time.datetime_to_time(dt) + if !ok { + return "" + } + + return fmt.aprintf("%02d-%02d-%04d @ %02d:%02d:%02d UTC", dt.month, dt.day, dt.year, dt.hour, dt.minute, dt.second, allocator = allocator) + + } else { + tm, ok := time.datetime_to_time(dt) + if !ok { + return "" + } + + record, ok2 := region_get_nearest(dt.tz, tm) + if !ok2 { + return "" + } + + hour := dt.hour + am_pm_str := "AM" + if hour > 12 { + am_pm_str = "PM" + hour -= 12 + } + + return fmt.aprintf("%02d-%02d-%04d @ %02d:%02d:%02d %s %s", dt.month, dt.day, dt.year, hour, dt.minute, dt.second, am_pm_str, record.shortname, allocator = allocator) + } +} diff --git a/core/time/timezone/tzif.odin b/core/time/timezone/tzif.odin new file mode 100644 index 000000000..609cbda73 --- /dev/null +++ b/core/time/timezone/tzif.odin @@ -0,0 +1,652 @@ +package timezone + +import "base:intrinsics" + +import "core:slice" +import "core:strings" +import "core:os" +import "core:strconv" +import "core:time/datetime" + +// Implementing RFC8536 [https://datatracker.ietf.org/doc/html/rfc8536] + +TZIF_MAGIC :: u32be(0x545A6966) // 'TZif' +TZif_Version :: enum u8 { + V1 = 0, + V2 = '2', + V3 = '3', + V4 = '4', +} +BIG_BANG_ISH :: -0x800000000000000 + +TZif_Header :: struct #packed { + magic: u32be, + version: TZif_Version, + reserved: [15]u8, + isutcnt: u32be, + isstdcnt: u32be, + leapcnt: u32be, + timecnt: u32be, + typecnt: u32be, + charcnt: u32be, +} + +Sun_Shift :: enum u8 { + Standard = 0, + DST = 1, +} + +Local_Time_Type :: struct #packed { + utoff: i32be, + dst: Sun_Shift, + idx: u8, +} + +Leapsecond_Record :: struct #packed { + occur: i64be, + corr: i32be, +} + +@private +tzif_data_block_size :: proc(hdr: ^TZif_Header, version: TZif_Version) -> (block_size: int, ok: bool) { + time_size : int + + if version == .V1 { + time_size = 4 + } else if version == .V2 || version == .V3 || version == .V4 { + time_size = 8 + } else { + return + } + + return (int(hdr.timecnt) * time_size) + + int(hdr.timecnt) + + int(hdr.typecnt * size_of(Local_Time_Type)) + + int(hdr.charcnt) + + (int(hdr.leapcnt) * (time_size + 4)) + + int(hdr.isstdcnt) + + int(hdr.isutcnt), true +} + + +load_tzif_file :: proc(filename: string, region_name: string, allocator := context.allocator) -> (out: ^datetime.TZ_Region, ok: bool) { + tzif_data := os.read_entire_file_from_filename(filename, allocator) or_return + defer delete(tzif_data, allocator) + return parse_tzif(tzif_data, region_name, allocator) +} + +@private +is_alphabetic :: proc(ch: u8) -> bool { + // ('A' -> 'Z') || ('a' -> 'z') + return (ch > 0x40 && ch < 0x5B) || (ch > 0x60 && ch < 0x7B) +} + +@private +is_numeric :: proc(ch: u8) -> bool { + // ('0' -> '9') + return (ch > 0x2F && ch < 0x3A) +} + +@private +is_alphanumeric :: proc(ch: u8) -> bool { + return is_alphabetic(ch) || is_numeric(ch) +} + +@private +is_valid_quoted_char :: proc(ch: u8) -> bool { + return is_alphabetic(ch) || is_numeric(ch) || ch == '+' || ch == '-' +} + +@private +parse_posix_tz_shortname :: proc(str: string) -> (out: string, idx: int, ok: bool) { + was_quoted := false + quoted := false + i := 0 + + for ; i < len(str); i += 1 { + ch := str[i] + + if !quoted && ch == '<' { + quoted = true + was_quoted = true + continue + } + + if quoted && ch == '>' { + quoted = false + break + } + + if !is_valid_quoted_char(ch) && ch != ',' { + return + } + + if !quoted && !is_alphabetic(ch) { + break + } + } + + // If we didn't see the trailing quote + if was_quoted && quoted { + return + } + + out_str: string + end_idx := i + if was_quoted { + end_idx += 1 + out_str = str[1:i] + } else { + out_str = str[:i] + } + + return out_str, end_idx, true +} + +@private +parse_posix_tz_offset :: proc(str: string) -> (out_sec: i64, idx: int, ok: bool) { + str := str + + sign : i64 = 1 + start_idx := 0 + i := 0 + if str[i] == '+' { + i += 1 + sign = 1 + start_idx = 1 + } else if str[i] == '-' { + i += 1 + sign = -1 + start_idx = 1 + } + + got_more_time := false + for ; i < len(str); i += 1 { + if is_numeric(str[i]) { + continue + } + + if str[i] == ':' { + got_more_time = true + break + } + + break + } + + ret_sec : i64 = 0 + hours := strconv.parse_int(str[start_idx:i], 10) or_return + if hours > 167 || hours < -167 { + return + } + ret_sec += i64(hours) * (60 * 60) + if !got_more_time { + return ret_sec * sign, i, true + } + + i += 1 + start_idx = i + + got_more_time = false + for ; i < len(str); i += 1 { + if is_numeric(str[i]) { + continue + } + + if str[i] == ':' { + got_more_time = true + break + } + + break + } + + mins_str := str[start_idx:i] + if len(mins_str) != 2 { + return + } + + mins := strconv.parse_int(mins_str, 10) or_return + if mins > 59 || mins < 0 { + return + } + ret_sec += i64(mins) * 60 + if !got_more_time { + return ret_sec * sign, i, true + } + + i += 1 + start_idx = i + + for ; i < len(str); i += 1 { + if !is_numeric(str[i]) { + break + } + } + secs_str := str[start_idx:i] + if len(secs_str) != 2 { + return + } + + secs := strconv.parse_int(secs_str, 10) or_return + if secs > 59 || secs < 0 { + return + } + ret_sec += i64(secs) + return ret_sec * sign, i, true +} + +@private +skim_digits :: proc(str: string) -> (out: string, idx: int, ok: bool) { + i := 0 + for ; i < len(str); i += 1 { + ch := str[i] + if ch == '.' || ch == '/' || ch == ',' { + break + } + + if !is_numeric(ch) { + return + } + } + + return str[:i], i, true +} + +TWO_AM :: 2 * 60 * 60 +parse_posix_rrule :: proc(str: string) -> (out: datetime.TZ_Transition_Date, idx: int, ok: bool) { + str := str + if len(str) < 2 { return } + + i := 0 + // No leap + if str[i] == 'J' { + i += 1 + + day_str, off := skim_digits(str[i:]) or_return + i += off + + day := strconv.parse_int(day_str, 10) or_return + if day < 1 || day > 365 { return } + + offset : i64 = TWO_AM + if len(str) != i && str[i] == '/' { + i += 1 + + offset, off = parse_posix_tz_offset(str[i:]) or_return + i += off + } + + if len(str) != i && str[i] == ',' { + i += 1 + } + + return datetime.TZ_Transition_Date{ + type = .No_Leap, + day = u16(day), + time = offset, + }, i, true + + // Leap + } else if is_numeric(str[i]) { + day_str, off := skim_digits(str[i:]) or_return + i += off + + day := strconv.parse_int(day_str, 10) or_return + if day < 0 || day > 365 { return } + + offset : i64 = TWO_AM + if len(str) != i && str[i] == '/' { + i += 1 + + offset, off = parse_posix_tz_offset(str[i:]) or_return + i += off + } + + if len(str) != i && str[i] == ',' { + i += 1 + } + + return datetime.TZ_Transition_Date{ + type = .Leap, + day = u16(day), + time = offset, + }, i, true + + } else if str[i] == 'M' { + i += 1 + + month_str, week_str, day_str: string + off := 0 + + month_str, off = skim_digits(str[i:]) or_return + i += off + 1 + + week_str, off = skim_digits(str[i:]) or_return + i += off + 1 + + day_str, off = skim_digits(str[i:]) or_return + i += off + + month := strconv.parse_int(month_str, 10) or_return + if month < 1 || month > 12 { return } + + week := strconv.parse_int(week_str, 10) or_return + if week < 1 || week > 5 { return } + + day := strconv.parse_int(day_str, 10) or_return + if day < 0 || day > 6 { return } + + offset : i64 = TWO_AM + if len(str) != i && str[i] == '/' { + i += 1 + + offset, off = parse_posix_tz_offset(str[i:]) or_return + i += off + } + + if len(str) != i && str[i] == ',' { + i += 1 + } + + return datetime.TZ_Transition_Date{ + type = .Month_Week_Day, + month = u8(month), + week = u8(week), + day = u16(day), + time = offset, + }, i, true + } + + return +} + +parse_posix_tz :: proc(posix_tz: string, allocator := context.allocator) -> (out: datetime.TZ_RRule, ok: bool) { + // TZ string contain at least 3 characters for the STD name, and 1 for the offset + if len(posix_tz) < 4 { + return + } + + str := posix_tz + + std_name, idx := parse_posix_tz_shortname(str) or_return + str = str[idx:] + + std_offset, idx2 := parse_posix_tz_offset(str) or_return + std_offset *= -1 + str = str[idx2:] + + std_name_str, err := strings.clone(std_name, allocator) + if err != nil { return } + defer if !ok { delete(std_name_str, allocator) } + + if len(str) == 0 { + return datetime.TZ_RRule{ + has_dst = false, + std_name = std_name_str, + std_offset = std_offset, + std_date = datetime.TZ_Transition_Date{ + type = .Leap, + day = 0, + time = TWO_AM, + }, + }, true + } + + dst_name: string + dst_offset := std_offset + (1 * 60 * 60) + if str[0] != ',' { + dst_name, idx = parse_posix_tz_shortname(str) or_return + str = str[idx:] + + if str[0] != ',' { + dst_offset, idx = parse_posix_tz_offset(str) or_return + dst_offset *= -1 + str = str[idx:] + } + } + if str[0] != ',' { return } + str = str[1:] + + std_td, idx3 := parse_posix_rrule(str) or_return + str = str[idx3:] + + dst_td, idx4 := parse_posix_rrule(str) or_return + str = str[idx4:] + + dst_name_str: string + dst_name_str, err = strings.clone(dst_name, allocator) + if err != nil { return } + + return datetime.TZ_RRule{ + has_dst = true, + + std_name = std_name_str, + std_offset = std_offset, + std_date = std_td, + + dst_name = dst_name_str, + dst_offset = dst_offset, + dst_date = dst_td, + }, true +} + +parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allocator) -> (out: ^datetime.TZ_Region, ok: bool) { + context.allocator = allocator + + buffer := _buffer + + // TZif is crufty. Skip the initial header. + + v1_hdr := slice.to_type(buffer, TZif_Header) or_return + if v1_hdr.magic != TZIF_MAGIC { + return + } + if v1_hdr.typecnt == 0 || v1_hdr.charcnt == 0 { + return + } + if v1_hdr.isutcnt != 0 && v1_hdr.isutcnt != v1_hdr.typecnt { + return + } + if v1_hdr.isstdcnt != 0 && v1_hdr.isstdcnt != v1_hdr.typecnt { + return + } + + // We don't bother supporting v1, it uses u32 timestamps + if v1_hdr.version == .V1 { + return + } + // We only support v2 and v3 + if v1_hdr.version != .V2 && v1_hdr.version != .V3 { + return + } + + // Skip the initial v1 block too. + first_block_size, _ := tzif_data_block_size(&v1_hdr, .V1) + if len(buffer) <= size_of(v1_hdr) + first_block_size { + return + } + buffer = buffer[size_of(v1_hdr)+first_block_size:] + + // Ok, time to parse real things + real_hdr := slice.to_type(buffer, TZif_Header) or_return + if real_hdr.magic != TZIF_MAGIC { + return + } + if real_hdr.typecnt == 0 || real_hdr.charcnt == 0 { + return + } + if real_hdr.isutcnt != 0 && real_hdr.isutcnt != real_hdr.typecnt { + return + } + if real_hdr.isstdcnt != 0 && real_hdr.isstdcnt != real_hdr.typecnt { + return + } + + // Grab the real data block + real_block_size, _ := tzif_data_block_size(&real_hdr, v1_hdr.version) + if len(buffer) <= size_of(real_hdr) + real_block_size { + return + } + buffer = buffer[size_of(real_hdr):] + + time_size := 8 + transition_times := slice.reinterpret([]i64be, buffer[:int(real_hdr.timecnt)*size_of(i64be)]) + for time in transition_times { + if time < BIG_BANG_ISH { + return + } + } + buffer = buffer[int(real_hdr.timecnt)*time_size:] + + transition_types := buffer[:int(real_hdr.timecnt)] + for type in transition_types { + if int(type) > int(real_hdr.typecnt - 1) { + return + } + } + buffer = buffer[int(real_hdr.timecnt):] + + local_time_types := slice.reinterpret([]Local_Time_Type, buffer[:int(real_hdr.typecnt)*size_of(Local_Time_Type)]) + for ltt in local_time_types { + // UT offset should be > -25 hours and < 26 hours + if int(ltt.utoff) < -89999 || int(ltt.utoff) > 93599 { + return + } + + if ltt.dst != .DST && ltt.dst != .Standard { + return + } + + if int(ltt.idx) > int(real_hdr.charcnt - 1) { + return + } + } + + buffer = buffer[int(real_hdr.typecnt) * size_of(Local_Time_Type):] + timezone_string_table := buffer[:real_hdr.charcnt] + buffer = buffer[real_hdr.charcnt:] + + leapsecond_records := slice.reinterpret([]Leapsecond_Record, buffer[:int(real_hdr.leapcnt)*size_of(Leapsecond_Record)]) + if len(leapsecond_records) > 0 { + if leapsecond_records[0].occur < 0 { + return + } + } + buffer = buffer[(int(real_hdr.leapcnt) * size_of(Leapsecond_Record)):] + + standard_wall_tags := buffer[:int(real_hdr.isstdcnt)] + buffer = buffer[int(real_hdr.isstdcnt):] + + ut_tags := buffer[:int(real_hdr.isutcnt)] + + for stdwall_tag, idx in standard_wall_tags { + ut_tag := ut_tags[idx] + + if (stdwall_tag != 0 && stdwall_tag != 1) { + return + } + if (ut_tag != 0 && ut_tag != 1) { + return + } + + if ut_tag == 1 && stdwall_tag != 1 { + return + } + } + buffer = buffer[int(real_hdr.isutcnt):] + + // Start of footer + if buffer[0] != '\n' { + return + } + buffer = buffer[1:] + + if buffer[0] == ':' { + return + } + + end_idx := 0 + for ch in buffer { + if ch == '\n' { + break + } + + if ch == 0 { + return + } + end_idx += 1 + } + footer_str := string(buffer[:end_idx]) + + // UTC is a special case, we don't need to alloc + if len(local_time_types) == 1 { + name := cstring(raw_data(timezone_string_table[local_time_types[0].idx:])) + if name != "UTC" { + return + } + + return nil, true + } + + ltt_names, err := make([dynamic]string, 0, len(local_time_types), allocator) + if err != nil { return } + defer if err != nil { + for name in ltt_names { + delete(name, allocator) + } + delete(ltt_names) + } + + for ltt in local_time_types { + name := cstring(raw_data(timezone_string_table[ltt.idx:])) + ltt_name: string + + ltt_name, err = strings.clone_from_cstring_bounded(name, len(timezone_string_table), allocator) + if err != nil { return } + + append(<t_names, ltt_name) + } + + records: []datetime.TZ_Record + records, err = make([]datetime.TZ_Record, len(transition_times), allocator) + if err != nil { return } + defer if err != nil { delete(records, allocator) } + + for trans_time, idx in transition_times { + trans_idx := transition_types[idx] + ltt := local_time_types[trans_idx] + + records[idx] = datetime.TZ_Record{ + time = i64(trans_time), + utc_offset = i64(ltt.utoff), + shortname = ltt_names[trans_idx], + dst = bool(ltt.dst), + } + } + + rrule, ok2 := parse_posix_tz(footer_str, allocator) + if !ok2 { return } + defer if err != nil { + delete(rrule.std_name, allocator) + delete(rrule.dst_name, allocator) + } + + region_name_out: string + region_name_out, err = strings.clone(region_name, allocator) + if err != nil { return } + defer if err != nil { delete(region_name_out, allocator) } + + region: ^datetime.TZ_Region + region, err = new_clone(datetime.TZ_Region{ + records = records, + shortnames = ltt_names[:], + name = region_name_out, + rrule = rrule, + }, allocator) + if err != nil { + return + } + + return region, true +} diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index 6bdd6558a..e2bcf7f68 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -106,6 +106,18 @@ decode :: proc(d: []rune, s: []u16) -> (n: int) { return } +rune_count :: proc(s: []u16) -> (n: int) { + for i := 0; i < len(s); i += 1 { + c := s[i] + if _surr1 <= c && c < _surr2 && i+1 < len(s) && + _surr2 <= s[i+1] && s[i+1] < _surr3 { + i += 1 + } + n += 1 + } + return +} + decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { @@ -127,4 +139,4 @@ decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) { n += copy(d[n:], b[:w]) } return -} \ No newline at end of file +} diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin index 431939efe..4b0fe7241 100644 --- a/core/unicode/utf8/utf8string/string.odin +++ b/core/unicode/utf8/utf8string/string.odin @@ -66,7 +66,7 @@ at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) { return case s.rune_count-1: - r, s.width = utf8.decode_rune_in_string(s.contents) + r, s.width = utf8.decode_last_rune(s.contents) s.rune_pos = i s.byte_pos = _len(s.contents) - s.width return diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index 5bd45fda4..4a8a198d3 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -135,6 +135,7 @@ import table "core:text/table" import thread "core:thread" import time "core:time" import datetime "core:time/datetime" +import timezone "core:time/timezone" import flags "core:flags" import orca "core:sys/orca" @@ -258,6 +259,7 @@ _ :: edit _ :: thread _ :: time _ :: datetime +_ :: timezone _ :: flags _ :: orca _ :: sysinfo diff --git a/examples/all/all_posix.odin b/examples/all/all_posix.odin index 76fac0b87..61b33a5c6 100644 --- a/examples/all/all_posix.odin +++ b/examples/all/all_posix.odin @@ -1,6 +1,8 @@ #+build darwin, openbsd, freebsd, netbsd package all -import posix "core:sys/posix" +import posix "core:sys/posix" +import kqueue "core:sys/kqueue" _ :: posix +_ :: kqueue diff --git a/examples/all/all_vendor.odin b/examples/all/all_vendor.odin index 1ab1debea..b224a3bbe 100644 --- a/examples/all/all_vendor.odin +++ b/examples/all/all_vendor.odin @@ -63,11 +63,15 @@ _ :: xlib // NOTE: needed for doc generator import NS "core:sys/darwin/Foundation" +import CF "core:sys/darwin/CoreFoundation" +import SEC "core:sys/darwin/Security" import MTL "vendor:darwin/Metal" import MTK "vendor:darwin/MetalKit" import CA "vendor:darwin/QuartzCore" _ :: NS +_ :: CF +_ :: SEC _ :: MTL _ :: MTK _ :: CA diff --git a/odin.rdi b/odin.rdi new file mode 100644 index 000000000..851ee3578 Binary files /dev/null and b/odin.rdi differ diff --git a/src/big_int.cpp b/src/big_int.cpp index 83235483c..8e476f090 100644 --- a/src/big_int.cpp +++ b/src/big_int.cpp @@ -62,6 +62,7 @@ gb_internal void big_int_shl (BigInt *dst, BigInt const *x, BigInt const *y); gb_internal void big_int_shr (BigInt *dst, BigInt const *x, BigInt const *y); gb_internal void big_int_mul (BigInt *dst, BigInt const *x, BigInt const *y); gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y); +gb_internal void big_int_exp_u64(BigInt *dst, BigInt const *x, u64 y, bool *success); gb_internal void big_int_quo_rem(BigInt const *x, BigInt const *y, BigInt *q, BigInt *r); gb_internal void big_int_quo (BigInt *z, BigInt const *x, BigInt const *y); @@ -250,9 +251,7 @@ gb_internal void big_int_from_string(BigInt *dst, String const &s, bool *success exp *= 10; exp += v; } - for (u64 x = 0; x < exp; x++) { - big_int_mul_eq(dst, &b); - } + big_int_exp_u64(dst, &b, exp, success); } if (is_negative) { @@ -328,6 +327,18 @@ gb_internal void big_int_mul_u64(BigInt *dst, BigInt const *x, u64 y) { big_int_dealloc(&d); } +gb_internal void big_int_exp_u64(BigInt *dst, BigInt const *x, u64 y, bool *success) { + if (y > INT_MAX) { + *success = false; + return; + } + + // Note: The cutoff for square-multiply being faster than the naive + // for loop is when exp > 4, but it probably isn't worth adding + // a fast path. + mp_err err = mp_expt_n(x, int(y), dst); + *success = err == MP_OKAY; +} gb_internal void big_int_mul(BigInt *dst, BigInt const *x, BigInt const *y) { mp_mul(x, y, dst); diff --git a/src/build_settings.cpp b/src/build_settings.cpp index e365d0324..e4413b1fe 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -453,7 +453,7 @@ struct BuildContext { bool no_threaded_checker; bool show_debug_messages; - + bool copy_file_contents; bool no_rtti; diff --git a/src/cached.cpp b/src/cached.cpp index 4ad65ee9e..efdadce7b 100644 --- a/src/cached.cpp +++ b/src/cached.cpp @@ -187,10 +187,7 @@ gb_internal bool try_copy_executable_from_cache(void) { extern char **environ; #endif -// returns false if different, true if it is the same -gb_internal bool try_cached_build(Checker *c, Array const &args) { - TEMPORARY_ALLOCATOR_GUARD(); - +Array cache_gather_files(Checker *c) { Parser *p = c->parser; auto files = array_make(heap_allocator()); @@ -222,29 +219,11 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { array_sort(files, string_cmp); - u64 crc = 0; - for (String const &path : files) { - crc = crc64_with_seed(path.text, path.len, crc); - } - - String base_cache_dir = build_context.build_paths[BuildPath_Output].basename; - base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache")); - (void)check_if_exists_directory_otherwise_create(base_cache_dir); - - gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16); - crc_str = gb_string_append_fmt(crc_str, "%016llx", crc); - String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); - String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest")); - String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest")); - String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest")); - - build_context.build_cache_data.cache_dir = cache_dir; - build_context.build_cache_data.files_path = files_path; - build_context.build_cache_data.args_path = args_path; - build_context.build_cache_data.env_path = env_path; + return files; +} +Array cache_gather_envs() { auto envs = array_make(heap_allocator()); - defer (array_free(&envs)); { #if defined(GB_SYSTEM_WINDOWS) wchar_t *strings = GetEnvironmentStringsW(); @@ -275,19 +254,50 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { #endif } array_sort(envs, string_cmp); + return envs; +} + +// returns false if different, true if it is the same +gb_internal bool try_cached_build(Checker *c, Array const &args) { + TEMPORARY_ALLOCATOR_GUARD(); + + auto files = cache_gather_files(c); + auto envs = cache_gather_envs(); + defer (array_free(&envs)); + + u64 crc = 0; + for (String const &path : files) { + crc = crc64_with_seed(path.text, path.len, crc); + } + + String base_cache_dir = build_context.build_paths[BuildPath_Output].basename; + base_cache_dir = concatenate_strings(permanent_allocator(), base_cache_dir, str_lit("/.odin-cache")); + (void)check_if_exists_directory_otherwise_create(base_cache_dir); + + gbString crc_str = gb_string_make_reserve(permanent_allocator(), 16); + crc_str = gb_string_append_fmt(crc_str, "%016llx", crc); + String cache_dir = concatenate3_strings(permanent_allocator(), base_cache_dir, str_lit("/"), make_string_c(crc_str)); + String files_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("files.manifest")); + String args_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("args.manifest")); + String env_path = concatenate3_strings(permanent_allocator(), cache_dir, str_lit("/"), str_lit("env.manifest")); + + build_context.build_cache_data.cache_dir = cache_dir; + build_context.build_cache_data.files_path = files_path; + build_context.build_cache_data.args_path = args_path; + build_context.build_cache_data.env_path = env_path; if (check_if_exists_directory_otherwise_create(cache_dir)) { - goto write_cache; + return false; } if (check_if_exists_file_otherwise_create(files_path)) { - goto write_cache; + return false; } if (check_if_exists_file_otherwise_create(args_path)) { - goto write_cache; + return false; } if (check_if_exists_file_otherwise_create(env_path)) { - goto write_cache; + return false; } { @@ -297,7 +307,7 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { LoadedFileError file_err = load_file_32( alloc_cstring(temporary_allocator(), files_path), &loaded_file, - false + true ); if (file_err > LoadedFile_Empty) { return false; @@ -315,7 +325,7 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { } isize sep = string_index_byte(line, ' '); if (sep < 0) { - goto write_cache; + return false; } String timestamp_str = substring(line, 0, sep); @@ -325,21 +335,21 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { path_str = string_trim_whitespace(path_str); if (file_count >= files.count) { - goto write_cache; + return false; } if (files[file_count] != path_str) { - goto write_cache; + return false; } u64 timestamp = exact_value_to_u64(exact_value_integer_from_string(timestamp_str)); gbFileTime last_write_time = gb_file_last_write_time(alloc_cstring(temporary_allocator(), path_str)); if (last_write_time != timestamp) { - goto write_cache; + return false; } } if (file_count != files.count) { - goto write_cache; + return false; } } { @@ -348,7 +358,7 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { LoadedFileError file_err = load_file_32( alloc_cstring(temporary_allocator(), args_path), &loaded_file, - false + true ); if (file_err > LoadedFile_Empty) { return false; @@ -366,11 +376,11 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { break; } if (args_count >= args.count) { - goto write_cache; + return false; } if (line != args[args_count]) { - goto write_cache; + return false; } } } @@ -380,7 +390,7 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { LoadedFileError file_err = load_file_32( alloc_cstring(temporary_allocator(), env_path), &loaded_file, - false + true ); if (file_err > LoadedFile_Empty) { return false; @@ -398,20 +408,26 @@ gb_internal bool try_cached_build(Checker *c, Array const &args) { break; } if (env_count >= envs.count) { - goto write_cache; + return false; } if (line != envs[env_count]) { - goto write_cache; + return false; } } } return try_copy_executable_from_cache(); +} + +void write_cached_build(Checker *c, Array const &args) { + auto files = cache_gather_files(c); + defer (array_free(&files)); + auto envs = cache_gather_envs(); + defer (array_free(&envs)); -write_cache:; { - char const *path_c = alloc_cstring(temporary_allocator(), files_path); + char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.files_path); gb_file_remove(path_c); debugf("Cache: updating %s\n", path_c); @@ -426,7 +442,7 @@ write_cache:; } } { - char const *path_c = alloc_cstring(temporary_allocator(), args_path); + char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.args_path); gb_file_remove(path_c); debugf("Cache: updating %s\n", path_c); @@ -441,7 +457,7 @@ write_cache:; } } { - char const *path_c = alloc_cstring(temporary_allocator(), env_path); + char const *path_c = alloc_cstring(temporary_allocator(), build_context.build_cache_data.env_path); gb_file_remove(path_c); debugf("Cache: updating %s\n", path_c); @@ -454,8 +470,5 @@ write_cache:; gb_fprintf(&f, "%.*s\n", LIT(env)); } } - - - return false; } diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index a4dc80733..85e532940 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1533,6 +1533,10 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c for (FileInfo fi : list) { LoadFileCache *cache = nullptr; + if (fi.is_dir) { + continue; + } + if (cache_load_file_directive(c, call, fi.fullpath, err_on_not_found, &cache, LoadFileTier_Contents, /*use_mutex*/false)) { array_add(&file_caches, cache); } else { @@ -2074,8 +2078,8 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As bool ok = check_builtin_simd_operation(c, operand, call, id, type_hint); if (!ok) { operand->type = t_invalid; + operand->mode = Addressing_Value; } - operand->mode = Addressing_Value; operand->value = {}; operand->expr = call; return ok; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 0cc89435d..60eb030ff 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -88,11 +88,12 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o e->type = t_invalid; return nullptr; } else if (is_type_polymorphic(t)) { - Entity *e = entity_of_node(operand->expr); - if (e == nullptr) { + Entity *e2 = entity_of_node(operand->expr); + if (e2 == nullptr) { + e->type = t_invalid; return nullptr; } - if (e->state.load() != EntityState_Resolved) { + if (e2->state.load() != EntityState_Resolved) { gbString str = type_to_string(t); defer (gb_string_free(str)); error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 5efa0ceee..71cd29817 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5394,22 +5394,25 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod Type *t = type_deref(operand->type); if (t == nullptr) { error(operand->expr, "Cannot use a selector expression on 0-value expression"); - } else if (is_type_dynamic_array(t)) { - init_mem_allocator(c->checker); - } - sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type); - entity = sel.entity; + } else { + if (is_type_dynamic_array(t)) { + init_mem_allocator(c->checker); + } + sel = lookup_field(operand->type, field_name, operand->mode == Addressing_Type); + entity = sel.entity; - // NOTE(bill): Add type info needed for fields like 'names' - if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) { - add_type_info_type(c, operand->type); - } - if (is_type_enum(operand->type)) { - add_type_info_type(c, operand->type); + // NOTE(bill): Add type info needed for fields like 'names' + if (entity != nullptr && (entity->flags&EntityFlag_TypeField)) { + add_type_info_type(c, operand->type); + } + if (is_type_enum(operand->type)) { + add_type_info_type(c, operand->type); + } } } - if (entity == nullptr && selector->kind == Ast_Ident && (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { + if (entity == nullptr && selector->kind == Ast_Ident && operand->type != nullptr && + (is_type_array(type_deref(operand->type)) || is_type_simd_vector(type_deref(operand->type)))) { String field_name = selector->Ident.token.string; if (1 < field_name.len && field_name.len <= 4) { u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'}; @@ -8010,6 +8013,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c pt = data.gen_entity->type; } } + pt = base_type(pt); if (pt->kind == Type_Proc && pt->Proc.calling_convention == ProcCC_Odin) { if ((c->scope->flags & ScopeFlag_ContextDefined) == 0) { diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 74a9e8825..2418fcc5c 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2600,6 +2600,23 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { check_expr(ctx, &o, fs->cond); if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) { error(fs->cond, "Non-boolean condition in 'for' statement"); + } else { + Ast *cond = unparen_expr(o.expr); + if (cond && cond->kind == Ast_BinaryExpr && + cond->BinaryExpr.left && cond->BinaryExpr.right && + cond->BinaryExpr.op.kind == Token_GtEq && + is_type_unsigned(type_of_expr(cond->BinaryExpr.left)) && + cond->BinaryExpr.right->tav.value.kind == ExactValue_Integer && + is_exact_value_zero(cond->BinaryExpr.right->tav.value)) { + warning(cond, "Expression is always true since unsigned numbers are always >= 0"); + } else if (cond && cond->kind == Ast_BinaryExpr && + cond->BinaryExpr.left && cond->BinaryExpr.right && + cond->BinaryExpr.op.kind == Token_LtEq && + is_type_unsigned(type_of_expr(cond->BinaryExpr.right)) && + cond->BinaryExpr.left->tav.value.kind == ExactValue_Integer && + is_exact_value_zero(cond->BinaryExpr.left->tav.value)) { + warning(cond, "Expression is always true since unsigned numbers are always >= 0"); + } } } if (fs->post != nullptr) { diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 1a42a82a9..5d6016ecc 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -687,6 +687,7 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_String: case ExactValue_Quaternion: case ExactValue_Pointer: + case ExactValue_Compound: case ExactValue_Procedure: case ExactValue_Typeid: return; diff --git a/src/gb/gb.h b/src/gb/gb.h index 1fef4b4f5..f74026c7d 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -2541,7 +2541,11 @@ gb_inline void const *gb_pointer_add_const(void const *ptr, isize bytes) { gb_inline void const *gb_pointer_sub_const(void const *ptr, isize bytes) { return cast(void const *)(cast(u8 const *)ptr - bytes); } gb_inline isize gb_pointer_diff (void const *begin, void const *end) { return cast(isize)(cast(u8 const *)end - cast(u8 const *)begin); } -gb_inline void gb_zero_size(void *ptr, isize size) { memset(ptr, 0, size); } +gb_inline void gb_zero_size(void *ptr, isize size) { + if (size != 0) { + memset(ptr, 0, size); + } +} #if defined(_MSC_VER) && !defined(__clang__) diff --git a/src/linker.cpp b/src/linker.cpp index 500fead69..1ffec3bf7 100644 --- a/src/linker.cpp +++ b/src/linker.cpp @@ -605,9 +605,18 @@ gb_internal i32 linker_stage(LinkerData *gen) { link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' "); } - } else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku && build_context.metrics.arch != TargetArch_riscv64) { - // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it. - link_settings = gb_string_appendc(link_settings, "-no-pie "); + } + + if (build_context.build_mode == BuildMode_Executable && build_context.reloc_mode == RelocMode_PIC) { + // Do not disable PIE, let the linker choose. (most likely you want it enabled) + } else if (build_context.build_mode != BuildMode_DynamicLibrary) { + if (build_context.metrics.os != TargetOs_openbsd + && build_context.metrics.os != TargetOs_haiku + && build_context.metrics.arch != TargetArch_riscv64 + ) { + // OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it. + link_settings = gb_string_appendc(link_settings, "-no-pie "); + } } gbString platform_lib_str = gb_string_make(heap_allocator(), ""); @@ -684,7 +693,7 @@ gb_internal i32 linker_stage(LinkerData *gen) { if (is_osx && build_context.ODIN_DEBUG) { // NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe // to the symbols in the object file - result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename)); + result = system_exec_command_line_app("dsymutil", "dsymutil \"%.*s\"", LIT(output_filename)); if (result) { return result; diff --git a/src/llvm_backend_expr.cpp b/src/llvm_backend_expr.cpp index 8967a4e03..8ad44035d 100644 --- a/src/llvm_backend_expr.cpp +++ b/src/llvm_backend_expr.cpp @@ -130,7 +130,7 @@ gb_internal lbValue lb_emit_unary_arith(lbProcedure *p, TokenKind op, lbValue x, LLVMTypeRef vector_type = nullptr; if (op != Token_Not && lb_try_vector_cast(p->module, val, &vector_type)) { LLVMValueRef vp = LLVMBuildPointerCast(p->builder, val.value, LLVMPointerType(vector_type, 0), ""); - LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, ""); + LLVMValueRef v = OdinLLVMBuildLoad(p, vector_type, vp); LLVMValueRef opv = nullptr; switch (op) { @@ -324,8 +324,8 @@ gb_internal bool lb_try_direct_vector_arith(lbProcedure *p, TokenKind op, lbValu LLVMValueRef lhs_vp = LLVMBuildPointerCast(p->builder, lhs_ptr.value, LLVMPointerType(vector_type, 0), ""); LLVMValueRef rhs_vp = LLVMBuildPointerCast(p->builder, rhs_ptr.value, LLVMPointerType(vector_type, 0), ""); - LLVMValueRef x = LLVMBuildLoad2(p->builder, vector_type, lhs_vp, ""); - LLVMValueRef y = LLVMBuildLoad2(p->builder, vector_type, rhs_vp, ""); + LLVMValueRef x = OdinLLVMBuildLoad(p, vector_type, lhs_vp); + LLVMValueRef y = OdinLLVMBuildLoad(p, vector_type, rhs_vp); LLVMValueRef z = nullptr; if (is_type_float(integral_type)) { @@ -551,15 +551,14 @@ gb_internal LLVMValueRef lb_matrix_to_vector(lbProcedure *p, lbValue matrix) { Type *mt = base_type(matrix.type); GB_ASSERT(mt->kind == Type_Matrix); LLVMTypeRef elem_type = lb_type(p->module, mt->Matrix.elem); - + unsigned total_count = cast(unsigned)matrix_type_total_internal_elems(mt); LLVMTypeRef total_matrix_type = LLVMVectorType(elem_type, total_count); - + #if 1 LLVMValueRef ptr = lb_address_from_load_or_generate_local(p, matrix).value; LLVMValueRef matrix_vector_ptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(total_matrix_type, 0), ""); - LLVMValueRef matrix_vector = LLVMBuildLoad2(p->builder, total_matrix_type, matrix_vector_ptr, ""); - LLVMSetAlignment(matrix_vector, cast(unsigned)type_align_of(mt)); + LLVMValueRef matrix_vector = OdinLLVMBuildLoadAligned(p, total_matrix_type, matrix_vector_ptr, type_align_of(mt)); return matrix_vector; #else LLVMValueRef matrix_vector = LLVMBuildBitCast(p->builder, matrix.value, total_matrix_type, ""); @@ -1648,7 +1647,7 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { lb_emit_store(p, a1, id); return lb_addr_load(p, res); } else if (dst->kind == Type_Basic) { - if (src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) { + if (src->kind == Type_Basic && src->Basic.kind == Basic_string && dst->Basic.kind == Basic_cstring) { String str = lb_get_const_string(m, value); lbValue res = {}; res.type = t; @@ -2364,12 +2363,23 @@ gb_internal lbValue lb_emit_conv(lbProcedure *p, lbValue value, Type *t) { GB_ASSERT(src->kind == Type_Matrix); lbAddr v = lb_add_local_generated(p, t, true); - if (is_matrix_square(dst) && is_matrix_square(dst)) { + if (dst->Matrix.row_count == src->Matrix.row_count && + dst->Matrix.column_count == src->Matrix.column_count) { + for (i64 j = 0; j < dst->Matrix.column_count; j++) { + for (i64 i = 0; i < dst->Matrix.row_count; i++) { + lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); + lbValue s = lb_emit_matrix_ev(p, value, i, j); + s = lb_emit_conv(p, s, dst->Matrix.elem); + lb_emit_store(p, d, s); + } + } + } else if (is_matrix_square(dst) && is_matrix_square(dst)) { for (i64 j = 0; j < dst->Matrix.column_count; j++) { for (i64 i = 0; i < dst->Matrix.row_count; i++) { if (i < src->Matrix.row_count && j < src->Matrix.column_count) { lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); lbValue s = lb_emit_matrix_ev(p, value, i, j); + s = lb_emit_conv(p, s, dst->Matrix.elem); lb_emit_store(p, d, s); } else if (i == j) { lbValue d = lb_emit_matrix_epi(p, v.addr, i, j); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 842a1cbc8..686724f6a 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -722,7 +722,10 @@ gb_internal unsigned lb_try_get_alignment(LLVMValueRef addr_ptr, unsigned defaul gb_internal bool lb_try_update_alignment(LLVMValueRef addr_ptr, unsigned alignment) { if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) { if (LLVMGetAlignment(addr_ptr) < alignment) { - if (LLVMIsAAllocaInst(addr_ptr) || LLVMIsAGlobalValue(addr_ptr)) { + if (LLVMIsAAllocaInst(addr_ptr)) { + LLVMSetAlignment(addr_ptr, alignment); + } else if (LLVMIsAGlobalValue(addr_ptr) && LLVMGetLinkage(addr_ptr) != LLVMExternalLinkage) { + // NOTE(laytan): setting alignment of an external global just changes the alignment we expect it to be. LLVMSetAlignment(addr_ptr, alignment); } } @@ -755,10 +758,7 @@ gb_internal bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vecto LLVMValueRef addr_ptr = ptr.value; if (LLVMIsAAllocaInst(addr_ptr) || LLVMIsAGlobalValue(addr_ptr)) { - unsigned alignment = LLVMGetAlignment(addr_ptr); - alignment = gb_max(alignment, vector_alignment); - possible = true; - LLVMSetAlignment(addr_ptr, alignment); + possible = lb_try_update_alignment(addr_ptr, vector_alignment); } else if (LLVMIsALoadInst(addr_ptr)) { unsigned alignment = LLVMGetAlignment(addr_ptr); possible = alignment >= vector_alignment; @@ -774,6 +774,36 @@ gb_internal bool lb_try_vector_cast(lbModule *m, lbValue ptr, LLVMTypeRef *vecto return false; } +gb_internal LLVMValueRef OdinLLVMBuildLoad(lbProcedure *p, LLVMTypeRef type, LLVMValueRef value) { + LLVMValueRef result = LLVMBuildLoad2(p->builder, type, value, ""); + + // If it is not an instruction it isn't a GEP, so we don't need to track alignment in the metadata, + // which is not possible anyway (only LLVM instructions can have metadata). + if (LLVMIsAInstruction(value)) { + u64 is_packed = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_IS_PACKED); + if (is_packed != 0) { + LLVMSetAlignment(result, 1); + } + } + + return result; +} + +gb_internal LLVMValueRef OdinLLVMBuildLoadAligned(lbProcedure *p, LLVMTypeRef type, LLVMValueRef value, i64 alignment) { + LLVMValueRef result = LLVMBuildLoad2(p->builder, type, value, ""); + + LLVMSetAlignment(result, cast(unsigned)alignment); + + if (LLVMIsAInstruction(value)) { + u64 is_packed = lb_get_metadata_custom_u64(p->module, value, ODIN_METADATA_IS_PACKED); + if (is_packed != 0) { + LLVMSetAlignment(result, 1); + } + } + + return result; +} + gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) { if (addr.addr.value == nullptr) { return; @@ -1119,7 +1149,7 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { Type *vt = base_type(value.type); GB_ASSERT(vt->kind == Type_MultiPointer); Type *t = vt->MultiPointer.elem; - LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, ""); + LLVMValueRef v = OdinLLVMBuildLoad(p, lb_type(p->module, t), value.value); return lbValue{v, t}; } else if (is_type_soa_pointer(value.type)) { lbValue ptr = lb_emit_struct_ev(p, value, 0); @@ -1130,16 +1160,7 @@ gb_internal lbValue lb_emit_load(lbProcedure *p, lbValue value) { GB_ASSERT_MSG(is_type_pointer(value.type), "%s", type_to_string(value.type)); Type *t = type_deref(value.type); - LLVMValueRef v = LLVMBuildLoad2(p->builder, lb_type(p->module, t), value.value, ""); - - // If it is not an instruction it isn't a GEP, so we don't need to track alignment in the metadata, - // which is not possible anyway (only LLVM instructions can have metadata). - if (LLVMIsAInstruction(value.value)) { - u64 is_packed = lb_get_metadata_custom_u64(p->module, value.value, ODIN_METADATA_IS_PACKED); - if (is_packed != 0) { - LLVMSetAlignment(v, 1); - } - } + LLVMValueRef v = OdinLLVMBuildLoad(p, lb_type(p->module, t), value.value); return lbValue{v, t}; } @@ -1413,7 +1434,7 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) { LLVMTypeRef vector_type = nullptr; if (lb_try_vector_cast(p->module, addr.addr, &vector_type)) { LLVMValueRef vp = LLVMBuildPointerCast(p->builder, addr.addr.value, LLVMPointerType(vector_type, 0), ""); - LLVMValueRef v = LLVMBuildLoad2(p->builder, vector_type, vp, ""); + LLVMValueRef v = OdinLLVMBuildLoad(p, vector_type, vp); LLVMValueRef scalars[4] = {}; for (u8 i = 0; i < addr.swizzle.count; i++) { scalars[i] = LLVMConstInt(lb_type(p->module, t_u32), addr.swizzle.indices[i], false); @@ -2710,7 +2731,7 @@ general_end:; if (LLVMIsALoadInst(val) && (src_size >= dst_size && src_align >= dst_align)) { LLVMValueRef val_ptr = LLVMGetOperand(val, 0); val_ptr = LLVMBuildPointerCast(p->builder, val_ptr, LLVMPointerType(dst_type, 0), ""); - LLVMValueRef loaded_val = LLVMBuildLoad2(p->builder, dst_type, val_ptr, ""); + LLVMValueRef loaded_val = OdinLLVMBuildLoad(p, dst_type, val_ptr); // LLVMSetAlignment(loaded_val, gb_min(src_align, dst_align)); @@ -2726,7 +2747,7 @@ general_end:; LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); LLVMBuildStore(p->builder, val, nptr); - return LLVMBuildLoad2(p->builder, dst_type, ptr, ""); + return OdinLLVMBuildLoad(p, dst_type, ptr); } } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index d84599eb0..5aee5b639 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -2568,7 +2568,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu case BuiltinProc_atomic_load_explicit: { lbValue dst = lb_build_expr(p, ce->args[0]); - LLVMValueRef instr = LLVMBuildLoad2(p->builder, lb_type(p->module, type_deref(dst.type)), dst.value, ""); + LLVMValueRef instr = OdinLLVMBuildLoad(p, lb_type(p->module, type_deref(dst.type)), dst.value); switch (id) { case BuiltinProc_non_temporal_load: { @@ -2621,8 +2621,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu if (is_type_simd_vector(t)) { lbValue res = {}; res.type = t; - res.value = LLVMBuildLoad2(p->builder, lb_type(p->module, t), src.value, ""); - LLVMSetAlignment(res.value, 1); + res.value = OdinLLVMBuildLoadAligned(p, lb_type(p->module, t), src.value, 1); return res; } else { lbAddr dst = lb_add_local_generated(p, t, false); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index df3d4bc03..06d66ac80 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -2001,7 +2001,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { LLVMValueRef ptr = p->temp_callee_return_struct_memory; LLVMValueRef nptr = LLVMBuildPointerCast(p->builder, ptr, LLVMPointerType(src_type, 0), ""); LLVMBuildStore(p->builder, ret_val, nptr); - ret_val = LLVMBuildLoad2(p->builder, ret_type, ptr, ""); + ret_val = OdinLLVMBuildLoad(p, ret_type, ptr); } else { ret_val = OdinLLVMBuildTransmute(p, ret_val, ret_type); } @@ -2018,14 +2018,7 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res) { gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return_results) { lb_ensure_abi_function_type(p->module, p); - lbValue res = {}; - - TypeTuple *tuple = &p->type->Proc.results->Tuple; isize return_count = p->type->Proc.result_count; - isize res_count = return_results.count; - - lbFunctionType *ft = lb_get_function_type(p->module, p->type); - bool return_by_pointer = ft->ret.kind == lbArg_Indirect; if (return_count == 0) { // No return values @@ -2038,7 +2031,17 @@ gb_internal void lb_build_return_stmt(lbProcedure *p, Slice const &return LLVMBuildRetVoid(p->builder); } return; - } else if (return_count == 1) { + } + + lbValue res = {}; + + TypeTuple *tuple = &p->type->Proc.results->Tuple; + isize res_count = return_results.count; + + lbFunctionType *ft = lb_get_function_type(p->module, p->type); + bool return_by_pointer = ft->ret.kind == lbArg_Indirect; + + if (return_count == 1) { Entity *e = tuple->variables[0]; if (res_count == 0) { rw_mutex_shared_lock(&p->module->values_mutex); diff --git a/src/llvm_backend_type.cpp b/src/llvm_backend_type.cpp index 9d4505bb0..f3fcc8de4 100644 --- a/src/llvm_backend_type.cpp +++ b/src/llvm_backend_type.cpp @@ -43,6 +43,8 @@ gb_internal u64 lb_typeid_kind(lbModule *m, Type *type, u64 id=0) { if (flags & BasicFlag_Pointer) kind = Typeid_Pointer; if (flags & BasicFlag_String) kind = Typeid_String; if (flags & BasicFlag_Rune) kind = Typeid_Rune; + + if (bt->Basic.kind == Basic_typeid) kind = Typeid_Type_Id; } break; case Type_Pointer: kind = Typeid_Pointer; break; case Type_MultiPointer: kind = Typeid_Multi_Pointer; break; diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index f63c42ab9..6367d0118 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -269,7 +269,7 @@ gb_internal lbValue lb_emit_transmute(lbProcedure *p, lbValue value, Type *t) { if (lb_try_update_alignment(ptr, align)) { LLVMTypeRef result_type = lb_type(p->module, t); res.value = LLVMBuildPointerCast(p->builder, ptr.value, LLVMPointerType(result_type, 0), ""); - res.value = LLVMBuildLoad2(p->builder, result_type, res.value, ""); + res.value = OdinLLVMBuildLoad(p, result_type, res.value); return res; } lbAddr addr = lb_add_local_generated(p, t, false); diff --git a/src/main.cpp b/src/main.cpp index 04d3bdf52..b04ee90f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2199,6 +2199,7 @@ gb_internal void print_show_help(String const arg0, String const &command) { print_usage_line(3, "-build-mode:test Builds as an executable that executes tests."); print_usage_line(3, "-build-mode:dll Builds as a dynamically linked library."); print_usage_line(3, "-build-mode:shared Builds as a dynamically linked library."); + print_usage_line(3, "-build-mode:dynamic Builds as a dynamically linked library."); print_usage_line(3, "-build-mode:lib Builds as a statically linked library."); print_usage_line(3, "-build-mode:static Builds as a statically linked library."); print_usage_line(3, "-build-mode:obj Builds as an object file."); @@ -3391,6 +3392,7 @@ int main(int arg_count, char const **arg_ptr) { Parser *parser = gb_alloc_item(permanent_allocator(), Parser); Checker *checker = gb_alloc_item(permanent_allocator(), Checker); + bool failed_to_cache_parsing = false; MAIN_TIME_SECTION("parse files"); @@ -3480,6 +3482,7 @@ int main(int arg_count, char const **arg_ptr) { if (try_cached_build(checker, args)) { goto end_of_code_gen; } + failed_to_cache_parsing = true; } #if ALLOW_TILDE @@ -3545,18 +3548,23 @@ int main(int arg_count, char const **arg_ptr) { end_of_code_gen:; - if (build_context.show_timings) { - show_timings(checker, &global_timings); - } - if (build_context.export_dependencies_format != DependenciesExportUnspecified) { export_dependencies(checker); } + if (build_context.cached) { + MAIN_TIME_SECTION("write cached build"); + if (!build_context.build_cache_data.copy_already_done) { + try_copy_executable_to_cache(); + } - if (!build_context.build_cache_data.copy_already_done && - build_context.cached) { - try_copy_executable_to_cache(); + if (failed_to_cache_parsing) { + write_cached_build(checker, args); + } + } + + if (build_context.show_timings) { + show_timings(checker, &global_timings); } if (run_output) { diff --git a/src/parser.cpp b/src/parser.cpp index 9f3d5efcc..9d164b3df 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -4262,8 +4262,6 @@ gb_internal bool allow_field_separator(AstFile *f) { gb_internal Ast *parse_struct_field_list(AstFile *f, isize *name_count_) { Token start_token = f->curr_token; - auto decls = array_make(ast_allocator(f)); - isize total_name_count = 0; Ast *params = parse_field_list(f, &total_name_count, FieldFlag_Struct, Token_CloseBrace, false, false); diff --git a/src/string.cpp b/src/string.cpp index 3c7d96934..190b69041 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -156,6 +156,7 @@ gb_internal isize string_index_byte(String const &s, u8 x) { gb_internal gb_inline bool str_eq(String const &a, String const &b) { if (a.len != b.len) return false; + if (a.len == 0) return true; return memcmp(a.text, b.text, a.len) == 0; } gb_internal gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); } diff --git a/src/unicode.cpp b/src/unicode.cpp index 665d5b182..cb9fb12ab 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -1,10 +1,15 @@ -#pragma warning(push) -#pragma warning(disable: 4245) +#if defined(GB_SYSTEM_WINDOWS) + #pragma warning(push) + #pragma warning(disable: 4245) +#endif extern "C" { #include "utf8proc/utf8proc.c" } -#pragma warning(pop) + +#if defined(GB_SYSTEM_WINDOWS) + #pragma warning(pop) +#endif gb_internal bool rune_is_letter(Rune r) { @@ -109,7 +114,7 @@ gb_internal isize utf8_decode(u8 const *str, isize str_len, Rune *codepoint_out) u8 b1, b2, b3; Utf8AcceptRange accept; if (x >= 0xf0) { - Rune mask = (cast(Rune)x << 31) >> 31; + Rune mask = -cast(Rune)(x & 1); codepoint = (cast(Rune)s0 & (~mask)) | (GB_RUNE_INVALID & mask); width = 1; goto end; diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index 45f47505a..c614727b9 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -382,6 +382,60 @@ test_lying_length_array :: proc(t: ^testing.T) { testing.expect_value(t, err, io.Error.Unexpected_EOF) // .Out_Of_Memory would be bad. } +@(test) +test_unmarshal_map_into_struct_partially :: proc(t: ^testing.T) { + + // Tests that when unmarshalling into a struct that has less fields than the binary map has fields, + // the additional fields in the binary are skipped and the rest of the binary is correctly decoded. + + Foo :: struct { + bar: struct { + hello: string, + world: string, + foo: string `cbor:"-"`, + }, + baz: int, + } + + Foo_More :: struct { + bar: struct { + hello: string, + world: string, + hellope: string, + foo: string, + }, + baz: int, + } + more := Foo_More{ + bar = { + hello = "hello", + world = "world", + hellope = "hellope", + foo = "foo", + }, + baz = 4, + } + + more_bin, err := cbor.marshal(more) + testing.expect_value(t, err, nil) + + less := Foo{ + bar = { + hello = "hello", + world = "world", + }, + baz = 4, + } + less_out: Foo + uerr := cbor.unmarshal(string(more_bin), &less_out) + testing.expect_value(t, uerr, nil) + testing.expect_value(t, less_out, less) + + delete(more_bin) + delete(less_out.bar.hello) + delete(less_out.bar.world) +} + @(test) test_decode_unsigned :: proc(t: ^testing.T) { expect_decoding(t, "\x00", "0", u8) diff --git a/tests/core/sys/posix/posix.odin b/tests/core/sys/posix/posix.odin index d73e49ffb..8daffc5b9 100644 --- a/tests/core/sys/posix/posix.odin +++ b/tests/core/sys/posix/posix.odin @@ -1,4 +1,4 @@ -#+build darwin, freebsd, openbsd, netbsd +#+build linux, darwin, freebsd, openbsd, netbsd package tests_core_posix import "core:log" @@ -144,7 +144,6 @@ test_libgen :: proc(t: ^testing.T) { { "usr/", ".", "usr" }, { "", ".", "." }, { "/", "/", "/" }, - { "//", "/", "/" }, { "///", "/", "/" }, { "/usr/", "/", "usr" }, { "/usr/lib", "/usr", "lib" }, @@ -203,18 +202,6 @@ test_stat :: proc(t: ^testing.T) { testing.expect_value(t, stat.st_size, posix.off_t(len(CONTENT))) } -@(test) -test_termios :: proc(t: ^testing.T) { - testing.expect_value(t, transmute(posix.CControl_Flags)posix.tcflag_t(posix._CSIZE), posix.CSIZE) - - testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._NLDLY), posix.NLDLY) - testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._CRDLY), posix.CRDLY) - testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._TABDLY), posix.TABDLY) - testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._BSDLY), posix.BSDLY) - testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._VTDLY), posix.VTDLY) - testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._FFDLY), posix.FFDLY) -} - @(test) test_pthreads :: proc(t: ^testing.T) { testing.set_fail_timeout(t, time.Second) diff --git a/tests/core/sys/posix/structs.odin b/tests/core/sys/posix/structs.odin index de9511634..a0e8fea99 100644 --- a/tests/core/sys/posix/structs.odin +++ b/tests/core/sys/posix/structs.odin @@ -1,4 +1,4 @@ -#+build darwin, freebsd, openbsd, netbsd +#+build linux, darwin, freebsd, openbsd, netbsd package tests_core_posix import "core:log" diff --git a/tests/core/sys/posix/structs/structs.c b/tests/core/sys/posix/structs/structs.c index 7d8038fbb..e78e872eb 100644 --- a/tests/core/sys/posix/structs/structs.c +++ b/tests/core/sys/posix/structs/structs.c @@ -40,7 +40,9 @@ int main(int argc, char *argv[]) printf("pthread_attr_t %zu %zu\n", sizeof(pthread_attr_t), _Alignof(pthread_attr_t)); printf("pthread_key_t %zu %zu\n", sizeof(pthread_key_t), _Alignof(pthread_key_t)); +#ifndef __linux__ printf("sched_param %zu %zu\n", sizeof(struct sched_param), _Alignof(struct sched_param)); +#endif printf("termios %zu %zu\n", sizeof(struct termios), _Alignof(struct termios)); @@ -99,5 +101,29 @@ int main(int argc, char *argv[]) printf("timespec %zu %zu\n", sizeof(struct timespec), _Alignof(struct timespec)); printf("clock_t %zu %zu\n", sizeof(clock_t), _Alignof(clock_t)); + printf("PTHREAD_CANCEL_ASYNCHRONOUS %d\n", PTHREAD_CANCEL_ASYNCHRONOUS); + printf("PTHREAD_CANCEL_DEFERRED %d\n", PTHREAD_CANCEL_DEFERRED); + + printf("PTHREAD_CANCEL_DISABLE %d\n", PTHREAD_CANCEL_DISABLE); + printf("PTHREAD_CANCEL_ENABLE %d\n", PTHREAD_CANCEL_ENABLE); + + printf("PTHREAD_CANCELED %p\n", PTHREAD_CANCELED); + + printf("PTHREAD_CREATE_JOINABLE %d\n", PTHREAD_CREATE_JOINABLE); + printf("PTHREAD_CREATE_DETACHED %d\n", PTHREAD_CREATE_DETACHED); + + printf("PTHREAD_EXPLICIT_SCHED %d\n", PTHREAD_EXPLICIT_SCHED); + printf("PTHREAD_INHERIT_SCHED %d\n", PTHREAD_INHERIT_SCHED); + + printf("PTHREAD_PRIO_INHERIT %d\n", PTHREAD_PRIO_INHERIT); + printf("PTHREAD_PRIO_NONE %d\n", PTHREAD_PRIO_NONE); + printf("PTHREAD_PRIO_PROTECT %d\n", PTHREAD_PRIO_PROTECT); + + printf("PTHREAD_PROCESS_SHARED %d\n", PTHREAD_PROCESS_SHARED); + printf("PTHREAD_PROCESS_PRIVATE %d\n", PTHREAD_PROCESS_PRIVATE); + + printf("PTHREAD_SCOPE_PROCESS %d\n", PTHREAD_SCOPE_PROCESS); + printf("PTHREAD_SCOPE_SYSTEM %d\n", PTHREAD_SCOPE_SYSTEM); + return 0; } diff --git a/tests/core/sys/posix/structs/structs.odin b/tests/core/sys/posix/structs/structs.odin index 2663d1e30..c94bb7c99 100644 --- a/tests/core/sys/posix/structs/structs.odin +++ b/tests/core/sys/posix/structs/structs.odin @@ -14,7 +14,11 @@ main :: proc() { fmt.println("pthread_attr_t", size_of(posix.pthread_attr_t), align_of(posix.pthread_attr_t)) fmt.println("pthread_key_t", size_of(posix.pthread_key_t), align_of(posix.pthread_key_t)) - fmt.println("sched_param", size_of(posix.sched_param), align_of(posix.sched_param)) + // NOTE: On Linux, differences between libc may mean the Odin side is larger than the other side, + // this is fine in practice. + when ODIN_OS != .Linux { + fmt.println("sched_param", size_of(posix.sched_param), align_of(posix.sched_param)) + } fmt.println("termios", size_of(posix.termios), align_of(posix.termios)) @@ -71,4 +75,28 @@ main :: proc() { fmt.println("time_t", size_of(posix.time_t), align_of(posix.time_t)) fmt.println("timespec", size_of(posix.timespec), align_of(posix.timespec)) fmt.println("clock_t", size_of(posix.clock_t), align_of(posix.clock_t)) + + fmt.println("PTHREAD_CANCEL_ASYNCHRONOUS", posix.PTHREAD_CANCEL_ASYNCHRONOUS) + fmt.println("PTHREAD_CANCEL_DEFERRED", posix.PTHREAD_CANCEL_DEFERRED) + + fmt.println("PTHREAD_CANCEL_DISABLE", posix.PTHREAD_CANCEL_DISABLE) + fmt.println("PTHREAD_CANCEL_ENABLE", posix.PTHREAD_CANCEL_ENABLE) + + fmt.printfln("PTHREAD_CANCELED %#x", posix.PTHREAD_CANCELED) + + fmt.println("PTHREAD_CREATE_JOINABLE", posix.PTHREAD_CREATE_JOINABLE) + fmt.println("PTHREAD_CREATE_DETACHED", posix.PTHREAD_CREATE_DETACHED) + + fmt.println("PTHREAD_EXPLICIT_SCHED", posix.PTHREAD_EXPLICIT_SCHED) + fmt.println("PTHREAD_INHERIT_SCHED", posix.PTHREAD_INHERIT_SCHED) + + fmt.println("PTHREAD_PRIO_INHERIT", posix.PTHREAD_PRIO_INHERIT) + fmt.println("PTHREAD_PRIO_NONE", posix.PTHREAD_PRIO_NONE) + fmt.println("PTHREAD_PRIO_PROTECT", posix.PTHREAD_PRIO_PROTECT) + + fmt.println("PTHREAD_PROCESS_SHARED", posix.PTHREAD_PROCESS_SHARED) + fmt.println("PTHREAD_PROCESS_PRIVATE", posix.PTHREAD_PROCESS_PRIVATE) + + fmt.println("PTHREAD_SCOPE_PROCESS", posix.PTHREAD_SCOPE_PROCESS) + fmt.println("PTHREAD_SCOPE_SYSTEM", posix.PTHREAD_SCOPE_SYSTEM) } diff --git a/tests/core/time/test_core_time.odin b/tests/core/time/test_core_time.odin index 424111aa3..93bc73789 100644 --- a/tests/core/time/test_core_time.odin +++ b/tests/core/time/test_core_time.odin @@ -3,6 +3,7 @@ package test_core_time import "core:testing" import "core:time" import dt "core:time/datetime" +import tz "core:time/timezone" is_leap_year :: time.is_leap_year @@ -349,3 +350,214 @@ date_component_roundtrip_test :: proc(t: ^testing.T, moment: dt.DateTime) { moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, YYYY, MM, DD, hh, mm, ss, ) } + +datetime_eq :: proc(dt1: dt.DateTime, dt2: dt.DateTime) -> bool { + return ( + dt1.year == dt2.year && dt1.month == dt2.month && dt1.day == dt2.day && + dt1.hour == dt2.hour && dt1.minute == dt2.minute && dt1.second == dt2.second + ) +} + +@test +test_convert_timezone_roundtrip :: proc(t: ^testing.T) { + dst_dt, _ := dt.components_to_datetime(2024, 10, 4, 23, 47, 0) + std_dt, _ := dt.components_to_datetime(2024, 11, 4, 23, 47, 0) + + local_tz, local_load_ok := tz.region_load("local") + testing.expectf(t, local_load_ok, "Failed to load local timezone") + defer tz.region_destroy(local_tz) + + edm_tz, edm_load_ok := tz.region_load("America/Edmonton") + testing.expectf(t, edm_load_ok, "Failed to load America/Edmonton timezone") + defer tz.region_destroy(edm_tz) + + shuffle_tz :: proc(start_dt: dt.DateTime, test_tz: ^dt.TZ_Region) -> dt.DateTime { + tz_dt := tz.datetime_to_tz(start_dt, test_tz) + utc_dt := tz.datetime_to_utc(tz_dt) + return utc_dt + } + + testing.expectf(t, datetime_eq(dst_dt, shuffle_tz(dst_dt, local_tz)), "Failed to convert to/from local dst timezone") + testing.expectf(t, datetime_eq(std_dt, shuffle_tz(std_dt, local_tz)), "Failed to convert to/from local std timezone") + testing.expectf(t, datetime_eq(dst_dt, shuffle_tz(dst_dt, edm_tz)), "Failed to convert to/from Edmonton dst timezone") + testing.expectf(t, datetime_eq(std_dt, shuffle_tz(std_dt, edm_tz)), "Failed to convert to/from Edmonton std timezone") +} + +@test +test_check_timezone_metadata :: proc(t: ^testing.T) { + dst_dt, _ := dt.components_to_datetime(2024, 10, 4, 23, 47, 0) + std_dt, _ := dt.components_to_datetime(2024, 11, 4, 23, 47, 0) + + pac_tz, pac_load_ok := tz.region_load("America/Los_Angeles") + testing.expectf(t, pac_load_ok, "Failed to load America/Los_Angeles timezone") + defer tz.region_destroy(pac_tz) + + pac_dst_dt := tz.datetime_to_tz(dst_dt, pac_tz) + pac_std_dt := tz.datetime_to_tz(std_dt, pac_tz) + testing.expectf(t, tz.shortname_unsafe(pac_dst_dt) == "PDT", "Invalid timezone shortname") + testing.expectf(t, tz.shortname_unsafe(pac_std_dt) == "PST", "Invalid timezone shortname") + testing.expectf(t, tz.dst_unsafe(pac_std_dt) == false, "Expected daylight savings == false, got true") + testing.expectf(t, tz.dst_unsafe(pac_dst_dt) == true, "Expected daylight savings == true, got false") + + pac_dst_name, ok := tz.shortname(pac_dst_dt) + testing.expectf(t, ok == true, "Invalid datetime") + testing.expectf(t, pac_dst_name == "PDT", "Invalid timezone shortname") + + pac_std_name, ok2 := tz.shortname(pac_std_dt) + testing.expectf(t, ok2 == true, "Invalid datetime") + testing.expectf(t, pac_std_name == "PST", "Invalid timezone shortname") + + pac_is_dst, ok3 := tz.dst(pac_dst_dt) + testing.expectf(t, ok3 == true, "Invalid datetime") + testing.expectf(t, pac_is_dst == true, "Expected daylight savings == false, got true") + + pac_is_dst, ok3 = tz.dst(pac_std_dt) + testing.expectf(t, ok3 == true, "Invalid datetime") + testing.expectf(t, pac_is_dst == false, "Expected daylight savings == false, got true") +} + +rrule_eq :: proc(r1, r2: dt.TZ_RRule) -> (eq: bool) { + if r1.has_dst != r2.has_dst { return } + + if r1.std_name != r2.std_name { return } + if r1.std_offset != r2.std_offset { return } + if r1.std_date != r2.std_date { return } + + if r1.dst_name != r2.dst_name { return } + if r1.dst_offset != r2.dst_offset { return } + if r1.dst_date != r2.dst_date { return } + + return true +} + +@test +test_check_timezone_posix_tz :: proc(t: ^testing.T) { + correct_simple_rrule := dt.TZ_RRule{ + has_dst = false, + + std_name = "UTC", + std_offset = -(5 * 60 * 60), + std_date = dt.TZ_Transition_Date{ + type = .Leap, + day = 0, + time = 2 * 60 * 60, + }, + } + + simple_rrule, simple_rrule_ok := tz.parse_posix_tz("UTC+5") + testing.expectf(t, simple_rrule_ok, "Failed to parse posix tz") + defer tz.rrule_destroy(simple_rrule) + testing.expectf(t, rrule_eq(simple_rrule, correct_simple_rrule), "POSIX TZ parsed incorrectly") + + correct_est_rrule := dt.TZ_RRule{ + has_dst = true, + + std_name = "EST", + std_offset = -(5 * 60 * 60), + std_date = dt.TZ_Transition_Date{ + type = .Month_Week_Day, + month = 3, + week = 2, + day = 0, + time = 2 * 60 * 60, + }, + + dst_name = "EDT", + dst_offset = -(4 * 60 * 60), + dst_date = dt.TZ_Transition_Date{ + type = .Month_Week_Day, + month = 11, + week = 1, + day = 0, + time = 2 * 60 * 60, + }, + } + + est_rrule, est_rrule_ok := tz.parse_posix_tz("EST+5EDT,M3.2.0/2,M11.1.0/2") + testing.expectf(t, est_rrule_ok, "Failed to parse posix tz") + defer tz.rrule_destroy(est_rrule) + testing.expectf(t, rrule_eq(est_rrule, correct_est_rrule), "POSIX TZ parsed incorrectly") + + correct_ist_rrule := dt.TZ_RRule{ + has_dst = true, + + std_name = "IST", + std_offset = (2 * 60 * 60), + std_date = dt.TZ_Transition_Date{ + type = .Month_Week_Day, + month = 3, + week = 4, + day = 4, + time = 26 * 60 * 60, + }, + + dst_name = "IDT", + dst_offset = (3 * 60 * 60), + dst_date = dt.TZ_Transition_Date{ + type = .Month_Week_Day, + month = 10, + week = 5, + day = 0, + time = 2 * 60 * 60, + }, + } + + ist_rrule, ist_rrule_ok := tz.parse_posix_tz("IST-2IDT,M3.4.4/26,M10.5.0") + testing.expectf(t, ist_rrule_ok, "Failed to parse posix tz") + defer tz.rrule_destroy(ist_rrule) + testing.expectf(t, rrule_eq(ist_rrule, correct_ist_rrule), "POSIX TZ parsed incorrectly") + + correct_warst_rrule := dt.TZ_RRule{ + has_dst = true, + + std_name = "WART", + std_offset = -(4 * 60 * 60), + std_date = dt.TZ_Transition_Date{ + type = .No_Leap, + day = 1, + time = 0 * 60 * 60, + }, + + dst_name = "WARST", + dst_offset = -(3 * 60 * 60), + dst_date = dt.TZ_Transition_Date{ + type = .No_Leap, + day = 365, + time = 25 * 60 * 60, + }, + } + + warst_rrule, warst_rrule_ok := tz.parse_posix_tz("WART4WARST,J1/0,J365/25") + testing.expectf(t, warst_rrule_ok, "Failed to parse posix tz") + defer tz.rrule_destroy(warst_rrule) + testing.expectf(t, rrule_eq(warst_rrule, correct_warst_rrule), "POSIX TZ parsed incorrectly") + + correct_wgt_rrule := dt.TZ_RRule{ + has_dst = true, + + std_name = "WGT", + std_offset = -(3 * 60 * 60), + std_date = dt.TZ_Transition_Date{ + type = .Month_Week_Day, + month = 3, + week = 5, + day = 0, + time = -2 * 60 * 60, + }, + + dst_name = "WGST", + dst_offset = -(2 * 60 * 60), + dst_date = dt.TZ_Transition_Date{ + type = .Month_Week_Day, + month = 10, + week = 5, + day = 0, + time = -1 * 60 * 60, + }, + } + + wgt_rrule, wgt_rrule_ok := tz.parse_posix_tz("WGT3WGST,M3.5.0/-2,M10.5.0/-1") + testing.expectf(t, wgt_rrule_ok, "Failed to parse posix tz") + defer tz.rrule_destroy(wgt_rrule) + testing.expectf(t, rrule_eq(wgt_rrule, correct_wgt_rrule), "POSIX TZ parsed incorrectly") +} diff --git a/vendor/miniaudio/common_unix.odin b/vendor/miniaudio/common_unix.odin index 8afcc0b5a..1c0158404 100644 --- a/vendor/miniaudio/common_unix.odin +++ b/vendor/miniaudio/common_unix.odin @@ -1,18 +1,18 @@ #+build !windows package miniaudio -import "core:sys/unix" +import "core:sys/posix" import "core:c" -thread :: unix.pthread_t -mutex :: unix.pthread_mutex_t +thread :: posix.pthread_t +mutex :: posix.pthread_mutex_t event :: struct { value: u32, - lock: unix.pthread_mutex_t, - cond: unix.pthread_cond_t, + lock: posix.pthread_mutex_t, + cond: posix.pthread_cond_t, } semaphore :: struct { value: c.int, - lock: unix.pthread_mutex_t, - cond: unix.pthread_cond_t, + lock: posix.pthread_mutex_t, + cond: posix.pthread_cond_t, } diff --git a/vendor/raylib/raymath.odin b/vendor/raylib/raymath.odin index eef5c2fcd..c66498e41 100644 --- a/vendor/raylib/raymath.odin +++ b/vendor/raylib/raymath.odin @@ -153,7 +153,7 @@ Vector2Normalize :: proc "c" (v: Vector2) -> Vector2 { // Transforms a Vector2 by a given Matrix @(require_results) Vector2Transform :: proc "c" (v: Vector2, m: Matrix) -> Vector2 { - v4 := Vector4{v.x, v.y, 0, 0} + v4 := Vector4{v.x, v.y, 0, 1} return (m * v4).xy } // Calculate linear interpolation between two vectors @@ -399,7 +399,7 @@ Vector3RotateByAxisAngle :: proc "c" (v: Vector3, axis: Vector3, angle: f32) -> // Transforms a Vector3 by a given Matrix @(require_results) Vector3Transform :: proc "c" (v: Vector3, m: Matrix) -> Vector3 { - v4 := Vector4{v.x, v.y, v.z, 0} + v4 := Vector4{v.x, v.y, v.z, 1} return (m * v4).xyz } // Calculate linear interpolation between two vectors diff --git a/vendor/wgpu/wgpu.js b/vendor/wgpu/wgpu.js index c100808e3..115a90062 100644 --- a/vendor/wgpu/wgpu.js +++ b/vendor/wgpu/wgpu.js @@ -2450,6 +2450,16 @@ class WebGPUInterface { renderPassEncoder.setBindGroup(groupIndex, group, dynamicOffsets); }, + /** + * @param {number} renderPassEncoderIdx + * @param {number} colorPtr + */ + wgpuRenderPassEncoderSetBlendConstant: (renderPassEncoderIdx, colorPtr) => { + const renderPassEncoder = this.renderPassEncoders.get(renderPassEncoderIdx); + this.assert(colorPtr != 0); + renderPassEncoder.setBlendConstant(this.Color(colorPtr)); + }, + /** * @param {number} renderPassEncoderIdx * @param {number} bufferIdx diff --git a/vendor/wgpu/wgpu.odin b/vendor/wgpu/wgpu.odin index ae4649aed..9854475a9 100644 --- a/vendor/wgpu/wgpu.odin +++ b/vendor/wgpu/wgpu.odin @@ -1299,7 +1299,8 @@ RenderPipelineDescriptor :: struct { @(link_prefix="wgpu", default_calling_convention="c") foreign libwgpu { - CreateInstance :: proc(/* NULLABLE */ descriptor: /* const */ ^InstanceDescriptor = nil) -> Instance --- + @(link_name="wgpuCreateInstance") + RawCreateInstance :: proc(/* NULLABLE */ descriptor: /* const */ ^InstanceDescriptor = nil) -> Instance --- GetProcAddress :: proc(device: Device, procName: cstring) -> Proc --- // Methods of Adapter @@ -1546,6 +1547,16 @@ foreign libwgpu { TextureViewRelease :: proc(textureView: TextureView) --- } +// Wrappers of Instance + +CreateInstance :: proc "c" (/* NULLABLE */ descriptor: /* const */ ^InstanceDescriptor = nil) -> Instance { + when ODIN_OS != .JS { + wgpu_native_version_check() + } + + return RawCreateInstance(descriptor) +} + // Wrappers of Adapter AdapterEnumerateFeatures :: proc(adapter: Adapter, allocator := context.allocator) -> []FeatureName { @@ -1555,49 +1566,49 @@ AdapterEnumerateFeatures :: proc(adapter: Adapter, allocator := context.allocato return features } -AdapterGetLimits :: proc(adapter: Adapter) -> (limits: SupportedLimits, ok: bool) { +AdapterGetLimits :: proc "c" (adapter: Adapter) -> (limits: SupportedLimits, ok: bool) { ok = bool(RawAdapterGetLimits(adapter, &limits)) return } -AdapterGetInfo :: proc(adapter: Adapter) -> (info: AdapterInfo) { +AdapterGetInfo :: proc "c" (adapter: Adapter) -> (info: AdapterInfo) { RawAdapterGetInfo(adapter, &info) return } // Wrappers of Buffer -BufferGetConstMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> []byte { +BufferGetConstMappedRange :: proc "c" (buffer: Buffer, offset: uint, size: uint) -> []byte { return ([^]byte)(RawBufferGetConstMappedRange(buffer, offset, size))[:size] } -BufferGetConstMappedRangeTyped :: proc(buffer: Buffer, offset: uint, $T: typeid) -> ^T +BufferGetConstMappedRangeTyped :: proc "c" (buffer: Buffer, offset: uint, $T: typeid) -> ^T where !intrinsics.type_is_sliceable(T) { return (^T)(RawBufferGetConstMappedRange(buffer, 0, size_of(T))) } -BufferGetConstMappedRangeSlice :: proc(buffer: Buffer, offset: uint, length: uint, $T: typeid) -> []T { +BufferGetConstMappedRangeSlice :: proc "c" (buffer: Buffer, offset: uint, length: uint, $T: typeid) -> []T { return ([^]T)(RawBufferGetConstMappedRange(buffer, offset, size_of(T)*length))[:length] } -BufferGetMappedRange :: proc(buffer: Buffer, offset: uint, size: uint) -> []byte { +BufferGetMappedRange :: proc "c" (buffer: Buffer, offset: uint, size: uint) -> []byte { return ([^]byte)(RawBufferGetMappedRange(buffer, offset, size))[:size] } -BufferGetMappedRangeTyped :: proc(buffer: Buffer, offset: uint, $T: typeid) -> ^T +BufferGetMappedRangeTyped :: proc "c" (buffer: Buffer, offset: uint, $T: typeid) -> ^T where !intrinsics.type_is_sliceable(T) { return (^T)(RawBufferGetMappedRange(buffer, offset, size_of(T))) } -BufferGetMappedRangeSlice :: proc(buffer: Buffer, offset: uint, $T: typeid, length: uint) -> []T { +BufferGetMappedRangeSlice :: proc "c" (buffer: Buffer, offset: uint, $T: typeid, length: uint) -> []T { return ([^]T)(RawBufferGetMappedRange(buffer, offset, size_of(T)*length))[:length] } // Wrappers of ComputePassEncoder -ComputePassEncoderSetBindGroup :: proc(computePassEncoder: ComputePassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { +ComputePassEncoderSetBindGroup :: proc "c" (computePassEncoder: ComputePassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { RawComputePassEncoderSetBindGroup(computePassEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets)) } @@ -1610,7 +1621,7 @@ DeviceEnumerateFeatures :: proc(device: Device, allocator := context.allocator) return features } -DeviceGetLimits :: proc(device: Device) -> (limits: SupportedLimits, ok: bool) { +DeviceGetLimits :: proc "c" (device: Device) -> (limits: SupportedLimits, ok: bool) { ok = bool(RawDeviceGetLimits(device, &limits)) return } @@ -1620,7 +1631,7 @@ BufferWithDataDescriptor :: struct { usage: BufferUsageFlags, } -DeviceCreateBufferWithDataSlice :: proc(device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: []$T) -> (buf: Buffer) { +DeviceCreateBufferWithDataSlice :: proc "c" (device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: []$T) -> (buf: Buffer) { size := u64(size_of(T) * len(data)) buf = DeviceCreateBuffer(device, &{ label = descriptor.label, @@ -1636,7 +1647,7 @@ DeviceCreateBufferWithDataSlice :: proc(device: Device, descriptor: /* const */ return } -DeviceCreateBufferWithDataTyped :: proc(device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: $T) -> (buf: Buffer) +DeviceCreateBufferWithDataTyped :: proc "c" (device: Device, descriptor: /* const */ ^BufferWithDataDescriptor, data: $T) -> (buf: Buffer) where !intrinsics.type_is_sliceable(T) { buf = DeviceCreateBuffer(device, &{ @@ -1660,34 +1671,34 @@ DeviceCreateBufferWithData :: proc { // Wrappers of Queue -QueueSubmit :: proc(queue: Queue, commands: []CommandBuffer) { +QueueSubmit :: proc "c" (queue: Queue, commands: []CommandBuffer) { RawQueueSubmit(queue, len(commands), raw_data(commands)) } // Wrappers of RenderBundleEncoder -RenderBundleEncoderSetBindGroup :: proc(renderBundleEncoder: RenderBundleEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { +RenderBundleEncoderSetBindGroup :: proc "c" (renderBundleEncoder: RenderBundleEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { RawRenderBundleEncoderSetBindGroup(renderBundleEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets)) } // Wrappers of RenderPassEncoder -RenderPassEncoderExecuteBundles :: proc(renderPassEncoder: RenderPassEncoder, bundles: []RenderBundle) { +RenderPassEncoderExecuteBundles :: proc "c" (renderPassEncoder: RenderPassEncoder, bundles: []RenderBundle) { RawRenderPassEncoderExecuteBundles(renderPassEncoder, len(bundles), raw_data(bundles)) } -RenderPassEncoderSetBindGroup :: proc(renderPassEncoder: RenderPassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { +RenderPassEncoderSetBindGroup :: proc "c" (renderPassEncoder: RenderPassEncoder, groupIndex: u32, /* NULLABLE */ group: BindGroup, dynamicOffsets: []u32 = nil) { RawRenderPassEncoderSetBindGroup(renderPassEncoder, groupIndex, group, len(dynamicOffsets), raw_data(dynamicOffsets)) } // Wrappers of Surface -SurfaceGetCapabilities :: proc(surface: Surface, adapter: Adapter) -> (capabilities: SurfaceCapabilities) { +SurfaceGetCapabilities :: proc "c" (surface: Surface, adapter: Adapter) -> (capabilities: SurfaceCapabilities) { RawSurfaceGetCapabilities(surface, adapter, &capabilities) return } -SurfaceGetCurrentTexture :: proc(surface: Surface) -> (surface_texture: SurfaceTexture) { +SurfaceGetCurrentTexture :: proc "c" (surface: Surface) -> (surface_texture: SurfaceTexture) { RawSurfaceGetCurrentTexture(surface, &surface_texture) return } @@ -1698,8 +1709,8 @@ BINDINGS_VERSION :: [4]u8{22, 1, 0, 1} BINDINGS_VERSION_STRING :: "22.1.0.1" when ODIN_OS != .JS { - @(private="file", init) - wgpu_native_version_check :: proc() { + @(private="file") + wgpu_native_version_check :: proc "c" () { v := (transmute([4]u8)GetVersion()).wzyx if v != BINDINGS_VERSION { @@ -1708,7 +1719,7 @@ when ODIN_OS != .JS { n += copy(buf[n:], "bindings are for version ") n += copy(buf[n:], BINDINGS_VERSION_STRING) n += copy(buf[n:], ", but a different version is linked") - panic(string(buf[:n])) + panic_contextless(string(buf[:n])) } } @@ -1745,7 +1756,7 @@ when ODIN_OS != .JS { RenderPassEncoderEndPipelineStatisticsQuery :: proc(renderPassEncoder: RenderPassEncoder) --- } - GenerateReport :: proc(instance: Instance) -> (report: GlobalReport) { + GenerateReport :: proc "c" (instance: Instance) -> (report: GlobalReport) { RawGenerateReport(instance, &report) return } @@ -1757,7 +1768,7 @@ when ODIN_OS != .JS { return } - QueueSubmitForIndex :: proc(queue: Queue, commands: []CommandBuffer) -> SubmissionIndex { + QueueSubmitForIndex :: proc "c" (queue: Queue, commands: []CommandBuffer) -> SubmissionIndex { return RawQueueSubmitForIndex(queue, len(commands), raw_data(commands)) } }