From 3287e1b0f0195401f6db8ffe9b94be67b352af57 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Apr 2025 13:19:00 +0200 Subject: [PATCH 1/9] Fix HXA defer warning --- core/encoding/hxa/read.odin | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin index a679946f8..6dde16848 100644 --- a/core/encoding/hxa/read.odin +++ b/core/encoding/hxa/read.odin @@ -79,7 +79,6 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato read_meta :: proc(r: ^Reader, capacity: u32le, allocator := context.allocator, loc := #caller_location) -> (meta_data: []Meta, err: Read_Error) { meta_data = make([]Meta, int(capacity), allocator=allocator) count := 0 - defer meta_data = meta_data[:count] for &m in meta_data { m.name = read_name(r) or_return @@ -105,6 +104,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato count += 1 } + meta_data = meta_data[:count] return } @@ -112,7 +112,6 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato stack_count := read_value(r, u32le) or_return layer_count := 0 layers = make(Layer_Stack, stack_count, allocator=allocator, loc=loc) - defer layers = layers[:layer_count] for &layer in layers { layer.name = read_name(r) or_return layer.components = read_value(r, u8) or_return @@ -136,6 +135,7 @@ read :: proc(data: []byte, filename := "", print_error := false, allocato layer_count += 1 } + layers = layers[:layer_count] return } From c13b68f103722dfdd12edcdff36c0b48685a35f0 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Apr 2025 13:33:21 +0200 Subject: [PATCH 2/9] Fix os2/process defer error. --- core/os/os2/process.odin | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin index c90e3add2..98995981b 100644 --- a/core/os/os2/process.odin +++ b/core/os/os2/process.odin @@ -407,11 +407,9 @@ process_exec :: proc( { stdout_b: [dynamic]byte stdout_b.allocator = allocator - defer stdout = stdout_b[:] stderr_b: [dynamic]byte stderr_b.allocator = allocator - defer stderr = stderr_b[:] buf: [1024]u8 = --- @@ -450,6 +448,9 @@ process_exec :: proc( } } } + + stdout = stdout_b[:] + stderr = stderr_b[:] } if err != nil { From a5e513567bd87fbae768ebe6e54d6e6c92260dfc Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Apr 2025 14:58:41 +0200 Subject: [PATCH 3/9] Optimize regex match iterator. Reuse virtual machine and capture groups between matches. --- core/text/regex/regex.odin | 56 +++++++++++++------ .../virtual_machine/virtual_machine.odin | 11 +++- .../core/text/regex/test_core_text_regex.odin | 3 +- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/core/text/regex/regex.odin b/core/text/regex/regex.odin index a887d5967..7f082caaf 100644 --- a/core/text/regex/regex.odin +++ b/core/text/regex/regex.odin @@ -70,9 +70,9 @@ An iterator to repeatedly match a pattern against a string, to be used with `*_i */ Match_Iterator :: struct { haystack: string, - offset: int, regex: Regular_Expression, capture: Capture, + vm: virtual_machine.Machine, idx: int, temp: runtime.Allocator, } @@ -283,10 +283,11 @@ create_iterator :: proc( flags := flags flags += {.Global} // We're iterating over a string, so the next match could start anywhere - result.haystack = str - result.regex = create(pattern, flags, permanent_allocator, temporary_allocator) or_return - result.capture = preallocate_capture() - result.temp = temporary_allocator + result.regex = create(pattern, flags, permanent_allocator, temporary_allocator) or_return + result.capture = preallocate_capture() + result.temp = temporary_allocator + result.vm = virtual_machine.create(result.regex.program, str) + result.vm.class_data = result.regex.class_data return } @@ -444,24 +445,47 @@ Returns: - ok: A bool indicating if there was a match, stopping the iteration on `false`. */ match_iterator :: proc(it: ^Match_Iterator) -> (result: Capture, index: int, ok: bool) { + assert(len(it.capture.groups) >= common.MAX_CAPTURE_GROUPS, + "Pre-allocated RegEx capture `groups` must be at least 10 elements long.") + assert(len(it.capture.pos) >= common.MAX_CAPTURE_GROUPS, + "Pre-allocated RegEx capture `pos` must be at least 10 elements long.") + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + + saved: ^[2 * common.MAX_CAPTURE_GROUPS]int + { + context.allocator = it.temp + if .Unicode in it.regex.flags { + saved, ok = virtual_machine.run(&it.vm, true) + } else { + saved, ok = virtual_machine.run(&it.vm, false) + } + } + + str := string(it.vm.memory) num_groups: int - num_groups, ok = match_with_preallocated_capture( - it.regex, - it.haystack[it.offset:], - &it.capture, - it.temp, - ) + + if saved != nil { + n := 0 + + #no_bounds_check for i := 0; i < len(saved); i += 2 { + a, b := saved[i], saved[i + 1] + if a == -1 || b == -1 { + continue + } + + it.capture.groups[n] = str[a:b] + it.capture.pos[n] = {a, b} + n += 1 + } + num_groups = n + } defer if ok { it.idx += 1 } if num_groups > 0 { - for i in 0.. (opcodes: int) { return } -create :: proc(code: Program, str: string) -> (vm: Machine) { +create :: proc(code: Program, str: string, allocator := context.allocator) -> (vm: Machine) { assert(len(code) > 0, "RegEx VM has no instructions.") + context.allocator = allocator vm.memory = str vm.code = code @@ -644,3 +645,11 @@ create :: proc(code: Program, str: string) -> (vm: Machine) { return } + +destroy :: proc(vm: Machine, allocator := context.allocator) { + context.allocator = allocator + + delete(vm.busy_map) + free(vm.threads) + free(vm.next_threads) +} \ No newline at end of file diff --git a/tests/core/text/regex/test_core_text_regex.odin b/tests/core/text/regex/test_core_text_regex.odin index 4a34ced68..913e716e5 100644 --- a/tests/core/text/regex/test_core_text_regex.odin +++ b/tests/core/text/regex/test_core_text_regex.odin @@ -1126,9 +1126,8 @@ test_match_iterator :: proc(t: ^testing.T) { testing.expect_value(t, err, nil) (err == nil) or_continue - count: int for capture, idx in regex.match(&it) { - if count > len(test.expected) { + if idx >= len(test.expected) { break } check_capture(t, capture, test.expected[idx]) From 2b26c0b39ee851509eaa29eb074fcfd0df242201 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Apr 2025 15:02:36 +0200 Subject: [PATCH 4/9] Remove now unused field. --- core/text/regex/regex.odin | 1 - 1 file changed, 1 deletion(-) diff --git a/core/text/regex/regex.odin b/core/text/regex/regex.odin index 7f082caaf..1e59b1800 100644 --- a/core/text/regex/regex.odin +++ b/core/text/regex/regex.odin @@ -69,7 +69,6 @@ Regular_Expression :: struct { An iterator to repeatedly match a pattern against a string, to be used with `*_iterator` procedures. */ Match_Iterator :: struct { - haystack: string, regex: Regular_Expression, capture: Capture, vm: virtual_machine.Machine, From 716bd479a93d858f1da07fc113b6a48b30cbcabd Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 7 Apr 2025 21:33:57 +0200 Subject: [PATCH 5/9] Disallow .Multiline in iterator. --- core/text/regex/regex.odin | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/text/regex/regex.odin b/core/text/regex/regex.odin index 1e59b1800..c805740f7 100644 --- a/core/text/regex/regex.odin +++ b/core/text/regex/regex.odin @@ -28,6 +28,8 @@ Creation_Error :: enum { Expected_Delimiter, // An unknown letter was supplied to `create_by_user` after the last delimiter. Unknown_Flag, + // An unsupported flag was supplied. + Unsupported_Flag, } Error :: union #shared_nil { @@ -67,6 +69,7 @@ Regular_Expression :: struct { /* An iterator to repeatedly match a pattern against a string, to be used with `*_iterator` procedures. +Note: Does not handle `.Multiline` properly. */ Match_Iterator :: struct { regex: Regular_Expression, @@ -282,6 +285,10 @@ create_iterator :: proc( flags := flags flags += {.Global} // We're iterating over a string, so the next match could start anywhere + if .Multiline in flags { + return {}, .Unsupported_Flag + } + result.regex = create(pattern, flags, permanent_allocator, temporary_allocator) or_return result.capture = preallocate_capture() result.temp = temporary_allocator @@ -435,6 +442,7 @@ match_with_preallocated_capture :: proc( /* Iterate over a `Match_Iterator` and return successive captures. +Note: Does not handle `.Multiline` properly. Inputs: - it: Pointer to the `Match_Iterator` to iterate over. From 92ac86ae3ca079d0485ed8b56fa45441b6c91356 Mon Sep 17 00:00:00 2001 From: Barinzaya Date: Mon, 7 Apr 2025 15:41:21 -0400 Subject: [PATCH 6/9] Fixed `fmt` handling of `bit_set[Enum]` when `min(Enum) != 0`. The lower bound of the `bit_set` was only being applied *after* searching for a matching enum value, so values wouldn't line up if the minimum value of the enum wasn't 0. --- core/fmt/fmt.odin | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 826a21ee9..f42ab700a 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1802,11 +1802,8 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') { e, is_enum := et.variant.(runtime.Type_Info_Enum) commas := 0 - loop: for i in 0 ..< bit_size { - if bits & (1< 0 { io.write_string(fi.writer, ", ", &fi.n) } @@ -1829,8 +1826,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "", verb: rune = 'v') { } } } - v := i64(i) + info.lower - io.write_i64(fi.writer, v, 10, &fi.n) + io.write_i64(fi.writer, i, 10, &fi.n) commas += 1 } } From 447177e48692177a2854c89901c10f9592280d56 Mon Sep 17 00:00:00 2001 From: Thomas Wagner Date: Mon, 7 Apr 2025 22:10:11 +0200 Subject: [PATCH 7/9] vendor/glfw: fix SetMonitorCallback and MonitorProc type definition SetMonitorCallback does not take a WindowHandle and MonitorProc has a MonitorHandle as the first argument. --- vendor/glfw/bindings/bindings.odin | 5 +++-- vendor/glfw/bindings/types.odin | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/vendor/glfw/bindings/bindings.odin b/vendor/glfw/bindings/bindings.odin index e59239483..75cdab4cd 100644 --- a/vendor/glfw/bindings/bindings.odin +++ b/vendor/glfw/bindings/bindings.odin @@ -193,7 +193,6 @@ foreign glfw { SetWindowPosCallback :: proc(window: WindowHandle, cbfun: WindowPosProc) -> WindowPosProc --- SetFramebufferSizeCallback :: proc(window: WindowHandle, cbfun: FramebufferSizeProc) -> FramebufferSizeProc --- SetDropCallback :: proc(window: WindowHandle, cbfun: DropProc) -> DropProc --- - SetMonitorCallback :: proc(window: WindowHandle, cbfun: MonitorProc) -> MonitorProc --- SetWindowMaximizeCallback :: proc(window: WindowHandle, cbfun: WindowMaximizeProc) -> WindowMaximizeProc --- SetWindowContentScaleCallback :: proc(window: WindowHandle, cbfun: WindowContentScaleProc) -> WindowContentScaleProc --- @@ -204,7 +203,9 @@ foreign glfw { SetCharCallback :: proc(window: WindowHandle, cbfun: CharProc) -> CharProc --- SetCharModsCallback :: proc(window: WindowHandle, cbfun: CharModsProc) -> CharModsProc --- SetCursorEnterCallback :: proc(window: WindowHandle, cbfun: CursorEnterProc) -> CursorEnterProc --- - SetJoystickCallback :: proc(cbfun: JoystickProc) -> JoystickProc --- + + SetMonitorCallback :: proc(cbfun: MonitorProc) -> MonitorProc --- + SetJoystickCallback :: proc(cbfun: JoystickProc) -> JoystickProc --- SetErrorCallback :: proc(cbfun: ErrorProc) -> ErrorProc --- diff --git a/vendor/glfw/bindings/types.odin b/vendor/glfw/bindings/types.odin index 5bdbf9cb9..bb1c7e431 100644 --- a/vendor/glfw/bindings/types.odin +++ b/vendor/glfw/bindings/types.odin @@ -48,7 +48,7 @@ WindowMaximizeProc :: #type proc "c" (window: WindowHandle, iconified: c.int WindowContentScaleProc :: #type proc "c" (window: WindowHandle, xscale, yscale: f32) FramebufferSizeProc :: #type proc "c" (window: WindowHandle, width, height: c.int) DropProc :: #type proc "c" (window: WindowHandle, count: c.int, paths: [^]cstring) -MonitorProc :: #type proc "c" (window: WindowHandle, event: c.int) +MonitorProc :: #type proc "c" (monitor: MonitorHandle, event: c.int) KeyProc :: #type proc "c" (window: WindowHandle, key, scancode, action, mods: c.int) MouseButtonProc :: #type proc "c" (window: WindowHandle, button, action, mods: c.int) From eeb8b8dcc409db59fd45ba1d736367963dcd2c56 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 8 Apr 2025 10:13:45 +0200 Subject: [PATCH 8/9] Fix #5020 --- src/check_type.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/check_type.cpp b/src/check_type.cpp index 9d4defbb2..cd55bfdc0 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2082,7 +2082,9 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) { bool ok = true; if (p->flags&FieldFlag_any_int) { - if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { + if (op.type == nullptr) { + ok = false; + } else if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) { ok = false; } else if (!check_is_castable_to(ctx, &op, type)) { ok = false; From 329b15961ab95ae2f9fb54e8eb15322089f81a61 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Tue, 8 Apr 2025 11:44:35 +0200 Subject: [PATCH 9/9] Don't run demo if building Odin fails. `cl`'s return value was stomped by `mt`, so we ran the demo if `cl` failed, but `mt` succeeded. This obscures `cl`'s output, so we're now checking both for errors. --- build.bat | 1 + 1 file changed, 1 insertion(+) diff --git a/build.bat b/build.bat index 4c015e133..ae733ff2a 100644 --- a/build.bat +++ b/build.bat @@ -138,6 +138,7 @@ del *.ilk > NUL 2> NUL rc %rc_flags% %odin_rc% cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name% +if %errorlevel% neq 0 goto end_of_build mt -nologo -inputresource:%exe_name%;#1 -manifest misc\odin.manifest -outputresource:%exe_name%;#1 -validate_manifest -identity:"odin, processorArchitecture=amd64, version=%odin_version_full%, type=win32" if %errorlevel% neq 0 goto end_of_build