From 91111937a71b3b86f447b68e932487f4c0a31109 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Wed, 4 Jun 2025 13:58:06 -0400 Subject: [PATCH 01/35] Guard against negative `index` in `inject_at` Fixes #4595 --- base/runtime/core_builtin.odin | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/base/runtime/core_builtin.odin b/base/runtime/core_builtin.odin index cf379aacb..bc201b6e1 100644 --- a/base/runtime/core_builtin.odin +++ b/base/runtime/core_builtin.odin @@ -648,6 +648,9 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i @builtin inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } if array == nil { return } @@ -666,6 +669,9 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcas @builtin inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } if array == nil { return } @@ -689,6 +695,9 @@ inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadca @builtin inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error { + when !ODIN_NO_BOUNDS_CHECK { + ensure(index >= 0, "Index must be positive.", loc) + } if array == nil { return } From b0d050dd90561c466a800dd4b7e5bb6d6bcaa499 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Wed, 4 Jun 2025 13:59:31 -0400 Subject: [PATCH 02/35] Print timings to stderr instead of stdout This is in line with other diagnostic messages. Fixes #4642 --- src/main.cpp | 104 ++++++++++++++++++++++++------------------------ src/timings.cpp | 4 +- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index ad07c9a12..d7fb66f99 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1996,39 +1996,39 @@ gb_internal void show_timings(Checker *c, Timings *t) { if (build_context.show_debug_messages && build_context.show_more_timings) { { - gb_printf("\n"); - gb_printf("Total Lines - %td\n", lines); - gb_printf("Total Tokens - %td\n", tokens); - gb_printf("Total Files - %td\n", files); - gb_printf("Total Packages - %td\n", packages); - gb_printf("Total File Size - %td\n", total_file_size); - gb_printf("\n"); + gb_printf_err("\n"); + gb_printf_err("Total Lines - %td\n", lines); + gb_printf_err("Total Tokens - %td\n", tokens); + gb_printf_err("Total Files - %td\n", files); + gb_printf_err("Total Packages - %td\n", packages); + gb_printf_err("Total File Size - %td\n", total_file_size); + gb_printf_err("\n"); } { f64 time = total_tokenizing_time; - gb_printf("Tokenization Only\n"); - gb_printf("LOC/s - %.3f\n", cast(f64)lines/time); - gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); - gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time); - gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); - gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time); - gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); - gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); + gb_printf_err("Tokenization Only\n"); + gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time); + gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); + gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time); + gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); + gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time); + gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); + gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); - gb_printf("\n"); + gb_printf_err("\n"); } { f64 time = total_parsing_time; - gb_printf("Parsing Only\n"); - gb_printf("LOC/s - %.3f\n", cast(f64)lines/time); - gb_printf("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); - gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/time); - gb_printf("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); - gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/time); - gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); - gb_printf("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); + gb_printf_err("Parsing Only\n"); + gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/time); + gb_printf_err("us/LOC - %.3f\n", 1.0e6*time/cast(f64)lines); + gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/time); + gb_printf_err("us/Token - %.3f\n", 1.0e6*time/cast(f64)tokens); + gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/time); + gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/time)/(1024*1024)); + gb_printf_err("us/bytes - %.3f\n", 1.0e6*time/cast(f64)total_file_size); - gb_printf("\n"); + gb_printf_err("\n"); } { TimeStamp ts = {}; @@ -2041,16 +2041,16 @@ gb_internal void show_timings(Checker *c, Timings *t) { GB_ASSERT(ts.label == "parse files"); f64 parse_time = time_stamp_as_s(ts, t->freq); - gb_printf("Parse pass\n"); - gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time); - gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); - gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); - gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); - gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); - gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024)); - gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); + gb_printf_err("Parse pass\n"); + gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time); + gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); + gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); + gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); + gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); + gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/parse_time)/(1024*1024)); + gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); - gb_printf("\n"); + gb_printf_err("\n"); } { TimeStamp ts = {}; @@ -2071,27 +2071,27 @@ gb_internal void show_timings(Checker *c, Timings *t) { ts.finish = ts_end.finish; f64 parse_time = time_stamp_as_s(ts, t->freq); - gb_printf("Checker pass\n"); - gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time); - gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); - gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); - gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); - gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); - gb_printf("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024)); - gb_printf("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); - gb_printf("\n"); + gb_printf_err("Checker pass\n"); + gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/parse_time); + gb_printf_err("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines); + gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/parse_time); + gb_printf_err("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens); + gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/parse_time); + gb_printf_err("MiB/s - %.3f\n", (cast(f64)total_file_size/parse_time)/(1024*1024)); + gb_printf_err("us/bytes - %.3f\n", 1.0e6*parse_time/cast(f64)total_file_size); + gb_printf_err("\n"); } { f64 total_time = t->total_time_seconds; - gb_printf("Total pass\n"); - gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time); - gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines); - gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time); - gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens); - gb_printf("bytes/s - %.3f\n", cast(f64)total_file_size/total_time); - gb_printf("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024)); - gb_printf("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size); - gb_printf("\n"); + gb_printf_err("Total pass\n"); + gb_printf_err("LOC/s - %.3f\n", cast(f64)lines/total_time); + gb_printf_err("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines); + gb_printf_err("Tokens/s - %.3f\n", cast(f64)tokens/total_time); + gb_printf_err("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens); + gb_printf_err("bytes/s - %.3f\n", cast(f64)total_file_size/total_time); + gb_printf_err("MiB/s - %.3f\n", cast(f64)(total_file_size/total_time)/(1024*1024)); + gb_printf_err("us/bytes - %.3f\n", 1.0e6*total_time/cast(f64)total_file_size); + gb_printf_err("\n"); } } } diff --git a/src/timings.cpp b/src/timings.cpp index 3f8402b36..f6e86867f 100644 --- a/src/timings.cpp +++ b/src/timings.cpp @@ -197,7 +197,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill f64 total_time = time_stamp(t->total, t->freq, unit); - gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n", + gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n", LIT(t->total.label), cast(int)(max_len-t->total.label.len), SPACES, total_time, @@ -207,7 +207,7 @@ gb_internal void timings_print_all(Timings *t, TimingUnit unit = TimingUnit_Mill for_array(i, t->sections) { TimeStamp ts = t->sections[i]; f64 section_time = time_stamp(ts, t->freq, unit); - gb_printf("%.*s%.*s - % 9.3f %s - %6.2f%%\n", + gb_printf_err("%.*s%.*s - % 9.3f %s - %6.2f%%\n", LIT(ts.label), cast(int)(max_len-ts.label.len), SPACES, section_time, From d046214f67baf9c580605fa4294818e7a12fc434 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Wed, 4 Jun 2025 14:00:33 -0400 Subject: [PATCH 03/35] Be strict with type switch case column alignment too This copies the same block used for regular switch cases. Fixes #4673 --- src/check_stmt.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 6cacef3ee..d92edf41d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1593,6 +1593,20 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_ error_line("\tSuggestion: Was '#partial switch' wanted?\n"); } } + + if (build_context.strict_style) { + Token stok = ss->token; + for_array(i, bs->stmts) { + Ast *stmt = bs->stmts[i]; + if (stmt->kind != Ast_CaseClause) { + continue; + } + Token ctok = stmt->CaseClause.token; + if (ctok.pos.column > stok.pos.column) { + error(ctok, "With '-strict-style', 'case' statements must share the same column as the 'switch' token"); + } + } + } } gb_internal void check_block_stmt_for_errors(CheckerContext *ctx, Ast *body) { From 85224b21e69678923b5a514586d50730b8d823a3 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 4 Jun 2025 21:51:12 +0200 Subject: [PATCH 04/35] encoding/cbor: support the matrix type --- core/encoding/cbor/marshal.odin | 18 ++++++++++++++++++ core/encoding/cbor/unmarshal.odin | 13 +++++++++++++ tests/core/encoding/cbor/test_core_cbor.odin | 20 ++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index aca71deb2..ec597c8a9 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -612,6 +612,24 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er case: panic("unknown bit_size size") } + case runtime.Type_Info_Matrix: + count := info.column_count * info.elem_stride + err_conv(_encode_u64(e, u64(count), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) + for i in 0.. count { + return _unsupported(v, hdr) + } + + da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator } + + out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return + if out_of_space { return _unsupported(v, hdr) } + return + case: return _unsupported(v, hdr) } } diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index ee853ebac..a76d690be 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -43,6 +43,7 @@ Foo :: struct { biggest: big.Int, smallest: big.Int, ignore_this: ^Foo `cbor:"-"`, + mat: matrix[4, 4]f32, } FooBar :: enum { @@ -95,6 +96,7 @@ test_marshalling :: proc(t: ^testing.T) { onetwenty = i128(12345), small_onetwenty = -i128(max(u64)), ignore_this = &Foo{}, + mat = 1, } big.atoi(&f.biggest, "1234567891011121314151617181920") @@ -120,6 +122,24 @@ test_marshalling :: proc(t: ^testing.T) { defer delete(diagnosis) testing.expect_value(t, diagnosis, `{ "no": null, + "mat": [ + 1.0000, + 0.0000, + 0.0000, + 0.0000, + 0.0000, + 1.0000, + 0.0000, + 0.0000, + 0.0000, + 0.0000, + 1.0000, + 0.0000, + 0.0000, + 0.0000, + 0.0000, + 1.0000 + ], "neg": -69, "nos": undefined, "now": 1(1701117968), From 9eefa2006eccdf942c01fc1421784edea78c5591 Mon Sep 17 00:00:00 2001 From: Laytan Laats Date: Wed, 4 Jun 2025 22:00:02 +0200 Subject: [PATCH 05/35] encoding/cbor: support simd vectors --- core/encoding/cbor/marshal.odin | 18 ++++++++++++++++++ core/encoding/cbor/unmarshal.odin | 12 ++++++++++++ tests/core/encoding/cbor/test_core_cbor.odin | 8 ++++++++ 3 files changed, 38 insertions(+) diff --git a/core/encoding/cbor/marshal.odin b/core/encoding/cbor/marshal.odin index ec597c8a9..b23087c90 100644 --- a/core/encoding/cbor/marshal.odin +++ b/core/encoding/cbor/marshal.odin @@ -630,6 +630,24 @@ _marshal_into_encoder :: proc(e: Encoder, v: any, ti: ^runtime.Type_Info) -> (er _marshal_into_encoder(e, any{rawptr(data), info.elem.id}, elem_ti) or_return } return + + case runtime.Type_Info_Simd_Vector: + err_conv(_encode_u64(e, u64(info.count), .Array)) or_return + + if impl, ok := _tag_implementations_type[info.elem.id]; ok { + for i in 0..marshal(e, any{rawptr(data), info.elem.id}) or_return + } + return + } + + elem_ti := runtime.type_info_core(type_info_of(info.elem.id)) + for i in 0.. t.count { + return _unsupported(v, hdr) + } + + da := mem.Raw_Dynamic_Array{rawptr(v.data), 0, length, allocator } + + out_of_space := assign_array(d, &da, t.elem, length, growable=false) or_return + if out_of_space { return _unsupported(v, hdr) } + return + case: return _unsupported(v, hdr) } } diff --git a/tests/core/encoding/cbor/test_core_cbor.odin b/tests/core/encoding/cbor/test_core_cbor.odin index a76d690be..7bea69d2e 100644 --- a/tests/core/encoding/cbor/test_core_cbor.odin +++ b/tests/core/encoding/cbor/test_core_cbor.odin @@ -44,6 +44,7 @@ Foo :: struct { smallest: big.Int, ignore_this: ^Foo `cbor:"-"`, mat: matrix[4, 4]f32, + vec: #simd [4]f64, } FooBar :: enum { @@ -97,6 +98,7 @@ test_marshalling :: proc(t: ^testing.T) { small_onetwenty = -i128(max(u64)), ignore_this = &Foo{}, mat = 1, + vec = 2, } big.atoi(&f.biggest, "1234567891011121314151617181920") @@ -145,6 +147,12 @@ test_marshalling :: proc(t: ^testing.T) { "now": 1(1701117968), "pos": 1212, "str": "Hellope", + "vec": [ + 2.0000, + 2.0000, + 2.0000, + 2.0000 + ], "yes": true, "comp": [ 32.0000, From 36b41ce163843c9524d789e6edbc171c1d723e85 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 07:31:03 -0400 Subject: [PATCH 06/35] Let compound literal array be broadcast to a struct field of arrays Fixes #4364 Patch courtesy of @cribalik --- src/llvm_backend_const.cpp | 11 +++++++++++ tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + tests/issues/test_issue_4364.odin | 18 ++++++++++++++++++ 4 files changed, 31 insertions(+) create mode 100644 tests/issues/test_issue_4364.odin diff --git a/src/llvm_backend_const.cpp b/src/llvm_backend_const.cpp index 02bb7473c..e897ae282 100644 --- a/src/llvm_backend_const.cpp +++ b/src/llvm_backend_const.cpp @@ -1054,9 +1054,20 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, lb } } + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); + return res; + } else if (value.value_compound->tav.type == elem_type) { + // Compound is of array item type; expand its value to all items in array. + LLVMValueRef* values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); + + for (isize i = 0; i < type->Array.count; i++) { + values[i] = lb_const_value(m, elem_type, value, cc).value; + } + res.value = lb_build_constant_array_values(m, type, elem_type, cast(isize)type->Array.count, values, cc); return res; } else { + // Assume that compound value is an array literal GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, cast(isize)type->Array.count); diff --git a/tests/issues/run.bat b/tests/issues/run.bat index bbe9b7ca6..8e71c3f3d 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -17,6 +17,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style ..\..\..\odin test ..\test_issue_2637.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_2666.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_4210.odin %COMMON% || exit /b +..\..\..\odin test ..\test_issue_4364.odin %COMMON% || exit /b ..\..\..\odin test ..\test_issue_4584.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5043.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_5097.odin %COMMON% || exit /b diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 228efce7f..fc8ab513f 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -18,6 +18,7 @@ $ODIN test ../test_issue_2615.odin $COMMON $ODIN test ../test_issue_2637.odin $COMMON $ODIN test ../test_issue_2666.odin $COMMON $ODIN test ../test_issue_4210.odin $COMMON +$ODIN test ../test_issue_4364.odin $COMMON $ODIN test ../test_issue_4584.odin $COMMON if [[ $($ODIN build ../test_issue_2395.odin $COMMON 2>&1 >/dev/null | grep -c "Error:") -eq 2 ]] ; then echo "SUCCESSFUL 1/1" diff --git a/tests/issues/test_issue_4364.odin b/tests/issues/test_issue_4364.odin new file mode 100644 index 000000000..817a1a26b --- /dev/null +++ b/tests/issues/test_issue_4364.odin @@ -0,0 +1,18 @@ +// Tests issue #4364 https://github.com/odin-lang/Odin/issues/4364 +package test_issues + +import "core:testing" + +@test +test_const_array_fill_assignment :: proc(t: ^testing.T) { + MAGIC :: 12345 + Struct :: struct {x: int} + CONST_ARR : [4]Struct : Struct{MAGIC} + arr := CONST_ARR + + testing.expect_value(t, len(arr), 4) + testing.expect_value(t, arr[0], Struct{MAGIC}) + testing.expect_value(t, arr[1], Struct{MAGIC}) + testing.expect_value(t, arr[2], Struct{MAGIC}) + testing.expect_value(t, arr[3], Struct{MAGIC}) +} From da3b3a4139f869561f1992e1d93f27d5c48597f0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Jun 2025 15:16:35 +0200 Subject: [PATCH 07/35] Allocate return value on provided allocator --- core/os/os2/user.odin | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index b2856a319..51ee60b1a 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -10,12 +10,12 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error case .Windows: dir = get_env("LocalAppData", temp_allocator) if dir != "" { - dir = clone_string(dir, temp_allocator) or_return + dir = clone_string(dir, allocator) or_return } case .Darwin: dir = get_env("HOME", temp_allocator) if dir != "" { - dir = concatenate({dir, "/Library/Caches"}, temp_allocator) or_return + dir = concatenate({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) @@ -24,7 +24,7 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error if dir == "" { return } - dir = concatenate({dir, "/.cache"}, temp_allocator) or_return + dir = concatenate({dir, "/.cache"}, allocator) or_return } } if dir == "" { @@ -49,7 +49,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro dir = concatenate({dir, "/.config"}, allocator) or_return } case: // All other UNIX systems - dir = get_env("XDG_CONFIG_HOME", allocator) + dir = get_env("XDG_CONFIG_HOME", temp_allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { From 93e1c6593e3a6ee132e3a0a75ed289adab2c7835 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Jun 2025 16:37:41 +0200 Subject: [PATCH 08/35] Change `os2.user_*` on Windows to use `SHGetKnownFolderPath`. Known folders can be redirected using `SHSetKnownFolderPath`, and it's a bit iffy to rely on environment variables. This also more easily allows us to add `user_*_dir` procedures for the remaining 139 GUIDs in `known_folders.odin`, provided they have equivalents on other platforms. --- core/os/os2/user.odin | 28 +++++++++++++--------------- core/os/os2/user_windows.odin | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 15 deletions(-) create mode 100644 core/os/os2/user_windows.odin diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 51ee60b1a..83dc564ac 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -1,6 +1,7 @@ package os2 import "base:runtime" +@(require) import win32 "core:sys/windows" @(require_results) user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { @@ -8,10 +9,8 @@ user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error #partial switch ODIN_OS { case .Windows: - dir = get_env("LocalAppData", temp_allocator) - if dir != "" { - dir = clone_string(dir, allocator) or_return - } + guid := win32.FOLDERID_LocalAppData + return _get_known_folder_path(&guid, allocator) case .Darwin: dir = get_env("HOME", temp_allocator) if dir != "" { @@ -39,10 +38,8 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro #partial switch ODIN_OS { case .Windows: - dir = get_env("AppData", temp_allocator) - if dir != "" { - dir = clone_string(dir, allocator) or_return - } + guid := win32.FOLDERID_RoamingAppData + return _get_known_folder_path(&guid, allocator) case .Darwin: dir = get_env("HOME", temp_allocator) if dir != "" { @@ -66,14 +63,15 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro @(require_results) user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - env := "HOME" #partial switch ODIN_OS { case .Windows: - env = "USERPROFILE" - } - if v := get_env(env, allocator); v != "" { - return v, nil + guid := win32.FOLDERID_Profile + return _get_known_folder_path(&guid, allocator) + case: + if v := get_env("HOME", allocator); v != "" { + return v, nil + } + } return "", .Invalid_Path -} - +} \ No newline at end of file diff --git a/core/os/os2/user_windows.odin b/core/os/os2/user_windows.odin new file mode 100644 index 000000000..6aef6cb4a --- /dev/null +++ b/core/os/os2/user_windows.odin @@ -0,0 +1,20 @@ +package os2 + +import "base:runtime" +@(require) import win32 "core:sys/windows" + +@(require_results) +_get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.Allocator) -> (dir: string, err: Error) { + // https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + // See also `known_folders.odin` in `core:sys/windows` for the GUIDs. + path_w: win32.LPWSTR + res := win32.SHGetKnownFolderPath(rfid, 0, nil, &path_w) + defer win32.CoTaskMemFree(path_w) + + if res != 0 { + return "", .Invalid_Path + } + + dir, _ = win32.wstring_to_utf8(path_w, -1, allocator) + return +} \ No newline at end of file From a1759aa2fafcb000359436d967d58a67a9b1a782 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Jun 2025 16:54:39 +0200 Subject: [PATCH 09/35] Fix --- core/os/os2/{user.odin => user_posix.odin} | 19 +++---------------- core/os/os2/user_windows.odin | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 16 deletions(-) rename core/os/os2/{user.odin => user_posix.odin} (75%) diff --git a/core/os/os2/user.odin b/core/os/os2/user_posix.odin similarity index 75% rename from core/os/os2/user.odin rename to core/os/os2/user_posix.odin index 83dc564ac..c49d51bb3 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user_posix.odin @@ -1,16 +1,13 @@ +#+build !windows package os2 import "base:runtime" -@(require) import win32 "core:sys/windows" @(require_results) user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) #partial switch ODIN_OS { - case .Windows: - guid := win32.FOLDERID_LocalAppData - return _get_known_folder_path(&guid, allocator) case .Darwin: dir = get_env("HOME", temp_allocator) if dir != "" { @@ -37,9 +34,6 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) #partial switch ODIN_OS { - case .Windows: - guid := win32.FOLDERID_RoamingAppData - return _get_known_folder_path(&guid, allocator) case .Darwin: dir = get_env("HOME", temp_allocator) if dir != "" { @@ -63,15 +57,8 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro @(require_results) user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - #partial switch ODIN_OS { - case .Windows: - guid := win32.FOLDERID_Profile - return _get_known_folder_path(&guid, allocator) - case: - if v := get_env("HOME", allocator); v != "" { - return v, nil - } - + if v := get_env("HOME", allocator); v != "" { + return v, nil } return "", .Invalid_Path } \ No newline at end of file diff --git a/core/os/os2/user_windows.odin b/core/os/os2/user_windows.odin index 6aef6cb4a..fd007af03 100644 --- a/core/os/os2/user_windows.odin +++ b/core/os/os2/user_windows.odin @@ -3,6 +3,24 @@ package os2 import "base:runtime" @(require) import win32 "core:sys/windows" +@(require_results) +user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_LocalAppData + return _get_known_folder_path(&guid, allocator) +} + +@(require_results) +user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_RoamingAppData + return _get_known_folder_path(&guid, allocator) +} + +@(require_results) +user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Profile + return _get_known_folder_path(&guid, allocator) +} + @(require_results) _get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.Allocator) -> (dir: string, err: Error) { // https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath From ccdd14394efb4d5e8341491528f5fc141a6d9e14 Mon Sep 17 00:00:00 2001 From: Hayden Gray Date: Thu, 5 Jun 2025 11:24:50 -0400 Subject: [PATCH 10/35] [parser.cpp] - packages with `.odin` in the name no longer attempt to parse as odin files --- src/parser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser.cpp b/src/parser.cpp index 0057ab611..942e83f29 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -5794,7 +5794,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const for (FileInfo fi : list) { String name = fi.name; String ext = path_extension(name); - if (ext == FILE_EXT) { + if (ext == FILE_EXT && !path_is_directory(name)) { files_with_ext += 1; } if (ext == FILE_EXT && !is_excluded_target_filename(name)) { @@ -5819,7 +5819,7 @@ gb_internal AstPackage *try_add_import_path(Parser *p, String path, String const for (FileInfo fi : list) { String name = fi.name; String ext = path_extension(name); - if (ext == FILE_EXT) { + if (ext == FILE_EXT && !path_is_directory(name)) { if (is_excluded_target_filename(name)) { continue; } From a9cdb4f446825c44128147473fe658d8a5fcdfa1 Mon Sep 17 00:00:00 2001 From: Ely Alon Date: Thu, 5 Jun 2025 21:50:59 +0300 Subject: [PATCH 11/35] add more user dirs --- core/os/os2/user.odin | 66 ++++++++++ core/os/os2/user_posix.odin | 234 +++++++++++++++++++++++++++++++--- core/os/os2/user_windows.odin | 49 ++++++- 3 files changed, 325 insertions(+), 24 deletions(-) create mode 100644 core/os/os2/user.odin diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin new file mode 100644 index 000000000..a4b958921 --- /dev/null +++ b/core/os/os2/user.odin @@ -0,0 +1,66 @@ +package os2 + +import "base:runtime" + +@(require_results) +user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_home_dir(allocator) +} + +// application caches, logs, temporary files +@(require_results) +user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_cache_dir(allocator) +} + +// application assets +@(require_results) +user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_data_dir(allocator) +} + +// application history, ui layout state, logs +@(require_results) +user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_state_dir(allocator) +} + +@(require_results) +user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_config_dir(allocator) +} + +@(require_results) +user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_music_dir(allocator) +} + +@(require_results) +user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_desktop_dir(allocator) +} + +@(require_results) +user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_documents_dir(allocator) +} + +@(require_results) +user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_downloads_dir(allocator) +} + +@(require_results) +user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_pictures_dir(allocator) +} + +@(require_results) +user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_public_dir(allocator) +} + +@(require_results) +user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_videos_dir(allocator) +} diff --git a/core/os/os2/user_posix.odin b/core/os/os2/user_posix.odin index c49d51bb3..ce37015ab 100644 --- a/core/os/os2/user_posix.odin +++ b/core/os/os2/user_posix.odin @@ -3,60 +3,260 @@ package os2 import "base:runtime" -@(require_results) -user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) #partial switch ODIN_OS { case .Darwin: dir = get_env("HOME", temp_allocator) - if dir != "" { - dir = concatenate({dir, "/Library/Caches"}, allocator) or_return + if dir == "" { + return "", .Invalid_Path } + dir = concatenate({dir, "/Library/Caches"}, allocator) or_return case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { - return + return "", .Invalid_Path } dir = concatenate({dir, "/.cache"}, allocator) or_return } } - if dir == "" { - err = .Invalid_Path - } return } -@(require_results) -user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) #partial switch ODIN_OS { case .Darwin: dir = get_env("HOME", temp_allocator) - if dir != "" { - dir = concatenate({dir, "/.config"}, allocator) or_return + if dir == "" { + return "", .Invalid_Path } + dir = concatenate({dir, "/.config"}, allocator) or_return case: // All other UNIX systems dir = get_env("XDG_CONFIG_HOME", temp_allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { - return + return "", .Invalid_Path } dir = concatenate({dir, "/.config"}, allocator) or_return } } - if dir == "" { - err = .Invalid_Path + return +} + +_user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/.local/state"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_STATE_HOME", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/.local/state"}, allocator) or_return + } } return } -@(require_results) -user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/.local/share"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_DATA_HOME", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/.local/share"}, allocator) or_return + } + } + return +} + +_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Music"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_MUSIC_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Music"}, allocator) or_return + } + } + return +} + +_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Desktop"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_DESKTOP_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Desktop"}, allocator) or_return + } + } + return +} + +_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Documents"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_DOCUMENTS_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Documents"}, allocator) or_return + } + } + return +} + +_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Downloads"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_DOWNLOAD_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Downloads"}, allocator) or_return + } + } + return +} + +_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Pictures"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_PICTURES_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Pictures"}, allocator) or_return + } + } + return +} + +_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Public"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_PUBLIC_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Public"}, allocator) or_return + } + } + return +} + +_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + #partial switch ODIN_OS { + case .Darwin: + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Movies"}, allocator) or_return + case: // All other UNIX systems + dir = get_env("XDG_VIDEOS_DIR", temp_allocator) + if dir == "" { + dir = get_env("HOME", temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, "/Videos"}, allocator) or_return + } + } + return +} + +_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { if v := get_env("HOME", allocator); v != "" { return v, nil } diff --git a/core/os/os2/user_windows.odin b/core/os/os2/user_windows.odin index fd007af03..94ac3c566 100644 --- a/core/os/os2/user_windows.odin +++ b/core/os/os2/user_windows.odin @@ -3,25 +3,60 @@ package os2 import "base:runtime" @(require) import win32 "core:sys/windows" -@(require_results) -user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { guid := win32.FOLDERID_LocalAppData return _get_known_folder_path(&guid, allocator) } -@(require_results) -user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { guid := win32.FOLDERID_RoamingAppData return _get_known_folder_path(&guid, allocator) } -@(require_results) -user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { guid := win32.FOLDERID_Profile return _get_known_folder_path(&guid, allocator) } -@(require_results) +_user_data_dir :: _user_config_dir + +_user_state_dir :: _user_cache_dir + +_user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Music + return _get_known_folder_path(&guid, allocator) +} + +_user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Desktop + return _get_known_folder_path(&guid, allocator) +} + +_user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Documents + return _get_known_folder_path(&guid, allocator) +} + +_user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Downloads + return _get_known_folder_path(&guid, allocator) +} + +_user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Pictures + return _get_known_folder_path(&guid, allocator) +} + +_user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Public + return _get_known_folder_path(&guid, allocator) +} + +_user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + guid := win32.FOLDERID_Videos + return _get_known_folder_path(&guid, allocator) +} + _get_known_folder_path :: proc(rfid: win32.REFKNOWNFOLDERID, allocator: runtime.Allocator) -> (dir: string, err: Error) { // https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath // See also `known_folders.odin` in `core:sys/windows` for the GUIDs. From 2428fa37a969b328f8f64106a1787eb55d9a34c8 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Jun 2025 21:38:59 +0200 Subject: [PATCH 12/35] tabs --- core/os/os2/user.odin | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index a4b958921..0f6134f21 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -4,63 +4,63 @@ import "base:runtime" @(require_results) user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_home_dir(allocator) + return _user_home_dir(allocator) } // application caches, logs, temporary files @(require_results) user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_cache_dir(allocator) + return _user_cache_dir(allocator) } // application assets @(require_results) user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_data_dir(allocator) + return _user_data_dir(allocator) } // application history, ui layout state, logs @(require_results) user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_state_dir(allocator) + return _user_state_dir(allocator) } @(require_results) user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_config_dir(allocator) + return _user_config_dir(allocator) } @(require_results) user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_music_dir(allocator) + return _user_music_dir(allocator) } @(require_results) user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_desktop_dir(allocator) + return _user_desktop_dir(allocator) } @(require_results) user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_documents_dir(allocator) + return _user_documents_dir(allocator) } @(require_results) user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_downloads_dir(allocator) + return _user_downloads_dir(allocator) } @(require_results) user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_pictures_dir(allocator) + return _user_pictures_dir(allocator) } @(require_results) user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_public_dir(allocator) + return _user_public_dir(allocator) } @(require_results) user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_videos_dir(allocator) -} + return _user_videos_dir(allocator) +} \ No newline at end of file From 90a1b0ec4b5a0630be329d086f4c08a7c54a74e7 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Jun 2025 21:45:36 +0200 Subject: [PATCH 13/35] UAF --- core/os/os2/user_posix.odin | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/os/os2/user_posix.odin b/core/os/os2/user_posix.odin index ce37015ab..f5ee427b3 100644 --- a/core/os/os2/user_posix.odin +++ b/core/os/os2/user_posix.odin @@ -37,7 +37,7 @@ _user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Err } dir = concatenate({dir, "/.config"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_CONFIG_HOME", temp_allocator) + dir = get_env("XDG_CONFIG_HOME", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -60,7 +60,7 @@ _user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro } dir = concatenate({dir, "/.local/state"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_STATE_HOME", temp_allocator) + dir = get_env("XDG_STATE_HOME", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -83,7 +83,7 @@ _user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error } dir = concatenate({dir, "/.local/share"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_DATA_HOME", temp_allocator) + dir = get_env("XDG_DATA_HOME", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -106,7 +106,7 @@ _user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro } dir = concatenate({dir, "/Music"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_MUSIC_DIR", temp_allocator) + dir = get_env("XDG_MUSIC_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -129,7 +129,7 @@ _user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Er } dir = concatenate({dir, "/Desktop"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_DESKTOP_DIR", temp_allocator) + dir = get_env("XDG_DESKTOP_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -152,7 +152,7 @@ _user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: } dir = concatenate({dir, "/Documents"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_DOCUMENTS_DIR", temp_allocator) + dir = get_env("XDG_DOCUMENTS_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -175,7 +175,7 @@ _user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: } dir = concatenate({dir, "/Downloads"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_DOWNLOAD_DIR", temp_allocator) + dir = get_env("XDG_DOWNLOAD_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -198,7 +198,7 @@ _user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: E } dir = concatenate({dir, "/Pictures"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_PICTURES_DIR", temp_allocator) + dir = get_env("XDG_PICTURES_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -221,7 +221,7 @@ _user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Err } dir = concatenate({dir, "/Public"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_PUBLIC_DIR", temp_allocator) + dir = get_env("XDG_PUBLIC_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { @@ -244,7 +244,7 @@ _user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Err } dir = concatenate({dir, "/Movies"}, allocator) or_return case: // All other UNIX systems - dir = get_env("XDG_VIDEOS_DIR", temp_allocator) + dir = get_env("XDG_VIDEOS_DIR", allocator) if dir == "" { dir = get_env("HOME", temp_allocator) if dir == "" { From 9c5640886d95cba73b10a59a43692c9bae4037fb Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 14:36:55 -0400 Subject: [PATCH 14/35] Add `@(no_sanitize_memory)` proc attribute with MSan additions to `base:sanitizer` --- base/sanitizer/memory.odin | 74 ++++++++++++++++++++++++++++++++++++++ src/check_decl.cpp | 1 + src/checker.cpp | 6 ++++ src/checker.hpp | 1 + src/entity.cpp | 1 + src/llvm_backend_proc.cpp | 2 +- 6 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 base/sanitizer/memory.odin diff --git a/base/sanitizer/memory.odin b/base/sanitizer/memory.odin new file mode 100644 index 000000000..b16309a49 --- /dev/null +++ b/base/sanitizer/memory.odin @@ -0,0 +1,74 @@ +#+no-instrumentation +package sanitizer + +@(private="file") +MSAN_ENABLED :: .Memory in ODIN_SANITIZER_FLAGS + +@(private="file") +@(default_calling_convention="system") +foreign { + __msan_unpoison :: proc(addr: rawptr, size: uint) --- +} + +/* +Marks a slice as fully initialized. + +Code instrumented with `-sanitize:memory` will be permitted to access any +address within the slice as if it had already been initialized. + +When msan is not enabled this procedure does nothing. +*/ +memory_unpoison_slice :: proc "contextless" (region: $T/[]$E) { + when MSAN_ENABLED { + __msan_unpoison(raw_data(region), size_of(E) * len(region)) + } +} + +/* +Marks a pointer as fully initialized. + +Code instrumented with `-sanitize:memory` will be permitted to access memory +within the region the pointer points to as if it had already been initialized. + +When msan is not enabled this procedure does nothing. +*/ +memory_unpoison_ptr :: proc "contextless" (ptr: ^$T) { + when MSAN_ENABLED { + __msan_unpoison(ptr, size_of(T)) + } +} + +/* +Marks the region covering `[ptr, ptr+len)` as fully initialized. + +Code instrumented with `-sanitize:memory` will be permitted to access memory +within this range as if it had already been initialized. + +When msan is not enabled this procedure does nothing. +*/ +memory_unpoison_rawptr :: proc "contextless" (ptr: rawptr, len: int) { + when MSAN_ENABLED { + __msan_unpoison(ptr, uint(len)) + } +} + +/* +Marks the region covering `[ptr, ptr+len)` as fully initialized. + +Code instrumented with `-sanitize:memory` will be permitted to access memory +within this range as if it had already been initialized. + +When msan is not enabled this procedure does nothing. +*/ +memory_unpoison_rawptr_uint :: proc "contextless" (ptr: rawptr, len: uint) { + when MSAN_ENABLED { + __msan_unpoison(ptr, len) + } +} + +memory_unpoison :: proc { + memory_unpoison_slice, + memory_unpoison_ptr, + memory_unpoison_rawptr, + memory_unpoison_rawptr_uint, +} diff --git a/src/check_decl.cpp b/src/check_decl.cpp index d53c3c6b7..c696fc4c1 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1370,6 +1370,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { e->Procedure.has_instrumentation = has_instrumentation; e->Procedure.no_sanitize_address = ac.no_sanitize_address; + e->Procedure.no_sanitize_memory = ac.no_sanitize_memory; e->deprecated_message = ac.deprecated_message; e->warning_message = ac.warning_message; diff --git a/src/checker.cpp b/src/checker.cpp index 9bc02cd87..4a5381014 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -3776,6 +3776,12 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { } ac->no_sanitize_address = true; return true; + } else if (name == "no_sanitize_memory") { + if (value != nullptr) { + error(value, "'%.*s' expects no parameter", LIT(name)); + } + ac->no_sanitize_memory = true; + return true; } return false; } diff --git a/src/checker.hpp b/src/checker.hpp index 0cdfd69ab..dabb7330a 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -140,6 +140,7 @@ struct AttributeContext { bool instrumentation_enter : 1; bool instrumentation_exit : 1; bool no_sanitize_address : 1; + bool no_sanitize_memory : 1; bool rodata : 1; bool ignore_duplicates : 1; u32 optimization_mode; // ProcedureOptimizationMode diff --git a/src/entity.cpp b/src/entity.cpp index a16779419..6c0aa6ace 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -263,6 +263,7 @@ struct Entity { bool uses_branch_location : 1; bool is_anonymous : 1; bool no_sanitize_address : 1; + bool no_sanitize_memory : 1; } Procedure; struct { Array entities; diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index f51ed2b4d..0a51b5cb5 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -345,7 +345,7 @@ gb_internal lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool i if (build_context.sanitizer_flags & SanitizerFlag_Address && !entity->Procedure.no_sanitize_address) { lb_add_attribute_to_proc(m, p->value, "sanitize_address"); } - if (build_context.sanitizer_flags & SanitizerFlag_Memory) { + if (build_context.sanitizer_flags & SanitizerFlag_Memory && !entity->Procedure.no_sanitize_memory) { lb_add_attribute_to_proc(m, p->value, "sanitize_memory"); } if (build_context.sanitizer_flags & SanitizerFlag_Thread) { From f8228a91d1823a2f586dcca5f5ff9f57d17dc92f Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:34:44 -0400 Subject: [PATCH 15/35] Mark some uninitialized memory as safe Syscalls (but not C functions) are opaque to MemorySanitizer, thus some memory addresses need to be manually marked as safe to access. --- core/os/os2/file_linux.odin | 1 + core/os/os_freebsd.odin | 6 +++--- core/os/os_haiku.odin | 6 +++--- core/os/os_linux.odin | 8 ++++---- core/os/os_netbsd.odin | 6 +++--- core/os/os_openbsd.odin | 6 +++--- 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/core/os/os2/file_linux.odin b/core/os/os2/file_linux.odin index a1ead7f9f..b1d11b425 100644 --- a/core/os/os2/file_linux.odin +++ b/core/os/os2/file_linux.odin @@ -269,6 +269,7 @@ _write_at :: proc(f: ^File_Impl, p: []byte, offset: i64) -> (nt: i64, err: Error return } +@(no_sanitize_memory) _file_size :: proc(f: ^File_Impl) -> (n: i64, err: Error) { // TODO: Identify 0-sized "pseudo" files and return No_Size. This would // eliminate the need for the _read_entire_pseudo_file procs. diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin index 87a56b057..f2ee6a496 100644 --- a/core/os/os_freebsd.odin +++ b/core/os/os_freebsd.odin @@ -662,7 +662,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Error) { return File_Time(modified), nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _stat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -674,7 +674,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _lstat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -688,7 +688,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _fstat :: proc(fd: Handle) -> (OS_Stat, Error) { s: OS_Stat = --- result := _unix_fstat(fd, &s) diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin index 4a57afb87..4ce726965 100644 --- a/core/os/os_haiku.odin +++ b/core/os/os_haiku.odin @@ -325,7 +325,7 @@ _alloc_command_line_arguments :: proc() -> []string { return res } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _stat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -339,7 +339,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _lstat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -353,7 +353,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _fstat :: proc(fd: Handle) -> (OS_Stat, Error) { // deliberately uninitialized s: OS_Stat = --- diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 2281e6a82..84a7f7b32 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -674,7 +674,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) { return i64(res), nil } -@(require_results) +@(require_results, no_sanitize_memory) file_size :: proc(fd: Handle) -> (i64, Error) { // deliberately uninitialized; the syscall fills this buffer for us s: OS_Stat = --- @@ -794,7 +794,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { return File_Time(modified), nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _stat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -808,7 +808,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _lstat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -822,7 +822,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _fstat :: proc(fd: Handle) -> (OS_Stat, Error) { // deliberately uninitialized; the syscall fills this buffer for us s: OS_Stat = --- diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin index e3ba760a4..40b41b133 100644 --- a/core/os/os_netbsd.odin +++ b/core/os/os_netbsd.odin @@ -724,7 +724,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { return File_Time(modified), nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _stat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -736,7 +736,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _lstat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -750,7 +750,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _fstat :: proc(fd: Handle) -> (OS_Stat, Error) { s: OS_Stat = --- result := _unix_fstat(fd, &s) diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin index 6548a57dc..d90e51e13 100644 --- a/core/os/os_openbsd.odin +++ b/core/os/os_openbsd.odin @@ -639,7 +639,7 @@ last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) { return File_Time(modified), nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _stat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -653,7 +653,7 @@ _stat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _lstat :: proc(path: string) -> (OS_Stat, Error) { runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() cstr := strings.clone_to_cstring(path, context.temp_allocator) @@ -667,7 +667,7 @@ _lstat :: proc(path: string) -> (OS_Stat, Error) { return s, nil } -@(private, require_results) +@(private, require_results, no_sanitize_memory) _fstat :: proc(fd: Handle) -> (OS_Stat, Error) { // deliberately uninitialized s: OS_Stat = --- From 108c8781c7da7342587a33a598051d3377bb0a4d Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 5 Jun 2025 22:27:18 +0200 Subject: [PATCH 16/35] Factor out into helper. --- core/os/os2/user_posix.odin | 220 ++++++++---------------------------- 1 file changed, 44 insertions(+), 176 deletions(-) diff --git a/core/os/os2/user_posix.odin b/core/os/os2/user_posix.odin index f5ee427b3..7541a99e1 100644 --- a/core/os/os2/user_posix.odin +++ b/core/os/os2/user_posix.odin @@ -4,256 +4,102 @@ package os2 import "base:runtime" _user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Library/Caches"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Library/Caches", allocator) case: // All other UNIX systems - dir = get_env("XDG_CACHE_HOME", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.cache"}, allocator) or_return - } + return _xdg_lookup("XDG_CACHE_HOME", "HOME", "/.cache", allocator) } - return } _user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.config"}, allocator) or_return + return _xdg_lookup("", "HOME", "/.config", allocator) case: // All other UNIX systems - dir = get_env("XDG_CONFIG_HOME", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.config"}, allocator) or_return - } + return _xdg_lookup("XDG_CONFIG_HOME", "HOME", "/.config", allocator) } - return } _user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.local/state"}, allocator) or_return + return _xdg_lookup("", "HOME", "/.local/state", allocator) case: // All other UNIX systems - dir = get_env("XDG_STATE_HOME", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.local/state"}, allocator) or_return - } + return _xdg_lookup("XDG_STATE_HOME", "HOME", "/.local/state", allocator) } - return } _user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.local/share"}, allocator) or_return + return _xdg_lookup("", "HOME", "/.local/share", allocator) case: // All other UNIX systems - dir = get_env("XDG_DATA_HOME", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/.local/share"}, allocator) or_return - } + return _xdg_lookup("XDG_DATA_HOME", "HOME", "/.local/share", allocator) } - return } _user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Music"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Music", allocator) case: // All other UNIX systems - dir = get_env("XDG_MUSIC_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Music"}, allocator) or_return - } + return _xdg_lookup("XDG_MUSIC_DIR", "HOME", "/Music", allocator) } - return } _user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Desktop"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Desktop", allocator) case: // All other UNIX systems - dir = get_env("XDG_DESKTOP_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Desktop"}, allocator) or_return - } + return _xdg_lookup("XDG_DESKTOP_DIR", "HOME", "/Desktop", allocator) } - return } _user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Documents"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Documents", allocator) case: // All other UNIX systems - dir = get_env("XDG_DOCUMENTS_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Documents"}, allocator) or_return - } + return _xdg_lookup("XDG_DOCUMENTS_DIR", "HOME", "/Documents", allocator) } - return } _user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Downloads"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Downloads", allocator) case: // All other UNIX systems - dir = get_env("XDG_DOWNLOAD_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Downloads"}, allocator) or_return - } + return _xdg_lookup("XDG_DOWNLOAD_DIR", "HOME", "/Downloads", allocator) } - return } _user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Pictures"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Pictures", allocator) case: // All other UNIX systems - dir = get_env("XDG_PICTURES_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Pictures"}, allocator) or_return - } + return _xdg_lookup("XDG_PICTURES_DIR", "HOME", "/Pictures", allocator) } - return } _user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Public"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Public", allocator) case: // All other UNIX systems - dir = get_env("XDG_PUBLIC_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Public"}, allocator) or_return - } + return _xdg_lookup("XDG_PUBLIC_DIR", "HOME", "/Public", allocator) } - return } _user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - #partial switch ODIN_OS { case .Darwin: - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Movies"}, allocator) or_return + return _xdg_lookup("", "HOME", "/Movies", allocator) case: // All other UNIX systems - dir = get_env("XDG_VIDEOS_DIR", allocator) - if dir == "" { - dir = get_env("HOME", temp_allocator) - if dir == "" { - return "", .Invalid_Path - } - dir = concatenate({dir, "/Videos"}, allocator) or_return - } + return _xdg_lookup("XDG_VIDEOS_DIR", "HOME", "/Videos", allocator) } - return } _user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { @@ -261,4 +107,26 @@ _user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error return v, nil } return "", .Invalid_Path +} + +_xdg_lookup :: proc(xdg_env, fallback_env: string, fallback_suffix: string, allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + if xdg_env == "" { // Darwin doesn't have XDG paths. + dir = get_env(fallback_env, temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + return concatenate({dir, fallback_suffix}, allocator) + } else { + dir = get_env(xdg_env, allocator) + if dir == "" { + dir = get_env(fallback_env, temp_allocator) + if dir == "" { + return "", .Invalid_Path + } + dir = concatenate({dir, fallback_suffix}, allocator) or_return + } + return + } } \ No newline at end of file From b7de15caa342fd81fb203015e2fd22a82e523342 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:29:41 -0400 Subject: [PATCH 17/35] Clarify `strconv.append_*` to `strconv.write_*` --- core/encoding/cbor/cbor.odin | 6 +-- core/encoding/json/marshal.odin | 8 +-- core/fmt/fmt.odin | 8 +-- core/io/util.odin | 16 +++--- core/math/fixed/fixed.odin | 6 +-- core/net/url.odin | 2 +- core/os/os.odin | 2 +- core/os/os2/file_util.odin | 2 +- core/strconv/integers.odin | 14 ++--- core/strconv/strconv.odin | 84 ++++++++++++++--------------- core/strings/builder.odin | 12 ++--- tests/core/math/test_core_math.odin | 2 +- 12 files changed, 81 insertions(+), 81 deletions(-) diff --git a/core/encoding/cbor/cbor.odin b/core/encoding/cbor/cbor.odin index 8eb829ed3..1fb7c34ab 100644 --- a/core/encoding/cbor/cbor.odin +++ b/core/encoding/cbor/cbor.odin @@ -385,17 +385,17 @@ to_diagnostic_format_writer :: proc(w: io.Writer, val: Value, padding := 0) -> i // which we want for the diagnostic format. case f16: buf: [64]byte - str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16)) + str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f16), 8*size_of(f16)) if str[0] == '+' && str != "+Inf" { str = str[1:] } io.write_string(w, str) or_return case f32: buf: [128]byte - str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32)) + str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f32), 8*size_of(f32)) if str[0] == '+' && str != "+Inf" { str = str[1:] } io.write_string(w, str) or_return case f64: buf: [256]byte - str := strconv.append_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64)) + str := strconv.write_float(buf[:], f64(v), 'f', 2*size_of(f64), 8*size_of(f64)) if str[0] == '+' && str != "+Inf" { str = str[1:] } io.write_string(w, str) or_return diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index 020facd14..ebb9a639c 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -108,13 +108,13 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) { switch i in a { case u8, u16, u32, u64, u128: - s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix }) + s = strconv.write_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix }) case: - s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) } } else { - s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) + s = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil) } io.write_string(w, s) or_return @@ -286,7 +286,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: case runtime.Type_Info_Integer: buf: [40]byte u := cast_any_int_to_u128(ka) - name = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil) + name = strconv.write_bits_128(buf[:], u, 10, info.signed, 8*kti.size, "0123456789", nil) opt_write_key(w, opt, name) or_return case: return .Unsupported_Type diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 9c07847dd..0f6470cca 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1122,7 +1122,7 @@ _fmt_int :: proc(fi: ^Info, u: u64, base: int, is_signed: bool, bit_size: int, d flags: strconv.Int_Flags if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} } if fi.plus { flags += {.Plus} } - s := strconv.append_bits(buf[start:], u, base, is_signed, bit_size, digits, flags) + s := strconv.write_bits(buf[start:], u, base, is_signed, bit_size, digits, flags) prev_zero := fi.zero defer fi.zero = prev_zero fi.zero = false @@ -1207,7 +1207,7 @@ _fmt_int_128 :: proc(fi: ^Info, u: u128, base: int, is_signed: bool, bit_size: i flags: strconv.Int_Flags if fi.hash && !fi.zero && start == 0 { flags += {.Prefix} } if fi.plus { flags += {.Plus} } - s := strconv.append_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags) + s := strconv.write_bits_128(buf[start:], u, base, is_signed, bit_size, digits, flags) if fi.hash && fi.zero && fi.indent == 0 { c: byte = 0 @@ -1272,7 +1272,7 @@ _fmt_memory :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, units: st } buf: [256]byte - str := strconv.append_float(buf[:], amt, 'f', prec, 64) + str := strconv.write_float(buf[:], amt, 'f', prec, 64) // Add the unit at the end. copy(buf[len(str):], units[off:off+unit_len]) @@ -1424,7 +1424,7 @@ _fmt_float_as :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune, float_fmt: b buf: [386]byte // Can return "NaN", "+Inf", "-Inf", "+", "-". - str := strconv.append_float(buf[:], v, float_fmt, prec, bit_size) + str := strconv.write_float(buf[:], v, float_fmt, prec, bit_size) if !fi.plus { // Strip sign from "+" but not "+Inf". diff --git a/core/io/util.odin b/core/io/util.odin index fdbbd5b9f..fa98e007b 100644 --- a/core/io/util.odin +++ b/core/io/util.odin @@ -22,12 +22,12 @@ write_ptr_at :: proc(w: Writer_At, p: rawptr, byte_size: int, offset: i64, n_wri write_u64 :: proc(w: Writer, i: u64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [32]byte - s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(w, s, n_written) } write_i64 :: proc(w: Writer, i: i64, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [32]byte - s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(w, s, n_written) } @@ -40,18 +40,18 @@ write_int :: proc(w: Writer, i: int, base: int = 10, n_written: ^int = nil) -> ( write_u128 :: proc(w: Writer, i: u128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [39]byte - s := strconv.append_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) + s := strconv.write_bits_128(buf[:], i, base, false, 128, strconv.digits, nil) return write_string(w, s, n_written) } write_i128 :: proc(w: Writer, i: i128, base: int = 10, n_written: ^int = nil) -> (n: int, err: Error) { buf: [40]byte - s := strconv.append_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) + s := strconv.write_bits_128(buf[:], u128(i), base, true, 128, strconv.digits, nil) return write_string(w, s, n_written) } write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: Error) { buf: [386]byte - str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) + str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) s := buf[:len(str)+1] if s[1] == '+' || s[1] == '-' { s = s[1:] @@ -67,7 +67,7 @@ write_f16 :: proc(w: Writer, val: f16, n_written: ^int = nil) -> (n: int, err: E write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: Error) { buf: [386]byte - str := strconv.append_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) + str := strconv.write_float(buf[1:], f64(val), 'f', 2*size_of(val), 8*size_of(val)) s := buf[:len(str)+1] if s[1] == '+' || s[1] == '-' { s = s[1:] @@ -83,7 +83,7 @@ write_f32 :: proc(w: Writer, val: f32, n_written: ^int = nil) -> (n: int, err: E write_f64 :: proc(w: Writer, val: f64, n_written: ^int = nil) -> (n: int, err: Error) { buf: [386]byte - str := strconv.append_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val)) + str := strconv.write_float(buf[1:], val, 'f', 2*size_of(val), 8*size_of(val)) s := buf[:len(str)+1] if s[1] == '+' || s[1] == '-' { s = s[1:] @@ -130,7 +130,7 @@ write_encoded_rune :: proc(w: Writer, r: rune, write_quote := true, n_written: ^ write_string(w, `\x`, &n) or_return buf: [2]byte - s := strconv.append_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { case 0: write_string(w, "00", &n) or_return diff --git a/core/math/fixed/fixed.odin b/core/math/fixed/fixed.odin index b23090307..119e727a7 100644 --- a/core/math/fixed/fixed.odin +++ b/core/math/fixed/fixed.odin @@ -124,16 +124,16 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { when size_of(Backing) < 16 { T :: u64 - append_uint :: strconv.append_uint + write_uint :: strconv.write_uint } else { T :: u128 - append_uint :: strconv.append_u128 + write_uint :: strconv.write_u128 } integer := T(x.i) >> Fraction_Width fraction := T(x.i) & (1< string { bytes, n := utf8.encode_rune(ch) for byte in bytes[:n] { buf: [2]u8 = --- - t := strconv.append_int(buf[:], i64(byte), 16) + t := strconv.write_int(buf[:], i64(byte), 16) strings.write_rune(&b, '%') strings.write_string(&b, t) } diff --git a/core/os/os.odin b/core/os/os.odin index 30b86d4cd..fde48fbf4 100644 --- a/core/os/os.odin +++ b/core/os/os.odin @@ -57,7 +57,7 @@ write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) { if r < 32 { if wrap(write_string(f, "\\x"), &n, &err) { return } b: [2]byte - s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) + s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { case 0: if wrap(write_string(f, "00"), &n, &err) { return } case 1: if wrap(write_rune(f, '0'), &n, &err) { return } diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin index 8af46fab3..407c38f88 100644 --- a/core/os/os2/file_util.odin +++ b/core/os/os2/file_util.odin @@ -59,7 +59,7 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) { if r < 32 { if wrap(write_string(f, "\\x"), &n, &err) { return } b: [2]byte - s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) + s := strconv.write_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) switch len(s) { case 0: if wrap(write_string(f, "00"), &n, &err) { return } case 1: if wrap(write_rune(f, '0'), &n, &err) { return } diff --git a/core/strconv/integers.odin b/core/strconv/integers.odin index 98a432ac5..0db110d10 100644 --- a/core/strconv/integers.odin +++ b/core/strconv/integers.odin @@ -48,7 +48,7 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, return } /* -Appends the string representation of an integer to a buffer with specified base, flags, and digit set. +Writes the string representation of an integer to a buffer with specified base, flags, and digit set. **Inputs** - buf: The buffer to append the integer representation to @@ -62,9 +62,9 @@ Appends the string representation of an integer to a buffer with specified base, **Returns** - The string containing the integer representation appended to the buffer */ -append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { +write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { if base < 2 || base > MAX_BASE { - panic("strconv: illegal base passed to append_bits") + panic("strconv: illegal base passed to write_bits") } a: [129]byte @@ -146,7 +146,7 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: return } /* -Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. +Writes the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. **Inputs** - buf: The buffer to append the integer representation to @@ -158,11 +158,11 @@ Appends the string representation of a 128-bit integer to a buffer with specifie - flags: The Int_Flags bit set to control integer formatting **Returns** -- The string containing the integer representation appended to the buffer +- The string containing the integer representation written to the buffer */ -append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { +write_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { if base < 2 || base > MAX_BASE { - panic("strconv: illegal base passed to append_bits") + panic("strconv: illegal base passed to write_bits") } a: [140]byte diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 4cecd1911..99a290d83 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -1451,19 +1451,19 @@ parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64, return cast(quaternion64)v, ok } /* -Appends a boolean value as a string to the given buffer +Writes a boolean value as a string to the given buffer **Inputs** -- buf: The buffer to append the boolean value to -- b: The boolean value to be appended +- buf: The buffer to write the boolean value to +- b: The boolean value to be written Example: import "core:fmt" import "core:strconv" - append_bool_example :: proc() { + write_bool_example :: proc() { buf: [6]byte - result := strconv.append_bool(buf[:], true) + result := strconv.write_bool(buf[:], true) fmt.println(result, buf) } @@ -1472,9 +1472,9 @@ Output: true [116, 114, 117, 101, 0, 0] **Returns** -- The resulting string after appending the boolean value +- The resulting string after writing the boolean value */ -append_bool :: proc(buf: []byte, b: bool) -> string { +write_bool :: proc(buf: []byte, b: bool) -> string { n := 0 if b { n = copy(buf, "true") @@ -1484,20 +1484,20 @@ append_bool :: proc(buf: []byte, b: bool) -> string { return string(buf[:n]) } /* -Appends an unsigned integer value as a string to the given buffer with the specified base +Writes an unsigned integer value as a string to the given buffer with the specified base **Inputs** -- buf: The buffer to append the unsigned integer value to -- u: The unsigned integer value to be appended +- buf: The buffer to write the unsigned integer value to +- u: The unsigned integer value to be written - base: The base to use for converting the integer value Example: import "core:fmt" import "core:strconv" - append_uint_example :: proc() { + write_uint_example :: proc() { buf: [4]byte - result := strconv.append_uint(buf[:], 42, 16) + result := strconv.write_uint(buf[:], 42, 16) fmt.println(result, buf) } @@ -1506,26 +1506,26 @@ Output: 2a [50, 97, 0, 0] **Returns** -- The resulting string after appending the unsigned integer value +- The resulting string after writing the unsigned integer value */ -append_uint :: proc(buf: []byte, u: u64, base: int) -> string { - return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil) +write_uint :: proc(buf: []byte, u: u64, base: int) -> string { + return write_bits(buf, u, base, false, 8*size_of(uint), digits, nil) } /* -Appends a signed integer value as a string to the given buffer with the specified base +Writes a signed integer value as a string to the given buffer with the specified base **Inputs** -- buf: The buffer to append the signed integer value to -- i: The signed integer value to be appended +- buf: The buffer to write the signed integer value to +- i: The signed integer value to be written - base: The base to use for converting the integer value Example: import "core:fmt" import "core:strconv" - append_int_example :: proc() { + write_int_example :: proc() { buf: [4]byte - result := strconv.append_int(buf[:], -42, 10) + result := strconv.write_int(buf[:], -42, 10) fmt.println(result, buf) } @@ -1534,16 +1534,16 @@ Output: -42 [45, 52, 50, 0] **Returns** -- The resulting string after appending the signed integer value +- The resulting string after writing the signed integer value */ -append_int :: proc(buf: []byte, i: i64, base: int) -> string { - return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) +write_int :: proc(buf: []byte, i: i64, base: int) -> string { + return write_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) } -append_u128 :: proc(buf: []byte, u: u128, base: int) -> string { - return append_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil) +write_u128 :: proc(buf: []byte, u: u128, base: int) -> string { + return write_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil) } /* @@ -1571,7 +1571,7 @@ Output: - The resulting string after converting the integer value */ itoa :: proc(buf: []byte, i: int) -> string { - return append_int(buf, i64(i), 10) + return write_int(buf, i64(i), 10) } /* Converts a string to an integer value @@ -1623,14 +1623,14 @@ atof :: proc(s: string) -> f64 { v, _ := parse_f64(s) return v } -// Alias to `append_float` -ftoa :: append_float +// Alias to `write_float` +ftoa :: write_float /* -Appends a float64 value as a string to the given buffer with the specified format and precision +Writes a float64 value as a string to the given buffer with the specified format and precision **Inputs** -- buf: The buffer to append the float64 value to -- f: The float64 value to be appended +- buf: The buffer to write the float64 value to +- f: The float64 value to be written - fmt: The byte specifying the format to use for the conversion - prec: The precision to use for the conversion - bit_size: The size of the float in bits (32 or 64) @@ -1639,9 +1639,9 @@ Example: import "core:fmt" import "core:strconv" - append_float_example :: proc() { + write_float_example :: proc() { buf: [8]byte - result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64) + result := strconv.write_float(buf[:], 3.14159, 'f', 2, 64) fmt.println(result, buf) } @@ -1650,16 +1650,16 @@ Output: +3.14 [43, 51, 46, 49, 52, 0, 0, 0] **Returns** -- The resulting string after appending the float +- The resulting string after writing the float */ -append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { +write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { return string(generic_ftoa(buf, f, fmt, prec, bit_size)) } /* -Appends a quoted string representation of the input string to a given byte slice and returns the result as a string +Writes a quoted string representation of the input string to a given byte slice and returns the result as a string **Inputs** -- buf: The byte slice to which the quoted string will be appended +- buf: The byte slice to which the quoted string will be written - str: The input string to be quoted !! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected @@ -1679,7 +1679,7 @@ Output: "'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0] **Returns** -- The resulting string after appending the quoted string representation +- The resulting string after writing the quoted string representation */ quote :: proc(buf: []byte, str: string) -> string { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { @@ -1719,10 +1719,10 @@ quote :: proc(buf: []byte, str: string) -> string { return string(buf[:i]) } /* -Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string +Writes a quoted rune representation of the input rune to a given byte slice and returns the result as a string **Inputs** -- buf: The byte slice to which the quoted rune will be appended +- buf: The byte slice to which the quoted rune will be written - r: The input rune to be quoted Example: @@ -1740,7 +1740,7 @@ Output: 'A' [39, 65, 39, 0] **Returns** -- The resulting string after appending the quoted rune representation +- The resulting string after writing the quoted rune representation */ quote_rune :: proc(buf: []byte, r: rune) -> string { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { @@ -1783,7 +1783,7 @@ quote_rune :: proc(buf: []byte, r: rune) -> string { if r < 32 { write_string(buf, &i, "\\x") b: [2]byte - s := append_bits(b[:], u64(r), 16, true, 64, digits, nil) + s := write_bits(b[:], u64(r), 16, true, 64, digits, nil) switch len(s) { case 0: write_string(buf, &i, "00") case 1: write_rune(buf, &i, '0') diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 05382f04e..b1180d5e9 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -675,7 +675,7 @@ Returns: */ write_float :: proc(b: ^Builder, f: f64, fmt: byte, prec, bit_size: int, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f, fmt, prec, bit_size) + s := strconv.write_float(buf[:], f, fmt, prec, bit_size) // If the result starts with a `+` then unless we always want signed results, // we skip it unless it's followed by an `I` (because of +Inf). if !always_signed && (buf[0] == '+' && buf[1] != 'I') { @@ -699,7 +699,7 @@ Returns: */ write_f16 :: proc(b: ^Builder, f: f16, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) + s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) if !always_signed && (buf[0] == '+' && buf[1] != 'I') { s = s[1:] } @@ -739,7 +739,7 @@ Output: */ write_f32 :: proc(b: ^Builder, f: f32, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) + s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) if !always_signed && (buf[0] == '+' && buf[1] != 'I') { s = s[1:] } @@ -761,7 +761,7 @@ Returns: */ write_f64 :: proc(b: ^Builder, f: f64, fmt: byte, always_signed := false) -> (n: int) { buf: [384]byte - s := strconv.append_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) + s := strconv.write_float(buf[:], f64(f), fmt, 2*size_of(f), 8*size_of(f)) if !always_signed && (buf[0] == '+' && buf[1] != 'I') { s = s[1:] } @@ -782,7 +782,7 @@ Returns: */ write_u64 :: proc(b: ^Builder, i: u64, base: int = 10) -> (n: int) { buf: [32]byte - s := strconv.append_bits(buf[:], i, base, false, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], i, base, false, 64, strconv.digits, nil) return write_string(b, s) } /* @@ -800,7 +800,7 @@ Returns: */ write_i64 :: proc(b: ^Builder, i: i64, base: int = 10) -> (n: int) { buf: [32]byte - s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) + s := strconv.write_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil) return write_string(b, s) } /* diff --git a/tests/core/math/test_core_math.odin b/tests/core/math/test_core_math.odin index 5797cb4ea..009e3fedd 100644 --- a/tests/core/math/test_core_math.odin +++ b/tests/core/math/test_core_math.odin @@ -1238,7 +1238,7 @@ test_count_digits :: proc(t: ^testing.T) { buf: [64]u8 for n in 0.. Date: Thu, 5 Jun 2025 16:31:48 -0400 Subject: [PATCH 18/35] Remove trailing whitespace --- core/strconv/decimal/decimal.odin | 30 ++--- core/strconv/generic_float.odin | 14 +-- core/strconv/integers.odin | 16 +-- core/strconv/strconv.odin | 192 +++++++++++++++--------------- 4 files changed, 126 insertions(+), 126 deletions(-) diff --git a/core/strconv/decimal/decimal.odin b/core/strconv/decimal/decimal.odin index cb9285083..5a878d0e1 100644 --- a/core/strconv/decimal/decimal.odin +++ b/core/strconv/decimal/decimal.odin @@ -12,11 +12,11 @@ Decimal :: struct { Sets a Decimal from a given string `s`. The string is expected to represent a float. Stores parsed number in the given Decimal structure. If parsing fails, the Decimal will be left in an undefined state. -**Inputs** +**Inputs** - d: Pointer to a Decimal struct where the parsed result will be stored - s: The input string representing the floating-point number -**Returns** +**Returns** - ok: A boolean indicating whether the parsing was successful */ set :: proc(d: ^Decimal, s: string) -> (ok: bool) { @@ -104,11 +104,11 @@ set :: proc(d: ^Decimal, s: string) -> (ok: bool) { /* Converts a Decimal to a string representation, using the provided buffer as storage. -**Inputs** +**Inputs** - buf: A byte slice buffer to hold the resulting string - a: The struct to be converted to a string -**Returns** +**Returns** - A string representation of the Decimal */ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { @@ -150,7 +150,7 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { /* Trims trailing zeros in the given Decimal, updating the count and decimal_point values as needed. -**Inputs** +**Inputs** - a: Pointer to the Decimal struct to be trimmed */ trim :: proc(a: ^Decimal) { @@ -166,7 +166,7 @@ Converts a given u64 integer `idx` to its Decimal representation in the provided **Used for internal Decimal Operations.** -**Inputs** +**Inputs** - a: Where the result will be stored - idx: The value to be assigned to the Decimal */ @@ -190,11 +190,11 @@ assign :: proc(a: ^Decimal, idx: u64) { trim(a) } /* -Shifts the Decimal value to the right by k positions. +Shifts the Decimal value to the right by k positions. **Used for internal Decimal Operations.** -**Inputs** +**Inputs** - a: The Decimal struct to be shifted - k: The number of positions to shift right */ @@ -344,7 +344,7 @@ Shifts the decimal of the input value to the left by `k` places WARNING: asserts `k < 61` -**Inputs** +**Inputs** - a: The Decimal to be modified - k: The number of places to shift the decimal to the left */ @@ -405,7 +405,7 @@ shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check { /* Shifts the decimal of the input value by the specified number of places -**Inputs** +**Inputs** - a: The Decimal to be modified - i: The number of places to shift the decimal (positive for left shift, negative for right shift) */ @@ -435,7 +435,7 @@ shift :: proc(a: ^Decimal, i: int) { /* Determines if the Decimal can be rounded up at the given digit index -**Inputs** +**Inputs** - a: The Decimal to check - nd: The digit index to consider for rounding up @@ -455,7 +455,7 @@ can_round_up :: proc(a: ^Decimal, nd: int) -> bool { /* Rounds the Decimal at the given digit index -**Inputs** +**Inputs** - a: The Decimal to be modified - nd: The digit index to round */ @@ -470,7 +470,7 @@ round :: proc(a: ^Decimal, nd: int) { /* Rounds the Decimal up at the given digit index -**Inputs** +**Inputs** - a: The Decimal to be modified - nd: The digit index to round up */ @@ -493,7 +493,7 @@ round_up :: proc(a: ^Decimal, nd: int) { /* Rounds down the decimal value to the specified number of decimal places -**Inputs** +**Inputs** - a: The Decimal value to be rounded down - nd: The number of decimal places to round down to @@ -522,7 +522,7 @@ round_down :: proc(a: ^Decimal, nd: int) { /* Extracts the rounded integer part of a decimal value -**Inputs** +**Inputs** - a: A pointer to the Decimal value to extract the rounded integer part from WARNING: There are no guarantees about overflow. diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin index b126dc3b6..163148156 100644 --- a/core/strconv/generic_float.odin +++ b/core/strconv/generic_float.odin @@ -23,7 +23,7 @@ _f64_info := Float_Info{52, 11, -1023} /* Converts a floating-point number to a string with the specified format and precision. -**Inputs** +**Inputs** buf: A byte slice to store the resulting string val: The floating-point value to be converted @@ -40,7 +40,7 @@ Example: bit_size := 64 result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14" -**Returns** +**Returns** - A byte slice containing the formatted string */ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte { @@ -122,7 +122,7 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) /* Converts a decimal floating-point number into a byte buffer with the given format -**Inputs** +**Inputs** - buf: The byte buffer to store the formatted number - shortest: If true, generates the shortest representation of the number - neg: If true, the number is negative @@ -130,7 +130,7 @@ Converts a decimal floating-point number into a byte buffer with the given forma - precision: The number of digits after the decimal point - fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G') -**Returns** +**Returns** - A byte slice containing the formatted decimal floating-point number */ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte { @@ -256,7 +256,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic /* Rounds the given decimal number to its shortest representation, considering the provided floating-point format -**Inputs** +**Inputs** - d: The decimal number to round - mant: The mantissa of the floating-point number - exp: The exponent of the floating-point number @@ -331,11 +331,11 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf /* Converts a decimal number to its floating-point representation with the given format and returns the resulting bits -**Inputs** +**Inputs** - d: Pointer to the decimal number to convert - info: Pointer to the Float_Info structure containing information about the floating-point format -**Returns** +**Returns** - b: The bits representing the floating-point number - overflow: A boolean indicating whether an overflow occurred during conversion */ diff --git a/core/strconv/integers.odin b/core/strconv/integers.odin index 0db110d10..16c31ac42 100644 --- a/core/strconv/integers.odin +++ b/core/strconv/integers.odin @@ -12,12 +12,12 @@ digits := "0123456789abcdefghijklmnopqrstuvwxyz" /* Determines whether the given unsigned 64-bit integer is a negative value by interpreting it as a signed integer with the specified bit size. -**Inputs** +**Inputs** - x: The unsigned 64-bit integer to check for negativity - is_signed: A boolean indicating if the input should be treated as a signed integer - bit_size: The bit size of the signed integer representation (8, 16, 32, or 64) -**Returns** +**Returns** - u: The absolute value of the input integer - neg: A boolean indicating whether the input integer is negative */ @@ -50,7 +50,7 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, /* Writes the string representation of an integer to a buffer with specified base, flags, and digit set. -**Inputs** +**Inputs** - buf: The buffer to append the integer representation to - x: The integer value to convert - base: The base for the integer representation (2 <= base <= MAX_BASE) @@ -59,7 +59,7 @@ Writes the string representation of an integer to a buffer with specified base, - digits: The digit set used for the integer representation - flags: The Int_Flags bit set to control integer formatting -**Returns** +**Returns** - The string containing the integer representation appended to the buffer */ write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { @@ -106,12 +106,12 @@ write_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: in /* Determines whether the given unsigned 128-bit integer is a negative value by interpreting it as a signed integer with the specified bit size. -**Inputs** +**Inputs** - x: The unsigned 128-bit integer to check for negativity - is_signed: A boolean indicating if the input should be treated as a signed integer - bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128) -**Returns** +**Returns** - u: The absolute value of the input integer - neg: A boolean indicating whether the input integer is negative */ @@ -148,7 +148,7 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: /* Writes the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. -**Inputs** +**Inputs** - buf: The buffer to append the integer representation to - x: The 128-bit integer value to convert - base: The base for the integer representation (2 <= base <= MAX_BASE) @@ -157,7 +157,7 @@ Writes the string representation of a 128-bit integer to a buffer with specified - digits: The digit set used for the integer representation - flags: The Int_Flags bit set to control integer formatting -**Returns** +**Returns** - The string containing the integer representation written to the buffer */ write_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index 99a290d83..c1ef31cd7 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -5,8 +5,8 @@ import "decimal" /* Parses a boolean value from the input string -**Inputs** -- s: The input string +**Inputs** +- s: The input string - true: "1", "t", "T", "true", "TRUE", "True" - false: "0", "f", "F", "false", "FALSE", "False" - n: An optional pointer to an int to store the length of the parsed substring (default: nil) @@ -386,7 +386,7 @@ Parses an unsigned integer value from the input string, using the specified base - If base is not 0, it will be used for parsing regardless of any prefix in the input string Example: - + import "core:fmt" import "core:strconv" parse_uint_example :: proc() { @@ -399,14 +399,14 @@ Example: n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base fmt.println(n,ok) } - + Output: 1234 true 65535 true 65535 true -**Returns** +**Returns** value: The parsed uint value ok: `false` if no appropriate value could be found; the value was negative; he input string contained more than just the number @@ -423,7 +423,7 @@ parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: boo /* Parses an integer value from a string in the given base, without any prefix -**Inputs** +**Inputs** - str: The input string containing the integer value - base: The base (radix) to use for parsing the integer (1-16) - n: An optional pointer to an int to store the length of the parsed substring (default: nil) @@ -436,12 +436,12 @@ Example: n, ok := strconv.parse_i128_of_base("-1234eeee", 10) fmt.println(n,ok) } - + Output: -1234 false -**Returns** +**Returns** - value: The parsed i128 value - ok: false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number. */ @@ -491,7 +491,7 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12 /* Parses an integer value from a string in base 10, unless there's a prefix -**Inputs** +**Inputs** - str: The input string containing the integer value - n: An optional pointer to an int to store the length of the parsed substring (default: nil) @@ -506,13 +506,13 @@ Example: n, ok = strconv.parse_i128_maybe_prefixed("0xeeee") fmt.println(n, ok) } - + Output: 1234 true 61166 true - -**Returns** + +**Returns** - value: The parsed i128 value - ok: `false` if a valid integer could not be found, or if the input string contained more than just the number. */ @@ -574,7 +574,7 @@ parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base} /* Parses an unsigned integer value from a string in the given base, without any prefix -**Inputs** +**Inputs** - str: The input string containing the integer value - base: The base (radix) to use for parsing the integer (1-16) - n: An optional pointer to an int to store the length of the parsed substring (default: nil) @@ -590,13 +590,13 @@ Example: n, ok = strconv.parse_u128_of_base("5678eeee", 16) fmt.println(n, ok) } - + Output: 1234 false 1450766062 true - -**Returns** + +**Returns** - value: The parsed u128 value - ok: `false` if no numeric value of the appropriate base could be found, or if the input string contained more than just the number. */ @@ -634,7 +634,7 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12 /* Parses an unsigned integer value from a string in base 10, unless there's a prefix -**Inputs** +**Inputs** - str: The input string containing the integer value - n: An optional pointer to an int to store the length of the parsed substring (default: nil) @@ -649,13 +649,13 @@ Example: n, ok = strconv.parse_u128_maybe_prefixed("5678eeee") fmt.println(n, ok) } - + Output: 1234 true 5678 false - -**Returns** + +**Returns** - value: The parsed u128 value - ok: false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number. */ @@ -706,10 +706,10 @@ parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base} /* Converts a byte to lowercase -**Inputs** +**Inputs** - ch: A byte character to be converted to lowercase. -**Returns** +**Returns** - A lowercase byte character. */ @(private) @@ -717,7 +717,7 @@ lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A' /* Parses a 32-bit floating point number from a string -**Inputs** +**Inputs** - s: The input string containing a 32-bit floating point number. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -732,13 +732,13 @@ Example: n, ok = strconv.parse_f32("5678e2") fmt.printfln("%.3f %v", n, ok) } - + Output: 0.000 false 567800.000 true - -**Returns** + +**Returns** - value: The parsed 32-bit floating point number. - ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number. */ @@ -750,7 +750,7 @@ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) { /* Parses a 64-bit floating point number from a string -**Inputs** +**Inputs** - str: The input string containing a 64-bit floating point number. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -765,13 +765,13 @@ Example: n, ok = strconv.parse_f64("5678e2") fmt.printfln("%.3f %v", n, ok) } - + Output: 0.000 false 567800.000 true - -**Returns** + +**Returns** - value: The parsed 64-bit floating point number. - ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number. */ @@ -787,7 +787,7 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) { /* Parses a 32-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful -**Inputs** +**Inputs** - str: The input string containing a 32-bit floating point number. Example: @@ -801,14 +801,14 @@ Example: n, _, ok = strconv.parse_f32_prefix("5678e2") fmt.printfln("%.3f %v", n, ok) } - + Output: 0.000 false 567800.000 true - -**Returns** + +**Returns** - value: The parsed 32-bit floating point number. - nr: The length of the parsed substring. - ok: A boolean indicating whether the parsing was successful. @@ -822,7 +822,7 @@ parse_f32_prefix :: proc(str: string) -> (value: f32, nr: int, ok: bool) { /* Parses a 64-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful -**Inputs** +**Inputs** - str: The input string containing a 64-bit floating point number. Example: @@ -846,7 +846,7 @@ Output: 1234.000 true 13.370 true -**Returns** +**Returns** - value: The parsed 64-bit floating point number. - nr: The length of the parsed substring. - ok: `false` if a base 10 float could not be found @@ -1184,7 +1184,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { /* Parses a 128-bit complex number from a string -**Inputs** +**Inputs** - str: The input string containing a 128-bit complex number. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -1200,13 +1200,13 @@ Example: c, ok = strconv.parse_complex128("5+7i hellope", &n) fmt.printfln("%v %i %t", c, n, ok) } - + Output: 3+1i 4 true 5+7i 4 false - -**Returns** + +**Returns** - value: The parsed 128-bit complex number. - ok: `false` if a complex number could not be found, or if the input string contained more than just the number. */ @@ -1232,12 +1232,12 @@ parse_complex128 :: proc(str: string, n: ^int = nil) -> (value: complex128, ok: } value = complex(real_value, imag_value) - return + return } /* Parses a 64-bit complex number from a string -**Inputs** +**Inputs** - str: The input string containing a 64-bit complex number. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -1253,13 +1253,13 @@ Example: c, ok = strconv.parse_complex64("5+7i hellope", &n) fmt.printfln("%v %i %t", c, n, ok) } - + Output: 3+1i 4 true 5+7i 4 false - -**Returns** + +**Returns** - value: The parsed 64-bit complex number. - ok: `false` if a complex number could not be found, or if the input string contained more than just the number. */ @@ -1271,7 +1271,7 @@ parse_complex64 :: proc(str: string, n: ^int = nil) -> (value: complex64, ok: bo /* Parses a 32-bit complex number from a string -**Inputs** +**Inputs** - str: The input string containing a 32-bit complex number. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -1287,13 +1287,13 @@ Example: c, ok = strconv.parse_complex32("5+7i hellope", &n) fmt.printfln("%v %i %t", c, n, ok) } - + Output: 3+1i 4 true 5+7i 4 false - -**Returns** + +**Returns** - value: The parsed 32-bit complex number. - ok: `false` if a complex number could not be found, or if the input string contained more than just the number. */ @@ -1305,7 +1305,7 @@ parse_complex32 :: proc(str: string, n: ^int = nil) -> (value: complex32, ok: bo /* Parses a 256-bit quaternion from a string -**Inputs** +**Inputs** - str: The input string containing a 256-bit quaternion. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -1321,13 +1321,13 @@ Example: q, ok = strconv.parse_quaternion256("1+2i+3j+4k hellope", &n) fmt.printfln("%v %i %t", q, n, ok) } - + Output: 1+2i+3j+4k 10 true 1+2i+3j+4k 10 false - -**Returns** + +**Returns** - value: The parsed 256-bit quaternion. - ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion. */ @@ -1385,7 +1385,7 @@ parse_quaternion256 :: proc(str: string, n: ^int = nil) -> (value: quaternion256 /* Parses a 128-bit quaternion from a string -**Inputs** +**Inputs** - str: The input string containing a 128-bit quaternion. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -1401,13 +1401,13 @@ Example: q, ok = strconv.parse_quaternion128("1+2i+3j+4k hellope", &n) fmt.printfln("%v %i %t", q, n, ok) } - + Output: 1+2i+3j+4k 10 true 1+2i+3j+4k 10 false - -**Returns** + +**Returns** - value: The parsed 128-bit quaternion. - ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion. */ @@ -1419,7 +1419,7 @@ parse_quaternion128 :: proc(str: string, n: ^int = nil) -> (value: quaternion128 /* Parses a 64-bit quaternion from a string -**Inputs** +**Inputs** - str: The input string containing a 64-bit quaternion. - n: An optional pointer to an int to store the length of the parsed substring (default: nil). @@ -1435,13 +1435,13 @@ Example: q, ok = strconv.parse_quaternion64("1+2i+3j+4k hellope", &n) fmt.printfln("%v %i %t", q, n, ok) } - + Output: 1+2i+3j+4k 10 true 1+2i+3j+4k 10 false - -**Returns** + +**Returns** - value: The parsed 64-bit quaternion. - ok: `false` if a quaternion could not be found, or if the input string contained more than just the quaternion. */ @@ -1450,10 +1450,10 @@ parse_quaternion64 :: proc(str: string, n: ^int = nil) -> (value: quaternion64, v, ok = parse_quaternion256(str, n) return cast(quaternion64)v, ok } -/* +/* Writes a boolean value as a string to the given buffer -**Inputs** +**Inputs** - buf: The buffer to write the boolean value to - b: The boolean value to be written @@ -1471,7 +1471,7 @@ Output: true [116, 114, 117, 101, 0, 0] -**Returns** +**Returns** - The resulting string after writing the boolean value */ write_bool :: proc(buf: []byte, b: bool) -> string { @@ -1483,10 +1483,10 @@ write_bool :: proc(buf: []byte, b: bool) -> string { } return string(buf[:n]) } -/* +/* Writes an unsigned integer value as a string to the given buffer with the specified base -**Inputs** +**Inputs** - buf: The buffer to write the unsigned integer value to - u: The unsigned integer value to be written - base: The base to use for converting the integer value @@ -1505,16 +1505,16 @@ Output: 2a [50, 97, 0, 0] -**Returns** +**Returns** - The resulting string after writing the unsigned integer value */ write_uint :: proc(buf: []byte, u: u64, base: int) -> string { return write_bits(buf, u, base, false, 8*size_of(uint), digits, nil) } -/* +/* Writes a signed integer value as a string to the given buffer with the specified base -**Inputs** +**Inputs** - buf: The buffer to write the signed integer value to - i: The signed integer value to be written - base: The base to use for converting the integer value @@ -1533,7 +1533,7 @@ Output: -42 [45, 52, 50, 0] -**Returns** +**Returns** - The resulting string after writing the signed integer value */ write_int :: proc(buf: []byte, i: i64, base: int) -> string { @@ -1546,10 +1546,10 @@ write_u128 :: proc(buf: []byte, u: u128, base: int) -> string { return write_bits_128(buf, u, base, false, 8*size_of(uint), digits, nil) } -/* +/* Converts an integer value to a string and stores it in the given buffer -**Inputs** +**Inputs** - buf: The buffer to store the resulting string - i: The integer value to be converted @@ -1567,7 +1567,7 @@ Output: 42 [52, 50, 0, 0] -**Returns** +**Returns** - The resulting string after converting the integer value */ itoa :: proc(buf: []byte, i: int) -> string { @@ -1576,7 +1576,7 @@ itoa :: proc(buf: []byte, i: int) -> string { /* Converts a string to an integer value -**Inputs** +**Inputs** - s: The string to be converted Example: @@ -1591,17 +1591,17 @@ Output: 42 -**Returns** +**Returns** - The resulting integer value */ atoi :: proc(s: string) -> int { v, _ := parse_int(s) return v } -/* +/* Converts a string to a float64 value -**Inputs** +**Inputs** - s: The string to be converted Example: @@ -1616,7 +1616,7 @@ Output: 3.140 -**Returns** +**Returns** - The resulting float64 value after converting the string */ atof :: proc(s: string) -> f64 { @@ -1625,10 +1625,10 @@ atof :: proc(s: string) -> f64 { } // Alias to `write_float` ftoa :: write_float -/* +/* Writes a float64 value as a string to the given buffer with the specified format and precision -**Inputs** +**Inputs** - buf: The buffer to write the float64 value to - f: The float64 value to be written - fmt: The byte specifying the format to use for the conversion @@ -1649,7 +1649,7 @@ Output: +3.14 [43, 51, 46, 49, 52, 0, 0, 0] -**Returns** +**Returns** - The resulting string after writing the float */ write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { @@ -1658,11 +1658,11 @@ write_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> stri /* Writes a quoted string representation of the input string to a given byte slice and returns the result as a string -**Inputs** +**Inputs** - buf: The byte slice to which the quoted string will be written - str: The input string to be quoted -!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected +!! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected Example: @@ -1678,7 +1678,7 @@ Output: "'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0] -**Returns** +**Returns** - The resulting string after writing the quoted string representation */ quote :: proc(buf: []byte, str: string) -> string { @@ -1721,7 +1721,7 @@ quote :: proc(buf: []byte, str: string) -> string { /* Writes a quoted rune representation of the input rune to a given byte slice and returns the result as a string -**Inputs** +**Inputs** - buf: The byte slice to which the quoted rune will be written - r: The input rune to be quoted @@ -1739,7 +1739,7 @@ Output: 'A' [39, 65, 39, 0] -**Returns** +**Returns** - The resulting string after writing the quoted rune representation */ quote_rune :: proc(buf: []byte, r: rune) -> string { @@ -1800,11 +1800,11 @@ quote_rune :: proc(buf: []byte, r: rune) -> string { /* Unquotes a single character from the input string, considering the given quote character -**Inputs** +**Inputs** - str: The input string containing the character to unquote - quote: The quote character to consider (e.g., '"') -Example: +Example: import "core:fmt" import "core:strconv" @@ -1815,12 +1815,12 @@ Example: fmt.printf("r: <%v>, multiple_bytes:%v, tail_string:<%s>, success:%v\n",r, multiple_bytes, tail_string, success) } -Output: +Output: Source: 'The' raven r: <'>, multiple_bytes:false, tail_string:, success:true -**Returns** +**Returns** - r: The unquoted rune - multiple_bytes: A boolean indicating if the rune has multiple bytes - tail_string: The remaining portion of the input string after unquoting the character @@ -1923,13 +1923,13 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool /* Unquotes the input string considering any type of quote character and returns the unquoted string -**Inputs** +**Inputs** - lit: The input string to unquote - allocator: (default: context.allocator) WARNING: This procedure gives unexpected results if the quotes are not the first and last characters. -Example: +Example: import "core:fmt" import "core:strconv" @@ -1947,10 +1947,10 @@ Example: src="The raven \'Huginn\' is black." s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results fmt.println(src) - fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok) + fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok) } -Output: +Output: "The raven Huginn is black." Unquoted: , alloc:false, ok:true @@ -1961,7 +1961,7 @@ Output: The raven 'Huginn' is black. Unquoted: , alloc:false, ok:true -**Returns** +**Returns** - res: The resulting unquoted string - allocated: A boolean indicating if the resulting string was allocated using the provided allocator - success: A boolean indicating whether the unquoting was successful @@ -2002,7 +2002,7 @@ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: str return s, false, true } } - + context.allocator = allocator buf_len := 3*len(s) / 2 From 153f7af594f9ff3956ae22f3508620b645e9ad15 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 16:56:29 -0400 Subject: [PATCH 19/35] Rename `fixed.append` to `fixed.write` too, for good measure --- core/math/fixed/fixed.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/math/fixed/fixed.odin b/core/math/fixed/fixed.odin index 119e727a7..eafdbe78e 100644 --- a/core/math/fixed/fixed.odin +++ b/core/math/fixed/fixed.odin @@ -103,7 +103,7 @@ round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing { } @(require_results) -append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { +write :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { Integer_Width :: 8*size_of(Backing) - Fraction_Width x := x @@ -155,7 +155,7 @@ append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { @(require_results) to_string :: proc(x: $T/Fixed($Backing, $Fraction_Width), allocator := context.allocator) -> string { buf: [48]byte - s := append(buf[:], x) + s := write(buf[:], x) str := make([]byte, len(s), allocator) copy(str, s) return string(str) From 2760f43ce7d9c2c5cb3b055d7f8cd7a1d0c57bbd Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Thu, 5 Jun 2025 17:34:14 -0400 Subject: [PATCH 20/35] Add deprecation warnings for `strconv.append_*` --- core/math/fixed/fixed.odin | 5 +++++ core/strconv/deprecated.odin | 38 ++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 core/strconv/deprecated.odin diff --git a/core/math/fixed/fixed.odin b/core/math/fixed/fixed.odin index eafdbe78e..9af6d7599 100644 --- a/core/math/fixed/fixed.odin +++ b/core/math/fixed/fixed.odin @@ -294,3 +294,8 @@ _power_of_two_table := [129]string{ "85070591730234615865843651857942052864", "170141183460469231731687303715884105728", } + +@(deprecated="Use write instead") +append :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string { + return write(dst, x) +} diff --git a/core/strconv/deprecated.odin b/core/strconv/deprecated.odin new file mode 100644 index 000000000..883822e4b --- /dev/null +++ b/core/strconv/deprecated.odin @@ -0,0 +1,38 @@ +package strconv + +// (2025-06-05) These procedures are to be removed at a later release. + +@(deprecated="Use write_bits instead") +append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { + return write_bits(buf, x, base, is_signed, bit_size, digits, flags) +} + +@(deprecated="Use write_bits_128 instead") +append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { + return write_bits_128(buf, x, base, is_signed, bit_size, digits, flags) +} + +@(deprecated="Use write_bool instead") +append_bool :: proc(buf: []byte, b: bool) -> string { + return write_bool(buf, b) +} + +@(deprecated="Use write_uint instead") +append_uint :: proc(buf: []byte, u: u64, base: int) -> string { + return write_uint(buf, u, base) +} + +@(deprecated="Use write_int instead") +append_int :: proc(buf: []byte, i: i64, base: int) -> string { + return write_int(buf, i, base) +} + +@(deprecated="Use write_u128 instead") +append_u128 :: proc(buf: []byte, u: u128, base: int) -> string { + return write_u128(buf, u, base) +} + +@(deprecated="Use write_float instead") +append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { + return write_float(buf, f, fmt, prec, bit_size) +} From 4b36306674a8db103d123b079ac00d06e568d57e Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 6 Jun 2025 10:42:34 +0200 Subject: [PATCH 21/35] Deprecate old @(deprecated) things. --- core/math/big/private.odin | 2 +- core/sort/sort.odin | 46 ------ core/strings/strings.odin | 146 ++++++++++-------- core/sys/darwin/Foundation/NSApplication.odin | 1 - core/sys/windows/util.odin | 8 - core/sys/windows/ws2_32.odin | 6 - 6 files changed, 83 insertions(+), 126 deletions(-) diff --git a/core/math/big/private.odin b/core/math/big/private.odin index bb6b9497c..c42bad0c6 100644 --- a/core/math/big/private.odin +++ b/core/math/big/private.odin @@ -1370,8 +1370,8 @@ _private_int_div_recursive :: proc(quotient, remainder, a, b: ^Int, allocator := /* Slower bit-bang division... also smaller. + Prefer `_int_div_school` for speed. */ -@(deprecated="Use `_int_div_school`, it's 3.5x faster.") _private_int_div_small :: proc(quotient, remainder, numerator, denominator: ^Int) -> (err: Error) { ta, tb, tq, q := &Int{}, &Int{}, &Int{}, &Int{} diff --git a/core/sort/sort.odin b/core/sort/sort.odin index 322613cc4..c4aca4188 100644 --- a/core/sort/sort.odin +++ b/core/sort/sort.odin @@ -30,14 +30,6 @@ sort :: proc(it: Interface) { _quick_sort(it, 0, n, max_depth(n)) } - -@(deprecated="use slice.sort") -slice :: proc(array: $T/[]$E) where ORD(E) { - _slice.sort(array) - // s := array; - // sort(slice_interface(&s)); -} - slice_interface :: proc(s: ^$T/[]$E) -> Interface where ORD(E) { return Interface{ collection = rawptr(s), @@ -80,31 +72,6 @@ reverse_sort :: proc(it: Interface) { sort(reverse_interface(&it)) } -@(deprecated="use slice.reverse") -reverse_slice :: proc(array: $T/[]$E) where ORD(E) { - _slice.reverse(array) - /* - s := array; - sort(Interface{ - collection = rawptr(&s), - len = proc(it: Interface) -> int { - s := (^T)(it.collection); - return len(s^); - }, - less = proc(it: Interface, i, j: int) -> bool { - s := (^T)(it.collection); - return s[j] < s[i]; // manual set up - }, - swap = proc(it: Interface, i, j: int) { - s := (^T)(it.collection); - s[i], s[j] = s[j], s[i]; - }, - }); - */ -} - - - is_sorted :: proc(it: Interface) -> bool { n := it->len() for i := n-1; i > 0; i -= 1 { @@ -294,11 +261,6 @@ _insertion_sort :: proc(it: Interface, a, b: int) { } } - - - - -// @(deprecated="use sort.sort or slice.sort_by") bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { assert(f != nil) count := len(array) @@ -327,7 +289,6 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { } } -// @(deprecated="use sort.sort_slice or slice.sort") bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { count := len(array) @@ -355,7 +316,6 @@ bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { } } -// @(deprecated="use sort.sort or slice.sort_by") quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { assert(f != nil) a := array @@ -384,7 +344,6 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { quick_sort_proc(a[i:n], f) } -// @(deprecated="use sort.sort_slice or slice.sort") quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { a := array n := len(a) @@ -420,7 +379,6 @@ _log2 :: proc(x: int) -> int { return res } -// @(deprecated="use sort.sort or slice.sort_by") merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { merge :: proc(a: A, start, mid, end: int, f: proc(T, T) -> int) { s, m := start, mid @@ -462,7 +420,6 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { internal_sort(array, 0, len(array)-1, f) } -// @(deprecated="use sort.sort_slice or slice.sort") merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { merge :: proc(a: A, start, mid, end: int) { s, m := start, mid @@ -504,8 +461,6 @@ merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { internal_sort(array, 0, len(array)-1) } - -// @(deprecated="use sort.sort or slice.sort_by") heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { sift_proc :: proc(a: A, pi: int, n: int, f: proc(T, T) -> int) #no_bounds_check { p := pi @@ -540,7 +495,6 @@ heap_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { } } -// @(deprecated="use sort.sort_slice or slice.sort") heap_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { sift :: proc(a: A, pi: int, n: int) #no_bounds_check { p := pi diff --git a/core/strings/strings.odin b/core/strings/strings.odin index e45b177d7..ffa11f219 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -28,24 +28,7 @@ clone :: proc(s: string, allocator := context.allocator, loc := #caller_location copy(c, s) return string(c), nil } -/* -Clones a string safely (returns early with an allocation error on failure) -*Allocates Using Provided Allocator* - -Inputs: -- s: The string to be cloned -- allocator: (default: context.allocator) -- loc: The caller location for debugging purposes (default: #caller_location) - -Returns: -- res: The cloned string -- err: An allocator error if one occured, `nil` otherwise -*/ -@(deprecated="Prefer clone. It now returns an optional allocator error") -clone_safe :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) { - return clone(s, allocator, loc) -} /* Clones a string and appends a null-byte to make it a cstring @@ -66,6 +49,7 @@ clone_to_cstring :: proc(s: string, allocator := context.allocator, loc := #call c[len(s)] = 0 return cstring(&c[0]), nil } + /* Transmutes a raw pointer into a string. Non-allocating. @@ -81,6 +65,7 @@ Returns: string_from_ptr :: proc(ptr: ^byte, len: int) -> (res: string) { return transmute(string)mem.Raw_String{ptr, len} } + /* Transmutes a raw pointer (null-terminated) into a string. Non-allocating. Searches for a null-byte from `0.. (res: ^byte) { - d := transmute(mem.Raw_String)str - return d.data -} /* Converts a string `str` to a cstring @@ -128,6 +100,7 @@ unsafe_string_to_cstring :: proc(str: string) -> (res: cstring) { d := transmute(mem.Raw_String)str return cstring(d.data) } + /* Truncates a string `str` at the first occurrence of char/byte `b` @@ -147,6 +120,7 @@ truncate_to_byte :: proc "contextless" (str: string, b: byte) -> (res: string) { } return str[:n] } + /* Truncates a string `str` at the first occurrence of rune `r` as a slice of the original, entire string if not found @@ -164,6 +138,7 @@ truncate_to_rune :: proc(str: string, r: rune) -> (res: string) { } return str[:n] } + /* Clones a byte array `s` and appends a null-byte @@ -184,6 +159,7 @@ clone_from_bytes :: proc(s: []byte, allocator := context.allocator, loc := #call c[len(s)] = 0 return string(c[:len(s)]), nil } + /* Clones a cstring `s` as a string @@ -201,6 +177,7 @@ Returns: clone_from_cstring :: proc(s: cstring, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error { return clone(string(s), allocator, loc) } + /* Clones a string from a byte pointer `ptr` and a byte length `len` @@ -222,6 +199,7 @@ clone_from_ptr :: proc(ptr: ^byte, len: int, allocator := context.allocator, loc s := string_from_ptr(ptr, len) return clone(s, allocator, loc) } + // Overloaded procedure to clone from a string, `[]byte`, `cstring` or a `^byte` + length clone_from :: proc{ clone, @@ -229,6 +207,7 @@ clone_from :: proc{ clone_from_cstring, clone_from_ptr, } + /* Clones a string from a null-terminated cstring `ptr` and a byte length `len` @@ -251,6 +230,7 @@ clone_from_cstring_bounded :: proc(ptr: cstring, len: int, allocator := context. s = truncate_to_byte(s, 0) return clone(s, allocator, loc) } + /* Compares two strings, returning a value representing which one comes first lexicographically. -1 for `lhs`; 1 for `rhs`, or 0 if they are equal. @@ -265,6 +245,7 @@ Returns: compare :: proc "contextless" (lhs, rhs: string) -> (result: int) { return mem.compare(transmute([]byte)lhs, transmute([]byte)rhs) } + /* Checks if rune `r` in the string `s` @@ -283,6 +264,7 @@ contains_rune :: proc(s: string, r: rune) -> (result: bool) { } return false } + /* Returns true when the string `substr` is contained inside the string `s` @@ -314,6 +296,7 @@ Output: contains :: proc(s, substr: string) -> (res: bool) { return index(s, substr) >= 0 } + /* Returns `true` when the string `s` contains any of the characters inside the string `chars` @@ -386,6 +369,7 @@ Output: rune_count :: proc(s: string) -> (res: int) { return utf8.rune_count_in_string(s) } + /* Returns whether the strings `u` and `v` are the same alpha characters, ignoring different casings Works with UTF-8 string content @@ -508,6 +492,7 @@ prefix_length :: proc "contextless" (a, b: string) -> (n: int) { } return } + /* Returns the common prefix between strings `a` and `b` @@ -540,6 +525,7 @@ Output: common_prefix :: proc(a, b: string) -> string { return a[:prefix_length(a, b)] } + /* Determines if a string `s` starts with a given `prefix` @@ -661,24 +647,7 @@ join :: proc(a: []string, sep: string, allocator := context.allocator, loc := #c } return string(b), nil } -/* -Joins a slice of strings `a` with a `sep` string, returns an error on allocation failure -*Allocates Using Provided Allocator* - -Inputs: -- a: A slice of strings to join -- sep: The separator string -- allocator: (default is context.allocator) - -Returns: -- str: A combined string from the slice of strings `a` separated with the `sep` string -- err: An allocator error if one occured, `nil` otherwise -*/ -@(deprecated="Prefer join. It now returns an optional allocator error") -join_safe :: proc(a: []string, sep: string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { - return join(a, sep, allocator) -} /* Returns a combined string from the slice of strings `a` without a separator @@ -723,22 +692,6 @@ concatenate :: proc(a: []string, allocator := context.allocator, loc := #caller_ } return string(b), nil } -/* -Returns a combined string from the slice of strings `a` without a separator, or an error if allocation fails - -*Allocates Using Provided Allocator* - -Inputs: -- a: A slice of strings to concatenate -- allocator: (default is context.allocator) - -Returns: -The concatenated string, and an error if allocation fails -*/ -@(deprecated="Prefer concatenate. It now returns an optional allocator error") -concatenate_safe :: proc(a: []string, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) { - return concatenate(a, allocator) -} /* Returns a substring of the input string `s` with the specified rune offset and length @@ -901,6 +854,7 @@ _split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocato return res[:i+1], nil } + /* Splits a string into parts based on a separator. @@ -936,6 +890,7 @@ Output: split :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { return _split(s, sep, 0, -1, allocator) } + /* Splits a string into parts based on a separator. If n < count of seperators, the remainder of the string is returned in the last entry. @@ -972,6 +927,7 @@ Output: split_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { return _split(s, sep, 0, n, allocator) } + /* Splits a string into parts after the separator, retaining it in the substrings. @@ -1007,6 +963,7 @@ Output: split_after :: proc(s, sep: string, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { return _split(s, sep, len(sep), -1, allocator) } + /* Splits a string into a total of `n` parts after the separator. @@ -1043,6 +1000,7 @@ Output: split_after_n :: proc(s, sep: string, n: int, allocator := context.allocator) -> (res: []string, err: mem.Allocator_Error) #optional_allocator_error { return _split(s, sep, len(sep), n, allocator) } + /* Searches for the first occurrence of `sep` in the given string and returns the substring up to (but not including) the separator, as well as a boolean indicating success. @@ -1083,6 +1041,7 @@ _split_iterator :: proc(s: ^string, sep: string, sep_save: int) -> (res: string, } return } + /* Splits the input string by the byte separator in an iterator fashion. @@ -1129,6 +1088,7 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { } return } + /* Splits the input string by the separator string in an iterator fashion. @@ -1164,6 +1124,7 @@ Output: split_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) { return _split_iterator(s, sep, 0) } + /* Splits the input string after every separator string in an iterator fashion. @@ -1199,6 +1160,7 @@ Output: split_after_iterator :: proc(s: ^string, sep: string) -> (res: string, ok: bool) { return _split_iterator(s, sep, len(sep)) } + /* Trims the carriage return character from the end of the input string. @@ -1220,6 +1182,7 @@ _trim_cr :: proc(s: string) -> (res: string) { } return s } + /* Splits the input string at every line break `\n`. @@ -1257,6 +1220,7 @@ split_lines :: proc(s: string, allocator := context.allocator) -> (res: []string } return lines, nil } + /* Splits the input string at every line break `\n` for `n` parts. @@ -1297,6 +1261,7 @@ split_lines_n :: proc(s: string, n: int, allocator := context.allocator) -> (res } return lines, nil } + /* Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. @@ -1336,6 +1301,7 @@ split_lines_after :: proc(s: string, allocator := context.allocator) -> (res: [] } return lines, nil } + /* Splits the input string at every line break `\n` leaving the `\n` in the resulting strings. Only runs for n parts. @@ -1377,6 +1343,7 @@ split_lines_after_n :: proc(s: string, n: int, allocator := context.allocator) - } return lines, nil } + /* Splits the input string at every line break `\n`. Returns the current split string every iteration until the string is consumed. @@ -1411,6 +1378,7 @@ split_lines_iterator :: proc(s: ^string) -> (line: string, ok: bool) { line = _split_iterator(s, sep, 0) or_return return _trim_cr(line), true } + /* Splits the input string at every line break `\n`. Returns the current split string with line breaks included every iteration until the string is consumed. @@ -1448,6 +1416,7 @@ split_lines_after_iterator :: proc(s: ^string) -> (line: string, ok: bool) { line = _split_iterator(s, sep, len(sep)) or_return return _trim_cr(line), true } + /* Returns the byte offset of the first byte `c` in the string s it finds, -1 when not found. NOTE: Can't find UTF-8 based runes. @@ -1482,6 +1451,7 @@ Output: index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { return #force_inline bytes.index_byte(transmute([]u8)s, c) } + /* Returns the byte offset of the last byte `c` in the string `s`, -1 when not found. @@ -1517,6 +1487,7 @@ Output: last_index_byte :: proc "contextless" (s: string, c: byte) -> (res: int) { return #force_inline bytes.last_index_byte(transmute([]u8)s, c) } + /* Returns the byte offset of the first rune `r` in the string `s` it finds, -1 when not found. Invalid runes return -1 @@ -1657,6 +1628,7 @@ index :: proc "contextless" (s, substr: string) -> (res: int) { } return -1 } + /* Returns the last byte offset of the string `substr` in the string `s`, -1 when not found. @@ -1734,6 +1706,7 @@ last_index :: proc(s, substr: string) -> (res: int) { } return -1 } + /* Returns the index of any first char of `chars` found in `s`, -1 if not found. @@ -1797,6 +1770,7 @@ index_any :: proc(s, chars: string) -> (res: int) { } return -1 } + /* Finds the last occurrence of any character in `chars` within `s`. Iterates in reverse. @@ -1878,6 +1852,7 @@ last_index_any :: proc(s, chars: string) -> (res: int) { } return -1 } + /* Finds the first occurrence of any substring in `substrs` within `s` @@ -1919,6 +1894,7 @@ index_multi :: proc(s: string, substrs: []string) -> (idx: int, width: int) { } return } + /* Counts the number of non-overlapping occurrences of `substr` in `s` @@ -1985,6 +1961,7 @@ count :: proc(s, substr: string) -> (res: int) { } return n } + /* Repeats the string `s` `count` times, concatenating the result @@ -2030,6 +2007,7 @@ repeat :: proc(s: string, count: int, allocator := context.allocator, loc := #ca } return string(b), nil } + /* Replaces all occurrences of `old` in `s` with `new` @@ -2063,9 +2041,11 @@ Output: zzzz true */ + replace_all :: proc(s, old, new: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, old, new, -1, allocator) } + /* Replaces n instances of old in the string s with the new string @@ -2144,6 +2124,7 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator, loc output = string(t[0:w]) return } + /* Removes the key string `n` times from the `s` string @@ -2182,6 +2163,7 @@ Output: remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) { return replace(s, key, "", n, allocator) } + /* Removes all the `key` string instances from the `s` string @@ -2217,6 +2199,7 @@ Output: remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) { return remove(s, key, -1, allocator) } + // Returns true if is an ASCII space character ('\t', '\n', '\v', '\f', '\r', ' ') @(private) _ascii_space := [256]bool{'\t' = true, '\n' = true, '\v' = true, '\f' = true, '\r' = true, ' ' = true} @@ -2320,6 +2303,7 @@ index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int } return -1 } + // Same as `index_proc`, but the procedure p takes a raw pointer for state index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) { for r, i in s { @@ -2329,6 +2313,7 @@ index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: r } return -1 } + // Finds the index of the *last* rune in the string s for which the procedure p returns the same value as truth last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res: int) { // TODO(bill): Probably use Rabin-Karp Search @@ -2341,6 +2326,7 @@ last_index_proc :: proc(s: string, p: proc(rune) -> bool, truth := true) -> (res } return -1 } + // Same as `index_proc_with_state`, runs through the string in reverse last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, state: rawptr, truth := true) -> (res: int) { // TODO(bill): Probably use Rabin-Karp Search @@ -2353,6 +2339,7 @@ last_index_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return -1 } + /* Trims the input string `s` from the left until the procedure `p` returns false @@ -2387,6 +2374,7 @@ trim_left_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) { } return s[i:] } + /* Trims the input string `s` from the left until the procedure `p` with state returns false @@ -2405,6 +2393,7 @@ trim_left_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, stat } return s[i:] } + /* Trims the input string `s` from the right until the procedure `p` returns `false` @@ -2442,6 +2431,7 @@ trim_right_proc :: proc(s: string, p: proc(rune) -> bool) -> (res: string) { } return s[0:i] } + /* Trims the input string `s` from the right until the procedure `p` with state returns `false` @@ -2463,6 +2453,7 @@ trim_right_proc_with_state :: proc(s: string, p: proc(rawptr, rune) -> bool, sta } return s[0:i] } + // Procedure for `trim_*_proc` variants, which has a string rawptr cast + rune comparison is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) { cutset := (^string)(state)^ @@ -2473,6 +2464,7 @@ is_in_cutset :: proc(state: rawptr, r: rune) -> (res: bool) { } return false } + /* Trims the cutset string from the `s` string @@ -2490,6 +2482,7 @@ trim_left :: proc(s: string, cutset: string) -> (res: string) { state := cutset return trim_left_proc_with_state(s, is_in_cutset, &state) } + /* Trims the cutset string from the `s` string from the right @@ -2507,6 +2500,7 @@ trim_right :: proc(s: string, cutset: string) -> (res: string) { state := cutset return trim_right_proc_with_state(s, is_in_cutset, &state) } + /* Trims the cutset string from the `s` string, both from left and right @@ -2520,6 +2514,7 @@ Returns: trim :: proc(s: string, cutset: string) -> (res: string) { return trim_right(trim_left(s, cutset), cutset) } + /* Trims until a valid non-space rune from the left, "\t\txyz\t\t" -> "xyz\t\t" @@ -2532,6 +2527,7 @@ Returns: trim_left_space :: proc(s: string) -> (res: string) { return trim_left_proc(s, is_space) } + /* Trims from the right until a valid non-space rune, "\t\txyz\t\t" -> "\t\txyz" @@ -2544,6 +2540,7 @@ Returns: trim_right_space :: proc(s: string) -> (res: string) { return trim_right_proc(s, is_space) } + /* Trims from both sides until a valid non-space rune, "\t\txyz\t\t" -> "xyz" @@ -2556,6 +2553,7 @@ Returns: trim_space :: proc(s: string) -> (res: string) { return trim_right_space(trim_left_space(s)) } + /* Trims null runes from the left, "\x00\x00testing\x00\x00" -> "testing\x00\x00" @@ -2568,6 +2566,7 @@ Returns: trim_left_null :: proc(s: string) -> (res: string) { return trim_left_proc(s, is_null) } + /* Trims null runes from the right, "\x00\x00testing\x00\x00" -> "\x00\x00testing" @@ -2580,6 +2579,7 @@ Returns: trim_right_null :: proc(s: string) -> (res: string) { return trim_right_proc(s, is_null) } + /* Trims null runes from both sides, "\x00\x00testing\x00\x00" -> "testing" @@ -2591,6 +2591,7 @@ Returns: trim_null :: proc(s: string) -> (res: string) { return trim_right_null(trim_left_null(s)) } + /* Trims a `prefix` string from the start of the `s` string and returns the trimmed string @@ -2623,6 +2624,7 @@ trim_prefix :: proc(s, prefix: string) -> (res: string) { } return s } + /* Trims a `suffix` string from the end of the `s` string and returns the trimmed string @@ -2655,6 +2657,7 @@ trim_suffix :: proc(s, suffix: string) -> (res: string) { } return s } + /* Splits the input string `s` by all possible `substrs` and returns an allocated array of strings @@ -2727,6 +2730,7 @@ split_multi :: proc(s: string, substrs: []string, allocator := context.allocator assert(len(results) == n) return results[:], nil } + /* Splits the input string `s` by all possible `substrs` in an iterator fashion. The full string is returned if no match. @@ -2786,6 +2790,7 @@ split_multi_iterate :: proc(it: ^string, substrs: []string) -> (res: string, ok: ok = true return } + /* Replaces invalid UTF-8 characters in the input string with a specified replacement string. Adjacent invalid bytes are only replaced once. @@ -2846,6 +2851,7 @@ scrub :: proc(s: string, replacement: string, allocator := context.allocator) -> return to_string(b), nil } + /* Reverses the input string `s` @@ -2889,6 +2895,7 @@ reverse :: proc(s: string, allocator := context.allocator, loc := #caller_locati } return string(buf), nil } + /* Expands the input string by replacing tab characters with spaces to align to a specified tab size @@ -2920,6 +2927,7 @@ Output: abc1 abc2 abc3 */ + expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error { if tab_size <= 0 { panic("tab size must be positive") @@ -2961,6 +2969,7 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return to_string(b), nil } + /* Splits the input string `str` by the separator `sep` string and returns 3 parts. The values are slices of the original string. @@ -3011,8 +3020,10 @@ partition :: proc(str, sep: string) -> (head, match, tail: string) { tail = str[i+len(sep):] return } + // Alias for centre_justify center_justify :: centre_justify // NOTE(bill): Because Americans exist + /* Centers the input string within a field of specified length by adding pad string on both sides, if its length is less than the target length. @@ -3048,6 +3059,7 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte return to_string(b), nil } + /* Left-justifies the input string within a field of specified length by adding pad string on the right side, if its length is less than the target length. @@ -3082,6 +3094,7 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context return to_string(b), nil } + /* Right-justifies the input string within a field of specified length by adding pad string on the left side, if its length is less than the target length. @@ -3116,6 +3129,7 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex return to_string(b), nil } + /* Writes a given pad string a specified number of times to an `io.Writer` @@ -3142,6 +3156,7 @@ write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { p = p[width:] } } + /* Splits a string into a slice of substrings at each instance of one or more consecutive white space characters, as defined by `unicode.is_space` @@ -3203,6 +3218,7 @@ fields :: proc(s: string, allocator := context.allocator, loc := #caller_locatio } return a, nil } + /* Splits a string into a slice of substrings at each run of unicode code points `r` satisfying the predicate `f(r)` @@ -3245,6 +3261,7 @@ fields_proc :: proc(s: string, f: proc(rune) -> bool, allocator := context.alloc return substrings[:], nil } + /* Retrieves the first non-space substring from a mutable string reference and advances the reference. `s` is advanced from any space after the substring, or be an empty string if the substring was the remaining characters @@ -3283,6 +3300,7 @@ fields_iterator :: proc(s: ^string) -> (field: string, ok: bool) { s^ = s[len(s):] return } + /* Computes the Levenshtein edit distance between two strings @@ -3460,4 +3478,4 @@ substring_to :: proc(s: string, rune_end: int) -> (sub: string, ok: bool) { } return internal_substring(s, -1, rune_end) -} +} \ No newline at end of file diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin index cad2ba777..a5e9f2e24 100644 --- a/core/sys/darwin/Foundation/NSApplication.odin +++ b/core/sys/darwin/Foundation/NSApplication.odin @@ -82,7 +82,6 @@ Application_setActivationPolicy :: proc "c" (self: ^Application, activationPolic // NOTE: this is technically deprecated but still actively used (Sokol, glfw, SDL, etc.) // and has no clear alternative although `activate` is what Apple tells you to use, // that does not work the same way. -// @(deprecated="Use NSApplication method activate instead.") @(objc_type=Application, objc_name="activateIgnoringOtherApps") Application_activateIgnoringOtherApps :: proc "c" (self: ^Application, ignoreOtherApps: BOOL) { msgSend(nil, self, "activateIgnoringOtherApps:", ignoreOtherApps) diff --git a/core/sys/windows/util.odin b/core/sys/windows/util.odin index b3eb800bc..2db55c4fe 100644 --- a/core/sys/windows/util.odin +++ b/core/sys/windows/util.odin @@ -75,14 +75,6 @@ LANGIDFROMLCID :: #force_inline proc "contextless" (lcid: LCID) -> LANGID { return LANGID(lcid) } -// this one gave me trouble as it do not mask the values. -// the _ in the name is also off comparing to the c code -// i can't find any usage in the odin repo -@(deprecated = "use MAKEWORD") -MAKE_WORD :: #force_inline proc "contextless" (x, y: WORD) -> WORD { - return x << 8 | y -} - utf8_to_utf16 :: proc(s: string, allocator := context.temp_allocator) -> []u16 { if len(s) < 1 { return nil diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index 0fc427728..6c68e43f3 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -242,10 +242,4 @@ foreign ws2_32 { ntohl :: proc(netlong: c_ulong) -> c_ulong --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs) ntohs :: proc(netshort: c_ushort) -> c_ushort --- - // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl) - @(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types") - htonl :: proc(hostlong: c_ulong) -> c_ulong --- - // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons) - @(deprecated="Use endian specific integers instead, https://odin-lang.org/docs/overview/#basic-types") - htons :: proc(hostshort: c_ushort) -> c_ushort --- } From f1ce9f98d8e5c838b353ac54b527bd9016dce8e6 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Fri, 6 Jun 2025 13:13:34 +0200 Subject: [PATCH 22/35] Re-add htonl and htons for those porting from C. --- core/sys/windows/ws2_32.odin | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/sys/windows/ws2_32.odin b/core/sys/windows/ws2_32.odin index 6c68e43f3..cb1071d9e 100644 --- a/core/sys/windows/ws2_32.odin +++ b/core/sys/windows/ws2_32.odin @@ -242,4 +242,10 @@ foreign ws2_32 { ntohl :: proc(netlong: c_ulong) -> c_ulong --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-ntohs) ntohs :: proc(netshort: c_ushort) -> c_ushort --- -} + // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htonl) + // Prefer using endian-specific integers instead, https://odin-lang.org/docs/overview/#basic-types + htonl :: proc(hostlong: c_ulong) -> c_ulong --- + // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-htons) + // Prefer using endian-specific integers instead, https://odin-lang.org/docs/overview/#basic-types + htons :: proc(hostshort: c_ushort) -> c_ushort --- +} \ No newline at end of file From 1cd1f9fec4307681cfb27555516188b2a2d8fd9a Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Fri, 6 Jun 2025 09:23:06 -0400 Subject: [PATCH 23/35] Add `nullptr` check in `add_type_info_type_internal` Fixes #5215 --- src/checker.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/checker.cpp b/src/checker.cpp index 9bc02cd87..ff7194835 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2054,7 +2054,7 @@ gb_internal void add_type_info_type(CheckerContext *c, Type *t) { } gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) { - if (t == nullptr) { + if (t == nullptr || c == nullptr) { return; } From 3152be01b9653fcf54d5d879bf9acde465f52a97 Mon Sep 17 00:00:00 2001 From: iarkn Date: Fri, 6 Jun 2025 16:01:13 +0200 Subject: [PATCH 24/35] Add `intrinsics.type_is_bit_field` proc --- base/intrinsics/intrinsics.odin | 1 + src/check_builtin.cpp | 2 ++ src/checker_builtin_procs.hpp | 2 ++ 3 files changed, 5 insertions(+) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index 46e39c8d1..c1d16c5e4 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -169,6 +169,7 @@ type_is_union :: proc($T: typeid) -> bool --- type_is_enum :: proc($T: typeid) -> bool --- type_is_proc :: proc($T: typeid) -> bool --- type_is_bit_set :: proc($T: typeid) -> bool --- +type_is_bit_field :: proc($T: typeid) -> bool --- type_is_simd_vector :: proc($T: typeid) -> bool --- type_is_matrix :: proc($T: typeid) -> bool --- diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 700978a76..bd265affc 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -41,6 +41,7 @@ gb_global BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_bool is_type_enum, is_type_proc, is_type_bit_set, + is_type_bit_field, is_type_simd_vector, is_type_matrix, @@ -6081,6 +6082,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As case BuiltinProc_type_is_enum: case BuiltinProc_type_is_proc: case BuiltinProc_type_is_bit_set: + case BuiltinProc_type_is_bit_field: case BuiltinProc_type_is_simd_vector: case BuiltinProc_type_is_matrix: case BuiltinProc_type_is_specialized_polymorphic_record: diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index f3b55daa4..90652cb0b 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -240,6 +240,7 @@ BuiltinProc__type_begin, BuiltinProc__type_simple_boolean_begin, BuiltinProc_type_is_boolean, + BuiltinProc_type_is_bit_field, BuiltinProc_type_is_integer, BuiltinProc_type_is_rune, BuiltinProc_type_is_float, @@ -630,6 +631,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_matrix"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, From 17746db55583ddb7453df5d06ea477738907a662 Mon Sep 17 00:00:00 2001 From: Ely Alon Date: Sat, 7 Jun 2025 01:22:30 +0300 Subject: [PATCH 25/35] Fix user dirs, add docs --- core/os/os2/errors.odin | 3 + core/os/os2/user.odin | 97 +++++++++++++++++++-- core/os/os2/user_posix.odin | 160 +++++++++++++++++++++++++--------- core/os/os2/user_windows.odin | 20 +++-- 4 files changed, 223 insertions(+), 57 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index b50f04cab..0aff335bb 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -27,6 +27,8 @@ General_Error :: enum u32 { Pattern_Has_Separator, + No_HOME_Variable, + Unsupported, } @@ -73,6 +75,7 @@ error_string :: proc(ferr: Error) -> string { case .Invalid_Command: return "invalid command" case .Unsupported: return "unsupported" case .Pattern_Has_Separator: return "pattern has separator" + case .No_HOME_Variable: return "no $HOME variable" } case io.Error: switch e { diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 0f6134f21..e2a4ec4d0 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -2,64 +2,147 @@ package os2 import "base:runtime" +// ``` +// Windows: C:\Users\Alice +// macOS: /Users/Alice +// Linux: /home/alice +// ``` @(require_results) user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_home_dir(allocator) } -// application caches, logs, temporary files +// Files that applications can regenerate/refetch at a loss of speed, e.g. shader caches +// +// Sometimes deleted for system maintenance +// +// ``` +// Windows: C:\Users\Alice\AppData\Local +// macOS: /Users/Alice/Library/Caches +// Linux: /home/alice/.cache +// ``` @(require_results) user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_cache_dir(allocator) } -// application assets +// User-hidden application data +// +// ``` +// Windows: C:\Users\Alice\AppData\Local ("C:\Users\Alice\AppData\Roaming" if `roaming`) +// macOS: /Users/Alice/Library/Application Support +// Linux: /home/alice/.local/share +// ``` +// +// NOTE: (Windows only) `roaming` is for syncing across multiple devices within a *domain network* @(require_results) -user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_data_dir(allocator) +user_data_dir :: proc(allocator: runtime.Allocator, roaming := false) -> (dir: string, err: Error) { + return _user_data_dir(allocator, roaming) } -// application history, ui layout state, logs +// Non-essential application data, e.g. history, ui layout state +// +// ``` +// Windows: C:\Users\Alice\AppData\Local +// macOS: /Users/Alice/Library/Application Support +// Linux: /home/alice/.local/state +// ``` @(require_results) user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_state_dir(allocator) } +// Application log files +// +// ``` +// Windows: C:\Users\Alice\AppData\Local +// macOS: /Users/Alice/Library/Logs +// Linux: /home/alice/.local/state +// ``` @(require_results) -user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - return _user_config_dir(allocator) +user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + return _user_log_dir(allocator) } +// Application settings/preferences +// +// ``` +// Windows: C:\Users\Alice\AppData\Local ("C:\Users\Alice\AppData\Roaming" if `roaming`) +// macOS: /Users/Alice/Library/Application Support +// Linux: /home/alice/.config +// ``` +// +// NOTE: (Windows only) `roaming` is for syncing across multiple devices within a *domain network* +@(require_results) +user_config_dir :: proc(allocator: runtime.Allocator, roaming := false) -> (dir: string, err: Error) { + return _user_config_dir(allocator, roaming) +} + +// ``` +// Windows: C:\Users\Alice\Music +// macOS: /Users/Alice/Music +// Linux: /home/alice/Music +// ``` @(require_results) user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_music_dir(allocator) } +// ``` +// Windows: C:\Users\Alice\Desktop +// macOS: /Users/Alice/Desktop +// Linux: /home/alice/Desktop +// ``` @(require_results) user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_desktop_dir(allocator) } +// ``` +// Windows: C:\Users\Alice\Documents +// macOS: /Users/Alice/Documents +// Linux: /home/alice/Documents +// ``` @(require_results) user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_documents_dir(allocator) } +// ``` +// Windows: C:\Users\Alice\Downloads +// macOS: /Users/Alice/Downloads +// Linux: /home/alice/Downloads +// ``` @(require_results) user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_downloads_dir(allocator) } +// ``` +// Windows: C:\Users\Alice\Pictures +// macOS: /Users/Alice/Pictures +// Linux: /home/alice/Pictures +// ``` @(require_results) user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_pictures_dir(allocator) } +// ``` +// Windows: C:\Users\Alice\Public +// macOS: /Users/Alice/Public +// Linux: /home/alice/Public +// ``` @(require_results) user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_public_dir(allocator) } +// ``` +// Windows: C:\Users\Alice\Videos +// macOS: /Users/Alice/Movies +// Linux: /home/alice/Videos +// ``` @(require_results) user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { return _user_videos_dir(allocator) diff --git a/core/os/os2/user_posix.odin b/core/os/os2/user_posix.odin index 7541a99e1..cc0f51f7d 100644 --- a/core/os/os2/user_posix.odin +++ b/core/os/os2/user_posix.odin @@ -2,103 +2,115 @@ package os2 import "base:runtime" +import "core:strings" +import "core:sys/posix" +import "core:fmt" //remove _user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Library/Caches", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_CACHE_HOME", "HOME", "/.cache", allocator) + return _xdg_lookup("", "/Library/Caches", allocator) + case: // Unix + return _xdg_lookup("XDG_CACHE_HOME", "/.cache", allocator) } } -_user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_config_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/.config", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_CONFIG_HOME", "HOME", "/.config", allocator) + return _xdg_lookup("", "/Library/Application Support", allocator) + case: // Unix + return _xdg_lookup("XDG_CONFIG_HOME", "/.config", allocator) } } _user_state_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/.local/state", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_STATE_HOME", "HOME", "/.local/state", allocator) + return _xdg_lookup("", "/Library/Application Support", allocator) + case: // Unix + return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator) } } -_user_data_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_user_log_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/.local/share", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_DATA_HOME", "HOME", "/.local/share", allocator) + return _xdg_lookup("", "/Library/Logs", allocator) + case: // Unix + return _xdg_lookup("XDG_STATE_HOME", "/.local/state", allocator) + } +} + +_user_data_dir :: proc(allocator: runtime.Allocator, _roaming: bool) -> (dir: string, err: Error) { + #partial switch ODIN_OS { + case .Darwin: + return _xdg_lookup("", "/Library/Application Support", allocator) + case: // Unix + return _xdg_lookup("XDG_DATA_HOME", "/.local/share", allocator) } } _user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Music", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_MUSIC_DIR", "HOME", "/Music", allocator) + return _xdg_lookup("", "/Music", allocator) + case: // Unix + return _xdg_lookup("XDG_MUSIC_DIR", "/Music", allocator) } } _user_desktop_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Desktop", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_DESKTOP_DIR", "HOME", "/Desktop", allocator) + return _xdg_lookup("", "/Desktop", allocator) + case: // Unix + return _xdg_lookup("XDG_DESKTOP_DIR", "/Desktop", allocator) } } _user_documents_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Documents", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_DOCUMENTS_DIR", "HOME", "/Documents", allocator) + return _xdg_lookup("", "/Documents", allocator) + case: // Unix + return _xdg_lookup("XDG_DOCUMENTS_DIR", "/Documents", allocator) } } _user_downloads_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Downloads", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_DOWNLOAD_DIR", "HOME", "/Downloads", allocator) + return _xdg_lookup("", "/Downloads", allocator) + case: // Unix + return _xdg_lookup("XDG_DOWNLOAD_DIR", "/Downloads", allocator) } } _user_pictures_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Pictures", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_PICTURES_DIR", "HOME", "/Pictures", allocator) + return _xdg_lookup("", "/Pictures", allocator) + case: // Unix + return _xdg_lookup("XDG_PICTURES_DIR", "/Pictures", allocator) } } _user_public_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Public", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_PUBLIC_DIR", "HOME", "/Public", allocator) + return _xdg_lookup("", "/Public", allocator) + case: // Unix + return _xdg_lookup("XDG_PUBLICSHARE_DIR", "/Public", allocator) } } _user_videos_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Darwin: - return _xdg_lookup("", "HOME", "/Movies", allocator) - case: // All other UNIX systems - return _xdg_lookup("XDG_VIDEOS_DIR", "HOME", "/Videos", allocator) + return _xdg_lookup("", "/Movies", allocator) + case: // Unix + return _xdg_lookup("XDG_VIDEOS_DIR", "/Videos", allocator) } } @@ -106,27 +118,89 @@ _user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error if v := get_env("HOME", allocator); v != "" { return v, nil } - return "", .Invalid_Path + err = .No_HOME_Variable + return } -_xdg_lookup :: proc(xdg_env, fallback_env: string, fallback_suffix: string, allocator: runtime.Allocator) -> (dir: string, err: Error) { +_xdg_lookup :: proc(xdg_key: string, fallback_suffix: string, allocator: runtime.Allocator) -> (dir: string, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - if xdg_env == "" { // Darwin doesn't have XDG paths. - dir = get_env(fallback_env, temp_allocator) + if xdg_key == "" { // Darwin doesn't have XDG paths. + dir = get_env("HOME", temp_allocator) if dir == "" { - return "", .Invalid_Path + err = .No_HOME_Variable + return } return concatenate({dir, fallback_suffix}, allocator) } else { - dir = get_env(xdg_env, allocator) + if strings.ends_with(xdg_key, "_DIR") { + dir = _xdg_user_dirs_lookup(xdg_key, allocator) or_return + } else { + dir = get_env(xdg_key, allocator) + } + if dir == "" { - dir = get_env(fallback_env, temp_allocator) + dir = get_env("HOME", temp_allocator) if dir == "" { - return "", .Invalid_Path + err = .No_HOME_Variable + return } dir = concatenate({dir, fallback_suffix}, allocator) or_return } return } +} + +// If `/user-dirs.dirs` doesn't exist, or `xdg_key` can't be found there: returns `""` +_xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) -> (dir: string, err: Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + config_dir := user_config_dir(temp_allocator) or_return + + user_dirs_path := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return + user_dirs_content_bytes, read_err := read_entire_file(user_dirs_path, temp_allocator) + if read_err == .Not_Exist { + return + } else if read_err != nil { + err = read_err + return + } + user_dirs_content := string(user_dirs_content_bytes) + + lines := strings.split_lines(user_dirs_content, temp_allocator) or_return + + home_env := get_env("HOME", temp_allocator) + if home_env == "" { + err = .No_HOME_Variable + return + } + + for line in lines { + ss := strings.split_n(line, "=", 2, temp_allocator) or_return + (len(ss) == 2) or_continue + sl := strings.trim_space(ss[0]) + sr := ss[1] + + (sl == xdg_key) or_continue + + (len(sr) > 2) or_continue + + lq := strings.index_byte(sr, '"') + (lq != -1) or_continue + rq := strings.index_byte(sr[lq+1:], '"') + lq+1 + (rq != -1) or_continue + + sr = sr[lq+1:rq] + + we: posix.wordexp_t + we_err := posix.wordexp(strings.clone_to_cstring(sr, temp_allocator), &we, nil) + (we_err == nil) or_continue + defer posix.wordfree(&we) + + (we.we_wordc == 1) or_continue + + dir = strings.clone_from_cstring(we.we_wordv[0], allocator) or_return + return + } + return } \ No newline at end of file diff --git a/core/os/os2/user_windows.odin b/core/os/os2/user_windows.odin index 94ac3c566..d68f933ce 100644 --- a/core/os/os2/user_windows.odin +++ b/core/os/os2/user_windows.odin @@ -3,25 +3,31 @@ package os2 import "base:runtime" @(require) import win32 "core:sys/windows" -_user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { +_local_appdata :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { guid := win32.FOLDERID_LocalAppData return _get_known_folder_path(&guid, allocator) } -_user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { - guid := win32.FOLDERID_RoamingAppData +_local_appdata_or_roaming :: proc(allocator: runtime.Allocator, roaming: bool) -> (dir: string, err: Error) { + guid := win32.FOLDERID_LocalAppData + if roaming { + guid = win32.FOLDERID_RoamingAppData + } return _get_known_folder_path(&guid, allocator) } +_user_config_dir :: _local_appdata_or_roaming +_user_data_dir :: _local_appdata_or_roaming + +_user_state_dir :: _local_appdata +_user_log_dir :: _local_appdata +_user_cache_dir :: _local_appdata + _user_home_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { guid := win32.FOLDERID_Profile return _get_known_folder_path(&guid, allocator) } -_user_data_dir :: _user_config_dir - -_user_state_dir :: _user_cache_dir - _user_music_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { guid := win32.FOLDERID_Music return _get_known_folder_path(&guid, allocator) From deeb9e2d12b17321669b01c745716b0374e983a0 Mon Sep 17 00:00:00 2001 From: Ely Alon Date: Sat, 7 Jun 2025 01:40:43 +0300 Subject: [PATCH 26/35] remove fmt import --- core/os/os2/user_posix.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/os/os2/user_posix.odin b/core/os/os2/user_posix.odin index cc0f51f7d..2c31f7eb8 100644 --- a/core/os/os2/user_posix.odin +++ b/core/os/os2/user_posix.odin @@ -4,7 +4,6 @@ package os2 import "base:runtime" import "core:strings" import "core:sys/posix" -import "core:fmt" //remove _user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { From 4a6c6cd3981db94dffffed315bb31f4c25443f39 Mon Sep 17 00:00:00 2001 From: Karl Zylinski Date: Sun, 8 Jun 2025 00:11:31 +0200 Subject: [PATCH 27/35] Fixed broken bindings of ICompiler and ICompiler2. See https://learn.microsoft.com/en-us/windows/win32/api/dxcapi/ns-dxcapi-idxccompiler and https://learn.microsoft.com/en-us/windows/win32/api/dxcapi/ns-dxcapi-idxccompiler2 for correct signatures. --- vendor/directx/dxc/dxcapi.odin | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/directx/dxc/dxcapi.odin b/vendor/directx/dxc/dxcapi.odin index 23dbc8f30..96a925ef9 100644 --- a/vendor/directx/dxc/dxcapi.odin +++ b/vendor/directx/dxc/dxcapi.odin @@ -194,7 +194,7 @@ ICompiler_VTable :: struct { using iunknown_vtable: IUnknown_VTable, Compile: proc "system" ( this: ^ICompiler, - pSource: ^Buffer, + pSource: ^IBlob, pSourceName: wstring, pEntryPoint: wstring, pTargetProfile: wstring, @@ -206,7 +206,7 @@ ICompiler_VTable :: struct { ppResult: ^^IOperationResult) -> HRESULT, Preprocess: proc "system" ( this: ^ICompiler, - pSource: ^Buffer, + pSource: ^IBlob, pSourceName: wstring, pArguments: [^]wstring, argCount: u32, @@ -227,7 +227,7 @@ ICompiler2_VTable :: struct { using idxccompiler_vtable: ^ICompiler_VTable, CompileWithDebug: proc "system" ( this: ^ICompiler2, - pSource: ^Buffer, + pSource: ^IBlob, pSourceName: wstring, pEntryPoint: wstring, pTargetProfile: wstring, From 77f4199af6bd332f58f342c6f536a2cff9d26397 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 8 Jun 2025 14:56:41 +0200 Subject: [PATCH 28/35] Simplify `_xdg_user_dirs_lookup` --- core/os/os2/errors.odin | 32 ++++++++++--------- core/os/os2/user_posix.odin | 62 ++++++++++++------------------------- 2 files changed, 37 insertions(+), 57 deletions(-) diff --git a/core/os/os2/errors.odin b/core/os/os2/errors.odin index 0aff335bb..a73aee9a6 100644 --- a/core/os/os2/errors.odin +++ b/core/os/os2/errors.odin @@ -28,6 +28,7 @@ General_Error :: enum u32 { Pattern_Has_Separator, No_HOME_Variable, + Wordexp_Failed, Unsupported, } @@ -61,21 +62,22 @@ error_string :: proc(ferr: Error) -> string { case General_Error: switch e { case .None: return "" - case .Permission_Denied: return "permission denied" - case .Exist: return "file already exists" - case .Not_Exist: return "file does not exist" - case .Closed: return "file already closed" - case .Timeout: return "i/o timeout" - case .Broken_Pipe: return "Broken pipe" - case .No_Size: return "file has no definite size" - case .Invalid_File: return "invalid file" - case .Invalid_Dir: return "invalid directory" - case .Invalid_Path: return "invalid path" - case .Invalid_Callback: return "invalid callback" - case .Invalid_Command: return "invalid command" - case .Unsupported: return "unsupported" - case .Pattern_Has_Separator: return "pattern has separator" - case .No_HOME_Variable: return "no $HOME variable" + case .Permission_Denied: return "permission denied" + case .Exist: return "file already exists" + case .Not_Exist: return "file does not exist" + case .Closed: return "file already closed" + case .Timeout: return "i/o timeout" + case .Broken_Pipe: return "Broken pipe" + case .No_Size: return "file has no definite size" + case .Invalid_File: return "invalid file" + case .Invalid_Dir: return "invalid directory" + case .Invalid_Path: return "invalid path" + case .Invalid_Callback: return "invalid callback" + case .Invalid_Command: return "invalid command" + case .Unsupported: return "unsupported" + case .Pattern_Has_Separator: return "pattern has separator" + case .No_HOME_Variable: return "no $HOME variable" + case .Wordexp_Failed: return "posix.wordexp was unable to expand" } case io.Error: switch e { diff --git a/core/os/os2/user_posix.odin b/core/os/os2/user_posix.odin index 2c31f7eb8..f271b8913 100644 --- a/core/os/os2/user_posix.odin +++ b/core/os/os2/user_posix.odin @@ -2,6 +2,7 @@ package os2 import "base:runtime" +import "core:encoding/ini" import "core:strings" import "core:sys/posix" @@ -153,53 +154,30 @@ _xdg_lookup :: proc(xdg_key: string, fallback_suffix: string, allocator: runtime // If `/user-dirs.dirs` doesn't exist, or `xdg_key` can't be found there: returns `""` _xdg_user_dirs_lookup :: proc(xdg_key: string, allocator: runtime.Allocator) -> (dir: string, err: Error) { temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + config_dir := user_config_dir(temp_allocator) or_return + user_dirs_path := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return + content := read_entire_file(user_dirs_path, temp_allocator) or_return - config_dir := user_config_dir(temp_allocator) or_return - - user_dirs_path := concatenate({config_dir, "/user-dirs.dirs"}, temp_allocator) or_return - user_dirs_content_bytes, read_err := read_entire_file(user_dirs_path, temp_allocator) - if read_err == .Not_Exist { - return - } else if read_err != nil { - err = read_err - return - } - user_dirs_content := string(user_dirs_content_bytes) - - lines := strings.split_lines(user_dirs_content, temp_allocator) or_return - - home_env := get_env("HOME", temp_allocator) - if home_env == "" { - err = .No_HOME_Variable - return + it := ini.Iterator{ + section = "", + _src = string(content), + options = ini.Options{ + comment = "#", + key_lower_case = false, + }, } - for line in lines { - ss := strings.split_n(line, "=", 2, temp_allocator) or_return - (len(ss) == 2) or_continue - sl := strings.trim_space(ss[0]) - sr := ss[1] + for k, v in ini.iterate(&it) { + if k == xdg_key { + we: posix.wordexp_t + defer posix.wordfree(&we) - (sl == xdg_key) or_continue + if _err := posix.wordexp(strings.clone_to_cstring(v, temp_allocator), &we, nil); _err != nil || we.we_wordc != 1 { + return "", .Wordexp_Failed + } - (len(sr) > 2) or_continue - - lq := strings.index_byte(sr, '"') - (lq != -1) or_continue - rq := strings.index_byte(sr[lq+1:], '"') + lq+1 - (rq != -1) or_continue - - sr = sr[lq+1:rq] - - we: posix.wordexp_t - we_err := posix.wordexp(strings.clone_to_cstring(sr, temp_allocator), &we, nil) - (we_err == nil) or_continue - defer posix.wordfree(&we) - - (we.we_wordc == 1) or_continue - - dir = strings.clone_from_cstring(we.we_wordv[0], allocator) or_return - return + return strings.clone_from_cstring(we.we_wordv[0], allocator) + } } return } \ No newline at end of file From 876f1c02b7ab0522179c5ef154a879973832af70 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Sun, 8 Jun 2025 12:47:50 -0400 Subject: [PATCH 29/35] Added missing parameter to `gettimeofday` --- core/sys/linux/sys.odin | 4 ++-- core/sys/linux/types.odin | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index 985623e85..e9d0f4d76 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -1412,8 +1412,8 @@ umask :: proc "contextless" (mask: Mode) -> Mode { Get current time. Available since Linux 1.0. */ -gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) { - ret := syscall(SYS_gettimeofday, tv) +gettimeofday :: proc "contextless" (tv: ^Time_Val, tz: ^Time_Zone) -> (Errno) { + ret := syscall(SYS_gettimeofday, tv, tz) return Errno(-ret) } diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 08e0026d3..77a0b8ef4 100644 --- a/core/sys/linux/types.odin +++ b/core/sys/linux/types.odin @@ -72,6 +72,14 @@ Time_Val :: struct { microseconds: int, } +/* + Represents a time zone. +*/ +Time_Zone :: struct { + minutes_west: int, + dst_time: int, +} + /* Access and modification times for files */ From 7662e7d843f4248103ca818c43ff59e2683e30d5 Mon Sep 17 00:00:00 2001 From: Tohei Ichikawa Date: Sun, 8 Jun 2025 16:07:11 -0400 Subject: [PATCH 30/35] Removed obsolete `tz` param from `gettimeofday` --- core/sys/linux/sys.odin | 4 ++-- core/sys/linux/types.odin | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index e9d0f4d76..0e4cebce2 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -1412,8 +1412,8 @@ umask :: proc "contextless" (mask: Mode) -> Mode { Get current time. Available since Linux 1.0. */ -gettimeofday :: proc "contextless" (tv: ^Time_Val, tz: ^Time_Zone) -> (Errno) { - ret := syscall(SYS_gettimeofday, tv, tz) +gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) { + ret := syscall(SYS_gettimeofday, tv, nil) return Errno(-ret) } diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin index 77a0b8ef4..08e0026d3 100644 --- a/core/sys/linux/types.odin +++ b/core/sys/linux/types.odin @@ -72,14 +72,6 @@ Time_Val :: struct { microseconds: int, } -/* - Represents a time zone. -*/ -Time_Zone :: struct { - minutes_west: int, - dst_time: int, -} - /* Access and modification times for files */ From d2d187eaaa4fecd33bf654dcd9013ca2ddbdef5b Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Sun, 8 Jun 2025 22:53:52 +0200 Subject: [PATCH 31/35] Work around untyped nil --- core/sys/linux/sys.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index 0e4cebce2..f28a5fdb2 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -1413,7 +1413,8 @@ umask :: proc "contextless" (mask: Mode) -> Mode { Available since Linux 1.0. */ gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) { - ret := syscall(SYS_gettimeofday, tv, nil) + null: uintptr + ret := syscall(SYS_gettimeofday, tv, null) return Errno(-ret) } From 54f018ffc7c76d12598c0fd8f6aca9b2030e9992 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Jun 2025 17:43:56 -0400 Subject: [PATCH 32/35] Guard against untyped `nil` in type cycle and type info sections Fixes #5299 --- src/checker.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/checker.cpp b/src/checker.cpp index ff7194835..67dee9963 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -6672,7 +6672,7 @@ gb_internal void check_sort_init_and_fini_procedures(Checker *c) { gb_internal void add_type_info_for_type_definitions(Checker *c) { for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; - if (e->kind == Entity_TypeName && e->type != nullptr) { + if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { i64 align = type_align_of(e->type); if (align > 0 && ptr_set_exists(&c->info.minimum_dependency_set, e)) { add_type_info_type(&c->builtin_ctx, e->type); @@ -6794,7 +6794,7 @@ gb_internal void check_parsed_files(Checker *c) { // NOTE(bill): Check for illegal cyclic type declarations for_array(i, c->info.definitions) { Entity *e = c->info.definitions[i]; - if (e->kind == Entity_TypeName && e->type != nullptr) { + if (e->kind == Entity_TypeName && e->type != nullptr && is_type_typed(e->type)) { (void)type_align_of(e->type); } else if (e->kind == Entity_Procedure) { DeclInfo *decl = e->decl_info; From 0747032e4a836a4295538d2c4945f2610ad2e615 Mon Sep 17 00:00:00 2001 From: Feoramund <161657516+Feoramund@users.noreply.github.com> Date: Sun, 8 Jun 2025 17:46:48 -0400 Subject: [PATCH 33/35] Use idiomatic `rawptr(nil)` --- core/sys/linux/sys.odin | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin index f28a5fdb2..deb22726f 100644 --- a/core/sys/linux/sys.odin +++ b/core/sys/linux/sys.odin @@ -1413,8 +1413,7 @@ umask :: proc "contextless" (mask: Mode) -> Mode { Available since Linux 1.0. */ gettimeofday :: proc "contextless" (tv: ^Time_Val) -> (Errno) { - null: uintptr - ret := syscall(SYS_gettimeofday, tv, null) + ret := syscall(SYS_gettimeofday, tv, rawptr(nil)) return Errno(-ret) } From 3407bd7cf9d1af6dc79ed0476f1d009630088809 Mon Sep 17 00:00:00 2001 From: Colin Davidson Date: Sun, 8 Jun 2025 17:03:43 -0700 Subject: [PATCH 34/35] better handle offset-0 case --- core/time/timezone/tzif.odin | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/core/time/timezone/tzif.odin b/core/time/timezone/tzif.odin index 3fec7be53..804211ef4 100644 --- a/core/time/timezone/tzif.odin +++ b/core/time/timezone/tzif.odin @@ -577,12 +577,7 @@ parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allo 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 - } - + if len(local_time_types) == 1 && local_time_types[0].utoff == 0 { return nil, true } From 179e5b88357f7d88e715713ec5c4d82bc51df811 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 9 Jun 2025 15:09:04 +0200 Subject: [PATCH 35/35] Fix #4705 Allocate `doc.tokenizer`, and free it in `destroy`. --- core/encoding/xml/xml_reader.odin | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/encoding/xml/xml_reader.odin b/core/encoding/xml/xml_reader.odin index d616be9dc..707d2b3f3 100644 --- a/core/encoding/xml/xml_reader.odin +++ b/core/encoding/xml/xml_reader.odin @@ -175,7 +175,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha data = bytes.clone(data) } - t := &Tokenizer{} + t := new(Tokenizer) init(t, string(data), path, error_handler) doc = new(Document) @@ -403,6 +403,7 @@ destroy :: proc(doc: ^Document) { } delete(doc.strings_to_free) + free(doc.tokenizer) free(doc) }