diff options
| author | Colin Davidson <colrdavidson@gmail.com> | 2024-11-20 15:51:08 -0800 |
|---|---|---|
| committer | Colin Davidson <colrdavidson@gmail.com> | 2024-11-20 15:51:08 -0800 |
| commit | d60fb5a44e4d2e371562fd38947f8125b06bceb9 (patch) | |
| tree | 4e924ee102c2af7b30d29017ab716ed00c51ab26 /core | |
| parent | f3ab14b8ccb45d0fef8a96937635bdf0943ce7d6 (diff) | |
| parent | 3229f4668dfaa5f43a374bc549f42661b002699d (diff) | |
update to master
Diffstat (limited to 'core')
167 files changed, 6143 insertions, 2622 deletions
diff --git a/core/c/frontend/tokenizer/tokenizer.odin b/core/c/frontend/tokenizer/tokenizer.odin index 2415e06a0..558077717 100644 --- a/core/c/frontend/tokenizer/tokenizer.odin +++ b/core/c/frontend/tokenizer/tokenizer.odin @@ -291,7 +291,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool { n -= 1 } - if x > max || 0xd800 <= x && x <= 0xe000 { + if x > max || 0xd800 <= x && x <= 0xdfff { error_offset(t, offset, "escape sequence is an invalid Unicode code point") return false } 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 | `<inttypes.h>` | Fully projected | | `<iso646.h>` | Not applicable, use Odin's operators | | `<limits.h>` | Not projected | -| `<locale.h>` | Not projected | +| `<locale.h>` | Fully projected | | `<math.h>` | Mostly projected, see [limitations](#Limitations) | | `<setjmp.h>` | Fully projected | | `<signal.h>` | Fully projected | @@ -70,4 +70,4 @@ with the following copyright. ``` Copyright 2021 Dale Weiler <weilercdale@gmail.com>. -```
\ 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/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin index 8dc243b78..43a0e51df 100644 --- a/core/c/libc/stdatomic.odin +++ b/core/c/libc/stdatomic.odin @@ -235,7 +235,7 @@ atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desire return ok } -atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool { +atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool { assert(failure != .release) assert(failure != .acq_rel) diff --git a/core/compress/zlib/doc.odin b/core/compress/zlib/doc.odin index 0a5b4eb40..6ae537a87 100644 --- a/core/compress/zlib/doc.odin +++ b/core/compress/zlib/doc.odin @@ -13,6 +13,7 @@ Example: package main import "core:bytes" + import "core:compress/zlib" import "core:fmt" main :: proc() { @@ -36,7 +37,7 @@ Example: buf: bytes.Buffer // We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one. - err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE) + err := zlib.inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE) defer bytes.buffer_destroy(&buf) if err != nil { diff --git a/core/container/bit_array/bit_array.odin b/core/container/bit_array/bit_array.odin index 9a76dc78f..85b941d12 100644 --- a/core/container/bit_array/bit_array.odin +++ b/core/container/bit_array/bit_array.odin @@ -259,6 +259,7 @@ Inputs: unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check { b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK) } + /* A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative). @@ -276,22 +277,50 @@ Returns: - ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64). */ create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok { - context.allocator = allocator size_in_bits := max_index - min_index if size_in_bits < 0 { return {}, false } + res = new(Bit_Array, allocator) + ok = init(res, max_index, min_index, allocator) + res.free_pointer = true + + if !ok { free(res, allocator) } + + return +} + +/* +A helper function to initialize a Bit Array with optional bias, in case your smallest index is non-zero (including negative). + +The range of bits created by this procedure is `min_index..<max_index`, and the +array will be able to expand beyond `max_index` if needed. + +*Allocates (`make(ba.bits)`)* + +Inputs: +- max_index: maximum starting index +- min_index: minimum starting index (used as a bias) +- allocator: (default is context.allocator) +*/ +init :: proc(res: ^Bit_Array, max_index: int, min_index: int = 0, allocator := context.allocator) -> (ok: bool) { + size_in_bits := max_index - min_index + + if size_in_bits < 0 { return false } + legs := size_in_bits >> INDEX_SHIFT - if size_in_bits & INDEX_MASK > 0 {legs+=1} - bits, err := make([dynamic]u64, legs) - ok = err == mem.Allocator_Error.None - res = new(Bit_Array) + if size_in_bits & INDEX_MASK > 0 { legs += 1 } + + bits, err := make([dynamic]u64, legs, allocator) + ok = err == nil + res.bits = bits res.bias = min_index res.length = max_index - min_index - res.free_pointer = true + res.free_pointer = false return } + /* Sets all values in the Bit_Array to zero. diff --git a/core/crypto/_chacha20/simd128/chacha20_simd128.odin b/core/crypto/_chacha20/simd128/chacha20_simd128.odin index 2f91ac52a..fe0d0d518 100644 --- a/core/crypto/_chacha20/simd128/chacha20_simd128.odin +++ b/core/crypto/_chacha20/simd128/chacha20_simd128.odin @@ -51,7 +51,7 @@ _ROT_16: simd.u32x4 : {16, 16, 16, 16} when ODIN_ENDIAN == .Big { @(private = "file") - _increment_counter :: #force_inline proc "contextless" (ctx: ^Context) -> simd.u32x4 { + _increment_counter :: #force_inline proc "contextless" (ctx: ^_chacha20.Context) -> simd.u32x4 { // In the Big Endian case, the low and high portions in the vector // are flipped, so the 64-bit addition can't be done with a simple // vector add. diff --git a/core/crypto/_sha3/sp800_185.odin b/core/crypto/_sha3/sp800_185.odin index f32398d5c..a96f78cc1 100644 --- a/core/crypto/_sha3/sp800_185.odin +++ b/core/crypto/_sha3/sp800_185.odin @@ -81,16 +81,18 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) { // 2. while len(z) mod 8 ≠0: // z = z || 0 - // 3. while (len(z)/8) mod w ≠0: + // 3. while (len(z)/8) mod w != 0: // z = z || 00000000 z_len := u128(z_hi) << 64 | u128(z_lo) z_rem := int(z_len % u128(w)) - pad := _PAD[:w - z_rem] + if z_rem != 0 { + pad := _PAD[:w - z_rem] - // We just add the padding to the state, instead of returning z. - // - // 4. return z. - update(ctx, pad) + // We just add the padding to the state, instead of returning z. + // + // 4. return z. + update(ctx, pad) + } } encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) { 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..<length { + key := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return + destroy(key, context.temp_allocator) + val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return + destroy(val, context.temp_allocator) + } + } + return case reflect.Type_Info_Map: diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin index 5c8503229..a679946f8 100644 --- a/core/encoding/hxa/read.odin +++ b/core/encoding/hxa/read.odin @@ -117,7 +117,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato layer.name = read_name(r) or_return layer.components = read_value(r, u8) or_return type := read_value(r, Layer_Data_Type) or_return - if type > max(type) { + if type > max(Layer_Data_Type) { if r.print_error { fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n", r.filename, u8(type), u8(max(Layer_Data_Type))) diff --git a/core/encoding/ini/ini.odin b/core/encoding/ini/ini.odin index 2bb7996a3..c32b1deb5 100644 --- a/core/encoding/ini/ini.odin +++ b/core/encoding/ini/ini.odin @@ -154,6 +154,7 @@ write_section :: proc(w: io.Writer, name: string, n_written: ^int = nil) -> (n: io.write_byte (w, '[', &n) or_return io.write_string(w, name, &n) or_return io.write_byte (w, ']', &n) or_return + io.write_byte (w, '\n', &n) or_return return } diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 009bf7ade..f0f0927a1 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -192,12 +192,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Simd_Vector: return .Unsupported_Type - - case runtime.Type_Info_Relative_Pointer: - return .Unsupported_Type - - case runtime.Type_Info_Relative_Multi_Pointer: - return .Unsupported_Type case runtime.Type_Info_Matrix: return .Unsupported_Type diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin index 738e20c68..ea33badae 100644 --- a/core/encoding/json/unmarshal.odin +++ b/core/encoding/json/unmarshal.odin @@ -172,20 +172,33 @@ assign_float :: proc(val: any, f: $T) -> bool { @(private) -unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool { +unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> (ok: bool, err: Error) { val := val switch &dst in val { case string: dst = str - return true + return true, nil case cstring: if str == "" { - dst = strings.clone_to_cstring("", p.allocator) + a_err: runtime.Allocator_Error + dst, a_err = strings.clone_to_cstring("", p.allocator) + #partial switch a_err { + case nil: + // okay + case .Out_Of_Memory: + err = .Out_Of_Memory + case: + err = .Invalid_Allocator + } + if err != nil { + return + } } else { // NOTE: This is valid because 'clone_string' appends a NUL terminator dst = cstring(raw_data(str)) } - return true + ok = true + return } #partial switch variant in ti.variant { @@ -193,31 +206,37 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.T for name, i in variant.names { if name == str { assign_int(val, variant.values[i]) - return true + return true, nil } } // TODO(bill): should this be an error or not? - return true + return true, nil case reflect.Type_Info_Integer: - i := strconv.parse_i128(str) or_return + i, pok := strconv.parse_i128(str) + if !pok { + return false, nil + } if assign_int(val, i) { - return true + return true, nil } if assign_float(val, i) { - return true + return true, nil } case reflect.Type_Info_Float: - f := strconv.parse_f64(str) or_return + f, pok := strconv.parse_f64(str) + if !pok { + return false, nil + } if assign_int(val, f) { - return true + return true, nil } if assign_float(val, f) { - return true + return true, nil } } - return false + return false, nil } @@ -304,7 +323,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { case .Ident: advance_token(p) if p.spec == .MJSON { - if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) { + if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) or_return { return nil } } @@ -312,13 +331,18 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) { case .String: advance_token(p) - str := unquote_string(token, p.spec, p.allocator) or_return - if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) { - return nil + str := unquote_string(token, p.spec, p.allocator) or_return + dest := any{v.data, ti.id} + if !(unmarshal_string_token(p, dest, str, ti) or_return) { + delete(str, p.allocator) + return UNSUPPORTED_TYPE } - delete(str, p.allocator) - return UNSUPPORTED_TYPE + switch destv in dest { + case string, cstring: + case: delete(str, p.allocator) + } + return nil case .Open_Brace: return unmarshal_object(p, v, .Close_Brace) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 0d11ad8a9..49e9f2e6d 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1531,7 +1531,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER) case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER) case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER) - case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER) + case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_LOWER) case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER) case: @@ -3002,18 +3002,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Bit_Set: fmt_bit_set(fi, v, verb = verb) - case runtime.Type_Info_Relative_Pointer: - ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id) - absolute_ptr := any{ptr, info.pointer.id} - - fmt_value(fi, absolute_ptr, verb) - - case runtime.Type_Info_Relative_Multi_Pointer: - ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id) - absolute_ptr := any{ptr, info.pointer.id} - - fmt_value(fi, absolute_ptr, verb) - case runtime.Type_Info_Matrix: fmt_matrix(fi, v, verb, info) diff --git a/core/image/general.odin b/core/image/general.odin index 17a8f35ea..c4a884071 100644 --- a/core/image/general.odin +++ b/core/image/general.odin @@ -23,7 +23,15 @@ register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_ load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) { loader := _internal_loaders[which(data)] if loader == nil { - return nil, .Unsupported_Format + + // Check if there is at least one loader, otherwise panic to let the user know about misuse. + for a_loader in _internal_loaders { + if a_loader != nil { + return nil, .Unsupported_Format + } + } + + panic("image.load called when no image loaders are registered. Register a loader by first importing a subpackage (eg: `import \"core:image/png\"`), or with image.register") } return loader(data, options, allocator) } @@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type { return .HDR case s[:4] == "\x38\x42\x50\x53": return .PSD - case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT": + case s[:4] == "\x53\x80\xF6\x34" && s[88:92] == "PICT": return .PIC case s[:4] == "\x69\x63\x6e\x73": return .ICNS 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/linalg/general.odin b/core/math/linalg/general.odin index 7587ad63f..f82d75bff 100644 --- a/core/math/linalg/general.odin +++ b/core/math/linalg/general.odin @@ -53,15 +53,15 @@ vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E) } @(require_results) quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) { - return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z + return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z } @(require_results) quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) { - return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z + return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z } @(require_results) quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) { - return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z + return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z } dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot} diff --git a/core/math/math_sincos.odin b/core/math/math_sincos.odin index b616f410d..9b4d3de40 100644 --- a/core/math/math_sincos.odin +++ b/core/math/math_sincos.odin @@ -189,12 +189,12 @@ sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check { // sin coefficients @(private="file") _sin := [?]f64{ - 0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10 - 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8 - 0h3ec71de3567d48a1, // 2.75573136213857245213e-6 - 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4 - 0h3f8111111110f7d0, // 8.33333333332211858878e-3 - 0hbfc5555555555548, // -1.66666666666666307295e-1 + 0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10 + 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8 + 0h3ec71de3567d48a1, // 2.75573136213857245213e-6 + 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4 + 0h3f8111111110f7d0, // 8.33333333332211858878e-3 + 0hbfc5555555555548, // -1.66666666666666307295e-1 } // cos coefficients 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)) + } +} + +/* +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})) } -}
\ No newline at end of file + +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/allocators.odin b/core/mem/allocators.odin index d729b902c..13d509f1e 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -14,16 +14,16 @@ but an attempt to allocate memory is not an error. nil_allocator :: proc() -> Allocator { return Allocator{ procedure = nil_allocator_proc, - data = nil, + data = nil, } } nil_allocator_proc :: proc( - allocator_data: rawptr, - mode: Allocator_Mode, + allocator_data: rawptr, + mode: Allocator_Mode, size, alignment: int, - old_memory: rawptr, - old_size: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { return nil, nil @@ -41,7 +41,7 @@ not be allocated, and an attempt to allocate memory is an error. panic_allocator :: proc() -> Allocator { return Allocator{ procedure = panic_allocator_proc, - data = nil, + data = nil, } } @@ -157,10 +157,10 @@ This procedure returns a pointer to the newly allocated memory region. */ @(require_results) arena_alloc :: proc( - a: ^Arena, + a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := arena_alloc_bytes(a, size, alignment, loc) return raw_data(bytes), err @@ -175,10 +175,10 @@ This procedure returns a slice of the newly allocated memory region. */ @(require_results) arena_alloc_bytes :: proc( - a: ^Arena, + a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc) if bytes != nil { @@ -197,10 +197,10 @@ memory region. */ @(require_results) arena_alloc_non_zeroed :: proc( - a: ^Arena, + a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc) return raw_data(bytes), err @@ -216,10 +216,10 @@ memory region. */ @(require_results) arena_alloc_bytes_non_zeroed :: proc( - a: ^Arena, + a: ^Arena, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> ([]byte, Allocator_Error) { if a.data == nil { panic("Arena is not initialized", loc) @@ -244,11 +244,11 @@ arena_free_all :: proc(a: ^Arena) { arena_allocator_proc :: proc( allocator_data: rawptr, - mode: Allocator_Mode, - size: int, - alignment: int, - old_memory: rawptr, - old_size: int, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { arena := cast(^Arena)allocator_data @@ -398,10 +398,10 @@ returns a pointer to the allocated memory region. */ @(require_results) scratch_alloc :: proc( - s: ^Scratch, + s: ^Scratch, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := scratch_alloc_bytes(s, size, alignment, loc) return raw_data(bytes), err @@ -416,10 +416,10 @@ returns a slice of the allocated memory region. */ @(require_results) scratch_alloc_bytes :: proc( - s: ^Scratch, + s: ^Scratch, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { @@ -437,10 +437,10 @@ This procedure returns a pointer to the allocated memory region. */ @(require_results) scratch_alloc_non_zeroed :: proc( - s: ^Scratch, + s: ^Scratch, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc) return raw_data(bytes), err @@ -455,10 +455,10 @@ This procedure returns a slice of the allocated memory region. */ @(require_results) scratch_alloc_bytes_non_zeroed :: proc( - s: ^Scratch, + s: ^Scratch, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { DEFAULT_BACKING_SIZE :: 4 * Megabyte @@ -477,7 +477,7 @@ scratch_alloc_bytes_non_zeroed :: proc( offset = 0 } start := uintptr(raw_data(s.data)) - ptr := align_forward_uintptr(offset+start, uintptr(alignment)) + ptr := align_forward_uintptr(offset+start, uintptr(alignment)) s.prev_allocation = rawptr(ptr) s.curr_offset = int(offset) + size return byte_slice(rawptr(ptr), size), nil @@ -574,12 +574,12 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) scratch_resize :: proc( - s: ^Scratch, + s: ^Scratch, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> (rawptr, Allocator_Error) { bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err @@ -603,11 +603,11 @@ This procedure returns the slice of the resized memory region. */ @(require_results) scratch_resize_bytes :: proc( - s: ^Scratch, + s: ^Scratch, old_data: []byte, - size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> ([]byte, Allocator_Error) { bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc) if bytes != nil && size > len(old_data) { @@ -634,12 +634,12 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) scratch_resize_non_zeroed :: proc( - s: ^Scratch, + s: ^Scratch, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> (rawptr, Allocator_Error) { bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err @@ -663,11 +663,11 @@ This procedure returns the slice of the resized memory region. */ @(require_results) scratch_resize_bytes_non_zeroed :: proc( - s: ^Scratch, + s: ^Scratch, old_data: []byte, - size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> ([]byte, Allocator_Error) { old_memory := raw_data(old_data) old_size := len(old_data) @@ -678,8 +678,8 @@ scratch_resize_bytes_non_zeroed :: proc( } scratch_init(s, DEFAULT_BACKING_SIZE) } - begin := uintptr(raw_data(s.data)) - end := begin + uintptr(len(s.data)) + begin := uintptr(raw_data(s.data)) + end := begin + uintptr(len(s.data)) old_ptr := uintptr(old_memory) if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end { s.curr_offset = int(old_ptr-begin)+size @@ -695,11 +695,11 @@ scratch_resize_bytes_non_zeroed :: proc( } scratch_allocator_proc :: proc( - allocator_data: rawptr, - mode: Allocator_Mode, + allocator_data: rawptr, + mode: Allocator_Mode, size, alignment: int, - old_memory: rawptr, - old_size: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { s := (^Scratch)(allocator_data) @@ -735,10 +735,10 @@ scratch_allocator_proc :: proc( Stack allocator data. */ Stack :: struct { - data: []byte, + data: []byte, prev_offset: int, curr_offset: int, - peak_used: int, + peak_used: int, } /* @@ -769,7 +769,7 @@ previous allocation header. stack_allocator :: proc(stack: ^Stack) -> Allocator { return Allocator{ procedure = stack_allocator_proc, - data = stack, + data = stack, } } @@ -780,18 +780,18 @@ This procedure initializes the stack allocator with a backing buffer specified by `data` parameter. */ stack_init :: proc(s: ^Stack, data: []byte) { - s.data = data + s.data = data s.prev_offset = 0 s.curr_offset = 0 - s.peak_used = 0 + s.peak_used = 0 } @(deprecated="prefer 'mem.stack_init'") init_stack :: proc(s: ^Stack, data: []byte) { - s.data = data + s.data = data s.prev_offset = 0 s.curr_offset = 0 - s.peak_used = 0 + s.peak_used = 0 } /* @@ -803,10 +803,10 @@ procedure returns the pointer to the allocated memory. */ @(require_results) stack_alloc :: proc( - s: ^Stack, + s: ^Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> (rawptr, Allocator_Error) { bytes, err := stack_alloc_bytes(s, size, alignment, loc) return raw_data(bytes), err @@ -821,10 +821,10 @@ procedure returns the slice of the allocated memory. */ @(require_results) stack_alloc_bytes :: proc( - s: ^Stack, + s: ^Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> ([]byte, Allocator_Error) { bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { @@ -842,10 +842,10 @@ zero-initialized. This procedure returns the pointer to the allocated memory. */ @(require_results) stack_alloc_non_zeroed :: proc( - s: ^Stack, + s: ^Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> (rawptr, Allocator_Error) { bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) return raw_data(bytes), err @@ -860,10 +860,10 @@ zero-initialized. This procedure returns the slice of the allocated memory. */ @(require_results) stack_alloc_bytes_non_zeroed :: proc( - s: ^Stack, + s: ^Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location + loc := #caller_location ) -> ([]byte, Allocator_Error) { if s.data == nil { panic("Stack allocation on an uninitialized stack allocator", loc) @@ -896,7 +896,7 @@ If the freeing does is an out of order freeing, the `.Invalid_Pointer` error is returned. */ stack_free :: proc( - s: ^Stack, + s: ^Stack, old_memory: rawptr, loc := #caller_location, ) -> (Allocator_Error) { @@ -953,12 +953,12 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) stack_resize :: proc( - s: ^Stack, + s: ^Stack, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment) return raw_data(bytes), err @@ -982,11 +982,11 @@ This procedure returns the slice of the resized memory region. */ @(require_results) stack_resize_bytes :: proc( - s: ^Stack, + s: ^Stack, old_data: []byte, - size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { @@ -1017,12 +1017,12 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) stack_resize_non_zeroed :: proc( - s: ^Stack, + s: ^Stack, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment) return raw_data(bytes), err @@ -1046,11 +1046,11 @@ This procedure returns the slice of the resized memory region. */ @(require_results) stack_resize_bytes_non_zeroed :: proc( - s: ^Stack, + s: ^Stack, old_data: []byte, - size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { old_memory := raw_data(old_data) old_size := len(old_data) @@ -1063,8 +1063,8 @@ stack_resize_bytes_non_zeroed :: proc( if size == 0 { return nil, nil } - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) curr_addr := uintptr(old_memory) if !(start <= curr_addr && curr_addr < end) { panic("Out of bounds memory address passed to stack allocator (resize)") @@ -1097,11 +1097,11 @@ stack_resize_bytes_non_zeroed :: proc( stack_allocator_proc :: proc( allocator_data: rawptr, - mode: Allocator_Mode, - size: int, - alignment: int, - old_memory: rawptr, - old_size: int, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { s := cast(^Stack)allocator_data @@ -1200,10 +1200,10 @@ returns a pointer to the allocated memory region. */ @(require_results) small_stack_alloc :: proc( - s: ^Small_Stack, + s: ^Small_Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := small_stack_alloc_bytes(s, size, alignment, loc) return raw_data(bytes), err @@ -1218,10 +1218,10 @@ returns a slice of the allocated memory region. */ @(require_results) small_stack_alloc_bytes :: proc( - s: ^Small_Stack, + s: ^Small_Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) if bytes != nil { @@ -1239,10 +1239,10 @@ procedure returns a pointer to the allocated memory region. */ @(require_results) small_stack_alloc_non_zeroed :: proc( - s: ^Small_Stack, + s: ^Small_Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) return raw_data(bytes), err @@ -1257,10 +1257,10 @@ procedure returns a slice of the allocated memory region. */ @(require_results) small_stack_alloc_bytes_non_zeroed :: proc( - s: ^Small_Stack, + s: ^Small_Stack, size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { panic("Small stack is not initialized", loc) @@ -1289,7 +1289,7 @@ by `alignment`. The allocated memory is not explicitly zero-initialized. This procedure returns a slice of the allocated memory region. */ small_stack_free :: proc( - s: ^Small_Stack, + s: ^Small_Stack, old_memory: rawptr, loc := #caller_location, ) -> Allocator_Error { @@ -1341,12 +1341,12 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) small_stack_resize :: proc( - s: ^Small_Stack, + s: ^Small_Stack, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err @@ -1370,11 +1370,11 @@ This procedure returns the slice of the resized memory region. */ @(require_results) small_stack_resize_bytes :: proc( - s: ^Small_Stack, + s: ^Small_Stack, old_data: []byte, - size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc) if bytes != nil { @@ -1405,12 +1405,12 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) small_stack_resize_non_zeroed :: proc( - s: ^Small_Stack, + s: ^Small_Stack, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc) return raw_data(bytes), err @@ -1434,18 +1434,18 @@ This procedure returns the slice of the resized memory region. */ @(require_results) small_stack_resize_bytes_non_zeroed :: proc( - s: ^Small_Stack, + s: ^Small_Stack, old_data: []byte, - size: int, + size: int, alignment := DEFAULT_ALIGNMENT, - loc := #caller_location, + loc := #caller_location, ) -> ([]byte, Allocator_Error) { if s.data == nil { panic("Small stack is not initialized", loc) } old_memory := raw_data(old_data) - old_size := len(old_data) - alignment := alignment + old_size := len(old_data) + alignment := alignment alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2) if old_memory == nil { return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc) @@ -1453,8 +1453,8 @@ small_stack_resize_bytes_non_zeroed :: proc( if size == 0 { return nil, nil } - start := uintptr(raw_data(s.data)) - end := start + uintptr(len(s.data)) + start := uintptr(raw_data(s.data)) + end := start + uintptr(len(s.data)) curr_addr := uintptr(old_memory) if !(start <= curr_addr && curr_addr < end) { // panic("Out of bounds memory address passed to stack allocator (resize)"); @@ -1476,11 +1476,11 @@ small_stack_resize_bytes_non_zeroed :: proc( } small_stack_allocator_proc :: proc( - allocator_data: rawptr, - mode: Allocator_Mode, - size, alignment: int, - old_memory: rawptr, - old_size: int, + allocator_data: rawptr, + mode: Allocator_Mode, + size, alignment: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { s := cast(^Small_Stack)allocator_data @@ -1514,17 +1514,17 @@ small_stack_allocator_proc :: proc( /* Preserved for compatibility */ -Dynamic_Pool :: Dynamic_Arena -DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT +Dynamic_Pool :: Dynamic_Arena +DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT -dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc -dynamic_pool_free_all :: dynamic_arena_free_all -dynamic_pool_reset :: dynamic_arena_reset -dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes -dynamic_pool_alloc :: dynamic_arena_alloc -dynamic_pool_init :: dynamic_arena_init -dynamic_pool_allocator :: dynamic_arena_allocator -dynamic_pool_destroy :: dynamic_arena_destroy +dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc +dynamic_pool_free_all :: dynamic_arena_free_all +dynamic_pool_reset :: dynamic_arena_reset +dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes +dynamic_pool_alloc :: dynamic_arena_alloc +dynamic_pool_init :: dynamic_arena_init +dynamic_pool_allocator :: dynamic_arena_allocator +dynamic_pool_destroy :: dynamic_arena_destroy /* Default block size for dynamic arena. @@ -1540,16 +1540,16 @@ DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554 Dynamic arena allocator data. */ Dynamic_Arena :: struct { - block_size: int, - out_band_size: int, - alignment: int, - unused_blocks: [dynamic]rawptr, - used_blocks: [dynamic]rawptr, + block_size: int, + out_band_size: int, + alignment: int, + unused_blocks: [dynamic]rawptr, + used_blocks: [dynamic]rawptr, out_band_allocations: [dynamic]rawptr, - current_block: rawptr, - current_pos: rawptr, - bytes_left: int, - block_allocator: Allocator, + current_block: rawptr, + current_pos: rawptr, + bytes_left: int, + block_allocator: Allocator, } /* @@ -1565,17 +1565,17 @@ dynamic_arena_init :: proc( pool: ^Dynamic_Arena, block_allocator := context.allocator, array_allocator := context.allocator, - block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, - out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, - alignment := DEFAULT_ALIGNMENT, + block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT, + out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT, + alignment := DEFAULT_ALIGNMENT, ) { - pool.block_size = block_size - pool.out_band_size = out_band_size - pool.alignment = alignment - pool.block_allocator = block_allocator + pool.block_size = block_size + pool.out_band_size = out_band_size + pool.alignment = alignment + pool.block_allocator = block_allocator pool.out_band_allocations.allocator = array_allocator - pool.unused_blocks.allocator = array_allocator - pool.used_blocks.allocator = array_allocator + pool.unused_blocks.allocator = array_allocator + pool.used_blocks.allocator = array_allocator } /* @@ -1783,10 +1783,10 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) dynamic_arena_resize :: proc( - a: ^Dynamic_Arena, + a: ^Dynamic_Arena, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc) @@ -1811,9 +1811,9 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes :: proc( - a: ^Dynamic_Arena, + a: ^Dynamic_Arena, old_data: []byte, - size: int, + size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc) @@ -1845,10 +1845,10 @@ This procedure returns the pointer to the resized memory region. */ @(require_results) dynamic_arena_resize_non_zeroed :: proc( - a: ^Dynamic_Arena, + a: ^Dynamic_Arena, old_memory: rawptr, - old_size: int, - size: int, + old_size: int, + size: int, loc := #caller_location, ) -> (rawptr, Allocator_Error) { bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc) @@ -1873,9 +1873,9 @@ This procedure returns the slice of the resized memory region. */ @(require_results) dynamic_arena_resize_bytes_non_zeroed :: proc( - a: ^Dynamic_Arena, + a: ^Dynamic_Arena, old_data: []byte, - size: int, + size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { old_memory := raw_data(old_data) @@ -1892,11 +1892,11 @@ dynamic_arena_resize_bytes_non_zeroed :: proc( dynamic_arena_allocator_proc :: proc( allocator_data: rawptr, - mode: Allocator_Mode, - size: int, - alignment: int, - old_memory: rawptr, - old_size: int, + mode: Allocator_Mode, + size: int, + alignment: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { arena := (^Dynamic_Arena)(allocator_data) @@ -2048,7 +2048,7 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl // to pick the buddy as it "bounces around" less best_block = buddy } - if (block.size <= buddy.size) { + if block.size <= buddy.size { block = buddy_block_next(buddy) if (block < tail) { // Delay the buddy block for the next iteration @@ -2072,8 +2072,8 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl The buddy allocator data. */ Buddy_Allocator :: struct { - head: ^Buddy_Block, - tail: ^Buddy_Block, + head: ^Buddy_Block, + tail: ^Buddy_Block, alignment: uint, } @@ -2234,11 +2234,11 @@ buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) { } buddy_allocator_proc :: proc( - allocator_data: rawptr, - mode: Allocator_Mode, + allocator_data: rawptr, + mode: Allocator_Mode, size, alignment: int, - old_memory: rawptr, - old_size: int, + old_memory: rawptr, + old_size: int, loc := #caller_location, ) -> ([]byte, Allocator_Error) { b := (^Buddy_Allocator)(allocator_data) 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<<uintptr(align) - 1)) == 0 + return (p & (uintptr(align) - 1)) == 0 } /* diff --git a/core/mem/mutex_allocator.odin b/core/mem/mutex_allocator.odin index b8062bca1..7361016c3 100644 --- a/core/mem/mutex_allocator.odin +++ b/core/mem/mutex_allocator.odin @@ -1,4 +1,4 @@ -#+build !freestanding +#+build !freestanding, wasm32, wasm64p32 package mem import "core:sync" diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin index 5da38e62f..cf780de3f 100644 --- a/core/mem/tracking_allocator.odin +++ b/core/mem/tracking_allocator.odin @@ -1,4 +1,4 @@ -#+build !freestanding +#+build !freestanding, wasm32, wasm64p32 package mem import "base:runtime" diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin index 105849774..c3d6a9095 100644 --- a/core/mem/virtual/virtual_posix.odin +++ b/core/mem/virtual/virtual_posix.odin @@ -6,17 +6,13 @@ import "core:sys/posix" // Define non-posix needed flags: when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { - MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */ - - MADV_FREE :: 5 /* pages unneeded, discard contents */ + MADV_FREE :: 5 /* pages unneeded, discard contents */ } else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD { - MAP_ANONYMOUS :: 0x1000 - - MADV_FREE :: 6 + MADV_FREE :: 6 } _reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) { - flags := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS) + flags := posix.Map_Flags{ .ANONYMOUS, .PRIVATE } result := posix.mmap(nil, size, {}, flags) if result == posix.MAP_FAILED { return nil, .Out_Of_Memory diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin index ec9255c3b..27927e973 100644 --- a/core/net/socket_darwin.odin +++ b/core/net/socket_darwin.odin @@ -88,8 +88,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio sockaddr := _endpoint_to_sockaddr(endpoint) res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) if res != nil { - err = Dial_Error(os.is_platform_error(res) or_else -1) - return + close(skt) + return {}, Dial_Error(os.is_platform_error(res) or_else -1) } return @@ -120,6 +120,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_ family := family_from_endpoint(interface_endpoint) sock := create_socket(family, .TCP) or_return skt = sock.(TCP_Socket) + defer if err != nil { close(skt) } // NOTE(tetra): This is so that if we crash while the socket is open, we can // bypass the cooldown period, and allow the next run of the program to diff --git a/core/net/socket_freebsd.odin b/core/net/socket_freebsd.odin index 0f3a85cbb..3a3774007 100644 --- a/core/net/socket_freebsd.odin +++ b/core/net/socket_freebsd.odin @@ -114,8 +114,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio sockaddr := _endpoint_to_sockaddr(endpoint) errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len) if errno != nil { - err = cast(Dial_Error)errno - return + close(socket) + return {}, cast(Dial_Error)errno } return @@ -137,6 +137,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T family := family_from_endpoint(interface_endpoint) new_socket := create_socket(family, .TCP) or_return socket = new_socket.(TCP_Socket) + defer if err != nil { close(socket) } bind(socket, interface_endpoint) or_return diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin index a3853874a..b7816b0b6 100644 --- a/core/net/socket_linux.odin +++ b/core/net/socket_linux.odin @@ -147,7 +147,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio addr := _unwrap_os_addr(endpoint) errno = linux.connect(linux.Fd(os_sock), &addr) if errno != .NONE { - return cast(TCP_Socket) os_sock, Dial_Error(errno) + close(cast(TCP_Socket) os_sock) + return {}, Dial_Error(errno) } // NOTE(tetra): Not vital to succeed; error ignored no_delay: b32 = cast(b32) options.no_delay @@ -166,40 +167,48 @@ _bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) { } @(private) -_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network_Error) { +_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) { errno: linux.Errno assert(backlog > 0 && i32(backlog) < max(i32)) + // Figure out the address family and address of the endpoint ep_family := _unwrap_os_family(family_from_endpoint(endpoint)) ep_address := _unwrap_os_addr(endpoint) + // Create TCP socket os_sock: linux.Fd os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP) if errno != .NONE { - // TODO(flysand): should return invalid file descriptor here casted as TCP_Socket - return {}, Create_Socket_Error(errno) + err = Create_Socket_Error(errno) + return } + socket = cast(TCP_Socket)os_sock + defer if err != nil { close(socket) } + // NOTE(tetra): This is so that if we crash while the socket is open, we can // bypass the cooldown period, and allow the next run of the program to // use the same address immediately. // // TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address! do_reuse_addr: b32 = true - errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr) - if errno != .NONE { - return cast(TCP_Socket) os_sock, Listen_Error(errno) + if errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr); errno != .NONE { + err = Listen_Error(errno) + return } + // Bind the socket to endpoint address - errno = linux.bind(os_sock, &ep_address) - if errno != .NONE { - return cast(TCP_Socket) os_sock, Bind_Error(errno) + if errno = linux.bind(os_sock, &ep_address); errno != .NONE { + err = Bind_Error(errno) + return } + // Listen on bound socket - errno = linux.listen(os_sock, cast(i32) backlog) - if errno != .NONE { - return cast(TCP_Socket) os_sock, Listen_Error(errno) + if errno = linux.listen(os_sock, cast(i32) backlog); errno != .NONE { + err = Listen_Error(errno) + return } - return cast(TCP_Socket) os_sock, nil + + return } @(private) diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin index 20f17619d..747d5cab3 100644 --- a/core/net/socket_windows.odin +++ b/core/net/socket_windows.odin @@ -80,8 +80,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio sockaddr := _endpoint_to_sockaddr(endpoint) res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr)) if res < 0 { - err = Dial_Error(win.WSAGetLastError()) - return + close(socket) + return {}, Dial_Error(win.WSAGetLastError()) } if options.no_delay { @@ -107,6 +107,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T family := family_from_endpoint(interface_endpoint) sock := create_socket(family, .TCP) or_return socket = sock.(TCP_Socket) + defer if err != nil { close(socket) } // NOTE(tetra): While I'm not 100% clear on it, my understanding is that this will // prevent hijacking of the server's endpoint by other applications. diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index d67eb31f3..f62feec8c 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -776,17 +776,18 @@ Dynamic_Array_Type :: struct { Struct_Type :: struct { using node: Expr, - tok_pos: tokenizer.Pos, - poly_params: ^Field_List, - align: ^Expr, - field_align: ^Expr, - where_token: tokenizer.Token, - where_clauses: []^Expr, - is_packed: bool, - is_raw_union: bool, - is_no_copy: bool, - fields: ^Field_List, - name_count: int, + tok_pos: tokenizer.Pos, + poly_params: ^Field_List, + align: ^Expr, + min_field_align: ^Expr, + max_field_align: ^Expr, + where_token: tokenizer.Token, + where_clauses: []^Expr, + is_packed: bool, + is_raw_union: bool, + is_no_copy: bool, + fields: ^Field_List, + name_count: int, } Union_Type_Kind :: enum u8 { diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index b0a1673b2..67f7ffa95 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -316,7 +316,8 @@ clone_node :: proc(node: ^Node) -> ^Node { case ^Struct_Type: r.poly_params = auto_cast clone(r.poly_params) r.align = clone(r.align) - r.field_align = clone(r.field_align) + r.min_field_align = clone(r.min_field_align) + r.max_field_align = clone(r.max_field_align) r.fields = auto_cast clone(r.fields) case ^Union_Type: r.poly_params = auto_cast clone(r.poly_params) diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index d42766bde..5a7440339 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2610,9 +2610,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { case .Struct: tok := expect_token(p, .Struct) - poly_params: ^ast.Field_List - align: ^ast.Expr - field_align: ^ast.Expr + poly_params: ^ast.Field_List + align: ^ast.Expr + min_field_align: ^ast.Expr + max_field_align: ^ast.Expr is_packed: bool is_raw_union: bool is_no_copy: bool @@ -2645,10 +2646,21 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } align = parse_expr(p, true) case "field_align": - if field_align != nil { + if min_field_align != nil { + error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) + } + warn(p, tag.pos, "#field_align has been deprecated in favour of #min_field_align") + min_field_align = parse_expr(p, true) + case "min_field_align": + if min_field_align != nil { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) } - field_align = parse_expr(p, true) + min_field_align = parse_expr(p, true) + case "max_field_align": + if max_field_align != nil { + error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) + } + max_field_align = parse_expr(p, true) case "raw_union": if is_raw_union { error(p, tag.pos, "duplicate struct tag '#%s'", tag.text) @@ -2689,16 +2701,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { close := expect_closing_brace_of_field_list(p) st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)) - st.poly_params = poly_params - st.align = align - st.field_align = field_align - st.is_packed = is_packed - st.is_raw_union = is_raw_union - st.is_no_copy = is_no_copy - st.fields = fields - st.name_count = name_count - st.where_token = where_token - st.where_clauses = where_clauses + st.poly_params = poly_params + st.align = align + st.min_field_align = min_field_align + st.max_field_align = max_field_align + st.is_packed = is_packed + st.is_raw_union = is_raw_union + st.is_no_copy = is_no_copy + st.fields = fields + st.name_count = name_count + st.where_token = where_token + st.where_clauses = where_clauses return st case .Union: @@ -3683,6 +3696,8 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou } } + end := p.prev_tok + if p.expr_level >= 0 { end: ^ast.Expr if !is_mutable && len(values) > 0 { @@ -3702,7 +3717,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou } } - decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok)) + decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(end)) decl.docs = docs decl.names = names decl.type = type diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index c3a30581c..d4da82c56 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -331,7 +331,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool { n -= 1 } - if x > max || 0xd800 <= x && x <= 0xe000 { + if x > max || 0xd800 <= x && x <= 0xdfff { error(t, offset, "escape sequence is an invalid Unicode code point") return false } diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin index a205cae48..864532850 100644 --- a/core/os/os2/allocators.odin +++ b/core/os/os2/allocators.odin @@ -62,8 +62,8 @@ TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_locati @(deferred_out=TEMP_ALLOCATOR_GUARD_END) TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) { - tmp := temp_allocator_temp_begin(loc) global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT + tmp := temp_allocator_temp_begin(loc) return tmp, loc } diff --git a/core/os/os2/dir_linux.odin b/core/os/os2/dir_linux.odin index 6a097e192..f26b4fc79 100644 --- a/core/os/os2/dir_linux.odin +++ b/core/os/os2/dir_linux.odin @@ -13,7 +13,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info @(require_results) _read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) { - return {}, nil + return {}, .Unsupported } _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin index 09990aeec..f71e7e763 100644 --- a/core/os/os2/dir_windows.odin +++ b/core/os/os2/dir_windows.odin @@ -16,28 +16,25 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al } path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return + handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0) + defer win32.CloseHandle(handle) fi.fullpath = path fi.name = basename(path) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0) + fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0) fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) - - handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0) - defer win32.CloseHandle(handle) - if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) { #assert(size_of(fi.inode) == size_of(file_id_info.FileId)) #assert(size_of(fi.inode) == 16) runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16) } - return } @@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { return } file_info_delete(it.impl.prev_fi, file_allocator()) + delete(it.impl.path, file_allocator()) win32.FindClose(it.impl.find_handle) } diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin index 09492110d..a7556c306 100644 --- a/core/os/os2/errors_linux.odin +++ b/core/os/os2/errors_linux.odin @@ -162,6 +162,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error { return .Invalid_File case .ENOMEM: return .Out_Of_Memory + case .ENOSYS: + return .Unsupported } return Platform_Error(i32(errno)) diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin index 59f0ba5f1..0b5876c0b 100644 --- a/core/os/os2/errors_posix.odin +++ b/core/os/os2/errors_posix.odin @@ -26,6 +26,8 @@ _get_platform_error :: proc() -> Error { return .Invalid_File case .ENOMEM: return .Out_Of_Memory + case .ENOSYS: + return .Unsupported case: return Platform_Error(errno) } diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin index 56acd503f..404560f98 100644 --- a/core/os/os2/errors_windows.odin +++ b/core/os/os2/errors_windows.odin @@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error { case win32.ERROR_NEGATIVE_SEEK: return .Invalid_Offset + case win32.ERROR_BROKEN_PIPE: + return .Broken_Pipe + case win32.ERROR_BAD_ARGUMENTS, win32.ERROR_INVALID_PARAMETER, win32.ERROR_NOT_ENOUGH_MEMORY, win32.ERROR_NO_MORE_FILES, win32.ERROR_LOCK_VIOLATION, - win32.ERROR_BROKEN_PIPE, win32.ERROR_CALL_NOT_IMPLEMENTED, win32.ERROR_INSUFFICIENT_BUFFER, win32.ERROR_INVALID_NAME, diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 963544985..8af46fab3 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -164,7 +164,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d } @(require_results) -write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error { +write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error { flags := O_WRONLY|O_CREATE if truncate { flags |= O_TRUNC diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin index 511935d74..b91a1bc3b 100644 --- a/core/os/os2/file_windows.odin +++ b/core/os/os2/file_windows.odin @@ -50,9 +50,9 @@ init_std_files :: proc() { } @(fini) fini_std_files :: proc() { - close(stdin) - close(stdout) - close(stderr) + _destroy((^File_Impl)(stdin.impl)) + _destroy((^File_Impl)(stdout.impl)) + _destroy((^File_Impl)(stderr.impl)) } diff --git a/core/os/os2/path_posix.odin b/core/os/os2/path_posix.odin index 6f358c58d..5ffdac28e 100644 --- a/core/os/os2/path_posix.odin +++ b/core/os/os2/path_posix.odin @@ -40,7 +40,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error { internal_mkdir_all :: proc(path: string, perm: int) -> Error { dir, file := filepath.split(path) - if file != path { + if file != path && dir != "/" { if len(dir) > 1 && dir[len(dir) - 1] == '/' { dir = dir[:len(dir) - 1] } diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin index 9254d6f8e..5d3e8368e 100644 --- a/core/os/os2/pipe.odin +++ b/core/os/os2/pipe.odin @@ -1,6 +1,43 @@ package os2 +/* +Create an anonymous pipe. + +This procedure creates an anonymous pipe, returning two ends of the pipe, `r` +and `w`. The file `r` is the readable end of the pipe. The file `w` is a +writeable end of the pipe. + +Pipes are used as an inter-process communication mechanism, to communicate +between a parent and a child process. The child uses one end of the pipe to +write data, and the parent uses the other end to read from the pipe +(or vice-versa). When a parent passes one of the ends of the pipe to the child +process, that end of the pipe needs to be closed by the parent, before any data +is attempted to be read. + +Although pipes look like files and is compatible with most file APIs in package +os2, the way it's meant to be read is different. Due to asynchronous nature of +the communication channel, the data may not be present at the time of a read +request. The other scenario is when a pipe has no data because the other end +of the pipe was closed by the child process. +*/ @(require_results) pipe :: proc() -> (r, w: ^File, err: Error) { return _pipe() } + +/* +Check if the pipe has any data. + +This procedure checks whether a read-end of the pipe has data that can be +read, and returns `true`, if the pipe has readable data, and `false` if the +pipe is empty. This procedure does not block the execution of the current +thread. + +**Note**: If the other end of the pipe was closed by the child process, the +`.Broken_Pipe` +can be returned by this procedure. Handle these errors accordingly. +*/ +@(require_results) +pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + return _pipe_has_data(r) +} diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin index ac3382bc3..852674c69 100644 --- a/core/os/os2/pipe_linux.odin +++ b/core/os/os2/pipe_linux.odin @@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) { return } + +@(require_results) +_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + if r == nil || r.impl == nil { + return false, nil + } + fd := linux.Fd((^File_Impl)(r.impl).fd) + poll_fds := []linux.Poll_Fd { + linux.Poll_Fd { + fd = fd, + events = {.IN, .HUP}, + }, + } + n, errno := linux.poll(poll_fds, 0) + if n != 1 || errno != nil { + return false, _get_platform_error(errno) + } + pipe_events := poll_fds[0].revents + if pipe_events >= {.IN} { + return true, nil + } + if pipe_events >= {.HUP} { + return false, .Broken_Pipe + } + return false, nil +}
\ No newline at end of file diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin index 487e32aea..df9425339 100644 --- a/core/os/os2/pipe_posix.odin +++ b/core/os/os2/pipe_posix.odin @@ -44,3 +44,30 @@ _pipe :: proc() -> (r, w: ^File, err: Error) { return } +@(require_results) +_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + if r == nil || r.impl == nil { + return false, nil + } + fd := __fd(r) + poll_fds := []posix.pollfd { + posix.pollfd { + fd = fd, + events = {.IN, .HUP}, + }, + } + n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0) + if n < 0 { + return false, _get_platform_error() + } else if n != 1 { + return false, nil + } + pipe_events := poll_fds[0].revents + if pipe_events >= {.IN} { + return true, nil + } + if pipe_events >= {.HUP} { + return false, .Broken_Pipe + } + return false, nil +} diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index ee93fb683..d6dc47c9c 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) { return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil } +@(require_results) +_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) { + if r == nil || r.impl == nil { + return false, nil + } + handle := win32.HANDLE((^File_Impl)(r.impl).fd) + bytes_available: u32 + if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) { + return false, _get_platform_error() + } + return bytes_available > 0, nil +}
\ No newline at end of file diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index ce65987b0..5b5a6e844 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -1,16 +1,17 @@ package os2 import "base:runtime" + import "core:time" /* - In procedures that explicitly state this as one of the allowed values, - specifies an infinite timeout. +In procedures that explicitly state this as one of the allowed values, +specifies an infinite timeout. */ TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity /* - Arguments to the current process. +Arguments to the current process. */ args := get_args() @@ -24,17 +25,17 @@ get_args :: proc() -> []string { } /* - Exit the current process. +Exit the current process. */ exit :: proc "contextless" (code: int) -> ! { _exit(code) } /* - Obtain the UID of the current process. +Obtain the UID of the current process. - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_uid :: proc() -> int { @@ -42,15 +43,15 @@ get_uid :: proc() -> int { } /* - Obtain the effective UID of the current process. - - The effective UID is typically the same as the UID of the process. In case - the process was run by a user with elevated permissions, the process may - lower the privilege to perform some tasks without privilege. In these cases - the real UID of the process and the effective UID are different. - - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +Obtain the effective UID of the current process. + +The effective UID is typically the same as the UID of the process. In case +the process was run by a user with elevated permissions, the process may +lower the privilege to perform some tasks without privilege. In these cases +the real UID of the process and the effective UID are different. + +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_euid :: proc() -> int { @@ -58,10 +59,10 @@ get_euid :: proc() -> int { } /* - Obtain the GID of the current process. - - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +Obtain the GID of the current process. + +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_gid :: proc() -> int { @@ -69,15 +70,15 @@ get_gid :: proc() -> int { } /* - Obtain the effective GID of the current process. - - The effective GID is typically the same as the GID of the process. In case - the process was run by a user with elevated permissions, the process may - lower the privilege to perform some tasks without privilege. In these cases - the real GID of the process and the effective GID are different. - - **Note(windows)**: Windows doesn't follow the posix permissions model, so - the function simply returns -1. +Obtain the effective GID of the current process. + +The effective GID is typically the same as the GID of the process. In case +the process was run by a user with elevated permissions, the process may +lower the privilege to perform some tasks without privilege. In these cases +the real GID of the process and the effective GID are different. + +**Note(windows)**: Windows doesn't follow the posix permissions model, so +the function simply returns -1. */ @(require_results) get_egid :: proc() -> int { @@ -85,7 +86,7 @@ get_egid :: proc() -> int { } /* - Obtain the ID of the current process. +Obtain the ID of the current process. */ @(require_results) get_pid :: proc() -> int { @@ -93,13 +94,13 @@ get_pid :: proc() -> int { } /* - Obtain the ID of the parent process. +Obtain the ID of the parent process. - **Note(windows)**: Windows does not mantain strong relationships between - parent and child processes. This function returns the ID of the process - that has created the current process. In case the parent has died, the ID - returned by this function can identify a non-existent or a different - process. +**Note(windows)**: Windows does not mantain strong relationships between +parent and child processes. This function returns the ID of the process +that has created the current process. In case the parent has died, the ID +returned by this function can identify a non-existent or a different +process. */ @(require_results) get_ppid :: proc() -> int { @@ -107,7 +108,7 @@ get_ppid :: proc() -> int { } /* - Obtain ID's of all processes running in the system. +Obtain ID's of all processes running in the system. */ @(require_results) process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) { @@ -115,9 +116,9 @@ process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) { } /* - Bit set specifying which fields of the `Process_Info` struct need to be - obtained by the `process_info()` procedure. Each bit corresponds to a - field in the `Process_Info` struct. +Bit set specifying which fields of the `Process_Info` struct need to be +obtained by the `process_info()` procedure. Each bit corresponds to a +field in the `Process_Info` struct. */ Process_Info_Fields :: bit_set[Process_Info_Field] Process_Info_Field :: enum { @@ -134,8 +135,8 @@ Process_Info_Field :: enum { ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir} /* - Contains information about the process as obtained by the `process_info()` - procedure. +Contains information about the process as obtained by the `process_info()` +procedure. */ Process_Info :: struct { // The information about a process the struct contains. `pid` is always @@ -162,19 +163,19 @@ Process_Info :: struct { } /* - Obtain information about a process. +Obtain information about a process. - This procedure obtains an information, specified by `selection` parameter of - a process given by `pid`. +This procedure obtains an information, specified by `selection` parameter of +a process given by `pid`. - Use `free_process_info` to free the memory allocated by this procedure. The - `free_process_info` procedure needs to be called, even if this procedure - returned an error, as some of the fields may have been allocated. +Use `free_process_info` to free the memory allocated by this procedure. The +`free_process_info` procedure needs to be called, even if this procedure +returned an error, as some of the fields may have been allocated. - **Note**: The resulting information may or may contain the fields specified - by the `selection` parameter. Always check whether the returned - `Process_Info` struct has the required fields before checking the error code - returned by this procedure. +**Note**: The resulting information may or may contain the fields specified +by the `selection` parameter. Always check whether the returned +`Process_Info` struct has the required fields before checking the error code +returned by this procedure. */ @(require_results) process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -182,20 +183,20 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: } /* - Obtain information about a process. +Obtain information about a process. - This procedure obtains information, specified by `selection` parameter - about a process that has been opened by the application, specified in - the `process` parameter. +This procedure obtains information, specified by `selection` parameter +about a process that has been opened by the application, specified in +the `process` parameter. - Use `free_process_info` to free the memory allocated by this procedure. The - `free_process_info` procedure needs to be called, even if this procedure - returned an error, as some of the fields may have been allocated. +Use `free_process_info` to free the memory allocated by this procedure. The +`free_process_info` procedure needs to be called, even if this procedure +returned an error, as some of the fields may have been allocated. - **Note**: The resulting information may or may contain the fields specified - by the `selection` parameter. Always check whether the returned - `Process_Info` struct has the required fields before checking the error code - returned by this procedure. +**Note**: The resulting information may or may contain the fields specified +by the `selection` parameter. Always check whether the returned +`Process_Info` struct has the required fields before checking the error code +returned by this procedure. */ @(require_results) process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -203,19 +204,19 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, } /* - Obtain information about the current process. +Obtain information about the current process. - This procedure obtains the information, specified by `selection` parameter - about the currently running process. +This procedure obtains the information, specified by `selection` parameter +about the currently running process. - Use `free_process_info` to free the memory allocated by this procedure. The - `free_process_info` procedure needs to be called, even if this procedure - returned an error, as some of the fields may have been allocated. +Use `free_process_info` to free the memory allocated by this procedure. The +`free_process_info` procedure needs to be called, even if this procedure +returned an error, as some of the fields may have been allocated. - **Note**: The resulting information may or may contain the fields specified - by the `selection` parameter. Always check whether the returned - `Process_Info` struct has the required fields before checking the error code - returned by this procedure. +**Note**: The resulting information may or may contain the fields specified +by the `selection` parameter. Always check whether the returned +`Process_Info` struct has the required fields before checking the error code +returned by this procedure. */ @(require_results) current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) { @@ -223,7 +224,7 @@ current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime. } /* - Obtain information about the specified process. +Obtain information about the specified process. */ process_info :: proc { process_info_by_pid, @@ -232,11 +233,11 @@ process_info :: proc { } /* - Free the information about the process. +Free the information about the process. - This procedure frees the memory occupied by process info using the provided - allocator. The allocator needs to be the same allocator that was supplied - to the `process_info` function. +This procedure frees the memory occupied by process info using the provided +allocator. The allocator needs to be the same allocator that was supplied +to the `process_info` function. */ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) { delete(pi.executable_path, allocator) @@ -254,13 +255,13 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) { } /* - Represents a process handle. +Represents a process handle. - When a process dies, the OS is free to re-use the pid of that process. The - `Process` struct represents a handle to the process that will refer to a - specific process, even after it has died. +When a process dies, the OS is free to re-use the pid of that process. The +`Process` struct represents a handle to the process that will refer to a +specific process, even after it has died. - **Note(linux)**: The `handle` will be referring to pidfd. +**Note(linux)**: The `handle` will be referring to pidfd. */ Process :: struct { pid: int, @@ -276,13 +277,13 @@ Process_Open_Flag :: enum { } /* - Open a process handle using it's pid. +Open a process handle using it's pid. - This procedure obtains a process handle of a process specified by `pid`. - This procedure can be subject to race conditions. See the description of - `Process`. +This procedure obtains a process handle of a process specified by `pid`. +This procedure can be subject to race conditions. See the description of +`Process`. - Use `process_close()` function to close the process handle. +Use `process_close()` function to close the process handle. */ @(require_results) process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) { @@ -322,32 +323,140 @@ Process_Desc :: struct { } /* - Create a new process and obtain its handle. - - This procedure creates a new process, with a given command and environment - strings as parameters. Use `environ()` to inherit the environment of the - current process. - - The `desc` parameter specifies the description of how the process should - be created. It contains information such as the command line, the - environment of the process, the starting directory and many other options. - Most of the fields in the struct can be set to `nil` or an empty value. - - Use `process_close` to close the handle to the process. Note, that this - is not the same as terminating the process. One can terminate the process - and not close the handle, in which case the handle would be leaked. In case - the function returns an error, an invalid handle is returned. - - This procedure is not thread-safe. It may alter the inheritance properties - of file handles in an unpredictable manner. In case multiple threads change - handle inheritance properties, make sure to serialize all those calls. +Create a new process and obtain its handle. + +This procedure creates a new process, with a given command and environment +strings as parameters. Use `environ()` to inherit the environment of the +current process. + +The `desc` parameter specifies the description of how the process should +be created. It contains information such as the command line, the +environment of the process, the starting directory and many other options. +Most of the fields in the struct can be set to `nil` or an empty value. + +Use `process_close` to close the handle to the process. Note, that this +is not the same as terminating the process. One can terminate the process +and not close the handle, in which case the handle would be leaked. In case +the function returns an error, an invalid handle is returned. + +This procedure is not thread-safe. It may alter the inheritance properties +of file handles in an unpredictable manner. In case multiple threads change +handle inheritance properties, make sure to serialize all those calls. */ @(require_results) -process_start :: proc(desc := Process_Desc {}) -> (Process, Error) { +process_start :: proc(desc: Process_Desc) -> (Process, Error) { return _process_start(desc) } /* +Execute the process and capture stdout and stderr streams. + +This procedure creates a new process, with a given command and environment +strings as parameters, and waits until the process finishes execution. While +the process is running, this procedure accumulates the output of its stdout +and stderr streams and returns byte slices containing the captured data from +the streams. + +This procedure expects that `stdout` and `stderr` fields of the `desc` parameter +are left at default, i.e. a `nil` value. You can not capture stdout/stderr and +redirect it to a file at the same time. + +This procedure does not free `stdout` and `stderr` slices before an error is +returned. Make sure to call `delete` on these slices. +*/ +@(require_results) +process_exec :: proc( + desc: Process_Desc, + allocator: runtime.Allocator, + loc := #caller_location, +) -> ( + state: Process_State, + stdout: []byte, + stderr: []byte, + err: Error, +) { + assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc) + assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc) + + stdout_r, stdout_w := pipe() or_return + defer close(stdout_r) + stderr_r, stderr_w := pipe() or_return + defer close(stderr_r) + + process: Process + { + // NOTE(flysand): Make sure the write-ends are closed, regardless + // of the outcome. This makes read-ends readable on our side. + defer close(stdout_w) + defer close(stderr_w) + desc := desc + desc.stdout = stdout_w + desc.stderr = stderr_w + process = process_start(desc) or_return + } + + { + stdout_b: [dynamic]byte + stdout_b.allocator = allocator + defer stdout = stdout_b[:] + + stderr_b: [dynamic]byte + stderr_b.allocator = allocator + defer stderr = stderr_b[:] + + buf: [1024]u8 = --- + + stdout_done, stderr_done, has_data: bool + for err == nil && (!stdout_done || !stderr_done) { + n := 0 + + if !stdout_done { + has_data, err = pipe_has_data(stdout_r) + if has_data { + n, err = read(stdout_r, buf[:]) + } + + switch err { + case nil: + _, err = append(&stdout_b, ..buf[:n]) + case .EOF, .Broken_Pipe: + stdout_done = true + err = nil + } + } + + if err == nil && !stderr_done { + n = 0 + has_data, err = pipe_has_data(stderr_r) + if has_data { + n, err = read(stderr_r, buf[:]) + } + + switch err { + case nil: + _, err = append(&stderr_b, ..buf[:n]) + case .EOF, .Broken_Pipe: + stderr_done = true + err = nil + } + } + } + } + + if err != nil { + state, _ = process_wait(process, timeout=0) + if !state.exited { + _ = process_kill(process) + state, _ = process_wait(process) + } + return + } + + state, err = process_wait(process) + return +} + +/* The state of the process after it has finished execution. */ Process_State :: struct { @@ -371,17 +480,17 @@ Process_State :: struct { } /* - Wait for a process event. +Wait for a process event. - This procedure blocks the execution until the process has exited or the - timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`, - no timeout restriction is imposed and the procedure can block indefinately. +This procedure blocks the execution until the process has exited or the +timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`, +no timeout restriction is imposed and the procedure can block indefinately. - If the timeout has expired, the `General_Error.Timeout` is returned as - the error. +If the timeout has expired, the `General_Error.Timeout` is returned as +the error. - If an error is returned for any other reason, other than timeout, the - process state is considered undetermined. +If an error is returned for any other reason, other than timeout, the +process state is considered undetermined. */ @(require_results) process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) { @@ -389,12 +498,12 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_ } /* - Close the handle to a process. +Close the handle to a process. - This procedure closes the handle associated with a process. It **does not** - terminate a process, in case it was running. In case a termination is - desired, kill the process first, wait for the process to finish, - then close the handle. +This procedure closes the handle associated with a process. It **does not** +terminate a process, in case it was running. In case a termination is +desired, kill the process first, wait for the process to finish, +then close the handle. */ @(require_results) process_close :: proc(process: Process) -> (Error) { @@ -402,10 +511,9 @@ process_close :: proc(process: Process) -> (Error) { } /* - Terminate a process. - - This procedure terminates a process, specified by it's handle, `process`. +Terminate a process. +This procedure terminates a process, specified by it's handle, `process`. */ @(require_results) process_kill :: proc(process: Process) -> (Error) { diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin index ea5ee41b1..7eb4dfa44 100644 --- a/core/os/os2/process_linux.odin +++ b/core/os/os2/process_linux.odin @@ -523,7 +523,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! { error_byte: [1]u8 = { u8(errno) } linux.write(parent_fd, error_byte[:]) - intrinsics.trap() + linux.exit(126) } stdin_fd: linux.Fd diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin index 5ac6babc1..b54374cec 100644 --- a/core/os/os2/process_posix.odin +++ b/core/os/os2/process_posix.odin @@ -163,7 +163,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { #assert(len(posix.Errno) < max(u8)) errno := u8(posix.errno()) posix.write(parent_fd, &errno, 1) - runtime.trap() + posix.exit(126) } null := posix.open("/dev/null", {.RDWR}) @@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { return } - process.pid = int(pid) process, _ = _process_open(int(pid), {}) return } diff --git a/core/os/os2/process_posix_other.odin b/core/os/os2/process_posix_other.odin index 77dbfa7eb..65da3e9e2 100644 --- a/core/os/os2/process_posix_other.odin +++ b/core/os/os2/process_posix_other.odin @@ -15,6 +15,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) } _process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) { + process.pid = pid err = .Unsupported return } diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin index 0c32373f3..1984cbfdf 100644 --- a/core/os/os2/process_windows.odin +++ b/core/os/os2/process_windows.odin @@ -442,7 +442,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) { stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd) } if desc.stdin != nil { - stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd) + stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd) } working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil @@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) -> strings.write_byte(&builder, ' ') } j := 0 - strings.write_byte(&builder, '"') - for j < len(arg) { - backslashes := 0 - for j < len(arg) && arg[j] == '\\' { - backslashes += 1 + if strings.contains_any(arg, "()[]{}^=;!'+,`~\" ") { + strings.write_byte(&builder, '"') + for j < len(arg) { + backslashes := 0 + for j < len(arg) && arg[j] == '\\' { + backslashes += 1 + j += 1 + } + if j == len(arg) { + _write_byte_n_times(&builder, '\\', 2*backslashes) + break + } else if arg[j] == '"' { + _write_byte_n_times(&builder, '\\', 2*backslashes+1) + strings.write_byte(&builder, arg[j]) + } else { + _write_byte_n_times(&builder, '\\', backslashes) + strings.write_byte(&builder, arg[j]) + } j += 1 } - if j == len(arg) { - _write_byte_n_times(&builder, '\\', 2*backslashes) - break - } else if arg[j] == '"' { - _write_byte_n_times(&builder, '\\', 2*backslashes+1) - strings.write_byte(&builder, '"') - } else { - _write_byte_n_times(&builder, '\\', backslashes) - strings.write_byte(&builder, arg[j]) - } - j += 1 + strings.write_byte(&builder, '"') + } else { + strings.write_string(&builder, arg) } - strings.write_byte(&builder, '"') } return strings.to_string(builder) } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index 8ed2a6fed..0a019e9da 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -200,22 +200,21 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi } else { mode |= 0o666 } + is_sym := false if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { is_sym = false } else { is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT } + if is_sym { type = .Symlink - } else { - if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - type = .Directory - mode |= 0o111 - } - if h != nil { - type = file_type(h) - } + } else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + type = .Directory + mode |= 0o111 + } else if h != nil { + type = file_type(h) } return } diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin index 371485a47..b09a41fbf 100644 --- a/core/os/os_darwin.odin +++ b/core/os/os_darwin.odin @@ -1061,7 +1061,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -1076,9 +1076,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> bool { 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 8c8cd7f73..e023ce7cb 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -242,10 +242,13 @@ F_SETFL: int : 4 /* Set file flags */ // NOTE(zangent): These are OS specific! // Do not mix these up! -RTLD_LAZY :: 0x001 -RTLD_NOW :: 0x002 -RTLD_BINDING_MASK :: 0x3 -RTLD_GLOBAL :: 0x100 +RTLD_LAZY :: 0x0001 +RTLD_NOW :: 0x0002 +RTLD_BINDING_MASK :: 0x0003 +RTLD_GLOBAL :: 0x0100 +RTLD_NOLOAD :: 0x0004 +RTLD_DEEPBIND :: 0x0008 +RTLD_NODELETE :: 0x1000 socklen_t :: c.int @@ -487,7 +490,7 @@ foreign libc { @(link_name="free") _unix_free :: proc(ptr: rawptr) --- @(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr --- - @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> int --- + @(link_name="execvp") _unix_execvp :: proc(path: cstring, argv: [^]cstring) -> c.int --- @(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring --- @(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int --- @(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int --- @@ -914,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) { } @(require_results) -absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { +absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) { rel := rel if rel == "" { rel = "." @@ -929,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) { } defer _unix_free(rawptr(path_ptr)) - path = strings.clone(string(path_ptr)) - - return path, nil + return strings.clone(string(path_ptr), allocator) } access :: proc(path: string, mask: int) -> (bool, Error) { 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/reflect/reflect.odin b/core/reflect/reflect.odin index c04afb380..7f79acb77 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -31,8 +31,6 @@ Type_Info_Enum :: runtime.Type_Info_Enum Type_Info_Map :: runtime.Type_Info_Map Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector -Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer -Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field @@ -67,8 +65,6 @@ Type_Kind :: enum { Map, Bit_Set, Simd_Vector, - Relative_Pointer, - Relative_Multi_Pointer, Matrix, Soa_Pointer, Bit_Field, @@ -80,35 +76,33 @@ type_kind :: proc(T: typeid) -> Type_Kind { ti := type_info_of(T) if ti != nil { switch _ in ti.variant { - case Type_Info_Named: return .Named - case Type_Info_Integer: return .Integer - case Type_Info_Rune: return .Rune - case Type_Info_Float: return .Float - case Type_Info_Complex: return .Complex - case Type_Info_Quaternion: return .Quaternion - case Type_Info_String: return .String - case Type_Info_Boolean: return .Boolean - case Type_Info_Any: return .Any - case Type_Info_Type_Id: return .Type_Id - case Type_Info_Pointer: return .Pointer - case Type_Info_Multi_Pointer: return .Multi_Pointer - case Type_Info_Procedure: return .Procedure - case Type_Info_Array: return .Array - case Type_Info_Enumerated_Array: return .Enumerated_Array - case Type_Info_Dynamic_Array: return .Dynamic_Array - case Type_Info_Slice: return .Slice - case Type_Info_Parameters: return .Tuple - case Type_Info_Struct: return .Struct - case Type_Info_Union: return .Union - case Type_Info_Enum: return .Enum - case Type_Info_Map: return .Map - case Type_Info_Bit_Set: return .Bit_Set - case Type_Info_Simd_Vector: return .Simd_Vector - case Type_Info_Relative_Pointer: return .Relative_Pointer - case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer - case Type_Info_Matrix: return .Matrix - case Type_Info_Soa_Pointer: return .Soa_Pointer - case Type_Info_Bit_Field: return .Bit_Field + case Type_Info_Named: return .Named + case Type_Info_Integer: return .Integer + case Type_Info_Rune: return .Rune + case Type_Info_Float: return .Float + case Type_Info_Complex: return .Complex + case Type_Info_Quaternion: return .Quaternion + case Type_Info_String: return .String + case Type_Info_Boolean: return .Boolean + case Type_Info_Any: return .Any + case Type_Info_Type_Id: return .Type_Id + case Type_Info_Pointer: return .Pointer + case Type_Info_Multi_Pointer: return .Multi_Pointer + case Type_Info_Procedure: return .Procedure + case Type_Info_Array: return .Array + case Type_Info_Enumerated_Array: return .Enumerated_Array + case Type_Info_Dynamic_Array: return .Dynamic_Array + case Type_Info_Slice: return .Slice + case Type_Info_Parameters: return .Tuple + case Type_Info_Struct: return .Struct + case Type_Info_Union: return .Union + case Type_Info_Enum: return .Enum + case Type_Info_Map: return .Map + case Type_Info_Bit_Set: return .Bit_Set + case Type_Info_Simd_Vector: return .Simd_Vector + case Type_Info_Matrix: return .Matrix + case Type_Info_Soa_Pointer: return .Soa_Pointer + case Type_Info_Bit_Field: return .Bit_Field } } @@ -723,6 +717,27 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) { return } +/* +Returns whether the value given has a defined name in the enum type. +*/ +@(require_results) +enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) { + when len(T) == cap(T) { + return value >= min(T) && value <= max(T) + } else { + if value < min(T) || value > max(T) { + return false + } + + for valid_value in T { + if valid_value == value { + return true + } + } + + return false + } +} @@ -1468,21 +1483,6 @@ as_string :: proc(a: any) -> (value: string, valid: bool) { } @(require_results) -relative_pointer_to_absolute :: proc(a: any) -> rawptr { - if a == nil { return nil } - a := a - ti := runtime.type_info_core(type_info_of(a.id)) - a.id = ti.id - - #partial switch info in ti.variant { - case Type_Info_Relative_Pointer: - return relative_pointer_to_absolute_raw(a.data, info.base_integer.id) - } - return nil -} - - -@(require_results) relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr { _handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) { if ptr^ == 0 { @@ -1543,10 +1543,6 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) { case cstring: value = rawptr(v) case: valid = false } - - case Type_Info_Relative_Pointer: - valid = true - value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id) } return @@ -1656,8 +1652,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_ Type_Info_Bit_Set, Type_Info_Enum, Type_Info_Simd_Vector, - Type_Info_Relative_Pointer, - Type_Info_Relative_Multi_Pointer, Type_Info_Soa_Pointer, Type_Info_Matrix: return runtime.memory_compare(a.data, b.data, t.size) == 0 diff --git a/core/reflect/types.odin b/core/reflect/types.odin index 4f0674dc8..cb31a27e2 100644 --- a/core/reflect/types.odin +++ b/core/reflect/types.odin @@ -158,14 +158,6 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool { case Type_Info_Simd_Vector: y := b.variant.(Type_Info_Simd_Vector) or_return return x.count == y.count && x.elem == y.elem - - case Type_Info_Relative_Pointer: - y := b.variant.(Type_Info_Relative_Pointer) or_return - return x.base_integer == y.base_integer && x.pointer == y.pointer - - case Type_Info_Relative_Multi_Pointer: - y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return - return x.base_integer == y.base_integer && x.pointer == y.pointer case Type_Info_Matrix: y := b.variant.(Type_Info_Matrix) or_return @@ -392,18 +384,6 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool { _, ok := type_info_base(info).variant.(Type_Info_Simd_Vector) return ok } -@(require_results) -is_relative_pointer :: proc(info: ^Type_Info) -> bool { - if info == nil { return false } - _, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer) - return ok -} -@(require_results) -is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool { - if info == nil { return false } - _, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer) - return ok -} @(require_results) @@ -736,18 +716,6 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt io.write_i64(w, i64(info.count), 10, &n) or_return io.write_byte(w, ']', &n) or_return write_type(w, info.elem, &n) or_return - - case Type_Info_Relative_Pointer: - io.write_string(w, "#relative(", &n) or_return - write_type(w, info.base_integer, &n) or_return - io.write_string(w, ") ", &n) or_return - write_type(w, info.pointer, &n) or_return - - case Type_Info_Relative_Multi_Pointer: - io.write_string(w, "#relative(", &n) or_return - write_type(w, info.base_integer, &n) or_return - io.write_string(w, ") ", &n) or_return - write_type(w, info.pointer, &n) or_return case Type_Info_Matrix: if info.layout == .Row_Major { diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin index aaddbe6b4..4c231c377 100644 --- a/core/simd/x86/sse2.odin +++ b/core/simd/x86/sse2.odin @@ -240,7 +240,7 @@ _mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { } @(require_results, enable_target_feature="sse2") _mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { - return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8) + return transmute(__m128i)psraiw(transmute(i16x8)a, IMM8) } @(require_results, enable_target_feature="sse2") _mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { @@ -262,7 +262,7 @@ _mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { } @(require_results, enable_target_feature="sse2") _mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i { - return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8) + return transmute(__m128i)psrliw(transmute(i16x8)a, IMM8) } @(require_results, enable_target_feature="sse2") _mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i { diff --git a/core/slice/map.odin b/core/slice/map.odin index 545ba8305..c68488d26 100644 --- a/core/slice/map.odin +++ b/core/slice/map.odin @@ -48,11 +48,11 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries return } -map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check { +map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V), err: runtime.Allocator_Error) #no_bounds_check { m := m rm := (^runtime.Raw_Map)(&m) - info := type_info_base(type_info_of(M)).variant.(Type_Info_Map) + info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map) if info.map_info != nil { entries = make(type_of(entries), len(m), allocator) or_return @@ -61,8 +61,8 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent entry_index := 0 for bucket_index in 0..<map_cap { if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) { - key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index) - value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index) + key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index) + value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index) entries[entry_index].hash = hash entries[entry_index].key = (^K)(key)^ entries[entry_index].value = (^V)(value)^ diff --git a/core/slice/slice.odin b/core/slice/slice.odin index 99ad15547..c31edf281 100644 --- a/core/slice/slice.odin +++ b/core/slice/slice.odin @@ -37,6 +37,17 @@ to_bytes :: proc "contextless" (s: []$T) -> []byte { } /* + Turns a byte slice into a type. +*/ +@(require_results) +to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok { + if len(buf) < size_of(T) { + return {}, false + } + return intrinsics.unaligned_load((^T)(raw_data(buf))), true +} + +/* Turn a slice of one type, into a slice of another type. Only converts the type and length of the slice itself. @@ -96,9 +107,37 @@ contains :: proc(array: $T/[]$E, value: E) -> bool where intrinsics.type_is_comp return found } +/* +Searches the given slice for the given element in O(n) time. + +If you need a custom search condition, see `linear_search_proc` + +Inputs: +- array: The slice to search in. +- key: The element to search for. + +Returns: +- index: The index `i`, such that `array[i]` is the first occurrence of `key` in `array`, or -1 if `key` is not present in `array`. + +Example: + index: int + found: bool + + a := []i32{10, 10, 10, 20} + + index, found = linear_search_reverse(a, 10) + assert(index == 0 && found == true) + + index, found = linear_search_reverse(a, 30) + assert(index == -1 && found == false) + + // Note that `index == 1`, since it is relative to `a[2:]` + index, found = linear_search_reverse(a[2:], 20) + assert(index == 1 && found == true) +*/ @(require_results) linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) - where intrinsics.type_is_comparable(T) #no_bounds_check { + where intrinsics.type_is_comparable(T) { for x, i in array { if x == key { return i, true @@ -107,8 +146,18 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) return -1, false } +/* +Searches the given slice for the first element satisfying predicate `f` in O(n) time. + +Inputs: +- array: The slice to search in. +- f: The search condition. + +Returns: +- index: The index `i`, such that `array[i]` is the first `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist. +*/ @(require_results) -linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) #no_bounds_check { +linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) { for x, i in array { if f(x) { return i, true @@ -118,22 +167,88 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f } /* - Binary search searches the given slice for the given element. - If the slice is not sorted, the returned index is unspecified and meaningless. +Searches the given slice for the given element in O(n) time, starting from the +slice end. + +If you need a custom search condition, see `linear_search_reverse_proc` + +Inputs: +- array: The slice to search in. +- key: The element to search for. - If the value is found then the returned int is the index of the matching element. - If there are multiple matches, then any one of the matches could be returned. +Returns: +- index: The index `i`, such that `array[i]` is the last occurrence of `key` in `array`, or -1 if `key` is not present in `array`. - If the value is not found then the returned int is the index where a matching - element could be inserted while maintaining sorted order. +Example: + index: int + found: bool + + a := []i32{10, 10, 10, 20} + + index, found = linear_search_reverse(a, 20) + assert(index == 3 && found == true) - # Examples + index, found = linear_search_reverse(a, 10) + assert(index == 2 && found == true) + index, found = linear_search_reverse(a, 30) + assert(index == -1 && found == false) + + // Note that `index == 1`, since it is relative to `a[2:]` + index, found = linear_search_reverse(a[2:], 20) + assert(index == 1 && found == true) +*/ +@(require_results) +linear_search_reverse :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) + where intrinsics.type_is_comparable(T) { + #reverse for x, i in array { + if x == key { + return i, true + } + } + return -1, false +} + +/* +Searches the given slice for the last element satisfying predicate `f` in O(n) +time, starting from the slice end. + +Inputs: +- array: The slice to search in. +- f: The search condition. + +Returns: +- index: The index `i`, such that `array[i]` is the last `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist. +*/ +@(require_results) +linear_search_reverse_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) { + #reverse for x, i in array { + if f(x) { + return i, true + } + } + return -1, false +} + +/* +Searches the given slice for the given element. +If the slice is not sorted, the returned index is unspecified and meaningless. + +If the value is found then the returned int is the index of the matching element. +If there are multiple matches, then any one of the matches could be returned. + +If the value is not found then the returned int is the index where a matching +element could be inserted while maintaining sorted order. + +For slices of more complex types see: `binary_search_by` + +Example: + /* Looks up a series of four elements. The first is found, with a uniquely determined position; the second and third are not found; the fourth could match any position in `[1, 4]`. + */ - ``` index: int found: bool @@ -150,9 +265,6 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f index, found = slice.binary_search(s, 1) assert(index >= 1 && index <= 4 && found == true) - ``` - - For slices of more complex types see: binary_search_by */ @(require_results) binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) @@ -161,21 +273,21 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool) } /* - Binary search searches the given slice for the given element. - If the slice is not sorted, the returned index is unspecified and meaningless. +Searches the given slice for the given element. +If the slice is not sorted, the returned index is unspecified and meaningless. - If the value is found then the returned int is the index of the matching element. - If there are multiple matches, then any one of the matches could be returned. +If the value is found then the returned int is the index of the matching element. +If there are multiple matches, then any one of the matches could be returned. - If the value is not found then the returned int is the index where a matching - element could be inserted while maintaining sorted order. +If the value is not found then the returned int is the index where a matching +element could be inserted while maintaining sorted order. - The array elements and key may be different types. This allows the filter procedure - to compare keys against a slice of structs, one struct value at a time. +The array elements and key may be different types. This allows the filter procedure +to compare keys against a slice of structs, one struct value at a time. - Returns: - index: int - found: bool +Returns: +- index: int +- found: bool */ @(require_results) @@ -359,6 +471,12 @@ is_empty :: proc(a: $T/[]$E) -> bool { return len(a) == 0 } +// Gets the byte size of the backing data +@(require_results) +size :: proc "contextless" (a: $T/[]$E) -> int { + return len(a) * size_of(E) +} + @(require_results) 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/strings/strings.odin b/core/strings/strings.odin index dbc84f8b7..af93ff33c 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -752,7 +752,7 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0)) -> (res: st count += 1 } - if rune_length <= 1 { + if rune_length < 1 { return s } diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin index 7191f6d07..254da75ad 100644 --- a/core/sys/darwin/Foundation/NSApplication.odin +++ b/core/sys/darwin/Foundation/NSApplication.odin @@ -108,6 +108,16 @@ Application_setMainMenu :: proc "c" (self: ^Application, menu: ^Menu) { msgSend(nil, self, "setMainMenu:", menu) } +@(objc_type=Application, objc_name="mainWindow") +Application_mainWindow :: proc "c" (self: ^Application) -> ^Window { + return msgSend(^Window, self, "mainWindow") +} + +@(objc_type=Application, objc_name="keyWindow") +Application_keyWindow :: proc "c" (self: ^Application) -> ^Window { + return msgSend(^Window, self, "keyWindow") +} + @(objc_type=Application, objc_name="windows") Application_windows :: proc "c" (self: ^Application) -> ^Array { return msgSend(^Array, self, "windows") diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin index 1e728fe78..01affa6e8 100644 --- a/core/sys/darwin/mach_darwin.odin +++ b/core/sys/darwin/mach_darwin.odin @@ -3,17 +3,18 @@ package darwin foreign import mach "system:System.framework" import "core:c" +import "base:intrinsics" // NOTE(tetra): Unclear whether these should be aligned 16 or not. // However all other sync primitives are aligned for robustness. // I cannot currently align these though. // See core/sys/unix/pthread_linux.odin/pthread_t. -mach_port_t :: distinct i32 +mach_port_t :: distinct c.uint task_t :: mach_port_t semaphore_t :: distinct u64 -kern_return_t :: distinct u64 +kern_return_t :: distinct c.int thread_act_t :: distinct u64 thread_state_t :: distinct ^u32 thread_list_t :: [^]thread_act_t @@ -37,11 +38,6 @@ MACH_MSGH_BITS_COMPLEX :: 0x80000000 MACH_PORT_RIGHT_SEND :: 0 MACH_PORT_RIGHT_RECEIVE :: 1 -VM_PROT_NONE :: 0 -VM_PROT_READ :: 1 -VM_PROT_WRITE :: 2 -VM_PROT_EXECUTE :: 4 - VM_INHERIT_SHARE :: 0 VM_INHERIT_COPY :: 1 VM_INHERIT_NONE :: 2 @@ -58,6 +54,27 @@ ARM_THREAD_STATE64 :: 6 mach_msg_option_t :: distinct i32 name_t :: distinct cstring +vm_map_t :: mach_port_t +mem_entry_name_port_t :: mach_port_t +ipc_space_t :: mach_port_t +thread_t :: mach_port_t + +vm_size_t :: distinct c.uintptr_t + +vm_address_t :: vm_offset_t +vm_offset_t :: distinct c.uintptr_t + +// NOTE(beau): typedefed to int in the original headers +boolean_t :: b32 + +vm_prot_t :: distinct c.int + +vm_inherit_t :: distinct c.uint + +mach_port_name_t :: distinct c.uint + +sync_policy_t :: distinct c.int + mach_msg_port_descriptor_t :: struct { name: mach_port_t, _: u32, @@ -257,20 +274,489 @@ foreign mach { task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t --- task_terminate :: proc(task: task_t) -> kern_return_t --- + semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return --- + semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return --- + + semaphore_signal :: proc(semaphore: semaphore_t) -> Kern_Return --- + semaphore_signal_all :: proc(semaphore: semaphore_t) -> Kern_Return --- + semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_t) -> Kern_Return --- + + semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return --- + thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t --- thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t --- bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t --- bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t --- - semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t --- - semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t --- + vm_map :: proc( + target_task: vm_map_t, + address: ^vm_address_t, + size: vm_size_t, + mask: vm_address_t, + flags: VM_Flags, + object: mem_entry_name_port_t, + offset: vm_offset_t, + copy: boolean_t, + cur_protection, + max_protection: VM_Prot_Flags, + inheritance: VM_Inherit, + ) -> Kern_Return --- + + mach_make_memory_entry :: proc( + target_task: vm_map_t, + size: ^vm_size_t, + offset: vm_offset_t, + permission: VM_Prot_Flags, + object_handle: ^mem_entry_name_port_t, + parent_entry: mem_entry_name_port_t, + ) -> Kern_Return --- +} + + + +Kern_Return :: enum kern_return_t { + Success, + + /* Specified address is not currently valid. + */ + Invalid_Address, + + /* Specified memory is valid, but does not permit the + * required forms of access. + */ + Protection_Failure, + + /* The address range specified is already in use, or + * no address range of the size specified could be + * found. + */ + No_Space, + + /* The function requested was not applicable to this + * type of argument, or an argument is invalid + */ + Invalid_Argument, + + /* The function could not be performed. A catch-all. + */ + Failure, + + /* A system resource could not be allocated to fulfill + * this request. This failure may not be permanent. + */ + Resource_Shortage, + + /* The task in question does not hold receive rights + * for the port argument. + */ + Not_Receiver, + + /* Bogus access restriction. + */ + No_Access, + + /* During a page fault, the target address refers to a + * memory object that has been destroyed. This + * failure is permanent. + */ + Memory_Failure, + + /* During a page fault, the memory object indicated + * that the data could not be returned. This failure + * may be temporary; future attempts to access this + * same data may succeed, as defined by the memory + * object. + */ + Memory_Error, + + /* The receive right is already a member of the portset. + */ + Already_In_Set, + + /* The receive right is not a member of a port set. + */ + Not_In_Set, + + /* The name already denotes a right in the task. + */ + Name_Exists, + + /* The operation was aborted. Ipc code will + * catch this and reflect it as a message error. + */ + Aborted, + + /* The name doesn't denote a right in the task. + */ + Invalid_Name, + + /* Target task isn't an active task. + */ + Invalid_Task, + + /* The name denotes a right, but not an appropriate right. + */ + Invalid_Right, + + /* A blatant range error. + */ + Invalid_Value, + + /* Operation would overflow limit on user-references. + */ + URefs_Overflow, + + /* The supplied (port) capability is improper. + */ + Invalid_Capability, + + /* The task already has send or receive rights + * for the port under another name. + */ + Right_Exists, + + /* Target host isn't actually a host. + */ + Invalid_Host, + + /* An attempt was made to supply "precious" data + * for memory that is already present in a + * memory object. + */ + Memory_Present, + + /* A page was requested of a memory manager via + * memory_object_data_request for an object using + * a MEMORY_OBJECT_COPY_CALL strategy, with the + * VM_PROT_WANTS_COPY flag being used to specify + * that the page desired is for a copy of the + * object, and the memory manager has detected + * the page was pushed into a copy of the object + * while the kernel was walking the shadow chain + * from the copy to the object. This error code + * is delivered via memory_object_data_error + * and is handled by the kernel (it forces the + * kernel to restart the fault). It will not be + * seen by users. + */ + Memory_Data_Moved, + + /* A strategic copy was attempted of an object + * upon which a quicker copy is now possible. + * The caller should retry the copy using + * vm_object_copy_quickly. This error code + * is seen only by the kernel. + */ + Memory_Restart_Copy, + + /* An argument applied to assert processor set privilege + * was not a processor set control port. + */ + Invalid_Processor_Set, + + /* The specified scheduling attributes exceed the thread's + * limits. + */ + Policy_Limit, + + /* The specified scheduling policy is not currently + * enabled for the processor set. + */ + Invalid_Policy, + + /* The external memory manager failed to initialize the + * memory object. + */ + Invalid_Object, + + /* A thread is attempting to wait for an event for which + * there is already a waiting thread. + */ + Already_Waiting, + + /* An attempt was made to destroy the default processor + * set. + */ + Default_Set, + + /* An attempt was made to fetch an exception port that is + * protected, or to abort a thread while processing a + * protected exception. + */ + Exception_Protected, + + /* A ledger was required but not supplied. + */ + Invalid_Ledger, + + /* The port was not a memory cache control port. + */ + Invalid_Memory_Control, + + /* An argument supplied to assert security privilege + * was not a host security port. + */ + Invalid_Security, + + /* thread_depress_abort was called on a thread which + * was not currently depressed. + */ + Not_Depressed, + + /* Object has been terminated and is no longer available + */ + Terminated, + + /* Lock set has been destroyed and is no longer available. + */ + Lock_Set_Destroyed, + + /* The thread holding the lock terminated before releasing + * the lock + */ + Lock_Unstable, + + /* The lock is already owned by another thread + */ + Lock_Owned, + + /* The lock is already owned by the calling thread + */ + Lock_Owned_Self, + + /* Semaphore has been destroyed and is no longer available. + */ + Semaphore_Destroyed, + + /* Return from RPC indicating the target server was + * terminated before it successfully replied + */ + Rpc_Server_Terminated, + + /* Terminate an orphaned activation. + */ + RPC_Terminate_Orphan, + + /* Allow an orphaned activation to continue executing. + */ + RPC_Continue_Orphan, + + /* Empty thread activation (No thread linked to it) + */ + Not_Supported, + + /* Remote node down or inaccessible. + */ + Node_Down, + + /* A signalled thread was not actually waiting. */ + Not_Waiting, + + /* Some thread-oriented operation (semaphore_wait) timed out + */ + Operation_Timed_Out, + + /* During a page fault, indicates that the page was rejected + * as a result of a signature check. + */ + Codesign_Error, + + /* The requested property cannot be changed at this time. + */ + Policy_Static, + + /* The provided buffer is of insufficient size for the requested data. + */ + Insufficient_Buffer_Size, + + /* Denied by security policy + */ + Denied, + + /* The KC on which the function is operating is missing + */ + Missing_KC, + + /* The KC on which the function is operating is invalid + */ + Invalid_KC, + + /* A search or query operation did not return a result + */ + Not_Found, + + /* Maximum return value allowable + */ + Return_Max = 0x100, +} + +/* + * VM allocation flags: + * + * VM_FLAGS_FIXED + * (really the absence of VM_FLAGS_ANYWHERE) + * Allocate new VM region at the specified virtual address, if possible. + * + * VM_FLAGS_ANYWHERE + * Allocate new VM region anywhere it would fit in the address space. + * + * VM_FLAGS_PURGABLE + * Create a purgable VM object for that new VM region. + * + * VM_FLAGS_4GB_CHUNK + * The new VM region will be chunked up into 4GB sized pieces. + * + * VM_FLAGS_NO_PMAP_CHECK + * (for DEBUG kernel config only, ignored for other configs) + * Do not check that there is no stale pmap mapping for the new VM region. + * This is useful for kernel memory allocations at bootstrap when building + * the initial kernel address space while some memory is already in use. + * + * VM_FLAGS_OVERWRITE + * The new VM region can replace existing VM regions if necessary + * (to be used in combination with VM_FLAGS_FIXED). + * + * VM_FLAGS_NO_CACHE + * Pages brought in to this VM region are placed on the speculative + * queue instead of the active queue. In other words, they are not + * cached so that they will be stolen first if memory runs low. + */ + +@(private="file") +LOG2 :: intrinsics.constant_log2 + +VM_Flag :: enum c.int { + Anywhere, + Purgable, + _4GB_Chunk, + Random_Addr, + No_Cache, + Resilient_Codesign, + Resilient_Media, + Permanent, + + // NOTE(beau): log 2 of the bit we want in the bit set so we get that bit in + // the bit set + + TPRO = LOG2(0x1000), + Overwrite = LOG2(0x4000),/* delete any existing mappings first */ + + Superpage_Size_Any = LOG2(0x10000), + Superpage_Size_2MB = LOG2(0x20000), + __Superpage3 = LOG2(0x40000), + + Return_Data_Addr = LOG2(0x100000), + Return_4K_Data_Addr = LOG2(0x800000), + + Alias_Mask1 = 24, + Alias_Mask2, + Alias_Mask3, + Alias_Mask4, + Alias_Mask5, + Alias_Mask6, + Alias_Mask7, + Alias_Mask8, + + HW = TPRO, +} + +VM_Flags :: distinct bit_set[VM_Flag; c.int] +VM_FLAGS_FIXED :: VM_Flags{} + +/* + * VM_FLAGS_SUPERPAGE_MASK + * 3 bits that specify whether large pages should be used instead of + * base pages (!=0), as well as the requested page size. + */ +VM_FLAGS_SUPERPAGE_MASK :: VM_Flags { + .Superpage_Size_Any, + .Superpage_Size_2MB, + .__Superpage3, +} + +// 0xFF000000 +VM_FLAGS_ALIAS_MASK :: VM_Flags { + .Alias_Mask1, + .Alias_Mask2, + .Alias_Mask3, + .Alias_Mask4, + .Alias_Mask5, + .Alias_Mask6, + .Alias_Mask7, + .Alias_Mask8, +} + +VM_GET_FLAGS_ALIAS :: proc(flags: VM_Flags) -> c.int { + return transmute(c.int)(flags & VM_FLAGS_ALIAS_MASK) >> 24 +} +// NOTE(beau): no need for VM_SET_FLAGS_ALIAS, just mask in things from +// VM_Flag.Alias_Mask* + +/* These are the flags that we accept from user-space */ +VM_FLAGS_USER_ALLOCATE :: VM_Flags { + .Anywhere, + .Purgable, + ._4GB_Chunk, + .Random_Addr, + .No_Cache, + .Permanent, + .Overwrite, +} | VM_FLAGS_FIXED | VM_FLAGS_SUPERPAGE_MASK | VM_FLAGS_ALIAS_MASK + +VM_FLAGS_USER_MAP :: VM_Flags { + .Return_4K_Data_Addr, + .Return_Data_Addr, +} | VM_FLAGS_USER_ALLOCATE + +VM_FLAGS_USER_REMAP :: VM_Flags { + .Anywhere, + .Random_Addr, + .Overwrite, + .Return_Data_Addr, + .Resilient_Codesign, + .Resilient_Media, +} | VM_FLAGS_FIXED + +VM_FLAGS_SUPERPAGE_NONE :: VM_Flags{} /* no superpages, if all bits are 0 */ + +/* + * Protection values, defined as bits within the vm_prot_t type + */ + +VM_Prot :: enum vm_prot_t { + Read, + Write, + Execute, +} + +VM_Prot_Flags :: distinct bit_set[VM_Prot; vm_prot_t] + +VM_PROT_NONE :: VM_Prot_Flags{} +VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write} +VM_PROT_ALL :: VM_Prot_Flags{.Read, .Write, .Execute} + +/* + * Enumeration of valid values for vm_inherit_t. + */ + +VM_Inherit :: enum vm_inherit_t { + Share, + Copy, + None, + Donate_Copy, + + Default = Copy, + Last_Valid = None, +} + +Sync_Policy :: enum sync_policy_t { + Fifo, + Fixed_Priority, + Reversed, + Order_Mask, - semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t --- - semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t --- - semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t --- - - semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t --- + Lifo = Fifo | Reversed, } mach_vm_trunc_page :: proc(v: u64) -> u64 { diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin index d6fa98507..95b53dda0 100644 --- a/core/sys/info/cpu_intel.odin +++ b/core/sys/info/cpu_intel.odin @@ -28,6 +28,23 @@ CPU_Feature :: enum u64 { ssse3, // Supplemental streaming SIMD extension 3 sse41, // Streaming SIMD extension 4 and 4.1 sse42, // Streaming SIMD extension 4 and 4.2 + + avx512bf16, // Vector Neural Network Instructions supporting bfloat16 + avx512bitalg, // Bit Algorithms + avx512bw, // Byte and Word instructions + avx512cd, // Conflict Detection instructions + avx512dq, // Doubleword and Quadword instructions + avx512er, // Exponential and Reciprocal instructions + avx512f, // Foundation + avx512fp16, // Vector 16-bit float instructions + avx512ifma, // Integer Fused Multiply Add + avx512pf, // Prefetch instructions + avx512vbmi, // Vector Byte Manipulation Instructions + avx512vbmi2, // Vector Byte Manipulation Instructions 2 + avx512vl, // Vector Length extensions + avx512vnni, // Vector Neural Network Instructions + avx512vp2intersect, // Vector Pair Intersection to a Pair of Mask Registers + avx512vpopcntdq, // Vector Population Count for Doubleword and Quadword } CPU_Features :: distinct bit_set[CPU_Feature; u64] @@ -82,9 +99,11 @@ init_cpu_features :: proc "c" () { // // See: crbug.com/375968 os_supports_avx := false + os_supports_avx512 := false if .os_xsave in set && is_set(26, ecx1) { eax, _ := xgetbv(0) os_supports_avx = is_set(1, eax) && is_set(2, eax) + os_supports_avx512 = is_set(5, eax) && is_set(6, eax) && is_set(7, eax) } if os_supports_avx { try_set(&set, .avx, 28, ecx1) @@ -94,11 +113,37 @@ init_cpu_features :: proc "c" () { return } - _, ebx7, _, _ := cpuid(7, 0) + _, ebx7, ecx7, edx7 := cpuid(7, 0) try_set(&set, .bmi1, 3, ebx7) if os_supports_avx { try_set(&set, .avx2, 5, ebx7) } + if os_supports_avx512 { + try_set(&set, .avx512f, 16, ebx7) + try_set(&set, .avx512dq, 17, ebx7) + try_set(&set, .avx512ifma, 21, ebx7) + try_set(&set, .avx512pf, 26, ebx7) + try_set(&set, .avx512er, 27, ebx7) + try_set(&set, .avx512cd, 28, ebx7) + try_set(&set, .avx512bw, 30, ebx7) + + // XMM/YMM are also required for 128/256-bit instructions + if os_supports_avx { + try_set(&set, .avx512vl, 31, ebx7) + } + + try_set(&set, .avx512vbmi, 1, ecx7) + try_set(&set, .avx512vbmi2, 6, ecx7) + try_set(&set, .avx512vnni, 11, ecx7) + try_set(&set, .avx512bitalg, 12, ecx7) + try_set(&set, .avx512vpopcntdq, 14, ecx7) + + try_set(&set, .avx512vp2intersect, 8, edx7) + try_set(&set, .avx512fp16, 23, edx7) + + eax7_1, _, _, _ := cpuid(7, 1) + try_set(&set, .avx512bf16, 5, eax7_1) + } try_set(&set, .bmi2, 8, ebx7) try_set(&set, .erms, 9, ebx7) try_set(&set, .rdseed, 18, ebx7) diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index 493f038f0..97a2199ab 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -494,6 +494,10 @@ macos_release_map: map[string]Darwin_To_Release = { "21G816" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}}, "21G920" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}}, "21G1974" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}}, + "21H1015" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 3}}}, + "21H1123" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 4}}}, + "21H1222" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 5}}}, + "21H1320" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 6}}}, // MacOS Ventura "22A380" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 0}}}, @@ -513,6 +517,15 @@ macos_release_map: map[string]Darwin_To_Release = { "22G120" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 0}}}, "22G313" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 1}}}, "22G320" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 2}}}, + "22G436" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 3}}}, + "22G513" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 4}}}, + "22G621" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 5}}}, + "22G630" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 6}}}, + "22G720" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 7}}}, + "22G820" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 8}}}, + "22G830" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 9}}}, + "22H123" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 0}}}, + "22H221" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 1}}}, // MacOS Sonoma "23A344" = {{23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}}, @@ -531,9 +544,12 @@ macos_release_map: map[string]Darwin_To_Release = { "23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}}, "23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}}, "23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}}, + "23H222" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 1}}}, // MacOS Sequoia "24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}}, + "24A348" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}}, + "24B83" = {{24, 1, 0}, "macOS", {"Sequoia", {15, 1, 0}}}, } @(private) diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin index 8a4a6dd7a..9ce2e206e 100644 --- a/core/sys/linux/bits.odin +++ b/core/sys/linux/bits.odin @@ -152,66 +152,43 @@ Errno :: enum i32 { RDONLY flag is not present, because it has the value of 0, i.e. it is the default, unless WRONLY or RDWR is specified. */ -when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 { - Open_Flags_Bits :: enum { - WRONLY = 0, - RDWR = 1, - CREAT = 6, - EXCL = 7, - NOCTTY = 8, - TRUNC = 9, - APPEND = 10, - NONBLOCK = 11, - DSYNC = 12, - ASYNC = 13, - DIRECT = 14, - LARGEFILE = 15, - DIRECTORY = 16, - NOFOLLOW = 17, - NOATIME = 18, - CLOEXEC = 19, - PATH = 21, - } - // https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19 - #assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1) - #assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2) - #assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100) - #assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200) - #assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400) - #assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000) - #assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000) - #assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000) - #assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000) - #assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000) - #assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000) - #assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000) - #assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000) - #assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000) - #assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000) - #assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000) - #assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000) - -} else { - Open_Flags_Bits :: enum { - WRONLY = 0, - RDWR = 1, - CREAT = 6, - EXCL = 7, - NOCTTY = 8, - TRUNC = 9, - APPEND = 10, - NONBLOCK = 11, - DSYNC = 12, - ASYNC = 13, - DIRECTORY = 14, - NOFOLLOW = 15, - DIRECT = 16, - LARGEFILE = 17, - NOATIME = 18, - CLOEXEC = 19, - PATH = 21, - } -} +Open_Flags_Bits :: enum { + WRONLY = 0, + RDWR = 1, + CREAT = 6, + EXCL = 7, + NOCTTY = 8, + TRUNC = 9, + APPEND = 10, + NONBLOCK = 11, + DSYNC = 12, + ASYNC = 13, + DIRECT = 14, + LARGEFILE = 15, + DIRECTORY = 16, + NOFOLLOW = 17, + NOATIME = 18, + CLOEXEC = 19, + PATH = 21, +} +// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19 +#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1) +#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2) +#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100) +#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200) +#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400) +#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000) +#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000) +#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000) +#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000) +#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000) +#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000) +#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000) +#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000) +#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000) +#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000) +#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000) +#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000) /* Bits for FD_Flags bitset @@ -542,6 +519,36 @@ Fd_Poll_Events_Bits :: enum { RDHUP = 13, } +Inotify_Init_Bits :: enum { + NONBLOCK = 11, + CLOEXEC = 19, +} + +Inotify_Event_Bits :: enum u32 { + ACCESS = 0, + MODIFY = 1, + ATTRIB = 2, + CLOSE_WRITE = 3, + CLOSE_NOWRITE = 4, + OPEN = 5, + MOVED_FROM = 6, + MOVED_TO = 7, + CREATE = 8, + DELETE = 9, + DELETE_SELF = 10, + MOVE_SELF = 11, + UNMOUNT = 13, + Q_OVERFLOW = 14, + IGNORED = 15, + ONLYDIR = 24, + DONT_FOLLOW = 25, + EXCL_UNLINK = 26, + MASK_CREATE = 28, + MASK_ADD = 29, + ISDIR = 30, + ONESHOT = 31, +} + /* Bits for Mem_Protection bitfield */ diff --git a/core/sys/linux/constants.odin b/core/sys/linux/constants.odin index 129444d0f..b3bbcafb3 100644 --- a/core/sys/linux/constants.odin +++ b/core/sys/linux/constants.odin @@ -135,6 +135,31 @@ STATX_BASIC_STATS :: Statx_Mask { .BLOCKS, } +IN_ALL_EVENTS :: Inotify_Event_Mask { + .ACCESS, + .MODIFY, + .ATTRIB, + .CLOSE_WRITE, + .CLOSE_NOWRITE, + .OPEN, + .MOVED_FROM, + .MOVED_TO, + .CREATE, + .DELETE, + .DELETE_SELF, + .MOVE_SELF, +} + +IN_CLOSE :: Inotify_Event_Mask { + .CLOSE_WRITE, + .CLOSE_NOWRITE, +} + +IN_MOVE :: Inotify_Event_Mask { + .MOVED_FROM, + .MOVED_TO, +} + /* Tell `shmget` to create a new key */ diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index c5894d78b..690902f07 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -2536,11 +2536,30 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt // TODO(flysand): ioprio_get -// TODO(flysand): inotify_init +inotify_init :: proc "contextless" () -> (Fd, Errno) { + when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 { + ret := syscall(SYS_inotify_init1, 0) + return errno_unwrap(ret, Fd) + } else { + ret := syscall(SYS_inotify_init) + return errno_unwrap(ret, Fd) + } +} -// TODO(flysand): inotify_add_watch +inotify_init1 :: proc "contextless" (flags: Inotify_Init_Flags) -> (Fd, Errno) { + ret := syscall(SYS_inotify_init1, transmute(i32)flags) + return errno_unwrap(ret, Fd) +} + +inotify_add_watch :: proc "contextless" (fd: Fd, pathname: cstring, mask: Inotify_Event_Mask) -> (Wd, Errno) { + ret := syscall(SYS_inotify_add_watch, fd, transmute(uintptr) pathname, transmute(u32) mask) + return errno_unwrap(ret, Wd) +} -// TODO(flysand): inotify_rm_watch +inotify_rm_watch :: proc "contextless" (fd: Fd, wd: Wd) -> (Errno) { + ret := syscall(SYS_inotify_rm_watch, fd, wd) + return Errno(-ret) +} // TODO(flysand): migrate_pages diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 0e5b8218b..42d5cc988 100644 --- a/core/sys/linux/types.odin +++ b/core/sys/linux/types.odin @@ -31,6 +31,11 @@ Id :: distinct uint Fd :: distinct i32 /* + Represents a watch descriptor. +*/ +Wd :: distinct i32 + +/* Type for PID file descriptors. */ Pid_FD :: distinct i32 @@ -343,6 +348,18 @@ Poll_Fd :: struct { revents: Fd_Poll_Events, } +Inotify_Init_Flags :: bit_set[Inotify_Init_Bits; i32] + +Inotify_Event :: struct { + wd: Wd, + mask: Inotify_Event_Mask, + cookie: u32, + len: u32, + name: [0]u8, +} + +Inotify_Event_Mask :: bit_set[Inotify_Event_Bits; u32] + /* Specifies protection for memory pages. */ @@ -1136,6 +1153,12 @@ when ODIN_ARCH == .arm32 { eflags: uint, rsp: uint, ss: uint, + fs_base: uint, + gs_base: uint, + ds: uint, + es: uint, + fs: uint, + gs: uint, } // All floating point state _Arch_User_FP_Regs :: struct { 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 bbb5416c5..bf32be8cf 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 ]] */ @@ -54,15 +55,6 @@ foreign lib { closedir :: proc(dirp: DIR) -> result --- /* - Return a file descriptor referring to the same directory as the dirp argument. - - // TODO: this is a macro on NetBSD? - - [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]] - */ - dirfd :: proc(dirp: DIR) -> FD --- - - /* Equivalent to the opendir() function except that the directory is specified by a file descriptor rather than by a name. The file offset associated with the file descriptor at the time of the call determines @@ -89,16 +81,16 @@ foreign lib { Returns nil when the end is reached or an error occurred (which sets errno). Example: - posix.set_errno(.NONE) - entry := posix.readdir(dirp) - if entry == nil { - if errno := posix.errno(); errno != .NONE { - panic(string(posix.strerror(errno))) - } else { - fmt.println("end of directory stream") - } - } else { - fmt.println(entry) + posix.set_errno(.NONE) + entry := posix.readdir(dirp) + if entry == nil { + if errno := posix.errno(); errno != .NONE { + panic(string(posix.strerror(errno))) + } else { + fmt.println("end of directory stream") + } + } else { + fmt.println(entry) } [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]] @@ -160,11 +152,36 @@ when ODIN_OS == .NetBSD { @(private) LSCANDIR :: "__scandir30" @(private) LOPENDIR :: "__opendir30" @(private) LREADDIR :: "__readdir30" + + /* + Return a file descriptor referring to the same directory as the dirp argument. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]] + */ + dirfd :: proc "c" (dirp: DIR) -> FD { + _dirdesc :: struct { + dd_fd: FD, + + // more stuff... + } + + return (^_dirdesc)(dirp).dd_fd + } + } else { @(private) LALPHASORT :: "alphasort" + INODE_SUFFIX @(private) LSCANDIR :: "scandir" + INODE_SUFFIX @(private) LOPENDIR :: "opendir" + INODE_SUFFIX @(private) LREADDIR :: "readdir" + INODE_SUFFIX + + foreign lib { + /* + Return a file descriptor referring to the same directory as the dirp argument. + + [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]] + */ + dirfd :: proc(dirp: DIR) -> FD --- + } } when ODIN_OS == .Darwin { @@ -193,13 +210,21 @@ when ODIN_OS == .Darwin { } else when ODIN_OS == .NetBSD { dirent :: struct { - d_ino: ino_t, /* [PSX] file number of entry */ - d_reclen: c.uint16_t, /* length of this record */ - d_namelen: c.uint16_t, /* length of string in d_name */ - d_type: D_Type, /* file type */ - d_name: [512]c.char `fmt:"s,0"`, /* [PSX] entry name */ + d_ino: ino_t, /* [PSX] file number of entry */ + d_reclen: c.uint16_t, /* length of this record */ + d_namelen: c.uint16_t, /* length of string in d_name */ + d_type: D_Type, /* file type */ + d_name: [512]c.char `fmt:"s,0"`, /* [PSX] entry name */ } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + dirent :: struct { + d_ino: u64, /* [PSX] file number of entry */ + d_off: i64, /* directory offset of the next entry */ + d_reclen: u16, /* length of this record */ + d_type: D_Type, /* file type */ + d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */ + } + } diff --git a/core/sys/posix/dlfcn.odin b/core/sys/posix/dlfcn.odin index 0ddc41337..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" @@ -111,7 +112,14 @@ when ODIN_OS == .Darwin { RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))} -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + RTLD_LAZY :: 0x001 + RTLD_NOW :: 0x002 + RTLD_GLOBAL :: 0x100 + + _RTLD_LOCAL :: 0 + RTLD_LOCAL :: RTLD_Flags{} + } diff --git a/core/sys/posix/errno.odin b/core/sys/posix/errno.odin index 4ef10aadf..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" @@ -141,7 +142,7 @@ when ODIN_OS == .Darwin { EMLINK :: 31 EPIPE :: 32 EAGAIN :: 35 - EWOULDBLOCK :: 35 + EWOULDBLOCK :: EAGAIN EINPROGRESS :: 36 EALREADY :: 37 ENOTSOCK :: 38 @@ -151,7 +152,7 @@ when ODIN_OS == .Darwin { ENOPROTOOPT :: 42 EPROTONOSUPPORT :: 43 ENOTSUP :: 45 - EOPNOTSUPP :: 45 + EOPNOTSUPP :: ENOTSUP EAFNOSUPPORT :: 47 EADDRINUSE :: 48 EADDRNOTAVAIL :: 49 @@ -220,7 +221,7 @@ when ODIN_OS == .Darwin { EMLINK :: 31 EPIPE :: 32 EAGAIN :: 35 - EWOULDBLOCK :: 35 + EWOULDBLOCK :: EAGAIN EINPROGRESS :: 36 EALREADY :: 37 ENOTSOCK :: 38 @@ -230,7 +231,7 @@ when ODIN_OS == .Darwin { ENOPROTOOPT :: 42 EPROTONOSUPPORT :: 43 ENOTSUP :: 45 - EOPNOTSUPP :: 45 + EOPNOTSUPP :: ENOTSUP EAFNOSUPPORT :: 47 EADDRINUSE :: 48 EADDRNOTAVAIL :: 49 @@ -301,7 +302,7 @@ when ODIN_OS == .Darwin { EMLINK :: 31 EPIPE :: 32 EAGAIN :: 35 - EWOULDBLOCK :: 35 + EWOULDBLOCK :: EAGAIN EINPROGRESS :: 36 EALREADY :: 37 ENOTSOCK :: 38 @@ -311,7 +312,7 @@ when ODIN_OS == .Darwin { ENOPROTOOPT :: 42 EPROTONOSUPPORT :: 43 ENOTSUP :: 45 - EOPNOTSUPP :: 45 + EOPNOTSUPP :: ENOTSUP EAFNOSUPPORT :: 47 EADDRINUSE :: 48 EADDRNOTAVAIL :: 49 @@ -367,7 +368,173 @@ when ODIN_OS == .Darwin { ETIME :: -1 } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + EPERM :: 1 + ENOENT :: 2 + ESRCH :: 3 + EINTR :: 4 + EIO :: 5 + ENXIO :: 6 + E2BIG :: 7 + ENOEXEC :: 8 + EBADF :: 9 + ECHILD :: 10 + EAGAIN :: 11 + EWOULDBLOCK :: EAGAIN + ENOMEM :: 12 + EACCES :: 13 + EFAULT :: 14 + EBUSY :: 16 + EEXIST :: 17 + EXDEV :: 18 + ENODEV :: 19 + ENOTDIR :: 20 + EISDIR :: 21 + EINVAL :: 22 + ENFILE :: 23 + EMFILE :: 24 + ENOTTY :: 25 + ETXTBSY :: 26 + EFBIG :: 27 + ENOSPC :: 28 + ESPIPE :: 29 + EROFS :: 30 + EMLINK :: 31 + EPIPE :: 32 + + EDEADLK :: 35 + ENAMETOOLONG :: 36 + ENOLCK :: 37 + ENOSYS :: 38 + ENOTEMPTY :: 39 + ELOOP :: 40 + ENOMSG :: 42 + EIDRM :: 43 + + ENOSTR :: 60 + ENODATA :: 61 + ETIME :: 62 + ENOSR :: 63 + + ENOLINK :: 67 + + EPROTO :: 71 + EMULTIHOP :: 72 + EBADMSG :: 74 + EOVERFLOW :: 75 + + ENOTSOCK :: 88 + EDESTADDRREQ :: 89 + EMSGSIZE :: 90 + EPROTOTYPE :: 91 + ENOPROTOOPT :: 92 + EPROTONOSUPPORT :: 93 + + EOPNOTSUPP :: 95 + ENOTSUP :: EOPNOTSUPP + EAFNOSUPPORT :: 97 + EADDRINUSE :: 98 + EADDRNOTAVAIL :: 99 + ENETDOWN :: 100 + ENETUNREACH :: 101 + ENETRESET :: 102 + ECONNABORTED :: 103 + ECONNRESET :: 104 + ENOBUFS :: 105 + EISCONN :: 106 + ENOTCONN :: 107 + + ETIMEDOUT :: 110 + ECONNREFUSED :: 111 + + EHOSTUNREACH :: 113 + EALREADY :: 114 + EINPROGRESS :: 115 + ESTALE :: 116 + + EDQUOT :: 122 + ECANCELED :: 125 + + EOWNERDEAD :: 130 + ENOTRECOVERABLE :: 131 +} else when ODIN_OS == .Windows { + E2BIG :: 7 + EACCES :: 13 + EADDRINUSE :: 100 + EADDRNOTAVAIL :: 101 + EAFNOSUPPORT :: 102 + EAGAIN :: 11 + EALREADY :: 103 + EBADF :: 9 + EBADMSG :: 104 + EBUSY :: 16 + ECANCELED :: 105 + ECHILD :: 10 + ECONNABORTED :: 106 + ECONNREFUSED :: 107 + ECONNRESET :: 108 + EDEADLK :: 36 + EDESTADDRREQ :: 109 + EDQUOT :: -1 // NOTE: not defined + EEXIST :: 17 + EFAULT :: 14 + EFBIG :: 27 + EHOSTUNREACH :: 110 + EIDRM :: 111 + EINPROGRESS :: 112 + EINTR :: 4 + EINVAL :: 22 + EIO :: 5 + EISCONN :: 113 + EISDIR :: 21 + ELOOP :: 114 + EMFILE :: 24 + EMLINK :: 31 + EMSGSIZE :: 115 + EMULTIHOP :: -1 // NOTE: not defined + ENAMETOOLONG :: 38 + ENETDOWN :: 116 + ENETRESET :: 117 + ENETUNREACH :: 118 + ENFILE :: 23 + ENOBUFS :: 119 + ENODATA :: 120 + ENODEV :: 19 + ENOENT :: 2 + ENOEXEC :: 8 + ENOLCK :: 39 + ENOLINK :: 121 + ENOMEM :: 12 + ENOMSG :: 122 + ENOPROTOOPT :: 123 + ENOSPC :: 28 + ENOSR :: 124 + ENOSTR :: 125 + ENOSYS :: 40 + ENOTCONN :: 126 + ENOTDIR :: 20 + ENOTEMPTY :: 41 + ENOTRECOVERABLE :: 127 + ENOTSOCK :: 128 + ENOTSUP :: 129 + ENOTTY :: 25 + ENXIO :: 6 + EOPNOTSUPP :: 130 + EOVERFLOW :: 132 + EOWNERDEAD :: 133 + EPERM :: 1 + EPIPE :: 32 + EPROTO :: 134 + EPROTONOSUPPORT :: 135 + EPROTOTYPE :: 136 + EROFS :: 30 + ESPIPE :: 29 + ESRCH :: 3 + ESTALE :: -1 // NOTE: not defined + ETIME :: 137 + ETIMEDOUT :: 138 + ETXTBSY :: 139 + EWOULDBLOCK :: 140 + EXDEV :: 18 } diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin index ca030a9a5..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" @@ -92,9 +93,6 @@ Lock_Type :: enum c.short { WRLCK = F_WRLCK, } -// Assertions made to unify this bit set. -#assert(O_RDONLY == 0) - O_Flag_Bits :: enum c.int { // Sets FD_CLOEXEC on the file descriptor. CLOEXEC = log2(O_CLOEXEC), @@ -107,11 +105,11 @@ O_Flag_Bits :: enum c.int { // If terminal device, do not make it the controlling terminal for the process. NOCTTY = log2(O_NOCTTY), // Don't follow symbolic links, fail with errno ELOOP. - NOFOLLOW = log2(O_NOFOLOW), + NOFOLLOW = log2(O_NOFOLLOW), // If exists and regular, truncate the length to 0. TRUNC = log2(O_TRUNC), - // NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in + // NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in // this bit set enum because it is 0 on some platforms and a value on others. // TTY_INIT = O_TTY_INIT, @@ -123,7 +121,8 @@ O_Flag_Bits :: enum c.int { NONBLOCK = log2(O_NONBLOCK), // Write I/O shall complete as defined by synchronized I/O file integrity completion. SYNC = log2(O_SYNC), - // NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in + + // NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in // this bit set enum because it is 0 on some platforms and a value on others. // RSYNC = O_RSYNC, @@ -135,11 +134,10 @@ O_Flag_Bits :: enum c.int { WRONLY = log2(O_WRONLY), // Reading only. // RDONLY = 0, // Default - } + O_Flags :: bit_set[O_Flag_Bits; c.int] -// A mask of all the access mode bits. O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY } AT_Flag_Bits :: enum c.int { @@ -152,8 +150,8 @@ AT_Flags :: bit_set[AT_Flag_Bits; c.int] when ODIN_OS == .Darwin { - off_t :: distinct c.int64_t - pid_t :: distinct c.int32_t + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t F_DUPFD :: 0 F_DUPFD_CLOEXEC :: 67 @@ -178,7 +176,7 @@ when ODIN_OS == .Darwin { O_DIRECTORY :: 0x00100000 O_EXCL :: 0x00000800 O_NOCTTY :: 0x00020000 - O_NOFOLOW :: 0x00000100 + O_NOFOLLOW :: 0x00000100 O_TRUNC :: 0x00000400 _O_TTY_INIT :: 0 @@ -189,16 +187,16 @@ when ODIN_OS == .Darwin { O_NONBLOCK :: 0x00000004 O_SYNC :: 0x0080 - _O_RSYNC :: 0 - O_RSYNC :: O_Flags{} + _O_RSYNC :: 0 + O_RSYNC :: O_Flags{} - O_EXEC :: 0x40000000 - O_RDONLY :: 0 - O_RDWR :: 0x0002 - O_WRONLY :: 0x0001 + O_EXEC :: 0x40000000 + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 _O_SEARCH :: O_EXEC | O_DIRECTORY - O_SEARCH :: O_Flags{ .EXEC, .DIRECTORY } + O_SEARCH :: O_Flags{.EXEC, .DIRECTORY} AT_FDCWD: FD: -2 @@ -217,8 +215,8 @@ when ODIN_OS == .Darwin { } else when ODIN_OS == .FreeBSD { - off_t :: distinct c.int64_t - pid_t :: distinct c.int32_t + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t F_DUPFD :: 0 F_DUPFD_CLOEXEC :: 17 @@ -243,7 +241,7 @@ when ODIN_OS == .Darwin { O_DIRECTORY :: 0x00020000 O_EXCL :: 0x0800 O_NOCTTY :: 0x8000 - O_NOFOLOW :: 0x0100 + O_NOFOLLOW :: 0x0100 O_TRUNC :: 0x0400 _O_TTY_INIT :: 0x00080000 @@ -256,10 +254,10 @@ when ODIN_OS == .Darwin { _O_RSYNC :: 0 O_RSYNC :: O_Flags{} // NOTE: not defined in headers - O_EXEC :: 0x00040000 - O_RDONLY :: 0 - O_RDWR :: 0x0002 - O_WRONLY :: 0x0001 + O_EXEC :: 0x00040000 + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 _O_SEARCH :: O_EXEC O_SEARCH :: O_Flags{ .EXEC } @@ -282,8 +280,8 @@ when ODIN_OS == .Darwin { } else when ODIN_OS == .NetBSD { - off_t :: distinct c.int64_t - pid_t :: distinct c.int32_t + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t F_DUPFD :: 0 F_DUPFD_CLOEXEC :: 12 @@ -308,7 +306,7 @@ when ODIN_OS == .Darwin { O_DIRECTORY :: 0x0020000 O_EXCL :: 0x0800 O_NOCTTY :: 0x8000 - O_NOFOLOW :: 0x0100 + O_NOFOLLOW :: 0x0100 O_TRUNC :: 0x0400 _O_TTY_INIT :: 0 @@ -319,14 +317,14 @@ when ODIN_OS == .Darwin { O_NONBLOCK :: 0x0004 O_SYNC :: 0x0080 - _O_RSYNC :: 0x0002 - O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))} + _O_RSYNC :: 0x0002 + O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))} - O_EXEC :: 0x04000000 - O_RDONLY :: 0 - O_RDWR :: 0x0002 - O_WRONLY :: 0x0001 + O_EXEC :: 0x04000000 + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 _O_SEARCH :: 0x00800000 O_SEARCH :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))} @@ -347,8 +345,8 @@ when ODIN_OS == .Darwin { } } else when ODIN_OS == .OpenBSD { - off_t :: distinct c.int64_t - pid_t :: distinct c.int32_t + off_t :: distinct c.int64_t + pid_t :: distinct c.int32_t F_DUPFD :: 0 F_DUPFD_CLOEXEC :: 10 @@ -373,7 +371,7 @@ when ODIN_OS == .Darwin { O_DIRECTORY :: 0x20000 O_EXCL :: 0x0800 O_NOCTTY :: 0x8000 - O_NOFOLOW :: 0x0100 + O_NOFOLLOW :: 0x0100 O_TRUNC :: 0x0400 _O_TTY_INIT :: 0 @@ -384,13 +382,13 @@ when ODIN_OS == .Darwin { O_NONBLOCK :: 0x0004 O_SYNC :: 0x0080 - _O_RSYNC :: O_SYNC - O_RSYNC :: O_Flags{ .SYNC } + _O_RSYNC :: O_SYNC + O_RSYNC :: O_Flags{.SYNC} - O_EXEC :: 0x04000000 // NOTE: not defined in the headers - O_RDONLY :: 0 - O_RDWR :: 0x0002 - O_WRONLY :: 0x0001 + O_EXEC :: 0x04000000 // NOTE: not defined in the headers + O_RDONLY :: 0 + O_RDWR :: 0x0002 + O_WRONLY :: 0x0001 _O_SEARCH :: 0 O_SEARCH :: O_Flags{} // NOTE: not defined in the headers @@ -410,6 +408,70 @@ when ODIN_OS == .Darwin { l_whence: c.short, /* [PSX] flag (Whence) of starting offset */ } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + off_t :: distinct c.int64_t + pid_t :: distinct c.int + + F_DUPFD :: 0 + F_GETFD :: 1 + F_SETFD :: 2 + F_GETFL :: 3 + F_SETFL :: 4 + F_GETLK :: 5 + F_SETLK :: 6 + F_SETLKW :: 7 + F_SETOWN :: 8 + F_GETOWN :: 9 + F_RDLCK :: 0 + F_UNLCK :: 2 + F_WRLCK :: 1 + + F_DUPFD_CLOEXEC :: 1030 + + FD_CLOEXEC :: 1 + + O_CREAT :: 0o0_000_100 + O_EXCL :: 0o0_000_200 + O_NOCTTY :: 0o0_000_400 + O_TRUNC :: 0o0_001_000 + O_DIRECTORY :: 0o0_200_000 + O_NOFOLLOW :: 0o0_400_000 + O_CLOEXEC :: 0o2_000_000 + + _O_TTY_INIT :: 0 + O_TTY_INIT :: O_Flags{} + + O_APPEND :: 0o0_002_000 + O_NONBLOCK :: 0o0_004_000 + O_DSYNC :: 0o0_010_000 + O_SYNC :: 0o4_010_000 + + _O_RSYNC :: 0 + O_RSYNC :: O_Flags{} + + O_EXEC :: 0x04000000 // NOTE: not defined in the headers + + O_RDONLY :: 0 + O_WRONLY :: 0o1 + O_RDWR :: 0o2 + + _O_SEARCH :: 0 + O_SEARCH :: O_Flags{} + + AT_FDCWD: FD: -100 + + AT_EACCESS :: 0x200 + AT_SYMLINK_NOFOLLOW :: 0x100 + AT_SYMLINK_FOLLOW :: 0x400 + AT_REMOVEDIR :: 0x200 + + flock :: struct { + l_type: Lock_Type, /* [PSX] type of lock. */ + l_whence: c.short, /* [PSX] flag (Whence) of starting offset. */ + l_start: off_t, /* [PSX] relative offset in bytes. */ + l_len: off_t, /* [PSX] size; if 0 then until EOF. */ + l_pid: pid_t, /* [PSX] process ID of the process holding the lock. */ + } + } diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin index 9e54972e7..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" @@ -53,6 +54,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS FNM_PERIOD :: 0x04 FNM_NOESCAPE :: 0x01 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + FNM_NOMATCH :: 1 + + FNM_PATHNAME :: 0x01 + FNM_NOESCAPE :: 0x02 + FNM_PERIOD :: 0x04 + } diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin index 4f41d83db..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" @@ -112,7 +113,7 @@ when ODIN_OS == .Darwin { glob_t :: struct { gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */ - gl_matchc: c.size_t, /* count of paths matching pattern */ + gl_matchc: c.size_t, /* count of paths matching pattern */ gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */ gl_flags: Glob_Flags, /* copy of flags parameter to glob */ gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */ @@ -144,7 +145,7 @@ when ODIN_OS == .Darwin { glob_t :: struct { gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */ - gl_matchc: c.size_t, /* count of paths matching pattern */ + gl_matchc: c.size_t, /* count of paths matching pattern */ gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */ gl_flags: Glob_Flags, /* copy of flags parameter to glob */ gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */ @@ -174,6 +175,33 @@ when ODIN_OS == .Darwin { GLOB_NOMATCH :: -3 GLOB_NOSPACE :: -1 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + glob_t :: struct { + gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */ + gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */ + gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */ + gl_flags: Glob_Flags, /* copy of flags parameter to glob */ + + // Non-standard alternate file system access functions: + + gl_closedir: proc "c" (dirp: DIR), + gl_readdir: proc "c" (dirp: DIR) -> ^dirent, + gl_opendir: proc "c" (path: cstring) -> DIR, + gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result, + gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result, + } + + GLOB_ERR :: 1 << 0 + GLOB_MARK :: 1 << 1 + GLOB_NOSORT :: 1 << 2 + GLOB_DOOFFS :: 1 << 3 + GLOB_NOCHECK :: 1 << 4 + GLOB_APPEND :: 1 << 5 + GLOB_NOESCAPE :: 1 << 6 + + GLOB_NOSPACE :: 1 + GLOB_ABORTED :: 2 + GLOB_NOMATCH :: 3 + } diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin index c8a39de6a..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" @@ -114,7 +115,7 @@ foreign lib { getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno --- } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { gid_t :: distinct c.uint32_t @@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS gr_mem: [^]cstring, /* [PSX] group members */ } -} else { - #panic("posix is unimplemented for the current target") } 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 24ecc917a..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" @@ -238,7 +239,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { ABDAY_4 :: 16 ABDAY_5 :: 17 ABDAY_6 :: 18 - ABDAY_7 :: 19 + ABDAY_7 :: 19 MON_1 :: 20 MON_2 :: 21 @@ -278,7 +279,91 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD { YESEXPR :: 47 NOEXPR :: 49 - CRNCYSTR :: 50 + CRNCYSTR :: 50 + +} else when ODIN_OS == .Linux { + + // NOTE: declared with `_t` so we can enumerate the real `nl_info`. + nl_item_t :: distinct c.int + + // NOTE: All these values are set in an enum on the Linux implementation. + // Some depend on locale.h contants (bits/locale.h to be precise). + + // NOTE: ABDAY_1 is set to LC_TIME << 16 (LC_TIME is 2) on the enum group of + // the Linux implementation. + ABDAY_1 :: 0x20_000 + ABDAY_2 :: 0x20_001 + ABDAY_3 :: 0x20_002 + ABDAY_4 :: 0x20_003 + ABDAY_5 :: 0x20_004 + ABDAY_6 :: 0x20_005 + ABDAY_7 :: 0x20_006 + + DAY_1 :: 0x20_007 + DAY_2 :: 0x20_008 + DAY_3 :: 0x20_009 + DAY_4 :: 0x20_00A + DAY_5 :: 0x20_00B + DAY_6 :: 0x20_00C + DAY_7 :: 0x20_00D + + ABMON_1 :: 0x20_00E + ABMON_2 :: 0x20_010 + ABMON_3 :: 0x20_011 + ABMON_4 :: 0x20_012 + ABMON_5 :: 0x20_013 + ABMON_6 :: 0x20_014 + ABMON_7 :: 0x20_015 + ABMON_8 :: 0x20_016 + ABMON_9 :: 0x20_017 + ABMON_10 :: 0x20_018 + ABMON_11 :: 0x20_019 + ABMON_12 :: 0x20_01A + + MON_1 :: 0x20_01B + MON_2 :: 0x20_01C + MON_3 :: 0x20_01D + MON_4 :: 0x20_01E + MON_5 :: 0x20_020 + MON_6 :: 0x20_021 + MON_7 :: 0x20_022 + MON_8 :: 0x20_023 + MON_9 :: 0x20_024 + MON_10 :: 0x20_025 + MON_11 :: 0x20_026 + MON_12 :: 0x20_027 + + AM_STR :: 0x20_028 + PM_STR :: 0x20_029 + + D_T_FMT :: 0x20_02A + D_FMT :: 0x20_02B + T_FMT :: 0x20_02C + T_FMT_AMPM :: 0x20_02D + + ERA :: 0x20_02E + ERA_D_FMT :: 0x20_030 + ALT_DIGITS :: 0x20_031 + ERA_D_T_FMT :: 0x20_032 + ERA_T_FMT :: 0x20_033 + + // NOTE: CODESET is the 16th member of the enum group starting with value + // LC_CTYPE << 16, LC_CTYPE is 0. + CODESET :: 0x0F + + // NOTE: CRNCYSTR is the 16th member of the enum group starting with value + // LC_MONETARY << 16, LC_MONETARY is 4. + CRNCYSTR :: 0x40_00F + + // NOTE: RADIXCHAR is the 1st member of the enum group starting with value + // LC_NUMERIC << 16, LC_NUMERIC is 1. + RADIXCHAR :: 0x10_000 + THOUSEP :: 0x10_001 + + // NOTE: YESEXPR is the 1st member of the enum group starting with value + // LC_MESSAGES << 16, LC_MESSAGES is 5. + YESEXPR :: 0x50_000 + NOEXPR :: 0x50_001 } else { #panic("posix is unimplemented for the current target") 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 7bb561215..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 @@ -454,6 +455,99 @@ when ODIN_OS == .Darwin { NL_TEXTMAX :: 255 NZERO :: 20 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + // A definition of one of the symbolic constants in the following list shall be omitted from + // <limits.h> on specific implementations where the corresponding value is equal to or greater + // than the stated minimum, but is unspecified. + // + // This indetermination might depend on the amount of available memory space on a specific + // instance of a specific implementation. The actual value supported by a specific instance shall + // be provided by the sysconf() function. + + // AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX) + // AIO_MAX :: sysconf(._AIO_MAX) + // AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX) + ARG_MAX :: 131_072 + // ATEXIT_MAX :: sysconf(._ATEXIT_MAX) + // CHILD_MAX :: sysconf(._POSIX_ARG_MAX) + // DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX) + // HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX) + // IOV_MAX :: sysconf(._XOPEN_IOV_MAX) + // LOGIN_NAME_MAX :: sysconf(._LOGIN_NAME_MAX) + // MQ_OPEN_MAX :: sysconf(._MQ_OPEN_MAX) + // MQ_PRIO_MAX :: sysconf(._MQ_PRIO_MAX) + // PAGESIZE :: PAGE_SIZE + // PAGE_SIZE :: sysconf(._PAGE_SIZE) + PTHREAD_DESTRUCTOR_ITERATIONS :: 4 + // PTHREAD_KEYS_MAX :: sysconf(._PTHREAD_KEYS_MAX) + // PTHREAD_STACK_MIN :: sysconf(._PTHREAD_STACK_MIN) + // RTSIG_MAX :: sysconf(._RTSIG_MAX) + // SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX) + // SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX) + // SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX) + // SS_REPL_MAX :: sysconf(._SS_REPL_MAX) + // STREAM_MAX :: sysconf(._STREAM_MAX) + // SYMLOOP_MAX :: sysconf(._SYSLOOP_MAX) + // TIMER_MAX :: sysconf(._TIMER_MAX) + // TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX) + // TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX) + // TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX) + // TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX) + // TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX) + // TZNAME_MAX :: sysconf(._TZNAME_MAX) + + // The values in the following list may be constants within an implementation or may vary from + // one pathname to another. + // For example, file systems or directories may have different characteristics. + // + // A definition of one of the symbolic constants in the following list shall be omitted from the + // <limits.h> header on specific implementations where the corresponding value is equal to or + // greater than the stated minimum, but where the value can vary depending on the file to which + // it is applied. + // The actual value supported for a specific pathname shall be provided by the pathconf() function. + + // FILESIZEBITS :: pathconf(".", ._FILESIZEBITS) + LINK_MAX :: 127 + MAX_CANON :: 255 + MAX_INPUT :: 255 + NAME_MAX :: 255 + PATH_MAX :: 4096 + PIPE_BUF :: 4096 + // POSIX_ALLOC_SIZE_MIN :: sysconf(._POSIX_ALLOC_SIZE_MIN) + // POSIX_REC_INCR_XFER_SIZE :: sysconf(._POSIX_REC_INCR_XFER_SIZE) + // POSIX_REC_MAX_XFER_SIZE :: sysconf(._POSIX_REC_MAX_XFER_SIZE) + // POSIX_REC_MIN_XFER_SIZE :: sysconf(._POSIX_REC_MIN_XFER_SIZE) + // POSIX_REC_XFER_ALIGN :: sysconf(._POSIX_REC_XFER_ALIGN) + // SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX) + + + // The magnitude limitations in the following list shall be fixed by specific implementations. + // An application should assume that the value of the symbolic constant defined by <limits.h> + // in a specific implementation is the minimum that pertains whenever the application is run + // under that implementation. + // A specific instance of a specific implementation may increase the value relative to that + // supplied by <limits.h> for that implementation. + // The actual value supported by a specific instance shall be provided by the sysconf() function. + + BC_BASE_MAX :: 99 + BC_DIM_MAX :: 2048 + BC_SCALE_MAX :: 99 + BC_STRING_MAX :: 1000 + CHARCLASS_NAME_MAX :: 14 + COLL_WEIGHTS_MAX :: 2 + EXPR_NEST_MAX :: 32 + // LINE_MAX :: sysconf(._LINE_MAX) + // NGROUPS_MAX :: sysconf(._NGROUPS_MAX) + RE_DUP_MAX :: 255 + + // Other limits. + + NL_ARGMAX :: 9 + NL_LANGMAX :: 32 // 14 on glibc, 32 on musl + NL_MSGMAX :: 32_767 + NL_SETMAX :: 255 + NL_TEXTMAX :: 2048 // 255 on glibc, 2048 on musl + NZERO :: 20 + } diff --git a/core/sys/posix/locale.odin b/core/sys/posix/locale.odin index 1f2a336b5..5b8d7c216 100644 --- a/core/sys/posix/locale.odin +++ b/core/sys/posix/locale.odin @@ -1,93 +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 { - #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 aaeb5088a..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" @@ -44,7 +45,7 @@ foreign lib { if_freenameindex :: proc(ptr: ^if_nameindex_t) --- } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { // NOTE: `_t` suffix added due to name conflict. @@ -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 7570f9a22..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" @@ -318,7 +319,7 @@ Info_Errno :: enum c.int { OVERFLOW = EAI_OVERFLOW, } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { hostent :: struct { h_name: cstring, /* [PSX] official name of host */ @@ -412,9 +413,28 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS NI_NUMERICSERV :: 2 NI_NUMERICSCOPE :: 32 NI_DGRAM :: 16 + + } else when ODIN_OS == .Linux { + + AI_PASSIVE :: 0x001 + AI_CANONNAME :: 0x002 + AI_NUMERICHOST :: 0x004 + AI_NUMERICSERV :: 0x400 + AI_V4MAPPED :: 0x008 + AI_ALL :: 0x010 + AI_ADDRCONFIG :: 0x020 + + NI_NOFQDN :: 4 + NI_NUMERICHOST :: 1 + NI_NAMEREQD :: 8 + NI_NUMERICSERV :: 2 + NI_NUMERICSCOPE :: 0x100 + NI_DGRAM :: 16 + } when ODIN_OS == .OpenBSD { + EAI_AGAIN :: -3 EAI_BADFLAGS :: -1 EAI_FAIL :: -4 @@ -425,7 +445,22 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS EAI_SOCKTYPE :: -7 EAI_SYSTEM :: -11 EAI_OVERFLOW :: -14 + + } else when ODIN_OS == .Linux { + + EAI_AGAIN :: -3 + EAI_BADFLAGS :: -1 + EAI_FAIL :: -4 + EAI_FAMILY :: -6 + EAI_MEMORY :: -10 + EAI_NONAME :: -2 + EAI_SERVICE :: -8 + EAI_SOCKTYPE :: -7 + EAI_SYSTEM :: -11 + EAI_OVERFLOW :: -12 + } else { + EAI_AGAIN :: 2 EAI_BADFLAGS :: 3 EAI_FAIL :: 4 @@ -438,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 3926c5288..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" @@ -30,7 +31,7 @@ Protocol :: enum c.int { UDP = IPPROTO_UDP, } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { in_addr :: struct { s_addr: in_addr_t, /* [PSX] big endian address */ @@ -44,26 +45,68 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS }, } - sockaddr_in :: struct { - sin_len: c.uint8_t, - sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */ - sin_port: in_port_t, /* [PSX] port number */ - sin_addr: in_addr, /* [PSX] IP address */ - sin_zero: [8]c.char, - } - - sockaddr_in6 :: struct { - sin6_len: c.uint8_t, - sin6_family: sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */ - sin6_port: in_port_t, /* [PSX] port number */ - sin6_flowinfo: c.uint32_t, /* [PSX] IPv6 traffic class and flow information */ - sin6_addr: in6_addr, /* [PSX] IPv6 address */ - sin6_scope_id: c.uint32_t, /* [PSX] set of interfaces for a scope */ - } + when ODIN_OS == .Linux { + + sockaddr_in :: struct { + sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */ + sin_port: in_port_t, /* [PSX] port number */ + sin_addr: in_addr, /* [PSX] IP address */ + sin_zero: [8]c.char, + } + + sockaddr_in6 :: struct { + sin6_family: sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */ + sin6_port: in_port_t, /* [PSX] port number */ + sin6_flowinfo: u32be, /* [PSX] IPv6 traffic class and flow information */ + sin6_addr: in6_addr, /* [PSX] IPv6 address */ + 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 + IPV6_MULTICAST_LOOP :: 19 + IPV6_JOIN_GROUP :: 20 + IPV6_LEAVE_GROUP :: 21 + IPV6_V6ONLY :: 26 + + } else { + + sockaddr_in :: struct { + sin_len: c.uint8_t, + sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */ + sin_port: in_port_t, /* [PSX] port number */ + sin_addr: in_addr, /* [PSX] IP address */ + sin_zero: [8]c.char, + } + + sockaddr_in6 :: struct { + sin6_len: c.uint8_t, + sin6_family: sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */ + sin6_port: in_port_t, /* [PSX] port number */ + sin6_flowinfo: c.uint32_t, /* [PSX] IPv6 traffic class and flow information */ + sin6_addr: in6_addr, /* [PSX] IPv6 address */ + 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_JOIN_GROUP :: 12 + IPV6_LEAVE_GROUP :: 13 + IPV6_MULTICAST_HOPS :: 10 + IPV6_MULTICAST_IF :: 9 + IPV6_MULTICAST_LOOP :: 11 + IPV6_UNICAST_HOPS :: 4 + IPV6_V6ONLY :: 27 - ipv6_mreq :: struct { - ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */ - ipv6mr_interface: c.uint, /* [PSX] interface index */ } IPPROTO_IP :: 0 @@ -76,14 +119,6 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS INADDR_ANY :: 0x00000000 INADDR_BROADCAST :: 0xFFFFFFFF - IPV6_JOIN_GROUP :: 12 - IPV6_LEAVE_GROUP :: 13 - IPV6_MULTICAST_HOPS :: 10 - IPV6_MULTICAST_IF :: 9 - IPV6_MULTICAST_LOOP :: 11 - IPV6_UNICAST_HOPS :: 4 - IPV6_V6ONLY :: 27 - IN6_IS_ADDR_UNSPECIFIED :: #force_inline proc "contextless" (a: in6_addr) -> b32 { return a.s6_addr == 0 } @@ -194,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 ecd084b38..b1da12f5e 100644 --- a/core/sys/posix/netinet_tcp.odin +++ b/core/sys/posix/netinet_tcp.odin @@ -1,11 +1,10 @@ +#+build linux, darwin, netbsd, openbsd, freebsd package posix // netinet/tcp.h - definitions for the Internet Transmission Control Protocol (TCP) -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { 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 3e825e009..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" @@ -72,7 +73,24 @@ 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") -} +} else when ODIN_OS == .Linux { + + pollfd :: struct { + fd: FD, /* [PSX] the following descriptor being polled */ + events: Poll_Event, /* [PSX] the input event flags */ + revents: Poll_Event, /* [PSX] the output event flags */ + } + POLLIN :: 0x0001 + POLLRDNORM :: 0x0040 + POLLRDBAND :: 0x0080 + POLLPRI :: 0x0002 + POLLOUT :: 0x0004 + POLLWRNORM :: 0x0100 + POLLWRBAND :: 0x0200 + + POLLERR :: 0x0008 + POLLHUP :: 0x0010 + POLLNVAL :: 0x0020 + +} diff --git a/core/sys/posix/posix.odin b/core/sys/posix/posix.odin index 5cb4122a6..d56217407 100644 --- a/core/sys/posix/posix.odin +++ b/core/sys/posix/posix.odin @@ -1,5 +1,7 @@ /* -Bindings for most POSIX APIs. +Raw bindings for most POSIX APIs. + +Targets glibc and musl compatibility. APIs that have been left out are due to not being useful, being fully replaced (and better) by other Odin packages, @@ -9,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 e264f6f6c..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,14 +544,68 @@ 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 */ } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + PTHREAD_CANCEL_DEFERRED :: 0 + PTHREAD_CANCEL_ASYNCHRONOUS :: 1 + + PTHREAD_CANCEL_ENABLE :: 0 + PTHREAD_CANCEL_DISABLE :: 1 + + PTHREAD_CANCELED :: rawptr(~uintptr(0)) + + PTHREAD_CREATE_JOINABLE :: 0 + PTHREAD_CREATE_DETACHED :: 1 + + PTHREAD_INHERIT_SCHED :: 0 + PTHREAD_EXPLICIT_SCHED :: 1 + + PTHREAD_PRIO_NONE :: 0 + PTHREAD_PRIO_INHERIT :: 1 + PTHREAD_PRIO_PROTECT :: 2 + + PTHREAD_PROCESS_PRIVATE :: 0 + PTHREAD_PROCESS_SHARED :: 1 + + PTHREAD_SCOPE_SYSTEM :: 0 + PTHREAD_SCOPE_PROCESS :: 1 + + pthread_t :: distinct c.ulong + + pthread_attr_t :: struct #raw_union { + __size: [56]c.char, // NOTE: may be smaller depending on libc or arch, but never larger. + __align: c.long, + } + + 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 */ + + // NOTE: may be smaller depending on libc or arch, but never larger. + __reserved1: c.int, + __reserved2: [4]c.long, + __reserved3: c.int, + } + } 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 6623ba6e6..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" @@ -94,12 +95,10 @@ when ODIN_OS == .Darwin { SCHED_RR :: 3 SCHED_OTHER :: 2 -} else when ODIN_OS == .NetBSD { +} else when ODIN_OS == .NetBSD || ODIN_OS == .Linux { SCHED_OTHER :: 0 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 c35494185..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" @@ -480,11 +375,6 @@ when ODIN_OS == .Darwin { uid_t :: distinct c.uint32_t sigset_t :: distinct c.uint32_t - // MOTE: unimplemented on darwin. - // - // SIGRTMIN :: - // SIGRTMAX :: - SIGHUP :: 1 SIGQUIT :: 3 SIGTRAP :: 5 @@ -625,11 +515,6 @@ when ODIN_OS == .Darwin { __bits: [4]c.uint32_t, } - // MOTE: unimplemented on darwin. - // - // SIGRTMIN :: 65 - // SIGRTMAX :: 126 - SIGHUP :: 1 SIGQUIT :: 3 SIGTRAP :: 5 @@ -794,11 +679,6 @@ when ODIN_OS == .Darwin { __bits: [4]c.uint32_t, } - // MOTE: unimplemented on darwin. - // - // SIGRTMIN :: 33 - // SIGRTMAX :: 63 - SIGHUP :: 1 SIGQUIT :: 3 SIGTRAP :: 5 @@ -1126,6 +1006,178 @@ when ODIN_OS == .Darwin { SI_ASYNCIO :: -4 // NOTE: not implemented SI_MESGQ :: -5 // NOTE: not implemented -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + // Request that signal be held + SIG_HOLD :: rawptr(uintptr(2)) + + uid_t :: distinct c.uint32_t + sigset_t :: struct { + __val: [1024/(8 * size_of(c.ulong))]c.ulong, + } + + SIGHUP :: 1 + SIGQUIT :: 3 + SIGTRAP :: 5 + SIGBUS :: 7 + SIGKILL :: 9 + SIGUSR1 :: 10 + SIGUSR2 :: 12 + SIGPIPE :: 13 + SIGALRM :: 14 + SIGCHLD :: 17 + SIGCONT :: 18 + SIGSTOP :: 19 + SIGTSTP :: 20 + SIGTTIN :: 21 + SIGTTOU :: 22 + SIGURG :: 23 + SIGXCPU :: 24 + SIGXFSZ :: 25 + SIGVTALRM :: 26 + SIGPROF :: 27 + SIGPOLL :: 29 + SIGSYS :: 31 + + // NOTE: this is actually defined as `sigaction`, but due to the function with the same name + // `_t` has been added. + + sigaction_t :: struct { + using _: struct #raw_union { + sa_handler: proc "c" (Signal), /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */ + sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */ + }, + sa_mask: sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */ + sa_flags: SA_Flags, /* [PSX] special flags */ + sa_restorer: proc "c" (), + } + + SIG_BLOCK :: 0 + SIG_UNBLOCK :: 1 + SIG_SETMASK :: 2 + + SA_NOCLDSTOP :: 1 + SA_NOCLDWAIT :: 2 + SA_SIGINFO :: 4 + SA_ONSTACK :: 0x08000000 + SA_RESTART :: 0x10000000 + SA_NODEFER :: 0x40000000 + SA_RESETHAND :: 0x80000000 + + SS_ONSTACK :: 1 + SS_DISABLE :: 2 + + when ODIN_ARCH == .arm64 { + MINSIGSTKSZ :: 6144 + SIGSTKSZ :: 12288 + } else { + MINSIGSTKSZ :: 2048 + SIGSTKSZ :: 8192 + } + + stack_t :: struct { + ss_sp: rawptr, /* [PSX] stack base or pointer */ + ss_flags: SS_Flags, /* [PSX] flags */ + ss_size: c.size_t, /* [PSX] stack size */ + } + + @(private) + __SI_MAX_SIZE :: 128 + + when size_of(int) == 8 { + @(private) + _pad0 :: struct { + _pad0: c.int, + } + @(private) + __SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 4 + + } else { + @(private) + _pad0 :: struct {} + @(private) + __SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 3 + } + + siginfo_t :: struct #align(8) { + si_signo: Signal, /* [PSX] signal number */ + si_errno: Errno, /* [PSX] errno value associated with this signal */ + si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */ + ill: ILL_Code, + fpe: FPE_Code, + segv: SEGV_Code, + bus: BUS_Code, + trap: TRAP_Code, + chld: CLD_Code, + poll: POLL_Code, + any: Any_Code, + }, + __pad0: _pad0, + using _sifields: struct #raw_union { + _pad: [__SI_PAD_SIZE]c.int, + + using _: struct { + si_pid: pid_t, /* [PSX] sending process ID */ + si_uid: uid_t, /* [PSX] real user ID of sending process */ + using _: struct #raw_union { + si_status: c.int, /* [PSX] exit value or signal */ + si_value: sigval, /* [PSX] signal value */ + }, + }, + using _: struct { + si_addr: rawptr, /* [PSX] address of faulting instruction */ + }, + using _: struct { + si_band: c.long, /* [PSX] band event for SIGPOLL */ + }, + }, + } + + ILL_ILLOPC :: 1 + ILL_ILLOPN :: 2 + ILL_ILLADR :: 3 + ILL_ILLTRP :: 4 + ILL_PRVOPC :: 5 + ILL_PRVREG :: 6 + ILL_COPROC :: 7 + ILL_BADSTK :: 8 + + FPE_INTDIV :: 1 + FPE_INTOVF :: 2 + FPE_FLTDIV :: 3 + FPE_FLTOVF :: 4 + FPE_FLTUND :: 5 + FPE_FLTRES :: 6 + FPE_FLTINV :: 7 + FPE_FLTSUB :: 8 + + SEGV_MAPERR :: 1 + SEGV_ACCERR :: 2 + + BUS_ADRALN :: 1 + BUS_ADRERR :: 2 + BUS_OBJERR :: 3 + + TRAP_BRKPT :: 1 + TRAP_TRACE :: 2 + + CLD_EXITED :: 1 + CLD_KILLED :: 2 + CLD_DUMPED :: 3 + CLD_TRAPPED :: 4 + CLD_STOPPED :: 5 + CLD_CONTINUED :: 6 + + POLL_IN :: 1 + POLL_OUT :: 2 + POLL_MSG :: 3 + POLL_ERR :: 4 + POLL_PRI :: 5 + POLL_HUP :: 6 + + SI_USER :: 0 + SI_QUEUE :: -1 + SI_TIMER :: -2 + SI_MESGQ :: -3 + SI_ASYNCIO :: -4 } 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" @@ -33,16 +33,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. Returns: nil (setting errno) on failure, the stream on success @@ -116,34 +106,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. Returns: the number of bytes written or -1 on failure/EOF @@ -182,60 +144,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. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html ]] @@ -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, @@ -343,21 +293,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. Example: @@ -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" @@ -14,16 +15,6 @@ when ODIN_OS == .Darwin { 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. Returns: ERANGE if the buffer is not big enough @@ -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 f8778ee15..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" @@ -84,6 +85,30 @@ when ODIN_OS == .Darwin { IPC_SET :: 1 IPC_STAT :: 2 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + key_t :: distinct c.int32_t + + ipc_perm :: struct { + __ipc_perm_key: key_t, + uid: uid_t, /* [PSX] owner's user ID */ + gid: gid_t, /* [PSX] owner's group ID */ + cuid: uid_t, /* [PSX] creator's user ID */ + cgid: gid_t, /* [PSX] creator's group ID */ + mode: mode_t, /* [PSX] read/write perms */ + __ipc_perm_seq: c.int, + __pad1: c.long, + __pad2: c.long, + } + + IPC_CREAT :: 0o01000 + IPC_EXCL :: 0o02000 + IPC_NOWAIT :: 0o04000 + + IPC_PRIVATE :: key_t(0) + + IPC_RMID :: 0 + IPC_SET :: 1 + IPC_STAT :: 2 + } diff --git a/core/sys/posix/sys_mman.odin b/core/sys/posix/sys_mman.odin index 217d321ac..0594672ae 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" @@ -115,12 +116,14 @@ Prot_Flag_Bits :: enum c.int { Prot_Flags :: bit_set[Prot_Flag_Bits; c.int] Map_Flag_Bits :: enum c.int { + // Map anonymous memory. + ANONYMOUS = log2(MAP_ANONYMOUS), // Interpret addr exactly. - FIXED = log2(MAP_FIXED), + FIXED = log2(MAP_FIXED), // Changes are private. - PRIVATE = log2(MAP_PRIVATE), + PRIVATE = log2(MAP_PRIVATE), // Changes are shared. - SHARED = log2(MAP_SHARED), + SHARED = log2(MAP_SHARED), } Map_Flags :: bit_set[Map_Flag_Bits; c.int] @@ -163,18 +166,19 @@ when ODIN_OS == .NetBSD { @(private) LMSYNC :: "msync" } -when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { PROT_EXEC :: 0x04 _PROT_NONE :: 0x00 PROT_READ :: 0x01 PROT_WRITE :: 0x02 - MAP_FIXED :: 0x0010 - MAP_PRIVATE :: 0x0002 - MAP_SHARED :: 0x0001 + MAP_FIXED :: 0x0010 + MAP_PRIVATE :: 0x0002 + MAP_SHARED :: 0x0001 + MAP_ANONYMOUS :: 0x0020 when ODIN_OS == .Linux else 0x1000 - when ODIN_OS == .Darwin { + when ODIN_OS == .Darwin || ODIN_OS == .Linux { MS_INVALIDATE :: 0x0002 _MS_SYNC :: 0x0010 } else when ODIN_OS == .NetBSD { @@ -184,6 +188,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { MS_INVALIDATE :: 0x0004 _MS_SYNC :: 0x0002 } + MS_ASYNC :: 0x0001 MS_SYNC :: Sync_Flags{Sync_Flags_Bits(log2(_MS_SYNC))} @@ -205,9 +210,10 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { PROT_READ :: 0x01 PROT_WRITE :: 0x02 - MAP_FIXED :: 0x0010 - MAP_PRIVATE :: 0x0002 - MAP_SHARED :: 0x0001 + MAP_FIXED :: 0x0010 + MAP_PRIVATE :: 0x0002 + MAP_SHARED :: 0x0001 + MAP_ANONYMOUS :: 0x1000 MS_ASYNC :: 0x0001 MS_INVALIDATE :: 0x0002 @@ -224,6 +230,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { 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 a8b86e501..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" @@ -64,28 +65,22 @@ when ODIN_OS == .Darwin { MSG_NOERROR :: 0o10000 - // NOTE: this is #pragma pack(4) - - msqid_ds :: struct #align(4) { - msg_perm: ipc_perm, /* [PSX] operation permission structure */ + msqid_ds :: struct #max_field_align(4) { + msg_perm: ipc_perm, /* [PSX] operation permission structure */ msg_first: c.int32_t, msg_last: c.int32_t, msg_cbytes: msglen_t, - 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() */ - msg_stime: time_t, /* [PSX] time of last msgsnd() */ + 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() */ + msg_stime: time_t, /* [PSX] time of last msgsnd() */ msg_pad1: c.int32_t, - using _: struct #align(4) { - msg_rtime: time_t, /* [PSX] time of last msgrcv() */ - msg_pad2: c.int32_t, - using _: struct #align(4) { - msg_ctime: time_t, /* [PSX] time of last change */ - msg_pad3: c.int32_t, - msg_pad4: [4]c.int32_t, - }, - }, + msg_rtime: time_t, /* [PSX] time of last msgrcv() */ + msg_pad2: c.int32_t, + msg_ctime: time_t, /* [PSX] time of last change */ + msg_pad3: c.int32_t, + msg_pad4: [4]c.int32_t, } } else when ODIN_OS == .FreeBSD { @@ -156,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 6716d60c3..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" @@ -95,7 +96,7 @@ when ODIN_OS == .NetBSD { @(private) LGETRUSAGE :: "getrusage" } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { PRIO_PROCESS :: 0 PRIO_PGRP :: 1 @@ -103,7 +104,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS rlim_t :: distinct c.uint64_t - RLIM_INFINITY :: (rlim_t(1) << 63) - 1 + RLIM_INFINITY :: ~rlim_t(0) when ODIN_OS == .Linux else (rlim_t(1) << 63) - 1 RLIM_SAVED_MAX :: RLIM_INFINITY RLIM_SAVED_CUR :: RLIM_INFINITY @@ -143,10 +144,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS RLIMIT_CPU :: 0 RLIMIT_DATA :: 2 RLIMIT_FSIZE :: 1 - RLIMIT_NOFILE :: 8 + RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 8 RLIMIT_STACK :: 3 - RLIMIT_AS :: 5 when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD else 10 -} else { - #panic("posix is unimplemented for the current target") + when ODIN_OS == .Linux { + RLIMIT_AS :: 9 + } else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD { + RLIMIT_AS :: 5 + } else { + RLIMIT_AS :: 10 + } + } diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin index 3392e02bc..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" @@ -55,7 +56,7 @@ when ODIN_OS == .NetBSD { LSELECT :: "select" } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long) @@ -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 3fcde325b..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" @@ -79,19 +80,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS SETALL :: 9 when ODIN_OS == .Darwin { - // NOTE: this is #pragma pack(4) - - semid_ds :: struct #align(4) { - sem_perm: ipc_perm, /* [PSX] operation permission structure */ - sem_base: c.int32_t, /* 32 bit base ptr for semaphore set */ - sem_nsems: c.ushort, /* [PSX] number of semaphores in set */ - sem_otime: time_t, /* [PSX] last semop() */ + semid_ds :: struct #max_field_align(4) { + sem_perm: ipc_perm, /* [PSX] operation permission structure */ + sem_base: c.int32_t, /* 32 bit base ptr for semaphore set */ + sem_nsems: c.ushort, /* [PSX] number of semaphores in set */ + sem_otime: time_t, /* [PSX] last semop() */ sem_pad1: c.int32_t, - using _: struct #align(4) { - sem_ctime: time_t, /* [PSX] last time changed by semctl() */ - sem_pad2: c.int32_t, - sem_pad3: [4]c.int32_t, - }, + sem_ctime: time_t, /* [PSX] last time changed by semctl() */ + sem_pad2: c.int32_t, + sem_pad3: [4]c.int32_t, } } else when ODIN_OS == .FreeBSD { semid_ds :: struct { @@ -127,6 +124,34 @@ 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") +} else when ODIN_OS == .Linux { + + SEM_UNDO :: 0x1000 // undo the operation on exit + + // Commands for `semctl'. + GETPID :: 11 + GETVAL :: 12 + GETALL :: 13 + GETNCNT :: 14 + GETZCNT :: 15 + SETVAL :: 16 + SETALL :: 17 + + semid_ds :: struct { + sem_perm: ipc_perm, // [PSX] operation permission structure + sem_otime: time_t, // [PSX] last semop() + __sem_otime_high: c.ulong, + sem_ctime: time_t, // [PSX] last time changed by semctl() + __sem_ctime_high: c.ulong, + sem_nsems: c.ulong, // [PSX] number of semaphores in set + __glibc_reserved3: c.ulong, + __glibc_reserved4: c.ulong, + } + + sembuf :: struct { + sem_num: c.ushort, /* [PSX] semaphore number */ + sem_op: c.short, /* [PSX] semaphore operation */ + sem_flg: c.short, /* [PSX] operation flags */ + } + } diff --git a/core/sys/posix/sys_shm.odin b/core/sys/posix/sys_shm.odin index 3bc883ce4..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" @@ -67,20 +68,16 @@ when ODIN_OS == .Darwin { shmatt_t :: distinct c.ushort - // NOTE: this is #pragma pack(4) - - shmid_ds :: struct #align(4) { - shm_perm: ipc_perm, /* [PSX] operation permission structure */ - shm_segsz: c.size_t, /* [PSX] size of segment in bytes */ - shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ - shm_cpid: pid_t, /* [PSX] process ID of creator */ - shm_nattch: shmatt_t, /* [PSX] number of current attaches */ - using _: struct #align(4) { - 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_internal: rawptr, - }, + shmid_ds :: struct #max_field_align(4) { + shm_perm: ipc_perm, /* [PSX] operation permission structure */ + shm_segsz: c.size_t, /* [PSX] size of segment in bytes */ + shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */ + shm_cpid: pid_t, /* [PSX] process ID of creator */ + shm_nattch: shmatt_t, /* [PSX] number of current attaches */ + 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_internal: rawptr, } } else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD { @@ -141,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 36c3c1467..4dd6074a3 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" @@ -47,6 +48,12 @@ foreign libc { addr.sun_family = .UNIX copy(addr.sun_path[:], "/somepath\x00") + /* + unlink the socket before binding in case + of previous runs not cleaning up the socket + */ + posix.unlink("/somepath") + if posix.bind(sfd, (^posix.sockaddr)(&addr), size_of(addr)) != .OK { /* Handle error */ } @@ -321,16 +328,25 @@ when ODIN_OS == .NetBSD { @(private) LSOCKET :: "socket" } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { 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 */ - sa_data: [14]c.char, /* [PSX] socket address */ + sockaddr :: struct { + sa_len: c.uint8_t, /* total length */ + sa_family: sa_family_t, /* [PSX] address family */ + sa_data: [14]c.char, /* [PSX] socket address */ + } } @@ -339,6 +355,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS _SS_PAD1SIZE :: 6 @(private) _SS_PAD2SIZE :: 240 + } else when ODIN_OS == .Linux { + @(private) + _SS_SIZE :: 128 + @(private) + _SS_PADSIZE :: _SS_SIZE - size_of(c.uint16_t) - size_of(c.uint64_t) } else { @(private) _SS_MAXSIZE :: 128 @@ -350,28 +371,52 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS _SS_PAD2SIZE :: _SS_MAXSIZE - size_of(c.uint8_t) - size_of(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE } - sockaddr_storage :: struct { - ss_len: c.uint8_t, /* address length */ - ss_family: sa_family_t, /* [PSX] address family */ - __ss_pad1: [_SS_PAD1SIZE]c.char, - __ss_align: c.int64_t, /* force structure storage alignment */ - __ss_pad2: [_SS_PAD2SIZE]c.char, - } + when ODIN_OS == .Linux { + sockaddr_storage :: struct { + ss_family: sa_family_t, /* [PSX] address family */ + __ss_padding: [_SS_PADSIZE]c.char, + __ss_align: c.uint64_t, /* force structure storage alignment */ + } - msghdr :: struct { - msg_name: rawptr, /* [PSX] optional address */ - msg_namelen: socklen_t, /* [PSX] size of address */ - msg_iov: [^]iovec, /* [PSX] scatter/gather array */ - msg_iovlen: c.int, /* [PSX] members in msg_iov */ - msg_control: rawptr, /* [PSX] ancillary data */ - msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */ - msg_flags: Msg_Flags, /* [PSX] flags on received message */ - } + msghdr :: struct { + msg_name: rawptr, /* [PSX] optional address */ + msg_namelen: socklen_t, /* [PSX] size of address */ + msg_iov: [^]iovec, /* [PSX] scatter/gather array */ + msg_iovlen: c.size_t, /* [PSX] members in msg_iov */ + msg_control: rawptr, /* [PSX] ancillary data */ + msg_controllen: c.size_t, /* [PSX] ancillary data buffer length */ + msg_flags: Msg_Flags, /* [PSX] flags on received message */ + } + + cmsghdr :: struct { + cmsg_len: c.size_t, /* [PSX] data byte count, including cmsghdr */ + cmsg_level: c.int, /* [PSX] originating protocol */ + cmsg_type: c.int, /* [PSX] protocol-specific type */ + } + } else { + sockaddr_storage :: struct { + ss_len: c.uint8_t, /* address length */ + ss_family: sa_family_t, /* [PSX] address family */ + __ss_pad1: [_SS_PAD1SIZE]c.char, + __ss_align: c.int64_t, /* force structure storage alignment */ + __ss_pad2: [_SS_PAD2SIZE]c.char, + } - cmsghdr :: struct { - cmsg_len: socklen_t, /* [PSX] data byte count, including cmsghdr */ - cmsg_level: c.int, /* [PSX] originating protocol */ - cmsg_type: c.int, /* [PSX] protocol-specific type */ + msghdr :: struct { + msg_name: rawptr, /* [PSX] optional address */ + msg_namelen: socklen_t, /* [PSX] size of address */ + msg_iov: [^]iovec, /* [PSX] scatter/gather array */ + msg_iovlen: c.int, /* [PSX] members in msg_iov */ + msg_control: rawptr, /* [PSX] ancillary data */ + msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */ + msg_flags: Msg_Flags, /* [PSX] flags on received message */ + } + + cmsghdr :: struct { + cmsg_len: socklen_t, /* [PSX] data byte count, including cmsghdr */ + cmsg_level: c.int, /* [PSX] originating protocol */ + cmsg_type: c.int, /* [PSX] protocol-specific type */ + } } SCM_RIGHTS :: 0x01 @@ -421,57 +466,90 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS SOCK_STREAM :: 1 // Options to be accessed at socket level, not protocol level. - SOL_SOCKET :: 0xffff - - SO_ACCEPTCONN :: 0x0002 - SO_BROADCAST :: 0x0020 - SO_DEBUG :: 0x0001 - SO_DONTROUTE :: 0x0010 - SO_ERROR :: 0x1007 - SO_KEEPALIVE :: 0x0008 - SO_OOBINLINE :: 0x0100 - SO_RCVBUF :: 0x1002 - SO_RCVLOWAT :: 0x1004 - SO_REUSEADDR :: 0x0004 - SO_SNDBUF :: 0x1001 - SO_SNDLOWAT :: 0x1003 - SO_TYPE :: 0x1008 - - when ODIN_OS == .Darwin { - SO_LINGER :: 0x1080 - SO_RCVTIMEO :: 0x1006 - SO_SNDTIMEO :: 0x1005 - } else when ODIN_OS == .FreeBSD { - SO_LINGER :: 0x0080 - SO_RCVTIMEO :: 0x1006 - SO_SNDTIMEO :: 0x1005 - } else when ODIN_OS == .NetBSD { - SO_LINGER :: 0x0080 - SO_RCVTIMEO :: 0x100c - SO_SNDTIMEO :: 0x100b - } else when ODIN_OS == .OpenBSD { - SO_LINGER :: 0x0080 - SO_RCVTIMEO :: 0x1006 - SO_SNDTIMEO :: 0x1005 + when ODIN_OS == .Linux { + SOL_SOCKET :: 1 + + SO_ACCEPTCONN :: 30 + SO_BROADCAST :: 6 + SO_DEBUG :: 1 + SO_DONTROUTE :: 5 + SO_ERROR :: 4 + SO_KEEPALIVE :: 9 + SO_OOBINLINE :: 10 + SO_RCVBUF :: 8 + SO_RCVLOWAT :: 18 + SO_REUSEADDR :: 2 + SO_SNDBUF :: 7 + SO_SNDLOWAT :: 19 + SO_TYPE :: 3 + SO_LINGER :: 13 + + SO_RCVTIMEO :: 66 + SO_SNDTIMEO :: 67 + } else { + SOL_SOCKET :: 0xffff + + SO_ACCEPTCONN :: 0x0002 + SO_BROADCAST :: 0x0020 + SO_DEBUG :: 0x0001 + SO_DONTROUTE :: 0x0010 + SO_ERROR :: 0x1007 + SO_KEEPALIVE :: 0x0008 + SO_OOBINLINE :: 0x0100 + SO_RCVBUF :: 0x1002 + SO_RCVLOWAT :: 0x1004 + SO_REUSEADDR :: 0x0004 + SO_SNDBUF :: 0x1001 + SO_SNDLOWAT :: 0x1003 + SO_TYPE :: 0x1008 + + when ODIN_OS == .Darwin { + SO_LINGER :: 0x1080 + SO_RCVTIMEO :: 0x1006 + SO_SNDTIMEO :: 0x1005 + } else when ODIN_OS == .FreeBSD { + SO_LINGER :: 0x0080 + SO_RCVTIMEO :: 0x1006 + SO_SNDTIMEO :: 0x1005 + } else when ODIN_OS == .NetBSD { + SO_LINGER :: 0x0080 + SO_RCVTIMEO :: 0x100c + SO_SNDTIMEO :: 0x100b + } else when ODIN_OS == .OpenBSD { + SO_LINGER :: 0x0080 + SO_RCVTIMEO :: 0x1006 + SO_SNDTIMEO :: 0x1005 + } } // The maximum backlog queue length for listen(). SOMAXCONN :: 128 - MSG_CTRUNC :: 0x20 - MSG_DONTROUTE :: 0x4 - MSG_EOR :: 0x8 - MSG_OOB :: 0x1 - MSG_PEEK :: 0x2 - MSG_TRUNC :: 0x10 - MSG_WAITALL :: 0x40 - - when ODIN_OS == .Darwin { - MSG_NOSIGNAL :: 0x80000 - } else when ODIN_OS == .FreeBSD { - MSG_NOSIGNAL :: 0x00020000 - } else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { - MSG_NOSIGNAL :: 0x0400 + when ODIN_OS == .Linux { + MSG_CTRUNC :: 0x008 + MSG_DONTROUTE :: 0x004 + MSG_EOR :: 0x080 + MSG_OOB :: 0x001 + MSG_PEEK :: 0x002 + MSG_TRUNC :: 0x020 + MSG_WAITALL :: 0x100 + MSG_NOSIGNAL :: 0x4000 + } else { + MSG_CTRUNC :: 0x20 + MSG_DONTROUTE :: 0x4 + MSG_EOR :: 0x8 + MSG_OOB :: 0x1 + MSG_PEEK :: 0x2 + MSG_TRUNC :: 0x10 + MSG_WAITALL :: 0x40 + + when ODIN_OS == .Darwin { + MSG_NOSIGNAL :: 0x80000 + } else when ODIN_OS == .FreeBSD { + MSG_NOSIGNAL :: 0x00020000 + } else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { + MSG_NOSIGNAL :: 0x0400 + } } AF_INET :: 2 @@ -483,13 +561,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS AF_INET6 :: 28 } else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { AF_INET6 :: 24 + } else when ODIN_OS == .Linux { + AF_INET6 :: 10 } SHUT_RD :: 0 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 093fdd688..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" @@ -66,7 +67,7 @@ when ODIN_OS == .NetBSD { @(private) LUTIMES :: "utimes" } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { itimerval :: struct { it_interval: timeval, /* [PSX] timer interval */ @@ -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 685ced515..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 { @@ -24,7 +25,7 @@ when ODIN_OS == .NetBSD { @(private) LTIMES :: "times" } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { tms :: struct { tms_utime: clock_t, /* [PSX] user CPU time */ @@ -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 01664e576..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" @@ -30,13 +31,11 @@ foreign libc { writev :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t --- } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { iovec :: struct { iov_base: rawptr, /* [PSX] base address of I/O memory region */ 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 146882051..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" @@ -12,6 +13,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS sun_path: [104]c.char, /* [PSX] socket pathname */ } -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + sockaddr_un :: struct { + sun_family: sa_family_t, /* [PSX] address family */ + sun_path: [108]c.char, /* [PSX] socket pathname */ + } + } diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin index 803f40ffd..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" @@ -50,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 8421c8bfd..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" @@ -124,11 +125,11 @@ WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool { idtype_t :: enum c.int { // Wait for any children and `id` is ignored. - P_ALL, + P_ALL = _P_ALL, // Wait for any child wiith a process group ID equal to `id`. - P_PID, + P_PID = _P_PID, // Wait for any child with a process group ID equal to `id`. - P_PGID, + P_PGID = _P_PGID, } Wait_Flag_Bits :: enum c.int { @@ -166,6 +167,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 0x00000020 WSTOPPED :: 0x00000008 + _P_ALL :: 0 + _P_PID :: 1 + _P_PGID :: 2 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -221,6 +226,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 8 WSTOPPED :: 2 + _P_ALL :: 7 + _P_PID :: 0 + _P_PGID :: 2 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -275,6 +284,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 0x00010000 WSTOPPED :: 0x00000002 + _P_ALL :: 0 + _P_PID :: 1 + _P_PGID :: 2 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -330,6 +343,10 @@ when ODIN_OS == .Darwin { WNOWAIT :: 0x00010000 WSTOPPED :: 0x00000002 + _P_ALL :: 0 + _P_PID :: 2 + _P_PGID :: 1 + @(private) _WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int { return x & 0o177 @@ -375,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 067b83271..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" @@ -31,13 +32,11 @@ Ulimit_Cmd :: enum c.int { SETFSIZE = UL_SETFSIZE, } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { UL_GETFSIZE :: 1 UL_SETFSIZE :: 2 // 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 15dbb576f..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" @@ -12,19 +13,6 @@ when ODIN_OS == .Darwin { 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`. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]] @@ -43,18 +31,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. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html ]] @@ -205,15 +181,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. There shall be no return from a successful exec, @@ -393,44 +360,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. Returns: the ID, no failure is defined @@ -830,13 +759,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. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html ]] @@ -913,13 +835,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. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html ]] @@ -959,13 +874,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. [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]] @@ -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, @@ -1181,20 +1075,20 @@ when ODIN_OS == .Darwin { F_TLOCK :: 2 F_ULOCK :: 0 - _CS_PATH :: 1 - _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 - _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 - _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 - _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 - _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 - _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 - _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 - _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 - _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 - _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 - _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 - _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 - _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 _PC_LINK_MAX :: 1 _PC_MAX_CANON :: 2 @@ -1362,20 +1256,20 @@ when ODIN_OS == .Darwin { F_TLOCK :: 2 F_ULOCK :: 0 - _CS_PATH :: 1 - _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 - _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 - _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 - _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 - _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 - _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 - _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 - _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 - _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 - _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 - _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 - _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 - _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 _PC_LINK_MAX :: 1 _PC_MAX_CANON :: 2 @@ -1543,20 +1437,20 @@ when ODIN_OS == .Darwin { F_TLOCK :: 2 F_ULOCK :: 0 - _CS_PATH :: 1 - _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 - _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 - _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 - _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 - _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 - _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 - _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 - _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 - _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 - _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 - _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 - _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 - _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 _PC_LINK_MAX :: 1 _PC_MAX_CANON :: 2 @@ -1655,7 +1549,6 @@ when ODIN_OS == .Darwin { _SC_TTY_NAME_MAX :: 68 _SC_HOST_NAME_MAX :: 69 - _SC_PASS_MAX :: 70 _SC_REGEXP :: 71 _SC_SHELL :: 72 _SC_SYMLOOP_MAX :: 73 @@ -1729,20 +1622,20 @@ when ODIN_OS == .Darwin { F_TLOCK :: 2 F_ULOCK :: 0 - _CS_PATH :: 1 - _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 - _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 - _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 - _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 - _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 - _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 - _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 - _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 - _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 - _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 - _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 - _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 - _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 + _CS_PATH :: 1 + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 2 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 3 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 4 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 5 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 6 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 7 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 8 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 9 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 10 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 11 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 12 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 13 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 14 _PC_LINK_MAX :: 1 _PC_MAX_CANON :: 2 @@ -1911,7 +1804,192 @@ when ODIN_OS == .Darwin { _POSIX_VDISABLE :: '\377' -} else { - #panic("posix is unimplemented for the current target") -} +} else when ODIN_OS == .Linux { + + _F_OK :: 0 + X_OK :: 1 + W_OK :: 2 + R_OK :: 4 + + F_LOCK :: 1 + F_TEST :: 3 + F_TLOCK :: 2 + F_ULOCK :: 0 + _CS_PATH :: 1 + _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 2 + + _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 1116 + _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 1117 + _CS_POSIX_V6_ILP32_OFF32_LIBS :: 1118 + _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 1120 + _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121 + _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 1122 + _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 1124 + _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 1125 + _CS_POSIX_V6_LP64_OFF64_LIBS :: 1126 + _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 1128 + _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129 + _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 1130 + + _PC_LINK_MAX :: 1 + _PC_MAX_CANON :: 2 + _PC_MAX_INPUT :: 3 + _PC_NAME_MAX :: 4 + _PC_PATH_MAX :: 5 + _PC_PIPE_BUF :: 6 + _PC_CHOWN_RESTRICTED :: 7 + _PC_NO_TRUNC :: 8 + _PC_VDISABLE :: 9 + _PC_SYNC_IO :: 10 + _PC_ASYNC_IO :: 11 + _PC_PRIO_IO :: 12 + _PC_FILESIZEBITS :: 14 + _PC_REC_INCR_XFER_SIZE :: 15 + _PC_REC_MAX_XFER_SIZE :: 16 + _PC_REC_MIN_XFER_SIZE :: 17 + _PC_REC_XFER_ALIGN :: 18 + _PC_ALLOC_SIZE_MIN :: 19 + _PC_SYMLINK_MAX :: 20 + _PC_2_SYMLINK :: 21 + + _SC_ARG_MAX :: 1 + _SC_CHILD_MAX :: 2 + _SC_CLK_TCK :: 3 + _SC_NGROUPS_MAX :: 4 + _SC_OPEN_MAX :: 5 + _SC_STREAM_MAX :: 6 + _SC_TZNAME_MAX :: 7 + _SC_JOB_CONTROL :: 8 + _SC_SAVED_IDS :: 9 + _SC_REALTIME_SIGNALS :: 10 + _SC_PRIORITY_SCHEDULING :: 11 + _SC_TIMERS :: 12 + _SC_ASYNCHRONOUS_IO :: 13 + _SC_PRIORITIZED_IO :: 14 + _SC_SYNCHRONIZED_IO :: 15 + _SC_FSYNC :: 16 + _SC_MAPPED_FILES :: 17 + _SC_MEMLOCK :: 18 + _SC_MEMLOCK_RANGE :: 19 + _SC_MEMORY_PROTECTION :: 20 + _SC_MESSAGE_PASSING :: 21 + _SC_SEMAPHORES :: 22 + _SC_SHARED_MEMORY_OBJECTS :: 23 + _SC_AIO_LISTIO_MAX :: 24 + _SC_AIO_MAX :: 25 + _SC_AIO_PRIO_DELTA_MAX :: 26 + _SC_DELAYTIMER_MAX :: 27 + _SC_MQ_OPEN_MAX :: 28 + _SC_MQ_PRIO_MAX :: 29 + _SC_VERSION :: 30 + _SC_PAGESIZE :: 31 + _SC_PAGE_SIZE :: _SC_PAGESIZE + _SC_RTSIG_MAX :: 32 + _SC_SEM_NSEMS_MAX :: 33 + _SC_SEM_VALUE_MAX :: 34 + _SC_SIGQUEUE_MAX :: 35 + _SC_TIMER_MAX :: 36 + _SC_BC_BASE_MAX :: 37 + _SC_BC_DIM_MAX :: 38 + _SC_BC_SCALE_MAX :: 39 + _SC_BC_STRING_MAX :: 40 + _SC_COLL_WEIGHTS_MAX :: 41 + _SC_EXPR_NEST_MAX :: 43 + _SC_LINE_MAX :: 44 + _SC_RE_DUP_MAX :: 45 + _SC_2_VERSION :: 47 + _SC_2_C_BIND :: 48 + _SC_2_C_DEV :: 49 + _SC_2_FORT_DEV :: 50 + _SC_2_FORT_RUN :: 51 + _SC_2_SW_DEV :: 52 + _SC_2_LOCALEDEF :: 53 + + _SC_IOV_MAX :: 62 + _SC_THREADS :: 69 + _SC_THREAD_SAFE_FUNCTIONS :: 70 + _SC_GETGR_R_SIZE_MAX :: 71 + _SC_GETPW_R_SIZE_MAX :: 72 + _SC_LOGIN_NAME_MAX :: 73 + _SC_TTY_NAME_MAX :: 74 + _SC_THREAD_DESTRUCTOR_ITERATIONS :: 75 + _SC_THREAD_KEYS_MAX :: 76 + _SC_THREAD_STACK_MIN :: 77 + _SC_THREAD_THREADS_MAX :: 78 + _SC_THREAD_ATTR_STACKADDR :: 79 + _SC_THREAD_ATTR_STACKSIZE :: 80 + _SC_THREAD_PRIORITY_SCHEDULING :: 81 + _SC_THREAD_PRIO_INHERIT :: 82 + _SC_THREAD_PRIO_PROTECT :: 83 + _SC_THREAD_PROCESS_SHARED :: 84 + _SC_NPROCESSORS_CONF :: 85 + _SC_NPROCESSORS_ONLN :: 86 + _SC_PHYS_PAGES :: 87 + _SC_AVPHYS_PAGES :: 88 + _SC_ATEXIT_MAX :: 89 + _SC_PASS_MAX :: 90 + _SC_XOPEN_VERSION :: 91 + _SC_XOPEN_UNIX :: 92 + _SC_XOPEN_CRYPT :: 93 + _SC_XOPEN_ENH_I18N :: 94 + _SC_XOPEN_SHM :: 95 + _SC_2_CHAR_TERM :: 96 + _SC_2_UPE :: 97 + + _SC_XOPEN_LEGACY :: 129 + _SC_XOPEN_REALTIME :: 130 + _SC_XOPEN_REALTIME_THREADS :: 131 + _SC_ADVISORY_INFO :: 132 + _SC_BARRIERS :: 133 + _SC_CLOCK_SELECTION :: 137 + _SC_CPUTIME :: 138 + _SC_THREAD_CPUTIME :: 139 + _SC_MONOTONIC_CLOCK :: 149 + _SC_READER_WRITER_LOCKS :: 153 + _SC_SPIN_LOCKS :: 154 + _SC_REGEXP :: 155 + _SC_SHELL :: 157 + _SC_SPAWN :: 159 + _SC_SPORADIC_SERVER :: 160 + _SC_THREAD_SPORADIC_SERVER :: 161 + _SC_TIMEOUTS :: 164 + _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 + _SC_2_PBS_CHECKPOINT :: 174 + _SC_V6_ILP32_OFF32 :: 175 + _SC_V6_ILP32_OFFBIG :: 176 + _SC_V6_LP64_OFF64 :: 177 + _SC_V6_LPBIG_OFFBIG :: 178 + _SC_HOST_NAME_MAX :: 179 + _SC_TRACE :: 180 + _SC_TRACE_EVENT_FILTER :: 181 + _SC_TRACE_INHERIT :: 182 + _SC_TRACE_LOG :: 183 + + _SC_IPV6 :: 234 + _SC_RAW_SOCKETS :: 235 + _SC_V7_ILP32_OFF32 :: 236 + _SC_V7_ILP32_OFFBIG :: 237 + _SC_V7_LP64_OFF64 :: 238 + _SC_V7_LPBIG_OFFBIG :: 239 + _SC_SS_REPL_MAX :: 240 + _SC_TRACE_EVENT_NAME_MAX :: 241 + _SC_TRACE_NAME_MAX :: 242 + _SC_TRACE_SYS_MAX :: 243 + _SC_TRACE_USER_EVENT_MAX :: 244 + _SC_XOPEN_STREAMS :: 245 + _SC_THREAD_ROBUST_PRIO_INHERIT :: 246 + _SC_THREAD_ROBUST_PRIO_PROTECT :: 247 + + // NOTE: Not implemented. + _SC_XOPEN_UUCP :: 0 + // NOTE: Not implemented. + _POSIX_VDISABLE :: 0 + +} 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 591a6db06..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 { @@ -24,13 +25,11 @@ when ODIN_OS == .NetBSD { @(private) LUTIME :: "utime" } -when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD { +when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux { utimbuf :: struct { actime: time_t, /* [PSX] access time (seconds since epoch) */ 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 d730db0f7..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" @@ -102,6 +103,25 @@ when ODIN_OS == .Darwin { WRDE_NOSPACE :: 4 WRDE_SYNTAX :: 6 -} else { - #panic("posix is unimplemented for the current target") +} else when ODIN_OS == .Linux { + + wordexp_t :: struct { + we_wordc: c.size_t, /* [PSX] count of words matched by words */ + we_wordv: [^]cstring, /* [PSX] pointer to list of expanded words */ + we_offs: c.size_t, /* [PSX] slots to reserve at the beginning of we_wordv */ + } + + WRDE_DOOFFS :: 1 << 0 /* Insert PWORDEXP->we_offs NULLs. */ + WRDE_APPEND :: 1 << 1 /* Append to results of a previous call. */ + WRDE_NOCMD :: 1 << 2 /* Don't do command substitution. */ + WRDE_REUSE :: 1 << 3 /* Reuse storage in PWORDEXP. */ + WRDE_SHOWERR :: 1 << 4 /* Don't redirect stderr to /dev/null. */ + WRDE_UNDEF :: 1 << 5 /* Error for expanding undefined variables. */ + + WRDE_NOSPACE :: 1 + WRDE_BADCHAR :: 2 + WRDE_BADVAL :: 3 + WRDE_CMDSUB :: 4 + WRDE_SYNTAX :: 5 + } 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/events.odin b/core/sys/wasm/js/events.odin index 905b3eba9..ffa3a1202 100644 --- a/core/sys/wasm/js/events.odin +++ b/core/sys/wasm/js/events.odin @@ -186,8 +186,8 @@ Key_Location :: enum u8 { Numpad = 3, } -KEYBOARD_MAX_KEY_SIZE :: 16 -KEYBOARD_MAX_CODE_SIZE :: 16 +KEYBOARD_MAX_KEY_SIZE :: 32 +KEYBOARD_MAX_CODE_SIZE :: 32 GAMEPAD_MAX_ID_SIZE :: 64 GAMEPAD_MAX_MAPPING_SIZE :: 64 diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js index bf002da74..07a77952c 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) { @@ -1535,8 +1548,8 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev wmi.storeInt(off(W, W), e.key.length) wmi.storeInt(off(W, W), e.code.length) - wmi.storeString(off(16, 1), e.key); - wmi.storeString(off(16, 1), e.code); + wmi.storeString(off(32, 1), e.key); + wmi.storeString(off(32, 1), e.code); } else if (e.type === 'scroll') { wmi.storeF64(off(8, 8), window.scrollX); wmi.storeF64(off(8, 8), window.scrollY); @@ -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) { @@ -1933,7 +1953,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory exports._start(); - // Define a `@export step :: proc(current_type: f64) -> (keep_going: bool) {` + // Define a `@export step :: proc(delta_time: f64) -> (keep_going: bool) {` // in your app and it will get called every frame. // return `false` to stop the execution of the module. if (exports.step) { @@ -1948,14 +1968,7 @@ 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(currTimeStamp*0.001, odin_ctx)) { + 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/kernel32.odin b/core/sys/windows/kernel32.odin index 2771581e6..8be50bceb 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -381,6 +381,14 @@ foreign kernel32 { nDefaultTimeOut: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, ) -> HANDLE --- + PeekNamedPipe :: proc( + hNamedPipe: HANDLE, + lpBuffer: rawptr, + nBufferSize: u32, + lpBytesRead: ^u32, + lpTotalBytesAvail: ^u32, + lpBytesLeftThisMessage: ^u32, + ) -> BOOL --- CancelIo :: proc(handle: HANDLE) -> BOOL --- GetOverlappedResult :: proc( hFile: HANDLE, diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 934d103e5..e1ace4133 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -253,8 +253,6 @@ FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE | FILE_APPEND_DATA | SYNCHRONIZE -FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000 -FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000 SECURITY_SQOS_PRESENT: DWORD : 0x00100000 FIONBIO: c_ulong : 0x8004667e @@ -2222,11 +2220,22 @@ WAIT_OBJECT_0: DWORD : 0x00000000 WAIT_TIMEOUT: DWORD : 258 WAIT_FAILED: DWORD : 0xFFFFFFFF +FILE_FLAG_WRITE_THROUGH: DWORD : 0x80000000 +FILE_FLAG_OVERLAPPED: DWORD : 0x40000000 +FILE_FLAG_NO_BUFFERING: DWORD : 0x20000000 +FILE_FLAG_RANDOM_ACCESS: DWORD : 0x10000000 +FILE_FLAG_SEQUENTIAL_SCAN: DWORD : 0x08000000 +FILE_FLAG_DELETE_ON_CLOSE: DWORD : 0x04000000 +FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000 +FILE_FLAG_POSIX_SEMANTICS: DWORD : 0x01000000 +FILE_FLAG_SESSION_AWARE: DWORD : 0x00800000 +FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000 +FILE_FLAG_OPEN_NO_RECALL: DWORD : 0x00100000 +FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000 + PIPE_ACCESS_INBOUND: DWORD : 0x00000001 PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002 PIPE_ACCESS_DUPLEX: DWORD : 0x00000003 -FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000 -FILE_FLAG_OVERLAPPED: DWORD : 0x40000000 PIPE_WAIT: DWORD : 0x00000000 PIPE_TYPE_BYTE: DWORD : 0x00000000 PIPE_TYPE_MESSAGE: DWORD : 0x00000004 diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin index 514592e7b..4ae33cd32 100644 --- a/core/sys/windows/user32.odin +++ b/core/sys/windows/user32.odin @@ -781,3 +781,64 @@ CF_GDIOBJLAST :: 0x03FF CF_OWNERDISPLAY :: 0x0080 CF_PRIVATEFIRST :: 0x0200 CF_PRIVATELAST :: 0x02FF + +STICKYKEYS :: struct { + cbSize: UINT, + dwFlags: DWORD, +} +LPSTICKYKEYS :: ^STICKYKEYS + +SKF_STICKYKEYSON :: 0x1 +SKF_AVAILABLE :: 0x2 +SKF_HOTKEYACTIVE :: 0x4 +SKF_CONFIRMHOTKEY :: 0x8 +SKF_HOTKEYSOUND :: 0x10 +SKF_INDICATOR :: 0x20 +SKF_AUDIBLEFEEDBACK :: 0x40 +SKF_TRISTATE :: 0x80 +SKF_TWOKEYSOFF :: 0x100 +SKF_LSHIFTLOCKED :: 0x10000 +SKF_RSHIFTLOCKED :: 0x20000 +SKF_LCTLLOCKED :: 0x40000 +SKF_RCTLLOCKED :: 0x80000 +SKF_LALTLOCKED :: 0x100000 +SKF_RALTLOCKED :: 0x200000 +SKF_LWINLOCKED :: 0x400000 +SKF_RWINLOCKED :: 0x800000 +SKF_LSHIFTLATCHED :: 0x1000000 +SKF_RSHIFTLATCHED :: 0x2000000 +SKF_LCTLLATCHED :: 0x4000000 +SKF_RCTLLATCHED :: 0x8000000 +SKF_LALTLATCHED :: 0x10000000 +SKF_RALTLATCHED :: 0x20000000 + +TOGGLEKEYS :: struct { + cbSize: UINT, + dwFlags: DWORD, +} +LPTOGGLEKEYS :: ^TOGGLEKEYS + +TKF_TOGGLEKEYSON :: 0x1 +TKF_AVAILABLE :: 0x2 +TKF_HOTKEYACTIVE :: 0x4 +TKF_CONFIRMHOTKEY :: 0x8 +TKF_HOTKEYSOUND :: 0x10 +TKF_INDICATOR :: 0x20 + +FILTERKEYS :: struct { + cbSize: UINT, + dwFlags: DWORD, + iWaitMSec: DWORD, + iDelayMSec: DWORD, + iRepeatMSec: DWORD, + iBounceMSec: DWORD, +} +LPFILTERKEYS :: ^FILTERKEYS + +FKF_FILTERKEYSON :: 0x1 +FKF_AVAILABLE :: 0x2 +FKF_HOTKEYACTIVE :: 0x4 +FKF_CONFIRMHOTKEY :: 0x8 +FKF_HOTKEYSOUND :: 0x10 +FKF_INDICATOR :: 0x20 +FKF_CLICKON :: 0x40 diff --git a/core/sys/windows/ux_theme.odin b/core/sys/windows/ux_theme.odin index 527abd62f..392cf1e18 100644 --- a/core/sys/windows/ux_theme.odin +++ b/core/sys/windows/ux_theme.odin @@ -3,7 +3,7 @@ package sys_windows foreign import uxtheme "system:UxTheme.lib" -MARGINS :: distinct [4]int +MARGINS :: distinct [4]i32 PMARGINS :: ^MARGINS @(default_calling_convention="system") 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/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index d27c66f24..24dbcc8a4 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -2,7 +2,7 @@ // It takes a string providing the source, which then can be tokenized through // repeated calls to the scan procedure. // For compatibility with existing tooling and languages, the NUL character is not allowed. -// If an UTF-8 encoded byte order mark (BOM) is the first character in the first character in the source, it will be discarded. +// If an UTF-8 encoded byte order mark (BOM) is the first character in the source, it will be discarded. // // By default, a Scanner skips white space and Odin comments and recognizes all literals defined by the Odin programming language specification. // A Scanner may be customized to recognize only a subset of those literals and to recognize different identifiers and white space characters. 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/time_linux.odin b/core/time/time_linux.odin index 649f601dc..4e557766e 100644 --- a/core/time/time_linux.odin +++ b/core/time/time_linux.odin @@ -6,8 +6,8 @@ _IS_SUPPORTED :: true _now :: proc "contextless" () -> Time { time_spec_now, _ := linux.clock_gettime(.REALTIME) - ns := time_spec_now.time_sec * 1e9 + time_spec_now.time_nsec - return Time{_nsec=i64(ns)} + ns := i64(time_spec_now.time_sec) * 1e9 + i64(time_spec_now.time_nsec) + return Time{_nsec=ns} } _sleep :: proc "contextless" (d: Duration) { @@ -29,7 +29,7 @@ _sleep :: proc "contextless" (d: Duration) { _tick_now :: proc "contextless" () -> Tick { t, _ := linux.clock_gettime(.MONOTONIC_RAW) - return Tick{_nsec = i64(t.time_sec*1e9 + t.time_nsec)} + return Tick{_nsec = i64(t.time_sec)*1e9 + i64(t.time_nsec)} } _yield :: proc "contextless" () { 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..238c4c933 --- /dev/null +++ b/core/time/timezone/tz_windows.odin @@ -0,0 +1,311 @@ +#+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) } + + if (tzi.std_date.month == 0) { + return datetime.TZ_RRule{ + has_dst = false, + + 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), + }, + }, true + } + + 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..96df44299 --- /dev/null +++ b/core/time/timezone/tzdate.odin @@ -0,0 +1,341 @@ +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: + if td.month < 1 { return } + + 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 |