From 3a7c4994acb13d2a77baf5a7911ddac666562092 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Feb 2026 13:11:23 +0100 Subject: [PATCH 1/2] Fix #6204 Use temporary stack buffers and some global scratch space to remove the need for any allocator. --- core/sys/info/platform_windows.odin | 152 ++++++++++++++-------------- core/sys/info/sysinfo.odin | 29 ++++++ core/unicode/utf16/utf16.odin | 22 ++-- 3 files changed, 118 insertions(+), 85 deletions(-) diff --git a/core/sys/info/platform_windows.odin b/core/sys/info/platform_windows.odin index bad99f811..c28170919 100644 --- a/core/sys/info/platform_windows.odin +++ b/core/sys/info/platform_windows.odin @@ -3,17 +3,12 @@ package sysinfo import sys "core:sys/windows" import "base:intrinsics" import "core:strings" -import "core:strconv" import "core:unicode/utf16" - -// import "core:fmt" import "base:runtime" -@(private) -version_string_buf: [1024]u8 - @(init, private) init_os_version :: proc "contextless" () { + // NOTE(Jeroen): Only needed for the string builder. context = runtime.default_context() /* @@ -226,14 +221,14 @@ init_os_version :: proc "contextless" () { // Grab Windows DisplayVersion (like 20H02) format_display_version :: proc (b: ^strings.Builder) -> (version: string) { - dv, ok := read_reg_string( + scratch: [512]u8 + + if dv, ok := read_reg_string( sys.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "DisplayVersion", - ) - defer delete(dv) // It'll be interned into `version_string_buf` - - if ok { + scratch[:], + ); ok { strings.write_string(b, " (version: ") l := strings.builder_len(b^) strings.write_string(b, dv) @@ -245,13 +240,11 @@ init_os_version :: proc "contextless" () { // Grab build number and UBR format_build_number :: proc (b: ^strings.Builder, major_build: int) -> (ubr: int) { - res, ok := read_reg_i32( + if res, ok := read_reg_i32( sys.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", "UBR", - ) - - if ok { + ); ok { ubr = int(res) strings.write_string(b, ", build: ") strings.write_int(b, major_build) @@ -283,9 +276,6 @@ init_ram :: proc "contextless" () { init_gpu_info :: proc "contextless" () { GPU_ROOT_KEY :: `SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}` - context = runtime.default_context() - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - gpu_key: sys.HKEY if status := sys.RegOpenKeyExW( sys.HKEY_LOCAL_MACHINE, @@ -298,16 +288,18 @@ init_gpu_info :: proc "contextless" () { } defer sys.RegCloseKey(gpu_key) - gpu_list: [dynamic]GPU gpu: ^GPU + gpu_count := 0 index := sys.DWORD(0) - for { + gpu_loop: for { defer index += 1 buf_wstring: [100]u16 buf_len := u32(len(buf_wstring)) - buf_utf8: [4 * len(buf_wstring)]u8 + buf_key: [4 * len(buf_wstring)]u8 + buf_leaf: [100]u8 + buf_scratch: [100]u8 if status := sys.RegEnumKeyW( gpu_key, @@ -318,59 +310,71 @@ init_gpu_info :: proc "contextless" () { break } - utf16.decode_to_utf8(buf_utf8[:], buf_wstring[:]) - leaf := string(cstring(&buf_utf8[0])) + utf16.decode_to_utf8(buf_leaf[:], buf_wstring[:]) + leaf := string(cstring(&buf_leaf[0])) // Skip leafs that are not of the form 000x - if _, is_integer := strconv.parse_int(leaf, 10); !is_integer { + if !is_integer(leaf) { continue } - key := strings.concatenate({GPU_ROOT_KEY, "\\", leaf}, context.temp_allocator) + n := copy(buf_key[:], GPU_ROOT_KEY) + buf_key[n] = '\\' + copy(buf_key[n+1:], leaf) - if vendor, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "ProviderName"); ok { - idx := append(&gpu_list, GPU{vendor_name = vendor}) - gpu = &gpu_list[idx - 1] + key_len := len(GPU_ROOT_KEY) + len(leaf) + 1 + + utf16.encode_string(buf_wstring[:], string(buf_key[:key_len])) + key := cstring16(&buf_wstring[0]) + + if res, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "ProviderName", buf_scratch[:]); ok { + if vendor, s_ok := intern_gpu_string(res); s_ok { + gpu = &_gpus[gpu_count] + gpu.vendor_name = vendor + } else { + break gpu_loop + } } else { continue } - if desc, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc"); ok { - gpu.model_name = desc + if res, ok := read_reg_string(sys.HKEY_LOCAL_MACHINE, key, "DriverDesc", buf_scratch[:]); ok { + if model_name, s_ok := intern_gpu_string(res); s_ok { + gpu = &_gpus[gpu_count] + gpu.model_name = model_name + } else { + break gpu_loop + } } if vram, ok := read_reg_i64(sys.HKEY_LOCAL_MACHINE, key, "HardwareInformation.qwMemorySize"); ok { gpu.total_ram = int(vram) } + + gpu_count += 1 + if gpu_count > MAX_GPUS { + break gpu_loop + } } - gpus = gpu_list[:] + gpus = _gpus[:gpu_count] } @(private) -read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok: bool) { +read_reg_string :: proc "contextless" (hkey: sys.HKEY, subkey, val: cstring16, res_buf: []u8) -> (res: string, ok: bool) { if len(subkey) == 0 || len(val) == 0 { return } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - - BUF_SIZE :: 1024 - key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) - val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) - - utf16.encode_string(key_name_wide, subkey) - utf16.encode_string(val_name_wide, val) - - result_wide := make([]u16, BUF_SIZE, context.temp_allocator) - result_size := sys.DWORD(BUF_SIZE * size_of(u16)) + buf_utf16: [1024]u16 + result_size := sys.DWORD(size_of(buf_utf16)) status := sys.RegGetValueW( hkey, - cstring16(&key_name_wide[0]), - cstring16(&val_name_wide[0]), + subkey, + val, sys.RRF_RT_REG_SZ, nil, - raw_data(result_wide[:]), + raw_data(buf_utf16[:]), &result_size, ) if status != 0 { @@ -378,31 +382,22 @@ read_reg_string :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: string, ok return } - // Result string will be allocated for the caller. - result_utf8 := make([]u8, BUF_SIZE * 4, context.temp_allocator) - utf16.decode_to_utf8(result_utf8, result_wide[:result_size]) - return strings.clone_from_cstring(cstring(raw_data(result_utf8))), true + utf16.decode_to_utf8(res_buf[:result_size], buf_utf16[:]) + res = string(cstring(&res_buf[0])) + return res, true } + @(private) -read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool) { +read_reg_i32 :: proc "contextless" (hkey: sys.HKEY, subkey, val: cstring16) -> (res: i32, ok: bool) { if len(subkey) == 0 || len(val) == 0 { return } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - - BUF_SIZE :: 1024 - key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) - val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) - - utf16.encode_string(key_name_wide, subkey) - utf16.encode_string(val_name_wide, val) - result_size := sys.DWORD(size_of(i32)) status := sys.RegGetValueW( hkey, - cstring16(&key_name_wide[0]), - cstring16(&val_name_wide[0]), + subkey, + val, sys.RRF_RT_REG_DWORD, nil, &res, @@ -410,26 +405,18 @@ read_reg_i32 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i32, ok: bool ) return res, status == 0 } + @(private) -read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool) { +read_reg_i64 :: proc "contextless" (hkey: sys.HKEY, subkey, val: cstring16) -> (res: i64, ok: bool) { if len(subkey) == 0 || len(val) == 0 { return } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - - BUF_SIZE :: 1024 - key_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) - val_name_wide := make([]u16, BUF_SIZE, context.temp_allocator) - - utf16.encode_string(key_name_wide, subkey) - utf16.encode_string(val_name_wide, val) - result_size := sys.DWORD(size_of(i64)) status := sys.RegGetValueW( hkey, - cstring16(&key_name_wide[0]), - cstring16(&val_name_wide[0]), + subkey, + val, sys.RRF_RT_REG_QWORD, nil, &res, @@ -437,3 +424,20 @@ read_reg_i64 :: proc(hkey: sys.HKEY, subkey, val: string) -> (res: i64, ok: bool ) return res, status == 0 } + +@(private) +is_integer :: proc "contextless" (s: string) -> (ok: bool) { + if s == "" { + return + } + + ok = true + + for r in s { + switch r { + case '0'..='9': continue + case: return false + } + } + return +} \ No newline at end of file diff --git a/core/sys/info/sysinfo.odin b/core/sys/info/sysinfo.odin index 75cc237c6..07f8f0c81 100644 --- a/core/sys/info/sysinfo.odin +++ b/core/sys/info/sysinfo.odin @@ -48,3 +48,32 @@ GPU :: struct { model_name: string, total_ram: int, } + +@(private) +version_string_buf: [1024]u8 + +@(private) +MAX_GPUS :: 16 + +@(private) +_gpus: [MAX_GPUS]GPU + +@(private) +_gpu_string_buf: [MAX_GPUS * 256 * 2]u8 // Reserve up to 256 bytes for each GPU's vendor and model name + +@(private) +_gpu_string_offset: int + +@(private) +intern_gpu_string :: proc "contextless" (str: string) -> (res: string, ok: bool) { + if _gpu_string_offset + len(str) + 1 > size_of(_gpu_string_buf) { + return "", false + } + + n := copy(_gpu_string_buf[_gpu_string_offset:], str) + _gpu_string_buf[_gpu_string_offset + len(str)] = 0 + res = string(_gpu_string_buf[_gpu_string_offset:][:len(str)]) + _gpu_string_offset += n + 1 + + return res, true +} \ No newline at end of file diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin index 990d5bafa..5cc996f8b 100644 --- a/core/unicode/utf16/utf16.odin +++ b/core/unicode/utf16/utf16.odin @@ -12,11 +12,11 @@ _surr3 :: 0xe000 _surr_self :: 0x10000 -is_surrogate :: proc(r: rune) -> bool { +is_surrogate :: proc "contextless" (r: rune) -> bool { return _surr1 <= r && r < _surr3 } -decode_surrogate_pair :: proc(r1, r2: rune) -> rune { +decode_surrogate_pair :: proc "contextless" (r1, r2: rune) -> rune { if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 { return (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self } @@ -24,7 +24,7 @@ decode_surrogate_pair :: proc(r1, r2: rune) -> rune { } -encode_surrogate_pair :: proc(c: rune) -> (r1, r2: rune) { +encode_surrogate_pair :: proc "contextless" (c: rune) -> (r1, r2: rune) { r := c if r < _surr_self || r > MAX_RUNE { return REPLACEMENT_CHAR, REPLACEMENT_CHAR @@ -33,7 +33,7 @@ encode_surrogate_pair :: proc(c: rune) -> (r1, r2: rune) { return _surr1 + (r>>10)&0x3ff, _surr2 + r&0x3ff } -encode :: proc(d: []u16, s: []rune) -> int { +encode :: proc "contextless" (d: []u16, s: []rune) -> int { n, m := 0, len(d) loop: for r in s { switch r { @@ -59,7 +59,7 @@ encode :: proc(d: []u16, s: []rune) -> int { } -encode_string :: proc(d: []u16, s: string) -> int { +encode_string :: proc "contextless" (d: []u16, s: string) -> int { n, m := 0, len(d) loop: for r in s { switch r { @@ -84,7 +84,7 @@ encode_string :: proc(d: []u16, s: string) -> int { return n } -decode :: proc(d: []rune, s: []u16) -> (n: int) { +decode :: proc "contextless" (d: []rune, s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { if n >= len(d) { return @@ -107,7 +107,7 @@ decode :: proc(d: []rune, s: []u16) -> (n: int) { return } -decode_rune_in_string :: proc(s: string16) -> (r: rune, width: int) { +decode_rune_in_string :: proc "contextless" (s: string16) -> (r: rune, width: int) { r = rune(REPLACEMENT_CHAR) n := len(s) if n < 1 { @@ -144,7 +144,7 @@ rune_count :: proc{ rune_count_in_string, rune_count_in_slice, } -rune_count_in_string :: proc(s: string16) -> (n: int) { +rune_count_in_string :: proc "contextless" (s: string16) -> (n: int) { for i := 0; i < len(s); i += 1 { c := s[i] if _surr1 <= c && c < _surr2 && i+1 < len(s) && @@ -157,7 +157,7 @@ rune_count_in_string :: proc(s: string16) -> (n: int) { } -rune_count_in_slice :: proc(s: []u16) -> (n: int) { +rune_count_in_slice :: proc "contextless" (s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { c := s[i] if _surr1 <= c && c < _surr2 && i+1 < len(s) && @@ -170,7 +170,7 @@ rune_count_in_slice :: proc(s: []u16) -> (n: int) { } -decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) { +decode_to_utf8 :: proc "contextless" (d: []byte, s: []u16) -> (n: int) { for i := 0; i < len(s); i += 1 { if n >= len(d) { return @@ -190,4 +190,4 @@ decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) { n += copy(d[n:], b[:w]) } return -} +} \ No newline at end of file From 98413ad15450ac871c1181d656a1afd717d482bb Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Mon, 2 Feb 2026 13:16:13 +0100 Subject: [PATCH 2/2] Remove duplicate buffer --- core/sys/info/platform_bsd.odin | 3 --- core/sys/info/platform_darwin.odin | 3 --- core/sys/info/platform_freebsd.odin | 3 --- core/sys/info/platform_linux.odin | 3 --- 4 files changed, 12 deletions(-) diff --git a/core/sys/info/platform_bsd.odin b/core/sys/info/platform_bsd.odin index 2f8d7f5bb..349b74002 100644 --- a/core/sys/info/platform_bsd.odin +++ b/core/sys/info/platform_bsd.odin @@ -6,9 +6,6 @@ import "core:strings" import "core:strconv" import "base:runtime" -@(private) -version_string_buf: [1024]u8 - @(init, private) init_os_version :: proc "contextless" () { context = runtime.default_context() diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin index 07c26ec28..498c21846 100644 --- a/core/sys/info/platform_darwin.odin +++ b/core/sys/info/platform_darwin.odin @@ -7,9 +7,6 @@ import "core:strings" import "core:sys/unix" import NS "core:sys/darwin/Foundation" -@(private) -version_string_buf: [1024]u8 - @(init, private) init_platform :: proc "contextless" () { context = runtime.default_context() diff --git a/core/sys/info/platform_freebsd.odin b/core/sys/info/platform_freebsd.odin index eb39769de..ed5db6243 100644 --- a/core/sys/info/platform_freebsd.odin +++ b/core/sys/info/platform_freebsd.odin @@ -5,9 +5,6 @@ import "core:strings" import "core:strconv" import "base:runtime" -@(private) -version_string_buf: [1024]u8 - @(init, private) init_os_version :: proc "contextless" () { context = runtime.default_context() diff --git a/core/sys/info/platform_linux.odin b/core/sys/info/platform_linux.odin index 43cd580c1..671600640 100644 --- a/core/sys/info/platform_linux.odin +++ b/core/sys/info/platform_linux.odin @@ -7,9 +7,6 @@ import "core:strconv" import "core:strings" import "core:sys/linux" -@(private) -version_string_buf: [1024]u8 - @(init, private) init_os_version :: proc "contextless" () { context = runtime.default_context()